Having some issue with migrating from gulp3 to 4. I have seen various posts but still cannot seem to get around the error
[12:17:41] The following tasks did not complete: serve-build, build,
[12:17:41] Did you forget to signal async completion?
Already tried adding the async function() and promise/return in function(done){.... done();}
cannot seem to get in the right place. It seems to stop and build my lib and app .js files but they are missing things.. specifically the tinymce plugin..
gulp.task('clean-styles', function(done) {
var files = [].concat(
config.temp + '**/*.css',
config.build + 'styles/**/*.css'
);
clean(files, done);
});
gulp.task('styles', gulp.series('clean-styles', function() {
log('Compiling Less --> CSS');
return gulp
.src(config.less)
.pipe($.plumber()) // exit gracefully if something fails after this
.pipe($.less())
// .on('error', errorLogger) // more verbose and dupe output.
requires emit.
.pipe($.autoprefixer({browsers: ['last 2 version', '> 5%']}))
.pipe(gulp.dest(config.temp));
}));
/**
* Remove all fonts from the build folder
* #param {Function} done - callback when complete
*/
gulp.task('clean-fonts', function(done) {
clean(config.build + 'fonts/**/*.*', done);
});
/**
* Copy TinyMCE fonts
* #return {Stream}
*/
gulp.task('fonts-tinymce', function () {
log('Copying tinymce fonts');
return gulp
.src(config.fontsTinyMCE)
.pipe(gulp.dest(config.build + 'styles/fonts'));
});
/**
* Copy fonts
* #return {Stream}
*/
gulp.task('fonts', gulp.series('clean-fonts', 'fonts-bootstrap', 'fonts-tinymce', function () {
log('Copying fonts');
return gulp
.src(config.fonts)
.pipe(gulp.dest(config.build + 'fonts'));
}));
/**
* Remove all images from the build folder
* #param {Function} done - callback when complete
*/
gulp.task('clean-images', function(done) {
clean(config.build + 'images/**/*.*', done);
});
/**
* Compress images
* #return {Stream}
*/
gulp.task('images', gulp.series('clean-images', function() {
log('Compressing and copying images');
return gulp
.src(config.images)
.pipe($.imagemin({optimizationLevel: 4}))
.pipe(gulp.dest(config.build + 'images'));
}));
gulp.task('less-watcher', function() {
gulp.watch([ config.less ], gulp.series('styles'));
});
/**
* Optimize all files, move to a build folder,
* and inject them into the new index.html
* #return {Stream}
*/
gulp.task('optimize', gulp.series('inject', 'test', function() {
log('Optimizing the js, css, and html');
var assets = $.useref.assets({searchPath: './'});
// Filters are named for the gulp-useref path
var cssFilter = $.filter('**/*.css');
var jsAppFilter = $.filter('**/' + config.optimized.app);
var jslibFilter = $.filter('**/' + config.optimized.lib);
var templateCache = config.temp + config.templateCache.file;
return gulp
.src(config.index)
.pipe($.plumber())
.pipe(inject(templateCache, 'templates'))
.pipe(assets) // Gather all assets from the html with useref
// Get the css
.pipe(cssFilter)
.pipe($.minifyCss())
.pipe(cssFilter.restore())
// Get the custom javascript
.pipe(jsAppFilter)
.pipe($.ngAnnotate({add: true}))
.pipe($.uglify())
.pipe(getHeader())
.pipe(jsAppFilter.restore())
// Get the vendor javascript
.pipe(jslibFilter)
.pipe($.uglify()) // another option is to override wiredep to use min files
.pipe(jslibFilter.restore())
// Take inventory of the file names for future rev numbers
.pipe($.rev())
// Apply the concat and file replacement with useref
.pipe(assets.restore())
.pipe($.useref())
// Replace the file names in the html with rev numbers
.pipe($.revReplace())
.pipe(gulp.dest(config.build));
}));
/**
* Build everything
* This is separate so we can run tests on
* optimize before handling image or fonts
*/
gulp.task('build', gulp.series('optimize', 'images', 'fonts', function() {
log('Building everything');
var msg = {
title: 'gulp build',
subtitle: 'Deployed to the build folder',
message: 'Running `gulp serve-build`'
};
del(config.temp);
log(msg);
notify(msg);
}));
/**
* Remove all tinymce fonts from the build folder
* #param {Function} done - callback when complete
*/
gulp.task('clean-fonts-tinymce', function (done) {
clean(config.build + 'styles/fonts/*.*', done);
});
/**
* serve the build environment
* --debug-brk or --debug
* --nosync
*/
gulp.task('serve-build', gulp.series('build', function() {
serve(false /*isDev*/);
}));
I have a somewhat similar setup and this should help with the error:
/**
* serve the build environment
* --debug-brk or --debug
* --nosync
*/
gulp.task('serve-build', gulp.series('build', function(done) {
serve(false /*isDev*/);
done();
}));
More on async completion can be found in the documentation
With normal gulp tasks (e.g. build my javascript) you have a well defined start and end to that task.
However with dev tasks like devServer or watch tasks these are started and then running perpetually. The reason for the error is generally that you started the task and it was still running when you hit CTRL-C and so gulp does not see the task as finished (because it wasn't).
The other case where you get this error is if your code DID finish but was async and you forgot to tell gulp you were done by using a callback. But I'm going to ignore that case here.
If you don't want an error from your never-ending tasks (devServer/watch/runRedis/etc) when you CTRL-C gulp, you have 2 choices for these type of tasks...
1) Lie to gulp
function myNeverEndingDevTask(done) {
// start an async process (dev server, watch, etc)
done(); // we tell gulp we are done, but we aren't really (async process keeps going)
}
In this scenario gulp will report Finished: myNeverEndingDevTask right away even though the task is not finished (e.g. devServer or watch still running). But you won't get the error when you CTRL-C gulp.
2) Handle CTRL-C
I'll use gulp.watch as an example but it can apply to any neverending gulp tasks.
The issue is that watch is still running when we hit CTRL-C and gulp knows it is still running because we didn't do option #1 above.
So we need to handle the CTRL-C and tell gulp we are done. We can do this by adding a SIGINT event handler that cleans up (if needed) our never-ending task and THEN calls done() to let gulp know we finished the task.
You can wrap this into a utility function for instance...
function gulpWatchTidy (paths, opts, task, done) {
// gulp.watch returns underlying chokidar watcher
var watcher = gulp.watch(paths, opts, task);
process.on('SIGINT', function() {
// chokidar 3 is async and needs .then()
// chokidar 2 (gulp glob-watcher depends on chokidar2)
watcher.close();
done();
});
return watcher;
}
And then instead of something like this...
function taskWatchJavascript() {
var watchOpts = {delay: 1000};
return gulp.watch('src/**/*.js', watchOpts, taskBuildJavascript);
}
...you do this...
function taskWatchJavascript(done) {
var watchOpts = {delay: 1000};
return gulpWatchTidy ('src/**/*.js', watchOpts, taskBuildJavascript, done);
}
Our utility function takes the gulp async callback and handles the SIGINT (CTRL-C) to clean up or stop the watcher (or whatever) and then let gulp know we are done.
The Did you forget to signal async completion? error then goes away because we cleaned up our forever tasks and let gulp know they were all done.
Related
I've not used Gulp before, so I'm not migrating an old gulpfile to Gulp 4. I'm just trying to get a few basic tasks set up. However, regardless of the method I use to signal async completion as well documented in this post I still get the "Did you forget to signal async completion?" error.
'use strict';
/*
=====
PLUGINS
=====
*/
var gulp = require('gulp'),
plumber = require('gulp-plumber');
// sass
var sass = require('gulp-sass');
sass.compiler = require('node-sass');
// js
var concat = require('gulp-concat'),
uglify = require('gulp-uglify');
/*
=====
TASKS
=====
*/
gulp.task('sass', function() {
return gulp.src('./lib/**/*.scss')
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest('./lib/build'))
})
/*
=====
EXPORT TASKS
=====
*/
exports.sass = sass;
With this super barebones setup shouldn't it work? Doesn't return gulp.src return a stream? What am I missing?
Node 10.15.0,
Gulp CLI 2.2.0,
Gulp 4.0.1
Try
// exports.sass = sass;
exports.sass = gulp.series('sass');
or
exports.sass = 'sass';
You are using the task version (gulp.task) so you need the quotes.
If you were using the function version (recommended):
function sass2 () {
return gulp.src('./lib/**/*.scss')
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest('./lib/build'))
};
then
exports.sass = sass2;
works fine. Note that you have a var sass already so you would have to name your function sass2 to something other than just sass again of course.
I'm using gulp to compile and minify my SASS in an ASP.NET5 website to use as a CDN. I've written a watcher that watches my *.scss files, it then uses dependencies to compile the scss and then concat/minify it into one style.min.css.
The issue I'm having is that the longer the project is open, or at least with each sequential run of the gulp tasks, they get considerably longer to the point where I had only been developing for about 20 minutes, and the compile task was taking 15+ second, and the minify was taking 25+ seconds. The very first run of each takes about 1 or 2 milliseconds so I'm really lost as to what's going on.
Here's my watcher:
gulp.task('Watch:Sass', function () {
watch(paths.cdn.sassLoc, { verbose: true }, function () {
gulp.start("Min:css");
});
});
Here is the Min:css task:
gulp.task("Min:css", ["Compile:Sass"], function () {
return gulp.src([paths.cdn.cssLoc, "!" + paths.cdnMinCssLoc + "**/*.*"])
.pipe(concat(paths.cdn.cssDest))
.pipe(cssmin())
.pipe(plumber({
handleError: function (err) {
console.log(err);
this.emit('end');
}
}))
.pipe(gulp.dest("."));
});
And here is the Compile:Sass task that is being injected as a dependency:
gulp.task('Compile:Sass', function () {
gulp.src(paths.cdn.imagesLoc)
.pipe(gulp.dest(paths.webroot + '/css/min/images'));
return gulp.src(paths.cdn.sassLoc).pipe(plumber({
handleError: function (err) {
console.log(err);
this.emit('end');
}
}))
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest(paths.cdn.sassDest));
});
Could it be because of my use of returns?
Any help would be greatly appreciated as I have to restart Visual Studio every half hour or so which gets extremely frustrating!
I'm sure I've included everything relevant, but please ask if you require any more info.
In this case it was in fact my return statements that were in the wrong place.
In order to fix the issue, I had to take off the return on the gulp.src(...) and I had to add a return true to the end of the Compile:Sass gulp task. I also took the return off of the Min:Css task completely and now my compile task takes ~1.5ms and my min task takes ~600us.
Here's the final Compile:Sass task:
gulp.task('Compile:Sass', function () {
gulp.src(paths.cdn.imagesLoc)
.pipe(gulp.dest(paths.webroot + '/css/min/images'));
gulp.src(paths.cdn.sassLoc).pipe(plumber({
handleError: function (err) {
console.log(err);
this.emit('end');
}
}))
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest(paths.cdn.sassDest));
return true;
});
Matt, you have a concat on one of your "Min:css" task pipes.
gulp.task("Min:css", ["Compile:Sass"], function () {
return gulp.src([paths.cdn.cssLoc, "!" + paths.cdnMinCssLoc + "**/*.*"])
.pipe(concat(paths.cdn.cssDest))
...
...
.pipe(gulp.dest("."));
});
Make sure that you are not infinitely concatenating that same css over and over and over again. I can't tell what your paths.cdn.cssLoc points to. That would account for longer and longer processing time. You can call that a leak if you want.
I want to run a command but after a task finishes in grunt.
uglify: {
compile: {
options: {...},
files: {...}
}
?onFinish?: {
cmd: 'echo done!',
// or even just a console.log
run: function(){
console.log('done!');
}
}
},
Either run a command in shell, or even just be able to console.log. Is this possible?
Grunt does not support before and after callbacks, but next version could implement events that would work in the same way, as discussed in issue #542.
For now, you should go the task composition way, this is, create tasks for those before and after actions, and group them with a new name:
grunt.registerTask('newuglify', ['before:uglify', 'uglify', 'after:uglify']);
Then remember to run newuglify instead of uglify.
Another option is not to group them but remember to add the before and after tasks individually to a queue containing uglify:
grunt.registerTask('default', ['randomtask1', 'before:uglify', 'uglify', 'after:uglify', 'randomtask2']);
For running commands you can use plugins like grunt-exec or grunt-shell.
If you only want to print something, try grunt.log.
The grunt has one of the horrible code that I've ever seen. I don't know why it is popular. I would never use it even as a joke. This is not related to "legacy code" problem. It is defected by design from the beginning.
var old_runTaskFn = grunt.task.runTaskFn;
grunt.task.runTaskFn = function(context, fn, done, asyncDone) {
var callback;
var promise = new Promise(function(resolve, reject) {
callback = function (err, success) {
if (success) {
resolve();
} else {
reject(err);
}
return done.apply(this, arguments);
};
});
something.trigger("new task", context.name, context.nameArgs, promise);
return old_runTaskFn.call(this, context, fn, callback, asyncDone);
}
You can use callback + function instead of promise + trigger. This function will request the new callback wrapper for new task.
I'm trying to pass the configuration values returned from the server(zookeeper) into compass (cdnHost, environment, etc) and seem to be having a hard time using the right approach.
I looked at ways to pass around args from one task to another on this page as a starting point
http://gruntjs.com/frequently-asked-questions#how-can-i-share-parameters-across-multiple-tasks
module.exports = function(grunt) {
grunt.initConfig({
compass: {
dist: {
//options: grunt.option('foo')
//options: global.bar
options: grunt.config.get('baz')
}
},
...
grunt.registerTask('compassWithConfig', 'Run compass with external async config loaded first', function () {
var done = this.async();
someZookeeperConfig( function () {
// some global.CONFIG object from zookeeper
var config = CONFIG;
// try grunt.option
grunt.option('foo', config);
// try config setting
grunt.config.set('bar', config);
// try global
global['baz'] = config;
done(true);
});
});
...
grunt.registerTask('default', ['clean', 'compassWithConfig', 'compass']);
I also tried calling the compass task directly, and it made no difference.
grunt.task.run('compass');
Any insights would be greatly appreciated. (e.g. way to use initConfig and have the value be available).
Thanks
When you write:
grunt.initConfig({
compass: {
dist: {
options: grunt.config.get('baz')
}
}
... grunt.config is called right away, and returns the value of baz as it is right now. Altering it (later) in another task simply won't get picked-up.
How to solve that?
#1: update compass.dist.options instead of updating baz
grunt.registerTask('compassWithConfig', 'Run compass with external async config loaded first', function () {
var done = this.async();
someZookeeperConfig( function () {
// some global.CONFIG object from zookeeper
var config = CONFIG;
grunt.config.set('compass.dist.options', config);
done();
});
});
Now, running task compassWithConfig first, then task compass will get the result you expect.
#2: wrap-up compass task execution in order to abstract away config mapping
grunt.registerTask('wrappedCompass', '', function () {
grunt.config.set('compass.dist.options', grunt.config.get('baz'));
grunt.task.run('compass');
});
// Then, you can manipulate 'baz' without knowing how it needs to be mapped for compass
grunt.registerTask('globalConfigurator', '', function () {
var done = this.async();
someZookeeperConfig( function () {
// some global.CONFIG object from zookeeper
var config = CONFIG;
grunt.config.set('baz', config);
done();
});
});
Finally, running task globalConfigurator then wrappedCompass will get you to the result.
I'm playing around with the idea of using Meteor as a backend for creating a static html generator (with an admin UI).
I want that, when triggered, a new file will be created on the public folder and instantly reviewed by the author on an iframe embed in the html.
The file gets created but two side effects happen:
The server is restarted.
The file is cached - so the user can't see that the change took place.
Any ideas?
if (Meteor.is_client) {
Template.hello.events = {
'click input' : function () {
Meteor.call('makeFile', 'filename.html', function(error, result){
alert(result);
});
//window.location = '/filename.txt';
// template data, if any, is available in 'this'
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
};
}
if (Meteor.is_server) {
var fs = __meteor_bootstrap__.require('fs');
Meteor.startup(function () {
// code to run on server at startup
});
Meteor.methods({
'makeFile': function(fileName) {
/*
fs.unlink("public/"+fileName, function (err) {
if (err) throw err;
console.log('successfully deleted ');
});
*/
fs.writeFile("public/"+fileName, "<html><body><h1>test</h1></body></html>", function(err) {
if(err) {
console.log(err);
} else {
console.log("The file was saved! "+ fileName);
}
});
return fileName;
}
});
}
I think the meteor server restarts whenever you change any file in a meteor subdirectory. So to prevent this don't write your files within the meteor app directory, make a created_files directory one level up or something. This is a good idea anyway, to separate the generated files from the code that's generating them.
Easiest way to show the content in the admin interface is probably to write it to the database (as opposed to trying to watch for directory changes). No sense re-inventing the wheel, since Meteor is designed to watch for db updates.
Create a collection (at the top, so it's on client & server):
GeneratedFiles = new Meteor.Collection("generated_files");
Then assign the file contents to a variable before writing them and in your fs.writeFile callback:
if(err) {
console.log(err);
} else {
GeneratedFiles.insert({name: fileName, contents: fileContents});
console.log("The file was saved! "+ fileName);
}
And then auto-publish the file contents to template in the client code (not sure why you want to use an iframe, seems like a div would work fine. But either way would probably work).
Hope that helps. And good luck, I think meteor is a great framework for this kind of thing. Most of the admin interfaces I use are still entirely synchronous and just annoying to work with.