@tsonic/cli 0.0.63 → 0.0.65
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/dist/.tsbuildinfo +1 -1
- package/dist/aikya/bindings.d.ts +44 -0
- package/dist/aikya/bindings.d.ts.map +1 -0
- package/dist/aikya/bindings.js +777 -0
- package/dist/aikya/bindings.js.map +1 -0
- package/dist/aikya/bindings.test.d.ts +2 -0
- package/dist/aikya/bindings.test.d.ts.map +1 -0
- package/dist/aikya/bindings.test.js +554 -0
- package/dist/aikya/bindings.test.js.map +1 -0
- package/dist/cli/dispatcher.d.ts.map +1 -1
- package/dist/cli/dispatcher.js +15 -0
- package/dist/cli/dispatcher.js.map +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +4 -1
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/parser.d.ts.map +1 -1
- package/dist/cli/parser.js +6 -0
- package/dist/cli/parser.js.map +1 -1
- package/dist/cli/parser.test.js +10 -0
- package/dist/cli/parser.test.js.map +1 -1
- package/dist/commands/add-npm.d.ts +3 -2
- package/dist/commands/add-npm.d.ts.map +1 -1
- package/dist/commands/add-npm.js +69 -177
- package/dist/commands/add-npm.js.map +1 -1
- package/dist/commands/add-npm.test.js +276 -2
- package/dist/commands/add-npm.test.js.map +1 -1
- package/dist/commands/build-library-bindings-aliases.test.js +352 -6
- package/dist/commands/build-library-bindings-aliases.test.js.map +1 -1
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +192 -145
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/build.test.js +22 -3
- package/dist/commands/build.test.js.map +1 -1
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +7 -0
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/init.d.ts +6 -2
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +76 -12
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/init.test.js +162 -8
- package/dist/commands/init.test.js.map +1 -1
- package/dist/commands/library-bindings-augment.d.ts.map +1 -1
- package/dist/commands/library-bindings-augment.js +378 -53
- package/dist/commands/library-bindings-augment.js.map +1 -1
- package/dist/commands/library-bindings-augment.test.js +460 -2
- package/dist/commands/library-bindings-augment.test.js.map +1 -1
- package/dist/commands/library-bindings-firstparty-regressions.test.d.ts +2 -0
- package/dist/commands/library-bindings-firstparty-regressions.test.d.ts.map +1 -0
- package/dist/commands/library-bindings-firstparty-regressions.test.js +214 -0
- package/dist/commands/library-bindings-firstparty-regressions.test.js.map +1 -0
- package/dist/commands/library-bindings-firstparty.d.ts +3 -0
- package/dist/commands/library-bindings-firstparty.d.ts.map +1 -0
- package/dist/commands/library-bindings-firstparty.js +2252 -0
- package/dist/commands/library-bindings-firstparty.js.map +1 -0
- package/dist/commands/restore.d.ts.map +1 -1
- package/dist/commands/restore.js +3 -1
- package/dist/commands/restore.js.map +1 -1
- package/dist/commands/restore.test.js +29 -0
- package/dist/commands/restore.test.js.map +1 -1
- package/dist/commands/run-build-regressions.test.js +72 -0
- package/dist/commands/run-build-regressions.test.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +19 -2
- package/dist/config.js.map +1 -1
- package/dist/config.test.js +98 -2
- package/dist/config.test.js.map +1 -1
- package/dist/dotnet/runtime-dlls.d.ts +1 -0
- package/dist/dotnet/runtime-dlls.d.ts.map +1 -1
- package/dist/dotnet/runtime-dlls.js +1 -0
- package/dist/dotnet/runtime-dlls.js.map +1 -1
- package/dist/surface/profiles.d.ts +15 -0
- package/dist/surface/profiles.d.ts.map +1 -0
- package/dist/surface/profiles.js +218 -0
- package/dist/surface/profiles.js.map +1 -0
- package/dist/surface/profiles.test.d.ts +2 -0
- package/dist/surface/profiles.test.d.ts.map +1 -0
- package/dist/surface/profiles.test.js +175 -0
- package/dist/surface/profiles.test.js.map +1 -0
- package/dist/types.d.ts +11 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/runtime/nodejs.dll +0 -0
|
@@ -0,0 +1,2252 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync, } from "node:fs";
|
|
2
|
+
import { join, posix, resolve } from "node:path";
|
|
3
|
+
import { buildModuleDependencyGraph } from "@tsonic/frontend";
|
|
4
|
+
import * as ts from "typescript";
|
|
5
|
+
import { resolveSurfaceCapabilities } from "../surface/profiles.js";
|
|
6
|
+
import { overlayDependencyBindings } from "./library-bindings-augment.js";
|
|
7
|
+
const primitiveImportLine = "import type { sbyte, byte, short, ushort, int, uint, long, ulong, int128, uint128, half, float, double, decimal, nint, nuint, char } from '@tsonic/core/types.js';";
|
|
8
|
+
const typePrinter = ts.createPrinter({ removeComments: true });
|
|
9
|
+
const printTypeNodeText = (node, sourceFile) => {
|
|
10
|
+
return typePrinter
|
|
11
|
+
.printNode(ts.EmitHint.Unspecified, node, sourceFile)
|
|
12
|
+
.trim();
|
|
13
|
+
};
|
|
14
|
+
const ensureUndefinedInType = (typeText) => {
|
|
15
|
+
const trimmed = typeText.trim();
|
|
16
|
+
if (/\bundefined\b/.test(trimmed))
|
|
17
|
+
return trimmed;
|
|
18
|
+
return `${trimmed} | undefined`;
|
|
19
|
+
};
|
|
20
|
+
const applyWrappersToBaseType = (baseType, wrappers) => {
|
|
21
|
+
let expr = baseType.trim();
|
|
22
|
+
for (const w of wrappers.slice().reverse()) {
|
|
23
|
+
expr = `${w.aliasName}<${expr}>`;
|
|
24
|
+
}
|
|
25
|
+
return expr;
|
|
26
|
+
};
|
|
27
|
+
const getPropertyNameText = (name) => {
|
|
28
|
+
if (ts.isIdentifier(name))
|
|
29
|
+
return name.text;
|
|
30
|
+
if (ts.isStringLiteral(name) || ts.isNumericLiteral(name))
|
|
31
|
+
return name.text;
|
|
32
|
+
return undefined;
|
|
33
|
+
};
|
|
34
|
+
const sanitizeForBrand = (value) => {
|
|
35
|
+
const normalized = value.replace(/[^A-Za-z0-9_]/g, "_");
|
|
36
|
+
return normalized.length > 0 ? normalized : "_";
|
|
37
|
+
};
|
|
38
|
+
const printTypeParameters = (typeParameters) => {
|
|
39
|
+
if (!typeParameters || typeParameters.length === 0)
|
|
40
|
+
return "";
|
|
41
|
+
return `<${typeParameters.map((typeParameter) => typeParameter.name).join(", ")}>`;
|
|
42
|
+
};
|
|
43
|
+
const normalizeTypeReferenceName = (name, arity) => {
|
|
44
|
+
const withoutNamespace = (() => {
|
|
45
|
+
const dotIndex = Math.max(name.lastIndexOf("."), name.lastIndexOf("+"));
|
|
46
|
+
return dotIndex >= 0 ? name.slice(dotIndex + 1) : name;
|
|
47
|
+
})();
|
|
48
|
+
const backtickNormalized = withoutNamespace.replace(/`(\d+)$/, "_$1");
|
|
49
|
+
if (!arity || arity <= 0)
|
|
50
|
+
return backtickNormalized;
|
|
51
|
+
if (new RegExp(`_${arity}$`).test(backtickNormalized)) {
|
|
52
|
+
return backtickNormalized;
|
|
53
|
+
}
|
|
54
|
+
return `${backtickNormalized}_${arity}`;
|
|
55
|
+
};
|
|
56
|
+
const renderReferenceType = (referenceName, typeArguments, typeParametersInScope) => {
|
|
57
|
+
if (typeParametersInScope.includes(referenceName))
|
|
58
|
+
return referenceName;
|
|
59
|
+
if (referenceName === "unknown")
|
|
60
|
+
return "unknown";
|
|
61
|
+
if (referenceName === "any")
|
|
62
|
+
return "unknown";
|
|
63
|
+
if (referenceName === "object")
|
|
64
|
+
return "object";
|
|
65
|
+
if (referenceName === "string")
|
|
66
|
+
return "string";
|
|
67
|
+
if (referenceName === "boolean")
|
|
68
|
+
return "boolean";
|
|
69
|
+
if (referenceName === "number")
|
|
70
|
+
return "number";
|
|
71
|
+
let normalizedName = normalizeTypeReferenceName(referenceName);
|
|
72
|
+
if (typeArguments && typeArguments.length > 0) {
|
|
73
|
+
const arityMatch = normalizedName.match(/_(\d+)$/);
|
|
74
|
+
if (arityMatch &&
|
|
75
|
+
arityMatch[1] &&
|
|
76
|
+
Number(arityMatch[1]) === typeArguments.length &&
|
|
77
|
+
!normalizedName.includes("__Alias_") &&
|
|
78
|
+
!normalizedName.includes("__")) {
|
|
79
|
+
normalizedName = normalizedName.slice(0, -arityMatch[0].length);
|
|
80
|
+
}
|
|
81
|
+
else if (normalizedName.endsWith("_") &&
|
|
82
|
+
!normalizedName.includes("__Alias_")) {
|
|
83
|
+
normalizedName = normalizedName.slice(0, -1);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (!typeArguments || typeArguments.length === 0)
|
|
87
|
+
return normalizedName;
|
|
88
|
+
return `${normalizedName}<${typeArguments
|
|
89
|
+
.map((arg) => renderPortableType(arg, typeParametersInScope))
|
|
90
|
+
.join(", ")}>`;
|
|
91
|
+
};
|
|
92
|
+
const renderPortableType = (type, typeParametersInScope = []) => {
|
|
93
|
+
if (!type)
|
|
94
|
+
return "object";
|
|
95
|
+
switch (type.kind) {
|
|
96
|
+
case "primitiveType":
|
|
97
|
+
return type.name;
|
|
98
|
+
case "literalType":
|
|
99
|
+
return typeof type.value === "string"
|
|
100
|
+
? JSON.stringify(type.value)
|
|
101
|
+
: String(type.value);
|
|
102
|
+
case "voidType":
|
|
103
|
+
return "void";
|
|
104
|
+
case "neverType":
|
|
105
|
+
return "never";
|
|
106
|
+
case "unknownType":
|
|
107
|
+
case "anyType":
|
|
108
|
+
return "unknown";
|
|
109
|
+
case "typeParameterType":
|
|
110
|
+
return type.name;
|
|
111
|
+
case "arrayType":
|
|
112
|
+
return `${renderPortableType(type.elementType, typeParametersInScope)}[]`;
|
|
113
|
+
case "tupleType":
|
|
114
|
+
return `[${type.elementTypes
|
|
115
|
+
.map((item) => renderPortableType(item, typeParametersInScope))
|
|
116
|
+
.join(", ")}]`;
|
|
117
|
+
case "unionType":
|
|
118
|
+
return type.types
|
|
119
|
+
.map((item) => renderPortableType(item, typeParametersInScope))
|
|
120
|
+
.join(" | ");
|
|
121
|
+
case "intersectionType":
|
|
122
|
+
return type.types
|
|
123
|
+
.map((item) => renderPortableType(item, typeParametersInScope))
|
|
124
|
+
.join(" & ");
|
|
125
|
+
case "dictionaryType":
|
|
126
|
+
return `Record<${renderPortableType(type.keyType, typeParametersInScope)}, ${renderPortableType(type.valueType, typeParametersInScope)}>`;
|
|
127
|
+
case "functionType":
|
|
128
|
+
return `(${type.parameters
|
|
129
|
+
.map((parameter, index) => {
|
|
130
|
+
const parameterName = parameter.pattern.kind === "identifierPattern"
|
|
131
|
+
? parameter.pattern.name
|
|
132
|
+
: `p${index + 1}`;
|
|
133
|
+
return `${parameterName}: ${renderPortableType(parameter.type, typeParametersInScope)}`;
|
|
134
|
+
})
|
|
135
|
+
.join(", ")}) => ${renderPortableType(type.returnType, typeParametersInScope)}`;
|
|
136
|
+
case "objectType":
|
|
137
|
+
return `{ ${type.members
|
|
138
|
+
.map((member) => {
|
|
139
|
+
if (member.kind === "methodSignature") {
|
|
140
|
+
return `${member.name}${printTypeParameters(member.typeParameters)}(${member.parameters
|
|
141
|
+
.map((parameter, index) => {
|
|
142
|
+
const parameterName = parameter.pattern.kind === "identifierPattern"
|
|
143
|
+
? parameter.pattern.name
|
|
144
|
+
: `p${index + 1}`;
|
|
145
|
+
const optionalMark = parameter.isOptional ? "?" : "";
|
|
146
|
+
return `${parameterName}${optionalMark}: ${renderPortableType(parameter.type, typeParametersInScope)}`;
|
|
147
|
+
})
|
|
148
|
+
.join(", ")}): ${renderPortableType(member.returnType, typeParametersInScope)}`;
|
|
149
|
+
}
|
|
150
|
+
const optionalMark = member.isOptional ? "?" : "";
|
|
151
|
+
const readonlyMark = member.isReadonly ? "readonly " : "";
|
|
152
|
+
return `${readonlyMark}${member.name}${optionalMark}: ${renderPortableType(member.type, typeParametersInScope)}`;
|
|
153
|
+
})
|
|
154
|
+
.join("; ")} }`;
|
|
155
|
+
case "referenceType": {
|
|
156
|
+
return renderReferenceType(type.name, type.typeArguments, typeParametersInScope);
|
|
157
|
+
}
|
|
158
|
+
default:
|
|
159
|
+
return "object";
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
const renderUnknownParameters = (parameters, typeParametersInScope) => {
|
|
163
|
+
return parameters
|
|
164
|
+
.map((parameter, index) => {
|
|
165
|
+
const baseName = parameter.pattern.kind === "identifierPattern"
|
|
166
|
+
? parameter.pattern.name
|
|
167
|
+
: `p${index + 1}`;
|
|
168
|
+
const restPrefix = parameter.isRest ? "..." : "";
|
|
169
|
+
const optionalMark = parameter.isOptional && !parameter.isRest ? "?" : "";
|
|
170
|
+
const parameterType = renderPortableType(parameter.type, typeParametersInScope);
|
|
171
|
+
const typeSuffix = parameter.isRest
|
|
172
|
+
? `${parameterType}[]`
|
|
173
|
+
: parameterType;
|
|
174
|
+
return `${restPrefix}${baseName}${optionalMark}: ${typeSuffix}`;
|
|
175
|
+
})
|
|
176
|
+
.join(", ");
|
|
177
|
+
};
|
|
178
|
+
const renderMethodSignature = (name, typeParameters, parameters, returnType) => {
|
|
179
|
+
const typeParametersText = printTypeParameters(typeParameters);
|
|
180
|
+
const typeParameterNames = typeParameters?.map((typeParameter) => typeParameter.name) ?? [];
|
|
181
|
+
const parametersText = renderUnknownParameters(parameters, typeParameterNames);
|
|
182
|
+
const returnTypeText = renderPortableType(returnType, typeParameterNames);
|
|
183
|
+
return `${name}${typeParametersText}(${parametersText}): ${returnTypeText};`;
|
|
184
|
+
};
|
|
185
|
+
const declarationNameOf = (statement) => {
|
|
186
|
+
switch (statement.kind) {
|
|
187
|
+
case "functionDeclaration":
|
|
188
|
+
case "classDeclaration":
|
|
189
|
+
case "interfaceDeclaration":
|
|
190
|
+
case "enumDeclaration":
|
|
191
|
+
case "typeAliasDeclaration":
|
|
192
|
+
return statement.name;
|
|
193
|
+
default:
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
const resolveModuleLocalDeclaration = (module, localName) => {
|
|
198
|
+
for (const statement of module.body) {
|
|
199
|
+
const statementName = declarationNameOf(statement);
|
|
200
|
+
if (statementName === localName)
|
|
201
|
+
return statement;
|
|
202
|
+
if (statement.kind === "variableDeclaration") {
|
|
203
|
+
for (const declarator of statement.declarations) {
|
|
204
|
+
if (declarator.name.kind === "identifierPattern" &&
|
|
205
|
+
declarator.name.name === localName) {
|
|
206
|
+
return statement;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return undefined;
|
|
212
|
+
};
|
|
213
|
+
const classifyDeclarationKind = (statement, filePath, exportName) => {
|
|
214
|
+
switch (statement.kind) {
|
|
215
|
+
case "functionDeclaration":
|
|
216
|
+
return { ok: true, value: "function" };
|
|
217
|
+
case "variableDeclaration":
|
|
218
|
+
return { ok: true, value: "variable" };
|
|
219
|
+
case "classDeclaration":
|
|
220
|
+
return { ok: true, value: "class" };
|
|
221
|
+
case "interfaceDeclaration":
|
|
222
|
+
return { ok: true, value: "interface" };
|
|
223
|
+
case "enumDeclaration":
|
|
224
|
+
return { ok: true, value: "enum" };
|
|
225
|
+
case "typeAliasDeclaration":
|
|
226
|
+
return { ok: true, value: "typeAlias" };
|
|
227
|
+
default:
|
|
228
|
+
return {
|
|
229
|
+
ok: false,
|
|
230
|
+
error: `Unsupported export '${exportName}' in ${filePath}: ${statement.kind}.\n` +
|
|
231
|
+
"First-party bindings generation requires explicit support for each exported declaration kind.",
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
const collectModuleExports = (module, modulesByFileKey) => {
|
|
236
|
+
const exportedSymbols = [];
|
|
237
|
+
const seen = new Set();
|
|
238
|
+
const pushExport = (symbol) => {
|
|
239
|
+
const key = `${symbol.exportName}|${symbol.localName}|${symbol.kind}`;
|
|
240
|
+
if (seen.has(key))
|
|
241
|
+
return;
|
|
242
|
+
seen.add(key);
|
|
243
|
+
exportedSymbols.push(symbol);
|
|
244
|
+
};
|
|
245
|
+
for (const item of module.exports) {
|
|
246
|
+
if (item.kind === "default") {
|
|
247
|
+
return {
|
|
248
|
+
ok: false,
|
|
249
|
+
error: `Unsupported default export in ${module.filePath}.\n` +
|
|
250
|
+
"First-party bindings generation currently requires named/declaration exports for deterministic namespace facades.",
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
if (item.kind === "declaration") {
|
|
254
|
+
const declaration = item.declaration;
|
|
255
|
+
if (declaration.kind === "variableDeclaration") {
|
|
256
|
+
for (const declarator of declaration.declarations) {
|
|
257
|
+
if (declarator.name.kind !== "identifierPattern") {
|
|
258
|
+
return {
|
|
259
|
+
ok: false,
|
|
260
|
+
error: `Unsupported exported variable declarator in ${module.filePath}: ${declarator.name.kind}.\n` +
|
|
261
|
+
"First-party bindings generation requires identifier-based exported variables.",
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
const localName = declarator.name.name;
|
|
265
|
+
pushExport({
|
|
266
|
+
exportName: localName,
|
|
267
|
+
localName,
|
|
268
|
+
kind: "variable",
|
|
269
|
+
declaration,
|
|
270
|
+
declaringNamespace: module.namespace,
|
|
271
|
+
declaringClassName: module.className,
|
|
272
|
+
declaringFilePath: module.filePath,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
const declarationName = declarationNameOf(declaration);
|
|
278
|
+
if (!declarationName) {
|
|
279
|
+
return {
|
|
280
|
+
ok: false,
|
|
281
|
+
error: `Unsupported exported declaration in ${module.filePath}: ${declaration.kind}.\n` +
|
|
282
|
+
"First-party bindings generation requires explicit support for each exported declaration kind.",
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
const declarationKind = classifyDeclarationKind(declaration, module.filePath, declarationName);
|
|
286
|
+
if (!declarationKind.ok)
|
|
287
|
+
return declarationKind;
|
|
288
|
+
pushExport({
|
|
289
|
+
exportName: declarationName,
|
|
290
|
+
localName: declarationName,
|
|
291
|
+
kind: declarationKind.value,
|
|
292
|
+
declaration,
|
|
293
|
+
declaringNamespace: module.namespace,
|
|
294
|
+
declaringClassName: module.className,
|
|
295
|
+
declaringFilePath: module.filePath,
|
|
296
|
+
});
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
if (item.kind === "reexport")
|
|
300
|
+
continue;
|
|
301
|
+
const resolved = resolveExportedDeclaration(module, item.name, modulesByFileKey);
|
|
302
|
+
if (!resolved.ok)
|
|
303
|
+
return resolved;
|
|
304
|
+
const declaration = resolved.value.declaration;
|
|
305
|
+
const declarationName = declarationNameOf(declaration);
|
|
306
|
+
if (!declarationName && declaration.kind !== "variableDeclaration") {
|
|
307
|
+
return {
|
|
308
|
+
ok: false,
|
|
309
|
+
error: `Unsupported named export '${item.name}' in ${module.filePath}: ${declaration.kind}.\n` +
|
|
310
|
+
"First-party bindings generation requires explicit support for each exported declaration kind.",
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
const declarationKind = classifyDeclarationKind(declaration, module.filePath, item.name);
|
|
314
|
+
if (!declarationKind.ok)
|
|
315
|
+
return declarationKind;
|
|
316
|
+
pushExport({
|
|
317
|
+
exportName: item.name,
|
|
318
|
+
localName: resolved.value.clrName,
|
|
319
|
+
kind: declarationKind.value,
|
|
320
|
+
declaration,
|
|
321
|
+
declaringNamespace: resolved.value.module.namespace,
|
|
322
|
+
declaringClassName: resolved.value.module.className,
|
|
323
|
+
declaringFilePath: resolved.value.module.filePath,
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
return {
|
|
327
|
+
ok: true,
|
|
328
|
+
value: exportedSymbols.sort((left, right) => left.exportName.localeCompare(right.exportName)),
|
|
329
|
+
};
|
|
330
|
+
};
|
|
331
|
+
const moduleNamespacePath = (namespace) => {
|
|
332
|
+
return namespace.length > 0 ? namespace : "index";
|
|
333
|
+
};
|
|
334
|
+
const normalizeModuleFileKey = (filePath) => {
|
|
335
|
+
return filePath
|
|
336
|
+
.replace(/\\/g, "/")
|
|
337
|
+
.replace(/^(\.\/)+/, "")
|
|
338
|
+
.replace(/^\/+/, "");
|
|
339
|
+
};
|
|
340
|
+
const resolveLocalModuleFile = (fromModule, fromFile, modulesByFile) => {
|
|
341
|
+
const dir = posix.dirname(fromFile);
|
|
342
|
+
const candidates = [];
|
|
343
|
+
const raw = fromModule.startsWith("/")
|
|
344
|
+
? posix.normalize(fromModule.slice(1))
|
|
345
|
+
: posix.normalize(posix.join(dir, fromModule));
|
|
346
|
+
candidates.push(raw);
|
|
347
|
+
if (raw.endsWith(".js")) {
|
|
348
|
+
candidates.push(raw.replace(/\.js$/, ".ts"));
|
|
349
|
+
}
|
|
350
|
+
if (!raw.endsWith(".ts") && !raw.endsWith(".js")) {
|
|
351
|
+
candidates.push(raw + ".ts");
|
|
352
|
+
candidates.push(raw + ".js");
|
|
353
|
+
candidates.push(posix.join(raw, "index.ts"));
|
|
354
|
+
candidates.push(posix.join(raw, "index.js"));
|
|
355
|
+
}
|
|
356
|
+
for (const cand of candidates) {
|
|
357
|
+
const normalized = normalizeModuleFileKey(cand);
|
|
358
|
+
const found = modulesByFile.get(normalized);
|
|
359
|
+
if (found)
|
|
360
|
+
return found;
|
|
361
|
+
}
|
|
362
|
+
return undefined;
|
|
363
|
+
};
|
|
364
|
+
const resolveReexportModuleKey = (fromFilePath, fromModule) => {
|
|
365
|
+
const fromDir = posix.dirname(normalizeModuleFileKey(fromFilePath));
|
|
366
|
+
return normalizeModuleFileKey(posix.normalize(posix.join(fromDir, fromModule)));
|
|
367
|
+
};
|
|
368
|
+
const isRelativeModuleSpecifier = (specifier) => specifier.startsWith(".") || specifier.startsWith("/");
|
|
369
|
+
const resolveImportedLocalDeclaration = (module, localName, modulesByFileKey, visited) => {
|
|
370
|
+
for (const importEntry of module.imports) {
|
|
371
|
+
for (const specifier of importEntry.specifiers) {
|
|
372
|
+
if (specifier.localName !== localName)
|
|
373
|
+
continue;
|
|
374
|
+
if (specifier.kind === "namespace") {
|
|
375
|
+
return {
|
|
376
|
+
ok: false,
|
|
377
|
+
error: `Unable to re-export '${localName}' from ${module.filePath}: namespace imports are not supported for first-party bindings generation.`,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
if (!importEntry.isLocal) {
|
|
381
|
+
return {
|
|
382
|
+
ok: false,
|
|
383
|
+
error: `Unsupported re-export in ${module.filePath}: '${localName}' resolves to non-local module '${importEntry.source}'.\n` +
|
|
384
|
+
"First-party bindings generation currently supports only local source-module exports.",
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
const targetModule = modulesByFileKey.get(resolveReexportModuleKey(module.filePath, importEntry.source));
|
|
388
|
+
if (!targetModule) {
|
|
389
|
+
return {
|
|
390
|
+
ok: false,
|
|
391
|
+
error: `Unable to resolve local import target for '${localName}' in ${module.filePath}: '${importEntry.source}'.\n` +
|
|
392
|
+
"First-party bindings generation requires local import targets to resolve deterministically.",
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
const importedName = specifier.kind === "named" ? specifier.name : "default";
|
|
396
|
+
return resolveExportedDeclaration(targetModule, importedName, modulesByFileKey, visited);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return {
|
|
400
|
+
ok: false,
|
|
401
|
+
error: `Unable to resolve local symbol '${localName}' in ${module.filePath}.\n` +
|
|
402
|
+
"First-party bindings generation requires resolvable local exports and aliases.",
|
|
403
|
+
};
|
|
404
|
+
};
|
|
405
|
+
const resolveExportedDeclaration = (module, exportName, modulesByFileKey, visited = new Set()) => {
|
|
406
|
+
const cycleKey = `${normalizeModuleFileKey(module.filePath)}::${exportName}`;
|
|
407
|
+
if (visited.has(cycleKey)) {
|
|
408
|
+
return {
|
|
409
|
+
ok: false,
|
|
410
|
+
error: `Cyclic re-export detected while resolving '${exportName}' in ${module.filePath}.\n` +
|
|
411
|
+
"First-party bindings generation requires acyclic local re-export graphs.",
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
const nextVisited = new Set(visited);
|
|
415
|
+
nextVisited.add(cycleKey);
|
|
416
|
+
for (const item of module.exports) {
|
|
417
|
+
if (item.kind === "declaration") {
|
|
418
|
+
const declaration = item.declaration;
|
|
419
|
+
if (declaration.kind === "variableDeclaration") {
|
|
420
|
+
for (const declarator of declaration.declarations) {
|
|
421
|
+
if (declarator.name.kind !== "identifierPattern")
|
|
422
|
+
continue;
|
|
423
|
+
if (declarator.name.name !== exportName)
|
|
424
|
+
continue;
|
|
425
|
+
return {
|
|
426
|
+
ok: true,
|
|
427
|
+
value: {
|
|
428
|
+
declaration,
|
|
429
|
+
module,
|
|
430
|
+
clrName: declarator.name.name,
|
|
431
|
+
},
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
const declarationName = declarationNameOf(declaration);
|
|
437
|
+
if (declarationName !== exportName)
|
|
438
|
+
continue;
|
|
439
|
+
return {
|
|
440
|
+
ok: true,
|
|
441
|
+
value: {
|
|
442
|
+
declaration,
|
|
443
|
+
module,
|
|
444
|
+
clrName: declarationName,
|
|
445
|
+
},
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
if (item.kind === "named") {
|
|
449
|
+
if (item.name !== exportName)
|
|
450
|
+
continue;
|
|
451
|
+
const declaration = resolveModuleLocalDeclaration(module, item.localName);
|
|
452
|
+
if (declaration) {
|
|
453
|
+
return {
|
|
454
|
+
ok: true,
|
|
455
|
+
value: {
|
|
456
|
+
declaration,
|
|
457
|
+
module,
|
|
458
|
+
clrName: item.localName,
|
|
459
|
+
},
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
return resolveImportedLocalDeclaration(module, item.localName, modulesByFileKey, nextVisited);
|
|
463
|
+
}
|
|
464
|
+
if (item.kind === "reexport") {
|
|
465
|
+
if (item.name !== exportName)
|
|
466
|
+
continue;
|
|
467
|
+
if (!isRelativeModuleSpecifier(item.fromModule)) {
|
|
468
|
+
return {
|
|
469
|
+
ok: false,
|
|
470
|
+
error: `Unsupported re-export in ${module.filePath}: '${item.name}' from '${item.fromModule}'.\n` +
|
|
471
|
+
"First-party bindings generation currently supports only relative re-exports from local source modules.",
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
const targetModule = modulesByFileKey.get(resolveReexportModuleKey(module.filePath, item.fromModule));
|
|
475
|
+
if (!targetModule) {
|
|
476
|
+
return {
|
|
477
|
+
ok: false,
|
|
478
|
+
error: `Unable to resolve local re-export target for '${item.name}' in ${module.filePath}: '${item.fromModule}'.\n` +
|
|
479
|
+
"First-party bindings generation requires local re-export targets to resolve deterministically.",
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
return resolveExportedDeclaration(targetModule, item.originalName, modulesByFileKey, nextVisited);
|
|
483
|
+
}
|
|
484
|
+
if (item.kind === "default" && exportName === "default") {
|
|
485
|
+
return {
|
|
486
|
+
ok: false,
|
|
487
|
+
error: `Unsupported default export in ${module.filePath}.\n` +
|
|
488
|
+
"First-party bindings generation currently requires named/declaration exports for deterministic namespace facades.",
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return {
|
|
493
|
+
ok: false,
|
|
494
|
+
error: `Unable to resolve exported symbol '${exportName}' in ${module.filePath}.\n` +
|
|
495
|
+
"First-party bindings generation requires explicit resolvable exports.",
|
|
496
|
+
};
|
|
497
|
+
};
|
|
498
|
+
const buildModuleSourceIndex = (absoluteFilePath, fileKey) => {
|
|
499
|
+
if (!existsSync(absoluteFilePath)) {
|
|
500
|
+
return {
|
|
501
|
+
ok: false,
|
|
502
|
+
error: `Failed to read source file for bindings generation: ${absoluteFilePath}`,
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
const content = readFileSync(absoluteFilePath, "utf-8");
|
|
506
|
+
const scriptKind = absoluteFilePath.endsWith(".tsx")
|
|
507
|
+
? ts.ScriptKind.TSX
|
|
508
|
+
: absoluteFilePath.endsWith(".js")
|
|
509
|
+
? ts.ScriptKind.JS
|
|
510
|
+
: ts.ScriptKind.TS;
|
|
511
|
+
const sourceFile = ts.createSourceFile(absoluteFilePath, content, ts.ScriptTarget.Latest, true, scriptKind);
|
|
512
|
+
const wrapperImportsByLocalName = new Map();
|
|
513
|
+
const typeImportsByLocalName = new Map();
|
|
514
|
+
const typeAliasesByName = new Map();
|
|
515
|
+
const exportedFunctionSignaturesByName = new Map();
|
|
516
|
+
const memberTypesByClassAndMember = new Map();
|
|
517
|
+
const printTypeParametersText = (typeParameters) => {
|
|
518
|
+
if (!typeParameters || typeParameters.length === 0)
|
|
519
|
+
return "";
|
|
520
|
+
return `<${typeParameters.map((tp) => tp.getText(sourceFile)).join(", ")}>`;
|
|
521
|
+
};
|
|
522
|
+
const printParameterText = (param) => {
|
|
523
|
+
const rest = param.dotDotDotToken ? "..." : "";
|
|
524
|
+
const name = param.name.getText(sourceFile);
|
|
525
|
+
const optional = param.questionToken ? "?" : "";
|
|
526
|
+
const type = param.type
|
|
527
|
+
? printTypeNodeText(param.type, sourceFile)
|
|
528
|
+
: "unknown";
|
|
529
|
+
return `${rest}${name}${optional}: ${type}`;
|
|
530
|
+
};
|
|
531
|
+
const addExportedFunctionSignature = (name, signature) => {
|
|
532
|
+
const signatures = exportedFunctionSignaturesByName.get(name) ?? [];
|
|
533
|
+
signatures.push(signature);
|
|
534
|
+
exportedFunctionSignaturesByName.set(name, signatures);
|
|
535
|
+
};
|
|
536
|
+
for (const stmt of sourceFile.statements) {
|
|
537
|
+
if (ts.isImportDeclaration(stmt)) {
|
|
538
|
+
const moduleSpecifier = ts.isStringLiteral(stmt.moduleSpecifier)
|
|
539
|
+
? stmt.moduleSpecifier.text
|
|
540
|
+
: undefined;
|
|
541
|
+
if (!moduleSpecifier)
|
|
542
|
+
continue;
|
|
543
|
+
const clause = stmt.importClause;
|
|
544
|
+
if (!clause)
|
|
545
|
+
continue;
|
|
546
|
+
const namedBindings = clause.namedBindings;
|
|
547
|
+
if (!namedBindings || !ts.isNamedImports(namedBindings))
|
|
548
|
+
continue;
|
|
549
|
+
for (const spec of namedBindings.elements) {
|
|
550
|
+
const localName = spec.name.text;
|
|
551
|
+
const importedName = (spec.propertyName ?? spec.name).text;
|
|
552
|
+
const isTypeOnly = clause.isTypeOnly || spec.isTypeOnly;
|
|
553
|
+
if (!isTypeOnly)
|
|
554
|
+
continue;
|
|
555
|
+
typeImportsByLocalName.set(localName, {
|
|
556
|
+
source: moduleSpecifier,
|
|
557
|
+
importedName,
|
|
558
|
+
});
|
|
559
|
+
if (importedName === "ExtensionMethods") {
|
|
560
|
+
wrapperImportsByLocalName.set(localName, {
|
|
561
|
+
source: moduleSpecifier,
|
|
562
|
+
importedName,
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
continue;
|
|
567
|
+
}
|
|
568
|
+
if (ts.isTypeAliasDeclaration(stmt)) {
|
|
569
|
+
const aliasName = stmt.name.text;
|
|
570
|
+
const typeParameterNames = (stmt.typeParameters ?? []).map((tp) => tp.name.text);
|
|
571
|
+
typeAliasesByName.set(aliasName, {
|
|
572
|
+
typeParametersText: printTypeParametersText(stmt.typeParameters),
|
|
573
|
+
typeParameterNames,
|
|
574
|
+
type: stmt.type,
|
|
575
|
+
typeText: printTypeNodeText(stmt.type, sourceFile),
|
|
576
|
+
});
|
|
577
|
+
continue;
|
|
578
|
+
}
|
|
579
|
+
if (ts.isFunctionDeclaration(stmt)) {
|
|
580
|
+
const hasExport = stmt.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword);
|
|
581
|
+
if (!hasExport || !stmt.name || !stmt.type)
|
|
582
|
+
continue;
|
|
583
|
+
const parametersText = stmt.parameters.map(printParameterText).join(", ");
|
|
584
|
+
addExportedFunctionSignature(stmt.name.text, {
|
|
585
|
+
typeParametersText: printTypeParametersText(stmt.typeParameters),
|
|
586
|
+
parametersText,
|
|
587
|
+
returnTypeText: printTypeNodeText(stmt.type, sourceFile),
|
|
588
|
+
});
|
|
589
|
+
continue;
|
|
590
|
+
}
|
|
591
|
+
if (ts.isVariableStatement(stmt)) {
|
|
592
|
+
const hasExport = stmt.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword);
|
|
593
|
+
if (!hasExport)
|
|
594
|
+
continue;
|
|
595
|
+
for (const declaration of stmt.declarationList.declarations) {
|
|
596
|
+
if (!ts.isIdentifier(declaration.name))
|
|
597
|
+
continue;
|
|
598
|
+
const exportName = declaration.name.text;
|
|
599
|
+
const initializer = declaration.initializer;
|
|
600
|
+
if (!initializer)
|
|
601
|
+
continue;
|
|
602
|
+
if (!ts.isArrowFunction(initializer) &&
|
|
603
|
+
!ts.isFunctionExpression(initializer)) {
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
if (!initializer.type)
|
|
607
|
+
continue;
|
|
608
|
+
const parametersText = initializer.parameters
|
|
609
|
+
.map(printParameterText)
|
|
610
|
+
.join(", ");
|
|
611
|
+
addExportedFunctionSignature(exportName, {
|
|
612
|
+
typeParametersText: printTypeParametersText(initializer.typeParameters),
|
|
613
|
+
parametersText,
|
|
614
|
+
returnTypeText: printTypeNodeText(initializer.type, sourceFile),
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
continue;
|
|
618
|
+
}
|
|
619
|
+
if (ts.isClassDeclaration(stmt) && stmt.name) {
|
|
620
|
+
const className = stmt.name.text;
|
|
621
|
+
const members = memberTypesByClassAndMember.get(className) ??
|
|
622
|
+
new Map();
|
|
623
|
+
for (const member of stmt.members) {
|
|
624
|
+
if (ts.isGetAccessorDeclaration(member)) {
|
|
625
|
+
if (!member.name || !member.type)
|
|
626
|
+
continue;
|
|
627
|
+
const name = getPropertyNameText(member.name);
|
|
628
|
+
if (!name)
|
|
629
|
+
continue;
|
|
630
|
+
members.set(name, {
|
|
631
|
+
typeNode: member.type,
|
|
632
|
+
typeText: printTypeNodeText(member.type, sourceFile),
|
|
633
|
+
isOptional: false,
|
|
634
|
+
});
|
|
635
|
+
continue;
|
|
636
|
+
}
|
|
637
|
+
if (ts.isPropertyDeclaration(member)) {
|
|
638
|
+
if (!member.name || !member.type)
|
|
639
|
+
continue;
|
|
640
|
+
const name = getPropertyNameText(member.name);
|
|
641
|
+
if (!name)
|
|
642
|
+
continue;
|
|
643
|
+
members.set(name, {
|
|
644
|
+
typeNode: member.type,
|
|
645
|
+
typeText: printTypeNodeText(member.type, sourceFile),
|
|
646
|
+
isOptional: member.questionToken !== undefined,
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
if (members.size > 0) {
|
|
651
|
+
memberTypesByClassAndMember.set(className, members);
|
|
652
|
+
}
|
|
653
|
+
continue;
|
|
654
|
+
}
|
|
655
|
+
if (ts.isInterfaceDeclaration(stmt)) {
|
|
656
|
+
const interfaceName = stmt.name.text;
|
|
657
|
+
const members = memberTypesByClassAndMember.get(interfaceName) ??
|
|
658
|
+
new Map();
|
|
659
|
+
for (const member of stmt.members) {
|
|
660
|
+
if (!ts.isPropertySignature(member))
|
|
661
|
+
continue;
|
|
662
|
+
if (!member.name || !member.type)
|
|
663
|
+
continue;
|
|
664
|
+
const name = getPropertyNameText(member.name);
|
|
665
|
+
if (!name)
|
|
666
|
+
continue;
|
|
667
|
+
members.set(name, {
|
|
668
|
+
typeNode: member.type,
|
|
669
|
+
typeText: printTypeNodeText(member.type, sourceFile),
|
|
670
|
+
isOptional: member.questionToken !== undefined,
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
if (members.size > 0) {
|
|
674
|
+
memberTypesByClassAndMember.set(interfaceName, members);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
return {
|
|
679
|
+
ok: true,
|
|
680
|
+
value: {
|
|
681
|
+
fileKey,
|
|
682
|
+
wrapperImportsByLocalName,
|
|
683
|
+
typeImportsByLocalName,
|
|
684
|
+
typeAliasesByName,
|
|
685
|
+
exportedFunctionSignaturesByName,
|
|
686
|
+
memberTypesByClassAndMember,
|
|
687
|
+
},
|
|
688
|
+
};
|
|
689
|
+
};
|
|
690
|
+
const typeNodeUsesImportedTypeNames = (node, typeImportsByLocalName) => {
|
|
691
|
+
const allowlistedImportSources = new Set(["@tsonic/core/types.js"]);
|
|
692
|
+
let found = false;
|
|
693
|
+
const visit = (current) => {
|
|
694
|
+
if (found)
|
|
695
|
+
return;
|
|
696
|
+
if (ts.isTypeReferenceNode(current) && ts.isIdentifier(current.typeName)) {
|
|
697
|
+
const imported = typeImportsByLocalName.get(current.typeName.text);
|
|
698
|
+
if (imported && !allowlistedImportSources.has(imported.source.trim())) {
|
|
699
|
+
found = true;
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
ts.forEachChild(current, visit);
|
|
704
|
+
};
|
|
705
|
+
visit(node);
|
|
706
|
+
return found;
|
|
707
|
+
};
|
|
708
|
+
const unwrapParens = (node) => {
|
|
709
|
+
let current = node;
|
|
710
|
+
while (ts.isParenthesizedTypeNode(current)) {
|
|
711
|
+
current = current.type;
|
|
712
|
+
}
|
|
713
|
+
return current;
|
|
714
|
+
};
|
|
715
|
+
const collectExtensionWrapperImportsFromSourceType = (opts) => {
|
|
716
|
+
const wrappers = [];
|
|
717
|
+
let currentModuleKey = opts.startModuleKey;
|
|
718
|
+
let currentNode = opts.typeNode;
|
|
719
|
+
let subst = new Map();
|
|
720
|
+
const aliasStack = [];
|
|
721
|
+
while (true) {
|
|
722
|
+
currentNode = unwrapParens(currentNode);
|
|
723
|
+
if (!ts.isTypeReferenceNode(currentNode))
|
|
724
|
+
break;
|
|
725
|
+
if (!ts.isIdentifier(currentNode.typeName))
|
|
726
|
+
break;
|
|
727
|
+
const ident = currentNode.typeName.text;
|
|
728
|
+
const info = opts.sourceIndexByFileKey.get(currentModuleKey);
|
|
729
|
+
if (!info)
|
|
730
|
+
break;
|
|
731
|
+
const substituted = subst.get(ident);
|
|
732
|
+
if (substituted) {
|
|
733
|
+
currentNode = substituted;
|
|
734
|
+
continue;
|
|
735
|
+
}
|
|
736
|
+
const expandAlias = (aliasKey, alias, typeArgs) => {
|
|
737
|
+
if (aliasStack.includes(aliasKey))
|
|
738
|
+
return;
|
|
739
|
+
aliasStack.push(aliasKey);
|
|
740
|
+
if (alias.typeParameterNames.length === typeArgs.length) {
|
|
741
|
+
const next = new Map(subst);
|
|
742
|
+
for (let i = 0; i < alias.typeParameterNames.length; i += 1) {
|
|
743
|
+
const paramName = alias.typeParameterNames[i];
|
|
744
|
+
const arg = typeArgs[i];
|
|
745
|
+
if (!paramName || !arg)
|
|
746
|
+
continue;
|
|
747
|
+
next.set(paramName, arg);
|
|
748
|
+
}
|
|
749
|
+
subst = next;
|
|
750
|
+
}
|
|
751
|
+
currentNode = alias.type;
|
|
752
|
+
};
|
|
753
|
+
const localAlias = info.typeAliasesByName.get(ident);
|
|
754
|
+
if (localAlias) {
|
|
755
|
+
expandAlias(`${currentModuleKey}:${ident}`, localAlias, currentNode.typeArguments ?? []);
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
758
|
+
const imported = info.typeImportsByLocalName.get(ident);
|
|
759
|
+
if (imported &&
|
|
760
|
+
(imported.source.startsWith(".") || imported.source.startsWith("/"))) {
|
|
761
|
+
const targetModule = resolveLocalModuleFile(imported.source, currentModuleKey, opts.modulesByFileKey);
|
|
762
|
+
if (targetModule) {
|
|
763
|
+
const targetKey = normalizeModuleFileKey(targetModule.filePath);
|
|
764
|
+
const targetInfo = opts.sourceIndexByFileKey.get(targetKey);
|
|
765
|
+
const targetAlias = targetInfo?.typeAliasesByName.get(imported.importedName);
|
|
766
|
+
if (targetAlias) {
|
|
767
|
+
currentModuleKey = targetKey;
|
|
768
|
+
expandAlias(`${targetKey}:${imported.importedName}`, targetAlias, currentNode.typeArguments ?? []);
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
const wrapperImport = info.wrapperImportsByLocalName.get(ident);
|
|
774
|
+
if (!wrapperImport)
|
|
775
|
+
break;
|
|
776
|
+
const args = currentNode.typeArguments ?? [];
|
|
777
|
+
if (args.length !== 1) {
|
|
778
|
+
return {
|
|
779
|
+
ok: false,
|
|
780
|
+
error: `ExtensionMethods wrapper '${ident}' must have exactly 1 type argument.\n` +
|
|
781
|
+
`Found: ${args.length} in ${currentModuleKey}.`,
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
wrappers.push({
|
|
785
|
+
source: wrapperImport.source,
|
|
786
|
+
importedName: wrapperImport.importedName,
|
|
787
|
+
localName: ident,
|
|
788
|
+
aliasName: `__TsonicExt_${ident}`,
|
|
789
|
+
});
|
|
790
|
+
const nextNode = args[0];
|
|
791
|
+
if (!nextNode) {
|
|
792
|
+
return {
|
|
793
|
+
ok: false,
|
|
794
|
+
error: `ExtensionMethods wrapper '${ident}' is missing its type argument in ${currentModuleKey}.`,
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
currentNode = nextNode;
|
|
798
|
+
}
|
|
799
|
+
return { ok: true, value: wrappers };
|
|
800
|
+
};
|
|
801
|
+
const classifyExportKind = (module, name) => {
|
|
802
|
+
const isNamed = (stmt) => typeof stmt.name === "string";
|
|
803
|
+
const findDecl = () => {
|
|
804
|
+
for (const stmt of module.body) {
|
|
805
|
+
if (!("isExported" in stmt) ||
|
|
806
|
+
stmt.isExported !== true)
|
|
807
|
+
continue;
|
|
808
|
+
if (isNamed(stmt) && stmt.name === name)
|
|
809
|
+
return stmt;
|
|
810
|
+
if (stmt.kind === "variableDeclaration") {
|
|
811
|
+
for (const decl of stmt.declarations) {
|
|
812
|
+
if (decl.name.kind === "identifierPattern" &&
|
|
813
|
+
decl.name.name === name) {
|
|
814
|
+
return stmt;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
return undefined;
|
|
820
|
+
};
|
|
821
|
+
const decl = findDecl();
|
|
822
|
+
if (!decl)
|
|
823
|
+
return "unknown";
|
|
824
|
+
switch (decl.kind) {
|
|
825
|
+
case "typeAliasDeclaration":
|
|
826
|
+
case "interfaceDeclaration":
|
|
827
|
+
return "type";
|
|
828
|
+
case "classDeclaration":
|
|
829
|
+
case "enumDeclaration":
|
|
830
|
+
case "functionDeclaration":
|
|
831
|
+
case "variableDeclaration":
|
|
832
|
+
return "value";
|
|
833
|
+
default:
|
|
834
|
+
return "unknown";
|
|
835
|
+
}
|
|
836
|
+
};
|
|
837
|
+
const moduleNamespaceToInternalSpecifier = (namespace) => {
|
|
838
|
+
const nsPath = moduleNamespacePath(namespace);
|
|
839
|
+
return `./${nsPath}/internal/index.js`;
|
|
840
|
+
};
|
|
841
|
+
const toClrTypeName = (namespace, typeName, arity) => {
|
|
842
|
+
const suffix = arity && arity > 0 ? `\`${arity}` : "";
|
|
843
|
+
return `${namespace}.${typeName}${suffix}`;
|
|
844
|
+
};
|
|
845
|
+
const toStableId = (assemblyName, clrName) => {
|
|
846
|
+
return `${assemblyName}:${clrName}`;
|
|
847
|
+
};
|
|
848
|
+
const primitiveSignatureType = (name) => {
|
|
849
|
+
const map = {
|
|
850
|
+
string: "System.String",
|
|
851
|
+
boolean: "System.Boolean",
|
|
852
|
+
number: "System.Double",
|
|
853
|
+
int: "System.Int32",
|
|
854
|
+
char: "System.Char",
|
|
855
|
+
null: "System.Object",
|
|
856
|
+
undefined: "System.Object",
|
|
857
|
+
};
|
|
858
|
+
return map[name] ?? name;
|
|
859
|
+
};
|
|
860
|
+
const isNumericValueType = (name) => {
|
|
861
|
+
return (name === "System.Int32" ||
|
|
862
|
+
name === "System.Double" ||
|
|
863
|
+
name === "System.Single" ||
|
|
864
|
+
name === "System.Decimal" ||
|
|
865
|
+
name === "System.Int64" ||
|
|
866
|
+
name === "System.Int16" ||
|
|
867
|
+
name === "System.UInt16" ||
|
|
868
|
+
name === "System.UInt32" ||
|
|
869
|
+
name === "System.UInt64" ||
|
|
870
|
+
name === "System.Byte" ||
|
|
871
|
+
name === "System.SByte");
|
|
872
|
+
};
|
|
873
|
+
const toSignatureType = (type, typeParametersInScope) => {
|
|
874
|
+
if (!type)
|
|
875
|
+
return "System.Object";
|
|
876
|
+
switch (type.kind) {
|
|
877
|
+
case "primitiveType":
|
|
878
|
+
return primitiveSignatureType(type.name);
|
|
879
|
+
case "literalType":
|
|
880
|
+
if (typeof type.value === "string")
|
|
881
|
+
return "System.String";
|
|
882
|
+
if (typeof type.value === "boolean")
|
|
883
|
+
return "System.Boolean";
|
|
884
|
+
if (typeof type.value === "number")
|
|
885
|
+
return "System.Double";
|
|
886
|
+
return "System.Object";
|
|
887
|
+
case "voidType":
|
|
888
|
+
return "System.Void";
|
|
889
|
+
case "neverType":
|
|
890
|
+
case "unknownType":
|
|
891
|
+
case "anyType":
|
|
892
|
+
return "System.Object";
|
|
893
|
+
case "typeParameterType":
|
|
894
|
+
return type.name;
|
|
895
|
+
case "arrayType":
|
|
896
|
+
return `${toSignatureType(type.elementType, typeParametersInScope)}[]`;
|
|
897
|
+
case "tupleType":
|
|
898
|
+
case "objectType":
|
|
899
|
+
case "functionType":
|
|
900
|
+
case "dictionaryType":
|
|
901
|
+
return "System.Object";
|
|
902
|
+
case "intersectionType":
|
|
903
|
+
return toSignatureType(type.types[0], typeParametersInScope);
|
|
904
|
+
case "unionType": {
|
|
905
|
+
const nonUndefined = type.types.filter((candidate) => {
|
|
906
|
+
return !(candidate.kind === "primitiveType" && candidate.name === "undefined");
|
|
907
|
+
});
|
|
908
|
+
if (nonUndefined.length === 1 && nonUndefined[0]) {
|
|
909
|
+
const single = toSignatureType(nonUndefined[0], typeParametersInScope);
|
|
910
|
+
if (isNumericValueType(single)) {
|
|
911
|
+
return `System.Nullable\`1[[${single}]]`;
|
|
912
|
+
}
|
|
913
|
+
return single;
|
|
914
|
+
}
|
|
915
|
+
return "System.Object";
|
|
916
|
+
}
|
|
917
|
+
case "referenceType": {
|
|
918
|
+
if (typeParametersInScope.includes(type.name))
|
|
919
|
+
return type.name;
|
|
920
|
+
const normalizedName = normalizeTypeReferenceName(type.name, type.typeArguments?.length);
|
|
921
|
+
if (!type.typeArguments || type.typeArguments.length === 0) {
|
|
922
|
+
return normalizedName;
|
|
923
|
+
}
|
|
924
|
+
const args = type.typeArguments
|
|
925
|
+
.map((arg) => toSignatureType(arg, typeParametersInScope))
|
|
926
|
+
.join(",");
|
|
927
|
+
return `${normalizedName}[[${args}]]`;
|
|
928
|
+
}
|
|
929
|
+
default:
|
|
930
|
+
return "System.Object";
|
|
931
|
+
}
|
|
932
|
+
};
|
|
933
|
+
const buildParameterModifiers = (parameters) => {
|
|
934
|
+
const modifiers = parameters
|
|
935
|
+
.map((parameter, index) => {
|
|
936
|
+
if (parameter.passing === "value")
|
|
937
|
+
return undefined;
|
|
938
|
+
return { index, modifier: parameter.passing };
|
|
939
|
+
})
|
|
940
|
+
.filter((modifier) => modifier !== undefined);
|
|
941
|
+
return modifiers;
|
|
942
|
+
};
|
|
943
|
+
const makeMethodBinding = (opts) => {
|
|
944
|
+
const typeParameterScope = Array.from(new Set(opts.parameters
|
|
945
|
+
.map((parameter) => parameter.type?.kind === "typeParameterType"
|
|
946
|
+
? parameter.type.name
|
|
947
|
+
: undefined)
|
|
948
|
+
.filter((name) => name !== undefined)));
|
|
949
|
+
const normalizedSignature = `${opts.methodName}|(${opts.parameters
|
|
950
|
+
.map((parameter) => toSignatureType(parameter.type, typeParameterScope))
|
|
951
|
+
.join(",")}):${toSignatureType(opts.returnType, typeParameterScope)}|static=${opts.isStatic ? "true" : "false"}`;
|
|
952
|
+
const stableId = `${toStableId(opts.declaringAssemblyName, opts.declaringClrType)}::method:${opts.methodName}|${normalizedSignature}`;
|
|
953
|
+
return {
|
|
954
|
+
stableId,
|
|
955
|
+
clrName: opts.methodName,
|
|
956
|
+
normalizedSignature,
|
|
957
|
+
arity: opts.arity,
|
|
958
|
+
parameterCount: opts.parameters.length,
|
|
959
|
+
isStatic: opts.isStatic,
|
|
960
|
+
isAbstract: opts.isAbstract ?? false,
|
|
961
|
+
isVirtual: opts.isVirtual ?? false,
|
|
962
|
+
isOverride: opts.isOverride ?? false,
|
|
963
|
+
isSealed: opts.isSealed ?? false,
|
|
964
|
+
declaringClrType: opts.declaringClrType,
|
|
965
|
+
declaringAssemblyName: opts.declaringAssemblyName,
|
|
966
|
+
parameterModifiers: opts.parameterModifiers.length > 0 ? opts.parameterModifiers : undefined,
|
|
967
|
+
isExtensionMethod: false,
|
|
968
|
+
};
|
|
969
|
+
};
|
|
970
|
+
const renderClassInternal = (declaration, namespace, memberOverrides) => {
|
|
971
|
+
const lines = [];
|
|
972
|
+
const typeParameterScope = (declaration.typeParameters ?? []).map((typeParameter) => typeParameter.name);
|
|
973
|
+
const typeParameters = printTypeParameters(declaration.typeParameters);
|
|
974
|
+
const markerName = `__tsonic_type_${sanitizeForBrand(namespace)}_${sanitizeForBrand(declaration.name)}`;
|
|
975
|
+
const heritageNames = [
|
|
976
|
+
declaration.superClass
|
|
977
|
+
? renderPortableType(declaration.superClass, typeParameterScope)
|
|
978
|
+
: undefined,
|
|
979
|
+
...declaration.implements.map((implementedType) => renderPortableType(implementedType, typeParameterScope)),
|
|
980
|
+
]
|
|
981
|
+
.filter((name) => name !== undefined)
|
|
982
|
+
.map((name) => name.trim())
|
|
983
|
+
.filter((name) => name.length > 0 &&
|
|
984
|
+
name !== "unknown" &&
|
|
985
|
+
name !== "never" &&
|
|
986
|
+
name !== "void");
|
|
987
|
+
const extendsClause = heritageNames.length > 0
|
|
988
|
+
? ` extends ${Array.from(new Set(heritageNames)).join(", ")}`
|
|
989
|
+
: "";
|
|
990
|
+
lines.push(`export interface ${declaration.name}$instance${typeParameters}${extendsClause} {`);
|
|
991
|
+
lines.push(` readonly ${markerName}: never;`);
|
|
992
|
+
const instanceMembers = declaration.members.filter((member) => {
|
|
993
|
+
if (member.kind === "constructorDeclaration")
|
|
994
|
+
return false;
|
|
995
|
+
if ("isStatic" in member && member.isStatic)
|
|
996
|
+
return false;
|
|
997
|
+
return true;
|
|
998
|
+
});
|
|
999
|
+
for (const member of instanceMembers) {
|
|
1000
|
+
if (member.kind === "methodDeclaration") {
|
|
1001
|
+
lines.push(` ${renderMethodSignature(member.name, member.typeParameters, member.parameters, member.returnType)}`);
|
|
1002
|
+
continue;
|
|
1003
|
+
}
|
|
1004
|
+
if (member.kind === "propertyDeclaration") {
|
|
1005
|
+
const memberOverride = memberOverrides.get(member.name);
|
|
1006
|
+
const hasAccessorBody = member.getterBody !== undefined || member.setterBody !== undefined;
|
|
1007
|
+
const hasGetter = hasAccessorBody
|
|
1008
|
+
? member.getterBody !== undefined
|
|
1009
|
+
: true;
|
|
1010
|
+
const hasSetter = hasAccessorBody
|
|
1011
|
+
? member.setterBody !== undefined
|
|
1012
|
+
: !member.isReadonly;
|
|
1013
|
+
const baseType = (memberOverride?.replaceWithSourceType
|
|
1014
|
+
? memberOverride.sourceTypeText
|
|
1015
|
+
: undefined) ?? renderPortableType(member.type);
|
|
1016
|
+
const wrappedType = applyWrappersToBaseType(baseType, memberOverride?.wrappers ?? []);
|
|
1017
|
+
const memberType = memberOverride?.isOptional === true
|
|
1018
|
+
? ensureUndefinedInType(wrappedType)
|
|
1019
|
+
: wrappedType;
|
|
1020
|
+
if (hasGetter && !hasSetter) {
|
|
1021
|
+
lines.push(` readonly ${member.name}: ${memberType};`);
|
|
1022
|
+
continue;
|
|
1023
|
+
}
|
|
1024
|
+
lines.push(` ${member.name}: ${memberType};`);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
lines.push("}");
|
|
1028
|
+
lines.push("");
|
|
1029
|
+
lines.push(`export const ${declaration.name}: {`);
|
|
1030
|
+
lines.push(` new(...args: unknown[]): ${declaration.name}${typeParameters};`);
|
|
1031
|
+
const staticMembers = declaration.members.filter((member) => {
|
|
1032
|
+
if (member.kind === "constructorDeclaration")
|
|
1033
|
+
return false;
|
|
1034
|
+
return "isStatic" in member && member.isStatic;
|
|
1035
|
+
});
|
|
1036
|
+
for (const member of staticMembers) {
|
|
1037
|
+
if (member.kind === "methodDeclaration") {
|
|
1038
|
+
lines.push(` ${renderMethodSignature(member.name, member.typeParameters, member.parameters, member.returnType)}`);
|
|
1039
|
+
continue;
|
|
1040
|
+
}
|
|
1041
|
+
if (member.kind === "propertyDeclaration") {
|
|
1042
|
+
lines.push(` ${member.name}: ${renderPortableType(member.type)};`);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
lines.push("};");
|
|
1046
|
+
lines.push("");
|
|
1047
|
+
lines.push(`export type ${declaration.name}${typeParameters} = ${declaration.name}$instance${typeParameters};`);
|
|
1048
|
+
lines.push("");
|
|
1049
|
+
return lines;
|
|
1050
|
+
};
|
|
1051
|
+
const renderInterfaceInternal = (declaration, namespace, memberOverrides) => {
|
|
1052
|
+
const lines = [];
|
|
1053
|
+
const typeParameterScope = (declaration.typeParameters ?? []).map((typeParameter) => typeParameter.name);
|
|
1054
|
+
const typeParameters = printTypeParameters(declaration.typeParameters);
|
|
1055
|
+
const markerName = `__tsonic_type_${sanitizeForBrand(namespace)}_${sanitizeForBrand(declaration.name)}`;
|
|
1056
|
+
const extendsNames = declaration.extends
|
|
1057
|
+
.map((baseType) => renderPortableType(baseType, typeParameterScope).trim())
|
|
1058
|
+
.filter((name) => name.length > 0 &&
|
|
1059
|
+
name !== "unknown" &&
|
|
1060
|
+
name !== "never" &&
|
|
1061
|
+
name !== "void");
|
|
1062
|
+
const extendsClause = extendsNames.length > 0
|
|
1063
|
+
? ` extends ${Array.from(new Set(extendsNames)).join(", ")}`
|
|
1064
|
+
: "";
|
|
1065
|
+
lines.push(`export interface ${declaration.name}$instance${typeParameters}${extendsClause} {`);
|
|
1066
|
+
lines.push(` readonly ${markerName}?: never;`);
|
|
1067
|
+
for (const member of declaration.members) {
|
|
1068
|
+
if (member.kind === "methodSignature") {
|
|
1069
|
+
lines.push(` ${renderMethodSignature(member.name, member.typeParameters, member.parameters, member.returnType)}`);
|
|
1070
|
+
continue;
|
|
1071
|
+
}
|
|
1072
|
+
if (member.kind === "propertySignature") {
|
|
1073
|
+
const memberOverride = memberOverrides.get(member.name);
|
|
1074
|
+
const optionalBySource = memberOverride?.emitOptionalPropertySyntax === true &&
|
|
1075
|
+
memberOverride.isOptional === true &&
|
|
1076
|
+
!member.name.startsWith("__tsonic_type_");
|
|
1077
|
+
const optionalMark = optionalBySource || member.isOptional ? "?" : "";
|
|
1078
|
+
const baseType = (memberOverride?.replaceWithSourceType
|
|
1079
|
+
? memberOverride.sourceTypeText
|
|
1080
|
+
: undefined) ?? renderPortableType(member.type);
|
|
1081
|
+
const wrappedType = applyWrappersToBaseType(baseType, memberOverride?.wrappers ?? []);
|
|
1082
|
+
const memberType = memberOverride?.isOptional && !optionalBySource
|
|
1083
|
+
? ensureUndefinedInType(wrappedType)
|
|
1084
|
+
: wrappedType;
|
|
1085
|
+
lines.push(` ${member.name}${optionalMark}: ${memberType};`);
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
lines.push("}");
|
|
1089
|
+
lines.push("");
|
|
1090
|
+
lines.push(`export type ${declaration.name}${typeParameters} = ${declaration.name}$instance${typeParameters};`);
|
|
1091
|
+
lines.push("");
|
|
1092
|
+
return lines;
|
|
1093
|
+
};
|
|
1094
|
+
const renderEnumInternal = (declaration) => {
|
|
1095
|
+
const lines = [];
|
|
1096
|
+
lines.push(`export enum ${declaration.name} {`);
|
|
1097
|
+
declaration.members.forEach((member, index) => {
|
|
1098
|
+
lines.push(` ${member.name} = ${index},`);
|
|
1099
|
+
});
|
|
1100
|
+
lines.push("}");
|
|
1101
|
+
lines.push("");
|
|
1102
|
+
return lines;
|
|
1103
|
+
};
|
|
1104
|
+
const renderStructuralAliasInternal = (declaration, namespace, memberOverrides) => {
|
|
1105
|
+
if (declaration.type.kind !== "objectType")
|
|
1106
|
+
return [];
|
|
1107
|
+
const lines = [];
|
|
1108
|
+
const arity = declaration.typeParameters?.length ?? 0;
|
|
1109
|
+
const typeParameters = printTypeParameters(declaration.typeParameters);
|
|
1110
|
+
const internalAliasName = `${declaration.name}__Alias${arity > 0 ? `_${arity}` : ""}`;
|
|
1111
|
+
const markerName = `__tsonic_type_${sanitizeForBrand(namespace)}_${sanitizeForBrand(internalAliasName)}`;
|
|
1112
|
+
lines.push(`export interface ${internalAliasName}$instance${typeParameters} {`);
|
|
1113
|
+
lines.push(` readonly ${markerName}?: never;`);
|
|
1114
|
+
for (const member of declaration.type.members) {
|
|
1115
|
+
if (member.kind === "methodSignature") {
|
|
1116
|
+
lines.push(` ${renderMethodSignature(member.name, member.typeParameters, member.parameters, member.returnType)}`);
|
|
1117
|
+
continue;
|
|
1118
|
+
}
|
|
1119
|
+
if (member.kind === "propertySignature") {
|
|
1120
|
+
const memberOverride = memberOverrides.get(member.name);
|
|
1121
|
+
const optionalMark = member.isOptional ? "?" : "";
|
|
1122
|
+
const baseType = (memberOverride?.replaceWithSourceType
|
|
1123
|
+
? memberOverride.sourceTypeText
|
|
1124
|
+
: undefined) ?? renderPortableType(member.type);
|
|
1125
|
+
const wrappedType = applyWrappersToBaseType(baseType, memberOverride?.wrappers ?? []);
|
|
1126
|
+
const memberType = memberOverride?.isOptional === true
|
|
1127
|
+
? ensureUndefinedInType(wrappedType)
|
|
1128
|
+
: wrappedType;
|
|
1129
|
+
lines.push(` ${member.name}${optionalMark}: ${memberType};`);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
lines.push("}");
|
|
1133
|
+
lines.push("");
|
|
1134
|
+
lines.push(`export type ${internalAliasName}${typeParameters} = ${internalAliasName}$instance${typeParameters};`);
|
|
1135
|
+
lines.push("");
|
|
1136
|
+
return lines;
|
|
1137
|
+
};
|
|
1138
|
+
const renderContainerInternal = (entry) => {
|
|
1139
|
+
const lines = [];
|
|
1140
|
+
lines.push(`export abstract class ${entry.module.className}$instance {`);
|
|
1141
|
+
for (const method of entry.methods) {
|
|
1142
|
+
lines.push(` static ${renderMethodSignature(method.localName, method.declaration.typeParameters, method.declaration.parameters, method.declaration.returnType)}`);
|
|
1143
|
+
}
|
|
1144
|
+
for (const variable of entry.variables) {
|
|
1145
|
+
lines.push(` static ${variable.localName}: ${renderPortableType(variable.declarator?.type)};`);
|
|
1146
|
+
}
|
|
1147
|
+
lines.push("}");
|
|
1148
|
+
lines.push("");
|
|
1149
|
+
lines.push(`export type ${entry.module.className} = ${entry.module.className}$instance;`);
|
|
1150
|
+
lines.push("");
|
|
1151
|
+
return lines;
|
|
1152
|
+
};
|
|
1153
|
+
const buildTypeBindingFromClass = (declaration, namespace, assemblyName) => {
|
|
1154
|
+
const declaringClrType = toClrTypeName(namespace, declaration.name, declaration.typeParameters?.length ?? 0);
|
|
1155
|
+
const typeStableId = toStableId(assemblyName, declaringClrType);
|
|
1156
|
+
const typeParameterScope = declaration.typeParameters?.map((typeParameter) => typeParameter.name) ??
|
|
1157
|
+
[];
|
|
1158
|
+
const methods = [];
|
|
1159
|
+
const properties = [];
|
|
1160
|
+
const constructors = [];
|
|
1161
|
+
for (const member of declaration.members) {
|
|
1162
|
+
if (member.kind === "constructorDeclaration") {
|
|
1163
|
+
constructors.push({
|
|
1164
|
+
normalizedSignature: `.ctor|(${member.parameters
|
|
1165
|
+
.map((parameter) => toSignatureType(parameter.type, typeParameterScope))
|
|
1166
|
+
.join(",")})|static=false`,
|
|
1167
|
+
isStatic: false,
|
|
1168
|
+
parameterCount: member.parameters.length,
|
|
1169
|
+
});
|
|
1170
|
+
continue;
|
|
1171
|
+
}
|
|
1172
|
+
if (member.kind === "methodDeclaration") {
|
|
1173
|
+
methods.push(makeMethodBinding({
|
|
1174
|
+
declaringClrType,
|
|
1175
|
+
declaringAssemblyName: assemblyName,
|
|
1176
|
+
methodName: member.name,
|
|
1177
|
+
parameters: member.parameters,
|
|
1178
|
+
returnType: member.returnType,
|
|
1179
|
+
arity: member.typeParameters?.length ?? 0,
|
|
1180
|
+
parameterModifiers: buildParameterModifiers(member.parameters),
|
|
1181
|
+
isStatic: member.isStatic,
|
|
1182
|
+
isAbstract: member.body === undefined,
|
|
1183
|
+
isVirtual: member.isVirtual,
|
|
1184
|
+
isOverride: member.isOverride,
|
|
1185
|
+
}));
|
|
1186
|
+
continue;
|
|
1187
|
+
}
|
|
1188
|
+
if (member.kind === "propertyDeclaration") {
|
|
1189
|
+
const hasAccessorBody = member.getterBody !== undefined || member.setterBody !== undefined;
|
|
1190
|
+
const hasGetter = hasAccessorBody
|
|
1191
|
+
? member.getterBody !== undefined
|
|
1192
|
+
: true;
|
|
1193
|
+
const hasSetter = hasAccessorBody
|
|
1194
|
+
? member.setterBody !== undefined
|
|
1195
|
+
: !member.isReadonly;
|
|
1196
|
+
const propertyType = toSignatureType(member.type, typeParameterScope);
|
|
1197
|
+
properties.push({
|
|
1198
|
+
stableId: `${typeStableId}::property:${member.name}`,
|
|
1199
|
+
clrName: member.name,
|
|
1200
|
+
normalizedSignature: `${member.name}|:${propertyType}|static=${member.isStatic ? "true" : "false"}|accessor=${hasGetter && hasSetter ? "getset" : hasSetter ? "set" : "get"}`,
|
|
1201
|
+
isStatic: member.isStatic,
|
|
1202
|
+
isAbstract: member.getterBody === undefined && member.setterBody === undefined
|
|
1203
|
+
? false
|
|
1204
|
+
: false,
|
|
1205
|
+
isVirtual: member.isVirtual ?? false,
|
|
1206
|
+
isOverride: member.isOverride ?? false,
|
|
1207
|
+
isIndexer: false,
|
|
1208
|
+
hasGetter,
|
|
1209
|
+
hasSetter,
|
|
1210
|
+
declaringClrType,
|
|
1211
|
+
declaringAssemblyName: assemblyName,
|
|
1212
|
+
});
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
return {
|
|
1216
|
+
stableId: typeStableId,
|
|
1217
|
+
clrName: declaringClrType,
|
|
1218
|
+
assemblyName,
|
|
1219
|
+
kind: declaration.isStruct ? "Struct" : "Class",
|
|
1220
|
+
accessibility: "Public",
|
|
1221
|
+
isAbstract: false,
|
|
1222
|
+
isSealed: false,
|
|
1223
|
+
isStatic: false,
|
|
1224
|
+
arity: declaration.typeParameters?.length ?? 0,
|
|
1225
|
+
typeParameters: declaration.typeParameters?.map((typeParameter) => typeParameter.name) ??
|
|
1226
|
+
[],
|
|
1227
|
+
methods,
|
|
1228
|
+
properties,
|
|
1229
|
+
fields: [],
|
|
1230
|
+
events: [],
|
|
1231
|
+
constructors: constructors.length > 0
|
|
1232
|
+
? constructors
|
|
1233
|
+
: [
|
|
1234
|
+
{
|
|
1235
|
+
normalizedSignature: ".ctor|()|static=false",
|
|
1236
|
+
isStatic: false,
|
|
1237
|
+
parameterCount: 0,
|
|
1238
|
+
},
|
|
1239
|
+
],
|
|
1240
|
+
};
|
|
1241
|
+
};
|
|
1242
|
+
const buildTypeBindingFromInterface = (declaration, namespace, assemblyName) => {
|
|
1243
|
+
const declaringClrType = toClrTypeName(namespace, declaration.name, declaration.typeParameters?.length ?? 0);
|
|
1244
|
+
const typeStableId = toStableId(assemblyName, declaringClrType);
|
|
1245
|
+
const typeParameterScope = declaration.typeParameters?.map((typeParameter) => typeParameter.name) ??
|
|
1246
|
+
[];
|
|
1247
|
+
const methods = [];
|
|
1248
|
+
const properties = [];
|
|
1249
|
+
for (const member of declaration.members) {
|
|
1250
|
+
if (member.kind === "methodSignature") {
|
|
1251
|
+
methods.push(makeMethodBinding({
|
|
1252
|
+
declaringClrType,
|
|
1253
|
+
declaringAssemblyName: assemblyName,
|
|
1254
|
+
methodName: member.name,
|
|
1255
|
+
parameters: member.parameters,
|
|
1256
|
+
returnType: member.returnType,
|
|
1257
|
+
arity: member.typeParameters?.length ?? 0,
|
|
1258
|
+
parameterModifiers: buildParameterModifiers(member.parameters),
|
|
1259
|
+
isStatic: false,
|
|
1260
|
+
isAbstract: true,
|
|
1261
|
+
}));
|
|
1262
|
+
continue;
|
|
1263
|
+
}
|
|
1264
|
+
properties.push({
|
|
1265
|
+
stableId: `${typeStableId}::property:${member.name}`,
|
|
1266
|
+
clrName: member.name,
|
|
1267
|
+
normalizedSignature: `${member.name}|:${toSignatureType(member.type, typeParameterScope)}|static=false|accessor=${member.isReadonly ? "get" : "getset"}`,
|
|
1268
|
+
isStatic: false,
|
|
1269
|
+
isAbstract: true,
|
|
1270
|
+
isVirtual: false,
|
|
1271
|
+
isOverride: false,
|
|
1272
|
+
isIndexer: false,
|
|
1273
|
+
hasGetter: true,
|
|
1274
|
+
hasSetter: !member.isReadonly,
|
|
1275
|
+
declaringClrType,
|
|
1276
|
+
declaringAssemblyName: assemblyName,
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
return {
|
|
1280
|
+
stableId: typeStableId,
|
|
1281
|
+
clrName: declaringClrType,
|
|
1282
|
+
assemblyName,
|
|
1283
|
+
kind: declaration.isStruct ? "Struct" : "Interface",
|
|
1284
|
+
accessibility: "Public",
|
|
1285
|
+
isAbstract: false,
|
|
1286
|
+
isSealed: false,
|
|
1287
|
+
isStatic: false,
|
|
1288
|
+
arity: declaration.typeParameters?.length ?? 0,
|
|
1289
|
+
typeParameters: declaration.typeParameters?.map((typeParameter) => typeParameter.name) ??
|
|
1290
|
+
[],
|
|
1291
|
+
methods,
|
|
1292
|
+
properties,
|
|
1293
|
+
fields: [],
|
|
1294
|
+
events: [],
|
|
1295
|
+
constructors: [],
|
|
1296
|
+
};
|
|
1297
|
+
};
|
|
1298
|
+
const buildTypeBindingFromEnum = (declaration, namespace, assemblyName) => {
|
|
1299
|
+
const declaringClrType = toClrTypeName(namespace, declaration.name);
|
|
1300
|
+
const typeStableId = toStableId(assemblyName, declaringClrType);
|
|
1301
|
+
const fields = declaration.members.map((member) => ({
|
|
1302
|
+
stableId: `${typeStableId}::field:${member.name}`,
|
|
1303
|
+
clrName: member.name,
|
|
1304
|
+
normalizedSignature: `${member.name}|${declaringClrType}|static=true|const=true`,
|
|
1305
|
+
isStatic: true,
|
|
1306
|
+
isReadOnly: true,
|
|
1307
|
+
isLiteral: true,
|
|
1308
|
+
declaringClrType,
|
|
1309
|
+
declaringAssemblyName: assemblyName,
|
|
1310
|
+
}));
|
|
1311
|
+
return {
|
|
1312
|
+
stableId: typeStableId,
|
|
1313
|
+
clrName: declaringClrType,
|
|
1314
|
+
assemblyName,
|
|
1315
|
+
kind: "Enum",
|
|
1316
|
+
accessibility: "Public",
|
|
1317
|
+
isAbstract: false,
|
|
1318
|
+
isSealed: true,
|
|
1319
|
+
isStatic: false,
|
|
1320
|
+
arity: 0,
|
|
1321
|
+
typeParameters: [],
|
|
1322
|
+
methods: [],
|
|
1323
|
+
properties: [],
|
|
1324
|
+
fields,
|
|
1325
|
+
events: [],
|
|
1326
|
+
constructors: [],
|
|
1327
|
+
};
|
|
1328
|
+
};
|
|
1329
|
+
const buildTypeBindingFromStructuralAlias = (declaration, namespace, assemblyName) => {
|
|
1330
|
+
if (declaration.type.kind !== "objectType")
|
|
1331
|
+
return undefined;
|
|
1332
|
+
const arity = declaration.typeParameters?.length ?? 0;
|
|
1333
|
+
const internalAliasName = `${declaration.name}__Alias`;
|
|
1334
|
+
const declaringClrType = toClrTypeName(namespace, internalAliasName, arity);
|
|
1335
|
+
const typeStableId = toStableId(assemblyName, declaringClrType);
|
|
1336
|
+
const typeParameterScope = declaration.typeParameters?.map((typeParameter) => typeParameter.name) ??
|
|
1337
|
+
[];
|
|
1338
|
+
const methods = [];
|
|
1339
|
+
const properties = [];
|
|
1340
|
+
for (const member of declaration.type.members) {
|
|
1341
|
+
if (member.kind === "methodSignature") {
|
|
1342
|
+
methods.push(makeMethodBinding({
|
|
1343
|
+
declaringClrType,
|
|
1344
|
+
declaringAssemblyName: assemblyName,
|
|
1345
|
+
methodName: member.name,
|
|
1346
|
+
parameters: member.parameters,
|
|
1347
|
+
returnType: member.returnType,
|
|
1348
|
+
arity: member.typeParameters?.length ?? 0,
|
|
1349
|
+
parameterModifiers: buildParameterModifiers(member.parameters),
|
|
1350
|
+
isStatic: false,
|
|
1351
|
+
isAbstract: true,
|
|
1352
|
+
}));
|
|
1353
|
+
continue;
|
|
1354
|
+
}
|
|
1355
|
+
properties.push({
|
|
1356
|
+
stableId: `${typeStableId}::property:${member.name}`,
|
|
1357
|
+
clrName: member.name,
|
|
1358
|
+
normalizedSignature: `${member.name}|:${toSignatureType(member.type, typeParameterScope)}|static=false|accessor=${member.isReadonly ? "get" : "getset"}`,
|
|
1359
|
+
isStatic: false,
|
|
1360
|
+
isAbstract: true,
|
|
1361
|
+
isVirtual: false,
|
|
1362
|
+
isOverride: false,
|
|
1363
|
+
isIndexer: false,
|
|
1364
|
+
hasGetter: true,
|
|
1365
|
+
hasSetter: !member.isReadonly,
|
|
1366
|
+
declaringClrType,
|
|
1367
|
+
declaringAssemblyName: assemblyName,
|
|
1368
|
+
});
|
|
1369
|
+
}
|
|
1370
|
+
return {
|
|
1371
|
+
stableId: typeStableId,
|
|
1372
|
+
clrName: declaringClrType,
|
|
1373
|
+
assemblyName,
|
|
1374
|
+
kind: declaration.isStruct ? "Struct" : "Class",
|
|
1375
|
+
accessibility: "Public",
|
|
1376
|
+
isAbstract: false,
|
|
1377
|
+
isSealed: false,
|
|
1378
|
+
isStatic: false,
|
|
1379
|
+
arity,
|
|
1380
|
+
typeParameters: declaration.typeParameters?.map((typeParameter) => typeParameter.name) ??
|
|
1381
|
+
[],
|
|
1382
|
+
methods,
|
|
1383
|
+
properties,
|
|
1384
|
+
fields: [],
|
|
1385
|
+
events: [],
|
|
1386
|
+
constructors: [],
|
|
1387
|
+
};
|
|
1388
|
+
};
|
|
1389
|
+
const buildTypeBindingFromContainer = (entry, namespace, assemblyName) => {
|
|
1390
|
+
const declaringClrType = toClrTypeName(namespace, entry.module.className);
|
|
1391
|
+
const typeStableId = toStableId(assemblyName, declaringClrType);
|
|
1392
|
+
const methods = entry.methods.map((method) => makeMethodBinding({
|
|
1393
|
+
declaringClrType,
|
|
1394
|
+
declaringAssemblyName: assemblyName,
|
|
1395
|
+
methodName: method.localName,
|
|
1396
|
+
parameters: method.declaration.parameters,
|
|
1397
|
+
returnType: method.declaration.returnType,
|
|
1398
|
+
arity: method.declaration.typeParameters?.length ?? 0,
|
|
1399
|
+
parameterModifiers: buildParameterModifiers(method.declaration.parameters),
|
|
1400
|
+
isStatic: true,
|
|
1401
|
+
}));
|
|
1402
|
+
const properties = entry.variables.map((variable) => ({
|
|
1403
|
+
stableId: `${typeStableId}::property:${variable.localName}`,
|
|
1404
|
+
clrName: variable.localName,
|
|
1405
|
+
normalizedSignature: `${variable.localName}|:${toSignatureType(variable.declarator?.type, [])}|static=true|accessor=getset`,
|
|
1406
|
+
isStatic: true,
|
|
1407
|
+
isAbstract: false,
|
|
1408
|
+
isVirtual: false,
|
|
1409
|
+
isOverride: false,
|
|
1410
|
+
isIndexer: false,
|
|
1411
|
+
hasGetter: true,
|
|
1412
|
+
hasSetter: true,
|
|
1413
|
+
declaringClrType,
|
|
1414
|
+
declaringAssemblyName: assemblyName,
|
|
1415
|
+
}));
|
|
1416
|
+
return {
|
|
1417
|
+
stableId: typeStableId,
|
|
1418
|
+
clrName: declaringClrType,
|
|
1419
|
+
assemblyName,
|
|
1420
|
+
kind: "Class",
|
|
1421
|
+
accessibility: "Public",
|
|
1422
|
+
isAbstract: true,
|
|
1423
|
+
isSealed: false,
|
|
1424
|
+
isStatic: true,
|
|
1425
|
+
arity: 0,
|
|
1426
|
+
typeParameters: [],
|
|
1427
|
+
methods,
|
|
1428
|
+
properties,
|
|
1429
|
+
fields: [],
|
|
1430
|
+
events: [],
|
|
1431
|
+
constructors: [],
|
|
1432
|
+
};
|
|
1433
|
+
};
|
|
1434
|
+
const collectNamespacePlans = (modules, assemblyName, rootNamespace, sourceIndexByFileKey) => {
|
|
1435
|
+
const modulesByNamespace = new Map();
|
|
1436
|
+
modulesByNamespace.set(rootNamespace, []);
|
|
1437
|
+
const modulesByFileKey = new Map();
|
|
1438
|
+
for (const module of modules) {
|
|
1439
|
+
const syntheticAnonymousModule = module.filePath.startsWith("__tsonic/") &&
|
|
1440
|
+
module.body.some((statement) => statement.kind === "classDeclaration" &&
|
|
1441
|
+
statement.name.startsWith("__Anon_"));
|
|
1442
|
+
if (module.filePath.startsWith("__tsonic/") && !syntheticAnonymousModule) {
|
|
1443
|
+
continue;
|
|
1444
|
+
}
|
|
1445
|
+
const list = modulesByNamespace.get(module.namespace) ?? [];
|
|
1446
|
+
list.push(module);
|
|
1447
|
+
modulesByNamespace.set(module.namespace, list);
|
|
1448
|
+
modulesByFileKey.set(normalizeModuleFileKey(module.filePath), module);
|
|
1449
|
+
}
|
|
1450
|
+
const plans = [];
|
|
1451
|
+
for (const [namespace, moduleList] of Array.from(modulesByNamespace.entries())) {
|
|
1452
|
+
const typeDeclarations = [];
|
|
1453
|
+
const moduleContainers = [];
|
|
1454
|
+
const valueExportsMap = new Map();
|
|
1455
|
+
const seenTypeDeclarationKeys = new Set();
|
|
1456
|
+
const sourceAliasLines = new Set();
|
|
1457
|
+
const sourceAliasInternalImports = new Set();
|
|
1458
|
+
const memberOverrides = [];
|
|
1459
|
+
const wrapperImportByAlias = new Map();
|
|
1460
|
+
const registerValueExport = (valueExport) => {
|
|
1461
|
+
const existing = valueExportsMap.get(valueExport.exportName);
|
|
1462
|
+
if (!existing) {
|
|
1463
|
+
valueExportsMap.set(valueExport.exportName, valueExport);
|
|
1464
|
+
return { ok: true, value: undefined };
|
|
1465
|
+
}
|
|
1466
|
+
const sameBinding = existing.binding.kind === valueExport.binding.kind &&
|
|
1467
|
+
existing.binding.clrName === valueExport.binding.clrName &&
|
|
1468
|
+
existing.binding.declaringClrType ===
|
|
1469
|
+
valueExport.binding.declaringClrType &&
|
|
1470
|
+
existing.binding.declaringAssemblyName ===
|
|
1471
|
+
valueExport.binding.declaringAssemblyName;
|
|
1472
|
+
const normalizeFunctionFacade = (facade) => {
|
|
1473
|
+
const declaration = facade.declaration;
|
|
1474
|
+
const typeParametersText = printTypeParameters(declaration.typeParameters);
|
|
1475
|
+
const typeParameterNames = declaration.typeParameters?.map((typeParameter) => typeParameter.name) ?? [];
|
|
1476
|
+
const parametersText = renderUnknownParameters(declaration.parameters, typeParameterNames);
|
|
1477
|
+
const returnTypeText = renderPortableType(declaration.returnType, typeParameterNames);
|
|
1478
|
+
const sourceSignatures = (facade.sourceSignatures ?? [])
|
|
1479
|
+
.map((signature) => `${signature.typeParametersText}(${signature.parametersText}):${signature.returnTypeText}`)
|
|
1480
|
+
.sort((left, right) => left.localeCompare(right))
|
|
1481
|
+
.join("||");
|
|
1482
|
+
return `${typeParametersText}(${parametersText}):${returnTypeText}|source=${sourceSignatures}`;
|
|
1483
|
+
};
|
|
1484
|
+
const normalizeVariableFacade = (declarator) => renderPortableType(declarator?.type);
|
|
1485
|
+
const sameFacade = (() => {
|
|
1486
|
+
if (existing.facade.kind !== valueExport.facade.kind)
|
|
1487
|
+
return false;
|
|
1488
|
+
if (existing.facade.kind === "function" &&
|
|
1489
|
+
valueExport.facade.kind === "function") {
|
|
1490
|
+
return (normalizeFunctionFacade(existing.facade) ===
|
|
1491
|
+
normalizeFunctionFacade(valueExport.facade));
|
|
1492
|
+
}
|
|
1493
|
+
if (existing.facade.kind === "variable" &&
|
|
1494
|
+
valueExport.facade.kind === "variable") {
|
|
1495
|
+
return (normalizeVariableFacade(existing.facade.declarator) ===
|
|
1496
|
+
normalizeVariableFacade(valueExport.facade.declarator));
|
|
1497
|
+
}
|
|
1498
|
+
return false;
|
|
1499
|
+
})();
|
|
1500
|
+
if (sameBinding && sameFacade) {
|
|
1501
|
+
return { ok: true, value: undefined };
|
|
1502
|
+
}
|
|
1503
|
+
return {
|
|
1504
|
+
ok: false,
|
|
1505
|
+
error: `Conflicting value export '${valueExport.exportName}' in namespace ${namespace}.\n` +
|
|
1506
|
+
"First-party bindings generation requires each exported value name to map deterministically to exactly one CLR member.",
|
|
1507
|
+
};
|
|
1508
|
+
};
|
|
1509
|
+
const registerWrapperImports = (wrappers, moduleFilePath) => {
|
|
1510
|
+
for (const wrapper of wrappers) {
|
|
1511
|
+
const existing = wrapperImportByAlias.get(wrapper.aliasName);
|
|
1512
|
+
if (existing) {
|
|
1513
|
+
if (existing.source !== wrapper.source ||
|
|
1514
|
+
existing.importedName !== wrapper.importedName) {
|
|
1515
|
+
return {
|
|
1516
|
+
ok: false,
|
|
1517
|
+
error: `Conflicting wrapper import alias '${wrapper.aliasName}' while generating ${moduleFilePath}.\n` +
|
|
1518
|
+
`- ${existing.importedName} from '${existing.source}'\n` +
|
|
1519
|
+
`- ${wrapper.importedName} from '${wrapper.source}'\n` +
|
|
1520
|
+
"Disambiguate ExtensionMethods aliases in source code.",
|
|
1521
|
+
};
|
|
1522
|
+
}
|
|
1523
|
+
continue;
|
|
1524
|
+
}
|
|
1525
|
+
wrapperImportByAlias.set(wrapper.aliasName, wrapper);
|
|
1526
|
+
}
|
|
1527
|
+
return { ok: true, value: undefined };
|
|
1528
|
+
};
|
|
1529
|
+
for (const module of moduleList.sort((left, right) => left.filePath.localeCompare(right.filePath))) {
|
|
1530
|
+
const moduleKey = normalizeModuleFileKey(module.filePath);
|
|
1531
|
+
const sourceIndex = sourceIndexByFileKey.get(moduleKey);
|
|
1532
|
+
if (sourceIndex) {
|
|
1533
|
+
const exportedAliasDecls = module.body.filter((stmt) => stmt.kind === "typeAliasDeclaration" && stmt.isExported);
|
|
1534
|
+
for (const alias of exportedAliasDecls) {
|
|
1535
|
+
const sourceAlias = sourceIndex.typeAliasesByName.get(alias.name);
|
|
1536
|
+
const sourceTypeParams = sourceAlias?.typeParametersText ??
|
|
1537
|
+
printTypeParameters(alias.typeParameters);
|
|
1538
|
+
if (alias.type.kind === "objectType") {
|
|
1539
|
+
const arity = alias.typeParameters?.length ?? 0;
|
|
1540
|
+
const internalName = `${alias.name}__Alias${arity > 0 ? `_${arity}` : ""}`;
|
|
1541
|
+
const typeArgs = sourceAlias && sourceAlias.typeParameterNames.length > 0
|
|
1542
|
+
? `<${sourceAlias.typeParameterNames.join(", ")}>`
|
|
1543
|
+
: alias.typeParameters && alias.typeParameters.length > 0
|
|
1544
|
+
? `<${alias.typeParameters.map((tp) => tp.name).join(", ")}>`
|
|
1545
|
+
: "";
|
|
1546
|
+
sourceAliasLines.add(`export type ${alias.name}${sourceTypeParams} = ${internalName}${typeArgs};`);
|
|
1547
|
+
sourceAliasInternalImports.add(internalName);
|
|
1548
|
+
continue;
|
|
1549
|
+
}
|
|
1550
|
+
const rhs = renderPortableType(alias.type, alias.typeParameters?.map((tp) => tp.name) ?? []);
|
|
1551
|
+
const shouldPreferSourceAliasText = sourceAlias !== undefined &&
|
|
1552
|
+
!typeNodeUsesImportedTypeNames(sourceAlias.type, sourceIndex.typeImportsByLocalName) &&
|
|
1553
|
+
/__\d+\b|\$instance\b/.test(rhs);
|
|
1554
|
+
sourceAliasLines.add(`export type ${alias.name}${sourceTypeParams} = ${shouldPreferSourceAliasText ? sourceAlias.typeText : rhs};`);
|
|
1555
|
+
}
|
|
1556
|
+
const exportedClasses = module.body.filter((stmt) => stmt.kind === "classDeclaration" && stmt.isExported);
|
|
1557
|
+
const exportedInterfaces = module.body.filter((stmt) => stmt.kind === "interfaceDeclaration" && stmt.isExported);
|
|
1558
|
+
const exportedAliases = module.body.filter((stmt) => stmt.kind === "typeAliasDeclaration" && stmt.isExported);
|
|
1559
|
+
for (const cls of exportedClasses) {
|
|
1560
|
+
const sourceMembers = sourceIndex.memberTypesByClassAndMember.get(cls.name);
|
|
1561
|
+
if (!sourceMembers)
|
|
1562
|
+
continue;
|
|
1563
|
+
for (const member of cls.members) {
|
|
1564
|
+
if (member.kind !== "propertyDeclaration")
|
|
1565
|
+
continue;
|
|
1566
|
+
if (member.isStatic || member.accessibility === "private")
|
|
1567
|
+
continue;
|
|
1568
|
+
const sourceMember = sourceMembers.get(member.name);
|
|
1569
|
+
if (!sourceMember)
|
|
1570
|
+
continue;
|
|
1571
|
+
const wrappersResult = collectExtensionWrapperImportsFromSourceType({
|
|
1572
|
+
startModuleKey: moduleKey,
|
|
1573
|
+
typeNode: sourceMember.typeNode,
|
|
1574
|
+
sourceIndexByFileKey,
|
|
1575
|
+
modulesByFileKey,
|
|
1576
|
+
});
|
|
1577
|
+
if (!wrappersResult.ok)
|
|
1578
|
+
return wrappersResult;
|
|
1579
|
+
const wrappers = wrappersResult.value;
|
|
1580
|
+
const canUseSourceTypeText = !typeNodeUsesImportedTypeNames(sourceMember.typeNode, sourceIndex.typeImportsByLocalName);
|
|
1581
|
+
if (!canUseSourceTypeText &&
|
|
1582
|
+
wrappers.length === 0 &&
|
|
1583
|
+
!sourceMember.isOptional) {
|
|
1584
|
+
continue;
|
|
1585
|
+
}
|
|
1586
|
+
const wrapperRegistered = registerWrapperImports(wrappers, module.filePath);
|
|
1587
|
+
if (!wrapperRegistered.ok)
|
|
1588
|
+
return wrapperRegistered;
|
|
1589
|
+
memberOverrides.push({
|
|
1590
|
+
className: cls.name,
|
|
1591
|
+
memberName: member.name,
|
|
1592
|
+
sourceTypeText: canUseSourceTypeText
|
|
1593
|
+
? sourceMember.typeText
|
|
1594
|
+
: undefined,
|
|
1595
|
+
replaceWithSourceType: canUseSourceTypeText,
|
|
1596
|
+
isOptional: sourceMember.isOptional,
|
|
1597
|
+
wrappers,
|
|
1598
|
+
});
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
for (const iface of exportedInterfaces) {
|
|
1602
|
+
const sourceMembers = sourceIndex.memberTypesByClassAndMember.get(iface.name);
|
|
1603
|
+
if (!sourceMembers)
|
|
1604
|
+
continue;
|
|
1605
|
+
for (const member of iface.members) {
|
|
1606
|
+
if (member.kind !== "propertySignature")
|
|
1607
|
+
continue;
|
|
1608
|
+
const sourceMember = sourceMembers.get(member.name);
|
|
1609
|
+
if (!sourceMember)
|
|
1610
|
+
continue;
|
|
1611
|
+
const wrappersResult = collectExtensionWrapperImportsFromSourceType({
|
|
1612
|
+
startModuleKey: moduleKey,
|
|
1613
|
+
typeNode: sourceMember.typeNode,
|
|
1614
|
+
sourceIndexByFileKey,
|
|
1615
|
+
modulesByFileKey,
|
|
1616
|
+
});
|
|
1617
|
+
if (!wrappersResult.ok)
|
|
1618
|
+
return wrappersResult;
|
|
1619
|
+
const wrappers = wrappersResult.value;
|
|
1620
|
+
const canUseSourceTypeText = !typeNodeUsesImportedTypeNames(sourceMember.typeNode, sourceIndex.typeImportsByLocalName);
|
|
1621
|
+
if (!canUseSourceTypeText && wrappers.length === 0)
|
|
1622
|
+
continue;
|
|
1623
|
+
const wrapperRegistered = registerWrapperImports(wrappers, module.filePath);
|
|
1624
|
+
if (!wrapperRegistered.ok)
|
|
1625
|
+
return wrapperRegistered;
|
|
1626
|
+
memberOverrides.push({
|
|
1627
|
+
className: iface.name,
|
|
1628
|
+
memberName: member.name,
|
|
1629
|
+
sourceTypeText: canUseSourceTypeText
|
|
1630
|
+
? sourceMember.typeText
|
|
1631
|
+
: undefined,
|
|
1632
|
+
replaceWithSourceType: canUseSourceTypeText,
|
|
1633
|
+
isOptional: sourceMember.isOptional,
|
|
1634
|
+
emitOptionalPropertySyntax: true,
|
|
1635
|
+
wrappers,
|
|
1636
|
+
});
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
for (const alias of exportedAliases) {
|
|
1640
|
+
const sourceAlias = sourceIndex.typeAliasesByName.get(alias.name);
|
|
1641
|
+
if (!sourceAlias)
|
|
1642
|
+
continue;
|
|
1643
|
+
const aliasType = unwrapParens(sourceAlias.type);
|
|
1644
|
+
if (!ts.isTypeLiteralNode(aliasType))
|
|
1645
|
+
continue;
|
|
1646
|
+
const arity = sourceAlias.typeParameterNames.length;
|
|
1647
|
+
const internalAliasName = `${alias.name}__Alias${arity > 0 ? `_${arity}` : ""}`;
|
|
1648
|
+
for (const member of aliasType.members) {
|
|
1649
|
+
if (!ts.isPropertySignature(member))
|
|
1650
|
+
continue;
|
|
1651
|
+
if (!member.name || !member.type)
|
|
1652
|
+
continue;
|
|
1653
|
+
const memberName = getPropertyNameText(member.name);
|
|
1654
|
+
if (!memberName)
|
|
1655
|
+
continue;
|
|
1656
|
+
const wrappersResult = collectExtensionWrapperImportsFromSourceType({
|
|
1657
|
+
startModuleKey: moduleKey,
|
|
1658
|
+
typeNode: member.type,
|
|
1659
|
+
sourceIndexByFileKey,
|
|
1660
|
+
modulesByFileKey,
|
|
1661
|
+
});
|
|
1662
|
+
if (!wrappersResult.ok)
|
|
1663
|
+
return wrappersResult;
|
|
1664
|
+
const wrappers = wrappersResult.value;
|
|
1665
|
+
const canUseSourceTypeText = !typeNodeUsesImportedTypeNames(member.type, sourceIndex.typeImportsByLocalName);
|
|
1666
|
+
if (!canUseSourceTypeText && wrappers.length === 0)
|
|
1667
|
+
continue;
|
|
1668
|
+
const wrapperRegistered = registerWrapperImports(wrappers, module.filePath);
|
|
1669
|
+
if (!wrapperRegistered.ok)
|
|
1670
|
+
return wrapperRegistered;
|
|
1671
|
+
memberOverrides.push({
|
|
1672
|
+
className: internalAliasName,
|
|
1673
|
+
memberName,
|
|
1674
|
+
sourceTypeText: canUseSourceTypeText
|
|
1675
|
+
? printTypeNodeText(member.type, member.getSourceFile())
|
|
1676
|
+
: undefined,
|
|
1677
|
+
replaceWithSourceType: canUseSourceTypeText,
|
|
1678
|
+
isOptional: member.questionToken !== undefined,
|
|
1679
|
+
wrappers,
|
|
1680
|
+
});
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
for (const exportItem of module.exports) {
|
|
1685
|
+
if (exportItem.kind !== "reexport")
|
|
1686
|
+
continue;
|
|
1687
|
+
const resolved = resolveExportedDeclaration(module, exportItem.name, modulesByFileKey);
|
|
1688
|
+
if (!resolved.ok)
|
|
1689
|
+
return resolved;
|
|
1690
|
+
const declaration = resolved.value.declaration;
|
|
1691
|
+
const declarationModule = resolved.value.module;
|
|
1692
|
+
const exportKind = classifyDeclarationKind(declaration, declarationModule.filePath, exportItem.name);
|
|
1693
|
+
if (!exportKind.ok)
|
|
1694
|
+
return exportKind;
|
|
1695
|
+
if (exportKind.value === "function") {
|
|
1696
|
+
const functionDeclaration = declaration.kind === "functionDeclaration"
|
|
1697
|
+
? declaration
|
|
1698
|
+
: undefined;
|
|
1699
|
+
if (!functionDeclaration) {
|
|
1700
|
+
return {
|
|
1701
|
+
ok: false,
|
|
1702
|
+
error: `Invalid function export '${exportItem.name}' in ${declarationModule.filePath}: expected function declaration.`,
|
|
1703
|
+
};
|
|
1704
|
+
}
|
|
1705
|
+
const registered = registerValueExport({
|
|
1706
|
+
exportName: exportItem.name,
|
|
1707
|
+
binding: {
|
|
1708
|
+
kind: "method",
|
|
1709
|
+
clrName: resolved.value.clrName,
|
|
1710
|
+
declaringClrType: toClrTypeName(declarationModule.namespace, declarationModule.className),
|
|
1711
|
+
declaringAssemblyName: assemblyName,
|
|
1712
|
+
},
|
|
1713
|
+
facade: {
|
|
1714
|
+
kind: "function",
|
|
1715
|
+
declaration: functionDeclaration,
|
|
1716
|
+
sourceSignatures: sourceIndexByFileKey
|
|
1717
|
+
.get(normalizeModuleFileKey(declarationModule.filePath))
|
|
1718
|
+
?.exportedFunctionSignaturesByName.get(resolved.value.clrName) ?? [],
|
|
1719
|
+
},
|
|
1720
|
+
});
|
|
1721
|
+
if (!registered.ok)
|
|
1722
|
+
return registered;
|
|
1723
|
+
continue;
|
|
1724
|
+
}
|
|
1725
|
+
if (exportKind.value === "variable") {
|
|
1726
|
+
const declarationStatement = declaration.kind === "variableDeclaration"
|
|
1727
|
+
? declaration
|
|
1728
|
+
: undefined;
|
|
1729
|
+
if (!declarationStatement) {
|
|
1730
|
+
return {
|
|
1731
|
+
ok: false,
|
|
1732
|
+
error: `Invalid variable export '${exportItem.name}' in ${declarationModule.filePath}: expected variable declaration.`,
|
|
1733
|
+
};
|
|
1734
|
+
}
|
|
1735
|
+
const declarator = declarationStatement.declarations.find((candidate) => candidate.name.kind === "identifierPattern" &&
|
|
1736
|
+
candidate.name.name === resolved.value.clrName);
|
|
1737
|
+
const registered = registerValueExport({
|
|
1738
|
+
exportName: exportItem.name,
|
|
1739
|
+
binding: {
|
|
1740
|
+
kind: "field",
|
|
1741
|
+
clrName: resolved.value.clrName,
|
|
1742
|
+
declaringClrType: toClrTypeName(declarationModule.namespace, declarationModule.className),
|
|
1743
|
+
declaringAssemblyName: assemblyName,
|
|
1744
|
+
},
|
|
1745
|
+
facade: {
|
|
1746
|
+
kind: "variable",
|
|
1747
|
+
declarator: declarator && declarator.name.kind === "identifierPattern"
|
|
1748
|
+
? {
|
|
1749
|
+
kind: declarator.kind,
|
|
1750
|
+
name: declarator.name,
|
|
1751
|
+
type: declarator.type,
|
|
1752
|
+
}
|
|
1753
|
+
: undefined,
|
|
1754
|
+
},
|
|
1755
|
+
});
|
|
1756
|
+
if (!registered.ok)
|
|
1757
|
+
return registered;
|
|
1758
|
+
continue;
|
|
1759
|
+
}
|
|
1760
|
+
if (exportKind.value === "class" ||
|
|
1761
|
+
exportKind.value === "interface" ||
|
|
1762
|
+
exportKind.value === "enum" ||
|
|
1763
|
+
exportKind.value === "typeAlias") {
|
|
1764
|
+
if (exportKind.value === "typeAlias" &&
|
|
1765
|
+
declaration.kind === "typeAliasDeclaration" &&
|
|
1766
|
+
declaration.type.kind !== "objectType") {
|
|
1767
|
+
continue;
|
|
1768
|
+
}
|
|
1769
|
+
const typeKey = `${declarationModule.namespace}|${declarationModule.className}|${resolved.value.clrName}|${exportKind.value}`;
|
|
1770
|
+
if (seenTypeDeclarationKeys.has(typeKey))
|
|
1771
|
+
continue;
|
|
1772
|
+
seenTypeDeclarationKeys.add(typeKey);
|
|
1773
|
+
typeDeclarations.push({
|
|
1774
|
+
exportName: exportItem.name,
|
|
1775
|
+
localName: resolved.value.clrName,
|
|
1776
|
+
kind: exportKind.value,
|
|
1777
|
+
declaration,
|
|
1778
|
+
declaringNamespace: declarationModule.namespace,
|
|
1779
|
+
declaringClassName: declarationModule.className,
|
|
1780
|
+
declaringFilePath: declarationModule.filePath,
|
|
1781
|
+
});
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
const moduleExports = collectModuleExports(module, modulesByFileKey);
|
|
1785
|
+
if (!moduleExports.ok)
|
|
1786
|
+
return moduleExports;
|
|
1787
|
+
const containerMethods = [];
|
|
1788
|
+
const containerVariables = [];
|
|
1789
|
+
if (module.filePath.startsWith("__tsonic/")) {
|
|
1790
|
+
for (const statement of module.body) {
|
|
1791
|
+
if (statement.kind !== "classDeclaration")
|
|
1792
|
+
continue;
|
|
1793
|
+
if (!statement.name.startsWith("__Anon_"))
|
|
1794
|
+
continue;
|
|
1795
|
+
const key = `${statement.name}|class`;
|
|
1796
|
+
if (seenTypeDeclarationKeys.has(key))
|
|
1797
|
+
continue;
|
|
1798
|
+
seenTypeDeclarationKeys.add(key);
|
|
1799
|
+
typeDeclarations.push({
|
|
1800
|
+
exportName: statement.name,
|
|
1801
|
+
localName: statement.name,
|
|
1802
|
+
kind: "class",
|
|
1803
|
+
declaration: statement,
|
|
1804
|
+
declaringNamespace: module.namespace,
|
|
1805
|
+
declaringClassName: module.className,
|
|
1806
|
+
declaringFilePath: module.filePath,
|
|
1807
|
+
});
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
for (const symbol of moduleExports.value) {
|
|
1811
|
+
if (symbol.kind === "class" ||
|
|
1812
|
+
symbol.kind === "interface" ||
|
|
1813
|
+
symbol.kind === "enum" ||
|
|
1814
|
+
symbol.kind === "typeAlias") {
|
|
1815
|
+
if (symbol.kind === "typeAlias" &&
|
|
1816
|
+
symbol.declaration.kind === "typeAliasDeclaration" &&
|
|
1817
|
+
symbol.declaration.type.kind !== "objectType") {
|
|
1818
|
+
continue;
|
|
1819
|
+
}
|
|
1820
|
+
const key = `${symbol.declaringNamespace}|${symbol.declaringClassName}|${symbol.localName}|${symbol.kind}`;
|
|
1821
|
+
if (!seenTypeDeclarationKeys.has(key)) {
|
|
1822
|
+
seenTypeDeclarationKeys.add(key);
|
|
1823
|
+
typeDeclarations.push(symbol);
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
if (symbol.kind === "function") {
|
|
1827
|
+
const functionDeclaration = symbol.declaration.kind === "functionDeclaration"
|
|
1828
|
+
? symbol.declaration
|
|
1829
|
+
: undefined;
|
|
1830
|
+
if (!functionDeclaration)
|
|
1831
|
+
continue;
|
|
1832
|
+
const isLocalContainerMember = symbol.declaringNamespace === module.namespace &&
|
|
1833
|
+
symbol.declaringClassName === module.className;
|
|
1834
|
+
if (isLocalContainerMember) {
|
|
1835
|
+
containerMethods.push({
|
|
1836
|
+
exportName: symbol.exportName,
|
|
1837
|
+
localName: symbol.localName,
|
|
1838
|
+
declaration: functionDeclaration,
|
|
1839
|
+
});
|
|
1840
|
+
}
|
|
1841
|
+
const registered = registerValueExport({
|
|
1842
|
+
exportName: symbol.exportName,
|
|
1843
|
+
binding: {
|
|
1844
|
+
kind: "method",
|
|
1845
|
+
clrName: symbol.localName,
|
|
1846
|
+
declaringClrType: toClrTypeName(symbol.declaringNamespace, symbol.declaringClassName),
|
|
1847
|
+
declaringAssemblyName: assemblyName,
|
|
1848
|
+
},
|
|
1849
|
+
facade: {
|
|
1850
|
+
kind: "function",
|
|
1851
|
+
declaration: functionDeclaration,
|
|
1852
|
+
sourceSignatures: sourceIndexByFileKey
|
|
1853
|
+
.get(normalizeModuleFileKey(symbol.declaringFilePath))
|
|
1854
|
+
?.exportedFunctionSignaturesByName.get(symbol.localName) ??
|
|
1855
|
+
[],
|
|
1856
|
+
},
|
|
1857
|
+
});
|
|
1858
|
+
if (!registered.ok)
|
|
1859
|
+
return registered;
|
|
1860
|
+
continue;
|
|
1861
|
+
}
|
|
1862
|
+
if (symbol.kind === "variable") {
|
|
1863
|
+
const declaration = symbol.declaration.kind === "variableDeclaration"
|
|
1864
|
+
? symbol.declaration
|
|
1865
|
+
: undefined;
|
|
1866
|
+
if (!declaration)
|
|
1867
|
+
continue;
|
|
1868
|
+
const declarator = declaration.declarations.find((candidate) => candidate.name.kind === "identifierPattern" &&
|
|
1869
|
+
candidate.name.name === symbol.localName);
|
|
1870
|
+
const isLocalContainerMember = symbol.declaringNamespace === module.namespace &&
|
|
1871
|
+
symbol.declaringClassName === module.className;
|
|
1872
|
+
if (isLocalContainerMember) {
|
|
1873
|
+
containerVariables.push({
|
|
1874
|
+
exportName: symbol.exportName,
|
|
1875
|
+
localName: symbol.localName,
|
|
1876
|
+
declaration,
|
|
1877
|
+
declarator: declarator && declarator.name.kind === "identifierPattern"
|
|
1878
|
+
? {
|
|
1879
|
+
kind: declarator.kind,
|
|
1880
|
+
name: declarator.name,
|
|
1881
|
+
type: declarator.type,
|
|
1882
|
+
}
|
|
1883
|
+
: undefined,
|
|
1884
|
+
});
|
|
1885
|
+
}
|
|
1886
|
+
const registered = registerValueExport({
|
|
1887
|
+
exportName: symbol.exportName,
|
|
1888
|
+
binding: {
|
|
1889
|
+
kind: "field",
|
|
1890
|
+
clrName: symbol.localName,
|
|
1891
|
+
declaringClrType: toClrTypeName(symbol.declaringNamespace, symbol.declaringClassName),
|
|
1892
|
+
declaringAssemblyName: assemblyName,
|
|
1893
|
+
},
|
|
1894
|
+
facade: {
|
|
1895
|
+
kind: "variable",
|
|
1896
|
+
declarator: declarator && declarator.name.kind === "identifierPattern"
|
|
1897
|
+
? {
|
|
1898
|
+
kind: declarator.kind,
|
|
1899
|
+
name: declarator.name,
|
|
1900
|
+
type: declarator.type,
|
|
1901
|
+
}
|
|
1902
|
+
: undefined,
|
|
1903
|
+
},
|
|
1904
|
+
});
|
|
1905
|
+
if (!registered.ok)
|
|
1906
|
+
return registered;
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
if (containerMethods.length > 0 || containerVariables.length > 0) {
|
|
1910
|
+
moduleContainers.push({
|
|
1911
|
+
module,
|
|
1912
|
+
methods: containerMethods,
|
|
1913
|
+
variables: containerVariables,
|
|
1914
|
+
});
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
plans.push({
|
|
1918
|
+
namespace,
|
|
1919
|
+
typeDeclarations: typeDeclarations.sort((left, right) => left.exportName.localeCompare(right.exportName)),
|
|
1920
|
+
moduleContainers: moduleContainers.sort((left, right) => left.module.className.localeCompare(right.module.className)),
|
|
1921
|
+
sourceAliasLines: Array.from(sourceAliasLines.values()).sort((left, right) => left.localeCompare(right)),
|
|
1922
|
+
sourceAliasInternalImports: Array.from(sourceAliasInternalImports.values()).sort((left, right) => left.localeCompare(right)),
|
|
1923
|
+
memberOverrides: memberOverrides.sort((left, right) => {
|
|
1924
|
+
const classCmp = left.className.localeCompare(right.className);
|
|
1925
|
+
if (classCmp !== 0)
|
|
1926
|
+
return classCmp;
|
|
1927
|
+
return left.memberName.localeCompare(right.memberName);
|
|
1928
|
+
}),
|
|
1929
|
+
wrapperImports: Array.from(wrapperImportByAlias.values()).sort((left, right) => left.aliasName.localeCompare(right.aliasName)),
|
|
1930
|
+
valueExports: Array.from(valueExportsMap.values()).sort((left, right) => left.exportName.localeCompare(right.exportName)),
|
|
1931
|
+
});
|
|
1932
|
+
}
|
|
1933
|
+
return {
|
|
1934
|
+
ok: true,
|
|
1935
|
+
value: plans.sort((left, right) => left.namespace.localeCompare(right.namespace)),
|
|
1936
|
+
};
|
|
1937
|
+
};
|
|
1938
|
+
const collectEntrypointReexports = (entryModule, modulesByFileKey) => {
|
|
1939
|
+
const grouped = new Map();
|
|
1940
|
+
for (const exported of entryModule.exports) {
|
|
1941
|
+
if (exported.kind !== "reexport")
|
|
1942
|
+
continue;
|
|
1943
|
+
const targetModule = resolveLocalModuleFile(exported.fromModule, entryModule.filePath, modulesByFileKey);
|
|
1944
|
+
if (!targetModule) {
|
|
1945
|
+
return {
|
|
1946
|
+
ok: false,
|
|
1947
|
+
error: `Failed to resolve re-export '${exported.name}' from '${exported.fromModule}' in ${entryModule.filePath}.`,
|
|
1948
|
+
};
|
|
1949
|
+
}
|
|
1950
|
+
if (targetModule.namespace === entryModule.namespace)
|
|
1951
|
+
continue;
|
|
1952
|
+
const kind = classifyExportKind(targetModule, exported.originalName);
|
|
1953
|
+
if (kind === "unknown") {
|
|
1954
|
+
return {
|
|
1955
|
+
ok: false,
|
|
1956
|
+
error: `Failed to classify re-export '${exported.name}' from '${exported.fromModule}' in ${entryModule.filePath}.`,
|
|
1957
|
+
};
|
|
1958
|
+
}
|
|
1959
|
+
const moduleSpecifier = `./${moduleNamespacePath(targetModule.namespace)}.js`;
|
|
1960
|
+
const key = `${moduleSpecifier}|${kind}`;
|
|
1961
|
+
const specifier = exported.name === exported.originalName
|
|
1962
|
+
? exported.name
|
|
1963
|
+
: `${exported.originalName} as ${exported.name}`;
|
|
1964
|
+
const list = grouped.get(key) ?? [];
|
|
1965
|
+
list.push(specifier);
|
|
1966
|
+
grouped.set(key, list);
|
|
1967
|
+
}
|
|
1968
|
+
const dtsStatements = [];
|
|
1969
|
+
const jsValueStatements = [];
|
|
1970
|
+
for (const [key, specs] of Array.from(grouped.entries()).sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
1971
|
+
const [moduleSpecifier, kind] = key.split("|");
|
|
1972
|
+
const unique = Array.from(new Set(specs)).sort((a, b) => a.localeCompare(b));
|
|
1973
|
+
if (kind === "type") {
|
|
1974
|
+
dtsStatements.push(`export type { ${unique.join(", ")} } from '${moduleSpecifier}';`);
|
|
1975
|
+
continue;
|
|
1976
|
+
}
|
|
1977
|
+
const statement = `export { ${unique.join(", ")} } from '${moduleSpecifier}';`;
|
|
1978
|
+
dtsStatements.push(statement);
|
|
1979
|
+
jsValueStatements.push(statement);
|
|
1980
|
+
}
|
|
1981
|
+
return {
|
|
1982
|
+
ok: true,
|
|
1983
|
+
value: { dtsStatements, jsValueStatements },
|
|
1984
|
+
};
|
|
1985
|
+
};
|
|
1986
|
+
const writeNamespaceArtifacts = (config, outDir, plan, entrypointReexports) => {
|
|
1987
|
+
const namespacePath = moduleNamespacePath(plan.namespace);
|
|
1988
|
+
const namespaceDir = join(outDir, namespacePath);
|
|
1989
|
+
const internalDir = join(namespaceDir, "internal");
|
|
1990
|
+
mkdirSync(internalDir, { recursive: true });
|
|
1991
|
+
const internalIndexPath = join(internalDir, "index.d.ts");
|
|
1992
|
+
const facadeDtsPath = join(outDir, `${namespacePath}.d.ts`);
|
|
1993
|
+
const facadeJsPath = join(outDir, `${namespacePath}.js`);
|
|
1994
|
+
const bindingsPath = join(namespaceDir, "bindings.json");
|
|
1995
|
+
const internalLines = [];
|
|
1996
|
+
const memberOverridesByClass = new Map();
|
|
1997
|
+
for (const override of plan.memberOverrides) {
|
|
1998
|
+
const byMember = memberOverridesByClass.get(override.className) ??
|
|
1999
|
+
new Map();
|
|
2000
|
+
byMember.set(override.memberName, override);
|
|
2001
|
+
memberOverridesByClass.set(override.className, byMember);
|
|
2002
|
+
}
|
|
2003
|
+
internalLines.push("// Generated by Tsonic - Source bindings");
|
|
2004
|
+
internalLines.push(`// Namespace: ${plan.namespace}`);
|
|
2005
|
+
internalLines.push(`// Assembly: ${config.outputName}`);
|
|
2006
|
+
internalLines.push("");
|
|
2007
|
+
internalLines.push(primitiveImportLine);
|
|
2008
|
+
if (plan.wrapperImports.length > 0) {
|
|
2009
|
+
internalLines.push("");
|
|
2010
|
+
internalLines.push("// Tsonic source member type imports (generated)");
|
|
2011
|
+
for (const wrapperImport of plan.wrapperImports) {
|
|
2012
|
+
if (wrapperImport.importedName === wrapperImport.aliasName) {
|
|
2013
|
+
internalLines.push(`import type { ${wrapperImport.importedName} } from '${wrapperImport.source}';`);
|
|
2014
|
+
continue;
|
|
2015
|
+
}
|
|
2016
|
+
internalLines.push(`import type { ${wrapperImport.importedName} as ${wrapperImport.aliasName} } from '${wrapperImport.source}';`);
|
|
2017
|
+
}
|
|
2018
|
+
internalLines.push("// End Tsonic source member type imports");
|
|
2019
|
+
}
|
|
2020
|
+
internalLines.push("");
|
|
2021
|
+
const typeBindings = [];
|
|
2022
|
+
for (const symbol of plan.typeDeclarations) {
|
|
2023
|
+
if (symbol.kind === "class" &&
|
|
2024
|
+
symbol.declaration.kind === "classDeclaration") {
|
|
2025
|
+
internalLines.push(...renderClassInternal(symbol.declaration, plan.namespace, memberOverridesByClass.get(symbol.declaration.name) ?? new Map()));
|
|
2026
|
+
typeBindings.push(buildTypeBindingFromClass(symbol.declaration, plan.namespace, config.outputName));
|
|
2027
|
+
continue;
|
|
2028
|
+
}
|
|
2029
|
+
if (symbol.kind === "interface" &&
|
|
2030
|
+
symbol.declaration.kind === "interfaceDeclaration") {
|
|
2031
|
+
internalLines.push(...renderInterfaceInternal(symbol.declaration, plan.namespace, memberOverridesByClass.get(symbol.declaration.name) ?? new Map()));
|
|
2032
|
+
typeBindings.push(buildTypeBindingFromInterface(symbol.declaration, plan.namespace, config.outputName));
|
|
2033
|
+
continue;
|
|
2034
|
+
}
|
|
2035
|
+
if (symbol.kind === "enum" &&
|
|
2036
|
+
symbol.declaration.kind === "enumDeclaration") {
|
|
2037
|
+
internalLines.push(...renderEnumInternal(symbol.declaration));
|
|
2038
|
+
typeBindings.push(buildTypeBindingFromEnum(symbol.declaration, plan.namespace, config.outputName));
|
|
2039
|
+
continue;
|
|
2040
|
+
}
|
|
2041
|
+
if (symbol.kind === "typeAlias" &&
|
|
2042
|
+
symbol.declaration.kind === "typeAliasDeclaration") {
|
|
2043
|
+
internalLines.push(...renderStructuralAliasInternal(symbol.declaration, plan.namespace, memberOverridesByClass.get(`${symbol.declaration.name}__Alias${(symbol.declaration.typeParameters?.length ?? 0) > 0
|
|
2044
|
+
? `_${symbol.declaration.typeParameters?.length ?? 0}`
|
|
2045
|
+
: ""}`) ?? new Map()));
|
|
2046
|
+
const binding = buildTypeBindingFromStructuralAlias(symbol.declaration, plan.namespace, config.outputName);
|
|
2047
|
+
if (binding)
|
|
2048
|
+
typeBindings.push(binding);
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
for (const container of plan.moduleContainers) {
|
|
2052
|
+
internalLines.push(...renderContainerInternal(container));
|
|
2053
|
+
typeBindings.push(buildTypeBindingFromContainer(container, plan.namespace, config.outputName));
|
|
2054
|
+
}
|
|
2055
|
+
writeFileSync(internalIndexPath, internalLines.join("\n").trimEnd() + "\n", "utf-8");
|
|
2056
|
+
const internalSpecifier = moduleNamespaceToInternalSpecifier(plan.namespace);
|
|
2057
|
+
const facadeLines = [];
|
|
2058
|
+
facadeLines.push(`// Namespace: ${plan.namespace}`);
|
|
2059
|
+
facadeLines.push("// Generated by Tsonic - Source bindings");
|
|
2060
|
+
facadeLines.push("");
|
|
2061
|
+
facadeLines.push(`import * as Internal from '${internalSpecifier}';`);
|
|
2062
|
+
facadeLines.push("");
|
|
2063
|
+
for (const symbol of plan.typeDeclarations) {
|
|
2064
|
+
if (symbol.kind === "typeAlias") {
|
|
2065
|
+
continue;
|
|
2066
|
+
}
|
|
2067
|
+
const isValueType = symbol.kind === "class" || symbol.kind === "enum";
|
|
2068
|
+
const isSyntheticAnonymousClass = symbol.kind === "class" && symbol.localName.startsWith("__Anon_");
|
|
2069
|
+
if (isValueType) {
|
|
2070
|
+
const specifier = symbol.exportName === symbol.localName
|
|
2071
|
+
? symbol.exportName
|
|
2072
|
+
: `${symbol.localName} as ${symbol.exportName}`;
|
|
2073
|
+
if (!isSyntheticAnonymousClass) {
|
|
2074
|
+
facadeLines.push(`export { ${specifier} } from '${internalSpecifier}';`);
|
|
2075
|
+
}
|
|
2076
|
+
facadeLines.push(`export type { ${specifier} } from '${internalSpecifier}';`);
|
|
2077
|
+
if (symbol.kind === "class") {
|
|
2078
|
+
facadeLines.push(`export type { ${symbol.localName}$instance } from '${internalSpecifier}';`);
|
|
2079
|
+
}
|
|
2080
|
+
continue;
|
|
2081
|
+
}
|
|
2082
|
+
const specifier = symbol.exportName === symbol.localName
|
|
2083
|
+
? symbol.exportName
|
|
2084
|
+
: `${symbol.localName} as ${symbol.exportName}`;
|
|
2085
|
+
facadeLines.push(`export type { ${specifier} } from '${internalSpecifier}';`);
|
|
2086
|
+
if (symbol.kind === "interface") {
|
|
2087
|
+
facadeLines.push(`export type { ${symbol.localName}$instance } from '${internalSpecifier}';`);
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
for (const container of plan.moduleContainers) {
|
|
2091
|
+
facadeLines.push(`export { ${container.module.className}$instance as ${container.module.className} } from '${internalSpecifier}';`);
|
|
2092
|
+
}
|
|
2093
|
+
const valueBindings = new Map();
|
|
2094
|
+
const localTypeImports = new Set();
|
|
2095
|
+
for (const symbol of plan.typeDeclarations) {
|
|
2096
|
+
if (symbol.kind === "typeAlias")
|
|
2097
|
+
continue;
|
|
2098
|
+
localTypeImports.add(symbol.localName);
|
|
2099
|
+
if (symbol.kind === "class" || symbol.kind === "interface") {
|
|
2100
|
+
localTypeImports.add(`${symbol.localName}$instance`);
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
for (const internalImport of plan.sourceAliasInternalImports) {
|
|
2104
|
+
localTypeImports.add(internalImport);
|
|
2105
|
+
}
|
|
2106
|
+
if (localTypeImports.size > 0) {
|
|
2107
|
+
facadeLines.push("");
|
|
2108
|
+
facadeLines.push("// Tsonic source alias imports (generated)");
|
|
2109
|
+
facadeLines.push(`import type { ${Array.from(localTypeImports.values())
|
|
2110
|
+
.sort((left, right) => left.localeCompare(right))
|
|
2111
|
+
.join(", ")} } from '${internalSpecifier}';`);
|
|
2112
|
+
facadeLines.push("// End Tsonic source alias imports");
|
|
2113
|
+
}
|
|
2114
|
+
if (plan.sourceAliasLines.length > 0) {
|
|
2115
|
+
facadeLines.push("");
|
|
2116
|
+
facadeLines.push("// Tsonic source type aliases (generated)");
|
|
2117
|
+
facadeLines.push(...plan.sourceAliasLines);
|
|
2118
|
+
facadeLines.push("// End Tsonic source type aliases");
|
|
2119
|
+
}
|
|
2120
|
+
if (entrypointReexports && entrypointReexports.dtsStatements.length > 0) {
|
|
2121
|
+
facadeLines.push("");
|
|
2122
|
+
facadeLines.push("// Tsonic entrypoint re-exports (generated)");
|
|
2123
|
+
facadeLines.push(...entrypointReexports.dtsStatements);
|
|
2124
|
+
facadeLines.push("// End Tsonic entrypoint re-exports");
|
|
2125
|
+
}
|
|
2126
|
+
for (const valueExport of plan.valueExports) {
|
|
2127
|
+
valueBindings.set(valueExport.exportName, valueExport.binding);
|
|
2128
|
+
if (valueExport.facade.kind === "function") {
|
|
2129
|
+
const typeParametersText = printTypeParameters(valueExport.facade.declaration.typeParameters);
|
|
2130
|
+
const typeParameterNames = valueExport.facade.declaration.typeParameters?.map((typeParameter) => typeParameter.name) ?? [];
|
|
2131
|
+
const parametersText = renderUnknownParameters(valueExport.facade.declaration.parameters, typeParameterNames);
|
|
2132
|
+
const returnTypeText = renderPortableType(valueExport.facade.declaration.returnType, typeParameterNames);
|
|
2133
|
+
facadeLines.push(`export declare function ${valueExport.exportName}${typeParametersText}(${parametersText}): ${returnTypeText};`);
|
|
2134
|
+
continue;
|
|
2135
|
+
}
|
|
2136
|
+
facadeLines.push(`export declare const ${valueExport.exportName}: ${renderPortableType(valueExport.facade.declarator?.type)};`);
|
|
2137
|
+
}
|
|
2138
|
+
if (plan.typeDeclarations.length === 0 &&
|
|
2139
|
+
plan.moduleContainers.length === 0 &&
|
|
2140
|
+
plan.valueExports.length === 0 &&
|
|
2141
|
+
plan.sourceAliasLines.length === 0 &&
|
|
2142
|
+
(!entrypointReexports || entrypointReexports.dtsStatements.length === 0)) {
|
|
2143
|
+
facadeLines.push("export {};");
|
|
2144
|
+
}
|
|
2145
|
+
writeFileSync(facadeDtsPath, facadeLines.join("\n").trimEnd() + "\n", "utf-8");
|
|
2146
|
+
writeFileSync(facadeJsPath, [
|
|
2147
|
+
`// Namespace: ${plan.namespace}`,
|
|
2148
|
+
"// Generated by Tsonic - Source bindings",
|
|
2149
|
+
"// Module Stub - Do Not Execute",
|
|
2150
|
+
"",
|
|
2151
|
+
...(entrypointReexports &&
|
|
2152
|
+
entrypointReexports.jsValueStatements.length > 0
|
|
2153
|
+
? [
|
|
2154
|
+
"// Tsonic entrypoint value re-exports (generated)",
|
|
2155
|
+
...entrypointReexports.jsValueStatements,
|
|
2156
|
+
"// End Tsonic entrypoint value re-exports",
|
|
2157
|
+
"",
|
|
2158
|
+
]
|
|
2159
|
+
: []),
|
|
2160
|
+
"throw new Error(",
|
|
2161
|
+
` 'Cannot import CLR namespace ${plan.namespace} in JavaScript runtime. ' +`,
|
|
2162
|
+
" 'This module provides TypeScript type definitions only. ' +",
|
|
2163
|
+
" 'Actual implementation requires .NET runtime via Tsonic compiler.'",
|
|
2164
|
+
");",
|
|
2165
|
+
"",
|
|
2166
|
+
].join("\n"), "utf-8");
|
|
2167
|
+
const bindings = {
|
|
2168
|
+
namespace: plan.namespace,
|
|
2169
|
+
contributingAssemblies: [config.outputName],
|
|
2170
|
+
types: typeBindings.sort((left, right) => left.clrName.localeCompare(right.clrName)),
|
|
2171
|
+
exports: valueBindings.size > 0
|
|
2172
|
+
? Object.fromEntries(Array.from(valueBindings.entries()).sort((left, right) => left[0].localeCompare(right[0])))
|
|
2173
|
+
: undefined,
|
|
2174
|
+
producer: {
|
|
2175
|
+
tool: "tsonic",
|
|
2176
|
+
mode: "aikya-firstparty",
|
|
2177
|
+
},
|
|
2178
|
+
};
|
|
2179
|
+
writeFileSync(bindingsPath, JSON.stringify(bindings, null, 2) + "\n", "utf-8");
|
|
2180
|
+
return { ok: true, value: undefined };
|
|
2181
|
+
};
|
|
2182
|
+
export const generateFirstPartyLibraryBindings = (config, bindingsOutDir) => {
|
|
2183
|
+
if (!config.entryPoint) {
|
|
2184
|
+
return {
|
|
2185
|
+
ok: false,
|
|
2186
|
+
error: "Library bindings generation requires an entryPoint in tsonic.json.",
|
|
2187
|
+
};
|
|
2188
|
+
}
|
|
2189
|
+
const absoluteEntryPoint = resolve(config.projectRoot, config.entryPoint);
|
|
2190
|
+
const absoluteSourceRoot = resolve(config.projectRoot, config.sourceRoot);
|
|
2191
|
+
const surfaceCapabilities = resolveSurfaceCapabilities(config.surface, {
|
|
2192
|
+
workspaceRoot: config.workspaceRoot,
|
|
2193
|
+
});
|
|
2194
|
+
const typeLibraries = config.libraries.filter((library) => !library.endsWith(".dll"));
|
|
2195
|
+
const allTypeRoots = [...config.typeRoots, ...typeLibraries].map((typeRoot) => resolve(config.workspaceRoot, typeRoot));
|
|
2196
|
+
const compilerOptions = {
|
|
2197
|
+
projectRoot: config.projectRoot,
|
|
2198
|
+
sourceRoot: absoluteSourceRoot,
|
|
2199
|
+
rootNamespace: config.rootNamespace,
|
|
2200
|
+
typeRoots: allTypeRoots,
|
|
2201
|
+
surface: config.surface,
|
|
2202
|
+
useStandardLib: surfaceCapabilities.useStandardLib,
|
|
2203
|
+
verbose: false,
|
|
2204
|
+
};
|
|
2205
|
+
const graphResult = buildModuleDependencyGraph(absoluteEntryPoint, compilerOptions);
|
|
2206
|
+
if (!graphResult.ok) {
|
|
2207
|
+
const message = graphResult.error
|
|
2208
|
+
.map((diagnostic) => diagnostic.location
|
|
2209
|
+
? `${diagnostic.location.file}:${diagnostic.location.line}:${diagnostic.location.column} ${diagnostic.message}`
|
|
2210
|
+
: diagnostic.message)
|
|
2211
|
+
.join("\n");
|
|
2212
|
+
return {
|
|
2213
|
+
ok: false,
|
|
2214
|
+
error: `Failed to generate first-party bindings from source:\n${message}`,
|
|
2215
|
+
};
|
|
2216
|
+
}
|
|
2217
|
+
rmSync(bindingsOutDir, { recursive: true, force: true });
|
|
2218
|
+
mkdirSync(bindingsOutDir, { recursive: true });
|
|
2219
|
+
const modulesByFileKey = new Map();
|
|
2220
|
+
for (const module of graphResult.value.modules) {
|
|
2221
|
+
modulesByFileKey.set(normalizeModuleFileKey(module.filePath), module);
|
|
2222
|
+
}
|
|
2223
|
+
const sourceIndexByFileKey = new Map();
|
|
2224
|
+
for (const module of graphResult.value.modules) {
|
|
2225
|
+
if (module.filePath.startsWith("__tsonic/"))
|
|
2226
|
+
continue;
|
|
2227
|
+
const moduleKey = normalizeModuleFileKey(module.filePath);
|
|
2228
|
+
const absolutePath = resolve(absoluteSourceRoot, moduleKey);
|
|
2229
|
+
const indexed = buildModuleSourceIndex(absolutePath, moduleKey);
|
|
2230
|
+
if (!indexed.ok)
|
|
2231
|
+
return indexed;
|
|
2232
|
+
sourceIndexByFileKey.set(moduleKey, indexed.value);
|
|
2233
|
+
}
|
|
2234
|
+
const plansResult = collectNamespacePlans(graphResult.value.modules, config.outputName, config.rootNamespace, sourceIndexByFileKey);
|
|
2235
|
+
if (!plansResult.ok)
|
|
2236
|
+
return plansResult;
|
|
2237
|
+
const entrypointReexportsResult = collectEntrypointReexports(graphResult.value.entryModule, modulesByFileKey);
|
|
2238
|
+
if (!entrypointReexportsResult.ok)
|
|
2239
|
+
return entrypointReexportsResult;
|
|
2240
|
+
for (const plan of plansResult.value) {
|
|
2241
|
+
const result = writeNamespaceArtifacts(config, bindingsOutDir, plan, plan.namespace === graphResult.value.entryModule.namespace
|
|
2242
|
+
? entrypointReexportsResult.value
|
|
2243
|
+
: undefined);
|
|
2244
|
+
if (!result.ok)
|
|
2245
|
+
return result;
|
|
2246
|
+
}
|
|
2247
|
+
const overlayResult = overlayDependencyBindings(config, bindingsOutDir);
|
|
2248
|
+
if (!overlayResult.ok)
|
|
2249
|
+
return overlayResult;
|
|
2250
|
+
return { ok: true, value: undefined };
|
|
2251
|
+
};
|
|
2252
|
+
//# sourceMappingURL=library-bindings-firstparty.js.map
|