Search code examples
gruntjssource-mapsgrunt-contrib-uglifygrunt-contrib-concat

How do I create a sourcemap for each individual js file with grunt-contrib-concat and grunt-contrib-uglify?


Currently, the concat and uglify settings are:

concat: {
  options: {
    // options
  },
  angular: {
    src: ['public/**/*.js', '!public/**/*.min.js', '!public/lib/**/*.js'],
    dest: 'build/_main.js',
  }
},

uglify: {
  options: {
    mangle: false,
    sourceMap: true,
  },
  app: {
    files: {
      'public/js/app.min.js': ['build/_main.js']
    }
  },
  bower: {
    files: {
      'public/js/lib.min.js': ['build/_bower.js']
    }
  }
},

This creates one source map in the dev tools under Sources. This source map is usable but I would way rather be able to search file by file and navigate through every file in the dev tools. It would be nice if the entire project directory and file structure was preserved so I could search and navigate through each file and add debuggers, etc.

How do I do this?


Solution

  • This can be achieved by configuring your tasks as follows:

    Note: Both solutions below will require the concat task to be run before the uglify task.


    1. Using default generated names for source maps:

    concat: {
      options: {
        sourceMap: true // <-- 1. Enable source maps
      },
      angular: {
        src: ['public/**/*.js', '!public/**/*.min.js', '!public/lib/**/*.js'],
        dest: 'build/_tsbc.js',
      }
    },
    
    uglify: {
      options: {
        mangle: false,
        sourceMap: true,
        sourceMapIn: './build/_tsbc.js.map' // <-- 2. Define .map file to use.
      },
      app: {
        files: {
          'public/js/app.min.js': ['build/_tsbc.js']
        }
      },
      // ...
    } 
    

    Explanation

    1. In your concat task you need to firstly set the sourceMap option to true. Given your current configuration this will create a source map file at the following path:

      build/_tsbc.js.map

      Note: the location of the generated map file defaults to the same dest path as defined in the angular target of the concat task - with the additional .map suffix appended.

    2. Then in your uglify task add the sourceMapIn option and set its value (String) to the path of the .map file generated at stage one above. I.e.

      sourceMapIn: './build/_tsbc.js.map'
      

    2. Explicitly defined names for source maps:

    It's also possible to define the specific name and path for the source map file generated by the concat task. For example:

    concat: {
      options: {
        sourceMap: true,  // <-- 1. Enable source maps
        sourceMapName: 'build/my_map.map'  // <-- 2. Specify output path
      },
      angular: {
        src: ['public/**/*.js', '!public/**/*.min.js', '!public/lib/**/*.js'],
        dest: 'build/_tsbc.js',
      }
    },
    
    uglify: {
      options: {
        mangle: false,
        sourceMap: true,
        sourceMapIn: './build/my_map.map'  // <-- 3. Define .map file to use.
      },
      app: {
        files: {
          'public/js/app.min.js': ['build/_tsbc.js']
        }
      },
      // ...
    } 
    

    Explanation

    1. Same as before, set the sourceMap option to true

    2. This time, add the sourceMapName option and specify the path of the generated source map.

      Note: the location of the generated map file is now set to:

      'build/my_map.map'

    3. Again, in your uglify task add the sourceMapIn option and set its value to the same path specified in the previous point. I.e.

      sourceMapIn: './build/my_map.map'
      

    EDIT

    I've just noticed that your bower target in the uglify task does not depend on output from the previous concat task. For this scenario you'll need to nest the options object in each target. For example:

    uglify: {
      app: {
        options: {  // <-- Specific options for `app` target 
          mangle: false,
          sourceMap: true,
          sourceMapIn: './build/my_map.map'  // <-- 3. Define .map file to use.
        },
        files: {
          'public/js/app.min.js': ['build/_tsbc.js']
        }
      },
      bower: {
        options: {  // <-- Specific options for `bower` target 
          mangle: false,
          sourceMap: true
          // <-- There's no `sourceMapIn` needed here.
        },
        files: {
          'public/js/lib.min.js': ['build/_bower.js']
        }
      }
    },