Gulp.watch tasks not called (gulp 4.0.0) - javascript

I have just migrated an old project from gulp 3 to gulp 4.0.0. I'm having trouble making gulp.watch tasks run within my watch function. I'm running on Windows.
The watch function is as follows:
function watch(done) {
gulp.watch(LAYOUTSFILES, copytosomelocation);
gulp.watch(MODULEFILES, copymodulestosomelocation);
gulp.watch(DLLFILES, gacdeploy);
gulp.watch("./Styles/**/*.less", cssless);
console.log("watching");
done();
}
Locations are formatted as follows (worked in gulp 3, so locations are at least correct):
let LAYOUTSFILES = [
"./Folder/Project/**/*.*"
];
This is the first task being called:
function copytosomelocation() {
console.log("In copy");
return gulp.src(LAYOUTSFILES)
.pipe(fileCache.filter())
.pipe(gulp.dest(OUTLAYOUTS));
}
At the end of the gulpfile, I have exports.watch = watch;.
When I run, I get the following output:
[18:43:59] Using gulpfile
D:\git\repo\folder\someproject\gulpfile.js
[18:43:59] Starting 'watch'...
watching
[18:43:59] Finished 'watch' after 17 ms
That is to say
- No files are copied
- No output is logged to the console from function copytosomelocation.
What am I missing?

I had neglected to take the necessary task dependency changes with me to the new structure.
The original watch function was structured as follows:
gulp.task("watch", ["webpack-watch"], () => { ... });
which described a dependency on the following:
gulp.task('webpack-watch', (done) => {
runWebpack(["-d"], true, done);
});
function runWebpack(params, watch, done) {
params = params || [];
params.push("--color");
if (watch) {
params.push("-w");
}
if (watch) {
spawnCommand("webpack", params);
done(); //return immidiately when watching
} else {
spawnCommand("webpack", params, done);
}
}
Without this functionality gulp was watching a directory which would only be updated when webpack-watch had run. I solved this by updating the watch export as follows:
exports.watch = gulp.series('webpack-watch', watch);

Related

Convert gulp watch in gulp#3.9.1 to gulp#4

We are switching from gulp#3.9.1 to gulp#4 and are having trouble switching over. When we run gulp watch, we are getting the following errors and trying to figure out how to resolve it.
What is the proper way to convert the gulp watch task to work with gulp#4?
Error message
AssertionError [ERR_ASSERTION]: Task never defined: minify-css
Command: gulp watch
This should run minify-js then minify-css in order
minify-js should run after clean-scripts has completed successfully
minify-css should run after clean-css has completed successfully
Current tasks.
var gulp = require('gulp'),
cssmin = require('gulp-clean-css'),
clean = require('gulp-clean'),
uglify = require('gulp-uglify');
var src = {
js: 'js/some-dir/**/*.js',
css: 'css/some-dir/**/*.css'
};
var dest = {
js: 'js/dest/some-dir/**/*.js',
css: 'css/dest/some-dir/**/*.css'
};
gulp.task('clean-css', function() {
return gulp.src(dest.css)
.pipe(clean({read:false, force: true});
});
gulp.task('minify-css', ['clean-css'], function() {
gulp.src(src.css)
.pipe(cssmin())
.pipe(gulp.dest(dest.css));
});
gulp.task('clean-scripts', function() {
return gulp.src(dest.js)
.pipe(clean({read:false, force: true});
});
gulp.task('minify-js', ['clean-scripts'], function() {
gulp.src(src.js)
.pipe(uglify())
.pipe(gulp.dest(dest.js));
});
gulp.task('watch', ['minify-js', 'minify-css'], function() {
gulp.watch(src.js, ['minify-js']);
gulp.watch(src.css, ['minify-css']);
});
We tried doing this, but it resulted in the error message
gulp.task('watch', gulp.series('minify-js', 'minify-css', function() {
gulp.watch(src.js, ['minify-js']);
gulp.watch(src.css, ['minify-css']);
}));
gulp.task('minify-css', gulp.series('clean-css', function() {
return gulp.src(src.css)
.pipe(cssmin())
.pipe(gulp.dest(dest.css));
}));
gulp.task('minify-js', gulp.series('clean-scripts', function() {
return gulp.src(src.js)
.pipe(uglify())
.pipe(gulp.dest(dest.js));
}));
gulp.task('watch', gulp.series('minify-js', 'minify-css', function() {
gulp.watch(src.js, gulp.series('minify-js'));
gulp.watch(src.css, gulp.series('minify-css'));
}));
As #Abdaylan suggested, I also advocate switching to functions. Nevertheless, so you can see where your code was wrong, I have fixed it here. Gulp 4 does not use this syntax:
gulp.task('someTask', ['task1', 'task2'], function () {}); // gulp 3
Gulp 4:
gulp.task('someTask', gulp.series('task1', 'task2', function () {})); // gulp 4 with string tasks
or gulp.parallel. So you can use your gulp.task syntax (rather than named functions) if you modify them to use the signatures that gulp 4 supports as I did in your modified code at the top of this answer.
Gulp 4 with named functions:
gulp.task(someTask, gulp.series(task1, task2, function () {})); // gulp 4 with named functions
So with named functions, the tasks are not referred to as strings.
See also task never defined for other potential problems when migrating from gulp3 to gulp4 with the same error message.
I would recommend converting your minify-js, minify-css, clean-scripts and clean-css tasks to functions:
var dest = {
js: 'js/dest/some-dir/**/*.js',
css: 'css/dest/some-dir/**/*.css'
};
function cleanCss() {
return gulp.src(dest.css)
.pipe(clean({read:false, force: true});
});
function minifyCss() {
return gulp.src(src.css)
.pipe(cssmin())
.pipe(gulp.dest(dest.css));
});
function cleanScripts() {
return gulp.src(dest.js)
.pipe(clean({read:false, force: true});
});
function minifyJs() {
return gulp.src(src.js)
.pipe(uglify())
.pipe(gulp.dest(dest.js));
});
var minifyJsAndClean = gulp.series(minifyJs, cleanScripts);
var minifyCssAndClean = gulp.series(minifyCss, cleanCss);
var watchers = function (done) {
gulp.watch(src.js, minifyJs);
gulp.watch(src.css, minifyCss);
done();
}
gulp.task('watch', gulp.series(minifyJsAndClean, minifyCssAndClean, watchers));
I just ran into this a couple days ago myself. What worked for me was to run each task in its own gulp.watch() with the gulp.series() on the watch task call instead of the watch task itself. For example:
gulp.task('watch', function() {
gulp.watch(src.js, gulp.series('minify-js'));
gulp.watch(src.css, gulp.series('minify-css'));
});

How can I get browserify's "bundle" function to emit an end event?

Say I have some relatively generic browserify scaffolding intended to be used with gulp:
var browserSync = require('browser-sync').create();
var browserify = require('browserify');
var gutil = require('gulp-util');
var exorcist = require('exorcist');
var bundler = browserify('app.jsx', {
debug: false,
extensions: ['.jsx'],
cache: {},
packageCache: {}
});
function bundle() {
return bundler.bundle()
.on('error', function(err) {
gutil.log(err.message);
browserSync.notify('Browserify error!');
this.emit('end');
})
.pipe(source('app.js'))
.pipe(transform(function () {
return exorcist('public/js/app.js.map');
}))
.pipe(gulp.dest('public/js'))
.pipe(browserSync.stream({ once: true }))
}
My issue is that gulp 4 needs explicit notification of completion of tasks, which bundle() as above does not provide:
gulp.task('js', function(callback) {
return bundle();
})
Gulp 4 output:
[timestamp] The following tasks did not complete: js
[timestamp] Did you forget to signal async completion?
However, bundle() doesn't emit an 'end' event on its own, so the below makes the same error:
gulp.task('js', function(callback) {
bundle().on('end', callback);
});
How do I get this function to emit an 'end' event after the last .pipe() call, or otherwise get gulp 4 to recognize the task is finished according to the API docs?
The issue you're facing is that of passing callback, and not calling it. If callback is undefined, all you need to do is return a stream.
The following should do the trick:
gulp.task('js', function () { return bundle(); });
As should this:
gulp.task('js', function (cb) { bundle().on('end', cb); });
You either return or invoke the callback, don't mix the two.
I could be wrong here, but I think the general recommendation is to return.
I don't know what the underlying issue is, but removing the exorcist line in the pipe fixes my problem.

How to send in a string into a Gulp task?

This is step 1 in creating versioned folders for my deployment builds.
How would it be possible to pass in a string into the gulp.task below?
gulp.task('build', function(cb) {
runSequence('html-templates',
'stx-css',
'app-css',
'stx-js',
'app-js',
'build:copy',
'build:remove',
'build:index', cb);
});
I'd like to do something like this: gulp build 1.0.1 which will then pass the string 1.0.1 into the Gulpfile.
gulp.task('build', function(version, cb) {
console.log('version = ', version);
runSequence('html-templates',
'stx-css',
'app-css',
'stx-js',
'app-js',
'build:copy',
'build:remove',
'build:index', cb);
});
This can be accomplished using the env variable!
Great video on the subject:
https://www.youtube.com/watch?v=gRzCAyNrPV8
So I added this to my Gulpfile:
var env = process.env.V; // V={version number}
And then added a version task, which calls a function to print out a string that I pass in before calling my build task:
gulp.task('build', function(cb) {
runSequence('version',
'html-templates',
'stx-css',
'app-css',
'stx-js',
'app-js',
'build:copy',
'build:remove',
'build:index', cb);
});
gulp.task('version', function() {
return printOut(env);
});
function printOut(version) {
console.log('version = ',version);
}

make some operations on factor-bundle's partial bundles

I am using gulp with browserify and factor-bundle.
I have the following code:
b = browserify({
entries: [ 'a.js', 'b.js'],
plugin: [ [ 'factor-bundle', { outputs: [ 'build/a.js', 'build/b.js' ] } ] ]
})
.bundle()
.pipe(source('bundle.js'))
.pipe(buffer())
.pipe(gulp.dest('/build/common'));
I want to pipe some actions (like uglify, bundle-collapser or other job) on the parial bundles ('build/a.js' and 'build/b.js'). I tried to use the method described on the factor-bundle's page:
b.plugin('factor-bundle', { outputs: [ write('x'), write('y') ] });
function write (name) {
return concat(function (body) {
console.log('// ----- ' + name + ' -----');
console.log(body.toString('utf8'));
});
}
But I don't understand the write() method and don't know how to perform uglification and how to gulp.dest the result.
Any idea? explanation?
The write() method returns a writable stream that allows you to pipe bundles
generated by the factor-bundle plugin through further downstream transformations.
For instance, your write() method may look something like this:
var path = require('path');
var file = require('gulp-file');
var sourcemaps = require('gulp-sourcemaps');
function write (filepath) {
return concat(function (content) {
// create new vinyl file from content and use the basename of the
// filepath in scope as its basename.
return file(path.basename(filepath), content, { src: true })
// uglify content
.pipe(uglify())
// write content to build directory
.pipe(gulp.dest('./build/scripts'))
});
}
And you would use it like this:
browserify({
entries: [ 'a.js', 'b.js'],
plugin: [ [ 'factor-bundle', { outputs: [ write('a.js'), write('b.js') ] } ] ]
})
.bundle()
.pipe(write('common.js'))
// Could have use these instead, but it wouldn't be as DRY.
// .pipe(source('common.js'))
// .pipe(uglify())
// .pipe(gulp.dest('./build/scripts'))
Using the factor-bundle plugin affects the output of browserify after
.bundle() is called. Normally, it would generate bundles as readable streams
mapping to each of your entry files, then you would be able to apply further
transformations to them.
Instead you will get a single readable stream that contains a bundle with the
shared common modules from the supplied entry files, which I have called
common.js on the example above. Then you need to handle the transfomations
of the readable streams that map to each entry file separately.
In the example above I have added writable streams to the outputs array, arranged
in the same order as my entry files, which receive their respective bundle as
readable stream and apply further transformations to them
You could also leverage the factor.pipeline event:
var b = browserify({ ... });
b.on('factor.pipeline', function (id, pipeline) {
pipeline.get('wrap').push(write(id));
});
b.plugin(factor);
return b.bundle().pipe(write('common.js'));
I think it is worth noting that applying further downstream work to the outputs
is completely detached from the pipeline. So if you were using gulp and returned
the stream from browserify, the task would have completed prematurely because
it would still be performing operations on the entry files. I haven't run into
issues with this yet.
Hope this helps.
This is a bit old, but it might be usefull to someone else.
The answer above from #Christian helped me, but i had to solve the issue of the task completion. I did it by adding a counter for opened streams, and calling the task callback once they are all closed.
gulp.task('build:js:compile', function(cb) {
const apps = getAllJavascriptFilesPaths(); // this returns an array of full path to the js files i want to bundle
const dist = 'dist'; // the output path
const files = [];
const streams = [];
let openedStreams = 0;
// We use browserify factor-bundle to get the shared code in a separated js file, and not in all apps files
// The write function here handles the post processing of each browserified app by returning a writable stream
// We check the number of opened streams, and call the callback once they are all closed, to be sure the task is
// complete
function write(filepath) {
openedStreams++;
return concat(function (content) {
// create new vinyl file from content and use the basename of the
// filepath in scope as its basename.
return file(path.basename(filepath), content, { src: true })
.pipe(uglify())
.pipe(gulp.dest(dist))
.on('finish', function () {
openedStreams--;
if (openedStreams == 0) {
cb();
}
});
});
}
apps.forEach(function (file) {
files.push(file);
streams.push(write(file)));
});
browserify(files)
.plugin(factor, { outputs: streams })
.transform("babelify", {presets: 'babel-preset-env'})
.bundle()
.pipe(write('common.js'));
});

Calling grunt.config.set inside grunt.util.spawn doesn't seem to have any effect

I'm trying to set the current Git SHA in my project's Grunt configuration, but when I try to access it from another task it isn't available, What am I missing?
grunt.registerTask('sha', function () {
var done = this.async();
grunt.util.spawn({
cmd: 'git',
args: ['rev-parse', '--short', 'HEAD']
}, function (err, res) {
if (err) {
grunt.fail.fatal(err);
} else {
grunt.config.set('git', {sha: res.stdout});
if (grunt.option('debug') || grunt.option('verbose')) {
console.log("[sha]:", res.stdout);
}
}
done();
});
});
After running the task, I expect the config to be available in another task configuration:
requirejs: {
dist: {
...
out: '<%= app.dist %>/scripts/module_name.<%= git.sha %>.js'
...
}
}
So... What's the problem?
The problem is that Require JS is writing to the file public/scripts/module_name..js, the SHA is not available in the configuration (when the filename should be public/scripts/module_name.d34dc0d3.js).
UPDATE:
The problem is that I'm running requirejs tasks with grunt-concurrent, so the Grunt configuration is not available for requirejs.
grunt.registerTask('build', [
...
'getsha',
'concurrent:dist',
...
]);
And the concurrent task, looks like:
concurrent: {
dist: [
...
'requirejs',
...
]
}
Since grunt-concurrent will spawn tasks in child processes, they do not have access to the context of the parent process. Which is why doing grunt.config.set() within the parent context is not available in the config of the child context.
Some of the solutions to make the change available in the child context are:
Write the data to the file system
Write the data to a temporary file with grunt.file.write('./tmp/gitsha', res.stdout) and then have the task being ran in a child process read the temporary file:
out: (function() {
var out = grunt.config('app.dist') + '/scripts/module_name.';
if (grunt.file.exists('./tmp/gitsha')) {
out += grunt.file.read('./tmp/gitsha');
} else {
out += 'unknown';
}
return out + '.js';
}())
Use a socket
This is a very convoluted solution but a solution nonetheless. See the net node docs: http://nodejs.org/api/net.html#net_net_createserver_options_connectionlistener on creating a server on the parent process then have the child process connect to the socket for the data.
Or check out https://github.com/shama/blackbox for a library that makes this method a bit simpler.
Fork the parent process instead of spawn/exec
Another method is to use fork: http://nodejs.org/api/child_process.html#child_process_child_process_fork_modulepath_args_options instead of grunt-concurrent. Fork lets you send messages to child processes with child.send('gitsha') and receive them in the child with process.on('message', function(gitsha) {})
This method also can get very convoluted.
Use a proxy task
Have your sha task set the config as you're currently doing:
grunt.registerTask('sha', function() {
grunt.config.set('git', { sha: '1234' });
});
Change your concurrent config to call a proxy task with the sha:
grunt.initConfig({
concurrent: {
dist: [
'proxy:requirejs:<%= git.sha %>'
]
}
});
Then create a proxy task that runs a task with setting the passed value first:
grunt.registerTask('proxy', function(task, gitsha) {
grunt.config.set('git', { sha: gitsha });
grunt.task.run(task);
});
The above can be simplified to set values specifically on requirejs but just shown here as a generic example that can be applied with any task.

Categories