Run eslint before watchify runs - gulp

I'm adding watchify to our build process but I want to put a precondition to watchify running, and that is that the file(s) that changed pass our linting step (which is using ESLint).
I was thinking of doing this:
function runBrowserify(watch){
var babel = babelify.configure({
optional: ['es7.objectRestSpread']
});
var b = browserify({
entries: './app/main.js',
debug: true,
extensions: ['.jsx', '.js'],
cache: {},
packageCache: {},
fullPaths: true
})
.transform(babel);
if(watch) {
// if watch is enable, wrap this bundle inside watchify
b = watchify(b);
b.on('update', function(ids) {
//run the linting step
lint(ids);
//run the watchify bundle step
gutil.log(gutil.colors.blue('watchify'), 'Started');
bundleShare(b);
});
b.on('time', function (time) {
gutil.log(gutil.colors.blue('watchify'), 'Finished', 'after', gutil.colors.magenta(time), gutil.colors.magenta('ms'));
});
}
bundleShare(b);
}
function bundleShare(b) {
b.bundle()
.pipe(source('main.min.js'))
.pipe(gulp.dest('./dist'));
}
function lint(glob) {
return gulp.src(glob)
.pipe(eslint())
.pipe(eslint.format())
.pipe(eslint.failOnError());
}
The problem is that the linting step is async so it doesn't finish before the bundling would be done (it also throws so I probably need to use plumber to stop it from terminating the watch step).
So how would I make a precondition before I call bundleShared?

I was able to do this using the closure method I mentioned above. I also moved my Browserify and Watchify code into helper functions that each build could take advantage of.
gulpfile.js (partial)
gulp.task('build:dev', buildDev);
gulp.task('lint:js', lintJS);
function lintJS(callback) {
return gulp.src(['src/**/*.js', 'src/**/*.jsx', '!src/js/vendor/**/*.*',])
.pipe(eslint())
.pipe(eslint.format())
.pipe(eslint.failAfterError());
}
function buildDev(callback) {
var bundler = getBundler('src/js/app.jsx', { debug: true }, callback);
var watcher = getWatcher(bundler, rebundle);
function rebundle() {
lintJS(callback);
return watcher.bundle()
.pipe(source('bundle.min.js'))
.pipe(buffer())
.pipe(gulp.dest('dist/js'));
}
rebundle();
// Call watch methods here, i.e.: watchHTML()
return callback();
}
/****************************** Helper functions ******************************/
/**
* Gets the default Browserify bundler used by all builds.
*
*
* #param path A string representing where Browserify should start from
* #param options An Object containing options for the bundler
* #param callback The Gulp callback function from the calling task
* #return A basically configured Browserify bundler
*/
function getBundler(path, options, callback) {
var bundler = browserify(path, { debug: options.debug, cache: {}, packageCache: {} });
bundler.transform(babelify);
bundler.on('log', gutil.log);
bundler.on('error', gutil.log.bind(gutil.colors.red, 'Browserify Error'));
return bundler;
}
/**
* Gets the default Watchify watcher used by dev builds. By default, the watcher
* will rebundle the Browserify package when an update occurs.
*
* #param bundle The Browserify bundler object
* #param rebundle A function to perform when Watchify detects a code update
* #return A basically configured Watchify watcher
*/
function getWatcher(bundle, rebundle) {
var watcher = watchify(bundle);
watcher.on('update', rebundle);
return watcher;
}
For my test and prod builds, I don't use Watchify (and thus have no rebundle() method) so I keep the 'lint:js' task as a dependency:
gulp.task('build:test', ['lint:js'], buildTest);
gulp.task('build:prod', ['lint:js'], buildProd);

Related

Gulpfile.js Throws exception for wrench default implemented method of readdirSyncRecursive while calling require function of node.js

i am new to Gulp and specially Gulp 4. here i have my gulpfile.js
/**
* Welcome to your gulpfile!
* The gulp tasks are splitted in several files in the gulp directory
* because putting all here was really too long
*/
'use strict';
var gulp = require('gulp');
var wrench = require('wrench');
/**
* This will load all js or coffee files in the gulp directory
* in order to load all gulp tasks
*/
wrench.readdirSyncRecursive('./gulp').filter(function(file) {
return (/\.(js|coffee)$/i).test(file);
}).map(function(file) {
require('./gulp/' + file);
});
/**
* Default task clean temporaries directories and launch the
* main optimization build task
*/
gulp.task('default', ['clean'], function () {
gulp.start(['build']);
});
function done() {
}
while trying to run => gulp build i am facing following Exception
AssertionError [ERR_ASSERTION]: Task function must be specified
at Gulp.set [as _setTask] (C:\Users\ahsan\Documents\dev-work\smsvoltapp\node_modules\undertaker\lib\set-task.js:10:3)
at Gulp.task (C:\Users\ahsan\Documents\dev-work\smsvoltapp\node_modules\undertaker\lib\task.js:13:8)
at Object.<anonymous> (C:\Users\ahsan\Documents\dev-work\smsvoltapp\gulp\build.js:28:6)
at Module._compile (node:internal/modules/cjs/loader:1159:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1213:10)
at Module.load (node:internal/modules/cjs/loader:1037:32)
at Module._load (node:internal/modules/cjs/loader:878:12)
at Module.require (node:internal/modules/cjs/loader:1061:19)
at require (node:internal/modules/cjs/helpers:103:18)
at C:\Users\ahsan\Documents\dev-work\smsvoltapp\gulpfile.js:19:3 {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: false,
expected: true,
operator: '=='
}
It seems the node.js function require at line 19:3 is not upto the mark and valid for gulp4
Gulp version:
CLI version: 2.3.0
Local version: 4.0.2
npm -v => 8.19.2
Please help

Aurelia bundle all jspm dependencies automatically

I'm trying to automatically bundle all jspm dependencies --so I don't have to maintain a manual list-- with a gulp task:
var gulp = require('gulp');
var bundler = require('aurelia-bundler');
var package = require('../../package.json');
var jspmDeps = Object.keys(package.jspm.dependencies);
var config = {
force: true,
baseURL: '.',
configPath: './system.config.js',
bundles: {
"output/jspm": {
"includes": jspmDeps,
"options": {
"minify": false,
"inject" : true
}
}
}
};
gulp.task('build-jspm', function () {
return bundler.bundle(config);
});
Unfortunately, this isn't picking up and bundling all sub-dependencies of the modules included in the bundle. Quite a few dependencies are dynamically loaded by aurelia, which don't get detected during bundling.
Is there a way to force aurelia-bundler (or jspm-cli) to bundle the entire dependency tree from jspm?
[Update] Turns out jspm's bundler has the same behavior, in that it won't bundle modules unless it detects a call to import 'some-sub-dependency'; there are plans to trace dynamic loading at some point

gulp: passing dependent task return stream as parameter

I'm trying to create two gulp tasks, and I'd like the second task to take the first one's output stream and keep applying plugins to it.
Can I pass the first task's return value to the second task?
The following doesn't work:
// first task to be run
gulp.task('concat', function() {
// returning a value to signal this is sync
return
gulp.src(['./src/js/*.js'])
.pipe(concat('app.js'))
.pipe(gulp.dest('./src'));
};
// second task to be run
// adding dependency
gulp.task('minify', ['concat'], function(stream) {
// trying to get first task's return stream
// and continue applying more plugins on it
stream
.pipe(uglify())
.pipe(rename({suffix: '.min'}))
.pipe(gulp.dest('./dest'));
};
gulp.task('default', ['minify']);
Is there any way to do this?
you can't pass stream to other task.
but you can use gulp-if module to skip some piped method depending on conditions.
var shouldMinify = (0 <= process.argv.indexOf('--uglify'));
gulp.task('script', function() {
return gulp.src(['./src/js/*.js'])
.pipe(concat('app.js'))
.pipe(gulpif(shouldMinify, uglify())
.pipe(gulpif(shouldMinify, rename({suffix: '.min'}))
.pipe(gulp.dest('./dest'));
});
execute task like this to minify
gulp script --minify
I am looking for the same solution, but ended up just chaining functions.
This is quite flexible.
Please share if anyone has better solution without using additional packages.
function concatenate() {
return gulp
.src(['./src/js/*.js'])
.pipe(concat('app.js'));
}
function minify() {
return this
.pipe(uglify())
.pipe(rename({suffix: '.min'}));
}
function output() {
return this.pipe(gulp.dest('./src'));
}
gulp.task('concat', function() {
return output.call(concatenate());
});
gulp.task('minify', function() {
return output.call(minify.call(concatenate()));
});
gulp.task('default', ['minify']);

Gulp to combine bower files

I'm aware this has probably been asked before, but I can't seem to be able to ask Google the right questions to find what I need. So I'm possibly thinking about this in the wrong way.
Basically, I need to know if there is a way to use Gulp with Bower so that all css files in subdirectories under bower_components are combined into one styles.css, all js files in subdirectories under bower_components are combined into one scripts.js. Kind of how assetic works in symfony2 to combine assets into single files. Does each 'built' file in each bower_componets subdirectory have to be linked to manually (in the Gulp config file), or is it more common to loop through them programatically?
Thanks
Would something like the below help? It loops through all css files in my 'src' directory and spits out one css file in the 'dist' folder. It does the same for my js files:
// Config
var requireJsRuntimeConfig = vm.runInNewContext(fs.readFileSync('src/app/require.config.js') + '; require;');
requireJsOptimizerConfig = merge(requireJsRuntimeConfig, {
out: 'scripts.js',
baseUrl: './src',
name: 'app/startup',
paths: {
requireLib: 'bower_modules/requirejs/require'
},
include: [
'requireLib',
'components/nav-bar/nav-bar',
'components/home-page/home',
'text!components/about-page/about.html'
],
insertRequire: ['app/startup'],
bundles: {
// If you want parts of the site to load on demand, remove them from the 'include' list
// above, and group them into bundles here.
// 'bundle-name': [ 'some/module', 'another/module' ],
// 'another-bundle-name': [ 'yet-another-module' ]
}
});
// Discovers all AMD dependencies, concatenates together all required .js files, minifies them
gulp.task('js', function () {
return rjs(requireJsOptimizerConfig)
.pipe(uglify({ preserveComments: 'some' }))
.pipe(gulp.dest('./dist/'));
});
// Concatenates CSS files, rewrites relative paths to Bootstrap fonts, copies Bootstrap fonts
gulp.task('css', function () {
var bowerCss = gulp.src('src/bower_modules/components-bootstrap/css/bootstrap.min.css')
.pipe(replace(/url\((')?\.\.\/fonts\//g, 'url($1fonts/')),
appCss = gulp.src('src/css/*.css'),
combinedCss = es.concat(bowerCss, appCss).pipe(concat('css.css')),
fontFiles = gulp.src('./src/bower_modules/components-bootstrap/fonts/*', { base: './src/bower_modules/components-bootstrap/' });
return es.concat(combinedCss, fontFiles)
.pipe(gulp.dest('./dist/'));
});
there is a way and its honestly really simple. you can install "gulp-run" to your npm devDependencies and then use the run to execute bower install.
var gulp = require('gulp'),
del = require('del'),
run = require('gulp-run'),
sass = require('gulp-sass'),
cssmin = require('gulp-minify-css'),
browserify = require('browserify'),
uglify = require('gulp-uglify'),
concat = require('gulp-concat'),
jshint = require('gulp-jshint'),
browserSync = require('browser-sync'),
source = require('vinyl-source-stream'),
buffer = require('vinyl-buffer'),
reactify = require('reactify'),
package = require('./package.json'),
reload = browserSync.reload;
/**
* Running Bower
*/
gulp.task('bower', function() {
run('bower install').exec();
})
/**
* Cleaning lib/ folder
*/
.task('clean', function(cb) {
del(['lib/**'], cb);
})
/**
* Running livereload server
*/
.task('server', function() {
browserSync({
server: {
baseDir: './'
}
});
})
/**
* sass compilation
*/
.task('sass', function() {
return gulp.src(package.paths.sass)
.pipe(sass())
.pipe(concat(package.dest.style))
.pipe(gulp.dest(package.dest.lib));
})
.task('sass:min', function() {
return gulp.src(package.paths.sass)
.pipe(sass())
.pipe(concat(package.dest.style))
.pipe(cssmin())
.pipe(gulp.dest(package.dest.lib));
})
/**
* JSLint/JSHint validation
*/
.task('lint', function() {
return gulp.src(package.paths.js)
.pipe(jshint())
.pipe(jshint.reporter('default'));
})
/** JavaScript compilation */
.task('js', function() {
return browserify(package.paths.app)
.transform(reactify)
.bundle()
.pipe(source(package.dest.app))
.pipe(gulp.dest(package.dest.lib));
})
.task('js:min', function() {
return browserify(package.paths.app)
.transform(reactify)
.bundle()
.pipe(source(package.dest.app))
.pipe(buffer())
.pipe(uglify())
.pipe(gulp.dest(package.dest.lib));
})
/**
* Compiling resources and serving application
*/
.task('serve', ['bower', 'clean', 'lint', 'sass', 'js', 'server'], function() {
return gulp.watch([
package.paths.js, package.paths.jsx, package.paths.html, package.paths.sass
], [
'lint', 'sass', 'js', browserSync.reload
]);
})
.task('serve:minified', ['bower', 'clean', 'lint', 'sass:min', 'js:min', 'server'], function() {
return gulp.watch([
package.paths.js, package.paths.jsx, package.paths.html, package.paths.sass
], [
'lint', 'sass:min', 'js:min', browserSync.reload
]);
});
what is really beautiful with this setup I just posted is this is making a custom gulp run called "serve" that will run your setup with a development server (with live reload and much better error intelligence) all you have to do is go to your directory and type "gulp serve" and it'll run bower install and build everything for you. obviously the folder structure is different so you will need to make some modifications, but hopefully this shows how you can run bower with gulp :)
Something like the below was what I was looking for, except I don't like having to add the paths manually. This is why I prefer something like CommonJS for Javascript. I definitely remember seeing a way in which CSS files were picked up automatically from the bower_components folder, I believe it was in the Wordpress Roots project (based on the settings/overrides in bower.json).
gulp.task('css', function () {
var files = [
'./public/bower_components/angular-loading-bar/build/loading-bar.css',
'./public/bower_components/fontawesome/css/font-awesome.css'
];
return gulp.src(files, { 'base': 'public/bower_components' })
.pipe(concat('lib.css'))
.pipe(minifyCss())
.pipe(gulp.dest('./public/build/css/')
);
});
gulp.task('js-lib', function () {
var files = [
'public/bower_components/jquery/dist/jquery.js',
'public/bower_components/bootstrap-sass/assets/javascripts/bootstrap.js'
];
return gulp.src(files, { 'base': 'public/bower_components/' })
.pipe(sourcemaps.init())
.pipe(uglify({ mangle: false }))
.pipe(concat('lib.js'))
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest('./public/build/js/')
);
});
You can use gulp-main-bower-files library to add the main files of packages in .bower.json instead to declare them manually

Gulp: passing parameters to task from watch declaration

The problem: I want to maintain 'collections' of files. This will help with build times, and flexibility. for example, everytime i edit my app.js file, I don't want to re-compile all my twitter bootstrap files.
I can certainly achieve this with 2 tasks and 2 watch declarations - the problem is that the tasks are identical save for the files array. Ideally I would like to pass through these as parameters in the watch declaration Is there a way to do something like the following psuedo-code?:
var files = {
scripts: [
'www/assets/scripts/plugins/**/*.js',
'www/assets/scripts/main.js',
],
vendor: [
'vendor/jquery/dist/jquery.js',
'vendor/jqueryui/ui/jquery.ui.widget.js',
'vendor/holderjs/holder.js'
],
};
...
gulp.task('js', ['lint'], function (files, output) {
return gulp.src(files)
.pipe(debug())
.pipe(concat(output))
.pipe(uglify({outSourceMap: true}))
.pipe(gulp.dest(targetJSDir))
.pipe(notify('JS minified'))
.on('error', gutil.log)
});
...
gulp.watch('scripts/**/*.js', ['lint', 'js'], files.scripts, 'app.min.js');
gulp.watch('vendor/**/*.js', ['lint', 'js'], files.vendor, 'vendor.min.js');
Flipping round another way: is to namespace the watch declaration that called the task? That way I could check which watch triggered the task, and conditional those things within the task itself.
the problem is that the tasks are identical save for the files array.
I believe lazypipe (see its gh page) is well
suited to your wants. This was an interesting problem. I'm going to try to answer both what I think you're asking about (which is satisfied by lazypipe) as well as what I think you're probably thinking about or would end up thinking about if you got past the parameterization of pipes issue.
One aspect of what we want is that we don't want to rerun jshint on files that haven't changed. Additionally, we want to keep it DRY, and we want to pick up new files in addition to changed ones.
This is tested and works for me:
var gulp = require('gulp');
var $ = require('gulp-load-plugins')();
var es = require('event-stream');
var lazypipe = require('lazypipe');
var gutil = require('gulp-util');
var path = require('path');
var files = {
scripts: ['src/**/*.js'],
vendor: ['vendor/**/*.js']
};
// sets up a lazy pipe that does jshint related stuff
function getJsMultiPipe(name) {
return lazypipe()
.pipe($.jshint)
.pipe($.jshint.reporter, 'jshint-stylish')
// if you don't want to fail on style errors remove/comment this out:
.pipe($.jshint.reporter, 'fail');
}
// sets up a lazy pipe that does concat and post-concat stuff
function getJsCombinedPipe(groupName, outfile) {
return lazypipe()
.pipe($.concat, outfile)
.pipe($.uglify, {outSourceMap: true})
.pipe(gulp.dest, 'build')
.pipe($.notify, {message: groupName + ' JS minified', onLast: true});
}
// sets up a pipe for the initial build task, combining the above two pipes
function getBuildPipe(groupName, outfile) {
return gulp.src(files[groupName])
.pipe(getJsMultiPipe(groupName)())
.pipe(getJsCombinedPipe(groupName, outfile)());
}
// sets up a watch pipe, such that only the changed file is jshinted,
// but all files are included in the concat steps
function setWatchPipe(groupName, outfile) {
return $.watch({
glob: files[groupName],
name: groupName,
emitOnGlob: false,
emit: 'one'
}, function(file, done) {
return file
.pipe($.debug({title: 'watch -- changed file'}))
.pipe(getJsMultiPipe(groupName)())
// switch context
.pipe(gulp.src(files[groupName]))
.pipe($.debug({title: 'watch -- entire group'}))
.pipe(getJsCombinedPipe(groupName, outfile)())
.pipe($.debug({title: 'watch -- concatted/source-mapped'}))
.pipe($.notify({message: 'JS minified', onLast: true}));
});
}
// task to do an initial full build
gulp.task('build', function() {
return es.merge(
getBuildPipe('scripts', 'app.min.js'),
getBuildPipe('vendor', 'vendor.min.js')
)
.pipe($.notify({message: 'JS minified', onLast: true}));
});
// task to do an initial full build and then set up watches for
// incremental change
gulp.task('watch', ['build'], function(done) {
setWatchPipe('scripts', 'app.min.js');
setWatchPipe('vendor', 'vendor.min.js');
done();
});
My dependencies look like:
"devDependencies": {
"jshint-stylish": "^0.1.5",
"gulp-concat": "^2.2.0",
"gulp-uglify": "^0.2.1",
"gulp-debug": "^0.3.0",
"gulp-notify": "^1.2.5",
"gulp-jshint": "^1.5.3",
"gulp": "^3.6.0",
"gulp-load-plugins": "^0.5.0",
"lazypipe": "^0.2.1",
"event-stream": "^3.1.1",
"gulp-util": "^2.2.14",
"gulp-watch": "^0.5.3"
}
EDIT: I just glanced at this again and I notice these lines:
// switch context
.pipe(gulp.src(files[groupName]))
Be aware that I believe the gulp.src API has changed since I wrote this, and that it currently doesn't switch the context when you pipe things into gulp.src, therefore this spot might require a change. For newer versions of gulp, I think what will happen is that you will be adding to the context, instead and presumably losing a small bit of efficiency.
You could write a wrapper function for tasks to capture parameters and pass it to the task. E.g. (with the help of the lodash library):
// We capture the options in this object. We use gulp.env as a base such that
// options from cli are also passed to the task.
var currentOpts = _.clone(gulp.env);
// Here we define a function that wraps a task such that it can receive
// an options object
function parameterized(taskFunc) {
return function() {
taskFunc.call(null, currentOpts);
}
}
// Here we create a function that can be used by gulp.watch to call
// a parameterized task. It can be passed an object of "task" : {options} pairs
// and it will return a task function that will capture these options
// before invoking the task.
function withArgs(tasks) {
return function() {
_.each(tasks, function (opts, task) {
currentOpts = _.extend(currentOpts, opts);
gulp.run(task);
currentOpts = _.clone(gulp.env);
});
}
}
var files = {
scripts : [ "src/**/*.js"],
vendor : ["vendor/**/*.js"
};
// We pass the task function to parameterized. This will create a wrapper
// function that will pass an options object to the actual task function
gulp.task("js", parameterized(function(opts) {
gulp.src(files[opts.target])
.pipe(concat(opts.output));
}));
gulp.task("watch", function() {
// The withArgs function creates a watch function that invokes
// tasks with an options argument
// In this case it will invoke the js task with the options object
// { target : "scripts", output : "scripts.min.js" }
gulp.watch(files.scripts, withArgs({
js : {
target : "scripts",
output : "scripts.min.js"
}
}));
gulp.watch(files.vendor, withArgs({
js : {
target : "vendor",
output : "vendor.min.js"
}
}));
});
I've faced the same problem - how to pass parameters to a gulp task. It's wierd that this feature is not builtin (it's such a common task to build, for instance, two versions of a package, parametrized task seems like a very DRY solution).
I wanted to make it as simple as possible, so my solution was to dynamically create tasks for an each possible parameter. It works ok if you have a small number of exactly defined values. It won't work for wide range values, like ints or floats.
The task definition is wrapped in a function that takes desired parameter and the parameter is appended to the task's name (with '$' between for convenience).
Your code could look like this:
function construct_js(myname, files, output) {
gulp.task('js$' + myname, ['lint'], function () {
return gulp.src(files)
.pipe(debug())
.pipe(concat(output))
.pipe(uglify({outSourceMap: true}))
.pipe(gulp.dest(targetJSDir))
.pipe(notify('JS minified'))
.on('error', gutil.log)
});
}
construct_js("app", files.scripts, 'app.min.js');
construct_js("vendor", files.vendor, 'vendor.min.js');
gulp.watch('scripts/**/*.js', ['lint', 'js$app']);
gulp.watch('vendor/**/*.js', ['lint', 'js$vendor']);
Or better, with a little change in the data definition, we invoke task generation in a loop (so if you add a new "version" in the configs array it will work right away:
var configs = [
{
name : "app",
output: 'app.min.js',
files: [ 'www/assets/scripts/plugins/**/*.js',
'www/assets/scripts/main.js',
]
},
{
name : "vendor",
output: 'vendor.min.js',
files: [ 'vendor/jquery/dist/jquery.js',
'vendor/jqueryui/ui/jquery.ui.widget.js',
'vendor/holderjs/holder.js'
]
}
];
function construct_js(taskConfig) {
gulp.task('js$' + taskConfig.name, ['lint'], function () {
return gulp.src(taskConfig.files)
.pipe(debug())
.pipe(concat(taskConfig.output))
.pipe(uglify({outSourceMap: true}))
.pipe(gulp.dest(targetJSDir))
.pipe(notify('JS minified'))
.on('error', gutil.log)
});
}
for (var i=0; i < configs.length; i++) {
construct_js(configs[i]);
}
If we use underscore for the last "for":
_(configs).each(construct_js);
I've used this approach in my project with good results.
I'd like to propose some alternatives. Suppose we have a task called build that we would like to conditionally uglify given a certain param.
The two approaches use two watches with a single build task.
Alternative #1:
You can use gulp-exec to fire up a task with parameters.
var exec = require('child_process').exec;
gulp.task('build', function(){
// Parse args here and determine whether to uglify or not
})
gulp.task('buildWithoutUglify' function(){
exec('gulp build --withoutUglify')
})
gulp.task('watch', function(){
gulp.watch(someFilePath, ['buildWithoutUglify'])
})
Please note that this approach is a bit slow since what it does is execute command line gulp.
Alternative #2:
Set a global variable:
var withUglify = false;
gulp.task('build', function(){
// Use something like ``gulp-if`` to conditionally uglify.
})
gulp.task('buildWithoutUglify' function(){
withUglify = true;
gulp.start('build');
})
gulp.task('watch', function(){
gulp.watch(someFilePath, ['buildWithoutUglify'])
})