@velarscript/cli 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +117 -0
- package/dist/application-package-host.d.ts +5 -0
- package/dist/application-package-host.d.ts.map +1 -0
- package/dist/application-package-host.js +67 -0
- package/dist/application-package-host.js.map +1 -0
- package/dist/bounded-text.d.ts +2 -0
- package/dist/bounded-text.d.ts.map +1 -0
- package/dist/bounded-text.js +13 -0
- package/dist/bounded-text.js.map +1 -0
- package/dist/browser-process-owner.d.ts +15 -0
- package/dist/browser-process-owner.d.ts.map +1 -0
- package/dist/browser-process-owner.js +151 -0
- package/dist/browser-process-owner.js.map +1 -0
- package/dist/browser-test-runner.d.ts +11 -0
- package/dist/browser-test-runner.d.ts.map +1 -0
- package/dist/browser-test-runner.js +1022 -0
- package/dist/browser-test-runner.js.map +1 -0
- package/dist/build-engine-tool.d.ts +3 -0
- package/dist/build-engine-tool.d.ts.map +1 -0
- package/dist/build-engine-tool.js +34 -0
- package/dist/build-engine-tool.js.map +1 -0
- package/dist/build-staging.d.ts +3 -0
- package/dist/build-staging.d.ts.map +1 -0
- package/dist/build-staging.js +3 -0
- package/dist/build-staging.js.map +1 -0
- package/dist/bundled-application-package-host-registry.d.ts +10 -0
- package/dist/bundled-application-package-host-registry.d.ts.map +1 -0
- package/dist/bundled-application-package-host-registry.js +16 -0
- package/dist/bundled-application-package-host-registry.js.map +1 -0
- package/dist/bundled-extension-registry.d.ts +22 -0
- package/dist/bundled-extension-registry.d.ts.map +1 -0
- package/dist/bundled-extension-registry.js +10 -0
- package/dist/bundled-extension-registry.js.map +1 -0
- package/dist/canonical-path.d.ts +3 -0
- package/dist/canonical-path.d.ts.map +1 -0
- package/dist/canonical-path.js +30 -0
- package/dist/canonical-path.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1274 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +26 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +403 -0
- package/dist/config.js.map +1 -0
- package/dist/deployment-verifier.d.ts +34 -0
- package/dist/deployment-verifier.d.ts.map +1 -0
- package/dist/deployment-verifier.js +260 -0
- package/dist/deployment-verifier.js.map +1 -0
- package/dist/dev-server.d.ts +3 -0
- package/dist/dev-server.d.ts.map +1 -0
- package/dist/dev-server.js +464 -0
- package/dist/dev-server.js.map +1 -0
- package/dist/embedded-modules.d.ts +17 -0
- package/dist/embedded-modules.d.ts.map +1 -0
- package/dist/embedded-modules.js +33 -0
- package/dist/embedded-modules.js.map +1 -0
- package/dist/extension-metadata.d.ts +23 -0
- package/dist/extension-metadata.d.ts.map +1 -0
- package/dist/extension-metadata.js +254 -0
- package/dist/extension-metadata.js.map +1 -0
- package/dist/file-integrity.d.ts +7 -0
- package/dist/file-integrity.d.ts.map +1 -0
- package/dist/file-integrity.js +14 -0
- package/dist/file-integrity.js.map +1 -0
- package/dist/framework-host.d.ts +12 -0
- package/dist/framework-host.d.ts.map +1 -0
- package/dist/framework-host.js +46 -0
- package/dist/framework-host.js.map +1 -0
- package/dist/host-error.d.ts +6 -0
- package/dist/host-error.d.ts.map +1 -0
- package/dist/host-error.js +54 -0
- package/dist/host-error.js.map +1 -0
- package/dist/installed-package.d.ts +9 -0
- package/dist/installed-package.d.ts.map +1 -0
- package/dist/installed-package.js +23 -0
- package/dist/installed-package.js.map +1 -0
- package/dist/language-server-bundle-entry.d.ts +2 -0
- package/dist/language-server-bundle-entry.d.ts.map +1 -0
- package/dist/language-server-bundle-entry.js +7 -0
- package/dist/language-server-bundle-entry.js.map +1 -0
- package/dist/language-server-tool.d.ts +3 -0
- package/dist/language-server-tool.d.ts.map +1 -0
- package/dist/language-server-tool.js +30 -0
- package/dist/language-server-tool.js.map +1 -0
- package/dist/language-server.d.ts +9 -0
- package/dist/language-server.d.ts.map +1 -0
- package/dist/language-server.js +1733 -0
- package/dist/language-server.js.map +1 -0
- package/dist/mechanical-fixer.d.ts +30 -0
- package/dist/mechanical-fixer.d.ts.map +1 -0
- package/dist/mechanical-fixer.js +103 -0
- package/dist/mechanical-fixer.js.map +1 -0
- package/dist/module-assets.d.ts +11 -0
- package/dist/module-assets.d.ts.map +1 -0
- package/dist/module-assets.js +90 -0
- package/dist/module-assets.js.map +1 -0
- package/dist/npm.d.ts +23 -0
- package/dist/npm.d.ts.map +1 -0
- package/dist/npm.js +456 -0
- package/dist/npm.js.map +1 -0
- package/dist/official-language-server-extensions.d.ts +2 -0
- package/dist/official-language-server-extensions.d.ts.map +1 -0
- package/dist/official-language-server-extensions.js +38 -0
- package/dist/official-language-server-extensions.js.map +1 -0
- package/dist/official-script-language-service.d.ts +2 -0
- package/dist/official-script-language-service.d.ts.map +1 -0
- package/dist/official-script-language-service.js +10 -0
- package/dist/official-script-language-service.js.map +1 -0
- package/dist/official-tool-assets.d.ts +3 -0
- package/dist/official-tool-assets.d.ts.map +1 -0
- package/dist/official-tool-assets.js +41 -0
- package/dist/official-tool-assets.js.map +1 -0
- package/dist/ownership-graph.d.ts +46 -0
- package/dist/ownership-graph.d.ts.map +1 -0
- package/dist/ownership-graph.js +255 -0
- package/dist/ownership-graph.js.map +1 -0
- package/dist/package-manager.d.ts +20 -0
- package/dist/package-manager.d.ts.map +1 -0
- package/dist/package-manager.js +345 -0
- package/dist/package-manager.js.map +1 -0
- package/dist/packaged-official-tool.d.ts +8 -0
- package/dist/packaged-official-tool.d.ts.map +1 -0
- package/dist/packaged-official-tool.js +32 -0
- package/dist/packaged-official-tool.js.map +1 -0
- package/dist/preview-server.d.ts +11 -0
- package/dist/preview-server.d.ts.map +1 -0
- package/dist/preview-server.js +163 -0
- package/dist/preview-server.js.map +1 -0
- package/dist/production-build.d.ts +57 -0
- package/dist/production-build.d.ts.map +1 -0
- package/dist/production-build.js +332 -0
- package/dist/production-build.js.map +1 -0
- package/dist/production-verifier.d.ts +9 -0
- package/dist/production-verifier.d.ts.map +1 -0
- package/dist/production-verifier.js +268 -0
- package/dist/production-verifier.js.map +1 -0
- package/dist/program-runner.d.ts +7 -0
- package/dist/program-runner.d.ts.map +1 -0
- package/dist/program-runner.js +98 -0
- package/dist/program-runner.js.map +1 -0
- package/dist/project-check.d.ts +31 -0
- package/dist/project-check.d.ts.map +1 -0
- package/dist/project-check.js +95 -0
- package/dist/project-check.js.map +1 -0
- package/dist/project-format.d.ts +6 -0
- package/dist/project-format.d.ts.map +1 -0
- package/dist/project-format.js +21 -0
- package/dist/project-format.js.map +1 -0
- package/dist/project-semantic.d.ts +62 -0
- package/dist/project-semantic.d.ts.map +1 -0
- package/dist/project-semantic.js +1079 -0
- package/dist/project-semantic.js.map +1 -0
- package/dist/project-session.d.ts +36 -0
- package/dist/project-session.d.ts.map +1 -0
- package/dist/project-session.js +339 -0
- package/dist/project-session.js.map +1 -0
- package/dist/project-task-bundle-entry.d.ts +2 -0
- package/dist/project-task-bundle-entry.d.ts.map +1 -0
- package/dist/project-task-bundle-entry.js +18 -0
- package/dist/project-task-bundle-entry.js.map +1 -0
- package/dist/project-task-invocation.d.ts +16 -0
- package/dist/project-task-invocation.d.ts.map +1 -0
- package/dist/project-task-invocation.js +72 -0
- package/dist/project-task-invocation.js.map +1 -0
- package/dist/project-task-tool.d.ts +3 -0
- package/dist/project-task-tool.d.ts.map +1 -0
- package/dist/project-task-tool.js +61 -0
- package/dist/project-task-tool.js.map +1 -0
- package/dist/project.d.ts +71 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/project.js +1750 -0
- package/dist/project.js.map +1 -0
- package/dist/reproduction.d.ts +50 -0
- package/dist/reproduction.d.ts.map +1 -0
- package/dist/reproduction.js +321 -0
- package/dist/reproduction.js.map +1 -0
- package/dist/script-language-service.d.ts +82 -0
- package/dist/script-language-service.d.ts.map +1 -0
- package/dist/script-language-service.js +18 -0
- package/dist/script-language-service.js.map +1 -0
- package/dist/source-limits.d.ts +5 -0
- package/dist/source-limits.d.ts.map +1 -0
- package/dist/source-limits.js +19 -0
- package/dist/source-limits.js.map +1 -0
- package/dist/standalone-build.d.ts +15 -0
- package/dist/standalone-build.d.ts.map +1 -0
- package/dist/standalone-build.js +76 -0
- package/dist/standalone-build.js.map +1 -0
- package/dist/standard-modules.d.ts +22 -0
- package/dist/standard-modules.d.ts.map +1 -0
- package/dist/standard-modules.js +3066 -0
- package/dist/standard-modules.js.map +1 -0
- package/dist/static-deployment.d.ts +48 -0
- package/dist/static-deployment.d.ts.map +1 -0
- package/dist/static-deployment.js +142 -0
- package/dist/static-deployment.js.map +1 -0
- package/dist/test-output.d.ts +20 -0
- package/dist/test-output.d.ts.map +1 -0
- package/dist/test-output.js +119 -0
- package/dist/test-output.js.map +1 -0
- package/dist/test-runner.d.ts +13 -0
- package/dist/test-runner.d.ts.map +1 -0
- package/dist/test-runner.js +226 -0
- package/dist/test-runner.js.map +1 -0
- package/dist/typescript-declarations.d.ts +20 -0
- package/dist/typescript-declarations.d.ts.map +1 -0
- package/dist/typescript-declarations.js +1609 -0
- package/dist/typescript-declarations.js.map +1 -0
- package/dist/uncaught-program-error.d.ts +24 -0
- package/dist/uncaught-program-error.d.ts.map +1 -0
- package/dist/uncaught-program-error.js +105 -0
- package/dist/uncaught-program-error.js.map +1 -0
- package/dist/unowned-errors.d.ts +56 -0
- package/dist/unowned-errors.d.ts.map +1 -0
- package/dist/unowned-errors.js +82 -0
- package/dist/unowned-errors.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/dist/workspace-index.d.ts +78 -0
- package/dist/workspace-index.d.ts.map +1 -0
- package/dist/workspace-index.js +493 -0
- package/dist/workspace-index.js.map +1 -0
- package/package.json +45 -0
- package/skill/ai-skill.md +749 -0
|
@@ -0,0 +1,1609 @@
|
|
|
1
|
+
import { access, readFile, realpath, stat } from "node:fs/promises";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { describeType, optionalOf, readonlyViewOf, semanticTypeIdentity, unionOf, VELAR_BYTES_TYPE_IDENTITY, VELAR_FLOAT32_BUFFER_TYPE_IDENTITY, VELAR_UINT8_BUFFER_TYPE_IDENTITY, VELAR_UINT16_BUFFER_TYPE_IDENTITY, VELAR_UINT32_BUFFER_TYPE_IDENTITY, } from "@velarscript/compiler";
|
|
6
|
+
import { resolveInstalledPackageRoot } from "./installed-package.js";
|
|
7
|
+
function oppositeDirection(direction) {
|
|
8
|
+
return direction === "to-js" ? "from-js" : direction === "from-js" ? "to-js" : "invariant";
|
|
9
|
+
}
|
|
10
|
+
const unknownType = { kind: "unknown" };
|
|
11
|
+
const unsupportedType = { kind: "unknown", restricted: true };
|
|
12
|
+
const nullType = { kind: "null" };
|
|
13
|
+
const stringType = { kind: "string" };
|
|
14
|
+
const numberType = { kind: "number" };
|
|
15
|
+
const boolType = { kind: "bool" };
|
|
16
|
+
const bytesType = { kind: "named", name: "Bytes", identity: VELAR_BYTES_TYPE_IDENTITY };
|
|
17
|
+
const uint8BufferType = { kind: "named", name: "UInt8Buffer", identity: VELAR_UINT8_BUFFER_TYPE_IDENTITY };
|
|
18
|
+
const uint16BufferType = { kind: "named", name: "UInt16Buffer", identity: VELAR_UINT16_BUFFER_TYPE_IDENTITY };
|
|
19
|
+
const uint32BufferType = { kind: "named", name: "UInt32Buffer", identity: VELAR_UINT32_BUFFER_TYPE_IDENTITY };
|
|
20
|
+
const float32BufferType = { kind: "named", name: "Float32Buffer", identity: VELAR_FLOAT32_BUFFER_TYPE_IDENTITY };
|
|
21
|
+
const MAX_PACKAGE_MANIFEST_BYTES = 1024 * 1024;
|
|
22
|
+
const MAX_TYPESCRIPT_DECLARATION_BYTES = 2 * 1024 * 1024;
|
|
23
|
+
const MAX_TYPESCRIPT_DECLARATION_FILES = 64;
|
|
24
|
+
const MAX_TYPESCRIPT_DECLARATION_DEPTH = 16;
|
|
25
|
+
export async function loadTypeScriptDeclarations(source, importerPath) {
|
|
26
|
+
if (source.startsWith(".") || source.startsWith("/") || source.startsWith("#"))
|
|
27
|
+
return null;
|
|
28
|
+
const packageName = packageNameOf(source);
|
|
29
|
+
const subpath = source === packageName ? "." : `.${source.slice(packageName.length)}`;
|
|
30
|
+
const require = createRequire(pathToFileURL(join(dirname(importerPath), "__velar_types__.js")));
|
|
31
|
+
let root;
|
|
32
|
+
try {
|
|
33
|
+
root = await resolveInstalledPackageRoot(packageName, source, require);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
let runtimeEntry = null;
|
|
39
|
+
try {
|
|
40
|
+
runtimeEntry = require.resolve(source);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// An ESM-only package can still publish a complete `types` condition.
|
|
44
|
+
}
|
|
45
|
+
const manifestPath = join(root, "package.json");
|
|
46
|
+
let manifest;
|
|
47
|
+
try {
|
|
48
|
+
manifest = JSON.parse(await readLimitedText(manifestPath, MAX_PACKAGE_MANIFEST_BYTES, "package manifest"));
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
if (manifest.name !== undefined && manifest.name !== packageName)
|
|
54
|
+
return null;
|
|
55
|
+
const declared = exportedTypes(manifest.exports, subpath)
|
|
56
|
+
?? (subpath === "." ? stringValue(manifest.types) ?? stringValue(manifest.typings) : null);
|
|
57
|
+
const path = await firstDeclarationEntry(root, declared, runtimeEntry, subpath === ".");
|
|
58
|
+
if (!path) {
|
|
59
|
+
// BRG-U3: a declared types path that cannot be read fires the polite
|
|
60
|
+
// degradation notice instead of degrading in silence.
|
|
61
|
+
if (declared !== null) {
|
|
62
|
+
return {
|
|
63
|
+
...emptyDeclarationBridge(manifestPath, `package '${packageName}' declares types '${declared}', but that is not a readable declaration file inside the package; the import is typed as unknown`),
|
|
64
|
+
unreadableDeclaredTypes: true,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
const bridge = await loadTypeScriptDeclarationGraph(root, path, packageName);
|
|
71
|
+
return { ...bridge, dependencies: [...unique([manifestPath, ...bridge.dependencies])].sort() };
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function firstDeclarationEntry(root, declared, runtimeEntry, rootFallback) {
|
|
78
|
+
const rootPath = await realpath(root);
|
|
79
|
+
const candidates = [];
|
|
80
|
+
if (declared && !isAbsolute(declared)) {
|
|
81
|
+
const target = resolve(root, declared);
|
|
82
|
+
if (insideRoot(root, target))
|
|
83
|
+
candidates.push(...declarationFileCandidates(target));
|
|
84
|
+
}
|
|
85
|
+
if (runtimeEntry)
|
|
86
|
+
candidates.push(...declarationFileCandidates(runtimeEntry));
|
|
87
|
+
if (rootFallback)
|
|
88
|
+
candidates.push(join(root, "index.d.ts"), join(root, "index.d.mts"), join(root, "index.d.cts"));
|
|
89
|
+
for (const candidate of unique(candidates)) {
|
|
90
|
+
try {
|
|
91
|
+
const path = await realpath(candidate);
|
|
92
|
+
if (!insideRoot(rootPath, path) || !isDeclarationFile(path) || !(await stat(path)).isFile())
|
|
93
|
+
continue;
|
|
94
|
+
return path;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// Try the next declaration convention.
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
function declarationFileCandidates(path) {
|
|
103
|
+
if (isDeclarationFile(path))
|
|
104
|
+
return [path];
|
|
105
|
+
if (path.endsWith(".mjs"))
|
|
106
|
+
return [`${path.slice(0, -4)}.d.mts`, `${path.slice(0, -4)}.d.ts`];
|
|
107
|
+
if (path.endsWith(".cjs"))
|
|
108
|
+
return [`${path.slice(0, -4)}.d.cts`, `${path.slice(0, -4)}.d.ts`];
|
|
109
|
+
if (path.endsWith(".js"))
|
|
110
|
+
return [`${path.slice(0, -3)}.d.ts`];
|
|
111
|
+
return [`${path}.d.ts`, `${path}.d.mts`, `${path}.d.cts`, join(path, "index.d.ts"), join(path, "index.d.mts"), join(path, "index.d.cts")];
|
|
112
|
+
}
|
|
113
|
+
function isDeclarationFile(path) {
|
|
114
|
+
return path.endsWith(".d.ts") || path.endsWith(".d.mts") || path.endsWith(".d.cts");
|
|
115
|
+
}
|
|
116
|
+
async function loadTypeScriptDeclarationGraph(root, entry, packageSource) {
|
|
117
|
+
const [rootPath, entryPath] = await Promise.all([realpath(root), realpath(entry)]);
|
|
118
|
+
if (!insideRoot(rootPath, entryPath))
|
|
119
|
+
throw new Error("TypeScript declaration entry escapes its package root");
|
|
120
|
+
const cache = new Map();
|
|
121
|
+
const visiting = new Set();
|
|
122
|
+
const counted = new Set();
|
|
123
|
+
let totalBytes = 0;
|
|
124
|
+
const load = async (path, depth) => {
|
|
125
|
+
const cached = cache.get(path);
|
|
126
|
+
if (cached)
|
|
127
|
+
return cached;
|
|
128
|
+
if (depth > MAX_TYPESCRIPT_DECLARATION_DEPTH) {
|
|
129
|
+
return emptyDeclarationBridge(path, `TypeScript declaration re-export depth exceeds ${MAX_TYPESCRIPT_DECLARATION_DEPTH}`);
|
|
130
|
+
}
|
|
131
|
+
if (visiting.has(path))
|
|
132
|
+
return emptyDeclarationBridge(path, "Cyclic TypeScript declaration re-export was kept as unknown");
|
|
133
|
+
if (!counted.has(path)) {
|
|
134
|
+
if (counted.size >= MAX_TYPESCRIPT_DECLARATION_FILES) {
|
|
135
|
+
return emptyDeclarationBridge(path, `TypeScript declaration graph exceeds ${MAX_TYPESCRIPT_DECLARATION_FILES} files`);
|
|
136
|
+
}
|
|
137
|
+
const metadata = await stat(path);
|
|
138
|
+
if (!metadata.isFile())
|
|
139
|
+
return emptyDeclarationBridge(path, "TypeScript declaration re-export is not a regular file");
|
|
140
|
+
if (metadata.size > MAX_TYPESCRIPT_DECLARATION_BYTES || totalBytes + metadata.size > MAX_TYPESCRIPT_DECLARATION_BYTES) {
|
|
141
|
+
return emptyDeclarationBridge(path, "TypeScript declaration graph exceeds the 2 MiB aggregate limit");
|
|
142
|
+
}
|
|
143
|
+
counted.add(path);
|
|
144
|
+
totalBytes += metadata.size;
|
|
145
|
+
}
|
|
146
|
+
visiting.add(path);
|
|
147
|
+
try {
|
|
148
|
+
const source = await readLimitedText(path, MAX_TYPESCRIPT_DECLARATION_BYTES, "TypeScript declaration");
|
|
149
|
+
const origin = `${packageSource}/${relative(rootPath, path).replaceAll("\\", "/")}`;
|
|
150
|
+
const importedTypes = new Map();
|
|
151
|
+
const importedRegistry = new Map();
|
|
152
|
+
const importWarnings = [];
|
|
153
|
+
for (const declarationImport of declarationImports(source)) {
|
|
154
|
+
if (declarationImport.unsupported) {
|
|
155
|
+
importWarnings.push(`Namespace declaration import from '${declarationImport.source}' is outside the VelarScript declaration bridge and was kept as unknown`);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
const importedPath = await resolveDeclarationReexport(rootPath, path, declarationImport.source);
|
|
159
|
+
if (!importedPath) {
|
|
160
|
+
importWarnings.push(`Declaration import '${declarationImport.source}' is not a package-local declaration file and was kept as unknown`);
|
|
161
|
+
for (const item of declarationImport.names)
|
|
162
|
+
importedTypes.set(item.local, unknownType);
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const imported = await load(importedPath, depth + 1);
|
|
166
|
+
importWarnings.push(...imported.warnings.map((warning) => `${relative(rootPath, importedPath).replaceAll("\\", "/")}: ${warning}`));
|
|
167
|
+
for (const [identity, info] of imported.classRegistry)
|
|
168
|
+
importedRegistry.set(identity, info);
|
|
169
|
+
for (const item of declarationImport.names) {
|
|
170
|
+
const type = imported.typeExports.get(item.imported);
|
|
171
|
+
if (type)
|
|
172
|
+
importedTypes.set(item.local, renameDeclarationType(type, item.local));
|
|
173
|
+
else {
|
|
174
|
+
importedTypes.set(item.local, unknownType);
|
|
175
|
+
importWarnings.push(`Declaration type import '${item.imported}' from '${declarationImport.source}' was not found and was kept as unknown`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const local = parseTypeScriptDeclarations(source, path, origin, true, importedTypes, importedRegistry);
|
|
180
|
+
const exports = new Map(local.exports);
|
|
181
|
+
const typeExports = new Map(local.typeExports);
|
|
182
|
+
const classes = new Map(local.classes);
|
|
183
|
+
const classRegistry = new Map([...importedRegistry, ...local.classRegistry]);
|
|
184
|
+
const warnings = [...importWarnings, ...local.warnings];
|
|
185
|
+
const explicit = new Set(exports.keys());
|
|
186
|
+
const explicitTypes = new Set(typeExports.keys());
|
|
187
|
+
const reexports = declarationReexports(source);
|
|
188
|
+
for (const reexport of reexports.filter((item) => item.names !== null)) {
|
|
189
|
+
const childPath = await resolveDeclarationReexport(rootPath, path, reexport.source);
|
|
190
|
+
if (!childPath) {
|
|
191
|
+
warnings.push(`Declaration re-export '${reexport.source}' is not a package-local .d.ts file and was kept as unknown`);
|
|
192
|
+
for (const item of reexport.names ?? []) {
|
|
193
|
+
if (!item.typeOnly) {
|
|
194
|
+
exports.set(item.exported, unknownType);
|
|
195
|
+
classes.delete(item.exported);
|
|
196
|
+
explicit.add(item.exported);
|
|
197
|
+
}
|
|
198
|
+
typeExports.set(item.exported, unknownType);
|
|
199
|
+
explicitTypes.add(item.exported);
|
|
200
|
+
}
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
const child = await load(childPath, depth + 1);
|
|
204
|
+
for (const [identity, info] of child.classRegistry)
|
|
205
|
+
classRegistry.set(identity, info);
|
|
206
|
+
warnings.push(...child.warnings.map((warning) => `${relative(rootPath, childPath).replaceAll("\\", "/")}: ${warning}`));
|
|
207
|
+
for (const item of reexport.names ?? []) {
|
|
208
|
+
const typeExport = child.typeExports.get(item.imported);
|
|
209
|
+
if (!item.typeOnly && explicit.has(item.exported)) {
|
|
210
|
+
warnings.push(`Duplicate explicit declaration export '${item.exported}' was kept as unknown`);
|
|
211
|
+
exports.set(item.exported, unknownType);
|
|
212
|
+
classes.delete(item.exported);
|
|
213
|
+
}
|
|
214
|
+
else if (!item.typeOnly) {
|
|
215
|
+
const type = child.exports.get(item.imported);
|
|
216
|
+
if (type) {
|
|
217
|
+
exports.set(item.exported, renameDeclarationExport(type, item.exported));
|
|
218
|
+
const info = child.classes.get(item.imported);
|
|
219
|
+
if (info)
|
|
220
|
+
classes.set(item.exported, info);
|
|
221
|
+
}
|
|
222
|
+
else if (!typeExport) {
|
|
223
|
+
warnings.push(`Declaration re-export '${item.imported}' from '${reexport.source}' was not found and was kept as unknown`);
|
|
224
|
+
exports.set(item.exported, unknownType);
|
|
225
|
+
}
|
|
226
|
+
explicit.add(item.exported);
|
|
227
|
+
}
|
|
228
|
+
if (explicitTypes.has(item.exported)) {
|
|
229
|
+
warnings.push(`Duplicate explicit declaration type export '${item.exported}' was kept as unknown`);
|
|
230
|
+
typeExports.set(item.exported, unknownType);
|
|
231
|
+
}
|
|
232
|
+
else if (typeExport) {
|
|
233
|
+
typeExports.set(item.exported, renameDeclarationType(typeExport, item.exported));
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
typeExports.set(item.exported, unknownType);
|
|
237
|
+
}
|
|
238
|
+
explicitTypes.add(item.exported);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const starKeys = new Map();
|
|
242
|
+
const starTypeKeys = new Map();
|
|
243
|
+
for (const reexport of reexports.filter((item) => item.names === null)) {
|
|
244
|
+
const childPath = await resolveDeclarationReexport(rootPath, path, reexport.source);
|
|
245
|
+
if (!childPath) {
|
|
246
|
+
warnings.push(`Declaration re-export '${reexport.source}' is not a package-local .d.ts file and was ignored`);
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
const child = await load(childPath, depth + 1);
|
|
250
|
+
for (const [identity, info] of child.classRegistry)
|
|
251
|
+
classRegistry.set(identity, info);
|
|
252
|
+
warnings.push(...child.warnings.map((warning) => `${relative(rootPath, childPath).replaceAll("\\", "/")}: ${warning}`));
|
|
253
|
+
for (const [name, type] of child.exports) {
|
|
254
|
+
if (name === "default" || explicit.has(name))
|
|
255
|
+
continue;
|
|
256
|
+
const key = semanticTypeIdentity(type);
|
|
257
|
+
const previous = starKeys.get(name);
|
|
258
|
+
if (previous && previous !== key) {
|
|
259
|
+
warnings.push(`Ambiguous declaration star export '${name}' was kept as unknown`);
|
|
260
|
+
exports.set(name, unknownType);
|
|
261
|
+
classes.delete(name);
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
if (!previous) {
|
|
265
|
+
starKeys.set(name, key);
|
|
266
|
+
exports.set(name, type);
|
|
267
|
+
const info = child.classes.get(name);
|
|
268
|
+
if (info)
|
|
269
|
+
classes.set(name, info);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
for (const [name, type] of child.typeExports) {
|
|
273
|
+
if (name === "default" || explicitTypes.has(name))
|
|
274
|
+
continue;
|
|
275
|
+
const key = semanticTypeIdentity(type);
|
|
276
|
+
const previous = starTypeKeys.get(name);
|
|
277
|
+
if (previous && previous !== key) {
|
|
278
|
+
warnings.push(`Ambiguous declaration star type export '${name}' was kept as unknown`);
|
|
279
|
+
typeExports.set(name, unknownType);
|
|
280
|
+
}
|
|
281
|
+
else if (!previous) {
|
|
282
|
+
starTypeKeys.set(name, key);
|
|
283
|
+
typeExports.set(name, type);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
const bridge = { path, dependencies: [path], exports, typeExports, classes, classRegistry, warnings: unique(warnings) };
|
|
288
|
+
cache.set(path, bridge);
|
|
289
|
+
return bridge;
|
|
290
|
+
}
|
|
291
|
+
finally {
|
|
292
|
+
visiting.delete(path);
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
const bridge = await load(entryPath, 0);
|
|
296
|
+
return { ...bridge, dependencies: [...counted].sort() };
|
|
297
|
+
}
|
|
298
|
+
function emptyDeclarationBridge(path, warning) {
|
|
299
|
+
return { path, dependencies: [path], exports: new Map(), typeExports: new Map(), classes: new Map(), classRegistry: new Map(), warnings: [warning] };
|
|
300
|
+
}
|
|
301
|
+
function declarationReexports(source) {
|
|
302
|
+
const text = source.replace(/\/\*[\s\S]*?\*\//gu, "").replace(/\/\/[^\n]*/gu, "");
|
|
303
|
+
const output = [];
|
|
304
|
+
const named = /export\s+(type\s+)?\{([\s\S]*?)\}\s*from\s*["']([^"']+)["']\s*;?/gu;
|
|
305
|
+
for (const match of text.matchAll(named)) {
|
|
306
|
+
const allTypeOnly = Boolean(match[1]);
|
|
307
|
+
const names = splitTopLevel(match[2] ?? "", ",").flatMap((part) => {
|
|
308
|
+
const specifier = /^(type\s+)?([A-Za-z_$][\w$]*|default)(?:\s+as\s+([A-Za-z_$][\w$]*|default))?$/u.exec(part.trim());
|
|
309
|
+
if (!specifier)
|
|
310
|
+
return [];
|
|
311
|
+
return [{ imported: specifier[2], exported: specifier[3] ?? specifier[2], typeOnly: allTypeOnly || Boolean(specifier[1]) }];
|
|
312
|
+
});
|
|
313
|
+
output.push({ source: match[3], names });
|
|
314
|
+
}
|
|
315
|
+
const star = /export\s+\*\s+from\s*["']([^"']+)["']\s*;?/gu;
|
|
316
|
+
for (const match of text.matchAll(star))
|
|
317
|
+
output.push({ source: match[1], names: null });
|
|
318
|
+
return output;
|
|
319
|
+
}
|
|
320
|
+
function declarationImports(source) {
|
|
321
|
+
const text = source.replace(/\/\*[\s\S]*?\*\//gu, "").replace(/\/\/[^\n]*/gu, "");
|
|
322
|
+
const output = [];
|
|
323
|
+
const pattern = /^[ \t]*import\s+(?:type\s+)?([\s\S]*?)\s+from\s*["']([^"']+)["']\s*;?/gmu;
|
|
324
|
+
for (const match of text.matchAll(pattern)) {
|
|
325
|
+
const clause = (match[1] ?? "").trim();
|
|
326
|
+
if (!clause || clause.startsWith("*")) {
|
|
327
|
+
output.push({ source: match[2], names: [], unsupported: clause.startsWith("*") });
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
const names = [];
|
|
331
|
+
const braceStart = clause.indexOf("{");
|
|
332
|
+
const braceEnd = clause.lastIndexOf("}");
|
|
333
|
+
const defaultName = (braceStart >= 0 ? clause.slice(0, braceStart).replace(/,$/u, "") : clause).trim();
|
|
334
|
+
if (defaultName && /^[A-Za-z_$][\w$]*$/u.test(defaultName))
|
|
335
|
+
names.push({ imported: "default", local: defaultName });
|
|
336
|
+
if (braceStart >= 0 && braceEnd > braceStart) {
|
|
337
|
+
for (const part of splitTopLevel(clause.slice(braceStart + 1, braceEnd), ",")) {
|
|
338
|
+
const specifier = /^(?:type\s+)?([A-Za-z_$][\w$]*|default)(?:\s+as\s+([A-Za-z_$][\w$]*))?$/u.exec(part.trim());
|
|
339
|
+
if (specifier)
|
|
340
|
+
names.push({ imported: specifier[1], local: specifier[2] ?? specifier[1] });
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
output.push({ source: match[2], names, unsupported: false });
|
|
344
|
+
}
|
|
345
|
+
return output;
|
|
346
|
+
}
|
|
347
|
+
function localDeclarationExports(source) {
|
|
348
|
+
const output = [];
|
|
349
|
+
const pattern = /export\s+(type\s+)?\{([\s\S]*?)\}\s*(?!from\b)(?:;|$)/gmu;
|
|
350
|
+
for (const match of source.matchAll(pattern)) {
|
|
351
|
+
const allTypeOnly = Boolean(match[1]);
|
|
352
|
+
for (const part of splitTopLevel(match[2] ?? "", ",")) {
|
|
353
|
+
const specifier = /^(type\s+)?([A-Za-z_$][\w$]*)(?:\s+as\s+([A-Za-z_$][\w$]*|default))?$/u.exec(part.trim());
|
|
354
|
+
if (!specifier)
|
|
355
|
+
continue;
|
|
356
|
+
output.push({
|
|
357
|
+
local: specifier[2],
|
|
358
|
+
exported: specifier[3] ?? specifier[2],
|
|
359
|
+
typeOnly: allTypeOnly || Boolean(specifier[1]),
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
const defaultPattern = /export\s+default\s+([A-Za-z_$][\w$]*)\s*;/gu;
|
|
364
|
+
for (const match of source.matchAll(defaultPattern)) {
|
|
365
|
+
output.push({ local: match[1], exported: "default", typeOnly: false });
|
|
366
|
+
}
|
|
367
|
+
return output;
|
|
368
|
+
}
|
|
369
|
+
async function resolveDeclarationReexport(root, importer, source) {
|
|
370
|
+
if (!source.startsWith("."))
|
|
371
|
+
return null;
|
|
372
|
+
const unresolved = resolve(dirname(importer), source);
|
|
373
|
+
const candidates = declarationFileCandidates(unresolved);
|
|
374
|
+
for (const candidate of candidates) {
|
|
375
|
+
try {
|
|
376
|
+
const path = await realpath(candidate);
|
|
377
|
+
if (!insideRoot(root, path) || !isDeclarationFile(path) || !(await stat(path)).isFile())
|
|
378
|
+
continue;
|
|
379
|
+
return path;
|
|
380
|
+
}
|
|
381
|
+
catch {
|
|
382
|
+
// Try the next declaration-file convention.
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
function insideRoot(root, path) {
|
|
388
|
+
const value = relative(root, path);
|
|
389
|
+
return value === "" || (!value.startsWith("..") && !isAbsolute(value));
|
|
390
|
+
}
|
|
391
|
+
function renameDeclarationExport(type, name) {
|
|
392
|
+
return type.kind === "classConstructor" ? { ...type, name } : type;
|
|
393
|
+
}
|
|
394
|
+
function renameDeclarationType(type, name) {
|
|
395
|
+
return type.kind === "class" ? { ...type, name } : type;
|
|
396
|
+
}
|
|
397
|
+
export function parseTypeScriptDeclarations(source, path = "<types>", moduleSource = path, reexportsHandled = false, importedTypes = new Map(), importedClassRegistry = new Map()) {
|
|
398
|
+
if (Buffer.byteLength(source, "utf8") > MAX_TYPESCRIPT_DECLARATION_BYTES) {
|
|
399
|
+
throw new RangeError(`${path} exceeds the 2 MiB TypeScript declaration bridge limit`);
|
|
400
|
+
}
|
|
401
|
+
const text = source.replace(/\/\*[\s\S]*?\*\//gu, "").replace(/\/\/[^\n]*/gu, "");
|
|
402
|
+
const warnings = [];
|
|
403
|
+
const aliases = new Map();
|
|
404
|
+
const interfaces = new Map();
|
|
405
|
+
const invalidInterfaces = new Set();
|
|
406
|
+
const directlyExportedTypes = new Set();
|
|
407
|
+
const interfacePattern = /(export\s+)?interface\s+([A-Za-z_$][\w$]*)(?:\s+extends\s+([^\{]+))?\s*\{([^{}]*)\}/gu;
|
|
408
|
+
for (const match of text.matchAll(interfacePattern)) {
|
|
409
|
+
const name = match[2];
|
|
410
|
+
if (match[1])
|
|
411
|
+
directlyExportedTypes.add(name);
|
|
412
|
+
if (interfaces.has(name)) {
|
|
413
|
+
warnings.push(`Merged interface '${name}' is outside the VelarScript declaration bridge and was kept as unknown`);
|
|
414
|
+
invalidInterfaces.add(name);
|
|
415
|
+
}
|
|
416
|
+
interfaces.set(name, {
|
|
417
|
+
bases: (match[3] ?? "").split(",").map((base) => base.trim()).filter(Boolean),
|
|
418
|
+
body: match[4] ?? "",
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
const aliasPattern = /(export\s+)?type\s+([A-Za-z_$][\w$]*)(?:\s*<[^=]+>)?\s*=\s*([^;]+);/gu;
|
|
422
|
+
for (const match of text.matchAll(aliasPattern)) {
|
|
423
|
+
const name = match[2];
|
|
424
|
+
if (match[1])
|
|
425
|
+
directlyExportedTypes.add(name);
|
|
426
|
+
if (aliases.has(name) || interfaces.has(name)) {
|
|
427
|
+
warnings.push(`Merged declaration '${name}' is outside the VelarScript declaration bridge and was kept as unknown`);
|
|
428
|
+
aliases.set(name, "unknown");
|
|
429
|
+
invalidInterfaces.add(name);
|
|
430
|
+
}
|
|
431
|
+
else
|
|
432
|
+
aliases.set(name, match[3] ?? "unknown");
|
|
433
|
+
}
|
|
434
|
+
const resolvingInterfaces = new Set();
|
|
435
|
+
const resolveInterface = (name) => {
|
|
436
|
+
if (invalidInterfaces.has(name))
|
|
437
|
+
return "unknown";
|
|
438
|
+
const cached = aliases.get(name);
|
|
439
|
+
if (cached)
|
|
440
|
+
return cached;
|
|
441
|
+
const declaration = interfaces.get(name);
|
|
442
|
+
if (!declaration)
|
|
443
|
+
return "unknown";
|
|
444
|
+
if (resolvingInterfaces.has(name)) {
|
|
445
|
+
warnings.push(`Recursive interface '${name}' is outside the VelarScript declaration bridge and was kept as unknown`);
|
|
446
|
+
invalidInterfaces.add(name);
|
|
447
|
+
return "unknown";
|
|
448
|
+
}
|
|
449
|
+
resolvingInterfaces.add(name);
|
|
450
|
+
const bodies = [];
|
|
451
|
+
for (const baseName of declaration.bases) {
|
|
452
|
+
if (!/^[A-Za-z_$][\w$]*$/u.test(baseName)) {
|
|
453
|
+
warnings.push(`Generic or complex interface base '${baseName}' is outside the VelarScript declaration bridge and '${name}' was kept as unknown`);
|
|
454
|
+
invalidInterfaces.add(name);
|
|
455
|
+
resolvingInterfaces.delete(name);
|
|
456
|
+
return "unknown";
|
|
457
|
+
}
|
|
458
|
+
const base = interfaces.has(baseName) ? resolveInterface(baseName) : aliases.get(baseName);
|
|
459
|
+
if (!base?.startsWith("{") || !base.endsWith("}")) {
|
|
460
|
+
warnings.push(`Interface base '${baseName}' cannot be expanded and '${name}' was kept as unknown`);
|
|
461
|
+
invalidInterfaces.add(name);
|
|
462
|
+
resolvingInterfaces.delete(name);
|
|
463
|
+
return "unknown";
|
|
464
|
+
}
|
|
465
|
+
bodies.push(base.slice(1, -1));
|
|
466
|
+
}
|
|
467
|
+
bodies.push(declaration.body);
|
|
468
|
+
resolvingInterfaces.delete(name);
|
|
469
|
+
const resolved = `{${bodies.filter(Boolean).join(";")}}`;
|
|
470
|
+
aliases.set(name, resolved);
|
|
471
|
+
return resolved;
|
|
472
|
+
};
|
|
473
|
+
for (const name of interfaces.keys())
|
|
474
|
+
resolveInterface(name);
|
|
475
|
+
const exports = new Map();
|
|
476
|
+
const typeExports = new Map();
|
|
477
|
+
const classes = new Map();
|
|
478
|
+
const classRegistry = new Map();
|
|
479
|
+
const localValues = new Map();
|
|
480
|
+
const localTypes = new Map();
|
|
481
|
+
const localClasses = new Map();
|
|
482
|
+
const directRuntimeExports = [];
|
|
483
|
+
const classDeclarations = readClassDeclarations(text, warnings);
|
|
484
|
+
const knownTypes = new Map(importedTypes);
|
|
485
|
+
for (const declaration of classDeclarations) {
|
|
486
|
+
const identity = `js:${moduleSource}#${declaration.name}`;
|
|
487
|
+
const type = { kind: "class", name: declaration.name, identity };
|
|
488
|
+
knownTypes.set(declaration.name, type);
|
|
489
|
+
}
|
|
490
|
+
const parse = (value, direction = "from-js") => parseTsType(value, aliases, warnings, new Set(), knownTypes, direction);
|
|
491
|
+
const classCounts = new Map();
|
|
492
|
+
for (const declaration of classDeclarations)
|
|
493
|
+
classCounts.set(declaration.name, (classCounts.get(declaration.name) ?? 0) + 1);
|
|
494
|
+
for (const declaration of classDeclarations) {
|
|
495
|
+
if (declaration.directExportName)
|
|
496
|
+
directRuntimeExports.push({ local: declaration.name, exported: declaration.directExportName });
|
|
497
|
+
if ((classCounts.get(declaration.name) ?? 0) > 1) {
|
|
498
|
+
if (!localTypes.has(declaration.name))
|
|
499
|
+
warnings.push(`Duplicate class declaration '${declaration.name}' is outside the VelarScript declaration bridge and was kept as unknown`);
|
|
500
|
+
localTypes.set(declaration.name, unknownType);
|
|
501
|
+
localValues.set(declaration.name, unknownType);
|
|
502
|
+
knownTypes.set(declaration.name, unknownType);
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
if (interfaces.has(declaration.name) || aliases.has(declaration.name)) {
|
|
506
|
+
warnings.push(`Merged class declaration '${declaration.name}' is outside the VelarScript declaration bridge and was kept as unknown`);
|
|
507
|
+
localTypes.set(declaration.name, unknownType);
|
|
508
|
+
localValues.set(declaration.name, unknownType);
|
|
509
|
+
knownTypes.set(declaration.name, unknownType);
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
const selfType = knownTypes.get(declaration.name);
|
|
513
|
+
if (selfType)
|
|
514
|
+
knownTypes.set("this", selfType);
|
|
515
|
+
const parsed = parseClassDeclaration(declaration, parse, warnings, moduleSource, knownTypes);
|
|
516
|
+
knownTypes.delete("this");
|
|
517
|
+
if (!parsed) {
|
|
518
|
+
localTypes.set(declaration.name, unknownType);
|
|
519
|
+
localValues.set(declaration.name, unknownType);
|
|
520
|
+
knownTypes.set(declaration.name, unknownType);
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
localClasses.set(declaration.name, parsed);
|
|
524
|
+
}
|
|
525
|
+
let removedBase = true;
|
|
526
|
+
while (removedBase) {
|
|
527
|
+
removedBase = false;
|
|
528
|
+
const accepted = new Set([
|
|
529
|
+
...[...localClasses.values()].map((info) => info.identity).filter((identity) => Boolean(identity)),
|
|
530
|
+
...importedClassRegistry.keys(),
|
|
531
|
+
]);
|
|
532
|
+
for (const [name, info] of localClasses) {
|
|
533
|
+
if (!info.base || info.base === "Error" || accepted.has(info.base))
|
|
534
|
+
continue;
|
|
535
|
+
warnings.push(`Class declaration '${name}' has an unsupported base contract and was kept as unknown`);
|
|
536
|
+
localClasses.delete(name);
|
|
537
|
+
knownTypes.set(name, unknownType);
|
|
538
|
+
removedBase = true;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
const byIdentity = new Map([
|
|
542
|
+
...[...importedClassRegistry].map(([identity, info]) => [identity, { name: identity, info }]),
|
|
543
|
+
...[...localClasses].flatMap(([name, info]) => info.identity ? [[info.identity, { name, info }]] : []),
|
|
544
|
+
]);
|
|
545
|
+
for (const [name, info] of [...localClasses]) {
|
|
546
|
+
const visited = new Set();
|
|
547
|
+
let current = info;
|
|
548
|
+
while (current?.identity) {
|
|
549
|
+
if (visited.has(current.identity)) {
|
|
550
|
+
warnings.push(`Class declaration '${name}' has cyclic inheritance and was kept as unknown`);
|
|
551
|
+
localClasses.delete(name);
|
|
552
|
+
knownTypes.set(name, unknownType);
|
|
553
|
+
break;
|
|
554
|
+
}
|
|
555
|
+
visited.add(current.identity);
|
|
556
|
+
current = current.base ? byIdentity.get(current.base)?.info : undefined;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
const acceptedByIdentity = new Map([
|
|
560
|
+
...[...importedClassRegistry].map(([identity, info]) => [identity, { name: identity, info }]),
|
|
561
|
+
...[...localClasses].flatMap(([name, info]) => info.identity ? [[info.identity, { name, info }]] : []),
|
|
562
|
+
]);
|
|
563
|
+
const inheritedMember = (info, name) => {
|
|
564
|
+
const visited = new Set();
|
|
565
|
+
let base = info.base;
|
|
566
|
+
while (base && !visited.has(base)) {
|
|
567
|
+
visited.add(base);
|
|
568
|
+
const parent = acceptedByIdentity.get(base)?.info;
|
|
569
|
+
if (!parent)
|
|
570
|
+
return null;
|
|
571
|
+
const field = parent.fields.get(name);
|
|
572
|
+
if (field)
|
|
573
|
+
return { kind: "field", mutable: field.mutable, type: field.type };
|
|
574
|
+
const method = parent.methods.get(name);
|
|
575
|
+
if (method)
|
|
576
|
+
return { kind: "method", type: method };
|
|
577
|
+
base = parent.base;
|
|
578
|
+
}
|
|
579
|
+
return null;
|
|
580
|
+
};
|
|
581
|
+
for (const [name, info] of [...localClasses]) {
|
|
582
|
+
let incompatible = false;
|
|
583
|
+
for (const [memberName, field] of info.fields) {
|
|
584
|
+
const inherited = inheritedMember(info, memberName);
|
|
585
|
+
if (inherited && (inherited.kind !== "field" || inherited.mutable !== field.mutable || semanticTypeIdentity(inherited.type) !== semanticTypeIdentity(field.type)))
|
|
586
|
+
incompatible = true;
|
|
587
|
+
}
|
|
588
|
+
for (const [memberName, method] of info.methods) {
|
|
589
|
+
const inherited = inheritedMember(info, memberName);
|
|
590
|
+
if (inherited && (inherited.kind !== "method" || semanticTypeIdentity(inherited.type) !== semanticTypeIdentity(method)))
|
|
591
|
+
incompatible = true;
|
|
592
|
+
}
|
|
593
|
+
if (incompatible) {
|
|
594
|
+
warnings.push(`Class declaration '${name}' has an incompatible inherited member contract and was kept as unknown`);
|
|
595
|
+
localClasses.delete(name);
|
|
596
|
+
knownTypes.set(name, unknownType);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
let pruned = true;
|
|
600
|
+
while (pruned) {
|
|
601
|
+
pruned = false;
|
|
602
|
+
const accepted = new Set([
|
|
603
|
+
...[...localClasses.values()].map((info) => info.identity).filter((identity) => Boolean(identity)),
|
|
604
|
+
...importedClassRegistry.keys(),
|
|
605
|
+
]);
|
|
606
|
+
for (const [name, info] of localClasses) {
|
|
607
|
+
if (!info.base || info.base === "Error" || accepted.has(info.base))
|
|
608
|
+
continue;
|
|
609
|
+
warnings.push(`Class declaration '${name}' has an unsupported base contract and was kept as unknown`);
|
|
610
|
+
localClasses.delete(name);
|
|
611
|
+
knownTypes.set(name, unknownType);
|
|
612
|
+
pruned = true;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
for (const name of new Set([...interfaces.keys(), ...aliases.keys()]))
|
|
616
|
+
localTypes.set(name, parse(name));
|
|
617
|
+
for (const declaration of classDeclarations) {
|
|
618
|
+
if ((classCounts.get(declaration.name) ?? 0) > 1 || interfaces.has(declaration.name) || aliases.has(declaration.name))
|
|
619
|
+
continue;
|
|
620
|
+
const info = localClasses.get(declaration.name);
|
|
621
|
+
if (!info?.identity) {
|
|
622
|
+
localTypes.set(declaration.name, unknownType);
|
|
623
|
+
localValues.set(declaration.name, unknownType);
|
|
624
|
+
continue;
|
|
625
|
+
}
|
|
626
|
+
const instance = { kind: "class", name: declaration.name, identity: info.identity };
|
|
627
|
+
localTypes.set(declaration.name, instance);
|
|
628
|
+
localValues.set(declaration.name, { kind: "classConstructor", name: declaration.name, identity: info.identity });
|
|
629
|
+
classRegistry.set(info.identity, info);
|
|
630
|
+
}
|
|
631
|
+
const setLocalValue = (name, type, label) => {
|
|
632
|
+
if (localValues.has(name)) {
|
|
633
|
+
warnings.push(`Overloaded export '${name}' or merged local ${label} declaration is outside the VelarScript declaration bridge and was kept as unknown`);
|
|
634
|
+
localValues.set(name, unknownType);
|
|
635
|
+
}
|
|
636
|
+
else
|
|
637
|
+
localValues.set(name, type);
|
|
638
|
+
};
|
|
639
|
+
const functions = /(export\s+)?(?:declare\s+)?(default\s+)?function\s+([A-Za-z_$][\w$]*)/gu;
|
|
640
|
+
for (const match of text.matchAll(functions)) {
|
|
641
|
+
const local = match[3];
|
|
642
|
+
const exported = match[2] ? "default" : local;
|
|
643
|
+
if (match[1])
|
|
644
|
+
directRuntimeExports.push({ local, exported });
|
|
645
|
+
const signature = readFunctionSignature(text, match.index + match[0].length);
|
|
646
|
+
let type = unknownType;
|
|
647
|
+
if (!signature)
|
|
648
|
+
warnings.push(`Function declaration '${local}' has an unsupported declaration and was kept as unknown`);
|
|
649
|
+
else if (signature.generic)
|
|
650
|
+
warnings.push(`Generic function '${local}' is outside the VelarScript declaration bridge and was kept as unknown`);
|
|
651
|
+
else {
|
|
652
|
+
const parameters = parseParameters(signature.parameters, (value) => parse(value, "to-js"), warnings);
|
|
653
|
+
type = parameters.invalid ? unsupportedType : {
|
|
654
|
+
kind: "function",
|
|
655
|
+
parameters: parameters.types,
|
|
656
|
+
requiredParameters: parameters.required,
|
|
657
|
+
...(parameters.rest ? { rest: parameters.rest } : {}),
|
|
658
|
+
result: parse(signature.result, "from-js"),
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
setLocalValue(local, type, "function");
|
|
662
|
+
}
|
|
663
|
+
for (const declaration of constantDeclarations(text)) {
|
|
664
|
+
if (declaration.exported)
|
|
665
|
+
directRuntimeExports.push({ local: declaration.name, exported: declaration.name });
|
|
666
|
+
setLocalValue(declaration.name, parse(declaration.type), "const");
|
|
667
|
+
}
|
|
668
|
+
const setTypeExport = (exported, type) => {
|
|
669
|
+
if (typeExports.has(exported)) {
|
|
670
|
+
warnings.push(`Duplicate explicit declaration type export '${exported}' was kept as unknown`);
|
|
671
|
+
typeExports.set(exported, unknownType);
|
|
672
|
+
}
|
|
673
|
+
else
|
|
674
|
+
typeExports.set(exported, renameDeclarationType(type, exported));
|
|
675
|
+
};
|
|
676
|
+
const setRuntimeExport = (local, exported, type) => {
|
|
677
|
+
if (exports.has(exported)) {
|
|
678
|
+
warnings.push(`Duplicate explicit declaration export '${exported}' was kept as unknown`);
|
|
679
|
+
exports.set(exported, unknownType);
|
|
680
|
+
classes.delete(exported);
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
exports.set(exported, renameDeclarationExport(type, exported));
|
|
684
|
+
const info = localClasses.get(local);
|
|
685
|
+
if (type.kind === "classConstructor" && info)
|
|
686
|
+
classes.set(exported, info);
|
|
687
|
+
};
|
|
688
|
+
for (const name of directlyExportedTypes)
|
|
689
|
+
setTypeExport(name, localTypes.get(name) ?? unknownType);
|
|
690
|
+
for (const item of directRuntimeExports) {
|
|
691
|
+
const value = localValues.get(item.local) ?? unknownType;
|
|
692
|
+
setRuntimeExport(item.local, item.exported, value);
|
|
693
|
+
const type = localTypes.get(item.local);
|
|
694
|
+
if (type)
|
|
695
|
+
setTypeExport(item.exported, type);
|
|
696
|
+
}
|
|
697
|
+
for (const item of localDeclarationExports(text)) {
|
|
698
|
+
const value = localValues.get(item.local);
|
|
699
|
+
const type = localTypes.get(item.local);
|
|
700
|
+
if (item.typeOnly) {
|
|
701
|
+
if (type)
|
|
702
|
+
setTypeExport(item.exported, type);
|
|
703
|
+
else {
|
|
704
|
+
warnings.push(`Local declaration type export '${item.local}' was not found and was kept as unknown`);
|
|
705
|
+
setTypeExport(item.exported, unknownType);
|
|
706
|
+
}
|
|
707
|
+
continue;
|
|
708
|
+
}
|
|
709
|
+
if (value)
|
|
710
|
+
setRuntimeExport(item.local, item.exported, value);
|
|
711
|
+
if (type)
|
|
712
|
+
setTypeExport(item.exported, type);
|
|
713
|
+
if (!value && !type) {
|
|
714
|
+
warnings.push(`Local declaration export '${item.local}' was not found and was kept as unknown`);
|
|
715
|
+
setRuntimeExport(item.local, item.exported, unknownType);
|
|
716
|
+
setTypeExport(item.exported, unknownType);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
if (/export\s*=/u.test(text))
|
|
720
|
+
warnings.push("TypeScript export assignment is outside the VelarScript declaration bridge");
|
|
721
|
+
if (!reexportsHandled && (/export\s+\*/u.test(text) || /export\s+(?:type\s+)?\{[^}]+\}\s+from\s*["']/u.test(text))) {
|
|
722
|
+
warnings.push("TypeScript re-export requires the package declaration graph loader");
|
|
723
|
+
}
|
|
724
|
+
return { path, dependencies: [path], exports, typeExports, classes, classRegistry, warnings: unique(warnings) };
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Reads a declaration type to its real top-level terminator. A semicolon and
|
|
728
|
+
* an ASI newline are equivalent here; nested object/function/generic syntax
|
|
729
|
+
* may contain either without ending the `const` declaration.
|
|
730
|
+
*/
|
|
731
|
+
function constantDeclarations(source) {
|
|
732
|
+
const output = [];
|
|
733
|
+
const heads = /(?:^|[;\r\n])[\t ]*(export\s+)?(?:declare\s+)?const\s+([A-Za-z_$][\w$]*)\s*:\s*/gmu;
|
|
734
|
+
for (const match of source.matchAll(heads)) {
|
|
735
|
+
const start = match.index + match[0].length;
|
|
736
|
+
let round = 0;
|
|
737
|
+
let square = 0;
|
|
738
|
+
let brace = 0;
|
|
739
|
+
let angle = 0;
|
|
740
|
+
let quote = "";
|
|
741
|
+
let end = start;
|
|
742
|
+
for (; end < source.length; end += 1) {
|
|
743
|
+
const character = source[end];
|
|
744
|
+
if (quote) {
|
|
745
|
+
if (character === "\\")
|
|
746
|
+
end += 1;
|
|
747
|
+
else if (character === quote)
|
|
748
|
+
quote = "";
|
|
749
|
+
continue;
|
|
750
|
+
}
|
|
751
|
+
if (character === "\"" || character === "'" || character === "`")
|
|
752
|
+
quote = character;
|
|
753
|
+
else if (character === "(")
|
|
754
|
+
round += 1;
|
|
755
|
+
else if (character === ")")
|
|
756
|
+
round = Math.max(0, round - 1);
|
|
757
|
+
else if (character === "[")
|
|
758
|
+
square += 1;
|
|
759
|
+
else if (character === "]")
|
|
760
|
+
square = Math.max(0, square - 1);
|
|
761
|
+
else if (character === "{")
|
|
762
|
+
brace += 1;
|
|
763
|
+
else if (character === "}")
|
|
764
|
+
brace = Math.max(0, brace - 1);
|
|
765
|
+
else if (character === "<")
|
|
766
|
+
angle += 1;
|
|
767
|
+
else if (character === ">")
|
|
768
|
+
angle = Math.max(0, angle - 1);
|
|
769
|
+
else if ((character === ";" || character === "\n" || character === "\r")
|
|
770
|
+
&& round === 0 && square === 0 && brace === 0 && angle === 0)
|
|
771
|
+
break;
|
|
772
|
+
}
|
|
773
|
+
const type = source.slice(start, end).trim();
|
|
774
|
+
if (type)
|
|
775
|
+
output.push({ name: match[2], type, exported: Boolean(match[1]) });
|
|
776
|
+
}
|
|
777
|
+
return output;
|
|
778
|
+
}
|
|
779
|
+
function readClassDeclarations(source, warnings) {
|
|
780
|
+
const declarations = [];
|
|
781
|
+
const pattern = /(export\s+)?(?:declare\s+)?(?:(default)\s+)?(?:(abstract)\s+)?class\s+([A-Za-z_$][\w$]*)/gu;
|
|
782
|
+
for (const match of source.matchAll(pattern)) {
|
|
783
|
+
const open = source.indexOf("{", match.index + match[0].length);
|
|
784
|
+
if (open < 0) {
|
|
785
|
+
warnings.push(`Class declaration '${match[4]}' has no readable body and was kept as unknown`);
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
const close = matchingDelimiter(source, open, "{", "}");
|
|
789
|
+
if (close < 0) {
|
|
790
|
+
warnings.push(`Class declaration '${match[4]}' has an unclosed body and was kept as unknown`);
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
793
|
+
const header = source.slice(match.index + match[0].length, open).trim();
|
|
794
|
+
const base = /^extends\s+([A-Za-z_$][\w$]*)$/u.exec(header);
|
|
795
|
+
declarations.push({
|
|
796
|
+
name: match[4],
|
|
797
|
+
directExportName: match[1] ? (match[2] ? "default" : match[4]) : null,
|
|
798
|
+
baseName: base?.[1] ?? null,
|
|
799
|
+
body: source.slice(open + 1, close),
|
|
800
|
+
unsupportedHeader: match[3] || (header && !base) ? [match[3] ? "abstract" : "", header].filter(Boolean).join(" ") : null,
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
return declarations;
|
|
804
|
+
}
|
|
805
|
+
function parseClassDeclaration(declaration, parse, warnings, moduleSource, classTypes) {
|
|
806
|
+
if (declaration.unsupportedHeader) {
|
|
807
|
+
warnings.push(`Class declaration '${declaration.name}' uses unsupported '${declaration.unsupportedHeader}' syntax and was kept as unknown`);
|
|
808
|
+
return null;
|
|
809
|
+
}
|
|
810
|
+
const warningCount = warnings.length;
|
|
811
|
+
let constructor = null;
|
|
812
|
+
const fields = new Map();
|
|
813
|
+
const methods = new Map();
|
|
814
|
+
const staticFields = new Map();
|
|
815
|
+
const staticMethods = new Map();
|
|
816
|
+
const getterNames = new Set();
|
|
817
|
+
const staticGetterNames = new Set();
|
|
818
|
+
const accessors = new Map();
|
|
819
|
+
const staticAccessors = new Map();
|
|
820
|
+
let invalid = false;
|
|
821
|
+
const reject = (member) => {
|
|
822
|
+
warnings.push(`Unsupported public class member '${member}' in '${declaration.name}' caused the class to be kept as unknown`);
|
|
823
|
+
invalid = true;
|
|
824
|
+
};
|
|
825
|
+
const duplicate = (name) => {
|
|
826
|
+
warnings.push(`Overloaded or duplicate class member '${name}' in '${declaration.name}' caused the class to be kept as unknown`);
|
|
827
|
+
invalid = true;
|
|
828
|
+
};
|
|
829
|
+
const reserved = (name, member) => {
|
|
830
|
+
if (name !== "constructor" && name !== "prototype" && name !== "__proto__")
|
|
831
|
+
return false;
|
|
832
|
+
reject(member);
|
|
833
|
+
return true;
|
|
834
|
+
};
|
|
835
|
+
for (const raw of splitFields(declaration.body)) {
|
|
836
|
+
const member = raw.trim();
|
|
837
|
+
if (!member)
|
|
838
|
+
continue;
|
|
839
|
+
if (/^(?:private|protected)\s+/u.test(member)) {
|
|
840
|
+
if (/^(?:private|protected)\s+constructor\b/u.test(member))
|
|
841
|
+
reject(member);
|
|
842
|
+
continue;
|
|
843
|
+
}
|
|
844
|
+
const constructorPrefix = /^constructor\s*/u.exec(member);
|
|
845
|
+
if (constructorPrefix) {
|
|
846
|
+
const open = constructorPrefix[0].length;
|
|
847
|
+
if (member[open] !== "(") {
|
|
848
|
+
reject(member);
|
|
849
|
+
continue;
|
|
850
|
+
}
|
|
851
|
+
const close = matchingDelimiter(member, open, "(", ")");
|
|
852
|
+
if (close < 0 || member.slice(close + 1).trim()) {
|
|
853
|
+
reject(member);
|
|
854
|
+
continue;
|
|
855
|
+
}
|
|
856
|
+
if (constructor) {
|
|
857
|
+
duplicate("constructor");
|
|
858
|
+
continue;
|
|
859
|
+
}
|
|
860
|
+
constructor = parseClassConstructorParameters(member.slice(open + 1, close), parse, warnings);
|
|
861
|
+
for (const [name, field] of constructor.fields) {
|
|
862
|
+
if (fields.has(name) || methods.has(name) || accessors.has(name))
|
|
863
|
+
duplicate(name);
|
|
864
|
+
else
|
|
865
|
+
fields.set(name, field);
|
|
866
|
+
}
|
|
867
|
+
continue;
|
|
868
|
+
}
|
|
869
|
+
const accessor = readAccessorSignature(member);
|
|
870
|
+
if (accessor) {
|
|
871
|
+
if (reserved(accessor.name, member))
|
|
872
|
+
continue;
|
|
873
|
+
const modifiers = new Set(accessor.modifiers);
|
|
874
|
+
const target = modifiers.has("static") ? staticAccessors : accessors;
|
|
875
|
+
const fieldsTarget = modifiers.has("static") ? staticFields : fields;
|
|
876
|
+
const methodsTarget = modifiers.has("static") ? staticMethods : methods;
|
|
877
|
+
if (fieldsTarget.has(accessor.name) || methodsTarget.has(accessor.name)) {
|
|
878
|
+
duplicate(accessor.name);
|
|
879
|
+
continue;
|
|
880
|
+
}
|
|
881
|
+
const contract = target.get(accessor.name) ?? {};
|
|
882
|
+
if ((accessor.kind === "get" && contract.getter) || (accessor.kind === "set" && contract.setter)) {
|
|
883
|
+
duplicate(accessor.name);
|
|
884
|
+
continue;
|
|
885
|
+
}
|
|
886
|
+
if (accessor.kind === "get")
|
|
887
|
+
contract.getter = parse(accessor.type, "from-js");
|
|
888
|
+
else
|
|
889
|
+
contract.setter = parse(accessor.type, "to-js");
|
|
890
|
+
target.set(accessor.name, contract);
|
|
891
|
+
continue;
|
|
892
|
+
}
|
|
893
|
+
const property = /^((?:(?:public|static|readonly|declare)\s+)*)?([A-Za-z_$][\w$]*)(\?)?\s*:\s*(.+)$/u.exec(member);
|
|
894
|
+
if (property) {
|
|
895
|
+
const modifiers = new Set((property[1] ?? "").trim().split(/\s+/u).filter(Boolean));
|
|
896
|
+
const target = modifiers.has("static") ? staticFields : fields;
|
|
897
|
+
const accessorsTarget = modifiers.has("static") ? staticAccessors : accessors;
|
|
898
|
+
const name = property[2];
|
|
899
|
+
if (reserved(name, member))
|
|
900
|
+
continue;
|
|
901
|
+
if (target.has(name) || accessorsTarget.has(name) || (modifiers.has("static") ? staticMethods : methods).has(name)) {
|
|
902
|
+
duplicate(name);
|
|
903
|
+
continue;
|
|
904
|
+
}
|
|
905
|
+
if (property[3] && !modifiers.has("readonly")) {
|
|
906
|
+
warnings.push(`Optional mutable class field '${name}' has no exact VelarScript field contract and caused '${declaration.name}' to be kept as unknown`);
|
|
907
|
+
invalid = true;
|
|
908
|
+
continue;
|
|
909
|
+
}
|
|
910
|
+
const type = parse(property[4] ?? "unknown", modifiers.has("readonly") ? "from-js" : "invariant");
|
|
911
|
+
target.set(name, { mutable: !modifiers.has("readonly"), type: property[3] ? optionalOf(type) : type });
|
|
912
|
+
continue;
|
|
913
|
+
}
|
|
914
|
+
const methodPrefix = /^((?:(?:public|static)\s+)*)?/u.exec(member);
|
|
915
|
+
const modifiersText = methodPrefix?.[1] ?? "";
|
|
916
|
+
const method = readMethodSignature(member.slice(methodPrefix?.[0].length ?? 0));
|
|
917
|
+
if (method) {
|
|
918
|
+
if (reserved(method.name, member))
|
|
919
|
+
continue;
|
|
920
|
+
const modifiers = new Set(modifiersText.trim().split(/\s+/u).filter(Boolean));
|
|
921
|
+
const target = modifiers.has("static") ? staticMethods : methods;
|
|
922
|
+
const fieldsTarget = modifiers.has("static") ? staticFields : fields;
|
|
923
|
+
const accessorsTarget = modifiers.has("static") ? staticAccessors : accessors;
|
|
924
|
+
if (target.has(method.name) || fieldsTarget.has(method.name) || accessorsTarget.has(method.name)) {
|
|
925
|
+
duplicate(method.name);
|
|
926
|
+
continue;
|
|
927
|
+
}
|
|
928
|
+
const parameters = parseParameters(method.parameters, (value) => parse(value, "to-js"), warnings);
|
|
929
|
+
const type = parameters.invalid ? unsupportedType : {
|
|
930
|
+
kind: "function",
|
|
931
|
+
parameters: parameters.types,
|
|
932
|
+
requiredParameters: parameters.required,
|
|
933
|
+
...(parameters.rest ? { rest: parameters.rest } : {}),
|
|
934
|
+
result: parse(method.result, "from-js"),
|
|
935
|
+
};
|
|
936
|
+
target.set(method.name, method.optional ? optionalOf(type) : type);
|
|
937
|
+
continue;
|
|
938
|
+
}
|
|
939
|
+
reject(member);
|
|
940
|
+
}
|
|
941
|
+
const materializeAccessors = (source, target, readonlyGetters) => {
|
|
942
|
+
for (const [name, contract] of source) {
|
|
943
|
+
if (!contract.getter) {
|
|
944
|
+
warnings.push(`Setter-only class accessor '${name}' in '${declaration.name}' caused the class to be kept as unknown`);
|
|
945
|
+
invalid = true;
|
|
946
|
+
continue;
|
|
947
|
+
}
|
|
948
|
+
if (contract.setter && semanticTypeIdentity(contract.getter) !== semanticTypeIdentity(contract.setter)) {
|
|
949
|
+
warnings.push(`Class accessor '${name}' in '${declaration.name}' has incompatible getter and setter types`);
|
|
950
|
+
invalid = true;
|
|
951
|
+
continue;
|
|
952
|
+
}
|
|
953
|
+
target.set(name, { mutable: Boolean(contract.setter), type: contract.getter });
|
|
954
|
+
if (!contract.setter)
|
|
955
|
+
readonlyGetters.add(name);
|
|
956
|
+
}
|
|
957
|
+
};
|
|
958
|
+
materializeAccessors(accessors, fields, getterNames);
|
|
959
|
+
materializeAccessors(staticAccessors, staticFields, staticGetterNames);
|
|
960
|
+
if (warnings.length > warningCount)
|
|
961
|
+
invalid = true;
|
|
962
|
+
if (invalid)
|
|
963
|
+
return null;
|
|
964
|
+
const identity = `js:${moduleSource}#${declaration.name}`;
|
|
965
|
+
const base = declaration.baseName ? classTypes.get(declaration.baseName) : null;
|
|
966
|
+
if (declaration.baseName && base?.kind !== "class") {
|
|
967
|
+
warnings.push(`Class base '${declaration.baseName}' cannot be expanded and '${declaration.name}' was kept as unknown`);
|
|
968
|
+
return null;
|
|
969
|
+
}
|
|
970
|
+
return {
|
|
971
|
+
identity,
|
|
972
|
+
parameters: constructor?.types ?? [],
|
|
973
|
+
requiredParameters: constructor?.required ?? 0,
|
|
974
|
+
...(constructor?.rest ? { constructorRest: constructor.rest } : {}),
|
|
975
|
+
base: base?.kind === "class" ? base.identity ?? base.name : null,
|
|
976
|
+
abstract: false,
|
|
977
|
+
fields,
|
|
978
|
+
getters: getterNames,
|
|
979
|
+
abstractGetters: new Set(),
|
|
980
|
+
methods,
|
|
981
|
+
abstractMethods: new Set(),
|
|
982
|
+
staticFields,
|
|
983
|
+
staticGetters: staticGetterNames,
|
|
984
|
+
staticMethods,
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
async function readLimitedText(path, maximum, label) {
|
|
988
|
+
const metadata = await stat(path);
|
|
989
|
+
if (!metadata.isFile())
|
|
990
|
+
throw new Error(`${label} is not a regular file`);
|
|
991
|
+
if (metadata.size > maximum)
|
|
992
|
+
throw new RangeError(`${label} exceeds ${maximum} bytes`);
|
|
993
|
+
const source = await readFile(path, "utf8");
|
|
994
|
+
if (Buffer.byteLength(source, "utf8") > maximum)
|
|
995
|
+
throw new RangeError(`${label} exceeds ${maximum} bytes`);
|
|
996
|
+
return source;
|
|
997
|
+
}
|
|
998
|
+
function parseRestElementType(source, parse) {
|
|
999
|
+
let value = source.trim();
|
|
1000
|
+
if (value.startsWith("readonly "))
|
|
1001
|
+
value = value.slice("readonly ".length).trim();
|
|
1002
|
+
if (value.endsWith("[]"))
|
|
1003
|
+
return parse(value.slice(0, -2));
|
|
1004
|
+
const generic = /^(?:Array|ReadonlyArray)\s*<([\s\S]+)>$/u.exec(value);
|
|
1005
|
+
return generic && splitTopLevel(generic[1] ?? "", ",").length === 1 ? parse(generic[1] ?? "unknown") : null;
|
|
1006
|
+
}
|
|
1007
|
+
function parseParameters(source, parse, warnings) {
|
|
1008
|
+
if (!source.trim())
|
|
1009
|
+
return { types: [], required: 0 };
|
|
1010
|
+
const types = [];
|
|
1011
|
+
let required = 0;
|
|
1012
|
+
let optionalSeen = false;
|
|
1013
|
+
let invalid = false;
|
|
1014
|
+
let rest;
|
|
1015
|
+
const parts = splitTopLevel(source, ",");
|
|
1016
|
+
for (const [index, part] of parts.entries()) {
|
|
1017
|
+
const match = /^(\.\.\.)?[A-Za-z_$][\w$]*(\?)?\s*:\s*(.+)$/u.exec(part.trim());
|
|
1018
|
+
if (!match) {
|
|
1019
|
+
warnings.push(`Unsupported parameter declaration '${part.trim()}' was kept as unknown`);
|
|
1020
|
+
types.push(unsupportedType);
|
|
1021
|
+
optionalSeen = true;
|
|
1022
|
+
continue;
|
|
1023
|
+
}
|
|
1024
|
+
const optional = Boolean(match[2]);
|
|
1025
|
+
if (match[1]) {
|
|
1026
|
+
if (index !== parts.length - 1)
|
|
1027
|
+
warnings.push(`Rest parameter '${part.trim()}' is not final and was kept as unknown`);
|
|
1028
|
+
if (optional)
|
|
1029
|
+
warnings.push(`Optional rest parameter '${part.trim()}' was kept as unknown`);
|
|
1030
|
+
const element = parseRestElementType(match[3] ?? "unknown", parse);
|
|
1031
|
+
if (element && index === parts.length - 1 && !optional)
|
|
1032
|
+
rest = element;
|
|
1033
|
+
else {
|
|
1034
|
+
if (!element)
|
|
1035
|
+
warnings.push(`Rest parameter '${part.trim()}' must use an array type and was kept as unknown`);
|
|
1036
|
+
rest = unsupportedType;
|
|
1037
|
+
}
|
|
1038
|
+
continue;
|
|
1039
|
+
}
|
|
1040
|
+
const type = parse(match[3] ?? "unknown");
|
|
1041
|
+
types.push(type);
|
|
1042
|
+
if (!optional && optionalSeen) {
|
|
1043
|
+
warnings.push(`Required parameter '${part.trim()}' follows an optional parameter and the callable was kept as unknown`);
|
|
1044
|
+
invalid = true;
|
|
1045
|
+
}
|
|
1046
|
+
if (!optional && !optionalSeen)
|
|
1047
|
+
required += 1;
|
|
1048
|
+
if (optional)
|
|
1049
|
+
optionalSeen = true;
|
|
1050
|
+
}
|
|
1051
|
+
return { types, required, ...(rest ? { rest } : {}), ...(invalid ? { invalid: true } : {}) };
|
|
1052
|
+
}
|
|
1053
|
+
function parseClassConstructorParameters(source, parse, warnings) {
|
|
1054
|
+
if (!source.trim())
|
|
1055
|
+
return { types: [], required: 0, fields: new Map() };
|
|
1056
|
+
const types = [];
|
|
1057
|
+
const fields = new Map();
|
|
1058
|
+
let required = 0;
|
|
1059
|
+
let optionalSeen = false;
|
|
1060
|
+
let rest;
|
|
1061
|
+
const parts = splitTopLevel(source, ",");
|
|
1062
|
+
for (const [index, part] of parts.entries()) {
|
|
1063
|
+
const match = /^((?:(?:public|private|protected|readonly)\s+)*)(\.\.\.)?([A-Za-z_$][\w$]*)(\?)?\s*:\s*(.+)$/u.exec(part.trim());
|
|
1064
|
+
if (!match) {
|
|
1065
|
+
warnings.push(`Unsupported constructor parameter declaration '${part.trim()}' was kept as unknown`);
|
|
1066
|
+
types.push(unsupportedType);
|
|
1067
|
+
optionalSeen = true;
|
|
1068
|
+
continue;
|
|
1069
|
+
}
|
|
1070
|
+
const modifiers = new Set((match[1] ?? "").trim().split(/\s+/u).filter(Boolean));
|
|
1071
|
+
const optional = Boolean(match[4]);
|
|
1072
|
+
if (match[2]) {
|
|
1073
|
+
if (index !== parts.length - 1)
|
|
1074
|
+
warnings.push(`Rest parameter '${part.trim()}' is not final and was kept as unknown`);
|
|
1075
|
+
if (optional || modifiers.size > 0)
|
|
1076
|
+
warnings.push(`Rest parameter '${part.trim()}' cannot be optional or declare a parameter property`);
|
|
1077
|
+
const element = parseRestElementType(match[5] ?? "unknown", (value) => parse(value, "to-js"));
|
|
1078
|
+
if (element && index === parts.length - 1 && !optional && modifiers.size === 0)
|
|
1079
|
+
rest = element;
|
|
1080
|
+
else
|
|
1081
|
+
rest = unsupportedType;
|
|
1082
|
+
continue;
|
|
1083
|
+
}
|
|
1084
|
+
const sourceType = match[5] ?? "unknown";
|
|
1085
|
+
const parameterType = parse(sourceType, "to-js");
|
|
1086
|
+
types.push(parameterType);
|
|
1087
|
+
if (!optional && optionalSeen) {
|
|
1088
|
+
warnings.push(`Required constructor parameter '${part.trim()}' follows an optional parameter`);
|
|
1089
|
+
}
|
|
1090
|
+
if (!optional && !optionalSeen)
|
|
1091
|
+
required += 1;
|
|
1092
|
+
if (optional)
|
|
1093
|
+
optionalSeen = true;
|
|
1094
|
+
if (modifiers.has("readonly") || modifiers.has("public")) {
|
|
1095
|
+
const mutable = !modifiers.has("readonly");
|
|
1096
|
+
if (optional && mutable)
|
|
1097
|
+
warnings.push(`Optional mutable parameter property '${match[3]}' has no exact VelarScript field contract`);
|
|
1098
|
+
const fieldType = parse(sourceType, mutable ? "invariant" : "from-js");
|
|
1099
|
+
fields.set(match[3], { mutable, type: optional ? optionalOf(fieldType) : fieldType });
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
return { types, required, ...(rest ? { rest } : {}), fields };
|
|
1103
|
+
}
|
|
1104
|
+
function parseTsType(source, aliases, warnings, stack, classTypes = new Map(), direction = "from-js") {
|
|
1105
|
+
let value = source.trim();
|
|
1106
|
+
while (value.startsWith("(") && value.endsWith(")") && balanced(value.slice(1, -1)))
|
|
1107
|
+
value = value.slice(1, -1).trim();
|
|
1108
|
+
if (/^readonly\s+/u.test(value)) {
|
|
1109
|
+
const inner = value.replace(/^readonly\s+/u, "");
|
|
1110
|
+
if (inner.endsWith("[]")) {
|
|
1111
|
+
return readonlyViewOf({
|
|
1112
|
+
kind: "list",
|
|
1113
|
+
element: parseTsType(inner.slice(0, -2), aliases, warnings, stack, classTypes, direction),
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1116
|
+
warnings.push(`Readonly TypeScript type '${value}' is outside the VelarScript declaration bridge and was kept as unknown`);
|
|
1117
|
+
return unsupportedType;
|
|
1118
|
+
}
|
|
1119
|
+
const union = splitTopLevel(value, "|");
|
|
1120
|
+
if (union.length > 1) {
|
|
1121
|
+
const hasNull = union.includes("null");
|
|
1122
|
+
const hasUndefined = union.includes("undefined");
|
|
1123
|
+
const members = union.filter((part) => part !== "null" && part !== "undefined").map((part) => parseTsType(part, aliases, warnings, stack, classTypes, direction));
|
|
1124
|
+
if (direction === "invariant" && hasUndefined) {
|
|
1125
|
+
warnings.push(`Type '${value}' admits JavaScript undefined in a writable position and was kept as unknown`);
|
|
1126
|
+
return unsupportedType;
|
|
1127
|
+
}
|
|
1128
|
+
if (members.length === 0) {
|
|
1129
|
+
if (hasNull || (hasUndefined && direction === "from-js"))
|
|
1130
|
+
return nullType;
|
|
1131
|
+
warnings.push(`Type '${value}' cannot be supplied from VelarScript and was kept as unknown`);
|
|
1132
|
+
return unsupportedType;
|
|
1133
|
+
}
|
|
1134
|
+
const type = unionOf(members);
|
|
1135
|
+
return hasNull || (hasUndefined && direction === "from-js") ? optionalOf(type) : type;
|
|
1136
|
+
}
|
|
1137
|
+
if (value.endsWith("[]"))
|
|
1138
|
+
return {
|
|
1139
|
+
kind: "list",
|
|
1140
|
+
element: parseTsType(value.slice(0, -2), aliases, warnings, stack, classTypes, "invariant"),
|
|
1141
|
+
};
|
|
1142
|
+
// The safe bridge maps storage semantics, not the host's incidental API.
|
|
1143
|
+
// Node Buffer is a Uint8Array representation and therefore enters source as
|
|
1144
|
+
// read-only Bytes; no Buffer-only member is exposed.
|
|
1145
|
+
if (value === "Uint8Array" || value === "Buffer")
|
|
1146
|
+
return direction === "to-js" ? unionOf([bytesType, uint8BufferType]) : bytesType;
|
|
1147
|
+
if (value === "Uint16Array")
|
|
1148
|
+
return uint16BufferType;
|
|
1149
|
+
if (value === "Uint32Array")
|
|
1150
|
+
return uint32BufferType;
|
|
1151
|
+
if (value === "Float32Array")
|
|
1152
|
+
return float32BufferType;
|
|
1153
|
+
const generic = /^([A-Za-z_$][\w$]*)\s*<([\s\S]+)>$/u.exec(value);
|
|
1154
|
+
if (generic) {
|
|
1155
|
+
const arguments_ = splitTopLevel(generic[2] ?? "", ",");
|
|
1156
|
+
if (generic[1] === "Uint8Array" || generic[1] === "Buffer")
|
|
1157
|
+
return direction === "to-js" ? unionOf([bytesType, uint8BufferType]) : bytesType;
|
|
1158
|
+
if (generic[1] === "Uint16Array")
|
|
1159
|
+
return uint16BufferType;
|
|
1160
|
+
if (generic[1] === "Uint32Array")
|
|
1161
|
+
return uint32BufferType;
|
|
1162
|
+
if (generic[1] === "Float32Array")
|
|
1163
|
+
return float32BufferType;
|
|
1164
|
+
if (generic[1] === "Array")
|
|
1165
|
+
return {
|
|
1166
|
+
kind: "list",
|
|
1167
|
+
element: parseTsType(arguments_[0] ?? "unknown", aliases, warnings, stack, classTypes, "invariant"),
|
|
1168
|
+
};
|
|
1169
|
+
if (generic[1] === "Set")
|
|
1170
|
+
return { kind: "set", element: parseTsType(arguments_[0] ?? "unknown", aliases, warnings, stack, classTypes, "invariant") };
|
|
1171
|
+
if (generic[1] === "Map")
|
|
1172
|
+
return {
|
|
1173
|
+
kind: "map",
|
|
1174
|
+
key: parseTsType(arguments_[0] ?? "unknown", aliases, warnings, stack, classTypes, "invariant"),
|
|
1175
|
+
value: parseTsType(arguments_[1] ?? "unknown", aliases, warnings, stack, classTypes, "invariant"),
|
|
1176
|
+
};
|
|
1177
|
+
if (generic[1] === "ReadonlyArray" || generic[1] === "ReadonlySet") {
|
|
1178
|
+
const element = parseTsType(arguments_[0] ?? "unknown", aliases, warnings, stack, classTypes, direction);
|
|
1179
|
+
return readonlyViewOf(generic[1] === "ReadonlyArray" ? { kind: "list", element } : { kind: "set", element });
|
|
1180
|
+
}
|
|
1181
|
+
if (generic[1] === "ReadonlyMap")
|
|
1182
|
+
return readonlyViewOf({
|
|
1183
|
+
kind: "map",
|
|
1184
|
+
key: parseTsType(arguments_[0] ?? "unknown", aliases, warnings, stack, classTypes, direction),
|
|
1185
|
+
value: parseTsType(arguments_[1] ?? "unknown", aliases, warnings, stack, classTypes, direction),
|
|
1186
|
+
});
|
|
1187
|
+
if (generic[1] === "Promise")
|
|
1188
|
+
return { kind: "promise", value: parseTsType(arguments_[0] ?? "unknown", aliases, warnings, stack, classTypes, direction) };
|
|
1189
|
+
if (generic[1] === "Record") {
|
|
1190
|
+
warnings.push("TypeScript Record is a plain JavaScript object, not a VelarScript Map, and was kept as unknown");
|
|
1191
|
+
return unsupportedType;
|
|
1192
|
+
}
|
|
1193
|
+
warnings.push(`Generic type '${generic[1]}' is outside the VelarScript declaration bridge and was kept as unknown`);
|
|
1194
|
+
return unsupportedType;
|
|
1195
|
+
}
|
|
1196
|
+
const arrow = /^\(([^()]*)\)\s*=>\s*(.+)$/u.exec(value);
|
|
1197
|
+
if (arrow) {
|
|
1198
|
+
const parameters = parseParameters(arrow[1] ?? "", (part) => parseTsType(part, aliases, warnings, stack, classTypes, oppositeDirection(direction)), warnings);
|
|
1199
|
+
if (parameters.invalid)
|
|
1200
|
+
return unsupportedType;
|
|
1201
|
+
const resultSource = arrow[2] ?? "unknown";
|
|
1202
|
+
return {
|
|
1203
|
+
kind: "function",
|
|
1204
|
+
parameters: parameters.types,
|
|
1205
|
+
requiredParameters: parameters.required,
|
|
1206
|
+
...(parameters.rest ? { rest: parameters.rest } : {}),
|
|
1207
|
+
result: resultSource.trim() === "void" ? nullType : parseTsType(resultSource, aliases, warnings, stack, classTypes, direction),
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
if (value.startsWith("{") && value.endsWith("}"))
|
|
1211
|
+
return objectType(value.slice(1, -1), aliases, warnings, stack, classTypes, direction);
|
|
1212
|
+
if (/^['"`]/u.test(value)) {
|
|
1213
|
+
if (direction === "from-js")
|
|
1214
|
+
return stringType;
|
|
1215
|
+
warnings.push(`String literal type '${value}' cannot be widened safely in an input position and was kept as unknown`);
|
|
1216
|
+
return unsupportedType;
|
|
1217
|
+
}
|
|
1218
|
+
if (/^-?\d/u.test(value)) {
|
|
1219
|
+
if (direction === "from-js")
|
|
1220
|
+
return numberType;
|
|
1221
|
+
warnings.push(`Numeric literal type '${value}' cannot be widened safely in an input position and was kept as unknown`);
|
|
1222
|
+
return unsupportedType;
|
|
1223
|
+
}
|
|
1224
|
+
if (value === "true" || value === "false") {
|
|
1225
|
+
if (direction === "from-js")
|
|
1226
|
+
return boolType;
|
|
1227
|
+
warnings.push(`Boolean literal type '${value}' cannot be widened safely in an input position and was kept as unknown`);
|
|
1228
|
+
return unsupportedType;
|
|
1229
|
+
}
|
|
1230
|
+
if (value === "string")
|
|
1231
|
+
return stringType;
|
|
1232
|
+
if (value === "number")
|
|
1233
|
+
return numberType;
|
|
1234
|
+
if (value === "boolean")
|
|
1235
|
+
return boolType;
|
|
1236
|
+
if (value === "void") {
|
|
1237
|
+
if (direction === "from-js")
|
|
1238
|
+
return nullType;
|
|
1239
|
+
warnings.push("TypeScript void cannot be supplied explicitly from VelarScript and was kept as unknown");
|
|
1240
|
+
return unsupportedType;
|
|
1241
|
+
}
|
|
1242
|
+
if (value === "null")
|
|
1243
|
+
return nullType;
|
|
1244
|
+
if (value === "undefined") {
|
|
1245
|
+
if (direction === "from-js")
|
|
1246
|
+
return nullType;
|
|
1247
|
+
warnings.push("TypeScript undefined cannot be supplied explicitly from VelarScript and was kept as unknown");
|
|
1248
|
+
return unsupportedType;
|
|
1249
|
+
}
|
|
1250
|
+
if (value === "unknown")
|
|
1251
|
+
return unknownType;
|
|
1252
|
+
if (value === "object") {
|
|
1253
|
+
if (direction === "from-js")
|
|
1254
|
+
return unknownType;
|
|
1255
|
+
warnings.push("TypeScript object cannot be represented precisely in a VelarScript input position and was kept as unknown");
|
|
1256
|
+
return unsupportedType;
|
|
1257
|
+
}
|
|
1258
|
+
if (value === "any") {
|
|
1259
|
+
warnings.push("TypeScript 'any' is kept as unknown at the safe JavaScript boundary");
|
|
1260
|
+
return unknownType;
|
|
1261
|
+
}
|
|
1262
|
+
const classType = classTypes.get(value);
|
|
1263
|
+
if (classType)
|
|
1264
|
+
return classType;
|
|
1265
|
+
const alias = aliases.get(value);
|
|
1266
|
+
if (alias && !stack.has(value))
|
|
1267
|
+
return parseTsType(alias, aliases, warnings, new Set([...stack, value]), classTypes, direction);
|
|
1268
|
+
if (alias)
|
|
1269
|
+
warnings.push(`Recursive declaration '${value}' is outside the VelarScript declaration bridge and was kept as unknown`);
|
|
1270
|
+
else
|
|
1271
|
+
warnings.push(`TypeScript type '${value}' is outside the VelarScript declaration bridge and was kept as unknown`);
|
|
1272
|
+
return unsupportedType;
|
|
1273
|
+
}
|
|
1274
|
+
function objectType(source, aliases, warnings, stack, classTypes, direction) {
|
|
1275
|
+
const fields = new Map();
|
|
1276
|
+
const readonlyFields = new Set();
|
|
1277
|
+
const optionalFields = new Set();
|
|
1278
|
+
let invalidInputShape = false;
|
|
1279
|
+
for (const part of splitFields(source)) {
|
|
1280
|
+
const match = /^(readonly\s+)?([A-Za-z_$][\w$]*)(\?)?\s*:\s*(.+)$/u.exec(part.trim());
|
|
1281
|
+
if (match) {
|
|
1282
|
+
const name = match[2];
|
|
1283
|
+
const type = parseTsType(match[4] ?? "unknown", aliases, warnings, stack, classTypes, match[1] ? direction : "invariant");
|
|
1284
|
+
setObjectField(fields, name, type, warnings);
|
|
1285
|
+
if (match[1])
|
|
1286
|
+
readonlyFields.add(name);
|
|
1287
|
+
if (match[3])
|
|
1288
|
+
optionalFields.add(name);
|
|
1289
|
+
continue;
|
|
1290
|
+
}
|
|
1291
|
+
const method = readMethodSignature(part.trim());
|
|
1292
|
+
if (method) {
|
|
1293
|
+
const parameters = parseParameters(method.parameters, (value) => parseTsType(value, aliases, warnings, stack, classTypes, oppositeDirection(direction)), warnings);
|
|
1294
|
+
const type = parameters.invalid ? unsupportedType : {
|
|
1295
|
+
kind: "function",
|
|
1296
|
+
parameters: parameters.types,
|
|
1297
|
+
requiredParameters: parameters.required,
|
|
1298
|
+
...(parameters.rest ? { rest: parameters.rest } : {}),
|
|
1299
|
+
result: method.result.trim() === "void" ? nullType : parseTsType(method.result, aliases, warnings, stack, classTypes, direction),
|
|
1300
|
+
};
|
|
1301
|
+
setObjectField(fields, method.name, type, warnings);
|
|
1302
|
+
if (method.optional)
|
|
1303
|
+
optionalFields.add(method.name);
|
|
1304
|
+
continue;
|
|
1305
|
+
}
|
|
1306
|
+
if (part.trim()) {
|
|
1307
|
+
warnings.push(`Unsupported object field '${part.trim()}' was ignored`);
|
|
1308
|
+
invalidInputShape ||= direction !== "from-js";
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
if (invalidInputShape)
|
|
1312
|
+
return unsupportedType;
|
|
1313
|
+
return {
|
|
1314
|
+
kind: "object",
|
|
1315
|
+
fields,
|
|
1316
|
+
...(readonlyFields.size > 0 ? { readonlyFields } : {}),
|
|
1317
|
+
...(optionalFields.size > 0 ? { optionalFields } : {}),
|
|
1318
|
+
};
|
|
1319
|
+
}
|
|
1320
|
+
function setObjectField(fields, name, type, warnings) {
|
|
1321
|
+
if (fields.has(name)) {
|
|
1322
|
+
warnings.push(`Overloaded or duplicate object member '${name}' was kept as unknown`);
|
|
1323
|
+
fields.set(name, unsupportedType);
|
|
1324
|
+
}
|
|
1325
|
+
else
|
|
1326
|
+
fields.set(name, type);
|
|
1327
|
+
}
|
|
1328
|
+
function readFunctionSignature(source, start) {
|
|
1329
|
+
let cursor = skipWhitespace(source, start);
|
|
1330
|
+
let generic = false;
|
|
1331
|
+
if (source[cursor] === "<") {
|
|
1332
|
+
const end = matchingDelimiter(source, cursor, "<", ">");
|
|
1333
|
+
if (end < 0)
|
|
1334
|
+
return null;
|
|
1335
|
+
generic = true;
|
|
1336
|
+
cursor = skipWhitespace(source, end + 1);
|
|
1337
|
+
}
|
|
1338
|
+
if (source[cursor] !== "(")
|
|
1339
|
+
return null;
|
|
1340
|
+
const close = matchingDelimiter(source, cursor, "(", ")");
|
|
1341
|
+
if (close < 0)
|
|
1342
|
+
return null;
|
|
1343
|
+
const parameters = source.slice(cursor + 1, close);
|
|
1344
|
+
cursor = skipWhitespace(source, close + 1);
|
|
1345
|
+
if (source[cursor] !== ":")
|
|
1346
|
+
return null;
|
|
1347
|
+
const end = topLevelTerminator(source, cursor + 1, ";");
|
|
1348
|
+
if (end < 0)
|
|
1349
|
+
return null;
|
|
1350
|
+
const result = source.slice(cursor + 1, end).trim();
|
|
1351
|
+
return result ? { generic, parameters, result } : null;
|
|
1352
|
+
}
|
|
1353
|
+
function readMethodSignature(source) {
|
|
1354
|
+
const prefix = /^([A-Za-z_$][\w$]*)(\?)?\s*/u.exec(source);
|
|
1355
|
+
if (!prefix)
|
|
1356
|
+
return null;
|
|
1357
|
+
const open = prefix[0].length;
|
|
1358
|
+
if (source[open] !== "(")
|
|
1359
|
+
return null;
|
|
1360
|
+
const close = matchingDelimiter(source, open, "(", ")");
|
|
1361
|
+
if (close < 0)
|
|
1362
|
+
return null;
|
|
1363
|
+
const colon = skipWhitespace(source, close + 1);
|
|
1364
|
+
if (source[colon] !== ":")
|
|
1365
|
+
return null;
|
|
1366
|
+
const result = source.slice(colon + 1).trim();
|
|
1367
|
+
return result ? { name: prefix[1], optional: Boolean(prefix[2]), parameters: source.slice(open + 1, close), result } : null;
|
|
1368
|
+
}
|
|
1369
|
+
function readAccessorSignature(source) {
|
|
1370
|
+
const prefix = /^((?:(?:public|static|override)\s+)*)(get|set)\s+([A-Za-z_$][\w$]*)\s*/u.exec(source);
|
|
1371
|
+
if (!prefix)
|
|
1372
|
+
return null;
|
|
1373
|
+
const open = prefix[0].length;
|
|
1374
|
+
if (source[open] !== "(")
|
|
1375
|
+
return null;
|
|
1376
|
+
const close = matchingDelimiter(source, open, "(", ")");
|
|
1377
|
+
if (close < 0)
|
|
1378
|
+
return null;
|
|
1379
|
+
const parameters = source.slice(open + 1, close).trim();
|
|
1380
|
+
const tail = source.slice(close + 1).trim();
|
|
1381
|
+
if (prefix[2] === "get") {
|
|
1382
|
+
const result = /^:\s*(.+)$/u.exec(tail);
|
|
1383
|
+
if (parameters || !result)
|
|
1384
|
+
return null;
|
|
1385
|
+
return {
|
|
1386
|
+
modifiers: (prefix[1] ?? "").trim().split(/\s+/u).filter(Boolean),
|
|
1387
|
+
kind: "get",
|
|
1388
|
+
name: prefix[3],
|
|
1389
|
+
type: result[1],
|
|
1390
|
+
};
|
|
1391
|
+
}
|
|
1392
|
+
const parameter = /^[A-Za-z_$][\w$]*\s*:\s*(.+)$/u.exec(parameters);
|
|
1393
|
+
if (!parameter || tail)
|
|
1394
|
+
return null;
|
|
1395
|
+
return {
|
|
1396
|
+
modifiers: (prefix[1] ?? "").trim().split(/\s+/u).filter(Boolean),
|
|
1397
|
+
kind: "set",
|
|
1398
|
+
name: prefix[3],
|
|
1399
|
+
type: parameter[1],
|
|
1400
|
+
};
|
|
1401
|
+
}
|
|
1402
|
+
function skipWhitespace(source, start) {
|
|
1403
|
+
let cursor = start;
|
|
1404
|
+
while (/\s/u.test(source[cursor] ?? ""))
|
|
1405
|
+
cursor += 1;
|
|
1406
|
+
return cursor;
|
|
1407
|
+
}
|
|
1408
|
+
function matchingDelimiter(source, start, open, close) {
|
|
1409
|
+
let depth = 0;
|
|
1410
|
+
let quote = "";
|
|
1411
|
+
for (let index = start; index < source.length; index += 1) {
|
|
1412
|
+
const character = source[index];
|
|
1413
|
+
if (quote) {
|
|
1414
|
+
if (character === "\\")
|
|
1415
|
+
index += 1;
|
|
1416
|
+
else if (character === quote)
|
|
1417
|
+
quote = "";
|
|
1418
|
+
continue;
|
|
1419
|
+
}
|
|
1420
|
+
if (character === "\"" || character === "'" || character === "`") {
|
|
1421
|
+
quote = character;
|
|
1422
|
+
continue;
|
|
1423
|
+
}
|
|
1424
|
+
if (character === open)
|
|
1425
|
+
depth += 1;
|
|
1426
|
+
else if (character === close && --depth === 0)
|
|
1427
|
+
return index;
|
|
1428
|
+
}
|
|
1429
|
+
return -1;
|
|
1430
|
+
}
|
|
1431
|
+
function topLevelTerminator(source, start, terminator) {
|
|
1432
|
+
let round = 0;
|
|
1433
|
+
let square = 0;
|
|
1434
|
+
let brace = 0;
|
|
1435
|
+
let angle = 0;
|
|
1436
|
+
let quote = "";
|
|
1437
|
+
for (let index = start; index < source.length; index += 1) {
|
|
1438
|
+
const character = source[index];
|
|
1439
|
+
if (quote) {
|
|
1440
|
+
if (character === "\\")
|
|
1441
|
+
index += 1;
|
|
1442
|
+
else if (character === quote)
|
|
1443
|
+
quote = "";
|
|
1444
|
+
continue;
|
|
1445
|
+
}
|
|
1446
|
+
if (character === "\"" || character === "'" || character === "`")
|
|
1447
|
+
quote = character;
|
|
1448
|
+
else if (character === "(")
|
|
1449
|
+
round += 1;
|
|
1450
|
+
else if (character === ")")
|
|
1451
|
+
round = Math.max(0, round - 1);
|
|
1452
|
+
else if (character === "[")
|
|
1453
|
+
square += 1;
|
|
1454
|
+
else if (character === "]")
|
|
1455
|
+
square = Math.max(0, square - 1);
|
|
1456
|
+
else if (character === "{")
|
|
1457
|
+
brace += 1;
|
|
1458
|
+
else if (character === "}")
|
|
1459
|
+
brace = Math.max(0, brace - 1);
|
|
1460
|
+
else if (character === "<")
|
|
1461
|
+
angle += 1;
|
|
1462
|
+
else if (character === ">")
|
|
1463
|
+
angle = Math.max(0, angle - 1);
|
|
1464
|
+
else if (character === terminator && round === 0 && square === 0 && brace === 0 && angle === 0)
|
|
1465
|
+
return index;
|
|
1466
|
+
}
|
|
1467
|
+
return -1;
|
|
1468
|
+
}
|
|
1469
|
+
function splitFields(source) {
|
|
1470
|
+
const semicolons = splitTopLevel(source, ";");
|
|
1471
|
+
return topLevelTerminator(source, 0, ";") >= 0 ? semicolons : source.split(/\n+/u);
|
|
1472
|
+
}
|
|
1473
|
+
function splitTopLevel(source, separator) {
|
|
1474
|
+
const output = [];
|
|
1475
|
+
let start = 0;
|
|
1476
|
+
let angle = 0;
|
|
1477
|
+
let round = 0;
|
|
1478
|
+
let brace = 0;
|
|
1479
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
1480
|
+
const character = source[index];
|
|
1481
|
+
if (character === "<")
|
|
1482
|
+
angle += 1;
|
|
1483
|
+
else if (character === ">")
|
|
1484
|
+
angle = Math.max(0, angle - 1);
|
|
1485
|
+
else if (character === "(")
|
|
1486
|
+
round += 1;
|
|
1487
|
+
else if (character === ")")
|
|
1488
|
+
round = Math.max(0, round - 1);
|
|
1489
|
+
else if (character === "{")
|
|
1490
|
+
brace += 1;
|
|
1491
|
+
else if (character === "}")
|
|
1492
|
+
brace = Math.max(0, brace - 1);
|
|
1493
|
+
else if (character === separator && angle === 0 && round === 0 && brace === 0) {
|
|
1494
|
+
output.push(source.slice(start, index).trim());
|
|
1495
|
+
start = index + 1;
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
output.push(source.slice(start).trim());
|
|
1499
|
+
return output.filter(Boolean);
|
|
1500
|
+
}
|
|
1501
|
+
function balanced(value) {
|
|
1502
|
+
let depth = 0;
|
|
1503
|
+
for (const character of value) {
|
|
1504
|
+
if (character === "(")
|
|
1505
|
+
depth += 1;
|
|
1506
|
+
else if (character === ")")
|
|
1507
|
+
depth -= 1;
|
|
1508
|
+
if (depth < 0)
|
|
1509
|
+
return false;
|
|
1510
|
+
}
|
|
1511
|
+
return depth === 0;
|
|
1512
|
+
}
|
|
1513
|
+
function exportedTypes(value, subpath) {
|
|
1514
|
+
const selected = selectPackageExport(value, subpath);
|
|
1515
|
+
return selected ? findTypesTarget(selected.value, selected.wildcard) : null;
|
|
1516
|
+
}
|
|
1517
|
+
function selectPackageExport(value, subpath) {
|
|
1518
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1519
|
+
return subpath === "." ? { value, wildcard: null } : null;
|
|
1520
|
+
}
|
|
1521
|
+
const entries = Object.entries(value);
|
|
1522
|
+
if (!entries.some(([key]) => key.startsWith(".")))
|
|
1523
|
+
return subpath === "." ? { value, wildcard: null } : null;
|
|
1524
|
+
const exact = entries.find(([key]) => key === subpath);
|
|
1525
|
+
if (exact)
|
|
1526
|
+
return { value: exact[1], wildcard: null };
|
|
1527
|
+
const patterns = entries.flatMap(([key, target]) => {
|
|
1528
|
+
const star = key.indexOf("*");
|
|
1529
|
+
if (star < 0 || star !== key.lastIndexOf("*"))
|
|
1530
|
+
return [];
|
|
1531
|
+
const prefix = key.slice(0, star);
|
|
1532
|
+
const suffix = key.slice(star + 1);
|
|
1533
|
+
if (!subpath.startsWith(prefix) || !subpath.endsWith(suffix) || subpath.length < prefix.length + suffix.length)
|
|
1534
|
+
return [];
|
|
1535
|
+
return [{ target, prefix, suffix, wildcard: subpath.slice(prefix.length, subpath.length - suffix.length) }];
|
|
1536
|
+
}).sort((left, right) => right.prefix.length - left.prefix.length || right.suffix.length - left.suffix.length);
|
|
1537
|
+
const match = patterns[0];
|
|
1538
|
+
return match ? { value: match.target, wildcard: match.wildcard } : null;
|
|
1539
|
+
}
|
|
1540
|
+
function findTypesTarget(value, wildcard) {
|
|
1541
|
+
if (Array.isArray(value)) {
|
|
1542
|
+
for (const item of value) {
|
|
1543
|
+
const found = findTypesTarget(item, wildcard);
|
|
1544
|
+
if (found)
|
|
1545
|
+
return found;
|
|
1546
|
+
}
|
|
1547
|
+
return null;
|
|
1548
|
+
}
|
|
1549
|
+
if (!value || typeof value !== "object")
|
|
1550
|
+
return null;
|
|
1551
|
+
const conditions = value;
|
|
1552
|
+
if (Object.hasOwn(conditions, "types")) {
|
|
1553
|
+
const found = firstStringTarget(conditions.types, wildcard);
|
|
1554
|
+
if (found)
|
|
1555
|
+
return found;
|
|
1556
|
+
}
|
|
1557
|
+
const preferred = ["import", "browser", "node", "default", "require"];
|
|
1558
|
+
for (const key of [...preferred, ...Object.keys(conditions).filter((key) => key !== "types" && !preferred.includes(key))]) {
|
|
1559
|
+
if (!Object.hasOwn(conditions, key))
|
|
1560
|
+
continue;
|
|
1561
|
+
const found = findTypesTarget(conditions[key], wildcard);
|
|
1562
|
+
if (found)
|
|
1563
|
+
return found;
|
|
1564
|
+
}
|
|
1565
|
+
return null;
|
|
1566
|
+
}
|
|
1567
|
+
function firstStringTarget(value, wildcard) {
|
|
1568
|
+
if (typeof value === "string")
|
|
1569
|
+
return wildcard === null ? value : value.replaceAll("*", wildcard);
|
|
1570
|
+
if (Array.isArray(value)) {
|
|
1571
|
+
for (const item of value) {
|
|
1572
|
+
const found = firstStringTarget(item, wildcard);
|
|
1573
|
+
if (found)
|
|
1574
|
+
return found;
|
|
1575
|
+
}
|
|
1576
|
+
return null;
|
|
1577
|
+
}
|
|
1578
|
+
if (!value || typeof value !== "object")
|
|
1579
|
+
return null;
|
|
1580
|
+
const conditions = value;
|
|
1581
|
+
for (const key of ["import", "browser", "node", "default", "require", ...Object.keys(conditions)]) {
|
|
1582
|
+
if (!Object.hasOwn(conditions, key))
|
|
1583
|
+
continue;
|
|
1584
|
+
const found = firstStringTarget(conditions[key], wildcard);
|
|
1585
|
+
if (found)
|
|
1586
|
+
return found;
|
|
1587
|
+
}
|
|
1588
|
+
return null;
|
|
1589
|
+
}
|
|
1590
|
+
async function exists(path) {
|
|
1591
|
+
try {
|
|
1592
|
+
await access(path);
|
|
1593
|
+
return true;
|
|
1594
|
+
}
|
|
1595
|
+
catch {
|
|
1596
|
+
return false;
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
function packageNameOf(source) {
|
|
1600
|
+
const parts = source.split("/");
|
|
1601
|
+
return source.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0] ?? source;
|
|
1602
|
+
}
|
|
1603
|
+
function stringValue(value) {
|
|
1604
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
1605
|
+
}
|
|
1606
|
+
function unique(values) {
|
|
1607
|
+
return [...new Set(values)];
|
|
1608
|
+
}
|
|
1609
|
+
//# sourceMappingURL=typescript-declarations.js.map
|