0
# Process Management
1
2
Core utilities for running CLI commands and managing child processes with intelligent module resolution and cross-platform support.
3
4
## Capabilities
5
6
### CLI Execution
7
8
Execute CLI tools with automatic module resolution and project-first dependency lookup.
9
10
```typescript { .api }
11
/**
12
* Run a CLI command with intelligent module resolution
13
* @param cli - CLI module path (e.g., "typescript/lib/tsc", "eslint/bin/eslint")
14
* @param args - Command line arguments to pass to the CLI
15
* @param options - Execution options for process control and module resolution
16
* @returns ChildProcess when executed, string when dry run
17
*/
18
function runCLI(
19
cli: string,
20
args: string[],
21
options?: RunCLIOptions
22
): ChildProcess | string;
23
24
interface RunCLIOptions {
25
dryRun?: boolean; // Return command string instead of executing
26
cwd?: string; // Working directory for execution
27
resolveFromProjectFirst?: boolean; // Try project dependencies before @loopback/build
28
nodeArgs?: string[]; // Additional Node.js arguments
29
stdio?: string; // stdio configuration
30
env?: Record<string, string>; // Environment variables
31
}
32
```
33
34
**Usage Examples:**
35
36
```typescript
37
import { runCLI } from "@loopback/build";
38
39
// Run TypeScript compiler
40
const tscProcess = runCLI(
41
"typescript/lib/tsc",
42
["--project", "tsconfig.json"]
43
);
44
45
// Run with custom Node.js arguments
46
const eslintProcess = runCLI(
47
"eslint/bin/eslint",
48
["src/", "--fix"],
49
{ nodeArgs: ["--max-old-space-size=4096"] }
50
);
51
52
// Dry run to see command
53
const command = runCLI(
54
"prettier/bin/prettier.cjs",
55
["--write", "**/*.ts"],
56
{ dryRun: true }
57
);
58
console.log(command); // "node /path/to/prettier.cjs --write **/*.ts"
59
60
// Custom working directory
61
runCLI("mocha/bin/mocha", ["test/"], { cwd: "/path/to/project" });
62
```
63
64
### Shell Command Execution
65
66
Execute shell commands with JSON-safe argument handling and cross-platform support.
67
68
```typescript { .api }
69
/**
70
* Run a shell command with cross-platform support
71
* @param command - Command to execute (must be in PATH or absolute path)
72
* @param args - Command arguments (automatically JSON-quoted for safety)
73
* @param options - Execution options for process control
74
* @returns ChildProcess when executed, string when dry run
75
*/
76
function runShell(
77
command: string,
78
args: string[],
79
options?: RunShellOptions
80
): ChildProcess | string;
81
82
interface RunShellOptions {
83
dryRun?: boolean; // Return command string instead of executing
84
cwd?: string; // Working directory for execution
85
stdio?: string; // stdio configuration
86
env?: Record<string, string>; // Environment variables
87
shell?: boolean; // Enable shell execution (default: true)
88
}
89
```
90
91
**Usage Examples:**
92
93
```typescript
94
import { runShell } from "@loopback/build";
95
96
// Run npm commands
97
const npmProcess = runShell("npm", ["install", "--save-dev", "typescript"]);
98
99
// Run git commands
100
const gitProcess = runShell("git", ["add", "."]);
101
102
// Run with custom environment
103
const envProcess = runShell("node", ["script.js"], {
104
env: { NODE_ENV: "production", DEBUG: "myapp:*" }
105
});
106
107
// Dry run to see command
108
const command = runShell("docker", ["build", "-t", "myapp", "."], { dryRun: true });
109
console.log(command); // 'docker "build" "-t" "myapp" "."'
110
```
111
112
### Module Resolution
113
114
Intelligent module resolution that prioritizes project dependencies over bundled tools.
115
116
```typescript { .api }
117
/**
118
* Resolve CLI module path with project-first resolution
119
* @param cli - CLI module path to resolve
120
* @param options - Resolution options
121
* @returns Absolute path to the resolved CLI module
122
*/
123
function resolveCLI(
124
cli: string,
125
options?: { resolveFromProjectFirst?: boolean }
126
): string;
127
128
/**
129
* Resolve CLI from project dependencies only
130
* @param cli - CLI module path to resolve
131
* @param projectRootDir - Project root directory (defaults to process.cwd())
132
* @returns Absolute path to CLI module or undefined if not found
133
*/
134
function resolveCLIFromProject(
135
cli: string,
136
projectRootDir?: string
137
): string | undefined;
138
139
/**
140
* Extract package name from CLI module path
141
* @param cli - CLI module path (e.g., "typescript/lib/tsc", "@babel/cli/bin/babel")
142
* @returns Package name (e.g., "typescript", "@babel/cli")
143
*/
144
function getPackageName(cli: string): string;
145
```
146
147
**Resolution Examples:**
148
149
```typescript
150
import { resolveCLI, resolveCLIFromProject, getPackageName } from "@loopback/build";
151
152
// Resolve TypeScript compiler
153
const tscPath = resolveCLI("typescript/lib/tsc");
154
// Returns: "/project/node_modules/typescript/lib/tsc.js" (project version)
155
// Falls back to: "/node_modules/@loopback/build/node_modules/typescript/lib/tsc.js"
156
157
// Force resolution from project only
158
const projectTsc = resolveCLIFromProject("typescript/lib/tsc");
159
// Returns: "/project/node_modules/typescript/lib/tsc.js" or undefined
160
161
// Extract package names
162
const pkgName1 = getPackageName("typescript/lib/tsc"); // "typescript"
163
const pkgName2 = getPackageName("@babel/cli/bin/babel"); // "@babel/cli"
164
const pkgName3 = getPackageName("eslint/bin/eslint"); // "eslint"
165
166
// Skip project resolution (use bundled version)
167
const bundledEslint = resolveCLI("eslint/bin/eslint", {
168
resolveFromProjectFirst: false
169
});
170
```
171
172
### Configuration and Utility Functions
173
174
Helper functions for configuration file discovery and CLI option processing.
175
176
```typescript { .api }
177
/**
178
* Get configuration file with fallback support
179
* @param name - Preferred configuration file name
180
* @param defaultName - Default fallback file name
181
* @returns Path to discovered configuration file
182
*/
183
function getConfigFile(name: string, defaultName?: string): string;
184
185
/**
186
* Get root directory of @loopback/build module
187
* @returns Absolute path to @loopback/build root directory
188
*/
189
function getRootDir(): string;
190
191
/**
192
* Get root directory of current npm package
193
* @returns Absolute path to current working directory
194
*/
195
function getPackageDir(): string;
196
197
/**
198
* Check if CLI options contain specified option names
199
* @param opts - Array of CLI option strings
200
* @param optionNames - Option names to check for
201
* @returns True if any option name is found
202
*/
203
function isOptionSet(opts: string[], ...optionNames: string[]): boolean;
204
205
/**
206
* Check if project has Mocha configuration files
207
* @returns True if any Mocha config file exists in project
208
*/
209
function mochaConfiguredForProject(): boolean;
210
```
211
212
**Utility Examples:**
213
214
```typescript
215
import {
216
getConfigFile,
217
getRootDir,
218
getPackageDir,
219
isOptionSet,
220
mochaConfiguredForProject
221
} from "@loopback/build";
222
223
// Find configuration files
224
const tsconfigPath = getConfigFile("tsconfig.build.json", "tsconfig.json");
225
const eslintrcPath = getConfigFile(".eslintrc.js", ".eslintrc.json");
226
227
// Get directory paths
228
const buildToolsRoot = getRootDir(); // "/node_modules/@loopback/build"
229
const projectRoot = getPackageDir(); // "/current/working/directory"
230
231
// Check CLI options
232
const opts = ["--fix", "--ext", ".ts", "src/"];
233
const hasFixFlag = isOptionSet(opts, "--fix"); // true
234
const hasConfigFlag = isOptionSet(opts, "-c", "--config"); // false
235
const hasExtFlag = isOptionSet(opts, "--ext"); // true
236
237
// Check project configuration
238
const hasMochaConfig = mochaConfiguredForProject();
239
// Checks for: .mocharc.js, .mocharc.json, .mocharc.yaml, .mocharc.yml
240
```
241
242
### Process Management and Error Handling
243
244
Comprehensive process management with proper error handling and exit code propagation.
245
246
```typescript { .api }
247
/**
248
* Process management features:
249
* - Proper stdio inheritance for interactive commands
250
* - Exit code propagation to parent process
251
* - Signal handling for graceful shutdowns
252
* - Cross-platform process spawning
253
* - Environment variable inheritance and customization
254
*/
255
256
interface ProcessFeatures {
257
stdioInheritance: "inherit"; // Pass stdio to child processes
258
exitCodePropagation: boolean; // Set process.exitCode on child exit
259
signalHandling: boolean; // Handle SIGTERM, SIGINT, etc.
260
crossPlatform: boolean; // Windows and Unix compatibility
261
environmentVariables: boolean; // Custom env var support
262
}
263
```
264
265
**Error Handling Examples:**
266
267
```typescript
268
import { runCLI, runShell } from "@loopback/build";
269
270
// Error handling with callbacks
271
const child = runCLI("eslint/bin/eslint", ["src/"]);
272
273
child.on('close', (code, signal) => {
274
if (code !== 0) {
275
console.error(`ESLint exited with code ${code}`);
276
process.exit(code);
277
}
278
});
279
280
// Shell command error handling
281
const shellChild = runShell("npm", ["test"]);
282
283
shellChild.on('error', (error) => {
284
console.error('Failed to start process:', error);
285
});
286
287
shellChild.on('close', (code, signal) => {
288
if (signal === 'SIGKILL') {
289
console.warn('Process was killed');
290
}
291
});
292
```
293
294
### Advanced Usage Patterns
295
296
Advanced patterns for complex build workflows and integrations.
297
298
**Pipeline Execution:**
299
300
```typescript
301
import { runCLI, runShell } from "@loopback/build";
302
303
async function buildPipeline() {
304
// Clean build artifacts
305
await new Promise(resolve => {
306
const clean = runShell("rm", ["-rf", "dist"]);
307
clean.on('close', resolve);
308
});
309
310
// Compile TypeScript
311
await new Promise(resolve => {
312
const tsc = runCLI("typescript/lib/tsc", []);
313
tsc.on('close', resolve);
314
});
315
316
// Run tests
317
await new Promise(resolve => {
318
const mocha = runCLI("mocha/bin/mocha", ["dist/__tests__"]);
319
mocha.on('close', resolve);
320
});
321
}
322
```
323
324
**Custom Tool Integration:**
325
326
```typescript
327
import { runCLI, resolveCLI } from "@loopback/build";
328
329
// Custom TypeScript plugin workflow
330
function runWithTTypescript() {
331
try {
332
// Try to use project's ttypescript
333
const ttscPath = resolveCLI("ttypescript/lib/tsc");
334
return runCLI("ttypescript/lib/tsc", process.argv.slice(2));
335
} catch (error) {
336
// Fall back to regular TypeScript
337
console.warn("ttypescript not found, using regular tsc");
338
return runCLI("typescript/lib/tsc", process.argv.slice(2));
339
}
340
}
341
342
// Conditional tool execution
343
function runLinter(fix = false) {
344
const args = ["."];
345
if (fix) args.unshift("--fix");
346
347
return runCLI("eslint/bin/eslint", args, {
348
nodeArgs: ["--max-old-space-size=4096"] // More memory for large projects
349
});
350
}
351
```
352
353
### TypeScript Path Resolution
354
355
Special handling for TypeScript installation path discovery.
356
357
```typescript { .api }
358
/**
359
* Resolved path to TypeScript installation
360
* Points to the directory containing TypeScript package
361
*/
362
const typeScriptPath: string;
363
364
// Usage example:
365
import { typeScriptPath } from "@loopback/build";
366
import path from "path";
367
368
const tscBin = path.join(typeScriptPath, "bin", "tsc");
369
const tsLib = path.join(typeScriptPath, "lib");
370
```