@tsonic/cli 0.0.63 → 0.0.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/aikya/bindings.d.ts +44 -0
- package/dist/aikya/bindings.d.ts.map +1 -0
- package/dist/aikya/bindings.js +777 -0
- package/dist/aikya/bindings.js.map +1 -0
- package/dist/aikya/bindings.test.d.ts +2 -0
- package/dist/aikya/bindings.test.d.ts.map +1 -0
- package/dist/aikya/bindings.test.js +554 -0
- package/dist/aikya/bindings.test.js.map +1 -0
- package/dist/cli/dispatcher.d.ts.map +1 -1
- package/dist/cli/dispatcher.js +15 -0
- package/dist/cli/dispatcher.js.map +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +4 -1
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/parser.d.ts.map +1 -1
- package/dist/cli/parser.js +6 -0
- package/dist/cli/parser.js.map +1 -1
- package/dist/cli/parser.test.js +10 -0
- package/dist/cli/parser.test.js.map +1 -1
- package/dist/commands/add-npm.d.ts +3 -2
- package/dist/commands/add-npm.d.ts.map +1 -1
- package/dist/commands/add-npm.js +69 -177
- package/dist/commands/add-npm.js.map +1 -1
- package/dist/commands/add-npm.test.js +276 -2
- package/dist/commands/add-npm.test.js.map +1 -1
- package/dist/commands/build-library-bindings-aliases.test.js +349 -4
- package/dist/commands/build-library-bindings-aliases.test.js.map +1 -1
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +168 -148
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/build.test.js +22 -3
- package/dist/commands/build.test.js.map +1 -1
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +5 -0
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/init.d.ts +5 -2
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +42 -7
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/init.test.js +82 -2
- package/dist/commands/init.test.js.map +1 -1
- package/dist/commands/library-bindings-augment.d.ts.map +1 -1
- package/dist/commands/library-bindings-augment.js +376 -53
- package/dist/commands/library-bindings-augment.js.map +1 -1
- package/dist/commands/library-bindings-augment.test.js +460 -2
- package/dist/commands/library-bindings-augment.test.js.map +1 -1
- package/dist/commands/library-bindings-firstparty-regressions.test.d.ts +2 -0
- package/dist/commands/library-bindings-firstparty-regressions.test.d.ts.map +1 -0
- package/dist/commands/library-bindings-firstparty-regressions.test.js +217 -0
- package/dist/commands/library-bindings-firstparty-regressions.test.js.map +1 -0
- package/dist/commands/library-bindings-firstparty.d.ts +3 -0
- package/dist/commands/library-bindings-firstparty.d.ts.map +1 -0
- package/dist/commands/library-bindings-firstparty.js +2250 -0
- package/dist/commands/library-bindings-firstparty.js.map +1 -0
- package/dist/commands/restore.d.ts.map +1 -1
- package/dist/commands/restore.js +3 -1
- package/dist/commands/restore.js.map +1 -1
- package/dist/commands/restore.test.js +29 -0
- package/dist/commands/restore.test.js.map +1 -1
- package/dist/commands/run-build-regressions.test.js +72 -0
- package/dist/commands/run-build-regressions.test.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +16 -2
- package/dist/config.js.map +1 -1
- package/dist/config.test.js +57 -0
- package/dist/config.test.js.map +1 -1
- package/dist/dotnet/runtime-dlls.d.ts +1 -0
- package/dist/dotnet/runtime-dlls.d.ts.map +1 -1
- package/dist/dotnet/runtime-dlls.js +1 -0
- package/dist/dotnet/runtime-dlls.js.map +1 -1
- package/dist/surface/profiles.d.ts +10 -0
- package/dist/surface/profiles.d.ts.map +1 -0
- package/dist/surface/profiles.js +61 -0
- package/dist/surface/profiles.js.map +1 -0
- package/dist/surface/profiles.test.d.ts +2 -0
- package/dist/surface/profiles.test.d.ts.map +1 -0
- package/dist/surface/profiles.test.js +49 -0
- package/dist/surface/profiles.test.js.map +1 -0
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { copyFileSync, existsSync, readFileSync, readdirSync, writeFileSync, } from "node:fs";
|
|
2
2
|
import { basename, dirname, join, resolve, posix } from "node:path";
|
|
3
3
|
import { buildModuleDependencyGraph, } from "@tsonic/frontend";
|
|
4
|
+
import { resolveSurfaceCapabilities } from "../surface/profiles.js";
|
|
4
5
|
import * as ts from "typescript";
|
|
5
6
|
const renderDiagnostics = (diags) => {
|
|
6
7
|
return diags
|
|
@@ -21,6 +22,10 @@ const printTypeNodeText = (node, sourceFile) => {
|
|
|
21
22
|
.printNode(ts.EmitHint.Unspecified, node, sourceFile)
|
|
22
23
|
.trim();
|
|
23
24
|
};
|
|
25
|
+
const textContainsIdentifier = (text, identifier) => {
|
|
26
|
+
const pattern = new RegExp(String.raw `\b${escapeRegExp(identifier)}\b`);
|
|
27
|
+
return pattern.test(text);
|
|
28
|
+
};
|
|
24
29
|
const ensureUndefinedInType = (typeText) => {
|
|
25
30
|
const trimmed = typeText.trim();
|
|
26
31
|
if (/\bundefined\b/.test(trimmed))
|
|
@@ -222,6 +227,36 @@ const patchInternalIndexWithMemberOverrides = (internalIndexDtsPath, overrides)
|
|
|
222
227
|
`This property was declared in TS source and should exist in CLR metadata.`,
|
|
223
228
|
};
|
|
224
229
|
}
|
|
230
|
+
if (o.isOptional &&
|
|
231
|
+
o.emitOptionalPropertySyntax &&
|
|
232
|
+
!o.memberName.startsWith("__tsonic_type_")) {
|
|
233
|
+
const baseType = (o.replaceWithSourceType ? o.sourceTypeText : undefined) ??
|
|
234
|
+
getterMatch?.[2] ??
|
|
235
|
+
setterMatch?.[2] ??
|
|
236
|
+
"";
|
|
237
|
+
const nextType = applyWrappersToBaseType(baseType, o.wrappers);
|
|
238
|
+
const matchForIndent = getterMatch ?? setterMatch;
|
|
239
|
+
const indent = matchForIndent?.[0].match(/^\s*/)?.[0] ?? " ";
|
|
240
|
+
const optionalPropertyLine = `${indent}${o.memberName}?: ${nextType};`;
|
|
241
|
+
if (getterMatch && setterMatch) {
|
|
242
|
+
const getterStart = getterMatch.index ?? 0;
|
|
243
|
+
const setterStart = setterMatch.index ?? 0;
|
|
244
|
+
const start = Math.min(getterStart, setterStart);
|
|
245
|
+
const getterEnd = getterStart + getterMatch[0].length;
|
|
246
|
+
const setterEnd = setterStart + setterMatch[0].length;
|
|
247
|
+
const end = Math.max(getterEnd, setterEnd);
|
|
248
|
+
body = body.slice(0, start) + optionalPropertyLine + body.slice(end);
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
if (getterMatch) {
|
|
252
|
+
body = body.replace(getterRe, optionalPropertyLine);
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
if (setterMatch) {
|
|
256
|
+
body = body.replace(setterRe, optionalPropertyLine);
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
225
260
|
if (getterMatch) {
|
|
226
261
|
const baseType = (o.replaceWithSourceType ? o.sourceTypeText : undefined) ??
|
|
227
262
|
getterMatch[2] ??
|
|
@@ -290,7 +325,7 @@ export const patchInternalIndexBrandMarkersOptional = (internalIndexDtsPath, typ
|
|
|
290
325
|
}
|
|
291
326
|
return { ok: true, value: undefined };
|
|
292
327
|
};
|
|
293
|
-
const
|
|
328
|
+
const splitTopLevelCommaSeparated = (text) => {
|
|
294
329
|
const parts = [];
|
|
295
330
|
let depthAngle = 0;
|
|
296
331
|
let depthParen = 0;
|
|
@@ -327,6 +362,9 @@ const splitTopLevelTypeArgs = (text) => {
|
|
|
327
362
|
parts.push(text.slice(start).trim());
|
|
328
363
|
return parts.filter((p) => p.length > 0);
|
|
329
364
|
};
|
|
365
|
+
const splitTopLevelTypeArgs = (text) => {
|
|
366
|
+
return splitTopLevelCommaSeparated(text);
|
|
367
|
+
};
|
|
330
368
|
const expandUnionsDeep = (typeText) => {
|
|
331
369
|
const unionPrefixRe = /Union_\d+</g;
|
|
332
370
|
let result = typeText;
|
|
@@ -366,6 +404,26 @@ const expandUnionsDeep = (typeText) => {
|
|
|
366
404
|
}
|
|
367
405
|
return result;
|
|
368
406
|
};
|
|
407
|
+
const collectSourceTypeImportsForSignature = (signature, typeImportsByLocalName) => {
|
|
408
|
+
const required = [];
|
|
409
|
+
for (const [localName, imported] of typeImportsByLocalName) {
|
|
410
|
+
const source = imported.source.trim();
|
|
411
|
+
// Relative source imports do not map to published facade paths.
|
|
412
|
+
if (source.startsWith(".") || source.startsWith("/"))
|
|
413
|
+
continue;
|
|
414
|
+
const appearsInSignature = textContainsIdentifier(signature.typeParametersText, localName) ||
|
|
415
|
+
textContainsIdentifier(signature.parametersText, localName) ||
|
|
416
|
+
textContainsIdentifier(signature.returnTypeText, localName);
|
|
417
|
+
if (!appearsInSignature)
|
|
418
|
+
continue;
|
|
419
|
+
required.push({
|
|
420
|
+
source,
|
|
421
|
+
importedName: imported.importedName,
|
|
422
|
+
localName,
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
return required.sort((a, b) => a.localName.localeCompare(b.localName));
|
|
426
|
+
};
|
|
369
427
|
const patchFacadeWithSourceFunctionSignatures = (facadeDtsPath, signaturesByName) => {
|
|
370
428
|
if (!existsSync(facadeDtsPath)) {
|
|
371
429
|
return {
|
|
@@ -383,47 +441,209 @@ const patchFacadeWithSourceFunctionSignatures = (facadeDtsPath, signaturesByName
|
|
|
383
441
|
if (currentMatch) {
|
|
384
442
|
const existingReturnType = currentMatch[2]?.trim() ?? "";
|
|
385
443
|
const replacement = Array.from(new Set(signatures.map((sig) => {
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
|
|
444
|
+
const existingIsUnknown = existingReturnType === "unknown";
|
|
445
|
+
const existingHasAnon = /__Anon_/.test(existingReturnType);
|
|
446
|
+
const existingHasGenericAritySuffix = /\b[A-Za-z_$][\w$]*_\d+\s*</.test(existingReturnType);
|
|
447
|
+
const returnType = (() => {
|
|
448
|
+
if (!sig.returnTypeText.includes("{")) {
|
|
449
|
+
return sig.returnTypeText;
|
|
450
|
+
}
|
|
451
|
+
if (existingIsUnknown) {
|
|
452
|
+
return sig.returnTypeText;
|
|
453
|
+
}
|
|
454
|
+
if (existingHasAnon) {
|
|
455
|
+
return expandUnionsDeep(existingReturnType);
|
|
456
|
+
}
|
|
457
|
+
if (existingHasGenericAritySuffix) {
|
|
458
|
+
return sig.returnTypeText;
|
|
459
|
+
}
|
|
460
|
+
return expandUnionsDeep(existingReturnType);
|
|
461
|
+
})();
|
|
389
462
|
return `export declare function ${name}${sig.typeParametersText}(${sig.parametersText}): ${returnType};`;
|
|
390
463
|
}))).join("\n");
|
|
391
464
|
next = next.replace(fnRe, replacement);
|
|
392
465
|
continue;
|
|
393
466
|
}
|
|
394
|
-
// If no function declaration match, try const
|
|
395
|
-
const
|
|
396
|
-
const constMatch =
|
|
467
|
+
// If no function declaration match, try const callable pattern.
|
|
468
|
+
const constDeclRe = new RegExp(String.raw `^export\s+declare\s+const\s+${escapeRegExp(name)}\s*:\s*([^;]+);`, "m");
|
|
469
|
+
const constMatch = constDeclRe.exec(next);
|
|
397
470
|
if (!constMatch || !constMatch[1])
|
|
398
471
|
continue;
|
|
399
|
-
const
|
|
400
|
-
|
|
472
|
+
const constTypeText = constMatch[1].trim();
|
|
473
|
+
let expectedParamCount;
|
|
474
|
+
let forcedReturnType;
|
|
475
|
+
const funcTypeMatch = /^Func<([\s\S]+)>$/.exec(constTypeText);
|
|
476
|
+
if (funcTypeMatch?.[1]) {
|
|
477
|
+
const funcTypeArgs = splitTopLevelTypeArgs(funcTypeMatch[1]);
|
|
478
|
+
if (funcTypeArgs.length < 2)
|
|
479
|
+
continue;
|
|
480
|
+
expectedParamCount = funcTypeArgs.length - 1;
|
|
481
|
+
const lastTypeArg = funcTypeArgs.at(-1);
|
|
482
|
+
if (!lastTypeArg)
|
|
483
|
+
continue;
|
|
484
|
+
forcedReturnType = expandUnionsDeep(lastTypeArg);
|
|
485
|
+
}
|
|
486
|
+
const replacement = Array.from(new Set(signatures
|
|
487
|
+
.filter((sig) => {
|
|
488
|
+
if (expectedParamCount === undefined)
|
|
489
|
+
return true;
|
|
490
|
+
const paramCount = splitTopLevelCommaSeparated(sig.parametersText).length;
|
|
491
|
+
return paramCount === expectedParamCount;
|
|
492
|
+
})
|
|
493
|
+
.map((sig) => {
|
|
494
|
+
const returnType = forcedReturnType ?? sig.returnTypeText;
|
|
495
|
+
return `export declare function ${name}${sig.typeParametersText}(${sig.parametersText}): ${returnType};`;
|
|
496
|
+
}))).join("\n");
|
|
497
|
+
if (replacement.length === 0)
|
|
401
498
|
continue;
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
499
|
+
next = next.replace(constDeclRe, replacement);
|
|
500
|
+
}
|
|
501
|
+
if (next !== original) {
|
|
502
|
+
writeFileSync(facadeDtsPath, next, "utf-8");
|
|
503
|
+
}
|
|
504
|
+
return { ok: true, value: undefined };
|
|
505
|
+
};
|
|
506
|
+
const parseNamedImports = (namedImportsText) => {
|
|
507
|
+
return namedImportsText
|
|
508
|
+
.split(",")
|
|
509
|
+
.map((part) => part.trim())
|
|
510
|
+
.filter((part) => part.length > 0)
|
|
511
|
+
.map((part) => {
|
|
512
|
+
const source = part.replace(/^\s*type\s+/, "");
|
|
513
|
+
const asMatch = /^\s*([A-Za-z_$][\w$]*)\s+as\s+([A-Za-z_$][\w$]*)\s*$/.exec(source);
|
|
514
|
+
if (asMatch && asMatch[1] && asMatch[2]) {
|
|
515
|
+
return { imported: asMatch[1], local: asMatch[2] };
|
|
516
|
+
}
|
|
517
|
+
const identMatch = /^\s*([A-Za-z_$][\w$]*)\s*$/.exec(source);
|
|
518
|
+
if (identMatch && identMatch[1]) {
|
|
519
|
+
return { imported: identMatch[1], local: identMatch[1] };
|
|
520
|
+
}
|
|
521
|
+
return undefined;
|
|
522
|
+
})
|
|
523
|
+
.filter((specifier) => specifier !== undefined);
|
|
524
|
+
};
|
|
525
|
+
const formatNamedImportSpecifier = (specifier) => {
|
|
526
|
+
if (specifier.imported === specifier.local) {
|
|
527
|
+
return specifier.local;
|
|
528
|
+
}
|
|
529
|
+
return `${specifier.imported} as ${specifier.local}`;
|
|
530
|
+
};
|
|
531
|
+
const ensureInternalTypeImportsForFacade = (facadeDtsPath) => {
|
|
532
|
+
if (!existsSync(facadeDtsPath)) {
|
|
533
|
+
return {
|
|
534
|
+
ok: false,
|
|
535
|
+
error: `Facade declaration file not found at ${facadeDtsPath}`,
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
const original = readFileSync(facadeDtsPath, "utf-8");
|
|
539
|
+
const internalImportRe = /^import\s+\*\s+as\s+Internal\s+from\s+['"]([^'"]+)['"];\s*$/m;
|
|
540
|
+
const internalImportMatch = internalImportRe.exec(original);
|
|
541
|
+
if (!internalImportMatch || !internalImportMatch[1]) {
|
|
542
|
+
return { ok: true, value: undefined };
|
|
543
|
+
}
|
|
544
|
+
const internalSpecifier = internalImportMatch[1];
|
|
545
|
+
const exportFromInternalRe = new RegExp(String.raw `^export\s+\{([^}]*)\}\s+from\s+['"]${escapeRegExp(internalSpecifier)}['"];\s*$`, "gm");
|
|
546
|
+
const neededTypeSpecifiers = new Map();
|
|
547
|
+
for (const match of original.matchAll(exportFromInternalRe)) {
|
|
548
|
+
const named = match[1];
|
|
549
|
+
if (!named)
|
|
550
|
+
continue;
|
|
551
|
+
for (const specifier of parseNamedImports(named)) {
|
|
552
|
+
neededTypeSpecifiers.set(specifier.local, specifier);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
if (neededTypeSpecifiers.size === 0) {
|
|
556
|
+
return { ok: true, value: undefined };
|
|
557
|
+
}
|
|
558
|
+
const existingTypeImportRe = new RegExp(String.raw `^import\s+type\s+\{([^}]*)\}\s+from\s+['"]${escapeRegExp(internalSpecifier)}['"];\s*$`, "m");
|
|
559
|
+
const existingTypeImportMatch = existingTypeImportRe.exec(original);
|
|
560
|
+
if (existingTypeImportMatch && existingTypeImportMatch[1]) {
|
|
561
|
+
for (const specifier of parseNamedImports(existingTypeImportMatch[1])) {
|
|
562
|
+
neededTypeSpecifiers.set(specifier.local, specifier);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
const sortedSpecifiers = Array.from(neededTypeSpecifiers.values()).sort((a, b) => a.local.localeCompare(b.local));
|
|
566
|
+
const importLine = `import type { ${sortedSpecifiers
|
|
567
|
+
.map((specifier) => formatNamedImportSpecifier(specifier))
|
|
568
|
+
.join(", ")} } from '${internalSpecifier}';`;
|
|
569
|
+
let next = original;
|
|
570
|
+
if (existingTypeImportMatch && existingTypeImportMatch[0]) {
|
|
571
|
+
next = next.replace(existingTypeImportRe, importLine);
|
|
572
|
+
}
|
|
573
|
+
else if (internalImportMatch[0]) {
|
|
574
|
+
const anchor = internalImportMatch[0];
|
|
575
|
+
next = next.replace(anchor, `${anchor}\n${importLine}`);
|
|
576
|
+
}
|
|
577
|
+
if (next !== original) {
|
|
578
|
+
writeFileSync(facadeDtsPath, next, "utf-8");
|
|
579
|
+
}
|
|
580
|
+
return { ok: true, value: undefined };
|
|
581
|
+
};
|
|
582
|
+
const ensureSourceTypeImportsForFacade = (facadeDtsPath, importsByLocalName) => {
|
|
583
|
+
if (!existsSync(facadeDtsPath)) {
|
|
584
|
+
return {
|
|
585
|
+
ok: false,
|
|
586
|
+
error: `Facade declaration file not found at ${facadeDtsPath}`,
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
const original = readFileSync(facadeDtsPath, "utf-8");
|
|
590
|
+
if (importsByLocalName.size === 0) {
|
|
591
|
+
return { ok: true, value: undefined };
|
|
592
|
+
}
|
|
593
|
+
const existingLocals = new Set();
|
|
594
|
+
const importRe = /^import\s+(.*)\s+from\s+['"][^'"]+['"];\s*$/gm;
|
|
595
|
+
for (const match of original.matchAll(importRe)) {
|
|
596
|
+
const clause = match[1]?.trim();
|
|
597
|
+
if (!clause)
|
|
598
|
+
continue;
|
|
599
|
+
const nsMatch = /^\*\s+as\s+([A-Za-z_$][\w$]*)$/.exec(clause);
|
|
600
|
+
if (nsMatch?.[1]) {
|
|
601
|
+
existingLocals.add(nsMatch[1]);
|
|
602
|
+
continue;
|
|
603
|
+
}
|
|
604
|
+
const defaultMatch = /^([A-Za-z_$][\w$]*)$/.exec(clause);
|
|
605
|
+
if (defaultMatch?.[1]) {
|
|
606
|
+
existingLocals.add(defaultMatch[1]);
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
const namedMatch = /^(?:type\s+)?\{([^}]*)\}$/.exec(clause);
|
|
610
|
+
if (namedMatch?.[1]) {
|
|
611
|
+
for (const specifier of parseNamedImports(namedMatch[1])) {
|
|
612
|
+
existingLocals.add(specifier.local);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
const grouped = new Map();
|
|
617
|
+
for (const binding of Array.from(importsByLocalName.values()).sort((a, b) => a.localName.localeCompare(b.localName))) {
|
|
618
|
+
if (existingLocals.has(binding.localName))
|
|
415
619
|
continue;
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
return `${paramName}: ${facadeParamTypes[idx]}`;
|
|
620
|
+
const list = grouped.get(binding.source) ?? [];
|
|
621
|
+
list.push({
|
|
622
|
+
imported: binding.importedName,
|
|
623
|
+
local: binding.localName,
|
|
421
624
|
});
|
|
422
|
-
|
|
423
|
-
const typeParamsText = sourceSig.typeParametersText;
|
|
424
|
-
const replacement = `export declare function ${name}${typeParamsText}(${pairedParams.join(", ")}): ${expandedReturnType};`;
|
|
425
|
-
next = next.replace(constFuncRe, replacement);
|
|
625
|
+
grouped.set(binding.source, list);
|
|
426
626
|
}
|
|
627
|
+
if (grouped.size === 0) {
|
|
628
|
+
const startMarker = "// Tsonic source function type imports (generated)";
|
|
629
|
+
const endMarker = "// End Tsonic source function type imports";
|
|
630
|
+
const next = stripExistingSection(original, startMarker, endMarker);
|
|
631
|
+
if (next !== original) {
|
|
632
|
+
writeFileSync(facadeDtsPath, next, "utf-8");
|
|
633
|
+
}
|
|
634
|
+
return { ok: true, value: undefined };
|
|
635
|
+
}
|
|
636
|
+
const lines = [];
|
|
637
|
+
for (const [source, specifiers] of Array.from(grouped.entries()).sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
638
|
+
const unique = Array.from(new Map(specifiers.map((specifier) => [
|
|
639
|
+
`${specifier.imported}|${specifier.local}`,
|
|
640
|
+
specifier,
|
|
641
|
+
])).values()).sort((a, b) => a.local.localeCompare(b.local));
|
|
642
|
+
lines.push(`import type { ${unique.map((s) => formatNamedImportSpecifier(s)).join(", ")} } from '${source}';`);
|
|
643
|
+
}
|
|
644
|
+
const startMarker = "// Tsonic source function type imports (generated)";
|
|
645
|
+
const endMarker = "// End Tsonic source function type imports";
|
|
646
|
+
const next = upsertSectionAfterImports(original, startMarker, endMarker, lines.join("\n"));
|
|
427
647
|
if (next !== original) {
|
|
428
648
|
writeFileSync(facadeDtsPath, next, "utf-8");
|
|
429
649
|
}
|
|
@@ -679,12 +899,14 @@ const buildModuleSourceIndex = (absoluteFilePath, fileKey) => {
|
|
|
679
899
|
};
|
|
680
900
|
};
|
|
681
901
|
const typeNodeUsesImportedTypeNames = (node, typeImportsByLocalName) => {
|
|
902
|
+
const allowlistedImportSources = new Set(["@tsonic/core/types.js"]);
|
|
682
903
|
let found = false;
|
|
683
904
|
const visit = (current) => {
|
|
684
905
|
if (found)
|
|
685
906
|
return;
|
|
686
907
|
if (ts.isTypeReferenceNode(current) && ts.isIdentifier(current.typeName)) {
|
|
687
|
-
|
|
908
|
+
const imported = typeImportsByLocalName.get(current.typeName.text);
|
|
909
|
+
if (imported && !allowlistedImportSources.has(imported.source.trim())) {
|
|
688
910
|
found = true;
|
|
689
911
|
return;
|
|
690
912
|
}
|
|
@@ -903,7 +1125,7 @@ const printIrType = (type, ctx) => {
|
|
|
903
1125
|
return "unknown";
|
|
904
1126
|
}
|
|
905
1127
|
};
|
|
906
|
-
const renderExportedTypeAlias = (stmt, internalIndexDts) => {
|
|
1128
|
+
const renderExportedTypeAlias = (stmt, internalIndexDts, _sourceAlias) => {
|
|
907
1129
|
const typeParams = printTypeParameters(stmt.typeParameters);
|
|
908
1130
|
if (stmt.type.kind === "objectType") {
|
|
909
1131
|
const arity = stmt.typeParameters?.length ?? 0;
|
|
@@ -921,11 +1143,17 @@ const renderExportedTypeAlias = (stmt, internalIndexDts) => {
|
|
|
921
1143
|
: "";
|
|
922
1144
|
return {
|
|
923
1145
|
ok: true,
|
|
924
|
-
value:
|
|
1146
|
+
value: {
|
|
1147
|
+
line: `export type ${stmt.name}${typeParams} = ${internalName}${typeArgs};`,
|
|
1148
|
+
internalAliasImport: internalName,
|
|
1149
|
+
},
|
|
925
1150
|
};
|
|
926
1151
|
}
|
|
927
1152
|
const rhs = printIrType(stmt.type, { parentPrecedence: 0 });
|
|
928
|
-
return {
|
|
1153
|
+
return {
|
|
1154
|
+
ok: true,
|
|
1155
|
+
value: { line: `export type ${stmt.name}${typeParams} = ${rhs};` },
|
|
1156
|
+
};
|
|
929
1157
|
};
|
|
930
1158
|
/**
|
|
931
1159
|
* Overlay already-augmented bindings from dependency assemblies.
|
|
@@ -1008,6 +1236,7 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
1008
1236
|
}
|
|
1009
1237
|
const absoluteEntryPoint = resolve(config.projectRoot, entryPoint);
|
|
1010
1238
|
const absoluteSourceRoot = resolve(config.projectRoot, config.sourceRoot);
|
|
1239
|
+
const surfaceCapabilities = resolveSurfaceCapabilities(config.surface);
|
|
1011
1240
|
const typeLibraries = config.libraries.filter((lib) => !lib.endsWith(".dll"));
|
|
1012
1241
|
const allTypeRoots = [...config.typeRoots, ...typeLibraries].map((p) => resolve(config.workspaceRoot, p));
|
|
1013
1242
|
const compilerOptions = {
|
|
@@ -1015,6 +1244,8 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
1015
1244
|
sourceRoot: absoluteSourceRoot,
|
|
1016
1245
|
rootNamespace: config.rootNamespace,
|
|
1017
1246
|
typeRoots: allTypeRoots,
|
|
1247
|
+
surface: config.surface,
|
|
1248
|
+
useStandardLib: surfaceCapabilities.useStandardLib,
|
|
1018
1249
|
verbose: false,
|
|
1019
1250
|
};
|
|
1020
1251
|
const graphResult = buildModuleDependencyGraph(absoluteEntryPoint, compilerOptions);
|
|
@@ -1071,6 +1302,7 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
1071
1302
|
};
|
|
1072
1303
|
// 1) Per-namespace exported type aliases (including non-structural aliases).
|
|
1073
1304
|
const exportedAliasesByNamespace = new Map();
|
|
1305
|
+
const sourceIndexByFileKeyForAliases = new Map();
|
|
1074
1306
|
for (const m of modules) {
|
|
1075
1307
|
const isExportedTypeAlias = (stmt) => stmt.kind === "typeAliasDeclaration" && stmt.isExported;
|
|
1076
1308
|
const exportedAliases = m.body.filter(isExportedTypeAlias);
|
|
@@ -1080,27 +1312,60 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
1080
1312
|
const internalIndexDts = existsSync(info.internalIndexDtsPath)
|
|
1081
1313
|
? readFileSync(info.internalIndexDtsPath, "utf-8")
|
|
1082
1314
|
: "";
|
|
1315
|
+
const moduleKey = normalizeModuleFileKey(m.filePath);
|
|
1316
|
+
const sourceIndex = (() => {
|
|
1317
|
+
const cached = sourceIndexByFileKeyForAliases.get(moduleKey);
|
|
1318
|
+
if (cached)
|
|
1319
|
+
return { ok: true, value: cached };
|
|
1320
|
+
const absolutePath = resolve(absoluteSourceRoot, moduleKey);
|
|
1321
|
+
const indexed = buildModuleSourceIndex(absolutePath, moduleKey);
|
|
1322
|
+
if (!indexed.ok)
|
|
1323
|
+
return indexed;
|
|
1324
|
+
sourceIndexByFileKeyForAliases.set(moduleKey, indexed.value);
|
|
1325
|
+
return indexed;
|
|
1326
|
+
})();
|
|
1327
|
+
if (!sourceIndex.ok)
|
|
1328
|
+
return sourceIndex;
|
|
1083
1329
|
for (const stmt of exportedAliases) {
|
|
1084
|
-
const rendered = renderExportedTypeAlias(stmt, internalIndexDts);
|
|
1330
|
+
const rendered = renderExportedTypeAlias(stmt, internalIndexDts, sourceIndex.value.typeAliasesByName.get(stmt.name));
|
|
1085
1331
|
if (!rendered.ok)
|
|
1086
1332
|
return rendered;
|
|
1087
|
-
const
|
|
1088
|
-
|
|
1089
|
-
|
|
1333
|
+
const current = exportedAliasesByNamespace.get(m.namespace) ?? {
|
|
1334
|
+
lines: [],
|
|
1335
|
+
internalAliasImports: new Set(),
|
|
1336
|
+
};
|
|
1337
|
+
current.lines.push(rendered.value.line);
|
|
1338
|
+
if (rendered.value.internalAliasImport) {
|
|
1339
|
+
current.internalAliasImports.add(rendered.value.internalAliasImport);
|
|
1340
|
+
}
|
|
1341
|
+
exportedAliasesByNamespace.set(m.namespace, current);
|
|
1090
1342
|
}
|
|
1091
1343
|
}
|
|
1092
1344
|
const aliasStart = "// Tsonic source type aliases (generated)";
|
|
1093
1345
|
const aliasEnd = "// End Tsonic source type aliases";
|
|
1094
|
-
|
|
1346
|
+
const aliasImportsStart = "// Tsonic source alias imports (generated)";
|
|
1347
|
+
const aliasImportsEnd = "// End Tsonic source alias imports";
|
|
1348
|
+
for (const [ns, aliasData] of exportedAliasesByNamespace) {
|
|
1095
1349
|
const info = facadesByNamespace.get(ns);
|
|
1096
1350
|
if (!info)
|
|
1097
1351
|
continue;
|
|
1098
|
-
if (lines.length === 0)
|
|
1352
|
+
if (aliasData.lines.length === 0)
|
|
1099
1353
|
continue;
|
|
1100
|
-
const unique = Array.from(new Set(lines)).sort((a, b) => a.localeCompare(b));
|
|
1354
|
+
const unique = Array.from(new Set(aliasData.lines)).sort((a, b) => a.localeCompare(b));
|
|
1101
1355
|
const body = unique.join("\n");
|
|
1102
1356
|
const current = readFileSync(info.facadeDtsPath, "utf-8");
|
|
1103
|
-
const
|
|
1357
|
+
const internalImportMatch = current.match(/^import\s+\*\s+as\s+Internal\s+from\s+['"](.+)['"];\s*$/m);
|
|
1358
|
+
let next = current;
|
|
1359
|
+
if (internalImportMatch?.[1] && aliasData.internalAliasImports.size > 0) {
|
|
1360
|
+
const internalAliasImportLine = `import type { ${Array.from(aliasData.internalAliasImports)
|
|
1361
|
+
.sort((a, b) => a.localeCompare(b))
|
|
1362
|
+
.join(", ")} } from '${internalImportMatch[1]}';`;
|
|
1363
|
+
next = upsertSectionAfterImports(next, aliasImportsStart, aliasImportsEnd, internalAliasImportLine);
|
|
1364
|
+
}
|
|
1365
|
+
else {
|
|
1366
|
+
next = stripExistingSection(next, aliasImportsStart, aliasImportsEnd);
|
|
1367
|
+
}
|
|
1368
|
+
next = upsertSectionAfterImports(next, aliasStart, aliasEnd, body);
|
|
1104
1369
|
writeFileSync(info.facadeDtsPath, next, "utf-8");
|
|
1105
1370
|
}
|
|
1106
1371
|
// 2) Entry-module re-export surface (matches TS semantics for library entrypoints).
|
|
@@ -1176,7 +1441,7 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
1176
1441
|
// graph and patching the published internal/index.d.ts:
|
|
1177
1442
|
// - Re-apply ExtensionMethods wrappers (class + interface members)
|
|
1178
1443
|
// - Preserve optional (`?`) semantics by allowing `undefined` on patched members
|
|
1179
|
-
// -
|
|
1444
|
+
// - Preserve TS structural typing on interfaces/type-literal aliases (exported + local)
|
|
1180
1445
|
const sourceIndexByFileKey = new Map();
|
|
1181
1446
|
for (const m of modules) {
|
|
1182
1447
|
// Synthetic IR modules (e.g., program-wide anonymous type declarations) do not
|
|
@@ -1193,6 +1458,7 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
1193
1458
|
const overridesByInternalIndex = new Map();
|
|
1194
1459
|
const brandOptionalTypesByInternalIndex = new Map();
|
|
1195
1460
|
const functionSignaturesByFacade = new Map();
|
|
1461
|
+
const sourceTypeImportsByFacade = new Map();
|
|
1196
1462
|
for (const m of modules) {
|
|
1197
1463
|
if (m.filePath.startsWith("__tsonic/"))
|
|
1198
1464
|
continue;
|
|
@@ -1203,13 +1469,37 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
1203
1469
|
const sourceIndex = sourceIndexByFileKey.get(moduleKey);
|
|
1204
1470
|
if (!sourceIndex)
|
|
1205
1471
|
continue;
|
|
1472
|
+
const allInterfaces = m.body.filter((s) => s.kind === "interfaceDeclaration");
|
|
1473
|
+
const allTypeAliases = m.body.filter((s) => s.kind === "typeAliasDeclaration");
|
|
1206
1474
|
const hasExportedSourceFunctions = sourceIndex.exportedFunctionSignaturesByName.size > 0;
|
|
1207
1475
|
if (exportedClasses.length === 0 &&
|
|
1208
1476
|
exportedInterfaces.length === 0 &&
|
|
1209
1477
|
exportedAliases.length === 0 &&
|
|
1478
|
+
allInterfaces.length === 0 &&
|
|
1479
|
+
allTypeAliases.length === 0 &&
|
|
1210
1480
|
!hasExportedSourceFunctions)
|
|
1211
1481
|
continue;
|
|
1212
1482
|
const info = facadesByNamespace.get(m.namespace) ?? ensureFacade(m.namespace);
|
|
1483
|
+
const brandTargets = brandOptionalTypesByInternalIndex.get(info.internalIndexDtsPath) ??
|
|
1484
|
+
new Set();
|
|
1485
|
+
for (const iface of allInterfaces) {
|
|
1486
|
+
brandTargets.add(iface.name);
|
|
1487
|
+
}
|
|
1488
|
+
for (const alias of allTypeAliases) {
|
|
1489
|
+
const sourceAlias = sourceIndex.typeAliasesByName.get(alias.name);
|
|
1490
|
+
if (!sourceAlias)
|
|
1491
|
+
continue;
|
|
1492
|
+
const aliasType = unwrapParens(sourceAlias.type);
|
|
1493
|
+
if (!ts.isTypeLiteralNode(aliasType))
|
|
1494
|
+
continue;
|
|
1495
|
+
const arity = sourceAlias.typeParameters.length;
|
|
1496
|
+
const internalAliasName = `${alias.name}__Alias${arity > 0 ? `_${arity}` : ""}`;
|
|
1497
|
+
brandTargets.add(alias.name);
|
|
1498
|
+
brandTargets.add(internalAliasName);
|
|
1499
|
+
}
|
|
1500
|
+
if (brandTargets.size > 0) {
|
|
1501
|
+
brandOptionalTypesByInternalIndex.set(info.internalIndexDtsPath, brandTargets);
|
|
1502
|
+
}
|
|
1213
1503
|
for (const [name, signatures,] of sourceIndex.exportedFunctionSignaturesByName) {
|
|
1214
1504
|
if (signatures.length === 0)
|
|
1215
1505
|
continue;
|
|
@@ -1219,6 +1509,28 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
1219
1509
|
list.push(...signatures);
|
|
1220
1510
|
byName.set(name, list);
|
|
1221
1511
|
functionSignaturesByFacade.set(info.facadeDtsPath, byName);
|
|
1512
|
+
const importsByLocal = sourceTypeImportsByFacade.get(info.facadeDtsPath) ??
|
|
1513
|
+
new Map();
|
|
1514
|
+
for (const signature of signatures) {
|
|
1515
|
+
for (const binding of collectSourceTypeImportsForSignature(signature, sourceIndex.typeImportsByLocalName)) {
|
|
1516
|
+
const existing = importsByLocal.get(binding.localName);
|
|
1517
|
+
if (existing) {
|
|
1518
|
+
if (existing.source !== binding.source ||
|
|
1519
|
+
existing.importedName !== binding.importedName) {
|
|
1520
|
+
return {
|
|
1521
|
+
ok: false,
|
|
1522
|
+
error: `Conflicting source type import alias '${binding.localName}' while augmenting ${info.facadeDtsPath}.\n` +
|
|
1523
|
+
`- ${existing.importedName} from '${existing.source}'\n` +
|
|
1524
|
+
`- ${binding.importedName} from '${binding.source}'\n` +
|
|
1525
|
+
`Fix: disambiguate source type imports for exported function signatures.`,
|
|
1526
|
+
};
|
|
1527
|
+
}
|
|
1528
|
+
continue;
|
|
1529
|
+
}
|
|
1530
|
+
importsByLocal.set(binding.localName, binding);
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
sourceTypeImportsByFacade.set(info.facadeDtsPath, importsByLocal);
|
|
1222
1534
|
}
|
|
1223
1535
|
for (const cls of exportedClasses) {
|
|
1224
1536
|
const memberTypes = sourceIndex.memberTypesByClassAndMember.get(cls.name);
|
|
@@ -1243,13 +1555,21 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
1243
1555
|
if (!wrappersResult.ok)
|
|
1244
1556
|
return wrappersResult;
|
|
1245
1557
|
const wrappers = wrappersResult.value;
|
|
1246
|
-
|
|
1558
|
+
const canUseSourceTypeText = !typeNodeUsesImportedTypeNames(sourceMember.typeNode, sourceIndex.typeImportsByLocalName);
|
|
1559
|
+
if (!canUseSourceTypeText &&
|
|
1560
|
+
wrappers.length === 0 &&
|
|
1561
|
+
!sourceMember.isOptional) {
|
|
1247
1562
|
continue;
|
|
1563
|
+
}
|
|
1248
1564
|
const list = overridesByInternalIndex.get(info.internalIndexDtsPath) ?? [];
|
|
1249
1565
|
list.push({
|
|
1250
1566
|
namespace: m.namespace,
|
|
1251
1567
|
className: cls.name,
|
|
1252
1568
|
memberName: member.name,
|
|
1569
|
+
sourceTypeText: canUseSourceTypeText
|
|
1570
|
+
? sourceMember.typeText
|
|
1571
|
+
: undefined,
|
|
1572
|
+
replaceWithSourceType: canUseSourceTypeText,
|
|
1253
1573
|
isOptional: sourceMember.isOptional,
|
|
1254
1574
|
wrappers,
|
|
1255
1575
|
});
|
|
@@ -1288,14 +1608,11 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
1288
1608
|
: undefined,
|
|
1289
1609
|
replaceWithSourceType: canUseSourceTypeText,
|
|
1290
1610
|
isOptional: sourceMember.isOptional,
|
|
1611
|
+
emitOptionalPropertySyntax: true,
|
|
1291
1612
|
wrappers,
|
|
1292
1613
|
});
|
|
1293
1614
|
overridesByInternalIndex.set(info.internalIndexDtsPath, list);
|
|
1294
1615
|
}
|
|
1295
|
-
const brandTargets = brandOptionalTypesByInternalIndex.get(info.internalIndexDtsPath) ??
|
|
1296
|
-
new Set();
|
|
1297
|
-
brandTargets.add(iface.name);
|
|
1298
|
-
brandOptionalTypesByInternalIndex.set(info.internalIndexDtsPath, brandTargets);
|
|
1299
1616
|
}
|
|
1300
1617
|
for (const alias of exportedAliases) {
|
|
1301
1618
|
const sourceAlias = sourceIndex.typeAliasesByName.get(alias.name);
|
|
@@ -1306,10 +1623,6 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
1306
1623
|
continue;
|
|
1307
1624
|
const arity = sourceAlias.typeParameters.length;
|
|
1308
1625
|
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
1626
|
for (const member of aliasType.members) {
|
|
1314
1627
|
if (!ts.isPropertySignature(member))
|
|
1315
1628
|
continue;
|
|
@@ -1361,6 +1674,16 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
1361
1674
|
if (!result.ok)
|
|
1362
1675
|
return result;
|
|
1363
1676
|
}
|
|
1677
|
+
for (const [facadePath, importsByLocalName] of sourceTypeImportsByFacade) {
|
|
1678
|
+
const result = ensureSourceTypeImportsForFacade(facadePath, importsByLocalName);
|
|
1679
|
+
if (!result.ok)
|
|
1680
|
+
return result;
|
|
1681
|
+
}
|
|
1682
|
+
for (const info of facadesByNamespace.values()) {
|
|
1683
|
+
const result = ensureInternalTypeImportsForFacade(info.facadeDtsPath);
|
|
1684
|
+
if (!result.ok)
|
|
1685
|
+
return result;
|
|
1686
|
+
}
|
|
1364
1687
|
return { ok: true, value: undefined };
|
|
1365
1688
|
};
|
|
1366
1689
|
//# sourceMappingURL=library-bindings-augment.js.map
|