Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

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.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

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:

... 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:

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!

... 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

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.

Leave a comment!

10
Login or Register to join the conversation
Avraham M. Avatar
Avraham M. Avatar Avraham M. | posted 3 years ago | edited

Hello!


const styleLoader = {
    loader: 'style-loader',
    options: {
        sourceMap: true
    }
};

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!

Reply

Hey Avraham,

Thank you for this tip! We're going to add a note about it

Cheers!

Reply
Max A. Avatar

Do not work properly.

"webpack": "^4.29.4",
"webpack-cli": "^3.2.3"

On the screenshot the line is not the same.
https://drive.google.com/fi...

Reply

Hey Max A.

I do see you got logged the right message row clicked!. What's not working properly?

Reply
Max A. Avatar
Max A. Avatar Max A. | MolloKhan | posted 4 years ago | edited

MolloKhan
The line is not the same and it is looking differently.

https://drive.google.com/fi...

Doesn't matter. I will just move on.

Reply

Oh, yes, that. That's because Babel (or Webpack?) transpile you JS code. Can you notice that _this3 at line 113? that's because of the transpiling process :)

Cheers!

1 Reply
GDIBass Avatar
GDIBass Avatar GDIBass | posted 5 years ago

I cannot for the life of me get this to work on Firefox (regular or dev) any one have tips? I'll switch to Chrome for now but I'd like to get it figured out.

Reply

Hey GDIBass

Looks like it's an issue of Webpack V3, you may want to try any of the solutions posted in this thread https://github.com/webpack/...

Cheers!

1 Reply
GDIBass Avatar

I'm just switching to webpack encore. Much easier :P

Reply

Indeed it is! We all can thank you Ryan because he's one of the main developers of that feature :D

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial explains the concepts of an old version of Webpack using an old version of Symfony. The most important concepts are still the same, but you should expect significant differences in new versions.

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.2.0",
        "symfony/symfony": "3.3.*", // v3.3.16
        "twig/twig": "2.10.*", // v2.10.0
        "doctrine/orm": "^2.5", // v2.7.0
        "doctrine/doctrine-bundle": "^1.6", // 1.10.3
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.5
        "symfony/swiftmailer-bundle": "^2.3", // v2.6.3
        "symfony/monolog-bundle": "^2.8", // v2.12.1
        "symfony/polyfill-apcu": "^1.0", // v1.4.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.26
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "friendsofsymfony/user-bundle": "^2.0", // v2.1.2
        "doctrine/doctrine-fixtures-bundle": "~2.3", // v2.4.1
        "doctrine/doctrine-migrations-bundle": "^1.2", // v1.3.2
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "friendsofsymfony/jsrouting-bundle": "^1.6" // 1.6.0
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.1.6
        "symfony/phpunit-bridge": "^3.0" // v3.3.5
    }
}

What JavaScript libraries does this tutorial use?

// package.json
{
    "dependencies": [],
    "devDependencies": {
        "babel-core": "^6.25.0", // 6.25.0
        "babel-loader": "^7.1.1", // 7.1.1
        "babel-plugin-syntax-dynamic-import": "^6.18.0", // 6.18.0
        "babel-preset-env": "^1.6.0", // 1.6.0
        "bootstrap-sass": "^3.3.7", // 3.3.7
        "clean-webpack-plugin": "^0.1.16", // 0.1.16
        "copy-webpack-plugin": "^4.0.1", // 4.0.1
        "core-js": "^2.4.1", // 2.4.1
        "css-loader": "^0.28.4", // 0.28.4
        "extract-text-webpack-plugin": "^3.0.0", // 3.0.0
        "file-loader": "^0.11.2", // 0.11.2
        "font-awesome": "^4.7.0", // 4.7.0
        "jquery": "^3.2.1", // 3.2.1
        "lodash": "^4.17.4", // 4.17.4
        "node-sass": "^4.5.3", // 4.5.3
        "resolve-url-loader": "^2.1.0", // 2.1.0
        "sass-loader": "^6.0.6", // 6.0.6
        "style-loader": "^0.18.2", // 0.18.2
        "sweetalert2": "^6.6.6", // 6.6.6
        "webpack": "^3.4.1", // 3.4.1
        "webpack-chunk-hash": "^0.4.0", // 0.4.0
        "webpack-dev-server": "^2.6.1", // 2.6.1
        "webpack-manifest-plugin": "^1.2.1" // 1.2.1
    }
}
userVoice