@hydranium/cli 1.0.0-next.42 → 1.0.0-next.420
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/README.md +125 -204
- package/bin/hydranium-cli.js +13 -0
- package/heap-analysis/heap/core/load.mjs +2 -2
- package/heap-analysis/heap/langium/langium.classifier.mjs +22 -11
- package/lib/cli-version.js.map +1 -1
- package/lib/cli.js +11 -2
- package/lib/cli.js.map +1 -1
- package/lib/commands/analyze-heap-args.d.ts.map +1 -1
- package/lib/commands/analyze-heap-args.js +1 -2
- package/lib/commands/analyze-heap-args.js.map +1 -1
- package/lib/commands/analyze-heap.d.ts +3 -0
- package/lib/commands/analyze-heap.d.ts.map +1 -1
- package/lib/commands/analyze-heap.js +2 -1
- package/lib/commands/analyze-heap.js.map +1 -1
- package/lib/commands/ast-ground-truth-args.d.ts +3 -2
- package/lib/commands/ast-ground-truth-args.d.ts.map +1 -1
- package/lib/commands/ast-ground-truth-args.js +12 -10
- package/lib/commands/ast-ground-truth-args.js.map +1 -1
- package/lib/commands/ast-ground-truth-driver.js +4 -2
- package/lib/commands/ast-ground-truth-driver.js.map +1 -1
- package/lib/commands/ast-ground-truth.js +1 -1
- package/lib/commands/ast-ground-truth.js.map +1 -1
- package/lib/commands/check-skills-args.d.ts +11 -0
- package/lib/commands/check-skills-args.d.ts.map +1 -0
- package/lib/commands/check-skills-args.js +71 -0
- package/lib/commands/check-skills-args.js.map +1 -0
- package/lib/commands/client-session.d.ts +17 -0
- package/lib/commands/client-session.d.ts.map +1 -0
- package/lib/commands/client-session.js +27 -0
- package/lib/commands/client-session.js.map +1 -0
- package/lib/commands/create-args.d.ts +17 -0
- package/lib/commands/create-args.d.ts.map +1 -0
- package/lib/commands/create-args.js +53 -0
- package/lib/commands/create-args.js.map +1 -0
- package/lib/commands/create.d.ts +38 -0
- package/lib/commands/create.d.ts.map +1 -0
- package/lib/commands/create.js +43 -0
- package/lib/commands/create.js.map +1 -0
- package/lib/commands/delete-args.d.ts +17 -0
- package/lib/commands/delete-args.d.ts.map +1 -0
- package/lib/commands/delete-args.js +49 -0
- package/lib/commands/delete-args.js.map +1 -0
- package/lib/commands/delete.d.ts +30 -0
- package/lib/commands/delete.d.ts.map +1 -0
- package/lib/commands/delete.js +32 -0
- package/lib/commands/delete.js.map +1 -0
- package/lib/commands/generate-ast-builder.d.ts +1 -1
- package/lib/commands/generate-ast-builder.d.ts.map +1 -1
- package/lib/commands/generate-ast-builder.js +2 -11
- package/lib/commands/generate-ast-builder.js.map +1 -1
- package/lib/commands/generate-languages.d.ts +35 -0
- package/lib/commands/generate-languages.d.ts.map +1 -0
- package/lib/commands/generate-languages.js +105 -0
- package/lib/commands/generate-languages.js.map +1 -0
- package/lib/commands/generate-transfer-builder.d.ts +33 -0
- package/lib/commands/generate-transfer-builder.d.ts.map +1 -0
- package/lib/commands/generate-transfer-builder.js +86 -0
- package/lib/commands/generate-transfer-builder.js.map +1 -0
- package/lib/commands/generate-transfer-model-args.d.ts.map +1 -1
- package/lib/commands/generate-transfer-model-args.js +20 -5
- package/lib/commands/generate-transfer-model-args.js.map +1 -1
- package/lib/commands/generate-transfer-model-config.d.ts.map +1 -1
- package/lib/commands/generate-transfer-model-config.js +8 -3
- package/lib/commands/generate-transfer-model-config.js.map +1 -1
- package/lib/commands/generate-transfer-model.d.ts +41 -9
- package/lib/commands/generate-transfer-model.d.ts.map +1 -1
- package/lib/commands/generate-transfer-model.js +225 -68
- package/lib/commands/generate-transfer-model.js.map +1 -1
- package/lib/commands/generated-module-path.d.ts +16 -0
- package/lib/commands/generated-module-path.d.ts.map +1 -0
- package/lib/commands/generated-module-path.js +21 -0
- package/lib/commands/generated-module-path.js.map +1 -0
- package/lib/commands/harness-args.d.ts +46 -43
- package/lib/commands/harness-args.d.ts.map +1 -1
- package/lib/commands/harness-args.js +170 -29
- package/lib/commands/harness-args.js.map +1 -1
- package/lib/commands/headless-harness.d.ts +37 -20
- package/lib/commands/headless-harness.d.ts.map +1 -1
- package/lib/commands/headless-harness.js +71 -19
- package/lib/commands/headless-harness.js.map +1 -1
- package/lib/commands/init-args.d.ts +5 -18
- package/lib/commands/init-args.d.ts.map +1 -1
- package/lib/commands/init-args.js +131 -35
- package/lib/commands/init-args.js.map +1 -1
- package/lib/commands/init-hosts.d.ts +24 -0
- package/lib/commands/init-hosts.d.ts.map +1 -0
- package/lib/commands/init-hosts.js +37 -0
- package/lib/commands/init-hosts.js.map +1 -0
- package/lib/commands/init-prompt.d.ts +3 -3
- package/lib/commands/init-prompt.d.ts.map +1 -1
- package/lib/commands/init-prompt.js +4 -5
- package/lib/commands/init-prompt.js.map +1 -1
- package/lib/commands/init-skills.d.ts +41 -0
- package/lib/commands/init-skills.d.ts.map +1 -0
- package/lib/commands/init-skills.js +110 -0
- package/lib/commands/init-skills.js.map +1 -0
- package/lib/commands/init-templates.d.ts +20 -14
- package/lib/commands/init-templates.d.ts.map +1 -1
- package/lib/commands/init-templates.js +3107 -759
- package/lib/commands/init-templates.js.map +1 -1
- package/lib/commands/init-wizard.d.ts +10 -0
- package/lib/commands/init-wizard.d.ts.map +1 -1
- package/lib/commands/init-wizard.js +76 -31
- package/lib/commands/init-wizard.js.map +1 -1
- package/lib/commands/init-workspace.d.ts +13 -11
- package/lib/commands/init-workspace.d.ts.map +1 -1
- package/lib/commands/init-workspace.js +21 -7
- package/lib/commands/init-workspace.js.map +1 -1
- package/lib/commands/init.d.ts +61 -24
- package/lib/commands/init.d.ts.map +1 -1
- package/lib/commands/init.js +244 -50
- package/lib/commands/init.js.map +1 -1
- package/lib/commands/kebab.d.ts +15 -0
- package/lib/commands/kebab.d.ts.map +1 -0
- package/lib/commands/kebab.js +17 -0
- package/lib/commands/kebab.js.map +1 -0
- package/lib/commands/lint-grammar-args.d.ts +3 -2
- package/lib/commands/lint-grammar-args.d.ts.map +1 -1
- package/lib/commands/lint-grammar-args.js +14 -11
- package/lib/commands/lint-grammar-args.js.map +1 -1
- package/lib/commands/lint-grammar-driver.js +2 -1
- package/lib/commands/lint-grammar-driver.js.map +1 -1
- package/lib/commands/lint-grammar-report.js.map +1 -1
- package/lib/commands/lint-grammar.js +1 -1
- package/lib/commands/lint-grammar.js.map +1 -1
- package/lib/commands/measure-memory-args.d.ts +3 -2
- package/lib/commands/measure-memory-args.d.ts.map +1 -1
- package/lib/commands/measure-memory-args.js +26 -14
- package/lib/commands/measure-memory-args.js.map +1 -1
- package/lib/commands/measure-memory-driver.js +5 -3
- package/lib/commands/measure-memory-driver.js.map +1 -1
- package/lib/commands/measure-memory.d.ts +1 -1
- package/lib/commands/measure-memory.d.ts.map +1 -1
- package/lib/commands/measure-memory.js +6 -3
- package/lib/commands/measure-memory.js.map +1 -1
- package/lib/commands/message-catalogue-args.d.ts +16 -0
- package/lib/commands/message-catalogue-args.d.ts.map +1 -0
- package/lib/commands/message-catalogue-args.js +53 -0
- package/lib/commands/message-catalogue-args.js.map +1 -0
- package/lib/commands/message-catalogue.d.ts +21 -0
- package/lib/commands/message-catalogue.d.ts.map +1 -0
- package/lib/commands/message-catalogue.js +48 -0
- package/lib/commands/message-catalogue.js.map +1 -0
- package/lib/commands/model-docs-args.d.ts +4 -3
- package/lib/commands/model-docs-args.d.ts.map +1 -1
- package/lib/commands/model-docs-args.js +13 -11
- package/lib/commands/model-docs-args.js.map +1 -1
- package/lib/commands/model-docs-driver.js +2 -1
- package/lib/commands/model-docs-driver.js.map +1 -1
- package/lib/commands/model-docs.js +1 -1
- package/lib/commands/model-docs.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 +6 -5
- package/lib/commands/query-args.js.map +1 -1
- package/lib/commands/query.d.ts +6 -5
- package/lib/commands/query.d.ts.map +1 -1
- package/lib/commands/query.js +5 -4
- package/lib/commands/query.js.map +1 -1
- package/lib/commands/reflect-args.d.ts +3 -2
- package/lib/commands/reflect-args.d.ts.map +1 -1
- package/lib/commands/reflect-args.js +14 -11
- package/lib/commands/reflect-args.js.map +1 -1
- package/lib/commands/reflect-driver.js +2 -1
- package/lib/commands/reflect-driver.js.map +1 -1
- package/lib/commands/reflect-report.js.map +1 -1
- package/lib/commands/reflect.js +1 -1
- package/lib/commands/reflect.js.map +1 -1
- package/lib/commands/save-args.d.ts.map +1 -1
- package/lib/commands/save-args.js +7 -4
- package/lib/commands/save-args.js.map +1 -1
- package/lib/commands/save.d.ts +8 -4
- package/lib/commands/save.d.ts.map +1 -1
- package/lib/commands/save.js +12 -23
- package/lib/commands/save.js.map +1 -1
- package/lib/commands/skills.d.ts +22 -0
- package/lib/commands/skills.d.ts.map +1 -0
- package/lib/commands/skills.js +129 -0
- package/lib/commands/skills.js.map +1 -0
- package/lib/commands/validate-args.d.ts +3 -2
- package/lib/commands/validate-args.d.ts.map +1 -1
- package/lib/commands/validate-args.js +14 -11
- package/lib/commands/validate-args.js.map +1 -1
- package/lib/commands/validate-driver.js +2 -1
- package/lib/commands/validate-driver.js.map +1 -1
- package/lib/commands/validate-report.js.map +1 -1
- package/lib/commands/validate.js +1 -1
- package/lib/commands/validate.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 +5 -2
- package/lib/commands/watch.js.map +1 -1
- package/lib/driver-heap.d.ts +52 -0
- package/lib/driver-heap.d.ts.map +1 -0
- package/lib/driver-heap.js +51 -0
- package/lib/driver-heap.js.map +1 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -0
- package/lib/index.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/lib/spawn-data-server.d.ts +9 -4
- package/lib/spawn-data-server.d.ts.map +1 -1
- package/lib/spawn-data-server.js +5 -4
- package/lib/spawn-data-server.js.map +1 -1
- package/lib/stderr-connection-logger.d.ts +12 -0
- package/lib/stderr-connection-logger.d.ts.map +1 -0
- package/lib/stderr-connection-logger.js +16 -0
- package/lib/stderr-connection-logger.js.map +1 -0
- package/lib/testing/echo-server.js +9 -1
- package/lib/testing/echo-server.js.map +1 -1
- package/package.json +99 -41
- package/src/cli.ts +11 -2
- package/src/commands/analyze-heap-args.ts +1 -2
- package/src/commands/analyze-heap.ts +4 -1
- package/src/commands/ast-ground-truth-args.ts +16 -13
- package/src/commands/ast-ground-truth-driver.ts +4 -2
- package/src/commands/ast-ground-truth.ts +1 -1
- package/src/commands/check-skills-args.ts +70 -0
- package/src/commands/client-session.ts +33 -0
- package/src/commands/create-args.ts +68 -0
- package/src/commands/create.ts +83 -0
- package/src/commands/delete-args.ts +64 -0
- package/src/commands/delete.ts +61 -0
- package/src/commands/generate-ast-builder.ts +2 -12
- package/src/commands/generate-languages.ts +132 -0
- package/src/commands/generate-transfer-builder.ts +101 -0
- package/src/commands/generate-transfer-model-args.ts +20 -5
- package/src/commands/generate-transfer-model-config.ts +8 -3
- package/src/commands/generate-transfer-model.ts +297 -66
- package/src/commands/generated-module-path.ts +22 -0
- package/src/commands/harness-args.ts +163 -33
- package/src/commands/headless-harness.ts +89 -28
- package/src/commands/init-args.ts +135 -35
- package/src/commands/init-hosts.ts +51 -0
- package/src/commands/init-prompt.ts +5 -6
- package/src/commands/init-skills.ts +129 -0
- package/src/commands/init-templates.ts +3300 -785
- package/src/commands/init-wizard.ts +97 -32
- package/src/commands/init-workspace.ts +31 -16
- package/src/commands/init.ts +311 -65
- package/src/commands/kebab.ts +17 -0
- package/src/commands/lint-grammar-args.ts +18 -14
- package/src/commands/lint-grammar-driver.ts +2 -1
- package/src/commands/lint-grammar.ts +1 -1
- package/src/commands/measure-memory-args.ts +31 -17
- package/src/commands/measure-memory-driver.ts +5 -3
- package/src/commands/measure-memory.ts +6 -3
- package/src/commands/message-catalogue-args.ts +55 -0
- package/src/commands/message-catalogue.ts +56 -0
- package/src/commands/model-docs-args.ts +17 -14
- package/src/commands/model-docs-driver.ts +2 -1
- package/src/commands/model-docs.ts +1 -1
- package/src/commands/projects-args.ts +5 -4
- package/src/commands/projects.ts +4 -6
- package/src/commands/query-args.ts +6 -4
- package/src/commands/query.ts +9 -9
- package/src/commands/reflect-args.ts +18 -14
- package/src/commands/reflect-driver.ts +2 -1
- package/src/commands/reflect.ts +1 -1
- package/src/commands/save-args.ts +7 -3
- package/src/commands/save.ts +24 -27
- package/src/commands/skills.ts +146 -0
- package/src/commands/validate-args.ts +18 -14
- package/src/commands/validate-driver.ts +2 -1
- package/src/commands/validate.ts +1 -1
- package/src/commands/watch-args.ts +6 -3
- package/src/commands/watch.ts +7 -6
- package/src/driver-heap.ts +65 -0
- package/src/index.ts +3 -0
- package/src/log-level.ts +42 -14
- package/src/spawn-data-server.ts +17 -5
- package/src/stderr-connection-logger.ts +18 -0
- package/src/testing/echo-server.ts +11 -2
- package/templates/client/gitignore +3 -0
- package/templates/client/src/__GRAMMAR_ID__-diagram-module.ts +82 -0
- package/templates/editorconfig +15 -0
- package/templates/gitignore +1 -0
- package/templates/host-browser/esbuild.mjs +82 -0
- package/templates/host-browser/gitignore +4 -0
- package/templates/host-browser/hydranium-mark.svg +40 -0
- package/templates/host-browser/index.html +287 -0
- package/templates/host-browser/playwright.config.mts +24 -0
- package/templates/host-browser/src/data-status.ts +15 -0
- package/templates/host-browser/src/diagram-save.ts +60 -0
- package/templates/host-browser/src/diagram.ts +121 -0
- package/templates/host-browser/src/editor.ts +116 -0
- package/templates/host-browser/src/log.ts +14 -0
- package/templates/host-browser/src/monaco-editor-core-worker.d.ts +7 -0
- package/templates/host-browser/src/monaco-editor-worker.ts +10 -0
- package/templates/host-browser/src/posix-path-shim.ts +23 -0
- package/templates/host-browser/src/shown-file.ts +61 -0
- package/templates/host-browser/src/workspace.ts +5 -0
- package/templates/host-browser/tsconfig.e2e.json +16 -0
- package/templates/host-browser/tsconfig.page.json +16 -0
- package/templates/host-browser/tsconfig.worker.json +16 -0
- package/templates/host-theia-browser/esbuild.mjs +24 -0
- package/templates/host-theia-browser/gitignore +9 -0
- package/templates/host-theia-browser/playwright.config.mts +42 -0
- package/templates/host-theia-browser/start.mjs +50 -0
- package/templates/host-theia-browser/tsconfig.e2e.json +16 -0
- package/templates/host-theia-electron/esbuild.mjs +27 -0
- package/templates/host-theia-electron/gitignore +8 -0
- package/templates/host-theia-electron/playwright.config.mts +12 -0
- package/templates/host-theia-electron/start.mjs +47 -0
- package/templates/host-theia-electron/tsconfig.e2e.json +16 -0
- package/templates/host-vscode/esbuild.mjs +38 -0
- package/templates/host-vscode/gitignore +7 -0
- package/templates/host-vscode/playwright.config.mts +14 -0
- package/templates/host-vscode/src/__GRAMMAR_ID__-diagram-editor.ts +19 -0
- package/templates/host-vscode/src/show-model-command.ts +24 -0
- package/templates/host-vscode/src/webview/__GRAMMAR_ID__-diagram.ts +18 -0
- package/templates/host-vscode/src/webview/diagram.css +40 -0
- package/templates/host-vscode/start.mjs +22 -0
- package/templates/host-vscode/test/content-server.cjs +40 -0
- package/templates/host-vscode/test/install-vscode.mts +21 -0
- package/templates/host-vscode/test/virtual-documents.spec.mts +28 -0
- package/templates/host-vscode/test/vscode-version.mts +10 -0
- package/templates/host-vscode/tsconfig.e2e.json +17 -0
- package/templates/host-vscode/tsconfig.json +19 -0
- package/templates/host-vscode/tsconfig.webview.json +17 -0
- package/templates/oxlintrc.json +4 -0
- package/templates/protocol/gitignore +3 -0
- package/templates/protocol/src/__GRAMMAR_ID__-diagram-types.ts +47 -0
- package/templates/server/gitignore +6 -0
- package/templates/server/src/glsp/__GRAMMAR_ID__/create-node-operation-handler.ts +62 -0
- package/templates/server/src/glsp/__GRAMMAR_ID__/delete-operation-handler.ts +63 -0
- package/templates/server/src/glsp/__GRAMMAR_ID__/gmodel-factory.ts +144 -0
- package/templates/server/src/glsp/__GRAMMAR_ID__/state.ts +18 -0
- package/templates/server/src/glsp/__GRAMMAR_ID__/storage.ts +16 -0
- package/templates/server/src/glsp/__GRAMMAR_ID__/submission-handler.ts +24 -0
- package/templates/server/src/grammar/common.langium +12 -0
- package/templates/server/src/language-server/__GRAMMAR_ID__-semantic-tokens.ts +10 -0
- package/templates/server/src/language-server/__GRAMMAR_ID__-serializer.ts +53 -0
- package/templates/server/src/language-server/__GRAMMAR_ID__-validation.ts +30 -0
- package/templates/server/src/language-server/ast.ts +23 -0
- package/templates/server/src/node/services.ts +16 -0
- package/templates/server/test/__GRAMMAR_ID__-diagram.test.ts +403 -0
- package/templates/server/test/__GRAMMAR_ID__-semantic-tokens.test.ts +34 -0
- package/templates/server/test/tsconfig.json +4 -0
- package/templates/server/tsconfig.test.json +10 -0
- package/templates/server/vitest.config.ts +14 -0
- package/templates/theia/gitignore +3 -0
- package/templates/theia/src/browser/__GRAMMAR_ID__-diagram-frontend-module.ts +65 -0
- package/templates/theia/src/browser/__PROJECT_ID__-connection-preload-module.ts +15 -0
- package/templates/theia/src/browser/__PROJECT_ID__-data-connection.ts +15 -0
- package/templates/theia/src/browser/__PROJECT_ID__-data-port.ts +11 -0
- package/templates/theia/src/browser/__PROJECT_ID__-glsp-client-contribution.ts +13 -0
- package/templates/theia/src/browser/__PROJECT_ID__-show-model-command.ts +51 -0
- package/templates/theia/src/common/__PROJECT_ID__-glsp-contribution.ts +3 -0
- package/templates/theia/src/node/__PROJECT_ID__-connection-backend-module.ts +12 -0
- package/templates/theia/src/node/__PROJECT_ID__-data-backend-module.ts +15 -0
- package/templates/theia/src/node/__PROJECT_ID__-glsp-backend-module.ts +16 -0
- package/templates/theia/style/diagram.css +20 -0
- package/templates/theia/tsconfig.json +24 -0
- package/templates/vscode-servers/gitignore +4 -0
- package/templates/vscode-servers/tsconfig.json +19 -0
- package/templates/workspace/nodes.__EXTENSION__ +5 -0
- package/templates/workspace/references.__EXTENSION__ +2 -0
|
@@ -32,6 +32,28 @@ export interface GenerateTransferModelOptions {
|
|
|
32
32
|
* and not the other has the two disagreeing about the same grammar.
|
|
33
33
|
*/
|
|
34
34
|
astBuilderFile?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Destination for a generated test module binding `makeTransferNodeBuilder`
|
|
37
|
+
* from `@hydranium/protocol/testing` to the transfer model, as `transferNode`,
|
|
38
|
+
* the way {@link astBuilderFile} binds `astNode`. Belongs in a test tree:
|
|
39
|
+
* production code has no use for a builder that fills grammar defaults.
|
|
40
|
+
*
|
|
41
|
+
* When it sits in another package than {@link outFile}, it imports the model
|
|
42
|
+
* by that package's name, so the package's main entry must re-export the
|
|
43
|
+
* generated model, and with it each type's constant: an interface the entry
|
|
44
|
+
* narrows hides the generated constant of the same name unless the entry
|
|
45
|
+
* re-exports that constant too, and the builder keeps the generated
|
|
46
|
+
* interface unless the entry declares `<LanguageId>TransferTypes` with the
|
|
47
|
+
* narrowed entry.
|
|
48
|
+
*/
|
|
49
|
+
transferBuilderFile?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Destination for a generated module stating each grammar's language id and
|
|
52
|
+
* file extensions, read from the `module.ts` beside {@link astFile}. Lets a
|
|
53
|
+
* package without Langium, such as one shared with clients, name the ids
|
|
54
|
+
* without copying them by hand.
|
|
55
|
+
*/
|
|
56
|
+
languagesFile?: string;
|
|
35
57
|
/** Name used for the base element type in the output. Defaults to `TransferElement`. */
|
|
36
58
|
elementTypeName?: string;
|
|
37
59
|
/** Name used for the terminal-patterns const in the output. Defaults to `ModelTerminals`. */
|
|
@@ -63,10 +85,18 @@ export interface GenerateTransferModelOptions {
|
|
|
63
85
|
regenCommand?: string;
|
|
64
86
|
}
|
|
65
87
|
/**
|
|
66
|
-
* Generate once, then re-generate whenever
|
|
88
|
+
* Generate once, then re-generate whenever an input file changes (the AST, the
|
|
89
|
+
* augmentation file, and `module.ts` when a languages file is written),
|
|
67
90
|
* until `signal` aborts (the CLI wires this to SIGINT). Regeneration is debounced
|
|
68
91
|
* so an editor's multi-event save fires a single run, and a generation error is
|
|
69
92
|
* logged without tearing down the watch. Resolves when the signal aborts.
|
|
93
|
+
* An already-aborted signal resolves immediately without generating or watching.
|
|
94
|
+
*
|
|
95
|
+
* Polled by path rather than watched through OS events: langium-cli removes
|
|
96
|
+
* its output directory and writes the files back, which an event watch, Node's
|
|
97
|
+
* and chokidar's alike, stops hearing after the first run. A run that finds an
|
|
98
|
+
* input missing is skipped, since the file's return is a change of its own, so
|
|
99
|
+
* nothing generates from a half-written output.
|
|
70
100
|
*/
|
|
71
101
|
export declare function watchTransferModel(options: GenerateTransferModelOptions, context: {
|
|
72
102
|
signal: AbortSignal;
|
|
@@ -94,10 +124,11 @@ export declare function parseTerminals(astSource: SourceFile, terminalsSourceNam
|
|
|
94
124
|
}>;
|
|
95
125
|
/**
|
|
96
126
|
* Type-guard expression (against an `item: unknown`) for one member of a non-literal type-alias
|
|
97
|
-
* union.
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* `
|
|
127
|
+
* union. A string literal is compared as written, so the escapes Langium wrote stay valid.
|
|
128
|
+
* Covers the full set of Langium data-type primitives. The `typeof`-checkable ones
|
|
129
|
+
* (`string`/`number`/`boolean`/`bigint`) use `typeof`, and `Date`, the one Langium primitive
|
|
130
|
+
* `typeof` reports as `'object'`, uses `instanceof`. Any other member is a named type whose
|
|
131
|
+
* generated `is<Member>` guard is called.
|
|
101
132
|
*/
|
|
102
133
|
export declare function typeAliasMemberGuard(member: string): string;
|
|
103
134
|
/**
|
|
@@ -110,10 +141,11 @@ export declare function typeAliasMemberGuard(member: string): string;
|
|
|
110
141
|
* name.
|
|
111
142
|
*
|
|
112
143
|
* Each referenced member is inlined and de-duplicated, preserving first-seen
|
|
113
|
-
* order
|
|
114
|
-
* namespace-qualified reference (the
|
|
115
|
-
*
|
|
116
|
-
* emitting a
|
|
144
|
+
* order; when no member holds a literal, the union is `never`. The definition
|
|
145
|
+
* is returned UNCHANGED when it holds no namespace-qualified reference (the
|
|
146
|
+
* single-language case) or when any referenced member is not a pure
|
|
147
|
+
* string-literal union — passing it through rather than emitting a
|
|
148
|
+
* half-resolved union.
|
|
117
149
|
*/
|
|
118
150
|
export declare function resolveNamespacedStringUnion(astSource: SourceFile, definition: string): string;
|
|
119
151
|
//# sourceMappingURL=generate-transfer-model.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-transfer-model.d.ts","sourceRoot":"","sources":["../../src/commands/generate-transfer-model.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;
|
|
1
|
+
{"version":3,"file":"generate-transfer-model.d.ts","sourceRoot":"","sources":["../../src/commands/generate-transfer-model.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAOlF,OAAO,EAQJ,KAAK,UAAU,EACjB,MAAM,UAAU,CAAC;AAGlB;;;;;;;;GAQG;AACH,MAAM,WAAW,4BAA4B;IAC1C,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,gBAAgB,EAAE,MAAM,CAAC;IACzB,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;;;;;;;;OAaG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wFAAwF;IACxF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,6FAA6F;IAC7F,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;;OAQG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACxB;AAqCD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,4BAA4B,EAAE,OAAO,EAAE;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAyDzH;AAOD,wCAAwC;AACxC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,4BAA4B,GAAG,IAAI,CAsSjF;AA2LD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAC3B,SAAS,EAAE,UAAU,EACrB,mBAAmB,EAAE,MAAM,EAC3B,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GAC1B,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CA8B1C;AAsFD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAe3D;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAqB9F"}
|
|
@@ -6,10 +6,13 @@
|
|
|
6
6
|
*
|
|
7
7
|
* SPDX-License-Identifier: MIT
|
|
8
8
|
********************************************************************************/
|
|
9
|
-
import * as fs from 'fs';
|
|
9
|
+
import * as fs from 'node:fs';
|
|
10
10
|
import * as path from 'path';
|
|
11
11
|
import { emitAstBuilder } from './generate-ast-builder.js';
|
|
12
|
-
import {
|
|
12
|
+
import { emitLanguages } from './generate-languages.js';
|
|
13
|
+
import { emitTransferBuilder } from './generate-transfer-builder.js';
|
|
14
|
+
import { Project, SyntaxKind, ts } from 'ts-morph';
|
|
15
|
+
import { describeError } from '@hydranium/protocol';
|
|
13
16
|
const DEFAULT_ELEMENT_TYPE_NAME = 'TransferElement';
|
|
14
17
|
const DEFAULT_TERMINALS_NAME = 'ModelTerminals';
|
|
15
18
|
const DEFAULT_REGEN_COMMAND = 'Run the hydranium-cli generate-transfer-model command to regenerate.';
|
|
@@ -19,35 +22,68 @@ const DEFAULT_REGEN_COMMAND = 'Run the hydranium-cli generate-transfer-model com
|
|
|
19
22
|
* `<LanguageId>Terminals` const then spreads these.
|
|
20
23
|
*/
|
|
21
24
|
const NAMESPACED_TERMINALS_NAME = 'Terminals';
|
|
25
|
+
/** How often `--watch` polls its input files. */
|
|
26
|
+
const WATCH_POLL_INTERVAL_MS = 250;
|
|
27
|
+
const WATCH_DEBOUNCE_MS = 150;
|
|
22
28
|
/**
|
|
23
|
-
* Generate once, then re-generate whenever
|
|
29
|
+
* Generate once, then re-generate whenever an input file changes (the AST, the
|
|
30
|
+
* augmentation file, and `module.ts` when a languages file is written),
|
|
24
31
|
* until `signal` aborts (the CLI wires this to SIGINT). Regeneration is debounced
|
|
25
32
|
* so an editor's multi-event save fires a single run, and a generation error is
|
|
26
33
|
* logged without tearing down the watch. Resolves when the signal aborts.
|
|
34
|
+
* An already-aborted signal resolves immediately without generating or watching.
|
|
35
|
+
*
|
|
36
|
+
* Polled by path rather than watched through OS events: langium-cli removes
|
|
37
|
+
* its output directory and writes the files back, which an event watch, Node's
|
|
38
|
+
* and chokidar's alike, stops hearing after the first run. A run that finds an
|
|
39
|
+
* input missing is skipped, since the file's return is a change of its own, so
|
|
40
|
+
* nothing generates from a half-written output.
|
|
27
41
|
*/
|
|
28
42
|
export function watchTransferModel(options, context) {
|
|
43
|
+
if (context.signal.aborted) {
|
|
44
|
+
return Promise.resolve();
|
|
45
|
+
}
|
|
29
46
|
const runSafely = () => {
|
|
30
47
|
try {
|
|
31
48
|
generateTransferModel(options);
|
|
32
49
|
}
|
|
33
50
|
catch (error) {
|
|
34
|
-
console.error(`Transfer-model generation failed: ${
|
|
51
|
+
console.error(`Transfer-model generation failed: ${describeError(error)}`);
|
|
35
52
|
}
|
|
36
53
|
};
|
|
37
54
|
runSafely();
|
|
38
|
-
|
|
55
|
+
// A grammar's language id and extensions reach only `module.ts`, so the
|
|
56
|
+
// languages module would go stale without it.
|
|
57
|
+
const watched = [
|
|
58
|
+
options.astFile,
|
|
59
|
+
options.augmentationFile,
|
|
60
|
+
...(options.languagesFile !== undefined ? [languagesModuleFile(options)] : [])
|
|
61
|
+
];
|
|
39
62
|
let timer;
|
|
40
|
-
const
|
|
63
|
+
const rerun = () => {
|
|
64
|
+
if (watched.every(file => fs.existsSync(file))) {
|
|
65
|
+
runSafely();
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
// The poll reports every stat it takes; a replaced file has a new inode, a
|
|
69
|
+
// rewritten one a new mtime, a removed or returning one zeroed stats.
|
|
70
|
+
const onPoll = (current, previous) => {
|
|
71
|
+
if (current.ino === previous.ino && current.mtimeMs === previous.mtimeMs && current.size === previous.size) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
41
74
|
if (timer) {
|
|
42
75
|
clearTimeout(timer);
|
|
43
76
|
}
|
|
44
|
-
timer = setTimeout(
|
|
77
|
+
timer = setTimeout(rerun, WATCH_DEBOUNCE_MS);
|
|
45
78
|
};
|
|
46
|
-
const
|
|
79
|
+
for (const file of watched) {
|
|
80
|
+
fs.watchFile(file, { interval: WATCH_POLL_INTERVAL_MS }, onPoll);
|
|
81
|
+
}
|
|
82
|
+
console.log(`Watching ${watched.join(', ')} for changes. Press Ctrl-C to stop.`);
|
|
47
83
|
return new Promise(resolve => {
|
|
48
84
|
context.signal.addEventListener('abort', () => {
|
|
49
|
-
for (const
|
|
50
|
-
|
|
85
|
+
for (const file of watched) {
|
|
86
|
+
fs.unwatchFile(file, onPoll);
|
|
51
87
|
}
|
|
52
88
|
if (timer) {
|
|
53
89
|
clearTimeout(timer);
|
|
@@ -56,6 +92,10 @@ export function watchTransferModel(options, context) {
|
|
|
56
92
|
}, { once: true });
|
|
57
93
|
});
|
|
58
94
|
}
|
|
95
|
+
/** The langium-cli `module.ts` beside the AST file, which the languages module is read from. */
|
|
96
|
+
function languagesModuleFile(options) {
|
|
97
|
+
return path.join(path.dirname(options.astFile), 'module.ts');
|
|
98
|
+
}
|
|
59
99
|
/** Run the transfer-model generator. */
|
|
60
100
|
export function generateTransferModel(options) {
|
|
61
101
|
const elementTypeName = options.elementTypeName ?? DEFAULT_ELEMENT_TYPE_NAME;
|
|
@@ -93,9 +133,13 @@ export function generateTransferModel(options) {
|
|
|
93
133
|
if (skipTypeAliases.has(name)) {
|
|
94
134
|
continue;
|
|
95
135
|
}
|
|
96
|
-
const
|
|
97
|
-
if (
|
|
98
|
-
typeAliases.push({
|
|
136
|
+
const typeNode = alias.getTypeNode();
|
|
137
|
+
if (typeNode) {
|
|
138
|
+
typeAliases.push({
|
|
139
|
+
name,
|
|
140
|
+
definition: mapType(resolveNamespacedStringUnion(astSource, typeNode.getText()), elementTypeName),
|
|
141
|
+
members: unionMembers(typeNode).map(member => mapType(member, elementTypeName))
|
|
142
|
+
});
|
|
99
143
|
}
|
|
100
144
|
}
|
|
101
145
|
// 5. Parse augmentations from the consumer's `declare module` blocks.
|
|
@@ -109,7 +153,6 @@ export function generateTransferModel(options) {
|
|
|
109
153
|
// 7. Classify interfaces: abstract (union $type) vs concrete (literal $type).
|
|
110
154
|
const interfaceNames = [];
|
|
111
155
|
const abstractTypes = new Set();
|
|
112
|
-
const concreteTypes = new Set();
|
|
113
156
|
for (const name of Object.keys(reflectionTypes)) {
|
|
114
157
|
const iface = astInterfaces.get(name);
|
|
115
158
|
if (!iface) {
|
|
@@ -121,9 +164,6 @@ export function generateTransferModel(options) {
|
|
|
121
164
|
if (typeText.includes('|')) {
|
|
122
165
|
abstractTypes.add(name);
|
|
123
166
|
}
|
|
124
|
-
else {
|
|
125
|
-
concreteTypes.add(name);
|
|
126
|
-
}
|
|
127
167
|
}
|
|
128
168
|
// 8. Resolve extends-chains. Filter out type-alias supertypes (Langium reflection
|
|
129
169
|
// includes them for the grammar hierarchy but TypeScript can't extend a union
|
|
@@ -158,12 +198,13 @@ export function generateTransferModel(options) {
|
|
|
158
198
|
out.push(' readonly $type: string;');
|
|
159
199
|
out.push('}');
|
|
160
200
|
out.push('');
|
|
161
|
-
//
|
|
201
|
+
// Langium's per-type constant under the same name, for every reflection type
|
|
202
|
+
// the model emits, abstract ones and unions included.
|
|
203
|
+
const reflectedTypes = Object.keys(reflectionTypes).filter(name => astInterfaces.has(name) || typeAliasNames.has(name));
|
|
162
204
|
out.push('// --- Type Constants ---');
|
|
163
|
-
for (const name of
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
205
|
+
for (const name of reflectedTypes) {
|
|
206
|
+
const keys = reflectionTypes[name].properties.map(property => `${property.name}: '${property.name}'`);
|
|
207
|
+
out.push(`export const ${name} = { ${[`$type: '${name}'`, ...keys].join(', ')} } as const;`);
|
|
167
208
|
}
|
|
168
209
|
out.push('');
|
|
169
210
|
// Type aliases.
|
|
@@ -173,7 +214,7 @@ export function generateTransferModel(options) {
|
|
|
173
214
|
out.push(`export type ${name} = ${definition};`);
|
|
174
215
|
const literals = extractStringLiterals(definition);
|
|
175
216
|
if (literals) {
|
|
176
|
-
const items = literals.map(
|
|
217
|
+
const items = literals.map(quote).join(', ');
|
|
177
218
|
out.push(`export const ${name}Values = [${items}] as const;`);
|
|
178
219
|
}
|
|
179
220
|
}
|
|
@@ -201,7 +242,7 @@ export function generateTransferModel(options) {
|
|
|
201
242
|
out.push(` readonly $type: ${typeValue};`);
|
|
202
243
|
}
|
|
203
244
|
else {
|
|
204
|
-
out.push(` readonly $type:
|
|
245
|
+
out.push(` readonly $type: '${name}';`);
|
|
205
246
|
}
|
|
206
247
|
const emitted = new Set(['$type']);
|
|
207
248
|
for (const prop of iface.getProperties()) {
|
|
@@ -257,24 +298,24 @@ export function generateTransferModel(options) {
|
|
|
257
298
|
out.push(` return is${elementTypeName}(item) && (${checks});`);
|
|
258
299
|
}
|
|
259
300
|
else {
|
|
260
|
-
out.push(` return is${elementTypeName}(item) && item.$type === ${name}
|
|
301
|
+
out.push(` return is${elementTypeName}(item) && item.$type === '${name}';`);
|
|
261
302
|
}
|
|
262
303
|
out.push('}');
|
|
263
304
|
out.push('');
|
|
264
305
|
}
|
|
265
|
-
for (const { name, definition } of typeAliases) {
|
|
306
|
+
for (const { name, definition, members } of typeAliases) {
|
|
266
307
|
const literals = extractStringLiterals(definition);
|
|
267
308
|
if (literals) {
|
|
268
|
-
const checks = literals.map(l => `item ===
|
|
309
|
+
const checks = literals.map(l => `item === ${quote(l)}`).join(' || ') || 'false';
|
|
269
310
|
out.push(`export function is${name}(item: unknown): item is ${name} {`);
|
|
270
311
|
out.push(` return ${checks};`);
|
|
271
312
|
out.push('}');
|
|
272
313
|
out.push('');
|
|
273
314
|
}
|
|
274
315
|
else {
|
|
275
|
-
// Union (or single) of named members
|
|
276
|
-
//
|
|
277
|
-
|
|
316
|
+
// Union (or single) of named members, Langium data-type primitives and the string
|
|
317
|
+
// literals a data-type rule's keywords add beside them. Each member maps to its own
|
|
318
|
+
// check via typeAliasMemberGuard.
|
|
278
319
|
const checks = members.map(typeAliasMemberGuard).join(' || ');
|
|
279
320
|
out.push(`export function is${name}(item: unknown): item is ${name} {`);
|
|
280
321
|
out.push(` return ${checks};`);
|
|
@@ -282,6 +323,8 @@ export function generateTransferModel(options) {
|
|
|
282
323
|
out.push('');
|
|
283
324
|
}
|
|
284
325
|
}
|
|
326
|
+
out.push(...renderTypeMap(languageId, reflectedTypes));
|
|
327
|
+
out.push(...renderReflection(languageId, elementTypeName, reflectedTypes, reflectionTypes));
|
|
285
328
|
// Coverage check: warn on reflection types not emitted.
|
|
286
329
|
const processedSet = new Set(sorted);
|
|
287
330
|
for (const name of Object.keys(reflectionTypes)) {
|
|
@@ -310,6 +353,73 @@ export function generateTransferModel(options) {
|
|
|
310
353
|
console.warn(`Warning: no '${languageId}AstType' alias in ${options.astFile}; skipping the AST builder.`);
|
|
311
354
|
}
|
|
312
355
|
}
|
|
356
|
+
if (options.transferBuilderFile !== undefined) {
|
|
357
|
+
emitTransferBuilder({ outFile: options.transferBuilderFile, transferModelFile: options.outFile, languageId, regenCommand });
|
|
358
|
+
}
|
|
359
|
+
if (options.languagesFile !== undefined) {
|
|
360
|
+
const moduleSource = project.addSourceFileAtPath(languagesModuleFile(options));
|
|
361
|
+
emitLanguages(moduleSource, { outFile: options.languagesFile, regenCommand });
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* `<LanguageId>TransferTypes`, each `$type` to its interface, so an encoder or a
|
|
366
|
+
* builder can type a node by the grammar the way Langium's `<LanguageId>AstType`
|
|
367
|
+
* does on the AST side.
|
|
368
|
+
*/
|
|
369
|
+
function renderTypeMap(languageId, types) {
|
|
370
|
+
return ['// --- Type Map ---', `export type ${languageId}TransferTypes = {`, ...types.map(name => ` ${name}: ${name};`), '};', ''];
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* `<LanguageId>TransferReflection`, the grammar metadata with the method names
|
|
374
|
+
* Langium's `AstReflection` uses. A plain object with no framework import, so
|
|
375
|
+
* the file stays self-contained, and nothing references it but its importers:
|
|
376
|
+
* the `is` guards test `$type` directly, so a guard never pulls it in. `types`
|
|
377
|
+
* keeps its literal type, which is how the transfer-node builder tells which
|
|
378
|
+
* fields the grammar defaults.
|
|
379
|
+
*/
|
|
380
|
+
function renderReflection(languageId, elementTypeName, types, reflectionTypes) {
|
|
381
|
+
const metaData = `${languageId}TransferTypeMetaData`;
|
|
382
|
+
// Index signatures, not `Readonly<Record<…>>`: a grammar type may be named
|
|
383
|
+
// `Record` or `Readonly`, which Langium does not reserve as it does `Object`.
|
|
384
|
+
const out = [
|
|
385
|
+
'// --- Reflection ---',
|
|
386
|
+
`export interface ${metaData} {`,
|
|
387
|
+
' readonly name: string;',
|
|
388
|
+
' readonly properties: {',
|
|
389
|
+
' readonly [name: string]: { readonly name: string; readonly defaultValue?: unknown; readonly optional?: boolean; readonly referenceType?: string };',
|
|
390
|
+
' };',
|
|
391
|
+
' readonly superTypes: readonly string[];',
|
|
392
|
+
'}',
|
|
393
|
+
'',
|
|
394
|
+
`export const ${languageId}TransferReflection = {`,
|
|
395
|
+
' types: {'
|
|
396
|
+
];
|
|
397
|
+
for (const name of types) {
|
|
398
|
+
const { properties, superTypes } = reflectionTypes[name];
|
|
399
|
+
const propertyEntries = properties.map(property => {
|
|
400
|
+
const fields = [`name: '${property.name}'`];
|
|
401
|
+
if (property.defaultValue !== undefined) {
|
|
402
|
+
fields.push(`defaultValue: ${property.defaultValue}`);
|
|
403
|
+
}
|
|
404
|
+
if (property.optional) {
|
|
405
|
+
fields.push('optional: true');
|
|
406
|
+
}
|
|
407
|
+
if (property.referenceType !== undefined) {
|
|
408
|
+
fields.push(`referenceType: '${property.referenceType}'`);
|
|
409
|
+
}
|
|
410
|
+
return `${property.name}: { ${fields.join(', ')} }`;
|
|
411
|
+
});
|
|
412
|
+
const supers = superTypes.map(superType => `'${superType}'`).join(', ');
|
|
413
|
+
const propertiesText = propertyEntries.length > 0 ? `{ ${propertyEntries.join(', ')} }` : '{}';
|
|
414
|
+
out.push(` ${name}: { name: '${name}', properties: ${propertiesText}, superTypes: [${supers}] },`);
|
|
415
|
+
}
|
|
416
|
+
out.push(` } as const satisfies { readonly [type: string]: ${metaData} },`, '', ' getAllTypes(): string[] {', ' return Object.keys(this.types);', ' },', '',
|
|
417
|
+
// An own-property check, not `types[type] ?? …`: a name the object
|
|
418
|
+
// prototype carries, `toString` say, would read the inherited member and
|
|
419
|
+
// make `isSubtype` throw. `hasOwnProperty.call` rather than `Object.hasOwn`
|
|
420
|
+
// because the adopter's protocol package may target a lib without it.
|
|
421
|
+
` getTypeMetaData(type: string): ${metaData} {`, ' return Object.prototype.hasOwnProperty.call(this.types, type)', ` ? (this.types as { readonly [type: string]: ${metaData} })[type]`, ' : { name: type, properties: {}, superTypes: [] };', ' },', '', ' isSubtype(subtype: string, supertype: string): boolean {', ' return subtype === supertype || this.getTypeMetaData(subtype).superTypes.some(type => this.isSubtype(type, supertype));', ' },', '', ' getAllSubTypes(type: string): string[] {', ' return this.getAllTypes().filter(candidate => this.isSubtype(candidate, type));', ' },', '', ' isInstance(item: unknown, type: string): boolean {', ` return is${elementTypeName}(item) && this.isSubtype(item.$type, type);`, ' }', '};', '');
|
|
422
|
+
return out;
|
|
313
423
|
}
|
|
314
424
|
// ---------------------------------------------------------------------------
|
|
315
425
|
// AST source parsing helpers
|
|
@@ -348,10 +458,11 @@ function findAstReflectionClass(source) {
|
|
|
348
458
|
throw new Error('Could not find a class extending AbstractAstReflection in the AST source.');
|
|
349
459
|
}
|
|
350
460
|
/**
|
|
351
|
-
* Extract `superTypes` for each entry of the
|
|
352
|
-
* initializer. We rely on the Langium
|
|
353
|
-
*
|
|
354
|
-
* `.$type
|
|
461
|
+
* Extract `superTypes` and the grammar properties for each entry of the
|
|
462
|
+
* reflection class's `types` property initializer. We rely on the Langium
|
|
463
|
+
* convention that `X.$type === 'X'` and `X.p === 'p'`, so a type expression
|
|
464
|
+
* resolves to the literal type-name string by stripping `.$type`, and a
|
|
465
|
+
* property's name is its key.
|
|
355
466
|
*/
|
|
356
467
|
function parseReflectionTypes(reflectionClass) {
|
|
357
468
|
const result = {};
|
|
@@ -372,29 +483,31 @@ function parseReflectionTypes(reflectionClass) {
|
|
|
372
483
|
const value = entry.getLastChildByKind(SyntaxKind.ObjectLiteralExpression);
|
|
373
484
|
const supersProp = value?.getProperty('superTypes');
|
|
374
485
|
const supersInit = supersProp?.getFirstChildByKind(SyntaxKind.ArrayLiteralExpression);
|
|
375
|
-
const superTypes = [];
|
|
376
|
-
|
|
377
|
-
for (const element of supersInit.getElements()) {
|
|
378
|
-
const text = element.getText();
|
|
379
|
-
const dotMatch = /^([A-Za-z_$][\w$]*)\.\$type$/.exec(text);
|
|
380
|
-
if (dotMatch) {
|
|
381
|
-
superTypes.push(dotMatch[1]);
|
|
382
|
-
continue;
|
|
383
|
-
}
|
|
384
|
-
// Bare identifier or quoted string fall-back.
|
|
385
|
-
const stringMatch = /^['"](.+)['"]$/.exec(text);
|
|
386
|
-
if (stringMatch) {
|
|
387
|
-
superTypes.push(stringMatch[1]);
|
|
388
|
-
}
|
|
389
|
-
else {
|
|
390
|
-
superTypes.push(text);
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
result[name] = { superTypes };
|
|
486
|
+
const superTypes = supersInit?.getElements().map(element => typeNameOf(element.getText())) ?? [];
|
|
487
|
+
result[name] = { superTypes, properties: parseReflectionProperties(value) };
|
|
395
488
|
}
|
|
396
489
|
return result;
|
|
397
490
|
}
|
|
491
|
+
function parseReflectionProperties(entry) {
|
|
492
|
+
const propertiesInit = entry?.getProperty('properties')?.getFirstChildByKind(SyntaxKind.ObjectLiteralExpression);
|
|
493
|
+
const properties = [];
|
|
494
|
+
for (const property of propertiesInit?.getChildrenOfKind(SyntaxKind.PropertyAssignment) ?? []) {
|
|
495
|
+
const meta = property.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);
|
|
496
|
+
const field = (key) => meta?.getProperty(key)?.asKind(SyntaxKind.PropertyAssignment)?.getInitializer()?.getText();
|
|
497
|
+
const referenceType = field('referenceType');
|
|
498
|
+
properties.push({
|
|
499
|
+
name: property.getName(),
|
|
500
|
+
defaultValue: field('defaultValue'),
|
|
501
|
+
optional: field('optional') === 'true',
|
|
502
|
+
referenceType: referenceType === undefined ? undefined : typeNameOf(referenceType)
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
return properties;
|
|
506
|
+
}
|
|
507
|
+
/** `X.$type` or a quoted `'X'` to `X`; any other text passes through. */
|
|
508
|
+
function typeNameOf(text) {
|
|
509
|
+
return /^([A-Za-z_$][\w$]*)\.\$type$/.exec(text)?.[1] ?? /^['"](.+)['"]$/.exec(text)?.[1] ?? text;
|
|
510
|
+
}
|
|
398
511
|
/**
|
|
399
512
|
* Collect terminal regex literals from the AST file.
|
|
400
513
|
*
|
|
@@ -513,12 +626,16 @@ function mapType(text, elementTypeName) {
|
|
|
513
626
|
}
|
|
514
627
|
/**
|
|
515
628
|
* Type-guard expression (against an `item: unknown`) for one member of a non-literal type-alias
|
|
516
|
-
* union.
|
|
517
|
-
*
|
|
518
|
-
*
|
|
519
|
-
* `
|
|
629
|
+
* union. A string literal is compared as written, so the escapes Langium wrote stay valid.
|
|
630
|
+
* Covers the full set of Langium data-type primitives. The `typeof`-checkable ones
|
|
631
|
+
* (`string`/`number`/`boolean`/`bigint`) use `typeof`, and `Date`, the one Langium primitive
|
|
632
|
+
* `typeof` reports as `'object'`, uses `instanceof`. Any other member is a named type whose
|
|
633
|
+
* generated `is<Member>` guard is called.
|
|
520
634
|
*/
|
|
521
635
|
export function typeAliasMemberGuard(member) {
|
|
636
|
+
if (member.startsWith("'") || member.startsWith('"')) {
|
|
637
|
+
return `item === ${member}`;
|
|
638
|
+
}
|
|
522
639
|
switch (member) {
|
|
523
640
|
case 'string':
|
|
524
641
|
case 'number':
|
|
@@ -541,10 +658,11 @@ export function typeAliasMemberGuard(member) {
|
|
|
541
658
|
* name.
|
|
542
659
|
*
|
|
543
660
|
* Each referenced member is inlined and de-duplicated, preserving first-seen
|
|
544
|
-
* order
|
|
545
|
-
* namespace-qualified reference (the
|
|
546
|
-
*
|
|
547
|
-
* emitting a
|
|
661
|
+
* order; when no member holds a literal, the union is `never`. The definition
|
|
662
|
+
* is returned UNCHANGED when it holds no namespace-qualified reference (the
|
|
663
|
+
* single-language case) or when any referenced member is not a pure
|
|
664
|
+
* string-literal union — passing it through rather than emitting a
|
|
665
|
+
* half-resolved union.
|
|
548
666
|
*/
|
|
549
667
|
export function resolveNamespacedStringUnion(astSource, definition) {
|
|
550
668
|
const references = [...definition.matchAll(/\b([A-Za-z_]\w*)\.([A-Za-z_]\w*)\b/g)];
|
|
@@ -566,15 +684,54 @@ export function resolveNamespacedStringUnion(astSource, definition) {
|
|
|
566
684
|
}
|
|
567
685
|
}
|
|
568
686
|
}
|
|
569
|
-
return literals.map(
|
|
687
|
+
return literals.length > 0 ? literals.map(quote).join(' | ') : 'never';
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* The members of a type-alias union as TypeScript parses them. If a data-type rule groups
|
|
691
|
+
* alternatives in parentheses, the alias Langium generates contains the same group, as in
|
|
692
|
+
* `'a' | ('b' | Keyword)`, so each group is flattened into its members. Splitting the text on
|
|
693
|
+
* `|` breaks such a group and any keyword that contains `|`, such as `'||'`, and the
|
|
694
|
+
* resulting guard does not compile.
|
|
695
|
+
*/
|
|
696
|
+
function unionMembers(typeNode) {
|
|
697
|
+
if (typeNode.isKind(SyntaxKind.UnionType)) {
|
|
698
|
+
return typeNode.getTypeNodes().flatMap(member => unionMembers(member));
|
|
699
|
+
}
|
|
700
|
+
if (typeNode.isKind(SyntaxKind.ParenthesizedType)) {
|
|
701
|
+
return unionMembers(typeNode.getTypeNode());
|
|
702
|
+
}
|
|
703
|
+
return [typeNode.getText()];
|
|
570
704
|
}
|
|
571
|
-
/**
|
|
705
|
+
/**
|
|
706
|
+
* The values of a union of string literals, or undefined when the definition holds anything
|
|
707
|
+
* else. The TypeScript scanner decodes each literal, so a keyword with a quote in it reads the
|
|
708
|
+
* same whichever quotes and escapes Langium wrote it with. Langium writes the keyword names of
|
|
709
|
+
* a language without keywords as `never`, which adds no value.
|
|
710
|
+
*/
|
|
572
711
|
function extractStringLiterals(definition) {
|
|
573
|
-
const
|
|
574
|
-
|
|
575
|
-
|
|
712
|
+
const scanner = ts.createScanner(ts.ScriptTarget.Latest, true, ts.LanguageVariant.Standard, definition);
|
|
713
|
+
const literals = [];
|
|
714
|
+
let members = 0;
|
|
715
|
+
for (let token = scanner.scan(); token !== ts.SyntaxKind.EndOfFileToken; token = scanner.scan()) {
|
|
716
|
+
if (token === ts.SyntaxKind.StringLiteral) {
|
|
717
|
+
literals.push(scanner.getTokenValue());
|
|
718
|
+
members++;
|
|
719
|
+
}
|
|
720
|
+
else if (token === ts.SyntaxKind.NeverKeyword) {
|
|
721
|
+
members++;
|
|
722
|
+
}
|
|
723
|
+
else if (token !== ts.SyntaxKind.BarToken) {
|
|
724
|
+
return undefined;
|
|
725
|
+
}
|
|
576
726
|
}
|
|
577
|
-
return
|
|
727
|
+
return members > 0 ? literals : undefined;
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* `value` as a string literal in the single quotes the generated file uses. A keyword can
|
|
731
|
+
* contain a quote or a backslash, and either one ends or escapes the literal unless escaped.
|
|
732
|
+
*/
|
|
733
|
+
function quote(value) {
|
|
734
|
+
return `'${value.replaceAll('\\', '\\\\').replaceAll("'", "\\'")}'`;
|
|
578
735
|
}
|
|
579
736
|
function topologicalSort(names, getDeps) {
|
|
580
737
|
const visited = new Set();
|