@tsonic/cli 0.0.61 → 0.0.63
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/cli/dispatcher.d.ts.map +1 -1
- package/dist/cli/dispatcher.js +15 -5
- package/dist/cli/dispatcher.js.map +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +1 -0
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/parser.d.ts.map +1 -1
- package/dist/cli/parser.js +9 -6
- package/dist/cli/parser.js.map +1 -1
- package/dist/cli/parser.test.js +12 -3
- package/dist/cli/parser.test.js.map +1 -1
- package/dist/commands/add-common.d.ts +1 -0
- package/dist/commands/add-common.d.ts.map +1 -1
- package/dist/commands/add-common.js +38 -4
- package/dist/commands/add-common.js.map +1 -1
- package/dist/commands/add-common.test.js.map +1 -1
- package/dist/commands/add-deps.test.js +1 -8
- package/dist/commands/add-deps.test.js.map +1 -1
- package/dist/commands/add-framework.d.ts.map +1 -1
- package/dist/commands/add-framework.js +3 -1
- package/dist/commands/add-framework.js.map +1 -1
- package/dist/commands/add-npm.d.ts.map +1 -1
- package/dist/commands/add-npm.js +18 -6
- package/dist/commands/add-npm.js.map +1 -1
- package/dist/commands/add-npm.test.js +25 -9
- package/dist/commands/add-npm.test.js.map +1 -1
- package/dist/commands/add-nuget.d.ts.map +1 -1
- package/dist/commands/add-nuget.js +3 -1
- package/dist/commands/add-nuget.js.map +1 -1
- package/dist/commands/add-package.d.ts.map +1 -1
- package/dist/commands/add-package.js +17 -5
- package/dist/commands/add-package.js.map +1 -1
- package/dist/commands/build-library-bindings-aliases.test.js +724 -5
- package/dist/commands/build-library-bindings-aliases.test.js.map +1 -1
- package/dist/commands/build-native-lib.test.js +64 -7
- package/dist/commands/build-native-lib.test.js.map +1 -1
- package/dist/commands/build-no-generate.test.js +19 -2
- package/dist/commands/build-no-generate.test.js.map +1 -1
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +37 -9
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/build.test.d.ts +2 -0
- package/dist/commands/build.test.d.ts.map +1 -0
- package/dist/commands/build.test.js +106 -0
- package/dist/commands/build.test.js.map +1 -0
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +8 -4
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +1 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/init.test.js +1 -1
- package/dist/commands/init.test.js.map +1 -1
- package/dist/commands/library-bindings-augment.d.ts +12 -0
- package/dist/commands/library-bindings-augment.d.ts.map +1 -1
- package/dist/commands/library-bindings-augment.js +648 -49
- package/dist/commands/library-bindings-augment.js.map +1 -1
- package/dist/commands/library-bindings-augment.test.d.ts +2 -0
- package/dist/commands/library-bindings-augment.test.d.ts.map +1 -0
- package/dist/commands/library-bindings-augment.test.js +89 -0
- package/dist/commands/library-bindings-augment.test.js.map +1 -0
- package/dist/commands/remove-nuget.d.ts.map +1 -1
- package/dist/commands/remove-nuget.js.map +1 -1
- package/dist/commands/restore.d.ts.map +1 -1
- package/dist/commands/restore.js +65 -22
- package/dist/commands/restore.js.map +1 -1
- package/dist/commands/restore.test.js +116 -27
- package/dist/commands/restore.test.js.map +1 -1
- package/dist/commands/run-build-regressions.test.js +42 -8
- package/dist/commands/run-build-regressions.test.js.map +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +1 -0
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/test.d.ts.map +1 -1
- package/dist/commands/test.js +5 -1
- package/dist/commands/test.js.map +1 -1
- package/dist/commands/update-nuget.d.ts.map +1 -1
- package/dist/commands/update-nuget.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +22 -9
- package/dist/config.js.map +1 -1
- package/dist/dotnet/nuget-config.test.js +1 -1
- package/dist/dotnet/nuget-config.test.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/runtime/nodejs.dll +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { readFileSync, readdirSync, writeFileSync,
|
|
2
|
-
import { basename, join, resolve, posix } from "node:path";
|
|
3
|
-
import { buildModuleDependencyGraph } from "@tsonic/frontend";
|
|
1
|
+
import { copyFileSync, existsSync, readFileSync, readdirSync, writeFileSync, } from "node:fs";
|
|
2
|
+
import { basename, dirname, join, resolve, posix } from "node:path";
|
|
3
|
+
import { buildModuleDependencyGraph, } from "@tsonic/frontend";
|
|
4
4
|
import * as ts from "typescript";
|
|
5
5
|
const renderDiagnostics = (diags) => {
|
|
6
6
|
return diags
|
|
@@ -15,8 +15,30 @@ const renderDiagnostics = (diags) => {
|
|
|
15
15
|
const escapeRegExp = (text) => {
|
|
16
16
|
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
17
17
|
};
|
|
18
|
+
const typePrinter = ts.createPrinter({ removeComments: true });
|
|
19
|
+
const printTypeNodeText = (node, sourceFile) => {
|
|
20
|
+
return typePrinter
|
|
21
|
+
.printNode(ts.EmitHint.Unspecified, node, sourceFile)
|
|
22
|
+
.trim();
|
|
23
|
+
};
|
|
24
|
+
const ensureUndefinedInType = (typeText) => {
|
|
25
|
+
const trimmed = typeText.trim();
|
|
26
|
+
if (/\bundefined\b/.test(trimmed))
|
|
27
|
+
return trimmed;
|
|
28
|
+
return `${trimmed} | undefined`;
|
|
29
|
+
};
|
|
18
30
|
const normalizeModuleFileKey = (filePath) => {
|
|
19
|
-
return posix
|
|
31
|
+
return posix
|
|
32
|
+
.normalize(filePath)
|
|
33
|
+
.replace(/^(\.\/)+/, "")
|
|
34
|
+
.replace(/^\/+/, "");
|
|
35
|
+
};
|
|
36
|
+
const getPropertyNameText = (name) => {
|
|
37
|
+
if (ts.isIdentifier(name))
|
|
38
|
+
return name.text;
|
|
39
|
+
if (ts.isStringLiteral(name) || ts.isNumericLiteral(name))
|
|
40
|
+
return name.text;
|
|
41
|
+
return undefined;
|
|
20
42
|
};
|
|
21
43
|
const stripExistingSection = (text, startMarker, endMarker) => {
|
|
22
44
|
const start = text.indexOf(startMarker);
|
|
@@ -35,7 +57,9 @@ const upsertSectionAfterImports = (text, startMarker, endMarker, body) => {
|
|
|
35
57
|
while (insertAt < lines.length) {
|
|
36
58
|
const line = lines[insertAt] ?? "";
|
|
37
59
|
const trimmed = line.trim();
|
|
38
|
-
if (trimmed === "" ||
|
|
60
|
+
if (trimmed === "" ||
|
|
61
|
+
trimmed.startsWith("//") ||
|
|
62
|
+
trimmed.startsWith("import ")) {
|
|
39
63
|
insertAt += 1;
|
|
40
64
|
continue;
|
|
41
65
|
}
|
|
@@ -103,7 +127,10 @@ const applyWrappersToBaseType = (baseType, wrappers) => {
|
|
|
103
127
|
};
|
|
104
128
|
const patchInternalIndexWithMemberOverrides = (internalIndexDtsPath, overrides) => {
|
|
105
129
|
if (!existsSync(internalIndexDtsPath)) {
|
|
106
|
-
return {
|
|
130
|
+
return {
|
|
131
|
+
ok: false,
|
|
132
|
+
error: `Internal index not found at ${internalIndexDtsPath}`,
|
|
133
|
+
};
|
|
107
134
|
}
|
|
108
135
|
const original = readFileSync(internalIndexDtsPath, "utf-8");
|
|
109
136
|
// Collect wrapper imports (dedupe + validate no alias conflicts).
|
|
@@ -112,7 +139,8 @@ const patchInternalIndexWithMemberOverrides = (internalIndexDtsPath, overrides)
|
|
|
112
139
|
for (const w of o.wrappers) {
|
|
113
140
|
const existing = wrapperByAlias.get(w.aliasName);
|
|
114
141
|
if (existing) {
|
|
115
|
-
if (existing.source !== w.source ||
|
|
142
|
+
if (existing.source !== w.source ||
|
|
143
|
+
existing.importedName !== w.importedName) {
|
|
116
144
|
return {
|
|
117
145
|
ok: false,
|
|
118
146
|
error: `Conflicting wrapper import alias '${w.aliasName}' while augmenting ${internalIndexDtsPath}.\n` +
|
|
@@ -173,30 +201,240 @@ const patchInternalIndexWithMemberOverrides = (internalIndexDtsPath, overrides)
|
|
|
173
201
|
for (const o of list.sort((a, b) => a.memberName.localeCompare(b.memberName))) {
|
|
174
202
|
const propRe = new RegExp(String.raw `(^\s*(?:readonly\s+)?${escapeRegExp(o.memberName)}\s*:\s*)([^;]+)(;)`, "m");
|
|
175
203
|
const propMatch = propRe.exec(body);
|
|
176
|
-
if (
|
|
204
|
+
if (propMatch) {
|
|
205
|
+
const baseType = (o.replaceWithSourceType ? o.sourceTypeText : undefined) ??
|
|
206
|
+
propMatch[2] ??
|
|
207
|
+
"";
|
|
208
|
+
let nextType = applyWrappersToBaseType(baseType, o.wrappers);
|
|
209
|
+
if (o.isOptional)
|
|
210
|
+
nextType = ensureUndefinedInType(nextType);
|
|
211
|
+
body = body.replace(propRe, `$1${nextType}$3`);
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const getterRe = new RegExp(String.raw `(^\s*get\s+${escapeRegExp(o.memberName)}\s*\(\)\s*:\s*)([^;]+)(;)`, "m");
|
|
215
|
+
const setterRe = new RegExp(String.raw `(^\s*set\s+${escapeRegExp(o.memberName)}\s*\(\s*value\s*:\s*)([^)]+)(\)\s*;)`, "m");
|
|
216
|
+
const getterMatch = getterRe.exec(body);
|
|
217
|
+
const setterMatch = setterRe.exec(body);
|
|
218
|
+
if (!getterMatch && !setterMatch) {
|
|
177
219
|
return {
|
|
178
220
|
ok: false,
|
|
179
221
|
error: `Failed to locate property '${o.memberName}' on '${ifaceName}' in ${internalIndexDtsPath}.\n` +
|
|
180
|
-
`This property was
|
|
222
|
+
`This property was declared in TS source and should exist in CLR metadata.`,
|
|
181
223
|
};
|
|
182
224
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
225
|
+
if (getterMatch) {
|
|
226
|
+
const baseType = (o.replaceWithSourceType ? o.sourceTypeText : undefined) ??
|
|
227
|
+
getterMatch[2] ??
|
|
228
|
+
"";
|
|
229
|
+
let nextType = applyWrappersToBaseType(baseType, o.wrappers);
|
|
230
|
+
if (o.isOptional)
|
|
231
|
+
nextType = ensureUndefinedInType(nextType);
|
|
232
|
+
body = body.replace(getterRe, `$1${nextType}$3`);
|
|
233
|
+
}
|
|
234
|
+
if (setterMatch) {
|
|
235
|
+
const baseType = (o.replaceWithSourceType ? o.sourceTypeText : undefined) ??
|
|
236
|
+
setterMatch[2] ??
|
|
237
|
+
"";
|
|
238
|
+
let nextType = applyWrappersToBaseType(baseType, o.wrappers);
|
|
239
|
+
if (o.isOptional)
|
|
240
|
+
nextType = ensureUndefinedInType(nextType);
|
|
241
|
+
body = body.replace(setterRe, `$1${nextType}$3`);
|
|
242
|
+
}
|
|
186
243
|
}
|
|
187
244
|
const patchedBlock = head + body + tail;
|
|
188
|
-
next =
|
|
245
|
+
next =
|
|
246
|
+
next.slice(0, match.index) +
|
|
247
|
+
patchedBlock +
|
|
248
|
+
next.slice(match.index + block.length);
|
|
189
249
|
}
|
|
190
250
|
if (next !== original) {
|
|
191
251
|
writeFileSync(internalIndexDtsPath, next, "utf-8");
|
|
192
252
|
}
|
|
193
253
|
return { ok: true, value: undefined };
|
|
194
254
|
};
|
|
255
|
+
export const patchInternalIndexBrandMarkersOptional = (internalIndexDtsPath, typeNames) => {
|
|
256
|
+
if (!existsSync(internalIndexDtsPath)) {
|
|
257
|
+
return {
|
|
258
|
+
ok: false,
|
|
259
|
+
error: `Internal index not found at ${internalIndexDtsPath}`,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
const original = readFileSync(internalIndexDtsPath, "utf-8");
|
|
263
|
+
let next = original;
|
|
264
|
+
for (const typeName of Array.from(new Set(typeNames)).sort((a, b) => a.localeCompare(b))) {
|
|
265
|
+
const ifaceName = `${typeName}$instance`;
|
|
266
|
+
const ifaceRe = new RegExp(String.raw `export\s+interface\s+${escapeRegExp(ifaceName)}\b[^{]*\{[\s\S]*?^\}`, "m");
|
|
267
|
+
const match = ifaceRe.exec(next);
|
|
268
|
+
if (!match || match.index === undefined)
|
|
269
|
+
continue;
|
|
270
|
+
const block = match[0];
|
|
271
|
+
const open = block.indexOf("{");
|
|
272
|
+
const close = block.lastIndexOf("}");
|
|
273
|
+
if (open < 0 || close < 0 || close <= open)
|
|
274
|
+
continue;
|
|
275
|
+
const head = block.slice(0, open + 1);
|
|
276
|
+
let body = block.slice(open + 1, close);
|
|
277
|
+
const tail = block.slice(close);
|
|
278
|
+
const brandRe = /(^\s*readonly\s+__tsonic_type_[A-Za-z0-9_]+)\s*:\s*never\s*;/gm;
|
|
279
|
+
if (!brandRe.test(body))
|
|
280
|
+
continue;
|
|
281
|
+
body = body.replace(brandRe, "$1?: never;");
|
|
282
|
+
const patchedBlock = head + body + tail;
|
|
283
|
+
next =
|
|
284
|
+
next.slice(0, match.index) +
|
|
285
|
+
patchedBlock +
|
|
286
|
+
next.slice(match.index + block.length);
|
|
287
|
+
}
|
|
288
|
+
if (next !== original) {
|
|
289
|
+
writeFileSync(internalIndexDtsPath, next, "utf-8");
|
|
290
|
+
}
|
|
291
|
+
return { ok: true, value: undefined };
|
|
292
|
+
};
|
|
293
|
+
const splitTopLevelTypeArgs = (text) => {
|
|
294
|
+
const parts = [];
|
|
295
|
+
let depthAngle = 0;
|
|
296
|
+
let depthParen = 0;
|
|
297
|
+
let depthBrace = 0;
|
|
298
|
+
let depthBracket = 0;
|
|
299
|
+
let start = 0;
|
|
300
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
301
|
+
const ch = text[i];
|
|
302
|
+
if (ch === "<")
|
|
303
|
+
depthAngle += 1;
|
|
304
|
+
else if (ch === ">")
|
|
305
|
+
depthAngle = Math.max(0, depthAngle - 1);
|
|
306
|
+
else if (ch === "(")
|
|
307
|
+
depthParen += 1;
|
|
308
|
+
else if (ch === ")")
|
|
309
|
+
depthParen = Math.max(0, depthParen - 1);
|
|
310
|
+
else if (ch === "{")
|
|
311
|
+
depthBrace += 1;
|
|
312
|
+
else if (ch === "}")
|
|
313
|
+
depthBrace = Math.max(0, depthBrace - 1);
|
|
314
|
+
else if (ch === "[")
|
|
315
|
+
depthBracket += 1;
|
|
316
|
+
else if (ch === "]")
|
|
317
|
+
depthBracket = Math.max(0, depthBracket - 1);
|
|
318
|
+
else if (ch === "," &&
|
|
319
|
+
depthAngle === 0 &&
|
|
320
|
+
depthParen === 0 &&
|
|
321
|
+
depthBrace === 0 &&
|
|
322
|
+
depthBracket === 0) {
|
|
323
|
+
parts.push(text.slice(start, i).trim());
|
|
324
|
+
start = i + 1;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
parts.push(text.slice(start).trim());
|
|
328
|
+
return parts.filter((p) => p.length > 0);
|
|
329
|
+
};
|
|
330
|
+
const expandUnionsDeep = (typeText) => {
|
|
331
|
+
const unionPrefixRe = /Union_\d+</g;
|
|
332
|
+
let result = typeText;
|
|
333
|
+
// Iterate until no more Union_N< patterns remain
|
|
334
|
+
while (true) {
|
|
335
|
+
unionPrefixRe.lastIndex = 0;
|
|
336
|
+
const prefixMatch = unionPrefixRe.exec(result);
|
|
337
|
+
if (!prefixMatch)
|
|
338
|
+
break;
|
|
339
|
+
// Find the matching > for the < at the end of the prefix
|
|
340
|
+
const openAngle = prefixMatch.index + prefixMatch[0].length - 1;
|
|
341
|
+
let depth = 1;
|
|
342
|
+
let closeAngle = -1;
|
|
343
|
+
for (let i = openAngle + 1; i < result.length; i += 1) {
|
|
344
|
+
const ch = result[i];
|
|
345
|
+
if (ch === "<")
|
|
346
|
+
depth += 1;
|
|
347
|
+
else if (ch === ">") {
|
|
348
|
+
depth -= 1;
|
|
349
|
+
if (depth === 0) {
|
|
350
|
+
closeAngle = i;
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
if (closeAngle < 0)
|
|
356
|
+
break;
|
|
357
|
+
const inner = result.slice(openAngle + 1, closeAngle);
|
|
358
|
+
const args = splitTopLevelTypeArgs(inner);
|
|
359
|
+
if (args.length < 2)
|
|
360
|
+
break;
|
|
361
|
+
const expanded = `(${args.join(" | ")})`;
|
|
362
|
+
result =
|
|
363
|
+
result.slice(0, prefixMatch.index) +
|
|
364
|
+
expanded +
|
|
365
|
+
result.slice(closeAngle + 1);
|
|
366
|
+
}
|
|
367
|
+
return result;
|
|
368
|
+
};
|
|
369
|
+
const patchFacadeWithSourceFunctionSignatures = (facadeDtsPath, signaturesByName) => {
|
|
370
|
+
if (!existsSync(facadeDtsPath)) {
|
|
371
|
+
return {
|
|
372
|
+
ok: false,
|
|
373
|
+
error: `Facade declaration file not found at ${facadeDtsPath}`,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
const original = readFileSync(facadeDtsPath, "utf-8");
|
|
377
|
+
let next = original;
|
|
378
|
+
for (const [name, signatures] of Array.from(signaturesByName.entries()).sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
379
|
+
if (signatures.length === 0)
|
|
380
|
+
continue;
|
|
381
|
+
const fnRe = new RegExp(String.raw `^(export\s+declare\s+function\s+${escapeRegExp(name)}(?:<[\s\S]*?>)?\s*\([\s\S]*?\)\s*:\s*)([^;]+)(;)`, "m");
|
|
382
|
+
const currentMatch = fnRe.exec(next);
|
|
383
|
+
if (currentMatch) {
|
|
384
|
+
const existingReturnType = currentMatch[2]?.trim() ?? "";
|
|
385
|
+
const replacement = Array.from(new Set(signatures.map((sig) => {
|
|
386
|
+
const returnType = sig.returnTypeText.includes("{")
|
|
387
|
+
? expandUnionsDeep(existingReturnType)
|
|
388
|
+
: sig.returnTypeText;
|
|
389
|
+
return `export declare function ${name}${sig.typeParametersText}(${sig.parametersText}): ${returnType};`;
|
|
390
|
+
}))).join("\n");
|
|
391
|
+
next = next.replace(fnRe, replacement);
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
// If no function declaration match, try const Func<...> pattern
|
|
395
|
+
const constFuncRe = new RegExp(String.raw `^export\s+declare\s+const\s+${escapeRegExp(name)}\s*:\s*Func<([\s\S]+?)>\s*;`, "m");
|
|
396
|
+
const constMatch = constFuncRe.exec(next);
|
|
397
|
+
if (!constMatch || !constMatch[1])
|
|
398
|
+
continue;
|
|
399
|
+
const funcTypeArgs = splitTopLevelTypeArgs(constMatch[1]);
|
|
400
|
+
if (funcTypeArgs.length < 2)
|
|
401
|
+
continue;
|
|
402
|
+
// Last arg = return type, remaining args = parameter types
|
|
403
|
+
const facadeParamTypes = funcTypeArgs.slice(0, -1);
|
|
404
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
405
|
+
const facadeReturnType = funcTypeArgs[funcTypeArgs.length - 1];
|
|
406
|
+
// Use the first source signature for parameter names
|
|
407
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
408
|
+
const sourceSig = signatures[0];
|
|
409
|
+
const sourceParams = sourceSig.parametersText
|
|
410
|
+
.split(",")
|
|
411
|
+
.map((p) => p.trim())
|
|
412
|
+
.filter((p) => p.length > 0);
|
|
413
|
+
// If count mismatch, skip patching for this declaration
|
|
414
|
+
if (sourceParams.length !== facadeParamTypes.length)
|
|
415
|
+
continue;
|
|
416
|
+
// Pair source parameter NAMES with facade parameter TYPES
|
|
417
|
+
const pairedParams = sourceParams.map((param, idx) => {
|
|
418
|
+
const colonIdx = param.indexOf(":");
|
|
419
|
+
const paramName = colonIdx >= 0 ? param.slice(0, colonIdx).trim() : param.trim();
|
|
420
|
+
return `${paramName}: ${facadeParamTypes[idx]}`;
|
|
421
|
+
});
|
|
422
|
+
const expandedReturnType = expandUnionsDeep(facadeReturnType);
|
|
423
|
+
const typeParamsText = sourceSig.typeParametersText;
|
|
424
|
+
const replacement = `export declare function ${name}${typeParamsText}(${pairedParams.join(", ")}): ${expandedReturnType};`;
|
|
425
|
+
next = next.replace(constFuncRe, replacement);
|
|
426
|
+
}
|
|
427
|
+
if (next !== original) {
|
|
428
|
+
writeFileSync(facadeDtsPath, next, "utf-8");
|
|
429
|
+
}
|
|
430
|
+
return { ok: true, value: undefined };
|
|
431
|
+
};
|
|
195
432
|
const classifyExportKind = (module, name) => {
|
|
196
433
|
const isNamed = (stmt) => typeof stmt.name === "string";
|
|
197
434
|
const findDecl = () => {
|
|
198
435
|
for (const stmt of module.body) {
|
|
199
|
-
if (!("isExported" in stmt) ||
|
|
436
|
+
if (!("isExported" in stmt) ||
|
|
437
|
+
stmt.isExported !== true)
|
|
200
438
|
continue;
|
|
201
439
|
if (isNamed(stmt) && stmt.name === name)
|
|
202
440
|
return stmt;
|
|
@@ -256,7 +494,10 @@ const resolveLocalModuleFile = (fromModule, fromFile, modulesByFile) => {
|
|
|
256
494
|
};
|
|
257
495
|
const buildModuleSourceIndex = (absoluteFilePath, fileKey) => {
|
|
258
496
|
if (!existsSync(absoluteFilePath)) {
|
|
259
|
-
return {
|
|
497
|
+
return {
|
|
498
|
+
ok: false,
|
|
499
|
+
error: `Failed to read source file for bindings augmentation: ${absoluteFilePath}`,
|
|
500
|
+
};
|
|
260
501
|
}
|
|
261
502
|
const content = readFileSync(absoluteFilePath, "utf-8");
|
|
262
503
|
const scriptKind = absoluteFilePath.endsWith(".tsx")
|
|
@@ -268,13 +509,26 @@ const buildModuleSourceIndex = (absoluteFilePath, fileKey) => {
|
|
|
268
509
|
const wrapperImportsByLocalName = new Map();
|
|
269
510
|
const typeImportsByLocalName = new Map();
|
|
270
511
|
const typeAliasesByName = new Map();
|
|
512
|
+
const exportedFunctionSignaturesByName = new Map();
|
|
271
513
|
const memberTypesByClassAndMember = new Map();
|
|
272
|
-
const
|
|
273
|
-
if (
|
|
274
|
-
return
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
514
|
+
const printTypeParametersText = (typeParameters) => {
|
|
515
|
+
if (!typeParameters || typeParameters.length === 0)
|
|
516
|
+
return "";
|
|
517
|
+
return `<${typeParameters.map((tp) => tp.getText(sourceFile)).join(", ")}>`;
|
|
518
|
+
};
|
|
519
|
+
const printParameterText = (param) => {
|
|
520
|
+
const rest = param.dotDotDotToken ? "..." : "";
|
|
521
|
+
const name = param.name.getText(sourceFile);
|
|
522
|
+
const optional = param.questionToken ? "?" : "";
|
|
523
|
+
const type = param.type
|
|
524
|
+
? printTypeNodeText(param.type, sourceFile)
|
|
525
|
+
: "unknown";
|
|
526
|
+
return `${rest}${name}${optional}: ${type}`;
|
|
527
|
+
};
|
|
528
|
+
const addExportedFunctionSignature = (name, sig) => {
|
|
529
|
+
const list = exportedFunctionSignaturesByName.get(name) ?? [];
|
|
530
|
+
list.push(sig);
|
|
531
|
+
exportedFunctionSignaturesByName.set(name, list);
|
|
278
532
|
};
|
|
279
533
|
for (const stmt of sourceFile.statements) {
|
|
280
534
|
if (ts.isImportDeclaration(stmt)) {
|
|
@@ -295,9 +549,15 @@ const buildModuleSourceIndex = (absoluteFilePath, fileKey) => {
|
|
|
295
549
|
const isTypeOnly = clause.isTypeOnly || spec.isTypeOnly;
|
|
296
550
|
if (!isTypeOnly)
|
|
297
551
|
continue;
|
|
298
|
-
typeImportsByLocalName.set(localName, {
|
|
552
|
+
typeImportsByLocalName.set(localName, {
|
|
553
|
+
source: moduleSpecifier,
|
|
554
|
+
importedName,
|
|
555
|
+
});
|
|
299
556
|
if (importedName === "ExtensionMethods") {
|
|
300
|
-
wrapperImportsByLocalName.set(localName, {
|
|
557
|
+
wrapperImportsByLocalName.set(localName, {
|
|
558
|
+
source: moduleSpecifier,
|
|
559
|
+
importedName,
|
|
560
|
+
});
|
|
301
561
|
}
|
|
302
562
|
}
|
|
303
563
|
continue;
|
|
@@ -308,31 +568,102 @@ const buildModuleSourceIndex = (absoluteFilePath, fileKey) => {
|
|
|
308
568
|
typeAliasesByName.set(aliasName, { typeParameters, type: stmt.type });
|
|
309
569
|
continue;
|
|
310
570
|
}
|
|
571
|
+
if (ts.isFunctionDeclaration(stmt)) {
|
|
572
|
+
const hasExport = stmt.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
|
|
573
|
+
if (!hasExport || !stmt.name || !stmt.type)
|
|
574
|
+
continue;
|
|
575
|
+
const parametersText = stmt.parameters.map(printParameterText).join(", ");
|
|
576
|
+
addExportedFunctionSignature(stmt.name.text, {
|
|
577
|
+
typeParametersText: printTypeParametersText(stmt.typeParameters),
|
|
578
|
+
parametersText,
|
|
579
|
+
returnTypeText: printTypeNodeText(stmt.type, sourceFile),
|
|
580
|
+
});
|
|
581
|
+
continue;
|
|
582
|
+
}
|
|
583
|
+
if (ts.isVariableStatement(stmt)) {
|
|
584
|
+
const hasExport = stmt.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
|
|
585
|
+
if (!hasExport)
|
|
586
|
+
continue;
|
|
587
|
+
for (const decl of stmt.declarationList.declarations) {
|
|
588
|
+
if (!ts.isIdentifier(decl.name))
|
|
589
|
+
continue;
|
|
590
|
+
const exportName = decl.name.text;
|
|
591
|
+
const init = decl.initializer;
|
|
592
|
+
if (!init)
|
|
593
|
+
continue;
|
|
594
|
+
if (ts.isArrowFunction(init) || ts.isFunctionExpression(init)) {
|
|
595
|
+
const returnType = init.type;
|
|
596
|
+
if (!returnType)
|
|
597
|
+
continue;
|
|
598
|
+
const parametersText = init.parameters
|
|
599
|
+
.map(printParameterText)
|
|
600
|
+
.join(", ");
|
|
601
|
+
addExportedFunctionSignature(exportName, {
|
|
602
|
+
typeParametersText: printTypeParametersText(init.typeParameters),
|
|
603
|
+
parametersText,
|
|
604
|
+
returnTypeText: printTypeNodeText(returnType, sourceFile),
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
311
610
|
if (ts.isClassDeclaration(stmt) && stmt.name) {
|
|
312
611
|
const className = stmt.name.text;
|
|
313
|
-
const members = memberTypesByClassAndMember.get(className) ??
|
|
612
|
+
const members = memberTypesByClassAndMember.get(className) ??
|
|
613
|
+
new Map();
|
|
314
614
|
for (const member of stmt.members) {
|
|
315
615
|
if (ts.isGetAccessorDeclaration(member)) {
|
|
316
616
|
if (!member.name || !member.type)
|
|
317
617
|
continue;
|
|
318
|
-
const name =
|
|
618
|
+
const name = getPropertyNameText(member.name);
|
|
319
619
|
if (!name)
|
|
320
620
|
continue;
|
|
321
|
-
members.set(name,
|
|
621
|
+
members.set(name, {
|
|
622
|
+
typeNode: member.type,
|
|
623
|
+
typeText: printTypeNodeText(member.type, sourceFile),
|
|
624
|
+
isOptional: false,
|
|
625
|
+
});
|
|
322
626
|
continue;
|
|
323
627
|
}
|
|
324
628
|
if (ts.isPropertyDeclaration(member)) {
|
|
325
629
|
if (!member.name || !member.type)
|
|
326
630
|
continue;
|
|
327
|
-
const name =
|
|
631
|
+
const name = getPropertyNameText(member.name);
|
|
328
632
|
if (!name)
|
|
329
633
|
continue;
|
|
330
|
-
members.set(name,
|
|
634
|
+
members.set(name, {
|
|
635
|
+
typeNode: member.type,
|
|
636
|
+
typeText: printTypeNodeText(member.type, sourceFile),
|
|
637
|
+
isOptional: member.questionToken !== undefined,
|
|
638
|
+
});
|
|
331
639
|
}
|
|
332
640
|
}
|
|
333
641
|
if (members.size > 0) {
|
|
334
642
|
memberTypesByClassAndMember.set(className, members);
|
|
335
643
|
}
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
if (ts.isInterfaceDeclaration(stmt)) {
|
|
647
|
+
const interfaceName = stmt.name.text;
|
|
648
|
+
const members = memberTypesByClassAndMember.get(interfaceName) ??
|
|
649
|
+
new Map();
|
|
650
|
+
for (const member of stmt.members) {
|
|
651
|
+
if (!ts.isPropertySignature(member))
|
|
652
|
+
continue;
|
|
653
|
+
if (!member.name || !member.type)
|
|
654
|
+
continue;
|
|
655
|
+
const name = getPropertyNameText(member.name);
|
|
656
|
+
if (!name)
|
|
657
|
+
continue;
|
|
658
|
+
members.set(name, {
|
|
659
|
+
typeNode: member.type,
|
|
660
|
+
typeText: printTypeNodeText(member.type, sourceFile),
|
|
661
|
+
isOptional: member.questionToken !== undefined,
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
if (members.size > 0) {
|
|
665
|
+
memberTypesByClassAndMember.set(interfaceName, members);
|
|
666
|
+
}
|
|
336
667
|
}
|
|
337
668
|
}
|
|
338
669
|
return {
|
|
@@ -342,10 +673,27 @@ const buildModuleSourceIndex = (absoluteFilePath, fileKey) => {
|
|
|
342
673
|
wrapperImportsByLocalName,
|
|
343
674
|
typeImportsByLocalName,
|
|
344
675
|
typeAliasesByName,
|
|
676
|
+
exportedFunctionSignaturesByName,
|
|
345
677
|
memberTypesByClassAndMember,
|
|
346
678
|
},
|
|
347
679
|
};
|
|
348
680
|
};
|
|
681
|
+
const typeNodeUsesImportedTypeNames = (node, typeImportsByLocalName) => {
|
|
682
|
+
let found = false;
|
|
683
|
+
const visit = (current) => {
|
|
684
|
+
if (found)
|
|
685
|
+
return;
|
|
686
|
+
if (ts.isTypeReferenceNode(current) && ts.isIdentifier(current.typeName)) {
|
|
687
|
+
if (typeImportsByLocalName.has(current.typeName.text)) {
|
|
688
|
+
found = true;
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
ts.forEachChild(current, visit);
|
|
693
|
+
};
|
|
694
|
+
visit(node);
|
|
695
|
+
return found;
|
|
696
|
+
};
|
|
349
697
|
const unwrapParens = (node) => {
|
|
350
698
|
let current = node;
|
|
351
699
|
while (ts.isParenthesizedTypeNode(current)) {
|
|
@@ -399,7 +747,8 @@ const collectExtensionWrapperImportsFromSourceType = (opts) => {
|
|
|
399
747
|
continue;
|
|
400
748
|
}
|
|
401
749
|
const imported = info.typeImportsByLocalName.get(ident);
|
|
402
|
-
if (imported &&
|
|
750
|
+
if (imported &&
|
|
751
|
+
(imported.source.startsWith(".") || imported.source.startsWith("/"))) {
|
|
403
752
|
const targetModule = resolveLocalModuleFile(imported.source, currentModuleKey, opts.modulesByFileKey);
|
|
404
753
|
if (targetModule) {
|
|
405
754
|
const targetKey = normalizeModuleFileKey(targetModule.filePath);
|
|
@@ -429,6 +778,7 @@ const collectExtensionWrapperImportsFromSourceType = (opts) => {
|
|
|
429
778
|
localName: ident,
|
|
430
779
|
aliasName: `__TsonicExt_${ident}`,
|
|
431
780
|
});
|
|
781
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
432
782
|
currentNode = args[0];
|
|
433
783
|
}
|
|
434
784
|
return { ok: true, value: wrappers };
|
|
@@ -453,7 +803,9 @@ const printIrType = (type, ctx) => {
|
|
|
453
803
|
case "primitiveType":
|
|
454
804
|
return type.name;
|
|
455
805
|
case "literalType":
|
|
456
|
-
return typeof type.value === "string"
|
|
806
|
+
return typeof type.value === "string"
|
|
807
|
+
? JSON.stringify(type.value)
|
|
808
|
+
: String(type.value);
|
|
457
809
|
case "anyType":
|
|
458
810
|
return "any";
|
|
459
811
|
case "unknownType":
|
|
@@ -469,7 +821,9 @@ const printIrType = (type, ctx) => {
|
|
|
469
821
|
const args = type.typeArguments ?? [];
|
|
470
822
|
if (args.length === 0)
|
|
471
823
|
return base;
|
|
472
|
-
const rendered = args
|
|
824
|
+
const rendered = args
|
|
825
|
+
.map((a) => printIrType(a, { parentPrecedence: 0 }))
|
|
826
|
+
.join(", ");
|
|
473
827
|
return `${base}<${rendered}>`;
|
|
474
828
|
}
|
|
475
829
|
case "arrayType":
|
|
@@ -484,7 +838,9 @@ const printIrType = (type, ctx) => {
|
|
|
484
838
|
return p.pattern.name;
|
|
485
839
|
return `p${i + 1}`;
|
|
486
840
|
})();
|
|
487
|
-
const t = p.type
|
|
841
|
+
const t = p.type
|
|
842
|
+
? printIrType(p.type, { parentPrecedence: 0 })
|
|
843
|
+
: "unknown";
|
|
488
844
|
return `${name}: ${t}`;
|
|
489
845
|
})
|
|
490
846
|
.join(", ");
|
|
@@ -492,11 +848,15 @@ const printIrType = (type, ctx) => {
|
|
|
492
848
|
return wrap(`(${ps}) => ${ret}`, 2);
|
|
493
849
|
}
|
|
494
850
|
case "unionType": {
|
|
495
|
-
const rendered = type.types
|
|
851
|
+
const rendered = type.types
|
|
852
|
+
.map((t) => printIrType(t, { parentPrecedence: 0 }))
|
|
853
|
+
.join(" | ");
|
|
496
854
|
return wrap(rendered, 0);
|
|
497
855
|
}
|
|
498
856
|
case "intersectionType": {
|
|
499
|
-
const rendered = type.types
|
|
857
|
+
const rendered = type.types
|
|
858
|
+
.map((t) => printIrType(t, { parentPrecedence: 1 }))
|
|
859
|
+
.join(" & ");
|
|
500
860
|
return wrap(rendered, 1);
|
|
501
861
|
}
|
|
502
862
|
case "dictionaryType": {
|
|
@@ -504,10 +864,40 @@ const printIrType = (type, ctx) => {
|
|
|
504
864
|
const v = printIrType(type.valueType, { parentPrecedence: 0 });
|
|
505
865
|
return `Record<${k}, ${v}>`;
|
|
506
866
|
}
|
|
507
|
-
case "objectType":
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
867
|
+
case "objectType": {
|
|
868
|
+
if (type.members.length === 0)
|
|
869
|
+
return "{}";
|
|
870
|
+
const members = type.members
|
|
871
|
+
.map((member) => {
|
|
872
|
+
if (member.kind === "propertySignature") {
|
|
873
|
+
const readonly = member.isReadonly ? "readonly " : "";
|
|
874
|
+
const optional = member.isOptional ? "?" : "";
|
|
875
|
+
const memberType = printIrType(member.type, {
|
|
876
|
+
parentPrecedence: 0,
|
|
877
|
+
});
|
|
878
|
+
return `${readonly}${member.name}${optional}: ${memberType}`;
|
|
879
|
+
}
|
|
880
|
+
const typeParams = printTypeParameters(member.typeParameters);
|
|
881
|
+
const args = member.parameters
|
|
882
|
+
.map((p, i) => {
|
|
883
|
+
const name = p.pattern.kind === "identifierPattern"
|
|
884
|
+
? p.pattern.name
|
|
885
|
+
: `p${i + 1}`;
|
|
886
|
+
const optional = p.isOptional ? "?" : "";
|
|
887
|
+
const paramType = p.type
|
|
888
|
+
? printIrType(p.type, { parentPrecedence: 0 })
|
|
889
|
+
: "unknown";
|
|
890
|
+
return `${name}${optional}: ${paramType}`;
|
|
891
|
+
})
|
|
892
|
+
.join(", ");
|
|
893
|
+
const returnType = member.returnType
|
|
894
|
+
? printIrType(member.returnType, { parentPrecedence: 0 })
|
|
895
|
+
: "void";
|
|
896
|
+
return `${member.name}${typeParams}(${args}): ${returnType}`;
|
|
897
|
+
})
|
|
898
|
+
.join("; ");
|
|
899
|
+
return `{ ${members} }`;
|
|
900
|
+
}
|
|
511
901
|
default:
|
|
512
902
|
// Exhaustiveness safety
|
|
513
903
|
return "unknown";
|
|
@@ -529,11 +919,88 @@ const renderExportedTypeAlias = (stmt, internalIndexDts) => {
|
|
|
529
919
|
const typeArgs = stmt.typeParameters && stmt.typeParameters.length > 0
|
|
530
920
|
? `<${stmt.typeParameters.map((tp) => tp.name).join(", ")}>`
|
|
531
921
|
: "";
|
|
532
|
-
return {
|
|
922
|
+
return {
|
|
923
|
+
ok: true,
|
|
924
|
+
value: `export type ${stmt.name}${typeParams} = Internal.${internalName}${typeArgs};`,
|
|
925
|
+
};
|
|
533
926
|
}
|
|
534
927
|
const rhs = printIrType(stmt.type, { parentPrecedence: 0 });
|
|
535
928
|
return { ok: true, value: `export type ${stmt.name}${typeParams} = ${rhs};` };
|
|
536
929
|
};
|
|
930
|
+
/**
|
|
931
|
+
* Overlay already-augmented bindings from dependency assemblies.
|
|
932
|
+
*
|
|
933
|
+
* When a library references sibling Tsonic-built libraries (via references.libraries),
|
|
934
|
+
* tsbindgen regenerates types from CLR metadata, losing source-level augmentation
|
|
935
|
+
* (literal types, optional markers, brand optionality, etc.). The dependency's published
|
|
936
|
+
* bindings already have this augmentation applied, so we overlay them onto the
|
|
937
|
+
* freshly-generated copies.
|
|
938
|
+
*/
|
|
939
|
+
export const overlayDependencyBindings = (config, bindingsOutDir) => {
|
|
940
|
+
const depBindingsDirByAssembly = new Map();
|
|
941
|
+
for (const lib of config.libraries) {
|
|
942
|
+
if (!lib.toLowerCase().endsWith(".dll"))
|
|
943
|
+
continue;
|
|
944
|
+
const assemblyName = basename(lib, ".dll");
|
|
945
|
+
// Skip the current package's own assembly.
|
|
946
|
+
if (assemblyName === config.outputName)
|
|
947
|
+
continue;
|
|
948
|
+
const depBindingsDir = resolveDependencyBindingsDirForDll(lib);
|
|
949
|
+
if (existsSync(depBindingsDir)) {
|
|
950
|
+
depBindingsDirByAssembly.set(assemblyName, depBindingsDir);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
if (depBindingsDirByAssembly.size === 0) {
|
|
954
|
+
return { ok: true, value: undefined };
|
|
955
|
+
}
|
|
956
|
+
// Scan generated bindings for namespace directories with internal/index.d.ts.
|
|
957
|
+
const generatedNamespaces = readdirSync(bindingsOutDir, {
|
|
958
|
+
withFileTypes: true,
|
|
959
|
+
})
|
|
960
|
+
.filter((e) => e.isDirectory())
|
|
961
|
+
.map((e) => e.name);
|
|
962
|
+
for (const ns of generatedNamespaces) {
|
|
963
|
+
const internalIndexPath = join(bindingsOutDir, ns, "internal", "index.d.ts");
|
|
964
|
+
if (!existsSync(internalIndexPath))
|
|
965
|
+
continue;
|
|
966
|
+
const content = readFileSync(internalIndexPath, "utf-8");
|
|
967
|
+
const assemblyMatch = content.match(/^\/\/ Assembly:\s*(.+)\s*$/m);
|
|
968
|
+
if (!assemblyMatch || !assemblyMatch[1])
|
|
969
|
+
continue;
|
|
970
|
+
const assembly = assemblyMatch[1].trim();
|
|
971
|
+
const depDir = depBindingsDirByAssembly.get(assembly);
|
|
972
|
+
if (!depDir)
|
|
973
|
+
continue;
|
|
974
|
+
// Overlay the dependency's already-augmented internal/index.d.ts.
|
|
975
|
+
const depInternalIndex = join(depDir, ns, "internal", "index.d.ts");
|
|
976
|
+
if (existsSync(depInternalIndex)) {
|
|
977
|
+
copyFileSync(depInternalIndex, internalIndexPath);
|
|
978
|
+
}
|
|
979
|
+
// Overlay the dependency's facade .d.ts (may have type aliases, re-exports).
|
|
980
|
+
const facadeDts = join(bindingsOutDir, `${ns}.d.ts`);
|
|
981
|
+
const depFacadeDts = join(depDir, `${ns}.d.ts`);
|
|
982
|
+
if (existsSync(facadeDts) && existsSync(depFacadeDts)) {
|
|
983
|
+
copyFileSync(depFacadeDts, facadeDts);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
return { ok: true, value: undefined };
|
|
987
|
+
};
|
|
988
|
+
export const resolveDependencyBindingsDirForDll = (dllPath) => {
|
|
989
|
+
// Walk up from the DLL and resolve the nearest project-style bindings directory:
|
|
990
|
+
// - <project>/dist/tsonic/bindings
|
|
991
|
+
let cursor = resolve(dirname(dllPath));
|
|
992
|
+
for (let i = 0; i < 24; i++) {
|
|
993
|
+
const projectStyle = join(cursor, "dist", "tsonic", "bindings");
|
|
994
|
+
if (existsSync(projectStyle))
|
|
995
|
+
return projectStyle;
|
|
996
|
+
const parent = dirname(cursor);
|
|
997
|
+
if (parent === cursor)
|
|
998
|
+
break;
|
|
999
|
+
cursor = parent;
|
|
1000
|
+
}
|
|
1001
|
+
// No bindings directory found; caller checks existsSync before use.
|
|
1002
|
+
return join(resolve(dirname(dllPath)), "dist", "tsonic", "bindings");
|
|
1003
|
+
};
|
|
537
1004
|
export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
538
1005
|
const entryPoint = config.entryPoint;
|
|
539
1006
|
if (!entryPoint) {
|
|
@@ -552,7 +1019,10 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
552
1019
|
};
|
|
553
1020
|
const graphResult = buildModuleDependencyGraph(absoluteEntryPoint, compilerOptions);
|
|
554
1021
|
if (!graphResult.ok) {
|
|
555
|
-
return {
|
|
1022
|
+
return {
|
|
1023
|
+
ok: false,
|
|
1024
|
+
error: `Failed to analyze library sources:\n${renderDiagnostics(graphResult.error)}`,
|
|
1025
|
+
};
|
|
556
1026
|
}
|
|
557
1027
|
const { modules, entryModule } = graphResult.value;
|
|
558
1028
|
const facadesByNamespace = new Map(indexFacadeFiles(bindingsOutDir));
|
|
@@ -661,8 +1131,10 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
661
1131
|
`Could not find an exported declaration named '${exp.originalName}' in ${targetModule.filePath}.`,
|
|
662
1132
|
};
|
|
663
1133
|
}
|
|
1134
|
+
const spec = exp.name === exp.originalName
|
|
1135
|
+
? exp.name
|
|
1136
|
+
: `${exp.originalName} as ${exp.name}`;
|
|
664
1137
|
const isTypeOnly = kind === "type";
|
|
665
|
-
const spec = exp.name === exp.originalName ? exp.name : `${exp.originalName} as ${exp.name}`;
|
|
666
1138
|
const key = `${targetFacade.moduleSpecifier}|${isTypeOnly ? "type" : "value"}`;
|
|
667
1139
|
const list = grouped.get(key) ?? [];
|
|
668
1140
|
list.push(spec);
|
|
@@ -697,13 +1169,20 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
697
1169
|
writeFileSync(entryFacade.facadeJsPath, next, "utf-8");
|
|
698
1170
|
}
|
|
699
1171
|
}
|
|
700
|
-
// 3) Preserve TS-authored
|
|
1172
|
+
// 3) Preserve TS-authored member typing semantics on exported class/interface members.
|
|
701
1173
|
// These wrapper types (ExtensionMethods<...>) do not exist in CLR metadata and therefore
|
|
702
1174
|
// cannot be discovered by tsbindgen when generating bindings from the DLL. We can,
|
|
703
1175
|
// however, safely re-apply them for Tsonic-authored libraries by reading the TS source
|
|
704
|
-
// graph and
|
|
1176
|
+
// graph and patching the published internal/index.d.ts:
|
|
1177
|
+
// - Re-apply ExtensionMethods wrappers (class + interface members)
|
|
1178
|
+
// - Preserve optional (`?`) semantics by allowing `undefined` on patched members
|
|
1179
|
+
// - For exported interfaces, preserve source structural member types when safe
|
|
705
1180
|
const sourceIndexByFileKey = new Map();
|
|
706
1181
|
for (const m of modules) {
|
|
1182
|
+
// Synthetic IR modules (e.g., program-wide anonymous type declarations) do not
|
|
1183
|
+
// correspond to real source files and must be ignored by source-based augmentation.
|
|
1184
|
+
if (m.filePath.startsWith("__tsonic/"))
|
|
1185
|
+
continue;
|
|
707
1186
|
const key = normalizeModuleFileKey(m.filePath);
|
|
708
1187
|
const absolutePath = resolve(absoluteSourceRoot, key);
|
|
709
1188
|
const indexed = buildModuleSourceIndex(absolutePath, key);
|
|
@@ -712,15 +1191,35 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
712
1191
|
sourceIndexByFileKey.set(key, indexed.value);
|
|
713
1192
|
}
|
|
714
1193
|
const overridesByInternalIndex = new Map();
|
|
1194
|
+
const brandOptionalTypesByInternalIndex = new Map();
|
|
1195
|
+
const functionSignaturesByFacade = new Map();
|
|
715
1196
|
for (const m of modules) {
|
|
716
|
-
|
|
717
|
-
if (exportedClasses.length === 0)
|
|
1197
|
+
if (m.filePath.startsWith("__tsonic/"))
|
|
718
1198
|
continue;
|
|
1199
|
+
const exportedClasses = m.body.filter((s) => s.kind === "classDeclaration" && s.isExported);
|
|
1200
|
+
const exportedInterfaces = m.body.filter((s) => s.kind === "interfaceDeclaration" && s.isExported);
|
|
1201
|
+
const exportedAliases = m.body.filter((s) => s.kind === "typeAliasDeclaration" && s.isExported);
|
|
719
1202
|
const moduleKey = normalizeModuleFileKey(m.filePath);
|
|
720
1203
|
const sourceIndex = sourceIndexByFileKey.get(moduleKey);
|
|
721
1204
|
if (!sourceIndex)
|
|
722
1205
|
continue;
|
|
1206
|
+
const hasExportedSourceFunctions = sourceIndex.exportedFunctionSignaturesByName.size > 0;
|
|
1207
|
+
if (exportedClasses.length === 0 &&
|
|
1208
|
+
exportedInterfaces.length === 0 &&
|
|
1209
|
+
exportedAliases.length === 0 &&
|
|
1210
|
+
!hasExportedSourceFunctions)
|
|
1211
|
+
continue;
|
|
723
1212
|
const info = facadesByNamespace.get(m.namespace) ?? ensureFacade(m.namespace);
|
|
1213
|
+
for (const [name, signatures,] of sourceIndex.exportedFunctionSignaturesByName) {
|
|
1214
|
+
if (signatures.length === 0)
|
|
1215
|
+
continue;
|
|
1216
|
+
const byName = functionSignaturesByFacade.get(info.facadeDtsPath) ??
|
|
1217
|
+
new Map();
|
|
1218
|
+
const list = byName.get(name) ?? [];
|
|
1219
|
+
list.push(...signatures);
|
|
1220
|
+
byName.set(name, list);
|
|
1221
|
+
functionSignaturesByFacade.set(info.facadeDtsPath, byName);
|
|
1222
|
+
}
|
|
724
1223
|
for (const cls of exportedClasses) {
|
|
725
1224
|
const memberTypes = sourceIndex.memberTypesByClassAndMember.get(cls.name);
|
|
726
1225
|
if (!memberTypes)
|
|
@@ -732,25 +1231,115 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
732
1231
|
continue;
|
|
733
1232
|
if (member.accessibility === "private")
|
|
734
1233
|
continue;
|
|
735
|
-
const
|
|
736
|
-
if (!
|
|
1234
|
+
const sourceMember = memberTypes.get(member.name);
|
|
1235
|
+
if (!sourceMember)
|
|
737
1236
|
continue;
|
|
738
1237
|
const wrappersResult = collectExtensionWrapperImportsFromSourceType({
|
|
739
1238
|
startModuleKey: moduleKey,
|
|
740
|
-
typeNode:
|
|
1239
|
+
typeNode: sourceMember.typeNode,
|
|
741
1240
|
sourceIndexByFileKey,
|
|
742
1241
|
modulesByFileKey: modulesByFile,
|
|
743
1242
|
});
|
|
744
1243
|
if (!wrappersResult.ok)
|
|
745
1244
|
return wrappersResult;
|
|
746
1245
|
const wrappers = wrappersResult.value;
|
|
747
|
-
if (wrappers.length === 0)
|
|
1246
|
+
if (wrappers.length === 0 && !sourceMember.isOptional)
|
|
748
1247
|
continue;
|
|
749
1248
|
const list = overridesByInternalIndex.get(info.internalIndexDtsPath) ?? [];
|
|
750
1249
|
list.push({
|
|
751
1250
|
namespace: m.namespace,
|
|
752
1251
|
className: cls.name,
|
|
753
1252
|
memberName: member.name,
|
|
1253
|
+
isOptional: sourceMember.isOptional,
|
|
1254
|
+
wrappers,
|
|
1255
|
+
});
|
|
1256
|
+
overridesByInternalIndex.set(info.internalIndexDtsPath, list);
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
for (const iface of exportedInterfaces) {
|
|
1260
|
+
const memberTypes = sourceIndex.memberTypesByClassAndMember.get(iface.name);
|
|
1261
|
+
if (!memberTypes)
|
|
1262
|
+
continue;
|
|
1263
|
+
for (const member of iface.members) {
|
|
1264
|
+
if (member.kind !== "propertySignature")
|
|
1265
|
+
continue;
|
|
1266
|
+
const sourceMember = memberTypes.get(member.name);
|
|
1267
|
+
if (!sourceMember)
|
|
1268
|
+
continue;
|
|
1269
|
+
const wrappersResult = collectExtensionWrapperImportsFromSourceType({
|
|
1270
|
+
startModuleKey: moduleKey,
|
|
1271
|
+
typeNode: sourceMember.typeNode,
|
|
1272
|
+
sourceIndexByFileKey,
|
|
1273
|
+
modulesByFileKey: modulesByFile,
|
|
1274
|
+
});
|
|
1275
|
+
if (!wrappersResult.ok)
|
|
1276
|
+
return wrappersResult;
|
|
1277
|
+
const wrappers = wrappersResult.value;
|
|
1278
|
+
const canUseSourceTypeText = !typeNodeUsesImportedTypeNames(sourceMember.typeNode, sourceIndex.typeImportsByLocalName);
|
|
1279
|
+
if (!canUseSourceTypeText && wrappers.length === 0)
|
|
1280
|
+
continue;
|
|
1281
|
+
const list = overridesByInternalIndex.get(info.internalIndexDtsPath) ?? [];
|
|
1282
|
+
list.push({
|
|
1283
|
+
namespace: m.namespace,
|
|
1284
|
+
className: iface.name,
|
|
1285
|
+
memberName: member.name,
|
|
1286
|
+
sourceTypeText: canUseSourceTypeText
|
|
1287
|
+
? sourceMember.typeText
|
|
1288
|
+
: undefined,
|
|
1289
|
+
replaceWithSourceType: canUseSourceTypeText,
|
|
1290
|
+
isOptional: sourceMember.isOptional,
|
|
1291
|
+
wrappers,
|
|
1292
|
+
});
|
|
1293
|
+
overridesByInternalIndex.set(info.internalIndexDtsPath, list);
|
|
1294
|
+
}
|
|
1295
|
+
const brandTargets = brandOptionalTypesByInternalIndex.get(info.internalIndexDtsPath) ??
|
|
1296
|
+
new Set();
|
|
1297
|
+
brandTargets.add(iface.name);
|
|
1298
|
+
brandOptionalTypesByInternalIndex.set(info.internalIndexDtsPath, brandTargets);
|
|
1299
|
+
}
|
|
1300
|
+
for (const alias of exportedAliases) {
|
|
1301
|
+
const sourceAlias = sourceIndex.typeAliasesByName.get(alias.name);
|
|
1302
|
+
if (!sourceAlias)
|
|
1303
|
+
continue;
|
|
1304
|
+
const aliasType = unwrapParens(sourceAlias.type);
|
|
1305
|
+
if (!ts.isTypeLiteralNode(aliasType))
|
|
1306
|
+
continue;
|
|
1307
|
+
const arity = sourceAlias.typeParameters.length;
|
|
1308
|
+
const internalAliasName = `${alias.name}__Alias${arity > 0 ? `_${arity}` : ""}`;
|
|
1309
|
+
const brandTargets = brandOptionalTypesByInternalIndex.get(info.internalIndexDtsPath) ??
|
|
1310
|
+
new Set();
|
|
1311
|
+
brandTargets.add(internalAliasName);
|
|
1312
|
+
brandOptionalTypesByInternalIndex.set(info.internalIndexDtsPath, brandTargets);
|
|
1313
|
+
for (const member of aliasType.members) {
|
|
1314
|
+
if (!ts.isPropertySignature(member))
|
|
1315
|
+
continue;
|
|
1316
|
+
if (!member.name || !member.type)
|
|
1317
|
+
continue;
|
|
1318
|
+
const memberName = getPropertyNameText(member.name);
|
|
1319
|
+
if (!memberName)
|
|
1320
|
+
continue;
|
|
1321
|
+
const wrappersResult = collectExtensionWrapperImportsFromSourceType({
|
|
1322
|
+
startModuleKey: moduleKey,
|
|
1323
|
+
typeNode: member.type,
|
|
1324
|
+
sourceIndexByFileKey,
|
|
1325
|
+
modulesByFileKey: modulesByFile,
|
|
1326
|
+
});
|
|
1327
|
+
if (!wrappersResult.ok)
|
|
1328
|
+
return wrappersResult;
|
|
1329
|
+
const wrappers = wrappersResult.value;
|
|
1330
|
+
const canUseSourceTypeText = !typeNodeUsesImportedTypeNames(member.type, sourceIndex.typeImportsByLocalName);
|
|
1331
|
+
if (!canUseSourceTypeText && wrappers.length === 0)
|
|
1332
|
+
continue;
|
|
1333
|
+
const list = overridesByInternalIndex.get(info.internalIndexDtsPath) ?? [];
|
|
1334
|
+
list.push({
|
|
1335
|
+
namespace: m.namespace,
|
|
1336
|
+
className: internalAliasName,
|
|
1337
|
+
memberName,
|
|
1338
|
+
sourceTypeText: canUseSourceTypeText
|
|
1339
|
+
? printTypeNodeText(member.type, member.getSourceFile())
|
|
1340
|
+
: undefined,
|
|
1341
|
+
replaceWithSourceType: canUseSourceTypeText,
|
|
1342
|
+
isOptional: member.questionToken !== undefined,
|
|
754
1343
|
wrappers,
|
|
755
1344
|
});
|
|
756
1345
|
overridesByInternalIndex.set(info.internalIndexDtsPath, list);
|
|
@@ -762,6 +1351,16 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
762
1351
|
if (!result.ok)
|
|
763
1352
|
return result;
|
|
764
1353
|
}
|
|
1354
|
+
for (const [internalIndex, typeNames] of brandOptionalTypesByInternalIndex) {
|
|
1355
|
+
const result = patchInternalIndexBrandMarkersOptional(internalIndex, Array.from(typeNames.values()));
|
|
1356
|
+
if (!result.ok)
|
|
1357
|
+
return result;
|
|
1358
|
+
}
|
|
1359
|
+
for (const [facadePath, signaturesByName] of functionSignaturesByFacade) {
|
|
1360
|
+
const result = patchFacadeWithSourceFunctionSignatures(facadePath, signaturesByName);
|
|
1361
|
+
if (!result.ok)
|
|
1362
|
+
return result;
|
|
1363
|
+
}
|
|
765
1364
|
return { ok: true, value: undefined };
|
|
766
1365
|
};
|
|
767
1366
|
//# sourceMappingURL=library-bindings-augment.js.map
|