first commit
This commit is contained in:
+22
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Toru Nagashima
|
||||
|
||||
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.
|
||||
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
| index | [npm-run-all] | [run-s] | [run-p] | [Node API] |
|
||||
|-------|---------------|---------|---------|------------|
|
||||
|
||||
# npm-run-all2
|
||||
|
||||
[](https://www.npmjs.com/package/npm-run-all2)
|
||||
[](http://www.npmtrends.com/npm-run-all2)
|
||||
[](https://github.com/bcomnes/npm-run-all2/actions)
|
||||
[](https://codecov.io/gh/bcomnes/npm-run-all2)
|
||||
[](https://github.com/neostandard/neostandard)
|
||||
|
||||
A CLI tool to run multiple npm-scripts in parallel or sequential.
|
||||
|
||||
## npm-run-all2 why?
|
||||
|
||||
**A maintenance fork of npm-run-all.** npm-run-all2 is a fork of npm-run-all with dependabot updates, release automation enabled and some troublesome babel stuff removed to further reduce maintenance burden. Hopefully this labor can upstream some day when mysticatea returns, but until then, welcome aboard!
|
||||
|
||||
## ⤴️ Motivation
|
||||
|
||||
- **Simplify.** The official `npm run-script` command cannot run multiple scripts, so if we want to run multiple scripts, it's redundant a bit. Let's shorten it by glob-like patterns.<br>
|
||||
Before: `npm run clean && npm run build:css && npm run build:js && npm run build:html`<br>
|
||||
After: `npm-run-all clean build:*`
|
||||
- **Cross platform.** We sometimes use `&` to run multiple command in parallel, but `cmd.exe` (`npm run-script` uses it by default) does not support the `&`. Half of Node.js users are using it on Windows, so the use of `&` might block contributions. `npm-run-all --parallel` works well on Windows as well.
|
||||
|
||||
## 💿 Installation
|
||||
|
||||
```bash
|
||||
$ npm install npm-run-all2 --save-dev
|
||||
# or
|
||||
$ yarn add npm-run-all2 --dev
|
||||
```
|
||||
|
||||
- It requires Node.js `^22.22.2 || ^24.15.0 || >=26.0.0`. It may work on older versions of Node, but no guarantees are given.
|
||||
|
||||
## 📖 Usage
|
||||
|
||||
### CLI Commands
|
||||
|
||||
This `npm-run-all` package provides 3 CLI commands.
|
||||
|
||||
- [npm-run-all]
|
||||
- [run-s]
|
||||
- [run-p]
|
||||
|
||||
The main command is [npm-run-all].
|
||||
We can make complex plans with [npm-run-all] command.
|
||||
|
||||
Both [run-s] and [run-p] are shorthand commands.
|
||||
[run-s] is for sequential, [run-p] is for parallel.
|
||||
We can make simple plans with those commands.
|
||||
|
||||
#### Yarn / pnpm Compatibility
|
||||
|
||||
`npm-run-all` is compatible with both Yarn and pnpm. If a script is invoked using either package manager, `npm-run-all` will correctly utilize it to execute the plan's child scripts.
|
||||
|
||||
### Node API
|
||||
|
||||
This `npm-run-all` package provides Node API.
|
||||
|
||||
- [Node API]
|
||||
|
||||
## 📰 Changelog
|
||||
|
||||
- https://github.com/bcomnes/npm-run-all2/releases
|
||||
|
||||
## 🍻 Contributing
|
||||
|
||||
Welcome♡
|
||||
|
||||
### Bug Reports or Feature Requests
|
||||
|
||||
Please use GitHub Issues.
|
||||
|
||||
### Correct Documents
|
||||
|
||||
Please use GitHub Pull Requests.
|
||||
|
||||
I'm not familiar with English, so I especially thank you for documents' corrections.
|
||||
|
||||
### Implementing
|
||||
|
||||
Please use GitHub Pull Requests.
|
||||
|
||||
There are some npm-scripts to help developments.
|
||||
|
||||
- **npm test** - Run tests and collect coverage.
|
||||
- **npm run clean** - Delete temporary files.
|
||||
- **npm run lint** - Run ESLint.
|
||||
- **npm run watch** - Run tests (not collect coverage) on every file change.
|
||||
|
||||
[npm-run-all]: docs/npm-run-all.md
|
||||
[run-s]: docs/run-s.md
|
||||
[run-p]: docs/run-p.md
|
||||
[Node API]: docs/node-api.md
|
||||
|
||||
## Security
|
||||
|
||||
See [SECURITY.md](./SECURITY.md) for this projects security policy.
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* @author Toru Nagashima
|
||||
* @copyright 2016 Toru Nagashima. All rights reserved.
|
||||
* @copyright 2026 Bret Comnes. All rights reserved.
|
||||
* See LICENSE file in root directory for full license.
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @param {'npm-run-all' | 'run-p' | 'run-s'} name - CLI implementation name.
|
||||
* @returns {Promise<unknown>}
|
||||
*/
|
||||
export default async function bootstrap (name) {
|
||||
const argv = process.argv.slice(2)
|
||||
|
||||
switch (argv[0]) {
|
||||
case undefined:
|
||||
case '-h':
|
||||
case '--help': {
|
||||
const { default: help } = await import(`../${name}/help.js`)
|
||||
return help(process.stdout)
|
||||
}
|
||||
|
||||
case '-v':
|
||||
case '--version': {
|
||||
const { default: version } = await import('./version.js')
|
||||
return version(process.stdout)
|
||||
}
|
||||
|
||||
default: {
|
||||
// https://github.com/mysticatea/npm-run-all/issues/105
|
||||
// Avoid MaxListenersExceededWarnings.
|
||||
process.stdout.setMaxListeners(0)
|
||||
process.stderr.setMaxListeners(0)
|
||||
process.stdin.setMaxListeners(0)
|
||||
|
||||
// Main
|
||||
const { default: main } = await import(`../${name}/main.js`)
|
||||
return main(
|
||||
argv,
|
||||
process.stdout,
|
||||
process.stderr
|
||||
).then(
|
||||
() => {
|
||||
// I'm not sure why, but maybe the process never exits
|
||||
// on Git Bash (MINGW64)
|
||||
process.exit(0)
|
||||
},
|
||||
() => {
|
||||
process.exit(1)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+302
@@ -0,0 +1,302 @@
|
||||
/**
|
||||
* @author Toru Nagashima
|
||||
* @copyright 2016 Toru Nagashima. All rights reserved.
|
||||
* @copyright 2026 Bret Comnes. All rights reserved.
|
||||
* See LICENSE file in root directory for full license.
|
||||
*
|
||||
* @import { ColorMode } from '../../lib/run-task.js'
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
const OVERWRITE_OPTION = /^--([^:]+?):([^=]+?)(?:=(.+))?$/
|
||||
const CONFIG_OPTION = /^--([^=]+?)(?:=(.+))$/
|
||||
const PACKAGE_CONFIG_PATTERN = /^npm_package_config_(.+)$/
|
||||
const CONCAT_OPTIONS = /^-[clnprsx]+$/
|
||||
|
||||
/**
|
||||
* @typedef {Record<string, string>} ConfigMap
|
||||
* @typedef {Record<string, ConfigMap>} PackageConfigMap
|
||||
* @typedef CliGroup
|
||||
* @property {boolean} parallel - Whether this group runs in parallel.
|
||||
* @property {string[]} patterns - Task patterns in this group.
|
||||
* @typedef {Partial<CliGroup>} InitialValues
|
||||
* @typedef ArgumentSetOptions
|
||||
* @property {boolean} [singleMode] - The flag to be single group mode.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Overwrites a specified package config.
|
||||
*
|
||||
* @param {PackageConfigMap} config - A config object to be overwritten.
|
||||
* @param {string} packageName - A package name to overwrite.
|
||||
* @param {string} variable - A variable name to overwrite.
|
||||
* @param {string} value - A new value to overwrite.
|
||||
* @returns {void}
|
||||
*/
|
||||
function overwriteConfig (config, packageName, variable, value) {
|
||||
const scope = config[packageName] || (config[packageName] = {})
|
||||
scope[variable] = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a package config object.
|
||||
* This checks `process.env` and creates the default value.
|
||||
*
|
||||
* @returns {PackageConfigMap} Created config object.
|
||||
*/
|
||||
function createPackageConfig () {
|
||||
/** @type {PackageConfigMap} */
|
||||
const retv = {}
|
||||
const packageName = process.env['npm_package_name']
|
||||
if (!packageName) {
|
||||
return retv
|
||||
}
|
||||
|
||||
for (const key of Object.keys(process.env)) {
|
||||
const m = PACKAGE_CONFIG_PATTERN.exec(key)
|
||||
if (m != null && m[1] && process.env[key] != null) {
|
||||
overwriteConfig(retv, packageName, m[1], process.env[key])
|
||||
}
|
||||
}
|
||||
|
||||
return retv
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new group into a given list.
|
||||
*
|
||||
* @param {CliGroup[]} groups - A group list to add.
|
||||
* @param {InitialValues} [initialValues] - A key-value map for the default of new value.
|
||||
* @returns {void}
|
||||
*/
|
||||
function addGroup (groups, initialValues) {
|
||||
groups.push({
|
||||
parallel: false,
|
||||
patterns: [],
|
||||
...(initialValues || {})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* ArgumentSet is values of parsed CLI arguments.
|
||||
* This class provides the getter to get the last group.
|
||||
*/
|
||||
class ArgumentSet {
|
||||
/**
|
||||
* @param {InitialValues} [initialValues] - A key-value map for the default of new value.
|
||||
* @param {ArgumentSetOptions} [options] - A key-value map for the options.
|
||||
*/
|
||||
constructor (initialValues, options) {
|
||||
/** @type {ConfigMap} */
|
||||
this.config = {}
|
||||
/** @type {boolean} */
|
||||
this.continueOnError = false
|
||||
/** @type {CliGroup[]} */
|
||||
this.groups = []
|
||||
/** @type {number} */
|
||||
this.maxParallel = 0
|
||||
/** @type {string | null} */
|
||||
this.npmPath = null
|
||||
/** @type {PackageConfigMap} */
|
||||
this.packageConfig = createPackageConfig()
|
||||
/** @type {boolean} */
|
||||
this.printLabel = false
|
||||
/** @type {boolean} */
|
||||
this.printName = false
|
||||
/** @type {boolean} */
|
||||
this.race = false
|
||||
/** @type {boolean} */
|
||||
this.aggregateOutput = false
|
||||
/** @type {ColorMode} */
|
||||
this.colorMode = 'auto'
|
||||
/** @type {string[]} */
|
||||
this.rest = []
|
||||
/** @type {boolean} */
|
||||
this.silent = process.env['npm_config_loglevel'] === 'silent'
|
||||
/** @type {boolean} */
|
||||
this.singleMode = Boolean(options?.singleMode)
|
||||
/** @type {boolean} */
|
||||
this.nodeRun = false
|
||||
|
||||
addGroup(this.groups, initialValues)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the last group.
|
||||
*/
|
||||
get lastGroup () {
|
||||
const group = this.groups[this.groups.length - 1]
|
||||
if (!group) {
|
||||
throw new Error('No argument group exists.')
|
||||
}
|
||||
return group
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets "parallel" flag.
|
||||
*/
|
||||
get parallel () {
|
||||
return this.groups.some(g => g.parallel)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses CLI arguments.
|
||||
*
|
||||
* @param {ArgumentSet} set - The parsed CLI arguments.
|
||||
* @param {string[]} args - CLI arguments.
|
||||
* @returns {ArgumentSet} set itself.
|
||||
*/
|
||||
function parseCLIArgsCore (set, args) {
|
||||
LOOP: // eslint-disable-line no-labels
|
||||
for (let i = 0; i < args.length; ++i) {
|
||||
const arg = args[i]
|
||||
if (arg == null) {
|
||||
continue
|
||||
}
|
||||
|
||||
switch (arg) {
|
||||
case '--':
|
||||
set.rest = args.slice(1 + i)
|
||||
break LOOP // eslint-disable-line no-labels
|
||||
|
||||
case '--color':
|
||||
case '--no-color':
|
||||
// do nothing.
|
||||
break
|
||||
|
||||
case '-c':
|
||||
case '--continue-on-error':
|
||||
set.continueOnError = true
|
||||
break
|
||||
|
||||
case '-l':
|
||||
case '--print-label':
|
||||
set.printLabel = true
|
||||
break
|
||||
|
||||
case '-n':
|
||||
case '--print-name':
|
||||
set.printName = true
|
||||
break
|
||||
|
||||
case '-r':
|
||||
case '--race':
|
||||
set.race = true
|
||||
break
|
||||
|
||||
case '--silent':
|
||||
set.silent = true
|
||||
break
|
||||
|
||||
case '--max-parallel':
|
||||
{
|
||||
const rawMaxParallel = args[++i]
|
||||
set.maxParallel = parseInt(rawMaxParallel ?? '', 10)
|
||||
if (!Number.isFinite(set.maxParallel) || set.maxParallel <= 0) {
|
||||
throw new Error(`Invalid Option: --max-parallel ${rawMaxParallel ?? ''}`)
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
case '-s':
|
||||
case '--sequential':
|
||||
case '--serial':
|
||||
if (set.singleMode && arg === '-s') {
|
||||
set.silent = true
|
||||
break
|
||||
}
|
||||
if (set.singleMode) {
|
||||
throw new Error(`Invalid Option: ${arg}`)
|
||||
}
|
||||
addGroup(set.groups)
|
||||
break
|
||||
|
||||
case '--aggregate-output':
|
||||
set.aggregateOutput = true
|
||||
break
|
||||
|
||||
case '-p':
|
||||
case '--parallel':
|
||||
if (set.singleMode) {
|
||||
throw new Error(`Invalid Option: ${arg}`)
|
||||
}
|
||||
addGroup(set.groups, { parallel: true })
|
||||
break
|
||||
|
||||
case '--npm-path':
|
||||
set.npmPath = args[++i] || null
|
||||
break
|
||||
|
||||
case '-x':
|
||||
case '--node-run':
|
||||
set.nodeRun = true
|
||||
break
|
||||
|
||||
case '--color-mode': {
|
||||
const mode = args[++i]
|
||||
if (mode !== 'auto' && mode !== 'none' && mode !== '16' && mode !== '256') {
|
||||
throw new Error(`Invalid Option: --color-mode ${mode ?? ''} (expected auto, none, 16, or 256)`)
|
||||
}
|
||||
set.colorMode = mode
|
||||
break
|
||||
}
|
||||
|
||||
default: {
|
||||
let matched = null
|
||||
if ((matched = OVERWRITE_OPTION.exec(arg))) {
|
||||
overwriteConfig(
|
||||
set.packageConfig,
|
||||
matched[1] ?? '',
|
||||
matched[2] ?? '',
|
||||
matched[3] ?? args[++i] ?? ''
|
||||
)
|
||||
} else if ((matched = CONFIG_OPTION.exec(arg))) {
|
||||
const configName = matched[1]
|
||||
if (configName) {
|
||||
set.config[configName] = matched[2] ?? ''
|
||||
}
|
||||
} else if (CONCAT_OPTIONS.test(arg)) {
|
||||
parseCLIArgsCore(
|
||||
set,
|
||||
arg.slice(1).split('').map(c => `-${c}`)
|
||||
)
|
||||
} else if (arg.startsWith('-')) {
|
||||
throw new Error(`Invalid Option: ${arg}`)
|
||||
} else {
|
||||
set.lastGroup.patterns.push(arg)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!set.parallel && set.aggregateOutput) {
|
||||
throw new Error('Invalid Option: --aggregate-output (without parallel)')
|
||||
}
|
||||
if (!set.parallel && set.race) {
|
||||
const race = args.indexOf('--race') !== -1 ? '--race' : '-r'
|
||||
throw new Error(`Invalid Option: ${race} (without parallel)`)
|
||||
}
|
||||
if (!set.parallel && set.maxParallel !== 0) {
|
||||
throw new Error('Invalid Option: --max-parallel (without parallel)')
|
||||
}
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses CLI arguments.
|
||||
*
|
||||
* @param {string[]} args - CLI arguments.
|
||||
* @param {InitialValues} [initialValues] - A key-value map for the default of new value.
|
||||
* @param {ArgumentSetOptions} [options] - A key-value map for the options.
|
||||
* @returns {ArgumentSet} The parsed CLI arguments.
|
||||
*/
|
||||
export default function parseCLIArgs (args, initialValues, options) {
|
||||
return parseCLIArgsCore(new ArgumentSet(initialValues, options), args)
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* @author Toru Nagashima
|
||||
* @copyright 2016 Toru Nagashima. All rights reserved.
|
||||
* @copyright 2026 Bret Comnes. All rights reserved.
|
||||
* See LICENSE file in root directory for full license.
|
||||
*
|
||||
* @import { Writable } from 'node:stream'
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import { resolve } from 'node:path'
|
||||
|
||||
const __dirname = import.meta.dirname
|
||||
|
||||
/**
|
||||
* Print a version text.
|
||||
*
|
||||
* @param {Writable} output - A writable stream to print.
|
||||
* @returns {Promise<null>} Always a fulfilled promise.
|
||||
* @private
|
||||
*/
|
||||
export default async function printVersion (output) {
|
||||
const packageJson = JSON.parse(
|
||||
await readFile(resolve(__dirname, '../../package.json'), 'utf8')
|
||||
)
|
||||
const version = packageJson.version
|
||||
|
||||
output.write(`v${version}\n`)
|
||||
|
||||
return Promise.resolve(null)
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* @author Toru Nagashima
|
||||
* @copyright 2015 Toru Nagashima. All rights reserved.
|
||||
* @copyright 2026 Bret Comnes. All rights reserved.
|
||||
* See LICENSE file in root directory for full license.
|
||||
*
|
||||
* @import { Writable } from 'node:stream'
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Print a help text.
|
||||
*
|
||||
* @param {Writable} output - A writable stream to print.
|
||||
* @returns {Promise<null>} Always a fulfilled promise.
|
||||
* @private
|
||||
*/
|
||||
export default function printHelp (output) {
|
||||
output.write(`
|
||||
Usage:
|
||||
$ npm-run-all [--help | -h | --version | -v]
|
||||
$ npm-run-all [tasks] [OPTIONS]
|
||||
|
||||
Run given npm-scripts in parallel or sequential.
|
||||
|
||||
<tasks> : A list of npm-scripts' names and Glob-like patterns.
|
||||
|
||||
Options:
|
||||
--aggregate-output - - - Avoid interleaving output by delaying printing of
|
||||
each command's output until it has finished.
|
||||
-c, --continue-on-error - Set the flag to continue executing
|
||||
other/subsequent tasks even if a task threw an
|
||||
error. 'npm-run-all' itself will exit with
|
||||
non-zero code if one or more tasks threw error(s)
|
||||
--max-parallel <number> - Set the maximum number of parallelism. Default is
|
||||
unlimited.
|
||||
--npm-path <string> - - - Set the path to npm. Default is the value of
|
||||
environment variable npm_execpath.
|
||||
If the variable is not defined, then it's "npm".
|
||||
In this case, the "npm" command must be found in
|
||||
environment variable PATH.
|
||||
-l, --print-label - - - - Set the flag to print the task name as a prefix
|
||||
on each line of output. Tools in tasks may stop
|
||||
coloring their output if this option was given.
|
||||
-n, --print-name - - - - Set the flag to print the task name before
|
||||
running each task.
|
||||
-p, --parallel <tasks> - Run a group of tasks in parallel.
|
||||
e.g. 'npm-run-all -p foo bar' is similar to
|
||||
'npm run foo & npm run bar'.
|
||||
-r, --race - - - - - - - Set the flag to kill all tasks when a task
|
||||
finished with zero. This option is valid only
|
||||
with 'parallel' option.
|
||||
-s, --sequential <tasks> - Run a group of tasks sequentially.
|
||||
--serial <tasks> e.g. 'npm-run-all -s foo bar' is similar to
|
||||
'npm run foo && npm run bar'.
|
||||
'--serial' is a synonym of '--sequential'.
|
||||
--silent - - - - - - - - Set 'silent' to the log level of npm.
|
||||
-x, --node-run - - - - - Use \`node --run\` to execute scripts instead of
|
||||
the package manager. This is faster but
|
||||
intentionally omits: pre/post lifecycle hooks
|
||||
and npm_* environment variables. node_modules/.bin
|
||||
is still added to PATH. Sets NODE_RUN_SCRIPT_NAME
|
||||
and NODE_RUN_PACKAGE_JSON_PATH instead.
|
||||
Can also be enabled project-wide by setting
|
||||
\`"npm-run-all2": { "nodeRun": true }\` in
|
||||
package.json.
|
||||
|
||||
Examples:
|
||||
$ npm-run-all --serial clean lint build:**
|
||||
$ npm-run-all --parallel watch:**
|
||||
$ npm-run-all clean lint --parallel "build:** -- --watch"
|
||||
$ npm-run-all -l -p start-server start-browser start-electron
|
||||
|
||||
See Also:
|
||||
https://github.com/mysticatea/npm-run-all#readme
|
||||
`)
|
||||
|
||||
return Promise.resolve(null)
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* @author Toru Nagashima
|
||||
* @copyright 2015 Toru Nagashima. All rights reserved.
|
||||
* @copyright 2026 Bret Comnes. All rights reserved.
|
||||
* See LICENSE file in root directory for full license.
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Main
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
import bootstrap from '#bin/common/bootstrap.js'
|
||||
|
||||
bootstrap('npm-run-all')
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* @author Toru Nagashima
|
||||
* @copyright 2015 Toru Nagashima. All rights reserved.
|
||||
* @copyright 2026 Bret Comnes. All rights reserved.
|
||||
* See LICENSE file in root directory for full license.
|
||||
*
|
||||
* @import { Writable } from 'node:stream'
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
import runAll from '#lib'
|
||||
import parseCLIArgs from '#bin/common/parse-cli-args.js'
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Parses arguments, then run specified npm-scripts.
|
||||
*
|
||||
* @param {string[]} args - Arguments to parse.
|
||||
* @param {Writable} stdout - A writable stream to print logs.
|
||||
* @param {Writable} stderr - A writable stream to print errors.
|
||||
* @returns {Promise<unknown>} A promise which comes to be fulfilled when all npm-scripts are completed.
|
||||
* @private
|
||||
*/
|
||||
export default function npmRunAll (args, stdout, stderr) {
|
||||
try {
|
||||
const stdin = process.stdin
|
||||
const argv = parseCLIArgs(args)
|
||||
|
||||
const promise = argv.groups.reduce(
|
||||
(prev, group) => {
|
||||
if (group.patterns.length === 0) {
|
||||
return prev
|
||||
}
|
||||
return prev.then(() => runAll(
|
||||
group.patterns,
|
||||
{
|
||||
stdout,
|
||||
stderr,
|
||||
stdin,
|
||||
parallel: group.parallel,
|
||||
maxParallel: group.parallel ? argv.maxParallel : 1,
|
||||
continueOnError: argv.continueOnError,
|
||||
printLabel: argv.printLabel,
|
||||
printName: argv.printName,
|
||||
config: argv.config,
|
||||
packageConfig: argv.packageConfig,
|
||||
silent: argv.silent,
|
||||
arguments: argv.rest,
|
||||
race: group.parallel && argv.race,
|
||||
npmPath: argv.npmPath,
|
||||
aggregateOutput: group.parallel && argv.aggregateOutput,
|
||||
nodeRun: argv.nodeRun,
|
||||
colorMode: argv.colorMode,
|
||||
}
|
||||
))
|
||||
},
|
||||
/** @type {Promise<unknown>} */ (Promise.resolve(null))
|
||||
)
|
||||
|
||||
if (!argv.silent) {
|
||||
promise.catch(
|
||||
/**
|
||||
* @param {unknown} err - The rejection reason.
|
||||
*/
|
||||
err => {
|
||||
const message = err instanceof Error ? err.message : String(err)
|
||||
console.error('ERROR:', message)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return promise
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err)
|
||||
console.error('ERROR:', message)
|
||||
|
||||
return Promise.reject(err)
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* @author Toru Nagashima
|
||||
* @copyright 2016 Toru Nagashima. All rights reserved.
|
||||
* @copyright 2026 Bret Comnes. All rights reserved.
|
||||
* See LICENSE file in root directory for full license.
|
||||
*
|
||||
* @import { Writable } from 'node:stream'
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Print a help text.
|
||||
*
|
||||
* @param {Writable} output - A writable stream to print.
|
||||
* @returns {Promise<null>} Always a fulfilled promise.
|
||||
* @private
|
||||
*/
|
||||
export default function printHelp (output) {
|
||||
output.write(`
|
||||
Usage:
|
||||
$ run-p [--help | -h | --version | -v]
|
||||
$ run-p [OPTIONS] <tasks>
|
||||
|
||||
Run given npm-scripts in parallel.
|
||||
|
||||
<tasks> : A list of npm-scripts' names and Glob-like patterns.
|
||||
|
||||
Options:
|
||||
--aggregate-output - - - Avoid interleaving output by delaying printing of
|
||||
each command's output until it has finished.
|
||||
-c, --continue-on-error - Set the flag to continue executing other tasks
|
||||
even if a task threw an error. 'run-p' itself
|
||||
will exit with non-zero code if one or more tasks
|
||||
threw error(s).
|
||||
--max-parallel <number> - Set the maximum number of parallelism. Default is
|
||||
unlimited.
|
||||
--npm-path <string> - - - Set the path to npm. Default is the value of
|
||||
environment variable npm_execpath.
|
||||
If the variable is not defined, then it's "npm."
|
||||
In this case, the "npm" command must be found in
|
||||
environment variable PATH.
|
||||
-l, --print-label - - - - Set the flag to print the task name as a prefix
|
||||
on each line of output. Tools in tasks may stop
|
||||
coloring their output if this option was given.
|
||||
-n, --print-name - - - - Set the flag to print the task name before
|
||||
running each task.
|
||||
-r, --race - - - - - - - Set the flag to kill all tasks when a task
|
||||
finished with zero.
|
||||
-s, --silent - - - - - - Set 'silent' to the log level of npm.
|
||||
-x, --node-run - - - - - Use \`node --run\` to execute scripts instead of
|
||||
the package manager. This is faster but
|
||||
intentionally omits: pre/post lifecycle hooks
|
||||
and npm_* environment variables. node_modules/.bin
|
||||
is still added to PATH. Sets NODE_RUN_SCRIPT_NAME
|
||||
and NODE_RUN_PACKAGE_JSON_PATH instead.
|
||||
Can also be enabled project-wide by setting
|
||||
\`"npm-run-all2": { "nodeRun": true }\` in
|
||||
package.json.
|
||||
|
||||
Shorthand aliases can be combined.
|
||||
For example, '-clns' equals to '-c -l -n -s'.
|
||||
|
||||
Examples:
|
||||
$ run-p watch:**
|
||||
$ run-p --print-label "build:** -- --watch"
|
||||
$ run-p -sl "build:** -- --watch"
|
||||
$ run-p start-server start-browser start-electron
|
||||
|
||||
See Also:
|
||||
https://github.com/mysticatea/npm-run-all#readme
|
||||
`)
|
||||
|
||||
return Promise.resolve(null)
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* @author Toru Nagashima
|
||||
* @copyright 2015 Toru Nagashima. All rights reserved.
|
||||
* @copyright 2026 Bret Comnes. All rights reserved.
|
||||
* See LICENSE file in root directory for full license.
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Main
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
import bootstrap from '#bin/common/bootstrap.js'
|
||||
|
||||
bootstrap('run-p')
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* @author Toru Nagashima
|
||||
* @copyright 2016 Toru Nagashima. All rights reserved.
|
||||
* @copyright 2026 Bret Comnes. All rights reserved.
|
||||
* See LICENSE file in root directory for full license.
|
||||
*
|
||||
* @import { Writable } from 'node:stream'
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
import runAll from '#lib'
|
||||
import parseCLIArgs from '#bin/common/parse-cli-args.js'
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Parses arguments, then run specified npm-scripts.
|
||||
*
|
||||
* @param {string[]} args - Arguments to parse.
|
||||
* @param {Writable} stdout - A writable stream to print logs.
|
||||
* @param {Writable} stderr - A writable stream to print errors.
|
||||
* @returns {Promise<unknown>} A promise which comes to be fulfilled when all npm-scripts are completed.
|
||||
* @private
|
||||
*/
|
||||
export default function npmRunAll (args, stdout, stderr) {
|
||||
try {
|
||||
const stdin = process.stdin
|
||||
const argv = parseCLIArgs(args, { parallel: true }, { singleMode: true })
|
||||
const group = argv.lastGroup
|
||||
|
||||
if (group.patterns.length === 0) {
|
||||
return Promise.resolve(null)
|
||||
}
|
||||
|
||||
const promise = runAll(
|
||||
group.patterns,
|
||||
{
|
||||
stdout,
|
||||
stderr,
|
||||
stdin,
|
||||
parallel: group.parallel,
|
||||
maxParallel: argv.maxParallel,
|
||||
continueOnError: argv.continueOnError,
|
||||
printLabel: argv.printLabel,
|
||||
printName: argv.printName,
|
||||
config: argv.config,
|
||||
packageConfig: argv.packageConfig,
|
||||
silent: argv.silent,
|
||||
arguments: argv.rest,
|
||||
race: argv.race,
|
||||
npmPath: argv.npmPath,
|
||||
aggregateOutput: argv.aggregateOutput,
|
||||
nodeRun: argv.nodeRun,
|
||||
colorMode: argv.colorMode,
|
||||
}
|
||||
)
|
||||
|
||||
if (!argv.silent) {
|
||||
promise.catch(
|
||||
/**
|
||||
* @param {unknown} err - The rejection reason.
|
||||
*/
|
||||
err => {
|
||||
const message = err instanceof Error ? err.message : String(err)
|
||||
console.error('ERROR:', message)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return promise
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err)
|
||||
console.error('ERROR:', message)
|
||||
|
||||
return Promise.reject(err)
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* @author Toru Nagashima
|
||||
* @copyright 2016 Toru Nagashima. All rights reserved.
|
||||
* @copyright 2026 Bret Comnes. All rights reserved.
|
||||
* See LICENSE file in root directory for full license.
|
||||
*
|
||||
* @import { Writable } from 'node:stream'
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Print a help text.
|
||||
*
|
||||
* @param {Writable} output - A writable stream to print.
|
||||
* @returns {Promise<null>} Always a fulfilled promise.
|
||||
* @private
|
||||
*/
|
||||
export default function printHelp (output) {
|
||||
output.write(`
|
||||
Usage:
|
||||
$ run-s [--help | -h | --version | -v]
|
||||
$ run-s [OPTIONS] <tasks>
|
||||
|
||||
Run given npm-scripts sequentially.
|
||||
|
||||
<tasks> : A list of npm-scripts' names and Glob-like patterns.
|
||||
|
||||
Options:
|
||||
-c, --continue-on-error - Set the flag to continue executing subsequent
|
||||
tasks even if a task threw an error. 'run-s'
|
||||
itself will exit with non-zero code if one or
|
||||
more tasks threw error(s).
|
||||
--npm-path <string> - - - Set the path to npm. Default is the value of
|
||||
environment variable npm_execpath.
|
||||
If the variable is not defined, then it's "npm."
|
||||
In this case, the "npm" command must be found in
|
||||
environment variable PATH.
|
||||
-l, --print-label - - - - Set the flag to print the task name as a prefix
|
||||
on each line of output. Tools in tasks may stop
|
||||
coloring their output if this option was given.
|
||||
-n, --print-name - - - - Set the flag to print the task name before
|
||||
running each task.
|
||||
-s, --silent - - - - - - Set 'silent' to the log level of npm.
|
||||
-x, --node-run - - - - - Use \`node --run\` to execute scripts instead of
|
||||
the package manager. This is faster but
|
||||
intentionally omits: pre/post lifecycle hooks
|
||||
and npm_* environment variables. node_modules/.bin
|
||||
is still added to PATH. Sets NODE_RUN_SCRIPT_NAME
|
||||
and NODE_RUN_PACKAGE_JSON_PATH instead.
|
||||
Can also be enabled project-wide by setting
|
||||
\`"npm-run-all2": { "nodeRun": true }\` in
|
||||
package.json.
|
||||
|
||||
Shorthand aliases can be combined.
|
||||
For example, '-clns' equals to '-c -l -n -s'.
|
||||
|
||||
Examples:
|
||||
$ run-s build:**
|
||||
$ run-s lint clean build:**
|
||||
$ run-s --silent --print-name lint clean build:**
|
||||
$ run-s -sn lint clean build:**
|
||||
|
||||
See Also:
|
||||
https://github.com/mysticatea/npm-run-all#readme
|
||||
`)
|
||||
|
||||
return Promise.resolve(null)
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* @author Toru Nagashima
|
||||
* @copyright 2015 Toru Nagashima. All rights reserved.
|
||||
* @copyright 2026 Bret Comnes. All rights reserved.
|
||||
* See LICENSE file in root directory for full license.
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Main
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
import bootstrap from '#bin/common/bootstrap.js'
|
||||
|
||||
bootstrap('run-s')
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* @author Toru Nagashima
|
||||
* @copyright 2016 Toru Nagashima. All rights reserved.
|
||||
* @copyright 2026 Bret Comnes. All rights reserved.
|
||||
* See LICENSE file in root directory for full license.
|
||||
*
|
||||
* @import { Writable } from 'node:stream'
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
import runAll from '#lib'
|
||||
import parseCLIArgs from '#bin/common/parse-cli-args.js'
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Parses arguments, then run specified npm-scripts.
|
||||
*
|
||||
* @param {string[]} args - Arguments to parse.
|
||||
* @param {Writable} stdout - A writable stream to print logs.
|
||||
* @param {Writable} stderr - A writable stream to print errors.
|
||||
* @returns {Promise<unknown>} A promise which comes to be fulfilled when all npm-scripts are completed.
|
||||
* @private
|
||||
*/
|
||||
export default function npmRunAll (args, stdout, stderr) {
|
||||
try {
|
||||
const stdin = process.stdin
|
||||
const argv = parseCLIArgs(args, { parallel: false }, { singleMode: true })
|
||||
const group = argv.lastGroup
|
||||
|
||||
if (group.patterns.length === 0) {
|
||||
return Promise.resolve(null)
|
||||
}
|
||||
|
||||
const promise = runAll(
|
||||
group.patterns,
|
||||
{
|
||||
stdout,
|
||||
stderr,
|
||||
stdin,
|
||||
parallel: group.parallel,
|
||||
continueOnError: argv.continueOnError,
|
||||
printLabel: argv.printLabel,
|
||||
printName: argv.printName,
|
||||
config: argv.config,
|
||||
packageConfig: argv.packageConfig,
|
||||
silent: argv.silent,
|
||||
arguments: argv.rest,
|
||||
npmPath: argv.npmPath,
|
||||
nodeRun: argv.nodeRun,
|
||||
colorMode: argv.colorMode,
|
||||
}
|
||||
)
|
||||
|
||||
if (!argv.silent) {
|
||||
promise.catch(
|
||||
/**
|
||||
* @param {unknown} err - The rejection reason.
|
||||
*/
|
||||
err => {
|
||||
const message = err instanceof Error ? err.message : String(err)
|
||||
console.error('ERROR:', message)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return promise
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err)
|
||||
console.error('ERROR:', message)
|
||||
|
||||
return Promise.reject(err)
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export default function createHeader(nameAndArgs: string, packageInfo: PackageInfo | null, isTTY: boolean | undefined): string;
|
||||
import type { PackageInfo } from './index.js';
|
||||
//# sourceMappingURL=create-header.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"create-header.d.ts","sourceRoot":"","sources":["create-header.js"],"names":[],"mappings":"AAwBA,kDALW,MAAM,eACN,WAAW,GAAG,IAAI,SAClB,OAAO,GAAG,SAAS,GACjB,MAAM,CAqBlB;iCApC+B,YAAY"}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* @module create-header
|
||||
* @author Toru Nagashima
|
||||
* @copyright 2016 Toru Nagashima. All rights reserved.
|
||||
* @copyright 2026 Bret Comnes. All rights reserved.
|
||||
* See LICENSE file in root directory for full license.
|
||||
*
|
||||
* @import { PackageInfo } from './index.js'
|
||||
*/
|
||||
|
||||
import ansiStyles from 'ansi-styles'
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates the header text for a given task.
|
||||
*
|
||||
* @param {string} nameAndArgs - A task name and arguments.
|
||||
* @param {PackageInfo | null} packageInfo - A package.json's information.
|
||||
* @param {boolean | undefined} isTTY - The flag to color the header.
|
||||
* @returns {string} The header of a given task.
|
||||
*/
|
||||
export default function createHeader (nameAndArgs, packageInfo, isTTY) {
|
||||
if (!packageInfo) {
|
||||
return `\n> ${nameAndArgs}\n\n`
|
||||
}
|
||||
|
||||
const index = nameAndArgs.indexOf(' ')
|
||||
const name = (index === -1) ? nameAndArgs : nameAndArgs.slice(0, index)
|
||||
const args = (index === -1) ? '' : nameAndArgs.slice(index + 1)
|
||||
const packageName = packageInfo.body.name
|
||||
const packageVersion = packageInfo.body.version
|
||||
const scriptBody = packageInfo.body.scripts?.[name] ?? ''
|
||||
const packagePath = packageInfo.path
|
||||
const color = isTTY ? ansiStyles.gray : { open: '', close: '' }
|
||||
|
||||
return `
|
||||
${color.open}> ${packageName}@${packageVersion} ${name} ${packagePath}${color.close}
|
||||
${color.open}> ${scriptBody} ${args}${color.close}
|
||||
|
||||
`
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
export default function createPrefixTransform(prefix: string, state: PrefixState): stream.Transform;
|
||||
export type PrefixState = {
|
||||
lastPrefix: string | null;
|
||||
lastIsLinebreak: boolean;
|
||||
};
|
||||
import stream from 'node:stream';
|
||||
//# sourceMappingURL=create-prefix-transform-stream.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"create-prefix-transform-stream.d.ts","sourceRoot":"","sources":["create-prefix-transform-stream.js"],"names":[],"mappings":"AAmGA,sDAJW,MAAM,SACN,WAAW,GACT,MAAM,CAAC,SAAS,CAI5B;;gBAzFa,MAAM,GAAG,IAAI;qBACb,OAAO;;mBAOF,aAAa"}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* @module create-prefix-transform-stream
|
||||
* @author Toru Nagashima
|
||||
* @copyright 2016 Toru Nagashima. All rights reserved.
|
||||
* @copyright 2026 Bret Comnes. All rights reserved.
|
||||
* See LICENSE file in root directory for full license.
|
||||
*
|
||||
* @import { TransformCallback } from 'node:stream'
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef PrefixState
|
||||
* @property {string | null} lastPrefix - The last prefix which is printed.
|
||||
* @property {boolean} lastIsLinebreak - The flag to check whether the last output is a line break or not.
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
import stream from 'node:stream'
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
const ALL_BR = /\n/g
|
||||
|
||||
/**
|
||||
* The transform stream to insert a specific prefix.
|
||||
*
|
||||
* Several streams can exist for the same output stream.
|
||||
* This stream will insert the prefix if the last output came from other instance.
|
||||
* To do that, this stream is using a shared state object.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
class PrefixTransform extends stream.Transform {
|
||||
/** @type {string} */
|
||||
prefix
|
||||
|
||||
/** @type {PrefixState} */
|
||||
state
|
||||
|
||||
/**
|
||||
* @param {string} prefix - A prefix text to be inserted.
|
||||
* @param {PrefixState} state - A state object.
|
||||
*/
|
||||
constructor (prefix, state) {
|
||||
super()
|
||||
|
||||
this.prefix = prefix
|
||||
this.state = state
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the output chunk.
|
||||
*
|
||||
* @override
|
||||
* @param {string|Buffer} chunk - A chunk to be transformed.
|
||||
* @param {BufferEncoding} _encoding - The encoding of the chunk.
|
||||
* @param {TransformCallback} callback - A callback function that is called when done.
|
||||
* @returns {void}
|
||||
*/
|
||||
_transform (chunk, _encoding, callback) {
|
||||
const prefix = this.prefix
|
||||
const nPrefix = `\n${prefix}`
|
||||
const state = this.state
|
||||
const firstPrefix =
|
||||
state.lastIsLinebreak
|
||||
? prefix
|
||||
: (state.lastPrefix !== prefix)
|
||||
? '\n'
|
||||
: ''
|
||||
const prefixed = `${firstPrefix}${chunk}`.replace(ALL_BR, nPrefix)
|
||||
const index = prefixed.indexOf(prefix, Math.max(0, prefixed.length - prefix.length))
|
||||
|
||||
state.lastPrefix = prefix
|
||||
state.lastIsLinebreak = (index !== -1)
|
||||
|
||||
callback(null, (index !== -1) ? prefixed.slice(0, index) : prefixed)
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Public API
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Create a transform stream to insert the specific prefix.
|
||||
*
|
||||
* Several streams can exist for the same output stream.
|
||||
* This stream will insert the prefix if the last output came from other instance.
|
||||
* To do that, this stream is using a shared state object.
|
||||
*
|
||||
* @param {string} prefix - A prefix text to be inserted.
|
||||
* @param {PrefixState} state - A state object.
|
||||
* @returns {stream.Transform} The created transform stream.
|
||||
*/
|
||||
export default function createPrefixTransform (prefix, state) {
|
||||
return new PrefixTransform(prefix, state)
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
export default function npmRunAll(patternOrPatterns: string | string[], options?: NpmRunAllOptions): Promise<null | NpmRunAllResult[]>;
|
||||
export type ConfigValue = string | number | boolean | null | undefined;
|
||||
export type ConfigMap = Record<string, ConfigValue>;
|
||||
export type PackageConfigMap = Record<string, ConfigMap>;
|
||||
export type PackageInfo = {
|
||||
path: string;
|
||||
body: PackageJson;
|
||||
};
|
||||
export type NpmRunAllOptions = {
|
||||
parallel?: boolean;
|
||||
stdin?: Readable | null;
|
||||
stdout?: Writable | null;
|
||||
stderr?: Writable | null;
|
||||
taskList?: string[] | null;
|
||||
config?: ConfigMap | null;
|
||||
packageConfig?: PackageConfigMap | null;
|
||||
arguments?: string[];
|
||||
silent?: boolean;
|
||||
continueOnError?: boolean;
|
||||
printLabel?: boolean;
|
||||
printName?: boolean;
|
||||
maxParallel?: number;
|
||||
npmPath?: string | null;
|
||||
race?: boolean;
|
||||
aggregateOutput?: boolean;
|
||||
nodeRun?: boolean;
|
||||
colorMode?: ColorMode;
|
||||
};
|
||||
export type NpmRunAllResult = {
|
||||
name: string;
|
||||
code: number | undefined;
|
||||
};
|
||||
export type PackageJsonConfig = {
|
||||
nodeRun?: boolean;
|
||||
};
|
||||
import type { PackageJson } from 'read-package-json-fast';
|
||||
import type { Readable } from 'node:stream';
|
||||
import type { Writable } from 'node:stream';
|
||||
import type { ColorMode } from './run-task.js';
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":"AAmPA,qDANW,MAAM,GAAC,MAAM,EAAE,YAEf,gBAAgB,GACd,OAAO,CAAC,IAAI,GAAG,eAAe,EAAE,CAAC,CAkF7C;0BArTY,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS;wBAC5C,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC;+BAC3B,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC;;UAExB,MAAM;UACN,WAAW;;;eAGX,OAAO;YACP,QAAQ,GAAG,IAAI;aACf,QAAQ,GAAG,IAAI;aACf,QAAQ,GAAG,IAAI;eACf,MAAM,EAAE,GAAG,IAAI;aACf,SAAS,GAAG,IAAI;oBAChB,gBAAgB,GAAG,IAAI;gBACvB,MAAM,EAAE;aACR,OAAO;sBACP,OAAO;iBACP,OAAO;gBACP,OAAO;kBACP,MAAM;cACN,MAAM,GAAG,IAAI;WACb,OAAO;sBACP,OAAO;cACP,OAAO;gBACP,SAAS;;;UAGT,MAAM;UACN,MAAM,GAAG,SAAS;;;cAGlB,OAAO;;iCArCW,wBAAwB;8BADjB,aAAa;8BAAb,aAAa;+BAEtB,eAAe"}
|
||||
+323
@@ -0,0 +1,323 @@
|
||||
/**
|
||||
* @module index
|
||||
* @author Toru Nagashima
|
||||
* @copyright 2015 Toru Nagashima. All rights reserved.
|
||||
* @copyright 2026 Bret Comnes. All rights reserved.
|
||||
* See LICENSE file in root directory for full license.
|
||||
*
|
||||
* @import { Readable, Writable } from 'node:stream'
|
||||
* @import { PackageJson } from 'read-package-json-fast'
|
||||
* @import { ColorMode } from './run-task.js'
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {string | number | boolean | null | undefined} ConfigValue
|
||||
* @typedef {Record<string, ConfigValue>} ConfigMap
|
||||
* @typedef {Record<string, ConfigMap>} PackageConfigMap
|
||||
* @typedef PackageInfo
|
||||
* @property {string} path - The package.json file path.
|
||||
* @property {PackageJson} body - The package.json body.
|
||||
*
|
||||
* @typedef NpmRunAllOptions
|
||||
* @property {boolean} [parallel] - Run scripts in parallel when true; sequentially otherwise.
|
||||
* @property {Readable | null} [stdin] - Input stream for child process stdin.
|
||||
* @property {Writable | null} [stdout] - Output stream for child process stdout.
|
||||
* @property {Writable | null} [stderr] - Output stream for child process stderr.
|
||||
* @property {string[] | null} [taskList] - Actual npm script names. Reads package.json when omitted.
|
||||
* @property {ConfigMap | null} [config] - npm config values passed as `--key=value`.
|
||||
* @property {PackageConfigMap | null} [packageConfig] - Package config values passed as `--pkg:key=value`.
|
||||
* @property {string[]} [arguments] - Arguments used to replace placeholders.
|
||||
* @property {boolean} [silent] - Pass `--silent` to npm.
|
||||
* @property {boolean} [continueOnError] - Continue after failures.
|
||||
* @property {boolean} [printLabel] - Prefix each output line with its task name.
|
||||
* @property {boolean} [printName] - Print task names before running tasks.
|
||||
* @property {number} [maxParallel] - Maximum parallelism when `parallel` is true.
|
||||
* @property {string | null} [npmPath] - Path to npm.
|
||||
* @property {boolean} [race] - Abort other tasks after one parallel task succeeds.
|
||||
* @property {boolean} [aggregateOutput] - Buffer each task's stdout and write it after task completion.
|
||||
* @property {boolean} [nodeRun] - Use `node --run` instead of the package manager to run scripts.
|
||||
* @property {ColorMode} [colorMode] - Override terminal color capability detection.
|
||||
*
|
||||
* @typedef NpmRunAllResult
|
||||
* @property {string} name - Task name.
|
||||
* @property {number | undefined} code - Exit code, or undefined if the task was aborted before completion.
|
||||
*
|
||||
* @typedef PackageJsonConfig
|
||||
* @property {boolean} [nodeRun] - Use `node --run` instead of the package manager to run scripts.
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
import shellQuote from 'shell-quote'
|
||||
import matchTasks from './match-tasks.js'
|
||||
import readPackageJson from './read-package-json.js'
|
||||
import runTasks from './run-tasks.js'
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
const ARGS_PATTERN = /\{(!)?([*@%]|\d+)([^}]+)?}/g
|
||||
const ARGS_UNPACK_PATTERN = /\{(!)?([%])([^}]+)?}/g
|
||||
|
||||
/**
|
||||
* Converts a given value to an array.
|
||||
*
|
||||
* @param {string|string[]|null|undefined} x - A value to convert.
|
||||
* @returns {string[]} An array.
|
||||
*/
|
||||
function toArray (x) {
|
||||
if (x == null) {
|
||||
return []
|
||||
}
|
||||
return Array.isArray(x) ? x : [x]
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces argument placeholders (such as `{1}`) by arguments.
|
||||
*
|
||||
* @param {string[]} patterns - Patterns to replace.
|
||||
* @param {string[]} args - Arguments to replace.
|
||||
* @returns {string[]} replaced
|
||||
*/
|
||||
function applyArguments (patterns, args) {
|
||||
const defaults = Object.create(null)
|
||||
|
||||
const unfoldedPatterns = patterns
|
||||
.flatMap(pattern => {
|
||||
const match = ARGS_UNPACK_PATTERN.exec(pattern)
|
||||
if (match && match[2] === '%') {
|
||||
const result = []
|
||||
for (let i = 0, length = args.length; i < length; i++) {
|
||||
const argPosition = i + 1
|
||||
result.push(pattern.replace(ARGS_UNPACK_PATTERN, (whole, indirectionMark, id, options) => {
|
||||
if (indirectionMark != null || options != null || id !== '%') {
|
||||
throw Error(`Invalid Placeholder: ${whole}`)
|
||||
}
|
||||
return `{${argPosition}}`
|
||||
}))
|
||||
}
|
||||
return result
|
||||
}
|
||||
return pattern
|
||||
})
|
||||
|
||||
return unfoldedPatterns.map(pattern => pattern.replace(ARGS_PATTERN, (whole, indirectionMark, id, options) => {
|
||||
if (indirectionMark != null) {
|
||||
throw Error(`Invalid Placeholder: ${whole}`)
|
||||
}
|
||||
if (id === '@') {
|
||||
return shellQuote.quote(args)
|
||||
}
|
||||
if (id === '*') {
|
||||
return shellQuote.quote([args.join(' ')])
|
||||
}
|
||||
|
||||
const position = parseInt(id, 10)
|
||||
if (position >= 1 && position <= args.length) {
|
||||
return shellQuote.quote([args[position - 1] ?? ''])
|
||||
}
|
||||
|
||||
// Address default values
|
||||
if (options != null) {
|
||||
const prefix = options.slice(0, 2)
|
||||
|
||||
if (prefix === ':=') {
|
||||
defaults[id] = shellQuote.quote([options.slice(2)])
|
||||
return defaults[id]
|
||||
}
|
||||
if (prefix === ':-') {
|
||||
return shellQuote.quote([options.slice(2)])
|
||||
}
|
||||
|
||||
throw Error(`Invalid Placeholder: ${whole}`)
|
||||
}
|
||||
if (defaults[id] != null) {
|
||||
return defaults[id]
|
||||
}
|
||||
|
||||
return ''
|
||||
}))
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse patterns.
|
||||
* In parsing process, it replaces argument placeholders (such as `{1}`) by arguments.
|
||||
*
|
||||
* @param {string|string[]} patternOrPatterns - Patterns to run.
|
||||
* A pattern is a npm-script name or a Glob-like pattern.
|
||||
* @param {string[]} args - Arguments to replace placeholders.
|
||||
* @returns {string[]} Parsed patterns.
|
||||
*/
|
||||
function parsePatterns (patternOrPatterns, args) {
|
||||
const patterns = toArray(patternOrPatterns)
|
||||
const hasPlaceholder = patterns.some(pattern => ARGS_PATTERN.test(pattern))
|
||||
|
||||
return hasPlaceholder ? applyArguments(patterns, args) : patterns
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a given config object to an `--:=` style option array.
|
||||
*
|
||||
* @param {PackageConfigMap} config -
|
||||
* A map-like object to overwrite package configs.
|
||||
* Keys are package names.
|
||||
* Every value is a map-like object (Pairs of variable name and value).
|
||||
* @returns {string[]} `--:=` style options.
|
||||
*/
|
||||
function toOverwriteOptions (config) {
|
||||
/** @type {string[]} */
|
||||
const options = []
|
||||
|
||||
for (const packageName of Object.keys(config)) {
|
||||
const packageConfig = config[packageName]
|
||||
if (!packageConfig) {
|
||||
continue
|
||||
}
|
||||
|
||||
for (const variableName of Object.keys(packageConfig)) {
|
||||
const value = packageConfig[variableName]
|
||||
|
||||
options.push(`--${packageName}:${variableName}=${value}`)
|
||||
}
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a given config object to an `--a=b` style option array.
|
||||
*
|
||||
* @param {ConfigMap} config -
|
||||
* A map-like object to set configs.
|
||||
* @returns {string[]} `--a=b` style options.
|
||||
*/
|
||||
function toConfigOptions (config) {
|
||||
return Object.keys(config).map(key => `--${key}=${config[key]}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum length.
|
||||
*
|
||||
* @param {number} length - The current maximum length.
|
||||
* @param {string} name - A name.
|
||||
* @returns {number} The maximum length.
|
||||
*/
|
||||
function maxLength (length, name) {
|
||||
return Math.max(name.length, length)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and validates the `"npm-run-all2"` config object from a package.json body.
|
||||
*
|
||||
* @param {PackageJson | undefined} body - The package.json body.
|
||||
* @returns {PackageJsonConfig} Validated config (unknown keys and non-boolean values are ignored).
|
||||
*/
|
||||
function readPackageConfig (body) {
|
||||
const raw = body?.['npm-run-all2']
|
||||
if (raw == null || typeof raw !== 'object' || Array.isArray(raw)) {
|
||||
return {}
|
||||
}
|
||||
/** @type {PackageJsonConfig} */
|
||||
const config = {}
|
||||
if ('nodeRun' in raw && /** @type {any} */ (raw).nodeRun === true) {
|
||||
config.nodeRun = true
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Runs npm-scripts which are matched with given patterns.
|
||||
*
|
||||
* @param {string|string[]} patternOrPatterns - Patterns to run.
|
||||
* A pattern is a npm-script name or a Glob-like pattern.
|
||||
* @param {NpmRunAllOptions} [options] Optional.
|
||||
* @returns {Promise<null | NpmRunAllResult[]>}
|
||||
* A promise object which becomes fulfilled when all npm-scripts are completed.
|
||||
*/
|
||||
export default function npmRunAll (patternOrPatterns, options) {
|
||||
const stdin = (options && options.stdin) || null
|
||||
const stdout = (options && options.stdout) || null
|
||||
const stderr = (options && options.stderr) || null
|
||||
const taskList = (options && options.taskList) || null
|
||||
const config = (options && options.config) || null
|
||||
const packageConfig = (options && options.packageConfig) || null
|
||||
const args = (options && options.arguments) || []
|
||||
const parallel = Boolean(options && options.parallel)
|
||||
const silent = Boolean(options && options.silent)
|
||||
const continueOnError = Boolean(options && options.continueOnError)
|
||||
const printLabel = Boolean(options && options.printLabel)
|
||||
const printName = Boolean(options && options.printName)
|
||||
const race = Boolean(options && options.race)
|
||||
const maxParallel = parallel ? ((options && options.maxParallel) || 0) : 1
|
||||
const aggregateOutput = Boolean(options && options.aggregateOutput)
|
||||
const npmPath = options && options.npmPath
|
||||
const nodeRun = Boolean(options && options.nodeRun)
|
||||
const colorMode = (options && options.colorMode) || 'auto'
|
||||
try {
|
||||
const patterns = parsePatterns(patternOrPatterns, args)
|
||||
if (patterns.length === 0) {
|
||||
return Promise.resolve(null)
|
||||
}
|
||||
if (taskList != null && Array.isArray(taskList) === false) {
|
||||
throw new Error('Invalid options.taskList')
|
||||
}
|
||||
if (typeof maxParallel !== 'number' || maxParallel < 0) {
|
||||
throw new Error('Invalid options.maxParallel')
|
||||
}
|
||||
if (!parallel && aggregateOutput) {
|
||||
throw new Error('Invalid options.aggregateOutput; It requires options.parallel')
|
||||
}
|
||||
if (!parallel && race) {
|
||||
throw new Error('Invalid options.race; It requires options.parallel')
|
||||
}
|
||||
|
||||
const prefixOptions = [
|
||||
...(silent ? ['--silent'] : []),
|
||||
...(packageConfig ? toOverwriteOptions(packageConfig) : []),
|
||||
...(config ? toConfigOptions(config) : []),
|
||||
]
|
||||
|
||||
return (async () => {
|
||||
const x = taskList != null
|
||||
? { taskList, packageInfo: null }
|
||||
: await readPackageJson()
|
||||
const tasks = matchTasks(x.taskList, patterns)
|
||||
const labelWidth = tasks.reduce(maxLength, 0)
|
||||
|
||||
const pkgConfig = readPackageConfig(x.packageInfo?.body)
|
||||
const effectiveNodeRun = nodeRun || pkgConfig.nodeRun === true
|
||||
|
||||
return runTasks(tasks, {
|
||||
stdin,
|
||||
stdout,
|
||||
stderr,
|
||||
prefixOptions,
|
||||
continueOnError,
|
||||
labelState: {
|
||||
enabled: printLabel,
|
||||
width: labelWidth,
|
||||
lastPrefix: null,
|
||||
lastIsLinebreak: true,
|
||||
},
|
||||
printName,
|
||||
packageInfo: x.packageInfo,
|
||||
race,
|
||||
maxParallel,
|
||||
npmPath,
|
||||
aggregateOutput,
|
||||
nodeRun: effectiveNodeRun,
|
||||
colorMode,
|
||||
})
|
||||
})()
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err)
|
||||
return Promise.reject(new Error(message))
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
export default function matchTasks(taskList: string[], patterns: string[]): string[];
|
||||
export type TaskFilter = {
|
||||
match: (candidate: string) => boolean;
|
||||
task: string;
|
||||
args: string;
|
||||
isGlob: boolean;
|
||||
};
|
||||
//# sourceMappingURL=match-tasks.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"match-tasks.d.ts","sourceRoot":"","sources":["match-tasks.js"],"names":[],"mappings":"AA0GA,6CALW,MAAM,EAAE,YACR,MAAM,EAAE,GACN,MAAM,EAAE,CAsCpB;;WAtHa,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO;UAC9B,MAAM;UACN,MAAM;YACN,OAAO"}
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* @module match-tasks
|
||||
* @author Toru Nagashima
|
||||
* @copyright 2015 Toru Nagashima. All rights reserved.
|
||||
* @copyright 2026 Bret Comnes. All rights reserved.
|
||||
* See LICENSE file in root directory for full license.
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
import picomatch from 'picomatch'
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
const COLON_OR_SLASH = /[:/]/g
|
||||
const CONVERT_MAP = /** @type {const} */ ({ ':': '/', '/': ':' })
|
||||
|
||||
/**
|
||||
* @typedef TaskFilter
|
||||
* @property {(candidate: string) => boolean} match - Pattern matcher.
|
||||
* @property {string} task - The task pattern without arguments.
|
||||
* @property {string} args - Arguments appended to matched tasks.
|
||||
* @property {boolean} isGlob - Whether the task pattern contains glob characters.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Swaps ":" and "/", in order to use ":" as the separator in picomatch.
|
||||
*
|
||||
* @param {string} s - A text to swap.
|
||||
* @returns {string} The text which was swapped.
|
||||
*/
|
||||
function swapColonAndSlash (s) {
|
||||
return s.replace(COLON_OR_SLASH, (matched) => CONVERT_MAP[/** @type {':' | '/'} */ (matched)])
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a filter from user-specified pattern text.
|
||||
*
|
||||
* The task name is the part until the first space.
|
||||
* The rest part is the arguments for this task.
|
||||
*
|
||||
* @param {string} pattern - A pattern to create filter.
|
||||
* @returns {TaskFilter} The filter object of the pattern.
|
||||
*/
|
||||
function createFilter (pattern) {
|
||||
const trimmed = pattern.trim()
|
||||
const spacePos = trimmed.indexOf(' ')
|
||||
const task = spacePos < 0 ? trimmed : trimmed.slice(0, spacePos)
|
||||
const args = spacePos < 0 ? '' : trimmed.slice(spacePos)
|
||||
const swapped = swapColonAndSlash(task)
|
||||
const { isGlob } = picomatch.scan(swapped)
|
||||
const match = picomatch(swapped, {
|
||||
nonegate: true,
|
||||
strictSlashes: true,
|
||||
})
|
||||
|
||||
return { match, task, args, isGlob }
|
||||
}
|
||||
|
||||
/**
|
||||
* The set to remove overlapped task.
|
||||
*/
|
||||
class TaskSet {
|
||||
/**
|
||||
* Creates a instance.
|
||||
*/
|
||||
constructor () {
|
||||
/** @type {string[]} */
|
||||
this.result = []
|
||||
/** @type {Record<string, string[]>} */
|
||||
this.sourceMap = Object.create(null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a command (a pattern) into this set if it's not overlapped.
|
||||
* "Overlapped" is meaning that the command was added from a different source.
|
||||
*
|
||||
* @param {string} command - A pattern text to add.
|
||||
* @param {string} source - A task name to check.
|
||||
* @returns {void}
|
||||
*/
|
||||
add (command, source) {
|
||||
const sourceList = this.sourceMap[command] || (this.sourceMap[command] = [])
|
||||
if (sourceList.length === 0 || sourceList.indexOf(source) !== -1) {
|
||||
this.result.push(command)
|
||||
}
|
||||
sourceList.push(source)
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Enumerates tasks which matches with given patterns.
|
||||
*
|
||||
* @param {string[]} taskList - A list of actual task names.
|
||||
* @param {string[]} patterns - Pattern texts to match.
|
||||
* @returns {string[]} Tasks which matches with the patterns.
|
||||
* @private
|
||||
*/
|
||||
export default function matchTasks (taskList, patterns) {
|
||||
const filters = patterns.map(createFilter)
|
||||
const candidates = taskList.map(swapColonAndSlash)
|
||||
const taskSet = new TaskSet()
|
||||
const unknownSet = Object.create(null)
|
||||
|
||||
// Take tasks while keep the order of patterns.
|
||||
for (const filter of filters) {
|
||||
let found = false
|
||||
|
||||
for (const candidate of candidates) {
|
||||
if (filter.match(candidate)) {
|
||||
found = true
|
||||
taskSet.add(
|
||||
swapColonAndSlash(candidate) + filter.args,
|
||||
filter.task
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Built-in tasks should be allowed.
|
||||
if (!found && (filter.task === 'restart' || filter.task === 'env')) {
|
||||
taskSet.add(filter.task + filter.args, filter.task)
|
||||
found = true
|
||||
}
|
||||
if (!found && !filter.isGlob) {
|
||||
unknownSet[filter.task] = true
|
||||
}
|
||||
}
|
||||
|
||||
const unknownTasks = Object.keys(unknownSet)
|
||||
if (unknownTasks.length > 0) {
|
||||
throw new Error(`Task not found: "${unknownTasks.join('", ')}"`)
|
||||
}
|
||||
return taskSet.result
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
export default class NpmRunAllError extends Error {
|
||||
constructor(causeResult: {
|
||||
name: string;
|
||||
task?: string;
|
||||
code: number;
|
||||
}, allResults: Array<{
|
||||
name: string;
|
||||
code: (number | undefined);
|
||||
}>);
|
||||
code: number;
|
||||
results: Array<{
|
||||
name: string;
|
||||
code: (number | undefined);
|
||||
}>;
|
||||
}
|
||||
//# sourceMappingURL=npm-run-all-error.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"npm-run-all-error.d.ts","sourceRoot":"","sources":["npm-run-all-error.js"],"names":[],"mappings":"AAeA;IASE,yBALa;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,cAE3C,KAAK,CAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,CAAC,MAAM,GAAC,SAAS,CAAC,CAAA;KAAC,CAAC,EAwB5D;IAPC,MAFc,MAAM,CAEQ;IAM5B,SAFc,KAAK,CAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,CAAC,MAAM,GAAC,SAAS,CAAC,CAAA;KAAC,CAAC,CAErC;CAE5B"}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @module npm-run-all-error
|
||||
* @author Toru Nagashima
|
||||
* @copyright 2016 Toru Nagashima. All rights reserved.
|
||||
* @copyright 2026 Bret Comnes. All rights reserved.
|
||||
* See LICENSE file in root directory for full license.
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Error object with some additional info.
|
||||
*/
|
||||
export default class NpmRunAllError extends Error {
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param {{name: string, task?: string, code: number}} causeResult -
|
||||
* The result item of the npm-script which causes an error.
|
||||
* @param {Array.<{name: string, code: (number|undefined)}>} allResults -
|
||||
* All result items of npm-scripts.
|
||||
*/
|
||||
constructor (causeResult, allResults) {
|
||||
super(`"${causeResult.task ?? causeResult.name}" exited with ${causeResult.code}.`)
|
||||
|
||||
/**
|
||||
* The name of a npm-script which exited with a non-zero code.
|
||||
* @type {string}
|
||||
*/
|
||||
this.name = causeResult.name
|
||||
|
||||
/**
|
||||
* The code of a npm-script which exited with a non-zero code.
|
||||
* This can be `undefined`.
|
||||
* @type {number}
|
||||
*/
|
||||
this.code = causeResult.code
|
||||
|
||||
/**
|
||||
* All result items of npm-scripts.
|
||||
* @type {Array.<{name: string, code: (number|undefined)}>}
|
||||
*/
|
||||
this.results = allResults
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
export default function readPackageJson(): Promise<ReadPackageJsonResult>;
|
||||
export type ReadPackageJsonResult = {
|
||||
taskList: string[];
|
||||
packageInfo: PackageInfo;
|
||||
};
|
||||
import type { PackageInfo } from './index.js';
|
||||
//# sourceMappingURL=read-package-json.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"read-package-json.d.ts","sourceRoot":"","sources":["read-package-json.js"],"names":[],"mappings":"AAiCA,2CAFa,OAAO,CAAC,qBAAqB,CAAC,CAQ1C;;cA3Ba,MAAM,EAAE;iBACR,WAAW;;iCANO,YAAY"}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @module read-package-json
|
||||
* @author Toru Nagashima
|
||||
* @copyright 2016 Toru Nagashima. All rights reserved.
|
||||
* @copyright 2026 Bret Comnes. All rights reserved.
|
||||
* See LICENSE file in root directory for full license.
|
||||
*
|
||||
* @import { PackageInfo } from './index.js'
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef ReadPackageJsonResult
|
||||
* @property {string[]} taskList - npm script names.
|
||||
* @property {PackageInfo} packageInfo - package.json metadata.
|
||||
*/
|
||||
|
||||
import readPackage from 'read-package-json-fast'
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
import { join as joinPath } from 'path'
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Reads the package.json in the current directory.
|
||||
*
|
||||
* @returns {Promise<ReadPackageJsonResult>} package.json's information.
|
||||
*/
|
||||
export default function readPackageJson () {
|
||||
const path = joinPath(process.cwd(), 'package.json')
|
||||
return readPackage(path).then(body => ({
|
||||
taskList: Object.keys(body.scripts || {}),
|
||||
packageInfo: { path, body },
|
||||
}))
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
export default function runTask(task: string, options: RunTaskOptions): AbortableRunTaskPromise;
|
||||
export type ColorMode = "auto" | "none" | "16" | "256";
|
||||
export type LabelState = PrefixState & {
|
||||
enabled: boolean;
|
||||
width: number;
|
||||
};
|
||||
export type RunTaskOptions = {
|
||||
stdin: Readable | null;
|
||||
stdout: Writable | null;
|
||||
stderr: Writable | null;
|
||||
prefixOptions: string[];
|
||||
labelState: LabelState;
|
||||
printName: boolean;
|
||||
packageInfo: PackageInfo | null;
|
||||
npmPath?: string | null | undefined;
|
||||
nodeRun?: boolean;
|
||||
colorMode?: ColorMode;
|
||||
};
|
||||
export type RunTaskResult = {
|
||||
task: string;
|
||||
code: number | null;
|
||||
signal: NodeJS.Signals | null;
|
||||
};
|
||||
export type AbortableRunTaskPromise = Promise<RunTaskResult> & {
|
||||
abort: () => void;
|
||||
};
|
||||
import type { PrefixState } from './create-prefix-transform-stream.js';
|
||||
import type { Readable } from 'node:stream';
|
||||
import type { Writable } from 'node:stream';
|
||||
import type { PackageInfo } from './index.js';
|
||||
//# sourceMappingURL=run-task.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"run-task.d.ts","sourceRoot":"","sources":["run-task.js"],"names":[],"mappings":"AAqOA,sCAPW,MAAM,WACN,cAAc,GACZ,uBAAuB,CAoInC;wBAtVY,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,KAAK;yBAC9B,WAAW,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE;;WAEhD,QAAQ,GAAG,IAAI;YACf,QAAQ,GAAG,IAAI;YACf,QAAQ,GAAG,IAAI;mBACf,MAAM,EAAE;gBACR,UAAU;eACV,OAAO;iBACP,WAAW,GAAG,IAAI;cAClB,MAAM,GAAG,IAAI,GAAG,SAAS;cACzB,OAAO;gBACP,SAAS;;;UAGT,MAAM;UACN,MAAM,GAAG,IAAI;YACb,MAAM,CAAC,OAAO,GAAG,IAAI;;sCAEtB,OAAO,CAAC,aAAa,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,IAAI,CAAA;CAAE;iCAxB3B,qCAAqC;8BAF9B,aAAa;8BAAb,aAAa;iCAGpB,YAAY"}
|
||||
+357
@@ -0,0 +1,357 @@
|
||||
/**
|
||||
* @module run-task
|
||||
* @author Toru Nagashima
|
||||
* @copyright 2015 Toru Nagashima. All rights reserved.
|
||||
* @copyright 2026 Bret Comnes. All rights reserved.
|
||||
* See LICENSE file in root directory for full license.
|
||||
*
|
||||
* @import { Readable, Writable } from 'node:stream'
|
||||
* @import { ChildProcess, SpawnOptions } from 'node:child_process'
|
||||
* @import { PrefixState } from './create-prefix-transform-stream.js'
|
||||
* @import { PackageInfo } from './index.js'
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {'auto' | 'none' | '16' | '256'} ColorMode
|
||||
* @typedef {PrefixState & { enabled: boolean, width: number }} LabelState
|
||||
* @typedef RunTaskOptions
|
||||
* @property {Readable | null} stdin - Stream to send messages to child process stdin.
|
||||
* @property {Writable | null} stdout - Stream to receive child process stdout.
|
||||
* @property {Writable | null} stderr - Stream to receive child process stderr.
|
||||
* @property {string[]} prefixOptions - Options inserted before the task name.
|
||||
* @property {LabelState} labelState - Shared output-label state.
|
||||
* @property {boolean} printName - Print task names before running each task.
|
||||
* @property {PackageInfo | null} packageInfo - package.json metadata.
|
||||
* @property {string | null | undefined} [npmPath] - Path to npm.
|
||||
* @property {boolean} [nodeRun] - Use `node --run` instead of the package manager to run scripts.
|
||||
* @property {ColorMode} [colorMode] - Override terminal color capability detection.
|
||||
*
|
||||
* @typedef RunTaskResult
|
||||
* @property {string} task - Task name.
|
||||
* @property {number | null} code - Exit code.
|
||||
* @property {NodeJS.Signals | null} signal - Exit signal.
|
||||
*
|
||||
* @typedef {Promise<RunTaskResult> & { abort: () => void }} AbortableRunTaskPromise
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
import ansiStyles from 'ansi-styles'
|
||||
import { parse as parseArgs } from 'shell-quote'
|
||||
import which from 'which'
|
||||
|
||||
import createHeader from './create-header.js'
|
||||
import createPrefixTransform from './create-prefix-transform-stream.js'
|
||||
import spawn from './spawn.js'
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/** Named colors for terminals with 16-color support. */
|
||||
const COLORS_NAMED = /** @type {const} */ ([
|
||||
'cyan', 'green', 'magenta', 'yellow', 'red', 'blue',
|
||||
'cyanBright', 'greenBright', 'magentaBright', 'yellowBright', 'redBright', 'blueBright',
|
||||
'white', 'gray',
|
||||
])
|
||||
|
||||
/** ANSI 256-color codes for terminals with 256+ color support. */
|
||||
const COLORS_256 = /** @type {const} */ ([
|
||||
51, 82, 196, 201, 226, 46, 45, 208, 93, 154,
|
||||
21, 213, 48, 165, 220, 33, 129, 214, 63, 202,
|
||||
])
|
||||
|
||||
let colorIndex = 0
|
||||
/** @type {Map<string, number>} */
|
||||
const taskNamesToColorIndex = new Map()
|
||||
|
||||
/**
|
||||
* Returns a consistent color index for a task name, assigning one on first use.
|
||||
*
|
||||
* @param {string} taskName - The task name.
|
||||
* @returns {number} Color index.
|
||||
*/
|
||||
function getColorIndex (taskName) {
|
||||
let idx = taskNamesToColorIndex.get(taskName)
|
||||
if (idx === undefined) {
|
||||
idx = colorIndex++
|
||||
taskNamesToColorIndex.set(taskName, idx)
|
||||
}
|
||||
return idx
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an `{ open, close }` ANSI color pair using the combined 256-color palette.
|
||||
* Named colors are used first, then the 256-color codes, for 34 distinct colors before wrapping.
|
||||
*
|
||||
* @param {number} idx - Color index.
|
||||
* @returns {{ open: string, close: string }} ANSI escape sequences.
|
||||
*/
|
||||
function selectColor256 (idx) {
|
||||
const total = COLORS_NAMED.length + COLORS_256.length
|
||||
const wrapped = idx % total
|
||||
if (wrapped < COLORS_NAMED.length) {
|
||||
const name = COLORS_NAMED[wrapped] ?? 'cyan'
|
||||
return /** @type {{ open: string, close: string }} */ (ansiStyles[name])
|
||||
}
|
||||
const code = COLORS_256[wrapped - COLORS_NAMED.length] ?? COLORS_256[0]
|
||||
return { open: ansiStyles.color.ansi256(code), close: ansiStyles.color.close }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an `{ open, close }` ANSI color pair for the given task name.
|
||||
*
|
||||
* @param {Writable} stream - The output stream (used to detect color depth when mode is 'auto').
|
||||
* @param {string} taskName - The task name.
|
||||
* @param {ColorMode} [colorMode] - Override color palette selection.
|
||||
* @returns {{ open: string, close: string }} ANSI escape sequences.
|
||||
*/
|
||||
function selectColor (stream, taskName, colorMode = 'auto') {
|
||||
const idx = getColorIndex(taskName)
|
||||
|
||||
if (colorMode === 'none') {
|
||||
return { open: '', close: '' }
|
||||
}
|
||||
|
||||
if (colorMode === '256') {
|
||||
return selectColor256(idx)
|
||||
}
|
||||
|
||||
if (colorMode === '16') {
|
||||
const name = COLORS_NAMED[idx % COLORS_NAMED.length] ?? 'cyan'
|
||||
return /** @type {{ open: string, close: string }} */ (ansiStyles[name])
|
||||
}
|
||||
|
||||
// auto: detect from stream
|
||||
const depth = /** @type {{ getColorDepth?: () => number }} */ (stream).getColorDepth?.() ?? 4
|
||||
|
||||
if (depth < 4) {
|
||||
return { open: '', close: '' }
|
||||
}
|
||||
|
||||
if (depth >= 8) {
|
||||
return selectColor256(idx)
|
||||
}
|
||||
|
||||
const name = COLORS_NAMED[idx % COLORS_NAMED.length] ?? 'cyan'
|
||||
return /** @type {{ open: string, close: string }} */ (ansiStyles[name])
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps stdout/stderr with a transform stream to add the task name as prefix.
|
||||
*
|
||||
* @param {string} taskName - The task name.
|
||||
* @param {Writable | null} source - An output stream to be wrapped.
|
||||
* @param {LabelState} labelState - An label state for the transform stream.
|
||||
* @param {ColorMode} [colorMode] - Override color palette selection.
|
||||
* @returns {Writable | null} `source` or the created wrapped stream.
|
||||
*/
|
||||
function wrapLabeling (taskName, source, labelState, colorMode) {
|
||||
if (source == null || !labelState.enabled) {
|
||||
return source
|
||||
}
|
||||
|
||||
const label = taskName.padEnd(labelState.width)
|
||||
const isTTY = Boolean((/** @type {Writable & { isTTY?: boolean }} */ (source)).isTTY)
|
||||
const color = isTTY ? selectColor(source, taskName, colorMode) : { open: '', close: '' }
|
||||
const prefix = `${color.open}[${label}]${color.close} `
|
||||
const stream = createPrefixTransform(prefix, labelState)
|
||||
|
||||
stream.pipe(source)
|
||||
|
||||
return stream
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a given stream to an option for `child_process.spawn`.
|
||||
*
|
||||
* @param {Readable | Writable | null} stream - An original stream to convert.
|
||||
* @param {Readable | Writable} std - A standard stream for this option.
|
||||
* @returns {'ignore' | 'pipe' | Readable | Writable} An option for `child_process.spawn`.
|
||||
*/
|
||||
function detectStreamKind (stream, std) {
|
||||
return (
|
||||
stream == null
|
||||
? 'ignore' // `|| !std.isTTY` is needed for the workaround of https://github.com/nodejs/node/issues/5620
|
||||
: stream !== std || !(/** @type {Readable & { isTTY?: boolean }} */ (std)).isTTY
|
||||
? 'pipe'
|
||||
: stream
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the output of shell-quote's `parse()` is acceptable input to npm-cli.
|
||||
*
|
||||
* The `parse()` method of shell-quote sometimes returns special objects in its
|
||||
* output array, e.g. if it thinks some elements should be globbed. But npm-cli
|
||||
* only accepts strings and will throw an error otherwise.
|
||||
*
|
||||
* See https://github.com/substack/node-shell-quote#parsecmd-env
|
||||
*
|
||||
* @param {unknown} arg - Item in the output of shell-quote's `parse()`.
|
||||
* @returns {string} A valid argument for npm-cli.
|
||||
*/
|
||||
function cleanTaskArg (arg) {
|
||||
if (typeof arg === 'string') {
|
||||
return arg
|
||||
}
|
||||
if (arg && typeof arg === 'object') {
|
||||
if ('pattern' in arg && typeof arg.pattern === 'string') {
|
||||
return arg.pattern
|
||||
}
|
||||
if ('op' in arg && typeof arg.op === 'string') {
|
||||
return arg.op
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Interface
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Run a npm-script of a given name.
|
||||
* The return value is a promise which has an extra method: `abort()`.
|
||||
* The `abort()` kills the child process to run the npm-script.
|
||||
*
|
||||
* @param {string} task - A npm-script name to run.
|
||||
* @param {RunTaskOptions} options - An option object.
|
||||
* @returns {AbortableRunTaskPromise}
|
||||
* A promise object which becomes fulfilled when the npm-script is completed.
|
||||
* This promise object has an extra method: `abort()`.
|
||||
* @private
|
||||
*/
|
||||
export default function runTask (task, options) {
|
||||
/** @type {ChildProcess | null} */
|
||||
let cp = null
|
||||
|
||||
async function asyncRunTask () {
|
||||
const stdin = options.stdin
|
||||
const stdout = wrapLabeling(task, options.stdout, options.labelState, options.colorMode)
|
||||
const stderr = wrapLabeling(task, options.stderr, options.labelState, options.colorMode)
|
||||
const stdinKind = detectStreamKind(stdin, process.stdin)
|
||||
const stdoutKind = detectStreamKind(stdout, process.stdout)
|
||||
const stderrKind = detectStreamKind(stderr, process.stderr)
|
||||
/** @type {SpawnOptions} */
|
||||
const spawnOptions = { stdio: [stdinKind, stdoutKind, stderrKind] }
|
||||
|
||||
// Print task name.
|
||||
if (options.printName && stdout != null) {
|
||||
stdout.write(createHeader(
|
||||
task,
|
||||
options.packageInfo,
|
||||
Boolean((/** @type {Writable & { isTTY?: boolean }} */ (stdout)).isTTY)
|
||||
))
|
||||
}
|
||||
|
||||
// Execute.
|
||||
let finalExecPath
|
||||
let finalSpawnArgs
|
||||
|
||||
if (options.nodeRun) {
|
||||
const taskParts = parseArgs(task).map(cleanTaskArg)
|
||||
const scriptName = taskParts[0] ?? task
|
||||
const restParts = taskParts.slice(1)
|
||||
const separatorIdx = restParts.indexOf('--')
|
||||
const scriptArgs = separatorIdx === -1 ? restParts : restParts.slice(separatorIdx + 1)
|
||||
finalExecPath = process.execPath
|
||||
finalSpawnArgs = scriptArgs.length > 0
|
||||
? ['--run', scriptName, '--', ...scriptArgs]
|
||||
: ['--run', scriptName]
|
||||
} else {
|
||||
let npmPath = options.npmPath
|
||||
if (!npmPath && process.env['npm_execpath']) {
|
||||
const npmExecPath = process.env['npm_execpath']
|
||||
const basename = path.basename(npmExecPath)
|
||||
let newBasename = basename
|
||||
if (basename.startsWith('npx')) {
|
||||
newBasename = basename.replace('npx', 'npm')
|
||||
} else if (basename.startsWith('pnpx')) {
|
||||
newBasename = basename.replace('pnpx', 'pnpm')
|
||||
}
|
||||
|
||||
npmPath = newBasename !== basename
|
||||
? path.join(path.dirname(npmExecPath), newBasename)
|
||||
: npmExecPath
|
||||
}
|
||||
|
||||
const npmPathIsJs = typeof npmPath === 'string' && /\.(c|m)?js/.test(path.extname(npmPath))
|
||||
let execPath = (npmPathIsJs ? process.execPath : npmPath || 'npm')
|
||||
|
||||
if (!npmPath && !process.env['npm_execpath'] && options.packageInfo != null) {
|
||||
// When a script is being run via pnpm, npmPath and npm_execpath will be null or undefined
|
||||
// Attempt to figure out whether we're running via pnpm
|
||||
const projectRoot = path.dirname(options.packageInfo.path)
|
||||
const hasPnpmLockfile = fs.existsSync(path.join(projectRoot, 'pnpm-lock.yaml'))
|
||||
const whichPnpmResults = await which('pnpm', { nothrow: true })
|
||||
const pnpmFound = whichPnpmResults?.status
|
||||
const pnpmWhichOutput = whichPnpmResults?.output
|
||||
if (hasPnpmLockfile && import.meta.dirname.split(path.sep).includes('.pnpm') && pnpmFound && pnpmWhichOutput) {
|
||||
execPath = pnpmWhichOutput
|
||||
}
|
||||
}
|
||||
|
||||
const isYarn = process.env['npm_config_user_agent']?.startsWith('yarn')
|
||||
const isPnpm = Boolean(process.env['PNPM_SCRIPT_SRC_DIR'])
|
||||
const isNpm = !isYarn && !isPnpm
|
||||
|
||||
const spawnArgs = ['run']
|
||||
|
||||
if (npmPathIsJs && npmPath) {
|
||||
spawnArgs.unshift(npmPath)
|
||||
}
|
||||
if (isNpm) {
|
||||
Array.prototype.push.apply(spawnArgs, options.prefixOptions)
|
||||
} else if (options.prefixOptions.indexOf('--silent') !== -1) {
|
||||
spawnArgs.push('--silent')
|
||||
}
|
||||
Array.prototype.push.apply(spawnArgs, parseArgs(task).map(cleanTaskArg))
|
||||
|
||||
finalExecPath = execPath
|
||||
finalSpawnArgs = spawnArgs
|
||||
}
|
||||
|
||||
const child = await spawn(finalExecPath, finalSpawnArgs, spawnOptions)
|
||||
cp = child
|
||||
|
||||
// Piping stdio.
|
||||
if (stdinKind === 'pipe' && stdin != null && child.stdin != null) {
|
||||
stdin.pipe(child.stdin)
|
||||
}
|
||||
if (stdoutKind === 'pipe' && stdout != null && child.stdout != null) {
|
||||
child.stdout.pipe(stdout, { end: false })
|
||||
}
|
||||
if (stderrKind === 'pipe' && stderr != null && child.stderr != null) {
|
||||
child.stderr.pipe(stderr, { end: false })
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// Register
|
||||
child.on('error', (err) => {
|
||||
cp = null
|
||||
reject(err)
|
||||
})
|
||||
child.on('close', (code, signal) => {
|
||||
cp = null
|
||||
resolve({ task, code, signal })
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const promise = /** @type {AbortableRunTaskPromise} */ (/** @type {unknown} */ (asyncRunTask()))
|
||||
|
||||
promise.abort = function abort () {
|
||||
if (cp != null) {
|
||||
cp.kill()
|
||||
cp = null
|
||||
}
|
||||
}
|
||||
|
||||
return promise
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
export default function runTasks(tasks: string[], options: RunTasksOptions): Promise<NpmRunAllResult[]>;
|
||||
export type RunTasksOptions = RunTaskOptions & {
|
||||
continueOnError: boolean;
|
||||
race: boolean;
|
||||
maxParallel: number;
|
||||
aggregateOutput: boolean;
|
||||
stdout: Writable | null;
|
||||
};
|
||||
import type { NpmRunAllResult } from './index.js';
|
||||
import type { RunTaskOptions } from './run-task.js';
|
||||
import type { Writable } from 'node:stream';
|
||||
//# sourceMappingURL=run-tasks.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"run-tasks.d.ts","sourceRoot":"","sources":["run-tasks.js"],"names":[],"mappings":"AAgGA,wCALW,MAAM,EAAE,WACR,eAAe,GACb,OAAO,CAAC,eAAe,EAAE,CAAC,CAmJtC;8BAnOY,cAAc,GAAG;IACzB,eAAe,EAAE,OAAO,CAAC;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,OAAO,CAAC;IACzB,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAA;CACxB;qCAVgC,YAAY;oCADY,eAAe;8BAD9C,aAAa"}
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
/**
|
||||
* @module run-tasks-in-parallel
|
||||
* @author Toru Nagashima
|
||||
* @copyright 2015 Toru Nagashima. All rights reserved.
|
||||
* @copyright 2026 Bret Comnes. All rights reserved.
|
||||
* See LICENSE file in root directory for full license.
|
||||
*
|
||||
* @import { Writable } from 'node:stream'
|
||||
* @import { AbortableRunTaskPromise, RunTaskOptions } from './run-task.js'
|
||||
* @import { NpmRunAllResult } from './index.js'
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {RunTaskOptions & {
|
||||
* continueOnError: boolean,
|
||||
* race: boolean,
|
||||
* maxParallel: number,
|
||||
* aggregateOutput: boolean,
|
||||
* stdout: Writable | null
|
||||
* }} RunTasksOptions
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
import MemoryStream from 'memorystream'
|
||||
import NpmRunAllError from './npm-run-all-error.js'
|
||||
import runTask from './run-task.js'
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Remove the given value from the array.
|
||||
* @template T
|
||||
* @param {T[]} array - The array to remove.
|
||||
* @param {T} x - The item to be removed.
|
||||
* @returns {void}
|
||||
*/
|
||||
function remove (array, x) {
|
||||
const index = array.indexOf(x)
|
||||
if (index !== -1) {
|
||||
array.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {Record<string, number>} */
|
||||
const signals = {
|
||||
SIGABRT: 6,
|
||||
SIGALRM: 14,
|
||||
SIGBUS: 10,
|
||||
SIGCHLD: 20,
|
||||
SIGCONT: 19,
|
||||
SIGFPE: 8,
|
||||
SIGHUP: 1,
|
||||
SIGILL: 4,
|
||||
SIGINT: 2,
|
||||
SIGKILL: 9,
|
||||
SIGPIPE: 13,
|
||||
SIGQUIT: 3,
|
||||
SIGSEGV: 11,
|
||||
SIGSTOP: 17,
|
||||
SIGTERM: 15,
|
||||
SIGTRAP: 5,
|
||||
SIGTSTP: 18,
|
||||
SIGTTIN: 21,
|
||||
SIGTTOU: 22,
|
||||
SIGUSR1: 30,
|
||||
SIGUSR2: 31,
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a signal name to a number.
|
||||
* @param {string | null} signal - the signal name to convert into a number
|
||||
* @returns {number} - the return code for the signal
|
||||
*/
|
||||
function convert (signal) {
|
||||
return signal ? signals[signal] || 0 : 0
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Run npm-scripts of given names in parallel.
|
||||
*
|
||||
* If a npm-script exited with a non-zero code, this aborts other all npm-scripts.
|
||||
*
|
||||
* @param {string[]} tasks - A list of npm-script name to run in parallel.
|
||||
* @param {RunTasksOptions} options - An option object.
|
||||
* @returns {Promise<NpmRunAllResult[]>} A promise object which becomes fulfilled when all npm-scripts are completed.
|
||||
* @private
|
||||
*/
|
||||
export default function runTasks (tasks, options) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (tasks.length === 0) {
|
||||
resolve([])
|
||||
return
|
||||
}
|
||||
|
||||
/** @type {NpmRunAllResult[]} */
|
||||
const results = tasks.map(task => ({ name: task, code: undefined }))
|
||||
const queue = tasks.map((task, index) => ({ name: task, index }))
|
||||
/** @type {AbortableRunTaskPromise[]} */
|
||||
const promises = []
|
||||
/** @type {Error | null} */
|
||||
let error = null
|
||||
let aborted = false
|
||||
|
||||
/**
|
||||
* Done.
|
||||
* @returns {void}
|
||||
*/
|
||||
function done () {
|
||||
if (error == null) {
|
||||
resolve(results)
|
||||
} else {
|
||||
reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Aborts all tasks.
|
||||
* @returns {void}
|
||||
*/
|
||||
function abort () {
|
||||
if (aborted) {
|
||||
return
|
||||
}
|
||||
aborted = true
|
||||
|
||||
if (promises.length === 0) {
|
||||
done()
|
||||
} else {
|
||||
for (const p of promises) {
|
||||
p.abort()
|
||||
}
|
||||
Promise.all(promises).then(done, reject)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a next task.
|
||||
* @returns {void}
|
||||
*/
|
||||
function next () {
|
||||
if (aborted) {
|
||||
return
|
||||
}
|
||||
if (queue.length === 0) {
|
||||
if (promises.length === 0) {
|
||||
done()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const originalOutputStream = options.stdout
|
||||
const optionsClone = { ...options }
|
||||
const writer = new MemoryStream(undefined, {
|
||||
readable: false,
|
||||
})
|
||||
|
||||
if (options.aggregateOutput) {
|
||||
optionsClone.stdout = writer
|
||||
}
|
||||
|
||||
const task = queue.shift()
|
||||
if (!task) {
|
||||
return
|
||||
}
|
||||
const promise = runTask(task.name, optionsClone)
|
||||
|
||||
promises.push(promise)
|
||||
promise.then(
|
||||
(result) => {
|
||||
remove(promises, promise)
|
||||
if (aborted) {
|
||||
return
|
||||
}
|
||||
|
||||
if (options.aggregateOutput && originalOutputStream != null) {
|
||||
originalOutputStream.write(writer.toString())
|
||||
}
|
||||
|
||||
// Check if the task failed as a result of a signal, and
|
||||
// amend the exit code as a result.
|
||||
if (result.code === null && result.signal !== null) {
|
||||
// An exit caused by a signal must return a status code
|
||||
// of 128 plus the value of the signal code.
|
||||
// Ref: https://nodejs.org/api/process.html#process_exit_codes
|
||||
result.code = 128 + convert(result.signal)
|
||||
}
|
||||
|
||||
// Save the result.
|
||||
const resultItem = results[task.index]
|
||||
if (resultItem) {
|
||||
resultItem.code = result.code ?? undefined
|
||||
}
|
||||
|
||||
// Aborts all tasks if it's an error.
|
||||
if (result.code) {
|
||||
error = new NpmRunAllError({ name: result.task, task: result.task, code: result.code }, results)
|
||||
if (!options.continueOnError) {
|
||||
abort()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Aborts all tasks if options.race is true.
|
||||
if (options.race && !result.code) {
|
||||
abort()
|
||||
return
|
||||
}
|
||||
|
||||
// Call the next task.
|
||||
next()
|
||||
},
|
||||
(thisError) => {
|
||||
remove(promises, promise)
|
||||
if (!options.continueOnError || options.race) {
|
||||
error = thisError
|
||||
abort()
|
||||
return
|
||||
}
|
||||
next()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const max = options.maxParallel
|
||||
const end = (typeof max === 'number' && max > 0)
|
||||
? Math.min(tasks.length, max)
|
||||
: tasks.length
|
||||
for (let i = 0; i < end; ++i) {
|
||||
next()
|
||||
}
|
||||
})
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
export default function spawn(command: string, args: string[], options: SpawnOptions): ChildProcess;
|
||||
import type { SpawnOptions } from 'node:child_process';
|
||||
import type { ChildProcess } from 'node:child_process';
|
||||
//# sourceMappingURL=spawn-posix.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"spawn-posix.d.ts","sourceRoot":"","sources":["spawn-posix.js"],"names":[],"mappings":"AAiEA,uCANW,MAAM,QACN,MAAM,EAAE,WACR,YAAY,GACV,YAAY,CAQxB;kCA/D8C,oBAAoB;kCAApB,oBAAoB"}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* @module spawn-posix
|
||||
* @author Toru Nagashima
|
||||
* @copyright 2015 Toru Nagashima. All rights reserved.
|
||||
* @copyright 2026 Bret Comnes. All rights reserved.
|
||||
* See LICENSE file in root directory for full license.
|
||||
*
|
||||
* @import { ChildProcess, SpawnOptions } from 'node:child_process'
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
import crossSpawn from 'cross-spawn'
|
||||
import getDescendentProcessInfo from 'pidtree'
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Kills the new process and its sub processes.
|
||||
* @this {ChildProcess}
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function kill () {
|
||||
if (this.pid == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
getDescendentProcessInfo(this.pid, { root: true }, (/** @type {Error | undefined} */ err, /** @type {number[]} */ pids) => {
|
||||
if (err) {
|
||||
return
|
||||
}
|
||||
|
||||
for (const pid of pids) {
|
||||
try {
|
||||
process.kill(pid)
|
||||
} catch (_err) {
|
||||
// ignore.
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Launches a new process with the given command.
|
||||
* This is almost same as `child_process.spawn`.
|
||||
*
|
||||
* This returns a `ChildProcess` instance.
|
||||
* `kill` method of the instance kills the new process and its sub processes.
|
||||
*
|
||||
* @param {string} command - The command to run.
|
||||
* @param {string[]} args - List of string arguments.
|
||||
* @param {SpawnOptions} options - Options.
|
||||
* @returns {ChildProcess} A ChildProcess instance of new process.
|
||||
* @private
|
||||
*/
|
||||
export default function spawn (command, args, options) {
|
||||
const child = crossSpawn(command, args, options)
|
||||
child.kill = kill
|
||||
|
||||
return child
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
export default function spawn(command: string, args: string[], options: SpawnOptions): ChildProcess;
|
||||
import type { SpawnOptions } from 'node:child_process';
|
||||
import type { ChildProcess } from 'node:child_process';
|
||||
//# sourceMappingURL=spawn-win32.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"spawn-win32.d.ts","sourceRoot":"","sources":["spawn-win32.js"],"names":[],"mappings":"AAkDA,uCANW,MAAM,QACN,MAAM,EAAE,WACR,YAAY,GACV,YAAY,CAQxB;kCAhD8C,oBAAoB;kCAApB,oBAAoB"}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* @module spawn-win32
|
||||
* @author Toru Nagashima
|
||||
* @copyright 2015 Toru Nagashima. All rights reserved.
|
||||
* @copyright 2026 Bret Comnes. All rights reserved.
|
||||
* See LICENSE file in root directory for full license.
|
||||
*
|
||||
* @import { ChildProcess, SpawnOptions } from 'node:child_process'
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
import crossSpawn from 'cross-spawn'
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Kills the new process and its sub processes forcibly.
|
||||
* @this {ChildProcess}
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function kill () {
|
||||
if (this.pid == null) {
|
||||
return false
|
||||
}
|
||||
crossSpawn('taskkill', ['/F', '/T', '/PID', String(this.pid)])
|
||||
return true
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Launches a new process with the given command.
|
||||
* This is almost same as `child_process.spawn`.
|
||||
*
|
||||
* This returns a `ChildProcess` instance.
|
||||
* `kill` method of the instance kills the new process and its sub processes forcibly.
|
||||
*
|
||||
* @param {string} command - The command to run.
|
||||
* @param {string[]} args - List of string arguments.
|
||||
* @param {SpawnOptions} options - Options.
|
||||
* @returns {ChildProcess} A ChildProcess instance of new process.
|
||||
* @private
|
||||
*/
|
||||
export default function spawn (command, args, options) {
|
||||
const child = crossSpawn(command, args, options)
|
||||
child.kill = kill
|
||||
|
||||
return child
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
export default function spawn(command: string, args: string[], options: SpawnOptions): Promise<ChildProcess>;
|
||||
import type { SpawnOptions } from 'node:child_process';
|
||||
import type { ChildProcess } from 'node:child_process';
|
||||
//# sourceMappingURL=spawn.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"spawn.d.ts","sourceRoot":"","sources":["spawn.js"],"names":[],"mappings":"AA0BA,uCALW,MAAM,QACN,MAAM,EAAE,WACR,YAAY,GACV,OAAO,CAAC,YAAY,CAAC,CAQjC;kCAzB8C,oBAAoB;kCAApB,oBAAoB"}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @module spawn
|
||||
* @author Toru Nagashima
|
||||
* @copyright 2015 Toru Nagashima. All rights reserved.
|
||||
* @copyright 2026 Bret Comnes. All rights reserved.
|
||||
* See LICENSE file in root directory for full license.
|
||||
*
|
||||
* @import { ChildProcess, SpawnOptions } from 'node:child_process'
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Launches a new process with the given command.
|
||||
* This is {@link ./spawn-posix.js:spawn} or {@link ./spawn-win32.js:spawn}
|
||||
* @private
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string} command - The command to run.
|
||||
* @param {string[]} args - List of string arguments.
|
||||
* @param {SpawnOptions} options - Options.
|
||||
* @returns {Promise<ChildProcess>}
|
||||
*/
|
||||
export default async function spawn (command, args, options) {
|
||||
const spawn = process.platform === 'win32'
|
||||
? (await import('./spawn-win32.js')).default
|
||||
: (await import('./spawn-posix.js')).default
|
||||
|
||||
return spawn(command, args, options)
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"name": "npm-run-all2",
|
||||
"type": "module",
|
||||
"version": "9.0.3",
|
||||
"description": "A CLI tool to run multiple npm-scripts in parallel or sequential. (Maintenance fork)",
|
||||
"bin": {
|
||||
"run-p": "bin/run-p/index.js",
|
||||
"run-s": "bin/run-s/index.js",
|
||||
"npm-run-all": "bin/npm-run-all/index.js",
|
||||
"npm-run-all2": "bin/npm-run-all/index.js"
|
||||
},
|
||||
"main": "./lib/index.js",
|
||||
"imports": {
|
||||
"#lib": "./lib/index.js",
|
||||
"#lib/*": "./lib/*",
|
||||
"#bin/common/*": "./bin/common/*"
|
||||
},
|
||||
"types": "./lib/index.d.ts",
|
||||
"engines": {
|
||||
"node": "^22.22.2 || ^24.15.0 || >=26.0.0",
|
||||
"npm": ">= 10"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "npm run clean:declarations-lib && rm -rf coverage jsdoc \"test-workspace/{build,test.txt}\"",
|
||||
"docs": "jsdoc -c jsdoc.json",
|
||||
"check:installed-check": "installed-check --ignore-dev",
|
||||
"check:eslint": "eslint",
|
||||
"check:knip": "knip",
|
||||
"check:tsc": "tsc",
|
||||
"precheck": "node scripts/make-slink.js",
|
||||
"check": "node ./bin/run-p/index.js check:*",
|
||||
"test": "node ./bin/run-s/index.js test:*",
|
||||
"test:check": "npm run check",
|
||||
"test:node-test": "node scripts/make-slink.js && node --test --test-reporter spec --test-concurrency=1 --test-timeout=240000 --experimental-test-coverage",
|
||||
"watch": "node --watch --test",
|
||||
"version": "releasearoni version",
|
||||
"build": "npm run build:declaration",
|
||||
"build:declaration": "tsc -p declaration.tsconfig.json",
|
||||
"clean:declarations-lib": "find lib -type f -name '*.d.*' -delete",
|
||||
"prepublishOnly": "releasearoni",
|
||||
"postpublish": "npm run clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": "^7.0.0",
|
||||
"cross-spawn": "^7.0.6",
|
||||
"memorystream": "^0.3.1",
|
||||
"picomatch": "^4.0.2",
|
||||
"pidtree": "^1.0.0",
|
||||
"read-package-json-fast": "^6.0.0",
|
||||
"shell-quote": "^1.8.4",
|
||||
"which": "^7.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cross-spawn": "^6.0.6",
|
||||
"@types/memorystream": "^0.3.4",
|
||||
"@types/node": "^26.0.0",
|
||||
"@types/picomatch": "^4.0.3",
|
||||
"@voxpelli/tsconfig": "^16.0.0",
|
||||
"eslint": "^9.6.0",
|
||||
"installed-check": "^10.0.1",
|
||||
"jsdoc": "^4.0.0",
|
||||
"knip": "^6.0.0",
|
||||
"neostandard": "^0.13.0",
|
||||
"releasearoni": "^0.2.0",
|
||||
"typescript": "^6.0.3",
|
||||
"yarn": "^1.12.3"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/bcomnes/npm-run-all2.git"
|
||||
},
|
||||
"keywords": [
|
||||
"cli",
|
||||
"command",
|
||||
"commandline",
|
||||
"tool",
|
||||
"npm",
|
||||
"npm-scripts",
|
||||
"run",
|
||||
"sequential",
|
||||
"serial",
|
||||
"parallel",
|
||||
"task"
|
||||
],
|
||||
"author": {
|
||||
"name": "Toru Nagashima"
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Bret Comnes",
|
||||
"email": "bcomnes@gmail.com",
|
||||
"url": "https://bret.io"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/bcomnes/npm-run-all2/issues"
|
||||
},
|
||||
"files": [
|
||||
"bin",
|
||||
"lib",
|
||||
"types"
|
||||
],
|
||||
"homepage": "https://github.com/bcomnes/npm-run-all2"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
declare module 'read-package-json-fast' {
|
||||
type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue }
|
||||
|
||||
export interface PackageJson {
|
||||
name?: string
|
||||
version?: string
|
||||
scripts?: Record<string, string>
|
||||
[key: string]: JsonValue | undefined
|
||||
}
|
||||
|
||||
export default function readPackageJsonFast (path: string): Promise<PackageJson>
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
declare module 'which' {
|
||||
export interface WhichOptions {
|
||||
path?: string
|
||||
pathExt?: string
|
||||
all?: boolean
|
||||
nothrow?: boolean
|
||||
}
|
||||
|
||||
export interface WhichResult {
|
||||
status: boolean
|
||||
output: string | null
|
||||
}
|
||||
|
||||
export default function which (command: string, options: WhichOptions & { nothrow: true }): Promise<WhichResult>
|
||||
export default function which (command: string, options?: WhichOptions): Promise<string>
|
||||
}
|
||||
Reference in New Issue
Block a user