@tsonic/cli 0.0.61 → 0.0.63

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