initial commit

This commit is contained in:
tmshv committed 2014-02-12 12:36:59 +04:00
commit d3fcd7f10c
3159 files changed
+349702

No files matched your search

+232
View File
@@ -0,0 +1,232 @@
## gulp API docs
### gulp.src(globs[, options])
Takes a glob and represents a file structure. Can be piped to plugins.
```javascript
gulp.src('./client/templates/*.jade')
.pipe(jade())
.pipe(minify())
.pipe(gulp.dest('./build/minified_templates'));
```
#### globs
Type: `String` or `Array`
Glob or globs to read.
#### options
Type: `Object`
Options to pass to [node-glob] through [glob-stream].
gulp adds two additional options in addition to the [options supported by node-glob][node-glob documentation]:
#### 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 may 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.
### gulp.dest(path)
Can be piped to and it will write files. Re-emits all data passed to it so you can pipe to multiple folders.
```javascript
gulp.src('./client/templates/*.jade')
.pipe(jade())
.pipe(gulp.dest('./build/templates'))
.pipe(minify())
.pipe(gulp.dest('./build/minified_templates'));
```
#### path
Type: `String`
The path (folder) to write files to.
### gulp.task(name[, deps], fn)
Define a task using [Orchestrator].
```javascript
gulp.task('somename', function() {
// Do stuff
});
```
#### name
The name of the task. Tasks that you want to run from the command line should not have spaces in them.
#### deps
Type: `Array`
An array of tasks to be executed and completed before your task will run.
```javascript
gulp.task('mytask', ['array', 'of', 'task', 'names'], function() {
// Do stuff
});
```
**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.
#### fn
The function that performs the task's operations. Generally this takes the form of `gulp.src().pipe(someplugin())`.
#### Async task support
Tasks can be made asynchronous if its `fn` does one of the following:
##### Accept a callback
```javascript
gulp.task('somename', function(cb) {
// Do stuff
cb(err);
});
```
##### Return a stream
```javascript
gulp.task('somename', function() {
var stream = gulp.src('./client/**/*.js')
.pipe(minify())
.pipe(gulp.dest('/build'));
return stream;
});
```
##### Return a promise
```javascript
var Q = require('q');
gulp.task('somename', function() {
var deferred = Q.defer();
// Do async stuff
setTimeout(function() {
deferred.resolve();
}, 1);
return deferred.promise;
});
```
**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:
- 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 this:
```javascript
var gulp = require('gulp');
// 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
});
// identifies a dependent task must be complete before this one begins
gulp.task('two', ['one'], function () {
// task 'one' is done now
});
gulp.task('default', ['one', 'two']);
```
### gulp.watch(glob [, opts], tasks) or gulp.watch(glob [, opts, cb])
Watch files and do something when a file changes. This always returns an EventEmitter that emits `changed` events.
### gulp.watch(glob[, opts], tasks)
#### glob
Type: `String` or `Array`
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).
#### tasks
Type: `Array`
Names of task(s) to run when a file changes, added with `gulp.task()`
```javascript
var watcher = gulp.watch('js/**/*.js', ['uglify','reload']);
watcher.on('changed', function(event){
console.log('File '+event.path+' was '+event.type+', running tasks...');
});
```
### gulp.watch(glob[, opts, cb])
#### glob
Type: `String` or `Array`
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).
#### cb(event)
Type: `Function`
Callback to be called on each change.
```javascript
gulp.watch('js/**/*.js', function(event) {
console.log('File '+event.path+' was '+event.type+', running tasks...');
});
```
The callback will be passed an object, `event`, that describes the change:
##### event.type
Type: `String`
The type of change that occurred, either `added`, `changed` or `deleted`.
##### event.path
Type: `String`
The path to the file that triggered the event.
[node-optimist]: https://github.com/substack/node-optimist
[node-glob documentation]: https://github.com/isaacs/node-glob#options
[node-glob]: https://github.com/isaacs/node-glob
[glob-stream]: https://github.com/wearefractal/glob-stream
[gulp-if]: https://github.com/robrich/gulp-if
[Orchestrator]: https://github.com/robrich/orchestrator
+25
View File
@@ -0,0 +1,25 @@
## gulp CLI docs
### Flags
gulp has very few flags to know about. All other flags are for tasks to use if needed.
- `-V` or `--version` will display the global and local gulp versions
- `--require <module path>` will require a module before running the gulpfile. This is useful for transpilers but also has other applications. You can use multiple `--require` flags
- `--gulpfile <gulpfile path>` manually set path of gulpfile. Useful if you have multiple gulpfiles. This will set the CWD to the gulpfile directory as well.
- `--cwd <dir path>` manually set the CWD. The search for the gulpfile, as well as the relativity of all requires will be from here.
- `-T` or `--tasks` will display the task dependency tree for the loaded gulpfile
### Tasks
Tasks can be executed by running `gulp <task> <othertask>`. Just running `gulp` will execute the task you registered called `default`. If there is no `default` task gulp will error.
### Compilers
You can use any language you want for your gulpfile. You will have to specify the language module name so the CLI can load it (and its associated extensions) before attempting to find your gulpfile. Make sure you have this module installed accessible by the folder you are running the CLI in.
Example:
```
gulp dosomething --require coffee-script/register
```
+50
View File
@@ -0,0 +1,50 @@
# FAQ
## Why gulp? Why not ____?
See the [gulp introduction slideshow] for a rundown on how gulp came to be.
## Is it "gulp" or "Gulp"?
gulp is always lowercase.
## Where can I find a list of gulp plugins?
gulp plugins always include the `gulpplugin` keyword. [Search gulp plugins][search-gulp-plugins] or [view all plugins][npm plugin search].
## I want to write a gulp plugin, how do I get started?
See the [Writing a gulp plugin] wiki page for guidelines and an example to get you started.
## My plugin does ____, is it doing too much?
Probably. Ask yourself:
1. Is my plugin doing something that other plugins may need to do?
- If so, that piece of functionality should be a separate plugin. [Check if it already exists on npm][npm plugin search].
1. Is my plugin doing two, completely different things based on a configuration option?
- If so, it may serve the community better to release it as two separate plugins
- If the two tasks are different, but very closely related, it's probably OK
## How should newlines be represented in plugin output?
Always use gulp-util.newline (which is \n) to prevent diff issues between operating systems.
## Where can I get updates on gulp?
gulp updates can be found on the following twitters:
- [@wearefractal](https://twitter.com/wearefractal)
- [@eschoff](https://twitter.com/eschoff)
- [@funkytek](https://twitter.com/funkytek)
- [@gulpjs](https://twitter.com/gulpjs)
## Does gulp have an IRC channel?
Yes, come chat with us in #gulpjs on [Freenode].
[Writing a gulp plugin]: writing-a-plugin/README.md
[gulp introduction slideshow]: http://slid.es/contra/gulp
[Freenode]: http://freenode.net/
[search-gulp-plugins]: http://gratimax.github.io/search-gulp-plugins/
[npm plugin search]: https://npmjs.org/browse/keyword/gulpplugin
+30
View File
@@ -0,0 +1,30 @@
# gulp documentation
* [Getting Started](getting-started.md) - How to get going with gulp
* [API documentation](API.md) - Learn the ins and outs of using gulp
* [CLI documentation](CLI.md) - Learn how to call tasks and use compilers
* [Writing a Plugin](writing-a-plugin/README.md) - So you're writing a gulp plugin? Go here for the essential dos and don'ts.
## FAQ
See [the FAQ](FAQ.md) for the answers to commonly asked questions.
## Recipes
The community has compiled guides on how to use gulp for common use cases. Check out the [recipes folder](recipes) for a full list.
## Articles
* [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/)
* [Why you shouldn’t create a gulp plugin (or, how to stop worrying and learn to love existing node packages)](http://blog.overzealous.com/post/74121048393/why-you-shouldnt-create-a-gulp-plugin-or-how-to-stop)
* [Inspiration (slides) about why gulp was made](http://slid.es/contra/gulp)
## License
All the documentation is covered by the CC0 license *(do whatever you want with it - public domain)*.
[![CC0](http://i.creativecommons.org/p/zero/1.0/88x31.png)](http://creativecommons.org/publicdomain/zero/1.0/)
To the extent possible under law, [Fractal](http://wearefractal.com) has waived all copyright and related or neighboring rights to this work.
+46
View File
@@ -0,0 +1,46 @@
## Getting Started
### 1. Install gulp globally:
```
npm install -g gulp
```
### 2. Install gulp and gulp-util in your project devDependencies:
```
npm install --save-dev gulp gulp-util
```
### 3. Create a `gulpfile.js` at the root of your project:
```javascript
var gulp = require('gulp');
var gutil = require('gulp-util');
gulp.task('default', function(){
// place code for your default task here
});
```
### 4. Run gulp
```
gulp
```
The default task will run and do nothing.
To run individual tasks, use `gulp <task> <othertask>`
## Where do I go now?
You have an empty gulpfile and everything is installed. How do you REALLY get started? Check out the [recipes and articles section](README.md#articles-and-recipes) for more information
## .src, .watch, .dest, CLI args - How do I use these things?
For API specific documentation you can check out the [documentation for that](API.md)
## Available Plugins
The gulp community is growing, with new plugins being added daily. See the [main website](http://gulpjs.com/) for a complete list.
+38
View File
@@ -0,0 +1,38 @@
# Combining streams to handle errors #
By default, emitting an error on a stream will cause it to be thrown
unless it already has a listener attached to the `error` event. This
gets a bit tricky when you're working with longer pipelines of streams.
By using [gulp-util](https://github.com/gulpjs/gulp-util)'s `combine`
method you can turn a series of streams into a single stream, meaning you
only need to listen to the `error` event in one place in your code.
Here's an example of using it in a gulpfile:
``` javascript
var combine = require('gulp-util').combine
var uglify = require('gulp-uglify')
var gulp = require('gulp')
gulp.task('test', function() {
var combined = combine(
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', function(err) {
console.warn(err.message)
})
return combined
})
```
You can use this technique in your gulp plugins and node modules too using
the [multipipe](http://npmjs.org/package/multipipe) module, and is a generally useful means of ensuring that errors aren't thrown from inaccessible streams
somewhere deep inside `./node_modules`.
+69
View File
@@ -0,0 +1,69 @@
# Mocha test-runner with gulp
### Passing shared module in all tests
```js
var gulp = require('gulp');
var mocha = require('gulp-mocha');
gulp.task('tests', function() {
return gulp.src(['test/test-*.js'], { read: false })
.pipe(mocha({
reporter: 'spec',
globals: {
should: require('should')
}
}));
});
```
### Running mocha tests when files change
With bundled `gulp.watch` and [`gulp-batch`](https://github.com/floatdrop/gulp-batch) (see readme of gulp-batch for reasons):
```js
// npm install gulp gulp-watch gulp-mocha gulp-batch
var gulp = require('gulp');
var mocha = require('gulp-mocha');
var batch = require('gulp-batch');
var gutil = require('gulp-util');
gulp.task('mocha', function () {
return gulp.src(['test/*.js'], { read: false })
.pipe(mocha({ reporter: 'list' }))
.on('error', gutil.log);
});
gulp.watch(['lib/**', 'test/**'], batch(function(events, cb) {
gulp.run('mocha', cb);
}));
```
With [`gulp-watch`](https://github.com/floatdrop/gulp-watch) plugin:
```js
// npm i gulp gulp-watch gulp-mocha
var gulp = require('gulp');
var mocha = require('gulp-mocha');
var watch = require('gulp-watch');
var gutil = require('gulp-util')
gulp.task('mocha', function () {
return gulp.src(['test/*.js'], { read: false })
.pipe(mocha({ reporter: 'list' }))
.on('error', gutil.log);
});
gulp.task('watch', function() {
return gulp.src(['lib/**', 'test/**'], { read: false })
.pipe(watch(function(events, cb) {
gulp.run('mocha', cb);
}));
});
gulp.task('default', ['mocha', 'watch']);
// run `gulp watch` or just `gulp` for watching and rerunning tests
```
+27
View File
@@ -0,0 +1,27 @@
# Only pass through changed files
Files are passed through the whole pipe chain on every run by default. By using [gulp-changed](https://github.com/sindresorhus/gulp-changed) only changed files will be passed through. This can speed up consecutive runs considerably.
```js
// run `npm install gulp gulp-changed gulp-jscs gulp-uglify`
var gulp = require('gulp');
var changed = require('gulp-changed');
var jscs = require('gulp-jscs');
var uglify = require('gulp-uglify');
// we define some constants here so they can be reused
var SRC = 'src/*.js';
var DEST = 'dist';
gulp.task('default', function () {
return gulp.src(SRC)
// the `changed` task needs to know the destination directory
// upfront to be able to figure out which files changed
.pipe(changed(DEST))
// only files that has changed will pass through here
.pipe(jscs())
.pipe(uglify())
.pipe(gulp.dest(DEST));
});
```
+28
View File
@@ -0,0 +1,28 @@
# Pass parameters from the command line
## bonus: keeping those tasks DRY
---
`gulpfile.js`
```js
// npm install gulp gulp-util gulp-if gulp-uglify
var gulp = require('gulp');
var gutil = require('gulp-util');
var gulpif = require('gulp-if');
var uglify = require('gulp-uglify');
var isProduction = gutil.env.type === 'production';
gulp.task('scripts', function () {
return gulp.src('**/*.js')
.pipe(gulpif(isProduction, uglify())) // only minify if production
.pipe(gulp.dest('dist'));
});
```
---
`cli`
`gulp scripts --type production`
+16
View File
@@ -0,0 +1,16 @@
# Rebuild only files that change
With [`gulp-watch`](https://github.com/floatdrop/gulp-watch):
```js
var gulp = require('gulp');
var sass = require('gulp-sass');
var watch = require('gulp-watch');
gulp.task('default', function() {
return gulp.src('./sass/*.scss')
.pipe(watch())
.pipe(sass())
.pipe(gulp.dest('./dist/'));
});
```
+34
View File
@@ -0,0 +1,34 @@
# 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 this:
```javascript
var gulp = require('gulp');
// 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 orchestration will stop, and 'two' will not run
});
// identifies a dependent task must be complete before this one begins
gulp.task('two', ['one'], function () {
// task 'one' is done now
});
gulp.task('default', ['one', 'two']);
// alternatively: gulp.task('default', ['two']);
```
+37
View File
@@ -0,0 +1,37 @@
# Specifying a new cwd (current working directory)
This is helpful for projects using a nested directory structure, such as:
```
/project
/layer1
/layer2
```
You can use the gulp CLI option `--cwd`
From the `project/` directory
```bash
gulp --cwd ./layer1/
```
Another option is to use `process.chdir` which is just vanilla node.
`gulpfile.js`
```js
var gulp = require('gulp');
try {
process.chdir(gulp.env.cwd);
} catch (err) {
console.error('Unable to chdir to %s', gulp.env.cwd);
}
```
If you only need to specify a cwd for a certain glob, you can use the `cwd` option on a [glob-stream](https://github.com/wearefractal/glob-stream)
```js
gulp.src('./some/dir/**/*.js', { cwd: './public' });
```
+23
View File
@@ -0,0 +1,23 @@
# Using coffee-script for gulpfile
As discussed in [issue #103](https://github.com/gulpjs/gulp/issues/103), there are 2 ways to do this.
1. Use `gulp --require coffee-script/register` at the command line
2. Require in `gulpfile.coffee` after requiring `coffee-script` in `gulpfile.js`
`gulpfile.js`
```js
require('coffee-script/register');
require('./gulpfile.coffee');
```
`gulpfile.coffee`
```coffeescript
gulp = require 'gulp'
gulp.task 'default', ->
console.log('default task called')
```
+49
View File
@@ -0,0 +1,49 @@
# Using external config file
## bonus: keeping those tasks DRY
## bonus2: config.json can be used by another task runner, like `Grunt`
---
`config.json`
```json
{
"desktop" : {
"src" : [
"dev/desktop/js/**/*.js",
"!dev/desktop/js/vendor/**"
],
"dest" : "build/desktop/js"
},
"mobile" : {
"src" : [
"dev/mobile/js/**/*.js",
"!dev/mobile/js/vendor/**"
],
"dest" : "build/mobile/js"
}
}
```
---
`gulpfile.js`
```js
// npm install gulp gulp-uglify
var gulp = require('gulp');
var uglify = require('gulp-uglify');
var config = require('./config.json');
function doStuff(cfg) {
return gulp.src(cfg.src)
.pipe(uglify())
.pipe(gulp.dest(cfg.dest));
}
gulp.task('dry', function () {
doStuff(config.desktop);
doStuff(config.mobile);
});
```
+17
View File
@@ -0,0 +1,17 @@
# Using multiple sources in one task
```js
// npm install gulp event-stream
var gulp = require('gulp');
var es = require('event-stream');
gulp.task('test', function(cb) {
return es.concat(
gulp.src('bootstrap/js/*.js')
.pipe(gulp.dest('public/bootstrap')),
gulp.src('jquery.cookie/jquery.cookie.js')
.pipe(gulp.dest('public/jquery'))
);
});
```
+53
View File
@@ -0,0 +1,53 @@
# Writing a plugin
If you plan to create your own Gulp plugin, you will save time by reading the full documentation.
* [Guidelines](guidelines.md) (a MUST read)
* [Using buffers](using-buffers.md)
* [Dealing with streams](dealing-with-streams.md)
* [Testing](testing.md)
* [README conventions](readme-conventions.md)
## What it does
### Streaming file objects
A gulp plugin always returns a stream in [object mode](http://nodejs.org/api/stream.html#stream_object_mode) that does the following:
1. Takes in [vinyl File objects](http://github.com/wearefractal/vinyl)
2. Outputs [vinyl File objects](http://github.com/wearefractal/vinyl)
These are known as [transform streams](http://nodejs.org/api/stream.html#stream_class_stream_transform_1) (also sometimes called through streams). Transform streams are streams that are readable and writable which manipulate objects as they're being passed through.
### Modifying file content
Vinyl files can have 3 possible forms for the contents attribute:
- [Streams](dealing-with-streams.md)
- [Buffers](using-buffers.md)
- Empty (null) - Useful for things like rimraf, clean, where contents is not neeeded.
## Useful resources
* [File object](https://github.com/wearefractal/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)
## Sample plugins
* [sindresorhus' gulp plugins](https://github.com/search?q=%40sindresorhus+gulp-)
* [Fractal's gulp plugins](https://github.com/search?q=%40wearefractal+gulp-)
* [gulp-replace](https://github.com/lazd/gulp-replace)
## About streams
If you're unfamiliar with streams, you will need to read up on them:
* https://github.com/substack/stream-handbook (a MUST read)
* http://nodejs.org/api/stream.html
Other libraries that are not file manipulating through streams but are made for use with gulp are tagged with the [gulpfriendly](https://npmjs.org/browse/keyword/gulpfriendly) keyword on npm.
+83
View File
@@ -0,0 +1,83 @@
# Dealing with streams
> It is highly recommended to write plugins supporting streams. Here is some information on creating a gulp plugin that supports streams.
> Make sure to follow the best practice regarging error handling and add the line that make the gulp plugin re-emit the first error catched during the transformation of the content
[Writing a Plugin](README.md) > Writing stream based plugins
## Dealing with streams
Let's implement a plugin prepending some text to files. This plugin supports all possible forms of file.contents.
```js
var through = require('through2');
var gutil = require('gulp-util');
var PluginError = gutil.PluginError;
// Consts
const PLUGIN_NAME = 'gulp-prefixer';
function prefixStream(prefixText) {
var stream = through();
stream.write(prefixText);
return stream;
}
// Plugin level function (dealing with files)
function gulpPrefixer(prefixText) {
if (!prefixText) {
throw PluginError(PLUGIN_NAME, "Missing prefix text!");
}
prefixText = new Buffer(prefixText); // allocate ahead of time
// Creating a stream through which each file will pass
var stream = through.obj(function (file, enc, callback) {
if (file.isNull()) {
this.push(file); // Do nothing if no contents
return callback();
}
if (file.isBuffer()) {
this.emit('error', new PluginError(PLUGIN_NAME, 'Buffers not supported!'));
return callback();
}
if (file.isStream()) {
// define the streamer that will transform the content
var streamer = prefixStream(prefixText);
// catch errors from the streamer and emit a gulp plugin error
streamer.on('error', this.emit.bind(this, 'error'));
// start the transformation
file.contents = file.contents.pipe(streamer);
// make sure the file goes through the next gulp plugin
this.push(file);
// tell the stream engine that we are done with this file
return callback();
}
});
// returning the file stream
return stream;
};
// Exporting the plugin main function
module.exports = gulpPrefixer;
```
The above plugin can be used like this:
```js
var gulp = require('gulp');
var gulpPrefixer = require('gulp-prefixer');
gulp.src('files/**/*.js')
.pipe(gulpPrefixer('prepended string'))
.pipe(gulp.dest('/modified-files/'));
```
## Some plugins using streams
* [gulp-svgicons2svgfont](https://github.com/nfroidure/gulp-svgiconstosvgfont)
* gulp-browserify (Soon)
+97
View File
@@ -0,0 +1,97 @@
# Guidelines
> While these guidelines are totally optional, we **HIGHLY** recommend that everyone follows them. Nobody wants to use a bad plugin. These guidelines will actually help make your life easier by giving you assurance that your plugin fits well within gulp.
[Writing a Plugin](README.md) > Guidelines
1. Your plugin should not do something that can be done easily with an existing node module
- Wrapping every possible thing just for the sake of wrapping it will pollute the ecosystem with low quality plugins that don't make sense
1. Your plugin should only do **one thing**, and do it well.
- Avoid config options that make your plugin do completely different tasks
1. Your plugin shouldn't do things that other plugins are responsible for
- It should not concat, [gulp-concat](https://github.com/wearefractal/gulp-concat) does that
- It should not add headers, [gulp-header](https://github.com/godaddy/gulp-header) does that
- It should not add footers, [gulp-footer](https://github.com/godaddy/gulp-footer) does that
- If it's a common but optional use case, document that your plugin is often used with another plugin
- If it's an internal requirement, make use of existing plugins by piping your plugin's output to them
1. Your plugin **must be tested**
- Testing a gulp plugin is easy, you don't even need gulp to test it
- Look at other plugins for examples
1. Add `gulpplugin` as a keyword in your `package.json` so you show up on our search
1. Do not throw errors inside a stream
- Instead, you should emit it as an **error** event.
- If you encounter an error **outside** the stream, such as invalid configuration, you may throw it.
1. Prefix any errors with the name of your plugin
- For example: `gulp-replace: Cannot do regexp replace on a stream`
- Use gulp-util's [PluginError](https://github.com/gulpjs/gulp-util#new-pluginerrorpluginname-message-options) class to make this easy
1. The type of `file.contents` should always be the same going out as it was when it came in
- If file.contents is null (non-read) just ignore the file and pass it along
- If file.contents is a Stream and you don't support that just emit an error
- If you choose to support file.contents as a Stream, use [BufferStream](https://github.com/nfroidure/BufferStream) to make this easy
1. Do not pass the `file` object downstream until you are done with it
1. Make use of the [gulp-util](https://github.com/gulpjs/gulp-util) library
- It provides templating, CLI colors, logging, changing file extensions
- Try looking for common things there first and add it if it doesn't exist
1. Do NOT require `gulp` as a dependency or peerDependency
- There is no reason you should have to do this and it will cause problems if you do
## Why are these guidelines so strict?
gulp aims to be simple for users. By providing strict guidelines we are able to provide a consistent and high-quality ecosystem for everyone. While this does add a little more work and thought for plugin authors, it removes a lot of problems later down the road.
### What happens if I don't follow them?
npm is open for everyone, and you are free to make whatever you want but these guidelines were prescribed for a reason. There are acceptances tests coming soon that will be integrated into the plugin search. If you fail to adhere to the plugin guidelines it will be publicly visible/sortable via a scoring system. People will always prefer to use plugins that match "the gulp way".
### What does a good plugin look like?
```js
// through2 is a thin wrapper around node transform streams
var through = require('through2');
var gutil = require('gulp-util');
var PluginError = gutil.PluginError;
// Consts
const PLUGIN_NAME = 'gulp-prefixer';
function prefixStream(prefixText) {
var stream = through();
stream.write(prefixText);
return stream;
}
// Plugin level function (dealing with files)
function gulpPrefixer(prefixText) {
if (!prefixText) {
throw PluginError(PLUGIN_NAME, "Missing prefix text!");
}
prefixText = new Buffer(prefixText); // allocate ahead of time
// Creating a stream through which each file will pass
var stream = through.obj(function (file, enc, callback) {
if (file.isNull()) {
this.push(file); // Do nothing if no contents
return callback();
}
if (file.isBuffer()) {
file.contents = Buffer.concat([prefixText, file.contents]);
this.push(file);
return callback();
}
if (file.isStream()) {
file.contents = file.contents.pipe(prefixStream(prefixText));
this.push(file);
return callback();
}
});
// returning the file stream
return stream;
};
// Exporting the plugin main function
module.exports = gulpPrefixer;
```
+218
View File
@@ -0,0 +1,218 @@
# README conventions
> Follow README conventions to increase your plugin readability and usability.
[Writing a Gulp Plugin](README.md) > README conventions
A plugin README should have the following parts at a minimum:
1. **Heading**: Give a short introduction to your plugin
* Include the version of gulp your plugin was designed for
* Include any badges on the same line as the heading
1. **Usage**: Include a simple usage example
* Show the plugin used on the context of a `gulpfile.js`
* Cover the most common or simplest use case
1. **API**: Document your plugin's API
* For individual parameters, include:
1. Parameter name
1. Type
1. Default value
1. Description
1. Optional example for complex options
* If a parameter accepts an Object, do the above for each property
* If your plugin accepts overloaded arguments, document each style individually
# Example: Options object
The following is an example of a plugin that takes a single options object (from [gulp-handlebars](https://github.com/lazd/gulp-handlebars)).
***
# gulp-handlebars [![NPM version][handlebars-npm-image]][handlebars-npm-url] [![Build status][handlebars-travis-image]][handlebars-travis-url]
> Handlebars plugin for gulp 3
## Usage
First, install `gulp-handlebars` as a development dependency:
```shell
npm install --save-dev gulp-handlebars
```
Then, add it to your `gulpfile.js`:
```js
var handlebars = require('gulp-handlebars');
gulp.task('templates', function(){
gulp.src(['client/templates/*.hbs'])
.pipe(handlebars({
namespace: 'MyApp.templates',
outputType: 'hybrid'
}))
.pipe(concat('templates.js'))
.pipe(gulp.dest('build/js/'));
});
```
## API
### handlebars(options)
#### options.namespace
Type: `String`
Default: `templates`
The namespace in which the precompiled templates will be assigned. Use dot notation (e.g. `App.Templates`) for nested namespaces or false to declare templates in the global namespace.
#### options.outputType
Type: `String`
Default: `browser`
The desired output type. One of the following:
* `browser` - Produce plain JavaScript files for the browser
* `hybrid` - Produce Node modules that can optionally be used on the frontend
* `node` - Produce Node modules
* `amd` - Produce AMD modules
* `commonjs` - Produce CommonJS modules
* `bare` - Return an unmolested function definition
#### options.declareNamespace
Type: `Boolean`
Default: `true`
If true, non-destructively declare all parts of the namespace. This option is only necessary when `options.type` is `browser` or `hybrid`.
For example, if the namespace is `MyApp.Templates` and a template is named `App.Header.hbs`, the following declaration will be present in the output file for that template:
```js
this["MyApp"] = this["MyApp"] || {};
this["MyApp"]["templates"] = this["MyApp"]["templates"] || {};
this["MyApp"]["templates"]["App"] = this["MyApp"]["templates"]["App"] || {};
this["MyApp"]["templates"]["App"]["Header"] = function () {};
```
When processing multiple templates under a given namespace, this will result in duplicate declarations. That is, the non-destructive declaration of the namespace will be repeated for each template compiled.
#### options.processName
Type: `Function`
Default: Strip file extension
This option accepts a function which takes one argument (the template filepath) and returns a string which will be used as the key for the precompiled template object. By default, the filename minus the extension is used.
If this function returns a string containing periods (not including the file extension), they will be represented as a sub-namespace. See `options.declareNamespace` for an example of the effect.
#### options.compilerOptions
Type: `Object`
Compiler options to pass to `Handlebars.precompile()`.
[handlebars-travis-url]: http://travis-ci.org/lazd/gulp-handlebars
[handlebars-travis-image]: https://secure.travis-ci.org/lazd/gulp-handlebars.png?branch=master
[handlebars-npm-url]: https://npmjs.org/package/gulp-handlebars
[handlebars-npm-image]: https://badge.fury.io/js/gulp-handlebars.png
***
# Example: Overloaded API
The following is an example of a plugin that has an overloaded API (from [gulp-csslint](https://github.com/lazd/gulp-csslint)).
***
# gulp-csslint [![NPM version][csslint-npm-image]][csslint-npm-url] [![Build status][csslint-travis-image]][csslint-travis-url]
> CSSLint plugin for gulp 3
## Usage
First, install `gulp-csslint` as a development dependency:
```shell
npm install --save-dev gulp-csslint
```
Then, add it to your `gulpfile.js`:
```javascript
var csslint = require('gulp-csslint');
gulp.task('css', function() {
gulp.src('./client/css/*.css')
.pipe(csslint())
.pipe(csslint.reporter());
});
```
## API
### csslint(ruleConfiguration)
#### ruleConfiguration
Type: `Object`
You can pass rule configuration as an object. See the [list of rules by ID on the CSSLint wiki](https://github.com/stubbornella/csslint/wiki/Rules-by-ID) for valid rule IDs.
```javascript
gulp.src('./client/css/*.css')
.pipe(csslint({
'shorthand': false
}))
.pipe(csslint.reporter());
```
### csslint(csslintrc)
#### csslintrc
Type: `String`
You can also pass the path to your csslintrc file instead of a rule configuration object.
```javascript
gulp.src('./client/css/*.css')
.pipe(csslint('csslintrc.json'))
.pipe(csslint.reporter());
```
## Results
Adds the following properties to the file object:
```javascript
file.csslint.success = true; // or false
file.csslint.errorCount = 0; // number of errors returned by CSSLint
file.csslint.results = []; // CSSLint errors
file.csslint.opt = {}; // The options you passed to CSSLint
```
## Custom Reporters
Custom reporter functions can be passed as `cssline.reporter(reporterFunc)`. The reporter function will be called for each linted file and passed the file object as described above.
```javascript
var csslint = require('gulp-csslint');
var gutil = require('gulp-util');
var customReporter = function(file) {
gutil.log(gutil.colors.cyan(file.csslint.errorCount)+' errors in '+gutil.colors.magenta(file.path));
file.csslint.results.forEach(function(result) {
gutil.log(result.error.message+' on line '+result.error.line);
});
};
gulp.task('lint', function() {
gulp.files('./lib/*.js')
.pipe(csslint())
.pipe(csslint.reporter(customReporter));
});
```
[csslint-travis-url]: http://travis-ci.org/lazd/gulp-csslint
[csslint-travis-image]: https://secure.travis-ci.org/lazd/gulp-csslint.png?branch=master
[csslint-npm-url]: https://npmjs.org/package/gulp-csslint
[csslint-npm-image]: https://badge.fury.io/js/gulp-csslint.png
***
+95
View File
@@ -0,0 +1,95 @@
# Testing
> Testing your plugin is the only way to ensure quality. It brings confidence to your users and makes your life easier.
[Writing a Plugin](README.md) > Testing
## Tooling
Most plugins use [mocha](https://github.com/visionmedia/mocha) and [event-stream](https://github.com/dominictarr/event-stream) to help them test. The following examples will use these tools.
## Testing plugins for streaming mode
```js
var assert = require('assert');
var es = require('event-stream');
var gutil = require('gulp-util');
var prefixer = require('../index');
describe('gulp-prefixer', function () {
describe('in streaming mode', function () {
it('should prepend text', function (done) {
// create the fake file
var fakeFile = new gutil.File({
contents: es.readArray(['stream', 'with', 'those', 'contents'])
});
// Create a prefixer plugin stream
var myPrefixer = prefixer('prependthis');
// write the fake file to it
myPrefixer.write(fakeFile);
// wait for the file to come back out
myPrefixer.once('data', function (file) {
// make sure it came out the same way it went in
assert(file.isStream());
// buffer the contents to make sure it got prepended to
file.contents.pipe(es.wait(function (err, data) {
// check the contents
assert.equal(data, 'prependthistostreamwiththosecontents');
done();
}));
});
});
});
});
```
## Testing plugins for buffer mode
```js
var assert = require('assert');
var es = require('event-stream');
var gutil = require('gulp-util');
var prefixer = require('../index');
describe('gulp-prefixer', function () {
describe('in buffer mode', function () {
it('should prepend text', function (done) {
// create the fake file
var fakeFile = new gutil.File({
contents: new Buffer('abufferwiththiscontent')
});
// Create a prefixer plugin stream
var myPrefixer = prefixer('prependthis');
// write the fake file to it
myPrefixer.write(fakeFile);
// wait for the file to come back out
myPrefixer.once('data', function (file) {
// make sure it came out the same way it went in
assert(file.isBuffer());
// check the contents
assert.equal(file.contents.toString('utf8'), 'prependthisabufferwiththiscontent');
done();
});
});
});
});
```
## Sample tests
* [gulp-cat](https://github.com/ben-eb/gulp-cat/blob/master/test.js)
+72
View File
@@ -0,0 +1,72 @@
# Using buffers
> Here is some information on creating gulp plugin that manipulates buffers.
[Writing a Plugin](README.md) > Using buffers
## Using buffers
If your plugin is relying on a buffer based library, you will probably choose to base your plugin around file.contents as a buffer. Let's implement a plugin prepending some text to files:
```js
var through = require('through2');
var gutil = require('gulp-util');
var PluginError = gutil.PluginError;
// Consts
const PLUGIN_NAME = 'gulp-prefixer';
// Plugin level function (dealing with files)
function gulpPrefixer(prefixText) {
if (!prefixText) {
throw new PluginError(PLUGIN_NAME, "Missing prefix text!");
}
prefixText = new Buffer(prefixText); // allocate ahead of time
// Creating a stream through which each file will pass
var stream = through.obj(function (file, enc, callback) {
if (file.isNull()) {
this.push(file); // Do nothing if no contents
return callback();
}
if (file.isBuffer()) {
file.contents = Buffer.concat([prefixText, file.contents]);
this.push(file);
return callback();
}
if (file.isStream()) {
this.emit('error', new PluginError(PLUGIN_NAME, 'Streams are not supported!'));
return callback();
}
});
// returning the file stream
return stream;
};
// Exporting the plugin main function
module.exports = gulpPrefixer;
```
The above plugin can be used like this:
```js
var gulp = require('gulp');
var gulpPrefixer = require('gulp-prefixer');
gulp.src('files/**/*.js')
.pipe(gulpPrefixer('prepended string'))
.pipe(gulp.dest('/modified-files/'));
```
## Handling streams
Unfortunately, the above plugin will error when using gulp.src in non-buffered (streaming) mode. You should support streams too if possible. See [Dealing with streams](dealing-with-streams.md) for more information.
## Some plugins based on buffers
* [gulp-coffee](https://github.com/wearefractal/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)