@tsonic/cli 0.0.61 → 0.0.62
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 +13 -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 -1
- package/dist/cli/parser.js.map +1 -1
- package/dist/cli/parser.test.js +9 -1
- 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 +37 -3
- 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 +24 -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 +16 -5
- package/dist/commands/add-package.js.map +1 -1
- package/dist/commands/build-library-bindings-aliases.test.js +196 -4
- package/dist/commands/build-library-bindings-aliases.test.js.map +1 -1
- package/dist/commands/build-native-lib.test.js +61 -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 +10 -0
- package/dist/commands/library-bindings-augment.d.ts.map +1 -1
- package/dist/commands/library-bindings-augment.js +632 -49
- package/dist/commands/library-bindings-augment.js.map +1 -1
- 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 +59 -15
- 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.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 +4 -4
- 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,239 @@ 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);
|
|
249
|
+
}
|
|
250
|
+
if (next !== original) {
|
|
251
|
+
writeFileSync(internalIndexDtsPath, next, "utf-8");
|
|
252
|
+
}
|
|
253
|
+
return { ok: true, value: undefined };
|
|
254
|
+
};
|
|
255
|
+
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*;/m;
|
|
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);
|
|
189
287
|
}
|
|
190
288
|
if (next !== original) {
|
|
191
289
|
writeFileSync(internalIndexDtsPath, next, "utf-8");
|
|
192
290
|
}
|
|
193
291
|
return { ok: true, value: undefined };
|
|
194
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
|
+
// eslint-disable-next-line no-constant-condition
|
|
335
|
+
while (true) {
|
|
336
|
+
unionPrefixRe.lastIndex = 0;
|
|
337
|
+
const prefixMatch = unionPrefixRe.exec(result);
|
|
338
|
+
if (!prefixMatch)
|
|
339
|
+
break;
|
|
340
|
+
// Find the matching > for the < at the end of the prefix
|
|
341
|
+
const openAngle = prefixMatch.index + prefixMatch[0].length - 1;
|
|
342
|
+
let depth = 1;
|
|
343
|
+
let closeAngle = -1;
|
|
344
|
+
for (let i = openAngle + 1; i < result.length; i += 1) {
|
|
345
|
+
const ch = result[i];
|
|
346
|
+
if (ch === "<")
|
|
347
|
+
depth += 1;
|
|
348
|
+
else if (ch === ">") {
|
|
349
|
+
depth -= 1;
|
|
350
|
+
if (depth === 0) {
|
|
351
|
+
closeAngle = i;
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (closeAngle < 0)
|
|
357
|
+
break;
|
|
358
|
+
const inner = result.slice(openAngle + 1, closeAngle);
|
|
359
|
+
const args = splitTopLevelTypeArgs(inner);
|
|
360
|
+
if (args.length < 2)
|
|
361
|
+
break;
|
|
362
|
+
const expanded = `(${args.join(" | ")})`;
|
|
363
|
+
result =
|
|
364
|
+
result.slice(0, prefixMatch.index) +
|
|
365
|
+
expanded +
|
|
366
|
+
result.slice(closeAngle + 1);
|
|
367
|
+
}
|
|
368
|
+
return result;
|
|
369
|
+
};
|
|
370
|
+
const patchFacadeWithSourceFunctionSignatures = (facadeDtsPath, signaturesByName) => {
|
|
371
|
+
if (!existsSync(facadeDtsPath)) {
|
|
372
|
+
return {
|
|
373
|
+
ok: false,
|
|
374
|
+
error: `Facade declaration file not found at ${facadeDtsPath}`,
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
const original = readFileSync(facadeDtsPath, "utf-8");
|
|
378
|
+
let next = original;
|
|
379
|
+
for (const [name, signatures] of Array.from(signaturesByName.entries()).sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
380
|
+
if (signatures.length === 0)
|
|
381
|
+
continue;
|
|
382
|
+
const fnRe = new RegExp(String.raw `^(export\s+declare\s+function\s+${escapeRegExp(name)}(?:<[\s\S]*?>)?\s*\([\s\S]*?\)\s*:\s*)([^;]+)(;)`, "m");
|
|
383
|
+
const currentMatch = fnRe.exec(next);
|
|
384
|
+
if (currentMatch) {
|
|
385
|
+
const existingReturnType = currentMatch[2]?.trim() ?? "";
|
|
386
|
+
const replacement = Array.from(new Set(signatures.map((sig) => {
|
|
387
|
+
const returnType = sig.returnTypeText.includes("{")
|
|
388
|
+
? expandUnionsDeep(existingReturnType)
|
|
389
|
+
: sig.returnTypeText;
|
|
390
|
+
return `export declare function ${name}${sig.typeParametersText}(${sig.parametersText}): ${returnType};`;
|
|
391
|
+
}))).join("\n");
|
|
392
|
+
next = next.replace(fnRe, replacement);
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
// If no function declaration match, try const Func<...> pattern
|
|
396
|
+
const constFuncRe = new RegExp(String.raw `^export\s+declare\s+const\s+${escapeRegExp(name)}\s*:\s*Func<([\s\S]+?)>\s*;`, "m");
|
|
397
|
+
const constMatch = constFuncRe.exec(next);
|
|
398
|
+
if (!constMatch || !constMatch[1])
|
|
399
|
+
continue;
|
|
400
|
+
const funcTypeArgs = splitTopLevelTypeArgs(constMatch[1]);
|
|
401
|
+
if (funcTypeArgs.length < 2)
|
|
402
|
+
continue;
|
|
403
|
+
// Last arg = return type, remaining args = parameter types
|
|
404
|
+
const facadeParamTypes = funcTypeArgs.slice(0, -1);
|
|
405
|
+
const facadeReturnType = funcTypeArgs[funcTypeArgs.length - 1];
|
|
406
|
+
// Use the first source signature for parameter names
|
|
407
|
+
const sourceSig = signatures[0];
|
|
408
|
+
const sourceParams = sourceSig.parametersText
|
|
409
|
+
.split(",")
|
|
410
|
+
.map((p) => p.trim())
|
|
411
|
+
.filter((p) => p.length > 0);
|
|
412
|
+
// If count mismatch, skip patching for this declaration
|
|
413
|
+
if (sourceParams.length !== facadeParamTypes.length)
|
|
414
|
+
continue;
|
|
415
|
+
// Pair source parameter NAMES with facade parameter TYPES
|
|
416
|
+
const pairedParams = sourceParams.map((param, idx) => {
|
|
417
|
+
const colonIdx = param.indexOf(":");
|
|
418
|
+
const paramName = colonIdx >= 0 ? param.slice(0, colonIdx).trim() : param.trim();
|
|
419
|
+
return `${paramName}: ${facadeParamTypes[idx]}`;
|
|
420
|
+
});
|
|
421
|
+
const expandedReturnType = expandUnionsDeep(facadeReturnType);
|
|
422
|
+
const typeParamsText = sourceSig.typeParametersText;
|
|
423
|
+
const replacement = `export declare function ${name}${typeParamsText}(${pairedParams.join(", ")}): ${expandedReturnType};`;
|
|
424
|
+
next = next.replace(constFuncRe, replacement);
|
|
425
|
+
}
|
|
426
|
+
if (next !== original) {
|
|
427
|
+
writeFileSync(facadeDtsPath, next, "utf-8");
|
|
428
|
+
}
|
|
429
|
+
return { ok: true, value: undefined };
|
|
430
|
+
};
|
|
195
431
|
const classifyExportKind = (module, name) => {
|
|
196
432
|
const isNamed = (stmt) => typeof stmt.name === "string";
|
|
197
433
|
const findDecl = () => {
|
|
198
434
|
for (const stmt of module.body) {
|
|
199
|
-
if (!("isExported" in stmt) ||
|
|
435
|
+
if (!("isExported" in stmt) ||
|
|
436
|
+
stmt.isExported !== true)
|
|
200
437
|
continue;
|
|
201
438
|
if (isNamed(stmt) && stmt.name === name)
|
|
202
439
|
return stmt;
|
|
@@ -256,7 +493,10 @@ const resolveLocalModuleFile = (fromModule, fromFile, modulesByFile) => {
|
|
|
256
493
|
};
|
|
257
494
|
const buildModuleSourceIndex = (absoluteFilePath, fileKey) => {
|
|
258
495
|
if (!existsSync(absoluteFilePath)) {
|
|
259
|
-
return {
|
|
496
|
+
return {
|
|
497
|
+
ok: false,
|
|
498
|
+
error: `Failed to read source file for bindings augmentation: ${absoluteFilePath}`,
|
|
499
|
+
};
|
|
260
500
|
}
|
|
261
501
|
const content = readFileSync(absoluteFilePath, "utf-8");
|
|
262
502
|
const scriptKind = absoluteFilePath.endsWith(".tsx")
|
|
@@ -268,13 +508,26 @@ const buildModuleSourceIndex = (absoluteFilePath, fileKey) => {
|
|
|
268
508
|
const wrapperImportsByLocalName = new Map();
|
|
269
509
|
const typeImportsByLocalName = new Map();
|
|
270
510
|
const typeAliasesByName = new Map();
|
|
511
|
+
const exportedFunctionSignaturesByName = new Map();
|
|
271
512
|
const memberTypesByClassAndMember = new Map();
|
|
272
|
-
const
|
|
273
|
-
if (
|
|
274
|
-
return
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
513
|
+
const printTypeParametersText = (typeParameters) => {
|
|
514
|
+
if (!typeParameters || typeParameters.length === 0)
|
|
515
|
+
return "";
|
|
516
|
+
return `<${typeParameters.map((tp) => tp.getText(sourceFile)).join(", ")}>`;
|
|
517
|
+
};
|
|
518
|
+
const printParameterText = (param) => {
|
|
519
|
+
const rest = param.dotDotDotToken ? "..." : "";
|
|
520
|
+
const name = param.name.getText(sourceFile);
|
|
521
|
+
const optional = param.questionToken ? "?" : "";
|
|
522
|
+
const type = param.type
|
|
523
|
+
? printTypeNodeText(param.type, sourceFile)
|
|
524
|
+
: "unknown";
|
|
525
|
+
return `${rest}${name}${optional}: ${type}`;
|
|
526
|
+
};
|
|
527
|
+
const addExportedFunctionSignature = (name, sig) => {
|
|
528
|
+
const list = exportedFunctionSignaturesByName.get(name) ?? [];
|
|
529
|
+
list.push(sig);
|
|
530
|
+
exportedFunctionSignaturesByName.set(name, list);
|
|
278
531
|
};
|
|
279
532
|
for (const stmt of sourceFile.statements) {
|
|
280
533
|
if (ts.isImportDeclaration(stmt)) {
|
|
@@ -295,9 +548,15 @@ const buildModuleSourceIndex = (absoluteFilePath, fileKey) => {
|
|
|
295
548
|
const isTypeOnly = clause.isTypeOnly || spec.isTypeOnly;
|
|
296
549
|
if (!isTypeOnly)
|
|
297
550
|
continue;
|
|
298
|
-
typeImportsByLocalName.set(localName, {
|
|
551
|
+
typeImportsByLocalName.set(localName, {
|
|
552
|
+
source: moduleSpecifier,
|
|
553
|
+
importedName,
|
|
554
|
+
});
|
|
299
555
|
if (importedName === "ExtensionMethods") {
|
|
300
|
-
wrapperImportsByLocalName.set(localName, {
|
|
556
|
+
wrapperImportsByLocalName.set(localName, {
|
|
557
|
+
source: moduleSpecifier,
|
|
558
|
+
importedName,
|
|
559
|
+
});
|
|
301
560
|
}
|
|
302
561
|
}
|
|
303
562
|
continue;
|
|
@@ -308,31 +567,102 @@ const buildModuleSourceIndex = (absoluteFilePath, fileKey) => {
|
|
|
308
567
|
typeAliasesByName.set(aliasName, { typeParameters, type: stmt.type });
|
|
309
568
|
continue;
|
|
310
569
|
}
|
|
570
|
+
if (ts.isFunctionDeclaration(stmt)) {
|
|
571
|
+
const hasExport = stmt.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
|
|
572
|
+
if (!hasExport || !stmt.name || !stmt.type)
|
|
573
|
+
continue;
|
|
574
|
+
const parametersText = stmt.parameters.map(printParameterText).join(", ");
|
|
575
|
+
addExportedFunctionSignature(stmt.name.text, {
|
|
576
|
+
typeParametersText: printTypeParametersText(stmt.typeParameters),
|
|
577
|
+
parametersText,
|
|
578
|
+
returnTypeText: printTypeNodeText(stmt.type, sourceFile),
|
|
579
|
+
});
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
if (ts.isVariableStatement(stmt)) {
|
|
583
|
+
const hasExport = stmt.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
|
|
584
|
+
if (!hasExport)
|
|
585
|
+
continue;
|
|
586
|
+
for (const decl of stmt.declarationList.declarations) {
|
|
587
|
+
if (!ts.isIdentifier(decl.name))
|
|
588
|
+
continue;
|
|
589
|
+
const exportName = decl.name.text;
|
|
590
|
+
const init = decl.initializer;
|
|
591
|
+
if (!init)
|
|
592
|
+
continue;
|
|
593
|
+
if (ts.isArrowFunction(init) || ts.isFunctionExpression(init)) {
|
|
594
|
+
const returnType = init.type;
|
|
595
|
+
if (!returnType)
|
|
596
|
+
continue;
|
|
597
|
+
const parametersText = init.parameters
|
|
598
|
+
.map(printParameterText)
|
|
599
|
+
.join(", ");
|
|
600
|
+
addExportedFunctionSignature(exportName, {
|
|
601
|
+
typeParametersText: printTypeParametersText(init.typeParameters),
|
|
602
|
+
parametersText,
|
|
603
|
+
returnTypeText: printTypeNodeText(returnType, sourceFile),
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
311
609
|
if (ts.isClassDeclaration(stmt) && stmt.name) {
|
|
312
610
|
const className = stmt.name.text;
|
|
313
|
-
const members = memberTypesByClassAndMember.get(className) ??
|
|
611
|
+
const members = memberTypesByClassAndMember.get(className) ??
|
|
612
|
+
new Map();
|
|
314
613
|
for (const member of stmt.members) {
|
|
315
614
|
if (ts.isGetAccessorDeclaration(member)) {
|
|
316
615
|
if (!member.name || !member.type)
|
|
317
616
|
continue;
|
|
318
|
-
const name =
|
|
617
|
+
const name = getPropertyNameText(member.name);
|
|
319
618
|
if (!name)
|
|
320
619
|
continue;
|
|
321
|
-
members.set(name,
|
|
620
|
+
members.set(name, {
|
|
621
|
+
typeNode: member.type,
|
|
622
|
+
typeText: printTypeNodeText(member.type, sourceFile),
|
|
623
|
+
isOptional: false,
|
|
624
|
+
});
|
|
322
625
|
continue;
|
|
323
626
|
}
|
|
324
627
|
if (ts.isPropertyDeclaration(member)) {
|
|
325
628
|
if (!member.name || !member.type)
|
|
326
629
|
continue;
|
|
327
|
-
const name =
|
|
630
|
+
const name = getPropertyNameText(member.name);
|
|
328
631
|
if (!name)
|
|
329
632
|
continue;
|
|
330
|
-
members.set(name,
|
|
633
|
+
members.set(name, {
|
|
634
|
+
typeNode: member.type,
|
|
635
|
+
typeText: printTypeNodeText(member.type, sourceFile),
|
|
636
|
+
isOptional: member.questionToken !== undefined,
|
|
637
|
+
});
|
|
331
638
|
}
|
|
332
639
|
}
|
|
333
640
|
if (members.size > 0) {
|
|
334
641
|
memberTypesByClassAndMember.set(className, members);
|
|
335
642
|
}
|
|
643
|
+
continue;
|
|
644
|
+
}
|
|
645
|
+
if (ts.isInterfaceDeclaration(stmt)) {
|
|
646
|
+
const interfaceName = stmt.name.text;
|
|
647
|
+
const members = memberTypesByClassAndMember.get(interfaceName) ??
|
|
648
|
+
new Map();
|
|
649
|
+
for (const member of stmt.members) {
|
|
650
|
+
if (!ts.isPropertySignature(member))
|
|
651
|
+
continue;
|
|
652
|
+
if (!member.name || !member.type)
|
|
653
|
+
continue;
|
|
654
|
+
const name = getPropertyNameText(member.name);
|
|
655
|
+
if (!name)
|
|
656
|
+
continue;
|
|
657
|
+
members.set(name, {
|
|
658
|
+
typeNode: member.type,
|
|
659
|
+
typeText: printTypeNodeText(member.type, sourceFile),
|
|
660
|
+
isOptional: member.questionToken !== undefined,
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
if (members.size > 0) {
|
|
664
|
+
memberTypesByClassAndMember.set(interfaceName, members);
|
|
665
|
+
}
|
|
336
666
|
}
|
|
337
667
|
}
|
|
338
668
|
return {
|
|
@@ -342,10 +672,27 @@ const buildModuleSourceIndex = (absoluteFilePath, fileKey) => {
|
|
|
342
672
|
wrapperImportsByLocalName,
|
|
343
673
|
typeImportsByLocalName,
|
|
344
674
|
typeAliasesByName,
|
|
675
|
+
exportedFunctionSignaturesByName,
|
|
345
676
|
memberTypesByClassAndMember,
|
|
346
677
|
},
|
|
347
678
|
};
|
|
348
679
|
};
|
|
680
|
+
const typeNodeUsesImportedTypeNames = (node, typeImportsByLocalName) => {
|
|
681
|
+
let found = false;
|
|
682
|
+
const visit = (current) => {
|
|
683
|
+
if (found)
|
|
684
|
+
return;
|
|
685
|
+
if (ts.isTypeReferenceNode(current) && ts.isIdentifier(current.typeName)) {
|
|
686
|
+
if (typeImportsByLocalName.has(current.typeName.text)) {
|
|
687
|
+
found = true;
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
ts.forEachChild(current, visit);
|
|
692
|
+
};
|
|
693
|
+
visit(node);
|
|
694
|
+
return found;
|
|
695
|
+
};
|
|
349
696
|
const unwrapParens = (node) => {
|
|
350
697
|
let current = node;
|
|
351
698
|
while (ts.isParenthesizedTypeNode(current)) {
|
|
@@ -399,7 +746,8 @@ const collectExtensionWrapperImportsFromSourceType = (opts) => {
|
|
|
399
746
|
continue;
|
|
400
747
|
}
|
|
401
748
|
const imported = info.typeImportsByLocalName.get(ident);
|
|
402
|
-
if (imported &&
|
|
749
|
+
if (imported &&
|
|
750
|
+
(imported.source.startsWith(".") || imported.source.startsWith("/"))) {
|
|
403
751
|
const targetModule = resolveLocalModuleFile(imported.source, currentModuleKey, opts.modulesByFileKey);
|
|
404
752
|
if (targetModule) {
|
|
405
753
|
const targetKey = normalizeModuleFileKey(targetModule.filePath);
|
|
@@ -453,7 +801,9 @@ const printIrType = (type, ctx) => {
|
|
|
453
801
|
case "primitiveType":
|
|
454
802
|
return type.name;
|
|
455
803
|
case "literalType":
|
|
456
|
-
return typeof type.value === "string"
|
|
804
|
+
return typeof type.value === "string"
|
|
805
|
+
? JSON.stringify(type.value)
|
|
806
|
+
: String(type.value);
|
|
457
807
|
case "anyType":
|
|
458
808
|
return "any";
|
|
459
809
|
case "unknownType":
|
|
@@ -469,7 +819,9 @@ const printIrType = (type, ctx) => {
|
|
|
469
819
|
const args = type.typeArguments ?? [];
|
|
470
820
|
if (args.length === 0)
|
|
471
821
|
return base;
|
|
472
|
-
const rendered = args
|
|
822
|
+
const rendered = args
|
|
823
|
+
.map((a) => printIrType(a, { parentPrecedence: 0 }))
|
|
824
|
+
.join(", ");
|
|
473
825
|
return `${base}<${rendered}>`;
|
|
474
826
|
}
|
|
475
827
|
case "arrayType":
|
|
@@ -484,7 +836,9 @@ const printIrType = (type, ctx) => {
|
|
|
484
836
|
return p.pattern.name;
|
|
485
837
|
return `p${i + 1}`;
|
|
486
838
|
})();
|
|
487
|
-
const t = p.type
|
|
839
|
+
const t = p.type
|
|
840
|
+
? printIrType(p.type, { parentPrecedence: 0 })
|
|
841
|
+
: "unknown";
|
|
488
842
|
return `${name}: ${t}`;
|
|
489
843
|
})
|
|
490
844
|
.join(", ");
|
|
@@ -492,11 +846,15 @@ const printIrType = (type, ctx) => {
|
|
|
492
846
|
return wrap(`(${ps}) => ${ret}`, 2);
|
|
493
847
|
}
|
|
494
848
|
case "unionType": {
|
|
495
|
-
const rendered = type.types
|
|
849
|
+
const rendered = type.types
|
|
850
|
+
.map((t) => printIrType(t, { parentPrecedence: 0 }))
|
|
851
|
+
.join(" | ");
|
|
496
852
|
return wrap(rendered, 0);
|
|
497
853
|
}
|
|
498
854
|
case "intersectionType": {
|
|
499
|
-
const rendered = type.types
|
|
855
|
+
const rendered = type.types
|
|
856
|
+
.map((t) => printIrType(t, { parentPrecedence: 1 }))
|
|
857
|
+
.join(" & ");
|
|
500
858
|
return wrap(rendered, 1);
|
|
501
859
|
}
|
|
502
860
|
case "dictionaryType": {
|
|
@@ -504,10 +862,40 @@ const printIrType = (type, ctx) => {
|
|
|
504
862
|
const v = printIrType(type.valueType, { parentPrecedence: 0 });
|
|
505
863
|
return `Record<${k}, ${v}>`;
|
|
506
864
|
}
|
|
507
|
-
case "objectType":
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
865
|
+
case "objectType": {
|
|
866
|
+
if (type.members.length === 0)
|
|
867
|
+
return "{}";
|
|
868
|
+
const members = type.members
|
|
869
|
+
.map((member) => {
|
|
870
|
+
if (member.kind === "propertySignature") {
|
|
871
|
+
const readonly = member.isReadonly ? "readonly " : "";
|
|
872
|
+
const optional = member.isOptional ? "?" : "";
|
|
873
|
+
const memberType = printIrType(member.type, {
|
|
874
|
+
parentPrecedence: 0,
|
|
875
|
+
});
|
|
876
|
+
return `${readonly}${member.name}${optional}: ${memberType}`;
|
|
877
|
+
}
|
|
878
|
+
const typeParams = printTypeParameters(member.typeParameters);
|
|
879
|
+
const args = member.parameters
|
|
880
|
+
.map((p, i) => {
|
|
881
|
+
const name = p.pattern.kind === "identifierPattern"
|
|
882
|
+
? p.pattern.name
|
|
883
|
+
: `p${i + 1}`;
|
|
884
|
+
const optional = p.isOptional ? "?" : "";
|
|
885
|
+
const paramType = p.type
|
|
886
|
+
? printIrType(p.type, { parentPrecedence: 0 })
|
|
887
|
+
: "unknown";
|
|
888
|
+
return `${name}${optional}: ${paramType}`;
|
|
889
|
+
})
|
|
890
|
+
.join(", ");
|
|
891
|
+
const returnType = member.returnType
|
|
892
|
+
? printIrType(member.returnType, { parentPrecedence: 0 })
|
|
893
|
+
: "void";
|
|
894
|
+
return `${member.name}${typeParams}(${args}): ${returnType}`;
|
|
895
|
+
})
|
|
896
|
+
.join("; ");
|
|
897
|
+
return `{ ${members} }`;
|
|
898
|
+
}
|
|
511
899
|
default:
|
|
512
900
|
// Exhaustiveness safety
|
|
513
901
|
return "unknown";
|
|
@@ -529,11 +917,74 @@ const renderExportedTypeAlias = (stmt, internalIndexDts) => {
|
|
|
529
917
|
const typeArgs = stmt.typeParameters && stmt.typeParameters.length > 0
|
|
530
918
|
? `<${stmt.typeParameters.map((tp) => tp.name).join(", ")}>`
|
|
531
919
|
: "";
|
|
532
|
-
return {
|
|
920
|
+
return {
|
|
921
|
+
ok: true,
|
|
922
|
+
value: `export type ${stmt.name}${typeParams} = Internal.${internalName}${typeArgs};`,
|
|
923
|
+
};
|
|
533
924
|
}
|
|
534
925
|
const rhs = printIrType(stmt.type, { parentPrecedence: 0 });
|
|
535
926
|
return { ok: true, value: `export type ${stmt.name}${typeParams} = ${rhs};` };
|
|
536
927
|
};
|
|
928
|
+
/**
|
|
929
|
+
* Overlay already-augmented bindings from dependency assemblies.
|
|
930
|
+
*
|
|
931
|
+
* When a library references sibling Tsonic-built libraries (via references.libraries),
|
|
932
|
+
* tsbindgen regenerates types from CLR metadata, losing source-level augmentation
|
|
933
|
+
* (literal types, optional markers, brand optionality, etc.). The dependency's published
|
|
934
|
+
* bindings already have this augmentation applied, so we overlay them onto the
|
|
935
|
+
* freshly-generated copies.
|
|
936
|
+
*/
|
|
937
|
+
export const overlayDependencyBindings = (config, bindingsOutDir) => {
|
|
938
|
+
const depBindingsDirByAssembly = new Map();
|
|
939
|
+
for (const lib of config.libraries) {
|
|
940
|
+
if (!lib.toLowerCase().endsWith(".dll"))
|
|
941
|
+
continue;
|
|
942
|
+
const assemblyName = basename(lib, ".dll");
|
|
943
|
+
// Skip the current package's own assembly.
|
|
944
|
+
if (assemblyName === config.outputName)
|
|
945
|
+
continue;
|
|
946
|
+
// Convention: DLL at <project>/dist/<dotnetVersion>/<name>.dll,
|
|
947
|
+
// bindings at <project>/dist/tsonic/bindings/.
|
|
948
|
+
const depBindingsDir = join(dirname(dirname(lib)), "tsonic", "bindings");
|
|
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
|
+
};
|
|
537
988
|
export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
538
989
|
const entryPoint = config.entryPoint;
|
|
539
990
|
if (!entryPoint) {
|
|
@@ -552,7 +1003,10 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
552
1003
|
};
|
|
553
1004
|
const graphResult = buildModuleDependencyGraph(absoluteEntryPoint, compilerOptions);
|
|
554
1005
|
if (!graphResult.ok) {
|
|
555
|
-
return {
|
|
1006
|
+
return {
|
|
1007
|
+
ok: false,
|
|
1008
|
+
error: `Failed to analyze library sources:\n${renderDiagnostics(graphResult.error)}`,
|
|
1009
|
+
};
|
|
556
1010
|
}
|
|
557
1011
|
const { modules, entryModule } = graphResult.value;
|
|
558
1012
|
const facadesByNamespace = new Map(indexFacadeFiles(bindingsOutDir));
|
|
@@ -661,8 +1115,10 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
661
1115
|
`Could not find an exported declaration named '${exp.originalName}' in ${targetModule.filePath}.`,
|
|
662
1116
|
};
|
|
663
1117
|
}
|
|
1118
|
+
const spec = exp.name === exp.originalName
|
|
1119
|
+
? exp.name
|
|
1120
|
+
: `${exp.originalName} as ${exp.name}`;
|
|
664
1121
|
const isTypeOnly = kind === "type";
|
|
665
|
-
const spec = exp.name === exp.originalName ? exp.name : `${exp.originalName} as ${exp.name}`;
|
|
666
1122
|
const key = `${targetFacade.moduleSpecifier}|${isTypeOnly ? "type" : "value"}`;
|
|
667
1123
|
const list = grouped.get(key) ?? [];
|
|
668
1124
|
list.push(spec);
|
|
@@ -697,13 +1153,20 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
697
1153
|
writeFileSync(entryFacade.facadeJsPath, next, "utf-8");
|
|
698
1154
|
}
|
|
699
1155
|
}
|
|
700
|
-
// 3) Preserve TS-authored
|
|
1156
|
+
// 3) Preserve TS-authored member typing semantics on exported class/interface members.
|
|
701
1157
|
// These wrapper types (ExtensionMethods<...>) do not exist in CLR metadata and therefore
|
|
702
1158
|
// cannot be discovered by tsbindgen when generating bindings from the DLL. We can,
|
|
703
1159
|
// however, safely re-apply them for Tsonic-authored libraries by reading the TS source
|
|
704
|
-
// graph and
|
|
1160
|
+
// graph and patching the published internal/index.d.ts:
|
|
1161
|
+
// - Re-apply ExtensionMethods wrappers (class + interface members)
|
|
1162
|
+
// - Preserve optional (`?`) semantics by allowing `undefined` on patched members
|
|
1163
|
+
// - For exported interfaces, preserve source structural member types when safe
|
|
705
1164
|
const sourceIndexByFileKey = new Map();
|
|
706
1165
|
for (const m of modules) {
|
|
1166
|
+
// Synthetic IR modules (e.g., program-wide anonymous type declarations) do not
|
|
1167
|
+
// correspond to real source files and must be ignored by source-based augmentation.
|
|
1168
|
+
if (m.filePath.startsWith("__tsonic/"))
|
|
1169
|
+
continue;
|
|
707
1170
|
const key = normalizeModuleFileKey(m.filePath);
|
|
708
1171
|
const absolutePath = resolve(absoluteSourceRoot, key);
|
|
709
1172
|
const indexed = buildModuleSourceIndex(absolutePath, key);
|
|
@@ -712,15 +1175,35 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
712
1175
|
sourceIndexByFileKey.set(key, indexed.value);
|
|
713
1176
|
}
|
|
714
1177
|
const overridesByInternalIndex = new Map();
|
|
1178
|
+
const brandOptionalTypesByInternalIndex = new Map();
|
|
1179
|
+
const functionSignaturesByFacade = new Map();
|
|
715
1180
|
for (const m of modules) {
|
|
716
|
-
|
|
717
|
-
if (exportedClasses.length === 0)
|
|
1181
|
+
if (m.filePath.startsWith("__tsonic/"))
|
|
718
1182
|
continue;
|
|
1183
|
+
const exportedClasses = m.body.filter((s) => s.kind === "classDeclaration" && s.isExported);
|
|
1184
|
+
const exportedInterfaces = m.body.filter((s) => s.kind === "interfaceDeclaration" && s.isExported);
|
|
1185
|
+
const exportedAliases = m.body.filter((s) => s.kind === "typeAliasDeclaration" && s.isExported);
|
|
719
1186
|
const moduleKey = normalizeModuleFileKey(m.filePath);
|
|
720
1187
|
const sourceIndex = sourceIndexByFileKey.get(moduleKey);
|
|
721
1188
|
if (!sourceIndex)
|
|
722
1189
|
continue;
|
|
1190
|
+
const hasExportedSourceFunctions = sourceIndex.exportedFunctionSignaturesByName.size > 0;
|
|
1191
|
+
if (exportedClasses.length === 0 &&
|
|
1192
|
+
exportedInterfaces.length === 0 &&
|
|
1193
|
+
exportedAliases.length === 0 &&
|
|
1194
|
+
!hasExportedSourceFunctions)
|
|
1195
|
+
continue;
|
|
723
1196
|
const info = facadesByNamespace.get(m.namespace) ?? ensureFacade(m.namespace);
|
|
1197
|
+
for (const [name, signatures,] of sourceIndex.exportedFunctionSignaturesByName) {
|
|
1198
|
+
if (signatures.length === 0)
|
|
1199
|
+
continue;
|
|
1200
|
+
const byName = functionSignaturesByFacade.get(info.facadeDtsPath) ??
|
|
1201
|
+
new Map();
|
|
1202
|
+
const list = byName.get(name) ?? [];
|
|
1203
|
+
list.push(...signatures);
|
|
1204
|
+
byName.set(name, list);
|
|
1205
|
+
functionSignaturesByFacade.set(info.facadeDtsPath, byName);
|
|
1206
|
+
}
|
|
724
1207
|
for (const cls of exportedClasses) {
|
|
725
1208
|
const memberTypes = sourceIndex.memberTypesByClassAndMember.get(cls.name);
|
|
726
1209
|
if (!memberTypes)
|
|
@@ -732,25 +1215,115 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
732
1215
|
continue;
|
|
733
1216
|
if (member.accessibility === "private")
|
|
734
1217
|
continue;
|
|
735
|
-
const
|
|
736
|
-
if (!
|
|
1218
|
+
const sourceMember = memberTypes.get(member.name);
|
|
1219
|
+
if (!sourceMember)
|
|
737
1220
|
continue;
|
|
738
1221
|
const wrappersResult = collectExtensionWrapperImportsFromSourceType({
|
|
739
1222
|
startModuleKey: moduleKey,
|
|
740
|
-
typeNode:
|
|
1223
|
+
typeNode: sourceMember.typeNode,
|
|
741
1224
|
sourceIndexByFileKey,
|
|
742
1225
|
modulesByFileKey: modulesByFile,
|
|
743
1226
|
});
|
|
744
1227
|
if (!wrappersResult.ok)
|
|
745
1228
|
return wrappersResult;
|
|
746
1229
|
const wrappers = wrappersResult.value;
|
|
747
|
-
if (wrappers.length === 0)
|
|
1230
|
+
if (wrappers.length === 0 && !sourceMember.isOptional)
|
|
748
1231
|
continue;
|
|
749
1232
|
const list = overridesByInternalIndex.get(info.internalIndexDtsPath) ?? [];
|
|
750
1233
|
list.push({
|
|
751
1234
|
namespace: m.namespace,
|
|
752
1235
|
className: cls.name,
|
|
753
1236
|
memberName: member.name,
|
|
1237
|
+
isOptional: sourceMember.isOptional,
|
|
1238
|
+
wrappers,
|
|
1239
|
+
});
|
|
1240
|
+
overridesByInternalIndex.set(info.internalIndexDtsPath, list);
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
for (const iface of exportedInterfaces) {
|
|
1244
|
+
const memberTypes = sourceIndex.memberTypesByClassAndMember.get(iface.name);
|
|
1245
|
+
if (!memberTypes)
|
|
1246
|
+
continue;
|
|
1247
|
+
for (const member of iface.members) {
|
|
1248
|
+
if (member.kind !== "propertySignature")
|
|
1249
|
+
continue;
|
|
1250
|
+
const sourceMember = memberTypes.get(member.name);
|
|
1251
|
+
if (!sourceMember)
|
|
1252
|
+
continue;
|
|
1253
|
+
const wrappersResult = collectExtensionWrapperImportsFromSourceType({
|
|
1254
|
+
startModuleKey: moduleKey,
|
|
1255
|
+
typeNode: sourceMember.typeNode,
|
|
1256
|
+
sourceIndexByFileKey,
|
|
1257
|
+
modulesByFileKey: modulesByFile,
|
|
1258
|
+
});
|
|
1259
|
+
if (!wrappersResult.ok)
|
|
1260
|
+
return wrappersResult;
|
|
1261
|
+
const wrappers = wrappersResult.value;
|
|
1262
|
+
const canUseSourceTypeText = !typeNodeUsesImportedTypeNames(sourceMember.typeNode, sourceIndex.typeImportsByLocalName);
|
|
1263
|
+
if (!canUseSourceTypeText && wrappers.length === 0)
|
|
1264
|
+
continue;
|
|
1265
|
+
const list = overridesByInternalIndex.get(info.internalIndexDtsPath) ?? [];
|
|
1266
|
+
list.push({
|
|
1267
|
+
namespace: m.namespace,
|
|
1268
|
+
className: iface.name,
|
|
1269
|
+
memberName: member.name,
|
|
1270
|
+
sourceTypeText: canUseSourceTypeText
|
|
1271
|
+
? sourceMember.typeText
|
|
1272
|
+
: undefined,
|
|
1273
|
+
replaceWithSourceType: canUseSourceTypeText,
|
|
1274
|
+
isOptional: sourceMember.isOptional,
|
|
1275
|
+
wrappers,
|
|
1276
|
+
});
|
|
1277
|
+
overridesByInternalIndex.set(info.internalIndexDtsPath, list);
|
|
1278
|
+
}
|
|
1279
|
+
const brandTargets = brandOptionalTypesByInternalIndex.get(info.internalIndexDtsPath) ??
|
|
1280
|
+
new Set();
|
|
1281
|
+
brandTargets.add(iface.name);
|
|
1282
|
+
brandOptionalTypesByInternalIndex.set(info.internalIndexDtsPath, brandTargets);
|
|
1283
|
+
}
|
|
1284
|
+
for (const alias of exportedAliases) {
|
|
1285
|
+
const sourceAlias = sourceIndex.typeAliasesByName.get(alias.name);
|
|
1286
|
+
if (!sourceAlias)
|
|
1287
|
+
continue;
|
|
1288
|
+
const aliasType = unwrapParens(sourceAlias.type);
|
|
1289
|
+
if (!ts.isTypeLiteralNode(aliasType))
|
|
1290
|
+
continue;
|
|
1291
|
+
const arity = sourceAlias.typeParameters.length;
|
|
1292
|
+
const internalAliasName = `${alias.name}__Alias${arity > 0 ? `_${arity}` : ""}`;
|
|
1293
|
+
const brandTargets = brandOptionalTypesByInternalIndex.get(info.internalIndexDtsPath) ??
|
|
1294
|
+
new Set();
|
|
1295
|
+
brandTargets.add(internalAliasName);
|
|
1296
|
+
brandOptionalTypesByInternalIndex.set(info.internalIndexDtsPath, brandTargets);
|
|
1297
|
+
for (const member of aliasType.members) {
|
|
1298
|
+
if (!ts.isPropertySignature(member))
|
|
1299
|
+
continue;
|
|
1300
|
+
if (!member.name || !member.type)
|
|
1301
|
+
continue;
|
|
1302
|
+
const memberName = getPropertyNameText(member.name);
|
|
1303
|
+
if (!memberName)
|
|
1304
|
+
continue;
|
|
1305
|
+
const wrappersResult = collectExtensionWrapperImportsFromSourceType({
|
|
1306
|
+
startModuleKey: moduleKey,
|
|
1307
|
+
typeNode: member.type,
|
|
1308
|
+
sourceIndexByFileKey,
|
|
1309
|
+
modulesByFileKey: modulesByFile,
|
|
1310
|
+
});
|
|
1311
|
+
if (!wrappersResult.ok)
|
|
1312
|
+
return wrappersResult;
|
|
1313
|
+
const wrappers = wrappersResult.value;
|
|
1314
|
+
const canUseSourceTypeText = !typeNodeUsesImportedTypeNames(member.type, sourceIndex.typeImportsByLocalName);
|
|
1315
|
+
if (!canUseSourceTypeText && wrappers.length === 0)
|
|
1316
|
+
continue;
|
|
1317
|
+
const list = overridesByInternalIndex.get(info.internalIndexDtsPath) ?? [];
|
|
1318
|
+
list.push({
|
|
1319
|
+
namespace: m.namespace,
|
|
1320
|
+
className: internalAliasName,
|
|
1321
|
+
memberName,
|
|
1322
|
+
sourceTypeText: canUseSourceTypeText
|
|
1323
|
+
? printTypeNodeText(member.type, member.getSourceFile())
|
|
1324
|
+
: undefined,
|
|
1325
|
+
replaceWithSourceType: canUseSourceTypeText,
|
|
1326
|
+
isOptional: member.questionToken !== undefined,
|
|
754
1327
|
wrappers,
|
|
755
1328
|
});
|
|
756
1329
|
overridesByInternalIndex.set(info.internalIndexDtsPath, list);
|
|
@@ -762,6 +1335,16 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
762
1335
|
if (!result.ok)
|
|
763
1336
|
return result;
|
|
764
1337
|
}
|
|
1338
|
+
for (const [internalIndex, typeNames] of brandOptionalTypesByInternalIndex) {
|
|
1339
|
+
const result = patchInternalIndexBrandMarkersOptional(internalIndex, Array.from(typeNames.values()));
|
|
1340
|
+
if (!result.ok)
|
|
1341
|
+
return result;
|
|
1342
|
+
}
|
|
1343
|
+
for (const [facadePath, signaturesByName] of functionSignaturesByFacade) {
|
|
1344
|
+
const result = patchFacadeWithSourceFunctionSignatures(facadePath, signaturesByName);
|
|
1345
|
+
if (!result.ok)
|
|
1346
|
+
return result;
|
|
1347
|
+
}
|
|
765
1348
|
return { ok: true, value: undefined };
|
|
766
1349
|
};
|
|
767
1350
|
//# sourceMappingURL=library-bindings-augment.js.map
|