问题
I want to have a gruntfile with 2 tasks: less (compiles all less files) and watch (listens to changes and re-compiles the changed file).
I have the following Gruntfile.js:
module.exports = function(grunt) {
var files = [
{
expand: true,
cwd: 'media/less',
src: ['*.less'],
dest: 'media/css/',
ext: '.css'
},
{
expand: true,
cwd: 'media/less/vendor',
src: ['*.less'],
dest: 'media/css/vendor/',
ext: '.css'
},
{
expand: true,
cwd: 'media/admin/less',
src: ['*.less'],
dest: 'media/admin/css/',
ext: '.css'
}
];
grunt.initConfig({
less: {
development: {
options: {
compress: false,
yuicompress: true,
optimization: 2
},
files: files
},
production: {
options: {
compress: true,
yuicompress: true,
optimization: 2
},
files: files
}
},
watch: {
styles: {
files: ['media/**/*.less'],
tasks: ['less:development'],
options: {
nospawn: true
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', ['less:development']);
};
The less
task runs correctly without any problems. The watch
task however listens to changes, but re-compiles all files when one is changed. I suspect it's something to do with how I set up my less
task, because I want my less file list to be dynamic and not to add each file manually.
As per this answer grunt should already support this, but I'm unsure how.
回答1:
Ended up using the watch
event and overriding the files
property of the less
task. Here's my final code:
module.exports = function(grunt) {
var files = [
{
expand: true,
cwd: 'media/less',
src: ['*.less'],
dest: 'media/css/',
ext: '.css',
extDot: 'last'
},
{
expand: true,
cwd: 'media/less/vendor',
src: ['*.less'],
dest: 'media/css/vendor/',
ext: '.css',
extDot: 'last'
},
{
expand: true,
cwd: 'media/admin/less',
src: ['*.less'],
dest: 'media/admin/css/',
ext: '.css',
extDot: 'last'
}
];
grunt.initConfig({
less: {
development: {
options: {
compress: false,
yuicompress: true,
optimization: 2
},
files: files
},
production: {
options: {
compress: true,
yuicompress: true,
optimization: 2
},
files: files
}
},
watch: {
styles: {
files: ['media/**/*.less'],
tasks: ['less:development'],
options: {
nospawn: true
}
}
}
});
grunt.event.on('watch', function(action, filepath){
// ignore include files, TODO: have naming convention
// if an include file has been changed, all files will be re-compiled
if(filepath.indexOf('.inc.') > -1)
return true;
// might not be the most efficient way to do this
var srcDir = filepath.split('/');
var filename = srcDir[srcDir.length - 1];
delete srcDir[srcDir.length - 1];
srcDir = srcDir.join('/');
var destDir = srcDir.replace(/less/g, 'css');
grunt.config('less.development.files', [{
src: filename,
dest: destDir,
expand: true,
cwd: srcDir,
ext: '.css',
extDot: 'last'
}]);
});
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', ['less:development']);
};
回答2:
I'm unsure what you mean with the current title of your question Grunt watch less on changed file only. Do you mean that is a problem? That's the expected behavior of a watch
task, it will watch the files specified for changes and run the tasks you specified - in this case the LESS
compilation.
I did some changes to your file. Some of it was simplified, some was changed keeping flexibility and expandability of the script in mind.
First install underscore
as a dependency, by running:
npm install underscore --save-dev
Then do the following changes to your Gruntfile.js
:
module.exports = function(grunt) {
var _ = require('underscore');
var files = {
app : {
'<%= path.styles.css %>/styles.css' : '<%= path.styles.less %>/*.less'
},
vendor : {
'<%= path.styles.css %>/styles-vendor.css' : '<%= path.styles.vendor %>/*.less'
},
admin : {
'<%= path.styles.css %>/styles-admin.css' : '<%= path.styles.admin %>/*.less'
}
}
function all() {
'use strict';
var allfiles = {},
i = {};
for (i in files) {
_.extend(allfiles, files[i]);
}
return allfiles;
}
grunt.initConfig({
path : {
media : 'media',
styles : {
css: 'media/css',
less: 'media/less',
admin: 'media/admin/less',
vendor: '<%= path.styles.less %>/vendor'
}
},
less: {
development: {
options: {
compress: false,
yuicompress: true,
optimization: 2
},
files: (all())
},
production: {
options: {
compress: true,
yuicompress: true,
optimization: 2
},
files: (all())
}
},
watch: {
styles: {
files: ['<%= path.media %>/**/*.less'],
tasks: ['less:development'],
options: {
nospawn: true
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-watch');
// run several tasks as default (handy for complex projects)
grunt.registerTask('dist', [ // run with 'grunt dist'
'less:production'
]);
grunt.registerTask('dev', [ // default, will run with 'grunt' only
'less:development'
]);
grunt.registerTask('default', 'dev');
};
If what you want is to actually compile the sets of files separately (files.app
, files.vendor
& files.admin
), you might need to split the task some more, like so:
less: {
app: {
options: {
compress: false,
yuicompress: true,
optimization: 2
},
files: files.app
},
vendor: {
options: {
compress: true,
yuicompress: true,
optimization: 2
},
files: files.vendor
},
admin: {
options: {
compress: true,
yuicompress: true,
optimization: 2
},
files: files.admin
},
development: {
options: {
compress: false,
yuicompress: true,
optimization: 2
},
files: (all())
},
production: {
options: {
compress: true,
yuicompress: true,
optimization: 2
},
files: (all())
}
},
watch: {
all: {
files: ['<%= path.media %>/**/*.less'],
tasks: ['less:development'],
options: {
nospawn: true
}
},
app : {
files: ['<%= path.styles.less %>/*.less'],
tasks: ['less:app'],
options: {
nospawn: true
}
},
vendor : {
files: ['<%= path.styles.vendor %>/*.less'],
tasks: ['less:vendor'],
options: {
nospawn: true
}
},
admin : {
files: ['<%= path.styles.admin %>/*.less'],
tasks: ['less:admin'],
options: {
nospawn: true
}
}
}
Then you could then run either of these:
grunt watch:app
grunt watch:vendor
grunt watch:admin
You can always to choose to run the tasks directly, once:
grunt less:app
grunt less:vendor
grunt less:admin
Hope this helps! Please note that I haven't tested this.
来源:https://stackoverflow.com/questions/22785240/grunt-watch-less-on-changed-file-only