I have a gulp watch task as following:
gulp.task("watch", () => {
const watch = {
'dev': [src_folder + '**/*.njk', src_js_folder + '**/*.js'],
'css': [src_sass_folder + '**/*.scss']
}
gulp.watch(watch.dev, gulp.series("dev")).on("change", browserSync.reload);
gulp.watch(watch.css, gulp.series("sass"));
});
gulp.task("dev", gulp.series("merge-json", "nunjucks", "sass", "js"));
So when there's any changes on *.sass files the sass task will run, and when there's changes on *.js and/or *.njk files the task dev will run.
The problem I'm having is when there are changes on *.sass, *.js and/or *.njk at the same time, the sass task will run twice in this case.
How can I skip the gulp.watch(watch.css, gulp.series("sass")); when the gulp.watch(watch.dev, gulp.series("dev")).on("change", browserSync.reload); is already run.
Problem
The double execution of the sass task when running the watch task occurs because in the code you have shared you are effectively defining the sass task to run twice.
gulp.task("watch", () => {
const watch = {
'dev': [src_folder + '**/*.njk', src_js_folder + '**/*.js'],
'css': [src_sass_folder + '**/*.scss']
}
// 1st execution > The `dev` task is defined into the `gulp.series()` as part
// of the combined tasks to run for this watcher, and, as `dev` contains the
// `sass` task in it this will run it the first time.
gulp.watch(watch.dev, gulp.series("dev")).on("change", browserSync.reload);
// 2nd execution > This watcher have the `sass` task defined into the
// `gulp.series()` as part of the combined tasks to run, this will run
// the second time
gulp.watch(watch.css, gulp.series("sass"));
});
// The `dev` task contains the`sass` task into the `gulp.series()` as part
// of the combined tasks to run
gulp.task("dev", gulp.series("merge-json", "nunjucks", "sass", "js"));
Therefore, every time you run gulp dev the sass task will be executed only once, and every time you run gulp watch the sass task will be executed twice.
Solution
To solve this problem you could perhaps reorder the executions in a slightly different way. Here are some ideas of what some possible solutions might look like.
- Possible Solution 1
const paths = {
njk: src_folder + '**/*.njk',
js: src_js_folder + '**/*.js',
sass: src_sass_folder + '**/*.scss'
}
gulp.task("watch", () => {
gulp.watch([
paths.njk,
paths.js,
paths.sass
],
gulp.series("dev")
).on("change", browserSync.reload);
});
gulp.task("dev", gulp.series("merge-json", "nunjucks", "sass", "js"));
In this example, we define the sass task as part of the combined tasks to run defined within the gulp.series for the dev task. This will allow us to run gulp dev and execute all those tasks only once. It will also allow us to run gulp watch and execute the tasks defined for the dev task only once each time the files defined in paths are updated.
- Possible Solution 2
const paths = {
njk: src_folder + '**/*.njk',
js: src_js_folder + '**/*.js',
sass: src_sass_folder + '**/*.scss'
}
gulp.task("watch", () => {
gulp.watch([
paths.njk,
paths.js,
],
gulp.series("dev")
).on("change", browserSync.reload);
gulp.watch(paths.sass, gulp.series("sass");
});
gulp.task("dev", gulp.series("merge-json", "nunjucks", "js"));
gulp.task("another_extra_task", gulp.series("dev", "sass"));
In this example, we removed the sass task from the combined tasks to run for the dev task, also, we defined the another_extra_task task where we are combining the dev and sass tasks. This will allow us to run gulp another_extra_task and execute all the combined tasks defined for dev and the sass task, but also, it will allow us to run gulp watch without repeating the execution of the sass task since in the first watcher we execute the dev task where the sass task isn't defined anymore and we just execute it with the second watcher.
Conclusion
Since the definition of the tasks depends on each particular use case, I recommend you try to reorder the execution of them in a more granular way. See the following reference which explains how to avoid duplicating tasks https://gulpjs.com/docs/en/api/series#avoid-duplicating-tasks
I hope these possible solutions help you to understand the problem you are having in the code you have shared!
Assuming this is due to rapid file changes, it may help to set the watch delay option:
gulp.watch(watch.dev, { delay: 500 }, gulp.series("dev"))
If that does not help, you may need to figure out a way to de-bounce the events. There's an old package called gulp-debounce which seems to achieve that, but is unmaintained and so is likely incompatible, but viewing its source may grant some insights.
Related
So I'm having a slight problem with producing production ready scripts for my project. I'm using gulp to concatenate and minify my css and js, and while the css is working fine the gulp js function isn't generating my final file. Please refer to my code below:
gulp.task('js', function() {
return gulp.src([source + 'js/app/**/*.js'])
.pipe(concat('development.js'))
.pipe(gulp.dest(source + 'js'))
.pipe(rename({
basename: 'production',
suffix: '-min',
}))
.pipe(uglify())
.pipe(gulp.dest(source + 'js/'))
.pipe(notify({ message: 'Scripts task complete', onLast: true }));
});
If anyone has encountered a similar problem or has any tips it would be much appreciated :)
There is nothing wrong with your gulpfile. I tested it and it works perfectly.
The only thing I can guess is that your source is not set correctly. Did you forget the trailing slash '/' ?
I would suggest 2 things to figure it out. Include node path library to check where source is actually pointing to like this:
var path = require('path');
// in gulp task ...
path.resolve(path.resolve(source + 'js/app'));
Make sure it points where you think it does.
Secondly, you could use gulp-debug to establish that any files are found:
npm install gulp-debug
Then
var debug = require('gulp-debug');
// in gulp task ...
return gulp.src([source + 'js/app/**/*.js'])
.pipe(concat('development.js'))
.pipe(debug())
.pipe(gulp.dest(source + 'js'))
.pipe(debug())
// etc.
Good luck!
Based on additional infomation in the comments I realise you are generating JS files in a separate process ...
gulp is asynchronous by default. What this boils down to is that all functions try to run at the same time - if you want a specific order it must be by design. This is great because it's very fast but can be a headache to work with.
Problem
Here is what's basically happening:
// SOME TASK THAT SHOULD BE RUN FIRST
gulp.task('copy-vendor-files-to-tempfolder', function (done) {
// copy files to vendor folder
done()
})
// SOME TASKS THAT DEPEND ON FIRST TASK
gulp.task('complile-styles', function () { /* independent task */ })
gulp.task('concat-vendor-files', function () { /* concat files in vendor folder. depends on vendor files existing */ })
// GENERAL TASK WHICH STARTS OTHERS
gulp.task('ready', ['copy-vendor-files-to-tempfolder', 'compile-styles', 'concat-vendor-files])
When you try to run:
$ gulp ready
GULP TASK WILL FAIL! Folder is being created at the same time!!
NOWHERE TO COPY FILES!
Solution
There are many solutions but the following module has come in handy for me again and again:
npm install run-sequence
Then in your gulpfile.js:
var runSequence = require('run-sequence')
gulp.task('ready', function (done) {
runSequence(
'create-folders', // do this first,
[
'copy-css-files',
'copy-html-files'
], // do these AFTER but in parallel
done // callback when ready
)
})
This will guarantee the folder exists when you try to run the other functions.
In your specific case, you should make sure the task that concatenates the JS files is run after the task that copies them out of vendor.
Note: I'm leaving other answer because it contains useful help for debugging similar issues.
HTH!
I looked at the official Grunt documentation for creating tasks here
http://gruntjs.com/creating-tasks
I have two tasks that I want to do, but the second one cannot run until after the first one completes. That's because the second task takes the output from the first task and uses it to create new output.
To break it down
My project involves Bootstrap, so it has a lot of unused code. My first objective is to remove the unused code with uncss. I would then take the output from this new css file and minify it with cssmin.
Here was the exact example from gruntjs
grunt.registerTask('foo', 'My "foo" task.', function() {
// Enqueue "bar" and "baz" tasks, to run after "foo" finishes, in-order.
grunt.task.run('bar', 'baz');
// Or:
grunt.task.run(['bar', 'baz']);
});
I tried to apply this to my code here
grunt.registerTask('default', 'uncss', function() {
grunt.task.run('cssmin');
});
This means that when grunt is entered, the default is to run the uncss task first, wait for it to complete, then run the cssmin task. However I got this output
Running "default" task
Running "cssmin:css" (cssmin) task
1 file created. 3.38kb -> 2.27kb
Done, without errors
Here is my initConfig
uncss: {
dist: {
files: {
'directory/assets/stylesheets/tidy.css': ['directory/*.html', 'directory/views/*.html']
}
}
},
cssmin: {
css: {
files: {
'directory/assets/stylesheets/styles.min.css': ['directory/assets/stylesheets/styles.css']
}
}
}
In other words, I have two stylesheets in my folder. One contains the custom styles I created, and another contains Bootstrap minified. By running uncss, I will get a new css file named tidy.css.
The cssmin task is supposed to look for this tidy.css file and minify it resulting in a new styles.min.css file.
I can get this to work, but I have to manually run one task and then run another one. How can I automate this to have them run in sequence
first, best practice is to use npm package to load all tasks automatically:
// Load grunt tasks automatically
require('load-grunt-tasks')(grunt);
here are two grunt tasks:
one: {
wake up...
},
two: {
dress up...
},
and here is how you run one after the other
grunt.registerTask('oneThenOther', [
'one',
'two'
]);
You're close. When registering your alias task pass an array of tasks in the sequence you desire instead of a single task.
grunt.registerTask('default', ['uncss', 'cssmin']);
Alternatively, the sequence can be specified via the CLI:
> grunt uncss cssmin
It turns out part of the problem was I was specifying the wrong file under cssmin.
Changing my cssmin config to
cssmin: {
css: {
files: {
'directory/assets/stylesheets/styles.min.css': ['directory/assets/stylesheets/tidy.css']
}
}
}
and then registering the tasks in an array solved it
grunt.registerTask('default', ['uncss', 'cssmin']);
Note that the tasks must be specified in that order.
I've got some tasks already defined in gulpfile.js and I want to use gulp-watch plugin (to run tasks on new files). My question is, because I couldn't find anything, can I run my existing tasks while running watch (from plugin) function?
var gulp = require('gulp'),
watch = require('gulp-watch'),
...;
gulp.task('lint', function () {
return gulp.src(path.scripts)
.pipe(jshint())
.pipe(jshint.reporter(stylish));
});
gulp.task('watch', function () {
watch({ glob: 'app/**/*.js' }); // Run 'lint' task for those files
});
Because I don't want to include watch() task in every task I have. I would like to have only 1 task - watch, which will combine all "watches".
----- EDIT ----
(as I probably didn't quite get my point):
I need to run task from inside of gulp('watch') task. for example:
like I did it with gulp.watch:
gulp.task('watch', function () {
gulp.watch('files', ['task1', 'task2']);
});
I need to do the same but with gulp-watch plugin, something like (I know it wouldn't work):
var watch = require('gulp-watch');
gulp.task('watch', function () {
watch({ glob: 'files' }, ['task1', 'task2']);
});
I have also run into the problem of wanting to use gulp-watch (not gulp.watch), needing to use the callback form, and having trouble finding a suitable way to run a task in the callback.
My use case was that I wanted to watch all stylus files, but only process the main stylus file that includes all the others. Newer versions of gulp-watch may address this but I'm having problems with 4.3.x so I'm stuck on 4.2.5.
gulp.run is deprecated so I don't want to use that.
gulp.start works well, but is also advised against by the gulp author, contra.
The run-sequence plugin works well and lets you define a run
order, but it is a self-proclaimed hack:
https://www.npmjs.com/package/run-sequence
Contra suggest writing plain old functions and calling
those. This is a new idea to me, but I think the example below captures the idea. https://github.com/gulpjs/gulp/issues/505
Take your pick.
var gulp = require('gulp'),
watch = require('gulp-watch'), // not gulp.watch
runSequence = require('run-sequence');
// plain old js function
var runStylus = function() {
return gulp.src('index.styl')
.pipe(...) // process single file
}
gulp.task('stylus', runStylus);
gulp.task('watch', function() {
// watch many files
watch('*.styl', function() {
runSequence('stylus');
OR
gulp.start('stylus');
OR
runStylus();
});
});
All of these are working for me without warnings, but I'm still unsure about getting the "done" callback from the 4.2.x version of gulp-watch.
You will most likely want to run specific tasks related to the files you are watching -
gulp.task('watch',['lint'], function () {
gulp.watch('app/**/*.js' , ['lint']);
});
You can also use the ['lint'] portion to run any required tasks when watch first gets called, or utilize the tasks to run async with
gulp.task('default', ['lint','watch'])
You can just call one task, that then includes both task
gulp.task('default', ['lint','watch'])
so here you would just call 'gulp'
gulp.task('watch', function() {
watch(files, function() {
gulp.run(['task1', 'task2']);
});
});
work fine, except a warning
I already have grunt-contrib-qunit set up. My Gruntfile.js includes something like this
qunit: { files: ['test/*.html'] }
Now I can run grunt qunit and all my tests run.
Question: how can I run just one single test without running all of them? Is there a way I can overload the value of files from the command line?
You definitely need to look into grunt-contrib-qunit and grunt-contrib-connect (https://github.com/gruntjs/grunt-contrib-qunit and https://github.com/gruntjs/grunt-contrib-connect) as the tandem will provide you with a headless phantom and a local webserver.
UPDATE - as for running just one specific test, you could write something like this, listing your tests as separate targets for your qunit task:
grunt.initConfig({
qunit: {
justSomething: ['test/justsomething.html'],
justSomethingElse: ['test/justsomethingelse.html'],
all: ['test/*.html']
}
});
Then you can call grunt qunit:justSomething, or grunt qunit:all - this is not specific to qunit, though - see http://gruntjs.com/configuring-tasks
Now, if you would really like to use the target to specify a test name, you would go with something like:
module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-qunit');
grunt.initConfig({
qunit: {
all: ['test/**/*.html']
}
});
grunt.task.registerTask('foo', 'A sample task that run one test.', function(testname) {
if(!!testname)
grunt.config('qunit.all', ['test/' + testname + '.html']);
grunt.task.run('qunit:all');
});
}
Then call grunt foo:testname.
Yet again, this is not specific to qunit - but rather grunt task writing.
Hope that (finally) helps.
When grunt.loadNpmTasks is used, a grunt task is automatically available to the command line. It can be useful, but sometimes, I would like this task to be private, so it can be used whithin the Grunt file but not available to the command line.
Here is a contrived example. If I do :
module.exports = function(grunt) {
grunt.initConfig({
clean: {
test: ['test'],
release: ['release']
},
});
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.registerTask('build', 'Build the project.', function() {
console.log("building project");
});
grunt.registerTask('release', ['clean:release', 'build']);
};
... I can use the following command :
$ grunt release
However, this one is also available, and both clean:release and clean:test will be executed:
$ grunt clean
I do not want that. I want to control what can be called from the command line, since I may not have foreseen some undesirable effects if the user directly calls some tasks or subtasks.
I thought about registering a new clean task to supersedes the main one, and then choose what to call when clean is invoked (or to call nothing at all), but it does not work well since it cannot call the original clean task:
grunt.registerTask('clean', ['clean:release']);
Use grunt.task.renameTask
var ticks = +new Date();
var clean = 'clean-' + ticks;
grunt.task.renameTask('clean', clean);
grunt.registerTask('release', [clean + ':release', 'build']);
grunt.config.set(clean, grunt.config.get('clean'));
Copying the configuration over is important if you want to preserve the targets configuration