@nudojs/harvester 0.4.2 → 0.4.4
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/index.d.ts +72 -2
- package/dist/index.js +2737 -500
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -9,12 +9,16 @@ import {
|
|
|
9
9
|
joinAbs,
|
|
10
10
|
relationFn,
|
|
11
11
|
getFnImpl,
|
|
12
|
-
substAbs
|
|
12
|
+
substAbs,
|
|
13
|
+
formatShape,
|
|
14
|
+
absShapeKey,
|
|
15
|
+
termToString
|
|
13
16
|
} from "@nudojs/core";
|
|
14
17
|
import { absLit } from "@nudojs/core/internal";
|
|
15
18
|
var MAX_ALIAS_DEPTH = 8;
|
|
16
19
|
function scopeKey(scope) {
|
|
17
|
-
|
|
20
|
+
const base = scope.moduleName ?? "\0global";
|
|
21
|
+
return scope.nsRec && scope.ns.length > 0 ? `${base}\0ns:${scope.ns.join(".")}` : base;
|
|
18
22
|
}
|
|
19
23
|
function absExact(shape) {
|
|
20
24
|
return makeAbs(shape, void 0, void 0, "exact");
|
|
@@ -115,10 +119,14 @@ function harvestDts(files, opts) {
|
|
|
115
119
|
typeParams: /* @__PURE__ */ new Set(),
|
|
116
120
|
moduleAliases: [],
|
|
117
121
|
importEquals: /* @__PURE__ */ new Map(),
|
|
122
|
+
exportEquals: /* @__PURE__ */ new Map(),
|
|
118
123
|
declaredModules: /* @__PURE__ */ new Set(),
|
|
119
124
|
pending: [],
|
|
120
125
|
symbols: 0,
|
|
121
|
-
skipped: 0
|
|
126
|
+
skipped: 0,
|
|
127
|
+
degraded: 0,
|
|
128
|
+
degradedMark: 0,
|
|
129
|
+
anonDefaultClass: 0
|
|
122
130
|
};
|
|
123
131
|
let parsed = 0;
|
|
124
132
|
for (const file of files) {
|
|
@@ -143,10 +151,16 @@ function harvestDts(files, opts) {
|
|
|
143
151
|
parsed++;
|
|
144
152
|
}
|
|
145
153
|
materialize(ctx);
|
|
154
|
+
const stats = {
|
|
155
|
+
files: parsed,
|
|
156
|
+
symbols: ctx.symbols,
|
|
157
|
+
skipped: ctx.skipped
|
|
158
|
+
};
|
|
159
|
+
if (ctx.degraded > 0) stats.degraded = ctx.degraded;
|
|
146
160
|
return {
|
|
147
161
|
globals: ctx.globals,
|
|
148
162
|
modules: ctx.modules,
|
|
149
|
-
stats
|
|
163
|
+
stats
|
|
150
164
|
};
|
|
151
165
|
}
|
|
152
166
|
function emitEnvModule(env, pkgName) {
|
|
@@ -173,7 +187,8 @@ function emitEnvModule(env, pkgName) {
|
|
|
173
187
|
lines.push("// Auto-generated by @nudojs/harvester \u2014 DO NOT EDIT");
|
|
174
188
|
lines.push(`// Source package: ${pkgName}`);
|
|
175
189
|
lines.push(
|
|
176
|
-
|
|
190
|
+
// 族 27 Bug 811:泛型 α 的 var term 要落盘 ⇒ 生成代码需要 `termVar`。
|
|
191
|
+
'import { num, str, bool, never as absNever, unknown as absUnknown, numLit, strLit, boolLit, objOf, relationFn, abs as makeAbs, v as termVar } from "@nudojs/core";'
|
|
177
192
|
);
|
|
178
193
|
lines.push("");
|
|
179
194
|
lines.push("export function defineEnv() {");
|
|
@@ -199,11 +214,23 @@ function emitEnvModule(env, pkgName) {
|
|
|
199
214
|
lines.push("");
|
|
200
215
|
return lines.join("\n");
|
|
201
216
|
}
|
|
217
|
+
function nullDict() {
|
|
218
|
+
return /* @__PURE__ */ Object.create(null);
|
|
219
|
+
}
|
|
220
|
+
function toNullDict(src) {
|
|
221
|
+
if (Object.getPrototypeOf(src) === null) return src;
|
|
222
|
+
const out = nullDict();
|
|
223
|
+
for (const key of Object.keys(src)) out[key] = src[key];
|
|
224
|
+
return out;
|
|
225
|
+
}
|
|
202
226
|
function recordFor(ctx, scope) {
|
|
227
|
+
ctx.globals = toNullDict(ctx.globals);
|
|
228
|
+
ctx.modules = toNullDict(ctx.modules);
|
|
229
|
+
if (scope.nsRec) return scope.nsRec;
|
|
203
230
|
if (scope.moduleName === void 0) return ctx.globals;
|
|
204
|
-
let rec = ctx.modules[scope.moduleName];
|
|
231
|
+
let rec = Object.hasOwn(ctx.modules, scope.moduleName) ? ctx.modules[scope.moduleName] : void 0;
|
|
205
232
|
if (!rec) {
|
|
206
|
-
rec =
|
|
233
|
+
rec = nullDict();
|
|
207
234
|
ctx.modules[scope.moduleName] = rec;
|
|
208
235
|
}
|
|
209
236
|
return rec;
|
|
@@ -213,6 +240,10 @@ function collectStatements(ctx, statements, scope) {
|
|
|
213
240
|
collectStatement(ctx, stmt, scope);
|
|
214
241
|
}
|
|
215
242
|
}
|
|
243
|
+
function isDefaultExportDecl(node) {
|
|
244
|
+
const flags = ts.getCombinedModifierFlags(node);
|
|
245
|
+
return (flags & ts.ModifierFlags.Default) !== 0;
|
|
246
|
+
}
|
|
216
247
|
function collectStatement(ctx, stmt, scope) {
|
|
217
248
|
if (ts.isModuleDeclaration(stmt)) {
|
|
218
249
|
const body = stmt.body;
|
|
@@ -226,22 +257,36 @@ function collectStatement(ctx, stmt, scope) {
|
|
|
226
257
|
} else if (stmt.name.text === "global") {
|
|
227
258
|
collectStatements(ctx, body.statements, { moduleName: void 0, ns: [] });
|
|
228
259
|
} else {
|
|
229
|
-
|
|
260
|
+
const nsName = stmt.name.text;
|
|
261
|
+
const nsRec = nullDict();
|
|
262
|
+
collectStatements(ctx, body.statements, {
|
|
263
|
+
moduleName: scope.moduleName,
|
|
264
|
+
ns: [...scope.ns, nsName],
|
|
265
|
+
nsRec
|
|
266
|
+
});
|
|
267
|
+
ctx.pending.push({ t: "ns", scope, name: nsName, rec: nsRec });
|
|
268
|
+
ctx.symbols++;
|
|
230
269
|
}
|
|
231
270
|
return;
|
|
232
271
|
}
|
|
233
272
|
if (ts.isFunctionDeclaration(stmt)) {
|
|
234
273
|
if (!stmt.name) {
|
|
274
|
+
if (isDefaultExportDecl(stmt)) {
|
|
275
|
+
ctx.pending.push({ t: "fn", scope, name: "default", decls: [stmt] });
|
|
276
|
+
ctx.symbols++;
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
235
279
|
ctx.skipped++;
|
|
236
280
|
return;
|
|
237
281
|
}
|
|
238
282
|
const name = stmt.name.text;
|
|
239
|
-
const
|
|
283
|
+
const exportName = isDefaultExportDecl(stmt) ? "default" : name;
|
|
284
|
+
const existing = ctx.pending.find((p) => p.t === "fn" && p.name === exportName && scopeKey(p.scope) === scopeKey(scope));
|
|
240
285
|
if (existing && existing.t === "fn") {
|
|
241
286
|
existing.decls.push(stmt);
|
|
242
287
|
return;
|
|
243
288
|
}
|
|
244
|
-
ctx.pending.push({ t: "fn", scope, name, decls: [stmt] });
|
|
289
|
+
ctx.pending.push({ t: "fn", scope, name: exportName, decls: [stmt] });
|
|
245
290
|
ctx.symbols++;
|
|
246
291
|
return;
|
|
247
292
|
}
|
|
@@ -279,13 +324,24 @@ function collectStatement(ctx, stmt, scope) {
|
|
|
279
324
|
}
|
|
280
325
|
if (ts.isClassDeclaration(stmt)) {
|
|
281
326
|
if (!stmt.name) {
|
|
282
|
-
|
|
327
|
+
if (isDefaultExportDecl(stmt)) {
|
|
328
|
+
const synth = `__nudo_default_class_${ctx.anonDefaultClass++}`;
|
|
329
|
+
registerTypeDecl(ctx, scope, synth, stmt);
|
|
330
|
+
if (!ctx.classes.has(synth)) ctx.classes.set(synth, stmt);
|
|
331
|
+
ctx.pending.push({ t: "value", scope, name: "default", lookup: synth });
|
|
332
|
+
} else {
|
|
333
|
+
ctx.skipped++;
|
|
334
|
+
}
|
|
283
335
|
return;
|
|
284
336
|
}
|
|
285
337
|
registerTypeDecl(ctx, scope, stmt.name.text, stmt);
|
|
286
338
|
if (!ctx.classes.has(stmt.name.text)) ctx.classes.set(stmt.name.text, stmt);
|
|
287
|
-
|
|
288
|
-
|
|
339
|
+
if (isDefaultExportDecl(stmt)) {
|
|
340
|
+
ctx.pending.push({ t: "value", scope, name: "default", lookup: stmt.name.text });
|
|
341
|
+
} else {
|
|
342
|
+
ctx.pending.push({ t: "value", scope, name: stmt.name.text });
|
|
343
|
+
ctx.symbols++;
|
|
344
|
+
}
|
|
289
345
|
return;
|
|
290
346
|
}
|
|
291
347
|
if (ts.isEnumDeclaration(stmt)) {
|
|
@@ -327,16 +383,15 @@ function collectStatement(ctx, stmt, scope) {
|
|
|
327
383
|
return;
|
|
328
384
|
}
|
|
329
385
|
if (stmt.exportClause && ts.isNamedExports(stmt.exportClause)) {
|
|
386
|
+
const clauseTypeOnly = stmt.isTypeOnly === true;
|
|
330
387
|
for (const el of stmt.exportClause.elements) {
|
|
331
388
|
const localName = (el.propertyName ?? el.name).text;
|
|
332
389
|
const exportedName = el.name.text;
|
|
333
|
-
|
|
334
|
-
if (isValue) {
|
|
335
|
-
ctx.pending.push({ t: "value", scope, name: exportedName, lookup: localName });
|
|
336
|
-
ctx.symbols++;
|
|
337
|
-
} else {
|
|
390
|
+
if (clauseTypeOnly || el.isTypeOnly) {
|
|
338
391
|
ctx.skipped++;
|
|
392
|
+
continue;
|
|
339
393
|
}
|
|
394
|
+
ctx.pending.push({ t: "value", scope, name: exportedName, lookup: localName });
|
|
340
395
|
}
|
|
341
396
|
return;
|
|
342
397
|
}
|
|
@@ -344,13 +399,20 @@ function collectStatement(ctx, stmt, scope) {
|
|
|
344
399
|
return;
|
|
345
400
|
}
|
|
346
401
|
if (ts.isExportAssignment(stmt)) {
|
|
347
|
-
if (stmt.expression && ts.isIdentifier(stmt.expression) && scope.moduleName !== void 0) {
|
|
402
|
+
if (stmt.isExportEquals && stmt.expression && ts.isIdentifier(stmt.expression) && scope.moduleName !== void 0) {
|
|
348
403
|
const target = ctx.importEquals.get(scopeKey(scope))?.get(stmt.expression.text);
|
|
349
404
|
if (target !== void 0) {
|
|
350
405
|
ctx.moduleAliases.push([scope.moduleName, target]);
|
|
351
406
|
return;
|
|
352
407
|
}
|
|
353
408
|
}
|
|
409
|
+
if (stmt.expression && ts.isIdentifier(stmt.expression)) {
|
|
410
|
+
if (stmt.isExportEquals && scope.moduleName !== void 0) {
|
|
411
|
+
ctx.exportEquals.set(scope.moduleName, stmt.expression.text);
|
|
412
|
+
}
|
|
413
|
+
ctx.pending.push({ t: "value", scope, name: "default", lookup: stmt.expression.text });
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
354
416
|
ctx.skipped++;
|
|
355
417
|
return;
|
|
356
418
|
}
|
|
@@ -366,58 +428,198 @@ function registerTypeDecl(ctx, scope, name, decl) {
|
|
|
366
428
|
}
|
|
367
429
|
};
|
|
368
430
|
push(ctx.interfaces, name);
|
|
369
|
-
if (ts.isClassDeclaration(decl)) return;
|
|
370
431
|
const qualified = [...scope.ns, name].join(".");
|
|
371
432
|
if (qualified !== name) push(ctx.qualifiedInterfaces, qualified);
|
|
372
433
|
}
|
|
373
|
-
function
|
|
434
|
+
function namespaceChainOf(decl) {
|
|
435
|
+
const cached = nsChainCache.get(decl);
|
|
436
|
+
if (cached) return cached;
|
|
437
|
+
const chain = [];
|
|
438
|
+
let node = decl.parent;
|
|
439
|
+
while (node) {
|
|
440
|
+
if (ts.isModuleDeclaration(node) && ts.isIdentifier(node.name) && node.name.text !== "global") {
|
|
441
|
+
chain.unshift(node.name.text);
|
|
442
|
+
}
|
|
443
|
+
node = node.parent;
|
|
444
|
+
}
|
|
445
|
+
nsChainCache.set(decl, chain);
|
|
446
|
+
return chain;
|
|
447
|
+
}
|
|
448
|
+
var nsChainCache = /* @__PURE__ */ new WeakMap();
|
|
449
|
+
function inAmbientModuleBlock(decl) {
|
|
450
|
+
let node = decl.parent;
|
|
451
|
+
while (node) {
|
|
452
|
+
if (ts.isModuleDeclaration(node) && ts.isStringLiteral(node.name)) return true;
|
|
453
|
+
node = node.parent;
|
|
454
|
+
}
|
|
455
|
+
return false;
|
|
456
|
+
}
|
|
457
|
+
function heritageBaseName(expr) {
|
|
458
|
+
const text = (node) => {
|
|
459
|
+
if (ts.isIdentifier(node)) return node.text;
|
|
460
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
461
|
+
const left = text(node.expression);
|
|
462
|
+
if (left === void 0) return void 0;
|
|
463
|
+
return `${left}.${node.name.text}`;
|
|
464
|
+
}
|
|
465
|
+
return void 0;
|
|
466
|
+
};
|
|
467
|
+
return text(expr.expression);
|
|
468
|
+
}
|
|
469
|
+
function computedMemberName(name) {
|
|
470
|
+
if (!name || !ts.isComputedPropertyName(name)) return void 0;
|
|
471
|
+
const expr = name.expression;
|
|
472
|
+
if (ts.isPropertyAccessExpression(expr) && ts.isIdentifier(expr.expression) && expr.expression.text === "Symbol") {
|
|
473
|
+
if (expr.name.text === "iterator") return "@@iterator";
|
|
474
|
+
if (expr.name.text === "asyncIterator") return "@@asyncIterator";
|
|
475
|
+
return void 0;
|
|
476
|
+
}
|
|
477
|
+
if (ts.isStringLiteral(expr)) return expr.text;
|
|
478
|
+
return void 0;
|
|
479
|
+
}
|
|
480
|
+
function resolveIfaceDecls(ctx, name, refNs) {
|
|
481
|
+
const short = name.includes(".") ? name.split(".").pop() : name;
|
|
482
|
+
const qualified = name !== short;
|
|
483
|
+
const seen = /* @__PURE__ */ new Set();
|
|
484
|
+
const out = [];
|
|
485
|
+
const add = (d) => {
|
|
486
|
+
if (seen.has(d)) return;
|
|
487
|
+
seen.add(d);
|
|
488
|
+
out.push(d);
|
|
489
|
+
};
|
|
490
|
+
if (qualified) {
|
|
491
|
+
if (refNs && refNs.length > 0) {
|
|
492
|
+
for (let i = refNs.length - 1; i >= 0; i--) {
|
|
493
|
+
const rel = [...refNs.slice(0, i + 1), name].join(".");
|
|
494
|
+
const decls = ctx.qualifiedInterfaces.get(rel);
|
|
495
|
+
if (decls && decls.length > 0) {
|
|
496
|
+
for (const d of decls) add(d);
|
|
497
|
+
return out;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
for (const d of ctx.qualifiedInterfaces.get(name) ?? []) add(d);
|
|
502
|
+
for (const d of ctx.interfaces.get(short) ?? []) {
|
|
503
|
+
if (namespaceChainOf(d).length === 0 && inAmbientModuleBlock(d)) add(d);
|
|
504
|
+
}
|
|
505
|
+
return out;
|
|
506
|
+
}
|
|
507
|
+
if (refNs && refNs.length > 0) {
|
|
508
|
+
for (let i = refNs.length - 1; i >= 0; i--) {
|
|
509
|
+
const rel = [...refNs.slice(0, i + 1), name].join(".");
|
|
510
|
+
const decls = ctx.qualifiedInterfaces.get(rel);
|
|
511
|
+
if (decls && decls.length > 0) {
|
|
512
|
+
for (const d of decls) add(d);
|
|
513
|
+
return out;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
for (const d of ctx.interfaces.get(name) ?? []) {
|
|
518
|
+
if (namespaceChainOf(d).length === 0) add(d);
|
|
519
|
+
}
|
|
520
|
+
return out;
|
|
521
|
+
}
|
|
522
|
+
function interfaceDeclsOf(ctx, typeNode, chain, refNs) {
|
|
374
523
|
const name = typeof typeNode === "string" ? typeNode : ts.isTypeReferenceNode(typeNode) ? typeNameString(typeNode.typeName) : void 0;
|
|
375
524
|
if (!name) return [];
|
|
376
|
-
const
|
|
377
|
-
|
|
525
|
+
const visited = chain ?? /* @__PURE__ */ new Set();
|
|
526
|
+
if (visited.has(name)) return [];
|
|
527
|
+
const nextChain = new Set(visited);
|
|
528
|
+
nextChain.add(name);
|
|
378
529
|
const seen = /* @__PURE__ */ new Set();
|
|
379
530
|
const out = [];
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
531
|
+
const add = (d) => {
|
|
532
|
+
if (seen.has(d)) return;
|
|
533
|
+
seen.add(d);
|
|
534
|
+
out.push(d);
|
|
535
|
+
};
|
|
536
|
+
for (const d of resolveIfaceDecls(ctx, name, refNs)) add(d);
|
|
537
|
+
for (const d of [...out]) {
|
|
538
|
+
for (const clause of d.heritageClauses ?? []) {
|
|
539
|
+
if (clause.token !== ts.SyntaxKind.ExtendsKeyword) continue;
|
|
540
|
+
for (const expr of clause.types) {
|
|
541
|
+
const baseName = heritageBaseName(expr);
|
|
542
|
+
if (!baseName) continue;
|
|
543
|
+
for (const b of interfaceDeclsOf(ctx, baseName, nextChain, namespaceChainOf(d))) add(b);
|
|
388
544
|
}
|
|
389
545
|
}
|
|
390
546
|
}
|
|
391
547
|
return out;
|
|
392
548
|
}
|
|
549
|
+
var realDeclNameCache = /* @__PURE__ */ new WeakMap();
|
|
550
|
+
function realDeclNames(ctx) {
|
|
551
|
+
const cached = realDeclNameCache.get(ctx);
|
|
552
|
+
if (cached) return cached;
|
|
553
|
+
const set = /* @__PURE__ */ new Set();
|
|
554
|
+
for (const p of ctx.pending) {
|
|
555
|
+
if (p.t === "iface-expand") continue;
|
|
556
|
+
set.add(`${scopeKey(p.scope)}::${p.name}`);
|
|
557
|
+
}
|
|
558
|
+
realDeclNameCache.set(ctx, set);
|
|
559
|
+
return set;
|
|
560
|
+
}
|
|
561
|
+
function declNameKey(scope, name) {
|
|
562
|
+
return `${scopeKey(scope)}::${name}`;
|
|
563
|
+
}
|
|
564
|
+
function optionalPropTypeNode(member) {
|
|
565
|
+
if (!member.type) return void 0;
|
|
566
|
+
if (!member.questionToken) return member.type;
|
|
567
|
+
return ts.factory.createUnionTypeNode([
|
|
568
|
+
member.type,
|
|
569
|
+
ts.factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword)
|
|
570
|
+
]);
|
|
571
|
+
}
|
|
572
|
+
function optionalMethodTypeNode(member) {
|
|
573
|
+
const fn = ts.factory.createFunctionTypeNode(
|
|
574
|
+
member.typeParameters,
|
|
575
|
+
member.parameters,
|
|
576
|
+
// 无返回类型标注 ⇒ `any`(`signatureToFnSig` 对缺省返回位的口径是 unknown)
|
|
577
|
+
member.type ?? ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)
|
|
578
|
+
);
|
|
579
|
+
return ts.factory.createUnionTypeNode([
|
|
580
|
+
fn,
|
|
581
|
+
ts.factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword)
|
|
582
|
+
]);
|
|
583
|
+
}
|
|
393
584
|
function expandInterfaceAsModuleMembers(ctx, scope, ifaces) {
|
|
585
|
+
const real = realDeclNames(ctx);
|
|
586
|
+
const methods = /* @__PURE__ */ new Map();
|
|
587
|
+
const props = /* @__PURE__ */ new Map();
|
|
394
588
|
for (const iface of ifaces) {
|
|
589
|
+
if ((iface.typeParameters ?? []).length > 0) continue;
|
|
395
590
|
for (const member of iface.members) {
|
|
396
591
|
if (ts.isMethodSignature(member)) {
|
|
397
592
|
const name = memberName(member);
|
|
398
593
|
if (name === void 0) continue;
|
|
399
|
-
const
|
|
400
|
-
|
|
401
|
-
);
|
|
402
|
-
if (existing && existing.t === "fn") {
|
|
403
|
-
existing.decls.push(member);
|
|
404
|
-
continue;
|
|
405
|
-
}
|
|
406
|
-
ctx.pending.push({ t: "fn", scope, name, decls: [member] });
|
|
407
|
-
ctx.symbols++;
|
|
594
|
+
const list = methods.get(name);
|
|
595
|
+
if (list) list.push(member);
|
|
596
|
+
else methods.set(name, [member]);
|
|
408
597
|
continue;
|
|
409
598
|
}
|
|
410
599
|
if (ts.isPropertySignature(member)) {
|
|
411
600
|
const name = memberName(member);
|
|
412
601
|
if (name === void 0) continue;
|
|
413
|
-
|
|
414
|
-
continue;
|
|
415
|
-
}
|
|
416
|
-
ctx.pending.push({ t: "var", scope, name, typeNode: member.type });
|
|
417
|
-
ctx.symbols++;
|
|
602
|
+
props.set(name, member);
|
|
418
603
|
}
|
|
419
604
|
}
|
|
420
605
|
}
|
|
606
|
+
for (const [name, decls] of methods) {
|
|
607
|
+
if (props.has(name)) continue;
|
|
608
|
+
if (real.has(declNameKey(scope, name))) continue;
|
|
609
|
+
const first = decls[0];
|
|
610
|
+
if (first.questionToken) {
|
|
611
|
+
ctx.pending.push({ t: "var", scope, name, typeNode: optionalMethodTypeNode(first) });
|
|
612
|
+
ctx.symbols++;
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
ctx.pending.push({ t: "fn", scope, name, decls });
|
|
616
|
+
ctx.symbols++;
|
|
617
|
+
}
|
|
618
|
+
for (const [name, member] of props) {
|
|
619
|
+
if (real.has(declNameKey(scope, name))) continue;
|
|
620
|
+
ctx.pending.push({ t: "var", scope, name, typeNode: optionalPropTypeNode(member) });
|
|
621
|
+
ctx.symbols++;
|
|
622
|
+
}
|
|
421
623
|
}
|
|
422
624
|
function registerAlias(ctx, scope, stmt) {
|
|
423
625
|
const name = stmt.name.text;
|
|
@@ -432,6 +634,126 @@ function registerAlias(ctx, scope, stmt) {
|
|
|
432
634
|
push(ctx.aliases, name);
|
|
433
635
|
const qualified = [...scope.ns, name].join(".");
|
|
434
636
|
if (qualified !== name) push(ctx.qualifiedAliases, qualified);
|
|
637
|
+
registerRecursiveAliases(ctx, name);
|
|
638
|
+
}
|
|
639
|
+
var RECURSIVE_ALIAS_MARKER = "__nudoRecursiveAlias";
|
|
640
|
+
var aliasRefsCache = /* @__PURE__ */ new WeakMap();
|
|
641
|
+
var recursiveAliasCache = /* @__PURE__ */ new WeakMap();
|
|
642
|
+
function aliasRefNames(decl) {
|
|
643
|
+
const hit = aliasRefsCache.get(decl);
|
|
644
|
+
if (hit) return hit;
|
|
645
|
+
const out = /* @__PURE__ */ new Set();
|
|
646
|
+
const visit = (n) => {
|
|
647
|
+
if (!n) return;
|
|
648
|
+
if (ts.isTypeReferenceNode(n)) out.add(typeNameString(n.typeName));
|
|
649
|
+
n.forEachChild(visit);
|
|
650
|
+
};
|
|
651
|
+
visit(decl.type);
|
|
652
|
+
aliasRefsCache.set(decl, out);
|
|
653
|
+
return out;
|
|
654
|
+
}
|
|
655
|
+
function aliasRefsOf(ctx, name) {
|
|
656
|
+
const out = /* @__PURE__ */ new Set();
|
|
657
|
+
for (const decl of ctx.aliases.get(name) ?? []) {
|
|
658
|
+
for (const r of aliasRefNames(decl)) out.add(r);
|
|
659
|
+
}
|
|
660
|
+
return out;
|
|
661
|
+
}
|
|
662
|
+
function aliasReachesSelf(ctx, name, aliasNames) {
|
|
663
|
+
const seen = /* @__PURE__ */ new Set();
|
|
664
|
+
const stack = [];
|
|
665
|
+
for (const r of aliasRefsOf(ctx, name)) if (aliasNames.has(r)) stack.push(r);
|
|
666
|
+
while (stack.length > 0) {
|
|
667
|
+
const cur = stack.pop();
|
|
668
|
+
if (cur === name) return true;
|
|
669
|
+
if (seen.has(cur)) continue;
|
|
670
|
+
seen.add(cur);
|
|
671
|
+
for (const r of aliasRefsOf(ctx, cur)) if (aliasNames.has(r)) stack.push(r);
|
|
672
|
+
}
|
|
673
|
+
return false;
|
|
674
|
+
}
|
|
675
|
+
function registerRecursiveAliases(ctx, newName) {
|
|
676
|
+
const aliasNames = new Set(ctx.aliases.keys());
|
|
677
|
+
const known = recursiveAliasCache.get(ctx) ?? /* @__PURE__ */ new Set();
|
|
678
|
+
const found = [];
|
|
679
|
+
if (!known.has(newName) && aliasReachesSelf(ctx, newName, aliasNames)) {
|
|
680
|
+
found.push(newName);
|
|
681
|
+
}
|
|
682
|
+
for (const name of aliasNames) {
|
|
683
|
+
if (name === newName || known.has(name)) continue;
|
|
684
|
+
if (!aliasRefsOf(ctx, name).has(newName)) continue;
|
|
685
|
+
if (aliasReachesSelf(ctx, name, aliasNames)) found.push(name);
|
|
686
|
+
}
|
|
687
|
+
for (const name of found) {
|
|
688
|
+
known.add(name);
|
|
689
|
+
ensureRecursiveAliasInterface(ctx, name);
|
|
690
|
+
}
|
|
691
|
+
recursiveAliasCache.set(ctx, known);
|
|
692
|
+
}
|
|
693
|
+
function isRecursiveAliasDecl(decl) {
|
|
694
|
+
return decl[RECURSIVE_ALIAS_MARKER] === true;
|
|
695
|
+
}
|
|
696
|
+
function ensureRecursiveAliasInterface(ctx, name) {
|
|
697
|
+
const decl = ctx.aliases.get(name)?.[0];
|
|
698
|
+
if (!decl) return;
|
|
699
|
+
const existing = ctx.interfaces.get(name) ?? [];
|
|
700
|
+
if (existing.some((d) => isRecursiveAliasDecl(d))) return;
|
|
701
|
+
const members = ts.isTypeLiteralNode(decl.type) ? [...decl.type.members] : [];
|
|
702
|
+
const iface = ts.factory.createInterfaceDeclaration(
|
|
703
|
+
void 0,
|
|
704
|
+
ts.factory.createIdentifier(name),
|
|
705
|
+
decl.typeParameters,
|
|
706
|
+
void 0,
|
|
707
|
+
ts.factory.createNodeArray(members)
|
|
708
|
+
);
|
|
709
|
+
iface[RECURSIVE_ALIAS_MARKER] = true;
|
|
710
|
+
ctx.interfaces.set(name, [...existing, iface]);
|
|
711
|
+
}
|
|
712
|
+
function classValueAbs(ctx, cls, key) {
|
|
713
|
+
const ctor = cls.members.find(ts.isConstructorDeclaration);
|
|
714
|
+
const params = [];
|
|
715
|
+
if (ctor) {
|
|
716
|
+
for (const param of ctor.parameters) {
|
|
717
|
+
if (isThisParameter(param)) continue;
|
|
718
|
+
let type = param.type ? mapType(ctx, param.type, 0) : absUnknown();
|
|
719
|
+
if (param.questionToken) type = absUnion([type, absUndefLit()]);
|
|
720
|
+
params.push(type);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
return absCtorSig(params, instanceFor(ctx, key));
|
|
724
|
+
}
|
|
725
|
+
function resolveLocalValue(ctx, scope, key) {
|
|
726
|
+
const rec = recordFor(ctx, scope);
|
|
727
|
+
if (Object.hasOwn(rec, key)) return rec[key];
|
|
728
|
+
if (scope.moduleName !== void 0 && Object.hasOwn(ctx.globals, key)) return ctx.globals[key];
|
|
729
|
+
const cls = ctx.classes.get(key);
|
|
730
|
+
if (cls) return classValueAbs(ctx, cls, key);
|
|
731
|
+
if (ctx.interfaces.has(key) || ctx.qualifiedInterfaces.has(key)) return instanceFor(ctx, key);
|
|
732
|
+
return void 0;
|
|
733
|
+
}
|
|
734
|
+
function mergeOverloadParams(sigs) {
|
|
735
|
+
const first = sigs[0];
|
|
736
|
+
if (!first || first.shape.k !== "fn") return void 0;
|
|
737
|
+
const base = first.shape.paramTypes ?? [];
|
|
738
|
+
if (base.length === 0) return void 0;
|
|
739
|
+
const perPos = base.map(() => []);
|
|
740
|
+
for (const s of sigs) {
|
|
741
|
+
if (s.shape.k !== "fn") return void 0;
|
|
742
|
+
const pts = s.shape.paramTypes ?? [];
|
|
743
|
+
if (pts.length !== base.length) return void 0;
|
|
744
|
+
for (let i = 0; i < pts.length; i++) {
|
|
745
|
+
const t = pts[i];
|
|
746
|
+
if (hasTypeVar(t)) return void 0;
|
|
747
|
+
perPos[i].push(t);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
return perPos.map((ts4) => ts4.length === 1 ? ts4[0] : absUnion(ts4));
|
|
751
|
+
}
|
|
752
|
+
function mergeModuleRecords(base, over) {
|
|
753
|
+
const merged = nullDict();
|
|
754
|
+
for (const k of Object.keys(base)) merged[k] = base[k];
|
|
755
|
+
for (const k of Object.keys(over)) merged[k] = over[k];
|
|
756
|
+
return merged;
|
|
435
757
|
}
|
|
436
758
|
function materialize(ctx) {
|
|
437
759
|
const expanded = [];
|
|
@@ -452,27 +774,83 @@ function materialize(ctx) {
|
|
|
452
774
|
}
|
|
453
775
|
}
|
|
454
776
|
ctx.pending = expanded;
|
|
777
|
+
const nsSlots = [];
|
|
778
|
+
const nsSlotsByName = /* @__PURE__ */ new Map();
|
|
455
779
|
for (const p of ctx.pending) {
|
|
456
780
|
if (p.t === "iface-expand") continue;
|
|
457
781
|
const rec = recordFor(ctx, p.scope);
|
|
782
|
+
if (p.t === "ns") {
|
|
783
|
+
const names = Object.keys(p.rec);
|
|
784
|
+
if (names.length === 0) {
|
|
785
|
+
continue;
|
|
786
|
+
}
|
|
787
|
+
const slots = nullDict();
|
|
788
|
+
for (const n of names) slots[n] = { value: p.rec[n] };
|
|
789
|
+
if (p.scope.moduleName !== void 0 && ctx.exportEquals.get(p.scope.moduleName) === p.name) {
|
|
790
|
+
const flat = recordFor(ctx, p.scope);
|
|
791
|
+
for (const [k, v] of Object.entries(p.rec)) {
|
|
792
|
+
if (Object.hasOwn(flat, k)) continue;
|
|
793
|
+
flat[k] = v;
|
|
794
|
+
}
|
|
795
|
+
nsSlots.push({ rec: p.rec, slots, flat });
|
|
796
|
+
continue;
|
|
797
|
+
}
|
|
798
|
+
const existing = rec[p.name];
|
|
799
|
+
if (existing === void 0) {
|
|
800
|
+
rec[p.name] = objOf(slots);
|
|
801
|
+
nsSlotsByName.set(`${scopeKey(p.scope)}\0${p.name}`, slots);
|
|
802
|
+
} else if (existing.shape.k === "fn") {
|
|
803
|
+
existing.shape.slots = slots;
|
|
804
|
+
nsSlotsByName.set(`${scopeKey(p.scope)}\0${p.name}`, slots);
|
|
805
|
+
} else {
|
|
806
|
+
ctx.skipped++;
|
|
807
|
+
continue;
|
|
808
|
+
}
|
|
809
|
+
nsSlots.push({ rec: p.rec, slots });
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
458
812
|
if (p.t === "fn") {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
813
|
+
const fnValue = () => {
|
|
814
|
+
const sigs = p.decls.map((d) => signatureToFnSig(ctx, d));
|
|
815
|
+
const value2 = sigs[0];
|
|
816
|
+
const shape0 = value2.shape.k === "fn" ? value2.shape : void 0;
|
|
817
|
+
if (shape0 && sigs.length > 1) {
|
|
818
|
+
const mergedParams = mergeOverloadParams(sigs);
|
|
819
|
+
if (mergedParams) {
|
|
820
|
+
shape0.paramTypes = mergedParams;
|
|
821
|
+
const rel0 = getFnImpl(value2)?.relation;
|
|
822
|
+
if (rel0) rel0.paramTypes = mergedParams;
|
|
823
|
+
}
|
|
824
|
+
const returns = [];
|
|
825
|
+
for (const s of sigs) {
|
|
826
|
+
if (s.shape.k !== "fn") continue;
|
|
827
|
+
const ret = s.shape.returnType;
|
|
828
|
+
if (ret && !isPlainUnknown(ret)) returns.push(ret);
|
|
829
|
+
}
|
|
830
|
+
const withVars = returns.filter((r) => hasTypeVar(r));
|
|
831
|
+
const pool = withVars.length > 0 ? withVars : returns;
|
|
832
|
+
const mergedRet = pool.length > 0 ? absUnion(pool) : absUnknown();
|
|
833
|
+
shape0.returnType = mergedRet;
|
|
834
|
+
const rel = getFnImpl(value2)?.relation;
|
|
835
|
+
if (rel) rel.returnType = mergedRet;
|
|
469
836
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
const
|
|
475
|
-
if (
|
|
837
|
+
return value2;
|
|
838
|
+
};
|
|
839
|
+
const existing = rec[p.name];
|
|
840
|
+
if (existing !== void 0) {
|
|
841
|
+
const nsStatic = nsSlotsByName.get(`${scopeKey(p.scope)}${p.name}`);
|
|
842
|
+
if (nsStatic && existing.shape.k === "obj") {
|
|
843
|
+
const value2 = fnValue();
|
|
844
|
+
value2.shape.slots = nsStatic;
|
|
845
|
+
rec[p.name] = value2;
|
|
846
|
+
}
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
const markBefore = ctx.degradedMark;
|
|
850
|
+
const value = fnValue();
|
|
851
|
+
if (ctx.degradedMark > markBefore) {
|
|
852
|
+
ctx.degraded++;
|
|
853
|
+
ctx.degradedMark = markBefore;
|
|
476
854
|
}
|
|
477
855
|
rec[p.name] = value;
|
|
478
856
|
continue;
|
|
@@ -480,30 +858,32 @@ function materialize(ctx) {
|
|
|
480
858
|
if (rec[p.name] !== void 0) continue;
|
|
481
859
|
switch (p.t) {
|
|
482
860
|
case "var": {
|
|
861
|
+
const markBefore = ctx.degradedMark;
|
|
483
862
|
const mapped = p.typeNode ? mapType(ctx, p.typeNode, 0) : absUnknown();
|
|
484
|
-
if (
|
|
485
|
-
ctx.
|
|
486
|
-
|
|
863
|
+
if (ctx.degradedMark > markBefore) {
|
|
864
|
+
ctx.degraded++;
|
|
865
|
+
ctx.degradedMark = markBefore;
|
|
487
866
|
}
|
|
488
867
|
rec[p.name] = mapped;
|
|
489
868
|
break;
|
|
490
869
|
}
|
|
491
870
|
case "value": {
|
|
492
|
-
const
|
|
871
|
+
const key = p.lookup ?? p.name;
|
|
872
|
+
const cls = ctx.classes.get(key);
|
|
493
873
|
if (cls) {
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
if (
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
if (param.questionToken) type = absUnion([type, absUndefLit()]);
|
|
500
|
-
params.push(type);
|
|
501
|
-
}
|
|
874
|
+
const markBefore = ctx.degradedMark;
|
|
875
|
+
rec[p.name] = classValueAbs(ctx, cls, key);
|
|
876
|
+
if (ctx.degradedMark > markBefore) {
|
|
877
|
+
ctx.degraded++;
|
|
878
|
+
ctx.degradedMark = markBefore;
|
|
502
879
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
rec[p.name] = instanceFor(ctx, p.lookup ?? p.name);
|
|
880
|
+
if (p.lookup !== void 0) ctx.symbols++;
|
|
881
|
+
break;
|
|
506
882
|
}
|
|
883
|
+
if (p.lookup !== void 0) {
|
|
884
|
+
break;
|
|
885
|
+
}
|
|
886
|
+
rec[p.name] = instanceFor(ctx, key);
|
|
507
887
|
break;
|
|
508
888
|
}
|
|
509
889
|
case "enum":
|
|
@@ -511,14 +891,69 @@ function materialize(ctx) {
|
|
|
511
891
|
break;
|
|
512
892
|
}
|
|
513
893
|
}
|
|
894
|
+
for (const p of ctx.pending) {
|
|
895
|
+
if (p.t !== "value" || p.lookup === void 0) continue;
|
|
896
|
+
const rec = recordFor(ctx, p.scope);
|
|
897
|
+
if (Object.hasOwn(rec, p.name)) continue;
|
|
898
|
+
const resolved = resolveLocalValue(ctx, p.scope, p.lookup);
|
|
899
|
+
if (resolved === void 0) {
|
|
900
|
+
ctx.skipped++;
|
|
901
|
+
continue;
|
|
902
|
+
}
|
|
903
|
+
rec[p.name] = resolved;
|
|
904
|
+
ctx.symbols++;
|
|
905
|
+
}
|
|
906
|
+
for (const { rec: nsRec, slots, flat } of nsSlots) {
|
|
907
|
+
for (const [k, v] of Object.entries(nsRec)) {
|
|
908
|
+
if (Object.hasOwn(slots, k)) continue;
|
|
909
|
+
slots[k] = { value: v };
|
|
910
|
+
if (flat && !Object.hasOwn(flat, k)) flat[k] = v;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
const aliasTargets = /* @__PURE__ */ new Map();
|
|
514
914
|
for (const [alias, target] of ctx.moduleAliases) {
|
|
515
|
-
|
|
516
|
-
|
|
915
|
+
const list = aliasTargets.get(alias);
|
|
916
|
+
if (list) {
|
|
917
|
+
if (!list.includes(target)) list.push(target);
|
|
918
|
+
} else {
|
|
919
|
+
aliasTargets.set(alias, [target]);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
const aliasResolved = /* @__PURE__ */ new Map();
|
|
923
|
+
const aliasResolving = /* @__PURE__ */ new Set();
|
|
924
|
+
const resolveModule = (name, depth) => {
|
|
925
|
+
const cached = aliasResolved.get(name);
|
|
926
|
+
if (cached) return cached;
|
|
927
|
+
if (depth > MAX_ALIAS_DEPTH || aliasResolving.has(name)) return void 0;
|
|
928
|
+
aliasResolving.add(name);
|
|
929
|
+
const own = ctx.modules[name];
|
|
930
|
+
const targets = aliasTargets.get(name);
|
|
931
|
+
let merged;
|
|
932
|
+
if (targets) {
|
|
933
|
+
for (const target of targets) {
|
|
934
|
+
const t = resolveModule(target, depth + 1);
|
|
935
|
+
if (!t) continue;
|
|
936
|
+
merged = merged ? mergeModuleRecords(merged, t) : t;
|
|
937
|
+
}
|
|
517
938
|
}
|
|
939
|
+
aliasResolving.delete(name);
|
|
940
|
+
if (!merged) return own;
|
|
941
|
+
const out = own && Object.keys(own).length > 0 ? mergeModuleRecords(merged, own) : merged;
|
|
942
|
+
aliasResolved.set(name, out);
|
|
943
|
+
return out;
|
|
944
|
+
};
|
|
945
|
+
for (const name of aliasTargets.keys()) {
|
|
946
|
+
const out = resolveModule(name, 0);
|
|
947
|
+
if (out) ctx.modules[name] = out;
|
|
518
948
|
}
|
|
519
949
|
}
|
|
520
950
|
function typeNameString(name) {
|
|
521
951
|
if (ts.isIdentifier(name)) return name.text;
|
|
952
|
+
if (ts.isPropertyAccessExpression(name)) {
|
|
953
|
+
const left = name.expression;
|
|
954
|
+
if (!ts.isIdentifier(left) && !ts.isPropertyAccessExpression(left)) return name.name.text;
|
|
955
|
+
return `${typeNameString(left)}.${name.name.text}`;
|
|
956
|
+
}
|
|
522
957
|
return `${typeNameString(name.left)}.${name.right.text}`;
|
|
523
958
|
}
|
|
524
959
|
function memberName(node) {
|
|
@@ -527,82 +962,220 @@ function memberName(node) {
|
|
|
527
962
|
if (ts.isIdentifier(n) || ts.isStringLiteral(n) || ts.isNumericLiteral(n)) return n.text;
|
|
528
963
|
return void 0;
|
|
529
964
|
}
|
|
530
|
-
function
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
const
|
|
965
|
+
function absBrandOpen(name) {
|
|
966
|
+
return absExact({ k: "brand", name, shape: absUnknown() });
|
|
967
|
+
}
|
|
968
|
+
function absFingerprint(a) {
|
|
969
|
+
const key = absShapeKey(a);
|
|
970
|
+
const term = a.term && a.term.op !== "lit" ? termToString(a.term) : "";
|
|
971
|
+
return term ? `${key}#${term}` : key;
|
|
972
|
+
}
|
|
973
|
+
function memberDict() {
|
|
974
|
+
return /* @__PURE__ */ Object.create(null);
|
|
975
|
+
}
|
|
976
|
+
function effectiveTypeArgs(ctx, decls, typeArgs) {
|
|
977
|
+
const tps = decls[0]?.typeParameters ?? [];
|
|
978
|
+
if (tps.length === 0) return [];
|
|
979
|
+
const out = [];
|
|
980
|
+
for (let i = 0; i < tps.length; i++) {
|
|
981
|
+
const tp = tps[i];
|
|
982
|
+
const explicit = typeArgs?.[i];
|
|
983
|
+
if (explicit !== void 0) {
|
|
984
|
+
out.push(explicit);
|
|
985
|
+
continue;
|
|
986
|
+
}
|
|
987
|
+
if (tp.default) {
|
|
988
|
+
out.push(mapType(ctx, tp.default, 0));
|
|
989
|
+
continue;
|
|
990
|
+
}
|
|
991
|
+
if (tp.constraint) {
|
|
992
|
+
const c = mapType(ctx, tp.constraint, 0);
|
|
993
|
+
out.push(isPlainUnknown(c) ? absUnknown() : c);
|
|
994
|
+
continue;
|
|
995
|
+
}
|
|
996
|
+
out.push(absUnknown());
|
|
997
|
+
}
|
|
998
|
+
return out;
|
|
999
|
+
}
|
|
1000
|
+
function mergeOverloadSigs(sigs) {
|
|
1001
|
+
const first = sigs[0];
|
|
1002
|
+
if (!first) return absUnknown();
|
|
1003
|
+
if (sigs.length === 1) return first;
|
|
1004
|
+
if (first.shape.k !== "fn") return first;
|
|
1005
|
+
const params = mergeOverloadParams(sigs) ?? first.shape.paramTypes ?? [];
|
|
1006
|
+
const returns = [];
|
|
1007
|
+
for (const s of sigs) {
|
|
1008
|
+
if (s.shape.k !== "fn") continue;
|
|
1009
|
+
const ret = s.shape.returnType;
|
|
1010
|
+
if (ret && !isPlainUnknown(ret)) returns.push(ret);
|
|
1011
|
+
}
|
|
1012
|
+
const withVars = returns.filter((r) => hasTypeVar(r));
|
|
1013
|
+
const pool = withVars.length > 0 ? withVars : returns;
|
|
1014
|
+
const mergedRet = pool.length > 0 ? absUnion(pool) : absUnknown();
|
|
1015
|
+
const rel0 = getFnImpl(first)?.relation;
|
|
1016
|
+
const extra = rel0 ? {
|
|
1017
|
+
...rel0.inferFrom ? { inferFrom: rel0.inferFrom } : {},
|
|
1018
|
+
...rel0.condFallback !== void 0 ? { condFallback: rel0.condFallback } : {}
|
|
1019
|
+
} : void 0;
|
|
1020
|
+
return absFnSig(params, mergedRet, extra);
|
|
1021
|
+
}
|
|
1022
|
+
function typeParamBindings(decls, args) {
|
|
1023
|
+
const map = /* @__PURE__ */ new Map();
|
|
1024
|
+
const tps = decls[0]?.typeParameters ?? [];
|
|
1025
|
+
tps.forEach((tp, i) => {
|
|
1026
|
+
const arg = args[i];
|
|
1027
|
+
if (arg) map.set(tp.name.text, arg);
|
|
1028
|
+
});
|
|
1029
|
+
return map;
|
|
1030
|
+
}
|
|
1031
|
+
function collectIfaceEntries(ctx, decls, args, chain, refNs, outerBindings) {
|
|
1032
|
+
const out = [];
|
|
1033
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1034
|
+
for (const d of decls) {
|
|
1035
|
+
if (seen.has(d)) continue;
|
|
1036
|
+
seen.add(d);
|
|
1037
|
+
out.push({ decl: d, typeArgs: [...args], bindings: typeParamBindings([d], args) });
|
|
1038
|
+
}
|
|
1039
|
+
for (const d of decls) {
|
|
1040
|
+
const dNs = namespaceChainOf(d);
|
|
1041
|
+
for (const clause of d.heritageClauses ?? []) {
|
|
1042
|
+
if (clause.token !== ts.SyntaxKind.ExtendsKeyword) continue;
|
|
1043
|
+
for (const expr of clause.types) {
|
|
1044
|
+
const baseName = heritageBaseName(expr);
|
|
1045
|
+
if (!baseName || chain.has(baseName)) continue;
|
|
1046
|
+
const baseDecls = resolveIfaceDecls(ctx, baseName, dNs);
|
|
1047
|
+
if (baseDecls.length === 0) continue;
|
|
1048
|
+
const explicit = expr.typeArguments ?? [];
|
|
1049
|
+
const baseArgs = explicit.length > 0 ? explicit.map((a) => mapType(ctx, a, 0, outerBindings)) : effectiveTypeArgs(ctx, baseDecls, void 0);
|
|
1050
|
+
const nextChain = new Set(chain);
|
|
1051
|
+
nextChain.add(baseName);
|
|
1052
|
+
out.push(
|
|
1053
|
+
...collectIfaceEntries(
|
|
1054
|
+
ctx,
|
|
1055
|
+
baseDecls,
|
|
1056
|
+
baseArgs,
|
|
1057
|
+
nextChain,
|
|
1058
|
+
dNs,
|
|
1059
|
+
typeParamBindings(baseDecls, baseArgs)
|
|
1060
|
+
)
|
|
1061
|
+
);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
return out;
|
|
1066
|
+
}
|
|
1067
|
+
function instanceFor(ctx, key, typeArgs, refNs) {
|
|
1068
|
+
const decls = resolveIfaceDecls(ctx, key, refNs);
|
|
535
1069
|
const decl = decls[0];
|
|
536
1070
|
if (!decl) return absUnknown();
|
|
537
|
-
|
|
538
|
-
|
|
1071
|
+
const nsTag = refNs && refNs.length > 0 ? `ns:${refNs.join(".")}:` : "";
|
|
1072
|
+
const cacheKey = `${nsTag}${key}`;
|
|
1073
|
+
if (ctx.expanding.has(cacheKey)) {
|
|
1074
|
+
return absBrandOpen(decl.name?.text ?? key);
|
|
539
1075
|
}
|
|
540
|
-
ctx.expanding.add(
|
|
1076
|
+
ctx.expanding.add(cacheKey);
|
|
541
1077
|
const savedTp = ctx.typeParams;
|
|
542
1078
|
ctx.typeParams = new Set(savedTp);
|
|
543
|
-
for (const d of decls) {
|
|
544
|
-
for (const tp of d.typeParameters ?? []) ctx.typeParams.add(tp.name.text);
|
|
545
|
-
}
|
|
546
|
-
const properties = {};
|
|
547
1079
|
try {
|
|
548
1080
|
for (const d of decls) {
|
|
1081
|
+
for (const tp of d.typeParameters ?? []) ctx.typeParams.add(tp.name.text);
|
|
1082
|
+
}
|
|
1083
|
+
const effective = effectiveTypeArgs(ctx, decls, typeArgs);
|
|
1084
|
+
const argKey = effective.length > 0 ? `${cacheKey}::${effective.map(absFingerprint).join("|")}` : cacheKey;
|
|
1085
|
+
const cached = ctx.instanceCache.get(argKey);
|
|
1086
|
+
if (cached !== void 0) return cached;
|
|
1087
|
+
const entries = collectIfaceEntries(
|
|
1088
|
+
ctx,
|
|
1089
|
+
decls,
|
|
1090
|
+
effective,
|
|
1091
|
+
/* @__PURE__ */ new Set([key]),
|
|
1092
|
+
refNs,
|
|
1093
|
+
typeParamBindings(decls, effective)
|
|
1094
|
+
);
|
|
1095
|
+
for (const entry of entries) {
|
|
1096
|
+
for (const tp of entry.decl.typeParameters ?? []) ctx.typeParams.add(tp.name.text);
|
|
1097
|
+
}
|
|
1098
|
+
const properties = memberDict();
|
|
1099
|
+
const methodSigs = /* @__PURE__ */ new Map();
|
|
1100
|
+
const optionalMethods = /* @__PURE__ */ new Map();
|
|
1101
|
+
for (const entry of entries) {
|
|
1102
|
+
const d = entry.decl;
|
|
549
1103
|
const members = d.members;
|
|
550
1104
|
for (const member of members) {
|
|
551
1105
|
const modifiers = member.modifiers;
|
|
552
1106
|
if (modifiers?.some((mod) => mod.kind === ts.SyntaxKind.StaticKeyword)) continue;
|
|
553
1107
|
if (ts.isPropertySignature(member) || ts.isPropertyDeclaration(member)) {
|
|
554
|
-
const name = memberName(member);
|
|
1108
|
+
const name = memberName(member) ?? computedMemberName(member.name);
|
|
555
1109
|
if (name === void 0) continue;
|
|
556
|
-
let type = member.type ? mapType(ctx, member.type, 0) : absUnknown();
|
|
1110
|
+
let type = member.type ? mapType(ctx, member.type, 0, entry.bindings) : absUnknown();
|
|
557
1111
|
if (member.questionToken) type = absUnion([type, absUndefLit()]);
|
|
558
1112
|
properties[name] = type;
|
|
1113
|
+
methodSigs.delete(name);
|
|
1114
|
+
optionalMethods.delete(name);
|
|
559
1115
|
} else if (ts.isMethodSignature(member) || ts.isMethodDeclaration(member)) {
|
|
560
|
-
const name = memberName(member);
|
|
561
|
-
if (name === void 0
|
|
562
|
-
properties[name]
|
|
1116
|
+
const name = memberName(member) ?? computedMemberName(member.name);
|
|
1117
|
+
if (name === void 0) continue;
|
|
1118
|
+
if (properties[name] !== void 0) continue;
|
|
1119
|
+
const sig = signatureToFnSig(ctx, member, entry.bindings);
|
|
1120
|
+
const list = methodSigs.get(name);
|
|
1121
|
+
if (list) list.push(sig);
|
|
1122
|
+
else methodSigs.set(name, [sig]);
|
|
1123
|
+
optionalMethods.set(
|
|
1124
|
+
name,
|
|
1125
|
+
(optionalMethods.get(name) ?? true) && member.questionToken !== void 0
|
|
1126
|
+
);
|
|
563
1127
|
} else if (ts.isGetAccessorDeclaration(member) || ts.isSetAccessorDeclaration(member)) {
|
|
564
|
-
const name = memberName(member);
|
|
1128
|
+
const name = memberName(member) ?? computedMemberName(member.name);
|
|
565
1129
|
if (name === void 0 || properties[name] !== void 0 || !member.type) continue;
|
|
566
|
-
properties[name] = mapType(ctx, member.type, 0);
|
|
1130
|
+
properties[name] = mapType(ctx, member.type, 0, entry.bindings);
|
|
567
1131
|
}
|
|
568
1132
|
}
|
|
569
1133
|
}
|
|
1134
|
+
for (const [name, sigs] of methodSigs) {
|
|
1135
|
+
const merged = mergeOverloadSigs(sigs);
|
|
1136
|
+
properties[name] = optionalMethods.get(name) ? absUnion([merged, absUndefLit()]) : merged;
|
|
1137
|
+
}
|
|
1138
|
+
const opaque = isRecursiveAliasDecl(decl) && Object.keys(properties).length === 0;
|
|
1139
|
+
let value = opaque ? absBrandOpen(decl.name?.text ?? key) : absBrand(decl.name?.text ?? key, properties);
|
|
1140
|
+
if (effective.length > 0 && Object.keys(properties).length <= 40) {
|
|
1141
|
+
const map = /* @__PURE__ */ new Map();
|
|
1142
|
+
const tps = decls[0].typeParameters ?? [];
|
|
1143
|
+
tps.forEach((tp, i) => {
|
|
1144
|
+
const arg = effective[i];
|
|
1145
|
+
if (arg) map.set(tp.name.text, arg);
|
|
1146
|
+
});
|
|
1147
|
+
if (map.size > 0) value = substAbs(value, map);
|
|
1148
|
+
}
|
|
1149
|
+
ctx.instanceCache.set(argKey, value);
|
|
1150
|
+
return value;
|
|
570
1151
|
} finally {
|
|
571
1152
|
ctx.typeParams = savedTp;
|
|
572
|
-
ctx.expanding.delete(
|
|
573
|
-
}
|
|
574
|
-
let value = absBrand(decl.name?.text ?? key, properties);
|
|
575
|
-
if (typeArgs && typeArgs.length > 0 && Object.keys(properties).length <= 40) {
|
|
576
|
-
const map = /* @__PURE__ */ new Map();
|
|
577
|
-
const tps = decls[0].typeParameters ?? [];
|
|
578
|
-
tps.forEach((tp, i) => {
|
|
579
|
-
const arg = typeArgs[i];
|
|
580
|
-
if (arg) map.set(tp.name.text, arg);
|
|
581
|
-
});
|
|
582
|
-
if (map.size > 0) value = substAbs(value, map);
|
|
1153
|
+
ctx.expanding.delete(cacheKey);
|
|
583
1154
|
}
|
|
584
|
-
ctx.instanceCache.set(cacheKey, value);
|
|
585
|
-
return value;
|
|
586
1155
|
}
|
|
587
|
-
function
|
|
1156
|
+
function isThisParameter(param) {
|
|
1157
|
+
const name = param.name;
|
|
1158
|
+
return ts.isIdentifier(name) && name.text === "this";
|
|
1159
|
+
}
|
|
1160
|
+
function signatureToFnSig(ctx, sig, bindings) {
|
|
588
1161
|
const savedTp = ctx.typeParams;
|
|
589
1162
|
ctx.typeParams = new Set(savedTp);
|
|
590
1163
|
for (const tp of sig.typeParameters ?? []) {
|
|
591
1164
|
ctx.typeParams.add(tp.name.text);
|
|
592
1165
|
}
|
|
593
1166
|
try {
|
|
594
|
-
const paramTypes = sig.parameters.map((param) => {
|
|
1167
|
+
const paramTypes = sig.parameters.filter((param) => !isThisParameter(param)).map((param) => {
|
|
595
1168
|
let type;
|
|
596
1169
|
if (param.dotDotDotToken) {
|
|
597
1170
|
const inner = param.type && ts.isArrayTypeNode(param.type) ? param.type.elementType : param.type;
|
|
598
|
-
type = inner ? absArr(mapType(ctx, inner, 0)) : absUnknown();
|
|
1171
|
+
type = inner ? absArr(mapType(ctx, inner, 0, bindings)) : absUnknown();
|
|
599
1172
|
} else {
|
|
600
|
-
type = param.type ? mapType(ctx, param.type, 0) : absUnknown();
|
|
1173
|
+
type = param.type ? mapType(ctx, param.type, 0, bindings) : absUnknown();
|
|
601
1174
|
}
|
|
602
1175
|
if (param.questionToken && !param.dotDotDotToken) type = absUnion([type, absUndefLit()]);
|
|
603
1176
|
return type;
|
|
604
1177
|
});
|
|
605
|
-
const returnType = sig.type ? mapType(ctx, sig.type, 0) : absUnknown();
|
|
1178
|
+
const returnType = sig.type ? mapType(ctx, sig.type, 0, bindings) : absUnknown();
|
|
606
1179
|
const cond = ctx.pendingCond;
|
|
607
1180
|
ctx.pendingCond = void 0;
|
|
608
1181
|
return absFnSig(paramTypes, returnType, cond ? {
|
|
@@ -613,123 +1186,506 @@ function signatureToFnSig(ctx, sig) {
|
|
|
613
1186
|
ctx.typeParams = savedTp;
|
|
614
1187
|
}
|
|
615
1188
|
}
|
|
616
|
-
function mapTypeMembers(ctx, members, depth) {
|
|
1189
|
+
function mapTypeMembers(ctx, members, depth, bindings) {
|
|
617
1190
|
const properties = {};
|
|
1191
|
+
let index;
|
|
1192
|
+
const accessorReads = /* @__PURE__ */ new Map();
|
|
1193
|
+
const accessorWrites = /* @__PURE__ */ new Map();
|
|
618
1194
|
for (const member of members) {
|
|
619
1195
|
if (ts.isPropertySignature(member)) {
|
|
620
|
-
const name = memberName(member);
|
|
1196
|
+
const name = memberName(member) ?? computedMemberName(member.name);
|
|
621
1197
|
if (name === void 0) continue;
|
|
622
|
-
let type = member.type ? mapType(ctx, member.type, depth) : absUnknown();
|
|
1198
|
+
let type = member.type ? mapType(ctx, member.type, depth, bindings) : absUnknown();
|
|
623
1199
|
if (member.questionToken) type = absUnion([type, absUndefLit()]);
|
|
624
1200
|
properties[name] = type;
|
|
625
1201
|
} else if (ts.isMethodSignature(member)) {
|
|
626
|
-
const name = memberName(member);
|
|
1202
|
+
const name = memberName(member) ?? computedMemberName(member.name);
|
|
627
1203
|
if (name === void 0 || properties[name] !== void 0) continue;
|
|
628
|
-
|
|
1204
|
+
const sig = signatureToFnSig(ctx, member);
|
|
1205
|
+
properties[name] = member.questionToken ? absUnion([sig, absUndefLit()]) : sig;
|
|
1206
|
+
} else if (ts.isGetAccessorDeclaration(member) || ts.isSetAccessorDeclaration(member)) {
|
|
1207
|
+
const name = memberName(member) ?? computedMemberName(member.name);
|
|
1208
|
+
if (name === void 0) continue;
|
|
1209
|
+
if (ts.isGetAccessorDeclaration(member)) {
|
|
1210
|
+
accessorReads.set(
|
|
1211
|
+
name,
|
|
1212
|
+
member.type ? mapType(ctx, member.type, depth, bindings) : absUnknown()
|
|
1213
|
+
);
|
|
1214
|
+
} else {
|
|
1215
|
+
const param = member.parameters[0];
|
|
1216
|
+
accessorWrites.set(
|
|
1217
|
+
name,
|
|
1218
|
+
param?.type ? mapType(ctx, param.type, depth, bindings) : absUnknown()
|
|
1219
|
+
);
|
|
1220
|
+
}
|
|
1221
|
+
} else if (ts.isIndexSignatureDeclaration(member)) {
|
|
1222
|
+
const keyParam = member.parameters[0];
|
|
1223
|
+
const key = keyParam?.type ? mapType(ctx, keyParam.type, depth, bindings) : absPrim("string");
|
|
1224
|
+
const value = member.type ? mapType(ctx, member.type, depth, bindings) : absUnknown();
|
|
1225
|
+
index = { key, value };
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
for (const [name, read] of accessorReads) {
|
|
1229
|
+
if (properties[name] === void 0) properties[name] = read;
|
|
1230
|
+
}
|
|
1231
|
+
for (const [name, write] of accessorWrites) {
|
|
1232
|
+
if (properties[name] === void 0) {
|
|
1233
|
+
properties[name] = absUnion([write, absUndefLit()]);
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
return { properties, index };
|
|
1237
|
+
}
|
|
1238
|
+
function stripParens(node) {
|
|
1239
|
+
let n = node;
|
|
1240
|
+
while (ts.isParenthesizedTypeNode(n)) n = n.type;
|
|
1241
|
+
return n;
|
|
1242
|
+
}
|
|
1243
|
+
function fileSaltOf(fileName) {
|
|
1244
|
+
let h = 2166136261;
|
|
1245
|
+
for (let i = 0; i < fileName.length; i++) {
|
|
1246
|
+
h ^= fileName.charCodeAt(i);
|
|
1247
|
+
h = Math.imul(h, 16777619) >>> 0;
|
|
1248
|
+
}
|
|
1249
|
+
return h.toString(36);
|
|
1250
|
+
}
|
|
1251
|
+
function scopeTagOf(scope) {
|
|
1252
|
+
let file = "";
|
|
1253
|
+
try {
|
|
1254
|
+
file = scope.getSourceFile()?.fileName ?? "";
|
|
1255
|
+
} catch {
|
|
1256
|
+
file = "";
|
|
1257
|
+
}
|
|
1258
|
+
return `${fileSaltOf(file)}${scope.pos}`;
|
|
1259
|
+
}
|
|
1260
|
+
function typeParamVarId(node, name) {
|
|
1261
|
+
const scopes = [];
|
|
1262
|
+
let p = node.parent;
|
|
1263
|
+
while (p) {
|
|
1264
|
+
const tps = p.typeParameters;
|
|
1265
|
+
if (tps) {
|
|
1266
|
+
for (const tp of tps) {
|
|
1267
|
+
if (tp.name.text === name) {
|
|
1268
|
+
scopes.push(p);
|
|
1269
|
+
break;
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
p = p.parent;
|
|
1274
|
+
}
|
|
1275
|
+
if (scopes.length <= 1) return name;
|
|
1276
|
+
return `${name}@${scopeTagOf(scopes[0])}`;
|
|
1277
|
+
}
|
|
1278
|
+
function lookupTypeParamBinding(bindings, node, name) {
|
|
1279
|
+
if (!bindings) return void 0;
|
|
1280
|
+
const id = typeParamVarId(node, name);
|
|
1281
|
+
return bindings.get(id) ?? bindings.get(`infer:${name}`);
|
|
1282
|
+
}
|
|
1283
|
+
function absOpenObj(properties, index) {
|
|
1284
|
+
const slots = {};
|
|
1285
|
+
for (const [k, v] of Object.entries(properties)) {
|
|
1286
|
+
slots[k] = { value: v };
|
|
1287
|
+
}
|
|
1288
|
+
return objOf(slots, index ? { index, open: true } : { open: true });
|
|
1289
|
+
}
|
|
1290
|
+
function slotsOfAbs(a) {
|
|
1291
|
+
const s = a.shape;
|
|
1292
|
+
if (s.k === "obj") {
|
|
1293
|
+
const out = {};
|
|
1294
|
+
for (const [k, slot] of Object.entries(s.slots)) out[k] = slot.value;
|
|
1295
|
+
return out;
|
|
1296
|
+
}
|
|
1297
|
+
if (s.k === "brand") return slotsOfAbs(s.shape);
|
|
1298
|
+
return void 0;
|
|
1299
|
+
}
|
|
1300
|
+
function sameAbsType(a, b) {
|
|
1301
|
+
if (a === b) return true;
|
|
1302
|
+
return formatShape(a) === formatShape(b);
|
|
1303
|
+
}
|
|
1304
|
+
function mergeIntersection(members) {
|
|
1305
|
+
const slots = {};
|
|
1306
|
+
let merged = 0;
|
|
1307
|
+
for (const member of members) {
|
|
1308
|
+
const mSlots = slotsOfAbs(member);
|
|
1309
|
+
if (!mSlots) return void 0;
|
|
1310
|
+
merged++;
|
|
1311
|
+
for (const [key, value] of Object.entries(mSlots)) {
|
|
1312
|
+
if (!Object.hasOwn(slots, key)) {
|
|
1313
|
+
slots[key] = value;
|
|
1314
|
+
continue;
|
|
1315
|
+
}
|
|
1316
|
+
const prev = slots[key];
|
|
1317
|
+
if (!sameAbsType(prev, value)) slots[key] = absNever();
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
if (merged === 0) return void 0;
|
|
1321
|
+
return absObj(slots);
|
|
1322
|
+
}
|
|
1323
|
+
function absCtorSig(params, instance, extra) {
|
|
1324
|
+
const a = absFnSig(params, instance, extra);
|
|
1325
|
+
a.shape.ctor = true;
|
|
1326
|
+
return a;
|
|
1327
|
+
}
|
|
1328
|
+
function takePendingCond(ctx) {
|
|
1329
|
+
const cond = ctx.pendingCond;
|
|
1330
|
+
ctx.pendingCond = void 0;
|
|
1331
|
+
return cond;
|
|
1332
|
+
}
|
|
1333
|
+
function condExtra(cond) {
|
|
1334
|
+
if (!cond) return void 0;
|
|
1335
|
+
return {
|
|
1336
|
+
inferFrom: { fromVar: cond.fromVar, via: cond.via, inferVar: cond.inferVar },
|
|
1337
|
+
condFallback: cond.fallback
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
function attachFnSlots(fn, properties) {
|
|
1341
|
+
const names = Object.keys(properties);
|
|
1342
|
+
if (names.length === 0) return fn;
|
|
1343
|
+
const slots = {};
|
|
1344
|
+
for (const [k, v] of Object.entries(properties)) slots[k] = { value: v };
|
|
1345
|
+
fn.shape.slots = slots;
|
|
1346
|
+
return fn;
|
|
1347
|
+
}
|
|
1348
|
+
function keyofNamesOf(ctx, node, depth) {
|
|
1349
|
+
const n = stripParens(node);
|
|
1350
|
+
if (ts.isTypeLiteralNode(n)) {
|
|
1351
|
+
const names = [];
|
|
1352
|
+
for (const member of n.members) {
|
|
1353
|
+
if (ts.isIndexSignatureDeclaration(member)) continue;
|
|
1354
|
+
const name = memberName(member);
|
|
1355
|
+
if (name !== void 0) names.push(name);
|
|
1356
|
+
}
|
|
1357
|
+
return names;
|
|
1358
|
+
}
|
|
1359
|
+
if (ts.isTypeReferenceNode(n)) {
|
|
1360
|
+
const name = typeNameString(n.typeName);
|
|
1361
|
+
if (ctx.interfaces.has(name) || ctx.qualifiedInterfaces.has(name)) {
|
|
1362
|
+
const slots = slotsOfAbs(instanceFor(ctx, name));
|
|
1363
|
+
return slots ? Object.keys(slots) : void 0;
|
|
1364
|
+
}
|
|
1365
|
+
const aliases = ctx.aliases.get(name) ?? ctx.qualifiedAliases.get(name) ?? [];
|
|
1366
|
+
const alias = aliases[0];
|
|
1367
|
+
if (alias) {
|
|
1368
|
+
const slots = slotsOfAbs(mapType(ctx, alias.type, depth + 1));
|
|
1369
|
+
return slots ? Object.keys(slots) : void 0;
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
return void 0;
|
|
1373
|
+
}
|
|
1374
|
+
var BUILTIN_TYPE_NAMES = /* @__PURE__ */ new Set([
|
|
1375
|
+
"Array",
|
|
1376
|
+
"ReadonlyArray",
|
|
1377
|
+
"Promise",
|
|
1378
|
+
"PromiseLike",
|
|
1379
|
+
"Map",
|
|
1380
|
+
"ReadonlyMap",
|
|
1381
|
+
"WeakMap",
|
|
1382
|
+
"Set",
|
|
1383
|
+
"ReadonlySet",
|
|
1384
|
+
"WeakSet",
|
|
1385
|
+
"Record",
|
|
1386
|
+
"Partial",
|
|
1387
|
+
"Readonly",
|
|
1388
|
+
"Required",
|
|
1389
|
+
"Pick",
|
|
1390
|
+
"Omit",
|
|
1391
|
+
"Exclude",
|
|
1392
|
+
"Extract",
|
|
1393
|
+
"NonNullable",
|
|
1394
|
+
"Parameters",
|
|
1395
|
+
"ReturnType",
|
|
1396
|
+
"Awaited",
|
|
1397
|
+
"Iterable",
|
|
1398
|
+
"Iterator",
|
|
1399
|
+
"IterableIterator",
|
|
1400
|
+
"AsyncIterable",
|
|
1401
|
+
"AsyncIterator",
|
|
1402
|
+
"AsyncIterableIterator",
|
|
1403
|
+
"Generator",
|
|
1404
|
+
"AsyncGenerator",
|
|
1405
|
+
"Date",
|
|
1406
|
+
"RegExp",
|
|
1407
|
+
"Error",
|
|
1408
|
+
"JSON",
|
|
1409
|
+
"Object",
|
|
1410
|
+
"Function",
|
|
1411
|
+
"ArrayBuffer",
|
|
1412
|
+
"Uint8Array",
|
|
1413
|
+
"Int8Array",
|
|
1414
|
+
"Uint16Array",
|
|
1415
|
+
"Int16Array",
|
|
1416
|
+
"Uint32Array",
|
|
1417
|
+
"Int32Array",
|
|
1418
|
+
"Float32Array",
|
|
1419
|
+
"Float64Array",
|
|
1420
|
+
"BigInt64Array",
|
|
1421
|
+
"BigUint64Array",
|
|
1422
|
+
"ArrayLike",
|
|
1423
|
+
"List",
|
|
1424
|
+
"Collection",
|
|
1425
|
+
"Many",
|
|
1426
|
+
"NumericDictionary",
|
|
1427
|
+
"Dictionary"
|
|
1428
|
+
]);
|
|
1429
|
+
function typeSideKind(ctx, node, depth) {
|
|
1430
|
+
const n = stripParens(node);
|
|
1431
|
+
if (ts.isTypeOperatorNode(n)) {
|
|
1432
|
+
if (n.operator === ts.SyntaxKind.KeyOfKeyword) {
|
|
1433
|
+
return keyofNamesOf(ctx, n.type, depth) ? "ok" : "degraded";
|
|
629
1434
|
}
|
|
1435
|
+
if (n.operator === ts.SyntaxKind.ReadonlyKeyword) return typeSideKind(ctx, n.type, depth);
|
|
1436
|
+
return "degraded";
|
|
1437
|
+
}
|
|
1438
|
+
if (ts.isTypeQueryNode(n)) return "degraded";
|
|
1439
|
+
if (ts.isMappedTypeNode(n)) return "degraded";
|
|
1440
|
+
if (ts.isIndexedAccessTypeNode(n)) return "degraded";
|
|
1441
|
+
if (ts.isImportTypeNode(n)) return "degraded";
|
|
1442
|
+
if (ts.isTemplateLiteralTypeNode(n)) return "degraded";
|
|
1443
|
+
if (ts.isConditionalTypeNode(n)) return "degraded";
|
|
1444
|
+
if (ts.isUnionTypeNode(n)) {
|
|
1445
|
+
for (const t of n.types) {
|
|
1446
|
+
if (typeSideKind(ctx, t, depth) === "degraded") return "degraded";
|
|
1447
|
+
}
|
|
1448
|
+
return "ok";
|
|
1449
|
+
}
|
|
1450
|
+
if (ts.isTypeReferenceNode(n)) {
|
|
1451
|
+
const name = typeNameString(n.typeName);
|
|
1452
|
+
if (ctx.typeParams.has(name)) return "ok";
|
|
1453
|
+
if (ctx.interfaces.has(name) || ctx.qualifiedInterfaces.has(name)) return "ok";
|
|
1454
|
+
if ((ctx.aliases.get(name) ?? ctx.qualifiedAliases.get(name) ?? []).length > 0) return "ok";
|
|
1455
|
+
if (BUILTIN_TYPE_NAMES.has(name)) return "ok";
|
|
1456
|
+
return "degraded";
|
|
1457
|
+
}
|
|
1458
|
+
return "ok";
|
|
1459
|
+
}
|
|
1460
|
+
function isSignatureReturnPos(node) {
|
|
1461
|
+
let cur = node;
|
|
1462
|
+
let p = cur.parent;
|
|
1463
|
+
while (p && ts.isParenthesizedTypeNode(p)) {
|
|
1464
|
+
cur = p;
|
|
1465
|
+
p = p.parent;
|
|
1466
|
+
}
|
|
1467
|
+
if (!p) return false;
|
|
1468
|
+
switch (p.kind) {
|
|
1469
|
+
case ts.SyntaxKind.FunctionType:
|
|
1470
|
+
case ts.SyntaxKind.ConstructorType:
|
|
1471
|
+
return p.type === cur;
|
|
1472
|
+
case ts.SyntaxKind.FunctionDeclaration:
|
|
1473
|
+
case ts.SyntaxKind.MethodDeclaration:
|
|
1474
|
+
case ts.SyntaxKind.MethodSignature:
|
|
1475
|
+
case ts.SyntaxKind.CallSignature:
|
|
1476
|
+
case ts.SyntaxKind.ConstructSignature:
|
|
1477
|
+
return p.type === cur;
|
|
1478
|
+
default:
|
|
1479
|
+
return false;
|
|
630
1480
|
}
|
|
631
|
-
return properties;
|
|
632
1481
|
}
|
|
633
|
-
function mapTypeRef(ctx, node, depth) {
|
|
1482
|
+
function mapTypeRef(ctx, node, depth, bindings) {
|
|
634
1483
|
const name = typeNameString(node.typeName);
|
|
635
1484
|
const args = node.typeArguments ?? [];
|
|
636
1485
|
if (ctx.typeParams.has(name) && args.length === 0) {
|
|
637
|
-
|
|
1486
|
+
const bound = lookupTypeParamBinding(bindings, node, name);
|
|
1487
|
+
if (bound) return bound;
|
|
1488
|
+
return typeVarAbs(typeParamVarId(node, name));
|
|
638
1489
|
}
|
|
639
1490
|
if ((name === "Array" || name === "ReadonlyArray") && args.length >= 1) {
|
|
640
|
-
return absArr(mapType(ctx, args[0], depth));
|
|
1491
|
+
return absArr(mapType(ctx, args[0], depth, bindings));
|
|
641
1492
|
}
|
|
642
1493
|
if ((name === "List" || name === "ArrayLike" || name === "Collection" || name === "Many" || name === "NumericDictionary" || name === "Dictionary") && args.length >= 1) {
|
|
643
|
-
return absArr(mapType(ctx, args[0], depth));
|
|
1494
|
+
return absArr(mapType(ctx, args[0], depth, bindings));
|
|
644
1495
|
}
|
|
645
1496
|
if (name === "Promise" && args.length >= 1) {
|
|
646
|
-
return absPromise(mapType(ctx, args[0], depth));
|
|
1497
|
+
return absPromise(mapType(ctx, args[0], depth, bindings));
|
|
647
1498
|
}
|
|
648
1499
|
if ((name === "Map" || name === "ReadonlyMap" || name === "WeakMap") && args.length >= 2) {
|
|
649
|
-
return absMapType(
|
|
1500
|
+
return absMapType(
|
|
1501
|
+
name,
|
|
1502
|
+
mapType(ctx, args[0], depth, bindings),
|
|
1503
|
+
mapType(ctx, args[1], depth, bindings)
|
|
1504
|
+
);
|
|
650
1505
|
}
|
|
651
1506
|
if ((name === "Set" || name === "ReadonlySet" || name === "WeakSet") && args.length >= 1) {
|
|
652
|
-
return absSetType(name, mapType(ctx, args[0], depth));
|
|
1507
|
+
return absSetType(name, mapType(ctx, args[0], depth, bindings));
|
|
653
1508
|
}
|
|
654
1509
|
if (name === "Record") {
|
|
655
|
-
|
|
1510
|
+
const value = args[1] ? mapType(ctx, args[1], depth, bindings) : absUnknown();
|
|
1511
|
+
const key = args[0] ? mapType(ctx, args[0], depth, bindings) : absPrim("string");
|
|
1512
|
+
return absOpenObj({}, { key, value });
|
|
656
1513
|
}
|
|
657
1514
|
if (ctx.interfaces.has(name) || ctx.qualifiedInterfaces.has(name)) {
|
|
658
|
-
return instanceFor(
|
|
1515
|
+
return instanceFor(
|
|
1516
|
+
ctx,
|
|
1517
|
+
name,
|
|
1518
|
+
args.map((a) => mapType(ctx, a, depth, bindings)),
|
|
1519
|
+
namespaceChainOf(node)
|
|
1520
|
+
);
|
|
659
1521
|
}
|
|
660
1522
|
const aliases = ctx.aliases.get(name) ?? ctx.qualifiedAliases.get(name) ?? [];
|
|
661
1523
|
const alias = aliases[0];
|
|
662
|
-
if (alias) return
|
|
1524
|
+
if (alias) return mapAliasWithArgs(ctx, alias, args, depth, bindings);
|
|
663
1525
|
return absUnknown();
|
|
664
1526
|
}
|
|
665
|
-
function
|
|
666
|
-
const
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
1527
|
+
function mapAliasWithArgs(ctx, alias, args, depth, bindings) {
|
|
1528
|
+
const tps = alias.typeParameters ?? [];
|
|
1529
|
+
if (tps.length === 0 || args.length === 0) return mapType(ctx, alias.type, depth + 1);
|
|
1530
|
+
const savedTp = ctx.typeParams;
|
|
1531
|
+
ctx.typeParams = new Set(savedTp);
|
|
1532
|
+
for (const tp of tps) ctx.typeParams.add(tp.name.text);
|
|
1533
|
+
try {
|
|
1534
|
+
const body = mapType(ctx, alias.type, depth + 1);
|
|
1535
|
+
const map = /* @__PURE__ */ new Map();
|
|
1536
|
+
tps.forEach((tp, i) => {
|
|
1537
|
+
const arg = args[i];
|
|
1538
|
+
if (!arg) return;
|
|
1539
|
+
map.set(
|
|
1540
|
+
typeParamVarId(alias.type, tp.name.text),
|
|
1541
|
+
mapType(ctx, arg, depth + 1, bindings)
|
|
1542
|
+
);
|
|
1543
|
+
});
|
|
1544
|
+
return map.size > 0 ? substAbs(body, map) : body;
|
|
1545
|
+
} finally {
|
|
1546
|
+
ctx.typeParams = savedTp;
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
function collectInferNames(node, out = /* @__PURE__ */ new Set()) {
|
|
1550
|
+
const visit = (n) => {
|
|
1551
|
+
if (!n) return;
|
|
1552
|
+
if (ts.isParenthesizedTypeNode(n)) {
|
|
1553
|
+
visit(n.type);
|
|
1554
|
+
return;
|
|
1555
|
+
}
|
|
1556
|
+
if (ts.isInferTypeNode(n)) {
|
|
1557
|
+
out.add(n.typeParameter.name.text);
|
|
1558
|
+
visit(n.typeParameter.constraint);
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
673
1561
|
n.forEachChild(visit);
|
|
674
1562
|
};
|
|
675
1563
|
visit(node);
|
|
676
1564
|
return out;
|
|
677
1565
|
}
|
|
678
|
-
function absExtends(check, ext) {
|
|
1566
|
+
function absExtends(check, ext, depth = 0) {
|
|
1567
|
+
if (depth > 6) return "unknown";
|
|
679
1568
|
if (check.term?.op === "var") return "unknown";
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
1569
|
+
const cs = check.shape;
|
|
1570
|
+
const es = ext.shape;
|
|
1571
|
+
const cIsLit = check.term?.op === "lit";
|
|
1572
|
+
const eIsLit = ext.term?.op === "lit";
|
|
1573
|
+
const cLit = check.term && check.term.op === "lit" ? check.term.value : void 0;
|
|
1574
|
+
const eLit = ext.term && ext.term.op === "lit" ? ext.term.value : void 0;
|
|
1575
|
+
if (cIsLit && eIsLit) {
|
|
1576
|
+
return cLit === eLit && typeof cLit === typeof eLit ? "true" : "false";
|
|
1577
|
+
}
|
|
1578
|
+
if (cIsLit) {
|
|
1579
|
+
if (eIsLit && (eLit === null || eLit === void 0)) return "false";
|
|
1580
|
+
if (es.k === "prim") {
|
|
1581
|
+
if (cLit === null) return "false";
|
|
1582
|
+
return typeof cLit === es.type ? "true" : "false";
|
|
683
1583
|
}
|
|
684
|
-
if (
|
|
685
|
-
|
|
1584
|
+
if (es.k === "sum") {
|
|
1585
|
+
const flags = es.members.map((m) => absExtends(check, m, depth + 1));
|
|
1586
|
+
if (flags.some((f) => f === "true")) return "true";
|
|
1587
|
+
if (flags.every((f) => f === "false")) return "false";
|
|
1588
|
+
return "unknown";
|
|
686
1589
|
}
|
|
1590
|
+
if (es.k === "unknown") return "true";
|
|
1591
|
+
if (es.k === "never") return "false";
|
|
1592
|
+
if (es.k === "any") return "unknown";
|
|
1593
|
+
if (es.k === "arr" || es.k === "tuple") return "false";
|
|
687
1594
|
return "unknown";
|
|
688
1595
|
}
|
|
689
|
-
if (
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
1596
|
+
if (eIsLit) {
|
|
1597
|
+
return "false";
|
|
1598
|
+
}
|
|
1599
|
+
if (es.k === "any") return "unknown";
|
|
1600
|
+
if (es.k === "unknown") return "true";
|
|
1601
|
+
if (cs.k === "any") return "unknown";
|
|
1602
|
+
if (cs.k === "unknown") return "false";
|
|
1603
|
+
if (cs.k === "never") return "true";
|
|
1604
|
+
if (es.k === "never") return "false";
|
|
693
1605
|
if (es.k === "prim" && cs.k === "prim") {
|
|
694
1606
|
return cs.type === es.type ? "true" : "false";
|
|
695
1607
|
}
|
|
1608
|
+
if ((es.k === "arr" || es.k === "tuple") && (cs.k === "prim" || cs.k === "obj" || cs.k === "fn" || cs.k === "brand")) {
|
|
1609
|
+
return "false";
|
|
1610
|
+
}
|
|
696
1611
|
if (es.k === "sum") {
|
|
697
|
-
const flags = es.members.map((m) => absExtends(check, m));
|
|
1612
|
+
const flags = es.members.map((m) => absExtends(check, m, depth + 1));
|
|
698
1613
|
if (flags.some((f) => f === "true")) return "true";
|
|
699
1614
|
if (flags.every((f) => f === "false")) return "false";
|
|
700
1615
|
return "unknown";
|
|
701
1616
|
}
|
|
702
1617
|
if (cs.k === "sum") {
|
|
703
|
-
const flags = cs.members.map((m) => absExtends(m, ext));
|
|
1618
|
+
const flags = cs.members.map((m) => absExtends(m, ext, depth + 1));
|
|
704
1619
|
if (flags.every((f) => f === "true")) return "true";
|
|
705
1620
|
if (flags.every((f) => f === "false")) return "false";
|
|
706
1621
|
return "unknown";
|
|
707
1622
|
}
|
|
708
1623
|
if (cs.k === "arr" && es.k === "arr") {
|
|
709
|
-
return absExtends(cs.element, es.element);
|
|
1624
|
+
return absExtends(cs.element, es.element, depth + 1);
|
|
1625
|
+
}
|
|
1626
|
+
if (cs.k === "tuple" && es.k === "arr") {
|
|
1627
|
+
const flags = cs.elements.map((el) => absExtends(el, es.element, depth + 1));
|
|
1628
|
+
return flags.some((f) => f === "false") ? "false" : "true";
|
|
1629
|
+
}
|
|
1630
|
+
if (cs.k === "tuple" && es.k === "tuple") {
|
|
1631
|
+
if (cs.elements.length !== es.elements.length) return "unknown";
|
|
1632
|
+
const flags = cs.elements.map((el, i) => absExtends(el, es.elements[i], depth + 1));
|
|
1633
|
+
if (flags.some((f) => f === "false")) return "false";
|
|
1634
|
+
if (flags.every((f) => f === "true")) return "true";
|
|
1635
|
+
return "unknown";
|
|
710
1636
|
}
|
|
711
1637
|
if (cs.k === "eff" && es.k === "eff" && cs.eff === es.eff) {
|
|
712
|
-
return absExtends(cs.inner, es.inner);
|
|
1638
|
+
return absExtends(cs.inner, es.inner, depth + 1);
|
|
713
1639
|
}
|
|
714
1640
|
if (cs.k === "brand" && es.k === "brand") {
|
|
715
|
-
|
|
1641
|
+
if (cs.name === es.name) return "true";
|
|
1642
|
+
return structuralExtends(check, ext, depth);
|
|
1643
|
+
}
|
|
1644
|
+
if (cs.k === "obj" && es.k === "obj" || cs.k === "brand" && es.k === "obj" || cs.k === "obj" && es.k === "brand") {
|
|
1645
|
+
return structuralExtends(check, ext, depth);
|
|
1646
|
+
}
|
|
1647
|
+
if (cs.k === "fn" && es.k === "fn") {
|
|
1648
|
+
const a = cs.returnType;
|
|
1649
|
+
const b = es.returnType;
|
|
1650
|
+
if (!a || !b) return "unknown";
|
|
1651
|
+
return absExtends(a, b, depth + 1);
|
|
716
1652
|
}
|
|
717
|
-
|
|
1653
|
+
return "unknown";
|
|
1654
|
+
}
|
|
1655
|
+
function structuralExtends(check, ext, depth) {
|
|
1656
|
+
const cs = slotsOfAbs(check);
|
|
1657
|
+
const es = slotsOfAbs(ext);
|
|
1658
|
+
if (!cs || !es) return "unknown";
|
|
1659
|
+
const extKeys = Object.keys(es);
|
|
1660
|
+
if (extKeys.length === 0) return "unknown";
|
|
1661
|
+
const flags = [];
|
|
1662
|
+
for (const key of extKeys) {
|
|
1663
|
+
const target = es[key];
|
|
1664
|
+
const source = cs[key];
|
|
1665
|
+
if (source === void 0) {
|
|
1666
|
+
flags.push("false");
|
|
1667
|
+
continue;
|
|
1668
|
+
}
|
|
1669
|
+
flags.push(absExtends(source, target, depth + 1));
|
|
1670
|
+
}
|
|
1671
|
+
if (flags.some((f) => f === "false")) return "false";
|
|
1672
|
+
if (flags.every((f) => f === "true")) return "true";
|
|
718
1673
|
return "unknown";
|
|
719
1674
|
}
|
|
720
1675
|
function matchExtendsInfer(pattern, actual, inferMap, depth = 0) {
|
|
721
1676
|
if (depth > 6) return "unknown";
|
|
722
|
-
|
|
723
|
-
|
|
1677
|
+
const p = stripParens(pattern);
|
|
1678
|
+
if (ts.isInferTypeNode(p)) {
|
|
1679
|
+
const name = p.typeParameter.name.text;
|
|
724
1680
|
if (!inferMap.has(name)) inferMap.set(name, actual);
|
|
725
1681
|
return "true";
|
|
726
1682
|
}
|
|
727
|
-
if (ts.isArrayTypeNode(
|
|
728
|
-
return matchExtendsInfer(
|
|
1683
|
+
if (ts.isArrayTypeNode(p) && actual.shape.k === "arr") {
|
|
1684
|
+
return matchExtendsInfer(p.elementType, actual.shape.element, inferMap, depth + 1);
|
|
729
1685
|
}
|
|
730
|
-
if (ts.isTypeReferenceNode(
|
|
731
|
-
const name = typeNameString(
|
|
732
|
-
const args =
|
|
1686
|
+
if (ts.isTypeReferenceNode(p)) {
|
|
1687
|
+
const name = typeNameString(p.typeName);
|
|
1688
|
+
const args = p.typeArguments ?? [];
|
|
733
1689
|
if ((name === "Array" || name === "ReadonlyArray" || name === "List" || name === "ArrayLike") && args[0] && actual.shape.k === "arr") {
|
|
734
1690
|
return matchExtendsInfer(args[0], actual.shape.element, inferMap, depth + 1);
|
|
735
1691
|
}
|
|
@@ -738,32 +1694,65 @@ function matchExtendsInfer(pattern, actual, inferMap, depth = 0) {
|
|
|
738
1694
|
}
|
|
739
1695
|
return "unknown";
|
|
740
1696
|
}
|
|
741
|
-
if (ts.isUnionTypeNode(
|
|
742
|
-
const flags =
|
|
1697
|
+
if (ts.isUnionTypeNode(p)) {
|
|
1698
|
+
const flags = p.types.map((t) => matchExtendsInfer(t, actual, inferMap, depth + 1));
|
|
743
1699
|
if (flags.some((f) => f === "true")) return "true";
|
|
744
1700
|
if (flags.every((f) => f === "false")) return "false";
|
|
745
1701
|
return "unknown";
|
|
746
1702
|
}
|
|
1703
|
+
if (ts.isTypeLiteralNode(p)) {
|
|
1704
|
+
const slots = slotsOfAbs(actual);
|
|
1705
|
+
if (!slots) return "unknown";
|
|
1706
|
+
const flags = [];
|
|
1707
|
+
for (const member of p.members) {
|
|
1708
|
+
if (!ts.isPropertySignature(member) || !member.type) continue;
|
|
1709
|
+
const name = memberName(member);
|
|
1710
|
+
if (name === void 0) continue;
|
|
1711
|
+
const source = slots[name];
|
|
1712
|
+
if (source === void 0) {
|
|
1713
|
+
flags.push("false");
|
|
1714
|
+
continue;
|
|
1715
|
+
}
|
|
1716
|
+
flags.push(matchExtendsInfer(member.type, source, inferMap, depth + 1));
|
|
1717
|
+
}
|
|
1718
|
+
if (flags.length === 0) return "unknown";
|
|
1719
|
+
if (flags.some((f) => f === "false")) return "false";
|
|
1720
|
+
if (flags.every((f) => f === "true")) return "true";
|
|
1721
|
+
return "unknown";
|
|
1722
|
+
}
|
|
747
1723
|
return "unknown";
|
|
748
1724
|
}
|
|
749
|
-
function mapConditionalType(ctx, node, depth) {
|
|
1725
|
+
function mapConditionalType(ctx, node, depth, bindings) {
|
|
750
1726
|
const inferMap = /* @__PURE__ */ new Map();
|
|
751
1727
|
const inferNames = collectInferNames(node.extendsType);
|
|
752
|
-
const check = mapType(ctx, node.checkType, depth + 1);
|
|
1728
|
+
const check = mapType(ctx, node.checkType, depth + 1, bindings);
|
|
753
1729
|
const astMatch = matchExtendsInfer(node.extendsType, check, inferMap);
|
|
754
|
-
const extAbs = mapType(ctx, node.extendsType, depth + 1);
|
|
755
|
-
const
|
|
1730
|
+
const extAbs = mapType(ctx, node.extendsType, depth + 1, bindings);
|
|
1731
|
+
const checkSide = typeSideKind(ctx, node.checkType, depth + 1);
|
|
1732
|
+
const extSide = typeSideKind(ctx, node.extendsType, depth + 1);
|
|
1733
|
+
let absMatch;
|
|
1734
|
+
if (extSide === "degraded" || checkSide === "degraded") {
|
|
1735
|
+
absMatch = "unknown";
|
|
1736
|
+
} else if (extSide === "unknownKeyword") {
|
|
1737
|
+
absMatch = "true";
|
|
1738
|
+
} else {
|
|
1739
|
+
absMatch = astMatch !== "unknown" ? astMatch : absExtends(check, extAbs);
|
|
1740
|
+
}
|
|
756
1741
|
const condMeta = analyzeCondPattern(ctx, node, check);
|
|
757
1742
|
const savedTp = ctx.typeParams;
|
|
758
1743
|
ctx.typeParams = new Set(savedTp);
|
|
759
1744
|
for (const n of inferNames) ctx.typeParams.add(n);
|
|
760
1745
|
try {
|
|
761
|
-
const
|
|
762
|
-
|
|
1746
|
+
const trueBindings = new Map(bindings ?? []);
|
|
1747
|
+
for (const [k, v] of inferMap) trueBindings.set(`infer:${k}`, v);
|
|
1748
|
+
const trueAbs = mapType(ctx, node.trueType, depth + 1, trueBindings);
|
|
1749
|
+
const falseAbs = mapType(ctx, node.falseType, depth + 1, bindings);
|
|
763
1750
|
if (absMatch === "true") return trueAbs;
|
|
764
1751
|
if (absMatch === "false") return falseAbs;
|
|
765
|
-
if (condMeta && check.term?.op === "var") {
|
|
766
|
-
|
|
1752
|
+
if (condMeta && check.term?.op === "var" && trueBranchIsInferVar(node, condMeta)) {
|
|
1753
|
+
if (isSignatureReturnPos(node)) {
|
|
1754
|
+
ctx.pendingCond = { ...condMeta, fallback: falseAbs };
|
|
1755
|
+
}
|
|
767
1756
|
return typeVarAbs(condMeta.inferVar);
|
|
768
1757
|
}
|
|
769
1758
|
return absUnion([trueAbs, falseAbs]);
|
|
@@ -771,10 +1760,18 @@ function mapConditionalType(ctx, node, depth) {
|
|
|
771
1760
|
ctx.typeParams = savedTp;
|
|
772
1761
|
}
|
|
773
1762
|
}
|
|
1763
|
+
function trueBranchIsInferVar(node, condMeta) {
|
|
1764
|
+
const t = stripParens(node.trueType);
|
|
1765
|
+
if (ts.isInferTypeNode(t)) return t.typeParameter.name.text === condMeta.inferVar;
|
|
1766
|
+
if (ts.isTypeReferenceNode(t) && !(t.typeArguments && t.typeArguments.length > 0)) {
|
|
1767
|
+
return typeNameString(t.typeName) === condMeta.inferVar;
|
|
1768
|
+
}
|
|
1769
|
+
return false;
|
|
1770
|
+
}
|
|
774
1771
|
function analyzeCondPattern(_ctx, node, check) {
|
|
775
1772
|
if (check.term?.op !== "var") return void 0;
|
|
776
1773
|
const fromVar = check.term.id;
|
|
777
|
-
const ext = node.extendsType;
|
|
1774
|
+
const ext = stripParens(node.extendsType);
|
|
778
1775
|
const names = collectInferNames(ext);
|
|
779
1776
|
const inferVar = [...names][0];
|
|
780
1777
|
if (!inferVar) return void 0;
|
|
@@ -790,20 +1787,40 @@ function analyzeCondPattern(_ctx, node, check) {
|
|
|
790
1787
|
}
|
|
791
1788
|
return void 0;
|
|
792
1789
|
}
|
|
793
|
-
function mapType(ctx, node, depth) {
|
|
1790
|
+
function mapType(ctx, node, depth, bindings) {
|
|
794
1791
|
if (!node || depth > MAX_ALIAS_DEPTH) return absUnknown();
|
|
795
|
-
if (ts.isParenthesizedTypeNode(node)) return mapType(ctx, node.type, depth);
|
|
796
|
-
if (ts.isTypeReferenceNode(node)) return mapTypeRef(ctx, node, depth);
|
|
797
|
-
if (ts.isArrayTypeNode(node)) return absArr(mapType(ctx, node.elementType, depth));
|
|
1792
|
+
if (ts.isParenthesizedTypeNode(node)) return mapType(ctx, node.type, depth, bindings);
|
|
1793
|
+
if (ts.isTypeReferenceNode(node)) return mapTypeRef(ctx, node, depth, bindings);
|
|
1794
|
+
if (ts.isArrayTypeNode(node)) return absArr(mapType(ctx, node.elementType, depth, bindings));
|
|
798
1795
|
if (ts.isTupleTypeNode(node)) {
|
|
1796
|
+
const elements = [];
|
|
1797
|
+
let rest;
|
|
799
1798
|
for (const element of node.elements) {
|
|
800
|
-
|
|
801
|
-
|
|
1799
|
+
if (ts.isNamedTupleMember(element)) {
|
|
1800
|
+
let t = mapType(ctx, element.type, depth, bindings);
|
|
1801
|
+
if (element.questionToken) t = absUnion([t, absUndefLit()]);
|
|
1802
|
+
elements.push(t);
|
|
1803
|
+
continue;
|
|
1804
|
+
}
|
|
1805
|
+
if (ts.isOptionalTypeNode(element)) {
|
|
1806
|
+
elements.push(absUnion([mapType(ctx, element.type, depth, bindings), absUndefLit()]));
|
|
1807
|
+
continue;
|
|
1808
|
+
}
|
|
1809
|
+
if (ts.isRestTypeNode(element)) {
|
|
1810
|
+
rest = mapType(ctx, element.type, depth, bindings);
|
|
1811
|
+
continue;
|
|
1812
|
+
}
|
|
1813
|
+
elements.push(mapType(ctx, element, depth, bindings));
|
|
802
1814
|
}
|
|
803
|
-
return
|
|
1815
|
+
return makeAbs(
|
|
1816
|
+
{ k: "tuple", elements, ...rest ? { rest } : {} },
|
|
1817
|
+
void 0,
|
|
1818
|
+
void 0,
|
|
1819
|
+
"exact"
|
|
1820
|
+
);
|
|
804
1821
|
}
|
|
805
1822
|
if (ts.isUnionTypeNode(node)) {
|
|
806
|
-
const members = node.types.map((t) => mapType(ctx, t, depth));
|
|
1823
|
+
const members = node.types.map((t) => mapType(ctx, t, depth, bindings));
|
|
807
1824
|
const hasLit = members.some((m) => m.term?.op === "lit");
|
|
808
1825
|
if (hasLit && members.length > 1) {
|
|
809
1826
|
return absExact({ k: "sum", members });
|
|
@@ -811,17 +1828,9 @@ function mapType(ctx, node, depth) {
|
|
|
811
1828
|
return absUnion(members);
|
|
812
1829
|
}
|
|
813
1830
|
if (ts.isIntersectionTypeNode(node)) {
|
|
814
|
-
const members = node.types.map((t) => mapType(ctx, t, depth));
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
for (const member of members) {
|
|
818
|
-
if (member.shape.k !== "obj") continue;
|
|
819
|
-
for (const [k, slot] of Object.entries(member.shape.slots)) {
|
|
820
|
-
properties[k] = slot.value;
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
return absObj(properties);
|
|
824
|
-
}
|
|
1831
|
+
const members = node.types.map((t) => mapType(ctx, t, depth, bindings));
|
|
1832
|
+
const merged = mergeIntersection(members);
|
|
1833
|
+
if (merged) return merged;
|
|
825
1834
|
return members[0] ?? absUnknown();
|
|
826
1835
|
}
|
|
827
1836
|
if (ts.isLiteralTypeNode(node)) {
|
|
@@ -842,22 +1851,61 @@ function mapType(ctx, node, depth) {
|
|
|
842
1851
|
if (ctor) {
|
|
843
1852
|
const params = [];
|
|
844
1853
|
for (const param of ctor.parameters) {
|
|
845
|
-
let type = param.type ? mapType(ctx, param.type, depth) : absUnknown();
|
|
1854
|
+
let type = param.type ? mapType(ctx, param.type, depth, bindings) : absUnknown();
|
|
1855
|
+
if (param.questionToken) type = absUnion([type, absUndefLit()]);
|
|
1856
|
+
params.push(type);
|
|
1857
|
+
}
|
|
1858
|
+
const instance = ctor.type ? mapType(ctx, ctor.type, depth, bindings) : absUnknown();
|
|
1859
|
+
const cond = takePendingCond(ctx);
|
|
1860
|
+
const { properties: properties2 } = mapTypeMembers(ctx, node.members, depth, bindings);
|
|
1861
|
+
return attachFnSlots(absCtorSig(params, instance, condExtra(cond)), properties2);
|
|
1862
|
+
}
|
|
1863
|
+
const call = node.members.find(ts.isCallSignatureDeclaration);
|
|
1864
|
+
if (call) {
|
|
1865
|
+
const params = [];
|
|
1866
|
+
for (const param of call.parameters) {
|
|
1867
|
+
let type = param.type ? mapType(ctx, param.type, depth, bindings) : absUnknown();
|
|
846
1868
|
if (param.questionToken) type = absUnion([type, absUndefLit()]);
|
|
847
1869
|
params.push(type);
|
|
848
1870
|
}
|
|
849
|
-
|
|
1871
|
+
const ret = call.type ? mapType(ctx, call.type, depth, bindings) : absUnknown();
|
|
1872
|
+
const cond = takePendingCond(ctx);
|
|
1873
|
+
const { properties: properties2 } = mapTypeMembers(ctx, node.members, depth, bindings);
|
|
1874
|
+
return attachFnSlots(absFnSig(params, ret, condExtra(cond)), properties2);
|
|
850
1875
|
}
|
|
851
|
-
|
|
1876
|
+
const { properties, index } = mapTypeMembers(ctx, node.members, depth, bindings);
|
|
1877
|
+
if (index) return absOpenObj(properties, index);
|
|
1878
|
+
return absObj(properties);
|
|
852
1879
|
}
|
|
853
1880
|
if (ts.isTypeOperatorNode(node)) {
|
|
854
|
-
|
|
1881
|
+
if (node.operator === ts.SyntaxKind.ReadonlyKeyword) {
|
|
1882
|
+
return mapType(ctx, node.type, depth, bindings);
|
|
1883
|
+
}
|
|
1884
|
+
if (node.operator === ts.SyntaxKind.KeyOfKeyword) {
|
|
1885
|
+
const names = keyofNamesOf(ctx, node.type, depth);
|
|
1886
|
+
if (!names) {
|
|
1887
|
+
ctx.degradedMark++;
|
|
1888
|
+
return absUnknown();
|
|
1889
|
+
}
|
|
1890
|
+
if (names.length === 0) return absNever();
|
|
1891
|
+
return absExact({ k: "sum", members: names.map((n) => absLit(n)) });
|
|
1892
|
+
}
|
|
1893
|
+
ctx.degradedMark++;
|
|
1894
|
+
return absUnknown();
|
|
855
1895
|
}
|
|
856
1896
|
if (ts.isConditionalTypeNode(node)) {
|
|
857
|
-
|
|
1897
|
+
const cond = mapConditionalType(ctx, node, depth, bindings);
|
|
1898
|
+
if (isPlainUnknown(cond)) ctx.degradedMark++;
|
|
1899
|
+
return cond;
|
|
858
1900
|
}
|
|
859
1901
|
if (ts.isInferTypeNode(node)) {
|
|
860
|
-
|
|
1902
|
+
const name = node.typeParameter.name.text;
|
|
1903
|
+
const bound = bindings?.get(`infer:${name}`);
|
|
1904
|
+
if (bound) return bound;
|
|
1905
|
+
return typeVarAbs(name);
|
|
1906
|
+
}
|
|
1907
|
+
if (ts.isTypePredicateNode(node)) {
|
|
1908
|
+
return node.assertsModifier ? absUndefLit() : absPrim("boolean");
|
|
861
1909
|
}
|
|
862
1910
|
switch (node.kind) {
|
|
863
1911
|
case ts.SyntaxKind.StringKeyword:
|
|
@@ -882,6 +1930,7 @@ function mapType(ctx, node, depth) {
|
|
|
882
1930
|
case ts.SyntaxKind.NullKeyword:
|
|
883
1931
|
return absNullLit();
|
|
884
1932
|
default:
|
|
1933
|
+
ctx.degradedMark++;
|
|
885
1934
|
return absUnknown();
|
|
886
1935
|
}
|
|
887
1936
|
}
|
|
@@ -889,6 +1938,7 @@ function indent(level) {
|
|
|
889
1938
|
return " ".repeat(level);
|
|
890
1939
|
}
|
|
891
1940
|
function emitKey(key) {
|
|
1941
|
+
if (key === "__proto__") return `["__proto__"]`;
|
|
892
1942
|
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
|
|
893
1943
|
}
|
|
894
1944
|
function emitEntries(lines, record, level) {
|
|
@@ -912,6 +1962,9 @@ function emitAbs(a, expanding) {
|
|
|
912
1962
|
}
|
|
913
1963
|
return "absUnknown";
|
|
914
1964
|
case "any":
|
|
1965
|
+
if (a.term?.op === "var") {
|
|
1966
|
+
return `makeAbs({ k: "any" }, termVar(${JSON.stringify(a.term.id)}), undefined, "path")`;
|
|
1967
|
+
}
|
|
915
1968
|
return "absUnknown";
|
|
916
1969
|
case "prim": {
|
|
917
1970
|
if (a.term?.op === "lit") {
|
|
@@ -923,6 +1976,12 @@ function emitAbs(a, expanding) {
|
|
|
923
1976
|
if (s.type === "number") return "num()";
|
|
924
1977
|
if (s.type === "string") return "str()";
|
|
925
1978
|
if (s.type === "boolean") return "bool()";
|
|
1979
|
+
if (s.type === "bigint") {
|
|
1980
|
+
return `makeAbs({ k: "prim", type: "bigint" }, undefined, undefined, "exact")`;
|
|
1981
|
+
}
|
|
1982
|
+
if (s.type === "symbol") {
|
|
1983
|
+
return `makeAbs({ k: "prim", type: "symbol" }, undefined, undefined, "exact")`;
|
|
1984
|
+
}
|
|
926
1985
|
return "absUnknown";
|
|
927
1986
|
}
|
|
928
1987
|
case "arr":
|
|
@@ -957,7 +2016,15 @@ function emitAbs(a, expanding) {
|
|
|
957
2016
|
const pts = s.paramTypes ?? [];
|
|
958
2017
|
const params = pts.map((p) => emitAbs(p, expanding)).join(", ");
|
|
959
2018
|
const ret = s.returnType ? emitAbs(s.returnType, expanding) : "absUnknown";
|
|
960
|
-
|
|
2019
|
+
const facets = [];
|
|
2020
|
+
if (s.ctor !== void 0) facets.push(`f.shape.ctor = ${s.ctor ? "true" : "false"};`);
|
|
2021
|
+
const slotEntries = Object.entries(s.slots ?? {});
|
|
2022
|
+
if (slotEntries.length > 0) {
|
|
2023
|
+
const entries = slotEntries.map(([key, slot]) => `${emitKey(key)}: { value: ${emitAbs(slot.value, expanding)} }`).join(", ");
|
|
2024
|
+
facets.push(`f.shape.slots = { ${entries} };`);
|
|
2025
|
+
}
|
|
2026
|
+
if (facets.length === 0) return `relationFn([${params}], ${ret}, { conf: "exact" })`;
|
|
2027
|
+
return `(() => { const f = relationFn([${params}], ${ret}, { conf: "exact" }); ${facets.join(" ")} return f; })()`;
|
|
961
2028
|
}
|
|
962
2029
|
default:
|
|
963
2030
|
return "absUnknown";
|
|
@@ -968,7 +2035,11 @@ function emitAbs(a, expanding) {
|
|
|
968
2035
|
import { readFileSync as readFileSync2, existsSync, statSync as statSync2, readdirSync } from "fs";
|
|
969
2036
|
import { basename, dirname, join } from "path";
|
|
970
2037
|
import ts2 from "typescript";
|
|
971
|
-
import {
|
|
2038
|
+
import {
|
|
2039
|
+
formatObjectKey,
|
|
2040
|
+
isJsBindingIdent,
|
|
2041
|
+
toJsBindingIdent
|
|
2042
|
+
} from "@nudojs/core/internal";
|
|
972
2043
|
var BUILDERS = [
|
|
973
2044
|
"fn",
|
|
974
2045
|
"number",
|
|
@@ -977,38 +2048,52 @@ var BUILDERS = [
|
|
|
977
2048
|
"any",
|
|
978
2049
|
"shape",
|
|
979
2050
|
"array",
|
|
980
|
-
"
|
|
981
|
-
"
|
|
2051
|
+
"union",
|
|
2052
|
+
"partial"
|
|
982
2053
|
];
|
|
983
2054
|
function keywordDsl(node) {
|
|
984
2055
|
switch (node.kind) {
|
|
985
2056
|
case ts2.SyntaxKind.NumberKeyword:
|
|
986
|
-
return "number()";
|
|
2057
|
+
return { dsl: "number()" };
|
|
987
2058
|
case ts2.SyntaxKind.StringKeyword:
|
|
988
|
-
return "string()";
|
|
2059
|
+
return { dsl: "string()" };
|
|
989
2060
|
case ts2.SyntaxKind.BooleanKeyword:
|
|
990
|
-
return "boolean()";
|
|
2061
|
+
return { dsl: "boolean()" };
|
|
2062
|
+
// any / unknown:TS 语义就是「无约束」⇒ any() 是**忠实**映射
|
|
991
2063
|
case ts2.SyntaxKind.AnyKeyword:
|
|
992
2064
|
case ts2.SyntaxKind.UnknownKeyword:
|
|
2065
|
+
// void 返回:调用方拿不到值 ⇒ 无返回义务
|
|
993
2066
|
case ts2.SyntaxKind.VoidKeyword:
|
|
994
|
-
|
|
995
|
-
case ts2.SyntaxKind.NullKeyword:
|
|
2067
|
+
// object:非原始类型 ⇒ 无约束(宽松但不引入假红)
|
|
996
2068
|
case ts2.SyntaxKind.ObjectKeyword:
|
|
2069
|
+
return { dsl: "any()" };
|
|
2070
|
+
// null:TS 的**真 null 面**(不是 any())——Bug 801
|
|
2071
|
+
case ts2.SyntaxKind.NullKeyword:
|
|
2072
|
+
return { dsl: "null", raw: true };
|
|
2073
|
+
// undefined:TS 的**真 undefined 面**(union 里不能被 any() 吞掉)
|
|
2074
|
+
case ts2.SyntaxKind.UndefinedKeyword:
|
|
2075
|
+
return { dsl: "undefined", raw: true };
|
|
2076
|
+
// never 是底类型:DSL 无底约束 ⇒ 标为未建模(不能静默放宽成 any())
|
|
997
2077
|
case ts2.SyntaxKind.NeverKeyword:
|
|
998
|
-
return "
|
|
2078
|
+
return { unmodeled: "never (bottom type is not expressible)" };
|
|
999
2079
|
case ts2.SyntaxKind.BigIntKeyword:
|
|
2080
|
+
return { unmodeled: "bigint" };
|
|
1000
2081
|
case ts2.SyntaxKind.SymbolKeyword:
|
|
1001
|
-
return
|
|
2082
|
+
return { unmodeled: "symbol" };
|
|
1002
2083
|
default:
|
|
1003
2084
|
return void 0;
|
|
1004
2085
|
}
|
|
1005
2086
|
}
|
|
1006
2087
|
function litDsl(node) {
|
|
1007
2088
|
const lit = node.literal;
|
|
1008
|
-
if (ts2.isStringLiteral(lit)) return
|
|
1009
|
-
if (ts2.isNumericLiteral(lit)) return
|
|
1010
|
-
if (lit.kind === ts2.SyntaxKind.TrueKeyword) return "
|
|
1011
|
-
if (lit.kind === ts2.SyntaxKind.FalseKeyword) return "
|
|
2089
|
+
if (ts2.isStringLiteral(lit)) return { dsl: JSON.stringify(lit.text), modeled: true, raw: true };
|
|
2090
|
+
if (ts2.isNumericLiteral(lit)) return { dsl: lit.text, modeled: true, raw: true };
|
|
2091
|
+
if (lit.kind === ts2.SyntaxKind.TrueKeyword) return { dsl: "true", modeled: true, raw: true };
|
|
2092
|
+
if (lit.kind === ts2.SyntaxKind.FalseKeyword) return { dsl: "false", modeled: true, raw: true };
|
|
2093
|
+
if (lit.kind === ts2.SyntaxKind.NullKeyword) return { dsl: "null", modeled: true, raw: true };
|
|
2094
|
+
if (ts2.isPrefixUnaryExpression(lit) && lit.operator === ts2.SyntaxKind.MinusToken && ts2.isNumericLiteral(lit.operand)) {
|
|
2095
|
+
return { dsl: `-${lit.operand.text}`, modeled: true, raw: true };
|
|
2096
|
+
}
|
|
1012
2097
|
return void 0;
|
|
1013
2098
|
}
|
|
1014
2099
|
function typeRefName(node) {
|
|
@@ -1026,173 +2111,1001 @@ function typeRefName(node) {
|
|
|
1026
2111
|
}
|
|
1027
2112
|
return "?";
|
|
1028
2113
|
}
|
|
1029
|
-
function
|
|
1030
|
-
if (!
|
|
2114
|
+
function memberKey(name) {
|
|
2115
|
+
if (!name) return void 0;
|
|
2116
|
+
if (ts2.isIdentifier(name)) return name.text;
|
|
2117
|
+
if (ts2.isStringLiteral(name) || ts2.isNumericLiteral(name)) return name.text;
|
|
2118
|
+
if (ts2.isComputedPropertyName(name)) {
|
|
2119
|
+
const expr = name.expression;
|
|
2120
|
+
if (ts2.isPropertyAccessExpression(expr) && ts2.isIdentifier(expr.expression) && expr.expression.text === "Symbol") {
|
|
2121
|
+
if (expr.name.text === "iterator") return "@@iterator";
|
|
2122
|
+
if (expr.name.text === "asyncIterator") return "@@asyncIterator";
|
|
2123
|
+
return void 0;
|
|
2124
|
+
}
|
|
2125
|
+
if (ts2.isStringLiteral(expr)) return expr.text;
|
|
2126
|
+
return void 0;
|
|
2127
|
+
}
|
|
2128
|
+
return void 0;
|
|
2129
|
+
}
|
|
2130
|
+
function memberKeyOrNote(name, what, ctx) {
|
|
2131
|
+
const key = memberKey(name);
|
|
2132
|
+
if (key !== void 0) return key;
|
|
2133
|
+
ctx.notes.push(`${what} member with a name that cannot be resolved statically is not projected`);
|
|
2134
|
+
return void 0;
|
|
2135
|
+
}
|
|
2136
|
+
function draftKey(key) {
|
|
2137
|
+
if (key === "__proto__") return `["__proto__"]`;
|
|
2138
|
+
return formatObjectKey(key);
|
|
2139
|
+
}
|
|
2140
|
+
function optionalize(d) {
|
|
2141
|
+
const base = !d.raw ? d.dsl : d.dsl === "undefined" ? "union(undefined)" : `union(${d.dsl}, undefined)`;
|
|
2142
|
+
return `${base}.optional()`;
|
|
2143
|
+
}
|
|
2144
|
+
function constraintSlot(d) {
|
|
2145
|
+
return d.raw ? `union(${d.dsl})` : d.dsl;
|
|
2146
|
+
}
|
|
2147
|
+
function typeToDsl(node, ctx) {
|
|
2148
|
+
if (!node) return void 0;
|
|
2149
|
+
if (ctx.depth > 6) {
|
|
2150
|
+
ctx.notes.push("type nesting too deep \u2192 any()");
|
|
2151
|
+
return { dsl: "any()", modeled: false };
|
|
2152
|
+
}
|
|
1031
2153
|
const kw = keywordDsl(node);
|
|
1032
|
-
if (kw
|
|
2154
|
+
if (kw) {
|
|
2155
|
+
if ("dsl" in kw) {
|
|
2156
|
+
return { dsl: kw.dsl, modeled: true, ...kw.raw ? { raw: true } : {} };
|
|
2157
|
+
}
|
|
2158
|
+
ctx.notes.push(`unmodeled type '${kw.unmodeled}' \u2192 any()`);
|
|
2159
|
+
return { dsl: "any()", modeled: false };
|
|
2160
|
+
}
|
|
1033
2161
|
if (ts2.isLiteralTypeNode(node)) {
|
|
1034
|
-
return litDsl(node) ?? "any()";
|
|
2162
|
+
return litDsl(node) ?? { dsl: "any()", modeled: false };
|
|
1035
2163
|
}
|
|
1036
2164
|
if (ts2.isArrayTypeNode(node)) {
|
|
1037
|
-
const el = typeToDsl(node.elementType,
|
|
1038
|
-
return `array(${el})
|
|
2165
|
+
const el = typeToDsl(node.elementType, child(ctx)) ?? { dsl: "any()", modeled: false };
|
|
2166
|
+
return { dsl: `array(${el.dsl})`, modeled: el.modeled };
|
|
1039
2167
|
}
|
|
1040
2168
|
if (ts2.isUnionTypeNode(node)) {
|
|
1041
|
-
const parts =
|
|
1042
|
-
|
|
2169
|
+
const parts = [];
|
|
2170
|
+
for (const t of node.types) {
|
|
2171
|
+
const d = typeToDsl(t, child(ctx));
|
|
2172
|
+
parts.push(d ?? { dsl: "any()", modeled: false });
|
|
2173
|
+
}
|
|
2174
|
+
if (parts.length === 0) return { dsl: "any()", modeled: false };
|
|
2175
|
+
if (parts.some((p) => !p.modeled)) {
|
|
2176
|
+
ctx.notes.push("union member not modeled \u2192 any()");
|
|
2177
|
+
return { dsl: "any()", modeled: false };
|
|
2178
|
+
}
|
|
1043
2179
|
if (parts.length === 1) return parts[0];
|
|
1044
|
-
return `union(${parts.join(", ")})
|
|
2180
|
+
return { dsl: `union(${parts.map((p) => p.dsl).join(", ")})`, modeled: true };
|
|
1045
2181
|
}
|
|
1046
2182
|
if (ts2.isParenthesizedTypeNode(node)) {
|
|
1047
|
-
return typeToDsl(node.type,
|
|
2183
|
+
return typeToDsl(node.type, child(ctx));
|
|
1048
2184
|
}
|
|
1049
2185
|
if (ts2.isTypeLiteralNode(node)) {
|
|
1050
|
-
const
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
const key = m.name && (ts2.isIdentifier(m.name) || ts2.isStringLiteral(m.name)) ? ts2.isIdentifier(m.name) ? m.name.text : m.name.text : void 0;
|
|
1054
|
-
if (!key) continue;
|
|
1055
|
-
const optional = m.questionToken ? true : false;
|
|
1056
|
-
const val = typeToDsl(m.type, depth + 1) ?? "any()";
|
|
1057
|
-
fields.push(optional ? `${key}: ${val}.optional()` : `${key}: ${val}`);
|
|
1058
|
-
}
|
|
1059
|
-
return fields.length === 0 ? "shape({})" : `shape({ ${fields.join(", ")} })`;
|
|
2186
|
+
const mark = noteMark(ctx);
|
|
2187
|
+
const dsl = membersToShape(node.members, ctx);
|
|
2188
|
+
return { dsl, modeled: ctx.notes.length === mark };
|
|
1060
2189
|
}
|
|
1061
2190
|
if (ts2.isFunctionTypeNode(node) || ts2.isConstructorTypeNode(node)) {
|
|
1062
|
-
return fnSignatureDsl(node.parameters, node.type,
|
|
2191
|
+
return fnSignatureDsl(node.parameters, node.type, ctx);
|
|
2192
|
+
}
|
|
2193
|
+
if (ts2.isConditionalTypeNode(node)) {
|
|
2194
|
+
return conditionalDsl(node, ctx);
|
|
1063
2195
|
}
|
|
1064
2196
|
if (ts2.isTypeReferenceNode(node)) {
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
2197
|
+
return typeRefDsl(node, ctx);
|
|
2198
|
+
}
|
|
2199
|
+
if (ts2.isExpressionWithTypeArguments(node)) {
|
|
2200
|
+
ctx.notes.push("unmodeled type reference \u2192 any()");
|
|
2201
|
+
return { dsl: "any()", modeled: false };
|
|
2202
|
+
}
|
|
2203
|
+
ctx.notes.push("unmodeled type \u2192 any()");
|
|
2204
|
+
return { dsl: "any()", modeled: false };
|
|
2205
|
+
}
|
|
2206
|
+
function conditionalDsl(node, ctx) {
|
|
2207
|
+
const ext = ts2.isParenthesizedTypeNode(node.extendsType) ? node.extendsType.type : node.extendsType;
|
|
2208
|
+
if (ext.kind === ts2.SyntaxKind.UnknownKeyword) {
|
|
2209
|
+
const t2 = typeToDsl(node.trueType, child(ctx));
|
|
2210
|
+
ctx.notes.push(
|
|
2211
|
+
"conditional type \u2014 the extends side is `unknown` (always true); only the true branch is projected"
|
|
2212
|
+
);
|
|
2213
|
+
if (!t2) return { dsl: "any()", modeled: false };
|
|
2214
|
+
return { dsl: t2.dsl, modeled: false, ...t2.raw ? { raw: true } : {} };
|
|
2215
|
+
}
|
|
2216
|
+
const t = typeToDsl(node.trueType, child(ctx));
|
|
2217
|
+
const f = typeToDsl(node.falseType, child(ctx));
|
|
2218
|
+
if (!t || !f || !t.modeled || !f.modeled) {
|
|
2219
|
+
ctx.notes.push(
|
|
2220
|
+
"conditional type \u2014 not decidable from the declaration and its branches are not modeled \u2192 any() (same as the harvest face's undecidable branch)"
|
|
2221
|
+
);
|
|
2222
|
+
return { dsl: "any()", modeled: false };
|
|
2223
|
+
}
|
|
2224
|
+
ctx.notes.push(
|
|
2225
|
+
"conditional type \u2014 not decidable from the declaration; both branches are projected (union), same as the harvest face"
|
|
2226
|
+
);
|
|
2227
|
+
if (t.dsl === f.dsl) {
|
|
2228
|
+
return { dsl: t.dsl, modeled: false, ...t.raw ? { raw: true } : {} };
|
|
2229
|
+
}
|
|
2230
|
+
return { dsl: `union(${t.dsl}, ${f.dsl})`, modeled: false };
|
|
2231
|
+
}
|
|
2232
|
+
function noteMark(ctx) {
|
|
2233
|
+
return ctx.notes.length;
|
|
2234
|
+
}
|
|
2235
|
+
function child(ctx) {
|
|
2236
|
+
return {
|
|
2237
|
+
symbols: ctx.symbols,
|
|
2238
|
+
notes: ctx.notes,
|
|
2239
|
+
depth: ctx.depth + 1,
|
|
2240
|
+
resolving: ctx.resolving,
|
|
2241
|
+
lost: ctx.lost
|
|
2242
|
+
};
|
|
2243
|
+
}
|
|
2244
|
+
function literalKeys(node) {
|
|
2245
|
+
if (!node) return void 0;
|
|
2246
|
+
if (ts2.isLiteralTypeNode(node)) {
|
|
2247
|
+
const lit = node.literal;
|
|
2248
|
+
if (ts2.isStringLiteral(lit) || ts2.isNumericLiteral(lit)) return [lit.text];
|
|
2249
|
+
return void 0;
|
|
2250
|
+
}
|
|
2251
|
+
if (ts2.isUnionTypeNode(node)) {
|
|
2252
|
+
const out = [];
|
|
2253
|
+
for (const t of node.types) {
|
|
2254
|
+
const k = literalKeys(t);
|
|
2255
|
+
if (!k) return void 0;
|
|
2256
|
+
out.push(...k);
|
|
1070
2257
|
}
|
|
1071
|
-
|
|
1072
|
-
|
|
2258
|
+
return out.length > 0 ? out : void 0;
|
|
2259
|
+
}
|
|
2260
|
+
return void 0;
|
|
2261
|
+
}
|
|
2262
|
+
function recordDsl(args, ctx) {
|
|
2263
|
+
const val = args[1] ? typeToDsl(args[1], child(ctx)) : void 0;
|
|
2264
|
+
const keys = literalKeys(args[0]);
|
|
2265
|
+
if (keys && keys.length > 0 && val) {
|
|
2266
|
+
const fields = keys.map((k) => `${draftKey(k)}: ${val.dsl}`);
|
|
2267
|
+
return { dsl: `shape({ ${fields.join(", ")} })`, modeled: val.modeled };
|
|
2268
|
+
}
|
|
2269
|
+
ctx.notes.push(
|
|
2270
|
+
"Record value domain is NOT modeled \u2014 the contract DSL has no index signature, so this position only requires an object (weaker than TypeScript)"
|
|
2271
|
+
);
|
|
2272
|
+
return { dsl: "shape({})", modeled: false };
|
|
2273
|
+
}
|
|
2274
|
+
function partialDsl(args, ctx) {
|
|
2275
|
+
const inner = args[0];
|
|
2276
|
+
if (!inner) {
|
|
2277
|
+
ctx.notes.push("Partial<> without a type argument \u2192 any()");
|
|
2278
|
+
return { dsl: "any()", modeled: false };
|
|
2279
|
+
}
|
|
2280
|
+
const d = typeToDsl(inner, child(ctx));
|
|
2281
|
+
if (!d) {
|
|
2282
|
+
ctx.notes.push("Partial<T> over an unmodeled type \u2192 any()");
|
|
2283
|
+
return { dsl: "any()", modeled: false };
|
|
2284
|
+
}
|
|
2285
|
+
if (!d.dsl.startsWith("shape(")) {
|
|
2286
|
+
ctx.notes.push("Partial<T> over a non-shape type is not expressible \u2192 any()");
|
|
2287
|
+
return { dsl: "any()", modeled: false };
|
|
2288
|
+
}
|
|
2289
|
+
return { dsl: `partial(${d.dsl})`, modeled: d.modeled };
|
|
2290
|
+
}
|
|
2291
|
+
function typeRefDsl(node, ctx) {
|
|
2292
|
+
const name = typeRefName(node);
|
|
2293
|
+
const args = node.typeArguments ?? [];
|
|
2294
|
+
if (name === "Array" || name === "ReadonlyArray" || name === "readonly") {
|
|
2295
|
+
const el = args[0] ? typeToDsl(args[0], child(ctx)) : void 0;
|
|
2296
|
+
return { dsl: `array(${el?.dsl ?? "any()"})`, modeled: el?.modeled ?? false };
|
|
2297
|
+
}
|
|
2298
|
+
if (name === "Promise" || name === "PromiseLike") {
|
|
2299
|
+
ctx.notes.push(
|
|
2300
|
+
`${name}<\u2026> is not modeled \u2014 the Promise shell is kept (same as the harvest face); this position carries no constraint`
|
|
2301
|
+
);
|
|
2302
|
+
return { dsl: "any()", modeled: false };
|
|
2303
|
+
}
|
|
2304
|
+
if (name === "Record") return recordDsl(args, ctx);
|
|
2305
|
+
if (name === "Partial") return partialDsl(args, ctx);
|
|
2306
|
+
const alias = lookupAlias(name, ctx);
|
|
2307
|
+
if (alias) {
|
|
2308
|
+
if (ctx.resolving.has(name)) {
|
|
2309
|
+
ctx.notes.push(`type alias '${name}' is recursive \u2192 any()`);
|
|
2310
|
+
return { dsl: "any()", modeled: false };
|
|
2311
|
+
}
|
|
2312
|
+
ctx.resolving.add(name);
|
|
2313
|
+
const d = typeToDsl(alias.type, child(ctx));
|
|
2314
|
+
ctx.resolving.delete(name);
|
|
2315
|
+
if (!d) {
|
|
2316
|
+
ctx.notes.push(`type alias '${name}' has no type \u2192 any()`);
|
|
2317
|
+
return { dsl: "any()", modeled: false };
|
|
2318
|
+
}
|
|
2319
|
+
return d;
|
|
2320
|
+
}
|
|
2321
|
+
const iface = lookupInterface(name, ctx);
|
|
2322
|
+
if (iface) {
|
|
2323
|
+
const mark = noteMark(ctx);
|
|
2324
|
+
ctx.resolving.add(name);
|
|
2325
|
+
const dsl = membersToShape(interfaceMembers(iface, ctx), ctx);
|
|
2326
|
+
ctx.resolving.delete(name);
|
|
2327
|
+
return { dsl, modeled: ctx.notes.length === mark };
|
|
2328
|
+
}
|
|
2329
|
+
ctx.notes.push(`unmodeled type reference '${name}' \u2192 any()`);
|
|
2330
|
+
return { dsl: "any()", modeled: false };
|
|
2331
|
+
}
|
|
2332
|
+
function lookupInterface(name, ctx) {
|
|
2333
|
+
if (!name.includes(".")) return ctx.symbols.interfaces.get(name);
|
|
2334
|
+
const qualified = ctx.symbols.qualifiedInterfaces.get(name);
|
|
2335
|
+
if (qualified) return qualified;
|
|
2336
|
+
const short = name.slice(name.lastIndexOf(".") + 1);
|
|
2337
|
+
const decl = ctx.symbols.interfaces.get(short);
|
|
2338
|
+
if (decl && inAmbientModuleBlock2(decl)) return decl;
|
|
2339
|
+
return void 0;
|
|
2340
|
+
}
|
|
2341
|
+
function lookupAlias(name, ctx) {
|
|
2342
|
+
if (!name.includes(".")) return ctx.symbols.aliases.get(name);
|
|
2343
|
+
const qualified = ctx.symbols.qualifiedAliases.get(name);
|
|
2344
|
+
if (qualified) return qualified;
|
|
2345
|
+
const short = name.slice(name.lastIndexOf(".") + 1);
|
|
2346
|
+
const decl = ctx.symbols.aliases.get(short);
|
|
2347
|
+
if (decl && inAmbientModuleBlock2(decl)) return decl;
|
|
2348
|
+
return void 0;
|
|
2349
|
+
}
|
|
2350
|
+
function inAmbientModuleBlock2(decl) {
|
|
2351
|
+
let node = decl.parent;
|
|
2352
|
+
while (node) {
|
|
2353
|
+
if (ts2.isModuleDeclaration(node) && ts2.isStringLiteral(node.name)) return true;
|
|
2354
|
+
node = node.parent;
|
|
2355
|
+
}
|
|
2356
|
+
return false;
|
|
2357
|
+
}
|
|
2358
|
+
function interfaceMembers(iface, ctx) {
|
|
2359
|
+
const out = [];
|
|
2360
|
+
for (const clause of iface.heritageClauses ?? []) {
|
|
2361
|
+
if (clause.token !== ts2.SyntaxKind.ExtendsKeyword) continue;
|
|
2362
|
+
for (const t of clause.types) {
|
|
2363
|
+
const name = heritageBaseName2(t);
|
|
2364
|
+
const base = name ? lookupInterface(name, ctx) : void 0;
|
|
2365
|
+
if (!base || !name) {
|
|
2366
|
+
ctx.notes.push(
|
|
2367
|
+
`interface '${iface.name.text}' extends ${name ?? "a non-local type"} \u2014 the inherited members are not modeled`
|
|
2368
|
+
);
|
|
2369
|
+
continue;
|
|
2370
|
+
}
|
|
2371
|
+
if (ctx.resolving.has(name)) {
|
|
2372
|
+
ctx.notes.push(`interface '${name}' is recursive \u2014 the inherited members are not modeled`);
|
|
2373
|
+
continue;
|
|
2374
|
+
}
|
|
2375
|
+
ctx.resolving.add(name);
|
|
2376
|
+
out.push(...interfaceMembers(base, ctx));
|
|
2377
|
+
ctx.resolving.delete(name);
|
|
1073
2378
|
}
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
2379
|
+
}
|
|
2380
|
+
out.push(...iface.members);
|
|
2381
|
+
return out;
|
|
2382
|
+
}
|
|
2383
|
+
function heritageBaseName2(t) {
|
|
2384
|
+
const expr = t.expression;
|
|
2385
|
+
if (ts2.isIdentifier(expr)) return expr.text;
|
|
2386
|
+
if (ts2.isPropertyAccessExpression(expr) && ts2.isIdentifier(expr.expression) && ts2.isIdentifier(expr.name)) {
|
|
2387
|
+
return `${expr.expression.text}.${expr.name.text}`;
|
|
2388
|
+
}
|
|
2389
|
+
return void 0;
|
|
2390
|
+
}
|
|
2391
|
+
function membersToShape(members, ctx) {
|
|
2392
|
+
const fields = [];
|
|
2393
|
+
let dropped = 0;
|
|
2394
|
+
for (const m of members) {
|
|
2395
|
+
const field = memberToField(m, ctx);
|
|
2396
|
+
if (field === void 0) {
|
|
2397
|
+
dropped++;
|
|
2398
|
+
continue;
|
|
1077
2399
|
}
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
2400
|
+
fields.push(field);
|
|
2401
|
+
}
|
|
2402
|
+
if (fields.length === 0) {
|
|
2403
|
+
if (dropped > 0) {
|
|
2404
|
+
ctx.notes.push("all members of the type are unmodeled \u2192 any()");
|
|
2405
|
+
return "any()";
|
|
1081
2406
|
}
|
|
2407
|
+
return "shape({})";
|
|
2408
|
+
}
|
|
2409
|
+
return `shape({ ${fields.join(", ")} })`;
|
|
2410
|
+
}
|
|
2411
|
+
function memberToField(m, ctx) {
|
|
2412
|
+
if (ts2.isPropertySignature(m)) {
|
|
2413
|
+
const key = memberKeyOrNote(m.name, "property", ctx);
|
|
2414
|
+
if (key === void 0) return void 0;
|
|
2415
|
+
const d = typeToDsl(m.type, child(ctx)) ?? { dsl: "any()", modeled: false };
|
|
2416
|
+
const v = m.questionToken ? optionalize(d) : d.dsl;
|
|
2417
|
+
return `${draftKey(key)}: ${v}`;
|
|
2418
|
+
}
|
|
2419
|
+
if (ts2.isMethodSignature(m)) {
|
|
2420
|
+
const key = memberKeyOrNote(m.name, "method", ctx);
|
|
2421
|
+
if (key === void 0) return void 0;
|
|
2422
|
+
const d = fnSignatureDsl(m.parameters, m.type, ctx);
|
|
2423
|
+
const v = m.questionToken ? optionalize(d) : d.dsl;
|
|
2424
|
+
return `${draftKey(key)}: ${v}`;
|
|
2425
|
+
}
|
|
2426
|
+
if (ts2.isIndexSignatureDeclaration(m)) {
|
|
2427
|
+
ctx.notes.push("index signature member is not modeled (the contract DSL has no index signature)");
|
|
1082
2428
|
return void 0;
|
|
1083
2429
|
}
|
|
1084
|
-
if (ts2.
|
|
2430
|
+
if (ts2.isCallSignatureDeclaration(m) || ts2.isConstructSignatureDeclaration(m)) {
|
|
2431
|
+
ctx.notes.push("call/construct signature member is not modeled");
|
|
1085
2432
|
return void 0;
|
|
1086
2433
|
}
|
|
2434
|
+
if (ts2.isGetAccessorDeclaration(m) || ts2.isSetAccessorDeclaration(m)) {
|
|
2435
|
+
ctx.notes.push("accessor member is not modeled");
|
|
2436
|
+
return void 0;
|
|
2437
|
+
}
|
|
2438
|
+
ctx.notes.push("member of an unsupported form is not projected");
|
|
1087
2439
|
return void 0;
|
|
1088
2440
|
}
|
|
1089
|
-
function
|
|
1090
|
-
const
|
|
1091
|
-
const notes = [];
|
|
2441
|
+
function paramSlots(params, ctx) {
|
|
2442
|
+
const out = [];
|
|
1092
2443
|
params.forEach((p, i) => {
|
|
1093
|
-
|
|
1094
|
-
if (
|
|
1095
|
-
else name = `p${i}`;
|
|
2444
|
+
const name = p.name && ts2.isIdentifier(p.name) ? p.name.text : `p${i}`;
|
|
2445
|
+
if (name === "this") return;
|
|
1096
2446
|
if (p.dotDotDotToken) {
|
|
1097
|
-
notes.push(`${name}: rest \
|
|
1098
|
-
|
|
2447
|
+
ctx.notes.push(`${name}: rest parameter \u2192 array(any())`);
|
|
2448
|
+
out.push({ name, optional: false, dsl: "array(any())", modeled: false });
|
|
1099
2449
|
return;
|
|
1100
2450
|
}
|
|
1101
|
-
const optional = Boolean(p.questionToken) || p.initializer;
|
|
1102
|
-
|
|
1103
|
-
if (
|
|
1104
|
-
notes.push(`${name}:
|
|
1105
|
-
dsl
|
|
2451
|
+
const optional = Boolean(p.questionToken) || Boolean(p.initializer);
|
|
2452
|
+
const d = typeToDsl(p.type, child(ctx));
|
|
2453
|
+
if (!d) {
|
|
2454
|
+
ctx.notes.push(`${name}: no type annotation \u2192 any()`);
|
|
2455
|
+
out.push({ name, optional, dsl: "any()", modeled: false, plain: true });
|
|
2456
|
+
return;
|
|
1106
2457
|
}
|
|
1107
|
-
|
|
2458
|
+
out.push({
|
|
2459
|
+
name,
|
|
2460
|
+
optional,
|
|
2461
|
+
dsl: d.dsl,
|
|
2462
|
+
...d.raw ? { raw: true } : {},
|
|
2463
|
+
modeled: d.modeled
|
|
2464
|
+
});
|
|
1108
2465
|
});
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
2466
|
+
return out;
|
|
2467
|
+
}
|
|
2468
|
+
function slotText(s) {
|
|
2469
|
+
if (s.plain) return s.dsl;
|
|
2470
|
+
if (!s.optional) return s.dsl;
|
|
2471
|
+
return optionalize({
|
|
2472
|
+
dsl: s.dsl,
|
|
2473
|
+
modeled: s.modeled,
|
|
2474
|
+
...s.raw ? { raw: true } : {}
|
|
2475
|
+
});
|
|
2476
|
+
}
|
|
2477
|
+
function slotsToDsl(slots) {
|
|
2478
|
+
const fields = slots.map((s) => `${s.name}: ${slotText(s)}`);
|
|
2479
|
+
return fields.length === 0 ? "{}" : `{ ${fields.join(", ")} }`;
|
|
2480
|
+
}
|
|
2481
|
+
function paramsDsl(params, ctx) {
|
|
2482
|
+
return slotsToDsl(paramSlots(params, ctx));
|
|
2483
|
+
}
|
|
2484
|
+
function mergeParamSlots(per) {
|
|
2485
|
+
const first = per[0];
|
|
2486
|
+
if (!first || first.length === 0) return void 0;
|
|
2487
|
+
const out = [];
|
|
2488
|
+
for (let i = 0; i < first.length; i++) {
|
|
2489
|
+
const slot = first[i];
|
|
2490
|
+
const texts = [];
|
|
2491
|
+
let ok = true;
|
|
2492
|
+
for (const slots of per) {
|
|
2493
|
+
const s = slots[i];
|
|
2494
|
+
if (!s || s.optional !== slot.optional || !s.modeled || !slot.modeled) {
|
|
2495
|
+
ok = false;
|
|
2496
|
+
break;
|
|
2497
|
+
}
|
|
2498
|
+
if (!texts.includes(s.dsl)) texts.push(s.dsl);
|
|
2499
|
+
}
|
|
2500
|
+
if (!ok) return void 0;
|
|
2501
|
+
const mergedText = texts.length === 1 ? texts[0] : `union(${texts.join(", ")})`;
|
|
2502
|
+
out.push({
|
|
2503
|
+
name: slot.name,
|
|
2504
|
+
optional: slot.optional,
|
|
2505
|
+
dsl: mergedText,
|
|
2506
|
+
modeled: true,
|
|
2507
|
+
...texts.length === 1 && slot.raw ? { raw: true } : {}
|
|
2508
|
+
});
|
|
2509
|
+
}
|
|
2510
|
+
return out;
|
|
2511
|
+
}
|
|
2512
|
+
function fnSignatureDsl(params, ret, ctx) {
|
|
2513
|
+
const p = paramsDsl(params, ctx);
|
|
2514
|
+
const r = typeToDsl(ret, child(ctx));
|
|
2515
|
+
if (!r) {
|
|
2516
|
+
return { dsl: `fn(${p}, any())`, modeled: true };
|
|
1113
2517
|
}
|
|
1114
|
-
|
|
1115
|
-
return `fn(${obj}, ${retDsl})`;
|
|
2518
|
+
return { dsl: `fn(${p}, ${constraintSlot(r)})`, modeled: r.modeled };
|
|
1116
2519
|
}
|
|
1117
2520
|
function pickName(decl) {
|
|
1118
2521
|
return decl.name && ts2.isIdentifier(decl.name) ? decl.name.text : void 0;
|
|
1119
2522
|
}
|
|
2523
|
+
function isExported(stmt) {
|
|
2524
|
+
const mods = stmt.modifiers;
|
|
2525
|
+
return mods?.some((m) => m.kind === ts2.SyntaxKind.ExportKeyword) ?? false;
|
|
2526
|
+
}
|
|
2527
|
+
function isDefaultExported(stmt) {
|
|
2528
|
+
const mods = stmt.modifiers;
|
|
2529
|
+
return mods?.some((m) => m.kind === ts2.SyntaxKind.DefaultKeyword) ?? false;
|
|
2530
|
+
}
|
|
2531
|
+
function collectSymbols(sf) {
|
|
2532
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
2533
|
+
const interfaces = /* @__PURE__ */ new Map();
|
|
2534
|
+
const qualifiedAliases = /* @__PURE__ */ new Map();
|
|
2535
|
+
const qualifiedInterfaces = /* @__PURE__ */ new Map();
|
|
2536
|
+
const walk = (stmts, ns) => {
|
|
2537
|
+
for (const stmt of stmts) {
|
|
2538
|
+
if (ts2.isTypeAliasDeclaration(stmt)) {
|
|
2539
|
+
if (!aliases.has(stmt.name.text)) aliases.set(stmt.name.text, stmt);
|
|
2540
|
+
const q = [...ns, stmt.name.text].join(".");
|
|
2541
|
+
if (q !== stmt.name.text && !qualifiedAliases.has(q)) qualifiedAliases.set(q, stmt);
|
|
2542
|
+
} else if (ts2.isInterfaceDeclaration(stmt)) {
|
|
2543
|
+
if (!interfaces.has(stmt.name.text)) interfaces.set(stmt.name.text, stmt);
|
|
2544
|
+
const q = [...ns, stmt.name.text].join(".");
|
|
2545
|
+
if (q !== stmt.name.text && !qualifiedInterfaces.has(q)) qualifiedInterfaces.set(q, stmt);
|
|
2546
|
+
} else if (ts2.isModuleDeclaration(stmt) && stmt.body && ts2.isModuleBlock(stmt.body) && ts2.isIdentifier(stmt.name) && stmt.name.text !== "global") {
|
|
2547
|
+
walk(stmt.body.statements, [...ns, stmt.name.text]);
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
};
|
|
2551
|
+
walk(sf.statements, []);
|
|
2552
|
+
return { aliases, interfaces, qualifiedAliases, qualifiedInterfaces };
|
|
2553
|
+
}
|
|
2554
|
+
function collectLocals(stmts) {
|
|
2555
|
+
const out = /* @__PURE__ */ new Map();
|
|
2556
|
+
const put = (name, stmt) => {
|
|
2557
|
+
if (name && !out.has(name)) out.set(name, stmt);
|
|
2558
|
+
};
|
|
2559
|
+
for (const stmt of stmts) {
|
|
2560
|
+
if (ts2.isVariableStatement(stmt)) {
|
|
2561
|
+
for (const d of stmt.declarationList.declarations) put(pickName(d), stmt);
|
|
2562
|
+
continue;
|
|
2563
|
+
}
|
|
2564
|
+
if (ts2.isFunctionDeclaration(stmt) || ts2.isClassDeclaration(stmt) || ts2.isEnumDeclaration(stmt) || ts2.isModuleDeclaration(stmt)) {
|
|
2565
|
+
put(pickName(stmt), stmt);
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
return out;
|
|
2569
|
+
}
|
|
2570
|
+
function takeNotes(ctx) {
|
|
2571
|
+
const out = ctx.notes.slice();
|
|
2572
|
+
ctx.notes.length = 0;
|
|
2573
|
+
return out;
|
|
2574
|
+
}
|
|
2575
|
+
function enumMemberValue(m, counter, ctx) {
|
|
2576
|
+
const init = m.initializer;
|
|
2577
|
+
if (!init) {
|
|
2578
|
+
const v = counter.value;
|
|
2579
|
+
counter.value = v + 1;
|
|
2580
|
+
return String(v);
|
|
2581
|
+
}
|
|
2582
|
+
if (ts2.isStringLiteral(init)) return JSON.stringify(init.text);
|
|
2583
|
+
if (ts2.isNumericLiteral(init)) {
|
|
2584
|
+
const v = Number(init.text);
|
|
2585
|
+
counter.value = v + 1;
|
|
2586
|
+
return init.text;
|
|
2587
|
+
}
|
|
2588
|
+
if (ts2.isPrefixUnaryExpression(init) && init.operator === ts2.SyntaxKind.MinusToken && ts2.isNumericLiteral(init.operand)) {
|
|
2589
|
+
const v = -Number(init.operand.text);
|
|
2590
|
+
counter.value = v + 1;
|
|
2591
|
+
return `-${init.operand.text}`;
|
|
2592
|
+
}
|
|
2593
|
+
ctx.notes.push("enum member with a computed initializer is not projected");
|
|
2594
|
+
return void 0;
|
|
2595
|
+
}
|
|
2596
|
+
function namespaceMemberField(stmt, ctx) {
|
|
2597
|
+
if (!isExported(stmt)) return void 0;
|
|
2598
|
+
if (ts2.isVariableStatement(stmt)) {
|
|
2599
|
+
const fields = [];
|
|
2600
|
+
for (const d of stmt.declarationList.declarations) {
|
|
2601
|
+
const name = pickName(d);
|
|
2602
|
+
if (!name) continue;
|
|
2603
|
+
if (d.type) {
|
|
2604
|
+
const t = typeToDsl(d.type, child(ctx));
|
|
2605
|
+
if (!t) continue;
|
|
2606
|
+
fields.push(`${draftKey(name)}: ${t.dsl}`);
|
|
2607
|
+
continue;
|
|
2608
|
+
}
|
|
2609
|
+
const init = d.initializer;
|
|
2610
|
+
if (init && (ts2.isStringLiteral(init) || ts2.isNumericLiteral(init))) {
|
|
2611
|
+
const raw = ts2.isStringLiteral(init) ? JSON.stringify(init.text) : init.text;
|
|
2612
|
+
fields.push(`${draftKey(name)}: ${raw}`);
|
|
2613
|
+
continue;
|
|
2614
|
+
}
|
|
2615
|
+
ctx.notes.push(`namespace member '${name}' has no inferable value type`);
|
|
2616
|
+
}
|
|
2617
|
+
return fields.length > 0 ? fields.join(", ") : void 0;
|
|
2618
|
+
}
|
|
2619
|
+
if (ts2.isFunctionDeclaration(stmt)) {
|
|
2620
|
+
const name = pickName(stmt);
|
|
2621
|
+
if (!name) return void 0;
|
|
2622
|
+
const d = fnSignatureDsl(stmt.parameters, stmt.type, ctx);
|
|
2623
|
+
return `${draftKey(name)}: ${d.dsl}`;
|
|
2624
|
+
}
|
|
2625
|
+
if (ts2.isEnumDeclaration(stmt)) {
|
|
2626
|
+
const name = pickName(stmt);
|
|
2627
|
+
if (!name) return void 0;
|
|
2628
|
+
const inner = enumShape(stmt, ctx);
|
|
2629
|
+
return `${draftKey(name)}: ${inner}`;
|
|
2630
|
+
}
|
|
2631
|
+
if (ts2.isModuleDeclaration(stmt)) {
|
|
2632
|
+
const name = pickName(stmt);
|
|
2633
|
+
if (!name || !stmt.body || !ts2.isModuleBlock(stmt.body)) return void 0;
|
|
2634
|
+
const fields = [];
|
|
2635
|
+
for (const inner of stmt.body.statements) {
|
|
2636
|
+
const f = namespaceMemberField(inner, ctx);
|
|
2637
|
+
if (f) fields.push(f);
|
|
2638
|
+
}
|
|
2639
|
+
return `${draftKey(name)}: shape({ ${fields.join(", ")} })`;
|
|
2640
|
+
}
|
|
2641
|
+
return void 0;
|
|
2642
|
+
}
|
|
2643
|
+
function enumShape(decl, ctx) {
|
|
2644
|
+
const fields = [];
|
|
2645
|
+
const counter = { value: 0 };
|
|
2646
|
+
for (const m of decl.members) {
|
|
2647
|
+
const key = memberKey(m.name);
|
|
2648
|
+
if (key === void 0) {
|
|
2649
|
+
ctx.notes.push("enum member with a computed name is not projected");
|
|
2650
|
+
continue;
|
|
2651
|
+
}
|
|
2652
|
+
const value = enumMemberValue(m, counter, ctx);
|
|
2653
|
+
if (value === void 0) continue;
|
|
2654
|
+
fields.push(`${draftKey(key)}: ${value}`);
|
|
2655
|
+
}
|
|
2656
|
+
return fields.length === 0 ? "shape({})" : `shape({ ${fields.join(", ")} })`;
|
|
2657
|
+
}
|
|
2658
|
+
function mergeOverloads(decls, ctx) {
|
|
2659
|
+
const first = decls[0];
|
|
2660
|
+
if (decls.length === 1) {
|
|
2661
|
+
const d = fnSignatureDsl(first.parameters, first.type, ctx);
|
|
2662
|
+
return {
|
|
2663
|
+
dsl: d,
|
|
2664
|
+
sigs: [d.dsl],
|
|
2665
|
+
paramsMerged: true,
|
|
2666
|
+
arity: arityOf(first),
|
|
2667
|
+
arityDiffers: false
|
|
2668
|
+
};
|
|
2669
|
+
}
|
|
2670
|
+
const slotsPer = [];
|
|
2671
|
+
const rets = [];
|
|
2672
|
+
const sigs = [];
|
|
2673
|
+
for (const d of decls) {
|
|
2674
|
+
const slots = paramSlots(d.parameters, ctx);
|
|
2675
|
+
slotsPer.push(slots);
|
|
2676
|
+
const params = slotsToDsl(slots);
|
|
2677
|
+
const r = typeToDsl(d.type, child(ctx));
|
|
2678
|
+
const ret2 = r ?? { dsl: "any()", modeled: true };
|
|
2679
|
+
rets.push(ret2);
|
|
2680
|
+
sigs.push(`fn(${params}, ${constraintSlot(ret2)})`);
|
|
2681
|
+
}
|
|
2682
|
+
const arity = slotsPer[0].length;
|
|
2683
|
+
const arityDiffers = slotsPer.some((s) => s.length !== arity);
|
|
2684
|
+
const merged = arityDiffers ? void 0 : mergeParamSlots(slotsPer);
|
|
2685
|
+
const p = slotsToDsl(merged ?? slotsPer[0]);
|
|
2686
|
+
if (rets.some((r) => !r.modeled)) {
|
|
2687
|
+
ctx.notes.push("an overload return type is not modeled \u2192 any()");
|
|
2688
|
+
return {
|
|
2689
|
+
dsl: { dsl: `fn(${p}, any())`, modeled: false },
|
|
2690
|
+
sigs,
|
|
2691
|
+
paramsMerged: merged !== void 0,
|
|
2692
|
+
arity,
|
|
2693
|
+
arityDiffers
|
|
2694
|
+
};
|
|
2695
|
+
}
|
|
2696
|
+
const uniq = /* @__PURE__ */ new Map();
|
|
2697
|
+
for (const r of rets) if (!uniq.has(r.dsl)) uniq.set(r.dsl, r);
|
|
2698
|
+
const texts = [...uniq.values()];
|
|
2699
|
+
const ret = texts.length === 1 ? constraintSlot(texts[0]) : `union(${texts.map((r) => r.dsl).join(", ")})`;
|
|
2700
|
+
return {
|
|
2701
|
+
dsl: { dsl: `fn(${p}, ${ret})`, modeled: true },
|
|
2702
|
+
sigs,
|
|
2703
|
+
paramsMerged: merged !== void 0,
|
|
2704
|
+
arity,
|
|
2705
|
+
arityDiffers
|
|
2706
|
+
};
|
|
2707
|
+
}
|
|
2708
|
+
function arityOf(d) {
|
|
2709
|
+
let n = 0;
|
|
2710
|
+
for (const p of d.parameters) {
|
|
2711
|
+
if (p.name && ts2.isIdentifier(p.name) && p.name.text === "this") continue;
|
|
2712
|
+
n++;
|
|
2713
|
+
}
|
|
2714
|
+
return n;
|
|
2715
|
+
}
|
|
1120
2716
|
function collectExports(sourceFile) {
|
|
2717
|
+
const symbols = collectSymbols(sourceFile);
|
|
2718
|
+
const ctx = {
|
|
2719
|
+
symbols,
|
|
2720
|
+
notes: [],
|
|
2721
|
+
depth: 0,
|
|
2722
|
+
resolving: /* @__PURE__ */ new Set(),
|
|
2723
|
+
lost: { n: 0 }
|
|
2724
|
+
};
|
|
1121
2725
|
const out = [];
|
|
2726
|
+
const fnGroups = /* @__PURE__ */ new Map();
|
|
2727
|
+
const emittedFns = /* @__PURE__ */ new Set();
|
|
1122
2728
|
const push = (e) => {
|
|
2729
|
+
const notes = takeNotes(ctx);
|
|
2730
|
+
if (notes.length > 0) {
|
|
2731
|
+
e.note = e.note ? `${e.note}; ${notes.join("; ")}` : notes.join("; ");
|
|
2732
|
+
if (e.dsl !== void 0) e.partial = true;
|
|
2733
|
+
}
|
|
1123
2734
|
out.push(e);
|
|
1124
2735
|
};
|
|
1125
|
-
|
|
1126
|
-
if (
|
|
1127
|
-
|
|
1128
|
-
if (
|
|
1129
|
-
const fn = stmt;
|
|
1130
|
-
const dsl = fnSignatureDsl(fn.parameters, fn.type, 0);
|
|
1131
|
-
push({ name, dsl, kind: "function" });
|
|
1132
|
-
continue;
|
|
1133
|
-
}
|
|
1134
|
-
if (ts2.isFunctionDeclaration(stmt) && pickName(stmt)) {
|
|
1135
|
-
const isExport = stmt.modifiers?.some((m) => m.kind === ts2.SyntaxKind.ExportKeyword) ?? false;
|
|
1136
|
-
if (!isExport) continue;
|
|
1137
|
-
const name = pickName(stmt);
|
|
1138
|
-
push({ name, dsl: fnSignatureDsl(stmt.parameters, stmt.type, 0), kind: "function" });
|
|
1139
|
-
continue;
|
|
2736
|
+
const pushScoped = (e, ambient) => {
|
|
2737
|
+
if (ambient) {
|
|
2738
|
+
e.note = e.note ? `${e.note}; ${ambient}` : ambient;
|
|
2739
|
+
if (e.dsl !== void 0) e.partial = true;
|
|
1140
2740
|
}
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
2741
|
+
push(e);
|
|
2742
|
+
};
|
|
2743
|
+
const fnDecl = (stmt, name, ambient) => {
|
|
2744
|
+
const group = fnGroups.get(name);
|
|
2745
|
+
if (group) group.decls.push(stmt);
|
|
2746
|
+
else fnGroups.set(name, { decls: [stmt], ambient });
|
|
2747
|
+
};
|
|
2748
|
+
const collectStatements2 = (stmts, prefix, ambient) => {
|
|
2749
|
+
const locals = collectLocals(stmts);
|
|
2750
|
+
for (const stmt of stmts) {
|
|
2751
|
+
if (ts2.isFunctionDeclaration(stmt)) {
|
|
2752
|
+
const exported = isExported(stmt);
|
|
2753
|
+
const isDefault = isDefaultExported(stmt);
|
|
2754
|
+
if (!exported && !isDefault) continue;
|
|
2755
|
+
const name = isDefault ? "default" : pickName(stmt);
|
|
2756
|
+
if (!name) {
|
|
2757
|
+
pushScoped(
|
|
2758
|
+
{
|
|
2759
|
+
name: `${prefix}default`,
|
|
2760
|
+
note: "anonymous default function is not projected",
|
|
2761
|
+
kind: "other"
|
|
2762
|
+
},
|
|
2763
|
+
ambient
|
|
2764
|
+
);
|
|
2765
|
+
continue;
|
|
1154
2766
|
}
|
|
2767
|
+
fnDecl(stmt, `${prefix}${name}`, ambient);
|
|
2768
|
+
continue;
|
|
1155
2769
|
}
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
2770
|
+
if (ts2.isVariableStatement(stmt)) {
|
|
2771
|
+
if (!isExported(stmt)) continue;
|
|
2772
|
+
for (const d of stmt.declarationList.declarations) {
|
|
2773
|
+
const name = pickName(d);
|
|
2774
|
+
if (!name) continue;
|
|
2775
|
+
const t = typeToDsl(d.type, ctx);
|
|
2776
|
+
if (!t) {
|
|
2777
|
+
pushScoped(
|
|
2778
|
+
{
|
|
2779
|
+
name: `${prefix}${name}`,
|
|
2780
|
+
note: "no type annotation \u2014 the value is not projected",
|
|
2781
|
+
kind: "value"
|
|
2782
|
+
},
|
|
2783
|
+
ambient
|
|
2784
|
+
);
|
|
2785
|
+
continue;
|
|
2786
|
+
}
|
|
2787
|
+
pushScoped(
|
|
2788
|
+
{
|
|
2789
|
+
name: `${prefix}${name}`,
|
|
2790
|
+
dsl: constraintSlot(t),
|
|
2791
|
+
kind: t.dsl.startsWith("fn(") ? "function" : "value",
|
|
2792
|
+
...t.modeled ? {} : { partial: true }
|
|
2793
|
+
},
|
|
2794
|
+
ambient
|
|
2795
|
+
);
|
|
2796
|
+
}
|
|
2797
|
+
continue;
|
|
2798
|
+
}
|
|
2799
|
+
if (ts2.isClassDeclaration(stmt)) {
|
|
2800
|
+
if (!isExported(stmt) && !isDefaultExported(stmt)) continue;
|
|
2801
|
+
const name = isDefaultExported(stmt) ? "default" : pickName(stmt);
|
|
2802
|
+
if (!name) {
|
|
2803
|
+
pushScoped(
|
|
2804
|
+
{ name: `${prefix}default`, note: "anonymous class is not projected", kind: "class" },
|
|
2805
|
+
ambient
|
|
2806
|
+
);
|
|
2807
|
+
continue;
|
|
2808
|
+
}
|
|
2809
|
+
const ctor = stmt.members.find(ts2.isConstructorDeclaration);
|
|
2810
|
+
if (ctor) {
|
|
2811
|
+
const slots = paramSlots(ctor.parameters, ctx);
|
|
2812
|
+
pushScoped(
|
|
2813
|
+
{
|
|
2814
|
+
name: `${prefix}${name}`,
|
|
2815
|
+
dsl: `fn(${slotsToDsl(slots)}, any())`,
|
|
2816
|
+
kind: "class",
|
|
2817
|
+
note: "constructor signature projected as a plain fn \u2014 the contract DSL has no `new` semantics, and the instance side is not modeled",
|
|
2818
|
+
partial: true
|
|
2819
|
+
},
|
|
2820
|
+
ambient
|
|
2821
|
+
);
|
|
2822
|
+
}
|
|
2823
|
+
let anyMethod = false;
|
|
2824
|
+
for (const m of stmt.members) {
|
|
2825
|
+
if (ts2.isMethodDeclaration(m) || ts2.isMethodSignature(m)) {
|
|
2826
|
+
const mname = pickName(m);
|
|
2827
|
+
if (!mname) continue;
|
|
2828
|
+
anyMethod = true;
|
|
2829
|
+
const d = fnSignatureDsl(m.parameters, m.type, ctx);
|
|
2830
|
+
pushScoped(
|
|
2831
|
+
{
|
|
2832
|
+
name: `${prefix}${name}.${mname}`,
|
|
2833
|
+
dsl: d.dsl,
|
|
2834
|
+
kind: "function",
|
|
2835
|
+
...d.modeled ? {} : { partial: true }
|
|
2836
|
+
},
|
|
2837
|
+
ambient
|
|
2838
|
+
);
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2841
|
+
if (!anyMethod && !ctor) {
|
|
2842
|
+
pushScoped(
|
|
2843
|
+
{
|
|
2844
|
+
name: `${prefix}${name}`,
|
|
2845
|
+
note: "class without public methods projected",
|
|
2846
|
+
kind: "class"
|
|
2847
|
+
},
|
|
2848
|
+
ambient
|
|
2849
|
+
);
|
|
2850
|
+
}
|
|
1164
2851
|
continue;
|
|
1165
2852
|
}
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
kind: "function"
|
|
1176
|
-
});
|
|
2853
|
+
if (ts2.isEnumDeclaration(stmt)) {
|
|
2854
|
+
if (!isExported(stmt) && !isDefaultExported(stmt)) continue;
|
|
2855
|
+
const name = isDefaultExported(stmt) ? "default" : pickName(stmt);
|
|
2856
|
+
if (!name) {
|
|
2857
|
+
pushScoped(
|
|
2858
|
+
{ name: `${prefix}enum`, note: "anonymous enum is not projected", kind: "other" },
|
|
2859
|
+
ambient
|
|
2860
|
+
);
|
|
2861
|
+
continue;
|
|
1177
2862
|
}
|
|
2863
|
+
const dsl = enumShape(stmt, ctx);
|
|
2864
|
+
pushScoped({ name: `${prefix}${name}`, dsl, kind: "value" }, ambient);
|
|
2865
|
+
continue;
|
|
1178
2866
|
}
|
|
1179
|
-
if (
|
|
1180
|
-
|
|
2867
|
+
if (ts2.isModuleDeclaration(stmt)) {
|
|
2868
|
+
if (ts2.isStringLiteral(stmt.name)) {
|
|
2869
|
+
const modName = stmt.name.text;
|
|
2870
|
+
if (!stmt.body || !ts2.isModuleBlock(stmt.body)) {
|
|
2871
|
+
pushScoped(
|
|
2872
|
+
{
|
|
2873
|
+
name: `${prefix}${modName}`,
|
|
2874
|
+
note: "ambient module without a body is not projected",
|
|
2875
|
+
kind: "other"
|
|
2876
|
+
},
|
|
2877
|
+
ambient
|
|
2878
|
+
);
|
|
2879
|
+
continue;
|
|
2880
|
+
}
|
|
2881
|
+
collectStatements2(
|
|
2882
|
+
stmt.body.statements,
|
|
2883
|
+
`${prefix}${modName}.`,
|
|
2884
|
+
`ambient module "${modName}" \u2014 not an export of this file (review before accepting)`
|
|
2885
|
+
);
|
|
2886
|
+
continue;
|
|
2887
|
+
}
|
|
2888
|
+
if (ts2.isIdentifier(stmt.name) && stmt.name.text === "global") {
|
|
2889
|
+
if (!stmt.body || !ts2.isModuleBlock(stmt.body)) {
|
|
2890
|
+
pushScoped(
|
|
2891
|
+
{ name: `${prefix}global`, note: "ambient global without a body", kind: "other" },
|
|
2892
|
+
ambient
|
|
2893
|
+
);
|
|
2894
|
+
continue;
|
|
2895
|
+
}
|
|
2896
|
+
collectStatements2(
|
|
2897
|
+
stmt.body.statements,
|
|
2898
|
+
`${prefix}global.`,
|
|
2899
|
+
`ambient global declaration \u2014 not an export of this file (review before accepting)`
|
|
2900
|
+
);
|
|
2901
|
+
continue;
|
|
2902
|
+
}
|
|
2903
|
+
if (!isExported(stmt) && !isDefaultExported(stmt)) continue;
|
|
2904
|
+
const name = isDefaultExported(stmt) ? "default" : pickName(stmt);
|
|
2905
|
+
if (!name) {
|
|
2906
|
+
pushScoped(
|
|
2907
|
+
{ name: `${prefix}namespace`, note: "anonymous namespace is not projected", kind: "other" },
|
|
2908
|
+
ambient
|
|
2909
|
+
);
|
|
2910
|
+
continue;
|
|
2911
|
+
}
|
|
2912
|
+
if (!stmt.body || !ts2.isModuleBlock(stmt.body)) {
|
|
2913
|
+
pushScoped(
|
|
2914
|
+
{
|
|
2915
|
+
name: `${prefix}${name}`,
|
|
2916
|
+
note: "namespace with a qualified name is not projected",
|
|
2917
|
+
kind: "other"
|
|
2918
|
+
},
|
|
2919
|
+
ambient
|
|
2920
|
+
);
|
|
2921
|
+
continue;
|
|
2922
|
+
}
|
|
2923
|
+
const fields = [];
|
|
2924
|
+
for (const inner of stmt.body.statements) {
|
|
2925
|
+
const f = namespaceMemberField(inner, ctx);
|
|
2926
|
+
if (f) fields.push(f);
|
|
2927
|
+
}
|
|
2928
|
+
pushScoped(
|
|
2929
|
+
{
|
|
2930
|
+
name: `${prefix}${name}`,
|
|
2931
|
+
dsl: fields.length > 0 ? `shape({ ${fields.join(", ")} })` : "shape({})",
|
|
2932
|
+
kind: "value"
|
|
2933
|
+
},
|
|
2934
|
+
ambient
|
|
2935
|
+
);
|
|
2936
|
+
continue;
|
|
2937
|
+
}
|
|
2938
|
+
if (ts2.isExportDeclaration(stmt)) {
|
|
2939
|
+
const spec = stmt.moduleSpecifier?.getText(sourceFile);
|
|
2940
|
+
if (!stmt.exportClause) {
|
|
2941
|
+
pushScoped(
|
|
2942
|
+
{
|
|
2943
|
+
name: spec ? `export * from ${spec}` : "export *",
|
|
2944
|
+
note: "star re-export \u2014 the exported names are unknown, so nothing is projected",
|
|
2945
|
+
kind: "other"
|
|
2946
|
+
},
|
|
2947
|
+
ambient
|
|
2948
|
+
);
|
|
2949
|
+
continue;
|
|
2950
|
+
}
|
|
2951
|
+
if (ts2.isNamespaceExport(stmt.exportClause)) {
|
|
2952
|
+
pushScoped(
|
|
2953
|
+
{
|
|
2954
|
+
name: `${prefix}${stmt.exportClause.name.text}`,
|
|
2955
|
+
note: `namespace re-export${spec ? ` from ${spec}` : ""} is not projected`,
|
|
2956
|
+
kind: "other"
|
|
2957
|
+
},
|
|
2958
|
+
ambient
|
|
2959
|
+
);
|
|
2960
|
+
continue;
|
|
2961
|
+
}
|
|
2962
|
+
for (const el of stmt.exportClause.elements) {
|
|
2963
|
+
const exported = el.name.text;
|
|
2964
|
+
if (spec) {
|
|
2965
|
+
pushScoped(
|
|
2966
|
+
{
|
|
2967
|
+
name: `${prefix}${exported}`,
|
|
2968
|
+
note: `re-export from ${spec} is not projected`,
|
|
2969
|
+
kind: "other"
|
|
2970
|
+
},
|
|
2971
|
+
ambient
|
|
2972
|
+
);
|
|
2973
|
+
continue;
|
|
2974
|
+
}
|
|
2975
|
+
const localName = el.propertyName?.text ?? el.name.text;
|
|
2976
|
+
const local = locals.get(localName);
|
|
2977
|
+
if (!local) {
|
|
2978
|
+
pushScoped(
|
|
2979
|
+
{
|
|
2980
|
+
name: `${prefix}${exported}`,
|
|
2981
|
+
note: `local re-export of '${localName}' \u2014 the local declaration was not found`,
|
|
2982
|
+
kind: "other"
|
|
2983
|
+
},
|
|
2984
|
+
ambient
|
|
2985
|
+
);
|
|
2986
|
+
continue;
|
|
2987
|
+
}
|
|
2988
|
+
const projected = projectLocal(local, localName, exported, ctx);
|
|
2989
|
+
pushScoped({ ...projected, name: `${prefix}${projected.name}` }, ambient);
|
|
2990
|
+
}
|
|
2991
|
+
continue;
|
|
2992
|
+
}
|
|
2993
|
+
if (ts2.isExportAssignment(stmt)) {
|
|
2994
|
+
pushScoped(
|
|
2995
|
+
{
|
|
2996
|
+
name: `${prefix}default`,
|
|
2997
|
+
note: "export assignment \u2014 bind via sidecar default",
|
|
2998
|
+
kind: "other"
|
|
2999
|
+
},
|
|
3000
|
+
ambient
|
|
3001
|
+
);
|
|
3002
|
+
continue;
|
|
3003
|
+
}
|
|
3004
|
+
if (ts2.isImportEqualsDeclaration(stmt)) {
|
|
3005
|
+
if (!isExported(stmt)) continue;
|
|
3006
|
+
const name = pickName(stmt);
|
|
3007
|
+
pushScoped(
|
|
3008
|
+
{
|
|
3009
|
+
name: `${prefix}${name ?? "import="}`,
|
|
3010
|
+
note: "import-equals export \u2014 the aliased module value is not projected",
|
|
3011
|
+
kind: "other"
|
|
3012
|
+
},
|
|
3013
|
+
ambient
|
|
3014
|
+
);
|
|
3015
|
+
continue;
|
|
3016
|
+
}
|
|
3017
|
+
if (ts2.isInterfaceDeclaration(stmt) || ts2.isTypeAliasDeclaration(stmt)) {
|
|
3018
|
+
continue;
|
|
1181
3019
|
}
|
|
1182
|
-
continue;
|
|
1183
3020
|
}
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
3021
|
+
};
|
|
3022
|
+
collectStatements2(sourceFile.statements, "", void 0);
|
|
3023
|
+
for (const [name, group] of fnGroups) {
|
|
3024
|
+
if (emittedFns.has(name)) continue;
|
|
3025
|
+
emittedFns.add(name);
|
|
3026
|
+
const decls = group.decls;
|
|
3027
|
+
const m = mergeOverloads(decls, ctx);
|
|
3028
|
+
if (decls.length > 1) {
|
|
3029
|
+
ctx.lost.n += decls.length - 1;
|
|
1187
3030
|
}
|
|
1188
|
-
|
|
1189
|
-
|
|
3031
|
+
const paramNote = m.paramsMerged ? "parameters merged position by position (union per position)" : m.arityDiffers ? `parameters from the first (arity ${m.arity} \u2014 calls with more arguments are not covered; the contract DSL has no overload set)` : "parameters from the first (the overload parameter types could not be merged)";
|
|
3032
|
+
pushScoped(
|
|
3033
|
+
{
|
|
3034
|
+
name,
|
|
3035
|
+
dsl: m.dsl.dsl,
|
|
3036
|
+
kind: "function",
|
|
3037
|
+
...decls.length > 1 ? {
|
|
3038
|
+
note: `${decls.length} overloads merged \u2014 ${paramNote}, return = union of all; overloads: ${m.sigs.join(" | ")}`
|
|
3039
|
+
} : {},
|
|
3040
|
+
...m.dsl.modeled ? {} : { partial: true }
|
|
3041
|
+
},
|
|
3042
|
+
group.ambient
|
|
3043
|
+
);
|
|
3044
|
+
}
|
|
3045
|
+
return { exports: out, lost: ctx.lost.n };
|
|
3046
|
+
}
|
|
3047
|
+
function projectLocal(stmt, localName, exported, ctx) {
|
|
3048
|
+
if (ts2.isFunctionDeclaration(stmt)) {
|
|
3049
|
+
const d = fnSignatureDsl(stmt.parameters, stmt.type, ctx);
|
|
3050
|
+
return {
|
|
3051
|
+
name: exported,
|
|
3052
|
+
dsl: d.dsl,
|
|
3053
|
+
kind: "function",
|
|
3054
|
+
...d.modeled ? {} : { partial: true }
|
|
3055
|
+
};
|
|
3056
|
+
}
|
|
3057
|
+
if (ts2.isVariableStatement(stmt)) {
|
|
3058
|
+
for (const decl of stmt.declarationList.declarations) {
|
|
3059
|
+
if (pickName(decl) !== localName) continue;
|
|
3060
|
+
const t = typeToDsl(decl.type, ctx);
|
|
3061
|
+
if (!t) {
|
|
3062
|
+
return {
|
|
3063
|
+
name: exported,
|
|
3064
|
+
note: "no type annotation \u2014 the value is not projected",
|
|
3065
|
+
kind: "value"
|
|
3066
|
+
};
|
|
3067
|
+
}
|
|
3068
|
+
return {
|
|
3069
|
+
name: exported,
|
|
3070
|
+
dsl: constraintSlot(t),
|
|
3071
|
+
kind: t.dsl.startsWith("fn(") ? "function" : "value",
|
|
3072
|
+
...t.modeled ? {} : { partial: true }
|
|
3073
|
+
};
|
|
1190
3074
|
}
|
|
1191
3075
|
}
|
|
1192
|
-
|
|
3076
|
+
if (ts2.isEnumDeclaration(stmt)) {
|
|
3077
|
+
return { name: exported, dsl: enumShape(stmt, ctx), kind: "value" };
|
|
3078
|
+
}
|
|
3079
|
+
if (ts2.isClassDeclaration(stmt)) {
|
|
3080
|
+
return {
|
|
3081
|
+
name: exported,
|
|
3082
|
+
note: "class re-export is not projected",
|
|
3083
|
+
kind: "class"
|
|
3084
|
+
};
|
|
3085
|
+
}
|
|
3086
|
+
return {
|
|
3087
|
+
name: exported,
|
|
3088
|
+
note: `local re-export of '${localName}' is not projected`,
|
|
3089
|
+
kind: "other"
|
|
3090
|
+
};
|
|
3091
|
+
}
|
|
3092
|
+
function draftIdentity(name) {
|
|
3093
|
+
return name.includes(".") ? toJsBindingIdent(name.replace(/\./g, "_")) : name;
|
|
3094
|
+
}
|
|
3095
|
+
function draftBindingName(identity, taken) {
|
|
3096
|
+
if (isJsBindingIdent(identity) && !taken(identity)) return identity;
|
|
3097
|
+
const alias = toJsBindingIdent(identity);
|
|
3098
|
+
if (isJsBindingIdent(alias) && !taken(alias)) return alias;
|
|
3099
|
+
let n = 1;
|
|
3100
|
+
while (taken(`_nudo_${n}`)) n++;
|
|
3101
|
+
return `_nudo_${n}`;
|
|
1193
3102
|
}
|
|
1194
|
-
function
|
|
1195
|
-
return
|
|
3103
|
+
function exportNameRepr(name) {
|
|
3104
|
+
return /^(?:[$_\p{ID_Start}])(?:[$\u200C\u200D\p{ID_Continue}])*$/u.test(name) ? name : JSON.stringify(name);
|
|
3105
|
+
}
|
|
3106
|
+
function draftExportStmt(binding, identity, dsl) {
|
|
3107
|
+
if (binding === identity) return [`export const ${binding} = ${dsl};`];
|
|
3108
|
+
return [`const ${binding} = ${dsl};`, `export { ${binding} as ${exportNameRepr(identity)} };`];
|
|
1196
3109
|
}
|
|
1197
3110
|
function formatDtsContractDraft(label, files, exports) {
|
|
1198
3111
|
const draftable = exports.filter((e) => e.dsl !== void 0);
|
|
@@ -1204,6 +3117,7 @@ function formatDtsContractDraft(label, files, exports) {
|
|
|
1204
3117
|
}
|
|
1205
3118
|
if (draftable.some((e) => /\bany\s*\(/.test(e.dsl))) used.add("any");
|
|
1206
3119
|
const importList = BUILDERS.filter((b) => used.has(b)).join(", ");
|
|
3120
|
+
const importNames = new Set(used);
|
|
1207
3121
|
const exportUsed = /* @__PURE__ */ new Set();
|
|
1208
3122
|
const lines = [
|
|
1209
3123
|
"// @nudo:draft",
|
|
@@ -1231,7 +3145,12 @@ function formatDtsContractDraft(label, files, exports) {
|
|
|
1231
3145
|
}
|
|
1232
3146
|
if (e.note) lines.push(`// ${e.name} \u2014 ${e.note}`);
|
|
1233
3147
|
else lines.push(`// ${e.name} \u2014 from TypeScript`);
|
|
1234
|
-
|
|
3148
|
+
const identity = draftIdentity(e.name);
|
|
3149
|
+
const binding = draftBindingName(identity, (n) => importNames.has(n) || exportUsed.has(n));
|
|
3150
|
+
exportUsed.add(binding);
|
|
3151
|
+
for (const stmt of draftExportStmt(binding, identity, e.dsl)) {
|
|
3152
|
+
lines.push(stmt);
|
|
3153
|
+
}
|
|
1235
3154
|
if (e.name.includes(".")) {
|
|
1236
3155
|
lines.push(`// sidecar key may also be written as \`${e.name}\``);
|
|
1237
3156
|
}
|
|
@@ -1239,14 +3158,20 @@ function formatDtsContractDraft(label, files, exports) {
|
|
|
1239
3158
|
}
|
|
1240
3159
|
return lines.join("\n");
|
|
1241
3160
|
}
|
|
1242
|
-
|
|
1243
|
-
|
|
3161
|
+
var MAX_WALK_DEPTH = 6;
|
|
3162
|
+
function readDtsFiles(paths) {
|
|
3163
|
+
const found = [];
|
|
3164
|
+
let droppedDirs = 0;
|
|
1244
3165
|
const walk = (dir, depth) => {
|
|
1245
|
-
if (depth >
|
|
3166
|
+
if (depth > MAX_WALK_DEPTH) {
|
|
3167
|
+
droppedDirs++;
|
|
3168
|
+
return;
|
|
3169
|
+
}
|
|
1246
3170
|
let entries;
|
|
1247
3171
|
try {
|
|
1248
3172
|
entries = readdirSync(dir);
|
|
1249
3173
|
} catch {
|
|
3174
|
+
droppedDirs++;
|
|
1250
3175
|
return;
|
|
1251
3176
|
}
|
|
1252
3177
|
for (const name of entries) {
|
|
@@ -1256,71 +3181,183 @@ function readDtsFiles(paths, maxFiles = 16) {
|
|
|
1256
3181
|
try {
|
|
1257
3182
|
st = statSync2(p);
|
|
1258
3183
|
} catch {
|
|
3184
|
+
droppedDirs++;
|
|
1259
3185
|
continue;
|
|
1260
3186
|
}
|
|
1261
3187
|
if (st.isDirectory()) walk(p, depth + 1);
|
|
1262
|
-
else if (name.endsWith(".d.ts") && !name.endsWith(".d.ts.map"))
|
|
3188
|
+
else if (name.endsWith(".d.ts") && !name.endsWith(".d.ts.map")) {
|
|
3189
|
+
found.push(p);
|
|
3190
|
+
}
|
|
3191
|
+
}
|
|
3192
|
+
};
|
|
3193
|
+
for (const p of paths) {
|
|
3194
|
+
const st = statSync2(p, { throwIfNoEntry: false });
|
|
3195
|
+
if (st === void 0) continue;
|
|
3196
|
+
if (st.isDirectory()) walk(p, 0);
|
|
3197
|
+
else if (/\.d\.ts$/.test(p) || /\.(m)?ts$/.test(p)) {
|
|
3198
|
+
if (!p.endsWith(".d.ts.map")) found.push(p);
|
|
3199
|
+
}
|
|
3200
|
+
}
|
|
3201
|
+
found.sort();
|
|
3202
|
+
return { files: found, droppedDirs };
|
|
3203
|
+
}
|
|
3204
|
+
function dtsToContractDraft(dtsFiles, label) {
|
|
3205
|
+
const files = dtsFiles.filter((f) => existsSync(f));
|
|
3206
|
+
const exports = [];
|
|
3207
|
+
let skippedFiles = 0;
|
|
3208
|
+
let droppedEntries = 0;
|
|
3209
|
+
let lostEntries = 0;
|
|
3210
|
+
for (const file of files) {
|
|
3211
|
+
let content;
|
|
3212
|
+
try {
|
|
3213
|
+
content = readFileSync2(file, "utf8");
|
|
3214
|
+
} catch {
|
|
3215
|
+
skippedFiles++;
|
|
3216
|
+
continue;
|
|
3217
|
+
}
|
|
3218
|
+
const sf = ts2.createSourceFile(file, content, ts2.ScriptTarget.Latest, true);
|
|
3219
|
+
const collected = collectExports(sf);
|
|
3220
|
+
lostEntries += collected.lost;
|
|
3221
|
+
for (const e of collected.exports) {
|
|
3222
|
+
if (exports.some((x) => x.name === e.name && x.dsl)) {
|
|
3223
|
+
droppedEntries++;
|
|
3224
|
+
continue;
|
|
3225
|
+
}
|
|
3226
|
+
exports.push(e);
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3229
|
+
const name = label ?? (files[0] ? basename(files[0]).replace(/\.d\.ts$/, "") : "dts");
|
|
3230
|
+
const draftSource = formatDtsContractDraft(name, files, exports);
|
|
3231
|
+
const unprojected = exports.filter((e) => e.dsl === void 0).length;
|
|
3232
|
+
const partial = exports.filter((e) => e.partial && e.dsl !== void 0).length;
|
|
3233
|
+
return {
|
|
3234
|
+
label: name,
|
|
3235
|
+
files,
|
|
3236
|
+
exports,
|
|
3237
|
+
draftSource,
|
|
3238
|
+
stats: {
|
|
3239
|
+
files: files.length - skippedFiles,
|
|
3240
|
+
exports: exports.filter((e) => e.dsl !== void 0).length,
|
|
3241
|
+
// skipped = 丢失的条数(整条没投影 + 投影了但有未建模部分 + 读失败 +
|
|
3242
|
+
// 同名去重丢掉的 + 声明被合并掉的)——汇总行报「0 skipped」的前提是
|
|
3243
|
+
// **真的**没丢东西
|
|
3244
|
+
skipped: unprojected + partial + skippedFiles + droppedEntries + lostEntries
|
|
3245
|
+
}
|
|
3246
|
+
};
|
|
3247
|
+
}
|
|
3248
|
+
function dtsPathToContractDraft(path, label) {
|
|
3249
|
+
const scan = readDtsFiles([path]);
|
|
3250
|
+
const draft = dtsToContractDraft(scan.files, label ?? basename(path).replace(/\.d\.ts$/, ""));
|
|
3251
|
+
if (scan.droppedDirs > 0) {
|
|
3252
|
+
draft.stats.skipped += scan.droppedDirs;
|
|
3253
|
+
draft.stats.droppedDirs = scan.droppedDirs;
|
|
3254
|
+
}
|
|
3255
|
+
return draft;
|
|
3256
|
+
}
|
|
3257
|
+
|
|
3258
|
+
// src/harvest-package.ts
|
|
3259
|
+
import { existsSync as existsSync2, readdirSync as readdirSync2, statSync as statSync3, readFileSync as readFileSync3 } from "fs";
|
|
3260
|
+
import { join as join2, resolve, dirname as dirname2, basename as basename2 } from "path";
|
|
3261
|
+
import ts3 from "typescript";
|
|
3262
|
+
import { formatShape as formatShape2 } from "@nudojs/core";
|
|
3263
|
+
|
|
3264
|
+
// src/lru-map.ts
|
|
3265
|
+
var BoundedLruMap = class {
|
|
3266
|
+
map = /* @__PURE__ */ new Map();
|
|
3267
|
+
max;
|
|
3268
|
+
constructor(max) {
|
|
3269
|
+
this.max = max;
|
|
3270
|
+
}
|
|
3271
|
+
get size() {
|
|
3272
|
+
return this.map.size;
|
|
3273
|
+
}
|
|
3274
|
+
/** 当前上限(可动态收紧;收紧时立刻按 LRU 压到新上限) */
|
|
3275
|
+
getMax() {
|
|
3276
|
+
return this.max;
|
|
3277
|
+
}
|
|
3278
|
+
setMax(max) {
|
|
3279
|
+
this.max = max;
|
|
3280
|
+
this.trim();
|
|
3281
|
+
}
|
|
3282
|
+
has(key) {
|
|
3283
|
+
return this.map.has(key);
|
|
3284
|
+
}
|
|
3285
|
+
get(key) {
|
|
3286
|
+
const hit = this.map.get(key);
|
|
3287
|
+
if (hit === void 0) return void 0;
|
|
3288
|
+
this.map.delete(key);
|
|
3289
|
+
this.map.set(key, hit);
|
|
3290
|
+
return hit;
|
|
3291
|
+
}
|
|
3292
|
+
/** 命中不改变 LRU 序(只读探测) */
|
|
3293
|
+
peek(key) {
|
|
3294
|
+
return this.map.get(key);
|
|
3295
|
+
}
|
|
3296
|
+
set(key, value) {
|
|
3297
|
+
if (this.max <= 0) return;
|
|
3298
|
+
while (this.map.size >= this.max && !this.map.has(key)) {
|
|
3299
|
+
const oldest = this.map.keys().next().value;
|
|
3300
|
+
if (oldest === void 0) break;
|
|
3301
|
+
this.map.delete(oldest);
|
|
1263
3302
|
}
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
3303
|
+
this.map.delete(key);
|
|
3304
|
+
this.map.set(key, value);
|
|
3305
|
+
}
|
|
3306
|
+
delete(key) {
|
|
3307
|
+
return this.map.delete(key);
|
|
3308
|
+
}
|
|
3309
|
+
clear() {
|
|
3310
|
+
this.map.clear();
|
|
3311
|
+
}
|
|
3312
|
+
keys() {
|
|
3313
|
+
return this.map.keys();
|
|
3314
|
+
}
|
|
3315
|
+
values() {
|
|
3316
|
+
return this.map.values();
|
|
3317
|
+
}
|
|
3318
|
+
entries() {
|
|
3319
|
+
return this.map.entries();
|
|
3320
|
+
}
|
|
3321
|
+
/** 压到当前 max(调低上限时收内存) */
|
|
3322
|
+
trim() {
|
|
3323
|
+
while (this.map.size > this.max) {
|
|
3324
|
+
const oldest = this.map.keys().next().value;
|
|
3325
|
+
if (oldest === void 0) break;
|
|
3326
|
+
this.map.delete(oldest);
|
|
1271
3327
|
}
|
|
1272
3328
|
}
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
const
|
|
1278
|
-
|
|
1279
|
-
for (const file of files) {
|
|
1280
|
-
let content;
|
|
3329
|
+
};
|
|
3330
|
+
|
|
3331
|
+
// src/harvest-package.ts
|
|
3332
|
+
function packageDeclaresTypes(root) {
|
|
3333
|
+
const pj = join2(root, "package.json");
|
|
3334
|
+
if (existsSync2(pj)) {
|
|
1281
3335
|
try {
|
|
1282
|
-
|
|
3336
|
+
const raw = JSON.parse(readFileSync3(pj, "utf8"));
|
|
3337
|
+
const entry = raw.types ?? raw.typings;
|
|
3338
|
+
if (typeof entry === "string" && entry.length > 0 && existsSync2(resolve(root, entry))) {
|
|
3339
|
+
return true;
|
|
3340
|
+
}
|
|
1283
3341
|
} catch {
|
|
1284
|
-
skippedFiles++;
|
|
1285
|
-
continue;
|
|
1286
|
-
}
|
|
1287
|
-
const sf = ts2.createSourceFile(file, content, ts2.ScriptTarget.Latest, true);
|
|
1288
|
-
for (const e of collectExports(sf)) {
|
|
1289
|
-
if (exports.some((x) => x.name === e.name && x.dsl)) continue;
|
|
1290
|
-
exports.push(e);
|
|
1291
3342
|
}
|
|
1292
3343
|
}
|
|
1293
|
-
|
|
1294
|
-
const draftSource = formatDtsContractDraft(name, files, exports);
|
|
1295
|
-
return {
|
|
1296
|
-
label: name,
|
|
1297
|
-
files,
|
|
1298
|
-
exports,
|
|
1299
|
-
draftSource,
|
|
1300
|
-
stats: {
|
|
1301
|
-
files: files.length - skippedFiles,
|
|
1302
|
-
exports: exports.filter((e) => e.dsl !== void 0).length,
|
|
1303
|
-
skipped: exports.filter((e) => e.dsl === void 0).length + skippedFiles
|
|
1304
|
-
}
|
|
1305
|
-
};
|
|
1306
|
-
}
|
|
1307
|
-
function dtsPathToContractDraft(path, label) {
|
|
1308
|
-
const files = readDtsFiles([path]);
|
|
1309
|
-
return dtsToContractDraft(files, label ?? basename(path).replace(/\.d\.ts$/, ""));
|
|
3344
|
+
return existsSync2(join2(root, "index.d.ts"));
|
|
1310
3345
|
}
|
|
1311
|
-
|
|
1312
|
-
// src/harvest-package.ts
|
|
1313
|
-
import { existsSync as existsSync2, readdirSync as readdirSync2, statSync as statSync3, readFileSync as readFileSync3 } from "fs";
|
|
1314
|
-
import { join as join2, resolve, dirname as dirname2, basename as basename2 } from "path";
|
|
1315
|
-
import { formatShape as formatShape2 } from "@nudojs/core";
|
|
1316
3346
|
function resolvePackageRoot(pkg, fromDir = process.cwd()) {
|
|
1317
|
-
let dir = resolve(fromDir);
|
|
1318
3347
|
const bare = pkg.replace(/^@/, "").replace(/\//g, "__");
|
|
3348
|
+
const start = resolve(fromDir);
|
|
3349
|
+
let dir = start;
|
|
3350
|
+
for (let i = 0; i < 8; i++) {
|
|
3351
|
+
const pkgPath = join2(dir, "node_modules", pkg);
|
|
3352
|
+
if (existsSync2(pkgPath) && packageDeclaresTypes(pkgPath)) return pkgPath;
|
|
3353
|
+
const parent = resolve(dir, "..");
|
|
3354
|
+
if (parent === dir) break;
|
|
3355
|
+
dir = parent;
|
|
3356
|
+
}
|
|
3357
|
+
dir = start;
|
|
1319
3358
|
for (let i = 0; i < 8; i++) {
|
|
1320
3359
|
const typesPath = join2(dir, "node_modules", "@types", bare);
|
|
1321
3360
|
if (existsSync2(typesPath)) return typesPath;
|
|
1322
|
-
const pkgPath = join2(dir, "node_modules", pkg);
|
|
1323
|
-
if (existsSync2(pkgPath)) return pkgPath;
|
|
1324
3361
|
const parent = resolve(dir, "..");
|
|
1325
3362
|
if (parent === dir) break;
|
|
1326
3363
|
dir = parent;
|
|
@@ -1435,9 +3472,170 @@ function harvestPackage(pkg, fromDir, maxFiles = 24) {
|
|
|
1435
3472
|
const env = harvestDts(dtsFiles);
|
|
1436
3473
|
return { pkg, root, dtsFiles, env };
|
|
1437
3474
|
}
|
|
3475
|
+
function topLevelNames(stmt) {
|
|
3476
|
+
if (ts3.isFunctionDeclaration(stmt) || ts3.isClassDeclaration(stmt)) {
|
|
3477
|
+
return stmt.name ? [stmt.name.text] : [];
|
|
3478
|
+
}
|
|
3479
|
+
if (ts3.isInterfaceDeclaration(stmt) || ts3.isTypeAliasDeclaration(stmt)) {
|
|
3480
|
+
return [stmt.name.text];
|
|
3481
|
+
}
|
|
3482
|
+
if (ts3.isEnumDeclaration(stmt)) return [stmt.name.text];
|
|
3483
|
+
if (ts3.isModuleDeclaration(stmt)) {
|
|
3484
|
+
return ts3.isIdentifier(stmt.name) ? [stmt.name.text] : [];
|
|
3485
|
+
}
|
|
3486
|
+
if (ts3.isVariableStatement(stmt)) {
|
|
3487
|
+
const names = [];
|
|
3488
|
+
for (const decl of stmt.declarationList.declarations) {
|
|
3489
|
+
if (ts3.isIdentifier(decl.name)) names.push(decl.name.text);
|
|
3490
|
+
}
|
|
3491
|
+
return names;
|
|
3492
|
+
}
|
|
3493
|
+
return [];
|
|
3494
|
+
}
|
|
3495
|
+
function ambientNames(stmt) {
|
|
3496
|
+
const names = [];
|
|
3497
|
+
if (!stmt.body || !ts3.isModuleBlock(stmt.body)) return names;
|
|
3498
|
+
for (const inner of stmt.body.statements) names.push(...topLevelNames(inner));
|
|
3499
|
+
return names;
|
|
3500
|
+
}
|
|
3501
|
+
function modifierKinds(stmt) {
|
|
3502
|
+
if (!ts3.canHaveModifiers(stmt)) return [];
|
|
3503
|
+
return (ts3.getModifiers(stmt) ?? []).map((m) => m.kind);
|
|
3504
|
+
}
|
|
3505
|
+
function emptyScan() {
|
|
3506
|
+
return {
|
|
3507
|
+
exported: /* @__PURE__ */ new Set(),
|
|
3508
|
+
ambient: /* @__PURE__ */ new Set(),
|
|
3509
|
+
stars: [],
|
|
3510
|
+
isModule: false
|
|
3511
|
+
};
|
|
3512
|
+
}
|
|
3513
|
+
function scanDtsFile(file) {
|
|
3514
|
+
let content;
|
|
3515
|
+
try {
|
|
3516
|
+
content = readFileSync3(file, "utf8");
|
|
3517
|
+
} catch {
|
|
3518
|
+
return emptyScan();
|
|
3519
|
+
}
|
|
3520
|
+
let sf;
|
|
3521
|
+
try {
|
|
3522
|
+
sf = ts3.createSourceFile(file, content, ts3.ScriptTarget.Latest, true);
|
|
3523
|
+
} catch {
|
|
3524
|
+
return emptyScan();
|
|
3525
|
+
}
|
|
3526
|
+
const scan = {
|
|
3527
|
+
exported: /* @__PURE__ */ new Set(),
|
|
3528
|
+
ambient: /* @__PURE__ */ new Set(),
|
|
3529
|
+
stars: [],
|
|
3530
|
+
isModule: ts3.isExternalModule(sf)
|
|
3531
|
+
};
|
|
3532
|
+
for (const stmt of sf.statements) {
|
|
3533
|
+
if (ts3.isModuleDeclaration(stmt)) {
|
|
3534
|
+
if (ts3.isIdentifier(stmt.name) && stmt.name.text === "global") {
|
|
3535
|
+
for (const n of ambientNames(stmt)) scan.ambient.add(n);
|
|
3536
|
+
}
|
|
3537
|
+
continue;
|
|
3538
|
+
}
|
|
3539
|
+
if (ts3.isExportDeclaration(stmt)) {
|
|
3540
|
+
const clause = stmt.exportClause;
|
|
3541
|
+
if (!clause) {
|
|
3542
|
+
if (stmt.moduleSpecifier && ts3.isStringLiteral(stmt.moduleSpecifier)) {
|
|
3543
|
+
scan.stars.push(stmt.moduleSpecifier.text);
|
|
3544
|
+
}
|
|
3545
|
+
continue;
|
|
3546
|
+
}
|
|
3547
|
+
if (ts3.isNamespaceExport(clause)) {
|
|
3548
|
+
scan.exported.add(clause.name.text);
|
|
3549
|
+
continue;
|
|
3550
|
+
}
|
|
3551
|
+
for (const el of clause.elements) scan.exported.add(el.name.text);
|
|
3552
|
+
continue;
|
|
3553
|
+
}
|
|
3554
|
+
if (ts3.isExportAssignment(stmt)) {
|
|
3555
|
+
scan.exported.add("default");
|
|
3556
|
+
continue;
|
|
3557
|
+
}
|
|
3558
|
+
const names = topLevelNames(stmt);
|
|
3559
|
+
const mods = modifierKinds(stmt);
|
|
3560
|
+
if (mods.includes(ts3.SyntaxKind.DefaultKeyword)) {
|
|
3561
|
+
scan.exported.add("default");
|
|
3562
|
+
continue;
|
|
3563
|
+
}
|
|
3564
|
+
const isExported2 = mods.includes(ts3.SyntaxKind.ExportKeyword);
|
|
3565
|
+
for (const n of names) {
|
|
3566
|
+
if (isExported2) scan.exported.add(n);
|
|
3567
|
+
else if (!scan.isModule) scan.ambient.add(n);
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3570
|
+
return scan;
|
|
3571
|
+
}
|
|
3572
|
+
var fileScanCache = new BoundedLruMap(64);
|
|
3573
|
+
function dtsFileScan(file) {
|
|
3574
|
+
const hit = fileScanCache.get(file);
|
|
3575
|
+
if (hit) return hit;
|
|
3576
|
+
const scan = scanDtsFile(file);
|
|
3577
|
+
fileScanCache.set(file, scan);
|
|
3578
|
+
return scan;
|
|
3579
|
+
}
|
|
3580
|
+
function resolveStarTarget(spec, fromFile) {
|
|
3581
|
+
if (!spec.startsWith(".")) return void 0;
|
|
3582
|
+
const raw = resolve(dirname2(fromFile), spec);
|
|
3583
|
+
const stem = raw.replace(/\.(js|mjs|cjs|ts)$/, "");
|
|
3584
|
+
for (const cand of [`${raw}.d.ts`, `${stem}.d.ts`, join2(raw, "index.d.ts"), join2(stem, "index.d.ts")]) {
|
|
3585
|
+
if (existsSync2(cand)) return cand;
|
|
3586
|
+
}
|
|
3587
|
+
return void 0;
|
|
3588
|
+
}
|
|
3589
|
+
function packageEntryFile(h) {
|
|
3590
|
+
const entry = entryDtsFromPackageJson(h.root) ?? join2(h.root, "index.d.ts");
|
|
3591
|
+
if (existsSync2(entry)) return entry;
|
|
3592
|
+
return h.dtsFiles[0];
|
|
3593
|
+
}
|
|
3594
|
+
var surfaceCache = /* @__PURE__ */ new WeakMap();
|
|
3595
|
+
function packageExportSurface(h) {
|
|
3596
|
+
const hit = surfaceCache.get(h);
|
|
3597
|
+
if (hit) return hit;
|
|
3598
|
+
const names = /* @__PURE__ */ new Set();
|
|
3599
|
+
let unresolved = false;
|
|
3600
|
+
const visited = /* @__PURE__ */ new Set();
|
|
3601
|
+
const visit = (file) => {
|
|
3602
|
+
if (visited.has(file)) return;
|
|
3603
|
+
visited.add(file);
|
|
3604
|
+
const scan = dtsFileScan(file);
|
|
3605
|
+
for (const n of scan.exported) names.add(n);
|
|
3606
|
+
for (const spec of scan.stars) {
|
|
3607
|
+
const target = resolveStarTarget(spec, file);
|
|
3608
|
+
if (target) visit(target);
|
|
3609
|
+
else unresolved = true;
|
|
3610
|
+
}
|
|
3611
|
+
};
|
|
3612
|
+
const entry = packageEntryFile(h);
|
|
3613
|
+
if (entry) visit(entry);
|
|
3614
|
+
const surface = { names, unresolved };
|
|
3615
|
+
surfaceCache.set(h, surface);
|
|
3616
|
+
return surface;
|
|
3617
|
+
}
|
|
3618
|
+
function packageAmbientNames(h) {
|
|
3619
|
+
const ambient = /* @__PURE__ */ new Set();
|
|
3620
|
+
for (const file of h.dtsFiles) {
|
|
3621
|
+
for (const n of dtsFileScan(file).ambient) ambient.add(n);
|
|
3622
|
+
}
|
|
3623
|
+
return ambient;
|
|
3624
|
+
}
|
|
3625
|
+
function resolveSubpathEntry(root, sub) {
|
|
3626
|
+
if (!sub) return void 0;
|
|
3627
|
+
const base = resolve(root, sub);
|
|
3628
|
+
for (const cand of [`${base}.d.ts`, join2(base, "index.d.ts")]) {
|
|
3629
|
+
if (existsSync2(cand)) return cand;
|
|
3630
|
+
}
|
|
3631
|
+
return void 0;
|
|
3632
|
+
}
|
|
1438
3633
|
function formatHarvestSummary(h) {
|
|
1439
3634
|
const lines = [];
|
|
1440
|
-
|
|
3635
|
+
const degraded = h.env.stats.degraded ?? 0;
|
|
3636
|
+
lines.push(
|
|
3637
|
+
`${h.pkg}: ${h.dtsFiles.length} d.ts, ${h.env.stats.symbols} symbols` + (degraded > 0 ? `, ${degraded} degraded` : "")
|
|
3638
|
+
);
|
|
1441
3639
|
const mods = Object.keys(h.env.modules);
|
|
1442
3640
|
for (const m of mods.slice(0, 5)) {
|
|
1443
3641
|
const exports = h.env.modules[m];
|
|
@@ -1513,7 +3711,10 @@ import {
|
|
|
1513
3711
|
relationFn as relationFn2,
|
|
1514
3712
|
getFnImpl as getFnImpl2
|
|
1515
3713
|
} from "@nudojs/core";
|
|
1516
|
-
|
|
3714
|
+
function nullDict2() {
|
|
3715
|
+
return /* @__PURE__ */ Object.create(null);
|
|
3716
|
+
}
|
|
3717
|
+
var HARVEST_DISK_ABI = "nudo-harvest-disk-v11";
|
|
1517
3718
|
function sha256Hex(data) {
|
|
1518
3719
|
return createHash("sha256").update(data).digest("hex");
|
|
1519
3720
|
}
|
|
@@ -1541,27 +3742,44 @@ function absToHarvestSig(a) {
|
|
|
1541
3742
|
case "arr":
|
|
1542
3743
|
return { k: "arr", element: absToHarvestSig(s.element) };
|
|
1543
3744
|
case "tuple":
|
|
1544
|
-
return {
|
|
3745
|
+
return {
|
|
3746
|
+
k: "tuple",
|
|
3747
|
+
elements: s.elements.map(absToHarvestSig),
|
|
3748
|
+
...s.rest ? { rest: absToHarvestSig(s.rest) } : {}
|
|
3749
|
+
};
|
|
1545
3750
|
case "obj": {
|
|
1546
|
-
const slots =
|
|
3751
|
+
const slots = nullDict2();
|
|
1547
3752
|
for (const [key, slot] of Object.entries(s.slots)) {
|
|
1548
3753
|
slots[key] = {
|
|
1549
3754
|
v: absToHarvestSig(slot.value),
|
|
1550
3755
|
...slot.optional ? { opt: true } : {}
|
|
1551
3756
|
};
|
|
1552
3757
|
}
|
|
1553
|
-
return {
|
|
3758
|
+
return {
|
|
3759
|
+
k: "obj",
|
|
3760
|
+
slots,
|
|
3761
|
+
...s.index ? { index: { key: absToHarvestSig(s.index.key), value: absToHarvestSig(s.index.value) } } : {},
|
|
3762
|
+
...s.open ? { open: true } : {}
|
|
3763
|
+
};
|
|
1554
3764
|
}
|
|
1555
3765
|
case "fn": {
|
|
1556
3766
|
const rel = getFnImpl2(a)?.relation;
|
|
1557
3767
|
const pts = rel?.paramTypes ?? s.paramTypes ?? s.params.map(() => ({ shape: { k: "unknown" }, conf: "mock" }));
|
|
1558
3768
|
const paramTypes = pts.map(absToHarvestSig);
|
|
1559
3769
|
const retSrc = rel?.returnType ?? s.returnType ?? { shape: { k: "unknown" }, conf: "mock" };
|
|
3770
|
+
const fnSlots = nullDict2();
|
|
3771
|
+
for (const [key, slot] of Object.entries(s.slots ?? {})) {
|
|
3772
|
+
fnSlots[key] = absToHarvestSig(slot.value);
|
|
3773
|
+
}
|
|
1560
3774
|
return {
|
|
1561
3775
|
k: "fn",
|
|
1562
3776
|
params: s.params,
|
|
1563
3777
|
paramTypes,
|
|
1564
|
-
returns: absToHarvestSig(retSrc)
|
|
3778
|
+
returns: absToHarvestSig(retSrc),
|
|
3779
|
+
...s.ctor !== void 0 ? { ctor: s.ctor } : {},
|
|
3780
|
+
...Object.keys(fnSlots).length > 0 ? { slots: fnSlots } : {},
|
|
3781
|
+
...rel?.inferFrom ? { inferFrom: rel.inferFrom } : {},
|
|
3782
|
+
...rel?.condFallback ? { condFallback: absToHarvestSig(rel.condFallback) } : {}
|
|
1565
3783
|
};
|
|
1566
3784
|
}
|
|
1567
3785
|
case "sum":
|
|
@@ -1583,15 +3801,13 @@ function harvestSigToAbs(sig) {
|
|
|
1583
3801
|
return a;
|
|
1584
3802
|
};
|
|
1585
3803
|
switch (sig.k) {
|
|
1586
|
-
case "prim":
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
)
|
|
1594
|
-
);
|
|
3804
|
+
case "prim": {
|
|
3805
|
+
const t = sig.type;
|
|
3806
|
+
if (t !== "number" && t !== "string" && t !== "boolean" && t !== "bigint" && t !== "symbol") {
|
|
3807
|
+
return mark(abs({ k: "unknown" }, void 0, void 0, "mock"));
|
|
3808
|
+
}
|
|
3809
|
+
return mark(abs({ k: "prim", type: t }, void 0, void 0, "mock"));
|
|
3810
|
+
}
|
|
1595
3811
|
case "unknown":
|
|
1596
3812
|
return mark(abs({ k: "unknown" }, void 0, void 0, "mock"));
|
|
1597
3813
|
case "any":
|
|
@@ -1618,29 +3834,65 @@ function harvestSigToAbs(sig) {
|
|
|
1618
3834
|
return mark(abs({ k: "arr", element: harvestSigToAbs(sig.element) }, void 0, void 0, "mock"));
|
|
1619
3835
|
case "tuple":
|
|
1620
3836
|
return mark(
|
|
1621
|
-
abs(
|
|
3837
|
+
abs(
|
|
3838
|
+
{
|
|
3839
|
+
k: "tuple",
|
|
3840
|
+
elements: sig.elements.map(harvestSigToAbs),
|
|
3841
|
+
...sig.rest ? { rest: harvestSigToAbs(sig.rest) } : {}
|
|
3842
|
+
},
|
|
3843
|
+
void 0,
|
|
3844
|
+
void 0,
|
|
3845
|
+
"mock"
|
|
3846
|
+
)
|
|
1622
3847
|
);
|
|
1623
3848
|
case "obj": {
|
|
1624
|
-
const slots =
|
|
3849
|
+
const slots = nullDict2();
|
|
1625
3850
|
for (const [key, slot] of Object.entries(sig.slots)) {
|
|
1626
3851
|
slots[key] = {
|
|
1627
3852
|
value: harvestSigToAbs(slot.v),
|
|
1628
3853
|
...slot.opt ? { optional: true } : {}
|
|
1629
3854
|
};
|
|
1630
3855
|
}
|
|
1631
|
-
return mark(
|
|
3856
|
+
return mark(
|
|
3857
|
+
abs(
|
|
3858
|
+
{
|
|
3859
|
+
k: "obj",
|
|
3860
|
+
slots,
|
|
3861
|
+
...sig.index ? { index: { key: harvestSigToAbs(sig.index.key), value: harvestSigToAbs(sig.index.value) } } : {},
|
|
3862
|
+
...sig.open ? { open: true } : {}
|
|
3863
|
+
},
|
|
3864
|
+
void 0,
|
|
3865
|
+
void 0,
|
|
3866
|
+
"mock"
|
|
3867
|
+
)
|
|
3868
|
+
);
|
|
1632
3869
|
}
|
|
1633
3870
|
case "tvar":
|
|
1634
3871
|
return mark(abs({ k: "any" }, { op: "var", id: sig.name }, void 0, "mock"));
|
|
1635
3872
|
case "fn": {
|
|
1636
3873
|
const paramTypes = sig.paramTypes.map(harvestSigToAbs);
|
|
1637
3874
|
const returnType = harvestSigToAbs(sig.returns);
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
3875
|
+
const fn = relationFn2(paramTypes, returnType, {
|
|
3876
|
+
params: sig.params,
|
|
3877
|
+
conf: "mock",
|
|
3878
|
+
// 族 27 Bug 821 残留:条件元数据必须一起往返,否则缓存命中后
|
|
3879
|
+
// `infer` 绑定与假支丢失 ⇒ 冷/热缓存结论不一致。
|
|
3880
|
+
...sig.inferFrom ? { inferFrom: sig.inferFrom } : {},
|
|
3881
|
+
...sig.condFallback ? { condFallback: harvestSigToAbs(sig.condFallback) } : {}
|
|
3882
|
+
});
|
|
3883
|
+
const fnShape = fn.shape;
|
|
3884
|
+
if (fnShape.k === "fn") {
|
|
3885
|
+
const fnSlots = nullDict2();
|
|
3886
|
+
for (const [key, v] of Object.entries(sig.slots ?? {})) {
|
|
3887
|
+
fnSlots[key] = { value: harvestSigToAbs(v) };
|
|
3888
|
+
}
|
|
3889
|
+
fn.shape = {
|
|
3890
|
+
...fnShape,
|
|
3891
|
+
...sig.ctor !== void 0 ? { ctor: sig.ctor } : {},
|
|
3892
|
+
...Object.keys(fnSlots).length > 0 ? { slots: fnSlots } : {}
|
|
3893
|
+
};
|
|
3894
|
+
}
|
|
3895
|
+
return mark(fn);
|
|
1644
3896
|
}
|
|
1645
3897
|
case "sum":
|
|
1646
3898
|
return mark(
|
|
@@ -1674,13 +3926,13 @@ function harvestSigToAbs(sig) {
|
|
|
1674
3926
|
}
|
|
1675
3927
|
}
|
|
1676
3928
|
function serializeHarvestJson(pkg, env, meta) {
|
|
1677
|
-
const modules =
|
|
3929
|
+
const modules = nullDict2();
|
|
1678
3930
|
for (const [mod, rec] of Object.entries(env.modules)) {
|
|
1679
|
-
const named =
|
|
3931
|
+
const named = nullDict2();
|
|
1680
3932
|
for (const [k, v] of Object.entries(rec)) named[k] = absToHarvestSig(v);
|
|
1681
3933
|
modules[mod] = named;
|
|
1682
3934
|
}
|
|
1683
|
-
const globals =
|
|
3935
|
+
const globals = nullDict2();
|
|
1684
3936
|
for (const [k, v] of Object.entries(env.globals)) globals[k] = absToHarvestSig(v);
|
|
1685
3937
|
return {
|
|
1686
3938
|
v: 1,
|
|
@@ -1700,13 +3952,13 @@ function materializeHarvestJson(j) {
|
|
|
1700
3952
|
if (h.knobs?.abi !== HARVEST_DISK_ABI) return null;
|
|
1701
3953
|
if (!h.modules || typeof h.modules !== "object") return null;
|
|
1702
3954
|
try {
|
|
1703
|
-
const modules =
|
|
3955
|
+
const modules = nullDict2();
|
|
1704
3956
|
for (const [mod, rec] of Object.entries(h.modules)) {
|
|
1705
|
-
const named =
|
|
3957
|
+
const named = nullDict2();
|
|
1706
3958
|
for (const [k, sig] of Object.entries(rec)) named[k] = harvestSigToAbs(sig);
|
|
1707
3959
|
modules[mod] = named;
|
|
1708
3960
|
}
|
|
1709
|
-
const globals =
|
|
3961
|
+
const globals = nullDict2();
|
|
1710
3962
|
for (const [k, sig] of Object.entries(h.globals ?? {})) {
|
|
1711
3963
|
globals[k] = harvestSigToAbs(sig);
|
|
1712
3964
|
}
|
|
@@ -1850,73 +4102,6 @@ function loadHarvestEnvFromDisk(pkg, meta) {
|
|
|
1850
4102
|
return hit ? materializeHarvestJson(hit) : null;
|
|
1851
4103
|
}
|
|
1852
4104
|
|
|
1853
|
-
// src/lru-map.ts
|
|
1854
|
-
var BoundedLruMap = class {
|
|
1855
|
-
map = /* @__PURE__ */ new Map();
|
|
1856
|
-
max;
|
|
1857
|
-
constructor(max) {
|
|
1858
|
-
this.max = max;
|
|
1859
|
-
}
|
|
1860
|
-
get size() {
|
|
1861
|
-
return this.map.size;
|
|
1862
|
-
}
|
|
1863
|
-
/** 当前上限(可动态收紧;收紧时立刻按 LRU 压到新上限) */
|
|
1864
|
-
getMax() {
|
|
1865
|
-
return this.max;
|
|
1866
|
-
}
|
|
1867
|
-
setMax(max) {
|
|
1868
|
-
this.max = max;
|
|
1869
|
-
this.trim();
|
|
1870
|
-
}
|
|
1871
|
-
has(key) {
|
|
1872
|
-
return this.map.has(key);
|
|
1873
|
-
}
|
|
1874
|
-
get(key) {
|
|
1875
|
-
const hit = this.map.get(key);
|
|
1876
|
-
if (hit === void 0) return void 0;
|
|
1877
|
-
this.map.delete(key);
|
|
1878
|
-
this.map.set(key, hit);
|
|
1879
|
-
return hit;
|
|
1880
|
-
}
|
|
1881
|
-
/** 命中不改变 LRU 序(只读探测) */
|
|
1882
|
-
peek(key) {
|
|
1883
|
-
return this.map.get(key);
|
|
1884
|
-
}
|
|
1885
|
-
set(key, value) {
|
|
1886
|
-
if (this.max <= 0) return;
|
|
1887
|
-
while (this.map.size >= this.max && !this.map.has(key)) {
|
|
1888
|
-
const oldest = this.map.keys().next().value;
|
|
1889
|
-
if (oldest === void 0) break;
|
|
1890
|
-
this.map.delete(oldest);
|
|
1891
|
-
}
|
|
1892
|
-
this.map.delete(key);
|
|
1893
|
-
this.map.set(key, value);
|
|
1894
|
-
}
|
|
1895
|
-
delete(key) {
|
|
1896
|
-
return this.map.delete(key);
|
|
1897
|
-
}
|
|
1898
|
-
clear() {
|
|
1899
|
-
this.map.clear();
|
|
1900
|
-
}
|
|
1901
|
-
keys() {
|
|
1902
|
-
return this.map.keys();
|
|
1903
|
-
}
|
|
1904
|
-
values() {
|
|
1905
|
-
return this.map.values();
|
|
1906
|
-
}
|
|
1907
|
-
entries() {
|
|
1908
|
-
return this.map.entries();
|
|
1909
|
-
}
|
|
1910
|
-
/** 压到当前 max(调低上限时收内存) */
|
|
1911
|
-
trim() {
|
|
1912
|
-
while (this.map.size > this.max) {
|
|
1913
|
-
const oldest = this.map.keys().next().value;
|
|
1914
|
-
if (oldest === void 0) break;
|
|
1915
|
-
this.map.delete(oldest);
|
|
1916
|
-
}
|
|
1917
|
-
}
|
|
1918
|
-
};
|
|
1919
|
-
|
|
1920
4105
|
// src/harvest-auto.ts
|
|
1921
4106
|
var NODE_BUILTINS = /* @__PURE__ */ new Set([
|
|
1922
4107
|
"assert",
|
|
@@ -2048,17 +4233,22 @@ function harvestedValueToAbs(a) {
|
|
|
2048
4233
|
body: [],
|
|
2049
4234
|
directives: []
|
|
2050
4235
|
};
|
|
4236
|
+
const ctor = a.shape.ctor;
|
|
2051
4237
|
return markMockConf(
|
|
2052
|
-
absFunction(
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
4238
|
+
absFunction(
|
|
4239
|
+
params,
|
|
4240
|
+
{
|
|
4241
|
+
body: dummyBody,
|
|
4242
|
+
apply: (args) => {
|
|
4243
|
+
if (relation) {
|
|
4244
|
+
return markMockConf(instantiateReturn(a, args));
|
|
4245
|
+
}
|
|
4246
|
+
return markMockConf({ ...ret });
|
|
4247
|
+
},
|
|
4248
|
+
...relation ? { relation } : {}
|
|
2059
4249
|
},
|
|
2060
|
-
|
|
2061
|
-
|
|
4250
|
+
ctor !== void 0 ? { ctor } : void 0
|
|
4251
|
+
)
|
|
2062
4252
|
);
|
|
2063
4253
|
}
|
|
2064
4254
|
return markMockConf({ ...a });
|
|
@@ -2078,7 +4268,7 @@ function packageHarvestToAbsModules(pkg, h) {
|
|
|
2078
4268
|
return named;
|
|
2079
4269
|
};
|
|
2080
4270
|
const register = (key, exports) => {
|
|
2081
|
-
if (!out
|
|
4271
|
+
if (!Object.hasOwn(out, key)) out[key] = exports;
|
|
2082
4272
|
};
|
|
2083
4273
|
for (const [mod, rec] of Object.entries(h.env.modules)) {
|
|
2084
4274
|
const named = convertRecord(rec);
|
|
@@ -2086,22 +4276,63 @@ function packageHarvestToAbsModules(pkg, h) {
|
|
|
2086
4276
|
const def = rec["default"];
|
|
2087
4277
|
if (def) exports.default = harvestedValueToAbs(def);
|
|
2088
4278
|
register(mod, exports);
|
|
2089
|
-
register(pkg, exports);
|
|
2090
4279
|
const stripped = mod.replace(/^@types\//, "").replace(/\\/g, "/");
|
|
2091
|
-
if (stripped) register(stripped, exports);
|
|
4280
|
+
if (stripped && stripped !== pkg) register(stripped, exports);
|
|
2092
4281
|
const base = stripped.split("/").filter(Boolean).pop();
|
|
2093
4282
|
if (base && base !== pkg) register(base, exports);
|
|
2094
4283
|
}
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
4284
|
+
out[pkg] = packageRootExports(pkg, h);
|
|
4285
|
+
return out;
|
|
4286
|
+
}
|
|
4287
|
+
function packageRootExports(pkg, h) {
|
|
4288
|
+
const globals = h.env.globals;
|
|
4289
|
+
const surface = packageExportSurface(h);
|
|
4290
|
+
const named = {};
|
|
4291
|
+
if (surface.unresolved) {
|
|
4292
|
+
const ambient = packageAmbientNames(h);
|
|
4293
|
+
for (const [k, v] of Object.entries(globals)) {
|
|
4294
|
+
if (ambient.has(k)) continue;
|
|
4295
|
+
named[k] = harvestedValueToAbs(v);
|
|
4296
|
+
}
|
|
4297
|
+
} else {
|
|
4298
|
+
for (const n of surface.names) {
|
|
4299
|
+
if (!Object.hasOwn(globals, n)) continue;
|
|
4300
|
+
named[n] = harvestedValueToAbs(globals[n]);
|
|
2102
4301
|
}
|
|
2103
4302
|
}
|
|
2104
|
-
|
|
4303
|
+
const exports = { named };
|
|
4304
|
+
if (surface.unresolved || surface.names.has("default")) {
|
|
4305
|
+
const def = Object.hasOwn(globals, "default") ? globals["default"] : void 0;
|
|
4306
|
+
if (def) exports.default = harvestedValueToAbs(def);
|
|
4307
|
+
}
|
|
4308
|
+
const aug = Object.hasOwn(h.env.modules, pkg) ? h.env.modules[pkg] : void 0;
|
|
4309
|
+
if (aug) {
|
|
4310
|
+
for (const [k, v] of Object.entries(aug)) {
|
|
4311
|
+
if (Object.hasOwn(named, k)) continue;
|
|
4312
|
+
named[k] = harvestedValueToAbs(v);
|
|
4313
|
+
}
|
|
4314
|
+
}
|
|
4315
|
+
return exports;
|
|
4316
|
+
}
|
|
4317
|
+
var subpathCache = new BoundedLruMap(32);
|
|
4318
|
+
function subpathExports(pkg, spec, h) {
|
|
4319
|
+
const sub = spec.slice(pkg.length + 1);
|
|
4320
|
+
const file = resolveSubpathEntry(h.root, sub);
|
|
4321
|
+
if (!file) return void 0;
|
|
4322
|
+
const hit = subpathCache.get(file);
|
|
4323
|
+
if (hit) return hit;
|
|
4324
|
+
const env = harvestDts([file]);
|
|
4325
|
+
const scan = dtsFileScan(file);
|
|
4326
|
+
const named = {};
|
|
4327
|
+
for (const n of scan.exported) {
|
|
4328
|
+
if (!Object.hasOwn(env.globals, n)) continue;
|
|
4329
|
+
named[n] = harvestedValueToAbs(env.globals[n]);
|
|
4330
|
+
}
|
|
4331
|
+
const exports = { named };
|
|
4332
|
+
const def = Object.hasOwn(env.globals, "default") ? env.globals["default"] : void 0;
|
|
4333
|
+
if (def) exports.default = harvestedValueToAbs(def);
|
|
4334
|
+
subpathCache.set(file, exports);
|
|
4335
|
+
return exports;
|
|
2105
4336
|
}
|
|
2106
4337
|
function bareSpecToAbsModules(spec, fromFile) {
|
|
2107
4338
|
if (!spec || spec.startsWith(".") || spec.startsWith("/") || spec.startsWith("node:")) {
|
|
@@ -2110,8 +4341,12 @@ function bareSpecToAbsModules(spec, fromFile) {
|
|
|
2110
4341
|
const parts = spec.split("/");
|
|
2111
4342
|
const pkg = spec.startsWith("@") ? parts.length >= 2 ? `${parts[0]}/${parts[1]}` : void 0 : parts[0];
|
|
2112
4343
|
if (!pkg) return void 0;
|
|
2113
|
-
const
|
|
2114
|
-
return
|
|
4344
|
+
const h = harvestPackageCached(pkg, dirname4(fromFile));
|
|
4345
|
+
if (!h) return void 0;
|
|
4346
|
+
const table = packageHarvestToAbsModules(pkg, h);
|
|
4347
|
+
if (spec === pkg) return table[pkg];
|
|
4348
|
+
if (Object.hasOwn(table, spec)) return table[spec];
|
|
4349
|
+
return subpathExports(pkg, spec, h);
|
|
2115
4350
|
}
|
|
2116
4351
|
|
|
2117
4352
|
// src/harvest-node.ts
|
|
@@ -2242,7 +4477,8 @@ function harvestNodeTypes(fromDir, maxFiles = HARVEST_NODE_DEFAULT_MAX_FILES, ma
|
|
|
2242
4477
|
const stats = {
|
|
2243
4478
|
files: env.stats.files,
|
|
2244
4479
|
symbols: env.stats.symbols,
|
|
2245
|
-
skipped: env.stats.skipped
|
|
4480
|
+
skipped: env.stats.skipped,
|
|
4481
|
+
...env.stats.degraded !== void 0 ? { degraded: env.stats.degraded } : {}
|
|
2246
4482
|
};
|
|
2247
4483
|
const result = {
|
|
2248
4484
|
ok: true,
|
|
@@ -2261,7 +4497,8 @@ function harvestNodeTypes(fromDir, maxFiles = HARVEST_NODE_DEFAULT_MAX_FILES, ma
|
|
|
2261
4497
|
const stats = {
|
|
2262
4498
|
files: env.stats.files,
|
|
2263
4499
|
symbols: env.stats.symbols,
|
|
2264
|
-
skipped: env.stats.skipped
|
|
4500
|
+
skipped: env.stats.skipped,
|
|
4501
|
+
...env.stats.degraded !== void 0 ? { degraded: env.stats.degraded } : {}
|
|
2265
4502
|
};
|
|
2266
4503
|
if (diskRoot) {
|
|
2267
4504
|
writeHarvestDisk(
|