Skip to content

Commit a56fc43

Browse files
committed
feat(scripts): enhacne output streaming
1 parent 01a76bd commit a56fc43

File tree

4 files changed

+116
-82
lines changed

4 files changed

+116
-82
lines changed

packages/scripts/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
"postpublish": "del ./package"
3636
},
3737
"dependencies": {
38+
"@simple-libs/child-process-utils": "^1.0.1",
39+
"@simple-libs/stream-utils": "^1.1.0",
3840
"argue-cli": "^2.0.0",
3941
"dotenv": "^16.0.0",
4042
"p-limit": "^5.0.0"

packages/scripts/src/utils/run.js

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,53 @@ import { spawn } from './spawn.js'
44
import { getArgs } from './args.js'
55

66
/**
7-
* Run package manager scripts serial.
7+
* Run package manager scripts parallel.
88
* @param {string} pm
99
* @param {string[][]} scripts
1010
* @param {{ scripts?: Record<string, string> }} pkg - package.json
11-
* @returns Exit code
11+
* @param {number} concurrency - Number of concurrent processes.
12+
* @returns Exit code.
1213
*/
13-
export async function runSerial(pm, scripts, pkg) {
14-
const cmds = scripts.map(script => getArgs(pm, script, pkg))
15-
let exitCode = 0
16-
let result
14+
export async function run(pm, scripts, pkg, concurrency) {
15+
const limit = pLimit(concurrency)
16+
/** @type {Promise<ReturnType<typeof spawn>>[]} */
17+
const tasks = scripts.map(script => new Promise(
18+
resolve => limit(() => {
19+
const [bin, args] = getArgs(pm, script, pkg)
20+
const child = spawn(bin, args)
21+
22+
resolve(child)
1723

18-
for (const [bin, args] of cmds) {
19-
result = await spawn(bin, args)
20-
exitCode = exitCode || result.exitCode
24+
return child.exitCode
25+
})
26+
))
27+
let finalExitCode = 0
2128

22-
if (result.output) {
23-
process.stdout.write(result.output)
29+
for await (const task of tasks) {
30+
for await (const data of task.output) {
31+
if (data.source === 'stdout') {
32+
process.stdout.write(data.chunk)
33+
} else
34+
if (data.source === 'stderr') {
35+
process.stderr.write(data.chunk)
36+
}
2437
}
38+
39+
finalExitCode = finalExitCode || await task.exitCode
2540
}
2641

27-
return exitCode
42+
return finalExitCode
43+
}
44+
45+
/**
46+
* Run package manager scripts serial.
47+
* @param {string} pm
48+
* @param {string[][]} scripts
49+
* @param {{ scripts?: Record<string, string> }} pkg - package.json
50+
* @returns Exit code
51+
*/
52+
export function runSerial(pm, scripts, pkg) {
53+
return run(pm, scripts, pkg, 1)
2854
}
2955

3056
/**
@@ -34,19 +60,6 @@ export async function runSerial(pm, scripts, pkg) {
3460
* @param {{ scripts?: Record<string, string> }} pkg - package.json
3561
* @returns Exit code.
3662
*/
37-
export async function runParallel(pm, scripts, pkg) {
38-
const limit = pLimit(cpus().length)
39-
const cmds = scripts.map(script => getArgs(pm, script, pkg))
40-
const tasks = cmds.map(([bin, args]) => limit(() => spawn(bin, args, false)))
41-
let exitCode = 0
42-
/** @type {{ exitCode: number, output?: string | Error }} */
43-
let result
44-
45-
for (const task of tasks) {
46-
result = await task
47-
exitCode = exitCode || result.exitCode
48-
process.stdout.write(result.output)
49-
}
50-
51-
return exitCode
63+
export function runParallel(pm, scripts, pkg) {
64+
return run(pm, scripts, pkg, cpus().length)
5265
}

packages/scripts/src/utils/spawn.js

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,26 @@
11
import { spawn as spawnChild } from 'child_process'
2+
import { exitCode } from '@simple-libs/child-process-utils'
3+
import { mergeReadables } from '@simple-libs/stream-utils'
24

35
/**
46
* Spawn child process.
57
* @param {string} cmd
68
* @param {string[]} args
7-
* @param {boolean} stdio
8-
* @returns {Promise<{ exitCode: number, output?: string | Error }>} Output.
9+
* @returns Output.
910
*/
10-
export function spawn(cmd, args, stdio = true) {
11-
return new Promise((resolve) => {
12-
const env = {
11+
export function spawn(cmd, args) {
12+
const child = spawnChild(cmd, args, {
13+
env: {
1314
FORCE_COLOR: true,
1415
...process.env
1516
}
16-
const options = {
17-
env,
18-
stdio: stdio ? 'inherit' : 'pipe'
19-
}
20-
const child = spawnChild(cmd, args, options)
21-
let output = ''
22-
const onData = (data) => {
23-
output += data.toString()
24-
}
25-
const onDone = (error) => {
26-
resolve({
27-
exitCode: child.exitCode,
28-
output: output || (typeof error === 'number' ? '' : `${error}\n`)
29-
})
30-
}
31-
32-
child.stdout?.on('data', onData)
33-
child.stderr?.on('data', onData)
34-
child.on('close', onDone)
35-
child.on('error', onDone)
3617
})
18+
19+
return {
20+
output: mergeReadables({
21+
stdout: child.stdout,
22+
stderr: child.stderr
23+
}),
24+
exitCode: exitCode(child)
25+
}
3726
}

0 commit comments

Comments
 (0)