Upgrading to Gulp 4 from Gulp 3 - javascript

I've attempted to update the gulpfile below to Gulp v4 from v3 but am still getting an error: AssertionError [ERR_ASSERTION]: Task never defined: client
Not sure what I am missing but realise that the functions may not be written correctly. The tasks in series are correct I believe: gulp.task('build', gulp.series('client', 'sass')); for example.
// gulpfile.js
var gulp = require('gulp'),
concat = require('gulp-concat'),
browserify = require('gulp-browserify'),
rename = require('gulp-rename'),
nodemon = require('gulp-nodemon'),
sass = require('gulp-sass');
gulp.task('build', gulp.series('client', 'sass'));
gulp.task('watch', gulp.series('client-watch', 'sass-watch'));
gulp.task('server', function () {
nodemon({
script: 'server/index',
ext: 'js json'
});
});
gulp.task('client', function () {
gulp.src('client/js/main.js')
.pipe(browserify({
transform: ['hbsfy'],
extensions: ['.hbs']
}))
.pipe(rename('hearthclone.js'))
.pipe(gulp.dest('./client/build'));
});
gulp.task('client-watch', function () {
gulp.watch('client/js/**/*.js', ['client']);
});
gulp.task('sass', function () {
gulp.src('client/**/*.scss')
.pipe(sass())
.pipe(concat('style.css'))
.pipe(gulp.dest('client/assets/css'));
});
gulp.task('sass-watch', function () {
gulp.watch('client/**/*.scss', ['sass']);
});
gulp.task('default', gulp.series('build', 'server', 'watch'));

When you use the gulp.task form of defining tasks (rather that as functions) then you cannot refer to those tasknames until after they have been declared. That would be a forward reference (see below). So just put your
gulp.task('build', gulp.series('client', 'sass'));
gulp.task('watch', gulp.series('client-watch', 'sass-watch'));
after all those tasks have been defined. I recommend right before the default task line.
See https://gulpjs.com/docs/en/api/series#forward-references
Forward references
A forward reference is when you compose tasks, using string
references, that haven't been registered yet. This was a common
practice in older versions, but this feature was removed to achieve
faster task runtime and promote the use of named functions.
In newer versions, you'll get an error, with the message "Task never
defined", if you try to use forward references. You may experience
this when trying to use exports for your task registration and
composing tasks by string. In this situation, use named functions
instead of string references.

Related

how to uglify javascript classes?

I'm writing basic javascript using the classes syntax and trying to uglify it with gulp, but getting this error:
events.js:72
throw er; // Unhandled 'error' event
I simply used the example code from mozilla's website:
class Polygon {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
And nothing special about my gulp file - works perfectly for any non-class js:
// Include gulp
var gulp = require('gulp');
// Include Our Plugins
var jshint = require('gulp-jshint');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var rename = require('gulp-rename');
// Lint Task
gulp.task('lint', function() {
return gulp.src('js/*.js')
.pipe(jshint())
.pipe(jshint.reporter('default'));
});
// Concatenate & Minify JS
gulp.task('scripts', function() {
return gulp.src('js/*.js')
.pipe(concat('all.js'))
.pipe(gulp.dest('dist/js'))
.pipe(rename('all.min.js'))
.pipe(uglify())
.pipe(gulp.dest('dist/js'));
});
// Watch Files For Changes
gulp.task('watch', function() {
gulp.watch('js/*.js', ['lint', 'scripts']);
gulp.watch('scss/*.scss', ['sass']);
});
// Default Task
gulp.task('default', ['lint', 'scripts', 'watch']);
At the time, I resolved this with a workaround by transpiling to ECMA 5 with Babel.
Later I discovered it was not a problem with my Node environment as the comments led me to believe, but in fact it was because the default uglify branch used in gulp-uglify doesn't support ECMA 6 yet.
The correct solution was to instead use the harmony branch of UglifyJS2 or to use the npm wrapper uglify-js-harmony.

Gulp doesn't write result to file

I have a gulpfile.js that looks like this:
var gulp = require('gulp'),
concat = require('gulp-concat'),
less = require('gulp-less'),
minifyCSS = require('gulp-minify-css'),
uglify = require('gulp-uglify'),
autoprefixer = require('gulp-autoprefixer');
gulp.task('less', function () {
gulp.src(['less/*.less'])
.pipe(less())
.pipe(concat('everything.css'))
.pipe(autoprefixer())
.pipe(minifyCSS())
.pipe(gulp.dest('asset/'));
});
gulp.task('js', function () {
gulp.src(['javascript/*.js'])
.pipe(concat('everything.js'))
.pipe(uglify())
.pipe(gulp.dest('asset/'));
});
gulp.task('watch', function() {
gulp.watch('javascript/*.js', ['js']);
gulp.watch('less/*.less', ['less']);
});
As I was developing, the less task stopped working out of the blue. The console doesn't throw any errors, everything looks just like it should but the everything.css file does not get updated. I even removed the file and it doesn't get created again.
I'm compeltely clueless to what's going on, even rebooted my computer as a sanity check but it didn't help.
EDIT:
So apparently it's the less pipe. But why did it start to fail suddenly?
Try updating your less files a bit, to simplify. This is a common design pattern, and it works very well for me:
/*main.less*/
#import "somefile.less"
#import "some/deep/nested/file.less"
In your other less files, those will be where you keep your actuall less code.
Then in your gulpfile.js you will want to modify a bit:
...
gulp.task('less', function () {
gulp.src(['less/main.less']) //only pull in your main.less file
.pipe(less())
.pipe(autoprefixer())
.pipe(minifyCSS())
.pipe(gulp.dest('asset/'));
});
...
gulp.task('watch', function() {
gulp.watch('javascript/*.js', ['js']);
gulp.watch('less/*.less', ['less']); //this remains the same - you want to watch for all changes
});
Note: gulp.watch will not find new files - you will need to restart when you have new files, or use something like gulp-watch.
This will solve all kinds of headaches for you. Hope it helps!

Is it possible to assign a variable in a gulp task before running dependencies?

I'm trying to conditionally pipe a file stream based on the value of a variable, as a way to define two separate build environments (ie. development and production).
Some tasks can be run individually with a command-line flag like so:
gulp scripts --env production
And will then do some production-only pipeline steps:
gulp.task('scripts', function() {
var jsFilter = filter(['*.js']),
appFiles;
return gulp.src(appFiles)
.pipe(jsFilter)
.pipe(concat('application-build.js'))
.pipe(gulpif(env === 'production', uglify()))
.pipe(size())
.pipe(gulpif(env === 'production', gulp.dest('dist/js'), gulp.dest('tmp/js')))
.pipe(browserSync.reload({ stream: true }));
});
I have a build task that calls a number of other tasks as dependencies (including this scripts task for instance). I want this build task to assign a variable (env, in this case) before running task dependencies. Which means that this:
gulp.task('build', ['scripts', 'styles', 'otherstuff'], function() {
env = 'production';
}
doesn't work, because the dependencies are run before the body of the task.
I currently have it implemented with gulp.start:
gulp.task('build', function() {
env = 'production';
gulp.start('scripts');
});
But the .start method isn't actually part of gulp's public API - it comes from Orchestrator - and isn't intended to be used for anything. Plus, the equivalent method gulp.run was deprecated from the API awhile ago.
So I'm wondering - is there another way I could assign a variable in a task before running its dependencies?
(Or maybe there's a better way to to implement something like build environments in gulp?)
THE RIGHT WAY
I disagree with #Justin. Defining an environmental variable with a task is a hackjob of an idea. This is better done with gutil.env this way.
gulp --env prod task
gulp.task( 'myTask', () => { console.log( gutil.env.env ) } )
Now from this point, you have gulp.env.env set.
Or, alternatively you can do like this example in this ticket.. which addresses this from the developers of Gulp which first suggest to use an environmental variable, but provide this idiom..
function js(shouldMinify) {
return gulp.src('./js/*.js')
.pipe(concat('app.js'))
.pipe(gulpif(shouldMinify, uglify()))
.pipe(gulp.dest('js'));
});
gulp.task('develop', function () {
shouldMinify = false;
return js(shouldMinify);
});
gulp.task('build', function () {
shouldMinify = true;
return js(shouldMinify);
});
That same developer (phated) always says to use env...
Not to mention, you should control this type of logic with environment variables or command line flags. - phated
Presumably, he's referring to the use of gutil.noop() in gulp-util's docs:
// gulp should be called like this :
// $ gulp --type production
gulp.task('scripts', function() {
gulp.src('src/**/*.js')
.pipe(concat('script.js'))
// LOOK BELOW: if we don't send to uglify, we push to noop stream.
.pipe(gutil.env.type === 'production' ? uglify() : gutil.noop())
.pipe(gulp.dest('dist/'));
});
You could create a task specifically to set the environment and run it before your other tasks.
gulp.task('set-production', function() {
env = 'production';
});
// Doesn't quite work because tasks are run in parallel
gulp.task('build', ['set-production', 'scripts', 'styles', 'otherstuff']);
The problem here is that your tasks will be run in parallel, meaning the set-production task may be run after the other tasks. You can solve this problem with the run-sequence package.
var runSequence = require('run-sequence');
gulp.task('build', function(callback) {
runSequence('set-production', ['scripts', 'styles', 'otherstuff'], callback);
});
This will run the set-production task first, then run the scripts, styles, and otherstuff tasks in parallel.

Pipe vendor js files from Bower and own js files into unique stream in Gulp

I'd like to grab my vendor js files from bower dependencies, and, along with my own js files, pipe through some other tasks and concat them into one .js file. And then do the same for Sass files.
How could I achieve that? I tried this, but it doesn't work:
gulp.task('scripts', function () {
return gulp.src([
mainBowerFiles(gulpFilter('*.js')),
'app/js/*.js'
])
.pipe(uglify())
.pipe(concat('app.js'))
.pipe(gulp.dest('./dist/js'));
});
It throws this error:
TypeError: Arguments to path.resolve must be strings
But it works if I have this, for example:
gulp.src([
'app/js/etc.js',
'app/js/main.js'
])
..and in the following case, it throws a different error (but no error without uglify()):
gulp.src(mainBowerFiles(gulpFilter('*.js')))
error:
events.js:72
throw er; // Unhandled 'error' event
How do I better debug this? Should I separate vendor / own js files in different streams?
update
I'm working now with two streams, and merging them later on with event-stream, like this:
gulp.task('scripts', function () {
var jsFilter = gulpFilter('*.js');
var vendorFiles = gulp.src(mainBowerFiles()) // don't read
.pipe(jsFilter)
.pipe(concat('vendor.js'));
var appFiles = gulp.src('app/js/*.js')
.pipe(jshint())
.pipe(jshint.reporter('default'))
.pipe(concat('app.js'));
return es.concat(vendorFiles, appFiles)
.pipe(uglify())
.pipe(concat('app.js'))
.pipe(gulp.dest('./dist/js'));
});
It works great, but I'm not able to handle the order of the files. Obviously I'd like that the vendor code come before my code at the destination's app.js, but that's doesn't happen. I created a thread about that here.
Solved it with gulp-event-stream and gulp-order. Intrigues me a little that gulp-order is trending downward at https://www.npmjs.org, though.
gulp.task('scripts', function () {
var jsFilter = gulpFilter('*.js');
var vendorFiles = gulp.src(mainBowerFiles())
.pipe(jsFilter)
.pipe(concat('vendor.js'));
var appFiles = gulp.src('app/js/*.js')
.pipe(jshint())
.pipe(jshint.reporter('default'))
.pipe(concat('app.js'));
return eventStream.concat(vendorFiles, appFiles)
.pipe(order([
"vendor.js",
"app.js"
]))
.pipe(concat('app.js'))
.pipe(uglify())
.pipe(gulp.dest('./dist/js'));
});
mainBowerFiles() returns an array. gulp.src is expecting an array of strings, not nested arrays, which is likely the cause of the first problem. You could try the following instead:
gulp.src(mainBowerFiles(gulpFilter('*.js')).push('app/js/*.js'))
I'm not sure what's causing the unhandled error event, but you could try using gulp-debug to get more information.

gulp error handling for dependent task

Please find the content of the gulpfile.js as below.
The task uglify depends on the task jshint. Currently when I run gulp, both the tasks get executed, irrespective of the outcome of the jshint task. I don't want the uglify task to get executed when there are 'jshint' error(s).
In other words, when ever there are dependent tasks, I don't want the subsequent tasks to get executed, if there are error detected by the preceding task.
Is it possible in gulp?
var gulp = require('gulp');
var jshint = require('gulp-jshint');
var uglify = require('gulp-uglify');
gulp.task('jshint', function () {
return gulp.src(['assets/js/**/*.js'])
.pipe(jshint('.jshintrc'))
.pipe(jshint.reporter('jshint-stylish'));
});
gulp.task('uglify', ['jshint'], function() {
return gulp.src('assets/js/**/*.js')
.pipe(uglify())
.pipe(gulp.dest('assets-min/js/'));
});
gulp.task('default', ['jshint', 'uglify']);
Please refer the below console output - not desired. Though there had been jshint error, the uglify task ran successfully.
I have also created a GitHub repository with the boilerplate code for the above mentioned.
Please find the same at #sarbbottam/gulp-workflow.
Console out of the undesired workflow
Console out of the expected workflow
For JSHint, there is a built-in reporter for this purpose, fail. If an error occurs, it will stop your gulp process.
You just have to redefine your task like :
gulp.task('jshint', function () {
return gulp.src(['assets/js/**/*.js'])
.pipe(jshint('.jshintrc'))
.pipe(jshint.reporter('jshint-stylish'))
.pipe(jshint.reporter('fail'))
})
With other tasks, you can add an error callback on it and exit the process to prevent subsequent tasks to run.
Here is an example with ngmin (cause uglify is hard to break, but it will be the same) :
function handleError (err) {
console.log(err.toString())
process.exit(-1)
}
gulp.task('min', function () {
return gulp.src(['assets/js/**/*.js'])
.pipe(ngmin())
.on('error', handleError)
})
To complement Aperçu's answer, if you don't want gulp to just exit (because you have watcher running) then you can do the following:
gulp.task('min', function(done) {
return gulp.src(['assets/js/**/*.js'])
.pipe(ngmin())
.on('error', done);
});
This will prevent the next task that depends on this one to run but your watchers will still be running.

Categories