grunt-autoprefixer loops endlessly when using grunt-contrib-watch - gruntjs

I'm just learning Grunt. I'm going to use compass for vertical rhythm and image sprite generation and autoprefixer for prefixing css3 styles.
Here's my Gruntfile.js.
module.exports = function(grunt) {
var globalConfig = {
sassDir: 'sass',
cssDir: 'css'
};
require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
// Project configuration.
grunt.initConfig({
globalConfig: globalConfig,
pkg: grunt.file.readJSON('package.json'),
compass: {
dev: {
options: {
sassDir: '<%= globalConfig.sassDir %>',
cssDir: '<%= globalConfig.cssDir %>'
}
}
},
autoprefixer: {
dev: {
options: {
browsers: ['last 2 versions']
},
src: '<%= globalConfig.cssDir %>/style.css',
dest: '<%= globalConfig.cssDir %>/style.css'
}
},
watch: {
compass: {
files: ['<%= globalConfig.sassDir %>/**/*.scss'],
tasks: ['compass:dev'],
},
autoprefixer: {
files: ['<%= globalConfig.cssDir %>/style.css'],
tasks: ['autoprefixer:dev']
},
livereload: {
options: { livereload: true },
files: ['<%= globalConfig.cssDir %>/style.css']
}
}
});
// Default task(s) that will be run by invoking 'grunt' w/o args
grunt.registerTask('styles:dev', ['compass', 'autoprefixer']);
grunt.registerTask('default', ['styles:dev', 'watch']);
};
But whenever i run
grunt
Everything works as expected except that grunt-contrib-watch calls grunt-autoprefixer endlessly.
I'm just beginning to learn Grunt. It's probably a wrong configuration on my Gruntfile.js
I hope you could help me out here.

Change your task configuration to:
watch: {
compass: {
files: ['<%= globalConfig.sassDir %>/**/*.scss'],
tasks: ['compass:dev', 'autoprefixer:dev']
},
livereload: {
options: { livereload: true },
files: ['<%= globalConfig.cssDir %>/style.css']
}
}
Basically, grunt-contrib-watch will run tasks whenever a file is updated, and because your source and destination files are the same it turns it into an infinite loop. Simply run the autoprefixing once your compass task has built your css. Hope this helps. :-)

Related

Using grunt-postcss - how do we run autoprefixer without the other plugins?

I have a Gruntfile.js set up with Post CSS running autoprefixer, mqpacker, and cssnano at the moment.
I have a default grunt build that will work to build everything for deployment. It includes the call to postcss, like so:
grunt.registerTask('default', ['sass', 'postcss', 'concat', 'uglify', 'browserSync', 'watch']);
The issue is I also want the default grunt build process to be for everyday dev work - one that omits (turns off) the media query compilation and minification from Post CSS.
However, I want to leave autoprefixer on. This wasn't a problem with grunt when autoprefixer ran as a separate program, we just make a new grunt.registerTask
Now that autoprefixer now runs inside of Post CSS, how do we make these different build processes work, without commenting out the individual plugins in CSS (clumsy) and restarting grunt every time we want to do a different build?
My Gruntfile:
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
// Sass to CSS
sass: {
app: {
files: [{
expand: true,
cwd: 'scss',
src: ['**/*.scss'],
dest: 'css/src',
ext: '.css'
}]
},
options: {
sourceMap: true,
outputStyle: 'expanded',
imagePath: "../"
}
},
// Post CSS (autoprefixer, mqpacker, cssnano)
postcss: {
// Configuration
options: {
map: true,
// Load plugins
processors: [
// Runs Post CSS Autoprefixer
require('autoprefixer')({browsers: ['last 2 versions']}),
// require('postcss-import')(),
require('css-mqpacker')(),
require('cssnano')({
autoprefixer: false,
safe: true,
sourcemap: false
})
]
},
// Operate on compiled Sass, write global.css
dist: {
src: 'css/src/global-sass.css',
dest: 'css/global.min.css'
}
},
// JS Concatenation Plugin
concat: {
dist: {
src: [
'js/libs/*.js', // All JS in the libs folder
'js/src/*.js' // All JS in the src folder
],
dest: 'js/scripts.js'
}
},
// JS Minification
uglify: {
build: {
src: 'js/scripts.js',
dest: 'js/scripts.min.js'
}
},
// Image Minification -- run on demand w/ `grunt imagemin`
imagemin: {
dynamic: {
files: [{
expand: true,
cwd: 'img/src/',
src: ['**/*.{png,jpg,gif}'],
dest: 'img/'
}]
}
},
// BrowserSync
browserSync: {
dev: {
bsFiles: {
src : [
'css/*.css',
'**/*.{html}'
]
},
options: {
watchTask: true,
server: './'
}
}
},
// Watch
watch: {
sass: {
files: ['scss/{,*/}*.{scss,sass}'],
tasks: ['sass', 'postcss']
},
js: {
files: ['js/src/*.js'],
tasks: ['concat', 'uglify']
},
options: {
livereload: false,
spawn: false
}
},
});
// Loads Grunt Tasks
grunt.loadNpmTasks('grunt-sass');
grunt.loadNpmTasks('grunt-postcss');
grunt.loadNpmTasks('grunt-browser-sync');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-imagemin');
// Default Task
grunt.registerTask('default', ['sass', 'postcss', 'concat', 'uglify', 'browserSync', 'watch']);
};
I would split them into subtasks so that I could call them separately in different registered tasks:
// Post CSS (autoprefixer, mqpacker, cssnano)
postcss: {
prefix: {
options: {
map: true,
// Load plugins
processors: [
require('autoprefixer')({browsers: ['last 2 versions']})
]
}
dist: {
src: 'css/src/global-sass.css',
dest: 'css/global.min.css'
}
},
pack: {
options: {
map: true,
// Load plugins
processors: [
require('css-mqpacker')()
]
}
dist: {
src: 'css/src/global-sass.css',
dest: 'css/global.min.css'
}
},
nano: {
options: {
map: true,
// Load plugins
processors: [
require('cssnano')({
autoprefixer: false,
safe: true,
sourcemap: false
})
]
}
dist: {
src: 'css/src/global-sass.css',
dest: 'css/global.min.css'
}
}
}
and then
grunt.registerTask('default', ['sass', 'postcss:prefix', 'postcss:pack', 'postcss:nano', 'concat', 'uglify', 'browserSync', 'watch']);
or whichever way suits your project.
Hope it helps

Looping grunt-watch with new version of autoprefixer

Up until recently I've been using a similar gruntfile to this with success. However in my latest project grunt watch is looping continually - sometimes 4 times, sometimes 15 times or so before stopping - I can't figure out how to change the gruntfile to get it working properly again.
module.exports = function (grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
// Define our source and build folders
js_path: 'public_html/js',
img_path: 'public_html/img',
css_path: 'public_html/css',
imagemin: {
dynamic: {
files: [{
expand: true,
cwd: '<%= img_path %>/',
src: ['*.{png,jpg,gif}'],
dest: '<%= img_path %>/'
}]
}
},
concat: {
dist: {
src: [
'<%= js_path %>/jquery-ui.js',
'<%= js_path %>/plugin_royal_slider.js',
'<%= js_path %>/plugins.js',
'<%= js_path %>/plugin_selectboxit.js',
'<%= js_path %>/plugin_picturefill.js',
'<%= js_path %>/plugin_jquery_placeholder.js',
'<%= js_path %>/plugin_pickadate.js',
'<%= js_path %>/script.js'
],
dest: '<%= js_path %>/output.js',
nonull: true
}
},
jshint: {
beforeconcat: [
'<%= js_path %>/jquery-ui.js',
'<%= js_path %>/plugin_royal_slider.js',
'<%= js_path %>/plugins.js',
'<%= js_path %>/plugin_selectboxit.js',
'<%= js_path %>/plugin_picturefill.js',
'<%= js_path %>/plugin_jquery_placeholder.js',
'<%= js_path %>/plugin_pickadate.js',
'<%= js_path %>/script.js'],
afterconcat: ['<%= js_path %>/output.js'
],
options: {
sub: true,
"-W004": true,
"-W041": true,
"-W093": true,
"-W032": true,
"-W099": true,
"-W033": true,
"-W030": true,
ignores: ['<%= js_path %>/jquery-ui.js']
}
},
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy H:M:s") %> */\n',
mangle: true,
compress: {drop_console: true} // true supresses console.log output: https://github.com/gruntjs/grunt-contrib-uglify#turn-off-console-warnings
},
dist: {
files: {
'<%= js_path %>/js.min.js': ['<%= js_path %>/output.js']
}
}
},
postcss: {
options: {
map: true,
processors: [
require('autoprefixer')({browsers: 'last 2 versions'}),
require('csswring')
]
},
dist: {
src: '<%= css_path %>/*.css'
}
},
watch: {
options: {nospawn: true},
scripts: {
files: ['<%= js_path %>/*.js'],
tasks: ['build']
},
css: {
files: ['<%= css_path %>/*.css'],
tasks: ['build']
},
grunt: {
files: ['Gruntfile.js']
}
}
});
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-imagemin');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-postcss');
grunt.registerTask('build', ['jshint', 'concat', 'uglify', 'postcss']); // run manually with grunt build or run grunt watch - ctrl c to exit
grunt.registerTask('optimg', ['imagemin']); // run with grunt optimg
grunt.event.on('watch', function (action, filepath) {
grunt.log.writeln(filepath + ' has ' + action);
});
};
Current dependencies:
"grunt": "~0.4.5",
"grunt-contrib-concat": "~0.5.1",
"grunt-contrib-imagemin": "~0.9.4",
"grunt-contrib-jshint": "~0.11.2",
"grunt-contrib-uglify": "~0.9.1",
"grunt-contrib-watch": "~0.6.1",
"grunt-postcss": "~0.6.0",
"csswring": "~4.0.0",
"autoprefixer-core": "~6.0.1",
"autoprefixer": "~6.0.3"
your issue is that concat generates its file into your js_path directory, and watch monitors all js files in that directory. So what happens is:
you modify a js source file
watch detects your modification and launches concat
concat generates output.js
watch detects output.js and launches concat
3 then 4 repeat - until you're saved by watch buffering (which prevents triggering too quickly in sequence)
To solve it, you have to remove the concat output from the watchlist.
What I recommend is to have concat output to a build directory outside your sources - it's best practice to separate build artefacts from source elements anyway. Define a build_path config variable and use it in your concat target.
If you really can't do that for a good reason, manually remove output.js from the watch with a negative directive (http://gruntjs.com/configuring-tasks#globbing-patterns):
watch: {
scripts: {
files: ['<%= js_path %>/*.js', '!<%= js_path %>/output.js'],
tasks: ['build']
},
}
Moving the postcss to a separate task has solved the problem for me.
Here are the changes to the gruntfile:
watch: {
options: {nospawn: true},
scripts: {
files: ['<%= js_path %>/*.js'],
tasks: ['build_js']
},
css: {
files: ['<%= css_path %>/*.css'],
tasks: ['build:css']
},
grunt: {
files: ['Gruntfile.js']
}
and
grunt.registerTask('build_js', ['jshint', 'concat', 'uglify']);
grunt.registerTask('build_css', ['postcss:dist']);

LESS/Grunt is not writing the sourcemap reference to the end of the compiled CSS

I am using grunt-contrib-less to compile my .less files in to a single CSS stylesheet. Everything is working, except the source map, which I cannot get to work under any circumstances!
Here is my Gruntfile:
'use strict';
module.exports = function(grunt) {
// Force use of Unix newlines
grunt.util.linefeed = '\n';
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
project: {
// Add entries to this array to create variables for use by Grunt
app: ['app'],
server: 'http://mysite.app',
// Components
bower: ['<%= project.app %>/bower_components'],
bootstrap: ['<%= project.bower %>/bootstrap'],
// Custom Assets
assets: ['<%= project.app %>/assets'],
css: ['<%= project.assets %>/css'],
less: ['<%= project.assets %>/less'],
js: ['<%= project.assets %>/js']
},
less: {
production: {
options: {
ieCompat: true,
sourceMap: true,
sourceMapFilename: '<%= project.css %>/style.css.map',
sourceMapURL: '<%= project.server %>/assets/css/style.css.map',
sourceMapBasepath: 'app',
sourceMapRootpath: '<%= project.server %>'
},
files: {
'<%= project.css %>/style.css': '<%= project.less %>/style.less'
}
}
},
autoprefixer: {
dist: {
files: {
'<%= project.assets %>/css/style.css': '<%= project.assets %>/css/style.css'
}
}
},
concat: {
options: {
separator: ';\n',
sourceMap: true
},
plugins_head: {
// Add further Javascript plugins to this array and they will be
// concatenated in to the plugins-built-head.js file
src: [
'<%= project.bower %>/modernizr/modernizr.js'
],
dest: '<%= project.js %>/built/plugins-built-head.js'
},
plugins: {
// Add further Javascript plugins to this array and they will be
// concatenated in to the plugins-built.js file
src: [
'<%= project.bootstrap %>/js/dropdown.js'
],
dest: '<%= project.js %>/built/plugins-built.js'
},
custom: {
// Add further custom-written javascript files to this array and
// they will be concatenated in to the scripts-built.js file
src: [
'<%= project.js %>/scripts.js'
],
dest: '<%= project.js %>/built/scripts-built.js'
}
},
watch: {
css: {
files: [
'<%= project.bootstrap %>/less/*.less',
'<%= project.less %>/*.less'
],
tasks: [
'less',
'autoprefixer'
],
options: {
livereload: true
}
},
js: {
files: [
'<%= project.js %>/scripts.js'
],
tasks: ['concat']
},
html: {
files: [
'<%= project.app %>/*.html'
],
options: {
livereload: true
}
}
}
});
grunt.loadNpmTasks('grunt-run-grunt');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-autoprefixer');
grunt.registerTask('default', [
'watch'
]);
};
What's happening is that Grunt is writing a proper and correct style.css.map file, but it's not writing the following line to the end of the compiled style.css file:
/*# sourceMappingURL=http://mysite.app/assets/css/style.css.map */
That's the one line that's missing. Everything else is getting compiled and written correctly. If I manually add that line to the end of the compiled CSS, Chrome picks up on the source map properly, but it's not being written in.
Additionally, trying options like sourceMapFileInline seems to make no difference - the file is never written inline.
Any ideas?
Hopefully you have found a solution by now. This is for other people with the same issue:
Make sure the soourcemap will be placed in the same folder as css.
Set sourceMapUrl to only the name of the map file.
This will add the following line to your .css file: /*# sourceMappingURL=default.css.map */
Here are the sourcemap settings in my grunt file:
sourceMap: true,
sourceMapFilename: "src/assets/css/default.css.map",
sourceMapURL: "default.css.map"

Grunt: Why is concat and uglify not working when the watch task sees a file change?

Grunt doesn't concat and uglify my javascript files when these files are changed and changes are watched for.
If I run grunt concat the js files are concatenated as expected.
If I then run grunt uglify the files are uglified as expected.
However when I simply run grunt the watch task starts and I change a javascript file I get this:
$ grunt
Running "watch" task
Waiting...
>> File "src/js/test.js" changed.
Running "uglify:my_target" (uglify) task
>> Destination dest/js/app.min.js not written because src files were empty.
>> No files created.
Why does it work when I run individual commands but not when changes to the files are being watched for?
Here's my grunt file:
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
options: {
separator: ';',
},
dist: {
src: ['src/**/*.js'],
dest: 'dest/js/app.js',
},
},
uglify: {
my_target: {
files: {
'dest/js/app.min.js': ['<%= concat.dist.dest %>']
}
}
},
compass: { // Task
dist: { // Target
options: { // Target options
sassDir: 'scss',
cssDir: 'css',
environment: 'production'
}
},
dev: { // Another target
options: {
sassDir: 'scss',
cssDir: 'css'
}
}
},
watch: {
css: {
files: '**/*.scss',
tasks: ['compass']
},
js: {
files: 'src/js/*.js', tasks: [ 'uglify' ]
},
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-compass');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.registerTask('default',['watch', 'compass', 'concat', 'uglify']);
};
I fixed this by changing the watch section. I also changed my_target to build.
My whole grunt file looks like this
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
options: {
separator: "\n\n\n",
},
dist: {
src: ['src/**/*.js'],
dest: 'dest/js/app.js',
},
},
uglify: {
build: {
files: {
'dest/js/app.min.js': ['<%= concat.dist.dest %>']
}
}
},
compass: { // Task
dist: { // Target
options: { // Target options
sassDir: 'scss',
cssDir: 'css',
environment: 'production'
}
},
dev: { // Another target
options: {
sassDir: 'scss',
cssDir: 'css'
}
}
},
watch: {
css: {
files: '**/*.scss',
tasks: ['compass']
},
scripts: {
files: ['<%= concat.dist.dest %>'],
tasks: ['concat', 'uglify:build'],
options: {
atBegin: true,
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-compass');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.registerTask('default',['watch', 'compass', 'concat', 'uglify']);
};

how do i make gruntjs do concat, uglify, and watch when i save my files?

I have a gruntjs file which has a registerTask('default',['concat','uglify','watch']);
If i save my scss files, it will update my css files. But if I want to have Grunt concat and uglify my JS, I have to go into terminal, exit out of the watch, then run the grunt command. Its really annoying.
Is there any way to have grunt run all these tasks when i save any of my JS and/or css files? Without needing to go into terminal every time? Heres my gruntfile if that helps.
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
compass: {
dist: {
options: {
sassDir: 'sass',
cssDir: 'css'
}
}
},
watch: {
css: {
files: '**/*.scss',
tasks: ['compass']
}
},
concat: {
options: {
separator: '\n\n'
},
dist: {
src: ['js-game/*.js'],
dest: 'js/<%= pkg.name %>.js'
}
},
uglify:{
options:{mangle:false},
my_target:{
files:{'js/<%= pkg.name %>.min.js' : ['js/<%= pkg.name %>.js']
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-compass');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default',['concat','uglify','watch']);
}
thanks so much everyone.
I think if you add:
scripts: {
files: ['**/*.js'],
tasks: ['jshint'],
}
to the watch task it should work, you might need:
scripts: {
files: ['**/*.js'],
tasks: ['jshint'],
options: {
spawn: false,
}
}
So in your Gruntfile.js above it would be something like:
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
compass: {
dist: {
options: {
sassDir: 'sass',
cssDir: 'css'
}
}
},
watch: {
css: {
files: '**/*.scss',
tasks: ['compass']
},
scripts: {
files: ['**/*.js'],
tasks: ['jshint'],
options: {
spawn: false,
}
}
},
concat: {
options: {
separator: '\n\n'
},
dist: {
src: ['js-game/*.js'],
dest: 'js/<%= pkg.name %>.js'
}
},
uglify:{
options:{mangle:false},
my_target:{
files:{'js/<%= pkg.name %>.min.js' : ['js/<%= pkg.name %>.js']
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-compass');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default',['concat','uglify','watch']);
}
That should hopefully do it.
You can added multiple tasks to watch. So if you wanted to concat and uglify on save you could do like this:
watch: {
css: {
files: '**/*.scss',
tasks: ['compass']
},
scripts: {
files: ['js-game/*.js'],
tasks: ['concat', 'uglify'],
options: {
spawn: false,
}
}
},
i hope this helps!

Resources