├── .editorconfig ├── .eslintrc.json ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── docs ├── API.md ├── CLI.md ├── README.md ├── REPL.md ├── arguments.md └── logging.md ├── examples ├── cli-as-tasks.js ├── functions-as-tasks.js ├── gulpfile.js ├── logging.js ├── pass-arguments.js ├── src │ └── file.js └── task-parameters.js ├── index.js ├── lib ├── cli.js └── util.js ├── package.json └── test ├── cli.js ├── create.js ├── index.js ├── task.js ├── util.js └── watch.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "mocha": true 5 | }, 6 | "rules": { 7 | "strict": 2, 8 | "quotes": [2, "single", "avoid-escape"], 9 | "indent": ["error", 2, {"SwitchCase": 1}], 10 | "eol-last": 2, 11 | "no-shadow": 2, 12 | "dot-notation": 2, 13 | "dot-location": [2, "property"], 14 | "comma-dangle": [2, "never"], 15 | "no-unused-vars": 2, 16 | "keyword-spacing": 2, 17 | "no-multi-spaces": 2, 18 | "no-process-exit": 0, 19 | "keyword-spacing": 2, 20 | "consistent-return": 0, 21 | "space-before-blocks": 2, 22 | "no-use-before-define": 0, 23 | "no-underscore-dangle": 0, 24 | "no-unused-expressions": 2, 25 | "space-before-function-paren": 2 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | test/dir/** 3 | examples/build 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4" 4 | - "stable" 5 | notifications: 6 | email: true 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This software is released under the MIT license: 2 | 3 | Copyright (c) 2014-present Javier Carrillo 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## gulp-runtime [![NPM version][badge-version]][npm] [![downloads][badge-downloads]][npm] 2 | 3 | [![build][badge-build]][travis-build] 4 | 5 | ![image](https://cloud.githubusercontent.com/assets/7457705/19326735/06d81c7e-90cc-11e6-9968-7ae3631326c3.png) 6 | 7 | [documentation](docs/README.md) - 8 | [install](#install) - 9 | [setup](docs/README.md#setup) - 10 | [why](#why) 11 | 12 | ### features 13 | 14 | - [gulp API and more](docs/API.md) 15 | - [customizable logging](docs/logging.md) 16 | - [REPL with autocomplete](docs/REPL.md) 17 | - [Tasks :names with :parameters](docs/task-parameters.md) 18 | - [pass arguments from the task runner](docs/task-arguments.md) 19 | 20 | ### samples 21 | 22 | #### CLI as tasks 23 | 24 | ```js 25 | var gulp = require('gulp-runtime').create(); 26 | 27 | gulp.task('default', ['--tasks', '--version']); 28 | ``` 29 | 30 | #### task :parameters 31 | 32 | ```js 33 | var gulp = require('gulp-runtime').create(); 34 | 35 | gulp.task('build :src :dest', function () { 36 | return gulp.src(this.params.src) 37 | // transform, compress, etc. 38 | .pipe(gulp.dest(this.params.dest)); 39 | }); 40 | 41 | gulp.task('default', 42 | gulp.parallel('build src/**/*.js build') 43 | ); 44 | ``` 45 | 46 | #### passing arguments 47 | 48 | ```js 49 | var gulp = require('gulp-runtime').create(); 50 | 51 | gulp.task('read src', function (callback, src, dest) { 52 | dest = path.join(dest, new Date().toISOString()); 53 | console.log('from', src, 'to', dest); 54 | 55 | var stream = gulp.src(src); 56 | 57 | callback(null, stream, dest); 58 | }); 59 | 60 | gulp.task('write', function (done, stream, dest) { 61 | return stream.pipe(gulp.dest(dest)); 62 | }); 63 | 64 | // the default takes any arguments after '--' from the terminal 65 | gulp.task('default', 66 | gulp.series('read src', 'write') 67 | ); 68 | ``` 69 | 70 | write 71 | 72 | ```js 73 | node gulplfile.js -- src/**/*.js build 74 | ``` 75 | 76 | and arguments after `--` will be passed to the `default` task. 77 | 78 | #### functions as tasks 79 | 80 | Just as gulp#4.0 81 | 82 | ```js 83 | var gulp = require('gulp-runtime').create(); 84 | 85 | function build (done, src, dest) { 86 | console.log('from', src, 'to', dest); 87 | return gulp.src(src) 88 | // some build step 89 | .pipe(gulp.dest(dest)); 90 | } 91 | 92 | function minify (done, src, dest) { 93 | return gulp.src(src) 94 | // minify 95 | .pipe(gulp.dest(dest)); 96 | } 97 | 98 | gulp.task('default', 99 | gulp.series(build, minify) 100 | ); 101 | ``` 102 | 103 | #### split builds in instances 104 | 105 | ```js 106 | var styles = require('gulp-runtime').create(); 107 | 108 | styles.task('less', function (done, sources, dest) { 109 | var less = require('gulp-less'); 110 | var options = require('./build/options'); 111 | 112 | return gulp.src(sources) 113 | .pipe(less(options.less)) 114 | .pipe(gulp.dest(dest)); 115 | }); 116 | 117 | styles.task('default', ['less']); 118 | 119 | exports = module.exports = styles; 120 | ``` 121 | 122 | #### a REPL after `default` has finished 123 | 124 | ```js 125 | var gulp = require('gulp-runtime').create({ repl: true }); 126 | 127 | gulp.task(':number', function (done) { 128 | setTimeout(done, 100); 129 | }); 130 | 131 | gulp.task('default', ['one', 'two']); 132 | ``` 133 | 134 | go to the terminal and do 135 | 136 | ```sh 137 | node gulpfile.js 138 | ``` 139 | 140 | which will run a REPL with the tasks defined. 141 | 142 | ### install 143 | 144 | With [npm][npm] 145 | 146 | ```sh 147 | npm install --save-dev gulp-runtime 148 | ``` 149 | 150 | ### why 151 | 152 | Soon after I started to use `gulp` it came to mind 153 | 154 | > I want a REPL for this 155 | 156 | Mainly because a REPL is the closest to `define and use as you like`. If that was possible then writing task names in this REPL will run them just as doing the same from the command line. 157 | 158 | Then I realized that what I really liked from `gulp` is the way you can bundle and compose async functions and how its this done under the hood. For that I had to try to do it by myself. 159 | 160 | The above has lead to [gulp-repl][gulp-repl], [parth][parth], [runtime][runtime] and finally [gulp-runtime][npm]. 161 | 162 | So yeah, it got out of hand :D. 163 | 164 | But well oh well, here we are. 165 | 166 | ### license 167 | 168 | [![License][badge-license]][license] 169 | 170 | 171 | 172 | [npm]: http://npmjs.com/gulp-runtime 173 | [parth]: http://npmjs.com/parth 174 | [license]: http://opensource.org/licenses/MIT 175 | [vinylFs]: http://npmjs.com/vinyl-fs 176 | [runtime]: http://github.com/stringparser/runtime 177 | [gulp-repl]: http://github.com/stringparser/gulp-repl 178 | [travis-build]: http://travis-ci.org/stringparser/gulp-runtime/builds 179 | 180 | [badge-build]: http://img.shields.io/travis/stringparser/gulp-runtime/master.svg?style=flat-square 181 | [badge-version]: http://img.shields.io/npm/v/gulp-runtime.svg?style=flat-square 182 | [badge-license]: http://img.shields.io/npm/l/gulp-runtime.svg?style=flat-square 183 | [badge-downloads]: http://img.shields.io/npm/dm/gulp-runtime.svg?style=flat-square 184 | -------------------------------------------------------------------------------- /docs/API.md: -------------------------------------------------------------------------------- 1 | [docs](./README.md) - 2 | [API](./API.md) - 3 | [CLI](./CLI.md) - 4 | [REPL](./REPL.md) - 5 | [logging](./logging.md) - 6 | [arguments](./arguments.md) 7 | 8 | # API 9 | 10 | The module has 2 static methods 11 | 12 | [Gulp.create](#gulpcreate) - 13 | [Gulp.createClass](#gulpcreateclass) 14 | 15 | the same [gulp API][gulp-api] methods we know and love 16 | 17 | [gulp.src](#gulptask) - 18 | [gulp.dest](#gulptask) - 19 | [gulp.task](#gulptask) - 20 | [gulp.watch](#gulptask) 21 | 22 | and 3 more to bundle/run tasks 23 | 24 | [gulp.series](#gulpseries) - 25 | [gulp.parallel](#gulpparallel) - 26 | [gulp.stack](#gulpstack) 27 | 28 | ## Static methods 29 | 30 | The module exports a constructor function 31 | 32 | ```js 33 | var Gulp = require('gulp-runtime'); 34 | ``` 35 | 36 | which has two static methods: `Gulp.create` and `Gulp.createClass`. 37 | 38 | ### Gulp.create 39 | 40 | ```js 41 | function create([Object props]) 42 | ``` 43 | 44 | `Gulp.create` returns a new instance with the given `props`. 45 | 46 | Defaults are: 47 | 48 | - `props.log = true` task logging is enabled, pass `false` to disable it 49 | - `props.repl = false` the REPL is disabled, pass `true` to enable it 50 | - `props.wait = false` tasks will run in **parallel** by default. Pass `wait: true` to make **series** the default when running tasks 51 | 52 | - `props.onStackEnd` called when a stack has ended, defaults to empty function 53 | - `props.onHandleEnd` called after a task has ended, defaults to empty function 54 | - `props.onHandleStart` called before a task starts, defaults to empty function 55 | - `props.onHandleError` called when a task throws, defaults to empty function 56 | 57 | These callbacks can be overridden in [`gulp.series`](#gulpseries), [`gulp.parallel`](#gulpparallel) and [`gulp.stack`](#gulpstack) passing an object as a last argument. 58 | 59 | ### Gulp.createClass 60 | 61 | ```js 62 | function createClass([Object mixin]) 63 | ``` 64 | 65 | `Gulp.createClass` returns a new constructor function that inherits from its parent prototype. 66 | 67 | - When `mixin` is given it overrides its parent prototype. 68 | - When `mixin.create` is given it will be used as the instance constructor. 69 | 70 | Example: 71 | 72 | Say we always want to make instances that log and have a REPL. 73 | 74 | ```js 75 | var Gulp = require('gulp-runtime').createClass({ 76 | create: function Gulp (props) { 77 | props = props || {}; 78 | props.log = props.repl = true; 79 | Gulp.super_.call(this, props); 80 | } 81 | }); 82 | 83 | exports = module.exports = Gulp; 84 | ``` 85 | 86 |

Instance methods

87 | 88 | ### gulp.task 89 | 90 | `gulp.src`, `gulp.dest`, `gulp.watch` and `gulp.task` behave the same as described in the [`gulp` API documentation][gulp-api]. 91 | 92 | In addition task names can use `:parameters` (like expressjs routes) and have `arguments` passed from other task or task runner. 93 | 94 | `:parameters` example: 95 | 96 | ```js 97 | var gulp = require('gulp-runtime').create(); 98 | 99 | gulp.task('build:mode', function (done) { 100 | console.log(this.params.mode); 101 | done(); // or do async things 102 | }); 103 | ``` 104 | 105 | `done` will be always passed as first argument to the task. It should be used if the task does not return a stream, promise or [RxJS][RxJS] observable. 106 | 107 | Tasks parameters can also use regular expressions using parens right after the parameter 108 | 109 | ```js 110 | var gulp = require('gulp-runtime').create(); 111 | 112 | gulp.task('build:mode(-dev|-prod)', function (done){ 113 | done(); // or do async things 114 | }); 115 | ``` 116 | 117 | To know more about how this tasks names can be set see [parth][parth]. 118 | 119 | `arguments` example: 120 | 121 | ```js 122 | var gulp = require('gulp-runtime').create(); 123 | 124 | gulp.task('build', function (done, sources, dest) { 125 | var stream = gulp.src(sources) 126 | // some build steps here 127 | 128 | stream.on('end', function () { 129 | // pass stream to next task 130 | done(stream); 131 | }); 132 | }); 133 | 134 | gulp.task('minify', function (done, stream) { 135 | return stream 136 | // minify 137 | .pipe(gulp.dest(dest)); 138 | }); 139 | 140 | gulp.task('build and min', function (done) { 141 | gulp.series('build', 'minify')('src/**/*.js', 'dest/source.min.js', done); 142 | }); 143 | ``` 144 | 145 | ### gulp.start 146 | 147 | Can be used in two ways 148 | 149 | ```js 150 | function start(tasks...) 151 | ``` 152 | 153 | Runs any number of `tasks...` given with the defaults of the instance. 154 | 155 | Each of the `tasks...` can be either a `string` or a `function`. 156 | 157 | Example: 158 | 159 | ```js 160 | var gulp = require('gulp-runtime').create({ wait: true }); 161 | 162 | function build(done){ 163 | done(); // or do async things 164 | } 165 | 166 | gulp.task('thing', function (done){ 167 | setTimeout(done, Math.random()*10); 168 | }); 169 | 170 | gulp.start(build, 'thing'); 171 | // ^ will run in series since the instance was created with `{wait: true}` 172 | ``` 173 | 174 | ```js 175 | function start(Array tasks, args...) 176 | ``` 177 | 178 | Same as `start(tasks...)` but passing the `args...` down to each of the tasks run. 179 | 180 | ### gulp.series 181 | 182 | ```js 183 | function series(tasks...[, Object options]) 184 | ``` 185 | 186 | `series` bundles the given `tasks...` into one async function and returns it. This function will **always** run the `tasks...` in **series**. 187 | 188 | Its sugar on top of [`gulp.stack`][#gulpstack]. See [`gulp.stack`][#gulpstack] for more information about `options`. 189 | 190 | ### gulp.parallel 191 | 192 | ```js 193 | function parallel(tasks...[, Object options]) 194 | ``` 195 | 196 | `parallel` bundles the given `tasks...` into one async function and returns it. This function will **always** run the `tasks...` in **parallel**. 197 | 198 | Its sugar on top of [`gulp.stack`][#gulpstack]. See [`gulp.stack`][#gulpstack] for more information about `options`. 199 | 200 | ### gulp.stack 201 | 202 | ```js 203 | function stack(tasks...[, Object options]) 204 | ``` 205 | 206 | `stack` bundles the given `tasks...` into one async function and returns it. 207 | 208 | Each `tasks...` can be either a `string` or a `function`. 209 | 210 | If given, `options` will override the instance `props` for this `stack`. 211 | 212 | --- 213 | [Back to top ↑](#) 214 | 215 | 216 | 217 | [npm]: https://npmjs.com/gulp-runtime 218 | [gulp]: https://github.com/gulpjs/gulp 219 | [RxJs]: https://github.com/Reactive-Extensions/RxJS 220 | [parth]: https://github.com/stringparser/parth 221 | [license]: http://opensource.org/licenses/MIT 222 | [runtime]: https://github.com/stringparser/runtime 223 | [gulp-api]: https://github.com/gulpjs/gulp/blob/master/docs/API.md 224 | [gulp-repl]: https://github.com/stringparser/gulp-repl 225 | [open-a-issue]: https://github.com/stringparser/gulp-runtime/issues/new 226 | [example-gulpfile]: https://github.com/gulpjs/gulp#sample-gulpfilejs 227 | -------------------------------------------------------------------------------- /docs/CLI.md: -------------------------------------------------------------------------------- 1 | [docs](./README.md) - 2 | [API](./API.md) - 3 | [CLI](./CLI.md) - 4 | [REPL](./REPL.md) - 5 | [logging](./logging.md) - 6 | [arguments](./arguments.md) 7 | 8 | ## CLI 9 | 10 | The initial aim of this project was to be able to run gulp tasks directly from a REPL. But when that was then possible, you would also assumme to be able to run the CLI from the REPL, right? 11 | 12 | For this reason the [gulp cli](https://github.com/gulpjs/gulp/blob/master/docs/CLI.md) commands are set as tasks for each instance. This has turn to be useful to use them directly from other tasks. 13 | 14 | Example: 15 | 16 | ```js 17 | var gulp = require('gulp-runtime').create(); 18 | 19 | gulp.task('info', ['--tasks', '--version']); 20 | // other tasks... 21 | gulp.task('default', ['info']); 22 | ``` 23 | 24 | --- 25 | [Back to top ↑](#) 26 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # documentation 2 | 3 | - [docs](./README.md) 4 | - [API](./API.md) 5 | - [CLI](./CLI.md) 6 | - [REPL](./REPL.md) 7 | - [logging](./logging.md) 8 | - [arguments](./arguments.md) 9 | 10 | ## setup 11 | 12 | 1. Install `npm install --save-dev gulp-runtime` 13 | 14 | 2. Open a `gulpfile`, or [create one][example-gulpfile], and 15 | 16 | change this line 17 | 18 | ```js 19 | var gulp = require('gulp'); 20 | ``` 21 | 22 | with 23 | 24 | ```js 25 | var gulp = require('gulp-runtime').create(); 26 | ``` 27 | 28 | After that run the `gulpfile` with `node` directly from the command line 29 | 30 | ```sh 31 | node gulpfile.js default watch serve 32 | ``` 33 | 34 | Thats it! When no arguments are given the `default` task will run instead. 35 | 36 | 3. What about the CLI? Can I just run `gulp-runtime` from the terminal? 37 | 38 | Yes. For this add an alias to your `.bashrc` or `.zshrc` 39 | 40 | ```sh 41 | alias gulp-runtime='node $(find . -name "gulpfile.js" -not -path "./node_modules/*" | head -n1)' 42 | ``` 43 | 44 | which will use the first `gulpfile.js` found in the current working directory excluding `node_modules`. 45 | 46 | Right after this open a new terminal tab and write 47 | 48 | `gulp-runtime --tasks default watch serve` 49 | 50 | 51 | 52 | [example-gulpfile]: https://github.com/gulpjs/gulp#sample-gulpfilejs 53 | -------------------------------------------------------------------------------- /docs/REPL.md: -------------------------------------------------------------------------------- 1 | [docs](./README.md) - 2 | [API](./API.md) - 3 | [CLI](./CLI.md) - 4 | [REPL](./REPL.md) - 5 | [logging](./logging.md) - 6 | [arguments](./arguments.md) 7 | 8 | # REPL 9 | 10 | ```js 11 | var gulp = require('gulp-runtime').create({ repl: true }); 12 | ``` 13 | 14 | When an instance passes `repl: true` the process running does not exit when all tasks are done. For this case, REPL listening on `stdin`. 15 | 16 | ```sh 17 | $ node gulpfile.js 18 | ``` 19 | 20 | press enter and you will see a prompt `>` that will run the tasks defined in the gulpfile 21 | 22 | ```sh 23 | > 24 | > build less compress png 25 | ``` 26 | 27 | For more information about how tasks are run see [gulp-repl][gulp-repl]. 28 | 29 | --- 30 | [Back to top ↑](#) 31 | 32 | 33 | 34 | [gulp-repl]: https://github.com/stringparser/gulp-repl 35 | -------------------------------------------------------------------------------- /docs/arguments.md: -------------------------------------------------------------------------------- 1 | [docs](./README.md) - 2 | [API](./API.md) - 3 | [CLI](./CLI.md) - 4 | [REPL](./REPL.md) - 5 | [logging](./logging.md) - 6 | [arguments](./arguments.md) 7 | 8 | # Task arguments 9 | 10 | Any of the task runners ([`gulp.start`](./API.md#gulpstart), [`gulp.series`](./API.md#gulpseries), [`gulp.parallel`](./API.md#gulpparallel) and [`gulp.stack`](./API.md#gulpstack)) can be used to pass arguments down. 11 | 12 | ```js 13 | var gulp = require('gulp-runtime').create(); 14 | var args = [1, 2, 3]; 15 | 16 | gulp.task(':name', function (done, one, two, three) { 17 | console.log(one, two, three); 18 | done(null, one + two + three); 19 | }); 20 | 21 | // with gulp.stack 22 | gulp.stack('taskNameHere')(1, 2, 3, function (error, result) { 23 | if (error) { 24 | console.log('ups, who farted?'); 25 | console.log(error.stack); 26 | } else { 27 | console.log('all right, we are done at', result, 'pm today'); 28 | } 29 | }); 30 | 31 | // with gulp.start 32 | gulp.task('default', function (done) { 33 | gulp.start(['taskNameHere'], 1, 2, 3, { 34 | onStackEnd: function (error, result) { 35 | if (error) { 36 | console.log('ups, who farted?'); 37 | console.log(error.stack); 38 | } else { 39 | console.log('all right, we are done at', result, 'pm today'); 40 | } 41 | done(); 42 | } 43 | }) 44 | }); 45 | ``` 46 | 47 | --- 48 | [Back to top ↑](#) 49 | -------------------------------------------------------------------------------- /docs/logging.md: -------------------------------------------------------------------------------- 1 | [docs](./README.md) - 2 | [API](./API.md) - 3 | [CLI](./CLI.md) - 4 | [REPL](./REPL.md) - 5 | [logging](./logging.md) - 6 | [arguments](./arguments.md) 7 | 8 | # logging 9 | 10 | All callbacks passed to [`Gulp.create`](./API.md#gulpcreate) 11 | 12 | - `onHandleStart` 13 | - `onHandleError` 14 | - `onHandleEnd` 15 | - `onStackEnd` 16 | 17 | are used to internally to produce logging. 18 | 19 | They can also be overridden at: 20 | 21 | - class level with [`Gulp.createClass`](./API.md#gulpcreateclass) 22 | - bunlde/run level using one of the composers ([`gulp.start`](./API.md#gulpstart), [`gulp.series`](./API.md#gulpseries), [`gulp.parallel`](./API.md#gulpparallel) and [`gulp.stack`](./API.md#gulpstack)). 23 | 24 | Example: 25 | 26 | ```js 27 | var MyGulp = require('gulp-runtime').createCLass({ 28 | onHandleEnd: function (task) { 29 | console.log('end', task.label); 30 | }, 31 | onHandleStart: function (task) { 32 | console.log('start', task.label); 33 | }, 34 | onHandleError: function (error, task, stack) { 35 | console.log('error!', task.label); 36 | throw error; 37 | } 38 | }); 39 | 40 | var myGulp = MyGulp.create({ 41 | onHandleStart: function (task) { 42 | console.log(task.label, 'ended!'); 43 | } 44 | }); 45 | 46 | myGulp.task(':name', function (done) { 47 | if (this.params && this.params.name === 'three') { 48 | throw new Error('ups, something broke'); 49 | } else { 50 | setTimeout(done, 1000); 51 | } 52 | }); 53 | 54 | myGulp.stack('one', 'two', 'three', { 55 | onHandleError: function (error, task, stack) { 56 | console.log(task.label, 'is dead'); 57 | console.log(error); 58 | } 59 | })(); 60 | ``` 61 | 62 | --- 63 | [Back to top ↑](#) 64 | -------------------------------------------------------------------------------- /examples/cli-as-tasks.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('../.').create(); 4 | 5 | gulp.task('default', ['--tasks', '--version']); 6 | -------------------------------------------------------------------------------- /examples/functions-as-tasks.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('../.').create(); 4 | 5 | function build (done, src, dest) { 6 | console.log('from', src, 'to', dest); 7 | return gulp.src(src) 8 | // some build step 9 | .pipe(gulp.dest(dest)); 10 | } 11 | 12 | function minify (done, src, dest) { 13 | return gulp.src(src) 14 | // minify 15 | .pipe(gulp.dest(dest)); 16 | } 17 | 18 | gulp.task('default', 19 | gulp.series(build, minify) 20 | ); 21 | -------------------------------------------------------------------------------- /examples/gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('../.').create({repl: true}); 4 | 5 | function rand () { 6 | return Math.floor((1000 - 100) * Math.random()); 7 | } 8 | 9 | gulp.task('cssmin', ['autoprefixer'], function (next) { 10 | setTimeout(next, rand()); 11 | }); 12 | 13 | gulp.task('autoprefixer', function (next) { 14 | setTimeout(next, rand()); 15 | }); 16 | 17 | gulp.task('less :src :dest', function (next) { 18 | return gulp.src(this.params.src) 19 | .pipe() 20 | setTimeout(next, rand()); 21 | }); 22 | 23 | gulp.task('less', function (next) { 24 | setTimeout(next, rand()); 25 | }); 26 | 27 | gulp.task('serve', function (next) { 28 | setTimeout(next, rand()); 29 | }); 30 | 31 | gulp.task('watch', function (next) { 32 | setTimeout(next, rand()); 33 | }); 34 | 35 | gulp.task('webpack', function (next) { 36 | setTimeout(next, rand()); 37 | }); 38 | 39 | 40 | gulp.task('default', 41 | gulp.series( 42 | gulp.parallel('less', 'webpack'), 43 | 'cssmin', 'serve', 'watch' 44 | ) 45 | ); 46 | -------------------------------------------------------------------------------- /examples/logging.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var MyGulp = require('../.').createClass({ 4 | onHandleEnd: function (task) { 5 | console.log('end', task.label); 6 | }, 7 | onHandleStart: function (task) { 8 | console.log('start', task.label); 9 | }, 10 | onHandleError: function (error, task, stack) { 11 | console.log('from', stack.tree().label); 12 | console.log('error!', task.label); 13 | throw error; 14 | } 15 | }); 16 | 17 | var myGulp = MyGulp.create({ 18 | onHandleStart: function (task) { 19 | console.log(task.label, 'ended!'); 20 | } 21 | }); 22 | 23 | myGulp.task(':name', function (done) { 24 | if (this.params && this.params.name === 'three') { 25 | throw new Error('ups, something broke'); 26 | } else { 27 | setTimeout(done, 1000); 28 | } 29 | }); 30 | 31 | myGulp.stack('one', 'two', 'three', { 32 | onHandleError: function (error, task) { 33 | console.log(task.label, 'is dead'); 34 | console.log(error); 35 | } 36 | })(); 37 | -------------------------------------------------------------------------------- /examples/pass-arguments.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('../.').create(); 4 | var path = require('path'); 5 | 6 | gulp.task('read src', function (callback, src, dest) { 7 | dest = path.join(dest, new Date().toISOString()); 8 | console.log('from', src, 'to', dest); 9 | var stream = gulp.src(src); 10 | 11 | callback(null, stream, dest); 12 | }); 13 | 14 | gulp.task('write', function (done, stream, dest) { 15 | return stream.pipe(gulp.dest(dest)); 16 | }); 17 | 18 | // the default can take the arguments after '--' from the terminal 19 | gulp.task('default', 20 | gulp.series('read src', 'write') 21 | ); 22 | -------------------------------------------------------------------------------- /examples/src/file.js: -------------------------------------------------------------------------------- 1 | // hello 2 | -------------------------------------------------------------------------------- /examples/task-parameters.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('../.').create(); 4 | 5 | gulp.task('build :src :dest', function () { 6 | return gulp.src(this.params.src) 7 | // transform, compress, etc. 8 | .pipe(gulp.dest(this.params.dest)); 9 | }); 10 | 11 | gulp.task('default', 12 | gulp.parallel('build src/**/*.js build') 13 | ); 14 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var __slice = Array.prototype.slice; 4 | 5 | var Parth = require('parth'); 6 | var Runtime = require('runtime'); 7 | var vinylFS = require('vinyl-fs'); 8 | var globWatch = require('glob-watcher'); 9 | 10 | var util = require('./lib/util'); 11 | var gulpCLI = require('./lib/cli'); 12 | 13 | var Gulp = module.exports = Runtime.createClass({ 14 | src: vinylFS.src, 15 | dest: vinylFS.dest, 16 | create: function Gulp (props) { 17 | Gulp.super_.call(this, props); 18 | this.tasks = new Parth({defaultRE: /\S+/}); 19 | 20 | this.log = this.props.log === void 0 || this.props.log; 21 | this.gulpfile = util.getGulpFile(); 22 | 23 | if (this.log) { 24 | util.log('Using gulpfile', util.format.path(this.gulpfile)); 25 | } 26 | 27 | if (this.props.repl) { 28 | this.repl = Gulp.repl.start(this); 29 | } 30 | 31 | // adds the CLI for this instance and runs it for this 32 | gulpCLI(this); 33 | } 34 | }); 35 | 36 | /** 37 | * static methods 38 | **/ 39 | 40 | Gulp.repl = require('gulp-repl'); 41 | 42 | /** 43 | * instance methods 44 | **/ 45 | 46 | // gulp.task 47 | Gulp.prototype.task = function (name, deps, handle) { 48 | handle = util.type(handle || deps || name).function || ''; 49 | deps = util.type(deps).array || util.type(name).array; 50 | name = util.type(name).string || util.type(handle.name).string; 51 | 52 | if (name && !deps && !handle) { 53 | return (this.tasks.get(name) || {fn: null}).fn; 54 | } 55 | 56 | if (handle && handle.name !== name) { 57 | handle.displayName = name; 58 | } 59 | 60 | if (name && deps && handle) { 61 | var tasks = deps.concat(handle); 62 | var composer = this.series.apply(this, tasks); 63 | this.tasks.set(name, { name: name, fn: composer }); 64 | } else if (name && deps) { 65 | var composer = this.stack.apply(this, deps); 66 | this.tasks.set(name, { name: name, fn: composer }); 67 | } else if (name) { 68 | this.tasks.set(name, { name: name, fn: handle }); 69 | } 70 | 71 | return this; 72 | }; 73 | 74 | // gulp.watch 75 | Gulp.prototype.watch = function (glob, opt, fn) { 76 | var tasks = util.type(opt).array; 77 | var handle = util.type(fn || opt).function; 78 | 79 | if (tasks) { 80 | var composer = this.stack.apply(this, tasks); 81 | return globWatch(glob, function (/* arguments */) { 82 | var args = __slice.call(arguments); 83 | composer.apply(null, handle ? args.concat(handle) : args); 84 | }); 85 | } 86 | 87 | return globWatch(glob, opt, fn); 88 | }; 89 | 90 | // gulp.tree 91 | Gulp.prototype.tree = function (stack, options) { 92 | options = options || {}; 93 | 94 | var self = this; 95 | var tasks = null; 96 | 97 | if (Array.isArray(stack)) { 98 | tasks = util.merge([], { props: stack.props }); 99 | 100 | for (var i = 0, l = stack.length; i < l; ++i) { 101 | tasks = this.reduceStack(tasks, stack[i], i, stack); 102 | } 103 | 104 | } else { 105 | 106 | tasks = Object.keys(this.tasks.store).filter(function (task) { 107 | return !/^\:cli/.test(task); 108 | }).map(function (name) { 109 | return self.tasks.store[name]; 110 | }); 111 | 112 | if (options.simple) { return tasks; } 113 | } 114 | 115 | var tree = {label: options.label || '', nodes: []}; 116 | var depth = options.depth === void 0 || options.depth; 117 | if (depth && typeof depth !== 'number') { depth = 1; } 118 | 119 | tasks.forEach(function (task) { 120 | if (!task || !task.fn) { return; } 121 | var node; 122 | 123 | if (Array.isArray(task.fn.stack)) { 124 | node = self.tree(task.fn.stack, { 125 | host: task, 126 | depth: depth && (depth + 1) || false 127 | }); 128 | } else { 129 | node = { 130 | label: depth == 1 && (task.match || task.name) || task.name 131 | }; 132 | } 133 | 134 | tree.label += (tree.label && ', ' + node.label) || node.label; 135 | tree.nodes.push(node); 136 | }); 137 | 138 | return tree; 139 | }; 140 | 141 | // override of runtime.reduceStack 142 | // maps all the arguments for the gulp.stack to functions 143 | 144 | Gulp.prototype.reduceStack = function (stack, site) { 145 | var task = null; 146 | 147 | if (site && typeof (site.fn || site) === 'function') { 148 | task = site.fn && util.clone(site, true) || { 149 | fn: site, 150 | name: site.displayName || site.name 151 | }; 152 | } else { 153 | task = this.tasks.get(site); 154 | } 155 | 156 | if (!task) { 157 | util.log('Task `%s` is not defined yet', util.format.task(site)); 158 | util.log('Not sure how to do that? See %s', util.docs.task); 159 | 160 | if (util.isFromCLI(this)) { 161 | process.exit(1); 162 | } else { 163 | throw new Error('task not found'); 164 | } 165 | } 166 | 167 | stack.push(task); 168 | 169 | if (Array.isArray(task.fn.stack)) { 170 | task.label = task.name || this.tree(task.fn.stack).label; 171 | } else { 172 | task.label = task.match || task.name; 173 | } 174 | 175 | return stack; 176 | }; 177 | 178 | 179 | /** 180 | * tasks logging 181 | **/ 182 | 183 | Gulp.prototype.onHandleStart = function (task, stack) { 184 | if (!this.log || (task.params && task.params.cli)) { 185 | return; 186 | } 187 | 188 | if (!stack.time) { 189 | stack.time = process.hrtime(); 190 | stack.label = this.tree(stack).label; 191 | 192 | if (Array.isArray(task.fn && task.fn.stack) && task.fn.stack.props) { 193 | stack.match = task.match || task.name || task.displayName; 194 | stack.siteProps = task.fn.stack.props; 195 | } 196 | 197 | util.log('Start', util.format.task(stack)); 198 | } 199 | 200 | task.time = process.hrtime(); 201 | }; 202 | 203 | Gulp.prototype.onHandleEnd = function (task, stack) { 204 | if (this.log && !(task.params && task.params.cli)) { 205 | 206 | if (!(task.fn && Array.isArray(task.fn.stack)) && task.label) { 207 | util.log(' %s took %s', 208 | util.format.task(task), 209 | util.format.time(task.time) 210 | ); 211 | } else if (stack.end) { 212 | util.log('Ended %s after %s', 213 | util.format.task(stack), 214 | util.format.time(stack.time) 215 | ); 216 | } 217 | } 218 | }; 219 | 220 | /** 221 | * error handling 222 | **/ 223 | 224 | Gulp.prototype.onHandleError = function (error, site, stack) { 225 | util.log('%s threw an error after %s', 226 | util.format.error(site && site.label || this.tree(stack).label), 227 | util.format.time(site.time) 228 | ); 229 | 230 | util.log(error.stack); 231 | }; 232 | 233 | /** 234 | * With some sugar on top please 235 | **/ 236 | 237 | Gulp.prototype.start = function (/* arguments */) { 238 | var args = null; 239 | var tasks = arguments; 240 | 241 | if (util.type(arguments[0]).array) { 242 | args = __slice.call(arguments, 1); 243 | tasks = arguments[0]; 244 | } 245 | 246 | var composer = this.stack.apply(this, tasks.length ? tasks : ['default']); 247 | return composer.apply(this, args); 248 | }; 249 | 250 | Gulp.prototype.series = function (/* arguments */) { 251 | var args = __slice.call(arguments); 252 | var props = util.type(args[args.length - 1]).plainObject && args.pop() || {}; 253 | var tasks = args.concat(util.merge(props, {wait: true})); 254 | return this.stack.apply(this, tasks); 255 | }; 256 | 257 | Gulp.prototype.parallel = function (/* arguments */) { 258 | var args = __slice.call(arguments); 259 | var props = util.type(args[args.length - 1]).plainObject && args.pop() || {}; 260 | var tasks = args.concat(util.merge(props, {wait: false})); 261 | return this.stack.apply(this, tasks); 262 | }; 263 | -------------------------------------------------------------------------------- /lib/cli.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('./util'); 4 | var path = require('path'); 5 | 6 | var argv = process.argv.slice(2); 7 | 8 | exports = module.exports = function (gulp) { 9 | 10 | gulp.task(':cli(--silent)', function (next) { 11 | gulp.log = false; 12 | util.log = util.gutil.log = util.silent; 13 | next(); 14 | }); 15 | 16 | gulp.task(':cli(--version|-v)', function (next) { 17 | util.log('gulp-runtime version', require('../package.json').version); 18 | next(); 19 | }); 20 | 21 | gulp.task(':cli(--cwd) :dirname(\\S+)', function (next) { 22 | var dirname = path.resolve(this.params.dirname); 23 | if (process.cwd() !== dirname) { 24 | process.chdir(dirname); 25 | util.log('cwd changed to', util.format.path(dirname)); 26 | } 27 | next(); 28 | }); 29 | 30 | gulp.task(':cli(--no-color|--color)', function (next) { 31 | util.chalk.enabled = Boolean(this.params.cli === '--color'); 32 | if (util.chalk.enabled) { 33 | util.log('colors', util.chalk.green('enabled')); 34 | } 35 | next(); 36 | }); 37 | 38 | gulp.task(':cli(--tasks-simple|--tasks|-T)', function (next) { 39 | var taskTree = gulp.tree({ 40 | label: 'Tasks for ' + util.format.path(gulp.gulpfile), 41 | simple: this.params.cli === '--tasks-simple' 42 | }); 43 | 44 | if (this.params.cli === '--tasks-simple') { 45 | console.log(taskTree.join('\n')); 46 | } else if (gulp.log) { 47 | util.archy(taskTree).trim().split('\n').map(function (line) { 48 | util.log(line); 49 | }); 50 | } 51 | 52 | next(); 53 | }); 54 | 55 | gulp.task(':cli(--require) :file(\\S+)', function (next) { 56 | var filename = path.resolve(this.params.file); 57 | util.log('loading', util.format.path(filename)); 58 | 59 | try { 60 | var filename = require.resolve(filename); 61 | 62 | if (require.cache[filename]) { 63 | delete require.cache[filename]; 64 | } 65 | 66 | require(filename); 67 | } catch (err) { 68 | console.log(util.format.error(err.message)); 69 | } 70 | 71 | next(); 72 | }); 73 | 74 | // skip running tasks if the file was not run from the CLI 75 | if (!util.isFromCLI(gulp)) { 76 | return; 77 | } 78 | 79 | // give some time to have all tasks defined 80 | process.nextTick(function () { 81 | var dirname = path.dirname(gulp.gulpfile); 82 | 83 | // change CWD to the gulpfile's if applies 84 | if (!/(^|[\s])(--cwd)/.test(argv.join(' ')) && process.cwd() !== dirname) { 85 | process.chdir(dirname); 86 | util.log('cwd changed to', util.format.path(dirname)); 87 | } 88 | 89 | var args = []; 90 | var index = argv.indexOf('--'); 91 | 92 | if (index > -1) { 93 | args = argv.slice(index + 1); 94 | argv = argv.slice(0, index); 95 | } 96 | 97 | if (gulp.repl) { 98 | gulp.repl.emit('line', argv.length ? argv.join(' ') : 'default'); 99 | } else { 100 | gulp.start.apply(gulp, [argv].concat(args)); 101 | } 102 | }); 103 | }; 104 | -------------------------------------------------------------------------------- /lib/util.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | 5 | var util = exports = module.exports = {}; 6 | 7 | // dependencies 8 | util.type = require('utils-type'); 9 | util.clone = require('lodash.clone'); 10 | util.archy = require('archy'); 11 | util.merge = require('lodash.merge'); 12 | util.gutil = require('gulp-util'); 13 | util.chalk = util.gutil.colors; 14 | util.tildify = require('tildify'); 15 | util.dateFormat = require('dateformat'); 16 | util.prettyTime = require('pretty-hrtime'); 17 | 18 | // refs 19 | var xargs = process.argv.slice(2).join(' '); 20 | var isSilent = /(^|[\s])(--silent|--tasks-simple)/.test(xargs); 21 | var execFile = path.extname(process.argv[1]) 22 | ? process.argv[1] 23 | : process.argv[1] + '.js'; 24 | 25 | // assorted 26 | util.docs = { 27 | task: 'http://git.io/vVYKS' 28 | }; 29 | 30 | // logging helpers 31 | util.silent = function () {}; 32 | util.logger = function (/* arguments */) { 33 | process.stdout.write(util.format.date()); 34 | console.log.apply(console, arguments); 35 | }; 36 | 37 | // assign the initial logger according to cli args 38 | util.log = util.gutil.log = isSilent ? util.silent : util.logger; 39 | 40 | util.format = { 41 | time: function (hrtime) { 42 | var time = util.prettyTime(process.hrtime(hrtime || [])); 43 | return util.chalk.magenta(time); 44 | }, 45 | path: function (pahtname) { 46 | return util.chalk.magenta(util.tildify(pahtname)); 47 | }, 48 | task: function (task) { 49 | if (task && task.label) { 50 | var name = ''; 51 | var label = task.label.split(',').map(function (taskName) { 52 | return util.chalk.cyan(taskName); 53 | }).join(','); 54 | 55 | if (task.match && task.siteProps) { 56 | name = task.match + ':' + (task.siteProps.wait ? 'series' : 'parallel'); 57 | } else if (task.props) { 58 | name = task.props.wait ? 'parallel' : 'series'; 59 | } 60 | 61 | return name ? name + '(' + label + ')' : label; 62 | } else { 63 | return task; 64 | } 65 | }, 66 | date: function (date) { 67 | var value = util.dateFormat(date || new Date(), 'HH:MM:ss'); 68 | return '[' + util.chalk.grey(value) + '] '; 69 | }, 70 | error: function (value) { 71 | return util.chalk.red(value); 72 | } 73 | }; 74 | 75 | util.isFromCLI = function (gulp) { 76 | return gulp.gulpfile === execFile; 77 | }; 78 | 79 | util.getGulpFile = function () { 80 | var match = new Error().stack.match(/\/([^:()]+)/g); 81 | return match && match[3] || ''; 82 | }; 83 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-runtime", 3 | "version": "2.0.0", 4 | "description": "an alternate interface to vinly-fs", 5 | "author": "Javier Carrillo Milla", 6 | "engines": { 7 | "node": " >= 0.10" 8 | }, 9 | "repository": { 10 | "url": "https://github.com/stringparser/gulp-runtime", 11 | "type": "git" 12 | }, 13 | "keywords": [ 14 | "gulp", 15 | "repl", 16 | "runtime", 17 | "readline", 18 | "terminal", 19 | "alternate", 20 | "interface" 21 | ], 22 | "dependencies": { 23 | "archy": "^1.0.x", 24 | "chalk": "^1.0.x", 25 | "dateformat": "^1.0.x", 26 | "glob-watcher": "^3.0.x", 27 | "gulp-repl": "^2.0.x", 28 | "gulp-util": "^3.0.x", 29 | "lodash.clone": "^4.5.0", 30 | "lodash.merge": "^4.1.x", 31 | "parth": "^4.2.x", 32 | "pretty-hrtime": "^1.0.x", 33 | "runtime": "^0.14.x", 34 | "tildify": "^1.0.x", 35 | "utils-type": "^0.12.x", 36 | "vinyl-fs": "^2.4.x" 37 | }, 38 | "devDependencies": { 39 | "eslint": "^3.3.x", 40 | "mkdirp": "*", 41 | "mocha": "*", 42 | "rimraf": "*", 43 | "should": "*", 44 | "through2": "*" 45 | }, 46 | "scripts": { 47 | "lint": "eslint lib examples *.js", 48 | "test": "npm run lint && mocha test" 49 | }, 50 | "license": "MIT" 51 | } 52 | -------------------------------------------------------------------------------- /test/cli.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | var should = require('should'); 6 | 7 | var test = { 8 | file: path.resolve(__dirname, 'dir', 'cliTestFile.js'), 9 | dirname: path.resolve(__dirname, 'dir'), 10 | content: 'exports = module.exports = { test: "cli" };' 11 | }; 12 | 13 | exports = module.exports = function (Gulp, util) { 14 | 15 | before(function (done) { 16 | util.mkdirp(test.dirname, function (error) { 17 | if (error) { return done(error); } 18 | fs.writeFile(test.file, test.content, done); 19 | }); 20 | }); 21 | 22 | after(function (done) { 23 | util.rimraf(test.dirname, done); 24 | }); 25 | 26 | it('--silent should turn off logging', function (done) { 27 | var gulp = Gulp.create(); 28 | 29 | gulp.log.should.be.eql(true); 30 | 31 | gulp.start(['--silent'], function (error) { 32 | if (error) { return done(error); } 33 | should(gulp.log).be.eql(false); 34 | done(); 35 | }); 36 | }); 37 | 38 | it('--no-color should turn colors off', function (done) { 39 | var gulp = Gulp.create(); 40 | 41 | gulp.start(['--no-color'], function (error) { 42 | if (error) { return done(error); } 43 | should(util.lib.chalk).have.property('enabled', false); 44 | done(); 45 | }); 46 | }); 47 | 48 | it('--color should restore them', function (done) { 49 | var gulp = Gulp.create(); 50 | 51 | gulp.start(['--color'], function (error) { 52 | if (error) { return done(error); } 53 | should(util.lib.chalk).have.property('enabled', true); 54 | done(); 55 | }); 56 | }); 57 | 58 | it('--cwd :dirname should change cwd', function (done) { 59 | var cwd = process.cwd(); 60 | var gulp = Gulp.create(); 61 | 62 | gulp.start(['--cwd ' + test.dirname], function (error) { 63 | if (error) { return done(error); } 64 | should(process.cwd()).be.eql(test.dirname); 65 | process.chdir(cwd); // restore previous working directory 66 | done(); 67 | }); 68 | }); 69 | 70 | it('--require should require a module from the cwd', function (done) { 71 | var cwd = process.cwd(); 72 | var gulp = Gulp.create(); 73 | 74 | should(require.cache).not.have.property(test.file); 75 | 76 | gulp.start(['--require ' + test.file], function (error) { 77 | if (error) { return done(error); } 78 | should(require.cache).have.property(test.file); 79 | process.cwd().should.be.eql(cwd); 80 | done(); 81 | }); 82 | }); 83 | }; 84 | -------------------------------------------------------------------------------- /test/create.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports = module.exports = function (Gulp) { 4 | it('create() should log by default', function () { 5 | var gulp = Gulp.create(); 6 | gulp.log.should.be.eql(true); 7 | }); 8 | 9 | it('create() should have no repl by default', function () { 10 | var gulp = Gulp.create(); 11 | gulp.should.not.have.property('repl'); 12 | }); 13 | 14 | it('create({log: false}) should disable logging', function () { 15 | var gulp = Gulp.create({log: false}); 16 | gulp.props.log.should.be.eql(false); 17 | gulp.log.should.be.eql(false); 18 | }); 19 | 20 | it('create({repl: true}) should make a repl at gulp.repl', function () { 21 | var gulp = Gulp.create({repl: true}); 22 | var readline = require('readline'); 23 | gulp.should.have.property('repl'); 24 | gulp.repl.should.be.instanceof(readline.Interface); 25 | gulp.repl.close(); 26 | }); 27 | }; 28 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('should'); 4 | 5 | var Gulp = require('../'); 6 | var util = require('./util'); 7 | 8 | util.lib.log = function () { 9 | // disable logging for tests 10 | }; 11 | 12 | var tests = [ 13 | 'create.js', 14 | 'task.js', 15 | 'cli.js', 16 | 'watch.js' 17 | ]; 18 | 19 | describe(require('../package').name, function () { 20 | var path = require('path'); 21 | 22 | tests.forEach(function (file) { 23 | var suite = path.basename(file, path.extname(file)); 24 | describe(suite, function () { 25 | require('./' + file)(Gulp, util); 26 | }); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/task.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports = module.exports = function (Gulp) { 4 | var should = require('should'); 5 | 6 | it('task(name, fn) registers task `name`', function () { 7 | var gulp = Gulp.create(); 8 | var handle = function () {}; 9 | gulp.task('name', handle); 10 | gulp.tasks.get('name').fn.should.be.eql(handle); 11 | }); 12 | 13 | it('task([Function: name]) registers task `name`', function () { 14 | var gulp = Gulp.create(); 15 | function name () {} 16 | gulp.task(name); 17 | gulp.tasks.get('name').fn.should.be.eql(name); 18 | }); 19 | 20 | it('task(name, [Function]) registers task `name`', function () { 21 | var gulp = Gulp.create(); 22 | function name () {} 23 | gulp.task('taskName', name); 24 | gulp.tasks.get('taskName').fn.should.be.eql(name); 25 | }); 26 | 27 | it('task(name) returns task\'s function', function () { 28 | var gulp = Gulp.create(); 29 | function name () {} 30 | gulp.task('taskName', name); 31 | gulp.task('taskName').should.be.eql(name); 32 | }); 33 | 34 | it('task(name, deps, fn) deps can be defined after', function (done) { 35 | var gulp = Gulp.create(); 36 | var deps = ['1', '2', '3']; 37 | 38 | gulp.task('taskName', deps, function () {}); 39 | deps.forEach(function (dep) { 40 | (gulp.tasks.get(dep) === null).should.be.eql(true); 41 | }); 42 | 43 | function one () {} 44 | gulp.task('1', one); 45 | gulp.task('1').should.be.eql(one); 46 | 47 | done(); 48 | }); 49 | 50 | it('task(name, deps, fn) non defined deps throw at runtime', function (done) { 51 | var gulp = Gulp.create({log: false}); 52 | var deps = ['1', '2', '3']; 53 | 54 | gulp.task('taskName', deps, function () {}); 55 | gulp.stack('taskName')(function (error) { 56 | should(error).be.instanceof(Error); 57 | done(); 58 | }); 59 | }); 60 | 61 | it('task(name, deps, fn) deps will run before task', function (done) { 62 | var gulp = Gulp.create({log: false}); 63 | 64 | gulp.task('task', ['one', 'two'], function (next, pile) { 65 | pile.push('task'); 66 | next(null, pile); 67 | }); 68 | 69 | gulp.task('one', function (next, pile) { 70 | pile.push('one'); 71 | next(null, pile); 72 | }); 73 | 74 | gulp.task('two', function (next, pile) { 75 | pile.push('two'); 76 | next(null, pile); 77 | }); 78 | 79 | var task = gulp.tasks.get('task'); 80 | 81 | task.fn([], function (err, pile) { 82 | if (err) { return done(err); } 83 | pile.should.containDeep(['one', 'two', 'task']); 84 | pile.pop().should.be.eql('task'); 85 | done(); 86 | }); 87 | }); 88 | 89 | it('task(name, deps) bundles deps into task `name`', function (done) { 90 | var gulp = Gulp.create({ log: false }); 91 | 92 | gulp.task('task', ['one', 'two']); 93 | 94 | gulp.task('one', function (next, pile) { 95 | pile.push('one'); 96 | next(null, pile); 97 | }); 98 | 99 | gulp.task('two', function (next, pile) { 100 | pile.push('two'); 101 | next(null, pile); 102 | }); 103 | 104 | var task = gulp.tasks.get('task'); 105 | 106 | task.fn([], function (err, pile) { 107 | if (err) { return done(err); } 108 | pile.should.be.eql(['one', 'two']); 109 | done(); 110 | }); 111 | }); 112 | }; 113 | -------------------------------------------------------------------------------- /test/util.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | lib: require('../lib/util'), 5 | mkdirp: require('mkdirp'), 6 | rimraf: require('rimraf'), 7 | through: require('through2') 8 | }; 9 | -------------------------------------------------------------------------------- /test/watch.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | 6 | var test = { 7 | file: path.resolve(__dirname, 'dir', 'watchTestFile.js'), 8 | content: 'exports = module.exports = { test: "watchTest" };', 9 | dirname: path.resolve(__dirname, 'dir') 10 | }; 11 | 12 | exports = module.exports = function (Gulp, util) { 13 | 14 | before(function (done) { 15 | util.mkdirp(test.dirname, done); 16 | }); 17 | 18 | after(function (done) { 19 | util.rimraf(test.dirname, done); 20 | }); 21 | 22 | function setupTest (onSuccess, onError) { 23 | fs.writeFile(test.file, test.content, function (writeError) { 24 | if (writeError) { 25 | return onError(writeError); 26 | } 27 | 28 | onSuccess(); 29 | 30 | setTimeout(function () { 31 | fs.writeFile(test.file, '// changed', onError); 32 | }, 10); 33 | }); 34 | } 35 | 36 | it('watch(glob, [Function]) should call function on change', function (done) { 37 | var gulp = Gulp.create({log: false}); 38 | 39 | setupTest(function () { 40 | var watcher = gulp.watch(test.file, function () { 41 | watcher.end(); 42 | done(); 43 | }); 44 | }, done); 45 | }); 46 | 47 | it('watch(glob, tasks) runs tasks in parallel after change', function (done) { 48 | var pile = []; 49 | var gulp = Gulp.create({ 50 | log: false, 51 | onHandleError: done 52 | }); 53 | 54 | gulp.task('one', function (next) { 55 | setTimeout(function () { 56 | pile.push('one'); 57 | if (pile.length > 1) { 58 | pile.should.containDeep(['one', 'two']); 59 | done(); 60 | } 61 | next(); 62 | }, Math.random() * 10); 63 | }); 64 | 65 | gulp.task('two', function (next) { 66 | setTimeout(function () { 67 | pile.push('two'); 68 | if (pile.length > 1) { 69 | pile.should.containDeep(['one', 'two']); 70 | done(); 71 | } 72 | next(); 73 | }, Math.random() * 10); 74 | }); 75 | 76 | setupTest(function () { 77 | var watcher = gulp.watch(test.file, ['one', 'two'], function () { 78 | watcher.end(); 79 | }); 80 | }, done); 81 | }); 82 | 83 | it('watch(glob, tasks, fn) onChange fn runs after tasks', function (done) { 84 | var pile = []; 85 | var gulp = Gulp.create({ 86 | log: false, 87 | onHandleError: done 88 | }); 89 | 90 | gulp.task('one', function (next) { 91 | setTimeout(function () { 92 | pile.push('one'); 93 | next(); 94 | }, Math.random() * 10); 95 | }); 96 | 97 | gulp.task('two', function (next) { 98 | setTimeout(function () { 99 | pile.push('two'); 100 | next(); 101 | }, Math.random() * 10); 102 | }); 103 | 104 | setupTest(function () { 105 | var watcher = gulp.watch(test.file, ['one', 'two'], function () { 106 | pile.should.containDeep(['one', 'two']); 107 | watcher.end(); 108 | done(); 109 | }); 110 | }, done); 111 | }); 112 | }; 113 | --------------------------------------------------------------------------------