@hydranium/cli 1.0.0-next.10
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 +240 -0
- package/heap-analysis/analyze-heap.mjs +798 -0
- package/heap-analysis/heap/core/aggregate.mjs +152 -0
- package/heap-analysis/heap/core/bucket-label.mjs +23 -0
- package/heap-analysis/heap/core/cutpoints.mjs +51 -0
- package/heap-analysis/heap/core/diff.mjs +56 -0
- package/heap-analysis/heap/core/format.mjs +47 -0
- package/heap-analysis/heap/core/holders.mjs +80 -0
- package/heap-analysis/heap/core/load.mjs +46 -0
- package/heap-analysis/heap/core/report.mjs +68 -0
- package/heap-analysis/heap/core/strings.mjs +80 -0
- package/heap-analysis/heap/langium/concepts.mjs +230 -0
- package/heap-analysis/heap/langium/glsp.classifier.mjs +80 -0
- package/heap-analysis/heap/langium/langium.classifier.mjs +203 -0
- package/heap-analysis/heap/renderer/renderer.mjs +122 -0
- package/lib/cli-version.d.ts +21 -0
- package/lib/cli-version.d.ts.map +1 -0
- package/lib/cli-version.js +30 -0
- package/lib/cli-version.js.map +1 -0
- package/lib/cli.d.ts +11 -0
- package/lib/cli.d.ts.map +1 -0
- package/lib/cli.js +135 -0
- package/lib/cli.js.map +1 -0
- package/lib/commands/analyze-heap-args.d.ts +46 -0
- package/lib/commands/analyze-heap-args.d.ts.map +1 -0
- package/lib/commands/analyze-heap-args.js +174 -0
- package/lib/commands/analyze-heap-args.js.map +1 -0
- package/lib/commands/analyze-heap.d.ts +37 -0
- package/lib/commands/analyze-heap.d.ts.map +1 -0
- package/lib/commands/analyze-heap.js +63 -0
- package/lib/commands/analyze-heap.js.map +1 -0
- package/lib/commands/ast-ground-truth-args.d.ts +23 -0
- package/lib/commands/ast-ground-truth-args.d.ts.map +1 -0
- package/lib/commands/ast-ground-truth-args.js +47 -0
- package/lib/commands/ast-ground-truth-args.js.map +1 -0
- package/lib/commands/ast-ground-truth-driver.d.ts +10 -0
- package/lib/commands/ast-ground-truth-driver.d.ts.map +1 -0
- package/lib/commands/ast-ground-truth-driver.js +72 -0
- package/lib/commands/ast-ground-truth-driver.js.map +1 -0
- package/lib/commands/ast-ground-truth.d.ts +31 -0
- package/lib/commands/ast-ground-truth.d.ts.map +1 -0
- package/lib/commands/ast-ground-truth.js +36 -0
- package/lib/commands/ast-ground-truth.js.map +1 -0
- package/lib/commands/generate-transfer-model-args.d.ts +38 -0
- package/lib/commands/generate-transfer-model-args.d.ts.map +1 -0
- package/lib/commands/generate-transfer-model-args.js +183 -0
- package/lib/commands/generate-transfer-model-args.js.map +1 -0
- package/lib/commands/generate-transfer-model-config.d.ts +32 -0
- package/lib/commands/generate-transfer-model-config.d.ts.map +1 -0
- package/lib/commands/generate-transfer-model-config.js +122 -0
- package/lib/commands/generate-transfer-model-config.js.map +1 -0
- package/lib/commands/generate-transfer-model.d.ts +110 -0
- package/lib/commands/generate-transfer-model.d.ts.map +1 -0
- package/lib/commands/generate-transfer-model.js +587 -0
- package/lib/commands/generate-transfer-model.js.map +1 -0
- package/lib/commands/harness-args.d.ts +198 -0
- package/lib/commands/harness-args.d.ts.map +1 -0
- package/lib/commands/harness-args.js +339 -0
- package/lib/commands/harness-args.js.map +1 -0
- package/lib/commands/headless-harness.d.ts +97 -0
- package/lib/commands/headless-harness.d.ts.map +1 -0
- package/lib/commands/headless-harness.js +121 -0
- package/lib/commands/headless-harness.js.map +1 -0
- package/lib/commands/init-args.d.ts +81 -0
- package/lib/commands/init-args.d.ts.map +1 -0
- package/lib/commands/init-args.js +250 -0
- package/lib/commands/init-args.js.map +1 -0
- package/lib/commands/init-prompt.d.ts +66 -0
- package/lib/commands/init-prompt.d.ts.map +1 -0
- package/lib/commands/init-prompt.js +71 -0
- package/lib/commands/init-prompt.js.map +1 -0
- package/lib/commands/init-templates.d.ts +42 -0
- package/lib/commands/init-templates.d.ts.map +1 -0
- package/lib/commands/init-templates.js +1902 -0
- package/lib/commands/init-templates.js.map +1 -0
- package/lib/commands/init-wizard.d.ts +65 -0
- package/lib/commands/init-wizard.d.ts.map +1 -0
- package/lib/commands/init-wizard.js +302 -0
- package/lib/commands/init-wizard.js.map +1 -0
- package/lib/commands/init-workspace.d.ts +168 -0
- package/lib/commands/init-workspace.d.ts.map +1 -0
- package/lib/commands/init-workspace.js +372 -0
- package/lib/commands/init-workspace.js.map +1 -0
- package/lib/commands/init.d.ts +273 -0
- package/lib/commands/init.d.ts.map +1 -0
- package/lib/commands/init.js +303 -0
- package/lib/commands/init.js.map +1 -0
- package/lib/commands/lint-grammar-args.d.ts +23 -0
- package/lib/commands/lint-grammar-args.d.ts.map +1 -0
- package/lib/commands/lint-grammar-args.js +63 -0
- package/lib/commands/lint-grammar-args.js.map +1 -0
- package/lib/commands/lint-grammar-driver.d.ts +10 -0
- package/lib/commands/lint-grammar-driver.d.ts.map +1 -0
- package/lib/commands/lint-grammar-driver.js +72 -0
- package/lib/commands/lint-grammar-driver.js.map +1 -0
- package/lib/commands/lint-grammar-report.d.ts +24 -0
- package/lib/commands/lint-grammar-report.d.ts.map +1 -0
- package/lib/commands/lint-grammar-report.js +65 -0
- package/lib/commands/lint-grammar-report.js.map +1 -0
- package/lib/commands/lint-grammar.d.ts +33 -0
- package/lib/commands/lint-grammar.d.ts.map +1 -0
- package/lib/commands/lint-grammar.js +42 -0
- package/lib/commands/lint-grammar.js.map +1 -0
- package/lib/commands/measure-memory-args.d.ts +23 -0
- package/lib/commands/measure-memory-args.d.ts.map +1 -0
- package/lib/commands/measure-memory-args.js +106 -0
- package/lib/commands/measure-memory-args.js.map +1 -0
- package/lib/commands/measure-memory-driver.d.ts +10 -0
- package/lib/commands/measure-memory-driver.d.ts.map +1 -0
- package/lib/commands/measure-memory-driver.js +110 -0
- package/lib/commands/measure-memory-driver.js.map +1 -0
- package/lib/commands/measure-memory.d.ts +68 -0
- package/lib/commands/measure-memory.d.ts.map +1 -0
- package/lib/commands/measure-memory.js +100 -0
- package/lib/commands/measure-memory.js.map +1 -0
- package/lib/commands/model-docs-args.d.ts +23 -0
- package/lib/commands/model-docs-args.d.ts.map +1 -0
- package/lib/commands/model-docs-args.js +53 -0
- package/lib/commands/model-docs-args.js.map +1 -0
- package/lib/commands/model-docs-driver.d.ts +10 -0
- package/lib/commands/model-docs-driver.d.ts.map +1 -0
- package/lib/commands/model-docs-driver.js +61 -0
- package/lib/commands/model-docs-driver.js.map +1 -0
- package/lib/commands/model-docs-report.d.ts +18 -0
- package/lib/commands/model-docs-report.d.ts.map +1 -0
- package/lib/commands/model-docs-report.js +123 -0
- package/lib/commands/model-docs-report.js.map +1 -0
- package/lib/commands/model-docs.d.ts +30 -0
- package/lib/commands/model-docs.d.ts.map +1 -0
- package/lib/commands/model-docs.js +37 -0
- package/lib/commands/model-docs.js.map +1 -0
- package/lib/commands/projects-args.d.ts +25 -0
- package/lib/commands/projects-args.d.ts.map +1 -0
- package/lib/commands/projects-args.js +49 -0
- package/lib/commands/projects-args.js.map +1 -0
- package/lib/commands/projects.d.ts +45 -0
- package/lib/commands/projects.d.ts.map +1 -0
- package/lib/commands/projects.js +39 -0
- package/lib/commands/projects.js.map +1 -0
- package/lib/commands/query-args.d.ts +25 -0
- package/lib/commands/query-args.d.ts.map +1 -0
- package/lib/commands/query-args.js +46 -0
- package/lib/commands/query-args.js.map +1 -0
- package/lib/commands/query.d.ts +32 -0
- package/lib/commands/query.d.ts.map +1 -0
- package/lib/commands/query.js +29 -0
- package/lib/commands/query.js.map +1 -0
- package/lib/commands/reflect-args.d.ts +23 -0
- package/lib/commands/reflect-args.d.ts.map +1 -0
- package/lib/commands/reflect-args.js +59 -0
- package/lib/commands/reflect-args.js.map +1 -0
- package/lib/commands/reflect-driver.d.ts +10 -0
- package/lib/commands/reflect-driver.d.ts.map +1 -0
- package/lib/commands/reflect-driver.js +66 -0
- package/lib/commands/reflect-driver.js.map +1 -0
- package/lib/commands/reflect-report.d.ts +20 -0
- package/lib/commands/reflect-report.d.ts.map +1 -0
- package/lib/commands/reflect-report.js +127 -0
- package/lib/commands/reflect-report.js.map +1 -0
- package/lib/commands/reflect.d.ts +30 -0
- package/lib/commands/reflect.d.ts.map +1 -0
- package/lib/commands/reflect.js +38 -0
- package/lib/commands/reflect.js.map +1 -0
- package/lib/commands/save-args.d.ts +26 -0
- package/lib/commands/save-args.d.ts.map +1 -0
- package/lib/commands/save-args.js +59 -0
- package/lib/commands/save-args.js.map +1 -0
- package/lib/commands/save.d.ts +38 -0
- package/lib/commands/save.d.ts.map +1 -0
- package/lib/commands/save.js +48 -0
- package/lib/commands/save.js.map +1 -0
- package/lib/commands/validate-args.d.ts +23 -0
- package/lib/commands/validate-args.d.ts.map +1 -0
- package/lib/commands/validate-args.js +60 -0
- package/lib/commands/validate-args.js.map +1 -0
- package/lib/commands/validate-driver.d.ts +10 -0
- package/lib/commands/validate-driver.d.ts.map +1 -0
- package/lib/commands/validate-driver.js +72 -0
- package/lib/commands/validate-driver.js.map +1 -0
- package/lib/commands/validate-report.d.ts +30 -0
- package/lib/commands/validate-report.d.ts.map +1 -0
- package/lib/commands/validate-report.js +87 -0
- package/lib/commands/validate-report.js.map +1 -0
- package/lib/commands/validate.d.ts +34 -0
- package/lib/commands/validate.d.ts.map +1 -0
- package/lib/commands/validate.js +41 -0
- package/lib/commands/validate.js.map +1 -0
- package/lib/commands/watch-args.d.ts +26 -0
- package/lib/commands/watch-args.d.ts.map +1 -0
- package/lib/commands/watch-args.js +59 -0
- package/lib/commands/watch-args.js.map +1 -0
- package/lib/commands/watch.d.ts +75 -0
- package/lib/commands/watch.d.ts.map +1 -0
- package/lib/commands/watch.js +112 -0
- package/lib/commands/watch.js.map +1 -0
- package/lib/index.d.ts +16 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +16 -0
- package/lib/index.js.map +1 -0
- package/lib/log-level.d.ts +39 -0
- package/lib/log-level.d.ts.map +1 -0
- package/lib/log-level.js +47 -0
- package/lib/log-level.js.map +1 -0
- package/lib/package-name.d.ts +33 -0
- package/lib/package-name.d.ts.map +1 -0
- package/lib/package-name.js +37 -0
- package/lib/package-name.js.map +1 -0
- package/lib/spawn-data-server.d.ts +102 -0
- package/lib/spawn-data-server.d.ts.map +1 -0
- package/lib/spawn-data-server.js +154 -0
- package/lib/spawn-data-server.js.map +1 -0
- package/lib/testing/echo-server.d.ts +11 -0
- package/lib/testing/echo-server.d.ts.map +1 -0
- package/lib/testing/echo-server.js +79 -0
- package/lib/testing/echo-server.js.map +1 -0
- package/package.json +105 -0
- package/src/cli-version.ts +32 -0
- package/src/cli.ts +139 -0
- package/src/commands/analyze-heap-args.ts +184 -0
- package/src/commands/analyze-heap.ts +76 -0
- package/src/commands/ast-ground-truth-args.ts +63 -0
- package/src/commands/ast-ground-truth-driver.ts +81 -0
- package/src/commands/ast-ground-truth.ts +49 -0
- package/src/commands/generate-transfer-model-args.ts +203 -0
- package/src/commands/generate-transfer-model-config.ts +128 -0
- package/src/commands/generate-transfer-model.ts +716 -0
- package/src/commands/harness-args.ts +429 -0
- package/src/commands/headless-harness.ts +165 -0
- package/src/commands/init-args.ts +273 -0
- package/src/commands/init-prompt.ts +149 -0
- package/src/commands/init-templates.ts +2037 -0
- package/src/commands/init-wizard.ts +378 -0
- package/src/commands/init-workspace.ts +442 -0
- package/src/commands/init.ts +554 -0
- package/src/commands/lint-grammar-args.ts +79 -0
- package/src/commands/lint-grammar-driver.ts +83 -0
- package/src/commands/lint-grammar-report.ts +74 -0
- package/src/commands/lint-grammar.ts +57 -0
- package/src/commands/measure-memory-args.ts +123 -0
- package/src/commands/measure-memory-driver.ts +128 -0
- package/src/commands/measure-memory.ts +143 -0
- package/src/commands/model-docs-args.ts +69 -0
- package/src/commands/model-docs-driver.ts +68 -0
- package/src/commands/model-docs-report.ts +133 -0
- package/src/commands/model-docs.ts +48 -0
- package/src/commands/projects-args.ts +62 -0
- package/src/commands/projects.ts +76 -0
- package/src/commands/query-args.ts +61 -0
- package/src/commands/query.ts +58 -0
- package/src/commands/reflect-args.ts +75 -0
- package/src/commands/reflect-driver.ts +76 -0
- package/src/commands/reflect-report.ts +138 -0
- package/src/commands/reflect.ts +51 -0
- package/src/commands/save-args.ts +74 -0
- package/src/commands/save.ts +84 -0
- package/src/commands/validate-args.ts +76 -0
- package/src/commands/validate-driver.ts +84 -0
- package/src/commands/validate-report.ts +95 -0
- package/src/commands/validate.ts +58 -0
- package/src/commands/watch-args.ts +74 -0
- package/src/commands/watch.ts +185 -0
- package/src/index.ts +16 -0
- package/src/log-level.ts +49 -0
- package/src/package-name.ts +39 -0
- package/src/spawn-data-server.ts +247 -0
- package/src/testing/echo-server.ts +93 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
* Spawned child of the `model-docs` subcommand. Launched by the parent with
|
|
12
|
+
* `node <this> --services <module> [--out-file <file>]`. It dynamic-imports the
|
|
13
|
+
* head's services module, runs the framework's `reflectGrammar` (the same
|
|
14
|
+
* reflection the `reflect` subcommand uses), and delivers a navigable Markdown
|
|
15
|
+
* reference to stdout or the named file. Read-only — exits 0 unless booting the
|
|
16
|
+
* head throws. Never imported — only spawned.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { emitReport, loadHeadlessContext, OUT_FILE_FLAG } from './headless-harness.js';
|
|
20
|
+
import { formatModelDocs } from './model-docs-report.js';
|
|
21
|
+
|
|
22
|
+
interface DriverArgs {
|
|
23
|
+
servicesModule?: string;
|
|
24
|
+
outFile?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function parseArgs(argv: string[]): DriverArgs {
|
|
28
|
+
const parsed: DriverArgs = {};
|
|
29
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
30
|
+
const flag = argv[index];
|
|
31
|
+
if (flag === '--services' || flag === OUT_FILE_FLAG) {
|
|
32
|
+
const value = argv[index + 1];
|
|
33
|
+
if (value === undefined) {
|
|
34
|
+
console.error(`Missing value for ${flag}`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
if (flag === '--services') {
|
|
38
|
+
parsed.servicesModule = value;
|
|
39
|
+
} else {
|
|
40
|
+
parsed.outFile = value;
|
|
41
|
+
}
|
|
42
|
+
index += 1;
|
|
43
|
+
} else {
|
|
44
|
+
console.error(`Unknown option: ${flag}`);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return parsed;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function main(argv: string[]): Promise<number> {
|
|
52
|
+
const args = parseArgs(argv);
|
|
53
|
+
if (!args.servicesModule) {
|
|
54
|
+
console.error('model-docs-driver: --services <module> is required.');
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
const { createServices, coreNode } = await loadHeadlessContext(args.servicesModule);
|
|
58
|
+
const reflection = coreNode.reflectGrammar({ createServices });
|
|
59
|
+
emitReport(formatModelDocs(reflection), args.outFile);
|
|
60
|
+
return 0;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
main(process.argv.slice(2))
|
|
64
|
+
.then(code => process.exit(code))
|
|
65
|
+
.catch((error: unknown) => {
|
|
66
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
67
|
+
process.exit(1);
|
|
68
|
+
});
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
// Type-only import: the CLI is head-neutral and carries no runtime `@hydranium/core`
|
|
11
|
+
// dependency (the head's copy is resolved at run time via `loadHeadlessContext`).
|
|
12
|
+
import type { GrammarReflection, TypeReflection } from '@hydranium/core/node';
|
|
13
|
+
|
|
14
|
+
/** GitHub-style heading slug: lowercase, non-alphanumerics to hyphens, collapse repeats. */
|
|
15
|
+
function slug(name: string): string {
|
|
16
|
+
return name
|
|
17
|
+
.toLowerCase()
|
|
18
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
19
|
+
.replace(/^-+|-+$/g, '');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** A Markdown link to a type's section, when that type has a section; else bare code. */
|
|
23
|
+
function typeLink(name: string, known: ReadonlySet<string>): string {
|
|
24
|
+
return known.has(name) ? `[${name}](#${slug(name)})` : `\`${name}\``;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Escape a value for a Markdown table cell (only `|` and newlines can break a row). */
|
|
28
|
+
function cell(value: string): string {
|
|
29
|
+
return value.replace(/\|/g, '\\|').replace(/\r?\n/g, ' ');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Per-language grammar summary — extensions, entry rule, terminals table. */
|
|
33
|
+
function languageSection(reflection: GrammarReflection): string[] {
|
|
34
|
+
const lines: string[] = ['## Languages', ''];
|
|
35
|
+
for (const language of reflection.languages) {
|
|
36
|
+
lines.push(`### ${language.languageId}`, '');
|
|
37
|
+
const extensions = language.fileExtensions.length ? language.fileExtensions.map(ext => `\`${ext}\``).join(', ') : '_none_';
|
|
38
|
+
lines.push(`- File extensions: ${extensions}`);
|
|
39
|
+
lines.push(`- Entry rule: ${language.entryRule ? `\`${language.entryRule}\`` : '_none_'}`);
|
|
40
|
+
lines.push('');
|
|
41
|
+
if (language.terminals.length) {
|
|
42
|
+
lines.push('| Terminal | Pattern | Hidden |', '| --- | --- | --- |');
|
|
43
|
+
for (const terminal of language.terminals) {
|
|
44
|
+
const pattern = terminal.pattern !== undefined ? `\`${cell(terminal.pattern)}\`` : '';
|
|
45
|
+
lines.push(`| ${cell(terminal.name)} | ${pattern} | ${terminal.hidden ? 'yes' : ''} |`);
|
|
46
|
+
}
|
|
47
|
+
lines.push('');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return lines;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** The alphabetical type index (TOC), each entry linked, marking abstract vs concrete. */
|
|
54
|
+
function typeIndexSection(reflection: GrammarReflection): string[] {
|
|
55
|
+
const lines: string[] = [`## Types (${reflection.types.length})`, ''];
|
|
56
|
+
for (const type of reflection.types) {
|
|
57
|
+
const kind = type.directSubTypes.length ? ' _(abstract)_' : '';
|
|
58
|
+
lines.push(`- [${type.name}](#${slug(type.name)})${kind}`);
|
|
59
|
+
}
|
|
60
|
+
lines.push('');
|
|
61
|
+
return lines;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Build the reverse index: for each type, the `Type.property` sites that cross-reference it. */
|
|
65
|
+
function referencedByIndex(reflection: GrammarReflection): Map<string, string[]> {
|
|
66
|
+
const index = new Map<string, string[]>();
|
|
67
|
+
for (const type of reflection.types) {
|
|
68
|
+
for (const property of type.properties) {
|
|
69
|
+
if (property.referenceType !== undefined) {
|
|
70
|
+
const sites = index.get(property.referenceType) ?? [];
|
|
71
|
+
sites.push(`${type.name}.${property.name}`);
|
|
72
|
+
index.set(property.referenceType, sites);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return index;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** One type's detail section — extends / subtypes / referenced-by / property table. */
|
|
80
|
+
function typeSection(type: TypeReflection, known: ReadonlySet<string>, referencedBy: readonly string[]): string[] {
|
|
81
|
+
const lines: string[] = [`### ${type.name}`, ''];
|
|
82
|
+
if (type.superTypes.length) {
|
|
83
|
+
lines.push(`- Extends: ${type.superTypes.map(name => typeLink(name, known)).join(', ')}`);
|
|
84
|
+
}
|
|
85
|
+
if (type.directSubTypes.length) {
|
|
86
|
+
lines.push(`- Known subtypes: ${type.directSubTypes.map(name => typeLink(name, known)).join(', ')}`);
|
|
87
|
+
}
|
|
88
|
+
if (referencedBy.length) {
|
|
89
|
+
// The reverse index is keyed by type, so each `Owner.property` site links to Owner's section.
|
|
90
|
+
const links = referencedBy.map(site => {
|
|
91
|
+
const owner = site.slice(0, site.indexOf('.'));
|
|
92
|
+
return `${typeLink(owner, known)}.${site.slice(site.indexOf('.') + 1)}`;
|
|
93
|
+
});
|
|
94
|
+
lines.push(`- Referenced by: ${links.join(', ')}`);
|
|
95
|
+
}
|
|
96
|
+
lines.push('');
|
|
97
|
+
if (type.properties.length) {
|
|
98
|
+
lines.push('| Property | Kind | Target | Array |', '| --- | --- | --- | --- |');
|
|
99
|
+
for (const property of type.properties) {
|
|
100
|
+
const isReference = property.referenceType !== undefined;
|
|
101
|
+
const target = isReference ? typeLink(property.referenceType!, known) : '';
|
|
102
|
+
lines.push(`| ${cell(property.name)} | ${isReference ? 'reference' : 'value'} | ${target} | ${property.array ? 'yes' : ''} |`);
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
lines.push('_No properties._');
|
|
106
|
+
}
|
|
107
|
+
lines.push('');
|
|
108
|
+
return lines;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Render a grammar reflection as a navigable Markdown model reference for adopter
|
|
113
|
+
* docs: a per-language grammar summary, an anchor-linked type index, and a per-type
|
|
114
|
+
* section with cross-linked super/sub types, a reverse "referenced by" index, and a
|
|
115
|
+
* property table. Distinct from `reflect`'s flat dump — this is the publishable
|
|
116
|
+
* reference (cross-linked, navigable), whereas `reflect --json` is the machine form.
|
|
117
|
+
*/
|
|
118
|
+
export function formatModelDocs(reflection: GrammarReflection): string {
|
|
119
|
+
const known = new Set(reflection.types.map(type => type.name));
|
|
120
|
+
const referencedBy = referencedByIndex(reflection);
|
|
121
|
+
const typeSections = reflection.types.flatMap(type => typeSection(type, known, referencedBy.get(type.name) ?? []));
|
|
122
|
+
return [
|
|
123
|
+
'# Model reference',
|
|
124
|
+
'',
|
|
125
|
+
'Generated from the grammar reflection. One section per AST node type.',
|
|
126
|
+
'',
|
|
127
|
+
...languageSection(reflection),
|
|
128
|
+
...typeIndexSection(reflection),
|
|
129
|
+
'## Type details',
|
|
130
|
+
'',
|
|
131
|
+
...typeSections
|
|
132
|
+
].join('\n');
|
|
133
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import { OUT_FILE_FLAG, runDriverChild, type DriverSpawnOptions } from './headless-harness.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Absolute path to the spawned driver. From `lib/commands/model-docs.js` the
|
|
15
|
+
* compiled driver sits beside it at `lib/commands/model-docs-driver.js`.
|
|
16
|
+
*/
|
|
17
|
+
const DRIVER = fileURLToPath(new URL('./model-docs-driver.js', import.meta.url));
|
|
18
|
+
|
|
19
|
+
/** Options for the {@link runModelDocs} subcommand. */
|
|
20
|
+
export interface ModelDocsCommandOptions extends DriverSpawnOptions {
|
|
21
|
+
/** ESM module exporting a zero-arg `createServices(): { shared }` thunk. */
|
|
22
|
+
readonly servicesModule: string;
|
|
23
|
+
/** Write the Markdown to this file instead of stdout. */
|
|
24
|
+
readonly outFile?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Build the argv the driver child parses. Pure → unit-testable. */
|
|
28
|
+
export function buildModelDocsDriverArgs(options: ModelDocsCommandOptions): string[] {
|
|
29
|
+
const args = ['--services', options.servicesModule];
|
|
30
|
+
if (options.outFile !== undefined) {
|
|
31
|
+
args.push(OUT_FILE_FLAG, options.outFile);
|
|
32
|
+
}
|
|
33
|
+
return args;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Headless model-surface documentation — emits a navigable Markdown reference of
|
|
38
|
+
* every AST node type (type index, cross-linked super/sub types, a reverse
|
|
39
|
+
* "referenced by" index, and a per-language grammar summary), suitable for
|
|
40
|
+
* publishing into adopter docs. Read-only: no workspace is built (the reflection is
|
|
41
|
+
* static once the head registers its language), so it takes no `<workspace>`.
|
|
42
|
+
* There is no `--json` — the machine-readable form is `hydranium-cli reflect --json`.
|
|
43
|
+
* The work runs in a spawned child that dynamic-imports the head's `createServices`
|
|
44
|
+
* module, keeping the binary language-agnostic.
|
|
45
|
+
*/
|
|
46
|
+
export function runModelDocs(options: ModelDocsCommandOptions): Promise<void> {
|
|
47
|
+
return runDriverChild(['--max-old-space-size=8192', DRIVER, ...buildModelDocsDriverArgs(options)], options);
|
|
48
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
exitWithUsage,
|
|
12
|
+
helpRequested,
|
|
13
|
+
parseFlagOptions,
|
|
14
|
+
parseServerSpawnOptions,
|
|
15
|
+
printHelp,
|
|
16
|
+
SERVER_SPAWN_VALUE_FLAGS,
|
|
17
|
+
type UsageError
|
|
18
|
+
} from './harness-args.js';
|
|
19
|
+
import { runProjects, type ProjectsCommandOptions } from './projects.js';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Every flag `projects` accepts. All three are consumed by the shared spawn
|
|
23
|
+
* parser; the command declares no options of its own beyond them.
|
|
24
|
+
*/
|
|
25
|
+
export const PROJECTS_FLAGS: readonly string[] = SERVER_SPAWN_VALUE_FLAGS;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The subset that takes a value, so `--help` in a value position reads as data.
|
|
29
|
+
* Every flag in this family takes one, so the two lists coincide.
|
|
30
|
+
*/
|
|
31
|
+
export const PROJECTS_VALUE_FLAGS: readonly string[] = PROJECTS_FLAGS;
|
|
32
|
+
|
|
33
|
+
/** The `--help` text, as data, held to {@link PROJECTS_FLAGS} by a test. */
|
|
34
|
+
export const PROJECTS_HELP: readonly string[] = [
|
|
35
|
+
'Usage: hydranium-cli projects --server "<cmd> [args...]" [--cwd <dir>] [--log-level <level>]',
|
|
36
|
+
'',
|
|
37
|
+
'List projects exposed by the data-server subprocess. Output is newline-delimited',
|
|
38
|
+
'JSON — one project envelope per line.',
|
|
39
|
+
'',
|
|
40
|
+
'Options:',
|
|
41
|
+
' --server "<cmd>" Command-line for the data-server subprocess (required).',
|
|
42
|
+
' --cwd <dir> Working directory for the spawned child. Default: cwd.',
|
|
43
|
+
' --log-level <lvl> Log threshold for the spawned server (off|error|warn|info|debug|trace).'
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
export function parseProjectsArgs(args: string[], onError: UsageError = exitWithUsage): ProjectsCommandOptions {
|
|
47
|
+
const { serverCommand, serverArgs, cwd, logLevel, extra } = parseServerSpawnOptions(args, 'projects', onError);
|
|
48
|
+
// `projects` declares no options beyond the shared ones, so nothing may
|
|
49
|
+
// survive the spawn parser. Draining `extra` against an empty set is what
|
|
50
|
+
// makes a typo an error here rather than a flag that silently did nothing —
|
|
51
|
+
// the siblings get this from the option list they pass.
|
|
52
|
+
parseFlagOptions(extra, 'projects', [], onError);
|
|
53
|
+
return { serverCommand, serverArgs, cwd, logLevel };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function runProjectsCommand(args: string[]): Promise<void> {
|
|
57
|
+
if (helpRequested(args, PROJECTS_VALUE_FLAGS)) {
|
|
58
|
+
printHelp(PROJECTS_HELP);
|
|
59
|
+
return Promise.resolve();
|
|
60
|
+
}
|
|
61
|
+
return runProjects(parseProjectsArgs(args));
|
|
62
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
import type { LogThreshold, Project, TransferElement } from '@hydranium/protocol';
|
|
11
|
+
import type { DataServerProtocol } from '@hydranium/protocol/data';
|
|
12
|
+
import { logLevelEnv } from '../log-level.js';
|
|
13
|
+
import { withDataServer } from '../spawn-data-server.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Options for the {@link runProjects} subcommand. `serverCommand` /
|
|
17
|
+
* `serverArgs` mirror what an adopter would pass to spawn the data-server
|
|
18
|
+
* subprocess.
|
|
19
|
+
*/
|
|
20
|
+
export interface ProjectsCommandOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Command-line tokens used to spawn the data-server child. Mirrors
|
|
23
|
+
* the `--server "<cmd> [args...]"` shape on the CLI surface — split
|
|
24
|
+
* into command + args here so callers don't have to handle quoting.
|
|
25
|
+
*/
|
|
26
|
+
readonly serverCommand: string;
|
|
27
|
+
readonly serverArgs?: readonly string[];
|
|
28
|
+
readonly cwd?: string;
|
|
29
|
+
/** Log threshold for the spawned server, set on its `HYDRANIUM_LOG_LEVEL` env. */
|
|
30
|
+
readonly logLevel?: LogThreshold;
|
|
31
|
+
/**
|
|
32
|
+
* Output sink. Default: `process.stdout.write`. Tests inject a
|
|
33
|
+
* capturing stub.
|
|
34
|
+
*/
|
|
35
|
+
readonly write?: (line: string) => void;
|
|
36
|
+
/**
|
|
37
|
+
* Test-only injection: skip the spawn + use this proxy directly. The
|
|
38
|
+
* production CLI never passes this; subcommand unit tests do.
|
|
39
|
+
*/
|
|
40
|
+
readonly __proxyForTest?: DataServerProtocol<TransferElement>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Print the projects exposed by the data-server as one JSON object per
|
|
45
|
+
* line (newline-delimited JSON). Each line is independently parseable —
|
|
46
|
+
* adopters piping the output into `jq` or another line-oriented tool
|
|
47
|
+
* don't need to wait for end-of-stream.
|
|
48
|
+
*/
|
|
49
|
+
export async function runProjects(options: ProjectsCommandOptions): Promise<void> {
|
|
50
|
+
const write = options.write ?? ((line: string) => process.stdout.write(line));
|
|
51
|
+
|
|
52
|
+
if (options.__proxyForTest) {
|
|
53
|
+
const projects = await options.__proxyForTest.getProjects();
|
|
54
|
+
writeProjects(projects, write);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
await withDataServer(
|
|
59
|
+
{
|
|
60
|
+
command: options.serverCommand,
|
|
61
|
+
args: options.serverArgs,
|
|
62
|
+
cwd: options.cwd,
|
|
63
|
+
env: options.logLevel ? logLevelEnv(options.logLevel) : undefined
|
|
64
|
+
},
|
|
65
|
+
async server => {
|
|
66
|
+
const projects = await server.getProjects();
|
|
67
|
+
writeProjects(projects, write);
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function writeProjects(projects: readonly Project[], write: (line: string) => void): void {
|
|
73
|
+
for (const project of projects) {
|
|
74
|
+
write(`${JSON.stringify(project)}\n`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
assertRequired,
|
|
12
|
+
exitWithUsage,
|
|
13
|
+
helpRequested,
|
|
14
|
+
parseFlagOptions,
|
|
15
|
+
parseServerSpawnOptions,
|
|
16
|
+
printHelp,
|
|
17
|
+
SERVER_SPAWN_VALUE_FLAGS,
|
|
18
|
+
type UsageError
|
|
19
|
+
} from './harness-args.js';
|
|
20
|
+
import { runQuery, type QueryCommandOptions } from './query.js';
|
|
21
|
+
|
|
22
|
+
const OWN_FLAGS = ['--uri'] as const;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Every flag `query` accepts, derived from its own list plus the shared spawn
|
|
26
|
+
* flags, so the list cannot claim a flag the parser would reject.
|
|
27
|
+
*/
|
|
28
|
+
export const QUERY_FLAGS: readonly string[] = [...SERVER_SPAWN_VALUE_FLAGS, ...OWN_FLAGS];
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The subset that takes a value, so `--help` in a value position reads as data.
|
|
32
|
+
* Every flag in this family takes one, so the two lists coincide.
|
|
33
|
+
*/
|
|
34
|
+
export const QUERY_VALUE_FLAGS: readonly string[] = QUERY_FLAGS;
|
|
35
|
+
|
|
36
|
+
/** The `--help` text, as data, held to {@link QUERY_FLAGS} by a test. */
|
|
37
|
+
export const QUERY_HELP: readonly string[] = [
|
|
38
|
+
'Usage: hydranium-cli query --server "<cmd> [args...]" --uri <uri> [--cwd <dir>] [--log-level <level>]',
|
|
39
|
+
'',
|
|
40
|
+
'Print the data-server document envelope for `<uri>` as a single JSON line.',
|
|
41
|
+
'',
|
|
42
|
+
'Options:',
|
|
43
|
+
' --server "<cmd>" Command-line for the data-server subprocess (required).',
|
|
44
|
+
' --uri <uri> Document URI (required).',
|
|
45
|
+
' --cwd <dir> Working directory for the spawned child. Default: cwd.',
|
|
46
|
+
' --log-level <lvl> Log threshold for the spawned server (off|error|warn|info|debug|trace).'
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
export function parseQueryArgs(args: string[], onError: UsageError = exitWithUsage): QueryCommandOptions {
|
|
50
|
+
const { serverCommand, serverArgs, cwd, logLevel, extra } = parseServerSpawnOptions(args, 'query', onError);
|
|
51
|
+
const { uri } = parseFlagOptions(extra, 'query', OWN_FLAGS, onError);
|
|
52
|
+
return { serverCommand, serverArgs, cwd, logLevel, uri: assertRequired(uri, '--uri', 'query', onError) };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function runQueryCommand(args: string[]): Promise<void> {
|
|
56
|
+
if (helpRequested(args, QUERY_VALUE_FLAGS)) {
|
|
57
|
+
printHelp(QUERY_HELP);
|
|
58
|
+
return Promise.resolve();
|
|
59
|
+
}
|
|
60
|
+
return runQuery(parseQueryArgs(args));
|
|
61
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
import type { LogThreshold, TransferElement } from '@hydranium/protocol';
|
|
11
|
+
import type { DataServerProtocol } from '@hydranium/protocol/data';
|
|
12
|
+
import { logLevelEnv } from '../log-level.js';
|
|
13
|
+
import { withDataServer } from '../spawn-data-server.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Options for the {@link runQuery} subcommand. Prints the data-server's
|
|
17
|
+
* current view of a document — invokes `getModelDocument({ uri })` on
|
|
18
|
+
* the spawned subprocess and writes the response envelope as a single
|
|
19
|
+
* JSON line. Adopters piping the output into `jq` get a complete record
|
|
20
|
+
* per invocation.
|
|
21
|
+
*
|
|
22
|
+
* Test-only `__proxyForTest` bypasses the spawn for unit tests; the
|
|
23
|
+
* production CLI never passes it.
|
|
24
|
+
*/
|
|
25
|
+
export interface QueryCommandOptions {
|
|
26
|
+
readonly serverCommand: string;
|
|
27
|
+
readonly serverArgs?: readonly string[];
|
|
28
|
+
readonly cwd?: string;
|
|
29
|
+
/** Log threshold for the spawned server, set on its `HYDRANIUM_LOG_LEVEL` env. */
|
|
30
|
+
readonly logLevel?: LogThreshold;
|
|
31
|
+
readonly uri: string;
|
|
32
|
+
readonly write?: (line: string) => void;
|
|
33
|
+
readonly __proxyForTest?: DataServerProtocol<TransferElement>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function runQuery(options: QueryCommandOptions): Promise<void> {
|
|
37
|
+
const write = options.write ?? ((line: string) => process.stdout.write(line));
|
|
38
|
+
|
|
39
|
+
if (options.__proxyForTest) {
|
|
40
|
+
// One-shot, unsubscribed read — request validation so the dumped envelope carries diagnostics.
|
|
41
|
+
const doc = await options.__proxyForTest.getModelDocument({ uri: options.uri, includeDiagnostics: true });
|
|
42
|
+
write(`${JSON.stringify(doc)}\n`);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
await withDataServer(
|
|
47
|
+
{
|
|
48
|
+
command: options.serverCommand,
|
|
49
|
+
args: options.serverArgs,
|
|
50
|
+
cwd: options.cwd,
|
|
51
|
+
env: options.logLevel ? logLevelEnv(options.logLevel) : undefined
|
|
52
|
+
},
|
|
53
|
+
async server => {
|
|
54
|
+
const doc = await server.getModelDocument({ uri: options.uri, includeDiagnostics: true });
|
|
55
|
+
write(`${JSON.stringify(doc)}\n`);
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
exitWithUsage,
|
|
12
|
+
helpRequested,
|
|
13
|
+
LOG_LEVEL_FLAG,
|
|
14
|
+
logLevelHelpLine,
|
|
15
|
+
logLevelOption,
|
|
16
|
+
parseHarnessArgs,
|
|
17
|
+
printHelp,
|
|
18
|
+
SERVICES_FLAG,
|
|
19
|
+
type UsageError
|
|
20
|
+
} from './harness-args.js';
|
|
21
|
+
import { OUT_FILE_FLAG } from './headless-harness.js';
|
|
22
|
+
import { runReflect, type ReflectCommandOptions } from './reflect.js';
|
|
23
|
+
|
|
24
|
+
const VALUE_FLAGS = [OUT_FILE_FLAG, LOG_LEVEL_FLAG] as const;
|
|
25
|
+
const BOOL_FLAGS = ['--json'] as const;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Every flag `reflect` accepts, derived from the sets the parser is handed so
|
|
29
|
+
* the list cannot claim a flag the parser would reject. `--services` is consumed
|
|
30
|
+
* by the shared harness parser itself, so it is in neither set.
|
|
31
|
+
*/
|
|
32
|
+
export const REFLECT_FLAGS: readonly string[] = [SERVICES_FLAG, ...VALUE_FLAGS, ...BOOL_FLAGS];
|
|
33
|
+
|
|
34
|
+
/** The subset that takes a value, so `--help` in a value position reads as data. */
|
|
35
|
+
export const REFLECT_VALUE_FLAGS: readonly string[] = [SERVICES_FLAG, ...VALUE_FLAGS];
|
|
36
|
+
|
|
37
|
+
/** The `--help` text, as data, held to {@link REFLECT_FLAGS} by a test. */
|
|
38
|
+
export const REFLECT_HELP: readonly string[] = [
|
|
39
|
+
'Usage: hydranium-cli reflect --services <module> [--json] [--out-file <file>] [--log-level <lvl>]',
|
|
40
|
+
'',
|
|
41
|
+
"Dump a head's grammar/AST reflection — the type hierarchy, each language's",
|
|
42
|
+
'terminals and entry rule, and every cross-reference target. Read-only: no',
|
|
43
|
+
'workspace is built (the reflection is static once the head registers its',
|
|
44
|
+
'language), so `reflect` takes no `<workspace>`. `<module>` is an ESM module',
|
|
45
|
+
'exporting a zero-arg `createServices(): { shared }` thunk. Needs @hydranium/core.',
|
|
46
|
+
'',
|
|
47
|
+
'Options:',
|
|
48
|
+
' --services <module> ESM module exporting `createServices(): { shared }` (required).',
|
|
49
|
+
' --json Emit the raw JSON reflection instead of the Markdown report.',
|
|
50
|
+
' --out-file <file> Write the report to this file instead of stdout. Written',
|
|
51
|
+
' only once the report exists, unlike a shell redirection,',
|
|
52
|
+
' which truncates the file before the head is even booted.',
|
|
53
|
+
logLevelHelpLine(22)
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
export function parseReflectArgs(args: string[], onError: UsageError = exitWithUsage): ReflectCommandOptions {
|
|
57
|
+
const { servicesModule, options } = parseHarnessArgs(args, 'reflect', VALUE_FLAGS, BOOL_FLAGS, {
|
|
58
|
+
requireWorkspace: false,
|
|
59
|
+
onError
|
|
60
|
+
});
|
|
61
|
+
return {
|
|
62
|
+
servicesModule,
|
|
63
|
+
json: options['--json'] === 'true',
|
|
64
|
+
outFile: options[OUT_FILE_FLAG],
|
|
65
|
+
logLevel: logLevelOption(options[LOG_LEVEL_FLAG])
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function runReflectCommand(args: string[]): Promise<void> {
|
|
70
|
+
if (helpRequested(args, REFLECT_VALUE_FLAGS)) {
|
|
71
|
+
printHelp(REFLECT_HELP);
|
|
72
|
+
return Promise.resolve();
|
|
73
|
+
}
|
|
74
|
+
return runReflect(parseReflectArgs(args));
|
|
75
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
* Spawned child of the `reflect` subcommand. Launched by the parent with
|
|
12
|
+
* `node <this> --services <module> [--json] [--out-file <file>]`. It
|
|
13
|
+
* dynamic-imports the head's services module, runs the framework's
|
|
14
|
+
* `reflectGrammar`, and delivers the report (markdown or `--json`) to stdout or
|
|
15
|
+
* the named file. Read-only — exits 0 unless booting the head throws. Never
|
|
16
|
+
* imported — only spawned.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { emitReport, loadHeadlessContext, OUT_FILE_FLAG } from './headless-harness.js';
|
|
20
|
+
import { formatReflectionReport } from './reflect-report.js';
|
|
21
|
+
|
|
22
|
+
interface DriverArgs {
|
|
23
|
+
servicesModule?: string;
|
|
24
|
+
json: boolean;
|
|
25
|
+
outFile?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function parseArgs(argv: string[]): DriverArgs {
|
|
29
|
+
const parsed: DriverArgs = { json: false };
|
|
30
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
31
|
+
const flag = argv[index];
|
|
32
|
+
const next = (): string => {
|
|
33
|
+
const value = argv[index + 1];
|
|
34
|
+
if (value === undefined) {
|
|
35
|
+
console.error(`Missing value for ${flag}`);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
index += 1;
|
|
39
|
+
return value;
|
|
40
|
+
};
|
|
41
|
+
switch (flag) {
|
|
42
|
+
case '--services':
|
|
43
|
+
parsed.servicesModule = next();
|
|
44
|
+
break;
|
|
45
|
+
case '--json':
|
|
46
|
+
parsed.json = true;
|
|
47
|
+
break;
|
|
48
|
+
case OUT_FILE_FLAG:
|
|
49
|
+
parsed.outFile = next();
|
|
50
|
+
break;
|
|
51
|
+
default:
|
|
52
|
+
console.error(`Unknown option: ${flag}`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return parsed;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function main(argv: string[]): Promise<number> {
|
|
60
|
+
const args = parseArgs(argv);
|
|
61
|
+
if (!args.servicesModule) {
|
|
62
|
+
console.error('reflect-driver: --services <module> is required.');
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
const { createServices, coreNode } = await loadHeadlessContext(args.servicesModule);
|
|
66
|
+
const result = coreNode.reflectGrammar({ createServices });
|
|
67
|
+
emitReport(formatReflectionReport(result, { json: args.json }), args.outFile);
|
|
68
|
+
return 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
main(process.argv.slice(2))
|
|
72
|
+
.then(code => process.exit(code))
|
|
73
|
+
.catch((error: unknown) => {
|
|
74
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
75
|
+
process.exit(1);
|
|
76
|
+
});
|