add template
This commit is contained in:
+114
@@ -0,0 +1,114 @@
|
||||
### v3.0.0
|
||||
|
||||
**Breaking changes**
|
||||
|
||||
- Support for custom inspect methods was removed. Please follow Node's [recommended practices](https://nodejs.org/api/util.html#util_util_inspect_custom) for creating a custom inspect method.
|
||||
|
||||
### v3.0.0
|
||||
|
||||
- Completely refactored from the ground up.
|
||||
- For the most part, `.task`, `.build`, `.series` and `.parallel` work the same way. However, event handling has changed. Please see the readme for more information and documentation.
|
||||
|
||||
### v2.0.0
|
||||
|
||||
- Now requires Node.js v4.0 or higher
|
||||
|
||||
### v1.0.0
|
||||
|
||||
- Updates the events that are emitted and adds statuses to the objects emitted on the events. see issues [#20](../../issues/20) and [#21](../../issues/21)
|
||||
- Updates the event objects to expose human readable durations. [see issue #23](../../issues/23)
|
||||
- Removes unused properties. [see issue #24](../../issues/24)
|
||||
- Updates `.build` to return a promise when the callback is not passed in. [see issue #28](../../issues/28)
|
||||
|
||||
### v0.14.0
|
||||
|
||||
- Updates [bach][] to `1.0.0`.
|
||||
- Errors emitted from inside a task now have the `'in task "foo":'` prefixed to the error message. [see issue #22](../../issues/22)
|
||||
- Expose `.runInfo` on the task object for use in event listeners and task functions.
|
||||
- Add `.duration` to the `.run/.runInfo` object that shows the duration in a human friendly format. This will also show the current duration from the time the task started to the time it's called if used inside a task function. [see issue #23](../../issues/23)
|
||||
|
||||
```js
|
||||
app.task('foo', function(cb) {
|
||||
console.log(this.runInfo.duration);
|
||||
});
|
||||
```
|
||||
|
||||
### v0.13.0
|
||||
|
||||
- Skip tasks by setting the `options.skip` option to the name of the task or an array of task names.
|
||||
- Making additional `err` properties non-enumerable to cut down on error output.
|
||||
|
||||
### v0.12.0
|
||||
|
||||
- You can no longer get a task from the `.task()` method by passing only the name. Instead do `var task = app.tasks[name];`
|
||||
- Passing only a name and no dependencies to `.task()` will result in a `noop` task being created.
|
||||
- `options` may be passed to `.build()`, `.series()` and `.parallel()`
|
||||
- `options` passed to `.build()` will be merged onto task options before running the task.
|
||||
- Skip tasks by setting their `options.run` option to `false`.
|
||||
|
||||
### v0.11.3
|
||||
|
||||
- Allow passing es2015 javascript generator functions to `.task()`.
|
||||
|
||||
### v0.11.2
|
||||
|
||||
- Allow using glob patterns for task dependencies.
|
||||
|
||||
### v0.11.0
|
||||
|
||||
- **BREAKING CHANGE**: Removed `.watch()`. Watch functionality can be added to [base][] applications using [base-watch][].
|
||||
|
||||
### v0.10.0
|
||||
|
||||
- Removes `session`.
|
||||
|
||||
### v0.9.0
|
||||
|
||||
- Use `default` when no tasks are passed to `.build()`.
|
||||
|
||||
### v0.8.4
|
||||
|
||||
- Ensure task dependencies are unique.
|
||||
|
||||
### v0.8.2
|
||||
|
||||
- Emitting `task` when adding a task through `.task()`
|
||||
- Returning task when calling `.task(name)` with only a name.
|
||||
|
||||
### v0.8.0
|
||||
|
||||
- Emitting `task:*` events instead of generic `*` events. See [event docs](#events) for more information.
|
||||
|
||||
### v0.7.0
|
||||
|
||||
- No longer returning the current task when `.task()` is called without a name.
|
||||
- Throwing an error when `.task()` is called without a name.
|
||||
|
||||
### v0.6.0
|
||||
|
||||
- Adding properties to `err` instances and emitting instead of emitting multiple parameters.
|
||||
- Adding series and parallel flows/methods.
|
||||
|
||||
### v0.5.0
|
||||
|
||||
- **BREAKING CHANGE** Renamed `.run()` to `.build()`
|
||||
|
||||
### v0.4.2
|
||||
|
||||
- `.watch` returns an instance of `FSWatcher`
|
||||
|
||||
### v0.4.1
|
||||
|
||||
- Currently running task returned when calling `.task()` without a name.
|
||||
|
||||
### v0.4.0
|
||||
|
||||
- Add session-cache to enable per-task data contexts.
|
||||
|
||||
### v0.3.0
|
||||
|
||||
- Event bubbling/emitting changed.
|
||||
|
||||
### v0.1.0
|
||||
|
||||
- Initial release.
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-present, Brian Woodward.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
+608
@@ -0,0 +1,608 @@
|
||||
# composer [](https://www.npmjs.com/package/composer) [](https://npmjs.org/package/composer) [](https://npmjs.org/package/composer) [](https://travis-ci.org/doowb/composer) [](https://ci.appveyor.com/project/doowb/composer)
|
||||
|
||||
> Run and compose async tasks. Easily define groups of tasks to run in series or parallel.
|
||||
|
||||
Please consider following this project's author, [Brian Woodward](https://github.com/doowb), and consider starring the project to show your :heart: and support.
|
||||
|
||||
- [Install](#install)
|
||||
- [Usage](#usage)
|
||||
- [API](#api)
|
||||
* [Tasks](#tasks)
|
||||
* [Generators](#generators)
|
||||
- [Events](#events)
|
||||
* [task](#task)
|
||||
* [task-pending](#task-pending)
|
||||
* [task-preparing](#task-preparing)
|
||||
- [Release history](#release-history)
|
||||
- [About](#about)
|
||||
|
||||
_(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc](https://github.com/jonschlinkert/markdown-toc))_
|
||||
|
||||
## Install
|
||||
|
||||
Install with [npm](https://www.npmjs.com/):
|
||||
|
||||
```sh
|
||||
$ npm install --save composer
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
// Create an instance of `Composer`
|
||||
const Composer = require('composer');
|
||||
const composer = new Composer();
|
||||
|
||||
// Define tasks with the .task() method
|
||||
composer.task('foo', callback => {
|
||||
callback(); // do stuff
|
||||
});
|
||||
composer.task('bar', callback => {
|
||||
callback(); // do stuff
|
||||
});
|
||||
|
||||
composer.task('baz', ['foo'. 'bar']);
|
||||
|
||||
// Run tasks with the .build() method
|
||||
composer.build('baz')
|
||||
.then(() => console.log('done!'))
|
||||
.catch(console.error);
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### [.factory](lib/tasks.js#L27)
|
||||
|
||||
Factory for creating a custom `Tasks` class that extends the given `Emitter`. Or, simply call the factory function to use the built-in emitter.
|
||||
|
||||
**Params**
|
||||
|
||||
* `Emitter` **{function}**: Event emitter.
|
||||
* `returns` **{Class}**: Returns a custom `Tasks` class.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
// custom emitter
|
||||
const Emitter = require('events');
|
||||
const Tasks = require('composer/lib/tasks')(Emitter);
|
||||
// built-in emitter
|
||||
const Tasks = require('composer/lib/tasks')();
|
||||
const composer = new Tasks();
|
||||
```
|
||||
|
||||
### [Tasks](lib/tasks.js#L42)
|
||||
|
||||
Create an instance of `Tasks` with the given `options`.
|
||||
|
||||
**Params**
|
||||
|
||||
* `options` **{object}**
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const Tasks = require('composer').Tasks;
|
||||
const composer = new Tasks();
|
||||
```
|
||||
|
||||
### [.task](lib/tasks.js#L86)
|
||||
|
||||
Define a task. Tasks run asynchronously, either in series (by default) or parallel (when `options.parallel` is true). In order for the build to determine when a task is complete, _one of the following_ things must happen: 1) the callback must be called, 2) a promise must be returned, or 3) a stream must be returned. Inside tasks, the "this" object is a composer Task instance created for each task with useful properties like the task name, options and timing information, which can be useful for logging, etc.
|
||||
|
||||
**Params**
|
||||
|
||||
* `name` **{String}**: The task name.
|
||||
* `deps` **{Object|Array|String|Function}**: Any of the following: task dependencies, callback(s), or options object, defined in any order.
|
||||
* `callback` **{Function}**: (optional) If the last argument is a function, it will be called after all of the task's dependencies have been run.
|
||||
* `returns` **{undefined}**
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
// 1. callback
|
||||
app.task('default', cb => {
|
||||
// do stuff
|
||||
cb();
|
||||
});
|
||||
// 2. promise
|
||||
app.task('default', () => {
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
// 3. stream (using vinyl-fs or your stream of choice)
|
||||
app.task('default', function() {
|
||||
return vfs.src('foo/*.js');
|
||||
});
|
||||
```
|
||||
|
||||
### [.build](lib/tasks.js#L209)
|
||||
|
||||
Run one or more tasks.
|
||||
|
||||
**Params**
|
||||
|
||||
* `tasks` **{object|array|string|function}**: One or more tasks to run, options, or callback function. If no tasks are defined, the default task is automatically run.
|
||||
* `callback` **{function}**: (optional)
|
||||
* `returns` **{undefined}**
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const build = app.series(['foo', 'bar', 'baz']);
|
||||
// promise
|
||||
build().then(console.log).catch(console.error);
|
||||
// or callback
|
||||
build(function() {
|
||||
if (err) return console.error(err);
|
||||
});
|
||||
```
|
||||
|
||||
### [.series](lib/tasks.js#L251)
|
||||
|
||||
Compose a function to run the given tasks in series.
|
||||
|
||||
**Params**
|
||||
|
||||
* `tasks` **{object|array|string|function}**: Tasks to run, options, or callback function. If no tasks are defined, the `default` task is automatically run, if one exists.
|
||||
* `callback` **{function}**: (optional)
|
||||
* `returns` **{promise|undefined}**: Returns a promise if no callback is passed.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const build = app.series(['foo', 'bar', 'baz']);
|
||||
// promise
|
||||
build().then(console.log).catch(console.error);
|
||||
// or callback
|
||||
build(function() {
|
||||
if (err) return console.error(err);
|
||||
});
|
||||
```
|
||||
|
||||
### [.parallel](lib/tasks.js#L304)
|
||||
|
||||
Compose a function to run the given tasks in parallel.
|
||||
|
||||
**Params**
|
||||
|
||||
* `tasks` **{object|array|string|function}**: Tasks to run, options, or callback function. If no tasks are defined, the `default` task is automatically run, if one exists.
|
||||
* `callback` **{function}**: (optional)
|
||||
* `returns` **{promise|undefined}**: Returns a promise if no callback is passed.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
// call the returned function to start the build
|
||||
const build = app.parallel(['foo', 'bar', 'baz']);
|
||||
// promise
|
||||
build().then(console.log).catch(console.error);
|
||||
// callback
|
||||
build(function() {
|
||||
if (err) return console.error(err);
|
||||
});
|
||||
// example task usage
|
||||
app.task('default', build);
|
||||
```
|
||||
|
||||
### [.create](lib/tasks.js#L388)
|
||||
|
||||
Static method for creating a custom Tasks class with the given `Emitter.
|
||||
|
||||
**Params**
|
||||
|
||||
* `Emitter` **{Function}**
|
||||
* `returns` **{Class}**: Returns the custom class.
|
||||
|
||||
### [.create](lib/generator.js#L30)
|
||||
|
||||
Static factory method for creating a custom `Composer` class that extends the given `Emitter`.
|
||||
|
||||
**Params**
|
||||
|
||||
* `Emitter` **{Function}**: Event emitter.
|
||||
* `returns` **{Class}**: Returns a custom `Composer` class.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
// Composer extends a basic event emitter by default
|
||||
const Composer = require('composer');
|
||||
const composer = new Composer();
|
||||
|
||||
// Create a custom Composer class with your even emitter of choice
|
||||
const Emitter = require('some-emitter');
|
||||
const CustomComposer = Composer.create(Emitter);
|
||||
const composer = new CustomComposer();
|
||||
```
|
||||
|
||||
**Params**
|
||||
|
||||
* `name` **{String}**
|
||||
* `options` **{Object}**
|
||||
* `returns` **{Object}**: Returns an instance of Composer.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const composer = new Composer();
|
||||
```
|
||||
|
||||
Create a wrapped generator function with the given `name`, `config`, and `fn`.
|
||||
|
||||
**Params**
|
||||
|
||||
* `name` **{String}**
|
||||
* `config` **{Object}**: (optional)
|
||||
* `fn` **{Function}**
|
||||
* `returns` **{Function}**
|
||||
|
||||
Returns true if the given value is a Composer generator object.
|
||||
|
||||
**Params**
|
||||
|
||||
* `val` **{Object}**
|
||||
* `returns` **{Boolean}**
|
||||
|
||||
### [.register](lib/generator.js#L167)
|
||||
|
||||
Alias to `.setGenerator`.
|
||||
|
||||
**Params**
|
||||
|
||||
* `name` **{String}**: The generator's name
|
||||
* `options` **{Object|Function|String}**: or generator
|
||||
* `generator` **{Object|Function|String}**: Generator function, instance or filepath.
|
||||
* `returns` **{Object}**: Returns the generator instance.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
app.register('foo', function(app, base) {
|
||||
// "app" is a private instance created for the generator
|
||||
// "base" is a shared instance
|
||||
});
|
||||
```
|
||||
|
||||
### [.generator](lib/generator.js#L190)
|
||||
|
||||
Get and invoke generator `name`, or register generator `name` with the given `val` and `options`, then invoke and return the generator instance. This method differs from `.register`, which lazily invokes generator functions when `.generate` is called.
|
||||
|
||||
**Params**
|
||||
|
||||
* `name` **{String}**
|
||||
* `fn` **{Function|Object}**: Generator function, instance or filepath.
|
||||
* `returns` **{Object}**: Returns the generator instance or undefined if not resolved.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
app.generator('foo', function(app, options) {
|
||||
// "app" - private instance created for generator "foo"
|
||||
// "options" - options passed to the generator
|
||||
});
|
||||
```
|
||||
|
||||
### [.setGenerator](lib/generator.js#L222)
|
||||
|
||||
Store a generator by file path or instance with the given `name` and `options`.
|
||||
|
||||
**Params**
|
||||
|
||||
* `name` **{String}**: The generator's name
|
||||
* `options` **{Object|Function|String}**: or generator
|
||||
* `generator` **{Object|Function|String}**: Generator function, instance or filepath.
|
||||
* `returns` **{Object}**: Returns the generator instance.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
app.setGenerator('foo', function(app, options) {
|
||||
// "app" - new instance of Generator created for generator "foo"
|
||||
// "options" - options passed to the generator
|
||||
});
|
||||
```
|
||||
|
||||
### [.getGenerator](lib/generator.js#L247)
|
||||
|
||||
Get generator `name` from `app.generators`, same as [findGenerator], but also invokes the returned generator with the current instance. Dot-notation may be used for getting sub-generators.
|
||||
|
||||
**Params**
|
||||
|
||||
* `name` **{String}**: Generator name.
|
||||
* `returns` **{Object|undefined}**: Returns the generator instance or undefined.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const foo = app.getGenerator('foo');
|
||||
|
||||
// get a sub-generator
|
||||
const baz = app.getGenerator('foo.bar.baz');
|
||||
```
|
||||
|
||||
### [.findGenerator](lib/generator.js#L280)
|
||||
|
||||
Find generator `name`, by first searching the cache, then searching the cache of the `base` generator. Use this to get a generator without invoking it.
|
||||
|
||||
**Params**
|
||||
|
||||
* `name` **{String}**
|
||||
* `options` **{Function}**: Optionally supply a rename function on `options.toAlias`
|
||||
* `returns` **{Object|undefined}**: Returns the generator instance if found, or undefined.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
// search by "alias"
|
||||
const foo = app.findGenerator('foo');
|
||||
|
||||
// search by "full name"
|
||||
const foo = app.findGenerator('generate-foo');
|
||||
```
|
||||
|
||||
**Params**
|
||||
|
||||
* `name` **{String}**
|
||||
* `returns` **{Boolean}**
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
console.log(app.hasGenerator('foo'));
|
||||
console.log(app.hasGenerator('foo.bar'));
|
||||
```
|
||||
|
||||
### [.generate](lib/generator.js#L362)
|
||||
|
||||
Run one or more tasks or sub-generators and returns a promise.
|
||||
|
||||
**Params**
|
||||
|
||||
* `name` **{String}**
|
||||
* `tasks` **{String|Array}**
|
||||
* `returns` **{Promise}**
|
||||
|
||||
**Events**
|
||||
|
||||
* `emits`: `generate` with the generator `name` and the array of `tasks` that are queued to run.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
// run tasks `bar` and `baz` on generator `foo`
|
||||
app.generate('foo', ['bar', 'baz']);
|
||||
|
||||
// or use shorthand
|
||||
app.generate('foo:bar,baz');
|
||||
|
||||
// run the `default` task on generator `foo`
|
||||
app.generate('foo');
|
||||
|
||||
// run the `default` task on the `default` generator, if defined
|
||||
app.generate();
|
||||
```
|
||||
|
||||
### [.toAlias](lib/generator.js#L413)
|
||||
|
||||
Create a generator alias from the given `name`. By default, `generate-` is stripped from beginning of the generator name.
|
||||
|
||||
**Params**
|
||||
|
||||
* `name` **{String}**
|
||||
* `options` **{Object}**
|
||||
* `returns` **{String}**: Returns the alias.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
// customize the alias
|
||||
const app = new Generate({ toAlias: require('camel-case') });
|
||||
```
|
||||
|
||||
### [.isGenerators](lib/generator.js#L434)
|
||||
|
||||
Returns true if every name in the given array is a registered generator.
|
||||
|
||||
**Params**
|
||||
|
||||
* `names` **{Array}**
|
||||
* `returns` **{Boolean}**
|
||||
|
||||
### [.formatError](lib/generator.js#L446)
|
||||
|
||||
Format task and generator errors.
|
||||
|
||||
**Params**
|
||||
|
||||
* `name` **{String}**
|
||||
* `returns` **{Error}**
|
||||
|
||||
### [.disableInspect](lib/generator.js#L466)
|
||||
|
||||
Disable inspect. Returns a function to re-enable inspect. Useful for debugging.
|
||||
|
||||
### [.base](lib/generator.js#L504)
|
||||
|
||||
Get the first ancestor instance of Composer. Only works if `generator.parent` is
|
||||
defined on child instances.
|
||||
|
||||
### [.name](lib/generator.js#L517)
|
||||
|
||||
Get or set the generator name.
|
||||
|
||||
**Params**
|
||||
|
||||
* **{String}**
|
||||
|
||||
* `returns` **{String}**
|
||||
|
||||
### [.alias](lib/generator.js#L534)
|
||||
|
||||
Get or set the generator `alias`. By default, the generator alias is created
|
||||
by passing the generator name to the [.toAlias](#toAlias) method.
|
||||
|
||||
**Params**
|
||||
|
||||
* **{String}**
|
||||
|
||||
* `returns` **{String}**
|
||||
|
||||
### [.namespace](lib/generator.js#L551)
|
||||
|
||||
Get the generator namespace. The namespace is created by joining the generator's `alias`
|
||||
to the alias of each ancestor generator.
|
||||
|
||||
**Params**
|
||||
|
||||
* **{String}**
|
||||
|
||||
* `returns` **{String}**
|
||||
|
||||
### [.depth](lib/generator.js#L564)
|
||||
|
||||
Get the depth of a generator - useful for debugging. The root generator
|
||||
has a depth of `0`, sub-generators add `1` for each level of nesting.
|
||||
|
||||
* `returns` **{Number}**
|
||||
|
||||
### [Composer#parse](lib/generator.js#L577)
|
||||
|
||||
Static method that returns a function for parsing task arguments.
|
||||
|
||||
**Params**
|
||||
|
||||
* `register` **{Function}**: Function that receives a name of a task or generator that cannot be found by the parse function. This allows the `register` function to dynamically register tasks or generators.
|
||||
* `returns` **{Function}**: Returns a function for parsing task args.
|
||||
|
||||
### [Composer#isGenerator](lib/generator.js#L590)
|
||||
|
||||
Static method that returns true if the given `val` is an instance of Generate.
|
||||
|
||||
**Params**
|
||||
|
||||
* `val` **{Object}**
|
||||
* `returns` **{Boolean}**
|
||||
|
||||
### [Composer#create](lib/generator.js#L603)
|
||||
|
||||
Static method for creating a custom Composer class with the given `Emitter.
|
||||
|
||||
**Params**
|
||||
|
||||
* `Emitter` **{Function}**
|
||||
* `returns` **{Class}**: Returns the custom class.
|
||||
|
||||
### [Composer#Tasks](lib/generator.js#L617)
|
||||
|
||||
Static getter for getting the Tasks class with the same `Emitter` class as Composer.
|
||||
|
||||
**Params**
|
||||
|
||||
* `Emitter` **{Function}**
|
||||
* `returns` **{Class}**: Returns the Tasks class.
|
||||
|
||||
### [Composer#Task](lib/generator.js#L633)
|
||||
|
||||
Static getter for getting the `Task` class.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const { Task } = require('composer');
|
||||
```
|
||||
|
||||
## Events
|
||||
|
||||
### task
|
||||
|
||||
```js
|
||||
app.on('task', function(task) {
|
||||
switch (task.status) {
|
||||
case 'starting':
|
||||
// Task is running
|
||||
break;
|
||||
case 'finished':
|
||||
// Task is finished running
|
||||
break;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### task-pending
|
||||
|
||||
Emitted after a task is registered.
|
||||
|
||||
### task-preparing
|
||||
|
||||
Emitted when a task is preparing to run, right before it's called. You can use this event to dynamically skip tasks by updating `task.skip` to `true` or a function.
|
||||
|
||||
## Release history
|
||||
|
||||
See the [changelog](./CHANGELOG.md).
|
||||
|
||||
## About
|
||||
|
||||
<details>
|
||||
<summary><strong>Contributing</strong></summary>
|
||||
|
||||
Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Running Tests</strong></summary>
|
||||
|
||||
Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
|
||||
|
||||
```sh
|
||||
$ npm install && npm test
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Building docs</strong></summary>
|
||||
|
||||
_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
|
||||
|
||||
To generate the readme, run the following command:
|
||||
|
||||
```sh
|
||||
$ npm install -g verbose/verb#dev verb-generate-readme && verb
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Related projects
|
||||
|
||||
You might also be interested in these projects:
|
||||
|
||||
* [assemble](https://www.npmjs.com/package/assemble): Get the rocks out of your socks! Assemble makes you fast at creating web projects… [more](https://github.com/assemble/assemble) | [homepage](https://github.com/assemble/assemble "Get the rocks out of your socks! Assemble makes you fast at creating web projects. Assemble is used by thousands of projects for rapid prototyping, creating themes, scaffolds, boilerplates, e-books, UI components, API documentation, blogs, building websit")
|
||||
* [enquirer](https://www.npmjs.com/package/enquirer): Stylish, intuitive and user-friendly prompt system. Fast and lightweight enough for small projects, powerful and… [more](https://github.com/enquirer/enquirer) | [homepage](https://github.com/enquirer/enquirer "Stylish, intuitive and user-friendly prompt system. Fast and lightweight enough for small projects, powerful and extensible enough for the most advanced use cases.")
|
||||
* [generate](https://www.npmjs.com/package/generate): Command line tool and developer framework for scaffolding out new GitHub projects. Generate offers the… [more](https://github.com/generate/generate) | [homepage](https://github.com/generate/generate "Command line tool and developer framework for scaffolding out new GitHub projects. Generate offers the robustness and configurability of Yeoman, the expressiveness and simplicity of Slush, and more powerful flow control and composability than either.")
|
||||
* [update](https://www.npmjs.com/package/update): Be scalable! Update is a new, open source developer framework and CLI for automating updates… [more](https://github.com/update/update) | [homepage](https://github.com/update/update "Be scalable! Update is a new, open source developer framework and CLI for automating updates of any kind in code projects.")
|
||||
* [verb](https://www.npmjs.com/package/verb): Documentation generator for GitHub projects. Verb is extremely powerful, easy to use, and is used… [more](https://github.com/verbose/verb) | [homepage](https://github.com/verbose/verb "Documentation generator for GitHub projects. Verb is extremely powerful, easy to use, and is used on hundreds of projects of all sizes to generate everything from API docs to readmes.")
|
||||
|
||||
### Contributors
|
||||
|
||||
| **Commits** | **Contributor** |
|
||||
| --- | --- |
|
||||
| 227 | [doowb](https://github.com/doowb) |
|
||||
| 72 | [jonschlinkert](https://github.com/jonschlinkert) |
|
||||
|
||||
### Author
|
||||
|
||||
**Brian Woodward**
|
||||
|
||||
* [GitHub Profile](https://github.com/doowb)
|
||||
* [Twitter Profile](https://twitter.com/doowb)
|
||||
* [LinkedIn Profile](https://linkedin.com/in/woodwardbrian)
|
||||
|
||||
### License
|
||||
|
||||
Copyright © 2018, [Brian Woodward](https://github.com/doowb).
|
||||
Released under the [MIT License](LICENSE).
|
||||
|
||||
***
|
||||
|
||||
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.8.0, on November 11, 2018._
|
||||
+1
@@ -0,0 +1 @@
|
||||
module.exports = require('./lib/generator');
|
||||
+645
@@ -0,0 +1,645 @@
|
||||
'use strict';
|
||||
|
||||
const util = require('util');
|
||||
const Task = require('./task');
|
||||
const Tasks = require('./tasks');
|
||||
const parse = require('./parse');
|
||||
const Events = require('events');
|
||||
const { define } = require('./utils');
|
||||
|
||||
/**
|
||||
* Static factory method for creating a custom `Composer` class that
|
||||
* extends the given `Emitter`.
|
||||
*
|
||||
* ```js
|
||||
* // Composer extends a basic event emitter by default
|
||||
* const Composer = require('composer');
|
||||
* const composer = new Composer();
|
||||
*
|
||||
* // Create a custom Composer class with your even emitter of choice
|
||||
* const Emitter = require('some-emitter');
|
||||
* const CustomComposer = Composer.create(Emitter);
|
||||
* const composer = new CustomComposer();
|
||||
* ```
|
||||
* @name .create
|
||||
* @param {Function} `Emitter` Event emitter.
|
||||
* @return {Class} Returns a custom `Composer` class.
|
||||
* @api public
|
||||
*/
|
||||
|
||||
const factory = (Emitter = Events) => {
|
||||
|
||||
/**
|
||||
* Create a new instance of Composer.
|
||||
*
|
||||
* ```js
|
||||
* const composer = new Composer();
|
||||
* ```
|
||||
* @extends EventEmitter
|
||||
* @param {String} `name`
|
||||
* @param {Object} `options`
|
||||
* @return {Object} Returns an instance of Composer.
|
||||
* @api public
|
||||
*/
|
||||
|
||||
class Generator extends Tasks.create(Emitter) {
|
||||
constructor(name, options = {}) {
|
||||
if (name && typeof name !== 'string') {
|
||||
options = name;
|
||||
name = void 0;
|
||||
}
|
||||
// ensure that options aren't passed to generic
|
||||
// emitter to prevent unintended side-effects
|
||||
super(!/(Event)?Emitter/i.test(Emitter.name) ? options : null);
|
||||
if (this.setMaxListeners) this.setMaxListeners(0);
|
||||
this.name = name;
|
||||
this.options = options;
|
||||
this.namespaces = new Map();
|
||||
this.generators = new Map();
|
||||
this.isGenerate = true;
|
||||
if (!this.use) {
|
||||
require('use')(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a wrapped generator function with the given `name`, `config`, and `fn`.
|
||||
*
|
||||
* @param {String} `name`
|
||||
* @param {Object} `config` (optional)
|
||||
* @param {Function} `fn`
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
toGenerator(name, config, fn) {
|
||||
if (typeof config === 'function' || this.isGenerator(config)) {
|
||||
fn = config;
|
||||
config = fn || {};
|
||||
}
|
||||
|
||||
let alias = this.toAlias(name);
|
||||
let generator = options => {
|
||||
if (generator.instance && generator.once !== false) {
|
||||
return generator.instance;
|
||||
}
|
||||
|
||||
let opts = Object.assign({}, config, options);
|
||||
let app = this.isGenerator(fn) ? fn : new this.constructor(opts);
|
||||
this.run(app);
|
||||
|
||||
generator.instance = app;
|
||||
generator.called++;
|
||||
fn.called = generator.called;
|
||||
|
||||
app.createGenerator = generator;
|
||||
app.alias = alias;
|
||||
app.name = name;
|
||||
app.fn = fn;
|
||||
|
||||
define(app, 'parent', this);
|
||||
this.emit('generator', app);
|
||||
|
||||
// emit errors that happen on initialization
|
||||
let listeners = {};
|
||||
let bubble = events => {
|
||||
for (let name of events) {
|
||||
let listener = listeners[name] || (listeners[name] = this.emit.bind(this, name));
|
||||
app.off(name, listener);
|
||||
app.on(name, listener);
|
||||
}
|
||||
};
|
||||
|
||||
bubble(['error', 'task', 'build', 'plugin']);
|
||||
|
||||
if (typeof fn === 'function') {
|
||||
fn.call(app, app, opts);
|
||||
// re-register emitters that we just registered a few lines ago,
|
||||
// to ensure that errors are bubbled up in the correct order
|
||||
bubble(['error', 'task', 'build', 'plugin']);
|
||||
}
|
||||
|
||||
if (opts && opts.once === false) {
|
||||
generator.once = false;
|
||||
}
|
||||
return app;
|
||||
};
|
||||
|
||||
define(generator, 'name', alias);
|
||||
define(generator, 'parent', this);
|
||||
define(generator, 'instance', null);
|
||||
generator.called = 0;
|
||||
generator.isGenerator = true;
|
||||
generator.alias = alias;
|
||||
generator.fn = fn;
|
||||
return generator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given value is a Composer generator object.
|
||||
*
|
||||
* @param {Object} `val`
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
isGenerator(val) {
|
||||
return this.constructor.isGenerator(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias to `.setGenerator`.
|
||||
*
|
||||
* ```js
|
||||
* app.register('foo', function(app, base) {
|
||||
* // "app" is a private instance created for the generator
|
||||
* // "base" is a shared instance
|
||||
* });
|
||||
* ```
|
||||
* @name .register
|
||||
* @param {String} `name` The generator's name
|
||||
* @param {Object|Function|String} `options` or generator
|
||||
* @param {Object|Function|String} `generator` Generator function, instance or filepath.
|
||||
* @return {Object} Returns the generator instance.
|
||||
* @api public
|
||||
*/
|
||||
|
||||
register(...args) {
|
||||
return this.setGenerator(...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get and invoke generator `name`, or register generator `name` with
|
||||
* the given `val` and `options`, then invoke and return the generator
|
||||
* instance. This method differs from `.register`, which lazily invokes
|
||||
* generator functions when `.generate` is called.
|
||||
*
|
||||
* ```js
|
||||
* app.generator('foo', function(app, options) {
|
||||
* // "app" - private instance created for generator "foo"
|
||||
* // "options" - options passed to the generator
|
||||
* });
|
||||
* ```
|
||||
* @name .generator
|
||||
* @param {String} `name`
|
||||
* @param {Function|Object} `fn` Generator function, instance or filepath.
|
||||
* @return {Object} Returns the generator instance or undefined if not resolved.
|
||||
* @api public
|
||||
*/
|
||||
|
||||
generator(name, options, fn) {
|
||||
if (typeof options === 'function') {
|
||||
fn = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
if (typeof fn !== 'function') {
|
||||
return this.getGenerator(name, options);
|
||||
}
|
||||
|
||||
this.setGenerator(name, options, fn);
|
||||
return this.getGenerator(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a generator by file path or instance with the given
|
||||
* `name` and `options`.
|
||||
*
|
||||
* ```js
|
||||
* app.setGenerator('foo', function(app, options) {
|
||||
* // "app" - new instance of Generator created for generator "foo"
|
||||
* // "options" - options passed to the generator
|
||||
* });
|
||||
* ```
|
||||
* @name .setGenerator
|
||||
* @param {String} `name` The generator's name
|
||||
* @param {Object|Function|String} `options` or generator
|
||||
* @param {Object|Function|String} `generator` Generator function, instance or filepath.
|
||||
* @return {Object} Returns the generator instance.
|
||||
* @api public
|
||||
*/
|
||||
|
||||
setGenerator(name, options, fn) {
|
||||
const generator = this.toGenerator(name, options, fn);
|
||||
const alias = generator.alias;
|
||||
this.base.namespaces.set(`${this.namespace}.${alias}`, generator);
|
||||
this.generators.set(alias, generator);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get generator `name` from `app.generators`, same as [findGenerator], but also invokes
|
||||
* the returned generator with the current instance. Dot-notation may be used for getting
|
||||
* sub-generators.
|
||||
*
|
||||
* ```js
|
||||
* const foo = app.getGenerator('foo');
|
||||
*
|
||||
* // get a sub-generator
|
||||
* const baz = app.getGenerator('foo.bar.baz');
|
||||
* ```
|
||||
* @name .getGenerator
|
||||
* @param {String} `name` Generator name.
|
||||
* @return {Object|undefined} Returns the generator instance or undefined.
|
||||
* @api public
|
||||
*/
|
||||
|
||||
getGenerator(name, options) {
|
||||
const fn = this.findGenerator(name);
|
||||
|
||||
if (!this.isGenerator(fn)) {
|
||||
throw this.formatError(name);
|
||||
}
|
||||
|
||||
if (typeof fn === 'function') {
|
||||
// return the generator instance if one has already created,
|
||||
// otherwise call the generator function with the parent instance
|
||||
return fn.instance || fn.call(fn.parent, options);
|
||||
}
|
||||
return fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find generator `name`, by first searching the cache, then searching the
|
||||
* cache of the `base` generator. Use this to get a generator without invoking it.
|
||||
*
|
||||
* ```js
|
||||
* // search by "alias"
|
||||
* const foo = app.findGenerator('foo');
|
||||
*
|
||||
* // search by "full name"
|
||||
* const foo = app.findGenerator('generate-foo');
|
||||
* ```
|
||||
* @name .findGenerator
|
||||
* @param {String} `name`
|
||||
* @param {Function} `options` Optionally supply a rename function on `options.toAlias`
|
||||
* @return {Object|undefined} Returns the generator instance if found, or undefined.
|
||||
* @api public
|
||||
*/
|
||||
|
||||
findGenerator(name) {
|
||||
if (!name) return null;
|
||||
let cached = this.base.namespaces.get(name);
|
||||
let names = [];
|
||||
let app = this;
|
||||
|
||||
if (this.isGenerator(cached)) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
names = typeof name === 'string'
|
||||
? name.split('.').map(n => this.toAlias(n))
|
||||
: name;
|
||||
|
||||
let key = names.join('.');
|
||||
|
||||
if (names.length === 1) {
|
||||
app = this.generators.get(key);
|
||||
|
||||
} else {
|
||||
do {
|
||||
let alias = names.shift();
|
||||
let gen = app.generators.get(alias);
|
||||
|
||||
if (!this.isGenerator(gen)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// only invoke the generator if it's not the last one
|
||||
if (names.length) {
|
||||
app = gen.instance || app.getGenerator(alias);
|
||||
} else {
|
||||
app = gen;
|
||||
}
|
||||
|
||||
} while (app && names.length);
|
||||
}
|
||||
|
||||
return this.isGenerator(app) ? app : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given name is a registered generator. Dot-notation may be
|
||||
* used to check for sub-generators.
|
||||
*
|
||||
* ```js
|
||||
* console.log(app.hasGenerator('foo'));
|
||||
* console.log(app.hasGenerator('foo.bar'));
|
||||
* ```
|
||||
* @param {String} `name`
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
hasGenerator(name) {
|
||||
return this.findGenerator(name) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run one or more tasks or sub-generators and returns a promise.
|
||||
*
|
||||
* ```js
|
||||
* // run tasks `bar` and `baz` on generator `foo`
|
||||
* app.generate('foo', ['bar', 'baz']);
|
||||
*
|
||||
* // or use shorthand
|
||||
* app.generate('foo:bar,baz');
|
||||
*
|
||||
* // run the `default` task on generator `foo`
|
||||
* app.generate('foo');
|
||||
*
|
||||
* // run the `default` task on the `default` generator, if defined
|
||||
* app.generate();
|
||||
* ```
|
||||
* @name .generate
|
||||
* @emits `generate` with the generator `name` and the array of `tasks` that are queued to run.
|
||||
* @param {String} `name`
|
||||
* @param {String|Array} `tasks`
|
||||
* @return {Promise}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
async generate(...args) {
|
||||
let parsed = this.parseTasks(...args);
|
||||
let { tasks, missing, options, callback } = parsed;
|
||||
|
||||
let promise = new Promise(async(resolve, reject) => {
|
||||
if (missing.length > 0) {
|
||||
reject(new Error('Invalid task(s) or generator(s): ' + missing.join(', ')));
|
||||
return;
|
||||
}
|
||||
|
||||
let generator = name => {
|
||||
let app = this.hasGenerator(name) ? this.getGenerator(name, options) : this;
|
||||
this.emit('generate', app);
|
||||
return app;
|
||||
};
|
||||
|
||||
if (options.parallel === true) {
|
||||
let pending = [];
|
||||
for (let ele of tasks) pending.push(generator(ele.name).build(ele.tasks));
|
||||
Promise.all(pending).then(resolve).catch(reject);
|
||||
} else {
|
||||
for (let ele of tasks) {
|
||||
await generator(ele.name).build(ele.tasks).catch(reject);
|
||||
}
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
promise.then(() => callback()).catch(callback);
|
||||
return;
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a generator alias from the given `name`. By default, `generate-`
|
||||
* is stripped from beginning of the generator name.
|
||||
*
|
||||
* ```js
|
||||
* // customize the alias
|
||||
* const app = new Generate({ toAlias: require('camel-case') });
|
||||
* ```
|
||||
* @name .toAlias
|
||||
* @param {String} `name`
|
||||
* @param {Object} `options`
|
||||
* @return {String} Returns the alias.
|
||||
* @api public
|
||||
*/
|
||||
|
||||
toAlias(name, options) {
|
||||
if (typeof options === 'function') {
|
||||
return options(name);
|
||||
}
|
||||
if (options && typeof options.toAlias === 'function') {
|
||||
return options.toAlias(name);
|
||||
}
|
||||
if (typeof this.options.toAlias === 'function') {
|
||||
return this.options.toAlias(name);
|
||||
}
|
||||
return name ? name.replace(/^generate-/, '') : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if every name in the given array is a registered generator.
|
||||
* @name .isGenerators
|
||||
* @param {Array} `names`
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
isGenerators(names) {
|
||||
return names.every(name => this.hasGenerator(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Format task and generator errors.
|
||||
* @name .formatError
|
||||
* @param {String} `name`
|
||||
* @return {Error}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
formatError(name, type = 'generator', appname = 'generator') {
|
||||
let key = this.namespace || 'default';
|
||||
let suffix = '.';
|
||||
|
||||
// if not the base instance, remove the first name segment
|
||||
if (this !== this.base) {
|
||||
key = key.split('.').slice(1).join('.');
|
||||
suffix = ` on ${appname} "${key}"`;
|
||||
}
|
||||
|
||||
let message = `${type} "${name}" is not registered`;
|
||||
return new Error(message + suffix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable inspect. Returns a function to re-enable inspect. Useful for debugging.
|
||||
* @name .disableInspect
|
||||
* @api public
|
||||
*/
|
||||
|
||||
disableInspect() {
|
||||
let inspect = this[util.inspect.custom];
|
||||
this[util.inspect.custom] = void 0;
|
||||
|
||||
return () => {
|
||||
define(this, util.inspect.custom, inspect);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse task arguments into an array of task configuration objects.
|
||||
*/
|
||||
|
||||
parseTasks(...args) {
|
||||
return parse(this.options.register)(this, ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom inspect function
|
||||
*/
|
||||
|
||||
[util.inspect.custom]() {
|
||||
if (typeof this.options.inspectFn === 'function') {
|
||||
return this.options.inspectFn(this);
|
||||
}
|
||||
const names = this.generators ? [...this.generators.keys()].join(', ') : '';
|
||||
const tasks = this.tasks ? [...this.tasks.keys()].join(', ') : '';
|
||||
return `<Generator "${this.namespace}" tasks: [${tasks}], generators: [${names}]>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first ancestor instance of Composer. Only works if `generator.parent` is
|
||||
* defined on child instances.
|
||||
* @name .base
|
||||
* @getter
|
||||
* @api public
|
||||
*/
|
||||
|
||||
get base() {
|
||||
return this.parent ? this.parent.base : this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set the generator name.
|
||||
* @name .name
|
||||
* @getter
|
||||
* @param {String} [name="root"]
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
set name(val) {
|
||||
define(this, '_name', val);
|
||||
}
|
||||
get name() {
|
||||
return this._name || 'generate';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set the generator `alias`. By default, the generator alias is created
|
||||
* by passing the generator name to the [.toAlias](#toAlias) method.
|
||||
* @name .alias
|
||||
* @getter
|
||||
* @param {String} [alias="generate"]
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
set alias(val) {
|
||||
define(this, '_alias', val);
|
||||
}
|
||||
get alias() {
|
||||
return this._alias || this.toAlias(this.name, this.options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the generator namespace. The namespace is created by joining the generator's `alias`
|
||||
* to the alias of each ancestor generator.
|
||||
* @name .namespace
|
||||
* @getter
|
||||
* @param {String} [namespace="root"]
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
get namespace() {
|
||||
return this.parent ? this.parent.namespace + '.' + this.alias : this.alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the depth of a generator - useful for debugging. The root generator
|
||||
* has a depth of `0`, sub-generators add `1` for each level of nesting.
|
||||
* @name .depth
|
||||
* @getter
|
||||
* @return {Number}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
get depth() {
|
||||
return this.parent ? this.parent.depth + 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method that returns a function for parsing task arguments.
|
||||
* @name Composer#parse
|
||||
* @param {Function} `register` Function that receives a name of a task or generator that cannot be found by the parse function. This allows the `register` function to dynamically register tasks or generators.
|
||||
* @return {Function} Returns a function for parsing task args.
|
||||
* @api public
|
||||
* @static
|
||||
*/
|
||||
|
||||
static parseTasks(register) {
|
||||
return parse(register);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method that returns true if the given `val` is an instance of Generate.
|
||||
* @name Composer#isGenerator
|
||||
* @param {Object} `val`
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
* @static
|
||||
*/
|
||||
|
||||
static isGenerator(val) {
|
||||
return val instanceof this || (typeof val === 'function' && val.isGenerator === true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method for creating a custom Composer class with the given `Emitter.
|
||||
* @name Composer#create
|
||||
* @param {Function} `Emitter`
|
||||
* @return {Class} Returns the custom class.
|
||||
* @static
|
||||
* @api public
|
||||
*/
|
||||
|
||||
static create(Emitter) {
|
||||
return factory(Emitter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static getter for getting the Tasks class with the same `Emitter` class as Composer.
|
||||
* @name Composer#Tasks
|
||||
* @param {Function} `Emitter`
|
||||
* @return {Class} Returns the Tasks class.
|
||||
* @getter
|
||||
* @static
|
||||
* @api public
|
||||
*/
|
||||
|
||||
static get Tasks() {
|
||||
return Tasks.create(Emitter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static getter for getting the `Task` class.
|
||||
*
|
||||
* ```js
|
||||
* const { Task } = require('composer');
|
||||
* ```
|
||||
* @name Composer#Task
|
||||
* @getter
|
||||
* @static
|
||||
* @api public
|
||||
*/
|
||||
|
||||
static get Task() {
|
||||
return Task;
|
||||
}
|
||||
}
|
||||
|
||||
return Generator;
|
||||
};
|
||||
|
||||
/**
|
||||
* Expose `factory` function
|
||||
*/
|
||||
|
||||
module.exports = factory();
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
'use strict';
|
||||
|
||||
const { isObject } = require('./utils');
|
||||
const noop = () => {};
|
||||
|
||||
/**
|
||||
* Parse task expressions from the argv._ (splat) array
|
||||
*/
|
||||
|
||||
module.exports = (register = noop) => {
|
||||
return function parse(app, ...rest) {
|
||||
if (rest.length === 1 && Array.isArray(rest[0])) rest = rest[0];
|
||||
let options = rest.find(val => isObject(val) && val.isTask !== true);
|
||||
rest = rest.filter(val => val !== options);
|
||||
|
||||
let callback = rest.find(val => typeof val === 'function');
|
||||
let args = rest.filter(val => val !== options && val !== callback);
|
||||
let opts = { ...app.options, ...options };
|
||||
|
||||
if (typeof args[0] === 'string' && Array.isArray(args[1])) {
|
||||
args = [args[0] + ':' + args[1].join(',')];
|
||||
}
|
||||
|
||||
args = args.join(' ').split(' ');
|
||||
let missing = [];
|
||||
let result = [];
|
||||
|
||||
for (const arg of args) {
|
||||
let segs = arg.split(':');
|
||||
if (segs.length > 2) {
|
||||
throw new SyntaxError('spaces must be used to separate multiple generator names');
|
||||
}
|
||||
|
||||
let tasks = segs[1] ? segs[1].split(',') : segs[0].split(',');
|
||||
let name = segs[1] ? segs[0] : null;
|
||||
let task = { name: null, tasks: [] };
|
||||
|
||||
tasks.forEach(val => {
|
||||
if (!app.tasks.has(val) && app.hasGenerator(name + '.' + val)) {
|
||||
result.push({ name: [name, val].join('.'), tasks: ['default'] });
|
||||
tasks = tasks.filter(v => v !== val);
|
||||
}
|
||||
});
|
||||
|
||||
if (segs.length === 2 && tasks.length) {
|
||||
task.name = name;
|
||||
task.tasks = tasks;
|
||||
result.push(task);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let key of tasks) {
|
||||
if ((!app.tasks.has(key) || app.taskStack.has(key)) && app.hasGenerator(key)) {
|
||||
if (app.name === 'default' && !key.startsWith('default.')) {
|
||||
key = `default.${key}`;
|
||||
}
|
||||
result.push({ name: key, tasks: ['default' ]});
|
||||
} else if (key && app.tasks.has(key)) {
|
||||
task.name = 'default';
|
||||
task.tasks.push(key);
|
||||
} else if (key) {
|
||||
missing.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
if (task.name) {
|
||||
result.push(task);
|
||||
}
|
||||
}
|
||||
|
||||
if (rest.length && !result.length && app.name !== 'default') {
|
||||
if (app.hasGenerator('default')) {
|
||||
return parse(app.getGenerator('default'), ...rest);
|
||||
}
|
||||
}
|
||||
|
||||
if (!rest.length && !result.length && !missing.length) {
|
||||
result = [{ name: 'default', tasks: ['default'] }];
|
||||
}
|
||||
|
||||
if (missing.length && register(missing) === true) {
|
||||
return parse(app, ...rest);
|
||||
}
|
||||
|
||||
register(result.map(task => task.name));
|
||||
|
||||
return { options: opts, callback, tasks: result, missing };
|
||||
};
|
||||
};
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
'use strict';
|
||||
|
||||
const util = require('util');
|
||||
const Events = require('events');
|
||||
const Timer = require('./timer');
|
||||
const { define, noop } = require('./utils');
|
||||
|
||||
class Task extends Events {
|
||||
constructor(task = {}) {
|
||||
if (typeof task.name !== 'string') {
|
||||
throw new TypeError('expected task name to be a string');
|
||||
}
|
||||
super();
|
||||
define(this, 'isTask', true);
|
||||
define(this, 'app', task.app);
|
||||
this.name = task.name;
|
||||
this.status = 'pending';
|
||||
this.options = Object.assign({ deps: [] }, task.options);
|
||||
this.callback = task.callback || noop;
|
||||
this.deps = [...task.deps || [], ...this.options.deps];
|
||||
this.time = new Timer();
|
||||
if (this.setMaxListeners) {
|
||||
this.setMaxListeners(0);
|
||||
}
|
||||
}
|
||||
|
||||
[util.inspect.custom]() {
|
||||
return `<Task "${this.name}" deps: [${this.deps.join(', ')}]>`;
|
||||
}
|
||||
|
||||
run(options) {
|
||||
let finished = false;
|
||||
let orig = Object.assign({}, this.options);
|
||||
this.options = Object.assign({}, this.options, options);
|
||||
this.status = 'preparing';
|
||||
this.emit('preparing', this);
|
||||
|
||||
if (this.skip(options)) {
|
||||
return () => Promise.resolve(null);
|
||||
}
|
||||
|
||||
this.time = new Timer();
|
||||
this.time.start();
|
||||
this.status = 'starting';
|
||||
this.emit('starting', this);
|
||||
|
||||
return () => new Promise(async(resolve, reject) => {
|
||||
const finish = (err, value) => {
|
||||
if (finished) return;
|
||||
finished = true;
|
||||
try {
|
||||
this.options = orig;
|
||||
this.time.end();
|
||||
this.status = 'finished';
|
||||
this.emit('finished', this);
|
||||
if (err) {
|
||||
define(err, 'task', this);
|
||||
reject(err);
|
||||
this.emit('error', err);
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
if (typeof this.callback !== 'function') {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
let res = this.callback.call(this, finish);
|
||||
if (res instanceof Promise) {
|
||||
let val = await res;
|
||||
if (val) res = val;
|
||||
}
|
||||
|
||||
if (isEmitter(res)) {
|
||||
res.on('error', finish);
|
||||
res.on('finish', finish);
|
||||
res.on('end', finish);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.callback.length === 0) {
|
||||
if (res && res.then) {
|
||||
res.then(() => finish());
|
||||
} else {
|
||||
finish(null, res);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
finish(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
skip(options) {
|
||||
let app = this.app || {};
|
||||
let opts = Object.assign({}, app.options, this.options, options);
|
||||
|
||||
if (opts.run === false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Array.isArray(opts.skip)) {
|
||||
return opts.skip.includes(this.name);
|
||||
}
|
||||
|
||||
switch (typeof opts.skip) {
|
||||
case 'boolean':
|
||||
return opts.skip === true;
|
||||
case 'function':
|
||||
return opts.skip(this) === true;
|
||||
case 'string':
|
||||
return opts.skip === this.name;
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isEmitter(val) {
|
||||
return val && (typeof val.on === 'function' || typeof val.pipe === 'function');
|
||||
}
|
||||
|
||||
module.exports = Task;
|
||||
+409
@@ -0,0 +1,409 @@
|
||||
'use strict';
|
||||
|
||||
const Task = require('./task');
|
||||
const Timer = require('./timer');
|
||||
const Events = require('events');
|
||||
const { createOptions, flatten, noop } = require('./utils');
|
||||
|
||||
/**
|
||||
* Factory for creating a custom `Tasks` class that extends the
|
||||
* given `Emitter`. Or, simply call the factory function to use
|
||||
* the built-in emitter.
|
||||
*
|
||||
* ```js
|
||||
* // custom emitter
|
||||
* const Emitter = require('events');
|
||||
* const Tasks = require('composer/lib/tasks')(Emitter);
|
||||
* // built-in emitter
|
||||
* const Tasks = require('composer/lib/tasks')();
|
||||
* const composer = new Tasks();
|
||||
* ```
|
||||
* @name .factory
|
||||
* @param {function} `Emitter` Event emitter.
|
||||
* @return {Class} Returns a custom `Tasks` class.
|
||||
* @api public
|
||||
*/
|
||||
|
||||
const factory = (Emitter = Events) => {
|
||||
|
||||
/**
|
||||
* Create an instance of `Tasks` with the given `options`.
|
||||
*
|
||||
* ```js
|
||||
* const Tasks = require('composer').Tasks;
|
||||
* const composer = new Tasks();
|
||||
* ```
|
||||
* @class
|
||||
* @name Tasks
|
||||
* @param {object} `options`
|
||||
* @api public
|
||||
*/
|
||||
|
||||
class Tasks extends Emitter {
|
||||
constructor(options = {}) {
|
||||
super(!Emitter.name.includes('Emitter') ? options : null);
|
||||
this.options = options;
|
||||
this.taskStack = new Map();
|
||||
this.tasks = new Map();
|
||||
this.taskId = 0;
|
||||
|
||||
if (this.off === void 0 && typeof this.removeListener === 'function') {
|
||||
this.off = this.removeListener.bind(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a task. Tasks run asynchronously, either in series (by default) or parallel
|
||||
* (when `options.parallel` is true). In order for the build to determine when a task is
|
||||
* complete, _one of the following_ things must happen: 1) the callback must be called, 2) a
|
||||
* promise must be returned, or 3) a stream must be returned. Inside tasks, the "this"
|
||||
* object is a composer Task instance created for each task with useful properties like
|
||||
* the task name, options and timing information, which can be useful for logging, etc.
|
||||
*
|
||||
* ```js
|
||||
* // 1. callback
|
||||
* app.task('default', cb => {
|
||||
* // do stuff
|
||||
* cb();
|
||||
* });
|
||||
* // 2. promise
|
||||
* app.task('default', () => {
|
||||
* return Promise.resolve(null);
|
||||
* });
|
||||
* // 3. stream (using vinyl-fs or your stream of choice)
|
||||
* app.task('default', function() {
|
||||
* return vfs.src('foo/*.js');
|
||||
* });
|
||||
* ```
|
||||
* @name .task
|
||||
* @param {String} `name` The task name.
|
||||
* @param {Object|Array|String|Function} `deps` Any of the following: task dependencies, callback(s), or options object, defined in any order.
|
||||
* @param {Function} `callback` (optional) If the last argument is a function, it will be called after all of the task's dependencies have been run.
|
||||
* @return {undefined}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
task(name, ...rest) {
|
||||
if (typeof name !== 'string') {
|
||||
throw new TypeError('expected task "name" to be a string');
|
||||
}
|
||||
const { options, tasks } = createOptions(this, false, ...rest);
|
||||
const callback = typeof tasks[tasks.length - 1] === 'function' ? tasks.pop() : noop;
|
||||
return this.setTask(name, options, tasks, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a task on `app.tasks`
|
||||
* @name .setTask
|
||||
* @param {string} name Task name
|
||||
* @param {object} name Task options
|
||||
* @param {object|array|string|function} `deps` Task dependencies
|
||||
* @param {Function} `callback` (optional) Final callback function to call after all task dependencies have been run.
|
||||
* @return {object} Returns the instance.
|
||||
*/
|
||||
|
||||
setTask(name, options = {}, deps = [], callback) {
|
||||
const task = new Task({ name, options, deps, callback, app: this });
|
||||
const emit = (key = 'task') => this.emit(key, task);
|
||||
task.on('error', this.emit.bind(this, 'error'));
|
||||
task.on('preparing', () => emit('task-preparing'));
|
||||
task.on('starting', task => {
|
||||
this.taskStack.set(task.name, task);
|
||||
emit();
|
||||
});
|
||||
task.on('finished', task => {
|
||||
this.taskStack.delete(task.name);
|
||||
emit();
|
||||
});
|
||||
this.tasks.set(name, task);
|
||||
task.status = 'registered';
|
||||
emit('task-registered');
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a task from `app.tasks`.
|
||||
* @name .getTask
|
||||
* @param {string} name
|
||||
* @return {object} Returns the task object.
|
||||
*/
|
||||
|
||||
getTask(name) {
|
||||
if (!this.tasks.has(name)) {
|
||||
throw this.formatError(name, 'task');
|
||||
}
|
||||
return this.tasks.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if all values in the array are registered tasks.
|
||||
* @name .isTasks
|
||||
* @param {array} tasks
|
||||
* @return {boolean}
|
||||
*/
|
||||
|
||||
isTasks(arr) {
|
||||
return Array.isArray(arr) && arr.every(name => this.tasks.has(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an array of tasks to run by resolving registered tasks from the values
|
||||
* in the given array.
|
||||
* @name .expandTasks
|
||||
* @param {...[string|function|glob]} tasks
|
||||
* @return {array}
|
||||
*/
|
||||
|
||||
expandTasks(...args) {
|
||||
let vals = flatten(args).filter(Boolean);
|
||||
let keys = [...this.tasks.keys()];
|
||||
let tasks = [];
|
||||
|
||||
for (let task of vals) {
|
||||
if (typeof task === 'function') {
|
||||
let name = `task-${this.taskId++}`;
|
||||
this.task(name, task);
|
||||
tasks.push(name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeof task === 'string') {
|
||||
if (/\*/.test(task)) {
|
||||
let matches = match(keys, task);
|
||||
if (matches.length === 0) {
|
||||
throw new Error(`glob "${task}" does not match any registered tasks`);
|
||||
}
|
||||
tasks.push.apply(tasks, matches);
|
||||
continue;
|
||||
}
|
||||
|
||||
tasks.push(task);
|
||||
continue;
|
||||
}
|
||||
|
||||
let msg = 'expected task dependency to be a string or function, but got: ';
|
||||
throw new TypeError(msg + typeof task);
|
||||
}
|
||||
return tasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run one or more tasks.
|
||||
*
|
||||
* ```js
|
||||
* const build = app.series(['foo', 'bar', 'baz']);
|
||||
* // promise
|
||||
* build().then(console.log).catch(console.error);
|
||||
* // or callback
|
||||
* build(function() {
|
||||
* if (err) return console.error(err);
|
||||
* });
|
||||
* ```
|
||||
* @name .build
|
||||
* @param {object|array|string|function} `tasks` One or more tasks to run, options, or callback function. If no tasks are defined, the default task is automatically run.
|
||||
* @param {function} `callback` (optional)
|
||||
* @return {undefined}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
async build(...args) {
|
||||
let state = { status: 'starting', time: new Timer(), app: this };
|
||||
state.time.start();
|
||||
this.emit('build', state);
|
||||
|
||||
args = flatten(args);
|
||||
let cb = typeof args[args.length - 1] === 'function' ? args.pop() : null;
|
||||
|
||||
let { options, tasks } = createOptions(this, true, ...args);
|
||||
if (!tasks.length) tasks = ['default'];
|
||||
|
||||
let each = options.parallel ? this.parallel : this.series;
|
||||
let build = each.call(this, options, ...tasks);
|
||||
let promise = build()
|
||||
.then(() => {
|
||||
state.time.end();
|
||||
state.status = 'finished';
|
||||
this.emit('build', state);
|
||||
});
|
||||
|
||||
return resolveBuild(promise, cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose a function to run the given tasks in series.
|
||||
*
|
||||
* ```js
|
||||
* const build = app.series(['foo', 'bar', 'baz']);
|
||||
* // promise
|
||||
* build().then(console.log).catch(console.error);
|
||||
* // or callback
|
||||
* build(function() {
|
||||
* if (err) return console.error(err);
|
||||
* });
|
||||
* ```
|
||||
* @name .series
|
||||
* @param {object|array|string|function} `tasks` Tasks to run, options, or callback function. If no tasks are defined, the `default` task is automatically run, if one exists.
|
||||
* @param {function} `callback` (optional)
|
||||
* @return {promise|undefined} Returns a promise if no callback is passed.
|
||||
* @api public
|
||||
*/
|
||||
|
||||
series(...args) {
|
||||
let stack = new Set();
|
||||
let compose = this.iterator('series', async(tasks, options, resolve) => {
|
||||
for (let ele of tasks) {
|
||||
let task = this.getTask(ele);
|
||||
task.series = true;
|
||||
|
||||
if (task.skip(options) || stack.has(task)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
task.once('finished', () => stack.delete(task));
|
||||
task.once('starting', () => stack.add(task));
|
||||
let run = task.run(options);
|
||||
|
||||
if (task.deps.length) {
|
||||
let opts = Object.assign({}, options, task.options);
|
||||
let each = opts.parallel ? this.parallel : this.series;
|
||||
let build = each.call(this, ...task.deps);
|
||||
await build();
|
||||
}
|
||||
|
||||
await run();
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
|
||||
return compose(...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose a function to run the given tasks in parallel.
|
||||
*
|
||||
* ```js
|
||||
* // call the returned function to start the build
|
||||
* const build = app.parallel(['foo', 'bar', 'baz']);
|
||||
* // promise
|
||||
* build().then(console.log).catch(console.error);
|
||||
* // callback
|
||||
* build(function() {
|
||||
* if (err) return console.error(err);
|
||||
* });
|
||||
* // example task usage
|
||||
* app.task('default', build);
|
||||
* ```
|
||||
* @name .parallel
|
||||
* @param {object|array|string|function} `tasks` Tasks to run, options, or callback function. If no tasks are defined, the `default` task is automatically run, if one exists.
|
||||
* @param {function} `callback` (optional)
|
||||
* @return {promise|undefined} Returns a promise if no callback is passed.
|
||||
* @api public
|
||||
*/
|
||||
|
||||
parallel(...args) {
|
||||
let stack = new Set();
|
||||
let compose = this.iterator('parallel', (tasks, options, resolve) => {
|
||||
let pending = [];
|
||||
|
||||
for (let ele of tasks) {
|
||||
let task = this.getTask(ele);
|
||||
task.parallel = true;
|
||||
|
||||
if (task.skip(options) || stack.has(task)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
task.once('finished', () => stack.delete(task));
|
||||
task.once('starting', () => stack.add(task));
|
||||
let run = task.run(options);
|
||||
|
||||
if (task.deps.length) {
|
||||
let opts = Object.assign({}, options, task.options);
|
||||
let each = opts.parallel ? this.parallel : this.series;
|
||||
let build = each.call(this, ...task.deps);
|
||||
pending.push(build().then(() => run()));
|
||||
} else {
|
||||
pending.push(run());
|
||||
}
|
||||
}
|
||||
|
||||
resolve(Promise.all(pending));
|
||||
});
|
||||
|
||||
return compose(...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an async iterator function that ensures that either a promise is
|
||||
* returned or the user-provided callback is called.
|
||||
* @param {function} `fn` Function to invoke inside the promise.
|
||||
* @return {function}
|
||||
*/
|
||||
|
||||
iterator(type, fn) {
|
||||
return (...args) => {
|
||||
let { options, tasks } = createOptions(this, true, ...args);
|
||||
|
||||
return cb => {
|
||||
let promise = new Promise(async(resolve, reject) => {
|
||||
if (tasks.length === 0) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let p = fn(tasks, options, resolve);
|
||||
if (type === 'series') await p;
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
|
||||
return resolveBuild(promise, cb);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Format task and generator errors.
|
||||
* @name .formatError
|
||||
* @param {String} `name`
|
||||
* @return {Error}
|
||||
*/
|
||||
|
||||
formatError(name) {
|
||||
return new Error(`task "${name}" is not registered`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method for creating a custom Tasks class with the given `Emitter.
|
||||
* @name .create
|
||||
* @param {Function} `Emitter`
|
||||
* @return {Class} Returns the custom class.
|
||||
* @api public
|
||||
* @static
|
||||
*/
|
||||
|
||||
static create(Emitter) {
|
||||
return factory(Emitter);
|
||||
}
|
||||
}
|
||||
return Tasks;
|
||||
};
|
||||
|
||||
function resolveBuild(promise, cb) {
|
||||
if (typeof cb === 'function') {
|
||||
promise.then(val => cb(null, val)).catch(cb);
|
||||
} else {
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
|
||||
function match(keys, pattern) {
|
||||
let chars = [...pattern].map(ch => ({ '*': '.*?', '.': '\\.' }[ch] || ch));
|
||||
let regex = new RegExp(chars.join(''));
|
||||
return keys.filter(key => regex.test(key));
|
||||
}
|
||||
|
||||
module.exports = factory();
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
'use strict';
|
||||
|
||||
const { nano } = require('./utils');
|
||||
|
||||
class Timer {
|
||||
constructor() {
|
||||
this.date = {};
|
||||
this.hr = {};
|
||||
}
|
||||
|
||||
start() {
|
||||
this.date.start = new Date();
|
||||
this.hr.start = process.hrtime();
|
||||
return this;
|
||||
}
|
||||
|
||||
end() {
|
||||
this.date.end = new Date();
|
||||
this.hr.end = process.hrtime();
|
||||
this.hr.duration = process.hrtime(this.hr.start);
|
||||
return this;
|
||||
}
|
||||
|
||||
get diff() {
|
||||
return nano(this.hr.end) - nano(this.hr.start);
|
||||
}
|
||||
|
||||
get duration() {
|
||||
return this.hr.duration ? require('pretty-time')(this.hr.duration) : '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose `Timer`
|
||||
*/
|
||||
|
||||
module.exports = Timer;
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Get hr time in nanoseconds
|
||||
*/
|
||||
|
||||
const nano = time => +time[0] * 1e9 + +time[1];
|
||||
|
||||
/**
|
||||
* Flatten an array
|
||||
*/
|
||||
|
||||
const flatten = arr => [].concat.apply([], arr);
|
||||
|
||||
/**
|
||||
* Return true if `val` is an object
|
||||
*/
|
||||
|
||||
const isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val);
|
||||
|
||||
/**
|
||||
* Create an options object from the given arguments.
|
||||
* @param {object} `app`
|
||||
* @param {...[function|string|object]} `rest`
|
||||
* @return {object}
|
||||
*/
|
||||
|
||||
const createOptions = (app, expand, ...rest) => {
|
||||
const args = flatten(rest);
|
||||
const config = args.find(val => isObject(val) && !val.isTask) || {};
|
||||
const options = Object.assign({}, app.options, config);
|
||||
const tasks = expand === true
|
||||
? app.expandTasks(args.filter(val => val && val !== config))
|
||||
: args.filter(val => val && val !== config);
|
||||
return { tasks, options };
|
||||
};
|
||||
|
||||
/**
|
||||
* Noop for tasks
|
||||
*/
|
||||
|
||||
const noop = cb => cb();
|
||||
|
||||
/**
|
||||
* Create a non-enumerable property on `obj`
|
||||
*/
|
||||
|
||||
const define = (obj, key, value) => {
|
||||
Reflect.defineProperty(obj, key, {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
writable: true,
|
||||
value
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Expose "utils"
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
createOptions,
|
||||
define,
|
||||
flatten,
|
||||
isObject,
|
||||
nano,
|
||||
noop
|
||||
};
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
{
|
||||
"_from": "composer",
|
||||
"_id": "composer@4.1.0",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-qIIoNYjwFHrQFUdB8kD3pZs30+JeYK9149EpOYr/NVrii00KMO31IonzZMeRSU4qazyWZpEgVzkBmQ6VFWxedA==",
|
||||
"_location": "/composer",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "tag",
|
||||
"registry": true,
|
||||
"raw": "composer",
|
||||
"name": "composer",
|
||||
"escapedName": "composer",
|
||||
"rawSpec": "",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "latest"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"#USER",
|
||||
"/"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/composer/-/composer-4.1.0.tgz",
|
||||
"_shasum": "d8b610a862b0a950aa29e760cec8520d903a051c",
|
||||
"_spec": "composer",
|
||||
"_where": "/home/kevin/Documents/Developer/docker/Popsu_colloque/wwwroot/html/web/themes/custom/popsu_colloque",
|
||||
"author": {
|
||||
"name": "Brian Woodward",
|
||||
"url": "https://github.com/doowb"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/doowb/composer/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Brian Woodward",
|
||||
"url": "https://twitter.com/doowb"
|
||||
},
|
||||
{
|
||||
"name": "Jon Schlinkert",
|
||||
"url": "http://twitter.com/jonschlinkert"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"pretty-time": "^1.1.0",
|
||||
"use": "^3.1.1"
|
||||
},
|
||||
"deprecated": false,
|
||||
"description": "Run and compose async tasks. Easily define groups of tasks to run in series or parallel.",
|
||||
"devDependencies": {
|
||||
"gulp-format-md": "^1.0.0",
|
||||
"minimist": "^1.2.0",
|
||||
"mocha": "^5.2.0",
|
||||
"through2": "^2.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"files": [
|
||||
"index.js",
|
||||
"lib"
|
||||
],
|
||||
"homepage": "https://github.com/doowb/composer",
|
||||
"keywords": [
|
||||
"async",
|
||||
"await",
|
||||
"build",
|
||||
"build-system",
|
||||
"compose",
|
||||
"composer",
|
||||
"composition",
|
||||
"control",
|
||||
"flow",
|
||||
"run",
|
||||
"system",
|
||||
"task",
|
||||
"workflow"
|
||||
],
|
||||
"license": "MIT",
|
||||
"main": "index.js",
|
||||
"name": "composer",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/doowb/composer.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha"
|
||||
},
|
||||
"verb": {
|
||||
"toc": true,
|
||||
"layout": "default",
|
||||
"data": {
|
||||
"author": {
|
||||
"linkedin": "woodwardbrian",
|
||||
"twitter": "doowb"
|
||||
}
|
||||
},
|
||||
"tasks": [
|
||||
"readme"
|
||||
],
|
||||
"plugins": [
|
||||
"gulp-format-md"
|
||||
],
|
||||
"related": {
|
||||
"list": [
|
||||
"enquirer",
|
||||
"assemble",
|
||||
"generate",
|
||||
"update",
|
||||
"verb"
|
||||
]
|
||||
},
|
||||
"lint": {
|
||||
"reflinks": true
|
||||
}
|
||||
},
|
||||
"version": "4.1.0"
|
||||
}
|
||||
Reference in New Issue
Block a user