2

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.

Mridang Agarwalla
  • 43,201
  • 71
  • 221
  • 382
  • `grunt-browserify` doesn't create external src map file - see [here](https://github.com/jmreidy/grunt-browserify/issues/307). Try: **1)** Using [grunt-extract-sourcemap](https://www.npmjs.com/package/grunt-extract-sourcemap) to read inline src maps from `myapp.js` and create external src map file _(note: run this task after your current browserify task and before uglify task)_. **2)** Set [`sourceMapIn`](https://github.com/gruntjs/grunt-contrib-uglify/blob/33724cd1008a02f865921e0a2c8f748461c84d43/docs/uglify-options.md#sourcemapin) option in `uglify` task to use src map created at step 1. – RobC Jun 25 '19 at 15:54
  • @RobC When the script gets minified with the uglify step, will the resultant script file still have a correct mappings to the source map file? If I've understood correctly, the Unglify step only minifies the JS file but does not change the source-map. – Mridang Agarwalla Jul 01 '19 at 09:40

1 Answers1

1

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:

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

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

  1. 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, ...
    
  2. 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
    
  3. 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

RobC
  • 22,977
  • 20
  • 73
  • 80
  • This seems to be removing the sources from the map file. The intermediate file has it but the resultant doesn't. I've added `sourceMapIncludeSources: true` but it doesn't work. Any suggestions Rob? – Mridang Agarwalla Jul 03 '19 at 08:45
  • Apparently it has changed: https://github.com/gruntjs/grunt-contrib-uglify/blob/master/docs/uglify-options.md#sourcemapincludesources – Mridang Agarwalla Jul 03 '19 at 08:49
  • Interestingly I get `"sources": [ .... ]` listed in the resultant `build/myapp.min.js.map` file using config shown in my answer. What happens If you add `sourceMap: { includeSources: true }` to your `uglify` tasks `options` object? Failing that perhaps try utilizing earlier version of _grunt-contrib-uglify_. – RobC Jul 03 '19 at 09:12
  • No luck with sourceMap: { includeSources: true }. @RobC - Do you know which version of grunt-contrib-uglify you used? – Adam Youngers Apr 24 '21 at 16:54
  • Rolling back to ^2.0.0 of grunt-contrib-uglify did not work either. I get an error of Warning: Uglification failed. Unable to read "dist/index.js.map" file (Error code: ENOENT).. – Adam Youngers Apr 24 '21 at 17:01