diff --git a/README.md b/README.md index 8e894f468..b492ea7d2 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ var paths = { }; // Not all tasks need to use streams -// A gulpfile is just another node program and you can use any package available on npm +// A gulpfile is just another node program and you can use all packages available on npm gulp.task('clean', function() { // You can use multiple globbing patterns as you would with `gulp.src` return del(['build']); diff --git a/docs/API.md b/docs/API.md index b5fac15bc..6c2443e7d 100644 --- a/docs/API.md +++ b/docs/API.md @@ -8,10 +8,8 @@ Jump to: ### gulp.src(globs[, options]) -Emits files matching provided glob or an array of globs. -Returns a [stream](https://nodejs.org/api/stream.html) of [Vinyl files](https://github.com/gulpjs/vinyl-fs) -that can be [piped](https://nodejs.org/api/stream.html#stream_readable_pipe_destination_options) -to plugins. +Emits files matching provided glob or array of globs. +Returns a [stream] of [Vinyl files] that can be [piped] to plugins. ```javascript gulp.src('client/templates/*.pug') @@ -20,6 +18,8 @@ gulp.src('client/templates/*.pug') .pipe(gulp.dest('build/minified_templates')); ``` +`glob` refers to [node-glob syntax][node-glob] or it can be a direct file path. + #### globs Type: `String` or `Array` @@ -37,33 +37,48 @@ The following expression matches `a.js` and `bad.js`: gulp.src(['client/*.js', '!client/b*.js', 'client/bad.js']) +Note that globs are evaluated in order, which means this is possible: +``` +// exclude every JS file that starts with a b except bad.js +gulp.src(['*.js', '!b*.js', 'bad.js']) +``` + #### options Type: `Object` Options to pass to [node-glob] through [glob-stream]. -gulp supports all [options supported by node-glob][node-glob documentation] and [glob-stream] except `ignore` and adds the following options. +gulp adds some additional options in addition to the +[options supported by node-glob][node-glob documentation] and [glob-stream]: ##### options.buffer Type: `Boolean` + Default: `true` -Setting this to `false` will return `file.contents` as a stream and not buffer files. This is useful when working with large files. **Note:** Plugins might not implement support for streams. +Setting this to `false` will return `file.contents` as a stream and not +buffer files. This is useful when working with large files. + +**Note:** Plugins might not implement support for streams. ##### options.read Type: `Boolean` + Default: `true` -Setting this to `false` will return `file.contents` as null and not read the file at all. +Setting this to `false` will return `file.contents` as null and not read +the file at all. ##### options.base Type: `String` -Default: everything before a glob starts (see [glob2base]) + +Default: everything before a glob starts (see [glob-parent]) E.g., consider `somefile.js` in `client/js/somedir`: ```js -gulp.src('client/js/**/*.js') // Matches 'client/js/somedir/somefile.js' and resolves `base` to `client/js/` +// Matches 'client/js/somedir/somefile.js' and resolves `base` to `client/js/` +gulp.src('client/js/**/*.js') .pipe(minify()) .pipe(gulp.dest('build')); // Writes 'build/somedir/somefile.js' @@ -72,9 +87,25 @@ gulp.src('client/js/**/*.js', { base: 'client' }) .pipe(gulp.dest('build')); // Writes 'build/js/somedir/somefile.js' ``` +##### options.since +Type: `Date` or `Number` + +Setting this to a Date or a time stamp will discard any file that have not been +modified since the time specified. + +##### options.passthrough +Type: `Boolean` + +Default: `false` + +If true, it will create a duplex stream which passes items through and +emits globbed files. + + ### gulp.dest(path[, options]) -Can be piped to and it will write files. Re-emits all data passed to it so you can pipe to multiple folders. Folders that don't exist will be created. +Can be piped to and it will write files. Re-emits all data passed to it so you +can pipe to multiple folders. Folders that don't exist will be created. ```javascript gulp.src('./client/templates/*.pug') @@ -85,32 +116,83 @@ gulp.src('./client/templates/*.pug') ``` The write path is calculated by appending the file relative path to the given -destination directory. In turn, relative paths are calculated against the file base. -See `gulp.src` above for more info. +destination directory. In turn, relative paths are calculated against +the file base. See `gulp.src` above for more info. #### path Type: `String` or `Function` -The path (output folder) to write files to. Or a function that returns it, the function will be provided a [vinyl File instance](https://github.com/gulpjs/vinyl). +The path (output folder) to write files to. Or a function that returns it, +the function will be provided a [vinyl File instance]. #### options Type: `Object` ##### options.cwd Type: `String` + Default: `process.cwd()` -`cwd` for the output folder, only has an effect if provided output folder is relative. +`cwd` for the output folder, only has an effect if provided output folder is +relative. ##### options.mode +Type: `String` or `Number` + +Default: the mode of the input file (file.stat.mode) or the process mode +if the input file has no mode property. + +Octal permission specifying the mode the files should be created with: e.g. +`"0744"`, `0744` or `484` (`0744` in base 10). + +##### options.dirMode +Type: `String` or `Number` + +Default: Default is the process mode. + +Octal permission specifying the mode the directory should be created with: e.g. +`"0755"`, `0755` or `493` (`0755` in base 10). + +##### options.overwrite +Type: `Boolean` + +Default: `true` + +Specify if existing files with the same path should be overwritten or not. + + +### gulp.symlink(folder[, options]) + +Functions exactly like `gulp.dest`, but will create symlinks instead of copying +a directory. + +#### folder +Type: `String` or `Function` + +A folder path or a function that receives in a file and returns a folder path. + +#### options +Type: `Object` + +##### options.cwd Type: `String` -Default: `0777` -Octal permission string specifying mode for any folders that need to be created for output folder. +Default: `process.cwd()` + +`cwd` for the output folder, only has an effect if provided output folder is +relative. + +##### options.dirMode +Type: `String` or `Number` + +Default: Default is the process mode. + +Octal permission specifying the mode the directory should be created with: e.g. +`"0755"`, `0755` or `493` (`0755` in base 10). -### gulp.task(name [, deps] [, fn]) +### gulp.task([name,] fn) -Define a task using [Orchestrator]. +Define a task using [Undertaker]. ```js gulp.task('somename', function() { @@ -121,20 +203,27 @@ gulp.task('somename', function() { #### name Type: `String` -The name of the task. Tasks that you want to run from the command line should not have spaces in them. +Optional, The name of the task. Tasks that you want to run from the command line +should not have spaces in them. -#### deps -Type: `Array` +If the name is not provided, the task will be named after the function +`name` attribute, set on any named function. -An array of tasks to be executed and completed before your task will run. +[Function.name] is not writable; it cannot be set or edited. +It will be empty for anonymous functions: ```js -gulp.task('mytask', ['array', 'of', 'task', 'names'], function() { - // Do stuff -}); +function foo() {}; +foo.name === 'foo' // true + +var bar = function() {}; +bar.name === '' // true + +bar.name = 'bar' +bar.name === '' // true ``` -**Note:** Are your tasks running before the dependencies are complete? Make sure your dependency tasks are correctly using the async run hints: take in a callback or return a promise or event stream. +You should either provide the task name or avoid anonymous functions. You can also omit the function if you only want to run a bundle of dependency tasks: @@ -147,35 +236,31 @@ gulp.task('build', ['array', 'of', 'task', 'names']); #### fn Type: `Function` -The function performs the task's main operations. Generally this takes the form of: - -```js -gulp.task('buildStuff', function() { - // Do something that "builds stuff" - var stream = gulp.src(/*some source path*/) - .pipe(somePlugin()) - .pipe(someOtherPlugin()) - .pipe(gulp.dest(/*some destination*/)); - - return stream; - }); +The function that performs the task's operations. Generally it takes this form: ``` +gulp.task('somename', function() { + return gulp.src(['some/glob/**/*.ext']).pipe(someplugin()); +}) +``` + +Gulp tasks are asynchronous and Gulp uses [async-done] to wait for the tasks' +completion. Tasks are called with a callback parameter to call to signal +completion. Alternatively, Task can return a stream, a promise, a child process +or a RxJS observable to signal the end of the task. -#### Async task support +**Warning:** Sync tasks are not supported and your function will never complete +if the one of the above strategies is not used to signal completion. However, +thrown errors will be caught by Gulp. -Tasks can be made asynchronous if its `fn` does one of the following: +#### Async support ##### Accept a callback -```javascript -// run a command in a shell -var exec = require('child_process').exec; -gulp.task('jekyll', function(cb) { - // build Jekyll - exec('jekyll build', function(err) { - if (err) return cb(err); // return error - cb(); // finished task - }); +```js +var del = require('del'); + +gulp.task('clean', function(done) { + del(['.build/'], done); }); // use an async result in a pipe @@ -190,96 +275,143 @@ gulp.task('somename', function(cb) { }); ``` +The callback accepts an optional `Error` object. If it receives an error, +the task will fail. + ##### Return a stream ```js gulp.task('somename', function() { - var stream = gulp.src('client/**/*.js') + return gulp.src('client/**/*.js') .pipe(minify()) .pipe(gulp.dest('build')); - return stream; }); ``` ##### Return a promise -```javascript -var Q = require('q'); +```js +var Promise = require('promise'); +var del = require('del'); + +gulp.task('clean', function() { + return new Promise(function (resolve, reject) { + del(['.build/'], function(err) { + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); +}); +``` -gulp.task('somename', function() { - var deferred = Q.defer(); +or: +```js +var promisedDel = require('promised-del'); - // do async stuff - setTimeout(function() { - deferred.resolve(); - }, 1); +gulp.task('clean', function() { + return promisedDel(['.build/']); +}); +``` - return deferred.promise; +##### Return a child process + +```js +gulp.task('clean', function() { + return spawn('rm', ['-rf', path.join(__dirname, 'build')]); }); + ``` -**Note:** By default, tasks run with maximum concurrency -- e.g. it launches all the tasks at once and waits for nothing. If you want to create a series where tasks run in a particular order, you need to do two things: +##### Return a [RxJS] observable -- give it a hint to tell it when the task is done, -- and give it a hint that a task depends on completion of another. +```js +var Observable = require('rx').Observable; -For these examples, let's presume you have two tasks, "one" and "two" that you specifically want to run in this order: +gulp.task('sometask', function() { + return Observable.return(42); +}); +``` -1. In task "one" you add a hint to tell it when the task is done. Either take in a callback and call it when you're -done or return a promise or stream that the engine should wait to resolve or end respectively. -2. In task "two" you add a hint telling the engine that it depends on completion of the first task. +### gulp.parallel(...tasks) -So this example would look like this: +Takes a number of task names or functions and returns a function of the composed +tasks or functions. -```js -var gulp = require('gulp'); +When using task names, the task should already be registered. -// takes in a callback so the engine knows when it'll be done -gulp.task('one', function(cb) { - // do stuff -- async or otherwise - cb(err); // if err is not null and not undefined, the run will stop, and note that it failed +When the returned function is executed, the tasks or functions will be executed +in parallel, all being executed at the same time. If an error occurs, +all execution will complete. + +```js +gulp.task('one', function(done) { + // do stuff + done(); }); -// identifies a dependent task must be complete before this one begins -gulp.task('two', ['one'], function() { - // task 'one' is done now +gulp.task('two', function(done) { + // do stuff + done(); }); -gulp.task('default', ['one', 'two']); +gulp.task('default', gulp.parallel('one', 'two', function(done) { + // do more stuff + done(); +})); ``` +#### tasks +Type: `Array`, `String` or `Function` -### gulp.watch(glob [, opts], tasks) or gulp.watch(glob [, opts, cb]) +A task name, a function or an array of either. -Watch files and do something when a file changes. This always returns an EventEmitter that emits `change` events. -### gulp.watch(glob[, opts], tasks) +### gulp.series(...tasks) -#### glob -Type: `String` or `Array` +Takes a number of task names or functions and returns a function of the composed +tasks or functions. -A single glob or array of globs that indicate which files to watch for changes. +When using task names, the task should already be registered. -#### opts -Type: `Object` +When the returned function is executed, the tasks or functions will be executed +in series, each waiting for the prior to finish. If an error occurs, +execution will stop. -Options, that are passed to [`gaze`](https://github.com/shama/gaze). +```js +gulp.task('one', function(done) { + // do stuff + done(); +}); + +gulp.task('two', function(done) { + // do stuff + done(); +}); + +gulp.task('default', gulp.series('one', 'two', function(done) { + // do more stuff + done(); +})); +``` #### tasks -Type: `Array` +Type: `Array`, `String` or `Function` + +A task name, a function or an array of either. -Names of task(s) to run when a file changes, added with `gulp.task()` + +### gulp.watch(glob[, opts], tasks) + +Watch files and do something when a file changes. ```js -var watcher = gulp.watch('js/**/*.js', ['uglify','reload']); -watcher.on('change', function(event) { - console.log('File ' + event.path + ' was ' + event.type + ', running tasks...'); -}); +gulp.watch('js/**/*.js', gulp.parallel('uglify', 'reload')); ``` -### gulp.watch(glob[, opts, cb]) - #### glob Type: `String` or `Array` @@ -288,36 +420,50 @@ A single glob or array of globs that indicate which files to watch for changes. #### opts Type: `Object` -Options, that are passed to [`gaze`](https://github.com/shama/gaze). +Options, that are passed to [`gaze`][gaze]. -#### cb(event) -Type: `Function` +#### tasks +Type: `Array`, `Function` or `String` + +A task name, a function or an array of either to run when a file changes. -Callback to be called on each change. +When `tasks` is an array, the tasks will be run in parallel: +``` +gulp.watch('*.js', [one, two]); +// is equivalent to +gulp.watch('*.js', gulp.parallel(one, two)); +``` +`gulp.watch` returns an `EventEmitter` object which emits `change` events with +the [gaze] `event`: ```js -gulp.watch('js/**/*.js', function(event) { - console.log('File ' + event.path + ' was ' + event.type + ', running tasks...'); +var watcher = gulp.watch('js/**/*.js', gulp.parallel('uglify', 'reload')); +watcher.on('change', function(event) { + console.log('File ' + event.path + ' was ' + event.type); }); ``` -The callback will be passed an object, `event`, that describes the change: - ##### event.type -Type: `String` +Type: String -The type of change that occurred, either `added`, `changed`, `deleted` or `renamed`. +The type of change that occurred, either "added", "changed" or "deleted". ##### event.path -Type: `String` +Type: String The path to the file that triggered the event. - -[node-glob]: https://github.com/isaacs/node-glob -[node-glob documentation]: https://github.com/isaacs/node-glob#options -[node-glob syntax]: https://github.com/isaacs/node-glob +[Function.name]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name +[gaze]: https://github.com/shama/gaze [glob-stream]: https://github.com/gulpjs/glob-stream +[glob-parent]: https://github.com/es128/glob-parent [gulp-if]: https://github.com/robrich/gulp-if -[Orchestrator]: https://github.com/robrich/orchestrator -[glob2base]: https://github.com/wearefractal/glob2base +[node-glob documentation]: https://github.com/isaacs/node-glob#options +[node-glob]: https://github.com/isaacs/node-glob +[piped]: http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options +[RxJS]: https://www.npmjs.com/package/rx +[stream]: http://nodejs.org/api/stream.html +[async-done]: https://www.npmjs.com/package/async-done +[Undertaker]: https://github.com/phated/undertaker +[vinyl File instance]: https://github.com/gulpjs/vinyl +[Vinyl files]: https://github.com/gulpjs/vinyl-fs diff --git a/docs/getting-started.md b/docs/getting-started.md index 980c22189..06e0c9b8a 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -37,8 +37,9 @@ In your project directory, create a file named `gulpfile.js` in your project roo ```js var gulp = require('gulp'); -gulp.task('default', function() { +gulp.task('default', function(done) { // place code for your default task here + done(); }); ``` diff --git a/docs/recipes/combining-streams-to-handle-errors.md b/docs/recipes/combining-streams-to-handle-errors.md index e376f6db5..7c38654af 100644 --- a/docs/recipes/combining-streams-to-handle-errors.md +++ b/docs/recipes/combining-streams-to-handle-errors.md @@ -12,16 +12,13 @@ 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; + return 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: + .on('error', console.error.bind(console)); }); ``` diff --git a/docs/recipes/delete-files-folder.md b/docs/recipes/delete-files-folder.md index 05e37762f..1373ce6a1 100644 --- a/docs/recipes/delete-files-folder.md +++ b/docs/recipes/delete-files-folder.md @@ -39,7 +39,7 @@ gulp.task('clean:mobile', function () { ]); }); -gulp.task('default', ['clean:mobile']); +gulp.task('default', gulp.series('clean:mobile')); ``` @@ -76,7 +76,7 @@ gulp.task('clean:tmp', function () { .pipe(gulp.dest('dist')); }); -gulp.task('default', ['clean:tmp']); +gulp.task('default', gulp.series('clean:tmp')); ``` This will only delete the tmp dir. diff --git a/docs/recipes/incremental-builds-with-concatenate.md b/docs/recipes/incremental-builds-with-concatenate.md index a1435eb8a..c9fdaa0de 100644 --- a/docs/recipes/incremental-builds-with-concatenate.md +++ b/docs/recipes/incremental-builds-with-concatenate.md @@ -27,7 +27,7 @@ gulp.task('scripts', function() { }); gulp.task('watch', function () { - var watcher = gulp.watch(scriptsGlob, ['scripts']); // watch the same files in our scripts task + var watcher = gulp.watch(scriptsGlob, gulp.series('scripts')); // watch the same files in our scripts task watcher.on('change', function (event) { if (event.type === 'deleted') { // if a file is deleted, forget about it delete cached.caches.scripts[event.path]; // gulp-cached remove api diff --git a/docs/recipes/maintain-directory-structure-while-globbing.md b/docs/recipes/maintain-directory-structure-while-globbing.md index 95d644691..aadd32ef4 100644 --- a/docs/recipes/maintain-directory-structure-while-globbing.md +++ b/docs/recipes/maintain-directory-structure-while-globbing.md @@ -27,7 +27,7 @@ If you want to maintain the structure, you need to pass `{base: '.'}` to `gulp.s ```js gulp.task('task', function () { - gulp.src(['index.html', + return gulp.src(['index.html', 'css/**', 'js/**', 'lib/**', diff --git a/docs/recipes/make-stream-from-buffer.md b/docs/recipes/make-stream-from-buffer.md index 1125451f6..f11c11612 100644 --- a/docs/recipes/make-stream-from-buffer.md +++ b/docs/recipes/make-stream-from-buffer.md @@ -35,7 +35,6 @@ A simple and modular way to do this would be the following: ```js var gulp = require('gulp'); -var runSequence = require('run-sequence'); var source = require('vinyl-source-stream'); var vinylBuffer = require('vinyl-buffer'); var tap = require('gulp-tap'); @@ -50,13 +49,13 @@ var memory = {}; // we'll keep our assets in memory gulp.task('load-lib-files', function() { // read the lib files from the disk return gulp.src('src/libs/*.js') - // concatenate all lib files into one - .pipe(concat('libs.concat.js')) - // tap into the stream to get each file's data - .pipe(tap(function(file) { - // save the file contents in memory - memory[path.basename(file.path)] = file.contents.toString(); - })); + // concatenate all lib files into one + .pipe(concat('libs.concat.js')) + // tap into the stream to get each file's data + .pipe(tap(function(file) { + // save the file contents in memory + memory[path.basename(file.path)] = file.contents.toString(); + })); }); gulp.task('load-versions', function() { @@ -111,30 +110,29 @@ gulp.task('write-versions', function() { }); //============================================ our main task -gulp.task('default', function(taskDone) { - runSequence( - ['load-lib-files', 'load-versions'], // load the files in parallel - 'write-versions', // ready to write once all resources are in memory - taskDone // done - ); -}); +gulp.task('default', gulp.series( + // load the files in parallel + gulp.parallel('load-lib-files', 'load-versions'), + // ready to write once all resources are in memory + 'write-versions' + ) +); //============================================ our watcher task // only watch after having run 'default' once so that all resources // are already in memory -gulp.task('watch', ['default'], function() { - gulp.watch('./src/libs/*.js', function() { - runSequence( - 'load-lib-files', // we only have to load the changed files +gulp.task('watch', gulp.series( + 'default', + function() { + gulp.watch('./src/libs/*.js', gulp.series( + 'load-lib-files', 'write-versions' - ); - }); + )); - gulp.watch('./src/versions/*.js', function() { - runSequence( - 'load-versions', // we only have to load the changed files + gulp.watch('./src/versions/*.js', gulp.series( + 'load-lib-files', 'write-versions' - ); - }); -}); + )); + } +)); ``` diff --git a/docs/recipes/mocha-test-runner-with-gulp.md b/docs/recipes/mocha-test-runner-with-gulp.md index 69788aad8..775e3a722 100644 --- a/docs/recipes/mocha-test-runner-with-gulp.md +++ b/docs/recipes/mocha-test-runner-with-gulp.md @@ -28,13 +28,13 @@ var gulp = require('gulp'); var mocha = require('gulp-mocha'); var gutil = require('gulp-util'); -gulp.task('default', function() { - gulp.watch(['lib/**', 'test/**'], ['mocha']); -}); - gulp.task('mocha', function() { return gulp.src(['test/*.js'], { read: false }) .pipe(mocha({ reporter: 'list' })) .on('error', gutil.log); }); + +gulp.task('watch-mocha', function() { + gulp.watch(['lib/**', 'test/**'], gulp.series('mocha')); +}); ``` diff --git a/docs/recipes/running-tasks-in-series.md b/docs/recipes/running-tasks-in-series.md index 4ac2e6c43..5dfad24d0 100644 --- a/docs/recipes/running-tasks-in-series.md +++ b/docs/recipes/running-tasks-in-series.md @@ -1,68 +1,89 @@ -# Running tasks in series, i.e. Task Dependency +# Running tasks in series -By default, tasks run with maximum concurrency -- e.g. it launches all the tasks at once and waits for nothing. If you want to create a series where tasks run in a particular order, you need to do two things: - -- give it a hint to tell it when the task is done, -- and give it a hint that a task depends on completion of another. - -For these examples, let's presume you have two tasks, "one" and "two" that you specifically want to run in this order: - -1. In task "one" you add a hint to tell it when the task is done. Either take in a callback and call it when you're done or return a promise or stream that the engine should wait to resolve or end respectively. - -2. In task "two" you add a hint telling the engine that it depends on completion of the first task. - -So this example would look like: +By default, gulp CLI run tasks with maximum concurrency - e.g. it launches +all the tasks at once and waits for nothing. If you want to create a series +where tasks run in a particular order, you should use `gulp.series`; ```js var gulp = require('gulp'); +var doAsyncStuff = require('./stuff'); -// takes in a callback so the engine knows when it'll be done -gulp.task('one', function (cb) { - // do stuff -- async or otherwise - fs.writeFile('filename', 'data', opts, function (err) { - cb(err); // if err is not null and not undefined, the orchestration will stop, and 'two' will not run - }); +gulp.task('one', function(done) { + doAsyncStuff(function(err){ + done(err); + }); }); -// identifies a dependent task must be complete before this one begins -gulp.task('two', ['one'], function() { - // task 'one' is done now +gulp.task('two', function(done) { + // do things + done(); }); -gulp.task('default', ['one', 'two']); -// alternatively: gulp.task('default', ['two']); +gulp.task('default', gulp.series('one', 'two')); ``` -Another example, which returns the stream instead of using a callback: - +Another example, using a dependency pattern. It uses +[`async-once`](https://www.npmjs.com/package/async-once) to run the `clean` +task operations only once: ```js var gulp = require('gulp'); var del = require('del'); // rm -rf - -gulp.task('clean', function() { - return del(['output']); +var once = require('async-once'); + +gulp.task('clean', once(function(done) { + // run only once. + // for the next call to the clean task, once will call done with + // the same arguments as the first call. + del(['output'], done); +})); + +gulp.task('templates', gulp.series('clean', function() { + return gulp.src(['src/templates/*.hbs']) + // do some concatenation, minification, etc. + .pipe(gulp.dest('output/templates/')); }); -gulp.task('templates', ['clean'], function() { - var stream = gulp.src(['src/templates/*.hbs']) - // do some concatenation, minification, etc. - .pipe(gulp.dest('output/templates/')); - return stream; // return the stream as the completion hint +gulp.task('styles', gulp.series('clean', function() { + return gulp.src(['src/styles/app.less']) + // do some hinting, minification, etc. + .pipe(gulp.dest('output/css/app.css')); +})); + +// templates and styles will be processed in parallel. +// `clean` will be guaranteed to complete before either start. +// `clean` operations will not be run twice, +// even though it is called as a dependency twice. +gulp.task('build', gulp.parallel('templates', 'styles')); + +// an alias. +gulp.task('default', gulp.parallel('build')); +``` + +Note that it's an anti-pattern in Gulp 4 and the logs will show the clean task +running twice. Instead, `templates` and `style` should use dedicated `clean:*` +tasks: +```js +var gulp = require('gulp'); +var del = require('del'); +gulp.task('clean:templates', function() { + return del(['output/templates/']); }); -gulp.task('styles', ['clean'], function() { - var stream = gulp.src(['src/styles/app.less']) - // do some hinting, minification, etc. - .pipe(gulp.dest('output/css/app.css')); - return stream; +gulp.task('templates', gulp.series('clean:templates', function() { + return gulp.src(['src/templates/*.hbs']) + .pipe(gulp.dest('output/templates/')); }); -gulp.task('build', ['templates', 'styles']); +gulp.task('clean:styles', function() { + return del(['output/css/']); +}); -// templates and styles will be processed in parallel. -// clean will be guaranteed to complete before either start. -// clean will not be run twice, even though it is called as a dependency twice. +gulp.task('styles', gulp.series('clean:styles', function() { + return gulp.src(['src/styles/app.less']) + .pipe(gulp.dest('output/css/app.css')); +})); -gulp.task('default', ['build']); +gulp.task('build', gulp.parallel('templates', 'styles')); +gulp.task('default', gulp.parallel('build')); ``` diff --git a/docs/recipes/server-with-livereload-and-css-injection.md b/docs/recipes/server-with-livereload-and-css-injection.md index 7a14a667b..74280505d 100644 --- a/docs/recipes/server-with-livereload-and-css-injection.md +++ b/docs/recipes/server-with-livereload-and-css-injection.md @@ -88,14 +88,14 @@ gulp.task('sass', function() { }); // watch Sass files for changes, run the Sass preprocessor with the 'sass' task and reload -gulp.task('serve', ['sass'], function() { +gulp.task('serve', gulp.series('sass', function() { browserSync({ server: { baseDir: 'app' } }); - gulp.watch('app/scss/*.scss', ['sass']); + gulp.watch('scss/*.scss', gulp.series('sass')); }); ``` diff --git a/docs/recipes/using-external-config-file.md b/docs/recipes/using-external-config-file.md index 5c83779d9..6c740761b 100644 --- a/docs/recipes/using-external-config-file.md +++ b/docs/recipes/using-external-config-file.md @@ -30,9 +30,11 @@ Beneficial because it's keeping tasks DRY and config.json can be used by another ###### `gulpfile.js` ```js -// npm install --save-dev gulp gulp-uglify +// npm install --save-dev gulp gulp-uglify merge-stream var gulp = require('gulp'); var uglify = require('gulp-uglify'); +var merge = require('merge-stream'); + var config = require('./config.json'); function doStuff(cfg) { @@ -42,7 +44,10 @@ function doStuff(cfg) { } gulp.task('dry', function() { - doStuff(config.desktop); - doStuff(config.mobile); + // return a stream to signal completion + return merge([ + doStuff(config.desktop), + doStuff(config.mobile) + ]) }); ```