I'm having trouble configuring Grunt to watch my project files, rebuild and update a page hosted in a connect server. If I run any of the build tasks and then 'watch' as part of a combined task, then 'watch' seems to get stuck in a loop, endlessly printing the message.
Running "watch" task
Waiting...
Warning: must provide pattern
If instead I just run $ grunt watch, it will happily watch my source files and compile/build as appropriate.
I think the relevant task configurations are these:
watch: {
html: {
files: [ '<%= site.partials %>', '<%= site.layouts %>', '<%= site.pages %>' ],
tasks: [ 'html' ]
},
sass: {
files: [ '<%= site.src %>sass/*.scss' ],
tasks: [ 'styles' ]
}
},
// development server
connect: {
options: {
port: 8080,
livereload: 35729,
hostname: 'localhost',
},
dev: {
options: {
directory: 'build',
}
}
},
and the task definitions:
grunt.registerTask( 'build', [ 'styles', 'html', ] );
grunt.registerTask( 'default', [ 'build','connect:dev', 'watch' ] );
The 'styles' and 'html' tasks run grunt-sass and assemble. As stated above, running any of these tasks, or even 'watch' on its own yields the expected results. This suggests my config object has site.partials, site.dest etc defined correctly. The problem only happens when I run any task and then 'watch', as in the default task.
I just encountered a similar problem when I had been editing my Gruntfile and left a field (that should have had a file pattern) blank.
Check your Gruntfile for an empty file field.
In my specific example:
wiredep: {
options: {
overrides: {
"jquery-ui": {
"main": [
"jquery-ui.js",
"themes/base/jquery-ui.css",
""
]
}
}
}
}
Note the empty string above. That generated an error very similar to yours. It seems that Grunt doesn't tell you where the error is, unfortunately. You'll just need to scan through your Gruntfile manually to find the error.
connect:dev is the problem. Remove that and it should work fine.
Related
I have the following gruntfile.js. Using grunt-contrib-concat and grunt-contrib-uglify together.
This is an extention of a developement asked #Dynamic Mapping and Concat with Grunt Uglify
I use banner in the uglify task to reference a file created in the concat task.
module.exports = function(grunt){
// Configure task(s)
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
// setup concats
concat: {
options: {
separator: ';\n',
},
dev: {
files: [{
src: [
'sports.js',
'salon.js',
'offRoad.js',
],
dest: 'src/js/concat/car-dev.js'
}],
},
},
// setup uglify task
// this will produce a car js file (which includes car-dev.js) for every car js file located within scr/js/cars/
uglify: {
// dev - use in development mode, global overrides section
dev: {
options: {
// include concat dev.js
banner: grunt.file.read('src/js/concat/car-dev.js'),
beautify: true,
mangle: false,
compress: false,
preserveComments: 'all'
},
files: [{
expand: true,
cwd: 'src/js/cars/',
src: '*.js',
dest: 'javascript/dev',
ext: '.dev.js',
extDot: 'first'
}],
},
},
});
// Load the plugins
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-sass');
// Register task(s)
grunt.registerTask('default',
'concat:dev'
'uglify:dev'
]);
};
My issue is that if src/js/concat/car-dev.js doesn't exist, I get the following error:
Error: Unable to read "src/js/concat/car-dev.js" file <Error code: ENOENT).
If I create an empty file #src/js/concat/car-dev.js the grunt command works fine.
However this is not always possible. I want uglify to reference the car-dev.js file even if it doesn't exist. What I'm looking for is someway to reference the grunt->concat->dev->files->dest file.
I've tried adding the following
banner: grunt.file.read(grunt.concat.dev.files.dest),
and
banner: grunt.concat.dev.files.dest
but neither work. Is this possible?
This is not a task dependency issue - the error gets thrown before any tasks are run because banner: src/js/concat/car-dev.js does not exist.
Any help - much appreciated.
I'm trying to use Grunt's grunt-contrib-watch plugin to watch some files and then run the tasks, just what it was made for, this is my Gruntfile.js
module.exports = function(grunt){
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
sass: {
options: {
sourceMap: true,
outputStyle: 'compressed'
},
dist: {
files: {
'static/stylesheets/main.min.css': 'static/stylesheets/sass/main.scss',
/*'bower_components/foundation-sites/dist/foundation.min.css': 'bower_components/foundation-sites/scss/foundation.scss'*/
}
}
},
uglify: {
dist: {
files: {
'static/javascript/main.min.js': 'static/javascript/main.js'
}
}
},
watch: {
files: [
'<%= sass.dist.files %>',
'<%= uglify.dist.files %>'
],
tasks: [
'sass',
'uglify'
]
}
});
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-sass');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.registerTask('default', ['watch']);
};
When I run it, it runs over and over and over again, and when I stop it, I see I have this message:
Running "watch" task
Waiting...
Warning: pattern.indexOf is not a function
I really don't know what's the problem. Does anybody know what happens here?
UPDATE:
Apparently, the problem is because of the way I call the files, because I changed it to: 'static/stylesheets/sass/*.scss' and it worked well, but I would like to know why the other way doesn't work, because I think is a more useful way to do it.
grunt-contrib-watch expects either a single pattern string or an array of pattern strings as the value of its files config. However, the templates you are using in your config evaluate to objects. Your watch.files value evaluates to the following:
files: [
{ 'static/stylesheets/main.min.css': 'static/stylesheets/sass/main.scss' },
{ 'static/javascript/main.min.js': 'static/javascript/main.js' }
]
When the pattern matching tries to run it fails because there is no indexOf method on an Object. Due to the fact that grunt-contrib-watch runs forever, there is an infinite loop of the trying and failing to build the list of watched files.
You would normally configure your watch files with something like the following:
files: [
'static/**/*.scss',
'static/**/*.js'
]
But the above will cause issues in your case because your minified JS file is in the same folder as your source JS file. You could probably get around this by adding '!static/**/*.min.js' to your files array, but a better solution would be to put all of your compiled files into a separate folder.
Extract the values from the object:
watch: {
files: [
'<%= sass.dist.files.values() %>',
'<%= uglify.dist.files.values() %>'
],
Still a bit of a newbie with Grunt, so please bear that in mind with your answers. I am trying to setup a task in Grunt that will concat any JS files within a directory called "custom", into a single file called custom-concat.js, however after running grunt watch (which runs fine without error), nothing is happening when I try and make changes to any of the files within my "custom" directory (i.e. console just sits at "waiting...." even after I make changes to any JS files within "custom" directory). Clearly there is something wrong with my concat task, but I can't seem to see what the problem is. Can anyone see where the problem lies? Full gruntfile below:
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
//pkg: grunt.file.readJSON('package.json'),
concat: {
options: {
separator: ';',
},
dist: {
src: ['scripts/custom/**/*.js'],
dest: 'scripts/custom-concat.js',
},
},
uglify: {
build: {
src: 'scripts/custom-concat.js',
dest: 'scripts/custom.min.js'
}
},
less: {
options: {
paths: ["css"]
},
files: {
"styles.css": "less/styles.less"
}
},
watch: {
scripts: {
files: 'scripts/**/*.js',
task: ['concat', 'uglify:build']
},
styles: {
files: 'css/less/**.less',
task: 'less'
}
}
});
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
// Default task(s).
grunt.registerTask('default', ['concat', 'uglify']);
};
As far as I see, there are three small issues with your watch task:
The correct attribute for watch is taskS not task
If you want to run the tasks of watch, directly at the beginning, use options { atBegin: true }
Your watch task monitors the script folder. However, this folder will also contain your concated and uglified files. So this task will run into an infinite loop. You should probably only watch the scripts/custom folder
So your watch task should probably look something like this:
watch: {
scripts: {
files: 'scripts/custom/**/*.js',
tasks: ['concat', 'uglify:build'],
options: {
atBegin: true
}
},
styles: ...
}
Github grunt-contrib-watch
My grunt-contrib-sass will stop on compilation error with a traceback when I run grunt sass, but not when sass is triggered via grunt-contrib-watch.
sass: {
development: {
options: {
style: 'expanded'
},
files: [
{
expand: true,
cwd: '<%= paths.development %>stylesheets/',
src: ['*.scss', '*.sass'],
dest: '<%= paths.development %>assets/',
ext: '.css.liquid'
},
]
},
watch: {
sass: {
files: [
'<%= paths.development %>stylesheets/*{.sass,.*scss}'
],
tasks: ['sass:development'] // this swallows errors
},
I can't seem to find any information on how to prevent this from happening in the grunt-contrib-sass or grunt-contrib-watch documentation. There is no error propagation option.
What am I missing?
I have not had this problem on 5+ other previous grunt based projects with similar watch->sass setups and am curious what the internet thinks.
Without any error message it cant be hard to determine how to solve your problem. What does grunt sass -v tell you?
Also I've had a similar problem in the past, tho I don't recall the actual error message. I solved the problem by using grunt-sass instead of grunt-contrib-sass.
I use the grunt-contrib-watch task (v. 0.5.3) in order to enable LiveReload:
livereload: {
options: {
middleware: function (connect) {
return [lrSnippet, mountFolder(connect, '.tmp'),
mountFolder(connect, 'src'),
proxySnippet];
}
}
}
//...some other tasks...
watch: {
livereload: {
options: {
livereload: LIVERELOAD_PORT
},
files: [
'src/*.html'
]
}
}
//...................................
grunt.registerTask('server', [
'clean:server',
'recess:compile',
'configureProxies',
'connect:livereload',
'open',
'watch'
]);
While running grunt server --verbose (including watch task), I end up with this console output:
Running "watch" task
Waiting...Verifying property watch exists in config...OK
Verifying property watch.livereload.files exists in config...OK
Live reload server started on port: 35729
Watching src/404.html for changes.
Watching src/app for changes.
Watching src/assets for changes.
Watching src/common for changes.
Watching src/less for changes.
Watching src/vendor for changes.
Watching src/index.html for changes.
For instance, we see here that src/index.html is observed, thus I attempt to change the page title to see the live modification. But, process exits as soon as I save my file..
I read about the fact that watch task may exit if the provided file paths are invalid.
But, Watching src/index.html for changes asserts that it exists, doesn't it?
I don't figure it out.
So the problem was.... a bug with NodeJs 0.10.18.
I updated to 0.10.21 and the whole works without changing anything in my first Gist.
In one word, if you have OSX 10.9 (Mavericks), you have to update Node to 0.10.21
Not sure if that's the issue you're having, but it seems like you're using the connect-livereload middleware directly instead of setting the livereload option directly, as supported by grunt-contrib-connect.
Here's a simple example GruntFile, using just grunt-contrib-watch and grunt-contrib-connect:
module.exports = function (grunt) {
var LIVERELOAD_PORT = 12345;
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
connect: {
server: {
options: {
port: 9001,
base: './src',
livereload: LIVERELOAD_PORT
}
}
},
watch: {
develop: {
files: 'src/*.html',
options: {
livereload: {
port: LIVERELOAD_PORT
}
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('server', [
'connect:server',
'watch'
]);
};
If you don't need to configure a specific port and can use the default port, it's even simpler - just change the livereload properties for both tasks to true:
livereload: true