Buy

You may not have noticed, but all these cool new tools have made debugging... well... a nightmare.

Check it out: run inspect element on the yellow leaderboard. What if I need to know what file this color comes from? Well... apparently... it comes from a style tag!? That's not helpful!

Luckily, I happen to know that this code originally lives in main.scss on line 48:

82 lines assets/css/main.scss
... lines 1 - 46
.leaderboard {
background-color: #FFDF00;
padding: 10px;
border-radius: 5px;
}
... lines 52 - 82

But really, I need my browser to help me out.

JavaScript is no better. When I click a row in the table, it logs a message to the console... which is apparently coming from rep_log.js on line 200. But, that's line 200 of the final, compiled file. If you look at the source assets/js/rep_log.js, it doesn't even have a line 200!

8 lines assets/js/rep_log.js
const $ = require('jquery');
const RepLogApp = require('./Components/RepLogApp');
$(document).ready(function() {
... lines 5 - 6
});

Guys, this sucks! The answer, is sourcemaps. This is not a new concept: it's the idea that when your JavaScript or CSS is transformed, you somehow output a map that says which source line and file each final line comes from. Browsers already know how to read sourcemaps. So as long we output them, we'll start seeing the correct file and line when debugging.

Ok, I'm sold! Let's do it!

A Bit of Refactoring

Open webpack.config.js. First, we need to do a little bit of refactoring to make our life easier. Check out the CSS and Sass loaders: we're starting to get just a little bit of duplication... which is about to get worse:

80 lines webpack.config.js
... lines 1 - 4
module.exports = {
... lines 6 - 15
module: {
rules: [
... lines 18 - 27
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
]
},
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'resolve-url-loader',
'sass-loader?sourceMap',
]
},
... lines 44 - 65
]
},
... lines 68 - 78
};

Near the top of this file, create a variable: const styleLoader set to an object with loader set to style-loader and some empty options:

100 lines webpack.config.js
... lines 1 - 4
const styleLoader = {
loader: 'style-loader',
options: {}
};
... lines 9 - 100

Thanks to this, we can replace the style-loader strings below with this variable:

100 lines webpack.config.js
... lines 1 - 4
const styleLoader = {
loader: 'style-loader',
options: {}
};
... lines 9 - 24
module.exports = {
... lines 26 - 35
module: {
rules: [
... lines 38 - 47
{
test: /\.css$/,
use: [
styleLoader,
... line 52
]
},
{
test: /\.scss$/,
use: [
styleLoader,
... lines 59 - 61
]
},
... lines 64 - 85
]
},
... lines 88 - 98
};

We're now using the expanded format for the loader... but this is exactly the same as before.

Do this for the other loaders. I'll copy the first and paste three times. Add cssLoader, then sassLoader:

100 lines webpack.config.js
... lines 1 - 4
const styleLoader = {
loader: 'style-loader',
options: {}
};
const cssLoader = {
loader: 'css-loader',
options: {}
};
const sassLoader = {
loader: 'sass-loader',
options: {
sourceMap: true
}
};
... lines 19 - 100

In this case, add an option: sourceMap: true. We need that for the resolve-url-loader.

Finally, add resolveUrlLoader:

100 lines webpack.config.js
... lines 1 - 4
const styleLoader = {
loader: 'style-loader',
options: {}
};
const cssLoader = {
loader: 'css-loader',
options: {}
};
const sassLoader = {
loader: 'sass-loader',
options: {
sourceMap: true
}
};
const resolveUrlLoader = {
loader: 'resolve-url-loader',
options: {}
};
... lines 23 - 100

Cool! Use these below: use cssLoader in both places, then resolveUrlLoader and sassLoader:

100 lines webpack.config.js
... lines 1 - 24
module.exports = {
... lines 26 - 35
module: {
rules: [
... lines 38 - 47
{
test: /\.css$/,
use: [
styleLoader,
cssLoader,
]
},
{
test: /\.scss$/,
use: [
styleLoader,
cssLoader,
resolveUrlLoader,
sassLoader,
]
},
... lines 64 - 85
]
},
... lines 88 - 98
};

This didn't change anything, but we're setup for success!

JavaScript Sourcemaps

Back to sourcemaps! First, let's activate them for JavaScript. How? At the bottom of your config, add devtool set to inline-source-map:

101 lines webpack.config.js
... lines 1 - 24
module.exports = {
... lines 26 - 98
devtool: 'inline-source-map'
};

Yep, that's it. Actually, there are multiple ways to generate source maps. Each has pros and cons, and each seems to do funny things in at least some situations. But, inline-source-map is the most reliable I've found while developing.

Give it a try! Find your Webpack tab and restart:

./node_modules/.bin/webpack --watch

Then, refresh the page! Moment of truth: click one of the rows.. then find the console. Hey! Yes! The log comes from RepLogApp.js line 95. That sounds much better!

Go see if it's right: in Components/ open RepLogApp.js, and on line 95... it's perfect!

217 lines assets/js/Components/RepLogApp.js
... lines 1 - 9
class RepLogApp {
... lines 11 - 105
handleRowClick() {
console.log('row clicked!');
}
... lines 109 - 197
}
... lines 199 - 217

CSS Sourcemaps

What about CSS? Well, CSS is handled by our loaders. All we need to do is activate sourcemaps in each. Literally, that means we can add the same option to each one: sourceMap: true. Add that to resolveUrlLoader, cssLoader and styleLoader:

107 lines webpack.config.js
... lines 1 - 4
const styleLoader = {
... line 6
options: {
sourceMap: true
}
};
const cssLoader = {
... line 12
options: {
sourceMap: true
}
};
const sassLoader = {
... line 18
options: {
sourceMap: true
}
};
const resolveUrlLoader = {
... line 24
options: {
sourceMap: true
}
};
... lines 29 - 107

Ok! Restart Webpack! Then, refresh. Inspect the Leaderboard one more time. And... yeehaw! The color comes from main.scss on line 48. We just got the debugging band back together.

If you're curious what a sourcemap looks like, you can open your web/build/rep_log.js file and look at the bottom. Yep! See that big, ugly, unreadable comment? That's the sourcemap!

Obviously, we won't want that in our production files. That's something we'll fix later.

Leave a comment!