@livestore/utils-dev 0.4.0 → 0.5.0-dev.1
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/dist/.tsbuildinfo +1 -1
- package/dist/node/DockerCompose/DockerCompose.d.ts +52 -0
- package/dist/node/DockerCompose/DockerCompose.d.ts.map +1 -0
- package/dist/node/DockerCompose/DockerCompose.js +158 -0
- package/dist/node/DockerCompose/DockerCompose.js.map +1 -0
- package/dist/node/DockerCompose/DockerCompose.test.d.ts +2 -0
- package/dist/node/DockerCompose/DockerCompose.test.d.ts.map +1 -0
- package/dist/node/{DockerComposeService/DockerComposeService.test.js → DockerCompose/DockerCompose.test.js} +16 -16
- package/dist/node/DockerCompose/DockerCompose.test.js.map +1 -0
- package/dist/node/DockerCompose/mod.d.ts +2 -0
- package/dist/node/DockerCompose/mod.d.ts.map +1 -0
- package/dist/node/DockerCompose/mod.js +2 -0
- package/dist/node/DockerCompose/mod.js.map +1 -0
- package/dist/node/FileLogger.d.ts +15 -8
- package/dist/node/FileLogger.d.ts.map +1 -1
- package/dist/node/FileLogger.js +63 -79
- package/dist/node/FileLogger.js.map +1 -1
- package/dist/node/cmd-log.d.ts.map +1 -1
- package/dist/node/cmd-log.js +19 -8
- package/dist/node/cmd-log.js.map +1 -1
- package/dist/node/cmd.d.ts +10 -12
- package/dist/node/cmd.d.ts.map +1 -1
- package/dist/node/cmd.js +76 -39
- package/dist/node/cmd.js.map +1 -1
- package/dist/node/cmd.test.js +19 -19
- package/dist/node/cmd.test.js.map +1 -1
- package/dist/node/mod.d.ts +4 -5
- package/dist/node/mod.d.ts.map +1 -1
- package/dist/node/mod.js +63 -60
- package/dist/node/mod.js.map +1 -1
- package/dist/node/workspace.d.ts +2 -2
- package/dist/node/workspace.d.ts.map +1 -1
- package/dist/node/workspace.js +7 -7
- package/dist/node/workspace.js.map +1 -1
- package/dist/node-vitest/Vitest.d.ts +8 -40
- package/dist/node-vitest/Vitest.d.ts.map +1 -1
- package/dist/node-vitest/Vitest.js +7 -78
- package/dist/node-vitest/Vitest.js.map +1 -1
- package/dist/node-vitest/mod.js +2 -2
- package/dist/wrangler/WranglerDevServer.d.ts +15 -20
- package/dist/wrangler/WranglerDevServer.d.ts.map +1 -1
- package/dist/wrangler/WranglerDevServer.js +77 -71
- package/dist/wrangler/WranglerDevServer.js.map +1 -1
- package/dist/wrangler/WranglerDevServer.test.js +28 -28
- package/dist/wrangler/WranglerDevServer.test.js.map +1 -1
- package/dist/wrangler/fixtures/cf-worker.d.ts.map +1 -1
- package/dist/wrangler/mod.d.ts +1 -1
- package/dist/wrangler/mod.d.ts.map +1 -1
- package/dist/wrangler/mod.js +1 -1
- package/dist/wrangler/mod.js.map +1 -1
- package/package.json +40 -58
- package/src/node/{DockerComposeService/DockerComposeService.test.ts → DockerCompose/DockerCompose.test.ts} +18 -18
- package/src/node/DockerCompose/DockerCompose.ts +328 -0
- package/src/node/DockerCompose/mod.ts +1 -0
- package/src/node/FileLogger.ts +89 -114
- package/src/node/cmd-log.ts +21 -10
- package/src/node/cmd.test.ts +19 -17
- package/src/node/cmd.ts +117 -88
- package/src/node/mod.ts +112 -104
- package/src/node/workspace.ts +9 -10
- package/src/node-vitest/Vitest.ts +21 -172
- package/src/wrangler/WranglerDevServer.test.ts +33 -37
- package/src/wrangler/WranglerDevServer.ts +145 -129
- package/src/wrangler/mod.ts +1 -6
- package/dist/node/DockerComposeService/DockerComposeService.d.ts +0 -63
- package/dist/node/DockerComposeService/DockerComposeService.d.ts.map +0 -1
- package/dist/node/DockerComposeService/DockerComposeService.js +0 -149
- package/dist/node/DockerComposeService/DockerComposeService.js.map +0 -1
- package/dist/node/DockerComposeService/DockerComposeService.test.d.ts +0 -2
- package/dist/node/DockerComposeService/DockerComposeService.test.d.ts.map +0 -1
- package/dist/node/DockerComposeService/DockerComposeService.test.js.map +0 -1
- package/dist/node-vitest/Vitest.test.d.ts +0 -10
- package/dist/node-vitest/Vitest.test.d.ts.map +0 -1
- package/dist/node-vitest/Vitest.test.js +0 -74
- package/dist/node-vitest/Vitest.test.js.map +0 -1
- package/src/node/DockerComposeService/DockerComposeService.ts +0 -343
- package/src/node-vitest/Vitest.test.ts +0 -117
- /package/src/node/{DockerComposeService → DockerCompose}/test-fixtures/docker-compose.yml +0 -0
package/src/node/cmd.test.ts
CHANGED
|
@@ -1,37 +1,39 @@
|
|
|
1
1
|
import fs from 'node:fs'
|
|
2
2
|
import path from 'node:path'
|
|
3
|
-
|
|
4
|
-
import { PlatformNode } from '@livestore/utils/node'
|
|
5
|
-
import { Vitest } from '@livestore/utils-dev/node-vitest'
|
|
3
|
+
|
|
6
4
|
import { expect } from 'vitest'
|
|
7
5
|
|
|
6
|
+
import { Vitest } from '@livestore/utils-dev/node-vitest'
|
|
7
|
+
import { ChildProcessSpawner, Duration, Effect, Layer } from '@livestore/utils/effect'
|
|
8
|
+
import { PlatformNode } from '@livestore/utils/node'
|
|
9
|
+
|
|
8
10
|
import { cmd } from './cmd.ts'
|
|
9
11
|
import { CurrentWorkingDirectory } from './workspace.ts'
|
|
10
12
|
|
|
11
13
|
const withNode = Vitest.makeWithTestCtx({
|
|
12
|
-
makeLayer: () => Layer.mergeAll(PlatformNode.
|
|
14
|
+
makeLayer: () => Layer.mergeAll(PlatformNode.NodeServices.layer, CurrentWorkingDirectory.live),
|
|
13
15
|
timeout: 20_000,
|
|
14
16
|
})
|
|
15
17
|
|
|
16
18
|
Vitest.describe('cmd helper', () => {
|
|
17
19
|
const ansiRegex = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, 'g')
|
|
18
20
|
|
|
19
|
-
Vitest.
|
|
21
|
+
Vitest.live('runs tokenized string without shell', (test) =>
|
|
20
22
|
Effect.gen(function* () {
|
|
21
23
|
const exit = yield* cmd('printf ok')
|
|
22
|
-
expect(exit).toBe(
|
|
24
|
+
expect(exit).toBe(ChildProcessSpawner.ExitCode(0))
|
|
23
25
|
}).pipe(withNode(test)),
|
|
24
26
|
)
|
|
25
27
|
|
|
26
|
-
Vitest.
|
|
28
|
+
Vitest.live('runs array input', (test) =>
|
|
27
29
|
Effect.gen(function* () {
|
|
28
30
|
const exit = yield* cmd(['printf', 'ok'])
|
|
29
|
-
expect(exit).toBe(
|
|
31
|
+
expect(exit).toBe(ChildProcessSpawner.ExitCode(0))
|
|
30
32
|
}).pipe(withNode(test)),
|
|
31
33
|
)
|
|
32
34
|
|
|
33
35
|
/** TODO(#1020): Investigate CI timeout flakiness for cmd tests that spawn many child processes */
|
|
34
|
-
Vitest.
|
|
36
|
+
Vitest.live(
|
|
35
37
|
'supports logging with archive + retention',
|
|
36
38
|
(test) =>
|
|
37
39
|
Effect.gen(function* () {
|
|
@@ -40,7 +42,7 @@ Vitest.describe('cmd helper', () => {
|
|
|
40
42
|
|
|
41
43
|
// first run
|
|
42
44
|
const exit1 = yield* cmd('printf first', { logDir: logsDir })
|
|
43
|
-
expect(exit1).toBe(
|
|
45
|
+
expect(exit1).toBe(ChildProcessSpawner.ExitCode(0))
|
|
44
46
|
const current = path.join(logsDir, 'dev.log')
|
|
45
47
|
expect(fs.existsSync(current)).toBe(true)
|
|
46
48
|
const firstLog = fs.readFileSync(current, 'utf8')
|
|
@@ -48,13 +50,13 @@ Vitest.describe('cmd helper', () => {
|
|
|
48
50
|
expect(firstStdoutLines.length).toBeGreaterThan(0)
|
|
49
51
|
for (const line of firstStdoutLines) {
|
|
50
52
|
expect(line).toContain('[stdout] first')
|
|
51
|
-
expect(line).
|
|
53
|
+
expect(line).toMatch(/Info/i)
|
|
52
54
|
expect(line).toContain('printf first')
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
// second run — archives previous
|
|
56
58
|
const exit2 = yield* cmd('printf second', { logDir: logsDir })
|
|
57
|
-
expect(exit2).toBe(
|
|
59
|
+
expect(exit2).toBe(ChildProcessSpawner.ExitCode(0))
|
|
58
60
|
const archiveDir = path.join(logsDir, 'archive')
|
|
59
61
|
const archives = fs.readdirSync(archiveDir).filter((f) => f.endsWith('.log'))
|
|
60
62
|
expect(archives.length).toBe(1)
|
|
@@ -71,7 +73,7 @@ Vitest.describe('cmd helper', () => {
|
|
|
71
73
|
expect(secondStdoutLines.length).toBeGreaterThan(0)
|
|
72
74
|
for (const line of secondStdoutLines) {
|
|
73
75
|
expect(line).toContain('[stdout] second')
|
|
74
|
-
expect(line).
|
|
76
|
+
expect(line).toMatch(/Info/i)
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
// generate many archives to exercise retention (keep 50)
|
|
@@ -85,7 +87,7 @@ Vitest.describe('cmd helper', () => {
|
|
|
85
87
|
{ timeout: 30_000, retry: 3 },
|
|
86
88
|
)
|
|
87
89
|
|
|
88
|
-
Vitest.
|
|
90
|
+
Vitest.live('streams stdout and stderr with logger formatting', (test) =>
|
|
89
91
|
Effect.gen(function* () {
|
|
90
92
|
const workspace = process.env.WORKSPACE_ROOT!
|
|
91
93
|
const logsDir = path.join(workspace, 'tmp', 'cmd-tests', `format-${Date.now()}`)
|
|
@@ -93,7 +95,7 @@ Vitest.describe('cmd helper', () => {
|
|
|
93
95
|
const exit = yield* cmd(['node', '-e', "console.log('out'); console.error('err')"], {
|
|
94
96
|
logDir: logsDir,
|
|
95
97
|
})
|
|
96
|
-
expect(exit).toBe(
|
|
98
|
+
expect(exit).toBe(ChildProcessSpawner.ExitCode(0))
|
|
97
99
|
|
|
98
100
|
const current = path.join(logsDir, 'dev.log')
|
|
99
101
|
const logContent = fs.readFileSync(current, 'utf8')
|
|
@@ -110,13 +112,13 @@ Vitest.describe('cmd helper', () => {
|
|
|
110
112
|
for (const line of relevantLines) {
|
|
111
113
|
const stripped = line.replace(ansiRegex, '')
|
|
112
114
|
expect(stripped.startsWith('[')).toBe(true)
|
|
113
|
-
expect(stripped).toMatch(/(
|
|
115
|
+
expect(stripped).toMatch(/(Info|Warn)/i)
|
|
114
116
|
expect(stripped).toMatch(/\[(stdout|stderr)]/)
|
|
115
117
|
}
|
|
116
118
|
}).pipe(withNode(test)),
|
|
117
119
|
)
|
|
118
120
|
|
|
119
|
-
Vitest.
|
|
121
|
+
Vitest.live('cleans up logged child process when interrupted', (test) =>
|
|
120
122
|
Effect.gen(function* () {
|
|
121
123
|
const workspace = process.env.WORKSPACE_ROOT!
|
|
122
124
|
const logsDir = path.join(workspace, 'tmp', 'cmd-tests', `timeout-${Date.now()}`)
|
package/src/node/cmd.ts
CHANGED
|
@@ -1,29 +1,24 @@
|
|
|
1
|
-
import fs from 'node:fs'
|
|
2
|
-
|
|
3
|
-
import { isNotUndefined } from '@livestore/utils'
|
|
4
1
|
import {
|
|
5
2
|
Cause,
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
ChildProcess,
|
|
4
|
+
ChildProcessSpawner,
|
|
8
5
|
Effect,
|
|
9
6
|
Fiber,
|
|
10
|
-
|
|
11
|
-
FiberRefs,
|
|
12
|
-
HashMap,
|
|
13
|
-
identity,
|
|
14
|
-
List,
|
|
15
|
-
LogLevel,
|
|
7
|
+
Logger,
|
|
16
8
|
type PlatformError,
|
|
9
|
+
Predicate,
|
|
10
|
+
References,
|
|
17
11
|
Schema,
|
|
18
12
|
Stream,
|
|
19
13
|
} from '@livestore/utils/effect'
|
|
14
|
+
import { NodeFileSystem } from '@livestore/utils/node'
|
|
20
15
|
|
|
21
16
|
import { applyLoggingToCommand } from './cmd-log.ts'
|
|
22
17
|
import * as FileLogger from './FileLogger.ts'
|
|
23
18
|
import { CurrentWorkingDirectory } from './workspace.ts'
|
|
24
19
|
|
|
25
20
|
// Branded zero value so we can compare exit codes without touching internals.
|
|
26
|
-
const SUCCESS_EXIT_CODE:
|
|
21
|
+
const SUCCESS_EXIT_CODE: ChildProcessSpawner.ExitCode = ChildProcessSpawner.ExitCode(0)
|
|
27
22
|
|
|
28
23
|
export const cmd: (
|
|
29
24
|
commandInput: string | (string | undefined)[],
|
|
@@ -43,14 +38,14 @@ export const cmd: (
|
|
|
43
38
|
logRetention?: number
|
|
44
39
|
},
|
|
45
40
|
) => Effect.Effect<
|
|
46
|
-
|
|
41
|
+
ChildProcessSpawner.ExitCode,
|
|
47
42
|
PlatformError.PlatformError | CmdError,
|
|
48
|
-
|
|
43
|
+
ChildProcessSpawner.ChildProcessSpawner | CurrentWorkingDirectory
|
|
49
44
|
> = Effect.fn('cmd')(function* (commandInput, options) {
|
|
50
45
|
const cwd = yield* CurrentWorkingDirectory
|
|
51
46
|
|
|
52
47
|
const asArray = Array.isArray(commandInput)
|
|
53
|
-
const parts = asArray === true ? commandInput.filter(isNotUndefined) : undefined
|
|
48
|
+
const parts = asArray === true ? commandInput.filter(Predicate.isNotUndefined) : undefined
|
|
54
49
|
const [command, ...args] = asArray === true ? (parts as string[]) : commandInput.split(' ')
|
|
55
50
|
|
|
56
51
|
const debugEnvStr = Object.entries(options?.env ?? {})
|
|
@@ -89,7 +84,7 @@ export const cmd: (
|
|
|
89
84
|
useShell,
|
|
90
85
|
} as const
|
|
91
86
|
|
|
92
|
-
const exitCode = yield* isNotUndefined(logPath) === true
|
|
87
|
+
const exitCode = yield* Predicate.isNotUndefined(logPath) === true
|
|
93
88
|
? Effect.gen(function* () {
|
|
94
89
|
yield* Effect.sync(() => console.log(`Logging output to ${logPath}`))
|
|
95
90
|
return yield* runWithLogging({ ...baseArgs, logPath, threadName: commandDebugStr })
|
|
@@ -116,37 +111,42 @@ export const cmdText: (
|
|
|
116
111
|
runInShell?: boolean
|
|
117
112
|
env?: Record<string, string | undefined>
|
|
118
113
|
},
|
|
119
|
-
) => Effect.Effect<
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
114
|
+
) => Effect.Effect<
|
|
115
|
+
string,
|
|
116
|
+
PlatformError.PlatformError,
|
|
117
|
+
ChildProcessSpawner.ChildProcessSpawner | CurrentWorkingDirectory
|
|
118
|
+
> = Effect.fn('cmdText')(function* (commandInput, options) {
|
|
119
|
+
const cwd = yield* CurrentWorkingDirectory
|
|
120
|
+
const commandParts =
|
|
121
|
+
Array.isArray(commandInput) === true ? commandInput.filter(Predicate.isNotUndefined) : commandInput.split(' ')
|
|
122
|
+
const [command, ...args] = commandParts
|
|
123
|
+
const debugEnvStr = Object.entries(options?.env ?? {})
|
|
124
|
+
.map(([key, value]) => `${key}='${String(value)}' `)
|
|
125
|
+
.join('')
|
|
126
|
+
|
|
127
|
+
const commandDebugStr = debugEnvStr + [command, ...args].join(' ')
|
|
128
|
+
const subshellStr = options?.runInShell === true ? ' (in subshell)' : ''
|
|
129
|
+
|
|
130
|
+
yield* Effect.logDebug(`Running '${commandDebugStr}' in '${cwd}'${subshellStr}`)
|
|
131
|
+
yield* Effect.annotateCurrentSpan({ 'span.label': commandDebugStr, command, cwd })
|
|
132
|
+
|
|
133
|
+
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
|
|
134
|
+
return yield* spawner.string(
|
|
135
|
+
buildCommand(commandParts, {
|
|
136
|
+
cwd,
|
|
137
|
+
env: options?.env ?? {},
|
|
138
|
+
stderr: options?.stderr ?? 'inherit',
|
|
139
|
+
shell: options?.runInShell === true,
|
|
140
|
+
}),
|
|
141
|
+
)
|
|
142
|
+
})
|
|
143
143
|
|
|
144
144
|
export class CmdError extends Schema.TaggedError<CmdError>('~@livestore/utils-dev/CmdError')('CmdError', {
|
|
145
145
|
command: Schema.String,
|
|
146
146
|
args: Schema.Array(Schema.String),
|
|
147
147
|
cwd: Schema.String,
|
|
148
|
-
env: Schema.Record(
|
|
149
|
-
stderr: Schema.
|
|
148
|
+
env: Schema.Record(Schema.String, Schema.String.pipe(Schema.UndefinedOr)),
|
|
149
|
+
stderr: Schema.Literals(['inherit', 'pipe']),
|
|
150
150
|
}) {}
|
|
151
151
|
|
|
152
152
|
type TRunBaseArgs = {
|
|
@@ -159,15 +159,19 @@ type TRunBaseArgs = {
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
const runWithoutLogging = ({ commandInput, cwd, env, stdoutMode, stderrMode, useShell }: TRunBaseArgs) =>
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
162
|
+
Effect.gen(function* () {
|
|
163
|
+
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
|
|
164
|
+
return yield* spawner.exitCode(
|
|
165
|
+
buildCommand(commandInput, {
|
|
166
|
+
cwd,
|
|
167
|
+
env,
|
|
168
|
+
stdin: 'inherit',
|
|
169
|
+
stdout: stdoutMode,
|
|
170
|
+
stderr: stderrMode,
|
|
171
|
+
shell: useShell,
|
|
172
|
+
}),
|
|
173
|
+
)
|
|
174
|
+
})
|
|
171
175
|
|
|
172
176
|
type TRunWithLoggingArgs = TRunBaseArgs & {
|
|
173
177
|
readonly logPath: string
|
|
@@ -192,47 +196,53 @@ const runWithLogging = ({
|
|
|
192
196
|
Effect.gen(function* () {
|
|
193
197
|
const envWithColor = env.FORCE_COLOR === undefined ? { ...env, FORCE_COLOR: '1' } : env
|
|
194
198
|
|
|
195
|
-
const
|
|
196
|
-
Effect.sync(() => fs.openSync(logPath, 'a', 0o666)),
|
|
197
|
-
(fd) => Effect.sync(() => fs.closeSync(fd)),
|
|
198
|
-
)
|
|
199
|
-
|
|
200
|
-
const prettyLogger = FileLogger.prettyLoggerTty({
|
|
199
|
+
const prettyLoggerOptions = {
|
|
201
200
|
colors: true,
|
|
202
201
|
stderr: false,
|
|
203
|
-
formatDate:
|
|
204
|
-
}
|
|
202
|
+
formatDate: FileLogger.formatLogTime,
|
|
203
|
+
} as const
|
|
204
|
+
|
|
205
|
+
const logStringToFile = yield* Logger.make<unknown, string>(({ message }) => {
|
|
206
|
+
if (typeof message === 'string') return message
|
|
207
|
+
return String(message)
|
|
208
|
+
}).pipe(Logger.toFile(logPath, { flag: 'a' }), Effect.provide(NodeFileSystem.layer))
|
|
209
|
+
|
|
210
|
+
const FileLoggerLive = Logger.layer([logStringToFile])
|
|
205
211
|
|
|
206
212
|
const appendLog = ({ channel, content }: { channel: 'stdout' | 'stderr'; content: string }) =>
|
|
207
|
-
Effect.
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
logLevel: channel === 'stdout' ?
|
|
213
|
+
Effect.gen(function* () {
|
|
214
|
+
const date = new Date()
|
|
215
|
+
const formatted = FileLogger.formatPrettyLog({
|
|
216
|
+
logLevel: channel === 'stdout' ? 'Info' : 'Warn',
|
|
211
217
|
message: [`[${channel}]${content.length > 0 ? ` ${content}` : ''}`],
|
|
212
218
|
cause: Cause.empty,
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
219
|
+
date,
|
|
220
|
+
fiberId: 0,
|
|
221
|
+
spans: [[threadName, date.getTime()]],
|
|
222
|
+
annotations: { thread: threadName },
|
|
223
|
+
options: prettyLoggerOptions,
|
|
217
224
|
})
|
|
218
|
-
|
|
225
|
+
|
|
226
|
+
yield* Effect.log(formatted.endsWith('\n') === true ? formatted.slice(0, -1) : formatted).pipe(
|
|
227
|
+
Effect.provideService(References.MinimumLogLevel, 'All'),
|
|
228
|
+
Effect.provide(FileLoggerLive),
|
|
229
|
+
)
|
|
219
230
|
})
|
|
220
231
|
|
|
221
|
-
const command = buildCommand(commandInput,
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
)
|
|
232
|
+
const command = buildCommand(commandInput, {
|
|
233
|
+
cwd,
|
|
234
|
+
env: envWithColor,
|
|
235
|
+
stdin: 'inherit',
|
|
236
|
+
stdout: 'pipe',
|
|
237
|
+
stderr: 'pipe',
|
|
238
|
+
shell: useShell,
|
|
239
|
+
})
|
|
229
240
|
|
|
230
241
|
// Acquire/start the command and make sure we kill it on interruption.
|
|
231
|
-
const
|
|
242
|
+
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
|
|
243
|
+
const runningProcess = yield* Effect.acquireRelease(spawner.spawn(command), (proc) =>
|
|
232
244
|
proc.isRunning.pipe(
|
|
233
|
-
Effect.flatMap((running) =>
|
|
234
|
-
running === true ? proc.kill().pipe(Effect.catchAll(() => Effect.void)) : Effect.void,
|
|
235
|
-
),
|
|
245
|
+
Effect.flatMap((running) => (running === true ? proc.kill().pipe(Effect.ignore) : Effect.void)),
|
|
236
246
|
Effect.ignore,
|
|
237
247
|
),
|
|
238
248
|
)
|
|
@@ -249,20 +259,20 @@ const runWithLogging = ({
|
|
|
249
259
|
})
|
|
250
260
|
|
|
251
261
|
const stdoutFiber = yield* runningProcess.stdout.pipe(
|
|
252
|
-
Stream.decodeText('utf8'),
|
|
262
|
+
Stream.decodeText({ encoding: 'utf8' }),
|
|
253
263
|
Stream.runForEach((chunk) => stdoutHandler.onChunk(chunk)),
|
|
254
264
|
Effect.forkScoped,
|
|
255
265
|
)
|
|
256
266
|
|
|
257
267
|
const stderrFiber = yield* runningProcess.stderr.pipe(
|
|
258
|
-
Stream.decodeText('utf8'),
|
|
268
|
+
Stream.decodeText({ encoding: 'utf8' }),
|
|
259
269
|
Stream.runForEach((chunk) => stderrHandler.onChunk(chunk)),
|
|
260
270
|
Effect.forkScoped,
|
|
261
271
|
)
|
|
262
272
|
|
|
263
273
|
// Dump any buffered data and finish both stream fibers before we return.
|
|
264
274
|
const flushOutputs = Effect.gen(function* () {
|
|
265
|
-
const stillRunning = yield* runningProcess.isRunning.pipe(Effect.
|
|
275
|
+
const stillRunning = yield* runningProcess.isRunning.pipe(Effect.orElseSucceed(() => false))
|
|
266
276
|
if (stillRunning === true) {
|
|
267
277
|
yield* Effect.ignore(runningProcess.kill())
|
|
268
278
|
}
|
|
@@ -278,18 +288,37 @@ const runWithLogging = ({
|
|
|
278
288
|
}),
|
|
279
289
|
)
|
|
280
290
|
|
|
281
|
-
|
|
291
|
+
type TBuildCommandOptions = {
|
|
292
|
+
readonly cwd: string
|
|
293
|
+
readonly env: Record<string, string | undefined>
|
|
294
|
+
readonly stdin?: ChildProcess.CommandInput
|
|
295
|
+
readonly stdout?: ChildProcess.CommandOutput
|
|
296
|
+
readonly stderr?: ChildProcess.CommandOutput
|
|
297
|
+
readonly shell: boolean
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const buildCommand = (input: string | string[], options: TBuildCommandOptions): ChildProcess.Command => {
|
|
301
|
+
const commandOptions = {
|
|
302
|
+
cwd: options.cwd,
|
|
303
|
+
env: options.env,
|
|
304
|
+
extendEnv: true,
|
|
305
|
+
shell: options.shell,
|
|
306
|
+
...(options.stdin !== undefined ? { stdin: options.stdin } : {}),
|
|
307
|
+
...(options.stdout !== undefined ? { stdout: options.stdout } : {}),
|
|
308
|
+
...(options.stderr !== undefined ? { stderr: options.stderr } : {}),
|
|
309
|
+
} as const
|
|
310
|
+
|
|
282
311
|
if (Array.isArray(input) === true) {
|
|
283
312
|
const [c, ...a] = input
|
|
284
|
-
return
|
|
313
|
+
return ChildProcess.make(c ?? '', a, commandOptions)
|
|
285
314
|
}
|
|
286
315
|
|
|
287
|
-
if (
|
|
288
|
-
return
|
|
316
|
+
if (options.shell === true) {
|
|
317
|
+
return ChildProcess.make(input, commandOptions)
|
|
289
318
|
}
|
|
290
319
|
|
|
291
320
|
const [c, ...a] = input.split(' ')
|
|
292
|
-
return
|
|
321
|
+
return ChildProcess.make(c ?? '', a, commandOptions)
|
|
293
322
|
}
|
|
294
323
|
|
|
295
324
|
type TLineTerminator = 'newline' | 'carriage-return' | 'none'
|