@hydranium/cli 1.0.0-next.189 → 1.0.0-next.192
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/lib/commands/ast-ground-truth-args.js +5 -5
- package/lib/commands/ast-ground-truth-args.js.map +1 -1
- package/lib/commands/harness-args.d.ts +19 -35
- package/lib/commands/harness-args.d.ts.map +1 -1
- package/lib/commands/harness-args.js +38 -16
- package/lib/commands/harness-args.js.map +1 -1
- package/lib/commands/headless-harness.d.ts +14 -15
- package/lib/commands/headless-harness.d.ts.map +1 -1
- package/lib/commands/headless-harness.js +8 -8
- package/lib/commands/headless-harness.js.map +1 -1
- package/lib/commands/lint-grammar-args.js +5 -5
- package/lib/commands/lint-grammar-args.js.map +1 -1
- package/lib/commands/measure-memory-args.d.ts.map +1 -1
- package/lib/commands/measure-memory-args.js +11 -4
- package/lib/commands/measure-memory-args.js.map +1 -1
- package/lib/commands/model-docs-args.js +5 -5
- package/lib/commands/model-docs-args.js.map +1 -1
- package/lib/commands/projects-args.d.ts.map +1 -1
- package/lib/commands/projects-args.js +5 -5
- package/lib/commands/projects-args.js.map +1 -1
- package/lib/commands/projects.d.ts +3 -4
- package/lib/commands/projects.d.ts.map +1 -1
- package/lib/commands/projects.js +2 -2
- package/lib/commands/projects.js.map +1 -1
- package/lib/commands/query-args.d.ts.map +1 -1
- package/lib/commands/query-args.js +5 -5
- package/lib/commands/query-args.js.map +1 -1
- package/lib/commands/query.d.ts +3 -4
- package/lib/commands/query.d.ts.map +1 -1
- package/lib/commands/query.js +2 -2
- package/lib/commands/query.js.map +1 -1
- package/lib/commands/reflect-args.js +5 -5
- package/lib/commands/reflect-args.js.map +1 -1
- package/lib/commands/save-args.d.ts.map +1 -1
- package/lib/commands/save-args.js +6 -4
- package/lib/commands/save-args.js.map +1 -1
- package/lib/commands/save.d.ts +3 -4
- package/lib/commands/save.d.ts.map +1 -1
- package/lib/commands/save.js +2 -2
- package/lib/commands/save.js.map +1 -1
- package/lib/commands/validate-args.js +5 -5
- package/lib/commands/validate-args.js.map +1 -1
- package/lib/commands/watch-args.d.ts.map +1 -1
- package/lib/commands/watch-args.js +6 -4
- package/lib/commands/watch-args.js.map +1 -1
- package/lib/commands/watch.d.ts +3 -4
- package/lib/commands/watch.d.ts.map +1 -1
- package/lib/commands/watch.js +2 -2
- package/lib/commands/watch.js.map +1 -1
- package/lib/log-level.d.ts +21 -11
- package/lib/log-level.d.ts.map +1 -1
- package/lib/log-level.js +25 -14
- package/lib/log-level.js.map +1 -1
- package/package.json +9 -9
- package/src/commands/ast-ground-truth-args.ts +7 -7
- package/src/commands/harness-args.ts +42 -19
- package/src/commands/headless-harness.ts +15 -20
- package/src/commands/lint-grammar-args.ts +7 -7
- package/src/commands/measure-memory-args.ts +14 -6
- package/src/commands/model-docs-args.ts +7 -7
- package/src/commands/projects-args.ts +5 -4
- package/src/commands/projects.ts +4 -6
- package/src/commands/query-args.ts +5 -4
- package/src/commands/query.ts +4 -6
- package/src/commands/reflect-args.ts +7 -7
- package/src/commands/save-args.ts +6 -3
- package/src/commands/save.ts +4 -6
- package/src/commands/validate-args.ts +7 -7
- package/src/commands/watch-args.ts +6 -3
- package/src/commands/watch.ts +4 -6
- package/src/log-level.ts +42 -14
package/lib/log-level.d.ts
CHANGED
|
@@ -7,20 +7,30 @@
|
|
|
7
7
|
* SPDX-License-Identifier: MIT
|
|
8
8
|
********************************************************************************/
|
|
9
9
|
import { type LogThreshold } from '@hydranium/protocol';
|
|
10
|
+
/** The log flags every subcommand takes, as the env {@link logEnv} hands its child. */
|
|
11
|
+
export interface LogOptions {
|
|
12
|
+
/** Threshold for the client sink; the server's log-level setting replaces it. */
|
|
13
|
+
readonly logLevel?: LogThreshold;
|
|
14
|
+
/** File the server tees its log to; `{workspace}` expands once it is known. */
|
|
15
|
+
readonly logFile?: string;
|
|
16
|
+
/** The file's own threshold, which the log-level setting does not change. */
|
|
17
|
+
readonly logFileLevel?: LogThreshold;
|
|
18
|
+
}
|
|
10
19
|
/**
|
|
11
|
-
* Validate a raw `--log-level` argument into a
|
|
12
|
-
* an unrecognised value so the CLI's top-level
|
|
13
|
-
* and exits non-zero (mirroring how the other
|
|
14
|
-
* for the argv-supplied value; the resolved
|
|
15
|
-
* {@link
|
|
20
|
+
* Validate a raw `--log-level` / `--log-file-level` argument into a
|
|
21
|
+
* {@link LogThreshold}. Throws on an unrecognised value so the CLI's top-level
|
|
22
|
+
* `catch` surfaces a clean error and exits non-zero (mirroring how the other
|
|
23
|
+
* option parsers fail fast). Use for the argv-supplied value; the resolved
|
|
24
|
+
* threshold is handed to {@link logEnv} to target the spawned server.
|
|
16
25
|
*/
|
|
17
|
-
export declare function parseLogLevelOption(raw: string): LogThreshold;
|
|
26
|
+
export declare function parseLogLevelOption(raw: string, flag?: string): LogThreshold;
|
|
18
27
|
/**
|
|
19
28
|
* Build the environment override that targets *only the spawned server's* log
|
|
20
|
-
*
|
|
21
|
-
* `
|
|
22
|
-
*
|
|
23
|
-
*
|
|
29
|
+
* configuration, or `undefined` when no log flag was given. Passed to
|
|
30
|
+
* `spawnDataServer` as `env`, it sets the same `HYDRANIUM_LOG_LEVEL` /
|
|
31
|
+
* `HYDRANIUM_LOG_FILE` / `HYDRANIUM_LOG_FILE_LEVEL` variables the server reads
|
|
32
|
+
* at startup — the CLI's own logging is unaffected, unlike ambient variables
|
|
33
|
+
* that the child would inherit alongside the parent. The server owns *how* log level
|
|
24
34
|
* works; this only routes the CLI's request onto the channel it already reads.
|
|
25
35
|
*
|
|
26
36
|
* **The variable is read by `LspLogger`'s constructor**, which is bound on the
|
|
@@ -35,5 +45,5 @@ export declare function parseLogLevelOption(raw: string): LogThreshold;
|
|
|
35
45
|
* not go to stdout: that is the JSON-RPC channel, so a log line there would
|
|
36
46
|
* corrupt the very request this flag was set to diagnose.
|
|
37
47
|
*/
|
|
38
|
-
export declare function
|
|
48
|
+
export declare function logEnv(options: LogOptions): Record<string, string> | undefined;
|
|
39
49
|
//# sourceMappingURL=log-level.d.ts.map
|
package/lib/log-level.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log-level.d.ts","sourceRoot":"","sources":["../src/log-level.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,
|
|
1
|
+
{"version":3,"file":"log-level.d.ts","sourceRoot":"","sources":["../src/log-level.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAMJ,KAAK,YAAY,EACnB,MAAM,qBAAqB,CAAC;AAE7B,uFAAuF;AACvF,MAAM,WAAW,UAAU;IACxB,iFAAiF;IACjF,QAAQ,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAC;IACjC,+EAA+E;IAC/E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,6EAA6E;IAC7E,QAAQ,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC;CACvC;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,SAAgB,GAAG,YAAY,CAMnF;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,MAAM,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAY9E"}
|
package/lib/log-level.js
CHANGED
|
@@ -6,27 +6,28 @@
|
|
|
6
6
|
*
|
|
7
7
|
* SPDX-License-Identifier: MIT
|
|
8
8
|
********************************************************************************/
|
|
9
|
-
import { DEFAULT_LOG_LEVEL_ENV, LEVEL_ORDER, parseLogLevel } from '@hydranium/protocol';
|
|
9
|
+
import { DEFAULT_LOG_FILE_ENV, DEFAULT_LOG_FILE_LEVEL_ENV, DEFAULT_LOG_LEVEL_ENV, LEVEL_ORDER, parseLogLevel } from '@hydranium/protocol';
|
|
10
10
|
/**
|
|
11
|
-
* Validate a raw `--log-level` argument into a
|
|
12
|
-
* an unrecognised value so the CLI's top-level
|
|
13
|
-
* and exits non-zero (mirroring how the other
|
|
14
|
-
* for the argv-supplied value; the resolved
|
|
15
|
-
* {@link
|
|
11
|
+
* Validate a raw `--log-level` / `--log-file-level` argument into a
|
|
12
|
+
* {@link LogThreshold}. Throws on an unrecognised value so the CLI's top-level
|
|
13
|
+
* `catch` surfaces a clean error and exits non-zero (mirroring how the other
|
|
14
|
+
* option parsers fail fast). Use for the argv-supplied value; the resolved
|
|
15
|
+
* threshold is handed to {@link logEnv} to target the spawned server.
|
|
16
16
|
*/
|
|
17
|
-
export function parseLogLevelOption(raw) {
|
|
17
|
+
export function parseLogLevelOption(raw, flag = '--log-level') {
|
|
18
18
|
const level = parseLogLevel(raw);
|
|
19
19
|
if (!level) {
|
|
20
|
-
throw new Error(`Invalid
|
|
20
|
+
throw new Error(`Invalid ${flag}: ${raw} (expected one of: ${Object.keys(LEVEL_ORDER).join(', ')})`);
|
|
21
21
|
}
|
|
22
22
|
return level;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
25
|
* Build the environment override that targets *only the spawned server's* log
|
|
26
|
-
*
|
|
27
|
-
* `
|
|
28
|
-
*
|
|
29
|
-
*
|
|
26
|
+
* configuration, or `undefined` when no log flag was given. Passed to
|
|
27
|
+
* `spawnDataServer` as `env`, it sets the same `HYDRANIUM_LOG_LEVEL` /
|
|
28
|
+
* `HYDRANIUM_LOG_FILE` / `HYDRANIUM_LOG_FILE_LEVEL` variables the server reads
|
|
29
|
+
* at startup — the CLI's own logging is unaffected, unlike ambient variables
|
|
30
|
+
* that the child would inherit alongside the parent. The server owns *how* log level
|
|
30
31
|
* works; this only routes the CLI's request onto the channel it already reads.
|
|
31
32
|
*
|
|
32
33
|
* **The variable is read by `LspLogger`'s constructor**, which is bound on the
|
|
@@ -41,7 +42,17 @@ export function parseLogLevelOption(raw) {
|
|
|
41
42
|
* not go to stdout: that is the JSON-RPC channel, so a log line there would
|
|
42
43
|
* corrupt the very request this flag was set to diagnose.
|
|
43
44
|
*/
|
|
44
|
-
export function
|
|
45
|
-
|
|
45
|
+
export function logEnv(options) {
|
|
46
|
+
const env = {};
|
|
47
|
+
if (options.logLevel !== undefined) {
|
|
48
|
+
env[DEFAULT_LOG_LEVEL_ENV] = options.logLevel;
|
|
49
|
+
}
|
|
50
|
+
if (options.logFile !== undefined) {
|
|
51
|
+
env[DEFAULT_LOG_FILE_ENV] = options.logFile;
|
|
52
|
+
}
|
|
53
|
+
if (options.logFileLevel !== undefined) {
|
|
54
|
+
env[DEFAULT_LOG_FILE_LEVEL_ENV] = options.logFileLevel;
|
|
55
|
+
}
|
|
56
|
+
return Object.keys(env).length > 0 ? env : undefined;
|
|
46
57
|
}
|
|
47
58
|
//# sourceMappingURL=log-level.js.map
|
package/lib/log-level.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log-level.js","sourceRoot":"","sources":["../src/log-level.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,
|
|
1
|
+
{"version":3,"file":"log-level.js","sourceRoot":"","sources":["../src/log-level.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EACJ,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,WAAW,EACX,aAAa,EAEf,MAAM,qBAAqB,CAAC;AAY7B;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAW,EAAE,IAAI,GAAG,aAAa;IAClE,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,CAAC,KAAK,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,WAAW,IAAI,KAAK,GAAG,sBAAsB,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxG,CAAC;IACD,OAAO,KAAK,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,MAAM,CAAC,OAAmB;IACvC,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAClC,GAAG,CAAC,qBAAqB,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC;IACjD,CAAC;IACD,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACjC,GAAG,CAAC,oBAAoB,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAC/C,CAAC;IACD,IAAI,OAAO,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;QACtC,GAAG,CAAC,0BAA0B,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC;IAC1D,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AACxD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hydranium/cli",
|
|
3
|
-
"version": "1.0.0-next.
|
|
3
|
+
"version": "1.0.0-next.192",
|
|
4
4
|
"description": "Build-time codegen tools + data-server subcommands for the hydranium framework.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -69,20 +69,20 @@
|
|
|
69
69
|
"ts-morph": "^25.0.0"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
|
-
"@hydranium/core": "1.0.0-next.
|
|
73
|
-
"@hydranium/data-server": "1.0.0-next.
|
|
74
|
-
"@hydranium/langium": "1.0.0-next.
|
|
75
|
-
"@hydranium/protocol": "1.0.0-next.
|
|
72
|
+
"@hydranium/core": "1.0.0-next.192",
|
|
73
|
+
"@hydranium/data-server": "1.0.0-next.192",
|
|
74
|
+
"@hydranium/langium": "1.0.0-next.192",
|
|
75
|
+
"@hydranium/protocol": "1.0.0-next.192",
|
|
76
76
|
"rimraf": "^5.0.0",
|
|
77
77
|
"typescript": "^7.0.2",
|
|
78
78
|
"vscode-jsonrpc": "9.0.1",
|
|
79
79
|
"vscode-languageserver": "~10.0.1"
|
|
80
80
|
},
|
|
81
81
|
"peerDependencies": {
|
|
82
|
-
"@hydranium/core": "1.0.0-next.
|
|
83
|
-
"@hydranium/data-server": "1.0.0-next.
|
|
84
|
-
"@hydranium/langium": "1.0.0-next.
|
|
85
|
-
"@hydranium/protocol": "1.0.0-next.
|
|
82
|
+
"@hydranium/core": "1.0.0-next.192",
|
|
83
|
+
"@hydranium/data-server": "1.0.0-next.192",
|
|
84
|
+
"@hydranium/langium": "1.0.0-next.192",
|
|
85
|
+
"@hydranium/protocol": "1.0.0-next.192",
|
|
86
86
|
"@memlab/core": "^2.0.3",
|
|
87
87
|
"@memlab/heap-analysis": "^2.0.3",
|
|
88
88
|
"vscode-jsonrpc": "9.0.1"
|
|
@@ -11,9 +11,9 @@ import { runAstGroundTruth, type AstGroundTruthCommandOptions } from './ast-grou
|
|
|
11
11
|
import {
|
|
12
12
|
exitWithUsage,
|
|
13
13
|
helpRequested,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
LOG_VALUE_FLAGS,
|
|
15
|
+
logHelpLines,
|
|
16
|
+
logOptions,
|
|
17
17
|
parseHarnessArgs,
|
|
18
18
|
printHelp,
|
|
19
19
|
SERVICES_FLAG,
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
} from './harness-args.js';
|
|
22
22
|
import { OUT_FILE_FLAG } from './headless-harness.js';
|
|
23
23
|
|
|
24
|
-
const VALUE_FLAGS = [OUT_FILE_FLAG,
|
|
24
|
+
const VALUE_FLAGS = [OUT_FILE_FLAG, ...LOG_VALUE_FLAGS] as const;
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Every flag `ast-ground-truth` accepts, derived from the sets the parser is
|
|
@@ -35,7 +35,7 @@ export const AST_GROUND_TRUTH_VALUE_FLAGS: readonly string[] = AST_GROUND_TRUTH_
|
|
|
35
35
|
|
|
36
36
|
/** The `--help` text, as data, held to {@link AST_GROUND_TRUTH_FLAGS} by a test. */
|
|
37
37
|
export const AST_GROUND_TRUTH_HELP: readonly string[] = [
|
|
38
|
-
'Usage: hydranium-cli ast-ground-truth --services <module> <workspace> [--out-file <file>] [--log-level <lvl>]',
|
|
38
|
+
'Usage: hydranium-cli ast-ground-truth --services <module> <workspace> [--out-file <file>] [--log-level <lvl>] [--log-file <file>] [--log-file-level <lvl>]',
|
|
39
39
|
'',
|
|
40
40
|
"Tally a workspace's live-model AST nodes by `$type` — the ground truth that",
|
|
41
41
|
'`analyze-heap --validate <gt.json>` checks a heap snapshot against. `<module>`',
|
|
@@ -46,12 +46,12 @@ export const AST_GROUND_TRUTH_HELP: readonly string[] = [
|
|
|
46
46
|
' --services <module> ESM module exporting `createServices(): { shared }` (required).',
|
|
47
47
|
' <workspace> Workspace root (path or file URI) to build (required).',
|
|
48
48
|
' --out-file <file> Write the JSON to this file instead of stdout.',
|
|
49
|
-
|
|
49
|
+
...logHelpLines(22)
|
|
50
50
|
];
|
|
51
51
|
|
|
52
52
|
export function parseAstGroundTruthArgs(args: string[], onError: UsageError = exitWithUsage): AstGroundTruthCommandOptions {
|
|
53
53
|
const { servicesModule, workspace, options } = parseHarnessArgs(args, 'ast-ground-truth', VALUE_FLAGS, [], { onError });
|
|
54
|
-
return { servicesModule, workspace, outFile: options[OUT_FILE_FLAG],
|
|
54
|
+
return { servicesModule, workspace, outFile: options[OUT_FILE_FLAG], ...logOptions(options) };
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
export function runAstGroundTruthCommand(args: string[]): Promise<void> {
|
|
@@ -35,7 +35,7 @@ import type { LogThreshold } from '@hydranium/protocol';
|
|
|
35
35
|
import { statSync } from 'node:fs';
|
|
36
36
|
import * as path from 'node:path';
|
|
37
37
|
import { fileURLToPath } from 'node:url';
|
|
38
|
-
import { parseLogLevelOption } from '../log-level.js';
|
|
38
|
+
import { type LogOptions, parseLogLevelOption } from '../log-level.js';
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* How a parser reports a usage problem. It never returns, so a parser can treat
|
|
@@ -68,33 +68,57 @@ export const SERVICES_FLAG = '--services';
|
|
|
68
68
|
* on which command a caller happened to reach for.
|
|
69
69
|
*/
|
|
70
70
|
export const LOG_LEVEL_FLAG = '--log-level';
|
|
71
|
+
export const LOG_FILE_FLAG = '--log-file';
|
|
72
|
+
export const LOG_FILE_LEVEL_FLAG = '--log-file-level';
|
|
73
|
+
|
|
74
|
+
/** The log flags every subcommand takes, read by {@link logOptions}. */
|
|
75
|
+
export const LOG_VALUE_FLAGS = [LOG_LEVEL_FLAG, LOG_FILE_FLAG, LOG_FILE_LEVEL_FLAG] as const;
|
|
71
76
|
|
|
72
77
|
/** The value flags {@link parseServerSpawnOptions} consumes itself. */
|
|
73
|
-
export const SERVER_SPAWN_VALUE_FLAGS: readonly string[] = ['--server', '--cwd',
|
|
78
|
+
export const SERVER_SPAWN_VALUE_FLAGS: readonly string[] = ['--server', '--cwd', ...LOG_VALUE_FLAGS];
|
|
74
79
|
|
|
75
80
|
/**
|
|
76
|
-
* The `--help`
|
|
81
|
+
* The `--help` lines every subcommand describes {@link LOG_VALUE_FLAGS} with.
|
|
77
82
|
*
|
|
78
83
|
* Shared text, per-command padding: `command-args.test.ts` compares the flags a
|
|
79
84
|
* help block DESCRIBES against the flags the parser accepts, and it recognises a
|
|
80
|
-
* description by the two-space indent — so
|
|
85
|
+
* description by the two-space indent — so each line has to be a real option entry
|
|
81
86
|
* in each block, and the column each block aligns its descriptions at is the
|
|
82
|
-
* block's own.
|
|
87
|
+
* block's own. A flag too wide for that column takes its description on the
|
|
88
|
+
* next line, at the column.
|
|
83
89
|
*/
|
|
84
|
-
export function
|
|
85
|
-
|
|
90
|
+
export function logHelpLines(padTo: number, head = 'the head this boots'): string[] {
|
|
91
|
+
const line = (flag: string, text: string): string[] =>
|
|
92
|
+
flag.length < padTo - 1 ? [` ${flag.padEnd(padTo)}${text}`] : [` ${flag}`, `${' '.repeat(padTo + 2)}${text}`];
|
|
93
|
+
return [
|
|
94
|
+
line(`${LOG_LEVEL_FLAG} <lvl>`, `Log threshold for ${head} (off|error|warn|info|debug|trace).`),
|
|
95
|
+
line(`${LOG_FILE_FLAG} <file>`, `Also write ${head}'s log to <file>; {workspace} expands to the workspace.`),
|
|
96
|
+
line(`${LOG_FILE_LEVEL_FLAG} <lvl>`, `Threshold for that file, which the log-level setting does not change.`)
|
|
97
|
+
].flat();
|
|
86
98
|
}
|
|
87
99
|
|
|
88
100
|
/**
|
|
89
|
-
* Narrow
|
|
90
|
-
*
|
|
101
|
+
* Narrow the raw {@link LOG_VALUE_FLAGS} values a parser collected, leaving
|
|
102
|
+
* absent flags absent.
|
|
103
|
+
*
|
|
104
|
+
* `--log-file` resolves against the caller's directory: the child may run
|
|
105
|
+
* elsewhere (`--cwd`), and a relative path would land the log there.
|
|
91
106
|
*
|
|
92
107
|
* Throws rather than reaching `onError`, matching {@link parseServerSpawnOptions}:
|
|
93
108
|
* the level vocabulary belongs to the protocol package, which reports it, and the
|
|
94
109
|
* entry point's catch turns that into the same message-and-exit.
|
|
95
110
|
*/
|
|
96
|
-
export function
|
|
97
|
-
|
|
111
|
+
export function logOptions(options: Readonly<Record<string, string | undefined>>): LogOptions {
|
|
112
|
+
const level = (flag: string): LogThreshold | undefined => {
|
|
113
|
+
const value = options[flag];
|
|
114
|
+
return value === undefined ? undefined : parseLogLevelOption(value, flag);
|
|
115
|
+
};
|
|
116
|
+
const logFile = options[LOG_FILE_FLAG];
|
|
117
|
+
return {
|
|
118
|
+
logLevel: level(LOG_LEVEL_FLAG),
|
|
119
|
+
logFile: logFile === undefined ? undefined : path.resolve(logFile),
|
|
120
|
+
logFileLevel: level(LOG_FILE_LEVEL_FLAG)
|
|
121
|
+
};
|
|
98
122
|
}
|
|
99
123
|
|
|
100
124
|
/**
|
|
@@ -336,11 +360,10 @@ function kebabToCamel(name: string): string {
|
|
|
336
360
|
}
|
|
337
361
|
|
|
338
362
|
/** What {@link parseServerSpawnOptions} recovered from an argv. */
|
|
339
|
-
export interface SpawnOptions {
|
|
363
|
+
export interface SpawnOptions extends LogOptions {
|
|
340
364
|
serverCommand: string;
|
|
341
365
|
serverArgs: string[];
|
|
342
366
|
cwd?: string;
|
|
343
|
-
logLevel?: LogThreshold;
|
|
344
367
|
/** The tokens this parser did not claim, for the subcommand's own options. */
|
|
345
368
|
extra: string[];
|
|
346
369
|
}
|
|
@@ -363,10 +386,10 @@ function isRelativePathToken(token: string, isCommand: boolean): boolean {
|
|
|
363
386
|
|
|
364
387
|
/**
|
|
365
388
|
* Shared option parser for subcommands that spawn a data-server child. Consumes
|
|
366
|
-
* `--server` / `--cwd` /
|
|
389
|
+
* `--server` / `--cwd` / the {@link LOG_VALUE_FLAGS} and returns the surviving tokens as
|
|
367
390
|
* `extra` for {@link parseFlagOptions} to finish.
|
|
368
391
|
*
|
|
369
|
-
* An unrecognised
|
|
392
|
+
* An unrecognised log level value throws rather than reaching `onError`: the
|
|
370
393
|
* level vocabulary belongs to the protocol package, which reports it, and the
|
|
371
394
|
* entry point's catch turns that into the same message-and-exit.
|
|
372
395
|
*
|
|
@@ -383,7 +406,7 @@ function isRelativePathToken(token: string, isCommand: boolean): boolean {
|
|
|
383
406
|
export function parseServerSpawnOptions(args: string[], commandName: string, onError: UsageError = exitWithUsage): SpawnOptions {
|
|
384
407
|
let serverSpec: string | undefined;
|
|
385
408
|
let cwd: string | undefined;
|
|
386
|
-
|
|
409
|
+
const logFlags: Record<string, string> = {};
|
|
387
410
|
const extra: string[] = [];
|
|
388
411
|
for (let index = 0; index < args.length; index += 1) {
|
|
389
412
|
const flag = args[index];
|
|
@@ -399,8 +422,8 @@ export function parseServerSpawnOptions(args: string[], commandName: string, onE
|
|
|
399
422
|
serverSpec = claim();
|
|
400
423
|
} else if (flag === '--cwd') {
|
|
401
424
|
cwd = claim();
|
|
402
|
-
} else if (
|
|
403
|
-
|
|
425
|
+
} else if ((LOG_VALUE_FLAGS as readonly string[]).includes(flag)) {
|
|
426
|
+
logFlags[flag] = claim();
|
|
404
427
|
} else {
|
|
405
428
|
extra.push(flag);
|
|
406
429
|
}
|
|
@@ -425,5 +448,5 @@ export function parseServerSpawnOptions(args: string[], commandName: string, onE
|
|
|
425
448
|
);
|
|
426
449
|
}
|
|
427
450
|
}
|
|
428
|
-
return { serverCommand, serverArgs, cwd,
|
|
451
|
+
return { serverCommand, serverArgs, cwd, ...logOptions(logFlags), extra };
|
|
429
452
|
}
|
|
@@ -8,14 +8,13 @@
|
|
|
8
8
|
********************************************************************************/
|
|
9
9
|
|
|
10
10
|
import type * as HydraniumCoreNode from '@hydranium/core/node';
|
|
11
|
-
import type { LogThreshold } from '@hydranium/protocol';
|
|
12
11
|
import { spawn } from 'node:child_process';
|
|
13
12
|
import { statSync, writeFileSync } from 'node:fs';
|
|
14
13
|
import { createRequire } from 'node:module';
|
|
15
14
|
import * as path from 'node:path';
|
|
16
15
|
import { pathToFileURL } from 'node:url';
|
|
17
16
|
import { driverHeapArgs, type HeapReading } from '../driver-heap.js';
|
|
18
|
-
import {
|
|
17
|
+
import { logEnv, type LogOptions } from '../log-level.js';
|
|
19
18
|
import { SERVICES_FLAG } from './harness-args.js';
|
|
20
19
|
|
|
21
20
|
/** The flag every report-producing subcommand names its destination file with. */
|
|
@@ -131,14 +130,13 @@ export function spawnNodeChild(execArgs: string[], env?: Record<string, string>)
|
|
|
131
130
|
/** How a subcommand parent reaches its driver child; the seam `__spawnForTest` replaces. */
|
|
132
131
|
export type SpawnDriverChild = (execArgs: string[], env?: Record<string, string>) => Promise<number>;
|
|
133
132
|
|
|
134
|
-
/**
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
readonly logLevel?: LogThreshold;
|
|
133
|
+
/**
|
|
134
|
+
* What every `--services` subcommand carries for the child it spawns, on top of
|
|
135
|
+
* its own options. The log options apply to the head the driver boots; an
|
|
136
|
+
* absent one leaves the child's inherited environment alone, so an ambient
|
|
137
|
+
* variable still wins where a caller set one.
|
|
138
|
+
*/
|
|
139
|
+
export interface DriverSpawnOptions extends LogOptions {
|
|
142
140
|
/** Test-only: capture the node argv and env instead of spawning the real child. */
|
|
143
141
|
readonly __spawnForTest?: SpawnDriverChild;
|
|
144
142
|
/**
|
|
@@ -154,12 +152,12 @@ export interface DriverSpawnOptions {
|
|
|
154
152
|
* Parent-side: run a subcommand's driver child and propagate its exit code, so a
|
|
155
153
|
* shell or CI step sees the gate.
|
|
156
154
|
*
|
|
157
|
-
* **The log
|
|
158
|
-
* head's logger reads `
|
|
159
|
-
* which is inside the driver's dynamic import — a flag the driver
|
|
160
|
-
* arrive after the only moment it can be read. Setting
|
|
161
|
-
* exporting
|
|
162
|
-
*
|
|
155
|
+
* **The log options travel in the child's ENVIRONMENT, not its argv.** The
|
|
156
|
+
* head's logger reads its `HYDRANIUM_LOG_*` variables while `createServices`
|
|
157
|
+
* constructs it, which is inside the driver's dynamic import — a flag the driver
|
|
158
|
+
* parsed would arrive after the only moment it can be read. Setting them here
|
|
159
|
+
* rather than exporting them also leaves the CLI's own output alone, unlike
|
|
160
|
+
* ambient variables the parent would carry too.
|
|
163
161
|
*
|
|
164
162
|
* A head that binds a logger of its own decides for itself whether the flag means
|
|
165
163
|
* anything, which is the intended seam: the CLI is language-agnostic and cannot
|
|
@@ -173,10 +171,7 @@ export interface DriverSpawnOptions {
|
|
|
173
171
|
*/
|
|
174
172
|
export async function runDriverChild(execArgs: string[], options: DriverSpawnOptions): Promise<void> {
|
|
175
173
|
const spawnChild = options.__spawnForTest ?? spawnNodeChild;
|
|
176
|
-
const code = await spawnChild(
|
|
177
|
-
[...driverHeapArgs(options.__heapReadingForTest), ...execArgs],
|
|
178
|
-
options.logLevel === undefined ? undefined : logLevelEnv(options.logLevel)
|
|
179
|
-
);
|
|
174
|
+
const code = await spawnChild([...driverHeapArgs(options.__heapReadingForTest), ...execArgs], logEnv(options));
|
|
180
175
|
if (code) {
|
|
181
176
|
process.exitCode = code;
|
|
182
177
|
}
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
import {
|
|
11
11
|
exitWithUsage,
|
|
12
12
|
helpRequested,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
LOG_VALUE_FLAGS,
|
|
14
|
+
logHelpLines,
|
|
15
|
+
logOptions,
|
|
16
16
|
parseHarnessArgs,
|
|
17
17
|
printHelp,
|
|
18
18
|
SERVICES_FLAG,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
} from './harness-args.js';
|
|
21
21
|
import { runLintGrammar, type LintGrammarCommandOptions } from './lint-grammar.js';
|
|
22
22
|
|
|
23
|
-
const VALUE_FLAGS = [
|
|
23
|
+
const VALUE_FLAGS = [...LOG_VALUE_FLAGS] as const;
|
|
24
24
|
const BOOL_FLAGS = ['--strict', '--json'] as const;
|
|
25
25
|
const REPEATABLE_VALUE_FLAGS = ['--name-property'] as const;
|
|
26
26
|
|
|
@@ -36,7 +36,7 @@ export const LINT_GRAMMAR_VALUE_FLAGS: readonly string[] = [SERVICES_FLAG, ...RE
|
|
|
36
36
|
|
|
37
37
|
/** The `--help` text, as data, held to {@link LINT_GRAMMAR_FLAGS} by a test. */
|
|
38
38
|
export const LINT_GRAMMAR_HELP: readonly string[] = [
|
|
39
|
-
'Usage: hydranium-cli lint-grammar --services <module> [--name-property <p>]... [--strict] [--json] [--log-level <lvl>]',
|
|
39
|
+
'Usage: hydranium-cli lint-grammar --services <module> [--name-property <p>]... [--strict] [--json] [--log-level <lvl>] [--log-file <file>] [--log-file-level <lvl>]',
|
|
40
40
|
'',
|
|
41
41
|
"Check a head's grammar against the framework's conventions — a CI gate: the",
|
|
42
42
|
'process exits non-zero when a violation is found. Read-only: no workspace is',
|
|
@@ -52,7 +52,7 @@ export const LINT_GRAMMAR_HELP: readonly string[] = [
|
|
|
52
52
|
' (repeatable). Default: name.',
|
|
53
53
|
' --strict Also fail (non-zero exit) on warnings, not only errors.',
|
|
54
54
|
' --json Emit the raw JSON result instead of the human report.',
|
|
55
|
-
|
|
55
|
+
...logHelpLines(23)
|
|
56
56
|
];
|
|
57
57
|
|
|
58
58
|
export function parseLintGrammarArgs(args: string[], onError: UsageError = exitWithUsage): LintGrammarCommandOptions {
|
|
@@ -66,7 +66,7 @@ export function parseLintGrammarArgs(args: string[], onError: UsageError = exitW
|
|
|
66
66
|
nameProperties: values['--name-property'],
|
|
67
67
|
strict: options['--strict'] === 'true',
|
|
68
68
|
json: options['--json'] === 'true',
|
|
69
|
-
|
|
69
|
+
...logOptions(options)
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
72
|
|
|
@@ -10,9 +10,10 @@
|
|
|
10
10
|
import {
|
|
11
11
|
exitWithUsage,
|
|
12
12
|
helpRequested,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
LOG_FILE_FLAG,
|
|
14
|
+
LOG_VALUE_FLAGS,
|
|
15
|
+
logHelpLines,
|
|
16
|
+
logOptions,
|
|
16
17
|
numericOption,
|
|
17
18
|
parseHarnessArgs,
|
|
18
19
|
printHelp,
|
|
@@ -29,7 +30,7 @@ const VALUE_FLAGS = [
|
|
|
29
30
|
'--snapshot-path',
|
|
30
31
|
'--profile',
|
|
31
32
|
'--session-out',
|
|
32
|
-
|
|
33
|
+
...LOG_VALUE_FLAGS
|
|
33
34
|
] as const;
|
|
34
35
|
const BOOL_FLAGS = ['--snapshot', '--json'] as const;
|
|
35
36
|
|
|
@@ -67,11 +68,13 @@ export const MEASURE_MEMORY_HELP: readonly string[] = [
|
|
|
67
68
|
' --profile <dims> Capture sampled profiles around the build (+churn) into a',
|
|
68
69
|
' profiling session. Comma-separated: cpu,alloc,gc,eld,heap.',
|
|
69
70
|
' --session-out <dir> Parent directory for the profiling session folder (with --profile).',
|
|
71
|
+
' The session logs to its server.log at --log-file-level, else at',
|
|
72
|
+
' --log-level, else info; --log-file is refused beside --profile.',
|
|
70
73
|
' --json Emit the measurement as one JSON document instead of the',
|
|
71
74
|
' progress lines: documentCount, buildMs, emptyHeapBytes,',
|
|
72
75
|
' afterBuildHeapBytes, and churnGrowthBytes / snapshotPath /',
|
|
73
76
|
' profilingSession where the run produced them.',
|
|
74
|
-
|
|
77
|
+
...logHelpLines(22)
|
|
75
78
|
];
|
|
76
79
|
|
|
77
80
|
/**
|
|
@@ -98,6 +101,11 @@ function validatedProfile(csv: string | undefined, onError: UsageError): string
|
|
|
98
101
|
|
|
99
102
|
export function parseMeasureMemoryArgs(args: string[], onError: UsageError = exitWithUsage): MeasureMemoryCommandOptions {
|
|
100
103
|
const { servicesModule, workspace, options } = parseHarnessArgs(args, 'measure-memory', VALUE_FLAGS, BOOL_FLAGS, { onError });
|
|
104
|
+
// The profiling session points the log at its own `server.log`, which would
|
|
105
|
+
// replace the requested file without a word.
|
|
106
|
+
if (options['--profile'] !== undefined && options[LOG_FILE_FLAG] !== undefined) {
|
|
107
|
+
onError(`${LOG_FILE_FLAG} cannot be combined with --profile, which logs to the session's server.log`);
|
|
108
|
+
}
|
|
101
109
|
return {
|
|
102
110
|
servicesModule,
|
|
103
111
|
workspace,
|
|
@@ -110,7 +118,7 @@ export function parseMeasureMemoryArgs(args: string[], onError: UsageError = exi
|
|
|
110
118
|
profile: validatedProfile(options['--profile'], onError),
|
|
111
119
|
sessionOut: options['--session-out'],
|
|
112
120
|
json: options['--json'] === 'true',
|
|
113
|
-
|
|
121
|
+
...logOptions(options)
|
|
114
122
|
};
|
|
115
123
|
}
|
|
116
124
|
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
import {
|
|
11
11
|
exitWithUsage,
|
|
12
12
|
helpRequested,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
LOG_VALUE_FLAGS,
|
|
14
|
+
logHelpLines,
|
|
15
|
+
logOptions,
|
|
16
16
|
parseHarnessArgs,
|
|
17
17
|
printHelp,
|
|
18
18
|
SERVICES_FLAG,
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
import { OUT_FILE_FLAG } from './headless-harness.js';
|
|
22
22
|
import { runModelDocs, type ModelDocsCommandOptions } from './model-docs.js';
|
|
23
23
|
|
|
24
|
-
const VALUE_FLAGS = [OUT_FILE_FLAG,
|
|
24
|
+
const VALUE_FLAGS = [OUT_FILE_FLAG, ...LOG_VALUE_FLAGS] as const;
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Every flag `model-docs` accepts, derived from the sets the parser is handed so
|
|
@@ -35,7 +35,7 @@ export const MODEL_DOCS_VALUE_FLAGS: readonly string[] = MODEL_DOCS_FLAGS;
|
|
|
35
35
|
|
|
36
36
|
/** The `--help` text, as data, held to {@link MODEL_DOCS_FLAGS} by a test. */
|
|
37
37
|
export const MODEL_DOCS_HELP: readonly string[] = [
|
|
38
|
-
'Usage: hydranium-cli model-docs --services <module> [--out-file <file>] [--log-level <lvl>]',
|
|
38
|
+
'Usage: hydranium-cli model-docs --services <module> [--out-file <file>] [--log-level <lvl>] [--log-file <file>] [--log-file-level <lvl>]',
|
|
39
39
|
'',
|
|
40
40
|
'Generate a navigable Markdown reference of every AST node type — a type index,',
|
|
41
41
|
'cross-linked super/sub types, a reverse "referenced by" index, and a per-language',
|
|
@@ -49,7 +49,7 @@ export const MODEL_DOCS_HELP: readonly string[] = [
|
|
|
49
49
|
' --out-file <file> Write the Markdown to this file instead of stdout. Written',
|
|
50
50
|
' only once the report exists, unlike a shell redirection,',
|
|
51
51
|
' which truncates the file before the head is even booted.',
|
|
52
|
-
|
|
52
|
+
...logHelpLines(22)
|
|
53
53
|
];
|
|
54
54
|
|
|
55
55
|
export function parseModelDocsArgs(args: string[], onError: UsageError = exitWithUsage): ModelDocsCommandOptions {
|
|
@@ -57,7 +57,7 @@ export function parseModelDocsArgs(args: string[], onError: UsageError = exitWit
|
|
|
57
57
|
requireWorkspace: false,
|
|
58
58
|
onError
|
|
59
59
|
});
|
|
60
|
-
return { servicesModule, outFile: options[OUT_FILE_FLAG],
|
|
60
|
+
return { servicesModule, outFile: options[OUT_FILE_FLAG], ...logOptions(options) };
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
export function runModelDocsCommand(args: string[]): Promise<void> {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import {
|
|
11
11
|
exitWithUsage,
|
|
12
12
|
helpRequested,
|
|
13
|
+
logHelpLines,
|
|
13
14
|
parseFlagOptions,
|
|
14
15
|
parseServerSpawnOptions,
|
|
15
16
|
printHelp,
|
|
@@ -32,7 +33,7 @@ export const PROJECTS_VALUE_FLAGS: readonly string[] = PROJECTS_FLAGS;
|
|
|
32
33
|
|
|
33
34
|
/** The `--help` text, as data, held to {@link PROJECTS_FLAGS} by a test. */
|
|
34
35
|
export const PROJECTS_HELP: readonly string[] = [
|
|
35
|
-
'Usage: hydranium-cli projects --server "<cmd> [args...]" [--cwd <dir>] [--log-level <level>]',
|
|
36
|
+
'Usage: hydranium-cli projects --server "<cmd> [args...]" [--cwd <dir>] [--log-level <lvl>] [--log-file <file>] [--log-file-level <lvl>]',
|
|
36
37
|
'',
|
|
37
38
|
'List projects exposed by the data-server subprocess. Output is newline-delimited',
|
|
38
39
|
'JSON — one project envelope per line.',
|
|
@@ -40,17 +41,17 @@ export const PROJECTS_HELP: readonly string[] = [
|
|
|
40
41
|
'Options:',
|
|
41
42
|
' --server "<cmd>" Command-line for the data-server subprocess (required).',
|
|
42
43
|
' --cwd <dir> Working directory for the spawned child. Default: cwd.',
|
|
43
|
-
'
|
|
44
|
+
...logHelpLines(19, 'the spawned server')
|
|
44
45
|
];
|
|
45
46
|
|
|
46
47
|
export function parseProjectsArgs(args: string[], onError: UsageError = exitWithUsage): ProjectsCommandOptions {
|
|
47
|
-
const { serverCommand, serverArgs, cwd, logLevel, extra } = parseServerSpawnOptions(args, 'projects', onError);
|
|
48
|
+
const { serverCommand, serverArgs, cwd, logLevel, logFile, logFileLevel, extra } = parseServerSpawnOptions(args, 'projects', onError);
|
|
48
49
|
// `projects` declares no options beyond the shared ones, so nothing may
|
|
49
50
|
// survive the spawn parser. Draining `extra` against an empty set is what
|
|
50
51
|
// makes a typo an error here rather than a flag that silently did nothing —
|
|
51
52
|
// the siblings get this from the option list they pass.
|
|
52
53
|
parseFlagOptions(extra, 'projects', [], onError);
|
|
53
|
-
return { serverCommand, serverArgs, cwd, logLevel };
|
|
54
|
+
return { serverCommand, serverArgs, cwd, logLevel, logFile, logFileLevel };
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
export function runProjectsCommand(args: string[]): Promise<void> {
|
package/src/commands/projects.ts
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
* SPDX-License-Identifier: MIT
|
|
8
8
|
********************************************************************************/
|
|
9
9
|
|
|
10
|
-
import type {
|
|
10
|
+
import type { Project, TransferElement } from '@hydranium/protocol';
|
|
11
11
|
import type { DataServerProtocol } from '@hydranium/protocol/data';
|
|
12
|
-
import {
|
|
12
|
+
import { logEnv, type LogOptions } from '../log-level.js';
|
|
13
13
|
import { withDataServer } from '../spawn-data-server.js';
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -17,7 +17,7 @@ import { withDataServer } from '../spawn-data-server.js';
|
|
|
17
17
|
* `serverArgs` mirror what an adopter would pass to spawn the data-server
|
|
18
18
|
* subprocess.
|
|
19
19
|
*/
|
|
20
|
-
export interface ProjectsCommandOptions {
|
|
20
|
+
export interface ProjectsCommandOptions extends LogOptions {
|
|
21
21
|
/**
|
|
22
22
|
* Command-line tokens used to spawn the data-server child. Mirrors
|
|
23
23
|
* the `--server "<cmd> [args...]"` shape on the CLI surface — split
|
|
@@ -26,8 +26,6 @@ export interface ProjectsCommandOptions {
|
|
|
26
26
|
readonly serverCommand: string;
|
|
27
27
|
readonly serverArgs?: readonly string[];
|
|
28
28
|
readonly cwd?: string;
|
|
29
|
-
/** Log threshold for the spawned server, set on its `HYDRANIUM_LOG_LEVEL` env. */
|
|
30
|
-
readonly logLevel?: LogThreshold;
|
|
31
29
|
/**
|
|
32
30
|
* Output sink. Default: `process.stdout.write`. Tests inject a
|
|
33
31
|
* capturing stub.
|
|
@@ -60,7 +58,7 @@ export async function runProjects(options: ProjectsCommandOptions): Promise<void
|
|
|
60
58
|
command: options.serverCommand,
|
|
61
59
|
args: options.serverArgs,
|
|
62
60
|
cwd: options.cwd,
|
|
63
|
-
env:
|
|
61
|
+
env: logEnv(options)
|
|
64
62
|
},
|
|
65
63
|
async server => {
|
|
66
64
|
const projects = await server.getProjects();
|