Debugging with webpack, ES6 and Babel

34,590

Solution 1

This is an issue with Javascript source maps, which don't currently support mapping symbol names, and babel, which changes the names of import-ed modules when compiling to CommonJS from ES2105 module syntax.

Babel does this to fully support the fact that ES2015 modules export bindings by resolving all references to imports whenever they are used in code, instead of at import time.

If you aren't writing modules that depend on exporting bindings (as is likely, since you couldn't actually do this with CommonJS), then you may prefer to preserve variable names when transpiling ES2015. I created an alternative to the native babel commonjs module transform for Babel 6 that preserves variable names: babel-plugin-transform-es2015-modules-commonjs-simple. This is a drop-in replacement for babel-plugin-transform-es2015-modules-commonjs, the native babel transform.

You can use this with webpack or node. A typical config might be:

npm install --save-dev babel-preset-es2015-webpack
npm install --save-dev babel-plugin-transform-es2015-modules-commonjs-simple

The module babel-preset-es2015-webpack is a fork of the standard es2015 preset that does not include the module transform, because you want to use the alternate version. This works for node also. These modules are used in .babelrc:

{
    "presets": [
        "es2015-webpack"
    ],
    "plugins": [
        "transform-runtime",
        ["transform-es2015-modules-commonjs-simple", {
            "noMangle": true
        }]
    ]
}

transform-runtime is usually a good idea to include in any substantive project to avoid extra repetition of generated code. Typical module config in webpack.config.js:

module: {
    loaders: [
        {
            loader: "babel-loader",
            include: [path.resolve(__dirname, "src")]                
        }
    ]
},
devtool: '#inline-source-map'

The resulting code won't change the names of imports, so debugging with source maps will provide you with access to symbols.

Solution 2

You'll need to use the compiled variable names, not the originals. Source maps only allow the browser to display the source code that corresponds to the compiled code; they can't make the browser resolve original variable names from the compiled code.

To see the compiled variable names, either switch to the compiled source, or look in the Scope Variables pane on the right, which will show you (like it says on the tin) what variables exist in the current scope.

IIRC Babel tends to prefix module names with _, so your BaseModel variable is probably called _baseModel or similar.

Solution 3

I had some good success by adding the statement

debugger;

in your javascript/typescript files even in framework files of angular or vue2 like *.vue

So even if the file gets converted or changed or renamed or your path mappings to URL don't work, the debugger will step anyway.

Share:
34,590
ZenMaster
Author by

ZenMaster

Education: CS Occupation: Software Architect Passion: front-end development and architecture

Updated on July 08, 2022

Comments

  • ZenMaster
    ZenMaster almost 2 years

    This seems like something that should have been relatively simple to achieve, but alas.

    I have ES6 class:

    'use strict';
    
    export class BaseModel {  
        constructor(options) {
            console.log(options);
        }
    };
    

    and root module that uses it:

    'use strict';
    
    import {BaseModel} from './base/model.js';
    
    export let init = function init() {
        console.log('In Bundle');
        new BaseModel({a: 30});    
    };
    

    My target is:

    1. pass the above through Babel, to get ES5 code
    2. pack the modules with webpack
    3. be able to debug the result

    After some trial, this is the webpack config that I have:

    {
        entry: {
            app: PATH.resolve(__dirname, 'src/bundle.js'),
        },
        output: {
            path: PATH.resolve(__dirname, 'public/js'),
            filename: 'bundle.js'
        },        
        devtool: 'inline-source-map',
        module: {
            loaders: [
                {
                    test: /\.js$/,
                    exclude: /(node_modules|bower_components)/,
                    loader: 'babel'
                }
            ]        
        }
    }
    

    This seems to be working, to an extent.

    So, I can do that:

    devtools breakpoint screenshot

    I can click F11 and enter the code, but I can't evaluate BaseModel:

    erro in console evaluation

    which kinda defeats the purpose (or one of purposes) of debugging.

    I've tried adding source-map-loader in various order with babel-loader:

    {
        test: /\.js$/,
        loader: "source-map-loader"
    }
    

    to no avail.

    Side note 1: if I abandon webpack and just compile my modules with source maps via Babel into, say, System.js:

    babel src/ --out-dir public/js/ --debug --source-maps inline --modules system
    
    • all works properly.

    enter image description here

    Side note 2: this ?sourceMaps=true doesn't seem to do anything at all, since, if I remove source map configuration from webpack - no source maps are preserved/generated at all. One would expect the initial, Babel-produced, source maps to be preserved in the resulting files. Nope.

    Any suggestions would be greatly appreciated

  • ZenMaster
    ZenMaster over 8 years
    I get the issue. Doesn't mean it is un-resolvable. In the end - source map is just that, a map. Theoretically, webpack can link back to the original source map that Babel produced. They even claim, AFAIU, that with source-map-loader, where it seems to be possible.
  • ZenMaster
    ZenMaster over 8 years
    Also, it's not in a scope variables, rather in closure.
  • Jordan Running
    Jordan Running over 8 years
    Webpack is handling the Babel source maps correctly—if it wasn't, the original source wouldn't be shown in Chrome. But source maps, regardless of how they're produced, don't affect the behavior of the JavaScript console. The JavaScript console (the REPL, which is distinct from the Source pane) will always reflect the variable names, etc. of the compiled source—i.e. the source actually executed by the browser—not the original source.
  • ZenMaster
    ZenMaster over 8 years
    I must be misunderstanding what you are saying. When I use System.js + babel and transpile files with source map - I can absoutely see both the source code and the dev. tools refer to original property names and such. See edit for Side note 1 for a screnshot and command that was used. (of course the console prints out a "real" toString, but that wasn't the issue.
  • Jordan Running
    Jordan Running over 8 years
    Ah. I see. I somehow forgot the fact that Webpack rewrites requires as part of its compilation. It's Webpack that's changing" those variable names, not Babel. (cont.)
  • Jordan Running
    Jordan Running over 8 years
    The same truth remains, however: The only variable names that are available in the console are the variable names in the source that's executed by the browser. Variable names from the original source will never be available in the console unless the same names are in the compiled source. The reason the variable names are the same with System.js is that the variable names happen to be the same in the compiled source—not because of anything to do with source maps.
  • Jordan Running
    Jordan Running over 8 years
    If this is a deal-breaker for you then you should file an issue with the Webpack project. Personally, though, I've never found it to be a problem: it doesn't take any effort to figure out what the compiled variable name is in the console, so I just do that and go about my work.
  • ZenMaster
    ZenMaster over 8 years
    Aha. Now I see. Still, webpack claim that they allow source map propagation, so there is that.
  • Jordan Running
    Jordan Running over 8 years
    Source maps, no matter how they're produced or configured, will never, ever make a variable name in the original source available in the console unless the same variable name is also in the compiled source. In the screenshots you've shown source maps are working correctly and to their fullest extent. Only variable names, etc. from the source actually executed by the browser, i.e. the compiled source, will be available in the console.
  • Jordan Running
    Jordan Running over 8 years
    Source maps inform the browser what line/column in the original source a given line/column in the compiled source corresponds to, so the browser can show you where in the original source an error or operation originates. They do nothing else. They do not, and cannot, tell the browser what the original variable names, etc. were.
  • ZenMaster
    ZenMaster over 8 years
    I know that, my whole point was that the initial ones worked and, as you correctly pointed out, it was because they didn't mangle the names
  • Mark Pieszak - Trilon.io
    Mark Pieszak - Trilon.io about 8 years
    Very nice! Going to have to try this.. debugging webpack bundled & transpiled code seems to be a bit of a pain =( Definitely makes me miss the old gulp / es5 JS route
  • Jamie Treworgy
    Jamie Treworgy about 8 years
    Thanks.. has been very helpful for us and particularly in getting devs to buy into a fully transpiled workflow. It would be easy for Babel to support this natively (since my code is just minor changes to the native module), but the babel team chose the route of purity/compleness versus practicality. Personally, I couldn't live without a good debugging experience, but I can live without writing modules with dynamic bindings. I hope that the spec for symbols in source maps emerges quickly making all this unnecessary but the process seems to be crawling.
  • ZenMaster
    ZenMaster about 8 years
    @JamieTreworgy You, my friend, are a hero. I've started poking into this lately, as it had begun to bug me. Thank you.
  • Jamie Treworgy
    Jamie Treworgy about 8 years
    Everyone interested in this issue keep an eye on the chromium bug bugs.chromium.org/p/chromium/issues/detail?id=327092 - there is indication that something's happening with the source map spec!
  • ZenMaster
    ZenMaster about 8 years
    @JamieTreworgy An FYI... currently transform-es2015-modules-commonjs-simple breaks tree-shaking in Webpack 2, due to making babel output CommonJS... See here 2ality.com/2015/12/webpack-tree-shaking.html for more.
  • Jamie Treworgy
    Jamie Treworgy about 8 years
    Interesting - so when using tree-shaking Webpack is handling ES6 module transformation after the babel compilation step, so no, this wouldn't work. I don't know what process Webpack ultimately uses to transform the ES6 module syntax, is it perhaps just pushing it through transform-es2015-modules-commonjs after tree shaking? I can't believe they would reinvent that. In any event I bet it's solvable, but since symbol mapping already has experimental support in Canary (as of a few days ago) I'm not sure it's worth figuring it out.
  • Rowan
    Rowan almost 4 years
    Ran into this issue recently. I can't believe it's 2020 and this is still an issue. How are you supposed to take JavaScript seriously when you can't even debug it correctly.