Webpack: UglifyJsPlugin source map problems

Created on 3 Feb 2016  路  1Comment  路  Source: webpack/webpack

I'm trying to get webpack to compile TypeScript, then produce a bundle that has been run through UglifyJsPlugin and also generate a source map (.map file) that points back to the original .ts files, not the compiled .js resulting from the TypeScript transpile. However, the resulting source map doesn't look right, and more importantly doesn't work when debugging in Visual Studio 2015 (breakpoints in TypeScript code are not hit). First, I started by trying to get a version working without UglifyJsPlugin:

var path = require('path');

module.exports = {
    entry: './src/app.ts',
    output: {
        path: path.join(__dirname, 'output'),
        filename: 'bundle.js'
    },
    devtool: 'source-map',
    resolve: {
        extensions: ['', '.webpack.js', '.web.js', '.ts', '.js']
    },
    module: {
        loaders: [
          { test: /\.ts$/, loader: 'ts-loader' }
        ]
    },
}

The resulting source map (bundle.js.map) looks like this:

{"version":3,"sources":["webpack:///webpack/bootstrap 524a8d01410401832cd9","webpack:///./src/greeter.ts","webpack:///./src/app.ts" ....

The Visual Studio debugger chokes because it doesn't understand the webpack:// urls:

"Document webpack:///src/greeter.ts read failed: The URI prefix is not recognized"

To solve this problem, I came up with a workaround to rewrite the sources urls via devtoolModuleFilenameTemplate and devtoolFallbackModuleFilenameTemplate:

var path = require('path');

module.exports = {
    entry: './src/app.ts',
    output: {
        path: path.join(__dirname, 'output'),
        filename: 'bundle.js',
        devtoolModuleFilenameTemplate: function (info) {
            var relative = path.relative(__dirname, info.absoluteResourcePath);
            return path.join('/', relative).replace(/\\/g, "/");
        },
        devtoolFallbackModuleFilenameTemplate: function (info) {
            var relative = path.relative(__dirname, info.absoluteResourcePath);
            return path.join('/', relative + info.hash).replace(/\\/g, "/");
        }
    },
    devtool: 'source-map',
    resolve: {
        extensions: ['', '.webpack.js', '.web.js', '.ts', '.js']
    },
    module: {
        loaders: [
          { test: /\.ts$/, loader: 'ts-loader' }
        ]
    },
}

Now the source map looks like this:

{"version":3,"sources":["/webpack/bootstrap 524a8d01410401832cd9","/src/greeter.ts","/src/app.ts" ....

This works correctly. The Visual Studio debugger successfully loads the source map, and I can step through my typescript code. So far, so good. The final step is to add UglifyJsPlugin:

var path = require('path');
var webpack = require('webpack');

module.exports = {
    entry: './src/app.ts',
    output: {
        path: path.join(__dirname, 'output'),
        filename: 'bundle.js',
        devtoolModuleFilenameTemplate: function (info) {
            var relative = path.relative(__dirname, info.absoluteResourcePath);
            return path.join('/', relative).replace(/\\/g, "/");
        },
        devtoolFallbackModuleFilenameTemplate: function (info) {
            var relative = path.relative(__dirname, info.absoluteResourcePath);
            return path.join('/', relative + info.hash).replace(/\\/g, "/");
        }
    },
    devtool: 'source-map',
    resolve: {
        extensions: ['', '.webpack.js', '.web.js', '.ts', '.js']
    },
    plugins: [
        new webpack.optimize.UglifyJsPlugin(),
    ],
    module: {
        loaders: [
          { test: /\.ts$/, loader: 'ts-loader' }
        ]
    },
}

The resulting source map looks like this:

{"version":3,"sources":["/bundle.js","/webpack/bootstrap 524a8d01410401832cd9","/src/greeter.ts","/src/app.ts" ....

Notice that bundle.js is now included in the source map (i.e., bundle.js references bundle.js.map, which now references bundles.js!). The Visual Studio debugger is unable to set breakpoints or step through the typescript code. I believe this is because the source map is now pointing to the transpiled javascript results, not the original typescript code (actually, the source map references both, but I'm guessing that bundle.js takes precedence since it's first in the array).

How can I prevent UglifyJsPlugin from adding bundle.js to resulting source map? I did try adding an 'exclude' option for UglifyJsPlugin:

    new webpack.optimize.UglifyJsPlugin({ exclude: 'bundle.js' } ),

This produces output that is identical to removing the plugin (i.e., source map is correct, but bundle.js is not minified).

If I'm reading the UglifyJS2 documentation correctly, my use case should be supported by using the --in-source-map option. However, I'm not sure how that works in the context of webpack since it's internal. Is this supposed to work with webpack out of the box, or am I missing config options somewhere?

Nice To Have Broken work required (PR / Help Wanted) enhancement

Most helpful comment

UglifyJsPlugin({
  sourceMap: true
})

>All comments

UglifyJsPlugin({
  sourceMap: true
})
Was this page helpful?
0 / 5 - 0 ratings

Related issues

olalonde picture olalonde  路  3Comments

haohcraft picture haohcraft  路  3Comments

adjavaherian picture adjavaherian  路  3Comments

Tjorriemorrie picture Tjorriemorrie  路  3Comments

sairion picture sairion  路  3Comments