@tsonic/cli 0.0.63 → 0.0.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/aikya/bindings.d.ts +44 -0
- package/dist/aikya/bindings.d.ts.map +1 -0
- package/dist/aikya/bindings.js +777 -0
- package/dist/aikya/bindings.js.map +1 -0
- package/dist/aikya/bindings.test.d.ts +2 -0
- package/dist/aikya/bindings.test.d.ts.map +1 -0
- package/dist/aikya/bindings.test.js +554 -0
- package/dist/aikya/bindings.test.js.map +1 -0
- package/dist/cli/dispatcher.d.ts.map +1 -1
- package/dist/cli/dispatcher.js +15 -0
- package/dist/cli/dispatcher.js.map +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +4 -1
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/parser.d.ts.map +1 -1
- package/dist/cli/parser.js +6 -0
- package/dist/cli/parser.js.map +1 -1
- package/dist/cli/parser.test.js +10 -0
- package/dist/cli/parser.test.js.map +1 -1
- package/dist/commands/add-npm.d.ts +3 -2
- package/dist/commands/add-npm.d.ts.map +1 -1
- package/dist/commands/add-npm.js +69 -177
- package/dist/commands/add-npm.js.map +1 -1
- package/dist/commands/add-npm.test.js +276 -2
- package/dist/commands/add-npm.test.js.map +1 -1
- package/dist/commands/build-library-bindings-aliases.test.js +352 -6
- package/dist/commands/build-library-bindings-aliases.test.js.map +1 -1
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +192 -145
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/build.test.js +22 -3
- package/dist/commands/build.test.js.map +1 -1
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +7 -0
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/init.d.ts +6 -2
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +76 -12
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/init.test.js +162 -8
- package/dist/commands/init.test.js.map +1 -1
- package/dist/commands/library-bindings-augment.d.ts.map +1 -1
- package/dist/commands/library-bindings-augment.js +378 -53
- package/dist/commands/library-bindings-augment.js.map +1 -1
- package/dist/commands/library-bindings-augment.test.js +460 -2
- package/dist/commands/library-bindings-augment.test.js.map +1 -1
- package/dist/commands/library-bindings-firstparty-regressions.test.d.ts +2 -0
- package/dist/commands/library-bindings-firstparty-regressions.test.d.ts.map +1 -0
- package/dist/commands/library-bindings-firstparty-regressions.test.js +214 -0
- package/dist/commands/library-bindings-firstparty-regressions.test.js.map +1 -0
- package/dist/commands/library-bindings-firstparty.d.ts +3 -0
- package/dist/commands/library-bindings-firstparty.d.ts.map +1 -0
- package/dist/commands/library-bindings-firstparty.js +2252 -0
- package/dist/commands/library-bindings-firstparty.js.map +1 -0
- package/dist/commands/restore.d.ts.map +1 -1
- package/dist/commands/restore.js +3 -1
- package/dist/commands/restore.js.map +1 -1
- package/dist/commands/restore.test.js +29 -0
- package/dist/commands/restore.test.js.map +1 -1
- package/dist/commands/run-build-regressions.test.js +72 -0
- package/dist/commands/run-build-regressions.test.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +19 -2
- package/dist/config.js.map +1 -1
- package/dist/config.test.js +98 -2
- package/dist/config.test.js.map +1 -1
- package/dist/dotnet/runtime-dlls.d.ts +1 -0
- package/dist/dotnet/runtime-dlls.d.ts.map +1 -1
- package/dist/dotnet/runtime-dlls.js +1 -0
- package/dist/dotnet/runtime-dlls.js.map +1 -1
- package/dist/surface/profiles.d.ts +15 -0
- package/dist/surface/profiles.d.ts.map +1 -0
- package/dist/surface/profiles.js +218 -0
- package/dist/surface/profiles.js.map +1 -0
- package/dist/surface/profiles.test.d.ts +2 -0
- package/dist/surface/profiles.test.d.ts.map +1 -0
- package/dist/surface/profiles.test.js +175 -0
- package/dist/surface/profiles.test.js.map +1 -0
- package/dist/types.d.ts +11 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/runtime/nodejs.dll +0 -0
|
@@ -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)
|
|
415
550
|
continue;
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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))
|
|
619
|
+
continue;
|
|
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,9 @@ 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, {
|
|
1240
|
+
workspaceRoot: config.workspaceRoot,
|
|
1241
|
+
});
|
|
1011
1242
|
const typeLibraries = config.libraries.filter((lib) => !lib.endsWith(".dll"));
|
|
1012
1243
|
const allTypeRoots = [...config.typeRoots, ...typeLibraries].map((p) => resolve(config.workspaceRoot, p));
|
|
1013
1244
|
const compilerOptions = {
|
|
@@ -1015,6 +1246,8 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
1015
1246
|
sourceRoot: absoluteSourceRoot,
|
|
1016
1247
|
rootNamespace: config.rootNamespace,
|
|
1017
1248
|
typeRoots: allTypeRoots,
|
|
1249
|
+
surface: config.surface,
|
|
1250
|
+
useStandardLib: surfaceCapabilities.useStandardLib,
|
|
1018
1251
|
verbose: false,
|
|
1019
1252
|
};
|
|
1020
1253
|
const graphResult = buildModuleDependencyGraph(absoluteEntryPoint, compilerOptions);
|
|
@@ -1071,6 +1304,7 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
1071
1304
|
};
|
|
1072
1305
|
// 1) Per-namespace exported type aliases (including non-structural aliases).
|
|
1073
1306
|
const exportedAliasesByNamespace = new Map();
|
|
1307
|
+
const sourceIndexByFileKeyForAliases = new Map();
|
|
1074
1308
|
for (const m of modules) {
|
|
1075
1309
|
const isExportedTypeAlias = (stmt) => stmt.kind === "typeAliasDeclaration" && stmt.isExported;
|
|
1076
1310
|
const exportedAliases = m.body.filter(isExportedTypeAlias);
|
|
@@ -1080,27 +1314,60 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
1080
1314
|
const internalIndexDts = existsSync(info.internalIndexDtsPath)
|
|
1081
1315
|
? readFileSync(info.internalIndexDtsPath, "utf-8")
|
|
1082
1316
|
: "";
|
|
1317
|
+
const moduleKey = normalizeModuleFileKey(m.filePath);
|
|
1318
|
+
const sourceIndex = (() => {
|
|
1319
|
+
const cached = sourceIndexByFileKeyForAliases.get(moduleKey);
|
|
1320
|
+
if (cached)
|
|
1321
|
+
return { ok: true, value: cached };
|
|
1322
|
+
const absolutePath = resolve(absoluteSourceRoot, moduleKey);
|
|
1323
|
+
const indexed = buildModuleSourceIndex(absolutePath, moduleKey);
|
|
1324
|
+
if (!indexed.ok)
|
|
1325
|
+
return indexed;
|
|
1326
|
+
sourceIndexByFileKeyForAliases.set(moduleKey, indexed.value);
|
|
1327
|
+
return indexed;
|
|
1328
|
+
})();
|
|
1329
|
+
if (!sourceIndex.ok)
|
|
1330
|
+
return sourceIndex;
|
|
1083
1331
|
for (const stmt of exportedAliases) {
|
|
1084
|
-
const rendered = renderExportedTypeAlias(stmt, internalIndexDts);
|
|
1332
|
+
const rendered = renderExportedTypeAlias(stmt, internalIndexDts, sourceIndex.value.typeAliasesByName.get(stmt.name));
|
|
1085
1333
|
if (!rendered.ok)
|
|
1086
1334
|
return rendered;
|
|
1087
|
-
const
|
|
1088
|
-
|
|
1089
|
-
|
|
1335
|
+
const current = exportedAliasesByNamespace.get(m.namespace) ?? {
|
|
1336
|
+
lines: [],
|
|
1337
|
+
internalAliasImports: new Set(),
|
|
1338
|
+
};
|
|
1339
|
+
current.lines.push(rendered.value.line);
|
|
1340
|
+
if (rendered.value.internalAliasImport) {
|
|
1341
|
+
current.internalAliasImports.add(rendered.value.internalAliasImport);
|
|
1342
|
+
}
|
|
1343
|
+
exportedAliasesByNamespace.set(m.namespace, current);
|
|
1090
1344
|
}
|
|
1091
1345
|
}
|
|
1092
1346
|
const aliasStart = "// Tsonic source type aliases (generated)";
|
|
1093
1347
|
const aliasEnd = "// End Tsonic source type aliases";
|
|
1094
|
-
|
|
1348
|
+
const aliasImportsStart = "// Tsonic source alias imports (generated)";
|
|
1349
|
+
const aliasImportsEnd = "// End Tsonic source alias imports";
|
|
1350
|
+
for (const [ns, aliasData] of exportedAliasesByNamespace) {
|
|
1095
1351
|
const info = facadesByNamespace.get(ns);
|
|
1096
1352
|
if (!info)
|
|
1097
1353
|
continue;
|
|
1098
|
-
if (lines.length === 0)
|
|
1354
|
+
if (aliasData.lines.length === 0)
|
|
1099
1355
|
continue;
|
|
1100
|
-
const unique = Array.from(new Set(lines)).sort((a, b) => a.localeCompare(b));
|
|
1356
|
+
const unique = Array.from(new Set(aliasData.lines)).sort((a, b) => a.localeCompare(b));
|
|
1101
1357
|
const body = unique.join("\n");
|
|
1102
1358
|
const current = readFileSync(info.facadeDtsPath, "utf-8");
|
|
1103
|
-
const
|
|
1359
|
+
const internalImportMatch = current.match(/^import\s+\*\s+as\s+Internal\s+from\s+['"](.+)['"];\s*$/m);
|
|
1360
|
+
let next = current;
|
|
1361
|
+
if (internalImportMatch?.[1] && aliasData.internalAliasImports.size > 0) {
|
|
1362
|
+
const internalAliasImportLine = `import type { ${Array.from(aliasData.internalAliasImports)
|
|
1363
|
+
.sort((a, b) => a.localeCompare(b))
|
|
1364
|
+
.join(", ")} } from '${internalImportMatch[1]}';`;
|
|
1365
|
+
next = upsertSectionAfterImports(next, aliasImportsStart, aliasImportsEnd, internalAliasImportLine);
|
|
1366
|
+
}
|
|
1367
|
+
else {
|
|
1368
|
+
next = stripExistingSection(next, aliasImportsStart, aliasImportsEnd);
|
|
1369
|
+
}
|
|
1370
|
+
next = upsertSectionAfterImports(next, aliasStart, aliasEnd, body);
|
|
1104
1371
|
writeFileSync(info.facadeDtsPath, next, "utf-8");
|
|
1105
1372
|
}
|
|
1106
1373
|
// 2) Entry-module re-export surface (matches TS semantics for library entrypoints).
|
|
@@ -1176,7 +1443,7 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
1176
1443
|
// graph and patching the published internal/index.d.ts:
|
|
1177
1444
|
// - Re-apply ExtensionMethods wrappers (class + interface members)
|
|
1178
1445
|
// - Preserve optional (`?`) semantics by allowing `undefined` on patched members
|
|
1179
|
-
// -
|
|
1446
|
+
// - Preserve TS structural typing on interfaces/type-literal aliases (exported + local)
|
|
1180
1447
|
const sourceIndexByFileKey = new Map();
|
|
1181
1448
|
for (const m of modules) {
|
|
1182
1449
|
// Synthetic IR modules (e.g., program-wide anonymous type declarations) do not
|
|
@@ -1193,6 +1460,7 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
1193
1460
|
const overridesByInternalIndex = new Map();
|
|
1194
1461
|
const brandOptionalTypesByInternalIndex = new Map();
|
|
1195
1462
|
const functionSignaturesByFacade = new Map();
|
|
1463
|
+
const sourceTypeImportsByFacade = new Map();
|
|
1196
1464
|
for (const m of modules) {
|
|
1197
1465
|
if (m.filePath.startsWith("__tsonic/"))
|
|
1198
1466
|
continue;
|
|
@@ -1203,13 +1471,37 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
1203
1471
|
const sourceIndex = sourceIndexByFileKey.get(moduleKey);
|
|
1204
1472
|
if (!sourceIndex)
|
|
1205
1473
|
continue;
|
|
1474
|
+
const allInterfaces = m.body.filter((s) => s.kind === "interfaceDeclaration");
|
|
1475
|
+
const allTypeAliases = m.body.filter((s) => s.kind === "typeAliasDeclaration");
|
|
1206
1476
|
const hasExportedSourceFunctions = sourceIndex.exportedFunctionSignaturesByName.size > 0;
|
|
1207
1477
|
if (exportedClasses.length === 0 &&
|
|
1208
1478
|
exportedInterfaces.length === 0 &&
|
|
1209
1479
|
exportedAliases.length === 0 &&
|
|
1480
|
+
allInterfaces.length === 0 &&
|
|
1481
|
+
allTypeAliases.length === 0 &&
|
|
1210
1482
|
!hasExportedSourceFunctions)
|
|
1211
1483
|
continue;
|
|
1212
1484
|
const info = facadesByNamespace.get(m.namespace) ?? ensureFacade(m.namespace);
|
|
1485
|
+
const brandTargets = brandOptionalTypesByInternalIndex.get(info.internalIndexDtsPath) ??
|
|
1486
|
+
new Set();
|
|
1487
|
+
for (const iface of allInterfaces) {
|
|
1488
|
+
brandTargets.add(iface.name);
|
|
1489
|
+
}
|
|
1490
|
+
for (const alias of allTypeAliases) {
|
|
1491
|
+
const sourceAlias = sourceIndex.typeAliasesByName.get(alias.name);
|
|
1492
|
+
if (!sourceAlias)
|
|
1493
|
+
continue;
|
|
1494
|
+
const aliasType = unwrapParens(sourceAlias.type);
|
|
1495
|
+
if (!ts.isTypeLiteralNode(aliasType))
|
|
1496
|
+
continue;
|
|
1497
|
+
const arity = sourceAlias.typeParameters.length;
|
|
1498
|
+
const internalAliasName = `${alias.name}__Alias${arity > 0 ? `_${arity}` : ""}`;
|
|
1499
|
+
brandTargets.add(alias.name);
|
|
1500
|
+
brandTargets.add(internalAliasName);
|
|
1501
|
+
}
|
|
1502
|
+
if (brandTargets.size > 0) {
|
|
1503
|
+
brandOptionalTypesByInternalIndex.set(info.internalIndexDtsPath, brandTargets);
|
|
1504
|
+
}
|
|
1213
1505
|
for (const [name, signatures,] of sourceIndex.exportedFunctionSignaturesByName) {
|
|
1214
1506
|
if (signatures.length === 0)
|
|
1215
1507
|
continue;
|
|
@@ -1219,6 +1511,28 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
1219
1511
|
list.push(...signatures);
|
|
1220
1512
|
byName.set(name, list);
|
|
1221
1513
|
functionSignaturesByFacade.set(info.facadeDtsPath, byName);
|
|
1514
|
+
const importsByLocal = sourceTypeImportsByFacade.get(info.facadeDtsPath) ??
|
|
1515
|
+
new Map();
|
|
1516
|
+
for (const signature of signatures) {
|
|
1517
|
+
for (const binding of collectSourceTypeImportsForSignature(signature, sourceIndex.typeImportsByLocalName)) {
|
|
1518
|
+
const existing = importsByLocal.get(binding.localName);
|
|
1519
|
+
if (existing) {
|
|
1520
|
+
if (existing.source !== binding.source ||
|
|
1521
|
+
existing.importedName !== binding.importedName) {
|
|
1522
|
+
return {
|
|
1523
|
+
ok: false,
|
|
1524
|
+
error: `Conflicting source type import alias '${binding.localName}' while augmenting ${info.facadeDtsPath}.\n` +
|
|
1525
|
+
`- ${existing.importedName} from '${existing.source}'\n` +
|
|
1526
|
+
`- ${binding.importedName} from '${binding.source}'\n` +
|
|
1527
|
+
`Fix: disambiguate source type imports for exported function signatures.`,
|
|
1528
|
+
};
|
|
1529
|
+
}
|
|
1530
|
+
continue;
|
|
1531
|
+
}
|
|
1532
|
+
importsByLocal.set(binding.localName, binding);
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
sourceTypeImportsByFacade.set(info.facadeDtsPath, importsByLocal);
|
|
1222
1536
|
}
|
|
1223
1537
|
for (const cls of exportedClasses) {
|
|
1224
1538
|
const memberTypes = sourceIndex.memberTypesByClassAndMember.get(cls.name);
|
|
@@ -1243,13 +1557,21 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
1243
1557
|
if (!wrappersResult.ok)
|
|
1244
1558
|
return wrappersResult;
|
|
1245
1559
|
const wrappers = wrappersResult.value;
|
|
1246
|
-
|
|
1560
|
+
const canUseSourceTypeText = !typeNodeUsesImportedTypeNames(sourceMember.typeNode, sourceIndex.typeImportsByLocalName);
|
|
1561
|
+
if (!canUseSourceTypeText &&
|
|
1562
|
+
wrappers.length === 0 &&
|
|
1563
|
+
!sourceMember.isOptional) {
|
|
1247
1564
|
continue;
|
|
1565
|
+
}
|
|
1248
1566
|
const list = overridesByInternalIndex.get(info.internalIndexDtsPath) ?? [];
|
|
1249
1567
|
list.push({
|
|
1250
1568
|
namespace: m.namespace,
|
|
1251
1569
|
className: cls.name,
|
|
1252
1570
|
memberName: member.name,
|
|
1571
|
+
sourceTypeText: canUseSourceTypeText
|
|
1572
|
+
? sourceMember.typeText
|
|
1573
|
+
: undefined,
|
|
1574
|
+
replaceWithSourceType: canUseSourceTypeText,
|
|
1253
1575
|
isOptional: sourceMember.isOptional,
|
|
1254
1576
|
wrappers,
|
|
1255
1577
|
});
|
|
@@ -1288,14 +1610,11 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
1288
1610
|
: undefined,
|
|
1289
1611
|
replaceWithSourceType: canUseSourceTypeText,
|
|
1290
1612
|
isOptional: sourceMember.isOptional,
|
|
1613
|
+
emitOptionalPropertySyntax: true,
|
|
1291
1614
|
wrappers,
|
|
1292
1615
|
});
|
|
1293
1616
|
overridesByInternalIndex.set(info.internalIndexDtsPath, list);
|
|
1294
1617
|
}
|
|
1295
|
-
const brandTargets = brandOptionalTypesByInternalIndex.get(info.internalIndexDtsPath) ??
|
|
1296
|
-
new Set();
|
|
1297
|
-
brandTargets.add(iface.name);
|
|
1298
|
-
brandOptionalTypesByInternalIndex.set(info.internalIndexDtsPath, brandTargets);
|
|
1299
1618
|
}
|
|
1300
1619
|
for (const alias of exportedAliases) {
|
|
1301
1620
|
const sourceAlias = sourceIndex.typeAliasesByName.get(alias.name);
|
|
@@ -1306,10 +1625,6 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
1306
1625
|
continue;
|
|
1307
1626
|
const arity = sourceAlias.typeParameters.length;
|
|
1308
1627
|
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
1628
|
for (const member of aliasType.members) {
|
|
1314
1629
|
if (!ts.isPropertySignature(member))
|
|
1315
1630
|
continue;
|
|
@@ -1361,6 +1676,16 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
|
|
|
1361
1676
|
if (!result.ok)
|
|
1362
1677
|
return result;
|
|
1363
1678
|
}
|
|
1679
|
+
for (const [facadePath, importsByLocalName] of sourceTypeImportsByFacade) {
|
|
1680
|
+
const result = ensureSourceTypeImportsForFacade(facadePath, importsByLocalName);
|
|
1681
|
+
if (!result.ok)
|
|
1682
|
+
return result;
|
|
1683
|
+
}
|
|
1684
|
+
for (const info of facadesByNamespace.values()) {
|
|
1685
|
+
const result = ensureInternalTypeImportsForFacade(info.facadeDtsPath);
|
|
1686
|
+
if (!result.ok)
|
|
1687
|
+
return result;
|
|
1688
|
+
}
|
|
1364
1689
|
return { ok: true, value: undefined };
|
|
1365
1690
|
};
|
|
1366
1691
|
//# sourceMappingURL=library-bindings-augment.js.map
|