Sourcemaps & Debugging
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeYou 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:
// ... 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!
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:
// ... 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:
// ... 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:
// ... 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
:
// ... 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
:
// ... 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
:
// ... 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
:
// ... 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!
// ... 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
:
// ... 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 |
Tip
If you're using style-loader
1.0.0 or newer, do not include the sourceMap
option:
the loader automatically detects if it should render the source maps.
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.
Hello!
when options is set to sourceMap:true caused webpack error,
according to https://github.com/webpack-contrib/style-loader#source-maps
sourceMap:true should be in previous cssLoader only.
Thanks for great tutorial!