Using Jade features with gulp-changed? - javascript

When I make changes to .jade files I want to Gulp task run only for that file, not for all files. For that I'm using gulp-changed. It's working fine, until I make changes to files that affect to global layout, eg _header.jade, _layout.jade. When I make changes to that files nothing happens. All my layout files have _ before title. How can I solve this issue?
Here is my gulpfile some lines
gulp.task('jade', function() {
return gulp.src('dev/templates/**/!(_)*.jade')
.pipe(plumber({
errorHandler: onError
}))
.pipe(changed('public', {extension: '.html'}))
.pipe(jade({
pretty: true,
}))
.pipe(gulp.dest('public'))
.pipe(browserSync.reload({
stream: true
}));
});
gulp.task('watch', function() {
gulp.watch('dev/templates/**/*.jade', gulp.series('jade'));
});

First thing I would do is to refactor out your jade compilation task into a separate function. That allows you to parameterize your jade compilation so that you can run it on one or more files of your choice:
function compileJade(files) {
return gulp.src(files, {base:'dev/templates'})
.pipe(plumber({
errorHandler: onError
}))
.pipe(jade({
pretty: true,
}))
.pipe(gulp.dest('public'))
.pipe(browserSync.reload({
stream: true
}));
}
Your existing jade task now simply calls that function:
gulp.task('jade', function() {
return compileJade('dev/templates/**/!(_)*.jade');
});
If a changed file is a partial (starts with _) we need to be able to determine which other files are affected by that change. This is facilitated by the jade-inheritance library:
var JadeInheritance = require('jade-inheritance');
var path = require('path');
function isPartial(file) {
return path.basename(file).match(/^_.*/);
}
function findAffectedFiles(changedFile) {
return new JadeInheritance(changedFile, 'dev/templates', {basedir: 'dev/templates'})
.files
.filter(function(file) { return !isPartial(file); })
.map(function(file) { return 'dev/templates/' + file; })
}
Finally whenever a file changes we call the compileJade function for the affected files only:
gulp.task('watch', function() {
gulp.watch('dev/templates/**/*.jade').on('change', function(changedFile) {
return compileJade(isPartial(changedFile) ? findAffectedFiles(changedFile) : changedFile);
});
});

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'));
});

Optimizing gulp watch task

I'm new to gulp and I've got a couple of gulp questions that I hope are pretty easy (meaning I'll probably have one of those forehead smacking moments when I hear the answer)...
My gulpfile has a number of repetitive one-way copy tasks, and then I'm watching these separate tasks in the watch command, however I'm almost certain that the way I'm doing it is totally inefficient.
Also, I'm noticing some interesting behavior, the copy command for syncHtmlRootDir task works exactly as I hoped (it will delete files as necessary), but none of my other one way copy tasks will remove deleted files, and I'm guessing it's a pathing issue, but I'm stumped on it.
gulpfile.js
var gulp = require('gulp');
var browserSync = require('browser-sync').create();
var sass = require('gulp-sass');
var minifyCss = require('gulp-minify-css')
var uglify = require('gulp-uglify');
var newer = require('gulp-newer');
var path = require('path');
var del = require('del');
function handleError (error) {
console.log(error.toString())
this.emit('end')
}
//setup browerSync to serve from both src and dist directories.
gulp.task('browserSync', function() {
browserSync.init({
server: {
baseDir: ["./", "src"] // ./ signifies root of folder, allows to load files from dist and src folders.
},
})
});
//one way sync of root folder
gulp.task('syncHtmlRootDir', function(done) {
return gulp.src(['src/*.html'])
.pipe(newer('dist/'))
.pipe(gulp.dest('dist/'))
.pipe(browserSync.reload({
stream: true
}))
});
//one way sync of app folder
gulp.task('syncHtmlAppDir', function(done) {
return gulp.src(['src/app/**/*'])
.pipe(newer('dist/app/'))
.pipe(gulp.dest('dist/app/'))
.pipe(browserSync.reload({
stream: true
}))
});
//one way sync of image folder
gulp.task('syncImgDir', function(done) {
return gulp.src(['src/assets/img/**/*'])
.pipe(newer('dist/assets/img/'))
.pipe(gulp.dest('dist/assets/img/'))
.pipe(browserSync.reload({
stream: true
}))
});
//copy and compile SCSS code
gulp.task('compileSass', function() {
return gulp.src('src/assets/css/**/*.scss')
.pipe(sass())
.on('error', handleError)
.pipe(minifyCss())
.pipe(gulp.dest('dist/assets/css'))
.pipe(browserSync.reload({
stream: true
}))
});
//minify JS
gulp.task('uglifyJS', function() {
gulp.src('src/assets/js/**/*.js')
.pipe(uglify())
.on('error', handleError)
.pipe(gulp.dest('dist/assets/js'))
.pipe(browserSync.reload({
stream: true
}))
});
//watch tasks
gulp.task('watch', ['browserSync'], function() {
var rootDir = gulp.watch('src/*.html', ['syncHtmlRootDir']);
rootDir.on('change', function(ev) {
if(ev.type === 'deleted') {
del(path.relative('./', ev.path).replace('src','dist'));
}
});
var appDir = gulp.watch('src/app/**/*', ['syncHtmlAppDir']);
appDir.on('change', function(ev) {
if(ev.type === 'deleted') {
del(path.relative('./', ev.path).replace('src/app/','dist/app/'));
}
});
var imgDir = gulp.watch('src/assets/img/**/*', ['syncImgDir']);
imgDir.on('change', function(ev) {
if(ev.type === 'deleted') {
del(path.relative('./', ev.path).replace('src/assets/img/','dist/assets/img/'));
}
});
var jsDir = gulp.watch('src/assets/js/**/*', ['uglifyJS']);
jsDir.on('change', function(ev) {
if(ev.type === 'deleted') {
del(path.relative('./', ev.path).replace('src/assets/js/','dist/assets/js/'));
}
});
var cssDir = gulp.watch('src/assets/css/**/*', ['compileSass']);
cssDir.on('change', function(ev) {
if(ev.type === 'deleted') {
del(path.relative('./', ev.path).replace('src/assets/css/','dist/assets/css/'));
}
});
});
So I'm 1) looking to combine my repetitive copy tasks into fewer tasks 2)have the delete file function work on all copy tasks and 3) optimize my watch task function to reduce repetition.
p.s., I've also noticed that if I add new files to the watch folders, it won't "recognize" them until I restart the watch command, so my method of syncing isn't exactly bulletproof. =/
Your thoughts are most appreciated.
Thanks!

Continue piping for gulp tasks after returning

I have two gulp tasks I need to run, one for js, one for css. I need them to run differently for build-dev (not minified) and build-prod (minified).
To do this, I stripped out the minification and gulp.dest portions of the tasks for js and css and am instead returning gulp.src to the tasks build-prod or build-dev. These two tasks build-prod or build-dev are now responsible for manipulating the js and css tasks differently.
How can I return the tasks' gulp.src to build-prod or build-dev for continued piping?
Note: since it's a stream, it seems I'm returning the stream after the last pipe finishes for that task, then since it's asynchronous, why can't I chain pipes after gulp.src returns?
gulp.task('css', function() {
return gulp.src('css/**.css')
.pipe(autoprefixer('last 10 version'))
.pipe(concatCss('main.css'));
// .pipe(minifycss())
// .pipe(gulp.dest('build/css'));
});
gulp.task('js', ...
return gulp.src(['./js/**/*.js'])
.pipe(
... // Huge function that I don't want to duplicate
});
gulp.task('build-prod', function() {
gulp.run('css')
.pipe(minifycss())
.pipe(gulp.dest('build/css'));
gulp.run('scripts')
.pipe(minifyjs())
.pipe(gulp.dest('build/js'));
});
gulp.task('build-dev', function() {
gulp.run('css')
.pipe(gulp.dest('build/css'));
gulp.run('scripts')
.pipe(gulp.dest('build/js'));
});
Why not make a function you call in multiple places?
function cssTask() {
return gulp.src('css/**.css')
.pipe(autoprefixer('last 10 version'))
.pipe(concatCss('main.css'));
}
gulp.task('css', function () {
return cssTask();
});
gulp.task('build-prod', function () {
cssTask()
.pipe(minifycss())
.pipe(gulp.dest('build/css'));
});
gulp.task('build-dev', function () {
cssTask()
.pipe(gulp.dest('build/css'));
});
The best way is to use gulp conditionals: .pipe(gulpif(isProduction, minifycss()))
Gulp If is a great way to do it.
let isProduction = false;
...
gulp.task('css', function() {
return gulp.src('css/**.css')
.pipe(autoprefixer('last 10 version'))
.pipe(concatCss('main.css'))
.pipe(gulpif(isProduction, minifycss())) // conditional
.pipe(gulp.dest('build/css'));
});
gulp.task('build-prod', function() {
isProduction = true;
gulp.run('css');
});
gulp.task('build-dev', function() {
isProduction = false;
gulp.run('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.

Concatenated files put into main.js just keeps stacking instead of clearing the file then adding the code

I'm trying to compile all my scripts into a single main.js file which I can then link to my index file. Problem is that all my script files are being concatenated and then just added to the main.js file, so if I save 3 times, I will basically have 3 copies of all my scripts concatenated and put in the main.js file.
I would like to either delete the main.js file each time I save and then run the concatenation, or just clean the file before adding the contents. Now if I try to delete the file using the del module, I receive an error stating that I can't delete files out of the working directory without forcing this action. I would like to avoid forcing this if possible.
I feel that there must be a more elegant way of doing this..
Here's my script task:
// Concat and compile our JS into a minified dist file
gulp.task('scripts', function() {
return gulp.src('../app/public/assets/scripts/**/*.js')
.pipe(jshint())
.pipe(jshint.reporter('default'))
.pipe(concat('main.js'))
//.pipe(del(['../app/public/assets/scripts/main.js'])) <-- Doesn't work without forcing
.pipe(gulp.dest('../app/public/assets/scripts'))
.pipe(gulpif(flags.build, gulp.dest('../app/dist/assets/scripts')))
.pipe(rename({ suffix: '.min' }))
.pipe(uglify())
.pipe(gulpif(flags.build, gulp.dest('../app/dist/assets/scripts')))
.pipe(notify({ message: 'Finished compiling scripts' }));
});
I usually do something like this (very simplified :) ):
var PARAMS = {
destPath: 'build',
js: [
'src/js/test1.js',
'src/js/test2.js',
// ecc...
],
// other
};
var TARGETS = {
dest: 'main.js', // dest file
extra: [
// This files are inclued only in .bundle.min.js version
// extra file here
],
js: [
'src/js/test1.js',
'src/js/test2.js',
// ecc...
]
};
gulp.task('connect', function() {
return connect.server({
livereload: true,
host: '0.0.0.0',
port: 8000
});
});
gulp.task('reload', ['build'], function () {
return gulp.src(['sandbox/**/*.html'])
.pipe(connect.reload());
});
gulp.task('watch', ['connect', 'build'], function () {
var src = [];
// other
src = src.concat(PARAMS.js);
return gulp.watch(src, ['reload']);
});
gulp.task('clean', function (done) {
return del(['build'], done);
});
gulp.task('build', ['clean'], function () {
return gulp.src(target.js);
.pipe(concat(target.dest))
.pipe(gulp.dest(PARAMS.destPath)) // Plain
.pipe(uglify())
.pipe(rename({ extname: '.min.js' })) // Minified
.pipe(gulp.dest(PARAMS.destPath))
.pipe(addsrc(target.extra))
.pipe(order(target.extra))
.pipe(concat(target.dest))
.pipe(rename({ extname: '.bundled.min.js' })) // Bundled
.pipe(gulp.dest(PARAMS.destPath));
});
gulp.task('default', ['build']);

Categories