Gulp webp function not respecting watch command - asynchronous

Ok, so I am using the typical image minification process. I have functions for jpg, pngs and I have a separate function to convert those to webp.
I have the following gulpfile (only the relevant parts are here):
function clean(done) {
rimraf(folder.public_img, done);
}
function images() {
return gulp.src([folder.preimages+'/**/*.{gif,png,jpg,svg}'])
.pipe(cache(imagemin([
imageminPngquant({
speed: 1,
quality: [0.95, 1]
}),
imageminZopfli({
more: true
}),
imageminMozjpeg({
quality: 65
}),
])))
.pipe(gulp.dest(folder.public_img));
}
gulp.task('webp', () =>
gulp.src([folder.preimages+'/**/*.{gif,png,jpg}'])
.pipe(webp({
quality: 65,
method: 6
}))
.pipe(gulp.dest(folder.public_img))
);
function serve() {
gulp.watch(folder.preimages+"/**/*.{gif,png,jpg,svg}", images);
gulp.watch(folder.preimages+"/**/*.{gif,png,jpg}", webp);
}
gulp.task('clean', clean);
gulp.task('images', images);
gulp.task('serve', gulp.series('clean', 'images', 'webp', serve));
gulp.task('default', gulp.series('clean', 'images', 'webp', serve));
So, basically when I run gulp the first time, all the processes run fine and the webp images are generated. BUT, if I add a new image, the images() function runs, but for some reason the webp() function doesn't execute, it's as if it doesn't see changes, and I have it configured the same way as images.
I tried two alternatives:
I added the webp process directly inside the images() function, but that way my JPG and PNG files were directly converted to webp and I didn't have fallback images.
I tried to create the webp function with the same format as the others function webp() { ... }, but when I tried to run gulp it showed this error:
The following tasks did not complete: default,
Did you forget to signal async completion?
So I found that I could use the format above gulp.task('webp', () =>... and that works, but doesn't respect the watch function. I think it could be related to the function to name assignation in the end of the file, but I am not that familiar with the syntax.
What should I change so that it watches correctly?

I was able to recreate your code and get it to work as follows:
In creating your gulp tasks, something goofy seems to be happening because you've created a series of tasks to run with your gulp.task('serve'). Instead, you can write out your tasks like this:
gulp.task(clean);
gulp.task(images);
gulp.task(serve);
gulp.task('default', gulp.series(clean, images, webp, serve));
You may want to call your webp function something else - I was getting a conflict because of also importing gulp-webp as const = webp. You also don't need to put quotation marks around the name of the function in the task and then also reference it in the task again. So gulp.task(clean) works just fine. You don't need gulp.task('clean', clean).
You can probably also write your default task as follows:
gulp.task('default', gulp.series(clean, serve);
This had the same effect for me as also including the images and webp tasks in the series. This setup worked for me. Running gulp, it first ran the clean function to remove the public_img folder, then ran images followed by webp. It worked up the initial run as well as when I added a new image to the preimages folder.
Update
Here's the complete gulpfile I got to work:
const gulp = require('gulp');
const imagemin = require('gulp-imagemin');
const rimraf = require('rimraf');
const webp = require('gulp-webp');
const folder = {
preimages: 'src/images',
public_img: 'src/public_img',
};
function clean(done) {
rimraf(folder.public_img, done);
}
function images() {
return gulp
.src([folder.preimages + '/**/*.{gif,png,jpg,svg}'])
.pipe(imagemin())
.pipe(gulp.dest(folder.public_img));
}
function webpTask() {
return gulp
.src([folder.preimages + '/**/*.{gif,png,jpg}'])
.pipe(webp())
.pipe(gulp.dest(folder.public_img));
}
function serve() {
gulp.watch(folder.preimages + '/**/*.{gif,png,jpg,svg}', images);
gulp.watch(folder.preimages + '/**/*.{gif,png,jpg}', webpTask);
}
gulp.task('clean', clean);
gulp.task('images', images);
gulp.task('default', gulp.series('clean', 'images', webpTask, serve));
Notes: I removed the gulp serve task. I couldn't get that to work under any configuration. I think you are running into a conflict by naming both the task and the function serve, although I'm not 100% positive. I changed the webp task name to webpTask, as I was getting a conflict by naming both the webp import and the webp function webp. I didn't include any of the webp() or imagemin() configuration options, but that shouldn't matter.
You should also be able to remove the calls for both the images and webpTask functions in the gulp default task, as running the serve function should trigger both of those.
This setup worked for me both when adding new images to my preimages folder while the gulp default task was running as well as when restarting the gulp default task.
FYI: I'm using gulp version 4.0.1., but I don't think that should make a difference for this.

Related

How do I compile Typescript source files into same directory, recursively, using a Gulp task?

I need to compile new .ts files into the same source directory recursively for each.
They're all sitting in different folders because I'm converting a project to use TypeScript.
I'm using a Gulp Task right now to try figure it out myself however I'd much appreciate it if someone has already worked out how to do it.
My code looks something like this
gulp.task('typescript', function () {
return gulp.src('src/**/*')
.pipe(ts({
target: 'ES6',
outDir: 'Directory'
}))
.pipe(gulp.dest('.'));
});
I figured it out...
Solution:
gulp.task('typescript', function () {
return gulp.src('src/**/*')
.pipe(ts({
outDir: 'directory',
target: 'es6',
pretty: true,
listFiles: true
}))
.pipe(gulp.dest('src/'));
});

Gulp Task: CleanCSS and Paths

I've created a task with Gulp that is supposed to:
Join multiple CSS files;
Minify + remove unnecessary CSS;
Fix paths for url() directives and others;
Generate source maps;
My current code for this is:
var gulp = require("gulp"),
concat = require("gulp-concat"),
cleanCSS = require("gulp-clean-css"),
sourcemaps = require("gulp-sourcemaps");
var styleList = [
"Resources/Include/ionicons/css/ionicons.css",
"Resources/base.css",
"Resources/extra.css",
];
gulp.task("deploy-css", function() {
gulp.src(styleList)
.pipe(sourcemaps.init())
.pipe(concat("style.min.css"))
.pipe(cleanCSS({
debug: true,
compatibility: "ie8",
keepSpecialComments : 0,
target: "Resources/",
relativeTo: "Resources/"
})
)
.pipe(sourcemaps.write())
.pipe(gulp.dest("Resources/"))
});
url() path example, taken from file Resources/Include/ionicons/css/ionicons.css:
#font-face { font-family: "Ionicons"; src: url("../fonts/ionicons.eot?v=2.0.0");
This is my current file structure:
./Resources/style.min.css // -> Final processed file
./Resources/base.css
./Resources/extras.css
./Resources/Include/ // -> Original CSS files with URL (installed via Bower)
Test folder: https://dl.dropboxusercontent.com/u/2333896/gulp-path-test.zip (install and then run with gulp deploy-css).
Almost everything works as expected, except for when CSS files include references to images or fonts using the url() option. After running the task (and style.min.css created) those references are broken - no change was made to the paths found on the original CSS files.
Isn't cleanCSS supposed to check where the referenced files are and fix the paths automatically? Aren't the options target and relativeTo used to control that?
How can I fix this? Thank you.
I managed to fix the issue, my main problems were a misplaced concat operation breaking gulp-clean-css rebase feature and wrong target and relativeTo options. Apparently I didn't think much about the previous workflow.
var gulp = require("gulp"),
concat = require("gulp-concat"),
cleanCSS = require("gulp-clean-css"),
sourcemaps = require("gulp-sourcemaps");
var styleList = [
"Resources/Include/ionicons/css/ionicons.css",
"Resources/base.css",
"Resources/extra.css",
"Resources/Include/teste/base.css"
];
gulp.task("deploy-css", function() {
gulp.src(styleList)
.pipe(sourcemaps.init())
.pipe(cleanCSS({
compatibility: "ie8",
keepSpecialComments : 0,
target: "Resources",
relativeTo: ""
})
)
.pipe(concat("style.min.css", {newLine: ""}))
.pipe(sourcemaps.write())
.pipe(gulp.dest("Resources"))
});
This new workflow works as:
Optimize all individual CSS files - including rebasing urls;
Contact individual optimized files into the final file - (note newLine: "" avoids line breaks in the file);
Write the file.

Gulp - TypeError: Cannot assign to read only property 'cwd' of bower / bootstrap.css

I'm just getting to grips with Gulp.js and this is the only error I can't seem to get around.
I have install bootstrap via bower, and I'm trying to minify the bootstrap css.
Here's my gulp task;
gulp.task('minifycss', function() {
gulp.src('www/css/animate.css', 'www/bower_components/bootstrap/dist/css/bootstrap.css')
.pipe(minifycss({compatibility: 'ie8'}))
.pipe(gulp.dest('www-deploy/css/'));
});
However when I run my main gulp task. It states that the file is "read only"
Karls-MacBook-Pro:cmp-2015 karltaylor$ gulp
[13:48:50] Using gulpfile ~/Documents/web/cmp-2015/gulpfile.js
[13:48:50] Starting 'sassMin'...
[13:48:50] Finished 'sassMin' after 12 ms
[13:48:50] Starting 'minifycss'...
[13:48:50] 'minifycss' errored after 508 μs
[13:48:50] TypeError: Cannot assign to read only property 'cwd' of www/bower_components/bootstrap/dist/css/bootstrap.css
at Object.gs.createStream (/Users/karltaylor/Documents/web/cmp-2015/node_modules/gulp/node_modules/vinyl-fs/node_modules/glob-stream/index.js:19:46)
at Object.gs.create (/Users/karltaylor/Documents/web/cmp-2015/node_modules/gulp/node_modules/vinyl-fs/node_modules/glob-stream/index.js:68:42)
at Gulp.src (/Users/karltaylor/Documents/web/cmp-2015/node_modules/gulp/node_modules/vinyl-fs/lib/src/index.js:33:23)
at Gulp.<anonymous> (/Users/karltaylor/Documents/web/cmp-2015/gulpfile.js:35:7)
at module.exports (/Users/karltaylor/Documents/web/cmp-2015/node_modules/gulp/node_modules/orchestrator/lib/runTask.js:34:7)
at Gulp.Orchestrator._runTask (/Users/karltaylor/Documents/web/cmp-2015/node_modules/gulp/node_modules/orchestrator/index.js:273:3)
at Gulp.Orchestrator._runStep (/Users/karltaylor/Documents/web/cmp-2015/node_modules/gulp/node_modules/orchestrator/index.js:214:10)
at /Users/karltaylor/Documents/web/cmp-2015/node_modules/gulp/node_modules/orchestrator/index.js:279:18
at finish (/Users/karltaylor/Documents/web/cmp-2015/node_modules/gulp/node_modules/orchestrator/lib/runTask.js:21:8)
at module.exports (/Users/karltaylor/Documents/web/cmp-2015/node_modules/gulp/node_modules/orchestrator/lib/runTask.js:60:3)
Things I've tried:
Reinstalling bootstrap
Moving the actual bootstrap.css file to my css file, however it does exactly the same thing.
EDIT: SOLVED = To use multiple sources you have to put them in an array.
Source: https://github.com/gulpjs/gulp/blob/master/docs/recipes/using-multiple-sources-in-one-task.md
Like #topleft mentioned, it was error in the syntax from using multiple files in my gulp.src. To use multiple sources you need to put them in an array.
Example:
var gulp = require('gulp');
var concat = require('gulp-concat');
gulp.task('default', function() {
return gulp.src(['foo/*', 'bar/*'])
.pipe(concat('result.txt'))
.pipe(gulp.dest('build'));
});
I had this issue when I tried to output 2 css files and I didn't use array [].
Instead of:
gulp.src('www/css/animate.css', 'www/bower_components/bootstrap/dist/css/bootstrap.css')
.pipe(minifycss({compatibility: 'ie8'}))
.pipe(gulp.dest('www-deploy/css/'));
});
Use this:
gulp.src(['www/css/animate.css', 'www/bower_components/bootstrap/dist/css/bootstrap.css'])
.pipe(minifycss({compatibility: 'ie8'}))
.pipe(gulp.dest('www-deploy/css/'));
});
Explanation:
gulp.src(['./file1.scss', './file2.scss']);
alternatively you could also try with
return gulp.src('www/**/*.css')
it should add every .css in your directories if thats the wanted effect

How to Gulp-Watch Multiple files?

I have something like this:
gulp.task('default', ['css', 'browser-sync'] , function() {
gulp.watch(['sass/**/*.scss', 'layouts/*.css'], function() {
gulp.run('css');
});
});
but it does not work, because it watches two directories, the sass and the layouts directory for changes.
How do I make it work, so that gulp watches anything that happens inside those directories?
gulp.task('default', ['css', 'browser-sync'] , function() {
gulp.watch(['sass/**/*.scss', 'layouts/**/*.css'], ['css']);
});
sass/**/*.scss and layouts/**/*.css will watch every directory and subdirectory for any changes to .scssand .css files that change. If you want to change that to any file make the last bit *.*
You can write a watch like this.
gulp.task('watch', function() {
gulp.watch('path/to/file', ['gulp task name for css/scss']);
gulp.watch('path/to/file', ['gulp task name for js']);
});
This way you can set up as many tasks as you want via the file path of what you want to watch followed by the name of the task you created. Then you can write your default like this:
gulp.task('default', ['gulp task name for css/scss', 'gulp task name for js']);
If you want to simply watch for various file changes, then just watch files using glob like *.css in your task.
One problem that has arisen for multiple people (including me) is that adding a gulp.filter outside of the task causes gulp.watch to fail after the first pass. So if you have something like this:
var filter = gulpFilter(['fileToExclude.js'])
gulp.task('newTask', function(){ ...
Then you need to change it to:
gulp.task('newTask', function(){
var filter = gulpFilter(['fileToExclude.js'])
The filter has to be included in the task function. Hope that helps someone.
This works for me (Gulp 4):
function watchSass() {
return gulp.watch(sassGlob, { ignoreInitial: false }, buildCss)
}
function watchImages() {
return gulp.watch(imagesGlob, copyImages)
}
exports.watch = gulp.parallel(watchSass, watchImages)
#A.J Alger's answer worked for me when using Gulp v3.x.
But starting with Gulp 4, The following appears to work for me.
Notice that each task has to return a value or call "done()". The main task in this example is 'watchSrc' which in parallel calls the other tasks.
gulp.task('watchHtml', function(){
return watch('src/**/*.html', function () {
gulp.src('src/**/*')
.pipe(gulp.dest(BUILD_DIR))
})
})
gulp.task('watchJS', function(){
return watch('src/**/*.js', 'devJS')
})
gulp.task('watchCSS', function(){
return watch(['src/**/*.css', 'src/**/*.scss'], 'buildStyles')
})
gulp.task('watchSrc', gulp.parallel('watchHtml', 'watchJS', 'watchCSS'), function(done)
{
done()
})
As of gulp 4, this is another option:
const { watch, series, parallel } = require('gulp');
function watchTask(cb) {
// this will execute all task on any changes
watch(['src/**/*'],
series(parallel(jsTask, htmlTask, assetTask),
));
// this will run specific task based on file type or folder
watch(['src/**/*.js'], series(jsTask));
watch(['src/**/*.html'], series(htmlTask));
watch(['assets/**/*'], series(assetTask));
}
exports.default = series(parallel(jsTask, htmlTask, assetTask), watchTask);
If you convert your tasks into functions
function task1(){
return gulp...
...
}
There are then 2 useful methods you can use:
GULP.SERIES will run the tasks synchronously
gulp.task('default', gulp.series(task1,task2));
GULP.PARALLEL will run them asynchronously
gulp.task('default', gulp.parallel(task1,task2));

How can I skip a grunt task if a directory is empty

I'm using grunt-contrib's concat and uglify modules to process some javascript. Currently if src/js/ is empty, they will still create an (empty) concat'd file, along with the minified version and a source map.
I want to task to detect if the src/js/ folder is empty before proceeding, and if it is, then the task should skip (not fail). Any ideas how to do this?
The solution may not be the prettiest, but could give you an idea. You'll need to run something like npm install --save-dev glob first. This is based on part of the Milkshake project you mentioned.
grunt.registerTask('build_js', function(){
// get first task's `src` config property and see
// if any file matches the glob pattern
if (grunt.config('concat').js.src.some(function(src){
return require('glob').sync(src).length;
})) {
// if so, run the task chain
grunt.task.run([
'trimtrailingspaces:js'
, 'concat:js'
, 'uglify:yomama'
]);
}
});
A gist for comparison: https://gist.github.com/kosmotaur/61bff2bc807b28a9fcfa
With this plugin:
https://www.npmjs.org/package/grunt-file-exists
You can check file existence. (I didn't try, but the source looks like supporting grunt expands. (*, ** ...)
For example like this::
grunt.initConfig({
fileExists: {
scripts: ['a.js', 'b.js']
},
});
grunt.registerTask('conditionaltask', [
'fileExists',
'maintask',
]);
But maybe if the file doesn't exist it will fail with error instead of simple skip.
(I didn't test it.)
If this is a problem you can modify a bit the source of this plugin to run the related task if the file exists:
The config:
grunt.initConfig({
fileExists: {
scripts: ['a.js', 'b.js'],
options: {tasks: ['maintask']}
},
});
grunt.registerTask('conditionaltask', [
'fileExists',
]);
And you should add this:
grunt.task.run(options.tasks);
In this file:
https://github.com/alexeiskachykhin/grunt-file-exists/blob/master/tasks/fileExists.js
after this line:
grunt.log.ok();
Maybe this is just a more up-to-date answer as the others are more than a year old, but you don't need a plugin for this; you can use grunt.file.expand to test if files matching a certain globbing pattern exist.
Update of #Kosmotaur's answer (path is just hard-code here though for simplicity):
grunt.registerTask('build_js', function(){
// if any file matches the glob pattern
if (grunt.file.expand("subdir/**/*.js").length) { /** new bit here **/
// if so, run the task chain
grunt.task.run([
'trimtrailingspaces:js'
, 'concat:js'
, 'uglify:yomama'
]);
}
});

Resources