How to configure sourceMaps for LESS using Grunt?
I found the LESS site documentation to be more clear regarding params used by grunt-contrib-less.
LESS: Command Line Usage
http://lesscss.org/usage/#command-line-usage-installing-lessc
NPM: grunt-contrib-less
https://www.npmjs.org/package/grunt-contrib-less
File structure:
laravel/gruntfile.js
laravel/public/less/main.less
laravel/public/css/main.css
laravel/public/css/main.css.map
File 'main.css.map' note:
- If you wish, you can rename to: main.css.source-map.json
- I guess you have some server rule setup that doesn't server *.map files properly from the 'css' folder
Compression notes:
- cleancss: true = will remove sourceMappingURL comment from main.css
- yuicompress: true = will NOT remove sourceMappingURL comment from main.css
Gruntfile.js
less: {
dev: {
options: {
compress: true,
yuicompress: true,
optimization: 2,
sourceMap: true,
sourceMapFilename: 'public/css/main.css.map', // where file is generated and located
sourceMapURL: '/css/main.css.map', // the complete url and filename put in the compiled css file
sourceMapBasepath: 'public', // Sets sourcemap base path, defaults to current working directory.
sourceMapRootpath: '/', // adds this path onto the sourcemap filename and less file paths
},
files: {
'public/css/main.css': 'public/less/main.less',
}
}
},
watch: {
styles: {
files: ["public/less/*"],
tasks: ['less'],
options: {
livereload: true,
nospaces: true
}
}
},
laravel/public/css/main.css
.class{ compiled css here } /*# sourceMappingURL=/css/main.css.map */
laravel/public/css/main.css.map
{"version":3,"sources":["/less/main.less"], etc...
How do I generate sourcemaps for Uglified files using Grunt?
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 subsequentgrunt-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 ofbuild/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 frombuild/myapp.js
and writes it to a new file namedmyapp.js.map
which is saved in yourbuild
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 ofbuild/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 itssourceMapIn
option is configured to readbuild/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 filebuild/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
How to set CSS Source Map output destination when grunt-contrib-less is used
The sourceMapFilename
option may include a path part as well.
I.e. just change it to:
sourceMapFilename: "qa1/avinash/html5/phase1/css/style.css.map"
Output multiple LESS source maps with Grunt?
The crummy brute-force method is to simply define a second build process and make sure that your build processes call both less.development1
and less.development2
:
less: {
"development1": {
options: {
compress: true,
yuicompress: true,
optimization: 2,
sourceMap: true,
sourceMapFilename: "<%= yeoman.app %>/live_preview/b/css/theme.css.map"
},
files: {
// target.css file: source.less file
"<%= yeoman.app %>/live_preview/b/css/theme.css": "<%= yeoman.app %>/less/theme.less"
}
},
"development2": {
options: {
compress: true,
yuicompress: true,
optimization: 2,
sourceMap: true,
sourceMapFilename: "<%= yeoman.app %>/live_preview/b/css/main.css.map"
},
files: {
// target.css file: source.less file
"<%= yeoman.app %>/live_preview/b/css/main.css": "<%= yeoman.app %>/less/main.less"
}
}
}
How do I create a sourcemap for each individual js file with grunt-contrib-concat and grunt-contrib-uglify?
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
In your
concat
task you need to firstly set thesourceMap
option totrue
. 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 theangular
target of theconcat
task - with the additional.map
suffix appended.Then in your
uglify
task add thesourceMapIn
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
Same as before, set the
sourceMap
option totrue
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'
Again, in your
uglify
task add thesourceMapIn
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']
}
}
},
Related Topics
Why Won't Opera (11.00) Display Custom (@Font-Face) Fonts
How to Use Nth-Child in CSS to Select All Elements After The 3Rd One
Scss/Sass to CSS in Special Folder with PHPstorm 7 File Watcher
How to Inspect CSS Pseudo Classes with Firebug
How to Add a Scroll Bar to a Component in React
How to Create 3X3 Grid Menu in React Native Without 3Rd Party Lib
Browser-Specific Prefixes with a CSS Transition on Transform
CSS Sprites with Dynamic Sizing
Have Border-Radius Cover Inner Divs
How to Make a Small Circle's Border Smooth
Relation Between Dp - Sp and Px
One CSS Declaration for All CSS Font Properties