Related
I want to set the flag --silentas default in my gulp tasks. Ist this possible?
https://github.com/gulpjs/gulp/blob/master/docs/CLI.md#flags
The easiest way if you are running a shell is to use alias:
alias gulp 'gulp --silent' (check the specific syntax for you shell type).
If you'd like to take it a step further, in your project go into:
node_modules/gulp/bin
and edit gulp.js
You'd see there the following lines:
var shouldLog = !argv.silent && !simpleTasksFlag;
if (!shouldLog) {
gutil.log = function(){};
}
If you want to categorically disable logging, just change it to:
var shouldLog = false;
If you want to disable it on a per task basis, you can do it by manipulating gulp-util's (gutil in this code) log method:
gulp.task('mySilentTask', function(){
gutil.log = function(){}; // make sure you obtain gutil properly
// task logic here
});
You can set default flags for Gulp by adding a configuration file .gulp.json next to the gulpfile:
{
"flags": {
"silent": true
}
}
See gulp-cli documentation for configuration for more details: https://github.com/gulpjs/gulp-cli#configuration
With gulp you often see patterns like this:
gulp.watch('src/*.jade',['templates']);
gulp.task('templates', function() {
return gulp.src('src/*.jade')
.pipe(jade({
pretty: true
}))
.pipe(gulp.dest('dist/'))
.pipe( livereload( server ));
});
Does this actually pass the watch'ed files into the templates task? How do these overwrite/extend/filter the src'ed tasks?
I had the same question some time ago and came to the following conclusion after digging for a bit.
gulp.watch is an eventEmitter that emits a change event, and so you can do this:
var watcher = gulp.watch('src/*.jade',['templates']);
watcher.on('change', function(f) {
console.log('Change Event:', f);
});
and you'll see this:
Change Event: { type: 'changed',
path: '/Users/developer/Sites/stackoverflow/src/touch.jade' }
This information could presumably be passed to the template task either via its task function, or the behavior of gulp.src.
The task function itself can only receive a callback (https://github.com/gulpjs/gulp/blob/master/docs/API.md#fn) and cannot receive any information about vinyl files (https://github.com/wearefractal/vinyl-fs) that are used by gulp.
The source starting a task (.watch in this case, or gulp command line) has no effect on the behavior of gulp.src('src-glob', [options]). 'src-glob' is a string (or array of strings) and options (https://github.com/isaacs/node-glob#options) has nothing about any file changes.
Hence, I don't see any way in which .watch could directly affect the behavior of a task it triggers.
If you want to process only the changed files, you can use gulp-changed (https://www.npmjs.com/package/gulp-changed) if you want to use gulp.watch, or you cold use gulp-watch.
Alternatively, you could do this as well:
var gulp = require('gulp');
var jade = require('gulp-jade');
var livereload = require('gulp-livereload');
gulp.watch('src/*.jade', function(event){
template(event.path);
});
gulp.task('templates', function() {
template('src/*.jade');
});
function template(files) {
return gulp.src(files)
.pipe(jade({
pretty: true
}))
.pipe(gulp.dest('dist/'))
}
One of the possible way to pass a parameter or a data from your watcher to a task. Is through using a global variable, or a variable that is in both blocks scops. Here is an example:
gulp.task('watch', function () {
//....
//json comments
watch('./app/tempGulp/json/**/*.json', function (evt) {
jsonCommentWatchEvt = evt; // we set the global variable first
gulp.start('jsonComment'); // then we start the task
})
})
//global variable
var jsonCommentWatchEvt = null
//json comments task
gulp.task('jsonComment', function () {
jsonComment_Task(jsonCommentWatchEvt)
})
And here the function doing the task work in case it interest any one, But know i didn't need to put the work in such another function i could just implemented it directly in the task. And for the file you have your global variable. Here it's jsonCommentWatchEvt. But know if you don't use a function as i did, a good practice is to assign the value of the global variable to a local one, that you will be using. And you do that at the all top entry of the task. So you will not be using the global variable itself. And that to avoid the problem that it can change by another watch handling triggering. When it stay in use by the current running task.
function jsonComment_Task(evt) {
console.log('handling : ' + evt.path);
gulp.src(evt.path, {
base: './app/tempGulp/json/'
}).
pipe(stripJsonComments({whitespace: false})).on('error', console.log).
on('data', function (file) { // here we want to manipulate the resulting stream
var str = file.contents.toString()
var stream = source(path.basename(file.path))
stream.end(str.replace(/\n\s*\n/g, '\n\n'))
stream.
pipe(gulp.dest('./app/json/')).on('error', console.log)
})
}
I had a directory of different json's files, where i will use comments on them. I'm watching them. When a file is modified the watch handling is triggered, and i need then to process only the file that was modified. To remove the comments, i used json-comment-strip plugin for that. Plus that i needed to do a more treatment. to remove the multiple successive line break. Whatever, at all first i needed to pass the path to the file that we can recover from the event parameter. I passed that to the task through a global variable, that does only that. Allow passing the data.
Note: Even though that doesn't have a relation with the question, in my example here, i needed to treat the stream getting out from the plugin processing. i used the on("data" event. it's asynchronous. so the task will mark the end before the work completely end (the task reach the end, but the launched asynchronous function will stay processing a little more). So the time you will get in the console at task end, isn't the time for the whole processing, but task block end. Just that you know. For me it doesn't matter.
I am trying to pass a parameter to a task that is being invoked by gulp-watch. I need it because I am trying to build a modular framework.
So if a file changes in module 1, the other modules don't need to be rebuild.
And I want just one function to create the concatted & uglified files per module.
This is what I got so far:
//here I need the 'module' parameter
gulp.task('script', function(module) { ... }
gulp.task('watch', function() {
gulp.watch('files/in/module1/*.js', ['script']); //here I want to pass module1
gulp.watch('files/in/module2/*.js', ['script']); //here I want to pass module2
});
A lot of the documentation/examples seems to be outdated (gulp.run(), gulp.start()).
I hope someone can help me out here.
I had the very same issue, searched for a while, and the "cleanest" way I came up with, uses the .on() event handler of gulp.watch(), and the .env property of gulp-util:
var gulp = require('gulp');
$.util = require('gulp-util');
var modules = {
module1: {}, // awesome module1
module2: {} // awesome module2
};
gulp.task('script', function(){
var moduleName = $.util.env.module;
// Exit if the value is missing...
var module = modules[moduleName];
if (!module) {
$.util.log($.util.colors.red('Error'), "Wrong module value!");
return;
}
$.util.log("Executing task on module '" + moduleName + "'");
// Do your task on "module" here.
});
gulp.task('watch', function () {
gulp.watch(['files/in/module1/*.js'], ['script']).on('change', function () {
$.util.env.module = 'module1';
});
gulp.watch(['files/in/module2/*.js'], ['script']).on('change', function () {
$.util.env.module = 'module2';
});
});
gulp-util also comes in handy if you need to pass (global) parameters from the shell:
[emiliano#dev ~]# gulp script --module=module1 --minify
Hope this helps someone else out there!
Regards.
In that i will answer directly the question "How to pass a parameter to gulp-watch invoked task"
My way of doing, and one of the possibility i see, is to use a global variable to pass the value between the two blocks. you set it just before launching the task in the watcher. And in the task, just at the start you pass it to a local variable.
See this answer for more details: https://stackoverflow.com/a/49733123/7668448
In what you want to achieve, you can too use just one watcher over the directory that hold all modules. If so is the structure. Then when a change happen, you can recover the changed file path. From that you can deduce what module does belong to. By getting the Module folder. That way you will not need to add a new watcher for each new module. Which can be nice when there is multiple contributors to the project for example when working on open source. And you do it one time, and don't have to care about adding anything. Just like with the delegation principle, with DOM event handling when there is multiple elements. Even if the chosen structure, doesn't have all the modules in one directory. You can stay pass multiple globs to the one watcher.
gulp.watch(['glob1/**/*.js', 'glob2/**/*.js',...], function(evt) {/*.....*/});
And following the structure you have, you can work your way to deduce what module is.
For the watcher here how i suggest you do it:
watch('./your/allModulesFolder/**/*.js', function (evt) {
rebuildModulWatchEvt = evt; //here you update the global var
gulp.start('rebuildModul'); // you start the task
})
The evt here hold multiple info: cwd, base, state, _contents ...etc And what interest us is path. So evt.path will give you the path of the changed file.
In your task either you do that:
gulp.task('rebuildModul', function() {
let evt = rebuildModulWatchEvt; // at all start you pass it to a local var
let filePath = evt.path; // how you get the changed file path
// your code go here for the rest, following your structure, get the path for the module folder
});
or you use a function :
gulp.task('rebuildModul', function() {
rebuildModulTaskRun(rebuildModulWatchEvt);
});
function rebuilModulTaskRun(evt) {
let filePath = evt.path;
// your code go here for the rest, following your structure, get the path for the module folder
}
Normally we can run gulp task from console via something like gulp mytask. Is there anyway that I can pass in parameter to gulp task? If possible, please show example how it can be done.
It's a feature programs cannot stay without. You can try yargs.
npm install --save-dev yargs
You can use it like this:
gulp mytask --production --test 1234
In the code, for example:
var argv = require('yargs').argv;
var isProduction = (argv.production === undefined) ? false : true;
For your understanding:
> gulp watch
console.log(argv.production === undefined); <-- true
console.log(argv.test === undefined); <-- true
> gulp watch --production
console.log(argv.production === undefined); <-- false
console.log(argv.production); <-- true
console.log(argv.test === undefined); <-- true
console.log(argv.test); <-- undefined
> gulp watch --production --test 1234
console.log(argv.production === undefined); <-- false
console.log(argv.production); <-- true
console.log(argv.test === undefined); <-- false
console.log(argv.test); <-- 1234
Hope you can take it from here.
There's another plugin that you can use, minimist. There's another post where there's good examples for both yargs and minimist: (Is it possible to pass a flag to Gulp to have it run tasks in different ways?)
If you want to avoid adding extra dependencies, I found node's process.argv to be useful:
gulp.task('mytask', function() {
console.log(process.argv);
});
So the following:
gulp mytask --option 123
should display:
[ 'node', 'path/to/gulp.js', 'mytask', '--option', '123']
If you are sure that the desired parameter is in the right position, then the flags aren't needed.** Just use (in this case):
var option = process.argv[4]; //set to '123'
BUT: as the option may not be set, or may be in a different position, I feel that a better idea would be something like:
var option, i = process.argv.indexOf("--option");
if(i>-1) {
option = process.argv[i+1];
}
That way, you can handle variations in multiple options, like:
//task should still find 'option' variable in all cases
gulp mytask --newoption somestuff --option 123
gulp mytask --option 123 --newoption somestuff
gulp mytask --flag --option 123
** Edit: true for node scripts, but gulp interprets anything without a leading "--" as another task name. So using gulp mytask 123 will fail because gulp can't find a task called '123'.
There's an official gulp recipe for this using minimist.
https://github.com/gulpjs/gulp/blob/master/docs/recipes/pass-arguments-from-cli.md
The basics are using minimist to separate the cli arguments and combine them with known options:
var options = minimist(process.argv.slice(2), knownOptions);
Which would parse something like
$ gulp scripts --env development
More complete info in the recipe.
Passing a parameter to gulp can mean a few things:
From the command line to the gulpfile (already exemplified here).
From the main body of the gulpfile.js script to gulp tasks.
From one gulp task to another gulp task.
Here's an approach of passing parameters from the main gulpfile to a gulp task. By moving the task that needs the parameter to it's own module and wrapping it in a function (so a parameter can be passed).:
// ./gulp-tasks/my-neat-task.js file
module.exports = function(opts){
opts.gulp.task('my-neat-task', function(){
console.log( 'the value is ' + opts.value );
});
};
//main gulpfile.js file
//...do some work to figure out a value called val...
var val = 'some value';
//pass that value as a parameter to the 'my-neat-task' gulp task
require('./gulp-tasks/my-neat-task.js')({ gulp: gulp, value: val});
This can come in handy if you have a lot of gulp tasks and want to pass them some handy environmental configs. I'm not sure if it can work between one task and another.
If you want to use environment params and other utils as well such as log, you can use gulp-util
/*
$npm install gulp-util --save-dev
$gulp --varName 123
*/
var util = require('gulp-util');
util.log(util.env.varName);
Update
gulp-util is now deprecated. You can use minimist instead.
var argv = require('minimist')(process.argv.slice(2));
console.dir(argv);
#Ethan's answer would completely work. From my experience, the more node way is to use environment variables. It's a standard way to configure programs deployed on hosting platforms (e.g. Heroku or Dokku).
To pass the parameter from the command line, do it like this:
Development:
gulp dev
Production:
NODE_ENV=production gulp dev
The syntax is different, but very Unix, and it's compatible with Heroku, Dokku, etc.
You can access the variable in your code at process.env.NODE_ENV
MYAPP=something_else gulp dev
would set
process.env.MYAPP === 'something_else'
This answer might give you some other ideas.
Here is my sample how I use it. For the css/less task. Can be applied for all.
var cssTask = function (options) {
var minifyCSS = require('gulp-minify-css'),
less = require('gulp-less'),
src = cssDependencies;
src.push(codePath + '**/*.less');
var run = function () {
var start = Date.now();
console.log('Start building CSS/LESS bundle');
gulp.src(src)
.pipe(gulpif(options.devBuild, plumber({
errorHandler: onError
})))
.pipe(concat('main.css'))
.pipe(less())
.pipe(gulpif(options.minify, minifyCSS()))
.pipe(gulp.dest(buildPath + 'css'))
.pipe(gulpif(options.devBuild, browserSync.reload({stream:true})))
.pipe(notify(function () {
console.log('END CSS/LESS built in ' + (Date.now() - start) + 'ms');
}));
};
run();
if (options.watch) {
gulp.watch(src, run);
}
};
gulp.task('dev', function () {
var options = {
devBuild: true,
minify: false,
watch: false
};
cssTask (options);
});
If you use gulp with yargs, notice the following:
If you have a task 'customer' and wan't to use yargs build in Parameter checking for required commands:
.command("customer <place> [language]","Create a customer directory")
call it with:
gulp customer --customer Bob --place Chicago --language english
yargs will allway throw an error, that there are not enough commands was assigned to the call, even if you have!! —
Give it a try and add only a digit to the command (to make it not equal to the gulp-task name)... and it will work:
.command("customer1 <place> [language]","Create a customer directory")
This is cause of gulp seems to trigger the task, before yargs is able to check for this required Parameter. It cost me surveral hours to figure this out.
Hope this helps you..
Here is another way without extra modules:
I needed to guess the environment from the task name, I have a 'dev' task and a 'prod' task.
When I run gulp prod it should be set to prod environment.
When I run gulp dev or anything else it should be set to dev environment.
For that I just check the running task name:
devEnv = process.argv[process.argv.length-1] !== 'prod';
I know I am late to answer this question but I would like to add something to answer of #Ethan, the highest voted and accepted answer.
We can use yargs to get the command line parameter and with that we can also add our own alias for some parameters like follow.
var args = require('yargs')
.alias('r', 'release')
.alias('d', 'develop')
.default('release', false)
.argv;
Kindly refer this link for more details.
https://github.com/yargs/yargs/blob/HEAD/docs/api.md
Following is use of alias as per given in documentation of yargs. We can also find more yargs function there and can make the command line passing experience even better.
.alias(key, alias)
Set key names as equivalent such that updates to a key will propagate
to aliases and vice-versa.
Optionally .alias() can take an object that maps keys to aliases. Each
key of this object should be the canonical version of the option, and
each value should be a string or an array of strings.
There is certainly a shorter notation, but that was my approach:
gulp.task('check_env', function () {
return new Promise(function (resolve, reject) {
// gulp --dev
var env = process.argv[3], isDev;
if (env) {
if (env == "--dev") {
log.info("Dev Mode on");
isDev = true;
} else {
log.info("Dev Mode off");
isDev = false;
}
} else {
if (variables.settings.isDev == true) {
isDev = true;
} else {
isDev = false;
}
}
resolve();
});
});
If you want to set the env based to the actual Git branch (master/develop):
gulp.task('set_env', function (cb) {
exec('git rev-parse --abbrev-ref HEAD', function (err, stdout, stderr) {
const git__branch = stdout.replace(/(\r\n|\n|\r)/gm, ""),
regex__feature = new RegExp('feature/feature-*');
if (git__branch == "develop") {
log.info("👨💻Develop Branch");
isCompressing = false;
} else if (git__branch == "master") {
log.info("🌎Master Branch");
isCompressing = true;
} else if (regex__feature.test(git__branch) === true){
log.info("✨Feature Branch");
isCompressing = true;
}else{
//TODO: check for other branch
log.warn("Unknown " + git__branch + ", maybe hotfix?");
//isCompressing = variables.settings.isCompressing;
}
log.info(stderr);
cb(err);
});
return;
})
P.s. For the log I added the following:
var log = require('fancy-log');
In Case you need it, thats my default Task:
gulp.task('default',
gulp.series('set_env', gulp.parallel('build_scss', 'minify_js', 'minify_ts', 'minify_html', 'browser_sync_func', 'watch'),
function () {
}));
Suggestions for optimization are welcome.
Just load it into a new object on process .. process.gulp = {} and have the task look there.
With Gulp it is pretty easy to orchestrate a build consisting of many little steps processed through a pipe.
One of the tasks in my current Gulp setup looks like this:
gulp.task("release-assets", ["angular-templates", "less-compile"], function() {
var assets = useref.assets();
var jsFilter = filter("**/*.js");
var moonwalkFilter = filter("**/" + MOONWALK_JS);
var cssFilter = filter("**/*.css");
return gulp.src("./Content/**/*.cshtml")
.pipe(assets) // Concatenate with gulp-useref
.pipe(jsFilter)
.pipe(ngAnnotate()) // Process javascript sources to add dependency injection annotations
.pipe(uglify()) // Minify javascript sources
.pipe(jsFilter.restore())
.pipe(cssFilter)
.pipe(less()) // Generate CSS from LESS
.pipe(cmq({ log: true })) // Combine matching media queries into one media query definition
.pipe(minifyCSS()) // Minify CSS sources
.pipe(cssFilter.restore())
.pipe(moonwalkFilter) // Filter the moonwalk.js source file, which is generated above by useref
.pipe(addsrc("Temp/" + TEMPLATES_JS))// Add the templates.js to the stream, which is generated by a seperate task
.pipe(order(["**/" + MOONWALK_JS, "*.js"]))// Order stream, so that templates.js is appended to moonwalk.js (needed, since templates depend on the angular module)
.pipe(concat(MOONWALK_JS))// Concat the existing moonwalk.js and the templates.js into moonwalk.js
.pipe(moonwalkFilter.restore())
.pipe(rev()) // Rename the concatenated files
.pipe(assets.restore())
.pipe(useref()) // Replace the original references in the cshtml with the concatenated and processed resources by usemin
.pipe(revReplace({replaceInExtensions:[".cshtml"]})) // Replace the usemin generated resources with the reved resources
.pipe(gulp.dest("Dist/"));
});
How do I do proper error handling in this task?
What I want:
- The build should fail if one of the dependencies of the task fails
- The build should fail and report an meaningful error fi one of the steps in this task produces an error.
As far as I gather, I have to care about error handling in the pipe/streams myself ... how do I do that?
Do I really have to include a on("error", errorHandler) after every step in the pipe?
Is there any documentation about error handling in Gulp?
Do I really have to include a on("error", errorHandler) after every step in the pipe?
Yes, unless you make use of a module that can merge all of these error events into a single one. Have a look at stream-combiner, which can do this. There's an example recipe in the gulp docs that references using this module to listen for errors in the pipe:
var combiner = require('stream-combiner2');
var uglify = require('gulp-uglify');
var gulp = require('gulp');
gulp.task('test', function() {
var combined = combiner.obj([
gulp.src('bootstrap/js/*.js'),
uglify(),
gulp.dest('public/bootstrap')
]);
// any errors in the above streams will get caught
// by this listener, instead of being thrown:
combined.on('error', console.error.bind(console));
return combined;
});
https://github.com/gulpjs/gulp/blob/master/docs/recipes/combining-streams-to-handle-errors.md
So, wrap your stream with combiner as in the example and you'll have a single error handler.