I have a Grunt project that uses both Browserify and Uglify. Here are the core bits of it:
browserify: {
myapp: {
options: {
transform: ['babelify'],
browserifyOptions: {
debug: true
},
},
src: 'src/index.js',
dest: 'build/myapp.js'
}
},
uglify: {
options: {
sourceMap: true,
banner: bannerContent
},
target: {
src: 'build/myapp.js',
dest: 'build/myapp.min.js'
}
},
It seems to generate a myapp.min.js.map
file but it no longer has the raw sources in the source-map that existed prior to the Browserification.
Here's what the resultant source-map file contains:
{
"version":3,
"sources":[
"myapp.js"
],
"names":[
...
...
...
],
"mappings":".........",
"file":"myapp.min.js"
}
I've tried using the uglifyify
transform for Browserify but that does not seem to generate as small files as the Uglify task.
I've also bumped all my dependencies to the latest but I haven't been able to resolve this issue.
grunt-contrib-uglify
has a sourceMapIn
option that allows you to specify the location of an input source map file from an earlier compilation - which in your scenario is the browserify
task.
However, whilst setting browserifyOptions: { debug: true }
in your browserify
task does generate an inline source map in the resultant .js
file (i.e. in build/myapp.js
), the crux of the problem is twofold:
We don't have an external source map file that we can configure the sourceMapIn
option of the subsequent grunt-contrib-uglify
task to utilize.
grunt-browserify
doesn't provide a feature to create an external .map
file, it only creates them inline (see here)
To address the aforementioned issue consider utilizing grunt-extract-sourcemap to extract the inline source map from build/myapp.js
(i.e. from the output file generated by your browserify
task) after it has been produced.
Gruntfile.js
The following gist shows how your Gruntfile.js should be configured:
module.exports = function (grunt) {
grunt.initConfig({
browserify: {
myapp: {
options: {
transform: ['babelify'],
browserifyOptions: {
debug: true
},
},
src: 'src/index.js',
dest: 'build/myapp.js'
}
},
extract_sourcemap: {
myapp: {
files: {
'build': ['build/myapp.js']
}
}
},
uglify: {
options: {
sourceMap: true,
sourceMapIn: 'build/myapp.js.map'
},
target: {
src: 'build/myapp.js',
dest: 'build/myapp.min.js'
}
}
});
grunt.loadNpmTasks('grunt-browserify');
grunt.loadNpmTasks('grunt-extract-sourcemap');
grunt.loadNpmTasks('grunt-contrib-uglify');
// Note the order of the tasks in your task list is important.
grunt.registerTask('default', ['browserify', 'extract_sourcemap', 'uglify']);
};
Explanation
First the browserify
task is invoked which outputs a new file (i.e. build/myapp.js
) containing your bundled JavaScript and an "inlined" source map info. If you were to inspect the content of build/myapp.js
at this stage it includes something like the following at the end:
//# sourceMappingURL=data:application/json;charset=utf-8;base64, ...
Next the extract_sourcemap
task is invoked. This essentially extracts the "inlined" source map info from build/myapp.js
and writes it to a new file named myapp.js.map
which is saved in your build
directory.
The original "inlined" source map info in build/myapp.js
is replaced with a link to the newly generated source map file, i.e. myapp.js.map
. If you inspect the content of build/myapp.js
you'll now notice the following at the end of the file instead:
//# sourceMappingURL=myapp.js.map
Lastly the uglify
task is invoked. Notice how its sourceMapIn
option is configured to read build/myapp.js.map
, i.e the source map file we generated at step 2.
This task creates your desired build/myapp.min.js
file containing; your minified JS, and a link to a newly generated source map file build/myapp.min.js.map
.
Note The final resultant file (i.e. build/myapp.min.js
) now correctly maps back to the original src/index.js
file and any file(s) that index.js
itself may have import
'ed or require()
'd