@hydranium/cli 1.0.0-next.42 → 1.0.0-next.421
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 +3134 -763
- 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 +3326 -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
|
@@ -7,18 +7,22 @@
|
|
|
7
7
|
* SPDX-License-Identifier: MIT
|
|
8
8
|
********************************************************************************/
|
|
9
9
|
|
|
10
|
-
import * as fs from 'fs';
|
|
10
|
+
import * as fs from 'node:fs';
|
|
11
11
|
import * as path from 'path';
|
|
12
12
|
import { emitAstBuilder } from './generate-ast-builder.js';
|
|
13
|
+
import { emitLanguages } from './generate-languages.js';
|
|
14
|
+
import { emitTransferBuilder } from './generate-transfer-builder.js';
|
|
13
15
|
import {
|
|
14
16
|
type Node,
|
|
15
17
|
type ObjectLiteralExpression,
|
|
16
18
|
Project,
|
|
17
19
|
SyntaxKind,
|
|
20
|
+
ts,
|
|
18
21
|
type ClassDeclaration,
|
|
19
22
|
type InterfaceDeclaration,
|
|
20
23
|
type SourceFile
|
|
21
24
|
} from 'ts-morph';
|
|
25
|
+
import { describeError } from '@hydranium/protocol';
|
|
22
26
|
|
|
23
27
|
/**
|
|
24
28
|
* Options for the transfer-model generator.
|
|
@@ -45,6 +49,28 @@ export interface GenerateTransferModelOptions {
|
|
|
45
49
|
* and not the other has the two disagreeing about the same grammar.
|
|
46
50
|
*/
|
|
47
51
|
astBuilderFile?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Destination for a generated test module binding `makeTransferNodeBuilder`
|
|
54
|
+
* from `@hydranium/protocol/testing` to the transfer model, as `transferNode`,
|
|
55
|
+
* the way {@link astBuilderFile} binds `astNode`. Belongs in a test tree:
|
|
56
|
+
* production code has no use for a builder that fills grammar defaults.
|
|
57
|
+
*
|
|
58
|
+
* When it sits in another package than {@link outFile}, it imports the model
|
|
59
|
+
* by that package's name, so the package's main entry must re-export the
|
|
60
|
+
* generated model, and with it each type's constant: an interface the entry
|
|
61
|
+
* narrows hides the generated constant of the same name unless the entry
|
|
62
|
+
* re-exports that constant too, and the builder keeps the generated
|
|
63
|
+
* interface unless the entry declares `<LanguageId>TransferTypes` with the
|
|
64
|
+
* narrowed entry.
|
|
65
|
+
*/
|
|
66
|
+
transferBuilderFile?: string;
|
|
67
|
+
/**
|
|
68
|
+
* Destination for a generated module stating each grammar's language id and
|
|
69
|
+
* file extensions, read from the `module.ts` beside {@link astFile}. Lets a
|
|
70
|
+
* package without Langium, such as one shared with clients, name the ids
|
|
71
|
+
* without copying them by hand.
|
|
72
|
+
*/
|
|
73
|
+
languagesFile?: string;
|
|
48
74
|
/** Name used for the base element type in the output. Defaults to `TransferElement`. */
|
|
49
75
|
elementTypeName?: string;
|
|
50
76
|
/** Name used for the terminal-patterns const in the output. Defaults to `ModelTerminals`. */
|
|
@@ -86,6 +112,15 @@ interface AugmentedProperty {
|
|
|
86
112
|
|
|
87
113
|
interface ReflectionEntry {
|
|
88
114
|
superTypes: string[];
|
|
115
|
+
properties: ReflectionProperty[];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** One grammar property of a reflection entry; `defaultValue` keeps the source text of the literal. */
|
|
119
|
+
interface ReflectionProperty {
|
|
120
|
+
name: string;
|
|
121
|
+
defaultValue?: string;
|
|
122
|
+
optional: boolean;
|
|
123
|
+
referenceType?: string;
|
|
89
124
|
}
|
|
90
125
|
|
|
91
126
|
const DEFAULT_ELEMENT_TYPE_NAME = 'TransferElement';
|
|
@@ -98,38 +133,72 @@ const DEFAULT_REGEN_COMMAND = 'Run the hydranium-cli generate-transfer-model com
|
|
|
98
133
|
*/
|
|
99
134
|
const NAMESPACED_TERMINALS_NAME = 'Terminals';
|
|
100
135
|
|
|
136
|
+
/** How often `--watch` polls its input files. */
|
|
137
|
+
const WATCH_POLL_INTERVAL_MS = 250;
|
|
138
|
+
const WATCH_DEBOUNCE_MS = 150;
|
|
139
|
+
|
|
101
140
|
/**
|
|
102
|
-
* Generate once, then re-generate whenever
|
|
141
|
+
* Generate once, then re-generate whenever an input file changes (the AST, the
|
|
142
|
+
* augmentation file, and `module.ts` when a languages file is written),
|
|
103
143
|
* until `signal` aborts (the CLI wires this to SIGINT). Regeneration is debounced
|
|
104
144
|
* so an editor's multi-event save fires a single run, and a generation error is
|
|
105
145
|
* logged without tearing down the watch. Resolves when the signal aborts.
|
|
146
|
+
* An already-aborted signal resolves immediately without generating or watching.
|
|
147
|
+
*
|
|
148
|
+
* Polled by path rather than watched through OS events: langium-cli removes
|
|
149
|
+
* its output directory and writes the files back, which an event watch, Node's
|
|
150
|
+
* and chokidar's alike, stops hearing after the first run. A run that finds an
|
|
151
|
+
* input missing is skipped, since the file's return is a change of its own, so
|
|
152
|
+
* nothing generates from a half-written output.
|
|
106
153
|
*/
|
|
107
154
|
export function watchTransferModel(options: GenerateTransferModelOptions, context: { signal: AbortSignal }): Promise<void> {
|
|
155
|
+
if (context.signal.aborted) {
|
|
156
|
+
return Promise.resolve();
|
|
157
|
+
}
|
|
108
158
|
const runSafely = (): void => {
|
|
109
159
|
try {
|
|
110
160
|
generateTransferModel(options);
|
|
111
161
|
} catch (error: unknown) {
|
|
112
|
-
console.error(`Transfer-model generation failed: ${
|
|
162
|
+
console.error(`Transfer-model generation failed: ${describeError(error)}`);
|
|
113
163
|
}
|
|
114
164
|
};
|
|
115
165
|
runSafely();
|
|
116
|
-
|
|
166
|
+
// A grammar's language id and extensions reach only `module.ts`, so the
|
|
167
|
+
// languages module would go stale without it.
|
|
168
|
+
const watched = [
|
|
169
|
+
options.astFile,
|
|
170
|
+
options.augmentationFile,
|
|
171
|
+
...(options.languagesFile !== undefined ? [languagesModuleFile(options)] : [])
|
|
172
|
+
];
|
|
117
173
|
|
|
118
174
|
let timer: NodeJS.Timeout | undefined;
|
|
119
|
-
const
|
|
175
|
+
const rerun = (): void => {
|
|
176
|
+
if (watched.every(file => fs.existsSync(file))) {
|
|
177
|
+
runSafely();
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
// The poll reports every stat it takes; a replaced file has a new inode, a
|
|
181
|
+
// rewritten one a new mtime, a removed or returning one zeroed stats.
|
|
182
|
+
const onPoll = (current: fs.Stats, previous: fs.Stats): void => {
|
|
183
|
+
if (current.ino === previous.ino && current.mtimeMs === previous.mtimeMs && current.size === previous.size) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
120
186
|
if (timer) {
|
|
121
187
|
clearTimeout(timer);
|
|
122
188
|
}
|
|
123
|
-
timer = setTimeout(
|
|
189
|
+
timer = setTimeout(rerun, WATCH_DEBOUNCE_MS);
|
|
124
190
|
};
|
|
125
|
-
const
|
|
191
|
+
for (const file of watched) {
|
|
192
|
+
fs.watchFile(file, { interval: WATCH_POLL_INTERVAL_MS }, onPoll);
|
|
193
|
+
}
|
|
194
|
+
console.log(`Watching ${watched.join(', ')} for changes. Press Ctrl-C to stop.`);
|
|
126
195
|
|
|
127
196
|
return new Promise<void>(resolve => {
|
|
128
197
|
context.signal.addEventListener(
|
|
129
198
|
'abort',
|
|
130
199
|
() => {
|
|
131
|
-
for (const
|
|
132
|
-
|
|
200
|
+
for (const file of watched) {
|
|
201
|
+
fs.unwatchFile(file, onPoll);
|
|
133
202
|
}
|
|
134
203
|
if (timer) {
|
|
135
204
|
clearTimeout(timer);
|
|
@@ -141,6 +210,11 @@ export function watchTransferModel(options: GenerateTransferModelOptions, contex
|
|
|
141
210
|
});
|
|
142
211
|
}
|
|
143
212
|
|
|
213
|
+
/** The langium-cli `module.ts` beside the AST file, which the languages module is read from. */
|
|
214
|
+
function languagesModuleFile(options: GenerateTransferModelOptions): string {
|
|
215
|
+
return path.join(path.dirname(options.astFile), 'module.ts');
|
|
216
|
+
}
|
|
217
|
+
|
|
144
218
|
/** Run the transfer-model generator. */
|
|
145
219
|
export function generateTransferModel(options: GenerateTransferModelOptions): void {
|
|
146
220
|
const elementTypeName = options.elementTypeName ?? DEFAULT_ELEMENT_TYPE_NAME;
|
|
@@ -178,15 +252,19 @@ export function generateTransferModel(options: GenerateTransferModelOptions): vo
|
|
|
178
252
|
astInterfaces.set(iface.getName(), iface);
|
|
179
253
|
}
|
|
180
254
|
|
|
181
|
-
const typeAliases: Array<{ name: string; definition: string }> = [];
|
|
255
|
+
const typeAliases: Array<{ name: string; definition: string; members: string[] }> = [];
|
|
182
256
|
for (const alias of astSource.getTypeAliases()) {
|
|
183
257
|
const name = alias.getName();
|
|
184
258
|
if (skipTypeAliases.has(name)) {
|
|
185
259
|
continue;
|
|
186
260
|
}
|
|
187
|
-
const
|
|
188
|
-
if (
|
|
189
|
-
typeAliases.push({
|
|
261
|
+
const typeNode = alias.getTypeNode();
|
|
262
|
+
if (typeNode) {
|
|
263
|
+
typeAliases.push({
|
|
264
|
+
name,
|
|
265
|
+
definition: mapType(resolveNamespacedStringUnion(astSource, typeNode.getText()), elementTypeName),
|
|
266
|
+
members: unionMembers(typeNode).map(member => mapType(member, elementTypeName))
|
|
267
|
+
});
|
|
190
268
|
}
|
|
191
269
|
}
|
|
192
270
|
|
|
@@ -203,7 +281,6 @@ export function generateTransferModel(options: GenerateTransferModelOptions): vo
|
|
|
203
281
|
// 7. Classify interfaces: abstract (union $type) vs concrete (literal $type).
|
|
204
282
|
const interfaceNames: string[] = [];
|
|
205
283
|
const abstractTypes = new Set<string>();
|
|
206
|
-
const concreteTypes = new Set<string>();
|
|
207
284
|
|
|
208
285
|
for (const name of Object.keys(reflectionTypes)) {
|
|
209
286
|
const iface = astInterfaces.get(name);
|
|
@@ -215,8 +292,6 @@ export function generateTransferModel(options: GenerateTransferModelOptions): vo
|
|
|
215
292
|
const typeText = iface.getProperty('$type')?.getTypeNode()?.getText() ?? '';
|
|
216
293
|
if (typeText.includes('|')) {
|
|
217
294
|
abstractTypes.add(name);
|
|
218
|
-
} else {
|
|
219
|
-
concreteTypes.add(name);
|
|
220
295
|
}
|
|
221
296
|
}
|
|
222
297
|
|
|
@@ -258,12 +333,13 @@ export function generateTransferModel(options: GenerateTransferModelOptions): vo
|
|
|
258
333
|
out.push('}');
|
|
259
334
|
out.push('');
|
|
260
335
|
|
|
261
|
-
//
|
|
336
|
+
// Langium's per-type constant under the same name, for every reflection type
|
|
337
|
+
// the model emits, abstract ones and unions included.
|
|
338
|
+
const reflectedTypes = Object.keys(reflectionTypes).filter(name => astInterfaces.has(name) || typeAliasNames.has(name));
|
|
262
339
|
out.push('// --- Type Constants ---');
|
|
263
|
-
for (const name of
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
}
|
|
340
|
+
for (const name of reflectedTypes) {
|
|
341
|
+
const keys = reflectionTypes[name].properties.map(property => `${property.name}: '${property.name}'`);
|
|
342
|
+
out.push(`export const ${name} = { ${[`$type: '${name}'`, ...keys].join(', ')} } as const;`);
|
|
267
343
|
}
|
|
268
344
|
out.push('');
|
|
269
345
|
|
|
@@ -274,7 +350,7 @@ export function generateTransferModel(options: GenerateTransferModelOptions): vo
|
|
|
274
350
|
out.push(`export type ${name} = ${definition};`);
|
|
275
351
|
const literals = extractStringLiterals(definition);
|
|
276
352
|
if (literals) {
|
|
277
|
-
const items = literals.map(
|
|
353
|
+
const items = literals.map(quote).join(', ');
|
|
278
354
|
out.push(`export const ${name}Values = [${items}] as const;`);
|
|
279
355
|
}
|
|
280
356
|
}
|
|
@@ -305,7 +381,7 @@ export function generateTransferModel(options: GenerateTransferModelOptions): vo
|
|
|
305
381
|
const typeValue = iface.getProperty('$type')?.getTypeNode()?.getText() ?? `'${name}'`;
|
|
306
382
|
out.push(` readonly $type: ${typeValue};`);
|
|
307
383
|
} else {
|
|
308
|
-
out.push(` readonly $type:
|
|
384
|
+
out.push(` readonly $type: '${name}';`);
|
|
309
385
|
}
|
|
310
386
|
|
|
311
387
|
const emitted = new Set<string>(['$type']);
|
|
@@ -367,24 +443,24 @@ export function generateTransferModel(options: GenerateTransferModelOptions): vo
|
|
|
367
443
|
const checks = members.map(m => `item.$type === '${m}'`).join(' || ');
|
|
368
444
|
out.push(` return is${elementTypeName}(item) && (${checks});`);
|
|
369
445
|
} else {
|
|
370
|
-
out.push(` return is${elementTypeName}(item) && item.$type === ${name}
|
|
446
|
+
out.push(` return is${elementTypeName}(item) && item.$type === '${name}';`);
|
|
371
447
|
}
|
|
372
448
|
out.push('}');
|
|
373
449
|
out.push('');
|
|
374
450
|
}
|
|
375
451
|
|
|
376
|
-
for (const { name, definition } of typeAliases) {
|
|
452
|
+
for (const { name, definition, members } of typeAliases) {
|
|
377
453
|
const literals = extractStringLiterals(definition);
|
|
378
454
|
if (literals) {
|
|
379
|
-
const checks = literals.map(l => `item ===
|
|
455
|
+
const checks = literals.map(l => `item === ${quote(l)}`).join(' || ') || 'false';
|
|
380
456
|
out.push(`export function is${name}(item: unknown): item is ${name} {`);
|
|
381
457
|
out.push(` return ${checks};`);
|
|
382
458
|
out.push('}');
|
|
383
459
|
out.push('');
|
|
384
460
|
} else {
|
|
385
|
-
// Union (or single) of named members
|
|
386
|
-
//
|
|
387
|
-
|
|
461
|
+
// Union (or single) of named members, Langium data-type primitives and the string
|
|
462
|
+
// literals a data-type rule's keywords add beside them. Each member maps to its own
|
|
463
|
+
// check via typeAliasMemberGuard.
|
|
388
464
|
const checks = members.map(typeAliasMemberGuard).join(' || ');
|
|
389
465
|
out.push(`export function is${name}(item: unknown): item is ${name} {`);
|
|
390
466
|
out.push(` return ${checks};`);
|
|
@@ -393,6 +469,9 @@ export function generateTransferModel(options: GenerateTransferModelOptions): vo
|
|
|
393
469
|
}
|
|
394
470
|
}
|
|
395
471
|
|
|
472
|
+
out.push(...renderTypeMap(languageId, reflectedTypes));
|
|
473
|
+
out.push(...renderReflection(languageId, elementTypeName, reflectedTypes, reflectionTypes));
|
|
474
|
+
|
|
396
475
|
// Coverage check: warn on reflection types not emitted.
|
|
397
476
|
const processedSet = new Set(sorted);
|
|
398
477
|
for (const name of Object.keys(reflectionTypes)) {
|
|
@@ -422,6 +501,107 @@ export function generateTransferModel(options: GenerateTransferModelOptions): vo
|
|
|
422
501
|
console.warn(`Warning: no '${languageId}AstType' alias in ${options.astFile}; skipping the AST builder.`);
|
|
423
502
|
}
|
|
424
503
|
}
|
|
504
|
+
|
|
505
|
+
if (options.transferBuilderFile !== undefined) {
|
|
506
|
+
emitTransferBuilder({ outFile: options.transferBuilderFile, transferModelFile: options.outFile, languageId, regenCommand });
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (options.languagesFile !== undefined) {
|
|
510
|
+
const moduleSource = project.addSourceFileAtPath(languagesModuleFile(options));
|
|
511
|
+
emitLanguages(moduleSource, { outFile: options.languagesFile, regenCommand });
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* `<LanguageId>TransferTypes`, each `$type` to its interface, so an encoder or a
|
|
517
|
+
* builder can type a node by the grammar the way Langium's `<LanguageId>AstType`
|
|
518
|
+
* does on the AST side.
|
|
519
|
+
*/
|
|
520
|
+
function renderTypeMap(languageId: string, types: readonly string[]): string[] {
|
|
521
|
+
return ['// --- Type Map ---', `export type ${languageId}TransferTypes = {`, ...types.map(name => ` ${name}: ${name};`), '};', ''];
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* `<LanguageId>TransferReflection`, the grammar metadata with the method names
|
|
526
|
+
* Langium's `AstReflection` uses. A plain object with no framework import, so
|
|
527
|
+
* the file stays self-contained, and nothing references it but its importers:
|
|
528
|
+
* the `is` guards test `$type` directly, so a guard never pulls it in. `types`
|
|
529
|
+
* keeps its literal type, which is how the transfer-node builder tells which
|
|
530
|
+
* fields the grammar defaults.
|
|
531
|
+
*/
|
|
532
|
+
function renderReflection(
|
|
533
|
+
languageId: string,
|
|
534
|
+
elementTypeName: string,
|
|
535
|
+
types: readonly string[],
|
|
536
|
+
reflectionTypes: Record<string, ReflectionEntry>
|
|
537
|
+
): string[] {
|
|
538
|
+
const metaData = `${languageId}TransferTypeMetaData`;
|
|
539
|
+
// Index signatures, not `Readonly<Record<…>>`: a grammar type may be named
|
|
540
|
+
// `Record` or `Readonly`, which Langium does not reserve as it does `Object`.
|
|
541
|
+
const out = [
|
|
542
|
+
'// --- Reflection ---',
|
|
543
|
+
`export interface ${metaData} {`,
|
|
544
|
+
' readonly name: string;',
|
|
545
|
+
' readonly properties: {',
|
|
546
|
+
' readonly [name: string]: { readonly name: string; readonly defaultValue?: unknown; readonly optional?: boolean; readonly referenceType?: string };',
|
|
547
|
+
' };',
|
|
548
|
+
' readonly superTypes: readonly string[];',
|
|
549
|
+
'}',
|
|
550
|
+
'',
|
|
551
|
+
`export const ${languageId}TransferReflection = {`,
|
|
552
|
+
' types: {'
|
|
553
|
+
];
|
|
554
|
+
for (const name of types) {
|
|
555
|
+
const { properties, superTypes } = reflectionTypes[name];
|
|
556
|
+
const propertyEntries = properties.map(property => {
|
|
557
|
+
const fields = [`name: '${property.name}'`];
|
|
558
|
+
if (property.defaultValue !== undefined) {
|
|
559
|
+
fields.push(`defaultValue: ${property.defaultValue}`);
|
|
560
|
+
}
|
|
561
|
+
if (property.optional) {
|
|
562
|
+
fields.push('optional: true');
|
|
563
|
+
}
|
|
564
|
+
if (property.referenceType !== undefined) {
|
|
565
|
+
fields.push(`referenceType: '${property.referenceType}'`);
|
|
566
|
+
}
|
|
567
|
+
return `${property.name}: { ${fields.join(', ')} }`;
|
|
568
|
+
});
|
|
569
|
+
const supers = superTypes.map(superType => `'${superType}'`).join(', ');
|
|
570
|
+
const propertiesText = propertyEntries.length > 0 ? `{ ${propertyEntries.join(', ')} }` : '{}';
|
|
571
|
+
out.push(` ${name}: { name: '${name}', properties: ${propertiesText}, superTypes: [${supers}] },`);
|
|
572
|
+
}
|
|
573
|
+
out.push(
|
|
574
|
+
` } as const satisfies { readonly [type: string]: ${metaData} },`,
|
|
575
|
+
'',
|
|
576
|
+
' getAllTypes(): string[] {',
|
|
577
|
+
' return Object.keys(this.types);',
|
|
578
|
+
' },',
|
|
579
|
+
'',
|
|
580
|
+
// An own-property check, not `types[type] ?? …`: a name the object
|
|
581
|
+
// prototype carries, `toString` say, would read the inherited member and
|
|
582
|
+
// make `isSubtype` throw. `hasOwnProperty.call` rather than `Object.hasOwn`
|
|
583
|
+
// because the adopter's protocol package may target a lib without it.
|
|
584
|
+
` getTypeMetaData(type: string): ${metaData} {`,
|
|
585
|
+
' return Object.prototype.hasOwnProperty.call(this.types, type)',
|
|
586
|
+
` ? (this.types as { readonly [type: string]: ${metaData} })[type]`,
|
|
587
|
+
' : { name: type, properties: {}, superTypes: [] };',
|
|
588
|
+
' },',
|
|
589
|
+
'',
|
|
590
|
+
' isSubtype(subtype: string, supertype: string): boolean {',
|
|
591
|
+
' return subtype === supertype || this.getTypeMetaData(subtype).superTypes.some(type => this.isSubtype(type, supertype));',
|
|
592
|
+
' },',
|
|
593
|
+
'',
|
|
594
|
+
' getAllSubTypes(type: string): string[] {',
|
|
595
|
+
' return this.getAllTypes().filter(candidate => this.isSubtype(candidate, type));',
|
|
596
|
+
' },',
|
|
597
|
+
'',
|
|
598
|
+
' isInstance(item: unknown, type: string): boolean {',
|
|
599
|
+
` return is${elementTypeName}(item) && this.isSubtype(item.$type, type);`,
|
|
600
|
+
' }',
|
|
601
|
+
'};',
|
|
602
|
+
''
|
|
603
|
+
);
|
|
604
|
+
return out;
|
|
425
605
|
}
|
|
426
606
|
|
|
427
607
|
// ---------------------------------------------------------------------------
|
|
@@ -462,10 +642,11 @@ function findAstReflectionClass(source: SourceFile): ClassDeclaration {
|
|
|
462
642
|
}
|
|
463
643
|
|
|
464
644
|
/**
|
|
465
|
-
* Extract `superTypes` for each entry of the
|
|
466
|
-
* initializer. We rely on the Langium
|
|
467
|
-
*
|
|
468
|
-
* `.$type
|
|
645
|
+
* Extract `superTypes` and the grammar properties for each entry of the
|
|
646
|
+
* reflection class's `types` property initializer. We rely on the Langium
|
|
647
|
+
* convention that `X.$type === 'X'` and `X.p === 'p'`, so a type expression
|
|
648
|
+
* resolves to the literal type-name string by stripping `.$type`, and a
|
|
649
|
+
* property's name is its key.
|
|
469
650
|
*/
|
|
470
651
|
function parseReflectionTypes(reflectionClass: ClassDeclaration): Record<string, ReflectionEntry> {
|
|
471
652
|
const result: Record<string, ReflectionEntry> = {};
|
|
@@ -487,29 +668,35 @@ function parseReflectionTypes(reflectionClass: ClassDeclaration): Record<string,
|
|
|
487
668
|
const value = entry.getLastChildByKind(SyntaxKind.ObjectLiteralExpression);
|
|
488
669
|
const supersProp = value?.getProperty('superTypes');
|
|
489
670
|
const supersInit = supersProp?.getFirstChildByKind(SyntaxKind.ArrayLiteralExpression);
|
|
490
|
-
const superTypes
|
|
491
|
-
|
|
492
|
-
for (const element of supersInit.getElements()) {
|
|
493
|
-
const text = element.getText();
|
|
494
|
-
const dotMatch = /^([A-Za-z_$][\w$]*)\.\$type$/.exec(text);
|
|
495
|
-
if (dotMatch) {
|
|
496
|
-
superTypes.push(dotMatch[1]);
|
|
497
|
-
continue;
|
|
498
|
-
}
|
|
499
|
-
// Bare identifier or quoted string fall-back.
|
|
500
|
-
const stringMatch = /^['"](.+)['"]$/.exec(text);
|
|
501
|
-
if (stringMatch) {
|
|
502
|
-
superTypes.push(stringMatch[1]);
|
|
503
|
-
} else {
|
|
504
|
-
superTypes.push(text);
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
result[name] = { superTypes };
|
|
671
|
+
const superTypes = supersInit?.getElements().map(element => typeNameOf(element.getText())) ?? [];
|
|
672
|
+
result[name] = { superTypes, properties: parseReflectionProperties(value) };
|
|
509
673
|
}
|
|
510
674
|
return result;
|
|
511
675
|
}
|
|
512
676
|
|
|
677
|
+
function parseReflectionProperties(entry: ObjectLiteralExpression | undefined): ReflectionProperty[] {
|
|
678
|
+
const propertiesInit = entry?.getProperty('properties')?.getFirstChildByKind(SyntaxKind.ObjectLiteralExpression);
|
|
679
|
+
const properties: ReflectionProperty[] = [];
|
|
680
|
+
for (const property of propertiesInit?.getChildrenOfKind(SyntaxKind.PropertyAssignment) ?? []) {
|
|
681
|
+
const meta = property.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);
|
|
682
|
+
const field = (key: string): string | undefined =>
|
|
683
|
+
meta?.getProperty(key)?.asKind(SyntaxKind.PropertyAssignment)?.getInitializer()?.getText();
|
|
684
|
+
const referenceType = field('referenceType');
|
|
685
|
+
properties.push({
|
|
686
|
+
name: property.getName(),
|
|
687
|
+
defaultValue: field('defaultValue'),
|
|
688
|
+
optional: field('optional') === 'true',
|
|
689
|
+
referenceType: referenceType === undefined ? undefined : typeNameOf(referenceType)
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
return properties;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/** `X.$type` or a quoted `'X'` to `X`; any other text passes through. */
|
|
696
|
+
function typeNameOf(text: string): string {
|
|
697
|
+
return /^([A-Za-z_$][\w$]*)\.\$type$/.exec(text)?.[1] ?? /^['"](.+)['"]$/.exec(text)?.[1] ?? text;
|
|
698
|
+
}
|
|
699
|
+
|
|
513
700
|
/**
|
|
514
701
|
* Collect terminal regex literals from the AST file.
|
|
515
702
|
*
|
|
@@ -647,12 +834,16 @@ function mapType(text: string, elementTypeName: string): string {
|
|
|
647
834
|
|
|
648
835
|
/**
|
|
649
836
|
* Type-guard expression (against an `item: unknown`) for one member of a non-literal type-alias
|
|
650
|
-
* union.
|
|
651
|
-
*
|
|
652
|
-
*
|
|
653
|
-
* `
|
|
837
|
+
* union. A string literal is compared as written, so the escapes Langium wrote stay valid.
|
|
838
|
+
* Covers the full set of Langium data-type primitives. The `typeof`-checkable ones
|
|
839
|
+
* (`string`/`number`/`boolean`/`bigint`) use `typeof`, and `Date`, the one Langium primitive
|
|
840
|
+
* `typeof` reports as `'object'`, uses `instanceof`. Any other member is a named type whose
|
|
841
|
+
* generated `is<Member>` guard is called.
|
|
654
842
|
*/
|
|
655
843
|
export function typeAliasMemberGuard(member: string): string {
|
|
844
|
+
if (member.startsWith("'") || member.startsWith('"')) {
|
|
845
|
+
return `item === ${member}`;
|
|
846
|
+
}
|
|
656
847
|
switch (member) {
|
|
657
848
|
case 'string':
|
|
658
849
|
case 'number':
|
|
@@ -676,10 +867,11 @@ export function typeAliasMemberGuard(member: string): string {
|
|
|
676
867
|
* name.
|
|
677
868
|
*
|
|
678
869
|
* Each referenced member is inlined and de-duplicated, preserving first-seen
|
|
679
|
-
* order
|
|
680
|
-
* namespace-qualified reference (the
|
|
681
|
-
*
|
|
682
|
-
* emitting a
|
|
870
|
+
* order; when no member holds a literal, the union is `never`. The definition
|
|
871
|
+
* is returned UNCHANGED when it holds no namespace-qualified reference (the
|
|
872
|
+
* single-language case) or when any referenced member is not a pure
|
|
873
|
+
* string-literal union — passing it through rather than emitting a
|
|
874
|
+
* half-resolved union.
|
|
683
875
|
*/
|
|
684
876
|
export function resolveNamespacedStringUnion(astSource: SourceFile, definition: string): string {
|
|
685
877
|
const references = [...definition.matchAll(/\b([A-Za-z_]\w*)\.([A-Za-z_]\w*)\b/g)];
|
|
@@ -701,16 +893,55 @@ export function resolveNamespacedStringUnion(astSource: SourceFile, definition:
|
|
|
701
893
|
}
|
|
702
894
|
}
|
|
703
895
|
}
|
|
704
|
-
return literals.map(
|
|
896
|
+
return literals.length > 0 ? literals.map(quote).join(' | ') : 'never';
|
|
705
897
|
}
|
|
706
898
|
|
|
707
|
-
/**
|
|
899
|
+
/**
|
|
900
|
+
* The members of a type-alias union as TypeScript parses them. If a data-type rule groups
|
|
901
|
+
* alternatives in parentheses, the alias Langium generates contains the same group, as in
|
|
902
|
+
* `'a' | ('b' | Keyword)`, so each group is flattened into its members. Splitting the text on
|
|
903
|
+
* `|` breaks such a group and any keyword that contains `|`, such as `'||'`, and the
|
|
904
|
+
* resulting guard does not compile.
|
|
905
|
+
*/
|
|
906
|
+
function unionMembers(typeNode: Node): string[] {
|
|
907
|
+
if (typeNode.isKind(SyntaxKind.UnionType)) {
|
|
908
|
+
return typeNode.getTypeNodes().flatMap(member => unionMembers(member));
|
|
909
|
+
}
|
|
910
|
+
if (typeNode.isKind(SyntaxKind.ParenthesizedType)) {
|
|
911
|
+
return unionMembers(typeNode.getTypeNode());
|
|
912
|
+
}
|
|
913
|
+
return [typeNode.getText()];
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
/**
|
|
917
|
+
* The values of a union of string literals, or undefined when the definition holds anything
|
|
918
|
+
* else. The TypeScript scanner decodes each literal, so a keyword with a quote in it reads the
|
|
919
|
+
* same whichever quotes and escapes Langium wrote it with. Langium writes the keyword names of
|
|
920
|
+
* a language without keywords as `never`, which adds no value.
|
|
921
|
+
*/
|
|
708
922
|
function extractStringLiterals(definition: string): string[] | undefined {
|
|
709
|
-
const
|
|
710
|
-
|
|
711
|
-
|
|
923
|
+
const scanner = ts.createScanner(ts.ScriptTarget.Latest, true, ts.LanguageVariant.Standard, definition);
|
|
924
|
+
const literals: string[] = [];
|
|
925
|
+
let members = 0;
|
|
926
|
+
for (let token = scanner.scan(); token !== ts.SyntaxKind.EndOfFileToken; token = scanner.scan()) {
|
|
927
|
+
if (token === ts.SyntaxKind.StringLiteral) {
|
|
928
|
+
literals.push(scanner.getTokenValue());
|
|
929
|
+
members++;
|
|
930
|
+
} else if (token === ts.SyntaxKind.NeverKeyword) {
|
|
931
|
+
members++;
|
|
932
|
+
} else if (token !== ts.SyntaxKind.BarToken) {
|
|
933
|
+
return undefined;
|
|
934
|
+
}
|
|
712
935
|
}
|
|
713
|
-
return
|
|
936
|
+
return members > 0 ? literals : undefined;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
/**
|
|
940
|
+
* `value` as a string literal in the single quotes the generated file uses. A keyword can
|
|
941
|
+
* contain a quote or a backslash, and either one ends or escapes the literal unless escaped.
|
|
942
|
+
*/
|
|
943
|
+
function quote(value: string): string {
|
|
944
|
+
return `'${value.replaceAll('\\', '\\\\').replaceAll("'", "\\'")}'`;
|
|
714
945
|
}
|
|
715
946
|
|
|
716
947
|
function topologicalSort(names: string[], getDeps: (n: string) => string[]): string[] {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 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 * as path from 'path';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Module specifier for `targetFile` as seen from the generated module
|
|
14
|
+
* `outFile`'s own directory. Emitted with a `.js` extension and posix
|
|
15
|
+
* separators because the output is ESM TypeScript, where the specifier names
|
|
16
|
+
* the compiled sibling.
|
|
17
|
+
*/
|
|
18
|
+
export function relativeImportPath(outFile: string, targetFile: string): string {
|
|
19
|
+
const relative = path.relative(path.dirname(path.resolve(outFile)), path.resolve(targetFile));
|
|
20
|
+
const posix = relative.split(path.sep).join('/').replace(/\.ts$/, '.js');
|
|
21
|
+
return posix.startsWith('.') ? posix : `./${posix}`;
|
|
22
|
+
}
|