daizong
Version:
`package.json` script runner for ES Modules
584 lines (467 loc) • 11.3 kB
Markdown
# daizong 🏃♂️
[](https://github.com/mgenware/daizong/actions)
[](https://npmjs.com/package/daizong)
[](https://nodejs.org/en/)
`package.json` script runner for ES Modules. daizong supports the following features out of the box:
- Run tasks sequentially or in parallel
- Built-in commands (create directories, delete files and directories)
- Environment variables
- Set environment variables for a specific task
- Set default environment variables for all tasks
- Define groups of environment variables to be inherited by tasks
- Define tasks in groups
- Private tasks
- Allow continue-on-error
- `before` and `after` fields
### Breaking changes
#### 0.20.0+
To avoid ambiguity between task names and arguments passed to tasks. Starting from 0.20.0, a task path has to be separated by `-` instead of a space:
```sh
# Prior to 0.20.0
daizong build windows --args # Don't use, deprecated.
# 0.20.0+
daizong build-windows --args
```
## Usage
- Install daizong as a dev dependency `npm i -D daizong` (you can skip this if you want daizong to be installed globally).
- Create a `daizong.config.js` to define tasks (see examples below).
- Use `npx daizong <task> --arg1 --arg2` to run a specific task.
<blockquote>
If you'd like to run daizong scripts in a shorter way, install daizong globally:
```sh
npm i -g daizong
```
Now instead of `npx daizong <task>`, you can do:
```sh
dz <task>
```
Note that the global `dz` command works regardless of whether daizong is installed locally or not.
</blockquote>
## Examples (daizong vs npm scripts)
`package.json`:
```json
{
"scripts": {
"dev": "tsc -b src -w"
}
}
```
daizong:
```js
export default {
dev: 'tsc -b src -w',
};
```
### Run tasks sequentially
`package.json`:
```json
{
"scripts": {
"dev": "touch a.md && touch b.md"
}
}
```
daizong:
```js
export default {
dev: ['touch a.md', 'touch b.md'],
};
```
<blockquote>
### Shorthand and full task definitions
Most tasks you see above are defined as a command string or an array of command strings:
```js
export default {
dev: ['touch a.md', 'touch b.md'],
};
```
This is a shorthand task definition. As we're moving to more daizong features, the shorthand task definition is no longer suited and we can switch to its full definition:
```js
export default {
// Shorthand task definition is either a string or an array of strings.
task1: 'echo hi',
// The `task1` above can be rewritten in its full form.
task1: {
run: 'echo hi',
/* More task settings can be used here. */
},
};
```
</blockquote>
### Run tasks in parallel
We'll need 3rd-party libraries like([concurrently](https://github.com/kimmobrunfeldt/concurrently)) to achieve this in `package.json`:
```json
{
"scripts": {
"dev": "concurrently \"touch a.md\" \"touch b.md\""
}
}
```
daizong supports it out of the box:
```js
export default {
dev: {
run: ['touch a.md', 'touch b.md'],
parallel: true,
},
};
```
### Reuse a task
`package.json`:
```json
{
"scripts": {
"dev": "concurrently \"npm run touch1\" \"npm run touch2\"",
"touch1": "touch a.md",
"touch2": "touch b.md"
}
}
```
daizong:
```js
export default {
dev: {
// Use `#<task_name>` to call an existing task.
run: ['#touch1', '#touch2'],
parallel: true,
},
touch1: 'touch a.md',
touch2: 'touch b.md',
};
```
### Built-in commands
`run` also accepts an object. In that case, you are running daizong's built-in commands:
- `mkdir`: `string` creates a directory and its parents if needed.
- `del`: `string | string[]` deletes files or directories based on the given paths or globs. See [del](https://github.com/sindresorhus/del#usage) for details.
- Examples: `del: 'dist/*.js.map'`, `del: ['a.txt', 'b.txt']`.
- `mkdirDel`: `string` = `del <dir>` + `mkdir <dir>`.
Example:
```js
export default {
prepare: {
run: {
mkdir: 'dist',
del: 'cache',
},
},
dev: {
run: ['#prepare', 'echo dev'],
},
build: {
run: ['#prepare', 'echo build'],
},
};
```
Parallel mode is also supported in built-in commands:
```js
export default {
prepare: {
run: {
mkdir: 'dist',
del: 'cache',
parallel: true,
},
},
dev: {
run: ['#prepare', 'echo dev'],
},
build: {
run: ['#prepare', 'echo build'],
},
};
```
Note that when `parallel` is false (which is the default value), **built-in commands are executed sequentially in declaring order**:
```js
export default {
prepare: {
run: {
// `del dist` runs first!
del: 'dist',
mkdir: 'dist',
},
},
};
```
The above example is equivalent to:
```js
export default {
prepare: {
run: {
// `mkdirDel` deletes and then creates the specified directory.
mkdirDel: 'dist',
},
},
};
```
You can mix built-in commands and command strings in `run`:
```js
export default {
prepare: {
run: [
// 1. Run a built-in command.
{
mkdir: 'dist',
},
// 2. Run a command.
'echo working...',
// 3. Run another task.
'#build',
// 4. Run another built-in command.
{
del: 'tmp',
},
],
},
};
```
### Groups
Tasks can be grouped to improve readability.
```js
export default {
build: {
win: {
run: 'echo Windows build started',
},
linux: {
run: 'echo Linux build started',
},
all: {
run: '#build-win', '#build-linux',
parallel: true,
}
},
};
```
To run a specified task in a group, separate its parent groups with `-`:
```sh
dz build-linux
dz build-all
```
### Environment variables
To support all major operating systems, you need to use 3rd-party libraries like([cross-env](https://github.com/kentcdodds/cross-env)) to achieve this in `package.json` scripts.
`package.json`:
```json
{
"scripts": {
"build": "cross-env NODE_ENV=production tsc -b src",
"dev": "cross-env NODE_ENV=development tsc -b src -w"
}
}
```
daizong supports it out of the box:
```js
export default {
build: {
run: 'tsc -b src',
// Use `env` to specify environment variables.
env: {
NODE_ENV: 'production',
},
},
dev: {
run: 'tsc -b src -w',
env: {
NODE_ENV: 'development',
},
},
};
```
Default environment variables are also supported. Once configured, they will be automatically applied to all tasks:
```js
export default {
// "_" is a preserved field for configuration.
_: {
defaultEnv: {
NODE_ENV: 'development',
},
},
dev: {
// NODE_ENV is 'development'
run: 'tsc -b src -w',
},
build: {
// NODE_ENV is 'production'
run: 'tsc -b src',
env: {
NODE_ENV: 'production',
},
},
};
```
You can also define groups of environment variables to be inherited by tasks:
```js
export default {
// "_" is a preserved field for configuration.
_: {
defaultEnv: {
NODE_ENV: 'development',
},
// Use `envGroups` to define multiple groups of environment variables.
envGroups: {
production: {
NODE_ENV: 'production',
compression_level: 'max',
},
},
},
dev: {
// NODE_ENV is 'development'
run: 'tsc -b src -w',
},
build-windows: {
run: 'build',
env: {
platform: 'windows'
},
// This task has all environment variables defined in "production" group.
envGroups: ['production'],
},
build-macos: {
run: 'build',
env: {
platform: 'macos'
},
// This task has all environment variables defined in "production" group.
envGroups: ['production'],
},
};
```
There are predefined env groups:
- `node:dev`: `NODE_ENV` = `development`
- `node:prod`: `NODE_ENV` = `production`
Example:
```js
export default {
build: {
run: 'tsc -b src',
envGroups: ['node:dev'],
},
};
```
#### Environment variable definitions precedence
Smaller numbers indicate higher precedence.
1. `Task.env`
2. `Task.envGroups` (last group overwrites preceding groups like `Object.assign`)
3. `_.defaultEnv`
### Continue on error
- `ignoreError` available on all tasks, defaults to `false`. If `true`, task errors are ignored.
- `continueOnChildError` only available on tasks with multiple subtasks. It controls if pending subtasks continue to run when one subtask fails. Defaults to `false`.
Example:
```js
export default {
build: {
run: [
'#clean',
// The `tsc` command will always run regardless of the result of clean command.
'tsc',
],
},
clean: {
run: 'echo cleaning...',
ignoreError: true,
},
};
```
### Private tasks
Tasks that are not intended to be called from CLI, and can only be called by other tasks.
```js
// You cannot call the "clean" task via `daizong clean`.
// It can only be called by other tasks.
export default {
// "_" is a preserved field for configuration.
_: {
privateTasks: {
clean: {
run: 'echo cleaning...',
},
},
},
build: {
run: ['#clean', 'tsc'],
},
};
```
### Before / After
`before` and `after` allow you to specify what to run before or after a certain task.
```js
export default {
build: {
run: 'echo hi',
before: ['echo prepare step 1', 'echo prepare step 1'],
after: '#clean',
},
clean: 'rm -rf out',
};
```
`before` and `after` come handy when you need to mix sequential and parallel commands:
```js
export default {
build: {
before: '#prepare',
after: '#clean',
run: ['cmd1', 'cmd2', '#task1', '#task2'],
parallel: true,
},
prepare: 'echo preparing',
clean: 'rm -rf out',
};
```
It runs as:
```
prepare
|
cmd1 | cmd2 | #task1 | #task2 <-- parallel
|
clean
|
```
### Aliases
> Note: To keep things simple, aliases are only allowed in top-level public tasks.
You can set an alias for a public task:
```js
export default {
build: {
run: ['#build-windows', '#build-macos', '#build-linux'],
parallel: true,
alias: 'b',
},
};
```
Now you can start the task by using either `dz build` or its alias `dz b`.
### Pass arguments to a task
Append the arguments to task path:
```js
export default {
hello: {
run: 'echo hello',
},
};
```
```sh
dz hello i am zzz --arg1 --arg2
```
Which runs:
```sh
echo hello i am zzz --arg1 --arg2
```
<blockquote>
NOTE: Arguments specified before task name are considered daizong arguments, not task arguments. Example:
```sh
dz --config <val> build-clean --config <val>
```
The first `--config` argument applies to the daizong CLI, while the second `--config` argument gets passed to the task.
</blockquote>
## CLI Usage
```sh
Usage
$ daizong [options] <task-path> [task arguments]
Options
--config Explicitly specify the config file, `--config config.js`
--verbose Print verbose information during execution
--private Allow private tasks to be called from CLI
--version, -v Print version information
Examples
$ daizong --verbose test-browser --script-arg1 --script-arg2
```
## Use daizong in a CommonJS module
Use `daizong.config.mjs` instead of `daizong.config.js`.