I'm in the process of migrating from gulp#3.9.1 to gulp#4.0.2 and upgrading my gulp dependencies in the process. I have the following task in my gulpfile, where you can assume directories is just an array of directories I want to perform this operation on:
var gulp = require('gulp');
var ngAnnotate = require('gulp-ng-annotate'); //annotates dependencies in Angular components
var rev = require('gulp-rev'); //appends a hash to the end of file names to eliminate stale cached files
var revReplace = require('gulp-rev-replace');
var uglify = require('gulp-uglify'); // minimizes javascript files
var compressCss = require('gulp-minify-css');
var useref = require('gulp-useref'); // replaces style and script blocks in HTML files
var filter = require('gulp-filter');
var merge = require('merge-stream');
var sourcemaps = require('gulp-sourcemaps');
function minify() {
var tasks = directories.map(function (directory) {
var cssFilter = filter("**/all.min.css", {restore:true});
var jsAppFilter = filter("**/app.min.js", {restore:true});
var jsFilter = filter("**/*.js", {restore:true});
return gulp.src(dstBasePath + directory + "index.html", {allowEmpty: true})
.pipe(useref())
.pipe(cssFilter)
.pipe(compressCss({keepSpecialComments:false}))
.pipe(rev())
.pipe(cssFilter.restore)
.pipe(jsAppFilter)
.pipe(sourcemaps.init())
.pipe(ngAnnotate({add:true, single_quotes:true}))
.pipe(jsAppFilter.restore)
.pipe(jsFilter)
.pipe(uglify())
.pipe(rev())
.pipe(jsFilter.restore)
.pipe(revReplace())
.pipe(sourcemaps.write('.')) // sourcemaps need to be written to same folder for Datadog upload to work
.pipe(gulp.dest(dstBasePath + directory))
});
return merge(tasks);
}
Why would this result in the error "Did you forget to signal async completion?" from Gulp when running the task? Note that I'm using Gulp 4. I've tried passing a callback done to this task, and adding .addListener('end', done) to the final pipe, but this causes my merged stream to end prematurely (presumably when the first one ends). So perhaps one of these plugins is not signaling when it's completed, but how would you even get this to work otherwise? Thanks for any insight you can provide.
return merge(folders.map(function (folder) { // this has worked for me in the past
as has this form without merge
gulp.task('init', function (done) {
var zips = getZips(zipsPath);
var tasks = zips.map(function (zip) {
return gulp.src(zipsPath + "/" + zip)
.pipe(unzip({ keepEmpty: true }))
.pipe(gulp.dest(path.join("src", path.basename(zip, ".zip"))))
.on('end', function() { // this bit is necessary
done();
});
});
return tasks;
});
Gulp 4 requires that you signal async completion. There's some good information about it in this answer to a similar question:
Gulp error: The following tasks did not complete: Did you forget to signal async completion?
I had a similar case where I was returning a merged set of tasks, and I was able to resolve the error by making the function async and awaiting the merge. My case looked something like this:
gulp.task("build", async function () {
...
return await merge(tasks);
});
so I think you should be able to do something like
async function minify(){
...
return await merge(tasks);
}
Related
Is it possible with gulp v.4.0.0 to watch for files outside of the folder where gulpfile.js is located?
In older gulp it was possible to set file path to ../../someFile.css and watch for its changes, but in v4 it doesn't detect changes for the same path.
// snip....
let rootFolder = '..\..\root\';
// Watch function
let watchThis = (filePath, gulpTasks) => {
gulp.watch(filePath, gulp.series(gulpTasks))
.on('change', function(path) {
console.log(`${chalk.blue('Changed')}: ${chalk.yellow(path)}`);
})
.on('unlink', function(path) {
console.log(`${chalk.red('Deleted')}: ${chalk.yellow(path)}`);
});
}
// Watch task configuration
let watchFiles = () => {
watchThis([`${rootFolder}/style1.scss`, `${rootFolder}/style2.scss`], 'scss')
watchThis('./js/main.js', 'js')
}
// Final watch task
gulp.task('watch', gulp.series(
'development',
gulp.parallel(
watchFiles,
'startLiveServers'
)
));
// ...snip
Changes to files ['../../style1.scss', '../../style2.scss'] will not be detected, but they will be for './js/main.js'. Am I missing something?
Problem with new Gulp 4 watch task is in paths. Unlike watch task from Gulp 3, new version is unforgiving and requires correct path structure with correct paths separators.
So instead of using paths that may result in ..\\..\\root\\/style1.scss, we must convert paths to proper structure like ../../root/style1.scss.
This simple function helps and the rest is handled by gulp and nodejs
let fixPath = (oldPath) => {
const pathString = oldPath;
const fix = /\\{1,}|\/{1,}/;
return pathString.replace(new RegExp(fix, 'gm'), '/').replace(new RegExp(fix, 'gm'), '/')
}
I want to implement very simple chain of tasks in my project by Gulp:
Copy all files;
Replace some placeholders with values;
Minify some
files;
And for these purposes I have created gulpfile with such main task:
gulp.task(tasks.build, [
tasks.simplyCopy,
tasks.minifyXml,
tasks.minifyJs,
tasks.subst]);
It's a pretty simple and self describable.
Below I wrote full gulpfile.js:
var gulp = require('gulp');
var prettyData = require('gulp-pretty-data');
var uglify = require('gulp-uglify');
var renvy = require('gulp-renvy');
var tasks = {
simplyCopy: "simply-copy",
minifyXml: "minify-xml",
minifyJs: "minify-js",
subst: "renvy-subst",
build: "build"
};
// Collection of tasks
gulp.task(tasks.build, [tasks.simplyCopy, tasks.minifyXml, tasks.minifyJs,
tasks.subst]);
// By this task sources simply copy to the destination
var destination = 'dist/';
gulp.task(tasks.simplyCopy, function () {
gulp.src(['Source/**/*.*', '!Source/www/res/strings/*.*'], {base:
'Source/www'})
.pipe(gulp.dest(destination));
});
var stringsDestPath = 'dist/res/strings/';
var stringSrcPath = 'Source/www/res/strings/';
// By this task some xml files minify
gulp.task(tasks.minifyXml, [tasks.simplyCopy], function() {
gulp.src(stringSrcPath + '*.xml')
.pipe(prettyData({
type: 'minify',
preserveComments: true,
extensions: {
'xlf': 'xml',
'svg': 'xml'
}
}))
.pipe(gulp.dest(stringsDestPath))
});
var placeholder = {
'%version%': {'prod':'010.00', 'dev':'010.00'}
};
// By this task in some files placeholders replaces with value
gulp.task(tasks.subst, [tasks.minifyXml, tasks.minifyJs], function(){
return gulp.src(stringsDestPath + '*.*')
.pipe(renvy(placeholder, 'dev'))
.pipe(gulp.dest(stringsDestPath));
});
// By this task some js files minify
gulp.task(tasks.minifyJs, [tasks.simplyCopy], function () {
gulp.src(stringSrcPath + '*.js')
.pipe(uglify())
.pipe(gulp.dest(stringsDestPath))
});
But I have such unexpected behavior:
replacing of placeholders is not happening, but it's executes.
[16:29:51] Using gulpfile C:\PDDirectory\Workspace\src\some_workbench\User_Part\gulpfile.js
[16:29:51] Starting 'simply-copy'...
[16:29:51] Finished 'simply-copy' after 17 ms
[16:29:51] Starting 'minify-xml'...
[16:29:51] Finished 'minify-xml' after 7.49 ms
[16:29:51] Starting 'minify-js'...
[16:29:51] Finished 'minify-js' after 5.84 ms
[16:29:51] Starting 'renvy-subst'...
[16:29:51] Finished 'renvy-subst' after 28 ms
[16:29:51] Starting 'build'...
[16:29:51] Finished 'build' after 5.66 ?s
Task tasks.subst executed sepparetly works fine, but in a chain with other tasks, I see results of executing copy and minify only.
Why so?
Place tasks.subst has the sole dependency for tasks.build
gulp.task(tasks.build, [tasks.subst]);
Since tasks.subst requires all the others, the ordering should be correct and adding all the other tasks may lead to ordering problems.
From the gulp.task documentation:
Note: Are your tasks running before the dependencies are complete?
Make sure your dependency tasks are correctly using the async run
hints: take in a callback or return a promise or event stream.
To ensure that a task dependencies are fulfilled before executing it, Gulp needs each task to return a stream or a promise, or call the task function callback parameter.
In your case, the following task should just return the stream:
tasks.minifyXml
tasks.minifyJs
tasks.simplyCopy
For example:
gulp.task(tasks.minifyXml, [tasks.simplyCopy], function(done) {
// just return the task stream here
return gulp.src(stringSrcPath + '*.xml')
.pipe(prettyData({
// ...
}))
.pipe(gulp.dest(stringsDestPath));
});
or using the callback when it's not possible to return a stream:
gulp.task('somename', function(done) {
// async function which does not return a stream like other gulp functions
getFilesAsync(function(err, res) {
// pass any errors to the callback
if (err) return done(err);
var stream = gulp.src(res)
.pipe(minify())
.pipe(gulp.dest('build'))
.on('end', done); // use the callback when it's done
});
});
Cant seem to find my problem here. After I run Gulp, the all-css.min.css gets outputted to _build folder but the JS will not go! am I missing something? Cant seem to find what is making this not work.
var gulp = require('gulp');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var minifyHTML = require('gulp-minify-html');
var sourcemaps = require('gulp-sourcemaps');
var minifyCSS = require('gulp-minify-css');
var inlineCss = require('gulp-inline-css');
var rev = require("gulp-rev");
var del = require('del');
var jsBase = {
src: [
'/Scripts/Core/ko.bindinghandlers-1.0.0.js',
'/Scripts/Twitter/typeahead-0.10.2.js',
'/Scripts/LdCore/mobile-core.js',
'/Scripts/LDCore/Chat.js',
'/Scripts/unsure.js' // These have any unknown lines in them.
]
};
gulp.task('clean', function () {
del.sync(['_build/*'])
});
gulp.task('produce-css', function () {
return gulp.src(cssBase.src)
.pipe(minifyCSS({ keepBreaks: false }))
.pipe(concat('all-css.min.css'))
.pipe(gulp.dest('_build/'))
});
gulp.task('produce-minified-js', function () {
return gulp.src(jsBase.src)
//.pipe(sourcemaps.init())
//.pipe(uglify())
.pipe(concat('all.min.js'))
//.pipe(rev()) // adds random numbers to end.
//.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('_build/'));
});
gulp.task('default', ['clean'], function () {
gulp.start('produce-css', 'produce-minified-js');
});
According to Contra at this post, we shouldn't be using gulp.start.
gulp.start is undocumented on purpose because it can lead to
complicated build files and we don't want people using it
Bad:
gulp.task('default', ['clean'], function () {
gulp.start('produce-css', 'produce-minified-js');
});
Good:
gulp.task('default', ['clean','produce-css','produce-minified-js'], function () {
// Run the dependency chains asynchronously 1st, then do nothing afterwards.
});
It's totally legit to have nothing in the gulp.task, as what it's doing is running the dependency chains asynchronously & then terminating successfully.
You could also do the following:
gulp.task('default', ['clean','produce-css','produce-minified-js'], function (cb) {
// Run a callback to watch the gulp CLI output messages.
cb();
});
Since Gulp creates "Starting default" on the CLI, this would help to display "Finished default" in the CLI after everything else runs.
I want to check if the file in the pipe is .js or not (it could be .map, .html, ...). And if so, uglifying it before copying it in the correct path.
ʕ •́؈•̀) I've try something like this (which not working):
gulpfile.js
gulp.src(current + '/**/*', {base: current})
.pipe($.tap(function (file) {
if (path.extname(file.path) === '.js') {
return gulp.src(file.path)
.pipe($.uglify());
}
}))
.pipe(gulp.dest(destination + '/' + name));
But for now, the uglify seems to do nothing...
Is anyone have a clue on how to do this ? (╥﹏╥)
If you're open to using plugins there is one called gulp-filter that does what you're asking for. https://www.npmjs.com/package/gulp-filter
It would probably look something like this
var gulp = require('gulp');
var gulpFilter = require('gulp-filter');
gulp.task('default', function () {
// create filter instance inside task function
var jsfilter = gulpFilter('**/*.js', {restore: true});
return gulp.src(current + '/**/*', {base: current})
// filter a subset of the files
.pipe(jsFilter)
// run them through a plugin
.pipe($.uglify())
// bring back the previously filtered out files (optional)
.pipe(jsFilter.restore)
.pipe(gulp.dest(destination + '/' + name));
});
try using gulp-filter
something like
var filter = require('gulp-filter');
var jsFilter = filter('**/*.js');
gulp.src('*/*')
.pipe(jsFilter)
.pipe(uglify)
I just have a quick question there:
I am using Node.JS to write a commandline tool that validates JSON Files with JSON Schemas. So, now I have a problem that when wanting to get all the schemas, that I always get "undefined" for using a async function but otherwise only sync functions.
For this commandline tool async is NOT needed.
Could someone help me out and give me a hand on how to make it work just fine?
var getJSONSchemaFiles = function (dir) {
results2 = [];
var recursive = require('recursive-readdir');
recursive(dir, function (err, files) {
// Files is an array of filename
// console.log(files);
files.forEach(function (entry) {
if (entry.indexOf(".schema.json") > -1) {
results2.push(entry);
}
});
console.log(results2);
});
return results2;
};
I am using the npm "recursive-readdir" but I think that I do not even need a npm for this kind of thing?
Ok, this enumerates all files under the given path synchronously:
var fs = require('fs');
function recursiveReaddir(path) {
var stat = fs.lstatSync(path);
if(stat.isFile())
return [path];
if(!stat.isDirectory())
return [];
return [].concat.apply([], fs.readdirSync(path).map(function(fname) {
return recursiveReaddir(path + '/' + fname);
}));
}
Use glob module https://github.com/isaacs/node-glob. There is async and Sync methods like: glob.sync(pattern, [options]); and glob(pattern, [options], cb);
Example from their docs:
var glob = require("glob")
// options is optional
glob("**/*.js", options, function (er, files) {
// files is an array of filenames.
// If the `nonull` option is set, and nothing
// was found, then files is ["**/*.js"]
// er is an error object or null.
})