├── .editorconfig ├── .eslintrc ├── .gitattributes ├── .github ├── SECURITY.md └── workflows │ ├── dev.yml │ └── release.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .tidelift.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── EXPENSE_POLICY.md ├── LICENSE ├── README.md ├── bin └── gulp.js ├── docs ├── CLI.md ├── FAQ.md ├── README.md ├── advanced │ └── creating-custom-registries.md ├── api │ ├── README.md │ ├── concepts.md │ ├── dest.md │ ├── last-run.md │ ├── parallel.md │ ├── registry.md │ ├── series.md │ ├── src.md │ ├── symlink.md │ ├── task.md │ ├── tree.md │ ├── vinyl-iscustomprop.md │ ├── vinyl-isvinyl.md │ ├── vinyl.md │ └── watch.md ├── documentation-missing.md ├── getting-started.md ├── getting-started │ ├── 1-quick-start.md │ ├── 2-javascript-and-gulpfiles.md │ ├── 3-creating-tasks.md │ ├── 4-async-completion.md │ ├── 5-working-with-files.md │ ├── 6-explaining-globs.md │ ├── 7-using-plugins.md │ ├── 8-watching-files.md │ └── README.md ├── recipes │ ├── README.md │ ├── automate-releases.md │ ├── browserify-multiple-destination.md │ ├── browserify-transforms.md │ ├── browserify-uglify-sourcemap.md │ ├── browserify-with-globs.md │ ├── combining-streams-to-handle-errors.md │ ├── cron-task.md │ ├── delete-files-folder.md │ ├── fast-browserify-builds-with-watchify.md │ ├── handling-the-delete-event-on-watch.md │ ├── incremental-builds-with-concatenate.md │ ├── maintain-directory-structure-while-globbing.md │ ├── make-stream-from-buffer.md │ ├── minified-and-non-minified.md │ ├── minimal-browsersync-setup-with-gulp4.md │ ├── mocha-test-runner-with-gulp.md │ ├── pass-arguments-from-cli.md │ ├── rollup-with-rollup-stream.md │ ├── run-grunt-tasks-from-gulp.md │ ├── running-task-steps-per-folder.md │ ├── server-with-livereload-and-css-injection.md │ ├── sharing-streams-with-stream-factories.md │ ├── templating-with-swig-and-yaml-front-matter.md │ └── using-multiple-sources-in-one-task.md ├── support │ └── for-enterprise.md ├── why-use-pump │ ├── README.md │ ├── pipe-error.png │ └── pump-error.png └── writing-a-plugin │ ├── README.md │ ├── dealing-with-streams.md │ ├── guidelines.md │ ├── recommended-modules.md │ ├── testing.md │ └── using-buffers.md ├── index.js ├── index.mjs ├── package.json └── test ├── .gitkeep ├── dest.js ├── fixtures ├── copy │ └── example.txt ├── gulpfiles │ ├── cjs │ │ └── gulpfile.cjs │ └── mjs │ │ └── gulpfile.mjs ├── stuff │ ├── run.dmc │ └── test.dmc ├── test.coffee └── test │ └── run.jade ├── index.test.js ├── src.js └── watch.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | end_of_line = lf 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "gulp" 3 | } 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | 3 | # Denote all files that are truly binary and should not be modified. 4 | *.png binary 5 | *.jpg binary 6 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | | Version | Supported | 6 | | ------- | ------------------ | 7 | | 4.x.x | :white_check_mark: | 8 | | < 4.0 | :x: | 9 | 10 | ## Reporting a Vulnerability 11 | 12 | To report a security vulnerability, please use the 13 | [Tidelift security contact](https://tidelift.com/security). 14 | Tidelift will coordinate the fix and disclosure. 15 | -------------------------------------------------------------------------------- /.github/workflows/dev.yml: -------------------------------------------------------------------------------- 1 | name: dev 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - master 7 | - main 8 | env: 9 | CI: true 10 | 11 | jobs: 12 | prettier: 13 | name: Format code 14 | runs-on: ubuntu-latest 15 | if: ${{ github.event_name == 'push' }} 16 | 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v2 20 | 21 | - name: Prettier 22 | uses: gulpjs/prettier_action@v3.0 23 | with: 24 | commit_message: 'chore: Run prettier' 25 | prettier_options: '--write .' 26 | 27 | test: 28 | name: Tests for Node ${{ matrix.node }} on ${{ matrix.os }} 29 | runs-on: ${{ matrix.os }} 30 | 31 | strategy: 32 | fail-fast: false 33 | matrix: 34 | node: [10, 12, 14, 16, 18, 20, 22, 24] 35 | os: [ubuntu-latest, windows-latest, macos-13] 36 | 37 | steps: 38 | - name: Clone repository 39 | uses: actions/checkout@v2 40 | 41 | - name: Set Node.js version 42 | uses: actions/setup-node@v2 43 | with: 44 | node-version: ${{ matrix.node }} 45 | 46 | - run: node --version 47 | - run: npm --version 48 | 49 | - name: Install npm dependencies 50 | run: npm install 51 | 52 | - name: Run lint 53 | run: npm run lint 54 | 55 | - name: Run tests 56 | run: npm test 57 | 58 | - name: Coveralls 59 | uses: coverallsapp/github-action@v1.1.2 60 | with: 61 | github-token: ${{ secrets.GITHUB_TOKEN }} 62 | flag-name: ${{matrix.os}}-node-${{ matrix.node }} 63 | parallel: true 64 | 65 | coveralls: 66 | needs: test 67 | name: Finish up 68 | 69 | runs-on: ubuntu-latest 70 | steps: 71 | - name: Coveralls Finished 72 | uses: coverallsapp/github-action@v1.1.2 73 | with: 74 | github-token: ${{ secrets.GITHUB_TOKEN }} 75 | parallel-finished: true 76 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - main 7 | 8 | jobs: 9 | release-please: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: GoogleCloudPlatform/release-please-action@v2 13 | with: 14 | token: ${{ secrets.GITHUB_TOKEN }} 15 | release-type: node 16 | package-name: release-please-action 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | .nyc_output 16 | 17 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # Compiled binary addons (https://nodejs.org/api/addons.html) 21 | build/Release 22 | 23 | # Dependency directory 24 | # Commenting this out is preferred by some people, see 25 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 26 | node_modules 27 | 28 | # Users Environment Variables 29 | .lock-wscript 30 | 31 | # Garbage files 32 | .DS_Store 33 | 34 | # Test results 35 | test.xunit 36 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | .nyc_output/ 3 | CHANGELOG.md 4 | -------------------------------------------------------------------------------- /.tidelift.yml: -------------------------------------------------------------------------------- 1 | ci: 2 | platform: 3 | NPM: 4 | # We use an older version that doesn't use ES6+ features to support back to node 0.10 5 | eslint: 6 | tests: 7 | outdated: skip 8 | # We use an older version that doesn't use ES6+ features to support back to node 0.10 9 | expect: 10 | tests: 11 | outdated: skip 12 | # We use an older version that doesn't use ES6+ features to support back to node 0.10 13 | mocha: 14 | tests: 15 | outdated: skip 16 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Request for contributions 2 | 3 | Please contribute to this repository if any of the following is true: 4 | - You have expertise in community development, communication, or education 5 | - You want open source communities to be more collaborative and inclusive 6 | - You want to help lower the burden to first time contributors 7 | 8 | # How to contribute 9 | 10 | Prerequisites: 11 | 12 | - familiarity with [GitHub PRs](https://help.github.com/articles/using-pull-requests) (pull requests) and issues 13 | - knowledge of Markdown for editing `.md` documents 14 | 15 | In particular, this community seeks the following types of contributions: 16 | 17 | - ideas: participate in an Issues thread or start your own to have your voice 18 | heard 19 | - resources: submit a PR to add to [docs README.md](/docs/README.md) with links to related content 20 | - outline sections: help us ensure that this repository is comprehensive. If 21 | there is a topic that is overlooked, please add it, even if it is just a stub 22 | in the form of a header and single sentence. Initially, most things fall into 23 | this category 24 | - write: contribute your expertise in an area by helping us expand the included 25 | content 26 | - copy editing: fix typos, clarify language, and generally improve the quality 27 | of the content 28 | - formatting: help keep content easy to read with consistent formatting 29 | - code: Fix issues or contribute new features to this or any related projects 30 | 31 | # Project structure 32 | 33 | Gulp itself is tiny: index.js contains [very few lines of code](https://github.com/gulpjs/gulp/blob/master/index.js). 34 | It is powered by a few other libraries which each handle a few specific tasks 35 | each. 36 | 37 | You can view all issues with the "help wanted" label across all gulp projects 38 | here: https://github.com/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+user%3Agulpjs+label%3A%22help+wanted%22+ 39 | 40 | ## Undertaker: task management 41 | 42 | Undertaker handles task management in Gulp: the `gulp.task()`, `gulp.series()` 43 | and `gulp.parallel()` functions. `gulp.series()` and `gulp.parallel()` are in 44 | turn powered by Bach. 45 | 46 | - https://github.com/gulpjs/undertaker 47 | - https://github.com/gulpjs/bach 48 | 49 | ## vinyl-fs: file streams 50 | 51 | vinyl-fs powers the `gulp.src()` and `gulp.dest()` functions: they take files 52 | and globs specified by the user, turns them into a stream of file objects, 53 | and then puts them back into the filesystem when `gulp.dest()` is called. 54 | 55 | The file objects themselves are vinyl objects: that's another library (a simple 56 | one!) 57 | 58 | - https://github.com/gulpjs/vinyl-fs 59 | - https://github.com/gulpjs/vinyl 60 | 61 | ## chokidar: file watching 62 | 63 | `gulp.watch()` is using chokidar for file watching. It's actually wrapped in a 64 | small library on the gulp organization, glob-watcher. 65 | 66 | - https://github.com/paulmillr/chokidar 67 | - https://github.com/gulpjs/glob-watcher 68 | 69 | ## gulp-cli: running gulp 70 | 71 | Finally, we have gulp-cli. This uses liftoff to take what people run in the 72 | command line and run the correct tasks. It works with both gulp 4 and older 73 | versions of gulp. 74 | 75 | - https://github.com/gulpjs/gulp-cli 76 | - https://github.com/js-cli/js-liftoff 77 | 78 | # Conduct 79 | 80 | We are committed to providing a friendly, safe and welcoming environment for 81 | all, regardless of gender, sexual orientation, disability, ethnicity, religion, 82 | or similar personal characteristic. 83 | 84 | On IRC, please avoid using overtly sexual nicknames or other nicknames that 85 | might detract from a friendly, safe and welcoming environment for all. 86 | 87 | Please be kind and courteous. There's no need to be mean or rude. 88 | Respect that people have differences of opinion and that every design or 89 | implementation choice carries a trade-off and numerous costs. There is seldom 90 | a right answer, merely an optimal answer given a set of values and 91 | circumstances. 92 | 93 | Please keep unstructured critique to a minimum. If you have solid ideas you 94 | want to experiment with, make a fork and see how it works. 95 | 96 | We will exclude you from interaction if you insult, demean or harass anyone. 97 | That is not welcome behavior. We interpret the term "harassment" as 98 | including the definition in the 99 | [Citizen Code of Conduct](http://citizencodeofconduct.org/); 100 | if you have any lack of clarity about what might be included in that concept, 101 | please read their definition. In particular, we don't tolerate behavior that 102 | excludes people in socially marginalized groups. 103 | 104 | Private harassment is also unacceptable. No matter who you are, if you feel 105 | you have been or are being harassed or made uncomfortable by a community 106 | member, please contact one of the channel ops or any of the 107 | [gulpjs](https://github.com/orgs/gulpjs/people) core team 108 | immediately. Whether you're a regular contributor or a newcomer, we care about 109 | making this community a safe place for you and we've got your back. 110 | 111 | Likewise any spamming, trolling, flaming, baiting or other attention-stealing 112 | behavior is not welcome. 113 | 114 | 115 | # Communication 116 | 117 | There is an IRC channel on irc.freenode.net, channel `#gulpjs`. You're 118 | welcome to drop in and ask questions, discuss bugs and such. The channel is 119 | not currently logged. 120 | 121 | GitHub issues are the primary way for communicating about specific proposed 122 | changes to this project. 123 | 124 | In both contexts, please follow the conduct guidelines above. Language issues 125 | are often contentious and we'd like to keep discussion brief, civil and focused 126 | on what we're actually doing, not wandering off into too much imaginary stuff. 127 | 128 | # Frequently Asked Questions 129 | 130 | See [the FAQ docs page](/docs/FAQ.md) 131 | -------------------------------------------------------------------------------- /EXPENSE_POLICY.md: -------------------------------------------------------------------------------- 1 | # Expense Policy 2 | 3 | ## Funding can be requested for significant changes made by Core Members. 4 | * Discuss the changes in the private gulp team forum. 5 | * Include a cost estimation with either a fixed price or hours + rate (suggested $50 per hour). 6 | * Notify the team before you exceed an estimate. 7 | 8 | ## Bug bounties may be assigned at the Core Members’ discretion to issues of significant importance - usually issues outstanding for at least 6 months. 9 | * Issues with bug bounties will be labeled “Bug Bounty: $x”. 10 | * In order to claim a bug bounty, create a Pull Request that fixes an issue with a “Bug Bounty” label. 11 | * The Pull Request must be reviewed and merged by a Core Member. If competing submissions exist, the best solution will be chosen by a Core Member. All else equal, the first submission will be chosen. 12 | * Once your Pull Request is merged, you can submit an expense to our [Open Collective](https://opencollective.com/gulpjs/expenses/new) which includes the link to your submission in the description (e.g. $100 bug bounty claim for https://github.com/gulpjs/gulp/pull/2226). You will also need to provide an invoice, see the [Open Collective Expense FAQ](https://opencollective.com/faq/expenses) for more details and to get a Google Docs template that you can use. 13 | * Then, add a comment on your Pull Request, noting that you’ve claimed the money, with a link to your Open Collective expense. This is to ensure the same person who fixed the issue is claiming the money. 14 | * Your expense will be validated by a Core Member and then your payment will be dispersed by Open Collective the following Friday. 15 | 16 | ## If you're doing other good things for gulp that end up costing you real money, feel free to reach out and we can discuss helping with those expenses! 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2024 Blaine Bublitz and Eric Schoffstall 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

The streaming build system

6 |

7 | 8 | [![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Build Status][ci-image]][ci-url] [![Coveralls Status][coveralls-image]][coveralls-url] 9 | 10 | ## What is gulp? 11 | 12 | - **Automation** - gulp is a toolkit that helps you automate painful or time-consuming tasks in your development workflow. 13 | - **Platform-agnostic** - Integrations are built into all major IDEs and people are using gulp with PHP, .NET, Node.js, Java, and other platforms. 14 | - **Strong Ecosystem** - Use npm modules to do anything you want + over 3000 curated plugins for streaming file transformations. 15 | - **Simple** - By providing only a minimal API surface, gulp is easy to learn and simple to use. 16 | 17 | ## Installation 18 | 19 | Follow our [Quick Start guide][quick-start]. 20 | 21 | ## Roadmap 22 | 23 | Find out about all our work-in-progress and outstanding issues at https://github.com/orgs/gulpjs/projects. 24 | 25 | ## Documentation 26 | 27 | Check out the [Getting Started guide][getting-started-guide] and [API docs][api-docs] on our website! 28 | 29 | __Excuse our dust! All other docs will be behind until we get everything updated. Please open an issue if something isn't working.__ 30 | 31 | ## Sample `gulpfile.js` 32 | 33 | This file will give you a taste of what gulp does. 34 | 35 | ```js 36 | var gulp = require('gulp'); 37 | var less = require('gulp-less'); 38 | var babel = require('gulp-babel'); 39 | var concat = require('gulp-concat'); 40 | var uglify = require('gulp-uglify'); 41 | var rename = require('gulp-rename'); 42 | var cleanCSS = require('gulp-clean-css'); 43 | var del = require('del'); 44 | 45 | var paths = { 46 | styles: { 47 | src: 'src/styles/**/*.less', 48 | dest: 'assets/styles/' 49 | }, 50 | scripts: { 51 | src: 'src/scripts/**/*.js', 52 | dest: 'assets/scripts/' 53 | } 54 | }; 55 | 56 | /* Not all tasks need to use streams, a gulpfile is just another node program 57 | * and you can use all packages available on npm, but it must return either a 58 | * Promise, a Stream or take a callback and call it 59 | */ 60 | function clean() { 61 | // You can use multiple globbing patterns as you would with `gulp.src`, 62 | // for example if you are using del 2.0 or above, return its promise 63 | return del([ 'assets' ]); 64 | } 65 | 66 | /* 67 | * Define our tasks using plain functions 68 | */ 69 | function styles() { 70 | return gulp.src(paths.styles.src) 71 | .pipe(less()) 72 | .pipe(cleanCSS()) 73 | // pass in options to the stream 74 | .pipe(rename({ 75 | basename: 'main', 76 | suffix: '.min' 77 | })) 78 | .pipe(gulp.dest(paths.styles.dest)); 79 | } 80 | 81 | function scripts() { 82 | return gulp.src(paths.scripts.src, { sourcemaps: true }) 83 | .pipe(babel()) 84 | .pipe(uglify()) 85 | .pipe(concat('main.min.js')) 86 | .pipe(gulp.dest(paths.scripts.dest)); 87 | } 88 | 89 | function watch() { 90 | gulp.watch(paths.scripts.src, scripts); 91 | gulp.watch(paths.styles.src, styles); 92 | } 93 | 94 | /* 95 | * Specify if tasks run in series or parallel using `gulp.series` and `gulp.parallel` 96 | */ 97 | var build = gulp.series(clean, gulp.parallel(styles, scripts)); 98 | 99 | /* 100 | * You can use CommonJS `exports` module notation to declare tasks 101 | */ 102 | exports.clean = clean; 103 | exports.styles = styles; 104 | exports.scripts = scripts; 105 | exports.watch = watch; 106 | exports.build = build; 107 | /* 108 | * Define default task that can be called by just running `gulp` from cli 109 | */ 110 | exports.default = build; 111 | ``` 112 | 113 | ## Use latest JavaScript version in your gulpfile 114 | 115 | Gulp provides a wrapper that will be loaded in your ESM code, so you can name your gulpfile as `gulpfile.mjs` or with `"type": "module"` specified in your `package.json` file. 116 | 117 | And here's the same sample from above written in **ESNext**. 118 | 119 | ```js 120 | import { src, dest, watch } from 'gulp'; 121 | import less from 'gulp-less'; 122 | import babel from 'gulp-babel'; 123 | import concat from 'gulp-concat'; 124 | import uglify from 'gulp-uglify'; 125 | import rename from 'gulp-rename'; 126 | import cleanCSS from 'gulp-clean-css'; 127 | import del from 'del'; 128 | 129 | const paths = { 130 | styles: { 131 | src: 'src/styles/**/*.less', 132 | dest: 'assets/styles/' 133 | }, 134 | scripts: { 135 | src: 'src/scripts/**/*.js', 136 | dest: 'assets/scripts/' 137 | } 138 | }; 139 | 140 | /* 141 | * For small tasks you can export arrow functions 142 | */ 143 | export const clean = () => del([ 'assets' ]); 144 | 145 | /* 146 | * You can also declare named functions and export them as tasks 147 | */ 148 | export function styles() { 149 | return src(paths.styles.src) 150 | .pipe(less()) 151 | .pipe(cleanCSS()) 152 | // pass in options to the stream 153 | .pipe(rename({ 154 | basename: 'main', 155 | suffix: '.min' 156 | })) 157 | .pipe(dest(paths.styles.dest)); 158 | } 159 | 160 | export function scripts() { 161 | return src(paths.scripts.src, { sourcemaps: true }) 162 | .pipe(babel()) 163 | .pipe(uglify()) 164 | .pipe(concat('main.min.js')) 165 | .pipe(dest(paths.scripts.dest)); 166 | } 167 | 168 | /* 169 | * You could even use `export as` to rename exported tasks 170 | */ 171 | function watchFiles() { 172 | watch(paths.scripts.src, scripts); 173 | watch(paths.styles.src, styles); 174 | } 175 | export { watchFiles as watch }; 176 | 177 | const build = gulp.series(clean, gulp.parallel(styles, scripts)); 178 | /* 179 | * Export a default task 180 | */ 181 | export default build; 182 | ``` 183 | 184 | ## Incremental Builds 185 | 186 | You can filter out unchanged files between runs of a task using 187 | the `gulp.src` function's `since` option and `gulp.lastRun`: 188 | ```js 189 | const paths = { 190 | ... 191 | images: { 192 | src: 'src/images/**/*.{jpg,jpeg,png}', 193 | dest: 'build/img/' 194 | } 195 | } 196 | 197 | function images() { 198 | return gulp.src(paths.images.src, {since: gulp.lastRun(images)}) 199 | .pipe(imagemin()) 200 | .pipe(gulp.dest(paths.images.dest)); 201 | } 202 | 203 | function watch() { 204 | gulp.watch(paths.images.src, images); 205 | } 206 | ``` 207 | Task run times are saved in memory and are lost when gulp exits. It will only 208 | save time during the `watch` task when running the `images` task 209 | for a second time. 210 | 211 | ## Want to contribute? 212 | 213 | Anyone can help make this project better - check out our [Contributing guide](/CONTRIBUTING.md)! 214 | 215 | 216 | [quick-start]: https://gulpjs.com/docs/en/getting-started/quick-start 217 | [getting-started-guide]: https://gulpjs.com/docs/en/getting-started/quick-start 218 | [api-docs]: https://gulpjs.com/docs/en/api/concepts 219 | [esm-module]: https://github.com/standard-things/esm 220 | 221 | 222 | 223 | [downloads-image]: https://img.shields.io/npm/dm/gulp.svg?style=flat-square 224 | [npm-url]: https://www.npmjs.com/package/gulp 225 | [npm-image]: https://img.shields.io/npm/v/gulp.svg?style=flat-square 226 | 227 | [ci-url]: https://github.com/gulpjs/gulp/actions?query=workflow:dev 228 | [ci-image]: https://img.shields.io/github/actions/workflow/status/gulpjs/gulp/dev.yml?branch=master&style=flat-square 229 | 230 | [coveralls-url]: https://coveralls.io/r/gulpjs/gulp 231 | [coveralls-image]: https://img.shields.io/coveralls/gulpjs/gulp/master.svg?style=flat-square 232 | 233 | -------------------------------------------------------------------------------- /bin/gulp.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('gulp-cli')(); 4 | -------------------------------------------------------------------------------- /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 ` will manually set path of gulpfile. Useful if you have multiple gulpfiles. This will set the CWD to the gulpfile directory as well 10 | - `--cwd ` will 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. It will include the task names and their [description](./API.md#fndescription). 12 | - `--tasks-simple` will display a plaintext list of tasks for the loaded gulpfile 13 | - `--verify` will verify plugins referenced in project's package.json against the plugins blacklist 14 | - `--color` will force gulp and gulp plugins to display colors even when no color support is detected 15 | - `--no-color` will force gulp and gulp plugins to not display colors even when color support is detected 16 | - `--silent` will disable all gulp logging 17 | 18 | The CLI adds process.env.INIT_CWD which is the original cwd it was launched from. 19 | 20 | #### Task specific flags 21 | 22 | Refer to this [StackOverflow](https://stackoverflow.com/questions/23023650/is-it-possible-to-pass-a-flag-to-gulp-to-have-it-run-tasks-in-different-ways) link for how to add task specific flags 23 | 24 | ### Tasks 25 | 26 | Tasks can be executed by running `gulp ...`. 27 | 28 | If more than one task is listed, Gulp will execute all of them 29 | concurrently, that is, as if they had all been listed as dependencies of 30 | a single task. 31 | 32 | Gulp does not serialize tasks listed on the command line. From using 33 | other comparable tools users may expect to execute something like 34 | `gulp clean build`, with tasks named `clean` and `build`. This will not 35 | produce the intended result, as the two tasks will be executed 36 | concurrently. 37 | 38 | Just running `gulp` will execute the task `default`. If there is no 39 | `default` task, gulp will error. 40 | 41 | ### Compilers 42 | 43 | You can find a list of supported languages at [interpret](https://github.com/tkellen/node-interpret#jsvariants). If you would like to add support for a new language send pull request/open issues there. 44 | 45 | ### Examples 46 | 47 | #### Example gulpfile 48 | 49 | ```js 50 | gulp.task('one', function(done) { 51 | // do stuff 52 | done(); 53 | }); 54 | 55 | gulp.task('two', function(done) { 56 | // do stuff 57 | done(); 58 | }); 59 | 60 | gulp.task('three', three); 61 | 62 | function three(done) { 63 | done(); 64 | } 65 | three.description = "This is the description of task three"; 66 | 67 | gulp.task('four', gulp.series('one', 'two')); 68 | 69 | gulp.task('five', 70 | gulp.series('four', 71 | gulp.parallel('three', function(done) { 72 | // do more stuff 73 | done(); 74 | }) 75 | ) 76 | ); 77 | ``` 78 | 79 | ### `-T` or `--tasks` 80 | 81 | Command: `gulp -T` or `gulp --tasks` 82 | 83 | Output: 84 | ```shell 85 | [20:58:55] Tasks for ~\exampleProject\gulpfile.js 86 | [20:58:55] ├── one 87 | [20:58:55] ├── two 88 | [20:58:55] ├── three This is the description of task three 89 | [20:58:55] ├─┬ four 90 | [20:58:55] │ └─┬ 91 | [20:58:55] │ ├── one 92 | [20:58:55] │ └── two 93 | [20:58:55] ├─┬ five 94 | [20:58:55] │ └─┬ 95 | [20:58:55] │ ├─┬ four 96 | [20:58:55] │ │ └─┬ 97 | [20:58:55] │ │ ├── one 98 | [20:58:55] │ │ └── two 99 | [20:58:55] │ └─┬ 100 | [20:58:55] │ ├── three 101 | [20:58:55] │ └── 102 | ``` 103 | 104 | ### `--tasks-simple` 105 | 106 | Command: `gulp --tasks-simple` 107 | 108 | Output: 109 | ```shell 110 | one 111 | two 112 | three 113 | four 114 | five 115 | ``` 116 | -------------------------------------------------------------------------------- /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. The only exception is in the gulp logo where gulp is capitalized. 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 `\n` to prevent diff issues between operating systems. 32 | 33 | ## I installed gulp as a dependency from package.json file by running `npm install` but I keep getting `command not found` whenever I try running a gulp command, why doesn't it work? 34 | 35 | Upon installing gulp as a project dependency, you need to add that to your PATH environment variable so that when you run a command, the system can find it. An easy solution is to install gulp globally, so that its binaries end up in your PATH environment variable. To install gulp globally, use the command `npm install gulp-cli -g` 36 | 37 | ## Where can I get updates on gulp? 38 | 39 | gulp updates can be found on the following twitters: 40 | 41 | - [@wearefractal](https://twitter.com/wearefractal) 42 | - [@eschoff](https://twitter.com/eschoff) 43 | - [@gulpjs](https://twitter.com/gulpjs) 44 | 45 | ## Does gulp have an chat channel? 46 | 47 | Yes, come chat with us on [Gitter](https://gitter.im/gulpjs/gulp). 48 | 49 | [Writing a gulp plugin]: writing-a-plugin/README.md 50 | [gulp introduction slideshow]: https://slid.es/contra/gulp 51 | [Freenode]: https://freenode.net/ 52 | [search-gulp-plugins]: https://gulpjs.com/plugins/ 53 | [npm plugin search]: https://npmjs.org/browse/keyword/gulpplugin 54 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # gulp documentation 2 | 3 | * [Getting Started](getting-started/) - Get started with gulp 4 | * [API documentation](api/) - The programming interface, defined 5 | * [CLI documentation](CLI.md) - Learn how to call tasks and use compilers 6 | * [Writing a Plugin](writing-a-plugin/) - The essentials of writing a gulp plugin 7 | * [Why Use Pump?](why-use-pump/README.md) - Why to use the `pump` module instead of calling `.pipe` yourself 8 | * [Simplified Chinese documentation][SimplifiedChineseDocs] - gulp 简体中文文档 9 | * [Korean documentation][KoreanDocs] - gulp 한국어 참조 문서 10 | * [Polish documentation](/docs/locale/pl_PL/README.md) - gulp Dokumentacja 11 | 12 | 13 | ## FAQ 14 | 15 | See the [FAQ](FAQ.md) for the answers to commonly asked questions. 16 | 17 | 18 | ## Recipes 19 | 20 | The community has written [recipes](recipes#recipes) for common gulp use-cases. 21 | 22 | 23 | ## Still got questions? 24 | 25 | Post on [StackOverflow with a #gulp tag](https://stackoverflow.com/questions/tagged/gulp) or come chat with us in [#gulpjs](https://webchat.freenode.net/?channels=gulpjs) on [Freenode](https://freenode.net/). 26 | 27 | ## Videos 28 | * [Intro to Gulp 4](https://youtu.be/N42LQ2dLoA8) presented by @addyosmani and @gauntface 29 | 30 | ## Books 31 | * [Developing a gulp Edge](http://shop.oreilly.com/product/9781939902146.do) 32 | * [Getting Started with Gulp – Second Edition](https://www.packtpub.com/application-development/getting-started-gulp-%E2%80%93-second-edition) - Travis Maynard, Packt (April 2017) 33 | 34 | 35 | ## Articles 36 | * [Tagtree intro to gulp video](http://tagtree.io/gulp) 37 | * [Introduction to node.js streams](https://github.com/substack/stream-handbook) 38 | * [Video introduction to node.js streams](https://www.youtube.com/watch?v=QgEuZ52OZtU) 39 | * [Getting started with gulp (by @markgdyr)](https://markgoodyear.com/2014/01/getting-started-with-gulp/) 40 | * [A cheatsheet for gulp](https://github.com/osscafe/gulp-cheatsheet) 41 | * [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) 42 | * [Inspiration (slides) about why gulp was made](http://slid.es/contra/gulp) 43 | * [Building With Gulp](http://www.smashingmagazine.com/2014/06/11/building-with-gulp/) 44 | * [Gulp - The Basics (screencast)](https://www.youtube.com/watch?v=dwSLFai8ovQ) 45 | * [Get started with gulp (video series)](https://www.youtube.com/playlist?list=PLRk95HPmOM6PN-G1xyKj9q6ap_dc9Yckm) 46 | * [Optimize your web code with gulp](http://www.linuxuser.co.uk/tutorials/optimise-your-web-code-with-gulp-js) 47 | * [Automate Your Tasks Easily with Gulp.js ](https://scotch.io/tutorials/automate-your-tasks-easily-with-gulp-js) 48 | * [How to upgrade to Gulp v4](https://www.liquidlight.co.uk/blog/article/how-do-i-update-to-gulp-4/) 49 | 50 | ## Examples 51 | 52 | - [Web Starter Kit gulpfile](https://github.com/google/web-starter-kit/blob/master/gulpfile.babel.js) 53 | 54 | 55 | ## License 56 | 57 | All the documentation is covered by the CC0 license *(do whatever you want with it - public domain)*. 58 | 59 | [![CC0](https://i.creativecommons.org/p/zero/1.0/88x31.png)](https://creativecommons.org/publicdomain/zero/1.0/) 60 | 61 | To the extent possible under law, [Fractal](http://wearefractal.com) has waived all copyright and related or neighboring rights to this work. 62 | 63 | [SpanishDocs]: https://github.com/bucaran/gulp-docs-es 64 | [SimplifiedChineseDocs]: https://github.com/lisposter/gulp-docs-zh-cn 65 | [KoreanDocs]: https://github.com/preco21/gulp-docs-ko 66 | -------------------------------------------------------------------------------- /docs/advanced/creating-custom-registries.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # Creating Custom Registries 9 | 10 | Allows custom registries to be plugged into the task system, which can provide shared tasks or augmented functionality. Registries are registered using [`registry()`][registry-api-docs]. 11 | 12 | ## Structure 13 | 14 | In order to be accepted by gulp, custom registries must follow a specific format. 15 | 16 | ```js 17 | // as a function 18 | function TestRegistry() {} 19 | 20 | TestRegistry.prototype.init = function (gulpInst) {} 21 | TestRegistry.prototype.get = function (name) {} 22 | TestRegistry.prototype.set = function (name, fn) {} 23 | TestRegistry.prototype.tasks = function () {} 24 | 25 | // as a class 26 | class TestRegistry { 27 | init(gulpInst) {} 28 | 29 | get(name) {} 30 | 31 | set(name, fn) {} 32 | 33 | tasks() {} 34 | } 35 | ``` 36 | 37 | If a registry instance passed to `registry()` doesn't have all four methods, an error will be thrown. 38 | 39 | ## Registration 40 | 41 | If we want to register our example registry from above, we will need to pass an instance of it to `registry()`. 42 | 43 | ```js 44 | const { registry } = require('gulp'); 45 | 46 | // ... TestRegistry setup code 47 | 48 | // good! 49 | registry(new TestRegistry()) 50 | 51 | // bad! 52 | registry(TestRegistry()) 53 | // This will trigger an error: 'Custom registries must be instantiated, but it looks like you passed a constructor' 54 | ``` 55 | 56 | ## Methods 57 | 58 | ### `init(gulpInst)` 59 | 60 | The `init()` method of a registry is called at the very end of the `registry()` function. The gulp instance passed as the only argument (`gulpInst`) can be used to pre-define tasks using 61 | `gulpInst.task(taskName, fn)`. 62 | 63 | #### Parameters 64 | 65 | | parameter | type | note | 66 | |:---------:|:----:|------| 67 | | gulpInst | object | Instance of gulp. | 68 | 69 | ### `get(name)` 70 | 71 | The `get()` method receives a task `name` for the custom registry to resolve and return, or `undefined` if no task with that name exists. 72 | 73 | #### Parameters 74 | 75 | | parameter | type | note | 76 | |:---------:|:----:|------| 77 | | name | string | Name of the task to be retrieved. | 78 | 79 | ### `set(name, fn)` 80 | 81 | The `set()` method receives a task `name` and `fn`. This is called internally by `task()` to provide user-registered tasks to custom registries. 82 | 83 | #### Parameters 84 | 85 | | parameter | type | note | 86 | |:---------:|:----:|------| 87 | | name | string | Name of the task to be set. | 88 | | fn | function | Task function to be set. | 89 | 90 | ### `tasks()` 91 | 92 | Must return an object listing all tasks in the registry. 93 | 94 | ## Use Cases 95 | 96 | ### Sharing Tasks 97 | 98 | To share common tasks with all your projects, you can expose an `init` method on the registry and it will receive an instance of gulp as the only argument. You can then use `gulpInst.task(name, fn)` to register pre-defined tasks. 99 | 100 | For example, you might want to share a `clean` task: 101 | 102 | ```js 103 | const fs = require('fs'); 104 | const util = require('util'); 105 | 106 | const DefaultRegistry = require('undertaker-registry'); 107 | const del = require('del'); 108 | 109 | function CommonRegistry(opts){ 110 | DefaultRegistry.call(this); 111 | 112 | opts = opts || {}; 113 | 114 | this.buildDir = opts.buildDir || './build'; 115 | } 116 | 117 | util.inherits(CommonRegistry, DefaultRegistry); 118 | 119 | CommonRegistry.prototype.init = function(gulpInst) { 120 | const buildDir = this.buildDir; 121 | const exists = fs.existsSync(buildDir); 122 | 123 | if(exists){ 124 | throw new Error('Cannot initialize common tasks. ' + buildDir + ' directory exists.'); 125 | } 126 | 127 | gulpInst.task('clean', function(){ 128 | return del([buildDir]); 129 | }); 130 | } 131 | 132 | module.exports = CommonRegistry; 133 | ``` 134 | 135 | Then to use it in a project: 136 | 137 | ```js 138 | const { registry, series, task } = require('gulp'); 139 | const CommonRegistry = require('myorg-common-tasks'); 140 | 141 | registry(new CommonRegistry({ buildDir: '/dist' })); 142 | 143 | task('build', series('clean', function build(cb) { 144 | // do things 145 | cb(); 146 | })); 147 | ``` 148 | 149 | ### Sharing Functionality 150 | 151 | By controlling how tasks are added to the registry, you can decorate them. 152 | 153 | For example, if you wanted all tasks to share some data, you can use a custom registry to bind them to that data. Be sure to return the altered task, as per the description of registry methods above: 154 | 155 | ```js 156 | const { registry, series, task } = require('gulp'); 157 | const util = require('util'); 158 | const DefaultRegistry = require('undertaker-registry'); 159 | 160 | // Some task defined somewhere else 161 | const BuildRegistry = require('./build.js'); 162 | const ServeRegistry = require('./serve.js'); 163 | 164 | function ConfigRegistry(config){ 165 | DefaultRegistry.call(this); 166 | this.config = config; 167 | } 168 | 169 | util.inherits(ConfigRegistry, DefaultRegistry); 170 | 171 | ConfigRegistry.prototype.set = function set(name, fn) { 172 | var bound = fn.bind(this.config); 173 | // Preserve internal properties and task metadata. 174 | var task = Object.assign(bound, fn); 175 | // The `DefaultRegistry` uses `this._tasks` for storage. 176 | this._tasks[name] = task; 177 | return task; 178 | }; 179 | 180 | registry(new BuildRegistry()); 181 | registry(new ServeRegistry()); 182 | 183 | // `registry` will reset each task in the registry with 184 | // `ConfigRegistry.prototype.set` which will bind them to the config object. 185 | registry(new ConfigRegistry({ 186 | src: './src', 187 | build: './build', 188 | bindTo: '0.0.0.0:8888' 189 | })); 190 | 191 | task('default', series('clean', 'build', 'serve', function(cb) { 192 | console.log('Server bind to ' + this.bindTo); 193 | console.log('Serving' + this.build); 194 | cb(); 195 | })); 196 | ``` 197 | 198 | ## Examples 199 | 200 | * [undertaker-registry][undertaker-registry-example]: The Gulp 4 default registry. 201 | * [undertaker-common-tasks][undertaker-common-tasks-example]: Proof-of-concept custom registry that pre-defines tasks. 202 | * [undertaker-task-metadata][undertaker-task-metadata-example]: Proof-of-concept custom registry that attaches metadata to each task. 203 | 204 | [registry-api-docs]: ../api/registry.md 205 | [undertaker-registry-example]: https://github.com/gulpjs/undertaker-registry 206 | [undertaker-common-tasks-example]: https://github.com/gulpjs/undertaker-common-tasks 207 | [undertaker-task-metadata-example]: https://github.com/gulpjs/undertaker-task-metadata 208 | -------------------------------------------------------------------------------- /docs/api/README.md: -------------------------------------------------------------------------------- 1 | ## Table of Contents 2 | 3 | * [API Concepts](concepts.md) 4 | * [src()](src.md) 5 | * [dest()](dest.md) 6 | * [symlink()](symlink.md) 7 | * [lastRun()](last-run.md) 8 | * [series()](series.md) 9 | * [parallel()](parallel.md) 10 | * [watch()](watch.md) 11 | * [task()](task.md) 12 | * [registry()](registry.md) 13 | * [tree()](tree.md) 14 | * [Vinyl](vinyl.md) 15 | * [Vinyl.isVinyl()](vinyl-isvinyl.md) 16 | * [Vinyl.isCustomProp()](vinyl-iscustomprop.md) 17 | -------------------------------------------------------------------------------- /docs/api/concepts.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # Concepts 9 | 10 | The following concepts are prerequisites to understanding the API docs. They will be referenced throughout, refer back to this page for detailed explanations. 11 | 12 | If you're new here, begin with the [Getting Started Guide][quick-start-docs]. 13 | 14 | ## Vinyl 15 | 16 | Vinyl is a metadata object that describes a file. The main properties of a Vinyl instance are `path` and `contents` - core aspects of a file on your file system. Vinyl objects can be used to describe files from many sources - on a local file system or any remote storage option. 17 | 18 | ## Vinyl adapters 19 | 20 | While Vinyl provides a way to describe a file, a way to access these files is needed. Each file source is accessed using a Vinyl adapter. 21 | 22 | An adapter exposes: 23 | * A method with the signature `src(globs, [options])` and returns a stream that produces Vinyl objects. 24 | * A method with the signature `dest(folder, [options])` and returns a stream that consumes Vinyl objects. 25 | * Any extra methods specific to their input/output medium - such as the `symlink` method `vinyl-fs` provides. They should always return streams that produce and/or consume Vinyl objects. 26 | 27 | ## Tasks 28 | 29 | Each gulp task is an asynchronous JavaScript function that either accepts an error-first callback or returns a stream, promise, event emitter, child process, or observable. Due to some platform limitations, synchronous tasks aren't supported. 30 | 31 | For a more detailed explanation, see [Creating Tasks][creating-tasks-doc]. 32 | 33 | ## Globs 34 | 35 | A glob is a string of literal and/or wildcard characters, like `*`, `**`, or `!`, used to match filepaths. Globbing is the act of locating files on a file system using one or more globs. 36 | 37 | If you don't have experience with globs, see [Explaining Globs][explaining-globs-docs]. 38 | 39 | ## Glob base 40 | 41 | A glob base - sometimes called glob parent - is the path segment before any special characters in a glob string. As such, the glob base of `/src/js/**.js` is `/src/js/`. All paths that match the glob are guaranteed to share the glob base - that path segment can't be variable. 42 | 43 | Vinyl instances generated by `src()` are constructed with the glob base set as their `base` property. When written to the file system with `dest()`, the `base` will be removed from the output path to preserve directory structures. 44 | 45 | For more in depth information, see the [glob-parent][glob-parent-external] repository. 46 | 47 | ## File system stats 48 | 49 | File metadata is provided as an instance of Node's [`fs.Stats`][fs-stats-external]. It is available as the `stat` property on your Vinyl instances and used internally to determine if a Vinyl object represents a directory or symbolic link. When written to the file system, permissions and time values are synchronized from the Vinyl object's `stat` property. 50 | 51 | ## File system modes 52 | 53 | File system modes determine what permissions exist for a file. Most files and directories on your file system will have a fairly permissive mode, allowing gulp to read/write/update files on your behalf. By default, gulp will create files with the same permissions as the running process, but you can configure the modes through options in `src()`, `dest()`, etc. If you're experiencing permission (EPERM) issues, check the modes on your files. 54 | 55 | ## Modules 56 | 57 | Gulp is made up of many small modules that are pulled together to work cohesively. By utilizing [semver][semver-external] within the small modules, we can release bug fixes and features without publishing new versions of gulp. Often, when you don't see progress on the main repository, work is being done in one of these modules. 58 | 59 | If you're having trouble, ensure your current modules are updated using the `npm update` command. If the problem persists, open an issue on the individual project repository. 60 | 61 | * [undertaker][undertaker-external] - the task registration system 62 | * [vinyl][vinyl-external] - the virtual file objects 63 | * [vinyl-fs][vinyl-fs-external] - a vinyl adapter to your local file system 64 | * [glob-watcher][glob-watcher-external] - the file watcher 65 | * [bach][bach-external] - task orchestration using `series()` and `parallel()` 66 | * [last-run][last-run-external] - tracks the last run time of a task 67 | * [vinyl-sourcemap][vinyl-sourcemap-external] - built-in sourcemap support 68 | * [gulp-cli][gulp-cli-external] - the command line interface for interacting with gulp 69 | 70 | 71 | [quick-start-docs]: ../getting-started/1-quick-start.md 72 | [creating-tasks-doc]: ../getting-started/3-creating-tasks.md 73 | [explaining-globs-docs]: ../getting-started/6-explaining-globs.md 74 | [undertaker-external]: https://github.com/gulpjs/undertaker 75 | [vinyl-external]: https://github.com/gulpjs/vinyl 76 | [vinyl-fs-external]: https://github.com/gulpjs/vinyl-fs 77 | [glob-watcher-external]: https://github.com/gulpjs/glob-watcher 78 | [bach-external]: https://github.com/gulpjs/bach 79 | [last-run-external]: https://github.com/gulpjs/last-run 80 | [vinyl-sourcemap-external]: https://github.com/gulpjs/vinyl-sourcemap 81 | [gulp-cli-external]: https://github.com/gulpjs/gulp-cli 82 | [semver-external]: https://semver.org 83 | [fs-stats-external]: https://nodejs.org/api/fs.html#fs_class_fs_stats 84 | [glob-parent-external]: https://github.com/es128/glob-parent 85 | -------------------------------------------------------------------------------- /docs/api/dest.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # dest() 9 | 10 | Creates a stream for writing [Vinyl][vinyl-concepts] objects to the file system. 11 | 12 | ## Usage 13 | 14 | ```js 15 | const { src, dest } = require('gulp'); 16 | 17 | function copy() { 18 | return src('input/*.js') 19 | .pipe(dest('output/')); 20 | } 21 | 22 | exports.copy = copy; 23 | ``` 24 | 25 | ## Signature 26 | 27 | ```js 28 | dest(directory, [options]) 29 | ``` 30 | 31 | ### Parameters 32 | 33 | | parameter | type | note | 34 | |:--------------:|:-----:|--------| 35 | | directory
**(required)** | string
function | The path of the output directory where files will be written. If a function is used, the function will be called with each Vinyl object and must return a string directory path. | 36 | | options | object | Detailed in [Options][options-section] below. | 37 | 38 | ### Returns 39 | 40 | A stream that can be used in the middle or at the end of a pipeline to create files on the file system. 41 | Whenever a Vinyl object is passed through the stream, it writes the contents and other details out to the file system at the given directory. If the Vinyl object has a `symlink` property, a symbolic link will be created instead of writing the contents. After the file is created, its [metadata will be updated][metadata-updates-section] to match the Vinyl object. 42 | 43 | Whenever a file is created on the file system, the Vinyl object will be modified. 44 | * The `cwd`, `base`, and `path` properties will be updated to match the created file. 45 | * The `stat` property will be updated to match the file on the file system. 46 | * If the `contents` property is a stream, it will be reset so it can be read again. 47 | 48 | ### Errors 49 | 50 | When `directory` is an empty string, throws an error with the message, "Invalid dest() folder argument. Please specify a non-empty string or a function." 51 | 52 | When `directory` is not a string or function, throws an error with the message, "Invalid dest() folder argument. Please specify a non-empty string or a function." 53 | 54 | When `directory` is a function that returns an empty string or `undefined`, emits an error with the message, "Invalid output folder". 55 | 56 | ### Options 57 | 58 | 59 | **For options that accept a function, the passed function will be called with each Vinyl object and must return a value of another listed type.** 60 | 61 | | name | type | default | note | 62 | |:-------:|:------:|-----------|-------| 63 | | cwd | string
function | `process.cwd()` | The directory that will be combined with any relative path to form an absolute path. Is ignored for absolute paths. Use to avoid combining `directory` with `path.join()`. | 64 | | mode | number
function | `stat.mode` of the Vinyl object | The mode used when creating files. If not set and `stat.mode` is missing, the process' mode will be used instead. | 65 | | dirMode | number
function | | The mode used when creating directories. If not set, the process' mode will be used. | 66 | | overwrite | boolean
function | true | When true, overwrites existing files with the same path. | 67 | | append | boolean
function | false | If true, adds contents to the end of the file, instead of replacing existing contents. | 68 | | sourcemaps | boolean
string
function | false | If true, writes inline sourcemaps to the output file. Specifying a `string` path will write external [sourcemaps][sourcemaps-section] at the given path. | 69 | | relativeSymlinks | boolean
function | false | When false, any symbolic links created will be absolute.
**Note**: Ignored if a junction is being created, as they must be absolute. | 70 | | useJunctions | boolean
function | true | This option is only relevant on Windows and ignored elsewhere. When true, creates directory symbolic link as a junction. Detailed in [Symbolic links on Windows][symbolic-links-section] below. | 71 | 72 | ## Metadata updates 73 | 74 | Whenever the `dest()` stream creates a file, the Vinyl object's `mode`, `mtime`, and `atime` are compared to the created file. If they differ, the created file will be updated to reflect the Vinyl object's metadata. If those properties are the same, or gulp doesn't have permissions to make changes, the attempt is skipped silently. 75 | 76 | This functionality is disabled on Windows or other operating systems that don't support Node's `process.getuid()` or `process.geteuid()` methods. This is due to Windows having unexpected results through usage of `fs.fchmod()` and `fs.futimes()`. 77 | 78 | **Note**: The `fs.futimes()` method internally converts `mtime` and `atime` timestamps to seconds. This division by 1000 may cause some loss of precision on 32-bit operating systems. 79 | 80 | ## Sourcemaps 81 | 82 | Sourcemap support is built directly into `src()` and `dest()`, but it is disabled by default. Enable it to produce inline or external sourcemaps. 83 | 84 | Inline sourcemaps: 85 | ```js 86 | const { src, dest } = require('gulp'); 87 | const uglify = require('gulp-uglify'); 88 | 89 | src('input/**/*.js', { sourcemaps: true }) 90 | .pipe(uglify()) 91 | .pipe(dest('output/', { sourcemaps: true })); 92 | ``` 93 | 94 | External sourcemaps: 95 | ```js 96 | const { src, dest } = require('gulp'); 97 | const uglify = require('gulp-uglify'); 98 | 99 | src('input/**/*.js', { sourcemaps: true }) 100 | .pipe(uglify()) 101 | .pipe(dest('output/', { sourcemaps: '.' })); 102 | ``` 103 | 104 | ## Symbolic links on Windows 105 | 106 | When creating symbolic links on Windows, a `type` argument is passed to Node's `fs.symlink()` method which specifies the kind of target being linked. The link type is set to: 107 | * `'file'` when the target is a regular file 108 | * `'junction'` when the target is a directory 109 | * `'dir'` when the target is a directory and the user disables the `useJunctions` option 110 | 111 | 112 | If you try to create a dangling (pointing to a non-existent target) link, the link type can't be determined automatically. In these cases, behavior will vary depending on whether the dangling link is being created via `symlink()` or via `dest()`. 113 | 114 | For dangling links created via `symlink()`, the incoming Vinyl object represents the target, so its stats will determine the desired link type. If `isDirectory()` returns false then a `'file'` link is created, otherwise a `'junction'` or a `'dir'` link is created depending on the value of the `useJunctions` option. 115 | 116 | For dangling links created via `dest()`, the incoming Vinyl object represents the link - typically loaded from disk via `src(..., { resolveSymlinks: false })`. In this case, the link type can't be reasonably determined and defaults to using `'file'`. This may cause unexpected behavior if you are creating a dangling link to a directory. **Avoid this scenario.** 117 | 118 | [sourcemaps-section]: #sourcemaps 119 | [symbolic-links-section]: #symbolic-links-on-windows 120 | [options-section]: #options 121 | [metadata-updates-section]: #metadata-updates 122 | [vinyl-concepts]: ../api/concepts.md#vinyl 123 | -------------------------------------------------------------------------------- /docs/api/last-run.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # lastRun() 9 | 10 | Retrieves the last time a task was successfully completed during the current running process. Most useful on subsequent task runs while a watcher is running. 11 | 12 | When combined with `src()`, enables incremental builds to speed up execution times by skipping files that haven't changed since the last successful task completion. 13 | 14 | ## Usage 15 | 16 | ```js 17 | const { src, dest, lastRun, watch } = require('gulp'); 18 | const imagemin = require('gulp-imagemin'); 19 | 20 | function images() { 21 | return src('src/images/**/*.jpg', { since: lastRun(images) }) 22 | .pipe(imagemin()) 23 | .pipe(dest('build/img/')); 24 | } 25 | 26 | exports.default = function() { 27 | watch('src/images/**/*.jpg', images); 28 | }; 29 | ``` 30 | 31 | 32 | ## Signature 33 | 34 | ```js 35 | lastRun(task, [precision]) 36 | ``` 37 | 38 | ### Parameters 39 | 40 | | parameter | type | note | 41 | |:--------------:|:------:|-------| 42 | | task
**(required)** | function
string | The task function or the string alias of a registered task. | 43 | | precision | number | Default: `1000` on Node v0.10, `0` on Node v0.12+. Detailed in [Timestamp precision][timestamp-precision-section] section below. | 44 | 45 | ### Returns 46 | 47 | A timestamp (in milliseconds), matching the last completion time of the task. If the task has not been run or has failed, returns `undefined`. 48 | 49 | To avoid an invalid state being cached, the returned value will be `undefined` if a task errors. 50 | 51 | ### Errors 52 | 53 | When called with a value other than a string or function, throws an error with the message, "Only functions can check lastRun". 54 | 55 | When called on a non-extensible function and Node is missing WeakMap, throws an error with the message, "Only extensible functions can check lastRun". 56 | 57 | ## Timestamp precision 58 | 59 | While there are sensible defaults for the precision of timestamps, they can be rounded using the `precision` parameter. Useful if your file system or Node version has a lossy precision on file time attributes. 60 | 61 | * `lastRun(someTask)` returns 1426000001111 62 | * `lastRun(someTask, 100)` returns 1426000001100 63 | * `lastRun(someTask, 1000)` returns 1426000001000 64 | 65 | A file's [mtime stat][fs-stats-concepts] precision may vary depending on the node version and/or the file system used. 66 | 67 | 68 | | platform | precision | 69 | |:-----------:|:------------:| 70 | | Node v0.10 | 1000ms | 71 | | Node v0.12+ | 1ms | 72 | | FAT32 file system | 2000ms | 73 | | HFS+ or Ext3 file systems | 1000ms | 74 | | NTFS using Node v0.10 | 1s | 75 | | NTFS using Node 0.12+ | 100ms | 76 | | Ext4 using Node v0.10 | 1000ms | 77 | | Ext4 using Node 0.12+ | 1ms | 78 | 79 | 80 | [timestamp-precision-section]: #timestamp-precision 81 | [fs-stats-concepts]: ../api/concepts.md#file-system-stats 82 | -------------------------------------------------------------------------------- /docs/api/parallel.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # parallel() 9 | 10 | Combines task functions and/or composed operations into larger operations that will be executed simultaneously. There are no imposed limits on the nesting depth of composed operations using `series()` and `parallel()`. 11 | 12 | ## Usage 13 | 14 | ```js 15 | const { parallel } = require('gulp'); 16 | 17 | function javascript(cb) { 18 | // body omitted 19 | cb(); 20 | } 21 | 22 | function css(cb) { 23 | // body omitted 24 | cb(); 25 | } 26 | 27 | exports.build = parallel(javascript, css); 28 | ``` 29 | 30 | ## Signature 31 | 32 | ```js 33 | parallel(...tasks) 34 | ``` 35 | 36 | ### Parameters 37 | 38 | | parameter | type | note | 39 | |:--------------:|:------:|-------| 40 | | tasks
**(required)** | function
string | Any number of task functions can be passed as individual arguments. Strings can be used if you've registered tasks previously, but this is not recommended. | 41 | 42 | ### Returns 43 | 44 | A composed operation to be registered as a task or nested within other `series` and/or `parallel` compositions. 45 | 46 | When the composed operation is executed, all tasks will be run at maximum concurrency. If an error occurs in one task, other tasks nondeterministically may or may not complete. 47 | 48 | ### Errors 49 | 50 | When no tasks are passed, throws an error with the message, "One or more tasks should be combined using series or parallel". 51 | 52 | When invalid tasks or unregistered tasks are passed, throws an error with the message, "Task never defined". 53 | 54 | ## Forward references 55 | 56 | A forward reference is when you compose tasks, using string references, that haven't been registered yet. This was a common practice in older versions, but this feature was removed to achieve faster task runtime and promote the use of named functions. 57 | 58 | In newer versions, you'll get an error, with the message "Task never defined", if you try to use forward references. You may experience this when trying to use `exports` for task registration _and_ composing tasks by string. In this situation, use named functions instead of string references. 59 | 60 | During migration, you may need the [forward reference registry][undertaker-forward-reference-external]. This will add an extra closure to every task reference and dramatically slow down your build. **Don't rely on this fix for very long**. 61 | 62 | ## Avoid duplicating tasks 63 | 64 | When a composed operation is run, each task will be executed every time it was supplied. 65 | 66 | A `clean` task referenced in two different compositions would be run twice and lead to undesired results. Instead, refactor the `clean` task to be specified in the final composition. 67 | 68 | If you have code like this: 69 | ```js 70 | // This is INCORRECT 71 | const { series, parallel } = require('gulp'); 72 | 73 | const clean = function(cb) { 74 | // body omitted 75 | cb(); 76 | }; 77 | 78 | const css = series(clean, function(cb) { 79 | // body omitted 80 | cb(); 81 | }); 82 | 83 | const javascript = series(clean, function(cb) { 84 | // body omitted 85 | cb(); 86 | }); 87 | 88 | exports.build = parallel(css, javascript); 89 | ``` 90 | 91 | Migrate to this: 92 | ```js 93 | const { series, parallel } = require('gulp'); 94 | 95 | function clean(cb) { 96 | // body omitted 97 | cb(); 98 | } 99 | 100 | function css(cb) { 101 | // body omitted 102 | cb(); 103 | } 104 | 105 | function javascript(cb) { 106 | // body omitted 107 | cb(); 108 | } 109 | 110 | exports.build = series(clean, parallel(css, javascript)); 111 | ``` 112 | 113 | [undertaker-forward-reference-external]: https://github.com/gulpjs/undertaker-forward-reference 114 | -------------------------------------------------------------------------------- /docs/api/registry.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # registry() 9 | 10 | Allows custom registries to be plugged into the task system, which can provide shared tasks or augmented functionality. 11 | 12 | **Note:** Only tasks registered with `task()` will be provided to the custom registry. The task functions passed directly to `series()` or `parallel()` will not be provided - if you need to customize the registry behavior, compose tasks with string references. 13 | 14 | When assigning a new registry, each task from the current registry will be transferred and the current registry will be replaced with the new one. This allows for adding multiple custom registries in sequential order. 15 | 16 | See [Creating Custom Registries][creating-custom-registries] for details. 17 | 18 | ## Usage 19 | 20 | ```js 21 | const { registry, task, series } = require('gulp'); 22 | const FwdRef = require('undertaker-forward-reference'); 23 | 24 | registry(FwdRef()); 25 | 26 | task('default', series('forward-ref')); 27 | 28 | task('forward-ref', function(cb) { 29 | // body omitted 30 | cb(); 31 | }); 32 | ``` 33 | 34 | ## Signature 35 | 36 | ```js 37 | registry([registryInstance]) 38 | ``` 39 | 40 | ### Parameters 41 | 42 | | parameter | type | note | 43 | |:--------------:|:-----:|--------| 44 | | registryInstance | object | An instance - not the class - of a custom registry. | 45 | 46 | ### Returns 47 | 48 | If a `registryInstance` is passed, nothing will be returned. If no arguments are passed, returns the current registry instance. 49 | 50 | ### Errors 51 | 52 | #### Incorrect parameter 53 | 54 | When a constructor (instead of an instance) is passed as `registryInstance`, throws an error with the message: 55 | 56 | > Custom registries must be instantiated, but it looks like you passed a constructor. 57 | 58 | #### Missing `get` method 59 | 60 | When a registry without a `get` method is passed as `registryInstance`, throws an error with the message: 61 | 62 | > Custom registry must have `get` function. 63 | 64 | #### Missing `set` method 65 | 66 | When a registry without a `set` method is passed as `registryInstance`, throws an error with the message: 67 | 68 | > Custom registry must have `set` function. 69 | 70 | #### Missing `init` method 71 | 72 | When a registry without an `init` method is passed as `registryInstance`, throws an error with the message: 73 | 74 | > Custom registry must have `init` function" 75 | 76 | #### Missing `tasks` method 77 | 78 | When a registry without a `tasks` method is passed as `registryInstance`, throws an error with the message: 79 | 80 | > Custom registry must have `tasks` function. 81 | 82 | [creating-custom-registries]: ../advanced/creating-custom-registries.md 83 | -------------------------------------------------------------------------------- /docs/api/series.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # series() 9 | 10 | Combines task functions and/or composed operations into larger operations that will be executed one after another, in sequential order. There are no imposed limits on the nesting depth of composed operations using `series()` and `parallel()`. 11 | 12 | ## Usage 13 | 14 | ```js 15 | const { series } = require('gulp'); 16 | 17 | function javascript(cb) { 18 | // body omitted 19 | cb(); 20 | } 21 | 22 | function css(cb) { 23 | // body omitted 24 | cb(); 25 | } 26 | 27 | exports.build = series(javascript, css); 28 | ``` 29 | 30 | ## Signature 31 | 32 | ```js 33 | series(...tasks) 34 | ``` 35 | 36 | ### Parameters 37 | 38 | | parameter | type | note | 39 | |:--------------:|:------:|-------| 40 | | tasks
**(required)** | function
string | Any number of task functions can be passed as individual arguments. Strings can be used if you've registered tasks previously, but this is not recommended. | 41 | 42 | ### Returns 43 | 44 | A composed operation to be registered as a task or nested within other `series` and/or `parallel` compositions. 45 | 46 | When the composed operation is executed, all tasks will be run sequentially. If an error occurs in one task, no subsequent tasks will be run. 47 | 48 | ### Errors 49 | 50 | When no tasks are passed, throws an error with the message, "One or more tasks should be combined using series or parallel". 51 | 52 | When invalid tasks or unregistered tasks are passed, throws an error with the message, "Task never defined". 53 | 54 | ## Forward references 55 | 56 | A forward reference is when you compose tasks, using string references, that haven't been registered yet. This was a common practice in older versions, but this feature was removed to achieve faster task runtime and promote the use of named functions. 57 | 58 | In newer versions, you'll get an error, with the message "Task never defined", if you try to use forward references. You may experience this when trying to use `exports` for your task registration *and* composing tasks by string. In this situation, use named functions instead of string references. 59 | 60 | During migration, you may need to use the [forward reference registry][undertaker-forward-reference-external]. This will add an extra closure to every task reference and dramatically slow down your build. **Don't rely on this fix for very long**. 61 | 62 | ## Avoid duplicating tasks 63 | 64 | When a composed operation is run, each task will be executed every time it was supplied. 65 | 66 | A `clean` task referenced in two different compositions would be run twice and lead to undesired results. Instead, refactor the `clean` task to be specified in the final composition. 67 | 68 | If you have code like this: 69 | ```js 70 | // This is INCORRECT 71 | const { series, parallel } = require('gulp'); 72 | 73 | const clean = function(cb) { 74 | // body omitted 75 | cb(); 76 | }; 77 | 78 | const css = series(clean, function(cb) { 79 | // body omitted 80 | cb(); 81 | }); 82 | 83 | const javascript = series(clean, function(cb) { 84 | // body omitted 85 | cb(); 86 | }); 87 | 88 | exports.build = parallel(css, javascript); 89 | ``` 90 | 91 | Migrate to this: 92 | ```js 93 | const { series, parallel } = require('gulp'); 94 | 95 | function clean(cb) { 96 | // body omitted 97 | cb(); 98 | } 99 | 100 | function css(cb) { 101 | // body omitted 102 | cb(); 103 | } 104 | 105 | function javascript(cb) { 106 | // body omitted 107 | cb(); 108 | } 109 | 110 | exports.build = series(clean, parallel(css, javascript)); 111 | ``` 112 | 113 | [undertaker-forward-reference-external]: https://github.com/gulpjs/undertaker-forward-reference 114 | -------------------------------------------------------------------------------- /docs/api/src.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # src() 9 | 10 | Creates a stream for reading [Vinyl][vinyl-concepts] objects from the file system. 11 | 12 | **Note:** BOMs (byte order marks) have no purpose in UTF-8 and will be removed from UTF-8 files read by `src()`, unless disabled using the `removeBOM` option. 13 | 14 | ## Usage 15 | 16 | ```javascript 17 | const { src, dest } = require('gulp'); 18 | 19 | function copy() { 20 | return src('input/*.js') 21 | .pipe(dest('output/')); 22 | } 23 | 24 | exports.copy = copy; 25 | ``` 26 | 27 | 28 | ## Signature 29 | 30 | ```js 31 | src(globs, [options]) 32 | ``` 33 | 34 | ### Parameters 35 | 36 | | parameter | type | note | 37 | |:--------------:|:------:|-------| 38 | | globs | string
array | [Globs][globs-concepts] to watch on the file system. | 39 | | options | object | Detailed in [Options][options-section] below. | 40 | 41 | ### Returns 42 | 43 | A stream that can be used at the beginning or in the middle of a pipeline to add files based on the given globs. 44 | 45 | ### Errors 46 | 47 | When the `globs` argument can only match one file (such as `foo/bar.js`) and no match is found, throws an error with the message, "File not found with singular glob". To suppress this error, set the `allowEmpty` option to `true`. 48 | 49 | When an invalid glob is given in `globs`, throws an error with the message, "Invalid glob argument". 50 | 51 | ### Options 52 | 53 | **For options that accept a function, the passed function will be called with each Vinyl object and must return a value of another listed type.** 54 | 55 | 56 | | name | type | default | note | 57 | |:--------:|:------:|------------|--------| 58 | | encoding | string
boolean | "utf8" | When false, file contents are treated as binary. When a string, this is used as the text encoding. | 59 | | buffer | boolean
function | true | When true, file contents are buffered into memory. If false, the Vinyl object's `contents` property will be a paused stream. It may not be possible to buffer the contents of large files.
**Note:** Plugins may not implement support for streaming contents. | 60 | | read | boolean
function | true | If false, files will be not be read and their Vinyl objects won't be writable to disk via `.dest()`. | 61 | | since | date
timestamp
function | | When set, only creates Vinyl objects for files modified since the specified time. | 62 | | removeBOM | boolean
function | true | When true, removes the BOM from UTF-8 encoded files. If false, ignores a BOM. | 63 | | sourcemaps | boolean
function | false | If true, enables [sourcemaps][sourcemaps-section] support on Vinyl objects created. Loads inline sourcemaps and resolves external sourcemap links. | 64 | | resolveSymlinks | boolean
function | true | When true, recursively resolves symbolic links to their targets. If false, preserves the symbolic links and sets the Vinyl object's `symlink` property to the original file's path. | 65 | | cwd | string | `process.cwd()` | The directory that will be combined with any relative path to form an absolute path. Is ignored for absolute paths. Use to avoid combining `globs` with `path.join()`.
_This option is passed directly to [glob-stream][glob-stream-external]._ | 66 | | base | string | | Explicitly set the `base` property on created Vinyl objects. Detailed in [API Concepts][glob-base-concepts].
_This option is passed directly to [glob-stream][glob-stream-external]._ | 67 | | cwdbase | boolean | false | If true, `cwd` and `base` options should be aligned.
_This option is passed directly to [glob-stream][glob-stream-external]._ | 68 | | root | string | | The root path that `globs` are resolved against.
_This option is passed directly to [glob-stream][glob-stream-external]._ | 69 | | allowEmpty | boolean | false | When false, `globs` which can only match one file (such as `foo/bar.js`) causes an error to be thrown if they don't find a match. If true, suppresses glob failures.
_This option is passed directly to [glob-stream][glob-stream-external]._ | 70 | | uniqueBy | string
function | `'path'` | Remove duplicates from the stream by comparing the string property name or the result of the function.
**Note:** When using a function, the function receives the streamed data (objects containing `cwd`, `base`, `path` properties). | 71 | | dot | boolean | false | If true, compare globs against dot files, like `.gitignore`.
_This option is passed directly to [anymatch][anymatch-external]._ | 72 | | nounique | boolean | false | When false, prevents duplicate files in the result set.
_This option is passed directly to [anymatch][anymatch-external]._ | 73 | | debug | boolean | false | If true, debugging information will be logged to the command line.
_This option is passed directly to [anymatch][anymatch-external]._ | 74 | | nobrace | boolean | false | If true, avoids expanding brace sets - e.g. `{a,b}` or `{1..3}`.
_This option is passed directly to [anymatch][anymatch-external]._ | 75 | | noglobstar | boolean | false | If true, treats double-star glob character as single-star glob character.
_This option is passed directly to [anymatch][anymatch-external]._ | 76 | | noext | boolean | false | If true, avoids matching [extglob][extglob-docs] patterns - e.g. `+(ab)`.
_This option is passed directly to [anymatch][anymatch-external]._ | 77 | | nocase | boolean | false | If true, performs a case-insensitive match.
**Note:** On case-insensitive file systems, non-magic patterns will match by default.
_This option is passed directly to [anymatch][anymatch-external]._ | 78 | | matchBase | boolean | false | If true and globs don't contain any `/` characters, traverses all directories and matches that glob - e.g. `*.js` would be treated as equivalent to `**/*.js`.
_This option is passed directly to [anymatch][anymatch-external]._ | 79 | | ignore | string
array | | Globs to exclude from matches. This option is combined with negated `globs`.
**Note:** These globs are always matched against dot files, regardless of any other settings.
_This option is passed directly to [anymatch][anymatch-external]._ | 80 | 81 | ## Sourcemaps 82 | 83 | Sourcemap support is built directly into `src()` and `dest()`, but is disabled by default. Enable it to produce inline or external sourcemaps. 84 | 85 | Inline sourcemaps: 86 | ```js 87 | const { src, dest } = require('gulp'); 88 | const uglify = require('gulp-uglify'); 89 | 90 | src('input/**/*.js', { sourcemaps: true }) 91 | .pipe(uglify()) 92 | .pipe(dest('output/', { sourcemaps: true })); 93 | ``` 94 | 95 | External sourcemaps: 96 | ```js 97 | const { src, dest } = require('gulp'); 98 | const uglify = require('gulp-uglify'); 99 | 100 | src('input/**/*.js', { sourcemaps: true }) 101 | .pipe(uglify()) 102 | .pipe(dest('output/', { sourcemaps: '.' })); 103 | ``` 104 | 105 | [sourcemaps-section]: #sourcemaps 106 | [options-section]: #options 107 | [vinyl-concepts]: ../api/concepts.md#vinyl 108 | [glob-base-concepts]: ../api/concepts.md#glob-base 109 | [globs-concepts]: ../api/concepts.md#globs 110 | [extglob-docs]: ../documentation-missing.md 111 | [anymatch-external]: https://github.com/micromatch/anymatch 112 | [glob-stream-external]: https://github.com/gulpjs/glob-stream 113 | -------------------------------------------------------------------------------- /docs/api/symlink.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # symlink() 9 | 10 | Creates a stream for linking [Vinyl][vinyl-concepts] objects to the file system. 11 | 12 | ## Usage 13 | 14 | ```js 15 | const { src, symlink } = require('gulp'); 16 | 17 | function link() { 18 | return src('input/*.js') 19 | .pipe(symlink('output/')); 20 | } 21 | 22 | exports.link = link; 23 | ``` 24 | 25 | ## Signature 26 | 27 | ```js 28 | symlink(directory, [options]) 29 | ``` 30 | 31 | ### Parameters 32 | 33 | | parameter | type | note | 34 | |:--------------:|:-----:|--------| 35 | | directory
**(required)** | string
function | The path of the output directory where symbolic links will be created. If a function is used, the function will be called with each Vinyl object and must return a string directory path. | 36 | | options | object | Detailed in [Options][options-section] below. | 37 | 38 | ### Returns 39 | 40 | A stream that can be used in the middle or at the end of a pipeline to create symbolic links on the file system. 41 | Whenever a Vinyl object is passed through the stream, it creates a symbolic link to the original file on the file system at the given directory. 42 | 43 | Whenever a symbolic link is created on the file system, the Vinyl object will be modified. 44 | * The `cwd`, `base`, and `path` properties will be updated to match the created symbolic link. 45 | * The `stat` property will be updated to match the symbolic link on the file system. 46 | * The `contents` property will be set to `null`. 47 | * The `symlink` property will be added or replaced with original path. 48 | 49 | **Note:** On Windows, directory links are created using junctions by default. The `useJunctions` option disables this behavior. 50 | 51 | 52 | ### Errors 53 | 54 | When `directory` is an empty string, throws an error with the message, "Invalid symlink() folder argument. Please specify a non-empty string or a function." 55 | 56 | When `directory` is not a string or function, throws an error with the message, "Invalid symlink() folder argument. Please specify a non-empty string or a function." 57 | 58 | When `directory` is a function that returns an empty string or `undefined`, emits an error with the message, "Invalid output folder". 59 | 60 | ### Options 61 | 62 | **For options that accept a function, the passed function will be called with each Vinyl object and must return a value of another listed type.** 63 | 64 | | name | type | default | note | 65 | |:-------:|:------:|-----------|-------| 66 | | cwd | string
function | `process.cwd()` |The directory that will be combined with any relative path to form an absolute path. Is ignored for absolute paths. Use to avoid combining `directory` with `path.join()`. | 67 | | dirMode | number
function | | The mode used when creating directories. If not set, the process' mode will be used. | 68 | | overwrite | boolean
function | true | When true, overwrites existing files with the same path. | 69 | | relativeSymlinks | boolean
function | false | When false, any symbolic links created will be absolute.
**Note**: Ignored if a junction is being created, as they must be absolute. | 70 | | useJunctions | boolean
function | true | This option is only relevant on Windows and ignored elsewhere. When true, creates directory symbolic link as a junction. Detailed in [Symbolic links on Windows][symbolic-links-section] below. | 71 | 72 | ## Symbolic links on Windows 73 | 74 | When creating symbolic links on Windows, a `type` argument is passed to Node's `fs.symlink()` method which specifies the type of target being linked. The link type is set to: 75 | * `'file'` when the target is a regular file 76 | * `'junction'` when the target is a directory 77 | * `'dir'` when the target is a directory and the user disables the `useJunctions` option 78 | 79 | 80 | If you try to create a dangling (pointing to a non-existent target) link, the link type can't be determined automatically. In these cases, behavior will vary depending on whether the dangling link is being created via `symlink()` or via `dest()`. 81 | 82 | For dangling links created via `symlink()`, the incoming Vinyl object represents the target, so its stats will determine the desired link type. If `isDirectory()` returns false then a `'file'` link is created, otherwise a `'junction'` or `'dir'` link is created depending on the value of the `useJunctions` option. 83 | 84 | For dangling links created via `dest()`, the incoming Vinyl object represents the link - typically loaded from disk via `src(..., { resolveSymlinks: false })`. In this case, the link type can't be reasonably determined and defaults to using `'file'`. This may cause unexpected behavior when creating a dangling link to a directory. **Avoid this scenario.** 85 | 86 | [options-section]: #options 87 | [symbolic-links-section]: #symbolic-links-on-windows 88 | [vinyl-concepts]: ../api/concepts.md#vinyl 89 | -------------------------------------------------------------------------------- /docs/api/task.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # task() 9 | 10 | **Reminder**: This API isn't the recommended pattern anymore - [export your tasks][creating-tasks-docs]. 11 | 12 | Defines a task within the task system. The task can then be accessed from the command line and the `series()`, `parallel()`, and `lastRun()` APIs. 13 | 14 | ## Usage 15 | 16 | Register a named function as a task: 17 | ```js 18 | const { task } = require('gulp'); 19 | 20 | function build(cb) { 21 | // body omitted 22 | cb(); 23 | } 24 | 25 | task(build); 26 | ``` 27 | 28 | Register an anonymous function as a task: 29 | ```js 30 | const { task } = require('gulp'); 31 | 32 | task('build', function(cb) { 33 | // body omitted 34 | cb(); 35 | }); 36 | ``` 37 | 38 | Retrieve a task that has been registered previously: 39 | ```js 40 | const { task } = require('gulp'); 41 | 42 | task('build', function(cb) { 43 | // body omitted 44 | cb(); 45 | }); 46 | 47 | const build = task('build'); 48 | ``` 49 | 50 | ## Signature 51 | 52 | ```js 53 | task([taskName], taskFunction) 54 | ``` 55 | 56 | ### Parameters 57 | 58 | If the `taskName` is not provided, the task will be referenced by the `name` property of a named function or a user-defined `displayName` property. The `taskName` parameter must be used for anonymous functions missing a `displayName` property. 59 | 60 | Since any registered task can be run from the command line, avoid using spaces in task names. 61 | 62 | | parameter | type | note | 63 | |:--------------:|:------:|-------| 64 | | taskName | string | An alias for the task function within the task system. Not needed when using named functions for `taskFunction`. | 65 | | taskFunction
**(required)** | function | A [task function][task-concepts] or composed task - generated by `series()` and `parallel()`. Ideally a named function. [Task metadata][task-metadata-section] can be attached to provide extra information to the command line. | 66 | 67 | ### Returns 68 | 69 | When registering a task, nothing is returned. 70 | 71 | When retrieving a task, a wrapped task (not the original function) registered as `taskName` will be returned. The wrapped task has an `unwrap()` method that will return the original function. 72 | 73 | ### Errors 74 | 75 | When registering a task where `taskName` is missing and `taskFunction` is anonymous, will throw an error with the message, "Task name must be specified". 76 | 77 | ## Task metadata 78 | 79 | | property | type | note | 80 | |:--------------:|:------:|-------| 81 | | name | string | A special property of named functions. Used to register the task.
**Note:** [`name`][function-name-external] is not writable; it cannot be set or changed. | 82 | | displayName | string | When attached to a `taskFunction` creates an alias for the task. If using characters that aren't allowed in function names, use this property. | 83 | | description | string | When attached to a `taskFunction` provides a description to be printed by the command line when listing tasks. | 84 | | flags | object | When attached to a `taskFunction` provides flags to be printed by the command line when listing tasks. The keys of the object represent the flags and the values are their descriptions. | 85 | 86 | ```js 87 | const { task } = require('gulp'); 88 | 89 | const clean = function(cb) { 90 | // body omitted 91 | cb(); 92 | }; 93 | clean.displayName = 'clean:all'; 94 | 95 | task(clean); 96 | 97 | function build(cb) { 98 | // body omitted 99 | cb(); 100 | } 101 | build.description = 'Build the project'; 102 | build.flags = { '-e': 'An example flag' }; 103 | 104 | task(build); 105 | ``` 106 | 107 | [task-metadata-section]: #task-metadata 108 | [task-concepts]: ../api/concepts.md#tasks 109 | [creating-tasks-docs]: ../getting-started/3-creating-tasks.md 110 | [function-name-external]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name 111 | -------------------------------------------------------------------------------- /docs/api/tree.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # tree() 9 | 10 | Fetches the current task dependency tree - in the rare case that it is needed. 11 | 12 | Generally, `tree()` won't be used by gulp consumers, but it is exposed so the CLI can show the dependency graph of the tasks defined in a gulpfile. 13 | 14 | ## Usage 15 | 16 | Example gulpfile: 17 | ```js 18 | 19 | const { series, parallel } = require('gulp'); 20 | 21 | function one(cb) { 22 | // body omitted 23 | cb(); 24 | } 25 | 26 | function two(cb) { 27 | // body omitted 28 | cb(); 29 | } 30 | 31 | function three(cb) { 32 | // body omitted 33 | cb(); 34 | } 35 | 36 | const four = series(one, two); 37 | 38 | const five = series(four, 39 | parallel(three, function(cb) { 40 | // Body omitted 41 | cb(); 42 | }) 43 | ); 44 | 45 | module.exports = { one, two, three, four, five }; 46 | ``` 47 | 48 | Output for `tree()`: 49 | ```js 50 | { 51 | label: 'Tasks', 52 | nodes: [ 'one', 'two', 'three', 'four', 'five' ] 53 | } 54 | ``` 55 | 56 | 57 | Output for `tree({ deep: true })`: 58 | ```js 59 | { 60 | label: "Tasks", 61 | nodes: [ 62 | { 63 | label: "one", 64 | type: "task", 65 | nodes: [] 66 | }, 67 | { 68 | label: "two", 69 | type: "task", 70 | nodes: [] 71 | }, 72 | { 73 | label: "three", 74 | type: "task", 75 | nodes: [] 76 | }, 77 | { 78 | label: "four", 79 | type: "task", 80 | nodes: [ 81 | { 82 | label: "", 83 | type: "function", 84 | branch: true, 85 | nodes: [ 86 | { 87 | label: "one", 88 | type: "function", 89 | nodes: [] 90 | }, 91 | { 92 | label: "two", 93 | type: "function", 94 | nodes: [] 95 | } 96 | ] 97 | } 98 | ] 99 | }, 100 | { 101 | label: "five", 102 | type: "task", 103 | nodes: [ 104 | { 105 | label: "", 106 | type: "function", 107 | branch: true, 108 | nodes: [ 109 | { 110 | label: "", 111 | type: "function", 112 | branch: true, 113 | nodes: [ 114 | { 115 | label: "one", 116 | type: "function", 117 | nodes: [] 118 | }, 119 | { 120 | label: "two", 121 | type: "function", 122 | nodes: [] 123 | } 124 | ] 125 | }, 126 | { 127 | label: "", 128 | type: "function", 129 | branch: true, 130 | nodes: [ 131 | { 132 | label: "three", 133 | type: "function", 134 | nodes: [] 135 | }, 136 | { 137 | label: "", 138 | type: "function", 139 | nodes: [] 140 | } 141 | ] 142 | } 143 | ] 144 | } 145 | ] 146 | } 147 | ] 148 | } 149 | ``` 150 | 151 | ## Signature 152 | 153 | ```js 154 | tree([options]) 155 | ``` 156 | 157 | ### Parameters 158 | 159 | | parameter | type | note | 160 | |:--------------:|------:|--------| 161 | | options | object | Detailed in [Options][options-section] below. | 162 | 163 | ### Returns 164 | 165 | An object detailing the tree of registered tasks - containing nested objects with `'label'` and `'nodes'` properties (which is [archy][archy-external] compatible). 166 | 167 | Each object may have a `type` property that can be used to determine if the node is a `task` or `function`. 168 | 169 | Each object may have a `branch` property that, when `true`, indicates the node was created using `series()` or `parallel()`. 170 | 171 | ### Options 172 | 173 | | name | type | default | note | 174 | |:-------:|:-------:|------------|--------| 175 | | deep | boolean | false | If true, the entire tree will be returned. When false, only top level tasks will be returned. | 176 | 177 | [options-section]: #options 178 | [archy-external]: https://www.npmjs.com/package/archy 179 | -------------------------------------------------------------------------------- /docs/api/vinyl-iscustomprop.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # Vinyl.isCustomProp() 9 | 10 | Determines if a property is internally managed by Vinyl. Used by Vinyl when setting values inside the constructor or when copying properties in the `clone()` instance method. 11 | 12 | This method is useful when extending the Vinyl class. Detailed in [Extending Vinyl][extending-vinyl-section] below. 13 | 14 | ## Usage 15 | 16 | ```js 17 | const Vinyl = require('vinyl'); 18 | 19 | Vinyl.isCustomProp('sourceMap') === true; 20 | Vinyl.isCustomProp('path') === false; 21 | ``` 22 | 23 | ## Signature 24 | 25 | ```js 26 | Vinyl.isCustomProp(property) 27 | ``` 28 | 29 | ### Parameters 30 | 31 | | parameter | type | note | 32 | |:--------------:|:------:|-------| 33 | | property | string | The property name to check. | 34 | 35 | ### Returns 36 | 37 | True if the property is not internally managed. 38 | 39 | ## Extending Vinyl 40 | 41 | When custom properties are managed internally, the static `isCustomProp` method must be extended and return false when one of the custom properties is queried. 42 | 43 | ```js 44 | const Vinyl = require('vinyl'); 45 | 46 | const builtInProps = ['foo', '_foo']; 47 | 48 | class SuperFile extends Vinyl { 49 | constructor(options) { 50 | super(options); 51 | this._foo = 'example internal read-only value'; 52 | } 53 | 54 | get foo() { 55 | return this._foo; 56 | } 57 | 58 | static isCustomProp(name) { 59 | return super.isCustomProp(name) && builtInProps.indexOf(name) === -1; 60 | } 61 | } 62 | ``` 63 | 64 | In the example above, `foo` and `_foo` will not be assigned to the new object when cloning or passed in `options` to `new SuperFile(options)`. 65 | 66 | If your custom properties or logic require special handling during cloning, override the `clone` method while extending Vinyl. 67 | 68 | [extending-vinyl-section]: #extending-vinyl 69 | -------------------------------------------------------------------------------- /docs/api/vinyl-isvinyl.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # Vinyl.isVinyl() 9 | 10 | Determines if an object is a Vinyl instance. Use this method instead of `instanceof`. 11 | 12 | **Note**: This method uses an internal property that some older versions of Vinyl didn't expose resulting in a false negative if using an outdated version. 13 | 14 | ## Usage 15 | 16 | ```js 17 | const Vinyl = require('vinyl'); 18 | 19 | const file = new Vinyl(); 20 | const notAFile = {}; 21 | 22 | Vinyl.isVinyl(file) === true; 23 | Vinyl.isVinyl(notAFile) === false; 24 | ``` 25 | 26 | ## Signature 27 | 28 | ```js 29 | Vinyl.isVinyl(file); 30 | ``` 31 | 32 | ### Parameters 33 | 34 | | parameter | type | note | 35 | |:--------------:|:------:|-------| 36 | | file | object | The object to check. | 37 | 38 | ### Returns 39 | 40 | True if the `file` object is a Vinyl instance. 41 | 42 | -------------------------------------------------------------------------------- /docs/documentation-missing.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Excuse our dust! 8 | 9 | We're in the process of rewriting **all** our documentation and some of the links we've added to completed docs haven't been written yet. You've likely clicked on one of those to end up here. We're sorry about that but please check back later on the topic you're interested in. If you want to help out, we'll happily accept a Pull Request for this missing documentation. 10 | 11 | -The Gulp Team 12 | -------------------------------------------------------------------------------- /docs/getting-started.md: -------------------------------------------------------------------------------- 1 | ## This documentation has moved! 2 | 3 | You can find the new documentation in our [Quick Start](getting-started/1-quick-start.md) guide. 4 | 5 | While you are there, check out our expanded [Getting Started](getting-started/) documentation. 6 | -------------------------------------------------------------------------------- /docs/getting-started/1-quick-start.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # Quick Start 9 | 10 | If you've previously installed gulp globally, run `npm rm --global gulp` before following these instructions. For more information, read this [Sip][sip-article]. 11 | 12 | ## Check for node, npm, and npx 13 | ```sh 14 | node --version 15 | ``` 16 | ![Output: v8.11.1][img-node-version-command] 17 | ```sh 18 | npm --version 19 | ``` 20 | ![Output: 5.6.0][img-npm-version-command] 21 | ```sh 22 | npx --version 23 | ``` 24 | ![Output: 9.7.1][img-npx-version-command] 25 | 26 | If they are not installed, follow the instructions [here][node-install]. 27 | 28 | ## Install the gulp command line utility 29 | ```sh 30 | npm install --global gulp-cli 31 | ``` 32 | 33 | 34 | ## Create a project directory and navigate into it 35 | ```sh 36 | npx mkdirp my-project 37 | ``` 38 | ```sh 39 | cd my-project 40 | ``` 41 | 42 | ## Create a package.json file in your project directory 43 | ```sh 44 | npm init 45 | ``` 46 | 47 | This will guide you through giving your project a name, version, description, etc. 48 | 49 | ## Install the gulp package in your devDependencies 50 | ```sh 51 | npm install --save-dev gulp 52 | ``` 53 | 54 | ## Verify your gulp versions 55 | 56 | ```sh 57 | gulp --version 58 | ``` 59 | 60 | Ensure the output matches the screenshot below or you might need to restart the steps in this guide. 61 | 62 | ![Output: CLI version 2.0.1 & Local version 4.0.0][img-gulp-version-command] 63 | 64 | ## Create a gulpfile 65 | Using your text editor, create a file named gulpfile.js in your project root with these contents: 66 | ```js 67 | function defaultTask(cb) { 68 | // place code for your default task here 69 | cb(); 70 | } 71 | 72 | exports.default = defaultTask 73 | ``` 74 | 75 | ## Test it 76 | Run the gulp command in your project directory: 77 | ```sh 78 | gulp 79 | ``` 80 | To run multiple tasks, you can use `gulp `. 81 | 82 | ## Result 83 | The default task will run and do nothing. 84 | ![Output: Starting default & Finished default][img-gulp-command] 85 | 86 | [sip-article]: https://medium.com/gulpjs/gulp-sips-command-line-interface-e53411d4467 87 | [node-install]: https://nodejs.org/en/ 88 | [img-node-version-command]: https://gulpjs.com/img/docs-node-version-command.png 89 | [img-npm-version-command]: https://gulpjs.com/img/docs-npm-version-command.png 90 | [img-npx-version-command]: https://gulpjs.com/img/docs-npx-version-command.png 91 | [img-gulp-version-command]: https://gulpjs.com/img/docs-gulp-version-command.png 92 | [img-gulp-command]: https://gulpjs.com/img/docs-gulp-command.png 93 | -------------------------------------------------------------------------------- /docs/getting-started/2-javascript-and-gulpfiles.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # JavaScript and Gulpfiles 9 | 10 | Gulp allows you to use existing JavaScript knowledge to write gulpfiles or to use your experience with gulpfiles to write plain JavaScript. Although a few utilities are provided to simplify working with the filesystem and command line, everything else you write is pure JavaScript. 11 | 12 | ## Gulpfile explained 13 | 14 | A gulpfile is a file in your project directory titled `gulpfile.js` (or capitalized as `Gulpfile.js`, like Makefile), that automatically loads when you run the `gulp` command. Within this file, you'll often see gulp APIs, like `src()`, `dest()`, `series()`, or `parallel()` but any vanilla JavaScript or Node modules can be used. Any exported functions will be registered into gulp's task system. 15 | 16 | ## Transpilation 17 | 18 | You can write a gulpfile using a language that requires transpilation, like TypeScript or Babel, by changing the extension on your `gulpfile.js` to indicate the language and install the matching transpiler module. 19 | 20 | * For TypeScript, rename to `gulpfile.ts` and install the [ts-node][ts-node-module] module. 21 | * For Babel, rename to `gulpfile.babel.js` and install the [@babel/register][babel-register-module] module. 22 | 23 | __Most new versions of node support most features that TypeScript or Babel provide, except the `import`/`export` syntax. When only that syntax is desired, rename to `gulpfile.esm.js` and install the [esm][esm-module] module.__ 24 | 25 | For a more advanced dive into this topic and the full list of supported extensions, see our [gulpfile transpilation][gulpfile-transpilation-advanced] documentation. 26 | 27 | ## Splitting a gulpfile 28 | 29 | Many users start by adding all logic to a gulpfile. If it ever grows too big, it can be refactored into separate files. 30 | 31 | Each task can be split into its own file, then imported into your gulpfile for composition. Not only does this keep things organized, but it allows you to test each task independently or vary composition based on conditions. 32 | 33 | Node's module resolution allows you to replace your `gulpfile.js` file with a directory named `gulpfile.js` that contains an `index.js` file which is treated as a `gulpfile.js`. This directory could then contain your individual modules for tasks. If you are using a transpiler, name the folder and file accordingly. 34 | 35 | [gulpfile-transpilation-advanced]: ../documentation-missing.md 36 | [ts-node-module]: https://www.npmjs.com/package/ts-node 37 | [babel-register-module]: https://www.npmjs.com/package/@babel/register 38 | [esm-module]: https://www.npmjs.com/package/esm 39 | -------------------------------------------------------------------------------- /docs/getting-started/3-creating-tasks.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # Creating Tasks 9 | 10 | Each gulp task is an asynchronous JavaScript function - a function that accepts an error-first callback or returns a stream, promise, event emitter, child process, or observable ([more on that later][async-completion-docs]). Due to some platform limitations, synchronous tasks aren't supported, though there is a pretty nifty [alternative][using-async-await-docs]. 11 | 12 | ## Exporting 13 | 14 | Tasks can be considered **public** or **private**. 15 | 16 | * **Public tasks** are exported from your gulpfile, which allows them to be run by the `gulp` command. 17 | * **Private tasks** are made to be used internally, usually used as part of `series()` or `parallel()` composition. 18 | 19 | A private task looks and acts like any other task, but an end-user can't ever execute it independently. To register a task publicly, export it from your gulpfile. 20 | 21 | ```js 22 | const { series } = require('gulp'); 23 | 24 | // The `clean` function is not exported so it can be considered a private task. 25 | // It can still be used within the `series()` composition. 26 | function clean(cb) { 27 | // body omitted 28 | cb(); 29 | } 30 | 31 | // The `build` function is exported so it is public and can be run with the `gulp` command. 32 | // It can also be used within the `series()` composition. 33 | function build(cb) { 34 | // body omitted 35 | cb(); 36 | } 37 | 38 | exports.build = build; 39 | exports.default = series(clean, build); 40 | ``` 41 | 42 | ![ALT TEXT MISSING][img-gulp-tasks-command] 43 | 44 | In the past, `task()` was used to register your functions as tasks. While that API is still available, exporting should be the primary registration mechanism, except in edge cases where exports won't work. 45 | 46 | ## Compose tasks 47 | 48 | Gulp provides two powerful composition methods, `series()` and `parallel()`, allowing individual tasks to be composed into larger operations. Both methods accept any number of task functions or composed operations. `series()` and `parallel()` can be nested within themselves or each other to any depth. 49 | 50 | To have your tasks execute in order, use the `series()` method. 51 | ```js 52 | const { series } = require('gulp'); 53 | 54 | function transpile(cb) { 55 | // body omitted 56 | cb(); 57 | } 58 | 59 | function bundle(cb) { 60 | // body omitted 61 | cb(); 62 | } 63 | 64 | exports.build = series(transpile, bundle); 65 | ``` 66 | 67 | For tasks to run at maximum concurrency, combine them with the `parallel()` method. 68 | ```js 69 | const { parallel } = require('gulp'); 70 | 71 | function javascript(cb) { 72 | // body omitted 73 | cb(); 74 | } 75 | 76 | function css(cb) { 77 | // body omitted 78 | cb(); 79 | } 80 | 81 | exports.build = parallel(javascript, css); 82 | ``` 83 | 84 | Tasks are composed immediately when either `series()` or `parallel()` is called. This allows variation in the composition instead of conditional behavior inside individual tasks. 85 | 86 | ```js 87 | const { series } = require('gulp'); 88 | 89 | function minify(cb) { 90 | // body omitted 91 | cb(); 92 | } 93 | 94 | 95 | function transpile(cb) { 96 | // body omitted 97 | cb(); 98 | } 99 | 100 | function livereload(cb) { 101 | // body omitted 102 | cb(); 103 | } 104 | 105 | if (process.env.NODE_ENV === 'production') { 106 | exports.build = series(transpile, minify); 107 | } else { 108 | exports.build = series(transpile, livereload); 109 | } 110 | ``` 111 | 112 | `series()` and `parallel()` can be nested to any arbitrary depth. 113 | 114 | ```js 115 | const { series, parallel } = require('gulp'); 116 | 117 | function clean(cb) { 118 | // body omitted 119 | cb(); 120 | } 121 | 122 | function cssTranspile(cb) { 123 | // body omitted 124 | cb(); 125 | } 126 | 127 | function cssMinify(cb) { 128 | // body omitted 129 | cb(); 130 | } 131 | 132 | function jsTranspile(cb) { 133 | // body omitted 134 | cb(); 135 | } 136 | 137 | function jsBundle(cb) { 138 | // body omitted 139 | cb(); 140 | } 141 | 142 | function jsMinify(cb) { 143 | // body omitted 144 | cb(); 145 | } 146 | 147 | function publish(cb) { 148 | // body omitted 149 | cb(); 150 | } 151 | 152 | exports.build = series( 153 | clean, 154 | parallel( 155 | cssTranspile, 156 | series(jsTranspile, jsBundle) 157 | ), 158 | parallel(cssMinify, jsMinify), 159 | publish 160 | ); 161 | ``` 162 | 163 | When a composed operation is run, each task will be executed every time it was referenced. For example, a `clean` task referenced before two different tasks would be run twice and lead to undesired results. Instead, refactor the `clean` task to be specified in the final composition. 164 | 165 | If you have code like this: 166 | 167 | ```js 168 | // This is INCORRECT 169 | const { series, parallel } = require('gulp'); 170 | 171 | const clean = function(cb) { 172 | // body omitted 173 | cb(); 174 | }; 175 | 176 | const css = series(clean, function(cb) { 177 | // body omitted 178 | cb(); 179 | }); 180 | 181 | const javascript = series(clean, function(cb) { 182 | // body omitted 183 | cb(); 184 | }); 185 | 186 | exports.build = parallel(css, javascript); 187 | ``` 188 | 189 | Migrate to this: 190 | 191 | ```js 192 | const { series, parallel } = require('gulp'); 193 | 194 | function clean(cb) { 195 | // body omitted 196 | cb(); 197 | } 198 | 199 | function css(cb) { 200 | // body omitted 201 | cb(); 202 | } 203 | 204 | function javascript(cb) { 205 | // body omitted 206 | cb(); 207 | } 208 | 209 | exports.build = series(clean, parallel(css, javascript)); 210 | ``` 211 | 212 | [async-completion-docs]: ../getting-started/4-async-completion.md 213 | [using-async-await-docs]: ../getting-started/4-async-completion.md#using-async-await 214 | [img-gulp-tasks-command]: https://gulpjs.com/img/docs-gulp-tasks-command.png 215 | [async-once]: https://github.com/gulpjs/async-once 216 | -------------------------------------------------------------------------------- /docs/getting-started/4-async-completion.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # Async Completion 9 | 10 | Node libraries handle asynchronicity in a variety of ways. The most common pattern is [error-first callbacks][node-api-error-first-callbacks], but you might also encounter [streams][stream-docs], [promises][promise-docs], [event emitters][event-emitter-docs], [child processes][child-process-docs], or [observables][observable-docs]. Gulp tasks normalize all these types of asynchronicity. 11 | 12 | ## Signal task completion 13 | 14 | When a stream, promise, event emitter, child process, or observable is returned from a task, the success or error informs gulp whether to continue or end. If a task errors, gulp will end immediately and show that error. 15 | 16 | When composing tasks with `series()`, an error will end the composition and no further tasks will be executed. When composing tasks with `parallel()`, an error will end the composition but the other parallel tasks may or may not complete. 17 | 18 | ### Returning a stream 19 | 20 | ```js 21 | const { src, dest } = require('gulp'); 22 | 23 | function streamTask() { 24 | return src('*.js') 25 | .pipe(dest('output')); 26 | } 27 | 28 | exports.default = streamTask; 29 | ``` 30 | 31 | ### Returning a promise 32 | 33 | ```js 34 | function promiseTask() { 35 | return Promise.resolve('the value is ignored'); 36 | } 37 | 38 | exports.default = promiseTask; 39 | ``` 40 | 41 | ### Returning an event emitter 42 | 43 | ```js 44 | const { EventEmitter } = require('events'); 45 | 46 | function eventEmitterTask() { 47 | const emitter = new EventEmitter(); 48 | // Emit has to happen async otherwise gulp isn't listening yet 49 | setTimeout(() => emitter.emit('finish'), 250); 50 | return emitter; 51 | } 52 | 53 | exports.default = eventEmitterTask; 54 | ``` 55 | 56 | ### Returning a child process 57 | 58 | ```js 59 | const { exec } = require('child_process'); 60 | 61 | function childProcessTask() { 62 | return exec('date'); 63 | } 64 | 65 | exports.default = childProcessTask; 66 | ``` 67 | 68 | ### Returning an observable 69 | 70 | ```js 71 | const { of } = require('rxjs'); 72 | 73 | function observableTask() { 74 | return of(1, 2, 3); 75 | } 76 | 77 | exports.default = observableTask; 78 | ``` 79 | 80 | ### Using an error-first callback 81 | 82 | If nothing is returned from your task, you must use the error-first callback to signal completion. The callback will be passed to your task as the only argument - named `cb()` in the examples below. 83 | 84 | ```js 85 | function callbackTask(cb) { 86 | // `cb()` should be called by some async work 87 | cb(); 88 | } 89 | 90 | exports.default = callbackTask; 91 | ``` 92 | 93 | To indicate to gulp that an error occurred in a task using an error-first callback, call it with an `Error` as the only argument. 94 | 95 | ```js 96 | function callbackError(cb) { 97 | // `cb()` should be called by some async work 98 | cb(new Error('kaboom')); 99 | } 100 | 101 | exports.default = callbackError; 102 | ``` 103 | 104 | However, you'll often pass this callback to another API instead of calling it yourself. 105 | 106 | ```js 107 | const fs = require('fs'); 108 | 109 | function passingCallback(cb) { 110 | fs.access('gulpfile.js', cb); 111 | } 112 | 113 | exports.default = passingCallback; 114 | ``` 115 | 116 | ## No synchronous tasks 117 | 118 | Synchronous tasks are no longer supported. They often led to subtle mistakes that were hard to debug, like forgetting to return your streams from a task. 119 | 120 | When you see the _"Did you forget to signal async completion?"_ warning, none of the techniques mentioned above were used. You'll need to use the error-first callback or return a stream, promise, event emitter, child process, or observable to resolve the issue. 121 | 122 | ## Using async/await 123 | 124 | When not using any of the previous options, you can define your task as an [`async` function][async-await-docs], which wraps your task in a promise. This allows you to work with promises synchronously using `await` and use other synchronous code. 125 | 126 | ```js 127 | const fs = require('fs'); 128 | 129 | async function asyncAwaitTask() { 130 | const { version } = JSON.parse(fs.readFileSync('package.json', 'utf8')); 131 | console.log(version); 132 | await Promise.resolve('some result'); 133 | } 134 | 135 | exports.default = asyncAwaitTask; 136 | ``` 137 | 138 | [node-api-error-first-callbacks]: https://nodejs.org/api/errors.html#errors_error_first_callbacks 139 | [stream-docs]: https://nodejs.org/api/stream.html#stream_stream 140 | [promise-docs]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises 141 | [event-emitter-docs]: https://nodejs.org/api/events.html#events_events 142 | [child-process-docs]: https://nodejs.org/api/child_process.html#child_process_child_process 143 | [observable-docs]: https://github.com/tc39/proposal-observable/blob/master/README.md 144 | [async-await-docs]: https://developers.google.com/web/fundamentals/primers/async-functions 145 | -------------------------------------------------------------------------------- /docs/getting-started/5-working-with-files.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # Working with Files 9 | 10 | The `src()` and `dest()` methods are exposed by gulp to interact with files on your computer. 11 | 12 | `src()` is given a [glob][explaining-globs-docs] to read from the file system and produces a [Node stream][node-streams-docs]. It locates all matching files and reads them into memory to pass through the stream. 13 | 14 | The stream produced by `src()` should be returned from a task to signal async completion, as mentioned in [Creating Tasks][creating-tasks-docs]. 15 | 16 | ```js 17 | const { src, dest } = require('gulp'); 18 | 19 | exports.default = function() { 20 | return src('src/*.js') 21 | .pipe(dest('output/')); 22 | } 23 | ``` 24 | 25 | The main API of a stream is the `.pipe()` method for chaining Transform or Writable streams. 26 | 27 | ```js 28 | const { src, dest } = require('gulp'); 29 | const babel = require('gulp-babel'); 30 | 31 | exports.default = function() { 32 | return src('src/*.js') 33 | .pipe(babel()) 34 | .pipe(dest('output/')); 35 | } 36 | ``` 37 | 38 | `dest()` is given an output directory string and also produces a [Node stream][node-streams-docs] which is generally used as a terminator stream. When it receives a file passed through the pipeline, it writes the contents and other details out to the filesystem at a given directory. The `symlink()` method is also available and operates like `dest()`, but creates links instead of files (see [`symlink()`][symlink-api-docs] for details). 39 | 40 | Most often plugins will be placed between `src()` and `dest()` using the `.pipe()` method and will transform the files within the stream. 41 | 42 | ## Adding files to the stream 43 | 44 | `src()` can also be placed in the middle of a pipeline to add files to the stream based on the given globs. The additional files will only be available to transformations later in the stream. If [globs overlap][overlapping-globs-docs], the files will be added again. 45 | 46 | This can be useful for transpiling some files before adding plain JavaScript files to the pipeline and uglifying everything. 47 | 48 | ```js 49 | const { src, dest } = require('gulp'); 50 | const babel = require('gulp-babel'); 51 | const uglify = require('gulp-uglify'); 52 | 53 | exports.default = function() { 54 | return src('src/*.js') 55 | .pipe(babel()) 56 | .pipe(src('vendor/*.js')) 57 | .pipe(uglify()) 58 | .pipe(dest('output/')); 59 | } 60 | ``` 61 | 62 | ## Output in phases 63 | 64 | `dest()` can be used in the middle of a pipeline to write intermediate states to the filesystem. When a file is received, the current state is written out to the filesystem, the path is updated to represent the new location of the output file, then that file continues down the pipeline. 65 | 66 | This feature can be useful to create unminified and minified files with the same pipeline. 67 | 68 | ```js 69 | const { src, dest } = require('gulp'); 70 | const babel = require('gulp-babel'); 71 | const uglify = require('gulp-uglify'); 72 | const rename = require('gulp-rename'); 73 | 74 | exports.default = function() { 75 | return src('src/*.js') 76 | .pipe(babel()) 77 | .pipe(src('vendor/*.js')) 78 | .pipe(dest('output/')) 79 | .pipe(uglify()) 80 | .pipe(rename({ extname: '.min.js' })) 81 | .pipe(dest('output/')); 82 | } 83 | ``` 84 | 85 | ## Modes: streaming, buffered, and empty 86 | 87 | `src()` can operate in three modes: buffering, streaming, and empty. These are configured with the `buffer` and `read` [options][src-options-api-docs] on `src()`. 88 | 89 | * Buffering mode is the default and loads the file contents into memory. Plugins usually operate in buffering mode and many don't support streaming mode. 90 | * Streaming mode exists mainly to operate on large files that can't fit in memory, like giant images or movies. The contents are streamed from the filesystem in small chunks instead of loaded all at once. If you need to use streaming mode, look for a plugin that supports it or write your own. 91 | * Empty mode contains no contents and is useful when only working with file metadata. 92 | 93 | [explaining-globs-docs]: ../getting-started/6-explaining-globs.md 94 | [creating-tasks-docs]: ../getting-started/3-creating-tasks.md 95 | [overlapping-globs-docs]: ../getting-started/6-explaining-globs.md#overlapping-globs 96 | [node-streams-docs]: https://nodejs.org/api/stream.html 97 | [symlink-api-docs]: ../api/symlink.md 98 | [src-options-api-docs]: ../api/src.md#options 99 | -------------------------------------------------------------------------------- /docs/getting-started/6-explaining-globs.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # Explaining Globs 9 | 10 | A glob is a string of literal and/or wildcard characters used to match filepaths. Globbing is the act of locating files on a filesystem using one or more globs. 11 | 12 | The `src()` method expects a single glob string or an array of globs to determine which files your pipeline will operate on. At least one match must be found for your glob(s) otherwise `src()` will error. When an array of globs is used, any negative globs will remove matches from any positive glob. 13 | 14 | ## Segments and separators 15 | 16 | A segment is everything between separators. The separator in a glob is always the `/` character - regardless of the operating system - even in Windows where the path separator is `\\`. In a glob, `\\` is reserved as the escape character. 17 | 18 | Here, the * is escaped, so it is treated as a literal instead of a wildcard character. 19 | ```js 20 | 'glob_with_uncommon_\\*_character.js' 21 | ``` 22 | 23 | Avoid using Node's `path` methods, like `path.join`, to create globs. On Windows, it produces an invalid glob because Node uses `\\` as the separator. Also avoid the `__dirname` global, `__filename` global, or `process.cwd()` for the same reasons. 24 | 25 | ```js 26 | const invalidGlob = path.join(__dirname, 'src/*.js'); 27 | ``` 28 | 29 | ## Special character: * (single-star) 30 | 31 | Matches any amount - including none - of characters within a single segment. Useful for globbing files within one directory. 32 | 33 | This glob will match files like `index.js`, but not files like `scripts/index.js` or `scripts/nested/index.js` 34 | ```js 35 | '*.js' 36 | ``` 37 | 38 | ## Special character: ** (double-star) 39 | 40 | Matches any amount - including none - of characters across segments. Useful for globbing files in nested directories. Make sure to appropriately restrict your double-star globs, to avoid matching large directories unnecessarily. 41 | 42 | Here, the glob is appropriately restricted to the `scripts/` directory. It will match files like `scripts/index.js`, `scripts/nested/index.js`, and `scripts/nested/twice/index.js`. 43 | 44 | ```js 45 | 'scripts/**/*.js' 46 | ``` 47 | 48 | In the previous example, if `scripts/` wasn't prefixed, all dependencies in `node_modules` or other directories would also be matched. 49 | 50 | ## Special character: ! (negative) 51 | 52 | Globs prefixed with the `!` character will "negate" the glob, excluding the match completely. All negative globs are applied to every positive glob, which is a departure from gulp versions before v5. 53 | 54 | Here, the `scripts/` directory will be traversed for all files ending in `.js`, but all files from the `scripts/vendor/` directory will be excluded. 55 | 56 | ```js 57 | ['scripts/**/*.js', '!scripts/vendor/**'] 58 | ``` 59 | 60 | Negative globs can be used as an alternative for restricting double-star globs. 61 | 62 | ```js 63 | ['**/*.js', '!node_modules/**'] 64 | ``` 65 | 66 | ## Ordered globs 67 | 68 | Versions of gulp before v5 allowed "ordered globs"; however, that has been removed to align with most globbing libraries in the ecosystem. 69 | 70 | If you need the "ordered glob" functionality, you can use the [ordered-read-streams][ordered-read-streams-docs] library to combine streams: 71 | 72 | ```js 73 | const order = require("ordered-read-streams"); 74 | 75 | exports.default = function () { 76 | return order([ 77 | gulp.src("input/jquery/dist/jquery.js"), 78 | gulp.src("input/detect_swipe/jquery.detect_swipe.js"), 79 | ]).pipe(gulp.dest('output/')); 80 | } 81 | ``` 82 | 83 | ## Overlapping globs 84 | 85 | Two or more globs that (un)intentionally match the same file are considered overlapping. When overlapping globs are used within a single `src()`, gulp does its best to remove the duplicates, but doesn't attempt to deduplicate across separate `src()` calls. 86 | 87 | ## Advanced resources 88 | 89 | Most of what you'll need to work with globs in gulp is covered here. If you'd like to get more in depth, here are a few resources. 90 | 91 | * [Micromatch Documentation][micromatch-docs] 92 | * [node-glob's Glob Primer][glob-primer-docs] 93 | * [Begin's Globbing Documentation][begin-globbing-docs] 94 | * [Wikipedia's Glob Page][wikipedia-glob] 95 | 96 | [micromatch-docs]: https://github.com/micromatch/micromatch 97 | [glob-primer-docs]: https://github.com/isaacs/node-glob#glob-primer 98 | [begin-globbing-docs]: https://github.com/begin/globbing#what-is-globbing 99 | [wikipedia-glob]: https://en.wikipedia.org/wiki/Glob_(programming) 100 | [ordered-read-streams-docs]: https://github.com/gulpjs/ordered-read-streams#orderedstreams-options 101 | -------------------------------------------------------------------------------- /docs/getting-started/7-using-plugins.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # Using Plugins 9 | 10 | Gulp plugins are [Node Transform Streams][through2-docs] that encapsulate common behavior to transform files in a pipeline - often placed between `src()` and `dest()` using the `.pipe()` method. They can change the filename, metadata, or contents of every file that passes through the stream. 11 | 12 | Plugins from npm - using the "gulpplugin" and "gulpfriendly" keywords - can be browsed and searched on the [plugin search page][gulp-plugin-site]. 13 | 14 | Each plugin should only do a small amount of work, so you can connect them like building blocks. You may need to combine a bunch of them to get the desired result. 15 | 16 | ```js 17 | const { src, dest } = require('gulp'); 18 | const uglify = require('gulp-uglify'); 19 | const rename = require('gulp-rename'); 20 | 21 | exports.default = function() { 22 | return src('src/*.js') 23 | // The gulp-uglify plugin won't update the filename 24 | .pipe(uglify()) 25 | // So use gulp-rename to change the extension 26 | .pipe(rename({ extname: '.min.js' })) 27 | .pipe(dest('output/')); 28 | } 29 | ``` 30 | 31 | ## Do you need a plugin? 32 | 33 | Not everything in gulp should use plugins. They are a quick way to get started, but many operations are improved by using a module or library instead. 34 | 35 | ```js 36 | const { rollup } = require('rollup'); 37 | 38 | // Rollup's promise API works great in an `async` task 39 | exports.default = async function() { 40 | const bundle = await rollup({ 41 | input: 'src/index.js' 42 | }); 43 | 44 | return bundle.write({ 45 | file: 'output/bundle.js', 46 | format: 'iife' 47 | }); 48 | } 49 | ``` 50 | 51 | Plugins should always transform files. Use a (non-plugin) Node module or library for any other operations. 52 | 53 | ```js 54 | const del = require('delete'); 55 | 56 | exports.default = function(cb) { 57 | // Use the `delete` module directly, instead of using gulp-rimraf 58 | del(['output/*.js'], cb); 59 | } 60 | ``` 61 | 62 | ## Conditional plugins 63 | 64 | Since plugin operations shouldn't be file-type-aware, you may need a plugin like [gulp-if][gulp-if-package] to transform subsets of files. 65 | 66 | ```js 67 | const { src, dest } = require('gulp'); 68 | const gulpif = require('gulp-if'); 69 | const uglify = require('gulp-uglify'); 70 | 71 | function isJavaScript(file) { 72 | // Check if file extension is '.js' 73 | return file.extname === '.js'; 74 | } 75 | 76 | exports.default = function() { 77 | // Include JavaScript and CSS files in a single pipeline 78 | return src(['src/*.js', 'src/*.css']) 79 | // Only apply gulp-uglify plugin to JavaScript files 80 | .pipe(gulpif(isJavaScript, uglify())) 81 | .pipe(dest('output/')); 82 | } 83 | ``` 84 | 85 | ## Inline plugins 86 | 87 | Inline plugins are one-off Transform Streams you define inside your gulpfile by writing the desired behavior. 88 | 89 | There are two situations where creating an inline plugin is helpful: 90 | * Instead of creating and maintaining your own plugin. 91 | * Instead of forking a plugin that exists to add a feature you want. 92 | 93 | ```js 94 | const { src, dest } = require('gulp'); 95 | const uglify = require('uglify-js'); 96 | const through2 = require('through2'); 97 | 98 | exports.default = function() { 99 | return src('src/*.js') 100 | // Instead of using gulp-uglify, you can create an inline plugin 101 | .pipe(through2.obj(function(file, _, cb) { 102 | if (file.isBuffer()) { 103 | const code = uglify.minify(file.contents.toString()) 104 | file.contents = Buffer.from(code.code) 105 | } 106 | cb(null, file); 107 | })) 108 | .pipe(dest('output/')); 109 | } 110 | ``` 111 | 112 | [gulp-plugin-site]: https://gulpjs.com/plugins/ 113 | [through2-docs]: https://github.com/rvagg/through2 114 | [gulp-if-package]: https://www.npmjs.com/package/gulp-if 115 | -------------------------------------------------------------------------------- /docs/getting-started/8-watching-files.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # Watching Files 9 | 10 | The `watch()` API connects [globs][globs-docs] to [tasks][creating-tasks-docs] using a file system watcher. It watches for changes to files that match the globs and executes the task when a change occurs. If the task doesn't signal [Async Completion][async-completion-doc], it will never be run a second time. 11 | 12 | This API provides built-in delay and queueing based on most-common-use defaults. 13 | 14 | ```js 15 | const { watch, series } = require('gulp'); 16 | 17 | function clean(cb) { 18 | // body omitted 19 | cb(); 20 | } 21 | 22 | function javascript(cb) { 23 | // body omitted 24 | cb(); 25 | } 26 | 27 | function css(cb) { 28 | // body omitted 29 | cb(); 30 | } 31 | 32 | exports.default = function() { 33 | // You can use a single task 34 | watch('src/*.css', css); 35 | // Or a composed task 36 | watch('src/*.js', series(clean, javascript)); 37 | }; 38 | ``` 39 | 40 | ## Warning: avoid synchronous 41 | 42 | A watcher's task cannot be synchronous, like tasks registered into the task system. If you pass a sync task, the completion can't be determined and the task won't run again - it is assumed to still be running. 43 | 44 | There is no error or warning message provided because the file watcher keeps your Node process running. Since the process doesn't exit, it cannot be determined whether the task is done or just taking a really, really long time to run. 45 | 46 | ## Watched events 47 | 48 | By default, the watcher executes tasks whenever a file is created, changed, or deleted. 49 | If you need to use different events, you can use the `events` option when calling `watch()`. The available events are `'add'`, `'addDir'`, `'change'`, `'unlink'`, `'unlinkDir'`, `'ready'`, `'error'`. Additionally `'all'` is available, which represents all events other than `'ready'` and `'error'`. 50 | 51 | ```js 52 | const { watch } = require('gulp'); 53 | 54 | exports.default = function() { 55 | // All events will be watched 56 | watch('src/*.js', { events: 'all' }, function(cb) { 57 | // body omitted 58 | cb(); 59 | }); 60 | }; 61 | ``` 62 | 63 | ## Initial execution 64 | 65 | Upon calling `watch()`, the tasks won't be executed, instead they'll wait for the first file change. 66 | 67 | To execute tasks before the first file change, set the `ignoreInitial` option to `false`. 68 | 69 | ```js 70 | const { watch } = require('gulp'); 71 | 72 | exports.default = function() { 73 | // The task will be executed upon startup 74 | watch('src/*.js', { ignoreInitial: false }, function(cb) { 75 | // body omitted 76 | cb(); 77 | }); 78 | }; 79 | ``` 80 | 81 | ## Queueing 82 | 83 | Each `watch()` guarantees that its currently running task won't execute again concurrently. When a file change is made while a watcher task is running, another execution will queue up to run when the task finishes. Only one run can be queued up at a time. 84 | 85 | To disable queueing, set the `queue` option to `false`. 86 | 87 | ```js 88 | const { watch } = require('gulp'); 89 | 90 | exports.default = function() { 91 | // The task will be run (concurrently) for every change made 92 | watch('src/*.js', { queue: false }, function(cb) { 93 | // body omitted 94 | cb(); 95 | }); 96 | }; 97 | ``` 98 | 99 | ## Delay 100 | 101 | Upon file change, a watcher task won't run until a 200ms delay has elapsed. This is to avoid starting a task too early when many files are being changed at once - like find-and-replace. 102 | 103 | To adjust the delay duration, set the `delay` option to a positive integer. 104 | 105 | ```js 106 | const { watch } = require('gulp'); 107 | 108 | exports.default = function() { 109 | // The task won't be run until 500ms have elapsed since the first change 110 | watch('src/*.js', { delay: 500 }, function(cb) { 111 | // body omitted 112 | cb(); 113 | }); 114 | }; 115 | ``` 116 | 117 | ## Using the watcher instance 118 | 119 | You likely won't use this feature, but if you need full control over changed files - like access to paths or metadata - use the [chokidar][chokidar-module-package] instance returned from `watch()`. 120 | 121 | __Be careful:__ The returned chokidar instance doesn't have queueing, delay, or async completion features. 122 | 123 | ## Optional dependency 124 | 125 | Gulp has an optional dependency called [fsevents][fsevents-package], which is a Mac-specific file watcher. If you see an installation warning for fsevents - _"npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents"_ - it is not an issue. 126 | If fsevents installation is skipped, a fallback watcher will be used and any errors occurring in your gulpfile aren't related to this warning. 127 | 128 | [globs-docs]: ../getting-started/6-explaining-globs.md 129 | [creating-tasks-docs]: ../getting-started/3-creating-tasks.md 130 | [async-completion-doc]: ../getting-started/4-async-completion.md 131 | [chokidar-module-package]: https://www.npmjs.com/package/chokidar 132 | [fsevents-package]: https://www.npmjs.com/package/fsevents 133 | -------------------------------------------------------------------------------- /docs/getting-started/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | 1. [Quick Start](1-quick-start.md) 4 | 2. [JavaScript and Gulpfiles](2-javascript-and-gulpfiles.md) 5 | 3. [Creating Tasks](3-creating-tasks.md) 6 | 4. [Async Completion](4-async-completion.md) 7 | 5. [Working with Files](5-working-with-files.md) 8 | 6. [Explaining Globs](6-explaining-globs.md) 9 | 7. [Using Plugins](7-using-plugins.md) 10 | 8. [Watching Files](8-watching-files.md) 11 | -------------------------------------------------------------------------------- /docs/recipes/README.md: -------------------------------------------------------------------------------- 1 | # Recipes 2 | 3 | * [Automate release workflow](automate-releases.md) 4 | * [Combining streams to handle errors](combining-streams-to-handle-errors.md) 5 | * [Delete files and folders](delete-files-folder.md) 6 | * [Fast browserify builds with watchify](fast-browserify-builds-with-watchify.md) 7 | * [Incremental rebuilding, including operating on full file sets](incremental-builds-with-concatenate.md) 8 | * [Make stream from buffer (memory contents)](make-stream-from-buffer.md) 9 | * [Mocha test-runner with gulp](mocha-test-runner-with-gulp.md) 10 | * [Pass parameters from the command line](pass-arguments-from-cli.md) 11 | * [Generating a file per folder](running-task-steps-per-folder.md) 12 | * [Server with live-reloading and CSS injection](server-with-livereload-and-css-injection.md) 13 | * [Sharing streams with stream factories](sharing-streams-with-stream-factories.md) 14 | * [Using multiple sources in one task](using-multiple-sources-in-one-task.md) 15 | * [Browserify + Uglify with sourcemaps](browserify-uglify-sourcemap.md) 16 | * [Browserify + Globs](browserify-with-globs.md) 17 | * [Browserify + Globs (multiple destination)](browserify-multiple-destination.md) 18 | * [Output both a minified and non-minified version](minified-and-non-minified.md) 19 | * [Templating with Swig and YAML front-matter](templating-with-swig-and-yaml-front-matter.md) 20 | * [Run Grunt Tasks from Gulp](run-grunt-tasks-from-gulp.md) 21 | * [Rollup with rollup-stream](rollup-with-rollup-stream.md) 22 | * [Run gulp task via cron job](cron-task.md) 23 | -------------------------------------------------------------------------------- /docs/recipes/automate-releases.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # Automate Releases 9 | 10 | If your project follows a semantic versioning, it may be a good idea to automatize the steps needed to do a release. 11 | The recipe below bumps the project version, commits the changes to git and creates a new GitHub release. 12 | 13 | For publishing a GitHub release you'll need to [create a personal access token](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/creating-a-personal-access-token) and add it to your project. However, we don't want to commit it, so we'll use [`dotenv`](https://www.npmjs.com/package/dotenv) to load it from a git-ignored `.env` file: 14 | 15 | ``` 16 | GH_TOKEN=ff34885... 17 | ``` 18 | 19 | Don't forget to add `.env` to your `.gitignore`. 20 | 21 | Next, install all the necessary dependencies for this recipe: 22 | 23 | ```sh 24 | npm install --save-dev conventional-recommended-bump conventional-changelog-cli conventional-github-releaser dotenv execa 25 | ``` 26 | 27 | Based on your environment, setup and preferences, your release workflow might look something like this: 28 | 29 | ``` js 30 | const gulp = require('gulp'); 31 | const conventionalRecommendedBump = require('conventional-recommended-bump'); 32 | const conventionalGithubReleaser = require('conventional-github-releaser'); 33 | const execa = require('execa'); 34 | const fs = require('fs'); 35 | const { promisify } = require('util'); 36 | const dotenv = require('dotenv'); 37 | 38 | // load environment variables 39 | const result = dotenv.config(); 40 | 41 | if (result.error) { 42 | throw result.error; 43 | } 44 | 45 | // Conventional Changelog preset 46 | const preset = 'angular'; 47 | // print output of commands into the terminal 48 | const stdio = 'inherit'; 49 | 50 | async function bumpVersion() { 51 | // get recommended version bump based on commits 52 | const { releaseType } = await promisify(conventionalRecommendedBump)({ preset }); 53 | // bump version without committing and tagging 54 | await execa('npm', ['version', releaseType, '--no-git-tag-version'], { 55 | stdio, 56 | }); 57 | } 58 | 59 | async function changelog() { 60 | await execa( 61 | 'npx', 62 | [ 63 | 'conventional-changelog', 64 | '--preset', 65 | preset, 66 | '--infile', 67 | 'CHANGELOG.md', 68 | '--same-file', 69 | ], 70 | { stdio } 71 | ); 72 | } 73 | 74 | async function commitTagPush() { 75 | // even though we could get away with "require" in this case, we're taking the safe route 76 | // because "require" caches the value, so if we happen to use "require" again somewhere else 77 | // we wouldn't get the current value, but the value of the last time we called "require" 78 | const { version } = JSON.parse(await promisify(fs.readFile)('package.json')); 79 | const commitMsg = `chore: release ${version}`; 80 | await execa('git', ['add', '.'], { stdio }); 81 | await execa('git', ['commit', '--message', commitMsg], { stdio }); 82 | await execa('git', ['tag', `v${version}`], { stdio }); 83 | await execa('git', ['push', '--follow-tags'], { stdio }); 84 | } 85 | 86 | function githubRelease(done) { 87 | conventionalGithubReleaser( 88 | { type: 'oauth', token: process.env.GH_TOKEN }, 89 | { preset }, 90 | done 91 | ); 92 | } 93 | 94 | exports.release = gulp.series( 95 | bumpVersion, 96 | changelog, 97 | commitTagPush, 98 | githubRelease 99 | ); 100 | ``` 101 | -------------------------------------------------------------------------------- /docs/recipes/browserify-multiple-destination.md: -------------------------------------------------------------------------------- 1 | # Browserify + Globs (multiple destination) 2 | 3 | This example shows how to set up a task of bundling multiple entry points into multiple destinations using browserify. 4 | 5 | The below `js` task bundles all the `.js` files under `src/` as entry points and writes the results under `dest/`. 6 | 7 | 8 | ```js 9 | var gulp = require('gulp'); 10 | var browserify = require('browserify'); 11 | var log = require('gulplog'); 12 | var tap = require('gulp-tap'); 13 | var buffer = require('gulp-buffer'); 14 | var sourcemaps = require('gulp-sourcemaps'); 15 | var uglify = require('gulp-uglify'); 16 | 17 | gulp.task('js', function () { 18 | 19 | return gulp.src('src/**/*.js', {read: false}) // no need of reading file because browserify does. 20 | 21 | // transform file objects using gulp-tap plugin 22 | .pipe(tap(function (file) { 23 | 24 | log.info('bundling ' + file.path); 25 | 26 | // replace file contents with browserify's bundle stream 27 | file.contents = browserify(file.path, {debug: true}).bundle(); 28 | 29 | })) 30 | 31 | // transform streaming contents into buffer contents (because gulp-sourcemaps does not support streaming contents) 32 | .pipe(buffer()) 33 | 34 | // load and init sourcemaps 35 | .pipe(sourcemaps.init({loadMaps: true})) 36 | 37 | .pipe(uglify()) 38 | 39 | // write sourcemaps 40 | .pipe(sourcemaps.write('./')) 41 | 42 | .pipe(gulp.dest('dest')); 43 | 44 | }); 45 | ``` 46 | -------------------------------------------------------------------------------- /docs/recipes/browserify-transforms.md: -------------------------------------------------------------------------------- 1 | # Browserify + Transforms 2 | 3 | [Browserify](https://github.com/browserify/browserify) has become an important and indispensable 4 | tool but requires being wrapped before working well with gulp. Below is a simple recipe for using 5 | Browserify with transforms. 6 | 7 | See also: the [Combining Streams to Handle Errors](https://github.com/gulpjs/gulp/blob/master/docs/recipes/combining-streams-to-handle-errors.md) recipe for handling errors with browserify or uglify in your stream. 8 | 9 | ``` javascript 10 | 'use strict'; 11 | 12 | var browserify = require('browserify'); 13 | var gulp = require('gulp'); 14 | var source = require('vinyl-source-stream'); 15 | var buffer = require('vinyl-buffer'); 16 | var log = require('gulplog'); 17 | var uglify = require('gulp-uglify'); 18 | var reactify = require('reactify'); 19 | 20 | gulp.task('javascript', function () { 21 | // set up the browserify instance on a task basis 22 | var b = browserify({ 23 | entries: './entry.js', 24 | debug: true, 25 | // defining transforms here will avoid crashing your stream 26 | transform: [reactify] 27 | }); 28 | 29 | return b.bundle() 30 | .pipe(source('app.js', { sourcemaps: true })) 31 | .pipe(buffer()) 32 | // Add transformation tasks to the pipeline here. 33 | .pipe(uglify()) 34 | .on('error', log.error) 35 | .pipe(gulp.dest('./dist/js/', { sourcemaps: '../sourcemaps/' })); 36 | }); 37 | ``` 38 | -------------------------------------------------------------------------------- /docs/recipes/browserify-uglify-sourcemap.md: -------------------------------------------------------------------------------- 1 | # Browserify + Uglify2 with sourcemaps 2 | 3 | [Browserify](https://github.com/browserify/browserify) has become an important and indispensable 4 | tool but requires being wrapped before working well with gulp. Below is a simple recipe for using 5 | Browserify with full sourcemaps that resolve to the original individual files. 6 | 7 | See also: the [Combining Streams to Handle Errors](https://github.com/gulpjs/gulp/blob/master/docs/recipes/combining-streams-to-handle-errors.md) recipe for handling errors with browserify or uglify in your stream. 8 | 9 | A simple `gulpfile.js` file for Browserify + Uglify2 with sourcemaps: 10 | 11 | ``` javascript 12 | 'use strict'; 13 | 14 | var browserify = require('browserify'); 15 | var gulp = require('gulp'); 16 | var source = require('vinyl-source-stream'); 17 | var buffer = require('vinyl-buffer'); 18 | var uglify = require('gulp-uglify'); 19 | var sourcemaps = require('gulp-sourcemaps'); 20 | var log = require('gulplog'); 21 | 22 | gulp.task('javascript', function () { 23 | // set up the browserify instance on a task basis 24 | var b = browserify({ 25 | entries: './entry.js', 26 | debug: true 27 | }); 28 | 29 | return b.bundle() 30 | .pipe(source('app.js')) 31 | .pipe(buffer()) 32 | .pipe(sourcemaps.init({loadMaps: true})) 33 | // Add transformation tasks to the pipeline here. 34 | .pipe(uglify()) 35 | .on('error', log.error) 36 | .pipe(sourcemaps.write('./')) 37 | .pipe(gulp.dest('./dist/js/')); 38 | }); 39 | ``` 40 | -------------------------------------------------------------------------------- /docs/recipes/browserify-with-globs.md: -------------------------------------------------------------------------------- 1 | # Browserify + Globs 2 | 3 | [Browserify + Uglify2](https://github.com/gulpjs/gulp/blob/master/docs/recipes/browserify-uglify-sourcemap.md) shows how to setup a basic gulp task to bundle a JavaScript file with its dependencies, and minify the bundle with UglifyJS while preserving source maps. 4 | It does not, however, show how one may use gulp and Browserify with multiple entry files. 5 | 6 | See also: the [Combining Streams to Handle Errors](https://github.com/gulpjs/gulp/blob/master/docs/recipes/combining-streams-to-handle-errors.md) recipe for handling errors with Browserify or UglifyJS in your stream. 7 | 8 | ``` javascript 9 | 'use strict'; 10 | 11 | var browserify = require('browserify'); 12 | var gulp = require('gulp'); 13 | var source = require('vinyl-source-stream'); 14 | var buffer = require('vinyl-buffer'); 15 | var globby = require('globby'); 16 | var through = require('through2'); 17 | var log = require('gulplog'); 18 | var uglify = require('gulp-uglify'); 19 | var sourcemaps = require('gulp-sourcemaps'); 20 | var reactify = require('reactify'); 21 | 22 | gulp.task('javascript', function () { 23 | // gulp expects tasks to return a stream, so we create one here. 24 | var bundledStream = through(); 25 | 26 | bundledStream 27 | // turns the output bundle stream into a stream containing 28 | // the normal attributes gulp plugins expect. 29 | .pipe(source('app.js')) 30 | // the rest of the gulp task, as you would normally write it. 31 | // here we're copying from the Browserify + Uglify2 recipe. 32 | .pipe(buffer()) 33 | .pipe(sourcemaps.init({loadMaps: true})) 34 | // Add gulp plugins to the pipeline here. 35 | .pipe(uglify()) 36 | .on('error', log.error) 37 | .pipe(sourcemaps.write('./')) 38 | .pipe(gulp.dest('./dist/js/')); 39 | 40 | // "globby" replaces the normal "gulp.src" as Browserify 41 | // creates it's own readable stream. 42 | globby(['./entries/*.js']).then(function(entries) { 43 | // create the Browserify instance. 44 | var b = browserify({ 45 | entries: entries, 46 | debug: true, 47 | transform: [reactify] 48 | }); 49 | 50 | // pipe the Browserify stream into the stream we created earlier 51 | // this starts our gulp pipeline. 52 | b.bundle().pipe(bundledStream); 53 | }).catch(function(err) { 54 | // ensure any errors from globby are handled 55 | bundledStream.emit('error', err); 56 | }); 57 | 58 | // finally, we return the stream, so gulp knows when this task is done. 59 | return bundledStream; 60 | }); 61 | ``` 62 | -------------------------------------------------------------------------------- /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 unless it already has a listener attached to the `error` event. This gets a bit tricky when you're working with longer pipelines of streams. 4 | 5 | By using [stream-combiner2](https://github.com/substack/stream-combiner2) you can turn a series of streams into a single stream, meaning you only need to listen to the `error` event in one place in your code. 6 | 7 | Here's an example of using it in a gulpfile: 8 | 9 | ```js 10 | var combiner = require('stream-combiner2'); 11 | var uglify = require('gulp-uglify'); 12 | var gulp = require('gulp'); 13 | 14 | gulp.task('test', function() { 15 | return combiner.obj([ 16 | gulp.src('bootstrap/js/*.js'), 17 | uglify(), 18 | gulp.dest('public/bootstrap') 19 | ]) 20 | // any errors in the above streams will get caught 21 | // by this listener, instead of being thrown: 22 | .on('error', console.error.bind(console)); 23 | }); 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/recipes/cron-task.md: -------------------------------------------------------------------------------- 1 | # Run gulp task via cron job 2 | 3 | While logged in via a user that has privileges to run `gulp`, run the following: 4 | 5 | crontab -e 6 | 7 | to edit your current "[crontab](https://en.wikipedia.org/wiki/Cron)" file. 8 | 9 | Typically, within a cron job, you want to run any binary using absolute paths, 10 | so an initial approach to running `gulp build` every minute might look like: 11 | 12 | * * * * * cd /your/dir/to/run/in && /usr/local/bin/gulp build 13 | 14 | However, you might see in the cron logs that you get this error: 15 | 16 | > `/usr/bin/env: node: No such file or directory` 17 | 18 | To fix this, we need to add a [symbolic link](https://en.wikipedia.org/wiki/Ln_\(Unix\)) 19 | within `/usr/bin` to point to the actual path of our node binary. 20 | 21 | Be sure you are logged in as a **sudo** user, and paste in the following command to your terminal: 22 | 23 | sudo ln -s $(which node) /usr/bin/node 24 | 25 | Once this link is established, your cron task should run successfully. 26 | -------------------------------------------------------------------------------- /docs/recipes/delete-files-folder.md: -------------------------------------------------------------------------------- 1 | # Delete files and folders 2 | 3 | You might want to delete some files before running your build. Since deleting files doesn't work on the file contents, there's no reason to use a gulp plugin. An excellent opportunity to use a vanilla node module. 4 | 5 | Let's use the [`del`](https://github.com/sindresorhus/del) module for this example as it supports multiple files and [globbing](https://github.com/sindresorhus/multimatch#globbing-patterns): 6 | 7 | ```sh 8 | $ npm install --save-dev gulp del 9 | ``` 10 | 11 | Imagine the following file structure: 12 | 13 | ``` 14 | . 15 | ├── dist 16 | │   ├── report.csv 17 | │   ├── desktop 18 | │   └── mobile 19 | │   ├── app.js 20 | │   ├── deploy.json 21 | │   └── index.html 22 | └── src 23 | ``` 24 | 25 | In the gulpfile we want to clean out the contents of the `mobile` folder before running our build: 26 | 27 | ```js 28 | var gulp = require('gulp'); 29 | var del = require('del'); 30 | 31 | gulp.task('clean:mobile', function () { 32 | return del([ 33 | 'dist/report.csv', 34 | // here we use a globbing pattern to match everything inside the `mobile` folder 35 | 'dist/mobile/**/*', 36 | // we don't want to clean this file though so we negate the pattern 37 | '!dist/mobile/deploy.json' 38 | ]); 39 | }); 40 | 41 | gulp.task('default', gulp.series('clean:mobile')); 42 | ``` 43 | 44 | 45 | ## Delete files in a pipeline 46 | 47 | You might want to delete some files after processing them in a pipeline. 48 | 49 | We'll use [vinyl-paths](https://github.com/sindresorhus/vinyl-paths) to easily get the file path of files in the stream and pass it to the `del` method. 50 | 51 | ```sh 52 | $ npm install --save-dev gulp del vinyl-paths 53 | ``` 54 | 55 | Imagine the following file structure: 56 | 57 | ``` 58 | . 59 | ├── tmp 60 | │   ├── rainbow.js 61 | │   └── unicorn.js 62 | └── dist 63 | ``` 64 | 65 | ```js 66 | var gulp = require('gulp'); 67 | var stripDebug = require('gulp-strip-debug'); // only as an example 68 | var del = require('del'); 69 | var vinylPaths = require('vinyl-paths'); 70 | 71 | gulp.task('clean:tmp', function () { 72 | return gulp.src('tmp/*') 73 | .pipe(vinylPaths(del)) 74 | .pipe(stripDebug()) 75 | .pipe(gulp.dest('dist')); 76 | }); 77 | 78 | gulp.task('default', gulp.series('clean:tmp')); 79 | ``` 80 | 81 | This will only delete the tmp dir. 82 | 83 | 84 | Only do this if you're already using other plugins in the pipeline, otherwise just use the module directly as `gulp.src` is costly. 85 | -------------------------------------------------------------------------------- /docs/recipes/fast-browserify-builds-with-watchify.md: -------------------------------------------------------------------------------- 1 | # Fast browserify builds with watchify 2 | 3 | As a [browserify](https://github.com/browserify/browserify) project begins to expand, the time to bundle it slowly gets longer and longer. While it might start at 1 second, it's possible to be waiting 30 seconds for your project to build on particularly large projects. 4 | 5 | That's why [substack](https://github.com/substack) wrote [watchify](https://github.com/browserify/watchify), a persistent browserify bundler that watches files for changes and *only rebuilds what it needs to*. This way, that first build might still take 30 seconds, but subsequent builds can still run in under 100ms – which is a huge improvement. 6 | 7 | Watchify doesn't have a gulp plugin, and it doesn't need one: you can use [vinyl-source-stream](https://github.com/hughsk/vinyl-source-stream) to pipe the bundle stream into your gulp pipeline. 8 | 9 | ``` javascript 10 | 'use strict'; 11 | 12 | var watchify = require('watchify'); 13 | var browserify = require('browserify'); 14 | var gulp = require('gulp'); 15 | var source = require('vinyl-source-stream'); 16 | var buffer = require('vinyl-buffer'); 17 | var log = require('gulplog'); 18 | var sourcemaps = require('gulp-sourcemaps'); 19 | var assign = require('lodash.assign'); 20 | 21 | // add custom browserify options here 22 | var customOpts = { 23 | entries: ['./src/index.js'], 24 | debug: true 25 | }; 26 | var opts = assign({}, watchify.args, customOpts); 27 | var b = watchify(browserify(opts)); 28 | 29 | // add transformations here 30 | // i.e. b.transform(coffeeify); 31 | 32 | gulp.task('js', bundle); // so you can run `gulp js` to build the file 33 | b.on('update', bundle); // on any dep update, runs the bundler 34 | b.on('log', log.info); // output build logs to terminal 35 | 36 | function bundle() { 37 | return b.bundle() 38 | // log errors if they happen 39 | .on('error', log.error.bind(log, 'Browserify Error')) 40 | .pipe(source('bundle.js')) 41 | // optional, remove if you don't need to buffer file contents 42 | .pipe(buffer()) 43 | // optional, remove if you dont want sourcemaps 44 | .pipe(sourcemaps.init({loadMaps: true})) // loads map from browserify file 45 | // Add transformation tasks to the pipeline here. 46 | .pipe(sourcemaps.write('./')) // writes .map file 47 | .pipe(gulp.dest('./dist')); 48 | } 49 | ``` 50 | -------------------------------------------------------------------------------- /docs/recipes/handling-the-delete-event-on-watch.md: -------------------------------------------------------------------------------- 1 | # Handling the Delete Event on Watch 2 | 3 | You can listen for `'unlink'` events to fire on the watcher returned from `gulp.watch`. 4 | This gets fired when files are removed, so you can delete the file from your destination 5 | directory, using something like: 6 | 7 | ```js 8 | 'use strict'; 9 | 10 | var del = require('del'); 11 | var path = require('path'); 12 | var gulp = require('gulp'); 13 | var header = require('gulp-header'); 14 | var footer = require('gulp-footer'); 15 | 16 | gulp.task('scripts', function() { 17 | return gulp.src('src/**/*.js', {base: 'src'}) 18 | .pipe(header('(function () {\r\n\t\'use strict\'\r\n')) 19 | .pipe(footer('\r\n})();')) 20 | .pipe(gulp.dest('build')); 21 | }); 22 | 23 | gulp.task('watch', function () { 24 | var watcher = gulp.watch('src/**/*.js', ['scripts']); 25 | 26 | watcher.on('unlink', function (filepath) { 27 | var filePathFromSrc = path.relative(path.resolve('src'), filepath); 28 | // Concatenating the 'build' absolute path used by gulp.dest in the scripts task 29 | var destFilePath = path.resolve('build', filePathFromSrc); 30 | del.sync(destFilePath); 31 | }); 32 | }); 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/recipes/incremental-builds-with-concatenate.md: -------------------------------------------------------------------------------- 1 | # Incremental rebuilding, including operating on full file sets 2 | 3 | The trouble with incremental rebuilds is you often want to operate on _all_ processed files, not just single files. For example, you may want to lint and module-wrap just the file(s) that have changed, then concatenate it with all other linted and module-wrapped files. This is difficult without the use of temp files. 4 | 5 | Use [gulp-cached](https://github.com/wearefractal/gulp-cached) and [gulp-remember](https://github.com/ahaurw01/gulp-remember) to achieve this. 6 | 7 | ```js 8 | var gulp = require('gulp'); 9 | var header = require('gulp-header'); 10 | var footer = require('gulp-footer'); 11 | var concat = require('gulp-concat'); 12 | var jshint = require('gulp-jshint'); 13 | var cached = require('gulp-cached'); 14 | var remember = require('gulp-remember'); 15 | 16 | var scriptsGlob = 'src/**/*.js'; 17 | 18 | gulp.task('scripts', function() { 19 | return gulp.src(scriptsGlob) 20 | .pipe(cached('scripts')) // only pass through changed files 21 | .pipe(jshint()) // do special things to the changed files... 22 | .pipe(header('(function () {')) // e.g. jshinting ^^^ 23 | .pipe(footer('})();')) // and some kind of module wrapping 24 | .pipe(remember('scripts')) // add back all files to the stream 25 | .pipe(concat('app.js')) // do things that require all files 26 | .pipe(gulp.dest('public/')); 27 | }); 28 | 29 | gulp.task('watch', function () { 30 | var watcher = gulp.watch(scriptsGlob, gulp.series('scripts')); // watch the same files in our scripts task 31 | watcher.on('change', function (event) { 32 | if (event.type === 'deleted') { // if a file is deleted, forget about it 33 | delete cached.caches.scripts[event.path]; // gulp-cached remove api 34 | remember.forget('scripts', event.path); // gulp-remember remove api 35 | } 36 | }); 37 | }); 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/recipes/maintain-directory-structure-while-globbing.md: -------------------------------------------------------------------------------- 1 | # Maintain Directory Structure while Globbing 2 | 3 | If you are planning to read a few files/folders from a directory and maintain their relative path, you need to pass `{base: '.'}` as the second argument to `gulp.src()`. 4 | 5 | 6 | For example, if you have a directory structure like 7 | 8 | ![Dev setup](https://cloud.githubusercontent.com/assets/2562992/3178498/bedf75b4-ec1a-11e3-8a71-a150ad94b450.png) 9 | 10 | and want to read only a few files say 11 | 12 | ```js 13 | [ 'index.html', 14 | 'css/**', 15 | 'js/**', 16 | 'lib/**', 17 | 'images/**', 18 | 'plugin/**' 19 | ] 20 | ``` 21 | 22 | In this case, Gulp will read all the sub-folders of (_say_) `css` folder and arrange them relative to your root folder and they will no longer be the sub-folder of `css`. The output after globbing would look like 23 | 24 | ![Zipped-Unzipped](https://cloud.githubusercontent.com/assets/2562992/3178614/27208c52-ec1c-11e3-852e-8bbb8e420c7f.png) 25 | 26 | If you want to maintain the structure, you need to pass `{base: '.'}` to `gulp.src()`. Like 27 | 28 | ```js 29 | gulp.task('task', function () { 30 | return gulp.src(['index.html', 31 | 'css/**', 32 | 'js/**', 33 | 'lib/**', 34 | 'images/**', 35 | 'plugin/**' 36 | ], {base: '.'}) 37 | .pipe(operation1()) 38 | .pipe(operation2()); 39 | }); 40 | ``` 41 | And the input to your `operation1()` will be a folder structure like 42 | 43 | ![with-base](https://cloud.githubusercontent.com/assets/2562992/3178607/053d6722-ec1c-11e3-9ba8-7ce39e1a480e.png) 44 | -------------------------------------------------------------------------------- /docs/recipes/make-stream-from-buffer.md: -------------------------------------------------------------------------------- 1 | # Make stream from buffer (memory contents) 2 | 3 | Sometimes you may need to start a stream with files that their contents are in a variable and not in a physical file. In other words, how to start a 'gulp' stream without using `gulp.src()`. 4 | 5 | Let's say for example that we have a directory with js lib files and another directory with versions of some module. The target of the build would be to create one js file for each version, containing all the libs and the version of the module concatenated. 6 | 7 | Logically we would break it down like this: 8 | 9 | * load the lib files 10 | * concatenate the lib file contents 11 | * load the versions files 12 | * for each version file, concatenate the libs' contents and the version file contents 13 | * for each version file, output the result in a file 14 | 15 | Imagine this file structure: 16 | 17 | ```sh 18 | ├── libs 19 | │   ├── lib1.js 20 | │   └── lib2.js 21 | └── versions 22 | ├── version.1.js 23 | └── version.2.js 24 | ``` 25 | 26 | You should get: 27 | 28 | ```sh 29 | └── output 30 | ├── version.1.complete.js # lib1.js + lib2.js + version.1.js 31 | └── version.2.complete.js # lib1.js + lib2.js + version.2.js 32 | ``` 33 | 34 | A simple and modular way to do this would be the following: 35 | 36 | ```js 37 | var gulp = require('gulp'); 38 | var source = require('vinyl-source-stream'); 39 | var vinylBuffer = require('vinyl-buffer'); 40 | var tap = require('gulp-tap'); 41 | var concat = require('gulp-concat'); 42 | var size = require('gulp-size'); 43 | var path = require('path'); 44 | var es = require('event-stream'); 45 | 46 | var memory = {}; // we'll keep our assets in memory 47 | 48 | // task of loading the files' contents in memory 49 | gulp.task('load-lib-files', function() { 50 | // read the lib files from the disk 51 | return gulp.src('src/libs/*.js') 52 | // concatenate all lib files into one 53 | .pipe(concat('libs.concat.js')) 54 | // tap into the stream to get each file's data 55 | .pipe(tap(function(file) { 56 | // save the file contents in memory 57 | memory[path.basename(file.path)] = file.contents.toString(); 58 | })); 59 | }); 60 | 61 | gulp.task('load-versions', function() { 62 | memory.versions = {}; 63 | // read the version files from the disk 64 | return gulp.src('src/versions/version.*.js') 65 | // tap into the stream to get each file's data 66 | .pipe( tap(function(file) { 67 | // save the file contents in the assets 68 | memory.versions[path.basename(file.path)] = file.contents.toString(); 69 | })); 70 | }); 71 | 72 | gulp.task('write-versions', function() { 73 | // we store all the different version file names in an array 74 | var availableVersions = Object.keys(memory.versions); 75 | // we make an array to store all the stream promises 76 | var streams = []; 77 | 78 | availableVersions.forEach(function(v) { 79 | // make a new stream with fake file name 80 | var stream = source('final.' + v); 81 | 82 | var streamEnd = stream; 83 | 84 | // we load the data from the concatenated libs 85 | var fileContents = memory['libs.concat.js'] + 86 | // we add the version's data 87 | '\n' + memory.versions[v]; 88 | 89 | // write the file contents to the stream 90 | stream.write(fileContents); 91 | 92 | process.nextTick(function() { 93 | // in the next process cycle, end the stream 94 | stream.end(); 95 | }); 96 | 97 | streamEnd = streamEnd 98 | // transform the raw data into the stream, into a vinyl object/file 99 | .pipe(vinylBuffer()) 100 | //.pipe(tap(function(file) { /* do something with the file contents here */ })) 101 | .pipe(gulp.dest('output')); 102 | 103 | // add the end of the stream, otherwise the task would finish before all the processing 104 | // is done 105 | streams.push(streamEnd); 106 | 107 | }); 108 | 109 | return es.merge.apply(this, streams); 110 | }); 111 | 112 | //============================================ our main task 113 | gulp.task('default', gulp.series( 114 | // load the files in parallel 115 | gulp.parallel('load-lib-files', 'load-versions'), 116 | // ready to write once all resources are in memory 117 | 'write-versions' 118 | ) 119 | ); 120 | 121 | //============================================ our watcher task 122 | // only watch after having run 'default' once so that all resources 123 | // are already in memory 124 | gulp.task('watch', gulp.series( 125 | 'default', 126 | function() { 127 | gulp.watch('./src/libs/*.js', gulp.series( 128 | 'load-lib-files', 129 | 'write-versions' 130 | )); 131 | 132 | gulp.watch('./src/versions/*.js', gulp.series( 133 | 'load-lib-files', 134 | 'write-versions' 135 | )); 136 | } 137 | )); 138 | ``` 139 | -------------------------------------------------------------------------------- /docs/recipes/minified-and-non-minified.md: -------------------------------------------------------------------------------- 1 | # Output both a minified and non-minified version 2 | 3 | Outputting both a minified and non-minified version of your combined JavaScript files can be achieved by using `gulp-rename` and piping to `dest` twice (once before minifying and once after minifying): 4 | 5 | ```js 6 | 'use strict'; 7 | 8 | var gulp = require('gulp'); 9 | var rename = require('gulp-rename'); 10 | var uglify = require('gulp-uglify'); 11 | 12 | var DEST = 'build/'; 13 | 14 | gulp.task('default', function() { 15 | return gulp.src('foo.js') 16 | // This will output the non-minified version 17 | .pipe(gulp.dest(DEST)) 18 | // This will minify and rename to foo.min.js 19 | .pipe(uglify()) 20 | .pipe(rename({ extname: '.min.js' })) 21 | .pipe(gulp.dest(DEST)); 22 | }); 23 | 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/recipes/minimal-browsersync-setup-with-gulp4.md: -------------------------------------------------------------------------------- 1 | # Minimal BrowserSync setup with Gulp 4 2 | 3 | [BrowserSync](https://www.browsersync.io/) is a great tool to streamline 4 | the development process with the ability to reflect code changes instantaneously 5 | in the browser through live-reloading. Setting up a live-reloading 6 | BrowserSync server with Gulp 4 is very clean and easy. 7 | 8 | ## Step 1: Install the dependencies 9 | 10 | ``` 11 | npm install --save-dev browser-sync 12 | ``` 13 | 14 | ## Step 2: Setup the project structure 15 | 16 | ``` 17 | src/ 18 | scripts/ 19 | |__ index.js 20 | dist/ 21 | scripts/ 22 | index.html 23 | gulpfile.babel.js 24 | ``` 25 | 26 | The goal here is to be able to: 27 | - Build the source script file in `src/scripts/`, e.g. compiling with babel, minifying, etc. 28 | - Put the compiled version in `dist/scripts` for use in `index.html` 29 | - Watch for changes in the source file and rebuild the `dist` package 30 | - With each rebuild of the `dist` package, reload the browser to immediately reflect the changes 31 | 32 | ## Step 3: Write the gulpfile 33 | 34 | The gulpfile could be broken in 3 parts. 35 | 36 | ### 1. Write the task to prepare the dist package as usual 37 | 38 | Refer to the main [README](https://github.com/gulpjs/gulp/blob/master/docs/README.md) 39 | for more information. 40 | 41 | ```javascript 42 | import babel from 'gulp-babel'; 43 | import concat from 'gulp-concat'; 44 | import del from 'del'; 45 | import gulp from 'gulp'; 46 | import uglify from 'gulp-uglify'; 47 | 48 | const paths = { 49 | scripts: { 50 | src: 'src/scripts/*.js', 51 | dest: 'dist/scripts/' 52 | } 53 | }; 54 | 55 | const clean = () => del(['dist']); 56 | 57 | function scripts() { 58 | return gulp.src(paths.scripts.src, { sourcemaps: true }) 59 | .pipe(babel()) 60 | .pipe(uglify()) 61 | .pipe(concat('index.min.js')) 62 | .pipe(gulp.dest(paths.scripts.dest)); 63 | } 64 | ``` 65 | 66 | ### 2. Setup the BrowserSync server 67 | 68 | And write the tasks to serve and reload the server accordingly. 69 | 70 | ```javascript 71 | import browserSync from 'browser-sync'; 72 | const server = browserSync.create(); 73 | 74 | function reload(done) { 75 | server.reload(); 76 | done(); 77 | } 78 | 79 | function serve(done) { 80 | server.init({ 81 | server: { 82 | baseDir: './' 83 | } 84 | }); 85 | done(); 86 | } 87 | ``` 88 | 89 | ### 3. Watch for source change, rebuild the scripts and reload the server 90 | 91 | This is trivially accomplished with `gulp.series` 92 | 93 | ```javascript 94 | const watch = () => gulp.watch(paths.scripts.src, gulp.series(scripts, reload)); 95 | ``` 96 | 97 | ## Step 4: Bring it all together 98 | 99 | The last step is to expose the default task 100 | 101 | ```javascript 102 | const dev = gulp.series(clean, scripts, serve, watch); 103 | export default dev; 104 | ``` 105 | 106 | And profit 107 | 108 | ```bash 109 | $ gulp 110 | ``` 111 | 112 | Now if you go to [http://localhost:3000](http://localhost:3000), which is the default address of the 113 | BrowserSync server, you will see that the end result in the browser is updated everytime you change 114 | the content of the source file. Here is the whole gulpfile: 115 | 116 | ```javascript 117 | import babel from 'gulp-babel'; 118 | import concat from 'gulp-concat'; 119 | import del from 'del'; 120 | import gulp from 'gulp'; 121 | import uglify from 'gulp-uglify'; 122 | import browserSync from 'browser-sync'; 123 | 124 | const server = browserSync.create(); 125 | 126 | const paths = { 127 | scripts: { 128 | src: 'src/scripts/*.js', 129 | dest: 'dist/scripts/' 130 | } 131 | }; 132 | 133 | const clean = () => del(['dist']); 134 | 135 | function scripts() { 136 | return gulp.src(paths.scripts.src, { sourcemaps: true }) 137 | .pipe(babel()) 138 | .pipe(uglify()) 139 | .pipe(concat('index.min.js')) 140 | .pipe(gulp.dest(paths.scripts.dest)); 141 | } 142 | 143 | function reload(done) { 144 | server.reload(); 145 | done(); 146 | } 147 | 148 | function serve(done) { 149 | server.init({ 150 | server: { 151 | baseDir: './' 152 | } 153 | }); 154 | done(); 155 | } 156 | 157 | const watch = () => gulp.watch(paths.scripts.src, gulp.series(scripts, reload)); 158 | 159 | const dev = gulp.series(clean, scripts, serve, watch); 160 | export default dev; 161 | ``` 162 | -------------------------------------------------------------------------------- /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 | // npm install gulp gulp-mocha 7 | 8 | var gulp = require('gulp'); 9 | var mocha = require('gulp-mocha'); 10 | 11 | gulp.task('default', function() { 12 | return gulp.src(['test/test-*.js'], { read: false }) 13 | .pipe(mocha({ 14 | reporter: 'spec', 15 | globals: { 16 | should: require('should') 17 | } 18 | })); 19 | }); 20 | ``` 21 | 22 | ### Running mocha tests when files change 23 | 24 | ```js 25 | // npm install gulp gulp-mocha gulplog 26 | 27 | var gulp = require('gulp'); 28 | var mocha = require('gulp-mocha'); 29 | var log = require('gulplog'); 30 | 31 | gulp.task('mocha', function() { 32 | return gulp.src(['test/*.js'], { read: false }) 33 | .pipe(mocha({ reporter: 'list' })) 34 | .on('error', log.error); 35 | }); 36 | 37 | gulp.task('watch-mocha', function() { 38 | gulp.watch(['lib/**', 'test/**'], gulp.series('mocha')); 39 | }); 40 | ``` 41 | -------------------------------------------------------------------------------- /docs/recipes/pass-arguments-from-cli.md: -------------------------------------------------------------------------------- 1 | # Pass arguments from the command line 2 | 3 | ```js 4 | // npm install --save-dev gulp gulp-if gulp-uglify minimist 5 | 6 | var gulp = require('gulp'); 7 | var gulpif = require('gulp-if'); 8 | var uglify = require('gulp-uglify'); 9 | 10 | var minimist = require('minimist'); 11 | 12 | var knownOptions = { 13 | string: 'env', 14 | default: { env: process.env.NODE_ENV || 'production' } 15 | }; 16 | 17 | var options = minimist(process.argv.slice(2), knownOptions); 18 | 19 | gulp.task('scripts', function() { 20 | return gulp.src('**/*.js') 21 | .pipe(gulpif(options.env === 'production', uglify())) // only minify in production 22 | .pipe(gulp.dest('dist')); 23 | }); 24 | ``` 25 | 26 | Then run gulp with: 27 | 28 | ```sh 29 | $ gulp scripts --env development 30 | ``` 31 | -------------------------------------------------------------------------------- /docs/recipes/rollup-with-rollup-stream.md: -------------------------------------------------------------------------------- 1 | # Rollup with rollup-stream 2 | 3 | Like Browserify, [Rollup](https://rollupjs.org/) is a bundler and thus only fits naturally into gulp if it's at the start of the pipeline. Unlike Browserify, Rollup doesn't natively produce a stream as output and needs to be wrapped before it can take this position. [rollup-stream](https://github.com/Permutatrix/rollup-stream) does this for you, producing output just like that of Browserify's `bundle()` method—as a result, most of the Browserify recipes here will also work with rollup-stream. 4 | 5 | ## Basic usage 6 | ```js 7 | // npm install --save-dev gulp @rollup/stream@1 vinyl-source-stream 8 | var gulp = require('gulp'); 9 | var rollup = require('rollup-stream'); 10 | var source = require('vinyl-source-stream'); 11 | 12 | gulp.task('rollup', function() { 13 | return rollup({ 14 | input: './src/main.js' 15 | }) 16 | 17 | // give the file the name you want to output with 18 | .pipe(source('app.js')) 19 | 20 | // and output to ./dist/app.js as normal. 21 | .pipe(gulp.dest('./dist')); 22 | }); 23 | ``` 24 | 25 | ## Usage with sourcemaps 26 | ```js 27 | // npm install --save-dev gulp @rollup/stream@1 gulp-sourcemaps vinyl-source-stream vinyl-buffer 28 | // optional: npm install --save-dev gulp-rename 29 | var gulp = require('gulp'); 30 | var rollup = require('rollup-stream'); 31 | var sourcemaps = require('gulp-sourcemaps'); 32 | //var rename = require('gulp-rename'); 33 | var source = require('vinyl-source-stream'); 34 | var buffer = require('vinyl-buffer'); 35 | 36 | gulp.task('rollup', function() { 37 | return rollup({ 38 | input: './src/main.js', 39 | sourcemap: true, 40 | format: 'umd' 41 | }) 42 | 43 | // point to the entry file. 44 | .pipe(source('main.js', './src')) 45 | 46 | // buffer the output. most gulp plugins, including gulp-sourcemaps, don't support streams. 47 | .pipe(buffer()) 48 | 49 | // tell gulp-sourcemaps to load the inline sourcemap produced by rollup-stream. 50 | .pipe(sourcemaps.init({loadMaps: true})) 51 | 52 | // transform the code further here. 53 | 54 | // if you want to output with a different name from the input file, use gulp-rename here. 55 | //.pipe(rename('index.js')) 56 | 57 | // write the sourcemap alongside the output file. 58 | .pipe(sourcemaps.write('.')) 59 | 60 | // and output to ./dist/main.js as normal. 61 | .pipe(gulp.dest('./dist')); 62 | }); 63 | ``` 64 | -------------------------------------------------------------------------------- /docs/recipes/run-grunt-tasks-from-gulp.md: -------------------------------------------------------------------------------- 1 | # Run Grunt Tasks from Gulp 2 | 3 | It is possible to run Grunt tasks / Grunt plugins from within Gulp. This can be useful during a gradual migration from Grunt to Gulp or if there's a specific plugin that you need. With the described approach no Grunt CLI and no Gruntfile is required. 4 | 5 | **This approach requires Grunt >=1.0.0** 6 | 7 | very simple example `gulpfile.js`: 8 | 9 | ```js 10 | // npm install gulp grunt grunt-contrib-copy --save-dev 11 | 12 | var gulp = require('gulp'); 13 | var grunt = require('grunt'); 14 | 15 | grunt.initConfig({ 16 | copy: { 17 | main: { 18 | src: 'src/*', 19 | dest: 'dest/' 20 | } 21 | } 22 | }); 23 | grunt.loadNpmTasks('grunt-contrib-copy'); 24 | 25 | gulp.task('copy', function (done) { 26 | grunt.tasks( 27 | ['copy:main'], //you can add more grunt tasks in this array 28 | {gruntfile: false}, //don't look for a Gruntfile - there is none. :-) 29 | function () {done();} 30 | ); 31 | }); 32 | 33 | ``` 34 | 35 | Now start the task with: 36 | `gulp copy` 37 | 38 | With the aforementioned approach the grunt tasks get registered within gulp's task system. **Keep in mind grunt tasks are usually blocking (unlike gulp), therefore no other task (not even a gulp task) can run until a grunt task is completed.** 39 | 40 | 41 | ### A few words on alternatives 42 | 43 | There's a *gulpfriendly* node module `gulp-grunt` [available](https://www.npmjs.org/package/gulp-grunt) which takes a different approach. It spawns child processes and within them the grunt tasks are executed. The way it works implies some limitations though: 44 | 45 | * It is at the moment not possible to pass options / cli args etc. to the grunt tasks via `gulp-grunt` 46 | * All grunt tasks have to be defined in a separate Gruntfile 47 | * You need to have the Grunt CLI installed 48 | * The output of some grunt tasks gets malformatted (.i.e. color coding). 49 | -------------------------------------------------------------------------------- /docs/recipes/running-task-steps-per-folder.md: -------------------------------------------------------------------------------- 1 | # Generating a file per folder 2 | 3 | If you have a set of folders, and wish to perform a set of tasks on each, for instance... 4 | 5 | ``` 6 | /scripts 7 | /scripts/jquery/*.js 8 | /scripts/angularjs/*.js 9 | ``` 10 | 11 | ...and want to end up with... 12 | 13 | ``` 14 | /scripts 15 | /scripts/jquery.min.js 16 | /scripts/angularjs.min.js 17 | ``` 18 | 19 | ...you'll need to do something like the following... 20 | 21 | ``` javascript 22 | var fs = require('fs'); 23 | var path = require('path'); 24 | var merge = require('merge-stream'); 25 | var gulp = require('gulp'); 26 | var concat = require('gulp-concat'); 27 | var rename = require('gulp-rename'); 28 | var uglify = require('gulp-uglify'); 29 | 30 | var scriptsPath = 'src/scripts'; 31 | 32 | function getFolders(dir) { 33 | return fs.readdirSync(dir) 34 | .filter(function(file) { 35 | return fs.statSync(path.join(dir, file)).isDirectory(); 36 | }); 37 | } 38 | 39 | gulp.task('scripts', function(done) { 40 | var folders = getFolders(scriptsPath); 41 | if (folders.length === 0) return done(); // nothing to do! 42 | var tasks = folders.map(function(folder) { 43 | return gulp.src(path.join(scriptsPath, folder, '/**/*.js')) 44 | // concat into foldername.js 45 | .pipe(concat(folder + '.js')) 46 | // write to output 47 | .pipe(gulp.dest(scriptsPath)) 48 | // minify 49 | .pipe(uglify()) 50 | // rename to folder.min.js 51 | .pipe(rename(folder + '.min.js')) 52 | // write to output again 53 | .pipe(gulp.dest(scriptsPath)); 54 | }); 55 | 56 | // process all remaining files in scriptsPath root into main.js and main.min.js files 57 | var root = gulp.src(path.join(scriptsPath, '/*.js')) 58 | .pipe(concat('main.js')) 59 | .pipe(gulp.dest(scriptsPath)) 60 | .pipe(uglify()) 61 | .pipe(rename('main.min.js')) 62 | .pipe(gulp.dest(scriptsPath)); 63 | 64 | return merge(tasks, root); 65 | }); 66 | ``` 67 | 68 | A few notes: 69 | 70 | - `folders.map` - executes the function once per folder, and returns the async stream 71 | - `merge` - combines the streams and ends only when all streams emitted end 72 | -------------------------------------------------------------------------------- /docs/recipes/server-with-livereload-and-css-injection.md: -------------------------------------------------------------------------------- 1 | # Server with live-reloading and CSS injection 2 | 3 | With [BrowserSync](https://browsersync.io) and gulp, you can easily create a development server that is accessible to any device on the same WiFi network. BrowserSync also has live-reload built in, so there's nothing else to configure. 4 | 5 | First install the modules: 6 | 7 | ```sh 8 | $ npm install --save-dev gulp browser-sync 9 | ``` 10 | 11 | Then, considering the following file structure... 12 | 13 | ``` 14 | gulpfile.js 15 | app/ 16 | styles/ 17 | main.css 18 | scripts/ 19 | main.js 20 | index.html 21 | ``` 22 | 23 | ... you can easily serve files from the `app` directory and have all browsers reload when any of them change with the following in `gulpfile.js`: 24 | 25 | ```js 26 | var gulp = require('gulp'); 27 | var browserSync = require('browser-sync'); 28 | var reload = browserSync.reload; 29 | 30 | // watch files for changes and reload 31 | gulp.task('serve', function() { 32 | browserSync({ 33 | server: { 34 | baseDir: 'app' 35 | } 36 | }); 37 | 38 | gulp.watch(['*.html', 'styles/**/*.css', 'scripts/**/*.js'], {cwd: 'app'}, reload); 39 | }); 40 | 41 | ``` 42 | 43 | and including the CSS in `index.html`: 44 | 45 | ```html 46 | 47 | 48 | ... 49 | 50 | ... 51 | 52 | ``` 53 | 54 | to serve your files and launch a browser window pointing to the default URL (http://localhost:3000) run: 55 | 56 | ```bash 57 | gulp serve 58 | ``` 59 | 60 | 61 | ## + CSS pre-processors 62 | 63 | A common use-case is to reload CSS files after they've been pre-processed. Using Sass as an example, this is how you can instruct browsers to reload the CSS without doing a full-page refresh. 64 | 65 | Considering this updated file structure... 66 | 67 | ``` 68 | gulpfile.js 69 | app/ 70 | scss/ 71 | main.scss 72 | scripts/ 73 | main.js 74 | index.html 75 | ``` 76 | ... you can easily watch Sass files from the `scss` directory and have all browsers reload when any of them change with the following in `gulpfile.js`: 77 | 78 | ```js 79 | var gulp = require('gulp'); 80 | var sass = require('gulp-ruby-sass'); 81 | var browserSync = require('browser-sync'); 82 | var reload = browserSync.reload; 83 | 84 | gulp.task('sass', function() { 85 | return sass('scss/styles.scss') 86 | .pipe(gulp.dest('app/css')) 87 | .pipe(reload({ stream:true })); 88 | }); 89 | 90 | // watch Sass files for changes, run the Sass preprocessor with the 'sass' task and reload 91 | gulp.task('serve', gulp.series('sass', function() { 92 | browserSync({ 93 | server: { 94 | baseDir: 'app' 95 | } 96 | }); 97 | 98 | gulp.watch('scss/*.scss', gulp.series('sass')); 99 | })); 100 | ``` 101 | 102 | and including the pre-processed CSS in `index.html`: 103 | 104 | ```html 105 | 106 | 107 | ... 108 | 109 | ... 110 | 111 | ``` 112 | 113 | to serve your files and launch a browser window pointing to the default URL (http://localhost:3000) run: 114 | 115 | ```bash 116 | gulp serve 117 | ``` 118 | 119 | ## Extras 120 | 121 | - Live reload, CSS injection and scroll/form syncing works seamlessly inside of [BrowserStack](https://www.browserstack.com/) virtual machines. 122 | - Set `tunnel: true` to view your local site at a public URL (complete with all BrowserSync features). 123 | -------------------------------------------------------------------------------- /docs/recipes/sharing-streams-with-stream-factories.md: -------------------------------------------------------------------------------- 1 | # Sharing streams with stream factories 2 | 3 | If you use the same plugins in multiple tasks you might find yourself getting that itch to DRY things up. This method will allow you to create factories to split out your commonly used stream chains. 4 | 5 | We'll use [lazypipe](https://github.com/OverZealous/lazypipe) to get the job done. 6 | 7 | This is our sample file: 8 | 9 | ```js 10 | var gulp = require('gulp'); 11 | var uglify = require('gulp-uglify'); 12 | var coffee = require('gulp-coffee'); 13 | var jshint = require('gulp-jshint'); 14 | var stylish = require('jshint-stylish'); 15 | 16 | gulp.task('bootstrap', function() { 17 | return gulp.src('bootstrap/js/*.js') 18 | .pipe(jshint()) 19 | .pipe(jshint.reporter(stylish)) 20 | .pipe(uglify()) 21 | .pipe(gulp.dest('public/bootstrap')); 22 | }); 23 | 24 | gulp.task('coffee', function() { 25 | return gulp.src('lib/js/*.coffee') 26 | .pipe(coffee()) 27 | .pipe(jshint()) 28 | .pipe(jshint.reporter(stylish)) 29 | .pipe(uglify()) 30 | .pipe(gulp.dest('public/js')); 31 | }); 32 | ``` 33 | 34 | and our file after using lazypipe looks like this: 35 | 36 | ```js 37 | var gulp = require('gulp'); 38 | var uglify = require('gulp-uglify'); 39 | var coffee = require('gulp-coffee'); 40 | var jshint = require('gulp-jshint'); 41 | var stylish = require('jshint-stylish'); 42 | var lazypipe = require('lazypipe'); 43 | 44 | // give lazypipe 45 | var jsTransform = lazypipe() 46 | .pipe(jshint) 47 | .pipe(jshint.reporter, stylish) 48 | .pipe(uglify); 49 | 50 | gulp.task('bootstrap', function() { 51 | return gulp.src('bootstrap/js/*.js') 52 | .pipe(jsTransform()) 53 | .pipe(gulp.dest('public/bootstrap')); 54 | }); 55 | 56 | gulp.task('coffee', function() { 57 | return gulp.src('lib/js/*.coffee') 58 | .pipe(coffee()) 59 | .pipe(jsTransform()) 60 | .pipe(gulp.dest('public/js')); 61 | }); 62 | ``` 63 | 64 | You can see we split out our JavaScript pipeline (JSHint + Uglify) that was being reused in multiple tasks into a factory. These factories can be reused in as many tasks as you want. You can also nest factories and you can chain factories together for great effect. Splitting out each shared pipeline also gives you one central location to modify if you decide to change up your workflow. 65 | -------------------------------------------------------------------------------- /docs/recipes/templating-with-swig-and-yaml-front-matter.md: -------------------------------------------------------------------------------- 1 | # Templating with Swig and YAML front-matter 2 | Templating can be setup using `gulp-swig` and `gulp-front-matter`: 3 | 4 | ##### `page.html` 5 | 6 | ```html 7 | --- 8 | title: Things to do 9 | todos: 10 | - First todo 11 | - Another todo item 12 | - A third todo item 13 | --- 14 | 15 | 16 | {{ title }} 17 | 18 | 19 |

{{ title }}

20 |
    {% for todo in todos %} 21 |
  • {{ todo }}
  • 22 | {% endfor %}
23 | 24 | 25 | ``` 26 | 27 | ##### `gulpfile.js` 28 | 29 | ```js 30 | var gulp = require('gulp'); 31 | var swig = require('gulp-swig'); 32 | var frontMatter = require('gulp-front-matter'); 33 | 34 | gulp.task('compile-page', function() { 35 | gulp.src('page.html') 36 | .pipe(frontMatter({ property: 'data' })) 37 | .pipe(swig()) 38 | .pipe(gulp.dest('build')); 39 | }); 40 | 41 | gulp.task('default', ['compile-page']); 42 | ``` 43 | -------------------------------------------------------------------------------- /docs/recipes/using-multiple-sources-in-one-task.md: -------------------------------------------------------------------------------- 1 | # Using multiple sources in one task 2 | 3 | ```js 4 | // npm install --save-dev gulp merge-stream 5 | 6 | var gulp = require('gulp'); 7 | var merge = require('merge-stream'); 8 | 9 | gulp.task('test', function() { 10 | var bootstrap = gulp.src('bootstrap/js/*.js') 11 | .pipe(gulp.dest('public/bootstrap')); 12 | 13 | var jquery = gulp.src('jquery.cookie/jquery.cookie.js') 14 | .pipe(gulp.dest('public/jquery')); 15 | 16 | return merge(bootstrap, jquery); 17 | }); 18 | ``` 19 | 20 | `gulp.src` will emit files in the order they were added: 21 | 22 | ```js 23 | // npm install gulp gulp-concat 24 | 25 | var gulp = require('gulp'); 26 | var concat = require('gulp-concat'); 27 | 28 | gulp.task('default', function() { 29 | return gulp.src(['foo/*', 'bar/*']) 30 | .pipe(concat('result.txt')) 31 | .pipe(gulp.dest('build')); 32 | }); 33 | -------------------------------------------------------------------------------- /docs/support/for-enterprise.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # Gulp for enterprise 9 | 10 | Available as part of the Tidelift Subscription. 11 | 12 | Tidelift is working with the maintainers of Gulp and thousands of other 13 | open source projects to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. 14 | 15 | Learn more 16 | 17 | Request a demo 18 | 19 | ## Enterprise-ready open source software—managed for you 20 | 21 | The Tidelift Subscription is a managed open source subscription for application dependencies covering millions of open source projects across JavaScript, Python, Java, PHP, Ruby, .NET, and more. 22 | 23 | Your subscription includes: 24 | 25 | * **Security updates** 26 | 27 | Tidelift’s security response team coordinates patches for new breaking security vulnerabilities and alerts immediately through a private channel, so your software supply chain is always secure. 28 | 29 | * **Licensing verification and indemnification** 30 | 31 | Tidelift verifies license information to enable easy policy enforcement and adds intellectual property indemnification to cover creators and users in case something goes wrong. You always have a 100% up-to-date bill of materials for your dependencies to share with your legal team, customers, or partners. 32 | 33 | * **Maintenance and code improvement** 34 | 35 | Tidelift ensures the software you rely on keeps working as long as you need it to work. Your managed dependencies are actively maintained and we recruit additional maintainers where required. 36 | 37 | * **Package selection and version guidance** 38 | 39 | We help you choose the best open source packages from the start—and then guide you through updates to stay on the best releases as new issues arise. 40 | 41 | * **Roadmap input** 42 | 43 | Take a seat at the table with the creators behind the software you use. Tidelift’s participating maintainers earn more income as their software is used by more subscribers, so they’re interested in knowing what you need. 44 | 45 | * **Tooling and cloud integration** 46 | 47 | Tidelift works with GitHub, GitLab, BitBucket, and more. We support every cloud platform (and other deployment targets, too). 48 | 49 | The end result? All of the capabilities you expect from commercial-grade software, for the full breadth of open source you use. That means less time grappling with esoteric open source trivia, and more time building your own applications—and your business. 50 | 51 | Learn more 52 | 53 | Request a demo 54 | -------------------------------------------------------------------------------- /docs/why-use-pump/README.md: -------------------------------------------------------------------------------- 1 | # Why Use Pump? 2 | 3 | When using `pipe` from the Node.js streams, errors are not propagated forward 4 | through the piped streams, and source streams aren’t closed if a destination 5 | stream closed. The [`pump`][pump] module normalizes these problems and passes 6 | you the errors in a callback. 7 | 8 | ## A common gulpfile example 9 | 10 | A common pattern in gulp files is to simply return a Node.js stream, and expect 11 | the gulp tool to handle errors. 12 | 13 | ```javascript 14 | // example of a common gulpfile 15 | var gulp = require('gulp'); 16 | var uglify = require('gulp-uglify'); 17 | 18 | gulp.task('compress', function () { 19 | // returns a Node.js stream, but no handling of error messages 20 | return gulp.src('lib/*.js') 21 | .pipe(uglify()) 22 | .pipe(gulp.dest('dist')); 23 | }); 24 | ``` 25 | 26 | ![pipe error](pipe-error.png) 27 | 28 | There’s an error in one of the JavaScript files, but that error message is the 29 | opposite of helpful. You want to know what file and line contains the error. So 30 | what is this mess? 31 | 32 | When there’s an error in a stream, the Node.js stream fire the 'error' event, 33 | but if there’s no handler for this event, it instead goes to the defined 34 | [uncaught exception][uncaughtException] handler. The default behavior of the 35 | uncaught exception handler is documented: 36 | 37 | > By default, Node.js handles such exceptions by printing the stack trace to 38 | > stderr and exiting. 39 | 40 | ## Handling the Errors 41 | 42 | Since allowing the errors to make it to the uncaught exception handler isn’t 43 | useful, we should handle the exceptions properly. Let’s give that a quick shot. 44 | 45 | ```javascript 46 | var gulp = require('gulp'); 47 | var uglify = require('gulp-uglify'); 48 | 49 | gulp.task('compress', function () { 50 | return gulp.src('lib/*.js') 51 | .pipe(uglify()) 52 | .pipe(gulp.dest('dist')) 53 | .on('error', function(err) { 54 | console.error('Error in compress task', err.toString()); 55 | }); 56 | }); 57 | ``` 58 | 59 | Unfortunately, Node.js stream’s `pipe` function doesn’t forward errors through 60 | the chain, so this error handler only handles the errors given by 61 | `gulp.dest`. Instead we need to handle errors for each stream. 62 | 63 | ```javascript 64 | var gulp = require('gulp'); 65 | var uglify = require('gulp-uglify'); 66 | 67 | gulp.task('compress', function () { 68 | function createErrorHandler(name) { 69 | return function (err) { 70 | console.error('Error from ' + name + ' in compress task', err.toString()); 71 | }; 72 | } 73 | 74 | return gulp.src('lib/*.js') 75 | .on('error', createErrorHandler('gulp.src')) 76 | .pipe(uglify()) 77 | .on('error', createErrorHandler('uglify')) 78 | .pipe(gulp.dest('dist')) 79 | .on('error', createErrorHandler('gulp.dest')); 80 | }); 81 | ``` 82 | 83 | This is a lot of complexity to add in each of your gulp tasks, and it’s easy to 84 | forget to do it. In addition, it’s still not perfect, as it doesn’t properly 85 | signal to gulp’s task system that the task has failed. We can fix this, and we 86 | can handle the other pesky issues with error propogations with streams, but it’s 87 | even more work! 88 | 89 | ## Using pump 90 | 91 | The [`pump`][pump] module is a cheat code of sorts. It’s a wrapper around the 92 | `pipe` functionality that handles these cases for you, so you can stop hacking 93 | on your gulpfiles, and get back to hacking new features into your app. 94 | 95 | ```javascript 96 | var gulp = require('gulp'); 97 | var uglify = require('gulp-uglify'); 98 | var pump = require('pump'); 99 | 100 | gulp.task('compress', function (cb) { 101 | pump([ 102 | gulp.src('lib/*.js'), 103 | uglify(), 104 | gulp.dest('dist') 105 | ], 106 | cb 107 | ); 108 | }); 109 | ``` 110 | 111 | The gulp task system provides a gulp task with a callback, which can signal 112 | successful task completion (being called with no arguments), or a task failure 113 | (being called with an Error argument). Fortunately, this is the exact same 114 | format `pump` uses! 115 | 116 | ![pump error](pump-error.png) 117 | 118 | Now it’s very clear what plugin the error was from, what the error actually was, 119 | and from what file and line number. 120 | 121 | [pump]: https://github.com/mafintosh/pump 122 | [uncaughtException]: https://nodejs.org/api/process.html#process_event_uncaughtexception 123 | -------------------------------------------------------------------------------- /docs/why-use-pump/pipe-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gulpjs/gulp/0003e9fd25ffc7dd8c4f1a9335c102b73de017c1/docs/why-use-pump/pipe-error.png -------------------------------------------------------------------------------- /docs/why-use-pump/pump-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gulpjs/gulp/0003e9fd25ffc7dd8c4f1a9335c102b73de017c1/docs/why-use-pump/pump-error.png -------------------------------------------------------------------------------- /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 practices regarding error handling and add a line that makes the gulp plugin re-emit the first error caught 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 PluginError = require('plugin-error'); 16 | 17 | // consts 18 | const PLUGIN_NAME = 'gulp-prefixer'; 19 | 20 | function prefixStream(prefixText) { 21 | var stream = through(); 22 | stream.write(prefixText); 23 | return stream; 24 | } 25 | 26 | // plugin level function (dealing with files) 27 | function gulpPrefixer(prefixText) { 28 | if (!prefixText) { 29 | throw new PluginError(PLUGIN_NAME, 'Missing prefix text!'); 30 | } 31 | 32 | prefixText = new Buffer(prefixText); // allocate ahead of time 33 | 34 | // creating a stream through which each file will pass 35 | var stream = through.obj(function(file, enc, cb) { 36 | if (file.isBuffer()) { 37 | this.emit('error', new PluginError(PLUGIN_NAME, 'Buffers not supported!')); 38 | return cb(); 39 | } 40 | 41 | if (file.isStream()) { 42 | // define the streamer that will transform the content 43 | var streamer = prefixStream(prefixText); 44 | // catch errors from the streamer and emit a gulp plugin error 45 | streamer.on('error', this.emit.bind(this, 'error')); 46 | // start the transformation 47 | file.contents = file.contents.pipe(streamer); 48 | } 49 | 50 | // make sure the file goes through the next gulp plugin 51 | this.push(file); 52 | // tell the stream engine that we are done with this file 53 | cb(); 54 | }); 55 | 56 | // returning the file stream 57 | return stream; 58 | } 59 | 60 | // exporting the plugin main function 61 | module.exports = gulpPrefixer; 62 | ``` 63 | 64 | The above plugin can be used like this: 65 | 66 | ```js 67 | var gulp = require('gulp'); 68 | var gulpPrefixer = require('gulp-prefixer'); 69 | 70 | gulp.src('files/**/*.js', { buffer: false }) 71 | .pipe(gulpPrefixer('prepended string')) 72 | .pipe(gulp.dest('modified-files')); 73 | ``` 74 | 75 | ## Some plugins using streams 76 | 77 | * [gulp-svgicons2svgfont](https://github.com/nfroidure/gulp-svgiconstosvgfont) 78 | -------------------------------------------------------------------------------- /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 | - For example: deleting a folder does not need to be a gulp plugin. Use a module like [del](https://github.com/sindresorhus/del) within a task instead. 9 | - Wrapping every possible thing just for the sake of wrapping it will pollute the ecosystem with low quality plugins that don't make sense within the gulp paradigm. 10 | - gulp plugins are for file-based operations! If you find yourself shoehorning a complex process into streams just make a normal node module instead. 11 | - A good example of a gulp plugin would be something like gulp-coffee. The coffee-script module does not work with Vinyl out of the box, so we wrap it to add this functionality and abstract away pain points to make it work well within gulp. 12 | 1. Your plugin should only do **one thing**, and do it well. 13 | - Avoid config options that make your plugin do completely different tasks 14 | - For example: A JS minification plugin should not have an option that adds a header as well 15 | 1. Your plugin shouldn't do things that other plugins are responsible for 16 | - It should not concat, [gulp-concat](https://github.com/contra/gulp-concat) does that 17 | - It should not add headers, [gulp-header](https://www.npmjs.com/package/gulp-header) does that 18 | - It should not add footers, [gulp-footer](https://www.npmjs.com/package/gulp-footer) does that 19 | - If it's a common but optional use case, document that your plugin is often used with another plugin 20 | - Make use of other plugins within your plugin! This reduces the amount of code you have to write and ensures a stable ecosystem. 21 | 1. Your plugin **must be tested** 22 | - Testing a gulp plugin is easy, you don't even need gulp to test it 23 | - Look at other plugins for examples 24 | 1. Add `gulpplugin` as a keyword in your `package.json` so you show up on our search 25 | 1. Your plugin API should be a function that returns a stream 26 | - If you need to store state somewhere, do it internally 27 | - If you need to pass state/options between plugins, tack it on the file object 28 | 1. Do not throw errors inside a stream 29 | - Instead, you should emit it as an **error** event. 30 | - If you encounter an error **outside** the stream, such as invalid configuration while creating the stream, you may throw it. 31 | 1. Prefix any errors with the name of your plugin 32 | - For example: `gulp-replace: Cannot do regexp replace on a stream` 33 | - Use [PluginError](https://github.com/gulpjs/plugin-error) module to make this easy 34 | 1. Name your plugin appropriately: it should begin with "gulp-" if it is a gulp plugin 35 | - If it is not a gulp plugin, it should not begin with "gulp-" 36 | 1. The type of `file.contents` should always be the same going out as it was when it came in 37 | - If file.contents is null (non-read) just ignore the file and pass it along 38 | - If file.contents is a Stream and you don't support that just emit an error 39 | - Do not buffer a stream to shoehorn your plugin to work with streams. This will cause horrible things to happen. 40 | 1. Do not pass the `file` object downstream until you are done with it 41 | 1. Use [`file.clone()`](https://github.com/gulpjs/vinyl#clone) when cloning a file or creating a new one based on a file. 42 | 1. Use modules from our [recommended modules page](recommended-modules.md) to make your life easier 43 | 1. Do NOT require `gulp` as a dependency or peerDependency in your plugin 44 | - Using gulp to test or automate your plugin workflow is totally cool, just make sure you put it as a devDependency 45 | - Requiring gulp as a dependency of your plugin means that anyone who installs your plugin is also installing a new gulp and its entire dependency tree. 46 | - There is no reason you should be using gulp within your actual plugin code. If you find yourself doing this open an issue so we can help you out. 47 | 48 | ## Why are these guidelines so strict? 49 | 50 | 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. 51 | 52 | ### What happens if I don't follow them? 53 | 54 | npm is open for everyone, and you are free to make whatever you want but these guidelines were prescribed for a reason. There are acceptance 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". 55 | 56 | ### What does a good plugin look like? 57 | 58 | ```js 59 | // through2 is a thin wrapper around node transform streams 60 | var through = require('through2'); 61 | var PluginError = require('plugin-error'); 62 | 63 | // Consts 64 | const PLUGIN_NAME = 'gulp-prefixer'; 65 | 66 | function prefixStream(prefixText) { 67 | var stream = through(); 68 | stream.write(prefixText); 69 | return stream; 70 | } 71 | 72 | // Plugin level function(dealing with files) 73 | function gulpPrefixer(prefixText) { 74 | 75 | if (!prefixText) { 76 | throw new PluginError(PLUGIN_NAME, 'Missing prefix text!'); 77 | } 78 | prefixText = new Buffer(prefixText); // allocate ahead of time 79 | 80 | // Creating a stream through which each file will pass 81 | return through.obj(function(file, enc, cb) { 82 | if (file.isNull()) { 83 | // return empty file 84 | return cb(null, file); 85 | } 86 | if (file.isBuffer()) { 87 | file.contents = Buffer.concat([prefixText, file.contents]); 88 | } 89 | if (file.isStream()) { 90 | file.contents = file.contents.pipe(prefixStream(prefixText)); 91 | } 92 | 93 | cb(null, file); 94 | 95 | }); 96 | 97 | } 98 | 99 | // Exporting the plugin main function 100 | module.exports = gulpPrefixer; 101 | ``` 102 | -------------------------------------------------------------------------------- /docs/writing-a-plugin/recommended-modules.md: -------------------------------------------------------------------------------- 1 | # Recommended Modules 2 | 3 | > Sticking to this curated list of recommended modules will make sure you don't violate the plugin guidelines and ensure consistency across plugins. 4 | 5 | [Writing a Plugin](README.md) > Recommended Modules 6 | 7 | #### Replacing a file extension 8 | 9 | Use [replace-ext](https://github.com/wearefractal/replace-ext) 10 | 11 | #### Errors 12 | 13 | Use [plugin-error](https://github.com/gulpjs/plugin-error) 14 | 15 | #### String colors 16 | 17 | Use [chalk](https://github.com/sindresorhus/chalk) 18 | 19 | #### Date formatting 20 | 21 | Use [dateformat](https://github.com/felixge/node-dateformat) 22 | 23 | Display as `HH:MM:ss` 24 | -------------------------------------------------------------------------------- /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 | 8 | ## Tooling 9 | 10 | Most plugins use [mocha](https://github.com/mochajs/mocha), [should](https://github.com/shouldjs/should.js) and [event-stream](https://github.com/dominictarr/event-stream) to help them test. The following examples will use these tools. 11 | 12 | 13 | ## Testing plugins for streaming mode 14 | 15 | ```js 16 | var assert = require('assert'); 17 | var es = require('event-stream'); 18 | var File = require('vinyl'); 19 | var prefixer = require('../'); 20 | 21 | describe('gulp-prefixer', function() { 22 | describe('in streaming mode', function() { 23 | 24 | it('should prepend text', function(done) { 25 | 26 | // create the fake file 27 | var fakeFile = new File({ 28 | contents: es.readArray(['stream', 'with', 'those', 'contents']) 29 | }); 30 | 31 | // Create a prefixer plugin stream 32 | var myPrefixer = prefixer('prependthis'); 33 | 34 | // write the fake file to it 35 | myPrefixer.write(fakeFile); 36 | 37 | // wait for the file to come back out 38 | myPrefixer.once('data', function(file) { 39 | // make sure it came out the same way it went in 40 | assert(file.isStream()); 41 | 42 | // buffer the contents to make sure it got prepended to 43 | file.contents.pipe(es.wait(function(err, data) { 44 | // check the contents 45 | assert.equal(data, 'prependthisstreamwiththosecontents'); 46 | done(); 47 | })); 48 | }); 49 | 50 | }); 51 | 52 | }); 53 | }); 54 | ``` 55 | 56 | 57 | ## Testing plugins for buffer mode 58 | 59 | ```js 60 | var assert = require('assert'); 61 | var es = require('event-stream'); 62 | var File = require('vinyl'); 63 | var prefixer = require('../'); 64 | 65 | describe('gulp-prefixer', function() { 66 | describe('in buffer mode', function() { 67 | 68 | it('should prepend text', function(done) { 69 | 70 | // create the fake file 71 | var fakeFile = new File({ 72 | contents: new Buffer('abufferwiththiscontent') 73 | }); 74 | 75 | // Create a prefixer plugin stream 76 | var myPrefixer = prefixer('prependthis'); 77 | 78 | // write the fake file to it 79 | myPrefixer.write(fakeFile); 80 | 81 | // wait for the file to come back out 82 | myPrefixer.once('data', function(file) { 83 | // make sure it came out the same way it went in 84 | assert(file.isBuffer()); 85 | 86 | // check the contents 87 | assert.equal(file.contents.toString('utf8'), 'prependthisabufferwiththiscontent'); 88 | done(); 89 | }); 90 | 91 | }); 92 | 93 | }); 94 | }); 95 | ``` 96 | 97 | 98 | ## Some plugins with high-quality Testing 99 | 100 | * [gulp-cat](https://github.com/ben-eb/gulp-cat/blob/master/test.js) 101 | * [gulp-concat](https://github.com/contra/gulp-concat/blob/master/test/main.js) 102 | -------------------------------------------------------------------------------- /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 PluginError = require('plugin-error'); 13 | 14 | // consts 15 | const PLUGIN_NAME = 'gulp-prefixer'; 16 | 17 | // plugin level function (dealing with files) 18 | function gulpPrefixer(prefixText) { 19 | if (!prefixText) { 20 | throw new PluginError(PLUGIN_NAME, 'Missing prefix text!'); 21 | } 22 | 23 | prefixText = new Buffer(prefixText); // allocate ahead of time 24 | 25 | // creating a stream through which each file will pass 26 | var stream = through.obj(function(file, enc, cb) { 27 | if (file.isStream()) { 28 | this.emit('error', new PluginError(PLUGIN_NAME, 'Streams are not supported!')); 29 | return cb(); 30 | } 31 | 32 | if (file.isBuffer()) { 33 | file.contents = Buffer.concat([prefixText, file.contents]); 34 | } 35 | 36 | // make sure the file goes through the next gulp plugin 37 | this.push(file); 38 | 39 | // tell the stream engine that we are done with this file 40 | cb(); 41 | }); 42 | 43 | // returning the file stream 44 | return stream; 45 | }; 46 | 47 | // exporting the plugin main function 48 | module.exports = gulpPrefixer; 49 | ``` 50 | 51 | The above plugin can be used like this: 52 | 53 | ```js 54 | var gulp = require('gulp'); 55 | var gulpPrefixer = require('gulp-prefixer'); 56 | 57 | gulp.src('files/**/*.js') 58 | .pipe(gulpPrefixer('prepended string')) 59 | .pipe(gulp.dest('modified-files')); 60 | ``` 61 | 62 | ## Handling streams 63 | 64 | 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. 65 | 66 | ## Some plugins based on buffers 67 | 68 | * [gulp-coffee](https://github.com/contra/gulp-coffee) 69 | * [gulp-svgmin](https://github.com/ben-eb/gulp-svgmin) 70 | * [gulp-marked](https://github.com/lmtm/gulp-marked) 71 | * [gulp-svg2ttf](https://github.com/nfroidure/gulp-svg2ttf) 72 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var Undertaker = require('undertaker'); 5 | var vfs = require('vinyl-fs'); 6 | var watch = require('glob-watcher'); 7 | 8 | function Gulp() { 9 | Undertaker.call(this); 10 | 11 | // Bind the functions for destructuring 12 | this.watch = this.watch.bind(this); 13 | this.task = this.task.bind(this); 14 | this.series = this.series.bind(this); 15 | this.parallel = this.parallel.bind(this); 16 | this.registry = this.registry.bind(this); 17 | this.tree = this.tree.bind(this); 18 | this.lastRun = this.lastRun.bind(this); 19 | this.src = this.src.bind(this); 20 | this.dest = this.dest.bind(this); 21 | this.symlink = this.symlink.bind(this); 22 | } 23 | util.inherits(Gulp, Undertaker); 24 | 25 | Gulp.prototype.src = vfs.src; 26 | Gulp.prototype.dest = vfs.dest; 27 | Gulp.prototype.symlink = vfs.symlink; 28 | Gulp.prototype.watch = function(glob, opt, task) { 29 | if (typeof opt === 'string' || typeof task === 'string' || 30 | Array.isArray(opt) || Array.isArray(task)) { 31 | throw new Error('watching ' + glob + ': watch task has to be ' + 32 | 'a function (optionally generated by using gulp.parallel ' + 33 | 'or gulp.series)'); 34 | } 35 | 36 | if (typeof opt === 'function') { 37 | task = opt; 38 | opt = {}; 39 | } 40 | 41 | opt = opt || {}; 42 | 43 | var fn; 44 | if (typeof task === 'function') { 45 | fn = this.parallel(task); 46 | } 47 | 48 | return watch(glob, opt, fn); 49 | }; 50 | 51 | // Let people use this class from our instance 52 | Gulp.prototype.Gulp = Gulp; 53 | 54 | var inst = new Gulp(); 55 | module.exports = inst; 56 | -------------------------------------------------------------------------------- /index.mjs: -------------------------------------------------------------------------------- 1 | import gulp from "./index.js"; 2 | 3 | // These are bound to the gulp instance in our CommonJS file 4 | // so it is okay to reassign them to export 5 | export const watch = gulp.watch; 6 | export const task = gulp.task; 7 | export const series = gulp.series; 8 | export const parallel = gulp.parallel; 9 | export const registry = gulp.registry; 10 | export const tree = gulp.tree; 11 | export const lastRun = gulp.lastRun; 12 | export const src = gulp.src; 13 | export const dest = gulp.dest; 14 | export const symlink = gulp.symlink; 15 | 16 | export default gulp; 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp", 3 | "version": "5.0.1", 4 | "description": "The streaming build system.", 5 | "homepage": "https://gulpjs.com", 6 | "author": "Gulp Team (https://gulpjs.com/)", 7 | "contributors": [ 8 | "Eric Schoffstall ", 9 | "Blaine Bublitz " 10 | ], 11 | "repository": "gulpjs/gulp", 12 | "license": "MIT", 13 | "engines": { 14 | "node": ">=10.13.0" 15 | }, 16 | "main": "index.js", 17 | "exports": { 18 | ".": { 19 | "import": "./index.mjs", 20 | "require": "./index.js" 21 | } 22 | }, 23 | "files": [ 24 | "LICENSE", 25 | "index.js", 26 | "index.mjs", 27 | "bin" 28 | ], 29 | "bin": { 30 | "gulp": "./bin/gulp.js" 31 | }, 32 | "scripts": { 33 | "lint": "eslint .", 34 | "pretest": "npm run lint", 35 | "test": "nyc mocha --async-only" 36 | }, 37 | "dependencies": { 38 | "glob-watcher": "^6.0.0", 39 | "gulp-cli": "^3.1.0", 40 | "undertaker": "^2.0.0", 41 | "vinyl-fs": "^4.0.2" 42 | }, 43 | "devDependencies": { 44 | "eslint": "^7.0.0", 45 | "eslint-config-gulp": "^5.0.0", 46 | "eslint-plugin-node": "^11.1.0", 47 | "expect": "^27.0.0", 48 | "mkdirp": "^3.0.1", 49 | "mocha": "^8.0.0", 50 | "nyc": "^15.0.0", 51 | "rimraf": "^3.0.0" 52 | }, 53 | "nyc": { 54 | "reporter": [ 55 | "lcov", 56 | "text-summary" 57 | ] 58 | }, 59 | "prettier": { 60 | "singleQuote": true 61 | }, 62 | "keywords": [ 63 | "build", 64 | "stream", 65 | "system", 66 | "make", 67 | "tool", 68 | "asset", 69 | "pipeline", 70 | "series", 71 | "parallel", 72 | "streaming" 73 | ] 74 | } 75 | -------------------------------------------------------------------------------- /test/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gulpjs/gulp/0003e9fd25ffc7dd8c4f1a9335c102b73de017c1/test/.gitkeep -------------------------------------------------------------------------------- /test/dest.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | 6 | var expect = require('expect'); 7 | var rimraf = require('rimraf'); 8 | 9 | var gulp = require('../'); 10 | 11 | var outpath = path.join(__dirname, './out-fixtures'); 12 | 13 | describe('gulp.dest()', function() { 14 | before(function () { 15 | if (process.versions.node.startsWith("10.")) { 16 | this.skip(); 17 | return; 18 | } 19 | }); 20 | 21 | beforeEach(rimraf.bind(null, outpath)); 22 | afterEach(rimraf.bind(null, outpath)); 23 | 24 | it('should return a stream', function(done) { 25 | var stream = gulp.dest(path.join(__dirname, './fixtures/')); 26 | expect(stream).toBeDefined(); 27 | expect(stream.on).toBeDefined(); 28 | done(); 29 | }); 30 | 31 | it('should return a output stream that writes files', function(done) { 32 | var instream = gulp.src('./fixtures/**/*.txt', { cwd: __dirname }); 33 | var outstream = gulp.dest(outpath); 34 | instream.pipe(outstream); 35 | 36 | var expectedContents = Buffer.from('this is a test'); 37 | 38 | outstream.on('error', done); 39 | outstream.on('data', function(file) { 40 | // Data should be re-emitted right 41 | expect(file).toBeDefined(); 42 | expect(file.path).toBeDefined(); 43 | expect(file.contents).toBeDefined(); 44 | expect(file.path).toEqual(path.join(outpath, './copy/example.txt')); 45 | expect(file.contents).toEqual(expectedContents); 46 | }); 47 | outstream.on('end', function() { 48 | fs.readFile(path.join(outpath, 'copy', 'example.txt'), function(err, contents) { 49 | expect(err).toBeNull(); 50 | expect(contents).toBeDefined(); 51 | expect(contents).toEqual(expectedContents); 52 | done(); 53 | }); 54 | }); 55 | }); 56 | 57 | it('should return a output stream that does not write non-read files', function(done) { 58 | var instream = gulp.src('./fixtures/**/*.txt', { read: false, cwd: __dirname }); 59 | var outstream = gulp.dest(outpath); 60 | instream.pipe(outstream); 61 | 62 | outstream.on('error', done); 63 | outstream.on('data', function(file) { 64 | // Data should be re-emitted right 65 | expect(file).toBeDefined(); 66 | expect(file.path).toBeDefined(); 67 | expect(file.contents).toBeNull(); 68 | expect(file.path).toEqual(path.join(outpath, './copy/example.txt')); 69 | }); 70 | outstream.on('end', function() { 71 | fs.readFile(path.join(outpath, 'copy', 'example.txt'), function(err, contents) { 72 | expect(err).toBeDefined(); 73 | expect(contents).toBeUndefined(); 74 | done(); 75 | }); 76 | }); 77 | }); 78 | 79 | it('should return a output stream that writes streaming files', function(done) { 80 | var instream = gulp.src('./fixtures/**/*.txt', { buffer: false, cwd: __dirname }); 81 | var outstream = instream.pipe(gulp.dest(outpath)); 82 | 83 | outstream.on('error', done); 84 | outstream.on('data', function(file) { 85 | // Data should be re-emitted right 86 | expect(file).toBeDefined(); 87 | expect(file.path).toBeDefined(); 88 | expect(file.contents).toBeDefined(); 89 | expect(file.path).toEqual(path.join(outpath, './copy/example.txt')); 90 | }); 91 | outstream.on('end', function() { 92 | fs.readFile(path.join(outpath, 'copy', 'example.txt'), function(err, contents) { 93 | expect(err).toBeNull(); 94 | expect(contents).toBeDefined(); 95 | expect(contents).toEqual(Buffer.from('this is a test')); 96 | done(); 97 | }); 98 | }); 99 | }); 100 | 101 | it('should return a output stream that writes streaming files into new directories', function(done) { 102 | testWriteDir({ cwd: __dirname }, done); 103 | }); 104 | 105 | it('should return a output stream that writes streaming files into new directories (buffer: false)', function(done) { 106 | testWriteDir({ buffer: false, cwd: __dirname }, done); 107 | }); 108 | 109 | it('should return a output stream that writes streaming files into new directories (read: false)', function(done) { 110 | testWriteDir({ read: false, cwd: __dirname }, done); 111 | }); 112 | 113 | it('should return a output stream that writes streaming files into new directories (read: false, buffer: false)', function(done) { 114 | testWriteDir({ buffer: false, read: false, cwd: __dirname }, done); 115 | }); 116 | 117 | function testWriteDir(srcOptions, done) { 118 | var instream = gulp.src('./fixtures/stuff', srcOptions); 119 | var outstream = instream.pipe(gulp.dest(outpath)); 120 | 121 | outstream.on('error', done); 122 | outstream.on('data', function(file) { 123 | // Data should be re-emitted right 124 | expect(file).toBeDefined(); 125 | expect(file.path).toBeDefined(); 126 | expect(file.path).toEqual(path.join(outpath, './stuff')); 127 | }); 128 | outstream.on('end', function() { 129 | fs.exists(path.join(outpath, 'stuff'), function(exists) { 130 | expect(exists).toBeDefined(); 131 | done(); 132 | }); 133 | }); 134 | } 135 | 136 | }); 137 | -------------------------------------------------------------------------------- /test/fixtures/copy/example.txt: -------------------------------------------------------------------------------- 1 | this is a test -------------------------------------------------------------------------------- /test/fixtures/gulpfiles/cjs/gulpfile.cjs: -------------------------------------------------------------------------------- 1 | exports.default = function (done) { 2 | done() 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/gulpfiles/mjs/gulpfile.mjs: -------------------------------------------------------------------------------- 1 | import assert from "assert"; 2 | import EventEmitter from "events"; 3 | 4 | import gulp, { 5 | watch, 6 | task, 7 | series, 8 | parallel, 9 | registry, 10 | tree, 11 | lastRun, 12 | src, 13 | dest, 14 | symlink, 15 | } from 'gulp'; 16 | 17 | export default function (done) { 18 | assert(typeof watch === 'function'); 19 | assert(typeof task === 'function'); 20 | assert(typeof series === 'function'); 21 | assert(typeof parallel === 'function'); 22 | assert(typeof registry === 'function'); 23 | assert(typeof tree === 'function'); 24 | assert(typeof lastRun === 'function'); 25 | assert(typeof src === 'function'); 26 | assert(typeof dest === 'function'); 27 | assert(typeof symlink === 'function'); 28 | assert(gulp instanceof EventEmitter); 29 | done(); 30 | } 31 | -------------------------------------------------------------------------------- /test/fixtures/stuff/run.dmc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gulpjs/gulp/0003e9fd25ffc7dd8c4f1a9335c102b73de017c1/test/fixtures/stuff/run.dmc -------------------------------------------------------------------------------- /test/fixtures/stuff/test.dmc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gulpjs/gulp/0003e9fd25ffc7dd8c4f1a9335c102b73de017c1/test/fixtures/stuff/test.dmc -------------------------------------------------------------------------------- /test/fixtures/test.coffee: -------------------------------------------------------------------------------- 1 | this is a test -------------------------------------------------------------------------------- /test/fixtures/test/run.jade: -------------------------------------------------------------------------------- 1 | test template -------------------------------------------------------------------------------- /test/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var cp = require('child_process'); 4 | var path = require('path'); 5 | 6 | var expect = require('expect'); 7 | 8 | var gulp = require('../'); 9 | 10 | describe('gulp', function() { 11 | 12 | describe('hasOwnProperty', function() { 13 | it('src', function(done) { 14 | expect(Object.prototype.hasOwnProperty.call(gulp, 'src')).toEqual(true); 15 | done(); 16 | }); 17 | 18 | it('dest', function(done) { 19 | expect(Object.prototype.hasOwnProperty.call(gulp, 'dest')).toEqual(true); 20 | done(); 21 | }); 22 | 23 | it('symlink', function(done) { 24 | expect(Object.prototype.hasOwnProperty.call(gulp, 'symlink')).toEqual(true); 25 | done(); 26 | }); 27 | 28 | it('watch', function(done) { 29 | expect(Object.prototype.hasOwnProperty.call(gulp, 'watch')).toEqual(true); 30 | done(); 31 | }); 32 | 33 | it('task', function(done) { 34 | expect(Object.prototype.hasOwnProperty.call(gulp, 'task')).toEqual(true); 35 | done(); 36 | }); 37 | 38 | it('series', function(done) { 39 | expect(Object.prototype.hasOwnProperty.call(gulp, 'series')).toEqual(true); 40 | done(); 41 | }); 42 | 43 | it('parallel', function(done) { 44 | expect(Object.prototype.hasOwnProperty.call(gulp, 'parallel')).toEqual(true); 45 | done(); 46 | }); 47 | 48 | it('tree', function(done) { 49 | expect(Object.prototype.hasOwnProperty.call(gulp, 'tree')).toEqual(true); 50 | done(); 51 | }); 52 | 53 | it('lastRun', function(done) { 54 | expect(Object.prototype.hasOwnProperty.call(gulp, 'lastRun')).toEqual(true); 55 | done(); 56 | }); 57 | 58 | it('registry', function(done) { 59 | expect(Object.prototype.hasOwnProperty.call(gulp, 'registry')).toEqual(true); 60 | done(); 61 | }); 62 | }); 63 | 64 | it('can run against gulpfile.cjs', function (done) { 65 | this.timeout(5000); 66 | 67 | var cli = path.join(__dirname, '../bin/gulp.js'); 68 | var opts = { cwd: path.join(__dirname, 'fixtures/gulpfiles/cjs' ) }; 69 | cp.exec('node ' + cli, opts, function (err, stdout, stderr) { 70 | expect(err).toBeNull(); 71 | expect(stdout).toMatch('gulpfile.cjs'); 72 | expect(stderr).toEqual(''); 73 | done(); 74 | }); 75 | }); 76 | 77 | it('can run against gulpfile.mjs', function (done) { 78 | // Node v10 didn't support `exports` in package.json 79 | if (process.version.startsWith('v10.')) { 80 | this.skip(); 81 | } 82 | 83 | this.timeout(5000); 84 | 85 | var cli = path.join(__dirname, '../bin/gulp.js'); 86 | var opts = { cwd: path.join(__dirname, 'fixtures/gulpfiles/mjs' ) }; 87 | cp.exec('node ' + cli, opts, function (err, stdout, stderr) { 88 | expect(err).toBeNull(); 89 | expect(stdout).toMatch('gulpfile.mjs'); 90 | expect(stderr).toEqual(''); 91 | done(); 92 | }); 93 | }); 94 | }); 95 | -------------------------------------------------------------------------------- /test/src.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | 5 | var expect = require('expect'); 6 | 7 | var gulp = require('../'); 8 | 9 | describe('gulp.src()', function() { 10 | before(function () { 11 | if (process.versions.node.startsWith("10.")) { 12 | this.skip(); 13 | return; 14 | } 15 | }); 16 | 17 | it('should return a stream', function(done) { 18 | var stream = gulp.src('./fixtures/*.coffee', { cwd: __dirname }); 19 | expect(stream).toBeDefined(); 20 | expect(stream.on).toBeDefined(); 21 | done(); 22 | }); 23 | 24 | it('should return a input stream from a flat glob', function(done) { 25 | var stream = gulp.src('./fixtures/*.coffee', { cwd: __dirname }); 26 | stream.on('error', done); 27 | stream.on('data', function(file) { 28 | expect(file).toBeDefined(); 29 | expect(file.path).toBeDefined(); 30 | expect(file.contents).toBeDefined(); 31 | expect(file.path).toEqual(path.join(__dirname, './fixtures/test.coffee')); 32 | expect(file.contents).toEqual(Buffer.from('this is a test')); 33 | }); 34 | stream.on('end', function() { 35 | done(); 36 | }); 37 | }); 38 | 39 | it('should return a input stream for multiple globs', function(done) { 40 | var globArray = [ 41 | './fixtures/stuff/run.dmc', 42 | './fixtures/stuff/test.dmc', 43 | ]; 44 | var stream = gulp.src(globArray, { cwd: __dirname }); 45 | 46 | var files = []; 47 | stream.on('error', done); 48 | stream.on('data', function(file) { 49 | expect(file).toBeDefined(); 50 | expect(file.path).toBeDefined(); 51 | files.push(file); 52 | }); 53 | stream.on('end', function() { 54 | expect(files.length).toEqual(2); 55 | expect(files[0].path).toEqual(path.join(__dirname, globArray[0])); 56 | expect(files[1].path).toEqual(path.join(__dirname, globArray[1])); 57 | done(); 58 | }); 59 | }); 60 | 61 | it('should return a input stream for multiple globs, with negation', function(done) { 62 | var expectedPath = path.join(__dirname, './fixtures/stuff/run.dmc'); 63 | var globArray = [ 64 | './fixtures/stuff/*.dmc', 65 | '!fixtures/stuff/test.dmc', 66 | ]; 67 | var stream = gulp.src(globArray, { cwd: __dirname }); 68 | 69 | var files = []; 70 | stream.on('error', done); 71 | stream.on('data', function(file) { 72 | expect(file).toBeDefined(); 73 | expect(file.path).toBeDefined(); 74 | files.push(file); 75 | }); 76 | stream.on('end', function() { 77 | expect(files.length).toEqual(1); 78 | expect(files[0].path).toEqual(expectedPath); 79 | done(); 80 | }); 81 | }); 82 | 83 | it('should return a input stream with no contents when read is false', function(done) { 84 | var stream = gulp.src('./fixtures/*.coffee', { read: false, cwd: __dirname }); 85 | stream.on('error', done); 86 | stream.on('data', function(file) { 87 | expect(file).toBeDefined(); 88 | expect(file.path).toBeDefined(); 89 | expect(file.contents).toBeNull(); 90 | expect(file.path).toEqual(path.join(__dirname, './fixtures/test.coffee')); 91 | }); 92 | stream.on('end', function() { 93 | done(); 94 | }); 95 | }); 96 | 97 | it('should return a input stream with contents as stream when buffer is false', function(done) { 98 | var stream = gulp.src('./fixtures/*.coffee', { buffer: false, cwd: __dirname }); 99 | stream.on('error', done); 100 | stream.on('data', function(file) { 101 | expect(file).toBeDefined(); 102 | expect(file.path).toBeDefined(); 103 | expect(file.contents).toBeDefined(); 104 | var buf = ''; 105 | file.contents.on('data', function(d) { 106 | buf += d; 107 | }); 108 | file.contents.on('end', function() { 109 | expect(buf).toEqual('this is a test'); 110 | done(); 111 | }); 112 | expect(file.path).toEqual(path.join(__dirname, './fixtures/test.coffee')); 113 | }); 114 | }); 115 | 116 | it('should return a input stream from a deep glob', function(done) { 117 | var stream = gulp.src('./fixtures/**/*.jade', { cwd: __dirname }); 118 | stream.on('error', done); 119 | stream.on('data', function(file) { 120 | expect(file).toBeDefined(); 121 | expect(file.path).toBeDefined(); 122 | expect(file.contents).toBeDefined(); 123 | expect(file.path).toEqual(path.join(__dirname, './fixtures/test/run.jade')); 124 | expect(file.contents).toEqual(Buffer.from('test template')); 125 | }); 126 | stream.on('end', function() { 127 | done(); 128 | }); 129 | }); 130 | 131 | it('should return a input stream from a deeper glob', function(done) { 132 | var stream = gulp.src('./fixtures/**/*.dmc', { cwd: __dirname }); 133 | var a = 0; 134 | stream.on('error', done); 135 | stream.on('data', function() { 136 | ++a; 137 | }); 138 | stream.on('end', function() { 139 | expect(a).toEqual(2); 140 | done(); 141 | }); 142 | }); 143 | 144 | it('should return a file stream from a flat path', function(done) { 145 | var a = 0; 146 | var stream = gulp.src(path.join(__dirname, './fixtures/test.coffee')); 147 | stream.on('error', done); 148 | stream.on('data', function(file) { 149 | ++a; 150 | expect(file).toBeDefined(); 151 | expect(file.path).toBeDefined(); 152 | expect(file.contents).toBeDefined(); 153 | expect(file.path).toEqual(path.join(__dirname, './fixtures/test.coffee')); 154 | expect(file.contents).toEqual(Buffer.from('this is a test')); 155 | }); 156 | stream.on('end', function() { 157 | expect(a).toEqual(1); 158 | done(); 159 | }); 160 | }); 161 | }); 162 | --------------------------------------------------------------------------------