├── test ├── fixtures │ ├── stuff │ │ ├── run.dmc │ │ └── test.dmc │ ├── test.coffee │ ├── test │ │ └── run.jade │ └── copy │ │ └── example.txt ├── taskTree.js ├── watch.js ├── tasks.js ├── dest.js └── src.js ├── .travis.yml ├── .gitignore ├── .npmignore ├── .jshintrc ├── lib ├── taskTree.js └── completion.js ├── completion ├── README.md ├── zsh └── bash ├── docs ├── recipes │ ├── rebuild-only-files-that-change.md │ ├── using-multiple-sources-in-one-task.md │ ├── using-coffee-script-for-gulpfile.md │ ├── pass-params-from-cli.md │ ├── specifying-a-cwd.md │ ├── only-pass-through-changed-files.md │ ├── using-external-config-file.md │ ├── combining-streams-to-handle-errors.md │ ├── running-tasks-in-series.md │ └── mocha-test-runner-with-gulp.md ├── getting-started.md ├── CLI.md ├── README.md ├── FAQ.md ├── writing-a-plugin │ ├── README.md │ ├── using-buffers.md │ ├── dealing-with-streams.md │ ├── testing.md │ ├── guidelines.md │ └── readme-conventions.md └── API.md ├── gulpfile.js ├── LICENSE ├── index.js ├── package.json ├── README.md ├── CHANGELOG.md └── bin └── gulp.js /test/fixtures/stuff/run.dmc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/stuff/test.dmc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/test.coffee: -------------------------------------------------------------------------------- 1 | this is a test -------------------------------------------------------------------------------- /test/fixtures/test/run.jade: -------------------------------------------------------------------------------- 1 | test template -------------------------------------------------------------------------------- /test/fixtures/copy/example.txt: -------------------------------------------------------------------------------- 1 | this is a test -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.9" 4 | - "0.10" 5 | after_script: 6 | - npm run coveralls -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.log 3 | node_modules 4 | build 5 | *.node 6 | components 7 | coverage 8 | *.orig 9 | .idea 10 | sandbox 11 | test/out-fixtures/* 12 | test/watch-*.txt 13 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.log 3 | node_modules 4 | build 5 | *.node 6 | components 7 | coverage 8 | *.orig 9 | .idea 10 | sandbox 11 | test/out-fixtures/* 12 | test/watch-*.txt 13 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "strict": true, 3 | "undef": true, 4 | "unused": true, 5 | "node": true, 6 | "globals": { 7 | "describe": true, 8 | "it": true, 9 | "beforeEach": true, 10 | "afterEach": true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/taskTree.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(tasks) { 4 | return Object.keys(tasks).reduce(function(prev, task) { 5 | prev.nodes.push({ 6 | label: task, 7 | nodes: tasks[task].dep 8 | }); 9 | return prev; 10 | }, {nodes: []}); 11 | }; -------------------------------------------------------------------------------- /completion/README.md: -------------------------------------------------------------------------------- 1 | # Completion for gulp 2 | > Thanks to grunt team and Tyler Kellen 3 | 4 | To enable tasks auto-completion in shell you should add `eval "$(gulp --completion=shell)"` in your `.shellrc` file. 5 | 6 | ## Bash 7 | 8 | Add `eval "$(gulp --completion=bash)"` to `~/.bashrc`. 9 | 10 | ## Zsh 11 | 12 | Add `eval "$(gulp --completion=zsh)"` to `~/.zshrc`. -------------------------------------------------------------------------------- /docs/recipes/rebuild-only-files-that-change.md: -------------------------------------------------------------------------------- 1 | # Rebuild only files that change 2 | 3 | With [`gulp-watch`](https://github.com/floatdrop/gulp-watch): 4 | 5 | ```js 6 | var gulp = require('gulp'); 7 | var sass = require('gulp-sass'); 8 | var watch = require('gulp-watch'); 9 | 10 | gulp.task('default', function() { 11 | return gulp.src('./sass/*.scss') 12 | .pipe(watch()) 13 | .pipe(sass()) 14 | .pipe(gulp.dest('./dist/')); 15 | }); 16 | ``` 17 | -------------------------------------------------------------------------------- /docs/recipes/using-multiple-sources-in-one-task.md: -------------------------------------------------------------------------------- 1 | # Using multiple sources in one task 2 | 3 | ```js 4 | // npm install gulp event-stream 5 | 6 | var gulp = require('gulp'); 7 | var es = require('event-stream'); 8 | 9 | gulp.task('test', function(cb) { 10 | return es.concat( 11 | gulp.src('bootstrap/js/*.js') 12 | .pipe(gulp.dest('public/bootstrap')), 13 | gulp.src('jquery.cookie/jquery.cookie.js') 14 | .pipe(gulp.dest('public/jquery')) 15 | ); 16 | }); 17 | ``` 18 | -------------------------------------------------------------------------------- /lib/completion.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | 6 | module.exports = function(name) { 7 | if (typeof name !== 'string') throw new Error('Missing completion type'); 8 | var file = path.join(__dirname, '../completion', name); 9 | try { 10 | console.log(fs.readFileSync(file, 'utf8')); 11 | process.exit(0); 12 | } catch (err) { 13 | console.log('echo "Specified gulp shell auto-completion rules for \''+name+'\' not found"'); 14 | process.exit(5); 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /docs/recipes/using-coffee-script-for-gulpfile.md: -------------------------------------------------------------------------------- 1 | # Using coffee-script for gulpfile 2 | 3 | As discussed in [issue #103](https://github.com/gulpjs/gulp/issues/103), there are 2 ways to do this. 4 | 5 | 1. Use `gulp --require coffee-script/register` at the command line 6 | 7 | 2. Require in `gulpfile.coffee` after requiring `coffee-script` in `gulpfile.js` 8 | 9 | `gulpfile.js` 10 | 11 | ```js 12 | require('coffee-script/register'); 13 | require('./gulpfile.coffee'); 14 | ``` 15 | 16 | `gulpfile.coffee` 17 | 18 | ```coffeescript 19 | gulp = require 'gulp' 20 | 21 | gulp.task 'default', -> 22 | console.log('default task called') 23 | ``` 24 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('./'); 4 | var env = require('gulp-util').env; 5 | var log = require('gulp-util').log; 6 | 7 | var jshint = require('gulp-jshint'); 8 | 9 | var codeFiles = ['**/*.js', '!node_modules/**']; 10 | 11 | gulp.task('lint', function(){ 12 | log('Linting Files'); 13 | return gulp.src(codeFiles) 14 | .pipe(jshint('.jshintrc')) 15 | .pipe(jshint.reporter()); 16 | }); 17 | 18 | gulp.task('watch', function(){ 19 | log('Watching Files'); 20 | gulp.watch(codeFiles, ['lint']); 21 | }); 22 | 23 | gulp.task('default', ['lint', 'watch']); 24 | 25 | var taskToRun = env.dev ? 'default' : 'lint'; 26 | 27 | gulp.start(taskToRun); 28 | -------------------------------------------------------------------------------- /docs/recipes/pass-params-from-cli.md: -------------------------------------------------------------------------------- 1 | # Pass parameters from the command line 2 | ## bonus: keeping those tasks DRY 3 | 4 | --- 5 | 6 | `gulpfile.js` 7 | 8 | ```js 9 | // npm install gulp gulp-util gulp-if gulp-uglify 10 | var gulp = require('gulp'); 11 | var gutil = require('gulp-util'); 12 | var gulpif = require('gulp-if'); 13 | var uglify = require('gulp-uglify'); 14 | 15 | var isProduction = gutil.env.type === 'production'; 16 | 17 | gulp.task('scripts', function () { 18 | return gulp.src('**/*.js') 19 | .pipe(gulpif(isProduction, uglify())) // only minify if production 20 | .pipe(gulp.dest('dist')); 21 | }); 22 | ``` 23 | 24 | --- 25 | 26 | `cli` 27 | 28 | `gulp scripts --type production` 29 | -------------------------------------------------------------------------------- /test/taskTree.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var taskTree = require('../lib/taskTree'); 4 | var should = require('should'); 5 | 6 | require('mocha'); 7 | 8 | describe('taskTree()', function() { 9 | it('should form a tree properly', function(done){ 10 | should.exist(taskTree); // lol shutup jshint 11 | 12 | var tasks = { 13 | test: { 14 | dep: ['abc', 'def'] 15 | }, 16 | abc: { 17 | dep: ['def'] 18 | }, 19 | def: { 20 | dep: [] 21 | } 22 | }; 23 | 24 | var expectTree = { 25 | nodes: [{ 26 | label: 'test', 27 | nodes: ['abc', 'def'] 28 | 29 | }, { 30 | label: 'abc', 31 | nodes: ['def'] 32 | 33 | }, { 34 | label: 'def', 35 | nodes: [] 36 | 37 | }] 38 | }; 39 | 40 | taskTree(tasks).should.eql(expectTree); 41 | done(); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /completion/zsh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | 3 | # Borrowed from grunt-cli 4 | # http://gruntjs.com/ 5 | # 6 | # Copyright (c) 2012 Tyler Kellen, contributors 7 | # Licensed under the MIT license. 8 | # https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT 9 | 10 | # Usage: 11 | # 12 | # To enable zsh completion for grunt, add the following line (minus the 13 | # leading #, which is the zsh comment character) to your ~/.zshrc file: 14 | # 15 | # eval "$(gulp --completion=zsh)" 16 | 17 | # Enable zsh autocompletion. 18 | function _gulp_completion() { 19 | # Grap tasks 20 | compls=$(node -e "try { var gulp = require('gulp'); require('./gulpfile'); console.log(Object.keys(gulp.tasks).join(' ')); } catch (e) {}") 21 | # Trim whitespace. 22 | compls=$(echo "$compls" | sed -e 's/^ *//g' -e 's/ *$//g') 23 | completions=(${=compls}) 24 | compadd -- $completions 25 | } 26 | 27 | compdef _gulp_completion gulp 28 | -------------------------------------------------------------------------------- /docs/recipes/specifying-a-cwd.md: -------------------------------------------------------------------------------- 1 | # Specifying a new cwd (current working directory) 2 | 3 | This is helpful for projects using a nested directory structure, such as: 4 | 5 | ``` 6 | /project 7 | /layer1 8 | /layer2 9 | ``` 10 | 11 | You can use the gulp CLI option `--cwd` 12 | 13 | From the `project/` directory 14 | 15 | ```bash 16 | gulp --cwd ./layer1/ 17 | ``` 18 | 19 | Another option is to use `process.chdir` which is just vanilla node. 20 | 21 | `gulpfile.js` 22 | 23 | ```js 24 | var gulp = require('gulp'); 25 | 26 | try { 27 | process.chdir(gulp.env.cwd); 28 | } catch (err) { 29 | console.error('Unable to chdir to %s', gulp.env.cwd); 30 | } 31 | ``` 32 | 33 | 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) 34 | 35 | ```js 36 | gulp.src('./some/dir/**/*.js', { cwd: './public' }); 37 | ``` 38 | -------------------------------------------------------------------------------- /docs/recipes/only-pass-through-changed-files.md: -------------------------------------------------------------------------------- 1 | # Only pass through changed files 2 | 3 | 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. 4 | 5 | 6 | ```js 7 | // run `npm install gulp gulp-changed gulp-jscs gulp-uglify` 8 | var gulp = require('gulp'); 9 | var changed = require('gulp-changed'); 10 | var jscs = require('gulp-jscs'); 11 | var uglify = require('gulp-uglify'); 12 | 13 | // we define some constants here so they can be reused 14 | var SRC = 'src/*.js'; 15 | var DEST = 'dist'; 16 | 17 | gulp.task('default', function () { 18 | return gulp.src(SRC) 19 | // the `changed` task needs to know the destination directory 20 | // upfront to be able to figure out which files changed 21 | .pipe(changed(DEST)) 22 | // only files that has changed will pass through here 23 | .pipe(jscs()) 24 | .pipe(uglify()) 25 | .pipe(gulp.dest(DEST)); 26 | }); 27 | ``` 28 | -------------------------------------------------------------------------------- /completion/bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Borrowed from grunt-cli 4 | # http://gruntjs.com/ 5 | # 6 | # Copyright (c) 2012 Tyler Kellen, contributors 7 | # Licensed under the MIT license. 8 | # https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT 9 | 10 | # Usage: 11 | # 12 | # To enable bash completion for grunt, add the following line (minus the 13 | # leading #, which is the bash comment character) to your ~/.bashrc file: 14 | # 15 | # eval "$(gulp --completion=bash)" 16 | 17 | # Enable bash autocompletion. 18 | function _gulp_completions() { 19 | # The currently-being-completed word. 20 | local cur="${COMP_WORDS[COMP_CWORD]}" 21 | #Grab tasks 22 | local compls=$(node -e "try { var gulp = require('gulp'); require('./gulpfile'); console.log(Object.keys(gulp.tasks).join(' ')); } catch (e) {}") 23 | # Trim whitespace. 24 | compls=$(echo "$compls" | sed -e 's/^ *//g' -e 's/ *$//g') 25 | # Tell complete what stuff to show. 26 | COMPREPLY=($(compgen -W "$compls" -- "$cur")) 27 | } 28 | 29 | complete -o default -F _gulp_completions gulp 30 | -------------------------------------------------------------------------------- /docs/recipes/using-external-config-file.md: -------------------------------------------------------------------------------- 1 | # Using external config file 2 | ## bonus: keeping those tasks DRY 3 | ## bonus2: config.json can be used by another task runner, like `Grunt` 4 | 5 | --- 6 | 7 | `config.json` 8 | 9 | ```json 10 | { 11 | "desktop" : { 12 | "src" : [ 13 | "dev/desktop/js/**/*.js", 14 | "!dev/desktop/js/vendor/**" 15 | ], 16 | "dest" : "build/desktop/js" 17 | }, 18 | "mobile" : { 19 | "src" : [ 20 | "dev/mobile/js/**/*.js", 21 | "!dev/mobile/js/vendor/**" 22 | ], 23 | "dest" : "build/mobile/js" 24 | } 25 | } 26 | ``` 27 | 28 | --- 29 | 30 | `gulpfile.js` 31 | 32 | ```js 33 | // npm install gulp gulp-uglify 34 | var gulp = require('gulp'); 35 | var uglify = require('gulp-uglify'); 36 | var config = require('./config.json'); 37 | 38 | function doStuff(cfg) { 39 | return gulp.src(cfg.src) 40 | .pipe(uglify()) 41 | .pipe(gulp.dest(cfg.dest)); 42 | } 43 | 44 | gulp.task('dry', function () { 45 | doStuff(config.desktop); 46 | doStuff(config.mobile); 47 | }); 48 | ``` 49 | 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Fractal 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /docs/getting-started.md: -------------------------------------------------------------------------------- 1 | ## Getting Started 2 | 3 | ### 1. Install gulp globally: 4 | 5 | ``` 6 | npm install -g gulp 7 | ``` 8 | 9 | ### 2. Install gulp and gulp-util in your project devDependencies: 10 | 11 | ``` 12 | npm install --save-dev gulp gulp-util 13 | ``` 14 | 15 | ### 3. Create a `gulpfile.js` at the root of your project: 16 | 17 | ```javascript 18 | var gulp = require('gulp'); 19 | var gutil = require('gulp-util'); 20 | 21 | gulp.task('default', function(){ 22 | // place code for your default task here 23 | }); 24 | ``` 25 | 26 | ### 4. Run gulp 27 | 28 | ``` 29 | gulp 30 | ``` 31 | 32 | The default task will run and do nothing. 33 | 34 | To run individual tasks, use `gulp ` 35 | 36 | ## Where do I go now? 37 | 38 | 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 39 | 40 | ## .src, .watch, .dest, CLI args - How do I use these things? 41 | 42 | For API specific documentation you can check out the [documentation for that](API.md) 43 | 44 | ## Available Plugins 45 | 46 | The gulp community is growing, with new plugins being added daily. See the [main website](http://gulpjs.com/) for a complete list. 47 | -------------------------------------------------------------------------------- /docs/CLI.md: -------------------------------------------------------------------------------- 1 | ## gulp CLI docs 2 | 3 | ### Flags 4 | 5 | gulp has very few flags to know about. All other flags are for tasks to use if needed. 6 | 7 | - `-V` or `--version` will display the global and local gulp versions 8 | - `--require ` will require a module before running the gulpfile. This is useful for transpilers but also has other applications. You can use multiple `--require` flags 9 | - `--gulpfile ` manually set path of gulpfile. Useful if you have multiple gulpfiles. This will set the CWD to the gulpfile directory as well. 10 | - `--cwd ` manually set the CWD. The search for the gulpfile, as well as the relativity of all requires will be from here. 11 | - `-T` or `--tasks` will display the task dependency tree for the loaded gulpfile 12 | 13 | ### Tasks 14 | 15 | Tasks can be executed by running `gulp `. Just running `gulp` will execute the task you registered called `default`. If there is no `default` task gulp will error. 16 | 17 | ### Compilers 18 | 19 | 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. 20 | 21 | Example: 22 | 23 | ``` 24 | gulp dosomething --require coffee-script/register 25 | ``` -------------------------------------------------------------------------------- /docs/recipes/combining-streams-to-handle-errors.md: -------------------------------------------------------------------------------- 1 | # Combining streams to handle errors # 2 | 3 | By default, emitting an error on a stream will cause it to be thrown 4 | unless it already has a listener attached to the `error` event. This 5 | gets a bit tricky when you're working with longer pipelines of streams. 6 | 7 | By using [gulp-util](https://github.com/gulpjs/gulp-util)'s `combine` 8 | method you can turn a series of streams into a single stream, meaning you 9 | only need to listen to the `error` event in one place in your code. 10 | 11 | Here's an example of using it in a gulpfile: 12 | 13 | ``` javascript 14 | var combine = require('gulp-util').combine 15 | var uglify = require('gulp-uglify') 16 | var gulp = require('gulp') 17 | 18 | gulp.task('test', function() { 19 | var combined = combine( 20 | gulp.src('bootstrap/js/*.js'), 21 | uglify(), 22 | gulp.dest('public/bootstrap') 23 | ) 24 | 25 | // any errors in the above streams 26 | // will get caught by this listener, 27 | // instead of being thrown: 28 | combined.on('error', function(err) { 29 | console.warn(err.message) 30 | }) 31 | 32 | return combined 33 | }) 34 | ``` 35 | 36 | You can use this technique in your gulp plugins and node modules too using 37 | the [multipipe](http://npmjs.org/package/multipipe) module, and is a generally useful means of ensuring that errors aren't thrown from inaccessible streams 38 | somewhere deep inside `./node_modules`. 39 | -------------------------------------------------------------------------------- /docs/recipes/running-tasks-in-series.md: -------------------------------------------------------------------------------- 1 | # Running tasks in series 2 | 3 | By default, tasks run with maximum concurrency -- e.g. it launches all the tasks at once and waits for nothing. 4 | If you want to create a series where tasks run in a particular order, you need to do two things: 5 | 6 | - give it a hint to tell it when the task is done, 7 | - and give it a hint that a task depends on completion of another. 8 | 9 | For these examples, let's presume you have two tasks, "one" and "two" that you specifically want to run in this order: 10 | 11 | 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 12 | done or return a promise or stream that the engine should wait to resolve or end respectively. 13 | 14 | 2. In task "two" you add a hint telling the engine that it depends on completion of the first task. 15 | 16 | So this example would look like this: 17 | 18 | ```javascript 19 | var gulp = require('gulp'); 20 | 21 | // takes in a callback so the engine knows when it'll be done 22 | gulp.task('one', function (cb) { 23 | // do stuff -- async or otherwise 24 | cb(err); // if err is not null and not undefined, the orchestration will stop, and 'two' will not run 25 | }); 26 | 27 | // identifies a dependent task must be complete before this one begins 28 | gulp.task('two', ['one'], function () { 29 | // task 'one' is done now 30 | }); 31 | 32 | gulp.task('default', ['one', 'two']); 33 | // alternatively: gulp.task('default', ['two']); 34 | ``` 35 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var Orchestrator = require('orchestrator'); 5 | var gutil = require('gulp-util'); 6 | var deprecated = require('deprecated'); 7 | var vfs = require('vinyl-fs'); 8 | 9 | function Gulp(){ 10 | Orchestrator.call(this); 11 | } 12 | util.inherits(Gulp, Orchestrator); 13 | 14 | Gulp.prototype.task = Gulp.prototype.add; 15 | Gulp.prototype.run = function(){ 16 | // run() is deprecated as of 3.5 and will be removed in 4.0 17 | // use task dependencies instead 18 | 19 | // impose our opinion of "default" tasks onto orchestrator 20 | var tasks = arguments.length ? arguments : ['default']; 21 | 22 | this.start.apply(this, tasks); 23 | }; 24 | 25 | Gulp.prototype.src = vfs.src; 26 | Gulp.prototype.dest = vfs.dest; 27 | Gulp.prototype.watch = function (glob, opt, fn) { 28 | if (!fn) { 29 | fn = opt; 30 | opt = null; 31 | } 32 | 33 | // array of tasks given 34 | if (Array.isArray(fn)) { 35 | return vfs.watch(glob, opt, function(){ 36 | this.start.apply(this, fn); 37 | }.bind(this)); 38 | } 39 | 40 | return vfs.watch(glob, opt, fn); 41 | }; 42 | 43 | // let people use this class from our instance 44 | Gulp.prototype.Gulp = Gulp; 45 | 46 | // deprecations 47 | deprecated.field('gulp.env has been deprecated. Use gulp-util.env or your own CLI parser instead.', console.log, Gulp.prototype, 'env', gutil.env); 48 | Gulp.prototype.run = deprecated.method('gulp.run() has been deprecated. Use task dependencies or gulp.watch task triggering instead.', console.log, Gulp.prototype.run); 49 | 50 | var inst = new Gulp(); 51 | module.exports = inst; 52 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # gulp documentation 2 | 3 | * [Getting Started](getting-started.md) - How to get going with gulp 4 | * [API documentation](API.md) - Learn the ins and outs of using gulp 5 | * [CLI documentation](CLI.md) - Learn how to call tasks and use compilers 6 | * [Writing a Plugin](writing-a-plugin/README.md) - So you're writing a gulp plugin? Go here for the essential dos and don'ts. 7 | 8 | ## FAQ 9 | 10 | See [the FAQ](FAQ.md) for the answers to commonly asked questions. 11 | 12 | ## Recipes 13 | 14 | The community has compiled guides on how to use gulp for common use cases. Check out the [recipes folder](recipes) for a full list. 15 | 16 | ## Articles 17 | 18 | * [Introduction to node.js streams](https://github.com/substack/stream-handbook) 19 | * [Video introduction to node.js streams](http://www.youtube.com/watch?v=QgEuZ52OZtU) 20 | * [Getting started with gulp (by @markgdyr)](http://markgoodyear.com/2014/01/getting-started-with-gulp/) 21 | * [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) 22 | * [Inspiration (slides) about why gulp was made](http://slid.es/contra/gulp) 23 | 24 | ## License 25 | 26 | All the documentation is covered by the CC0 license *(do whatever you want with it - public domain)*. 27 | 28 | [![CC0](http://i.creativecommons.org/p/zero/1.0/88x31.png)](http://creativecommons.org/publicdomain/zero/1.0/) 29 | 30 | To the extent possible under law, [Fractal](http://wearefractal.com) has waived all copyright and related or neighboring rights to this work. 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp", 3 | "description": "The streaming build system", 4 | "version": "3.5.2", 5 | "homepage": "http://github.com/wearefractal/gulp", 6 | "repository": "git://github.com/wearefractal/gulp.git", 7 | "author": "Fractal (http://wearefractal.com/)", 8 | "main": "./index.js", 9 | "tags": [ 10 | "build", 11 | "stream", 12 | "system" 13 | ], 14 | "bin": { 15 | "gulp": "./bin/gulp.js" 16 | }, 17 | "dependencies": { 18 | "gulp-util": "~2.2.0", 19 | "orchestrator": "~0.3.0", 20 | "resolve": "~0.6.1", 21 | "findup-sync": "~0.1.2", 22 | "pretty-hrtime": "~0.2.0", 23 | "vinyl-fs": "~0.0.2", 24 | "semver": "~2.2.1", 25 | "archy": "~0.0.2", 26 | "deprecated": "~0.0.1", 27 | "minimist": "~0.0.5" 28 | }, 29 | "devDependencies": { 30 | "mocha": "~1.17.0", 31 | "mocha-lcov-reporter": "~0.0.1", 32 | "coveralls": "~2.7.0", 33 | "istanbul": "~0.2.3", 34 | "should": "~3.1.0", 35 | "rimraf": "~2.2.5", 36 | "q": "~1.0.0", 37 | "jshint": "~2.4.1", 38 | "graceful-fs": "~2.0.1", 39 | "gulp-jshint": "~1.3.4", 40 | "mkdirp": "~0.3.5" 41 | }, 42 | "scripts": { 43 | "test": "node gulpfile.js && mocha --reporter spec", 44 | "coveralls": "istanbul cover _mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | coveralls && rm -rf ./coverage" 45 | }, 46 | "engineStrict": true, 47 | "engines": { 48 | "node": ">= 0.9" 49 | }, 50 | "licenses": [ 51 | { 52 | "type": "MIT", 53 | "url": "http://github.com/wearefractal/gulp/raw/master/LICENSE" 54 | } 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /docs/FAQ.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | ## Why gulp? Why not ____? 4 | 5 | See the [gulp introduction slideshow] for a rundown on how gulp came to be. 6 | 7 | ## Is it "gulp" or "Gulp"? 8 | 9 | gulp is always lowercase. 10 | 11 | ## Where can I find a list of gulp plugins? 12 | 13 | gulp plugins always include the `gulpplugin` keyword. [Search gulp plugins][search-gulp-plugins] or [view all plugins][npm plugin search]. 14 | 15 | ## I want to write a gulp plugin, how do I get started? 16 | 17 | See the [Writing a gulp plugin] wiki page for guidelines and an example to get you started. 18 | 19 | ## My plugin does ____, is it doing too much? 20 | 21 | Probably. Ask yourself: 22 | 23 | 1. Is my plugin doing something that other plugins may need to do? 24 | - If so, that piece of functionality should be a separate plugin. [Check if it already exists on npm][npm plugin search]. 25 | 1. Is my plugin doing two, completely different things based on a configuration option? 26 | - If so, it may serve the community better to release it as two separate plugins 27 | - If the two tasks are different, but very closely related, it's probably OK 28 | 29 | ## How should newlines be represented in plugin output? 30 | 31 | Always use gulp-util.newline (which is \n) to prevent diff issues between operating systems. 32 | 33 | ## Where can I get updates on gulp? 34 | 35 | gulp updates can be found on the following twitters: 36 | 37 | - [@wearefractal](https://twitter.com/wearefractal) 38 | - [@eschoff](https://twitter.com/eschoff) 39 | - [@funkytek](https://twitter.com/funkytek) 40 | - [@gulpjs](https://twitter.com/gulpjs) 41 | 42 | ## Does gulp have an IRC channel? 43 | 44 | Yes, come chat with us in #gulpjs on [Freenode]. 45 | 46 | [Writing a gulp plugin]: writing-a-plugin/README.md 47 | [gulp introduction slideshow]: http://slid.es/contra/gulp 48 | [Freenode]: http://freenode.net/ 49 | [search-gulp-plugins]: http://gratimax.github.io/search-gulp-plugins/ 50 | [npm plugin search]: https://npmjs.org/browse/keyword/gulpplugin 51 | -------------------------------------------------------------------------------- /docs/recipes/mocha-test-runner-with-gulp.md: -------------------------------------------------------------------------------- 1 | # Mocha test-runner with gulp 2 | 3 | ### Passing shared module in all tests 4 | 5 | ```js 6 | var gulp = require('gulp'); 7 | var mocha = require('gulp-mocha'); 8 | 9 | gulp.task('tests', function() { 10 | return gulp.src(['test/test-*.js'], { read: false }) 11 | .pipe(mocha({ 12 | reporter: 'spec', 13 | globals: { 14 | should: require('should') 15 | } 16 | })); 17 | }); 18 | ``` 19 | 20 | ### Running mocha tests when files change 21 | 22 | With bundled `gulp.watch` and [`gulp-batch`](https://github.com/floatdrop/gulp-batch) (see readme of gulp-batch for reasons): 23 | 24 | ```js 25 | // npm install gulp gulp-watch gulp-mocha gulp-batch 26 | 27 | var gulp = require('gulp'); 28 | var mocha = require('gulp-mocha'); 29 | var batch = require('gulp-batch'); 30 | var gutil = require('gulp-util'); 31 | 32 | gulp.task('mocha', function () { 33 | return gulp.src(['test/*.js'], { read: false }) 34 | .pipe(mocha({ reporter: 'list' })) 35 | .on('error', gutil.log); 36 | }); 37 | 38 | gulp.watch(['lib/**', 'test/**'], batch(function(events, cb) { 39 | gulp.run('mocha', cb); 40 | })); 41 | ``` 42 | 43 | With [`gulp-watch`](https://github.com/floatdrop/gulp-watch) plugin: 44 | 45 | ```js 46 | // npm i gulp gulp-watch gulp-mocha 47 | 48 | var gulp = require('gulp'); 49 | var mocha = require('gulp-mocha'); 50 | var watch = require('gulp-watch'); 51 | var gutil = require('gulp-util') 52 | 53 | gulp.task('mocha', function () { 54 | return gulp.src(['test/*.js'], { read: false }) 55 | .pipe(mocha({ reporter: 'list' })) 56 | .on('error', gutil.log); 57 | }); 58 | 59 | gulp.task('watch', function() { 60 | return gulp.src(['lib/**', 'test/**'], { read: false }) 61 | .pipe(watch(function(events, cb) { 62 | gulp.run('mocha', cb); 63 | })); 64 | }); 65 | 66 | gulp.task('default', ['mocha', 'watch']); 67 | 68 | // run `gulp watch` or just `gulp` for watching and rerunning tests 69 | ``` 70 | -------------------------------------------------------------------------------- /docs/writing-a-plugin/README.md: -------------------------------------------------------------------------------- 1 | # Writing a plugin 2 | 3 | If you plan to create your own Gulp plugin, you will save time by reading the full documentation. 4 | 5 | * [Guidelines](guidelines.md) (a MUST read) 6 | * [Using buffers](using-buffers.md) 7 | * [Dealing with streams](dealing-with-streams.md) 8 | * [Testing](testing.md) 9 | * [README conventions](readme-conventions.md) 10 | 11 | ## What it does 12 | 13 | ### Streaming file objects 14 | 15 | A gulp plugin always returns a stream in [object mode](http://nodejs.org/api/stream.html#stream_object_mode) that does the following: 16 | 17 | 1. Takes in [vinyl File objects](http://github.com/wearefractal/vinyl) 18 | 2. Outputs [vinyl File objects](http://github.com/wearefractal/vinyl) 19 | 20 | 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. 21 | 22 | ### Modifying file content 23 | 24 | Vinyl files can have 3 possible forms for the contents attribute: 25 | 26 | - [Streams](dealing-with-streams.md) 27 | - [Buffers](using-buffers.md) 28 | - Empty (null) - Useful for things like rimraf, clean, where contents is not neeeded. 29 | 30 | ## Useful resources 31 | 32 | * [File object](https://github.com/wearefractal/gulp-util/#new-fileobj) 33 | * [PluginError](https://github.com/gulpjs/gulp-util#new-pluginerrorpluginname-message-options) 34 | * [event-stream](https://github.com/dominictarr/event-stream) 35 | * [BufferStream](https://github.com/nfroidure/BufferStream) 36 | * [gulp-util](https://github.com/wearefractal/gulp-util) 37 | 38 | 39 | ## Sample plugins 40 | 41 | * [sindresorhus' gulp plugins](https://github.com/search?q=%40sindresorhus+gulp-) 42 | * [Fractal's gulp plugins](https://github.com/search?q=%40wearefractal+gulp-) 43 | * [gulp-replace](https://github.com/lazd/gulp-replace) 44 | 45 | 46 | ## About streams 47 | 48 | If you're unfamiliar with streams, you will need to read up on them: 49 | 50 | * https://github.com/substack/stream-handbook (a MUST read) 51 | * http://nodejs.org/api/stream.html 52 | 53 | 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. 54 | -------------------------------------------------------------------------------- /docs/writing-a-plugin/using-buffers.md: -------------------------------------------------------------------------------- 1 | # Using buffers 2 | 3 | > Here is some information on creating gulp plugin that manipulates buffers. 4 | 5 | [Writing a Plugin](README.md) > Using buffers 6 | 7 | ## Using buffers 8 | 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: 9 | 10 | ```js 11 | var through = require('through2'); 12 | var gutil = require('gulp-util'); 13 | var PluginError = gutil.PluginError; 14 | 15 | // Consts 16 | const PLUGIN_NAME = 'gulp-prefixer'; 17 | 18 | // Plugin level function (dealing with files) 19 | function gulpPrefixer(prefixText) { 20 | 21 | if (!prefixText) { 22 | throw new PluginError(PLUGIN_NAME, "Missing prefix text!"); 23 | } 24 | prefixText = new Buffer(prefixText); // allocate ahead of time 25 | 26 | // Creating a stream through which each file will pass 27 | var stream = through.obj(function (file, enc, callback) { 28 | if (file.isNull()) { 29 | this.push(file); // Do nothing if no contents 30 | return callback(); 31 | } 32 | 33 | if (file.isBuffer()) { 34 | file.contents = Buffer.concat([prefixText, file.contents]); 35 | this.push(file); 36 | return callback(); 37 | } 38 | 39 | if (file.isStream()) { 40 | this.emit('error', new PluginError(PLUGIN_NAME, 'Streams are not supported!')); 41 | return callback(); 42 | } 43 | }); 44 | 45 | // returning the file stream 46 | return stream; 47 | }; 48 | 49 | // Exporting the plugin main function 50 | module.exports = gulpPrefixer; 51 | ``` 52 | The above plugin can be used like this: 53 | 54 | ```js 55 | var gulp = require('gulp'); 56 | var gulpPrefixer = require('gulp-prefixer'); 57 | 58 | gulp.src('files/**/*.js') 59 | .pipe(gulpPrefixer('prepended string')) 60 | .pipe(gulp.dest('/modified-files/')); 61 | ``` 62 | 63 | ## Handling streams 64 | 65 | 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. 66 | 67 | ## Some plugins based on buffers 68 | 69 | * [gulp-coffee](https://github.com/wearefractal/gulp-coffee) 70 | * [gulp-svgmin](https://github.com/ben-eb/gulp-svgmin) 71 | * [gulp-marked](https://github.com/lmtm/gulp-marked) 72 | * [gulp-svg2ttf](https://github.com/nfroidure/gulp-svg2ttf) 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |
6 | Visit our website! 7 |

8 | 9 | # gulp [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Coveralls Status][coveralls-image]][coveralls-url] [![Dependency Status][daviddm-url]][daviddm-image] 10 | > The streaming build system 11 | 12 | ## Like what we do? 13 | 14 | [Support us via gittip](https://www.gittip.com/Contra/) 15 | 16 | ## Documentation 17 | 18 | For a Getting started guide, API docs, recipes, making a plugin, etc. see the [documentation page](/docs/README.md)! 19 | 20 | ## Sample gulpfile 21 | 22 | This file is just a quick sample to give you a taste of what gulp does. 23 | 24 | ```javascript 25 | var gulp = require('gulp'); 26 | 27 | var coffee = require('gulp-coffee'); 28 | var concat = require('gulp-concat'); 29 | var uglify = require('gulp-uglify'); 30 | var imagemin = require('gulp-imagemin'); 31 | 32 | var paths = { 33 | scripts: ['client/js/**/*.coffee', '!client/external/**/*.coffee'], 34 | images: 'client/img/**/*' 35 | }; 36 | 37 | gulp.task('scripts', function() { 38 | // Minify and copy all JavaScript (except vendor scripts) 39 | return gulp.src(paths.scripts) 40 | .pipe(coffee()) 41 | .pipe(uglify()) 42 | .pipe(concat('all.min.js')) 43 | .pipe(gulp.dest('build/js')); 44 | }); 45 | 46 | // Copy all static images 47 | gulp.task('images', function() { 48 | return gulp.src(paths.images) 49 | // Pass in options to the task 50 | .pipe(imagemin({optimizationLevel: 5})) 51 | .pipe(gulp.dest('build/img')); 52 | }); 53 | 54 | // Rerun the task when a file changes 55 | gulp.task('watch', function () { 56 | gulp.watch(paths.scripts, ['scripts']); 57 | gulp.watch(paths.images, ['images']); 58 | }); 59 | 60 | // The default task (called when you run `gulp` from cli) 61 | gulp.task('default', ['scripts', 'images', 'watch']); 62 | 63 | ``` 64 | 65 | 66 | [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/wearefractal/gulp/trend.png)](https://bitdeli.com/free "Bitdeli Badge") 67 | 68 | [npm-url]: https://npmjs.org/package/gulp 69 | [npm-image]: https://badge.fury.io/js/gulp.png 70 | [travis-url]: https://travis-ci.org/gulpjs/gulp 71 | [travis-image]: https://travis-ci.org/gulpjs/gulp.png?branch=master 72 | [coveralls-url]: https://coveralls.io/r/gulpjs/gulp 73 | [coveralls-image]: https://coveralls.io/repos/gulpjs/gulp/badge.png 74 | [depstat-url]: https://david-dm.org/gulpjs/gulp 75 | [depstat-image]: https://david-dm.org/gulpjs/gulp.png 76 | [daviddm-url]: https://david-dm.org/gulpjs/gulp.png?theme=shields.io 77 | [daviddm-image]: https://david-dm.org/gulpjs/gulp 78 | -------------------------------------------------------------------------------- /docs/writing-a-plugin/dealing-with-streams.md: -------------------------------------------------------------------------------- 1 | # Dealing with streams 2 | 3 | > It is highly recommended to write plugins supporting streams. Here is some information on creating a gulp plugin that supports streams. 4 | 5 | > 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 6 | 7 | [Writing a Plugin](README.md) > Writing stream based plugins 8 | 9 | ## Dealing with streams 10 | 11 | Let's implement a plugin prepending some text to files. This plugin supports all possible forms of file.contents. 12 | 13 | ```js 14 | var through = require('through2'); 15 | var gutil = require('gulp-util'); 16 | var PluginError = gutil.PluginError; 17 | 18 | // Consts 19 | const PLUGIN_NAME = 'gulp-prefixer'; 20 | 21 | function prefixStream(prefixText) { 22 | var stream = through(); 23 | stream.write(prefixText); 24 | return stream; 25 | } 26 | 27 | // Plugin level function (dealing with files) 28 | function gulpPrefixer(prefixText) { 29 | 30 | if (!prefixText) { 31 | throw PluginError(PLUGIN_NAME, "Missing prefix text!"); 32 | } 33 | prefixText = new Buffer(prefixText); // allocate ahead of time 34 | 35 | // Creating a stream through which each file will pass 36 | var stream = through.obj(function (file, enc, callback) { 37 | if (file.isNull()) { 38 | this.push(file); // Do nothing if no contents 39 | return callback(); 40 | } 41 | 42 | if (file.isBuffer()) { 43 | this.emit('error', new PluginError(PLUGIN_NAME, 'Buffers not supported!')); 44 | return callback(); 45 | } 46 | 47 | if (file.isStream()) { 48 | // define the streamer that will transform the content 49 | var streamer = prefixStream(prefixText); 50 | // catch errors from the streamer and emit a gulp plugin error 51 | streamer.on('error', this.emit.bind(this, 'error')); 52 | // start the transformation 53 | file.contents = file.contents.pipe(streamer); 54 | // make sure the file goes through the next gulp plugin 55 | this.push(file); 56 | // tell the stream engine that we are done with this file 57 | return callback(); 58 | } 59 | }); 60 | 61 | // returning the file stream 62 | return stream; 63 | }; 64 | 65 | // Exporting the plugin main function 66 | module.exports = gulpPrefixer; 67 | ``` 68 | 69 | The above plugin can be used like this: 70 | 71 | ```js 72 | var gulp = require('gulp'); 73 | var gulpPrefixer = require('gulp-prefixer'); 74 | 75 | gulp.src('files/**/*.js') 76 | .pipe(gulpPrefixer('prepended string')) 77 | .pipe(gulp.dest('/modified-files/')); 78 | ``` 79 | 80 | ## Some plugins using streams 81 | 82 | * [gulp-svgicons2svgfont](https://github.com/nfroidure/gulp-svgiconstosvgfont) 83 | * gulp-browserify (Soon) 84 | -------------------------------------------------------------------------------- /docs/writing-a-plugin/testing.md: -------------------------------------------------------------------------------- 1 | # Testing 2 | 3 | > Testing your plugin is the only way to ensure quality. It brings confidence to your users and makes your life easier. 4 | 5 | [Writing a Plugin](README.md) > Testing 6 | 7 | ## Tooling 8 | 9 | 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. 10 | 11 | ## Testing plugins for streaming mode 12 | 13 | ```js 14 | var assert = require('assert'); 15 | var es = require('event-stream'); 16 | var gutil = require('gulp-util'); 17 | var prefixer = require('../index'); 18 | 19 | describe('gulp-prefixer', function () { 20 | describe('in streaming mode', function () { 21 | 22 | it('should prepend text', function (done) { 23 | 24 | // create the fake file 25 | var fakeFile = new gutil.File({ 26 | contents: es.readArray(['stream', 'with', 'those', 'contents']) 27 | }); 28 | 29 | // Create a prefixer plugin stream 30 | var myPrefixer = prefixer('prependthis'); 31 | 32 | // write the fake file to it 33 | myPrefixer.write(fakeFile); 34 | 35 | // wait for the file to come back out 36 | myPrefixer.once('data', function (file) { 37 | // make sure it came out the same way it went in 38 | assert(file.isStream()); 39 | 40 | // buffer the contents to make sure it got prepended to 41 | file.contents.pipe(es.wait(function (err, data) { 42 | // check the contents 43 | assert.equal(data, 'prependthistostreamwiththosecontents'); 44 | done(); 45 | })); 46 | }); 47 | 48 | }); 49 | 50 | }); 51 | }); 52 | ``` 53 | 54 | ## Testing plugins for buffer mode 55 | 56 | ```js 57 | var assert = require('assert'); 58 | var es = require('event-stream'); 59 | var gutil = require('gulp-util'); 60 | var prefixer = require('../index'); 61 | 62 | describe('gulp-prefixer', function () { 63 | describe('in buffer mode', function () { 64 | 65 | it('should prepend text', function (done) { 66 | 67 | // create the fake file 68 | var fakeFile = new gutil.File({ 69 | contents: new Buffer('abufferwiththiscontent') 70 | }); 71 | 72 | // Create a prefixer plugin stream 73 | var myPrefixer = prefixer('prependthis'); 74 | 75 | // write the fake file to it 76 | myPrefixer.write(fakeFile); 77 | 78 | // wait for the file to come back out 79 | myPrefixer.once('data', function (file) { 80 | // make sure it came out the same way it went in 81 | assert(file.isBuffer()); 82 | 83 | // check the contents 84 | assert.equal(file.contents.toString('utf8'), 'prependthisabufferwiththiscontent'); 85 | done(); 86 | }); 87 | 88 | }); 89 | 90 | }); 91 | }); 92 | ``` 93 | 94 | ## Sample tests 95 | * [gulp-cat](https://github.com/ben-eb/gulp-cat/blob/master/test.js) 96 | -------------------------------------------------------------------------------- /test/watch.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('../'); 4 | var fs = require('graceful-fs'); 5 | var rimraf = require('rimraf'); 6 | var mkdirp = require('mkdirp'); 7 | var path = require('path'); 8 | 9 | var should = require('should'); 10 | require('mocha'); 11 | 12 | var outpath = path.join(__dirname, "./out-fixtures"); 13 | 14 | describe('gulp', function() { 15 | describe('watch()', function() { 16 | beforeEach(rimraf.bind(null, outpath)); 17 | beforeEach(mkdirp.bind(null, outpath)); 18 | afterEach(rimraf.bind(null, outpath)); 19 | 20 | var tempFileContent = 'A test generated this file and it is safe to delete'; 21 | 22 | var writeTimeout = 125; // Wait for it to get to the filesystem 23 | var writeFileWait = function (name, content, cb) { 24 | if (!cb) cb = function(){}; 25 | setTimeout(function () { 26 | fs.writeFile(name, content, cb); 27 | }, writeTimeout); 28 | }; 29 | it('should call the function when file changes: no options', function(done) { 30 | 31 | // arrange 32 | var tempFile = path.join(outpath, 'watch-func.txt'); 33 | fs.writeFile(tempFile, tempFileContent, function () { 34 | 35 | // assert: it works if it calls done 36 | var watcher = gulp.watch(tempFile, function (evt) { 37 | should.exist(evt); 38 | should.exist(evt.path); 39 | should.exist(evt.type); 40 | evt.type.should.equal('changed'); 41 | evt.path.should.equal(path.resolve(tempFile)); 42 | watcher.end(); 43 | done(); 44 | }); 45 | 46 | // act: change file 47 | writeFileWait(tempFile, tempFileContent+' changed'); 48 | }); 49 | }); 50 | 51 | it('should call the function when file changes: w/ options', function(done) { 52 | // arrange 53 | var tempFile = path.join(outpath, 'watch-func-options.txt'); 54 | fs.writeFile(tempFile, tempFileContent, function () { 55 | 56 | // assert: it works if it calls done 57 | var watcher = gulp.watch(tempFile, {debounceDelay:5}, function (evt) { 58 | should.exist(evt); 59 | should.exist(evt.path); 60 | should.exist(evt.type); 61 | evt.type.should.equal('changed'); 62 | evt.path.should.equal(path.resolve(tempFile)); 63 | watcher.end(); 64 | done(); 65 | }); 66 | 67 | // act: change file 68 | writeFileWait(tempFile, tempFileContent+' changed'); 69 | }); 70 | }); 71 | 72 | it('should run many tasks: w/ options', function(done) { 73 | // arrange 74 | var tempFile = path.join(outpath, 'watch-task-options.txt'); 75 | var task1 = 'task1'; 76 | var task2 = 'task2'; 77 | var task3 = 'task3'; 78 | var a = 0; 79 | var timeout = writeTimeout * 2.5; 80 | 81 | fs.writeFile(tempFile, tempFileContent, function () { 82 | 83 | gulp.task(task1, function () { 84 | a++; 85 | }); 86 | gulp.task(task2, function () { 87 | a += 10; 88 | }); 89 | gulp.task(task3, function () { 90 | throw new Error("task3 called!"); 91 | }); 92 | 93 | // assert 94 | setTimeout(function () { 95 | a.should.equal(11); // task1 and task2 96 | 97 | gulp.reset(); 98 | watcher.end(); 99 | done(); 100 | }, timeout); 101 | 102 | // it works if it calls the task 103 | var watcher = gulp.watch(tempFile, {debounceDelay:timeout/2}, [task1,task2]); 104 | 105 | // act: change file 106 | writeFileWait(tempFile, tempFileContent+' changed'); 107 | }); 108 | }); 109 | 110 | }); 111 | }); 112 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # gulp changelog 2 | 3 | # 3.5.2 4 | 5 | - add -V for version on CLI (unix standard) 6 | - -v is deprecated, use -V 7 | - add -T as an alias for --tasks 8 | - documentation 9 | 10 | # 3.5 11 | 12 | - added `gulp.watch(globs, tasksArray)` sugar 13 | - remove gulp.taskQueue 14 | - deprecate gulp.run 15 | - deprecate gulp.env 16 | - add engineStrict to prevent people with node < 0.9 from installing 17 | 18 | # 3.4 19 | 20 | - added `--tasks` that prints out the tree of tasks + deps 21 | - global cli + local install mismatch is no longer fatal 22 | - remove tests for fs stuff 23 | - switch core src, dest, and watch to vinyl-fs 24 | - internal cleaning 25 | 26 | ## 3.3.4 27 | 28 | - `--base` is now `--cwd` 29 | 30 | ## 3.3.3 31 | 32 | - support for `--base` CLI arg to change where the search for gulpfile/`--require`s starts 33 | - support for `--gulpfile` CLI arg to point to a gulpfile specifically 34 | 35 | ## 3.3.0 36 | 37 | - file.contents streams are no longer paused coming out of src 38 | - dest now passes files through before they are empty to fix passing to multiple dests 39 | 40 | ## 3.2.4 41 | 42 | - Bug fix - we didn't have any CLI tests 43 | 44 | ## 3.2.3 45 | 46 | - Update dependencies for bug fixes 47 | - autocomplete stuff in the completion folder 48 | 49 | ## 3.2 50 | 51 | - File object is now [vinyl](https://github.com/wearefractal/vinyl) 52 | - .watch() is now [glob-watcher](https://github.com/wearefractal/glob-watcher) 53 | - Fix CLI -v when no gulpfile found 54 | - gulp-util updated 55 | - Logging moved to CLI bin file 56 | - Will cause double logging if you update global CLI to 3.2 but not local 57 | - Will cause no logging if you update local to 3.1 but not global CLI 58 | - Drop support for < 0.9 59 | 60 | ## 3.1.3 61 | 62 | - Move isStream and isBuffer to gulp-util 63 | 64 | ## 3.1 65 | 66 | - Move file class to gulp-util 67 | 68 | ## 3.0 69 | 70 | - Ability to pass multiple globs and glob negations to glob-stream 71 | - Breaking change to the way glob-stream works 72 | - File object is now a class 73 | - file.shortened changed to file.relative 74 | - file.cwd added 75 | - Break out getStats to avoid nesting 76 | - Major code reorganization 77 | 78 | ## 2.7 79 | 80 | - Breaking change to the way options are passed to glob-stream 81 | - Introduce new File object to ease pain of computing shortened names (now a getter) 82 | 83 | ## 2.4 - 2.6 84 | 85 | - Moved stuff to gulp-util 86 | - Quit exposing createGlobStream (just use the glob-stream module) 87 | - More logging 88 | - Prettier time durations 89 | - Tons of documentation changes 90 | - gulp.trigger(tasks...) as a through stream 91 | 92 | ## 1.2-2.4 (11/12/13) 93 | 94 | - src buffer=false fixed for 0.8 and 0.9 (remember to .resume() on these versions before consuming) 95 | - CLI completely rewritten 96 | - Colorful logging 97 | - Uses local version of gulp to run tasks 98 | - Uses findup to locate gulpfile (so you can run it anywhere in your project) 99 | - chdir to gulpfile directory before loading it 100 | - Correct exit codes on errors 101 | - silent flag added to gulp to disable logging 102 | - Fixes to task orchestration (3rd party) 103 | - Better support for globbed directories (thanks @robrich) 104 | 105 | ## 1.2 (10/28/13) 106 | 107 | - Can specify buffer=false on src streams to make file.content a stream 108 | - Can specify read=false on src streams to disable file.content 109 | 110 | ## 1.1 (10/21/13) 111 | 112 | - Can specify run callback 113 | - Can specify task dependencies 114 | - Tasks can accept callback or return promise 115 | - `gulp.verbose` exposes run-time internals 116 | 117 | ## 1.0 (9/26/13) 118 | 119 | - Specify dependency versions 120 | - Updated docs 121 | 122 | ## 0.2 (8/6/13) 123 | 124 | - Rename .files() to .src() and .folder() to .dest() 125 | 126 | ## 0.1 (7/18/13) 127 | 128 | - Initial Release 129 | -------------------------------------------------------------------------------- /test/tasks.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('../'); 4 | var Q = require('q'); 5 | var should = require('should'); 6 | require('mocha'); 7 | 8 | describe('gulp tasks', function() { 9 | describe('task()', function() { 10 | it('should define a task', function(done) { 11 | var fn; 12 | fn = function() {}; 13 | gulp.task('test', fn); 14 | should.exist(gulp.tasks.test); 15 | gulp.tasks.test.fn.should.equal(fn); 16 | gulp.reset(); 17 | done(); 18 | }); 19 | }); 20 | describe('run()', function() { 21 | it('should run multiple tasks', function(done) { 22 | var a, fn, fn2; 23 | a = 0; 24 | fn = function() { 25 | this.should.equal(gulp); 26 | ++a; 27 | }; 28 | fn2 = function() { 29 | this.should.equal(gulp); 30 | ++a; 31 | }; 32 | gulp.task('test', fn); 33 | gulp.task('test2', fn2); 34 | gulp.run('test', 'test2'); 35 | a.should.equal(2); 36 | gulp.reset(); 37 | done(); 38 | }); 39 | it('should run all tasks when call run() multiple times', function(done) { 40 | var a, fn, fn2; 41 | a = 0; 42 | fn = function() { 43 | this.should.equal(gulp); 44 | ++a; 45 | }; 46 | fn2 = function() { 47 | this.should.equal(gulp); 48 | ++a; 49 | }; 50 | gulp.task('test', fn); 51 | gulp.task('test2', fn2); 52 | gulp.run('test'); 53 | gulp.run('test2'); 54 | a.should.equal(2); 55 | gulp.reset(); 56 | done(); 57 | }); 58 | it('should run all async promise tasks', function(done) { 59 | var a, fn, fn2; 60 | a = 0; 61 | fn = function() { 62 | var deferred = Q.defer(); 63 | setTimeout(function () { 64 | ++a; 65 | deferred.resolve(); 66 | },1); 67 | return deferred.promise; 68 | }; 69 | fn2 = function() { 70 | var deferred = Q.defer(); 71 | setTimeout(function () { 72 | ++a; 73 | deferred.resolve(); 74 | },1); 75 | return deferred.promise; 76 | }; 77 | gulp.task('test', fn); 78 | gulp.task('test2', fn2); 79 | gulp.run('test'); 80 | gulp.run('test2', function () { 81 | gulp.isRunning.should.equal(false); 82 | a.should.equal(2); 83 | gulp.reset(); 84 | done(); 85 | }); 86 | gulp.isRunning.should.equal(true); 87 | }); 88 | it('should run all async callback tasks', function(done) { 89 | var a, fn, fn2; 90 | a = 0; 91 | fn = function(cb) { 92 | setTimeout(function () { 93 | ++a; 94 | cb(null); 95 | },1); 96 | }; 97 | fn2 = function(cb) { 98 | setTimeout(function () { 99 | ++a; 100 | cb(null); 101 | },1); 102 | }; 103 | gulp.task('test', fn); 104 | gulp.task('test2', fn2); 105 | gulp.run('test'); 106 | gulp.run('test2', function () { 107 | gulp.isRunning.should.equal(false); 108 | a.should.equal(2); 109 | gulp.reset(); 110 | done(); 111 | }); 112 | gulp.isRunning.should.equal(true); 113 | }); 114 | it('should emit task_not_found and throw an error when task is not defined', function(done) { 115 | gulp.on('task_not_found', function(err){ 116 | should.exist(err); 117 | should.exist(err.task); 118 | err.task.should.equal('test'); 119 | gulp.reset(); 120 | done(); 121 | }); 122 | try { 123 | gulp.run('test'); 124 | } catch (err) { 125 | should.exist(err); 126 | } 127 | }); 128 | it('should run task scoped to gulp', function(done) { 129 | var a, fn; 130 | a = 0; 131 | fn = function() { 132 | this.should.equal(gulp); 133 | ++a; 134 | }; 135 | gulp.task('test', fn); 136 | gulp.run('test'); 137 | a.should.equal(1); 138 | gulp.isRunning.should.equal(false); 139 | gulp.reset(); 140 | done(); 141 | }); 142 | it('should run default task scoped to gulp', function(done) { 143 | var a, fn; 144 | a = 0; 145 | fn = function() { 146 | this.should.equal(gulp); 147 | ++a; 148 | }; 149 | gulp.task('default', fn); 150 | gulp.run(); 151 | a.should.equal(1); 152 | gulp.isRunning.should.equal(false); 153 | gulp.reset(); 154 | done(); 155 | }); 156 | }); 157 | }); 158 | -------------------------------------------------------------------------------- /docs/writing-a-plugin/guidelines.md: -------------------------------------------------------------------------------- 1 | # Guidelines 2 | 3 | > 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. 4 | 5 | [Writing a Plugin](README.md) > Guidelines 6 | 7 | 1. Your plugin should not do something that can be done easily with an existing node module 8 | - Wrapping every possible thing just for the sake of wrapping it will pollute the ecosystem with low quality plugins that don't make sense 9 | 1. Your plugin should only do **one thing**, and do it well. 10 | - Avoid config options that make your plugin do completely different tasks 11 | 1. Your plugin shouldn't do things that other plugins are responsible for 12 | - It should not concat, [gulp-concat](https://github.com/wearefractal/gulp-concat) does that 13 | - It should not add headers, [gulp-header](https://github.com/godaddy/gulp-header) does that 14 | - It should not add footers, [gulp-footer](https://github.com/godaddy/gulp-footer) does that 15 | - If it's a common but optional use case, document that your plugin is often used with another plugin 16 | - If it's an internal requirement, make use of existing plugins by piping your plugin's output to them 17 | 1. Your plugin **must be tested** 18 | - Testing a gulp plugin is easy, you don't even need gulp to test it 19 | - Look at other plugins for examples 20 | 1. Add `gulpplugin` as a keyword in your `package.json` so you show up on our search 21 | 1. Do not throw errors inside a stream 22 | - Instead, you should emit it as an **error** event. 23 | - If you encounter an error **outside** the stream, such as invalid configuration, you may throw it. 24 | 1. Prefix any errors with the name of your plugin 25 | - For example: `gulp-replace: Cannot do regexp replace on a stream` 26 | - Use gulp-util's [PluginError](https://github.com/gulpjs/gulp-util#new-pluginerrorpluginname-message-options) class to make this easy 27 | 1. The type of `file.contents` should always be the same going out as it was when it came in 28 | - If file.contents is null (non-read) just ignore the file and pass it along 29 | - If file.contents is a Stream and you don't support that just emit an error 30 | - If you choose to support file.contents as a Stream, use [BufferStream](https://github.com/nfroidure/BufferStream) to make this easy 31 | 1. Do not pass the `file` object downstream until you are done with it 32 | 1. Make use of the [gulp-util](https://github.com/gulpjs/gulp-util) library 33 | - It provides templating, CLI colors, logging, changing file extensions 34 | - Try looking for common things there first and add it if it doesn't exist 35 | 1. Do NOT require `gulp` as a dependency or peerDependency 36 | - There is no reason you should have to do this and it will cause problems if you do 37 | 38 | ## Why are these guidelines so strict? 39 | 40 | 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. 41 | 42 | ### What happens if I don't follow them? 43 | 44 | 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". 45 | 46 | ### What does a good plugin look like? 47 | 48 | ```js 49 | // through2 is a thin wrapper around node transform streams 50 | var through = require('through2'); 51 | var gutil = require('gulp-util'); 52 | var PluginError = gutil.PluginError; 53 | 54 | // Consts 55 | const PLUGIN_NAME = 'gulp-prefixer'; 56 | 57 | function prefixStream(prefixText) { 58 | var stream = through(); 59 | stream.write(prefixText); 60 | return stream; 61 | } 62 | 63 | // Plugin level function (dealing with files) 64 | function gulpPrefixer(prefixText) { 65 | 66 | if (!prefixText) { 67 | throw PluginError(PLUGIN_NAME, "Missing prefix text!"); 68 | } 69 | prefixText = new Buffer(prefixText); // allocate ahead of time 70 | 71 | // Creating a stream through which each file will pass 72 | var stream = through.obj(function (file, enc, callback) { 73 | if (file.isNull()) { 74 | this.push(file); // Do nothing if no contents 75 | return callback(); 76 | } 77 | 78 | if (file.isBuffer()) { 79 | file.contents = Buffer.concat([prefixText, file.contents]); 80 | this.push(file); 81 | return callback(); 82 | } 83 | 84 | if (file.isStream()) { 85 | file.contents = file.contents.pipe(prefixStream(prefixText)); 86 | this.push(file); 87 | return callback(); 88 | } 89 | }); 90 | 91 | // returning the file stream 92 | return stream; 93 | }; 94 | 95 | // Exporting the plugin main function 96 | module.exports = gulpPrefixer; 97 | ``` 98 | -------------------------------------------------------------------------------- /test/dest.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('../'); 4 | var should = require('should'); 5 | var join = require('path').join; 6 | var rimraf = require('rimraf'); 7 | var fs = require('graceful-fs'); 8 | 9 | require('mocha'); 10 | 11 | var outpath = join(__dirname, "./out-fixtures"); 12 | 13 | describe('gulp output stream', function() { 14 | describe('dest()', function() { 15 | beforeEach(rimraf.bind(null, outpath)); 16 | afterEach(rimraf.bind(null, outpath)); 17 | 18 | it('should return a stream', function(done) { 19 | var stream = gulp.dest(join(__dirname, "./fixtures/")); 20 | should.exist(stream); 21 | should.exist(stream.on); 22 | done(); 23 | }); 24 | 25 | it('should return a output stream that writes files', function(done) { 26 | var instream = gulp.src(join(__dirname, "./fixtures/**/*.txt")); 27 | var outstream = gulp.dest(outpath); 28 | instream.pipe(outstream); 29 | 30 | outstream.on('error', done); 31 | outstream.on('data', function(file) { 32 | // data should be re-emitted right 33 | should.exist(file); 34 | should.exist(file.path); 35 | should.exist(file.contents); 36 | join(file.path,'').should.equal(join(outpath, "./copy/example.txt")); 37 | String(file.contents).should.equal("this is a test"); 38 | }); 39 | outstream.on('end', function() { 40 | fs.readFile(join(outpath, "copy", "example.txt"), function(err, contents){ 41 | should.not.exist(err); 42 | should.exist(contents); 43 | String(contents).should.equal("this is a test"); 44 | done(); 45 | }); 46 | }); 47 | }); 48 | 49 | it('should return a output stream that does not write non-read files', function(done) { 50 | var instream = gulp.src(join(__dirname, "./fixtures/**/*.txt"), {read:false}); 51 | var outstream = gulp.dest(outpath); 52 | instream.pipe(outstream); 53 | 54 | outstream.on('error', done); 55 | outstream.on('data', function(file) { 56 | // data should be re-emitted right 57 | should.exist(file); 58 | should.exist(file.path); 59 | should.not.exist(file.contents); 60 | join(file.path,'').should.equal(join(outpath, "./copy/example.txt")); 61 | }); 62 | outstream.on('end', function() { 63 | fs.readFile(join(outpath, "copy", "example.txt"), function(err, contents){ 64 | should.exist(err); 65 | should.not.exist(contents); 66 | done(); 67 | }); 68 | }); 69 | }); 70 | 71 | it('should return a output stream that writes streaming files', function(done) { 72 | var instream = gulp.src(join(__dirname, "./fixtures/**/*.txt"), {buffer:false}); 73 | var outstream = instream.pipe(gulp.dest(outpath)); 74 | 75 | outstream.on('error', done); 76 | outstream.on('data', function(file) { 77 | // data should be re-emitted right 78 | should.exist(file); 79 | should.exist(file.path); 80 | should.exist(file.contents); 81 | join(file.path,'').should.equal(join(outpath, "./copy/example.txt")); 82 | }); 83 | outstream.on('end', function() { 84 | fs.readFile(join(outpath, "copy", "example.txt"), function(err, contents){ 85 | should.not.exist(err); 86 | should.exist(contents); 87 | String(contents).should.equal("this is a test"); 88 | done(); 89 | }); 90 | }); 91 | }); 92 | 93 | it('should return a output stream that writes streaming files into new directories', function(done) { 94 | testWriteDir({}, done); 95 | }); 96 | 97 | it('should return a output stream that writes streaming files into new directories (buffer: false)', function(done) { 98 | testWriteDir({buffer: false}, done); 99 | }); 100 | 101 | it('should return a output stream that writes streaming files into new directories (read: false)', function(done) { 102 | testWriteDir({read: false}, done); 103 | }); 104 | 105 | it('should return a output stream that writes streaming files into new directories (read: false, buffer: false)', function(done) { 106 | testWriteDir({buffer: false, read: false}, done); 107 | }); 108 | 109 | function testWriteDir(srcOptions, done) { 110 | var instream = gulp.src(join(__dirname, "./fixtures/stuff"), srcOptions); 111 | var outstream = instream.pipe(gulp.dest(outpath)); 112 | 113 | outstream.on('error', done); 114 | outstream.on('data', function(file) { 115 | // data should be re-emitted right 116 | should.exist(file); 117 | should.exist(file.path); 118 | join(file.path,'').should.equal(join(outpath, "./stuff")); 119 | }); 120 | outstream.on('end', function() { 121 | fs.exists(join(outpath, "stuff"), function(exists) { 122 | /* stinks that ok is an expression instead of a function call */ 123 | /* jshint expr: true */ 124 | should(exists).be.ok; 125 | /* jshint expr: false */ 126 | done(); 127 | }); 128 | }); 129 | } 130 | 131 | }); 132 | }); 133 | -------------------------------------------------------------------------------- /bin/gulp.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict'; 4 | 5 | var path = require('path'); 6 | 7 | var resolve = require('resolve'); 8 | var findup = require('findup-sync'); 9 | var gutil = require('gulp-util'); 10 | var prettyTime = require('pretty-hrtime'); 11 | var minimist = require('minimist'); 12 | var semver = require('semver'); 13 | var archy = require('archy'); 14 | 15 | var completion = require('../lib/completion'); 16 | var taskTree = require('../lib/taskTree'); 17 | var cliPkg = require('../package.json'); 18 | 19 | // parse what we want off the CLI 20 | var argv = minimist(process.argv.slice(2)); 21 | 22 | if (argv.completion) { 23 | return completion(argv.completion); 24 | } 25 | 26 | var tasks = argv._; 27 | var tasksFlag = argv.T || argv.tasks; 28 | 29 | // TODO: drop argv.v in the next breaking release 30 | var versionFlag = argv.v || argv.V || argv.version; 31 | var cwdFlag = argv.cwd; 32 | var fileFlag = argv.gulpfile; 33 | 34 | var cwd; 35 | 36 | if (cwdFlag) { 37 | cwd = path.resolve(cwdFlag); 38 | } else { 39 | cwd = process.cwd(); 40 | } 41 | 42 | loadRequires(argv.require, cwd); 43 | 44 | var gulpFile; 45 | 46 | if (fileFlag) { 47 | gulpFile = path.join(cwd, fileFlag); 48 | } else { 49 | gulpFile = getGulpFile(cwd); 50 | } 51 | 52 | // find the local gulp 53 | var localGulp = findLocalGulp(gulpFile); 54 | var localPkg = findLocalGulpPackage(gulpFile); 55 | 56 | // print some versions and shit 57 | if (versionFlag) { 58 | gutil.log('CLI version', cliPkg.version); 59 | if (localGulp) { 60 | gutil.log('Local version', localPkg.version); 61 | } 62 | process.exit(0); 63 | } 64 | 65 | if (!localGulp) { 66 | gutil.log(gutil.colors.red('No local gulp install found in'), gutil.colors.magenta(getLocalBase(gulpFile))); 67 | gutil.log(gutil.colors.red('Try running: npm install gulp')); 68 | process.exit(1); 69 | } 70 | 71 | // check for semver difference in running gulp vs locally installed and warn if running gulp > locally installed 72 | if (semver.gt(cliPkg.version, localPkg.version)) { 73 | gutil.log(gutil.colors.red('gulp version mismatch:')); 74 | gutil.log(gutil.colors.red('Running gulp is', cliPkg.version)); 75 | gutil.log(gutil.colors.red('Local gulp (installed in gulpfile dir) is', localPkg.version)); 76 | } 77 | 78 | if (!gulpFile) { 79 | gutil.log(gutil.colors.red('No gulpfile found')); 80 | process.exit(1); 81 | } 82 | 83 | // Wire up logging for tasks 84 | // on local gulp singleton 85 | logEvents(localGulp); 86 | 87 | // Load their gulpfile and run it 88 | gutil.log('Using file', gutil.colors.magenta(gulpFile)); 89 | loadGulpFile(localGulp, gulpFile, tasks); 90 | 91 | function loadRequires(requires, baseDir) { 92 | if (typeof requires === 'undefined') requires = []; 93 | if (!Array.isArray(requires)) requires = [requires]; 94 | return requires.map(function(modName){ 95 | gutil.log('Requiring external module', gutil.colors.magenta(modName)); 96 | var mod = findLocalModule(modName, baseDir); 97 | if (typeof mod === 'undefined') { 98 | gutil.log('Failed to load external module', gutil.colors.magenta(modName)); 99 | } 100 | }); 101 | } 102 | 103 | function getLocalBase(gulpFile) { 104 | return path.resolve(path.dirname(gulpFile)); 105 | } 106 | 107 | function findLocalGulp(gulpFile){ 108 | var baseDir = getLocalBase(gulpFile); 109 | return findLocalModule('gulp', baseDir); 110 | } 111 | 112 | function findLocalModule(modName, baseDir){ 113 | try { 114 | return require(resolve.sync(modName, {basedir: baseDir})); 115 | } catch(e) {} 116 | return; 117 | } 118 | 119 | function findLocalGulpPackage(gulpFile){ 120 | var baseDir = getLocalBase(gulpFile); 121 | var packageBase; 122 | try { 123 | packageBase = path.dirname(resolve.sync('gulp', {basedir: baseDir})); 124 | return require(path.join(packageBase, 'package.json')); 125 | } catch(e) {} 126 | return; 127 | } 128 | 129 | function loadGulpFile(localGulp, gulpFile, tasks){ 130 | var gulpFileCwd = path.dirname(gulpFile); 131 | process.chdir(gulpFileCwd); 132 | gutil.log('Working directory changed to', gutil.colors.magenta(gulpFileCwd)); 133 | 134 | var theGulpfile = require(gulpFile); 135 | 136 | // just for good measure 137 | process.nextTick(function(){ 138 | if (tasksFlag) { 139 | return logTasks(gulpFile, localGulp); 140 | } 141 | 142 | startGulp(localGulp, tasks); 143 | }); 144 | return theGulpfile; 145 | } 146 | 147 | function startGulp(localGulp, tasks) { 148 | // impose our opinion of "default" tasks onto orchestrator 149 | var toRun = tasks.length ? tasks : ['default']; 150 | return localGulp.start.apply(localGulp, toRun); 151 | } 152 | 153 | function logTasks(gulpFile, localGulp) { 154 | var tree = taskTree(localGulp.tasks); 155 | tree.label = 'Tasks for '+gutil.colors.magenta(gulpFile); 156 | archy(tree).split('\n').forEach(function(v){ 157 | if (v.trim().length === 0) return; 158 | gutil.log(v); 159 | }); 160 | } 161 | 162 | function getGulpFile() { 163 | var extensions; 164 | if (require.extensions) { 165 | extensions = Object.keys(require.extensions).join(','); 166 | } else { 167 | extensions = ['.js']; 168 | } 169 | var gulpFile = findup('gulpfile{'+extensions+'}', {cwd: cwd, nocase: true}); 170 | return gulpFile; 171 | } 172 | 173 | // format orchestrator errors 174 | function formatError (e) { 175 | if (!e.err) return e.message; 176 | if (e.err.message) return e.err.message; 177 | return JSON.stringify(e.err); 178 | } 179 | 180 | // wire up logging events 181 | function logEvents(gulp) { 182 | gulp.on('task_start', function(e){ 183 | gutil.log('Running', "'"+gutil.colors.cyan(e.task)+"'..."); 184 | }); 185 | 186 | gulp.on('task_stop', function(e){ 187 | var time = prettyTime(e.hrDuration); 188 | gutil.log('Finished', "'"+gutil.colors.cyan(e.task)+"'", 'in', gutil.colors.magenta(time)); 189 | }); 190 | 191 | gulp.on('task_err', function(e){ 192 | var msg = formatError(e); 193 | var time = prettyTime(e.hrDuration); 194 | gutil.log('Errored', "'"+gutil.colors.cyan(e.task)+"'", 'in', gutil.colors.magenta(time), gutil.colors.red(msg)); 195 | }); 196 | 197 | gulp.on('task_not_found', function(err){ 198 | gutil.log(gutil.colors.red("Task '"+err.task+"' was not defined in your gulpfile but you tried to run it.")); 199 | gutil.log('Please check the documentation for proper gulpfile formatting.'); 200 | process.exit(1); 201 | }); 202 | } 203 | -------------------------------------------------------------------------------- /test/src.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('../'); 4 | var should = require('should'); 5 | var join = require('path').join; 6 | 7 | require('mocha'); 8 | 9 | describe('gulp input stream', function() { 10 | describe('src()', function() { 11 | it('should return a stream', function(done) { 12 | var stream = gulp.src(join(__dirname, "./fixtures/*.coffee")); 13 | should.exist(stream); 14 | should.exist(stream.on); 15 | done(); 16 | }); 17 | it('should return a input stream from a flat glob', function(done) { 18 | var stream = gulp.src(join(__dirname, "./fixtures/*.coffee")); 19 | stream.on('error', done); 20 | stream.on('data', function(file) { 21 | should.exist(file); 22 | should.exist(file.path); 23 | should.exist(file.contents); 24 | join(file.path,'').should.equal(join(__dirname, "./fixtures/test.coffee")); 25 | String(file.contents).should.equal("this is a test"); 26 | }); 27 | stream.on('end', function() { 28 | done(); 29 | }); 30 | }); 31 | 32 | it('should return a input stream for multiple globs', function(done) { 33 | var globArray = [ 34 | join(__dirname, "./fixtures/stuff/run.dmc"), 35 | join(__dirname, "./fixtures/stuff/test.dmc") 36 | ]; 37 | var stream = gulp.src(globArray); 38 | 39 | var files = []; 40 | stream.on('error', done); 41 | stream.on('data', function(file) { 42 | should.exist(file); 43 | should.exist(file.path); 44 | files.push(file); 45 | }); 46 | stream.on('end', function() { 47 | files.length.should.equal(2); 48 | files[0].path.should.equal(globArray[0]); 49 | files[1].path.should.equal(globArray[1]); 50 | done(); 51 | }); 52 | }); 53 | 54 | it('should return a input stream for multiple globs, with negation', function(done) { 55 | var expectedPath = join(__dirname, "./fixtures/stuff/run.dmc"); 56 | var globArray = [ 57 | join(__dirname, "./fixtures/stuff/*.dmc"), 58 | '!' + join(__dirname, "./fixtures/stuff/test.dmc"), 59 | ]; 60 | var stream = gulp.src(globArray); 61 | 62 | var files = []; 63 | stream.on('error', done); 64 | stream.on('data', function(file) { 65 | should.exist(file); 66 | should.exist(file.path); 67 | files.push(file); 68 | }); 69 | stream.on('end', function() { 70 | files.length.should.equal(1); 71 | files[0].path.should.equal(expectedPath); 72 | done(); 73 | }); 74 | }); 75 | 76 | it('should return a input stream for multiple globs, with negation', function(done) { 77 | var expectedPath = join(__dirname, "./fixtures/stuff/run.dmc"); 78 | var globArray = [ 79 | join(__dirname, "./fixtures/stuff/run.dmc"), 80 | '!' + join(__dirname, "./fixtures/stuff/test.dmc"), 81 | ]; 82 | var stream = gulp.src(globArray); 83 | 84 | var files = []; 85 | stream.on('error', done); 86 | stream.on('data', function(file) { 87 | should.exist(file); 88 | should.exist(file.path); 89 | files.push(file); 90 | }); 91 | stream.on('end', function() { 92 | files.length.should.equal(1); 93 | files[0].path.should.equal(expectedPath); 94 | done(); 95 | }); 96 | }); 97 | 98 | it('should return a input stream with no contents when read is false', function(done) { 99 | var stream = gulp.src(join(__dirname, "./fixtures/*.coffee"), {read: false}); 100 | stream.on('error', done); 101 | stream.on('data', function(file) { 102 | should.exist(file); 103 | should.exist(file.path); 104 | should.not.exist(file.contents); 105 | join(file.path,'').should.equal(join(__dirname, "./fixtures/test.coffee")); 106 | }); 107 | stream.on('end', function() { 108 | done(); 109 | }); 110 | }); 111 | it('should return a input stream with contents as stream when buffer is false', function(done) { 112 | var stream = gulp.src(join(__dirname, "./fixtures/*.coffee"), {buffer: false}); 113 | stream.on('error', done); 114 | stream.on('data', function(file) { 115 | should.exist(file); 116 | should.exist(file.path); 117 | should.exist(file.contents); 118 | var buf = ""; 119 | file.contents.on('data', function(d){ 120 | buf += d; 121 | }); 122 | file.contents.on('end', function(){ 123 | buf.should.equal("this is a test"); 124 | done(); 125 | }); 126 | join(file.path,'').should.equal(join(__dirname, "./fixtures/test.coffee")); 127 | }); 128 | }); 129 | it('should return a input stream from a deep glob', function(done) { 130 | var stream = gulp.src(join(__dirname, "./fixtures/**/*.jade")); 131 | stream.on('error', done); 132 | stream.on('data', function(file) { 133 | should.exist(file); 134 | should.exist(file.path); 135 | should.exist(file.contents); 136 | join(file.path,'').should.equal(join(__dirname, "./fixtures/test/run.jade")); 137 | String(file.contents).should.equal("test template"); 138 | }); 139 | stream.on('end', function() { 140 | done(); 141 | }); 142 | }); 143 | it('should return a input stream from a deeper glob', function(done) { 144 | var stream = gulp.src(join(__dirname, "./fixtures/**/*.dmc")); 145 | var a = 0; 146 | stream.on('error', done); 147 | stream.on('data', function() { 148 | ++a; 149 | }); 150 | stream.on('end', function() { 151 | a.should.equal(2); 152 | done(); 153 | }); 154 | }); 155 | 156 | it('should return a file stream from a flat path', function(done) { 157 | var a = 0; 158 | var stream = gulp.src(join(__dirname, "./fixtures/test.coffee")); 159 | stream.on('error', done); 160 | stream.on('data', function(file) { 161 | ++a; 162 | should.exist(file); 163 | should.exist(file.path); 164 | should.exist(file.contents); 165 | join(file.path,'').should.equal(join(__dirname, "./fixtures/test.coffee")); 166 | String(file.contents).should.equal("this is a test"); 167 | }); 168 | stream.on('end', function() { 169 | a.should.equal(1); 170 | done(); 171 | }); 172 | }); 173 | }); 174 | }); 175 | -------------------------------------------------------------------------------- /docs/API.md: -------------------------------------------------------------------------------- 1 | ## gulp API docs 2 | 3 | ### gulp.src(globs[, options]) 4 | 5 | Takes a glob and represents a file structure. Can be piped to plugins. 6 | 7 | ```javascript 8 | gulp.src('./client/templates/*.jade') 9 | .pipe(jade()) 10 | .pipe(minify()) 11 | .pipe(gulp.dest('./build/minified_templates')); 12 | ``` 13 | 14 | #### globs 15 | Type: `String` or `Array` 16 | 17 | Glob or globs to read. 18 | 19 | #### options 20 | Type: `Object` 21 | 22 | Options to pass to [node-glob] through [glob-stream]. 23 | 24 | gulp adds two additional options in addition to the [options supported by node-glob][node-glob documentation]: 25 | 26 | #### options.buffer 27 | Type: `Boolean` 28 | Default: `true` 29 | 30 | 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. 31 | 32 | #### options.read 33 | Type: `Boolean` 34 | Default: `true` 35 | 36 | Setting this to `false` will return `file.contents` as null and not read the file at all. 37 | 38 | 39 | ### gulp.dest(path) 40 | 41 | Can be piped to and it will write files. Re-emits all data passed to it so you can pipe to multiple folders. 42 | 43 | ```javascript 44 | gulp.src('./client/templates/*.jade') 45 | .pipe(jade()) 46 | .pipe(gulp.dest('./build/templates')) 47 | .pipe(minify()) 48 | .pipe(gulp.dest('./build/minified_templates')); 49 | ``` 50 | 51 | #### path 52 | Type: `String` 53 | 54 | The path (folder) to write files to. 55 | 56 | 57 | ### gulp.task(name[, deps], fn) 58 | 59 | Define a task using [Orchestrator]. 60 | 61 | ```javascript 62 | gulp.task('somename', function() { 63 | // Do stuff 64 | }); 65 | ``` 66 | 67 | #### name 68 | 69 | The name of the task. Tasks that you want to run from the command line should not have spaces in them. 70 | 71 | #### deps 72 | Type: `Array` 73 | 74 | An array of tasks to be executed and completed before your task will run. 75 | 76 | ```javascript 77 | gulp.task('mytask', ['array', 'of', 'task', 'names'], function() { 78 | // Do stuff 79 | }); 80 | ``` 81 | 82 | **Note:** Are your tasks running before the dependencies are complete? Make sure your dependency tasks 83 | are correctly using the async run hints: take in a callback or return a promise or event stream. 84 | 85 | #### fn 86 | 87 | The function that performs the task's operations. Generally this takes the form of `gulp.src().pipe(someplugin())`. 88 | 89 | #### Async task support 90 | 91 | Tasks can be made asynchronous if its `fn` does one of the following: 92 | 93 | ##### Accept a callback 94 | 95 | ```javascript 96 | gulp.task('somename', function(cb) { 97 | // Do stuff 98 | cb(err); 99 | }); 100 | ``` 101 | 102 | ##### Return a stream 103 | 104 | ```javascript 105 | gulp.task('somename', function() { 106 | var stream = gulp.src('./client/**/*.js') 107 | .pipe(minify()) 108 | .pipe(gulp.dest('/build')); 109 | return stream; 110 | }); 111 | ``` 112 | 113 | ##### Return a promise 114 | 115 | ```javascript 116 | var Q = require('q'); 117 | 118 | gulp.task('somename', function() { 119 | var deferred = Q.defer(); 120 | 121 | // Do async stuff 122 | setTimeout(function() { 123 | deferred.resolve(); 124 | }, 1); 125 | 126 | return deferred.promise; 127 | }); 128 | ``` 129 | 130 | **Note:** By default, tasks run with maximum concurrency -- e.g. it launches all the tasks at once and waits for nothing. 131 | If you want to create a series where tasks run in a particular order, you need to do two things: 132 | 133 | - give it a hint to tell it when the task is done, 134 | - and give it a hint that a task depends on completion of another. 135 | 136 | For these examples, let's presume you have two tasks, "one" and "two" that you specifically want to run in this order: 137 | 138 | 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 139 | done or return a promise or stream that the engine should wait to resolve or end respectively. 140 | 141 | 2. In task "two" you add a hint telling the engine that it depends on completion of the first task. 142 | 143 | So this example would look like this: 144 | 145 | ```javascript 146 | var gulp = require('gulp'); 147 | 148 | // takes in a callback so the engine knows when it'll be done 149 | gulp.task('one', function (cb) { 150 | // do stuff -- async or otherwise 151 | cb(err); // if err is not null and not undefined, the run will stop, and note that it failed 152 | }); 153 | 154 | // identifies a dependent task must be complete before this one begins 155 | gulp.task('two', ['one'], function () { 156 | // task 'one' is done now 157 | }); 158 | 159 | gulp.task('default', ['one', 'two']); 160 | ``` 161 | 162 | 163 | ### gulp.watch(glob [, opts], tasks) or gulp.watch(glob [, opts, cb]) 164 | 165 | Watch files and do something when a file changes. This always returns an EventEmitter that emits `changed` events. 166 | 167 | ### gulp.watch(glob[, opts], tasks) 168 | 169 | #### glob 170 | Type: `String` or `Array` 171 | 172 | A single glob or array of globs that indicate which files to watch for changes. 173 | 174 | #### opts 175 | Type: `Object` 176 | 177 | Options, that are passed to [`gaze`](https://github.com/shama/gaze). 178 | 179 | #### tasks 180 | Type: `Array` 181 | 182 | Names of task(s) to run when a file changes, added with `gulp.task()` 183 | 184 | ```javascript 185 | var watcher = gulp.watch('js/**/*.js', ['uglify','reload']); 186 | watcher.on('changed', function(event){ 187 | console.log('File '+event.path+' was '+event.type+', running tasks...'); 188 | }); 189 | ``` 190 | 191 | ### gulp.watch(glob[, opts, cb]) 192 | 193 | #### glob 194 | Type: `String` or `Array` 195 | 196 | A single glob or array of globs that indicate which files to watch for changes. 197 | 198 | #### opts 199 | Type: `Object` 200 | 201 | Options, that are passed to [`gaze`](https://github.com/shama/gaze). 202 | 203 | #### cb(event) 204 | Type: `Function` 205 | 206 | Callback to be called on each change. 207 | 208 | ```javascript 209 | gulp.watch('js/**/*.js', function(event) { 210 | console.log('File '+event.path+' was '+event.type+', running tasks...'); 211 | }); 212 | ``` 213 | 214 | The callback will be passed an object, `event`, that describes the change: 215 | 216 | ##### event.type 217 | Type: `String` 218 | 219 | The type of change that occurred, either `added`, `changed` or `deleted`. 220 | 221 | ##### event.path 222 | Type: `String` 223 | 224 | The path to the file that triggered the event. 225 | 226 | 227 | [node-optimist]: https://github.com/substack/node-optimist 228 | [node-glob documentation]: https://github.com/isaacs/node-glob#options 229 | [node-glob]: https://github.com/isaacs/node-glob 230 | [glob-stream]: https://github.com/wearefractal/glob-stream 231 | [gulp-if]: https://github.com/robrich/gulp-if 232 | [Orchestrator]: https://github.com/robrich/orchestrator 233 | -------------------------------------------------------------------------------- /docs/writing-a-plugin/readme-conventions.md: -------------------------------------------------------------------------------- 1 | # README conventions 2 | 3 | > Follow README conventions to increase your plugin readability and usability. 4 | 5 | [Writing a Gulp Plugin](README.md) > README conventions 6 | 7 | A plugin README should have the following parts at a minimum: 8 | 9 | 1. **Heading**: Give a short introduction to your plugin 10 | * Include the version of gulp your plugin was designed for 11 | * Include any badges on the same line as the heading 12 | 1. **Usage**: Include a simple usage example 13 | * Show the plugin used on the context of a `gulpfile.js` 14 | * Cover the most common or simplest use case 15 | 1. **API**: Document your plugin's API 16 | * For individual parameters, include: 17 | 1. Parameter name 18 | 1. Type 19 | 1. Default value 20 | 1. Description 21 | 1. Optional example for complex options 22 | * If a parameter accepts an Object, do the above for each property 23 | * If your plugin accepts overloaded arguments, document each style individually 24 | 25 | 26 | # Example: Options object 27 | 28 | The following is an example of a plugin that takes a single options object (from [gulp-handlebars](https://github.com/lazd/gulp-handlebars)). 29 | 30 | 31 | *** 32 | 33 | # gulp-handlebars [![NPM version][handlebars-npm-image]][handlebars-npm-url] [![Build status][handlebars-travis-image]][handlebars-travis-url] 34 | > Handlebars plugin for gulp 3 35 | 36 | ## Usage 37 | 38 | First, install `gulp-handlebars` as a development dependency: 39 | 40 | ```shell 41 | npm install --save-dev gulp-handlebars 42 | ``` 43 | 44 | Then, add it to your `gulpfile.js`: 45 | 46 | ```js 47 | var handlebars = require('gulp-handlebars'); 48 | 49 | gulp.task('templates', function(){ 50 | gulp.src(['client/templates/*.hbs']) 51 | .pipe(handlebars({ 52 | namespace: 'MyApp.templates', 53 | outputType: 'hybrid' 54 | })) 55 | .pipe(concat('templates.js')) 56 | .pipe(gulp.dest('build/js/')); 57 | }); 58 | ``` 59 | 60 | ## API 61 | 62 | ### handlebars(options) 63 | 64 | #### options.namespace 65 | Type: `String` 66 | Default: `templates` 67 | 68 | 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. 69 | 70 | #### options.outputType 71 | Type: `String` 72 | Default: `browser` 73 | 74 | The desired output type. One of the following: 75 | 76 | * `browser` - Produce plain JavaScript files for the browser 77 | * `hybrid` - Produce Node modules that can optionally be used on the frontend 78 | * `node` - Produce Node modules 79 | * `amd` - Produce AMD modules 80 | * `commonjs` - Produce CommonJS modules 81 | * `bare` - Return an unmolested function definition 82 | 83 | #### options.declareNamespace 84 | Type: `Boolean` 85 | Default: `true` 86 | 87 | If true, non-destructively declare all parts of the namespace. This option is only necessary when `options.type` is `browser` or `hybrid`. 88 | 89 | 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: 90 | 91 | ```js 92 | this["MyApp"] = this["MyApp"] || {}; 93 | this["MyApp"]["templates"] = this["MyApp"]["templates"] || {}; 94 | this["MyApp"]["templates"]["App"] = this["MyApp"]["templates"]["App"] || {}; 95 | this["MyApp"]["templates"]["App"]["Header"] = function () {}; 96 | ``` 97 | 98 | 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. 99 | 100 | #### options.processName 101 | Type: `Function` 102 | Default: Strip file extension 103 | 104 | 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. 105 | 106 | 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. 107 | 108 | #### options.compilerOptions 109 | Type: `Object` 110 | 111 | Compiler options to pass to `Handlebars.precompile()`. 112 | 113 | 114 | [handlebars-travis-url]: http://travis-ci.org/lazd/gulp-handlebars 115 | [handlebars-travis-image]: https://secure.travis-ci.org/lazd/gulp-handlebars.png?branch=master 116 | [handlebars-npm-url]: https://npmjs.org/package/gulp-handlebars 117 | [handlebars-npm-image]: https://badge.fury.io/js/gulp-handlebars.png 118 | *** 119 | 120 | 121 | # Example: Overloaded API 122 | 123 | The following is an example of a plugin that has an overloaded API (from [gulp-csslint](https://github.com/lazd/gulp-csslint)). 124 | 125 | 126 | *** 127 | # gulp-csslint [![NPM version][csslint-npm-image]][csslint-npm-url] [![Build status][csslint-travis-image]][csslint-travis-url] 128 | > CSSLint plugin for gulp 3 129 | 130 | ## Usage 131 | 132 | First, install `gulp-csslint` as a development dependency: 133 | 134 | ```shell 135 | npm install --save-dev gulp-csslint 136 | ``` 137 | 138 | Then, add it to your `gulpfile.js`: 139 | 140 | ```javascript 141 | var csslint = require('gulp-csslint'); 142 | 143 | gulp.task('css', function() { 144 | gulp.src('./client/css/*.css') 145 | .pipe(csslint()) 146 | .pipe(csslint.reporter()); 147 | }); 148 | ``` 149 | 150 | ## API 151 | 152 | ### csslint(ruleConfiguration) 153 | 154 | #### ruleConfiguration 155 | Type: `Object` 156 | 157 | 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. 158 | 159 | ```javascript 160 | gulp.src('./client/css/*.css') 161 | .pipe(csslint({ 162 | 'shorthand': false 163 | })) 164 | .pipe(csslint.reporter()); 165 | ``` 166 | 167 | ### csslint(csslintrc) 168 | 169 | #### csslintrc 170 | Type: `String` 171 | 172 | You can also pass the path to your csslintrc file instead of a rule configuration object. 173 | 174 | ```javascript 175 | gulp.src('./client/css/*.css') 176 | .pipe(csslint('csslintrc.json')) 177 | .pipe(csslint.reporter()); 178 | ``` 179 | 180 | ## Results 181 | 182 | Adds the following properties to the file object: 183 | 184 | ```javascript 185 | file.csslint.success = true; // or false 186 | file.csslint.errorCount = 0; // number of errors returned by CSSLint 187 | file.csslint.results = []; // CSSLint errors 188 | file.csslint.opt = {}; // The options you passed to CSSLint 189 | ``` 190 | 191 | ## Custom Reporters 192 | 193 | 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. 194 | 195 | ```javascript 196 | var csslint = require('gulp-csslint'); 197 | var gutil = require('gulp-util'); 198 | 199 | var customReporter = function(file) { 200 | gutil.log(gutil.colors.cyan(file.csslint.errorCount)+' errors in '+gutil.colors.magenta(file.path)); 201 | 202 | file.csslint.results.forEach(function(result) { 203 | gutil.log(result.error.message+' on line '+result.error.line); 204 | }); 205 | }; 206 | 207 | gulp.task('lint', function() { 208 | gulp.files('./lib/*.js') 209 | .pipe(csslint()) 210 | .pipe(csslint.reporter(customReporter)); 211 | }); 212 | ``` 213 | 214 | [csslint-travis-url]: http://travis-ci.org/lazd/gulp-csslint 215 | [csslint-travis-image]: https://secure.travis-ci.org/lazd/gulp-csslint.png?branch=master 216 | [csslint-npm-url]: https://npmjs.org/package/gulp-csslint 217 | [csslint-npm-image]: https://badge.fury.io/js/gulp-csslint.png 218 | *** 219 | --------------------------------------------------------------------------------