runset 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +153 -0
- package/dist/index.d.mts +243 -0
- package/dist/index.mjs +1665 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alexey Prokhorov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# runset
|
|
2
|
+
|
|
3
|
+
Run npm scripts in sequence or in parallel. Based on ideas from
|
|
4
|
+
[`npm-run-all`](https://github.com/mysticatea/npm-run-all), with reusable
|
|
5
|
+
pipelines, colored labels, and grouped output.
|
|
6
|
+
|
|
7
|
+
Requires Node.js 24.2 or later. No runtime dependencies.
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install --save-dev runset
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Use `runset` in your npm scripts, or run it with `npx runset`:
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
npx runset lint test build # one after another
|
|
17
|
+
npx runset -p lint test # together
|
|
18
|
+
npx runset clean -p lint test -s build # clean, then lint + test, then build
|
|
19
|
+
npx runset -p "watch:**" # all watch scripts together
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Each `-p` or `-s` starts a new group. Groups run in order, so in the mixed
|
|
23
|
+
example above, build waits for both lint and test to finish.
|
|
24
|
+
|
|
25
|
+
By default, a failed command stops the run. Ctrl+C stops all running commands
|
|
26
|
+
and their child processes.
|
|
27
|
+
|
|
28
|
+
## Commands
|
|
29
|
+
|
|
30
|
+
Names are looked up in `package.json` scripts. Other commands run in the shell.
|
|
31
|
+
Quote commands that contain spaces or patterns:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
npx runset lint "node scripts/check.js"
|
|
35
|
+
npx runset "build:*" # build:api, build:web
|
|
36
|
+
npx runset "build:**" # also includes build:api:types
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`*` matches one part of a script name between colons; `**` matches across
|
|
40
|
+
colons. A pattern with no matches is skipped.
|
|
41
|
+
|
|
42
|
+
Pass arguments to a script inside the quotes, or use placeholders for arguments
|
|
43
|
+
after `--`:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
npx runset "test --watch"
|
|
47
|
+
npx runset "serve --port {1}" -- 8080
|
|
48
|
+
npx runset "test {@}" -- --watch --verbose
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`{1}`, `{2}`, etc. insert individual arguments. `{@}` inserts all arguments.
|
|
52
|
+
Placeholder values are quoted automatically for the shell.
|
|
53
|
+
|
|
54
|
+
## Options
|
|
55
|
+
|
|
56
|
+
| Option | What it does |
|
|
57
|
+
| ----------------------- | --------------------------------------------- |
|
|
58
|
+
| `-p, --parallel` | Run the following commands together |
|
|
59
|
+
| `-s, --serial` | Run the following commands one after another |
|
|
60
|
+
| `-j, --jobs <n>` | Limit how many commands run at once |
|
|
61
|
+
| `--on-failure <action>` | `stop` (default), `continue`, or `restart` |
|
|
62
|
+
| `--on-success <action>` | `continue` (default), `stop`, or `restart` |
|
|
63
|
+
| `-o, --output grouped` | Buffer each command's output until it exits |
|
|
64
|
+
| `--stdout <file>` | Write stdout to a file |
|
|
65
|
+
| `--labels <mode>` | `auto` (default), `all`, `custom`, or `none` |
|
|
66
|
+
| `--cwd <dir>` | Set the working directory |
|
|
67
|
+
| `--dry-run` | Show what would run without starting anything |
|
|
68
|
+
| `-c, --config <path>` | Use a specific config file |
|
|
69
|
+
|
|
70
|
+
Use `--help` for all options.
|
|
71
|
+
|
|
72
|
+
Add `::` to set options for one command:
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
npx runset -p "api::label=server,color=cyan" web
|
|
76
|
+
npx runset "lint::on-failure=continue" build
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`stop` ends the whole run. `continue` lets it carry on. `restart` runs the
|
|
80
|
+
command again without a delay or retry limit.
|
|
81
|
+
|
|
82
|
+
## Output
|
|
83
|
+
|
|
84
|
+
Commands running together get colored labels automatically, so you can tell
|
|
85
|
+
which command wrote each line. Set your own with `::label=api,color=cyan`. Use
|
|
86
|
+
`--no-color` for plain text or `--labels none` to hide labels.
|
|
87
|
+
|
|
88
|
+
To keep each command's output together, use grouped output:
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
npx runset -p lint test -o grouped
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Each command's output is buffered and printed when it exits.
|
|
95
|
+
|
|
96
|
+
## Config
|
|
97
|
+
|
|
98
|
+
Save a pipeline in `runset.config.ts`, then run `npx runset`:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import type { ConfigJs } from 'runset';
|
|
102
|
+
|
|
103
|
+
export default {
|
|
104
|
+
commands: [
|
|
105
|
+
'clean',
|
|
106
|
+
{ parallel: true },
|
|
107
|
+
'lint',
|
|
108
|
+
'test',
|
|
109
|
+
{ serial: true },
|
|
110
|
+
'build',
|
|
111
|
+
],
|
|
112
|
+
} satisfies ConfigJs;
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
This runs clean, then lint and test together, then build. Settings entries apply
|
|
116
|
+
to the commands that follow them.
|
|
117
|
+
|
|
118
|
+
Use `commandDictionary` to give a command or pipeline a reusable name:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import type { ConfigJs } from 'runset';
|
|
122
|
+
|
|
123
|
+
export default {
|
|
124
|
+
commandDictionary: {
|
|
125
|
+
check: ['lint', 'test'],
|
|
126
|
+
api: { command: 'node server.js', env: { PORT: '4000' } },
|
|
127
|
+
},
|
|
128
|
+
} satisfies ConfigJs;
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Run these with `npx runset check` or `npx runset api`. Dictionary names take
|
|
132
|
+
priority over npm script names.
|
|
133
|
+
|
|
134
|
+
Config files also support `.js`, `.mjs`, `.cjs`, and `.json`. runset looks in
|
|
135
|
+
the working directory and its parents. CLI commands run **after** any `commands`
|
|
136
|
+
listed in the config.
|
|
137
|
+
|
|
138
|
+
## Library
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
import runset from 'runset';
|
|
142
|
+
|
|
143
|
+
await runset(['lint', 'test', 'build']);
|
|
144
|
+
await runset({ commands: ['lint', 'test'], parallel: true, jobs: 2 });
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The promise resolves with a `Run`, or rejects with a `RunsetError` if the run
|
|
148
|
+
fails. Use `Run.fromConfigJs(config)` to prepare a run, `describe()` to inspect
|
|
149
|
+
it, `start()` to run it, and `terminate()` to stop it.
|
|
150
|
+
|
|
151
|
+
## License
|
|
152
|
+
|
|
153
|
+
[MIT](LICENSE)
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
export type StdTiming = 'grouped' | 'realtime';
|
|
3
|
+
/** How a command stream is handled. */
|
|
4
|
+
export interface Std {
|
|
5
|
+
/** `stdout`, `stderr`, `none`, or a file path. */
|
|
6
|
+
destination: string;
|
|
7
|
+
timing: StdTiming;
|
|
8
|
+
}
|
|
9
|
+
export type ExitAction = 'continue' | 'restart' | 'stop';
|
|
10
|
+
export declare const EXIT_ACTIONS: Set<ExitAction>;
|
|
11
|
+
export type LogLevel = 'debug' | 'error' | 'info' | 'warn';
|
|
12
|
+
/** Which commands get a label in front of their output: see `--labels`. */
|
|
13
|
+
export type LabelMode = 'all' | 'auto' | 'custom' | 'none';
|
|
14
|
+
/** A resolved command, one per process a run spawns. */
|
|
15
|
+
export interface Command {
|
|
16
|
+
bgColor: string;
|
|
17
|
+
color: string;
|
|
18
|
+
command: string;
|
|
19
|
+
cwd: string;
|
|
20
|
+
disabled: boolean;
|
|
21
|
+
env: NodeJS.ProcessEnv;
|
|
22
|
+
label: string;
|
|
23
|
+
/** What is handed to the shell: for an npm command, the script's body. */
|
|
24
|
+
line: string;
|
|
25
|
+
onFailure: ExitAction;
|
|
26
|
+
onSuccess: ExitAction;
|
|
27
|
+
parallel: boolean;
|
|
28
|
+
/** The `package.json` script name, when `type` is `'npm'`. */
|
|
29
|
+
scriptName?: string;
|
|
30
|
+
/** Commands sharing a stage run together; stages run in order. */
|
|
31
|
+
stage: number;
|
|
32
|
+
stderr: Std;
|
|
33
|
+
stdout: Std;
|
|
34
|
+
type: 'npm' | 'shell';
|
|
35
|
+
}
|
|
36
|
+
export type LabelFormatter = (context: {
|
|
37
|
+
/** Whether runset is emitting ANSI color at all. */
|
|
38
|
+
color: boolean;
|
|
39
|
+
/** Its `label` is already padded. */
|
|
40
|
+
command: Command;
|
|
41
|
+
/** runset's own prefix, to decorate rather than replace. */
|
|
42
|
+
defaultPrefix: string;
|
|
43
|
+
stream: 'stderr' | 'stdout';
|
|
44
|
+
}) => string;
|
|
45
|
+
/** The parts of a {@link Command} a user may set by hand. */
|
|
46
|
+
export type CommandOptions = {
|
|
47
|
+
stderr?: Partial<Std> | string;
|
|
48
|
+
stdout?: Partial<Std> | string;
|
|
49
|
+
} & Partial<Omit<Command, 'line' | 'scriptName' | 'stage' | 'stderr' | 'stdout' | 'type'>>;
|
|
50
|
+
/** One command written as an object; `command` is what makes it one. */
|
|
51
|
+
export type CommandEntry = {
|
|
52
|
+
command: string;
|
|
53
|
+
} & CommandOptions;
|
|
54
|
+
/**
|
|
55
|
+
* Defaults for the entries after it — the config spelling of `-p`/`-s`:
|
|
56
|
+
* `commands: ['clean', { parallel: true }, 'lint', 'test']`.
|
|
57
|
+
*/
|
|
58
|
+
export type CommandSettings = {
|
|
59
|
+
serial?: boolean;
|
|
60
|
+
} & Omit<CommandOptions, 'command'>;
|
|
61
|
+
/** A command before normalization; falsy entries are skipped. */
|
|
62
|
+
export type CommandDefinition = CommandEntry | CommandSettings | false | null | string | undefined;
|
|
63
|
+
/** What a user writes in `runset.config.*`, or hands to `runset({ … })`. */
|
|
64
|
+
export interface ConfigJs {
|
|
65
|
+
color?: boolean;
|
|
66
|
+
/** Named commands, resolved before `package.json` scripts. An entry may be a list. */
|
|
67
|
+
commandDictionary?: Record<string, CommandDefinition | CommandDefinition[]>;
|
|
68
|
+
commands?: CommandDefinition[];
|
|
69
|
+
cwd?: string;
|
|
70
|
+
dryRun?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Renders the prefix of each labelled output line.
|
|
73
|
+
*
|
|
74
|
+
* ```js
|
|
75
|
+
* formatLabel: ({ defaultPrefix }) => `${new Date().toISOString()} ${defaultPrefix}`,
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
formatLabel?: LabelFormatter;
|
|
79
|
+
/** Max commands running at once. Default: unlimited. */
|
|
80
|
+
jobs?: number;
|
|
81
|
+
/** Milliseconds from SIGTERM/SIGINT to SIGKILL; `0` for none. Default: 5000. */
|
|
82
|
+
killTimeout?: number;
|
|
83
|
+
/** Default: `'auto'`. */
|
|
84
|
+
labels?: LabelMode;
|
|
85
|
+
logLevel?: LogLevel;
|
|
86
|
+
/** What a non-zero exit does to the run. Default: `'stop'`. */
|
|
87
|
+
onFailure?: ExitAction;
|
|
88
|
+
/** What a clean exit does to the run. Default: `'continue'`. */
|
|
89
|
+
onSuccess?: ExitAction;
|
|
90
|
+
/** The default `parallel` for every command. Default: `false`. */
|
|
91
|
+
parallel?: boolean;
|
|
92
|
+
stderr?: Partial<Std> | string;
|
|
93
|
+
stdout?: Partial<Std> | string;
|
|
94
|
+
}
|
|
95
|
+
/** A config module may export the object itself or a sync factory for it. */
|
|
96
|
+
export type ConfigJsExport = ((context: {
|
|
97
|
+
argv: string[];
|
|
98
|
+
cwd: string;
|
|
99
|
+
env: NodeJS.ProcessEnv;
|
|
100
|
+
}) => ConfigJs) | ConfigJs;
|
|
101
|
+
export interface TerminateOptions {
|
|
102
|
+
/** Send SIGKILL. */
|
|
103
|
+
force?: boolean;
|
|
104
|
+
/** Default: SIGTERM. Ignored under `force`. */
|
|
105
|
+
signal?: NodeJS.Signals;
|
|
106
|
+
}
|
|
107
|
+
/** Where a run writes. */
|
|
108
|
+
export interface Destinations {
|
|
109
|
+
stderr: NodeJS.WritableStream;
|
|
110
|
+
stdout: NodeJS.WritableStream;
|
|
111
|
+
}
|
|
112
|
+
/** True for an object written without a `command` — a settings entry. */
|
|
113
|
+
export declare function isCommandSettings(definition: CommandEntry | CommandSettings): definition is CommandSettings;
|
|
114
|
+
export declare function isStreamDestination(destination: string): destination is 'stderr' | 'stdout';
|
|
115
|
+
//#endregion
|
|
116
|
+
//#region src/config/parseCli.d.ts
|
|
117
|
+
/** Run-wide options a CLI flag can set. */
|
|
118
|
+
interface CliOptions {
|
|
119
|
+
color?: boolean;
|
|
120
|
+
config?: string;
|
|
121
|
+
cwd?: string;
|
|
122
|
+
dryRun?: boolean;
|
|
123
|
+
jobs?: number;
|
|
124
|
+
killTimeout?: number;
|
|
125
|
+
labels?: string;
|
|
126
|
+
logLevel?: string;
|
|
127
|
+
onFailure?: string;
|
|
128
|
+
onSuccess?: string;
|
|
129
|
+
output?: string;
|
|
130
|
+
stderr?: string;
|
|
131
|
+
stdout?: string;
|
|
132
|
+
}
|
|
133
|
+
interface ParsedCli {
|
|
134
|
+
/** The argv this is the parse of. */
|
|
135
|
+
argv: string[];
|
|
136
|
+
/** The tasks as written, with a settings entry where a `-p`/`-s` stood. */
|
|
137
|
+
commands: CommandDefinition[];
|
|
138
|
+
help: boolean;
|
|
139
|
+
options: CliOptions;
|
|
140
|
+
positional: string[];
|
|
141
|
+
version: boolean;
|
|
142
|
+
}
|
|
143
|
+
//#endregion
|
|
144
|
+
//#region src/config/Config.d.ts
|
|
145
|
+
/** The resolved run-wide configuration: CLI flag → config file → default. */
|
|
146
|
+
declare class Config {
|
|
147
|
+
readonly commands: CommandDefinition[];
|
|
148
|
+
readonly commandDictionary: Record<string, CommandDefinition | CommandDefinition[]>;
|
|
149
|
+
/** Everything after `--`, for placeholders. */
|
|
150
|
+
readonly args: string[];
|
|
151
|
+
readonly cwd: string;
|
|
152
|
+
readonly env: NodeJS.ProcessEnv;
|
|
153
|
+
readonly jobs: number;
|
|
154
|
+
readonly killTimeout: number;
|
|
155
|
+
readonly onSuccess: ExitAction;
|
|
156
|
+
readonly onFailure: ExitAction;
|
|
157
|
+
readonly color: boolean;
|
|
158
|
+
readonly parallel: boolean;
|
|
159
|
+
readonly labels: LabelMode;
|
|
160
|
+
readonly logLevel: LogLevel;
|
|
161
|
+
readonly dryRun: boolean;
|
|
162
|
+
readonly formatLabel: LabelFormatter | undefined;
|
|
163
|
+
readonly stdout: Std;
|
|
164
|
+
readonly stderr: Std;
|
|
165
|
+
readonly destinations: Destinations;
|
|
166
|
+
/** Printed by the run once it is built. */
|
|
167
|
+
readonly warnings: string[];
|
|
168
|
+
constructor({ cli, configJs, cwd, destinations, env }: ResolvedConfigOptions);
|
|
169
|
+
/** Throws on the first invalid value; types too, as config files are arbitrary JS. */
|
|
170
|
+
validate(): void;
|
|
171
|
+
/** True when `commands` holds something to run, not just settings entries. */
|
|
172
|
+
hasCommands(): boolean;
|
|
173
|
+
}
|
|
174
|
+
interface ResolvedConfigOptions {
|
|
175
|
+
/** Defaults to the process's own argv; a library caller passes `parseCli([])`. */
|
|
176
|
+
cli: ParsedCli;
|
|
177
|
+
/** Skips the config-file lookup; absent means "look for a file". */
|
|
178
|
+
configJs?: ConfigJsExport;
|
|
179
|
+
cwd: string;
|
|
180
|
+
/** Where the run writes; read for color autodetection. */
|
|
181
|
+
destinations: Destinations;
|
|
182
|
+
env: NodeJS.ProcessEnv;
|
|
183
|
+
}
|
|
184
|
+
//#endregion
|
|
185
|
+
//#region src/utils/fs.d.ts
|
|
186
|
+
interface PackageInfo {
|
|
187
|
+
filePath: string;
|
|
188
|
+
name: string;
|
|
189
|
+
scripts: Record<string, string>;
|
|
190
|
+
version: string;
|
|
191
|
+
}
|
|
192
|
+
//#endregion
|
|
193
|
+
//#region src/plan/plan.d.ts
|
|
194
|
+
interface Plan {
|
|
195
|
+
commands: Command[];
|
|
196
|
+
config: Config;
|
|
197
|
+
packageInfo: PackageInfo;
|
|
198
|
+
}
|
|
199
|
+
//#endregion
|
|
200
|
+
//#region src/run/Run.d.ts
|
|
201
|
+
/** One run: the processes built from a plan, and what stops them. */
|
|
202
|
+
export declare class Run {
|
|
203
|
+
readonly config: Config;
|
|
204
|
+
private readonly logger;
|
|
205
|
+
private readonly files;
|
|
206
|
+
private stopping;
|
|
207
|
+
constructor({ commands, config, packageInfo }: Plan);
|
|
208
|
+
/** A library caller's config object, through the same pipeline as the CLI. */
|
|
209
|
+
static fromConfigJs(configJs: ConfigJs): Run;
|
|
210
|
+
isFailed(): boolean;
|
|
211
|
+
getExitCode(): number;
|
|
212
|
+
/** The resolved run, and every option in force, as `--dry-run` prints it. */
|
|
213
|
+
describe(): string;
|
|
214
|
+
/** Rejects with a {@link RunsetError} when the run did not succeed. */
|
|
215
|
+
start(): Promise<Run>;
|
|
216
|
+
terminate(options?: TerminateOptions): void;
|
|
217
|
+
private failed;
|
|
218
|
+
/** Says a signal arrived before any command's parting words, then stops. */
|
|
219
|
+
private onSignal;
|
|
220
|
+
private stopEverything;
|
|
221
|
+
private forceKill;
|
|
222
|
+
private clearKillTimer;
|
|
223
|
+
}
|
|
224
|
+
//#endregion
|
|
225
|
+
//#region src/utils/errors.d.ts
|
|
226
|
+
/** Any failure runset reports itself; `exitCode` is what the CLI exits with. */
|
|
227
|
+
export declare class RunsetError extends Error {
|
|
228
|
+
readonly exitCode: number;
|
|
229
|
+
constructor(message: string, exitCode?: number);
|
|
230
|
+
}
|
|
231
|
+
/** A command line runset cannot read. */
|
|
232
|
+
export declare class CliError extends RunsetError {}
|
|
233
|
+
/** A run-wide option or config file runset cannot use. */
|
|
234
|
+
export declare class ConfigError extends RunsetError {}
|
|
235
|
+
/** A command that cannot be resolved. */
|
|
236
|
+
export declare class NormalizeError extends RunsetError {}
|
|
237
|
+
//#endregion
|
|
238
|
+
//#region src/index.d.ts
|
|
239
|
+
/** Runs commands, or a whole config object, and resolves once they are done. */
|
|
240
|
+
export declare function runset(config: ConfigJs): Promise<Run>;
|
|
241
|
+
export declare function runset(commands: CommandDefinition | CommandDefinition[], configJs?: ConfigJs): Promise<Run>;
|
|
242
|
+
//#endregion
|
|
243
|
+
export { runset as default };
|