I am attempting to use some gulp plugins ( jscs, csscomb ) to style my code on the fly during dev time.
I'm having a problem with the gulp process running an infinite loop with the format task.
What's I believe to be happening:
start a serve task of some kind
an initial run is performed with all tasks to prep files for the staging server
a local staging server is started in parallel with a watch task
myfile.scss is updated by a developer
the gulp watcher starts the csscomb task
csscomb plugin changes the file and replaces it
the watcher task sees the change from the file replacement & starts the format task again...
the csscomb plugin runs again and so on ...
Here is a snippet that causes this loop. (Note: this uses v4 of gulp)
'use strict'
import { task, parallel, series, src, dest, watch, plugins } from './gulp';
import { startStagingServer } from './servers';
import { solution } from './solution.map';
const path = require('path');
task('serve', parallel(startStagingServer, watchStyles);
function watchStyles() {
watch([ solution.src.styles ], series(formatStyles, compileStyles))
}
function formatStyles(done) {
return src([ solution.src.styles ])
.pipe(plugins.csscomb())
.pipe(dest(solution.src.mount)) // the root of the solution
}
function compileStyles() {
return src([ solution.src.styles ])
.pipe(plugins.sass().on('error', plug.sass.logError))
.pipe(dest(path.join(solution.dest.stage, 'serve')));
}
Does anyone know a way to avoid this?
The way to avoid this is not to put the fix in the watcher. Use 2 separate functions: one that fixes and the other that doesn't. Only watch the one that doesn't. Example:
function taskJscsFix() {
return gulp.src(path.JS)
.pipe(jscs({
configPath: './gulp/.jscsrc',
fix: true
}))
.pipe(gulp.dest(path.SRC.JS));
}
function taskScripts() {
return gulp.src(path.JS)
.pipe(jscs({
configPath: './gulp/.jscsrc'
}))
.pipe(jscs.reporter())
.pipe(gulp.dest(path.DEST.JS));
}
Related
I use gulp-notify to trigger notifications when tasks complete. If a task is ran standalone, a notification for that specific task is triggered. If a task is ran as a dependency of another task, a notification for all dependencies is triggered.
In gulp#3, I check if the task is being called as a dependency using gulp.seq, which contains an array of the tasks being ran. Let's say I have three tasks: default, styles, and scripts, with the later two set as dependencies of the first. When running gulp styles, gulp.seq will contain [ 'styles' ]. When running gulp (the default task), gulp.seq will contain [ 'styles', 'scripts', 'default' ]. Knowing that, I then check gulp.seq.indexOf("styles") > gulp.seq.indexOf("default"), which tells me weather or not styles was ran as part of the default task.
With gulp#4, it appears that gulp.seq no longer exists. I've tried digging through the documentation and source code with no luck. It seems like gulp.tree({ deep:true }) (docs) might be what I'm looking for, but I don't see anything in it that returns anything useful.
Is there an equivalent of gulp.seq in gulp#4?
The API gulp.seq was never an official prop exposed by Gulp. With Gulp 4, you cannot do that. gulp.tree({ /* */ }) will not solve this problem for you.
Having said that, if you still need to find whether a task has run during some other task's pipeline, then you will have to decorate every gulp task with your own wrapper using something like this:
let runTasks = [];
function taskWrapper(taskName, tasks, thisTask) {
let callbackTask;
function innerCallback(cb) {
runTasks.push(taskName);
cb();
}
if (thisTask) {
callbackTask = function(cb) {
thisTask(function () {
innerCallback(cb);
});
}
} else {
callbackTask = innerCallback;
}
const newTasks = [ ...tasks, callbackTask ];
gulp.task(taskName, gulp.series(newTasks));
}
// INSTEAD OF THIS
// gulp.task('default', gulp.series('style', 'script', function () { }));
// DO THIS
taskWrapper('default', ['style', 'script'], function(cb) {
console.log('default task starting');
cb();
});
NOTE: Above code snippets has limitation. If you use watch mode, array maintaining the executed tasks i.e. runTasks will keep on growing. Also, it assumes tasks will always run in series. For a parallel mode, the logic gets little complicated.
Finally, you can also have a predefault task to help it further:
taskWrapper('predefault', [], function(cb) {
// RESET runTasks
runTasks = [];
cb();
});
taskWrapper('default', ['predefault', 'style', 'script'], function(cb) {
console.log('default task starting');
cb();
});
Also, I am doubtful if gulp-notify will work with Gulp 4.
Through a bit of luck, I discovered this was possible via the module yargs, which I already have installed.
When running gulp styles, for example, I can check argv._.indexOf("styles") > -1, as it contains ['styles']. When running gulp (i.e the default task), it contains []. In my testing, this works perfectly for my use case.
I want to put inside gulpfile something like:
require('web-component-tester').gulp.init(gulp);
gulp.task('default', function() {
gulp.watch(['elements/**', 'test/**'], ['test:local']);
});
The purpose is to watch test folders or elements folders (with Polymer components). If some of them will change, run test with each build.
my wct.conf.js:
module.exports = {
root: '.tmp/elements/',
suites: ['**/test/'],
plugins: {
local: {browsers: ['chrome']},
}
};
I found the code above on some page but after I add some tests and then type gulp in my terminal I found error, because .tmp folder is not updated and strange errors like Polymer is not definedor ajax.generateRequest is not a function. I got also right errors when I intentionally made a mistake in a test to fail it, so it looks like something is ok, but not at all.
I add the tests to the existing project with lots of files. When I tried to do the same thing on empty project I also got the same error until I type bower install.
Is there any chance that this is the problem with bower dependencies?
Or have you any idea what is wrong? Is this part of code in gulpfile right to perform the desired effect?
Thanks a lot.
I am not answering your question directly, because its been a while since I've done it that way. But the following defines a sub task from among others to define a task called 'client' which then runs the tests in a frame buffer (so I don't have disturbing windows popping up all over the place when the tests run - they just run and output in a console window. Its effectively spawning a command line version of wct and I don't have a wct.conf file at all.
(function() {
'use strict';
const spawn = require('child_process').spawn;
module.exports = function(gulp) {
gulp.task('test:client',['lint:client'], () => {
var child = spawn('xvfb-run', ['-a', 'wct', '--color'], {cwd: process.cwd()});
child.stdout.setEncoding('utf8');
child.stdout.on('data', function(data) {
process.stdout.write(data);
});
child.stderr.setEncoding('utf8');
child.stderr.on('data', function(data) {
process.stderr.write(data);
});
});
gulp.task('client',function() {
gulp.watch([
'app/index.html',
'app/src/*.html',
'app/test/*.html',
'aoo/mocks/*.html',
'gulpfile.js',
'tasks/*.js'
],['test:client']);
});
};
})();
This file is one file within the tasks directory (which as you can see I am watching)
My gulpfile loads this, and other tasks like so (I copied this from the angular.js team who used it to load some of there tasks supporting angular)
(function() {
'use strict';
require('dotenv').config(); //load our environment
var gulp = require('gulp');
var includeAll = require('include-all');
/**
* Loads task modules from a relative path.
*/
function loadTasks(relPath) {
return includeAll({
dirname: require('path').resolve(__dirname, relPath),
filter: /(.+)\.js$/
}) || {};
}
// *
// * Invokes the function from a Gulp configuration module with
// * a single argument - the `gulp` object.
function addTasks(tasks) {
for (var taskName in tasks) {
if (tasks.hasOwnProperty(taskName)) {
tasks[taskName](gulp);
}
}
}
/**
* Add all Gulp tasks to the gulpfile.
* Tasks are in `tasks/`
*/
addTasks(loadTasks('tasks/'));
// require('gulp-load-tasks')(__dirname + '/tasks');
gulp.task('default', ['lint:gulp','client','server']);
})();
I'm new to Gulp and I found a Gulpfile.js example I wanted to use to livereload my express app's server whenever a change takes place in either my app.js file or ./public directory. Here is the Gulpfile.js code:
var gulp = require('gulp'),
spawn = require('child_process').spawn,
node;
/**
* $ gulp server
* description: Launch the server. If there's a server already running, kill it.
*/
gulp.task('server', function() {
if (node) node.kill()
node = spawn('node', ['app.js'], {stdio: 'inherit'})
node.on('close', function (code) {
if (code === 8) {
gulp.log('Error detected, waiting for changes...');
}
});
})
/**
* $ gulp default
* description: Start the development environment
*/
gulp.task('default', function() {
gulp.run('server')
gulp.watch(['./app.js', './public/'], function() {
gulp.run('server')
})
})
// clean up if an error goes unhandled.
process.on('exit', function() {
if (node) node.kill()
})
In my terminal window I keep getting the following warning:
gulp.run() has been deprecated. Use task dependencies or gulp.watch task triggering instead.
Gulp is working and it is livereloading the web application like I want it to but I'd like to fix this issue to future proof my development process, as well as get rid of this annoying warning message.
Thanks for the help!
One option would be to simply replace all occurrences of gulp.run() with gulp.start():
gulp.task('default', function() {
gulp.start('server');
gulp.watch(['./app.js', './public/'], function() {
gulp.start('server');
});
});
However calling a task explicitly using gulp.start() is not the idiomatic way of doing things in gulp (although sometimes it's necessary).
The warning message you receive already hints at the idiomatic way of solving this:
Use task dependencies or gulp.watch task triggering
Task dependencies allow you to run a task before another task. That means you can get rid of the first gulp.run().
Task triggering in gulp.watch() allows you to run a task when a file changes. That means you can get rid of the second gulp.run().
Therefore your default task ends up looking like this:
gulp.task('default', ['server'], function() {
gulp.watch(['./app.js', './public/'], ['server']);
});
I'm learning the gulp way of doing things after using grunt exclusively in the past. I'm struggling to understand how to pass multiple inputs to get multiple outputs w/gulp.
Let's say I have a large project that has specialized js on a per page basis:
The Grunt Way:
grunt.initConfig({
uglify: {
my_target: {
files: {
'dest/everypage.min.js': ['src/jquery.js', 'src/navigation.js'],
'dest/special-page.min.js': ['src/vendor/handlebars.js', 'src/something-else.js']
}
}
}
});
This may be a poor example as it violates the "do only one thing" principle since grunt-uglify is concatenating and uglifying. In any event I'm interested in learning how to accomplish the same thing using gulp.
Thanks to #AnilNatha I'm starting to think with more of a Gulp mindset.
For my case I have a load of files that need to be concatenated. I offloaded these to a config object that my concat task iterates over:
// Could be moved to another file and `required` in.
var files = {
'polyfills.js': ['js/vendor/picturefill.js', 'js/vendor/augment.js'],
'map.js': [
'js/vendor/leaflet.js',
'js/vendor/leaflet.markercluster.min.js',
'js/vendor/jquery.easyModal.js',
'js/vendor/jquery-autocomplete.min.js',
'js/vendor/underscore.1.8.3.js',
'js/map.js'
],
...
};
var output = './build/js';
// Using underscore.js pass the key/value pair to custom concat function
gulp.task('concat', function (done) {
_.each(files, concat);
// bs.reload(); if you're using browsersync
done(); // tell gulp this asynchronous process is complete
});
// Custom concat function
function concat(files, dest) {
return gulp.src(files)
.pipe($.concat(dest))
.pipe(gulp.dest(output));
}
This must be obvious but I can't find it. I want to preprocess my stylus/coffee files with a watcher in the dev environment and in production with a build task (isn't that common to all of us?) and also run a few more minification and uglification steps in production but I want to share the pipe steps common to both dev and production for DRY
The problem is that when I run the task which watches the files, the task which preprocesses does that to all the files since it has its own gulp.src statement which includes all stylus files.
How do I avoid compiling all files on watching while still keeping the compile task separate. Thanks
paths = {
jade: ['www/**/*.jade']
};
gulp.task('jade', function() {
return gulp.src(paths.jade).pipe(jade({
pretty: true
})).pipe(gulp.dest('www/')).pipe(browserSync.stream());
});
gulp.task('serve', ['jade', 'coffee'], function() {
browserSync.init({
server: './www'
});
watch(paths.jade, function() {
return gulp.start(['jade']);
});
return gulp.watch('www/**/*.coffee', ['coffee']);
});
One important thing in Gulp is not to duplicate pipelines. If you want to process your stylus files, it has to be the one and only stylus pipe. If you want to execute different steps in your pipe however, you have multiple choices. One that I would suggest would be a noop() function in conjunction with a selection function:
var through = require('through2'); // Gulp's stream engine
/** creates an empty pipeline step **/
function noop() {
return through.obj();
}
/** the isProd variable denotes if we are in
production mode. If so, we execute the task.
If not, we pass it through an empty step
**/
function prod(task) {
if(isProd) {
return task;
} else {
return noop();
}
}
gulp.task('stylus', function() {
return gulp.src(path.styles)
.pipe(stylus())
.pipe(prod(minifyCss())) // We just minify in production mode
.pipe(gulp.dest(path.whatever))
})
As for the incremental builds (building just the changed files with every iteration), the best way would be to get on the gulp-cached plugin:
var cached = require('gulp-cached');
gulp.task('stylus', function() {
return gulp.src(path.styles)
.pipe(cached('styles')) // we just pass through the files that have changed
.pipe(stylus())
.pipe(prod(minifyCss()))
.pipe(gulp.dest(path.whatever))
})
This plugin will check if the contents have changed with each iteration you have done.
I spend a whole chapter on Gulp for different environments in my book, and I found those to be the most suitable ones. For more information on incremental builds, you can also check on my article on that (includes Gulp4): http://fettblog.eu/gulp-4-incremental-builds/