Gulp is a build tool for automating repeat tasks
cross platform
streaming task runner
Tasks are codes that do something. "defaultTask" is the default task
function someTask(callback) {... callback();}
Tasks can be organized using composite tasks (series(), parallel()).
Gulp can reads/watches files (src(), watch())as Node object streams of Vinyl objects. Then can pipes streams to different Stream.Transforms for processing. A Transform is a Duplex streams (both input & output) implementing both Readable & Writable interfaces. The _transform method can accept a Vinyl as chunk and process as required. A Transform can operate the file (change name, content, etc.) and the stream (filter, etc.)
src('src/*.js').pipe(...).pipe(...).pipe(dest('output/')) #typical use
Gulp plugins (just JS modules) provide reusable codes.
Destination (dest()) finally write files into destination.
See https://medium.com/@bingren/how-to-implement-a-stream-transform-for-gulp-f0e5341585b7
# requires node, npm, npx (run with --version)
npm install --global gulp-cli
npx mkdirp my-project
cd my-project
npm init
npm install --save-dev gulp # install local
gulp --version
# create a gulp file 'gulpfile.js'
gulp # test gulp file
gulp <task> <othertask>...
language
JS: gulpfile.js
TS: gulpfile.ts (install ts-node module)
other transpilation available
can use
JS & Node modules
gulp APIs like src(), dest(), series(), parallel()
organize
start form single file
can split into modules with node/ts module system
exported functions are public tasks
function defaultTask(cb) {
// place code for your default task here
cb();
}
function private(cb) {
// body omitted
cb();
}
// public
function build(cb) {
// body omitted
cb();
}
exports.build = build;
exports.default = defaultTask;
exports.seriesTask = series(clean, build);
Is asynchronous JS function
no "synchronous tasks", all asynchronous tasks
'function someTask(callback){}' or 'async function...' (in which can use await')
exported : public, otherwise private
async completion (single task)
when
success without returned obj: callback()
success with returned obj: when one of below is returned
error: callback(new Error('something wrong'))
delegate (by passing as parameter) callback to another API: "fs.access('somefile.js', callback)"
returns
stream - example "src('*.js').pipe(dest('output'));"
promise
event emitter - see https://nodejs.org/api/events.html
child process - see https://nodejs.org/api/child_process.html
observable - see https://www.learnrxjs.io/
composition tasks (and completion)
series(task1, task2, task3) - error end composition
parallel(task1, task2, task3) - error end composition but other tasks may or may not complete
Vinyl - a meta object describes a file, properties are:
path
base - the glob base (see below)
stat - the file system stats
content
Vinyl adapters allow access to file described by Vinyl in file sources. More to see https://gulpjs.com/docs/en/api/vinyl
Glob is a string of file path with optional wildcards (*) to match files. Globbing - verb, locating files using one or more globs.
/ is separator (even in Windows),
\\ escape character, e.g. "\\*" literal *
* wildcard
** 0~many characters, across segments
! negative (remove from previous results), use in an array, e.g. ['srcipt/**/*.js', '!script/vendor/']
overlapping globs:
gulp do best to remove duplicates (don't repeat exact same tasks on same file)
if separate src() added overlapping globs, keep all (pipes are different, earlier one do the full and later one partial)
glob base: unchangeable segments before the first special character, e.g. the glob base of /src/js/**.js is /src/js/. In Vinyl instances the .base property is set with the glob base. dest() will remove the base from the output path
src(filePattern) - locate all matching files & pass as stream (Node.js stream in object mode) of Vinyl objects
src('src/*.js').pipe(...).pipe(...).pipe(dest('output/')) #typical use
when used in the middle of pipeline src() add files to the stream based on given globs and the added (additional) files are only available to transforms later in the stream. if globs overlap, files will be added again.
dest(outputDir) - given output dir and return stream that consume Vinyl objects
symlink() - creates symbol links
Node stream 'readable.pipe(destination, [, options])' method - destnation is <stream.Writable> and options are pipe options, now just 'end' <boolean> if to end the writer when reader ends, default true, automatically switch the readable into flowing mode and push all data to the destination writable.
https://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
watch('src/*.css', cssTask); - watch files, then run task. WARNING: task cannot be synchronous. Can select watched events like below. Also can set 'ignoreInitial' (default true) to false to execute task before change
// The available events are 'add', 'addDir', 'change', 'unlink', 'unlinkDir', 'ready', 'error'. Additionally 'all' is available, which represents all events other than 'ready' and 'error'.
watch('src/*.js', { events: 'all' }, function(cb) {
// body omitted
cb();
});
// The task will be executed upon startup
watch('src/*.js', { ignoreInitial: false }, function(cb) {
// body omitted
cb();
});
// The task will be run (concurrently) for every change made
watch('src/*.js', { queue: false }, function(cb) {
// body omitted
cb();
});
// The task won't be run until 500ms have elapsed since the first change
watch('src/*.js', { delay: 500 }, function(cb) {
// body omitted
cb();
});
series()
parallel()
lastRun(taskFunction) - return the last time a task was successfully run, to skip unchanged files
task() - not recommended anymore
registry() - ??
tree() - fetch the task dependency tree
Plugins are "Node Transform Streams" that (should always) transform files in a pipeline. They can change
names
contents
metadata
const { src, dest } = require('gulp');
const uglify = require('gulp-uglify');
const rename = require('gulp-rename');
exports.default = function() {
return src('src/*.js')
// The gulp-uglify plugin won't update the filename
.pipe(uglify())
// So use gulp-rename to change the extension
.pipe(rename({ extname: '.min.js' }))
.pipe(dest('output/'));
}
const { src, dest } = require('gulp');
const uglify = require('uglify-js');
const through2 = require('through2');
exports.default = function() {
return src('src/*.js')
// Instead of using gulp-uglify, you can create an inline plugin
.pipe(through2.obj(function(file, _, cb) {
if (file.isBuffer()) {
const code = uglify.minify(file.contents.toString())
file.contents = Buffer.from(code)
}
cb(null, file);
}))
.pipe(dest('output/'));
}
See https://medium.com/@bingren/how-to-implement-a-stream-transform-for-gulp-f0e5341585b7 for an implementation example.
gulp-concat: concatenate all files in the stream into a single file
gulp-append : [don't know what does this do... not useful as its name suggests. use gulp-append-prepend].
gulp.dest (the default 'dest') - dest({append:true}) does not work for 3.xx thus not good for slush)
gulp-template: <%= name %>, using lodash template
gulp-conflict: check file in stream conflict with destination, with options to use new, keep old, show diff, etc., use before dest()
gulp-install: automatically install npm, bower, tsd, typing, composer & pip packages/dependencies if configurations found in the gulp file stream
gulp-rename: rename
gulp-debug: print out files and processes for debug
gulp-append-prepend: append / prepend contents of file in file system to the current file. So it's good to add something (same for every file maybe) to all files in the stream.
gulp-append-data: for template file such as "a.html", find corresponding "a.json" as data, attach to the streaming Vinyl object (so accessible at object.data) then use gulp-consolidate to compile data with template or for other use.
through2 - https://github.com/rvagg/through2 wrapper for easy writing inline plugins.
https://github.com/gulpjs/gulp/blob/master/docs/recipes/README.md