diff --git a/API.md b/API.md index 7d2eee1..46e5e08 100644 --- a/API.md +++ b/API.md @@ -1,9 +1,15 @@ ## gulp API 文档 +跳转: + [gulp.src](#gulpsrcglobs-options) | + [gulp.dest](#gulpdestpath-options) | + [gulp.task](#gulptaskname--deps--fn) | + [gulp.watch](#gulpwatchglob--opts-tasks-or-gulpwatchglob--opts-cb) + ### gulp.src(globs[, options]) 输出(Emits)符合所提供的匹配模式(glob)或者匹配模式的数组(array of globs)的文件。 -将返回一个 [Vinyl files](https://github.com/wearefractal/vinyl-fs) 的 [stream](http://nodejs.org/api/stream.html) +将返回一个 [Vinyl files](https://github.com/gulpjs/vinyl-fs) 的 [stream](http://nodejs.org/api/stream.html) 它可以被 [piped](http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options) 到别的插件中。 ```javascript @@ -18,28 +24,39 @@ gulp.src('client/templates/*.jade') #### globs 类型: `String` 或 `Array` -所要读取的 glob 或者包含 globs 的数组。 +所读取的 glob 或者 glob 数组,除了 negation(`!`) 以外的 [node-glob 语法] 均被支持。 + +一个 `!` 开头的 glob 会在结果中排除掉到这个地方为止的所匹配到的文件。举个例子,考虑如下的目录结构: + + client/ + a.js + bob.js + bad.js + +下面的表达式匹配到的结果是 `a.js` 和 `bad.js`: + + gulp.src(['client/*.js', '!client/b*.js', 'client/bad.js']) #### options 类型: `Object` 通过 [glob-stream] 所传递给 [node-glob] 的参数。 -除了 [node-glob][node-glob 文档] 和 [glob-stream] 所支持的参数外,gulp 增加了一些额外的选项参数: +除了 [node-glob][node-glob 文档] 和 [glob-stream] 所支持的参数(除了 `ignore`)外,gulp 增加了一些额外的选项参数: -#### options.buffer +##### options.buffer 类型: `Boolean` 默认值: `true` 如果该项被设置为 `false`,那么将会以 stream 方式返回 `file.contents` 而不是文件 buffer 的形式。这在处理一些大文件的时候将会很有用。**注意:**插件可能并不会实现对 stream 的支持。 -#### options.read +##### options.read 类型: `Boolean` 默认值: `true` 如果该项被设置为 `false`, 那么 `file.contents` 会返回空值(null),也就是并不会去读取文件。 -#### options.base +##### options.base 类型: `String` 默认值: 将会加在 glob 之前 (请看 [glob2base]) @@ -73,24 +90,24 @@ gulp.src('./client/templates/*.jade') #### path 类型: `String` or `Function` -文件将被写入的路径(输出目录)。也可以传入一个函数,在函数中返回相应路径,这个函数也可以由 [vinyl 文件实例](https://github.com/wearefractal/vinyl) 来提供。 +文件将被写入的路径(输出目录)。也可以传入一个函数,在函数中返回相应路径,这个函数也可以由 [vinyl 文件实例](https://github.com/gulpjs/vinyl) 来提供。 #### options 类型: `Object` -#### options.cwd +##### options.cwd 类型: `String` 默认值: `process.cwd()` 输出目录的 `cwd` 参数,只在所给的输出目录是相对路径时候有效。 -#### options.mode +##### options.mode 类型: `String` 默认值: `0777` 八进制权限字符,用以定义所有在输出目录中所创建的目录的权限。 -### gulp.task(name[, deps], fn) +### gulp.task(name [, deps] [, fn]) 定义一个使用 [Orchestrator] 实现的任务(task)。 @@ -101,6 +118,7 @@ gulp.task('somename', function() { ``` #### name +类型:`String` 任务的名字,如果你需要在命令行中运行你的某些任务,那么,请不要在名字中使用空格。 @@ -126,8 +144,21 @@ gulp.task('mytask', ['array', 'of', 'task', 'names']); **注意:** 这些任务会一次并发执行,因此,请不要假定他们会按顺序开始和结束。 #### fn +类型:`Function` -该函数定义任务所要执行的一些操作。通常来说,它会是这种形式:`gulp.src().pipe(someplugin())`。 +该函数定义任务所要执行的主要操作。通常来说,它会是这种形式: + +```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; + }); +``` #### 异步任务支持 @@ -136,15 +167,26 @@ gulp.task('mytask', ['array', 'of', 'task', 'names']); ##### 接受一个 callback ```javascript -// 在 shell 中执行一个命令 +// 在 shell 中运行一个命令 var exec = require('child_process').exec; gulp.task('jekyll', function(cb) { - // 编译 Jekyll + // 构建 Jekyll exec('jekyll build', function(err) { - if (err) return cb(err); // 返回 error + if (err) return cb(err); // return error cb(); // 完成 task }); }); + +// 在 pipe 中使用异步的结果 +gulp.task('somename', function(cb) { + getFilesAsync(function(err, res) { + if (err) return cb(err); + var stream = gulp.src(res) + .pipe(minify()) + .pipe(gulp.dest('build')) + .on('end', cb); + }); +}); ``` ##### 返回一个 stream @@ -262,17 +304,16 @@ callback 会被传入一个名为 `event` 的对象。这个对象描述了所 ##### event.type 类型: `String` -发生的变动的类型:`added`, `changed` 或者 `deleted`。 +发生的变动的类型:`added`, `changed`, `deleted` 或者 `renamed`。 ##### event.path 类型: `String` 触发了该事件的文件的路径。 - -[node-glob 文档]: https://github.com/isaacs/node-glob#options [node-glob]: https://github.com/isaacs/node-glob -[glob-stream]: https://github.com/wearefractal/glob-stream +[node-glob 文档]: https://github.com/isaacs/node-glob#options +[node-glob 语法]: https://github.com/isaacs/node-glob [gulp-if]: https://github.com/robrich/gulp-if [Orchestrator]: https://github.com/robrich/orchestrator [glob2base]: https://github.com/wearefractal/glob2base diff --git a/README.md b/README.md index 6cf927e..ccc126a 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ ## 文章(英文) -* [Tagtree intro to gulp video](http://tagtree.tv/gulp) +* [Tagtree intro to gulp video](http://tagtree.io/gulp) * [Introduction to node.js streams](https://github.com/substack/stream-handbook) * [Video introduction to node.js streams](http://www.youtube.com/watch?v=QgEuZ52OZtU) * [Getting started with gulp (by @markgdyr)](http://markgoodyear.com/2014/01/getting-started-with-gulp/) @@ -39,6 +39,7 @@ * [Gulp - The Basics (screencast)](https://www.youtube.com/watch?v=dwSLFai8ovQ) * [Get started with gulp (video series)](http://www.youtube.com/playlist?list=PLRk95HPmOM6PN-G1xyKj9q6ap_dc9Yckm) * [Optimize your web code with gulp](http://www.linuxuser.co.uk/tutorials/optimise-your-web-code-with-gulp-js) +* [Automate Your Tasks Easily with Gulp.js ](https://scotch.io/tutorials/automate-your-tasks-easily-with-gulp-js) ## 例子 diff --git a/getting-started.md b/getting-started.md index 3d54772..b4802c2 100644 --- a/getting-started.md +++ b/getting-started.md @@ -2,8 +2,10 @@ #### 1. 全局安装 gulp: +__如果你之前有全局安装过一个版本的 gulp,请执行一下 `npm rm --global gulp` 来避免和 gulp-cli 冲突__ + ```sh -$ npm install --global gulp +$ npm install --global gulp-cli ``` #### 2. 作为项目的开发依赖(devDependencies)安装: diff --git a/recipes/README.md b/recipes/README.md index c71d6c5..43ae648 100644 --- a/recipes/README.md +++ b/recipes/README.md @@ -20,5 +20,8 @@ * [在一个任务中使用多个文件来源](using-multiple-sources-in-one-task.md) * [Browserify + Uglify2 和 sourcemaps](browserify-uglify-sourcemap.md) * [Browserify + Globs](browserify-with-globs.md) +* [Browserify + Globs (多个目标路径)](browserify-multiple-destination.md) * [同时输出一个压缩过和一个未压缩版本的文件](minified-and-non-minified.md) -* [Swig 亦即 YAML front-matter 模板](templating-with-swig-and-yaml-front-matter.md) +* [Swig 以及 YAML front-matter 模板](templating-with-swig-and-yaml-front-matter.md) +* [在 Gulp 中执行 Grunt 任务](run-grunt-tasks-from-gulp.md) +* [使用 ES2015 的 export 来写 tasks](exports-as-tasks.md) diff --git a/recipes/automate-release-workflow.md b/recipes/automate-release-workflow.md index ecee6c7..36ae63a 100644 --- a/recipes/automate-release-workflow.md +++ b/recipes/automate-release-workflow.md @@ -7,7 +7,7 @@ var gulp = require('gulp'); var runSequence = require('run-sequence'); -var conventionalChangelog = require('conventional-changelog'); +var conventionalChangelog = require('gulp-conventional-changelog'); var conventionalGithubReleaser = require('conventional-github-releaser'); var bump = require('gulp-bump'); var gutil = require('gulp-util'); diff --git a/recipes/browserify-multiple-destination.md b/recipes/browserify-multiple-destination.md new file mode 100644 index 0000000..0d18ea8 --- /dev/null +++ b/recipes/browserify-multiple-destination.md @@ -0,0 +1,45 @@ +# Browserify + Globs (多个目标路径) + +这个例子用来展示如何设置一个使用 browserify 打包多个输入并且有多个输出目的地的任务。 + +下边的 `js` 任务会打包所有在 `src/` 目录下所有的 `.js` 文件,然后输出每一个对应的文件到 `dest/` 目录。 + + +```js +var gulp = require('gulp'); +var browserify = require('browserify'); +var gutil = require('gulp-util'); +var tap = require('gulp-tap'); +var buffer = require('gulp-buffer'); +var sourcemaps = require('gulp-sourcemaps'); +var uglify = require('gulp-uglify'); + +gulp.task('js', function () { + + return gulp.src('src/**/*.js', {read: false}) // 不需要读取文件内容,browserify 会处理这个问题 + + // 使用 gulp-tap 转换文件内容 + .pipe(tap(function (file) { + + gutil.log('bundling ' + file.path); + + // 使用 browserify 的打包 stream 来替换文件 + file.contents = browserify(file.path, {debug: true}).bundle(); + + })) + + // 转换 stram 内容为 buff 内容(因为 gulp-sourcemaps 不支持 stream 形式的内容) + .pipe(buffer()) + + // 载入并初始化 sourcemaps + .pipe(sourcemaps.init({loadMaps: true})) + + .pipe(uglify()) + + // 写入 sourcemaps + .pipe(sourcemaps.write('./')) + + .pipe(gulp.dest('dest')); + +}); +``` diff --git a/recipes/combining-streams-to-handle-errors.md b/recipes/combining-streams-to-handle-errors.md index 12ddaae..727e654 100644 --- a/recipes/combining-streams-to-handle-errors.md +++ b/recipes/combining-streams-to-handle-errors.md @@ -1,6 +1,6 @@ # 整合 streams 来处理错误 -默认情况下,在 stream 中发生一个错误的话,它会被直接抛出,除非已经有一个时间监听器监听着 `error` 事件。 这在处理一个比较长的管道操作的时候会显得比较棘手。 +默认情况下,在 stream 中发生一个错误的话,它会被直接抛出,除非已经有一个事件监听器监听着 `error` 事件。 这在处理一个比较长的管道操作的时候会显得比较棘手。 通过使用 [stream-combiner2](https://github.com/substack/stream-combiner2),你可以将一系列的 stream 合并成一个,这意味着,你只需要在你的代码中一个地方添加监听器监听 `error` 事件就可以了。 diff --git a/recipes/exports-as-tasks.md b/recipes/exports-as-tasks.md new file mode 100644 index 0000000..8dfcf2e --- /dev/null +++ b/recipes/exports-as-tasks.md @@ -0,0 +1,22 @@ +# 使用 ES2015 的 export 来写 tasks + +使用 ES2015 模块语法,你可以使用 export 来写 task。 + +```js +import gulp from 'gulp'; +import babel from 'gulp-babel'; + +// 命名的 task +export function build() { + return gulp.src('src/*.js') + .pipe(babel()) + .pipe(gulp.dest('lib')); +} + +// 默认 task +export default function dev() { + gulp.watch('src/*.js', ['build']); +} +``` + +这个写法 **无法** 在 gulp 3.x 所打包的 gulp-cli 上工作。你必须使用最新版来尝试这个功能。 diff --git a/recipes/make-stream-from-buffer.md b/recipes/make-stream-from-buffer.md index d29d61b..c99b723 100644 --- a/recipes/make-stream-from-buffer.md +++ b/recipes/make-stream-from-buffer.md @@ -79,13 +79,14 @@ gulp.task('write-versions', function() { availableVersions.forEach(function(v) { // 以一个假文件名创建一个新的 stream var stream = source('final.' + v); + + var streamEnd = stream; + // 从拼接后的文件中读取数据 var fileContents = memory['libs.concat.js'] + // 增加版本文件的数据 '\n' + memory.versions[v]; - streams.push(stream); - // 将文件的内容写入 stream stream.write(fileContents); @@ -94,11 +95,14 @@ gulp.task('write-versions', function() { stream.end(); }); - stream + streamEnd = streamEnd // 转换原始数据到 stream 中去,到一个 vinyl 对象/文件 .pipe(vinylBuffer()) //.pipe(tap(function(file) { /* 这里可以做一些对文件内容的处理操作 */ })) .pipe(gulp.dest('output')); + + // 加到 stream 的尾部,不然,task 会在这些处理完成之前被结束 + streams.push(streamEnd); }); return es.merge.apply(this, streams); diff --git a/recipes/mocha-test-runner-with-gulp.md b/recipes/mocha-test-runner-with-gulp.md index 333c735..ee04b66 100644 --- a/recipes/mocha-test-runner-with-gulp.md +++ b/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/**'], ['mocha']); -}); ``` diff --git a/recipes/run-grunt-tasks-from-gulp.md b/recipes/run-grunt-tasks-from-gulp.md new file mode 100644 index 0000000..c406888 --- /dev/null +++ b/recipes/run-grunt-tasks-from-gulp.md @@ -0,0 +1,48 @@ +# 在 Gulp 中执行 Grunt 任务 + +在 Gulp 中运行 Grunt 任务或者 Grunt 插件是可能的。这在从 Grunt 迁移到 Gulp 阶段或者你对某个 Grunt 插件有需求的时候会非常有用。按照如下描述的方法,不需要引入 Grunt 命令行工具或者 Gruntfile。 + +**这个方法需要 Grunt 版本 >=1.0.0** + +非常简单的 `gulpfile.js` 示例: + +```js +// npm install gulp grunt grunt-contrib-copy --save-dev + +var gulp = require('gulp'); +var grunt = require('grunt'); + +grunt.initConfig({ + copy: { + main: { + src: 'src/*', + dest: 'dest/' + } + } +}); +grunt.loadNpmTasks('grunt-contrib-copy'); + +gulp.task('copy', function (done) { + grunt.tasks( + ['copy:main'], // 你可以在这个数组中加入更多的 Grunt 任务 + {gruntfile: false}, // 不查找 Gruntfile - 因为并没有. :-) + function () {done();} + ); +}); + +``` + +现在开始执行任务: +`gulp copy` + +通过上述方式,grunt 任务会被注册到 gulp 的任务系统中。**需要注意的是,grunt 任务通常是阻塞的(不像 gulp),因此在 grunt 任务执行完成之前,没有任何其他的任务可以执行(甚至 gulp 任务也不可以)**。 + + +### A few words on alternatives + +有一个 *gulp友好* 的 node 模块 `gulp-grunt` [可以使用](https://www.npmjs.org/package/gulp-grunt) ,它使用了一个不同的方案。它会创建一些子进程来执行 grunt 任务,这意味着它有一些限制: + +* 到现在为止,使用 `gulp-grunt` 还不能把类似命令行参数或者其他的选项参数传递到 grunt 任务中 +* 所有的 grunt 任务都需要被定义到一个独立的 Gruntfile +* 你需要安装 Grunt 命令行 +* 一些 grunt 任务的输出可能会有奇怪的格式(.i.e. color coding). diff --git a/recipes/running-task-steps-per-folder.md b/recipes/running-task-steps-per-folder.md index 92b864d..2dc6837 100644 --- a/recipes/running-task-steps-per-folder.md +++ b/recipes/running-task-steps-per-folder.md @@ -40,16 +40,16 @@ gulp.task('scripts', function() { var folders = getFolders(scriptsPath); var tasks = folders.map(function(folder) { - // 拼接成 foldername.js - // 写入输出 - // 压缩 - // 重命名为 folder.min.js - // 再一次写入输出 return gulp.src(path.join(scriptsPath, folder, '/*.js')) + // 拼接进 foldername.js .pipe(concat(folder + '.js')) + // 写入输出 .pipe(gulp.dest(scriptsPath)) + // 代码压缩 .pipe(uglify()) + // 重命名为 folder.min.js .pipe(rename(folder + '.min.js')) + // 再一次写入输出 .pipe(gulp.dest(scriptsPath)); }); diff --git a/recipes/specifying-a-cwd.md b/recipes/specifying-a-cwd.md index 4ad93cf..7a9418a 100644 --- a/recipes/specifying-a-cwd.md +++ b/recipes/specifying-a-cwd.md @@ -16,7 +16,7 @@ gulp --cwd layer1 ``` -如果你需要对特定的匹配指定一个 cwd,你可以使用 [glob-stream](https://github.com/wearefractal/glob-stream) 的 `cwd` 选项: +如果你需要对特定的匹配指定一个 cwd,你可以使用 [glob-stream](https://github.com/gulpjs/glob-stream) 的 `cwd` 选项: ```js gulp.src('./some/dir/**/*.js', { cwd: 'public' }); diff --git a/recipes/split-tasks-across-multiple-files.md b/recipes/split-tasks-across-multiple-files.md index 11dcd71..f0b3a49 100644 --- a/recipes/split-tasks-across-multiple-files.md +++ b/recipes/split-tasks-across-multiple-files.md @@ -1,6 +1,20 @@ # 分离任务到多个文件中 -如果你的 `gulpfile.js` 开始变得很大,你可以通过使用 [require-dir](https://github.com/aseemk/requireDir) 模块将任务分离到多个文件 +如果你的 `gulpfile.js` 开始变得很大,你可以通过使用下面的方法之一将任务分离到多个文件: + +> 注意,这个方法 [考虑弃用][deprecated] 了 +> 并且当迁移到 `gulp 4` 后会引发一些问题。 + +## 使用 `gulp-require-tasks` + +你可以使用 [gulp-require-tasks][gulp-require-tasks] +模块来自动载入所有单独文件中的任务。 + +请查看 [模块的 README][gulp-require-tasks] 获取最新的说明。 + +## 使用 `require-dir` + +你可以使用 [require-dir][require-dir] 模块来手动载入任务。 想象如下的文件结构: @@ -12,15 +26,20 @@ tasks/ └── test.js ``` -安装 `require-dir` 模块: +安装 `require-dir`: ```sh npm install --save-dev require-dir ``` -在 `gulpfile.js` 中增加如下几行代码: +在你的 `gulpfile.js` 文件中加入以下代码: ```js var requireDir = require('require-dir'); -var dir = requireDir('./tasks'); +var tasks = requireDir('./tasks'); ``` + + + [gulp-require-tasks]: https://github.com/betsol/gulp-require-tasks + [require-dir]: https://github.com/aseemk/requireDir + [deprecated]: https://github.com/gulpjs/gulp/pull/1554#issuecomment-202614391 diff --git a/writing-a-plugin/README.md b/writing-a-plugin/README.md index ac8be74..981da71 100644 --- a/writing-a-plugin/README.md +++ b/writing-a-plugin/README.md @@ -13,32 +13,175 @@ gulp 插件总是返回一个 [object mode](http://nodejs.org/api/stream.html#stream_object_mode) 形式的 stream 来做这些事情: -1. 接收 [vinyl File 对象](http://github.com/wearefractal/vinyl) -2. 输出 [vinyl File 对象](http://github.com/wearefractal/vinyl) - -这通常被叫做 [transform streams](http://nodejs.org/api/stream.html#stream_class_stream_transform_1) (有时候也叫做 through streams)。transform streams 是可读又可写的,它会对传给它的对象做一些转换的操作。 +1. 接收 [vinyl File 对象](http://github.com/gulpjs/vinyl) +2. 输出 [vinyl File 对象](http://github.com/gulpjs/vinyl) (通过 `transform.push()` 以及/或者插件的回调函数) + +这通常被叫做 [transform streams](http://nodejs.org/api/stream.html#stream_class_stream_transform_1) (有时候也叫做 through streams)。 +transform streams 是双工可读又可写的,它会对传给它的对象做一些转换的操作。 + +所有的插件本质上都可以归结成如此: + +```js +var Transform = require('transform'); + +module.exports = function() { + // 加 Monkey patch 或者创建你自己的子类 + // 实现 `_transform()` 和可选的 `_flush()` + var transformStream = new Transform({objectMode: true}); + /** + * @param {Buffer|string} 文件 + * @param {string=} 编码 - 如果文件包含 Buffer 则忽略 + * @param {function(Error, object)} 回调 - 当对传递进来的 chunk 处理完成后执行(可选的一个错误参数和数据) + */ + transformStream._transform = function(file, encoding, callback) { + var error = null, + output = doSomethingWithTheFile(file); + callback(error, output); + }; + + return transformStream; +}; +``` + +很多插件使用 [through2](https://github.com/rvagg/through2/) 模块来简化代码: + +```js +var through = require('through2'); // npm install --save through2 + +module.exports = function() { + return through.obj(function(file, encoding, callback) { + callback(null, doSomethingWithTheFile(file)); + }); +}; +``` + +`through()` 所返回的 stream (以及你 transform 函数中的 `this`) 都是 [Transform](https://github.com/iojs/readable-stream/blob/master/lib/_stream_transform.js) 类的实例,它继承自 [Duplex](https://github.com/iojs/readable-stream/blob/master/lib/_stream_duplex.js), +[Readable](https://github.com/iojs/readable-stream/blob/master/lib/_stream_readable.js) +(并寄生自 Writable) 最终继承 [Stream](https://nodejs.org/api/stream.html). +如果你需要处理额外的参数,你可以直接调用 `through()` 函数: + +```js + return through({objectMode: true /* other options... */}, function(file, encoding, callback) { ... +``` + +支持的参数包括: + +* highWaterMark (defaults to 16) +* defaultEncoding (defaults to 'utf8') +* encoding - 'utf8', 'base64', 'utf16le', 'ucs2' etc. + 指定后, 一个 [StringDecoder](https://github.com/rvagg/string_decoder/blob/master/index.js) `decoder` 会被加到这个 stream。 +* readable {boolean} +* writable {boolean} +* allowHalfOpen {boolean} 如果为 false,那么 stream 会在 sriteable 结束的时候自动结束 readable,反之亦然。 ### 修改文内容 +传递给 `through.obj()` 的参数是一个 [_transform](https://nodejs.org/api/stream.html#stream_transform_transform_chunk_encoding_callback) +函数,它将对输入的 `file` 做一些操作,你可能也将提供一个可选 [_flush](https://nodejs.org/api/stream.html#stream_transform_flush_callback) +函数,如果你需要在结束时候发送更多的数据的话。 + +从 transform 函数内部调用 `this.push(file)` 0 次或者多次来传递 transformed/cloned 的文件. +如果你把所有输出提供给 `callback()` 那么,你不需要调用 `this.push(file)`。 + +只在当前文件(stream/buffer) 被完全的消费后才调用 `callback` 函数 +如果发生了一个错误,则将它以第一个参数的形式传递给回调函数,否则,将第一个参数设置为 null。 +如果你已经将所有输出的内容传递给了 `this.push()` 那么,你可以省略回调函数的第二个参数。 + +通常,一个 gulp 插件会更新 `file.contents` 然后,选择以下一个: + + - call `callback(null, file)` + _或_ + - 调用 `this.push(file)` + +如果一个插件为一个输入的文件创建了多个输出文件,它会多次调用 `this.push()` - 如: + +```js +module.exports = function() { + /** + * @this {Transform} + */ + var transform = function(file, encoding, callback) { + var files = splitFile(file); + this.push(files[0]); + this.push(files[1]); + callback(); + }; + + return through.obj(transform); +}; +``` + +[gulp-unzip](https://github.com/suisho/gulp-unzip/blob/master/index.js) 插件提供了一个很好的例子,来演示如何多次调用 `push()`。它同时也使用了一个 chunk transform stream 和 _在_ Vinyl transform 函数中. `_flush()` 函数 + Vinyl 文件可以通过三种不同形式来访问文件内容: - [Streams](dealing-with-streams.md) - [Buffers](using-buffers.md) - 空 (null) - 对于删除, 清理, 等操作来说,会很有用,因为这时候内容是不需要处理的。 +以下是一个简单的例子来展示如何检测和处理每一种形式,更多的细节请参考上面的链接。 + +```js +var PluginError = require('gulp-util').PluginError; + +// 常量声明 +var PLUGIN_NAME = 'gulp-example'; + +module.exports = function() { + return through.obj(function(file, encoding, callback) { + if (file.isNull()) { + // 不做处理 + return callback(null, file); + } + + if (file.isStream()) { + // file.contents 是一个 Stream - https://nodejs.org/api/stream.html + this.emit('error', new PluginError(PLUGIN_NAME, 'Streams not supported!')); + + // 或者, 你可以这样处理: + //file.contents = file.contents.pipe(... + //return callback(null, file); + } else if (file.isBuffer()) { + // file.contents 是 Buffer - https://nodejs.org/api/buffer.html + this.emit('error', new PluginError(PLUGIN_NAME, 'Buffers not supported!')); + + // 或者, 你可以这样处理: + //file.contents = ... + //return callback(null, file); + } + }); +}; +``` + +注意:当你查看其他的插件的代码时候(以及上面的例子),你会注意到, transform 函数会返回 callback: + +```js +return callback(null, file); +``` + +...不要被困扰 - gulp 会忽略所有 transform 函数返回的结果,上面的例子只是以下代码的简写方式: + +```js +if (someCondition) { + callback(null, file); + return; +} +// 进一步执行... +``` + ## 有用的资源 -* [File object](https://github.com/wearefractal/gulp-util/#new-fileobj) +* [File object](https://github.com/gulpjs/gulp-util/#new-fileobj) * [PluginError](https://github.com/gulpjs/gulp-util#new-pluginerrorpluginname-message-options) * [event-stream](https://github.com/dominictarr/event-stream) * [BufferStream](https://github.com/nfroidure/BufferStream) -* [gulp-util](https://github.com/wearefractal/gulp-util) +* [gulp-util](https://github.com/gulpjs/gulp-util) ## 插件范例 * [sindresorhus' gulp plugins](https://github.com/search?q=%40sindresorhus+gulp-) -* [Fractal's gulp plugins](https://github.com/search?q=%40wearefractal+gulp-) +* [contra's gulp plugins](https://github.com/search?q=%40contra+gulp-) * [gulp-replace](https://github.com/lazd/gulp-replace) diff --git a/writing-a-plugin/guidelines.md b/writing-a-plugin/guidelines.md index b050309..2842edd 100644 --- a/writing-a-plugin/guidelines.md +++ b/writing-a-plugin/guidelines.md @@ -13,7 +13,7 @@ - 避免使用配置选项,使得你的插件能胜任不同场合的任务。 - 比如:一个 JS 压缩插件不应该有一个加头部的选项 1. 你的插件不能去做一些其他插件做的事: - - 不应该去拼接,用 [gulp-concat](https://github.com/wearefractal/gulp-concat) 去做 + - 不应该去拼接,用 [gulp-concat](https://github.com/contra/gulp-concat) 去做 - 不应该去增加头部,用 [gulp-header](https://github.com/godaddy/gulp-header) 去做 - 不应该去增加尾部,用 [gulp-footer](https://github.com/godaddy/gulp-footer) 去做 - 如果是一个常用的可选的操作,那么,请在文档中注明你的插件通常和其他某个插件一起使用 @@ -22,7 +22,7 @@ - 测试一个插件很简单,你甚至不需要 gulp 就能测试 - 参考其他的插件是怎么做的 1. 在 `package.json` 中增加一个名为 `gulpplugin` 的关键字,这可以让它能在我们的搜索中出现 -1. 不要再 stream 里面抛出错误 +1. 不要在 stream 里面抛出错误 - 你应该以触发 **error** 事件来代替 - 如果你在 stream 外面遇到错误,比如在创建 stream 时候发现错误的配置选项等,那么你应该抛出它。 1. 错误需要加上以你插件名字作为前缀 @@ -33,7 +33,7 @@ - 如果 file.contents 是一个 stream,但是你不支持,那么请触发一个错误 - 不要把 stream 硬转成 buffer 来使你的插件支持 stream,这会引发很严重的问题。 1. 在你处理完成之前,不要将 `file` 传到下游去 -1. 使用 [`file.clone()`](https://github.com/wearefractal/vinyl#clone) 来复制一个文件或者创建另一个以此为基础的文件 +1. 使用 [`file.clone()`](https://github.com/gulpjs/vinyl#clone) 来复制一个文件或者创建另一个以此为基础的文件 1. 使用我们 [模块推荐页](recommended-modules.md) 上列举的模块来让你的开发更加轻松 1. 不要把 `gulp` 作为一个依赖 - 使用 gulp 来测试你的插件的工作流这的确很酷,但请务必确保你将它放到 devDependency 中 diff --git a/writing-a-plugin/recommended-modules.md b/writing-a-plugin/recommended-modules.md index 3d8a2a7..ba2e1ac 100644 --- a/writing-a-plugin/recommended-modules.md +++ b/writing-a-plugin/recommended-modules.md @@ -10,7 +10,7 @@ #### 错误 -当它完成时使用 [BetterError](https://github.com/wearefractal/BetterError) +使用 [BetterError](https://github.com/contra/BetterError) 如果完成了的话。 #### 字符串颜色 diff --git a/writing-a-plugin/testing.md b/writing-a-plugin/testing.md index 48bda92..58ec42c 100644 --- a/writing-a-plugin/testing.md +++ b/writing-a-plugin/testing.md @@ -98,4 +98,4 @@ describe('gulp-prefixer', function() { ## 一些拥有高质量的测试用例的插件 * [gulp-cat](https://github.com/ben-eb/gulp-cat/blob/master/test.js) -* [gulp-concat](https://github.com/wearefractal/gulp-concat/blob/master/test/main.js) +* [gulp-concat](https://github.com/contra/gulp-concat/blob/master/test/main.js) diff --git a/writing-a-plugin/using-buffers.md b/writing-a-plugin/using-buffers.md index 9276be6..398ccbb 100644 --- a/writing-a-plugin/using-buffers.md +++ b/writing-a-plugin/using-buffers.md @@ -67,7 +67,7 @@ gulp.src('files/**/*.js') ## 一些基于 buffer 的插件 -* [gulp-coffee](https://github.com/wearefractal/gulp-coffee) +* [gulp-coffee](https://github.com/contra/gulp-coffee) * [gulp-svgmin](https://github.com/ben-eb/gulp-svgmin) * [gulp-marked](https://github.com/lmtm/gulp-marked) * [gulp-svg2ttf](https://github.com/nfroidure/gulp-svg2ttf)