@dudousxd/nestjs-codegen 0.29.0 → 0.30.0
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/CHANGELOG.md +24 -0
- package/dist/cli/main.cjs +711 -436
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.js +862 -582
- package/dist/cli/main.js.map +1 -1
- package/dist/extension/index.d.cts +1 -1
- package/dist/extension/index.d.ts +1 -1
- package/dist/{index-Dc4s883u.d.cts → index-D4Blt-RT.d.cts} +1 -13
- package/dist/{index-Dc4s883u.d.ts → index-D4Blt-RT.d.ts} +1 -13
- package/dist/index.cjs +696 -434
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +854 -587
- package/dist/index.js.map +1 -1
- package/dist/nest/index.cjs +559 -284
- package/dist/nest/index.cjs.map +1 -1
- package/dist/nest/index.d.cts +1 -1
- package/dist/nest/index.d.ts +1 -1
- package/dist/nest/index.js +369 -89
- package/dist/nest/index.js.map +1 -1
- package/package.json +9 -9
- package/skills/codegen-setup/SKILL.md +5 -1
package/dist/index.js
CHANGED
|
@@ -105,11 +105,6 @@ function validateUserConfig(userConfig) {
|
|
|
105
105
|
);
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
|
-
if (userConfig.scopes && Object.keys(userConfig.scopes).length > 0) {
|
|
109
|
-
console.warn(
|
|
110
|
-
"[nestjs-codegen] The top-level `scopes` option has no effect and is deprecated. For several Inertia apps use `pages.scopes` (one entry per InertiaModule.forFeature scope); for several controller sets use one `contracts.glob` (e.g. a brace glob)."
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
108
|
}
|
|
114
109
|
function isGlobInput(value) {
|
|
115
110
|
if (typeof value === "string") return true;
|
|
@@ -157,7 +152,6 @@ function applyDefaults(userConfig, cwd) {
|
|
|
157
152
|
glob: userConfig.contracts?.glob ?? "src/**/*.controller.ts",
|
|
158
153
|
debounceMs: userConfig.contracts?.debounceMs ?? 500
|
|
159
154
|
},
|
|
160
|
-
scopes: userConfig.scopes ?? {},
|
|
161
155
|
codegen: {
|
|
162
156
|
outDir,
|
|
163
157
|
cwd: resolvedCwd
|
|
@@ -252,7 +246,7 @@ async function discoverPages(opts) {
|
|
|
252
246
|
const rel = relative2(opts.cwd, file);
|
|
253
247
|
const nameRel = relative2(pagesBase, file).split(sep2).join("/");
|
|
254
248
|
const name = `${prefix}${computeName(nameRel, opts.componentNameStrategy)}`;
|
|
255
|
-
const source = await readFile(file, "utf8");
|
|
249
|
+
const source = await (opts.read ? opts.read(file) : readFile(file, "utf8"));
|
|
256
250
|
const propsSource = extractPropsSource(source, opts.propsExport);
|
|
257
251
|
out.push({ name, absolutePath: file, relativePath: rel, propsSource, scope });
|
|
258
252
|
}
|
|
@@ -260,10 +254,14 @@ async function discoverPages(opts) {
|
|
|
260
254
|
assertUniqueNames(out);
|
|
261
255
|
return out;
|
|
262
256
|
}
|
|
263
|
-
async function discoverPageScopes(config) {
|
|
257
|
+
async function discoverPageScopes(config, read) {
|
|
264
258
|
const pages = config.pages;
|
|
265
259
|
if (!pages) return [];
|
|
266
|
-
const common = {
|
|
260
|
+
const common = {
|
|
261
|
+
cwd: config.codegen.cwd,
|
|
262
|
+
propsExport: pages.propsExport,
|
|
263
|
+
...read ? { read } : {}
|
|
264
|
+
};
|
|
267
265
|
const out = await discoverPages({
|
|
268
266
|
...common,
|
|
269
267
|
glob: pages.glob,
|
|
@@ -334,305 +332,69 @@ function extractPropsSource(source, exportName) {
|
|
|
334
332
|
}
|
|
335
333
|
|
|
336
334
|
// src/discovery/shared-props.ts
|
|
337
|
-
import {
|
|
338
|
-
import {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
335
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
336
|
+
import { join as join4 } from "path";
|
|
337
|
+
import { Node as Node11, Project as Project4, SyntaxKind as SyntaxKind4 } from "ts-morph";
|
|
338
|
+
|
|
339
|
+
// src/discovery/contracts-fast.ts
|
|
340
|
+
import { existsSync } from "fs";
|
|
341
|
+
import { dirname as dirname2, join as join3, resolve as resolve3 } from "path";
|
|
342
|
+
import fg2 from "fast-glob";
|
|
343
|
+
import {
|
|
344
|
+
FileSystemRefreshResult as FileSystemRefreshResult3,
|
|
345
|
+
Node as Node10,
|
|
346
|
+
Project as Project3,
|
|
347
|
+
ts as ts4
|
|
348
|
+
} from "ts-morph";
|
|
349
|
+
|
|
350
|
+
// src/discovery/disk-fingerprints.ts
|
|
351
|
+
import { readFileSync, statSync } from "fs";
|
|
352
|
+
var DiskFingerprints = class {
|
|
353
|
+
stats = /* @__PURE__ */ new Map();
|
|
354
|
+
/** The paths whose disk content differs from `text`, or that no longer exist. */
|
|
355
|
+
changed(files) {
|
|
356
|
+
const changed = [];
|
|
357
|
+
for (const { path, text } of files) {
|
|
358
|
+
let key;
|
|
343
359
|
try {
|
|
344
|
-
|
|
360
|
+
const stat2 = statSync(path);
|
|
361
|
+
key = `${stat2.mtimeMs}:${stat2.size}`;
|
|
345
362
|
} catch {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
const forRootCall = findForRootCall(sourceFile);
|
|
350
|
-
if (!forRootCall) return null;
|
|
351
|
-
const initializer = findShareInitializer(forRootCall);
|
|
352
|
-
if (!initializer) return null;
|
|
353
|
-
return extractShareType(initializer, sourceFile, project);
|
|
354
|
-
} catch {
|
|
355
|
-
return null;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
function discoverSharedPropsFromConfig(config) {
|
|
359
|
-
if (!config.app?.moduleEntry) return null;
|
|
360
|
-
try {
|
|
361
|
-
const tsconfigPath = config.app.tsconfig ?? join3(config.codegen.cwd, "tsconfig.json");
|
|
362
|
-
let project;
|
|
363
|
-
try {
|
|
364
|
-
project = new Project({
|
|
365
|
-
tsConfigFilePath: tsconfigPath,
|
|
366
|
-
skipAddingFilesFromTsConfig: true,
|
|
367
|
-
skipLoadingLibFiles: true,
|
|
368
|
-
skipFileDependencyResolution: true
|
|
369
|
-
});
|
|
370
|
-
} catch {
|
|
371
|
-
project = new Project({
|
|
372
|
-
skipAddingFilesFromTsConfig: true,
|
|
373
|
-
skipLoadingLibFiles: true,
|
|
374
|
-
skipFileDependencyResolution: true,
|
|
375
|
-
compilerOptions: { allowJs: true, strict: false }
|
|
376
|
-
});
|
|
377
|
-
}
|
|
378
|
-
return discoverSharedProps(project, config.app.moduleEntry);
|
|
379
|
-
} catch {
|
|
380
|
-
return null;
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
function findForRootCall(sourceFile) {
|
|
384
|
-
const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
|
|
385
|
-
for (const call of callExpressions) {
|
|
386
|
-
const expr = call.getExpression();
|
|
387
|
-
if (!Node.isPropertyAccessExpression(expr)) continue;
|
|
388
|
-
const methodName = expr.getName();
|
|
389
|
-
const objectExpr = expr.getExpression();
|
|
390
|
-
if (methodName === "forRoot" && Node.isIdentifier(objectExpr)) {
|
|
391
|
-
const name = objectExpr.getText();
|
|
392
|
-
if (name === "InertiaModule") {
|
|
393
|
-
return call;
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
return null;
|
|
398
|
-
}
|
|
399
|
-
function findShareInitializer(forRootCall) {
|
|
400
|
-
if (!Node.isCallExpression(forRootCall)) return null;
|
|
401
|
-
const args = forRootCall.getArguments();
|
|
402
|
-
const firstArg2 = args[0];
|
|
403
|
-
if (!firstArg2 || !Node.isObjectLiteralExpression(firstArg2)) return null;
|
|
404
|
-
for (const prop of firstArg2.getProperties()) {
|
|
405
|
-
if (Node.isPropertyAssignment(prop) && prop.getName() === "share") {
|
|
406
|
-
return prop.getInitializer() ?? null;
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
return null;
|
|
410
|
-
}
|
|
411
|
-
function extractShareType(node, sourceFile, project) {
|
|
412
|
-
if (Node.isIdentifier(node)) {
|
|
413
|
-
const ref = resolveIdentifierToImportRef(node, sourceFile, project);
|
|
414
|
-
if (ref) {
|
|
415
|
-
return {
|
|
416
|
-
typeString: `Awaited<ReturnType<typeof import('${ref.importPath}').${ref.exportName}>>`,
|
|
417
|
-
properties: null,
|
|
418
|
-
isImportRef: true
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
if (Node.isArrowFunction(node)) {
|
|
423
|
-
const result = extractFromFunctionLike(node, sourceFile);
|
|
424
|
-
return result ? { ...result, isImportRef: false } : null;
|
|
425
|
-
}
|
|
426
|
-
if (Node.isFunctionExpression(node)) {
|
|
427
|
-
const result = extractFromFunctionLike(node, sourceFile);
|
|
428
|
-
return result ? { ...result, isImportRef: false } : null;
|
|
429
|
-
}
|
|
430
|
-
if (Node.isObjectLiteralExpression(node)) {
|
|
431
|
-
const result = extractFromObjectLiteral(node);
|
|
432
|
-
return result ? { ...result, isImportRef: false } : null;
|
|
433
|
-
}
|
|
434
|
-
return null;
|
|
435
|
-
}
|
|
436
|
-
function resolveIdentifierToImportRef(id, sourceFile, project) {
|
|
437
|
-
if (!Node.isIdentifier(id)) return null;
|
|
438
|
-
const name = id.getText();
|
|
439
|
-
const localFunc = sourceFile.getFunction(name);
|
|
440
|
-
if (localFunc?.isExported()) {
|
|
441
|
-
const filePath = sourceFile.getFilePath().replace(/\.ts$/, "");
|
|
442
|
-
return { importPath: filePath, exportName: name };
|
|
443
|
-
}
|
|
444
|
-
const localVar = sourceFile.getVariableDeclaration(name);
|
|
445
|
-
if (localVar?.isExported()) {
|
|
446
|
-
const filePath = sourceFile.getFilePath().replace(/\.ts$/, "");
|
|
447
|
-
return { importPath: filePath, exportName: name };
|
|
448
|
-
}
|
|
449
|
-
for (const imp of sourceFile.getImportDeclarations()) {
|
|
450
|
-
for (const named of imp.getNamedImports()) {
|
|
451
|
-
const importedName = named.getAliasNode()?.getText() ?? named.getName();
|
|
452
|
-
if (importedName !== name) continue;
|
|
453
|
-
const resolvedSource = imp.getModuleSpecifierSourceFile();
|
|
454
|
-
if (!resolvedSource) continue;
|
|
455
|
-
const originalName = named.getName();
|
|
456
|
-
const fn = resolvedSource.getFunction(originalName);
|
|
457
|
-
if (fn?.isExported()) {
|
|
458
|
-
const filePath = resolvedSource.getFilePath().replace(/\.ts$/, "");
|
|
459
|
-
return { importPath: filePath, exportName: originalName };
|
|
460
|
-
}
|
|
461
|
-
const v = resolvedSource.getVariableDeclaration(originalName);
|
|
462
|
-
if (v?.isExported()) {
|
|
463
|
-
const filePath = resolvedSource.getFilePath().replace(/\.ts$/, "");
|
|
464
|
-
return { importPath: filePath, exportName: originalName };
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
return null;
|
|
469
|
-
}
|
|
470
|
-
function extractFromFunctionLike(node, _sourceFile) {
|
|
471
|
-
const returnTypeNode = Node.isArrowFunction(node) || Node.isFunctionExpression(node) ? node.getReturnTypeNode() : null;
|
|
472
|
-
if (returnTypeNode) {
|
|
473
|
-
return extractFromReturnTypeAnnotation(returnTypeNode);
|
|
474
|
-
}
|
|
475
|
-
if (Node.isArrowFunction(node)) {
|
|
476
|
-
const body = node.getBody();
|
|
477
|
-
if (Node.isParenthesizedExpression(body)) {
|
|
478
|
-
const inner = body.getExpression();
|
|
479
|
-
if (Node.isObjectLiteralExpression(inner)) {
|
|
480
|
-
return extractFromObjectLiteral(inner);
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
if (Node.isObjectLiteralExpression(body)) {
|
|
484
|
-
return extractFromObjectLiteral(body);
|
|
485
|
-
}
|
|
486
|
-
if (Node.isBlock(body)) {
|
|
487
|
-
return extractFromBlockReturn(body);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
if (Node.isFunctionExpression(node)) {
|
|
491
|
-
const body = node.getBody();
|
|
492
|
-
if (Node.isBlock(body)) {
|
|
493
|
-
return extractFromBlockReturn(body);
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
return null;
|
|
497
|
-
}
|
|
498
|
-
function extractFromReturnTypeAnnotation(typeNode) {
|
|
499
|
-
if (Node.isTypeReference(typeNode)) {
|
|
500
|
-
const typeName = typeNode.getTypeName();
|
|
501
|
-
if (Node.isIdentifier(typeName) && typeName.getText() === "Promise") {
|
|
502
|
-
const typeArgs = typeNode.getTypeArguments();
|
|
503
|
-
const firstArg2 = typeArgs[0];
|
|
504
|
-
if (firstArg2) {
|
|
505
|
-
return extractFromReturnTypeAnnotation(firstArg2);
|
|
363
|
+
this.stats.delete(path);
|
|
364
|
+
changed.push(path);
|
|
365
|
+
continue;
|
|
506
366
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
if (Node.isPropertySignature(member)) {
|
|
514
|
-
const name = member.getName();
|
|
515
|
-
const memberTypeNode = member.getTypeNode();
|
|
516
|
-
const type = memberTypeNode ? memberTypeNode.getText() : "unknown";
|
|
517
|
-
properties.push({ name, type });
|
|
367
|
+
if (this.stats.get(path) === key) continue;
|
|
368
|
+
let disk;
|
|
369
|
+
try {
|
|
370
|
+
disk = readFileSync(path, "utf-8");
|
|
371
|
+
} catch {
|
|
372
|
+
disk = void 0;
|
|
518
373
|
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
return null;
|
|
525
|
-
}
|
|
526
|
-
function extractFromBlockReturn(block) {
|
|
527
|
-
if (!Node.isBlock(block)) return null;
|
|
528
|
-
const statements = block.getStatements();
|
|
529
|
-
for (let i = statements.length - 1; i >= 0; i--) {
|
|
530
|
-
const stmt = statements[i];
|
|
531
|
-
if (!Node.isReturnStatement(stmt)) continue;
|
|
532
|
-
const expr = stmt.getExpression();
|
|
533
|
-
if (!expr) continue;
|
|
534
|
-
if (Node.isObjectLiteralExpression(expr)) {
|
|
535
|
-
return extractFromObjectLiteral(expr);
|
|
536
|
-
}
|
|
537
|
-
if (Node.isParenthesizedExpression(expr)) {
|
|
538
|
-
const inner = expr.getExpression();
|
|
539
|
-
if (Node.isObjectLiteralExpression(inner)) {
|
|
540
|
-
return extractFromObjectLiteral(inner);
|
|
374
|
+
if (disk === text) {
|
|
375
|
+
this.stats.set(path, key);
|
|
376
|
+
} else {
|
|
377
|
+
this.stats.delete(path);
|
|
378
|
+
changed.push(path);
|
|
541
379
|
}
|
|
542
380
|
}
|
|
543
|
-
|
|
381
|
+
return changed;
|
|
544
382
|
}
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
if (!Node.isObjectLiteralExpression(objLiteral)) return null;
|
|
549
|
-
const properties = [];
|
|
550
|
-
for (const prop of objLiteral.getProperties()) {
|
|
551
|
-
if (!Node.isPropertyAssignment(prop)) continue;
|
|
552
|
-
const name = prop.getName();
|
|
553
|
-
const initializer = prop.getInitializer();
|
|
554
|
-
if (!initializer) continue;
|
|
555
|
-
const type = inferExpressionType(initializer);
|
|
556
|
-
properties.push({ name, type });
|
|
383
|
+
/** Forget `path`, so its next check compares content again. */
|
|
384
|
+
forget(path) {
|
|
385
|
+
this.stats.delete(path);
|
|
557
386
|
}
|
|
558
|
-
|
|
559
|
-
const typeString = `{ ${properties.map((p) => `${p.name}: ${p.type}`).join("; ")} }`;
|
|
560
|
-
return { typeString, properties, isImportRef: false };
|
|
561
|
-
}
|
|
562
|
-
function inferExpressionType(node) {
|
|
563
|
-
if (Node.isStringLiteral(node)) return "string";
|
|
564
|
-
if (Node.isTemplateExpression(node) || Node.isNoSubstitutionTemplateLiteral(node))
|
|
565
|
-
return "string";
|
|
566
|
-
if (Node.isNumericLiteral(node)) return "number";
|
|
567
|
-
if (node.getKind() === SyntaxKind.TrueKeyword || node.getKind() === SyntaxKind.FalseKeyword) {
|
|
568
|
-
return "boolean";
|
|
569
|
-
}
|
|
570
|
-
if (node.getKind() === SyntaxKind.NullKeyword) return "null";
|
|
571
|
-
if (Node.isIdentifier(node) && node.getText() === "undefined") return "undefined";
|
|
572
|
-
if (Node.isObjectLiteralExpression(node)) {
|
|
573
|
-
const props = node.getProperties();
|
|
574
|
-
if (props.length === 0) return "Record<string, unknown>";
|
|
575
|
-
const entries = [];
|
|
576
|
-
for (const prop of props) {
|
|
577
|
-
if (!Node.isPropertyAssignment(prop)) continue;
|
|
578
|
-
const key = prop.getName();
|
|
579
|
-
const init = prop.getInitializer();
|
|
580
|
-
if (!init) continue;
|
|
581
|
-
entries.push(`${key}: ${inferExpressionType(init)}`);
|
|
582
|
-
}
|
|
583
|
-
if (entries.length === 0) return "Record<string, unknown>";
|
|
584
|
-
return `{ ${entries.join("; ")} }`;
|
|
585
|
-
}
|
|
586
|
-
if (Node.isArrayLiteralExpression(node)) {
|
|
587
|
-
const elements = node.getElements();
|
|
588
|
-
if (elements.length === 0) return "Array<unknown>";
|
|
589
|
-
const first = elements[0];
|
|
590
|
-
if (first) return `Array<${inferExpressionType(first)}>`;
|
|
591
|
-
return "Array<unknown>";
|
|
592
|
-
}
|
|
593
|
-
if (Node.isConditionalExpression(node)) {
|
|
594
|
-
const whenTrue = inferExpressionType(node.getWhenTrue());
|
|
595
|
-
const whenFalse = inferExpressionType(node.getWhenFalse());
|
|
596
|
-
if (whenTrue === whenFalse) return whenTrue;
|
|
597
|
-
return `${whenTrue} | ${whenFalse}`;
|
|
598
|
-
}
|
|
599
|
-
if (Node.isParenthesizedExpression(node)) {
|
|
600
|
-
return inferExpressionType(node.getExpression());
|
|
601
|
-
}
|
|
602
|
-
if (Node.isAsExpression(node)) {
|
|
603
|
-
const typeNode = node.getTypeNode();
|
|
604
|
-
if (typeNode) return typeNode.getText();
|
|
605
|
-
}
|
|
606
|
-
return "unknown";
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
// src/emit/emit-api.ts
|
|
610
|
-
import { mkdir, writeFile } from "fs/promises";
|
|
611
|
-
import { isAbsolute as isAbsolute2, join as join5, relative as relative4 } from "path";
|
|
612
|
-
|
|
613
|
-
// src/extension/registry.ts
|
|
614
|
-
import { relative as relative3, resolve as resolve4 } from "path";
|
|
615
|
-
import { Project as Project5 } from "ts-morph";
|
|
616
|
-
|
|
617
|
-
// src/discovery/contracts-fast.ts
|
|
618
|
-
import { existsSync } from "fs";
|
|
619
|
-
import { dirname as dirname2, join as join4, resolve as resolve3 } from "path";
|
|
620
|
-
import fg2 from "fast-glob";
|
|
621
|
-
import {
|
|
622
|
-
Node as Node11,
|
|
623
|
-
Project as Project4,
|
|
624
|
-
ts as ts3
|
|
625
|
-
} from "ts-morph";
|
|
387
|
+
};
|
|
626
388
|
|
|
627
389
|
// src/discovery/dto-type-resolver.ts
|
|
628
390
|
import {
|
|
629
|
-
Node as
|
|
630
|
-
SyntaxKind as
|
|
391
|
+
Node as Node9,
|
|
392
|
+
SyntaxKind as SyntaxKind3
|
|
631
393
|
} from "ts-morph";
|
|
632
394
|
|
|
633
395
|
// src/discovery/dto-to-ir.ts
|
|
634
396
|
import {
|
|
635
|
-
Node as
|
|
397
|
+
Node as Node2
|
|
636
398
|
} from "ts-morph";
|
|
637
399
|
|
|
638
400
|
// src/util/debug-log.ts
|
|
@@ -647,7 +409,7 @@ function debugWarn(message) {
|
|
|
647
409
|
// src/discovery/type-ref-resolution.ts
|
|
648
410
|
import { dirname, resolve as resolve2 } from "path";
|
|
649
411
|
import {
|
|
650
|
-
Node
|
|
412
|
+
Node
|
|
651
413
|
} from "ts-morph";
|
|
652
414
|
var _EMPTY_CTX = { projectRoot: "", tsconfigPaths: null };
|
|
653
415
|
var _ctxByProject = /* @__PURE__ */ new WeakMap();
|
|
@@ -913,13 +675,13 @@ function resolveTypeRef(nodeOrName, sourceFile, project, opts) {
|
|
|
913
675
|
name = nodeOrName;
|
|
914
676
|
} else {
|
|
915
677
|
const typeNode = nodeOrName;
|
|
916
|
-
if (opts.unwrapContainers &&
|
|
678
|
+
if (opts.unwrapContainers && Node.isArrayTypeNode(typeNode)) {
|
|
917
679
|
const inner = resolveTypeRef(typeNode.getElementTypeNode(), sourceFile, project, opts);
|
|
918
680
|
return inner ? { ...inner, isArray: true } : null;
|
|
919
681
|
}
|
|
920
|
-
if (!
|
|
682
|
+
if (!Node.isTypeReference(typeNode)) return null;
|
|
921
683
|
const typeName = typeNode.getTypeName();
|
|
922
|
-
const refName =
|
|
684
|
+
const refName = Node.isIdentifier(typeName) ? typeName.getText() : null;
|
|
923
685
|
if (!refName) return null;
|
|
924
686
|
if (opts.unwrapContainers && refName === "Promise") {
|
|
925
687
|
const first = typeNode.getTypeArguments()[0];
|
|
@@ -1053,7 +815,7 @@ function buildProperty(prop, classFile, ctx) {
|
|
|
1053
815
|
const dec = (n) => decorators.get(n);
|
|
1054
816
|
const typeNode = prop.getTypeNode();
|
|
1055
817
|
const typeText = typeNode?.getText() ?? "unknown";
|
|
1056
|
-
const isArrayType = !!typeNode &&
|
|
818
|
+
const isArrayType = !!typeNode && Node2.isArrayTypeNode(typeNode);
|
|
1057
819
|
const asField = (child) => applyPresence(
|
|
1058
820
|
has("IsArray") || isArrayType ? { kind: "array", element: child } : child,
|
|
1059
821
|
decorators
|
|
@@ -1267,22 +1029,22 @@ function firstArgText(decorator) {
|
|
|
1267
1029
|
}
|
|
1268
1030
|
function numericArg(decorator) {
|
|
1269
1031
|
const arg = firstArg(decorator);
|
|
1270
|
-
if (arg &&
|
|
1032
|
+
if (arg && Node2.isNumericLiteral(arg)) return arg.getText();
|
|
1271
1033
|
return null;
|
|
1272
1034
|
}
|
|
1273
1035
|
function numericArgs(decorator) {
|
|
1274
1036
|
const args = decorator?.getArguments() ?? [];
|
|
1275
|
-
const num = (n) => n &&
|
|
1037
|
+
const num = (n) => n && Node2.isNumericLiteral(n) ? n.getText() : null;
|
|
1276
1038
|
return [num(args[0]), num(args[1])];
|
|
1277
1039
|
}
|
|
1278
1040
|
function messageRaw(decorator) {
|
|
1279
1041
|
const args = decorator?.getArguments() ?? [];
|
|
1280
1042
|
for (const arg of args) {
|
|
1281
|
-
if (
|
|
1043
|
+
if (Node2.isObjectLiteralExpression(arg)) {
|
|
1282
1044
|
for (const prop of arg.getProperties()) {
|
|
1283
|
-
if (
|
|
1045
|
+
if (Node2.isPropertyAssignment(prop) && prop.getName() === "message") {
|
|
1284
1046
|
const init = prop.getInitializer();
|
|
1285
|
-
if (init &&
|
|
1047
|
+
if (init && Node2.isStringLiteral(init)) return init.getText();
|
|
1286
1048
|
}
|
|
1287
1049
|
}
|
|
1288
1050
|
}
|
|
@@ -1291,30 +1053,30 @@ function messageRaw(decorator) {
|
|
|
1291
1053
|
}
|
|
1292
1054
|
function resolveDiscriminator(decorator) {
|
|
1293
1055
|
const optsArg = decorator?.getArguments()[1];
|
|
1294
|
-
if (!optsArg || !
|
|
1056
|
+
if (!optsArg || !Node2.isObjectLiteralExpression(optsArg)) return null;
|
|
1295
1057
|
let discProp;
|
|
1296
1058
|
for (const prop of optsArg.getProperties()) {
|
|
1297
|
-
if (
|
|
1059
|
+
if (Node2.isPropertyAssignment(prop) && prop.getName() === "discriminator") {
|
|
1298
1060
|
discProp = prop.getInitializer();
|
|
1299
1061
|
}
|
|
1300
1062
|
}
|
|
1301
|
-
if (!discProp || !
|
|
1063
|
+
if (!discProp || !Node2.isObjectLiteralExpression(discProp)) return null;
|
|
1302
1064
|
let property = null;
|
|
1303
1065
|
const subTypes = [];
|
|
1304
1066
|
for (const prop of discProp.getProperties()) {
|
|
1305
|
-
if (!
|
|
1067
|
+
if (!Node2.isPropertyAssignment(prop)) continue;
|
|
1306
1068
|
const name = prop.getName();
|
|
1307
1069
|
const init = prop.getInitializer();
|
|
1308
1070
|
if (!init) continue;
|
|
1309
|
-
if (name === "property" &&
|
|
1071
|
+
if (name === "property" && Node2.isStringLiteral(init)) {
|
|
1310
1072
|
property = init.getLiteralValue();
|
|
1311
|
-
} else if (name === "subTypes" &&
|
|
1073
|
+
} else if (name === "subTypes" && Node2.isArrayLiteralExpression(init)) {
|
|
1312
1074
|
for (const el of init.getElements()) {
|
|
1313
|
-
if (!
|
|
1075
|
+
if (!Node2.isObjectLiteralExpression(el)) continue;
|
|
1314
1076
|
for (const p of el.getProperties()) {
|
|
1315
|
-
if (!
|
|
1077
|
+
if (!Node2.isPropertyAssignment(p) || p.getName() !== "name") continue;
|
|
1316
1078
|
const nameInit = p.getInitializer();
|
|
1317
|
-
if (nameInit &&
|
|
1079
|
+
if (nameInit && Node2.isIdentifier(nameInit)) subTypes.push(nameInit.getText());
|
|
1318
1080
|
}
|
|
1319
1081
|
}
|
|
1320
1082
|
}
|
|
@@ -1325,9 +1087,9 @@ function resolveDiscriminator(decorator) {
|
|
|
1325
1087
|
function resolveTypeFactoryName(decorator) {
|
|
1326
1088
|
const arg = firstArg(decorator);
|
|
1327
1089
|
if (!arg) return null;
|
|
1328
|
-
if (
|
|
1090
|
+
if (Node2.isArrowFunction(arg)) {
|
|
1329
1091
|
const body = arg.getBody();
|
|
1330
|
-
if (
|
|
1092
|
+
if (Node2.isIdentifier(body)) return body.getText();
|
|
1331
1093
|
}
|
|
1332
1094
|
return null;
|
|
1333
1095
|
}
|
|
@@ -1336,12 +1098,12 @@ function singularClassName(typeText) {
|
|
|
1336
1098
|
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(inner) ? inner : null;
|
|
1337
1099
|
}
|
|
1338
1100
|
function genericTypeArgNames(typeNode) {
|
|
1339
|
-
if (!typeNode || !
|
|
1101
|
+
if (!typeNode || !Node2.isTypeReference(typeNode)) return [];
|
|
1340
1102
|
const names = [];
|
|
1341
1103
|
for (const arg of typeNode.getTypeArguments()) {
|
|
1342
|
-
if (!
|
|
1104
|
+
if (!Node2.isTypeReference(arg)) return [];
|
|
1343
1105
|
const tn = arg.getTypeName();
|
|
1344
|
-
if (!
|
|
1106
|
+
if (!Node2.isIdentifier(tn)) return [];
|
|
1345
1107
|
names.push(tn.getText());
|
|
1346
1108
|
}
|
|
1347
1109
|
return names;
|
|
@@ -1349,7 +1111,7 @@ function genericTypeArgNames(typeNode) {
|
|
|
1349
1111
|
function enumSchemaFromDecorator(decorator, classFile, ctx) {
|
|
1350
1112
|
const arg = firstArg(decorator);
|
|
1351
1113
|
if (!arg) return null;
|
|
1352
|
-
if (
|
|
1114
|
+
if (Node2.isIdentifier(arg)) {
|
|
1353
1115
|
const name = arg.getText();
|
|
1354
1116
|
const resolved = findType(name, classFile, ctx.project);
|
|
1355
1117
|
if (resolved && resolved.kind === "enum") {
|
|
@@ -1363,12 +1125,12 @@ function enumSchemaFromDecorator(decorator, classFile, ctx) {
|
|
|
1363
1125
|
}
|
|
1364
1126
|
return { kind: "unknown", note: `@IsEnum(${name}): enum not resolvable to literals` };
|
|
1365
1127
|
}
|
|
1366
|
-
if (
|
|
1128
|
+
if (Node2.isObjectLiteralExpression(arg)) {
|
|
1367
1129
|
const values = [];
|
|
1368
1130
|
for (const p of arg.getProperties()) {
|
|
1369
|
-
if (!
|
|
1131
|
+
if (!Node2.isPropertyAssignment(p)) continue;
|
|
1370
1132
|
const init = p.getInitializer();
|
|
1371
|
-
if (init &&
|
|
1133
|
+
if (init && Node2.isStringLiteral(init)) values.push(init.getText());
|
|
1372
1134
|
}
|
|
1373
1135
|
if (values.length > 0) return { kind: "enum", literals: values };
|
|
1374
1136
|
}
|
|
@@ -1376,9 +1138,9 @@ function enumSchemaFromDecorator(decorator, classFile, ctx) {
|
|
|
1376
1138
|
}
|
|
1377
1139
|
function inSchemaFromDecorator(decorator) {
|
|
1378
1140
|
const arg = firstArg(decorator);
|
|
1379
|
-
if (arg &&
|
|
1141
|
+
if (arg && Node2.isArrayLiteralExpression(arg)) {
|
|
1380
1142
|
const elements = arg.getElements();
|
|
1381
|
-
const allStrings = elements.every((e) =>
|
|
1143
|
+
const allStrings = elements.every((e) => Node2.isStringLiteral(e));
|
|
1382
1144
|
if (allStrings && elements.length > 0) {
|
|
1383
1145
|
return { kind: "enum", literals: elements.map((e) => e.getText()) };
|
|
1384
1146
|
}
|
|
@@ -1394,13 +1156,13 @@ function inSchemaFromDecorator(decorator) {
|
|
|
1394
1156
|
|
|
1395
1157
|
// src/discovery/filter-for.ts
|
|
1396
1158
|
import {
|
|
1397
|
-
Node as
|
|
1159
|
+
Node as Node5
|
|
1398
1160
|
} from "ts-morph";
|
|
1399
1161
|
|
|
1400
1162
|
// src/discovery/filter-field-types.ts
|
|
1401
1163
|
import {
|
|
1402
|
-
Node as
|
|
1403
|
-
SyntaxKind
|
|
1164
|
+
Node as Node3,
|
|
1165
|
+
SyntaxKind
|
|
1404
1166
|
} from "ts-morph";
|
|
1405
1167
|
|
|
1406
1168
|
// src/discovery/enum-resolution.ts
|
|
@@ -1454,28 +1216,28 @@ function markNullable(r, nullable) {
|
|
|
1454
1216
|
}
|
|
1455
1217
|
var TRANSPARENT_TYPE_BRANDS = /* @__PURE__ */ new Set(["Opt", "Hidden"]);
|
|
1456
1218
|
function classifyTypeNode(typeNode, sourceFile, project, opts) {
|
|
1457
|
-
if (
|
|
1219
|
+
if (Node3.isUnionTypeNode(typeNode)) {
|
|
1458
1220
|
let nullable = false;
|
|
1459
1221
|
const stringLits = [];
|
|
1460
1222
|
const numberLits = [];
|
|
1461
1223
|
const others = [];
|
|
1462
1224
|
for (const member of typeNode.getTypeNodes()) {
|
|
1463
1225
|
const kind = member.getKind();
|
|
1464
|
-
if (kind ===
|
|
1226
|
+
if (kind === SyntaxKind.NullKeyword || kind === SyntaxKind.UndefinedKeyword) {
|
|
1465
1227
|
nullable = true;
|
|
1466
1228
|
continue;
|
|
1467
1229
|
}
|
|
1468
|
-
if (
|
|
1230
|
+
if (Node3.isLiteralTypeNode(member)) {
|
|
1469
1231
|
const lit = member.getLiteral();
|
|
1470
|
-
if (
|
|
1232
|
+
if (Node3.isStringLiteral(lit)) {
|
|
1471
1233
|
stringLits.push(lit.getLiteralValue());
|
|
1472
1234
|
continue;
|
|
1473
1235
|
}
|
|
1474
|
-
if (
|
|
1236
|
+
if (Node3.isNumericLiteral(lit)) {
|
|
1475
1237
|
numberLits.push(lit.getText());
|
|
1476
1238
|
continue;
|
|
1477
1239
|
}
|
|
1478
|
-
if (lit.getKind() ===
|
|
1240
|
+
if (lit.getKind() === SyntaxKind.NullKeyword) {
|
|
1479
1241
|
nullable = true;
|
|
1480
1242
|
continue;
|
|
1481
1243
|
}
|
|
@@ -1495,19 +1257,19 @@ function classifyTypeNode(typeNode, sourceFile, project, opts) {
|
|
|
1495
1257
|
return markNullable({ kind: "unknown" }, nullable);
|
|
1496
1258
|
}
|
|
1497
1259
|
switch (typeNode.getKind()) {
|
|
1498
|
-
case
|
|
1260
|
+
case SyntaxKind.StringKeyword:
|
|
1499
1261
|
return { kind: "string" };
|
|
1500
|
-
case
|
|
1262
|
+
case SyntaxKind.NumberKeyword:
|
|
1501
1263
|
return { kind: "number" };
|
|
1502
|
-
case
|
|
1264
|
+
case SyntaxKind.BooleanKeyword:
|
|
1503
1265
|
return { kind: "boolean" };
|
|
1504
|
-
case
|
|
1505
|
-
case
|
|
1266
|
+
case SyntaxKind.AnyKeyword:
|
|
1267
|
+
case SyntaxKind.UnknownKeyword:
|
|
1506
1268
|
return { kind: "unknown" };
|
|
1507
1269
|
default:
|
|
1508
1270
|
break;
|
|
1509
1271
|
}
|
|
1510
|
-
if (
|
|
1272
|
+
if (Node3.isTypeReference(typeNode)) {
|
|
1511
1273
|
const refName = typeNode.getTypeName().getText();
|
|
1512
1274
|
if (refName === "Date") return { kind: "date" };
|
|
1513
1275
|
if (refName === "Record" || refName === "Object") return { kind: "json" };
|
|
@@ -1525,25 +1287,25 @@ function classifyTypeNode(typeNode, sourceFile, project, opts) {
|
|
|
1525
1287
|
if (typeRef) return { kind: "unknown", typeRef };
|
|
1526
1288
|
return { kind: "unknown" };
|
|
1527
1289
|
}
|
|
1528
|
-
if (
|
|
1290
|
+
if (Node3.isTypeLiteral(typeNode)) return { kind: "json" };
|
|
1529
1291
|
return { kind: "unknown" };
|
|
1530
1292
|
}
|
|
1531
1293
|
function enumFromDecoratorArgs(args, sourceFile, project) {
|
|
1532
1294
|
for (const arg of args) {
|
|
1533
|
-
if (
|
|
1295
|
+
if (Node3.isArrowFunction(arg)) {
|
|
1534
1296
|
const body = arg.getBody();
|
|
1535
|
-
if (
|
|
1297
|
+
if (Node3.isIdentifier(body)) {
|
|
1536
1298
|
const en = resolveEnumValues(body.getText(), sourceFile, project);
|
|
1537
1299
|
if (en) return en;
|
|
1538
1300
|
}
|
|
1539
1301
|
}
|
|
1540
|
-
if (
|
|
1302
|
+
if (Node3.isObjectLiteralExpression(arg)) {
|
|
1541
1303
|
const itemsProp = arg.getProperty("items");
|
|
1542
|
-
if (itemsProp &&
|
|
1304
|
+
if (itemsProp && Node3.isPropertyAssignment(itemsProp)) {
|
|
1543
1305
|
const init = itemsProp.getInitializer();
|
|
1544
|
-
if (init &&
|
|
1306
|
+
if (init && Node3.isArrowFunction(init)) {
|
|
1545
1307
|
const body = init.getBody();
|
|
1546
|
-
if (
|
|
1308
|
+
if (Node3.isIdentifier(body)) {
|
|
1547
1309
|
const en = resolveEnumValues(body.getText(), sourceFile, project);
|
|
1548
1310
|
if (en) return en;
|
|
1549
1311
|
}
|
|
@@ -1566,7 +1328,7 @@ function classifyFromColumnDecorator(prop, sourceFile, project) {
|
|
|
1566
1328
|
return { kind: "string" };
|
|
1567
1329
|
}
|
|
1568
1330
|
for (const arg of args) {
|
|
1569
|
-
if (
|
|
1331
|
+
if (Node3.isStringLiteral(arg)) {
|
|
1570
1332
|
const raw = arg.getLiteralValue();
|
|
1571
1333
|
const kind = classifyTypeKeyword(raw);
|
|
1572
1334
|
if (kind) {
|
|
@@ -1574,11 +1336,11 @@ function classifyFromColumnDecorator(prop, sourceFile, project) {
|
|
|
1574
1336
|
return { kind };
|
|
1575
1337
|
}
|
|
1576
1338
|
}
|
|
1577
|
-
if (
|
|
1339
|
+
if (Node3.isObjectLiteralExpression(arg)) {
|
|
1578
1340
|
const enumProp = arg.getProperty("enum");
|
|
1579
|
-
if (enumProp &&
|
|
1341
|
+
if (enumProp && Node3.isPropertyAssignment(enumProp)) {
|
|
1580
1342
|
const init = enumProp.getInitializer();
|
|
1581
|
-
if (init &&
|
|
1343
|
+
if (init && Node3.isIdentifier(init)) {
|
|
1582
1344
|
const en = resolveEnumValues(init.getText(), sourceFile, project);
|
|
1583
1345
|
if (en) {
|
|
1584
1346
|
return en.numeric ? { kind: "number", enumValues: en.values, numericEnum: true } : { kind: "string", enumValues: en.values };
|
|
@@ -1588,15 +1350,15 @@ function classifyFromColumnDecorator(prop, sourceFile, project) {
|
|
|
1588
1350
|
}
|
|
1589
1351
|
for (const key of ["columnType", "type"]) {
|
|
1590
1352
|
const typeProp = arg.getProperty(key);
|
|
1591
|
-
if (!typeProp || !
|
|
1353
|
+
if (!typeProp || !Node3.isPropertyAssignment(typeProp)) continue;
|
|
1592
1354
|
const init = typeProp.getInitializer();
|
|
1593
1355
|
if (!init) continue;
|
|
1594
|
-
if (
|
|
1356
|
+
if (Node3.isStringLiteral(init)) {
|
|
1595
1357
|
const kind = classifyTypeKeyword(init.getLiteralValue());
|
|
1596
1358
|
if (kind) return { kind };
|
|
1597
1359
|
continue;
|
|
1598
1360
|
}
|
|
1599
|
-
if (
|
|
1361
|
+
if (Node3.isIdentifier(init)) {
|
|
1600
1362
|
const kind = classifyTypeKeyword(init.getText());
|
|
1601
1363
|
if (kind) return { kind };
|
|
1602
1364
|
}
|
|
@@ -1638,23 +1400,70 @@ function toFilterFieldType(name, r) {
|
|
|
1638
1400
|
}
|
|
1639
1401
|
|
|
1640
1402
|
// src/discovery/heritage.ts
|
|
1641
|
-
import {
|
|
1403
|
+
import {
|
|
1404
|
+
FileSystemRefreshResult,
|
|
1405
|
+
Node as Node4,
|
|
1406
|
+
Project
|
|
1407
|
+
} from "ts-morph";
|
|
1408
|
+
|
|
1409
|
+
// src/discovery/unresolved-imports.ts
|
|
1410
|
+
import { ts } from "ts-morph";
|
|
1411
|
+
var UnresolvedImports = class {
|
|
1412
|
+
read;
|
|
1413
|
+
/**
|
|
1414
|
+
* The files with an import that failed to resolve in `program` and now resolves
|
|
1415
|
+
* to a TypeScript file the program does not hold.
|
|
1416
|
+
*/
|
|
1417
|
+
nowResolving(program) {
|
|
1418
|
+
if (this.read?.program !== program) {
|
|
1419
|
+
this.read = { program, imports: failedImports(program) };
|
|
1420
|
+
}
|
|
1421
|
+
const options = program.getCompilerOptions();
|
|
1422
|
+
const files = /* @__PURE__ */ new Set();
|
|
1423
|
+
for (const { specifier, from } of this.read.imports) {
|
|
1424
|
+
const file = ts.resolveModuleName(specifier, from, options, ts.sys).resolvedModule?.resolvedFileName;
|
|
1425
|
+
if (file !== void 0 && /\.(m|c)?tsx?$/.test(file) && !program.getSourceFile(file)) {
|
|
1426
|
+
files.add(from);
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
return [...files];
|
|
1430
|
+
}
|
|
1431
|
+
};
|
|
1432
|
+
function failedImports(program) {
|
|
1433
|
+
const checker = program.getTypeChecker();
|
|
1434
|
+
const imports = [];
|
|
1435
|
+
for (const file of program.getSourceFiles()) {
|
|
1436
|
+
if (program.isSourceFileDefaultLibrary(file) || file.isDeclarationFile) continue;
|
|
1437
|
+
if (program.isSourceFileFromExternalLibrary(file)) continue;
|
|
1438
|
+
for (const statement of file.statements) {
|
|
1439
|
+
if (!ts.isImportDeclaration(statement) && !ts.isExportDeclaration(statement)) continue;
|
|
1440
|
+
const specifier = statement.moduleSpecifier;
|
|
1441
|
+
if (!specifier || !ts.isStringLiteral(specifier)) continue;
|
|
1442
|
+
if (!checker.getSymbolAtLocation(specifier)) {
|
|
1443
|
+
imports.push({ specifier: specifier.text, from: file.fileName });
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
return imports;
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
// src/discovery/heritage.ts
|
|
1642
1451
|
function resolveHeritageCall(node) {
|
|
1643
1452
|
if (!node) return void 0;
|
|
1644
1453
|
const expr = unwrapExpression(node);
|
|
1645
|
-
if (
|
|
1646
|
-
if (!
|
|
1454
|
+
if (Node4.isCallExpression(expr)) return expr;
|
|
1455
|
+
if (!Node4.isIdentifier(expr)) return void 0;
|
|
1647
1456
|
const decl = expr.getDefinitions().map((d) => d.getDeclarationNode()).find(
|
|
1648
|
-
(n) => n !== void 0 &&
|
|
1457
|
+
(n) => n !== void 0 && Node4.isVariableDeclaration(n)
|
|
1649
1458
|
);
|
|
1650
1459
|
const init = decl?.getInitializer();
|
|
1651
1460
|
if (!init) return void 0;
|
|
1652
1461
|
const unwrapped = unwrapExpression(init);
|
|
1653
|
-
return
|
|
1462
|
+
return Node4.isCallExpression(unwrapped) ? unwrapped : void 0;
|
|
1654
1463
|
}
|
|
1655
1464
|
function unwrapExpression(node) {
|
|
1656
1465
|
let current = node;
|
|
1657
|
-
while (
|
|
1466
|
+
while (Node4.isAsExpression(current) || Node4.isSatisfiesExpression(current) || Node4.isParenthesizedExpression(current) || Node4.isTypeAssertion(current)) {
|
|
1658
1467
|
current = current.getExpression();
|
|
1659
1468
|
}
|
|
1660
1469
|
return current;
|
|
@@ -1662,17 +1471,17 @@ function unwrapExpression(node) {
|
|
|
1662
1471
|
function resolveReturnedClass(factoryDecl) {
|
|
1663
1472
|
const body = factoryDecl.getBody();
|
|
1664
1473
|
if (!body) return void 0;
|
|
1665
|
-
const returns = body.getDescendants().filter((n) =>
|
|
1474
|
+
const returns = body.getDescendants().filter((n) => Node4.isReturnStatement(n));
|
|
1666
1475
|
for (const ret of returns) {
|
|
1667
1476
|
const expr = ret.getExpression();
|
|
1668
1477
|
if (!expr) continue;
|
|
1669
1478
|
const inner = unwrapExpression(expr);
|
|
1670
|
-
if (
|
|
1479
|
+
if (Node4.isClassExpression(inner)) {
|
|
1671
1480
|
return inner;
|
|
1672
1481
|
}
|
|
1673
|
-
if (
|
|
1482
|
+
if (Node4.isIdentifier(inner)) {
|
|
1674
1483
|
const name = inner.getText();
|
|
1675
|
-
const decl = body.getDescendants().find((n) =>
|
|
1484
|
+
const decl = body.getDescendants().find((n) => Node4.isClassDeclaration(n) && n.getName() === name);
|
|
1676
1485
|
if (decl) return decl;
|
|
1677
1486
|
}
|
|
1678
1487
|
}
|
|
@@ -1680,10 +1489,10 @@ function resolveReturnedClass(factoryDecl) {
|
|
|
1680
1489
|
}
|
|
1681
1490
|
function factoryCalleeName(call) {
|
|
1682
1491
|
const callee = unwrapExpression(call.getExpression());
|
|
1683
|
-
if (
|
|
1684
|
-
if (
|
|
1492
|
+
if (Node4.isIdentifier(callee)) return callee;
|
|
1493
|
+
if (Node4.isPropertyAccessExpression(callee)) {
|
|
1685
1494
|
const name = callee.getNameNode();
|
|
1686
|
-
return
|
|
1495
|
+
return Node4.isIdentifier(name) ? name : void 0;
|
|
1687
1496
|
}
|
|
1688
1497
|
return void 0;
|
|
1689
1498
|
}
|
|
@@ -1696,19 +1505,19 @@ function resolveFactoryFromName(name, seen) {
|
|
|
1696
1505
|
seen.add(name);
|
|
1697
1506
|
const decls = name.getDefinitions().map((d) => d.getDeclarationNode()).filter((n) => n !== void 0);
|
|
1698
1507
|
for (const decl of decls) {
|
|
1699
|
-
if (
|
|
1700
|
-
if (
|
|
1508
|
+
if (Node4.isFunctionDeclaration(decl) || Node4.isMethodDeclaration(decl)) return decl;
|
|
1509
|
+
if (Node4.isPropertyAssignment(decl)) {
|
|
1701
1510
|
const init = decl.getInitializer();
|
|
1702
1511
|
const inner = init ? unwrapExpression(init) : void 0;
|
|
1703
|
-
if (inner &&
|
|
1512
|
+
if (inner && Node4.isIdentifier(inner)) {
|
|
1704
1513
|
const resolved = resolveFactoryFromName(inner, seen);
|
|
1705
1514
|
if (resolved) return resolved;
|
|
1706
1515
|
}
|
|
1707
1516
|
continue;
|
|
1708
1517
|
}
|
|
1709
|
-
if (
|
|
1518
|
+
if (Node4.isShorthandPropertyAssignment(decl)) {
|
|
1710
1519
|
const nameNode = decl.getNameNode();
|
|
1711
|
-
const resolved =
|
|
1520
|
+
const resolved = Node4.isIdentifier(nameNode) ? resolveFactoryFromName(nameNode, seen) : void 0;
|
|
1712
1521
|
if (resolved) return resolved;
|
|
1713
1522
|
}
|
|
1714
1523
|
}
|
|
@@ -1720,7 +1529,7 @@ function warnUnresolvedFactory(cls, calleeText, reason) {
|
|
|
1720
1529
|
);
|
|
1721
1530
|
}
|
|
1722
1531
|
function resolveFactoryStaticClass(node) {
|
|
1723
|
-
if (!
|
|
1532
|
+
if (!Node4.isPropertyAccessExpression(node)) return void 0;
|
|
1724
1533
|
const call = resolveHeritageCall(node.getExpression());
|
|
1725
1534
|
if (!call) return void 0;
|
|
1726
1535
|
const factoryDecl = resolveFactoryDeclaration(call);
|
|
@@ -1728,13 +1537,13 @@ function resolveFactoryStaticClass(node) {
|
|
|
1728
1537
|
const returnedClass = resolveReturnedClass(factoryDecl);
|
|
1729
1538
|
if (!returnedClass) return void 0;
|
|
1730
1539
|
const staticProp = returnedClass.getStaticProperties().find((p) => p.getName() === node.getName());
|
|
1731
|
-
if (!staticProp || !
|
|
1540
|
+
if (!staticProp || !Node4.isPropertyDeclaration(staticProp)) return void 0;
|
|
1732
1541
|
const init = staticProp.getInitializer();
|
|
1733
1542
|
if (!init) return void 0;
|
|
1734
1543
|
const inner = unwrapExpression(init);
|
|
1735
|
-
if (
|
|
1736
|
-
if (!
|
|
1737
|
-
return inner.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 &&
|
|
1544
|
+
if (Node4.isClassExpression(inner)) return inner;
|
|
1545
|
+
if (!Node4.isIdentifier(inner)) return void 0;
|
|
1546
|
+
return inner.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 && Node4.isClassDeclaration(n));
|
|
1738
1547
|
}
|
|
1739
1548
|
function resolveBaseClass(cls) {
|
|
1740
1549
|
const heritage = cls.getExtends()?.getExpression();
|
|
@@ -1745,8 +1554,8 @@ function resolveBaseClass(cls) {
|
|
|
1745
1554
|
return factoryDecl ? resolveReturnedClass(factoryDecl) : void 0;
|
|
1746
1555
|
}
|
|
1747
1556
|
const expr = unwrapExpression(heritage);
|
|
1748
|
-
if (!
|
|
1749
|
-
return expr.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 &&
|
|
1557
|
+
if (!Node4.isIdentifier(expr)) return void 0;
|
|
1558
|
+
return expr.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 && Node4.isClassDeclaration(n));
|
|
1750
1559
|
}
|
|
1751
1560
|
function collectMethodsThroughChain(returnedClass) {
|
|
1752
1561
|
const byName = /* @__PURE__ */ new Map();
|
|
@@ -1793,7 +1602,7 @@ function resolveInheritedMethods(cls) {
|
|
|
1793
1602
|
classArgs.push(positional);
|
|
1794
1603
|
continue;
|
|
1795
1604
|
}
|
|
1796
|
-
if (!
|
|
1605
|
+
if (!Node4.isObjectLiteralExpression(arg)) continue;
|
|
1797
1606
|
for (const prop of arg.getProperties()) {
|
|
1798
1607
|
const named = resolvePropertyClassReference(prop);
|
|
1799
1608
|
if (!named) continue;
|
|
@@ -1815,8 +1624,8 @@ function resolveInheritedMethods(cls) {
|
|
|
1815
1624
|
}
|
|
1816
1625
|
function resolveClassReference(node) {
|
|
1817
1626
|
const expr = unwrapExpression(node);
|
|
1818
|
-
if (!
|
|
1819
|
-
const decl = expr.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 &&
|
|
1627
|
+
if (!Node4.isIdentifier(expr)) return void 0;
|
|
1628
|
+
const decl = expr.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 && Node4.isClassDeclaration(n));
|
|
1820
1629
|
if (!decl) return void 0;
|
|
1821
1630
|
return {
|
|
1822
1631
|
name: decl.getName() ?? expr.getText(),
|
|
@@ -1824,29 +1633,56 @@ function resolveClassReference(node) {
|
|
|
1824
1633
|
};
|
|
1825
1634
|
}
|
|
1826
1635
|
function resolvePropertyClassReference(prop) {
|
|
1827
|
-
if (
|
|
1636
|
+
if (Node4.isShorthandPropertyAssignment(prop)) {
|
|
1828
1637
|
const ref2 = resolveClassReference(prop.getNameNode());
|
|
1829
1638
|
return ref2 ? { key: prop.getName(), ref: ref2 } : void 0;
|
|
1830
1639
|
}
|
|
1831
|
-
if (!
|
|
1640
|
+
if (!Node4.isPropertyAssignment(prop)) return void 0;
|
|
1832
1641
|
const init = prop.getInitializer();
|
|
1833
1642
|
if (!init) return void 0;
|
|
1834
1643
|
const ref = resolveClassReference(init);
|
|
1835
1644
|
if (!ref) return void 0;
|
|
1836
1645
|
const nameNode = prop.getNameNode();
|
|
1837
|
-
const key =
|
|
1646
|
+
const key = Node4.isStringLiteral(nameNode) ? nameNode.getLiteralValue() : prop.getName();
|
|
1838
1647
|
return { key, ref };
|
|
1839
1648
|
}
|
|
1840
1649
|
var mixinTypeProject;
|
|
1650
|
+
var mixinFiles = new DiskFingerprints();
|
|
1651
|
+
var mixinStale = false;
|
|
1652
|
+
var mixinUnresolved = new UnresolvedImports();
|
|
1841
1653
|
function getMixinTypeProject() {
|
|
1842
|
-
mixinTypeProject
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1654
|
+
if (mixinTypeProject && mixinStale && !refreshMixinTypeProject(mixinTypeProject)) {
|
|
1655
|
+
mixinTypeProject = void 0;
|
|
1656
|
+
}
|
|
1657
|
+
mixinStale = false;
|
|
1658
|
+
if (!mixinTypeProject) {
|
|
1659
|
+
mixinTypeProject = new Project({
|
|
1660
|
+
skipAddingFilesFromTsConfig: true,
|
|
1661
|
+
compilerOptions: { strict: true }
|
|
1662
|
+
});
|
|
1663
|
+
mixinFiles = new DiskFingerprints();
|
|
1664
|
+
mixinUnresolved = new UnresolvedImports();
|
|
1665
|
+
}
|
|
1846
1666
|
return mixinTypeProject;
|
|
1847
1667
|
}
|
|
1848
1668
|
function clearMixinTypeProject() {
|
|
1849
1669
|
mixinTypeProject = void 0;
|
|
1670
|
+
mixinStale = false;
|
|
1671
|
+
}
|
|
1672
|
+
function invalidateMixinTypeProject() {
|
|
1673
|
+
mixinStale = true;
|
|
1674
|
+
}
|
|
1675
|
+
function refreshMixinTypeProject(project) {
|
|
1676
|
+
const program = project.getProgram().compilerObject;
|
|
1677
|
+
const files = program.getSourceFiles().filter((file) => !program.isSourceFileDefaultLibrary(file)).map((file) => ({ path: file.fileName, text: file.text }));
|
|
1678
|
+
const changed = mixinFiles.changed(files);
|
|
1679
|
+
if (changed.length === 0) return mixinUnresolved.nowResolving(program).length === 0;
|
|
1680
|
+
for (const path of changed) {
|
|
1681
|
+
const sf = project.getSourceFile(path);
|
|
1682
|
+
if (sf?.refreshFromFileSystemSync() !== FileSystemRefreshResult.Updated) return false;
|
|
1683
|
+
}
|
|
1684
|
+
project.resolveSourceFileDependencies();
|
|
1685
|
+
return true;
|
|
1850
1686
|
}
|
|
1851
1687
|
function resolveInstantiatedReturnType(cls, methodName) {
|
|
1852
1688
|
const filePath = cls.getSourceFile().getFilePath();
|
|
@@ -1877,7 +1713,7 @@ function resolveClassRef(ref, project) {
|
|
|
1877
1713
|
return file?.getClass(ref.name);
|
|
1878
1714
|
}
|
|
1879
1715
|
function classifyFilterForHint(typeInit) {
|
|
1880
|
-
if (
|
|
1716
|
+
if (Node5.isStringLiteral(typeInit)) {
|
|
1881
1717
|
switch (typeInit.getLiteralValue()) {
|
|
1882
1718
|
case "string":
|
|
1883
1719
|
return { kind: "string" };
|
|
@@ -1891,10 +1727,10 @@ function classifyFilterForHint(typeInit) {
|
|
|
1891
1727
|
return null;
|
|
1892
1728
|
}
|
|
1893
1729
|
}
|
|
1894
|
-
if (
|
|
1730
|
+
if (Node5.isArrayLiteralExpression(typeInit)) {
|
|
1895
1731
|
const values = [];
|
|
1896
1732
|
for (const el of typeInit.getElements()) {
|
|
1897
|
-
if (!
|
|
1733
|
+
if (!Node5.isStringLiteral(el)) return null;
|
|
1898
1734
|
values.push(el.getLiteralValue());
|
|
1899
1735
|
}
|
|
1900
1736
|
if (values.length === 0) return null;
|
|
@@ -1929,11 +1765,11 @@ function extractFilterForHints(classDecl, project) {
|
|
|
1929
1765
|
if (!filterForDec) continue;
|
|
1930
1766
|
const args = filterForDec.getArguments();
|
|
1931
1767
|
const keyArg = args[0];
|
|
1932
|
-
const inputKey = keyArg &&
|
|
1768
|
+
const inputKey = keyArg && Node5.isStringLiteral(keyArg) ? keyArg.getLiteralValue() : method.getName();
|
|
1933
1769
|
const optsArg = args[1];
|
|
1934
|
-
if (optsArg &&
|
|
1770
|
+
if (optsArg && Node5.isObjectLiteralExpression(optsArg)) {
|
|
1935
1771
|
const typeProp = optsArg.getProperty("type");
|
|
1936
|
-
if (typeProp &&
|
|
1772
|
+
if (typeProp && Node5.isPropertyAssignment(typeProp)) {
|
|
1937
1773
|
const typeInit = typeProp.getInitializer();
|
|
1938
1774
|
if (typeInit) {
|
|
1939
1775
|
const classified = classifyFilterForHint(typeInit);
|
|
@@ -1960,14 +1796,14 @@ function extractApplyFilterInfo(method, sourceFile, project, mixin, controllerCl
|
|
|
1960
1796
|
const filterClassArg = args[0];
|
|
1961
1797
|
if (!filterClassArg) continue;
|
|
1962
1798
|
const factoryStaticClass = resolveFactoryStaticClass(filterClassArg);
|
|
1963
|
-
if (!factoryStaticClass && !
|
|
1799
|
+
if (!factoryStaticClass && !Node5.isIdentifier(filterClassArg)) continue;
|
|
1964
1800
|
let source = "query";
|
|
1965
1801
|
const optionsArg = args[1];
|
|
1966
|
-
if (optionsArg &&
|
|
1802
|
+
if (optionsArg && Node5.isObjectLiteralExpression(optionsArg)) {
|
|
1967
1803
|
const sourceProp = optionsArg.getProperty("source");
|
|
1968
|
-
if (sourceProp &&
|
|
1804
|
+
if (sourceProp && Node5.isPropertyAssignment(sourceProp)) {
|
|
1969
1805
|
const init = sourceProp.getInitializer();
|
|
1970
|
-
if (init &&
|
|
1806
|
+
if (init && Node5.isStringLiteral(init) && init.getLiteralValue() === "body") {
|
|
1971
1807
|
source = "body";
|
|
1972
1808
|
}
|
|
1973
1809
|
}
|
|
@@ -1976,7 +1812,7 @@ function extractApplyFilterInfo(method, sourceFile, project, mixin, controllerCl
|
|
|
1976
1812
|
const resolved = factoryStaticClass ? void 0 : findType(filterClassName, declFile, project);
|
|
1977
1813
|
const declaredClass = factoryStaticClass ?? (resolved?.kind === "class" ? resolved.decl : resolveLocalClassDeclaration(filterClassArg));
|
|
1978
1814
|
const ownRoute = !mixin || (controllerClass ? method.getParent() === controllerClass : declFile.getFilePath() !== mixin.factoryFilePath);
|
|
1979
|
-
const namedByRoute = ownRoute &&
|
|
1815
|
+
const namedByRoute = ownRoute && Node5.isIdentifier(filterClassArg) ? declaredClass : void 0;
|
|
1980
1816
|
for (const candidate of orderFilterCandidates({
|
|
1981
1817
|
namedByRoute,
|
|
1982
1818
|
supplied: resolveMixinFilterClass(mixin, project),
|
|
@@ -2054,22 +1890,22 @@ function resolveRelationEntity(prop, sourceFile, project) {
|
|
|
2054
1890
|
const args = dec.getArguments();
|
|
2055
1891
|
if (args.length === 0) continue;
|
|
2056
1892
|
const arg = args[0];
|
|
2057
|
-
if (
|
|
1893
|
+
if (Node5.isObjectLiteralExpression(arg)) {
|
|
2058
1894
|
const entityProp = arg.getProperty("entity");
|
|
2059
|
-
if (entityProp &&
|
|
1895
|
+
if (entityProp && Node5.isPropertyAssignment(entityProp)) {
|
|
2060
1896
|
const init = entityProp.getInitializer();
|
|
2061
|
-
if (init &&
|
|
1897
|
+
if (init && Node5.isArrowFunction(init)) {
|
|
2062
1898
|
const body = init.getBody();
|
|
2063
|
-
if (
|
|
1899
|
+
if (Node5.isIdentifier(body)) {
|
|
2064
1900
|
const resolved = findType(body.getText(), prop.getSourceFile(), project);
|
|
2065
1901
|
if (resolved?.kind === "class") return resolved.decl;
|
|
2066
1902
|
}
|
|
2067
1903
|
}
|
|
2068
1904
|
}
|
|
2069
1905
|
}
|
|
2070
|
-
if (
|
|
1906
|
+
if (Node5.isArrowFunction(arg)) {
|
|
2071
1907
|
const body = arg.getBody();
|
|
2072
|
-
if (
|
|
1908
|
+
if (Node5.isIdentifier(body)) {
|
|
2073
1909
|
const resolved = findType(body.getText(), prop.getSourceFile(), project);
|
|
2074
1910
|
if (resolved?.kind === "class") return resolved.decl;
|
|
2075
1911
|
}
|
|
@@ -2088,36 +1924,36 @@ function extractClassPropertyTypes(classDecl, project) {
|
|
|
2088
1924
|
return fields;
|
|
2089
1925
|
}
|
|
2090
1926
|
function resolveLocalClassDeclaration(identifier) {
|
|
2091
|
-
if (!
|
|
2092
|
-
return identifier.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 &&
|
|
1927
|
+
if (!Node5.isIdentifier(identifier)) return void 0;
|
|
1928
|
+
return identifier.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 && Node5.isClassDeclaration(n));
|
|
2093
1929
|
}
|
|
2094
1930
|
function resolveDeclaredEntity(optionsArg, filterClass, project) {
|
|
2095
|
-
if (!
|
|
1931
|
+
if (!Node5.isObjectLiteralExpression(optionsArg)) return void 0;
|
|
2096
1932
|
const entityProp = optionsArg.getProperty("entity");
|
|
2097
|
-
if (!entityProp || !
|
|
1933
|
+
if (!entityProp || !Node5.isPropertyAssignment(entityProp)) return void 0;
|
|
2098
1934
|
const entityInit = entityProp.getInitializer();
|
|
2099
|
-
if (!entityInit || !
|
|
1935
|
+
if (!entityInit || !Node5.isIdentifier(entityInit)) return void 0;
|
|
2100
1936
|
const resolved = findType(entityInit.getText(), filterClass.getSourceFile(), project);
|
|
2101
1937
|
if (!resolved || resolved.kind !== "class") return void 0;
|
|
2102
1938
|
return resolved.decl;
|
|
2103
1939
|
}
|
|
2104
1940
|
function readFieldNameList(optionsArg, key) {
|
|
2105
|
-
if (!
|
|
1941
|
+
if (!Node5.isObjectLiteralExpression(optionsArg)) return void 0;
|
|
2106
1942
|
const prop = optionsArg.getProperty(key);
|
|
2107
|
-
if (!prop || !
|
|
1943
|
+
if (!prop || !Node5.isPropertyAssignment(prop)) return void 0;
|
|
2108
1944
|
const init = prop.getInitializer();
|
|
2109
|
-
if (!init || !
|
|
1945
|
+
if (!init || !Node5.isArrayLiteralExpression(init)) return void 0;
|
|
2110
1946
|
const names = /* @__PURE__ */ new Set();
|
|
2111
1947
|
for (const el of init.getElements()) {
|
|
2112
|
-
if (
|
|
1948
|
+
if (Node5.isStringLiteral(el)) {
|
|
2113
1949
|
names.add(el.getLiteralValue());
|
|
2114
1950
|
continue;
|
|
2115
1951
|
}
|
|
2116
|
-
if (
|
|
1952
|
+
if (Node5.isObjectLiteralExpression(el)) {
|
|
2117
1953
|
const fieldProp = el.getProperty("field");
|
|
2118
|
-
if (fieldProp &&
|
|
1954
|
+
if (fieldProp && Node5.isPropertyAssignment(fieldProp)) {
|
|
2119
1955
|
const fieldInit = fieldProp.getInitializer();
|
|
2120
|
-
if (fieldInit &&
|
|
1956
|
+
if (fieldInit && Node5.isStringLiteral(fieldInit)) {
|
|
2121
1957
|
names.add(fieldInit.getLiteralValue());
|
|
2122
1958
|
continue;
|
|
2123
1959
|
}
|
|
@@ -2141,7 +1977,7 @@ function extractFilterableEntityFields(filterClass, project, entityOverride, pre
|
|
|
2141
1977
|
const args = filterableDecorator.getArguments();
|
|
2142
1978
|
if (args.length === 0) return [];
|
|
2143
1979
|
const optionsArg = args[0];
|
|
2144
|
-
if (!
|
|
1980
|
+
if (!Node5.isObjectLiteralExpression(optionsArg)) return [];
|
|
2145
1981
|
const declaredEntity = resolveDeclaredEntity(optionsArg, filterClass, project);
|
|
2146
1982
|
const entityDecl = preferDeclaredEntity ? declaredEntity ?? entityOverride : entityOverride ?? declaredEntity;
|
|
2147
1983
|
if (!entityDecl) return [];
|
|
@@ -2152,17 +1988,17 @@ function extractFilterableEntityFields(filterClass, project, entityOverride, pre
|
|
|
2152
1988
|
const relationsDecorator = filterClass.getDecorators().find((d) => d.getName() === "Relations");
|
|
2153
1989
|
if (relationsDecorator) {
|
|
2154
1990
|
const relArgs = relationsDecorator.getArguments();
|
|
2155
|
-
if (relArgs.length > 0 &&
|
|
1991
|
+
if (relArgs.length > 0 && Node5.isObjectLiteralExpression(relArgs[0])) {
|
|
2156
1992
|
for (const relProp of relArgs[0].getProperties()) {
|
|
2157
|
-
if (!
|
|
1993
|
+
if (!Node5.isPropertyAssignment(relProp)) continue;
|
|
2158
1994
|
const relInit = relProp.getInitializer();
|
|
2159
|
-
if (!relInit || !
|
|
1995
|
+
if (!relInit || !Node5.isObjectLiteralExpression(relInit)) continue;
|
|
2160
1996
|
const keysProp = relInit.getProperty("keys");
|
|
2161
|
-
if (!keysProp || !
|
|
1997
|
+
if (!keysProp || !Node5.isPropertyAssignment(keysProp)) continue;
|
|
2162
1998
|
const keysInit = keysProp.getInitializer();
|
|
2163
|
-
if (!keysInit || !
|
|
1999
|
+
if (!keysInit || !Node5.isArrayLiteralExpression(keysInit)) continue;
|
|
2164
2000
|
for (const el of keysInit.getElements()) {
|
|
2165
|
-
if (
|
|
2001
|
+
if (Node5.isStringLiteral(el)) {
|
|
2166
2002
|
fields.push(toFilterFieldType(el.getLiteralValue(), { kind: "unknown" }));
|
|
2167
2003
|
}
|
|
2168
2004
|
}
|
|
@@ -2173,15 +2009,15 @@ function extractFilterableEntityFields(filterClass, project, entityOverride, pre
|
|
|
2173
2009
|
}
|
|
2174
2010
|
|
|
2175
2011
|
// src/discovery/pipe-schema.ts
|
|
2176
|
-
import { Node as
|
|
2012
|
+
import { Node as Node8 } from "ts-morph";
|
|
2177
2013
|
|
|
2178
2014
|
// src/discovery/schema-type-inference.ts
|
|
2179
2015
|
import {
|
|
2180
|
-
ts as
|
|
2016
|
+
ts as ts3
|
|
2181
2017
|
} from "ts-morph";
|
|
2182
2018
|
|
|
2183
2019
|
// src/discovery/schema-type-program.ts
|
|
2184
|
-
import { Node as
|
|
2020
|
+
import { FileSystemRefreshResult as FileSystemRefreshResult2, Node as Node6, Project as Project2, ts as ts2 } from "ts-morph";
|
|
2185
2021
|
var PARAM_DECORATORS = /* @__PURE__ */ new Set(["Body", "Query", "Param"]);
|
|
2186
2022
|
var SchemaTypeProgram = class {
|
|
2187
2023
|
constructor(discovery) {
|
|
@@ -2191,10 +2027,10 @@ var SchemaTypeProgram = class {
|
|
|
2191
2027
|
...base,
|
|
2192
2028
|
// A tsconfig-less consumer gets ES5 by default, whose lib has no `Map`/`Set`/
|
|
2193
2029
|
// `Promise` for the schema libraries' declarations to lean on.
|
|
2194
|
-
target: base.target ??
|
|
2030
|
+
target: base.target ?? ts2.ScriptTarget.ES2022,
|
|
2195
2031
|
// With neither set, resolve the way a bundler does: package `exports`
|
|
2196
2032
|
// (`zod/v4`) and extensionless or `.js` relative imports all resolve.
|
|
2197
|
-
...base.module === void 0 && base.moduleResolution === void 0 ? { module:
|
|
2033
|
+
...base.module === void 0 && base.moduleResolution === void 0 ? { module: ts2.ModuleKind.ESNext, moduleResolution: ts2.ModuleResolutionKind.Bundler } : {},
|
|
2198
2034
|
// Without it `.nullable()` and `.optional()` would erase to the bare type.
|
|
2199
2035
|
strictNullChecks: true,
|
|
2200
2036
|
// No ambient `@types/*` (a tsconfig's `types: ['node']`): a schema does not
|
|
@@ -2202,17 +2038,16 @@ var SchemaTypeProgram = class {
|
|
|
2202
2038
|
types: [],
|
|
2203
2039
|
noEmit: true
|
|
2204
2040
|
};
|
|
2205
|
-
this.project = new
|
|
2041
|
+
this.project = new Project2({
|
|
2206
2042
|
skipAddingFilesFromTsConfig: true,
|
|
2207
2043
|
compilerOptions: this.options
|
|
2208
2044
|
});
|
|
2209
|
-
|
|
2210
|
-
this.flush();
|
|
2045
|
+
this.requestDiscoveryFiles();
|
|
2211
2046
|
}
|
|
2212
2047
|
project;
|
|
2213
2048
|
options;
|
|
2214
2049
|
/** Parses a file that is not in discovery's Project, without adding it there. */
|
|
2215
|
-
scratch = new
|
|
2050
|
+
scratch = new Project2({
|
|
2216
2051
|
skipAddingFilesFromTsConfig: true,
|
|
2217
2052
|
skipLoadingLibFiles: true,
|
|
2218
2053
|
skipFileDependencyResolution: true
|
|
@@ -2220,6 +2055,53 @@ var SchemaTypeProgram = class {
|
|
|
2220
2055
|
needs = /* @__PURE__ */ new Map();
|
|
2221
2056
|
/** Pruned text per file, as last written into {@link project}. */
|
|
2222
2057
|
written = /* @__PURE__ */ new Map();
|
|
2058
|
+
/** What {@link project} read from disk unpruned (packages, declaration files). */
|
|
2059
|
+
externalFiles = new DiskFingerprints();
|
|
2060
|
+
scratchFiles = new DiskFingerprints();
|
|
2061
|
+
/** The last pruning of each file, with the source text and needs it was pruned for. */
|
|
2062
|
+
pruned = /* @__PURE__ */ new Map();
|
|
2063
|
+
/**
|
|
2064
|
+
* Bring the program up to date with discovery's files and the disk, keeping the
|
|
2065
|
+
* lib and package declarations it has already parsed and checked. Leaves it
|
|
2066
|
+
* holding exactly the files a new program built now would hold, with the same
|
|
2067
|
+
* text. Returns false when a file the program read unpruned has changed on disk
|
|
2068
|
+
* (a package was reinstalled, a `.d.ts` was edited): the caller builds a new
|
|
2069
|
+
* program instead.
|
|
2070
|
+
*/
|
|
2071
|
+
refresh() {
|
|
2072
|
+
const program = this.project.getProgram().compilerObject;
|
|
2073
|
+
const external = program.getSourceFiles().filter(
|
|
2074
|
+
(file) => !program.isSourceFileDefaultLibrary(file) && !this.written.has(file.fileName)
|
|
2075
|
+
).map((file) => ({ path: file.fileName, text: file.text }));
|
|
2076
|
+
if (this.externalFiles.changed(external).length > 0) return false;
|
|
2077
|
+
const scratch = this.scratch.getSourceFiles().map((sf) => ({ path: sf.getFilePath(), text: sf.getFullText() }));
|
|
2078
|
+
for (const path of this.scratchFiles.changed(scratch)) {
|
|
2079
|
+
const sf = this.scratch.getSourceFile(path);
|
|
2080
|
+
if (sf?.refreshFromFileSystemSync() === FileSystemRefreshResult2.Deleted) {
|
|
2081
|
+
this.scratchFiles.forget(path);
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
this.needs.clear();
|
|
2085
|
+
this.requestDiscoveryFiles();
|
|
2086
|
+
for (const path of [...this.written.keys()]) {
|
|
2087
|
+
if (this.needs.has(path)) continue;
|
|
2088
|
+
const sf = this.project.getSourceFile(path);
|
|
2089
|
+
if (sf) this.project.removeSourceFile(sf);
|
|
2090
|
+
this.written.delete(path);
|
|
2091
|
+
}
|
|
2092
|
+
return true;
|
|
2093
|
+
}
|
|
2094
|
+
/**
|
|
2095
|
+
* Everything discovery holds now (the controllers) goes in before the first
|
|
2096
|
+
* query: a file added afterwards discards the program and its checker, and
|
|
2097
|
+
* rebuilding them once per controller is exactly the cost this avoids.
|
|
2098
|
+
*/
|
|
2099
|
+
requestDiscoveryFiles() {
|
|
2100
|
+
for (const sf of this.discovery.getSourceFiles()) {
|
|
2101
|
+
this.request(sf.getFilePath(), { schema: true });
|
|
2102
|
+
}
|
|
2103
|
+
this.flush();
|
|
2104
|
+
}
|
|
2223
2105
|
/**
|
|
2224
2106
|
* The typed copy of a discovery file, with its schema positions kept. Adding one
|
|
2225
2107
|
* that was not there yet (a contract file discovery reached lazily) rebuilds the
|
|
@@ -2260,12 +2142,21 @@ var SchemaTypeProgram = class {
|
|
|
2260
2142
|
while (this.queue.length > 0) {
|
|
2261
2143
|
const path = this.queue.pop();
|
|
2262
2144
|
const original = this.original(path);
|
|
2263
|
-
if (!original)
|
|
2264
|
-
|
|
2265
|
-
|
|
2145
|
+
if (!original) {
|
|
2146
|
+
if (this.written.has(path)) changed.set(path, void 0);
|
|
2147
|
+
continue;
|
|
2148
|
+
}
|
|
2149
|
+
const { text, requests } = this.pruneCached(path, original, this.needs.get(path));
|
|
2150
|
+
for (const { path: target, want } of requests) this.request(target, want);
|
|
2266
2151
|
if (this.written.get(path) !== text) changed.set(path, text);
|
|
2267
2152
|
}
|
|
2268
2153
|
for (const [path, text] of changed) {
|
|
2154
|
+
if (text === void 0) {
|
|
2155
|
+
const sf = this.project.getSourceFile(path);
|
|
2156
|
+
if (sf) this.project.removeSourceFile(sf);
|
|
2157
|
+
this.written.delete(path);
|
|
2158
|
+
continue;
|
|
2159
|
+
}
|
|
2269
2160
|
this.project.createSourceFile(path, text, { overwrite: true });
|
|
2270
2161
|
this.written.set(path, text);
|
|
2271
2162
|
}
|
|
@@ -2274,11 +2165,29 @@ var SchemaTypeProgram = class {
|
|
|
2274
2165
|
original(path) {
|
|
2275
2166
|
return this.discovery.getSourceFile(path) ?? this.scratch.getSourceFile(path) ?? this.scratch.addSourceFileAtPathIfExists(path);
|
|
2276
2167
|
}
|
|
2168
|
+
/** {@link prune}, reusing the last result while the file's text and needs are the same. */
|
|
2169
|
+
pruneCached(path, sf, needs) {
|
|
2170
|
+
const source = sf.getFullText();
|
|
2171
|
+
const key = `${needs.schema}:${needs.all}:${[...needs.names].sort().join(",")}`;
|
|
2172
|
+
const last = this.pruned.get(path);
|
|
2173
|
+
if (last && last.source === source && last.needs === key) return last;
|
|
2174
|
+
const result = this.prune(sf, needs);
|
|
2175
|
+
if (result.resolved) this.pruned.set(path, { ...result, source, needs: key });
|
|
2176
|
+
else this.pruned.delete(path);
|
|
2177
|
+
return result;
|
|
2178
|
+
}
|
|
2277
2179
|
/**
|
|
2278
2180
|
* `sf`'s text with every import/re-export nothing in `needs` references blanked,
|
|
2279
|
-
*
|
|
2181
|
+
* and the names each kept one brings in from its target.
|
|
2280
2182
|
*/
|
|
2281
2183
|
prune(sf, needs) {
|
|
2184
|
+
const requests = [];
|
|
2185
|
+
let resolved = true;
|
|
2186
|
+
const resolveFrom = (specifier) => {
|
|
2187
|
+
const target = this.resolve(specifier, sf);
|
|
2188
|
+
if (target === null) resolved = false;
|
|
2189
|
+
return target ?? void 0;
|
|
2190
|
+
};
|
|
2282
2191
|
const locals = topLevelDeclarations(sf);
|
|
2283
2192
|
const roots = [];
|
|
2284
2193
|
if (needs.all) roots.push(...sf.getStatements());
|
|
@@ -2312,7 +2221,7 @@ var SchemaTypeProgram = class {
|
|
|
2312
2221
|
const seen = new Set(queue);
|
|
2313
2222
|
while (queue.length > 0) {
|
|
2314
2223
|
const node = queue.pop();
|
|
2315
|
-
const ids =
|
|
2224
|
+
const ids = Node6.isIdentifier(node) ? [node] : node.getDescendantsOfKind(ts2.SyntaxKind.Identifier);
|
|
2316
2225
|
for (const id of ids) {
|
|
2317
2226
|
const name = id.getText();
|
|
2318
2227
|
if (referenced.has(name)) continue;
|
|
@@ -2331,7 +2240,7 @@ var SchemaTypeProgram = class {
|
|
|
2331
2240
|
}
|
|
2332
2241
|
};
|
|
2333
2242
|
for (const decl of sf.getImportDeclarations()) {
|
|
2334
|
-
const target =
|
|
2243
|
+
const target = resolveFrom(decl.getModuleSpecifierValue());
|
|
2335
2244
|
const namespace = decl.getNamespaceImport()?.getText();
|
|
2336
2245
|
const def = decl.getDefaultImport()?.getText();
|
|
2337
2246
|
const named = decl.getNamedImports().map((n) => ({
|
|
@@ -2345,21 +2254,24 @@ var SchemaTypeProgram = class {
|
|
|
2345
2254
|
continue;
|
|
2346
2255
|
}
|
|
2347
2256
|
if (target) {
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2257
|
+
requests.push({
|
|
2258
|
+
path: target,
|
|
2259
|
+
want: {
|
|
2260
|
+
all: wantsNamespace,
|
|
2261
|
+
names: [...wantsDefault ? ["default"] : [], ...named.map((n) => n.imported)]
|
|
2262
|
+
}
|
|
2351
2263
|
});
|
|
2352
2264
|
}
|
|
2353
2265
|
}
|
|
2354
2266
|
for (const exp of sf.getExportDeclarations()) {
|
|
2355
2267
|
const spec = exp.getModuleSpecifierValue();
|
|
2356
2268
|
if (spec === void 0) continue;
|
|
2357
|
-
const target =
|
|
2269
|
+
const target = resolveFrom(spec);
|
|
2358
2270
|
if (exp.isNamespaceExport() || exp.getNamedExports().length === 0) {
|
|
2359
2271
|
if (!needs.all && unresolvedNames.length === 0) {
|
|
2360
2272
|
blank(exp);
|
|
2361
2273
|
} else if (target) {
|
|
2362
|
-
|
|
2274
|
+
requests.push({ path: target, want: { all: needs.all, names: unresolvedNames } });
|
|
2363
2275
|
}
|
|
2364
2276
|
continue;
|
|
2365
2277
|
}
|
|
@@ -2367,22 +2279,24 @@ var SchemaTypeProgram = class {
|
|
|
2367
2279
|
(n) => needs.all || needs.names.has((n.getAliasNode() ?? n.getNameNode()).getText())
|
|
2368
2280
|
).map((n) => n.getNameNode().getText());
|
|
2369
2281
|
if (wanted.length === 0) blank(exp);
|
|
2370
|
-
else if (target)
|
|
2282
|
+
else if (target) requests.push({ path: target, want: { names: wanted } });
|
|
2371
2283
|
}
|
|
2372
|
-
return chars.join("");
|
|
2284
|
+
return { text: chars.join(""), requests, resolved };
|
|
2373
2285
|
}
|
|
2374
2286
|
/**
|
|
2375
|
-
* The project source file a module specifier resolves to
|
|
2376
|
-
* package (read unpruned by the program itself)
|
|
2287
|
+
* The project source file a module specifier resolves to; undefined for a
|
|
2288
|
+
* package or a declaration file (read unpruned by the program itself), null for
|
|
2289
|
+
* one that does not resolve.
|
|
2377
2290
|
*/
|
|
2378
2291
|
resolve(specifier, from) {
|
|
2379
|
-
const resolved =
|
|
2292
|
+
const resolved = ts2.resolveModuleName(
|
|
2380
2293
|
specifier,
|
|
2381
2294
|
from.getFilePath(),
|
|
2382
2295
|
this.options,
|
|
2383
|
-
|
|
2296
|
+
ts2.sys
|
|
2384
2297
|
).resolvedModule;
|
|
2385
|
-
if (!resolved
|
|
2298
|
+
if (!resolved) return null;
|
|
2299
|
+
if (resolved.isExternalLibraryImport) return void 0;
|
|
2386
2300
|
const file = resolved.resolvedFileName;
|
|
2387
2301
|
if (file.endsWith(".d.ts") || !/\.(m|c)?tsx?$/.test(file)) return void 0;
|
|
2388
2302
|
return file;
|
|
@@ -2391,20 +2305,20 @@ var SchemaTypeProgram = class {
|
|
|
2391
2305
|
function schemaPositions(sf) {
|
|
2392
2306
|
const roots = [];
|
|
2393
2307
|
sf.forEachDescendant((node) => {
|
|
2394
|
-
if (
|
|
2308
|
+
if (Node6.isDecorator(node)) {
|
|
2395
2309
|
const name = node.getName();
|
|
2396
2310
|
if (name === "ApplyContract") {
|
|
2397
2311
|
roots.push(...node.getArguments());
|
|
2398
2312
|
} else if (PARAM_DECORATORS.has(name)) {
|
|
2399
2313
|
for (const arg of node.getArguments()) {
|
|
2400
|
-
if (
|
|
2314
|
+
if (Node6.isNewExpression(arg) || Node6.isCallExpression(arg)) {
|
|
2401
2315
|
roots.push(...arg.getArguments());
|
|
2402
2316
|
}
|
|
2403
2317
|
}
|
|
2404
2318
|
}
|
|
2405
|
-
} else if (
|
|
2319
|
+
} else if (Node6.isCallExpression(node)) {
|
|
2406
2320
|
const callee = node.getExpression();
|
|
2407
|
-
const name =
|
|
2321
|
+
const name = Node6.isIdentifier(callee) ? callee.getText() : Node6.isPropertyAccessExpression(callee) ? callee.getName() : void 0;
|
|
2408
2322
|
if (name === "defineContract") roots.push(...node.getArguments());
|
|
2409
2323
|
}
|
|
2410
2324
|
});
|
|
@@ -2437,8 +2351,13 @@ function defaultExport(sf) {
|
|
|
2437
2351
|
|
|
2438
2352
|
// src/discovery/schema-type-inference.ts
|
|
2439
2353
|
var programs = /* @__PURE__ */ new WeakMap();
|
|
2354
|
+
var stale = /* @__PURE__ */ new WeakSet();
|
|
2440
2355
|
function programFor(discovery) {
|
|
2441
2356
|
let program = programs.get(discovery);
|
|
2357
|
+
if (program && stale.has(discovery)) {
|
|
2358
|
+
stale.delete(discovery);
|
|
2359
|
+
if (!program.refresh()) program = void 0;
|
|
2360
|
+
}
|
|
2442
2361
|
if (!program) {
|
|
2443
2362
|
program = new SchemaTypeProgram(discovery);
|
|
2444
2363
|
programs.set(discovery, program);
|
|
@@ -2447,6 +2366,10 @@ function programFor(discovery) {
|
|
|
2447
2366
|
}
|
|
2448
2367
|
function clearSchemaTypeProject(discovery) {
|
|
2449
2368
|
programs.delete(discovery);
|
|
2369
|
+
stale.delete(discovery);
|
|
2370
|
+
}
|
|
2371
|
+
function invalidateSchemaTypeProject(discovery) {
|
|
2372
|
+
if (programs.has(discovery)) stale.add(discovery);
|
|
2450
2373
|
}
|
|
2451
2374
|
function counterpart(typedSf, node) {
|
|
2452
2375
|
const start = node.getStart();
|
|
@@ -2510,7 +2433,7 @@ function objectKey(name) {
|
|
|
2510
2433
|
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
|
|
2511
2434
|
}
|
|
2512
2435
|
function isOptionalSymbol(sym) {
|
|
2513
|
-
return (sym.getFlags() &
|
|
2436
|
+
return (sym.getFlags() & ts3.SymbolFlags.Optional) !== 0;
|
|
2514
2437
|
}
|
|
2515
2438
|
function isSymbolKeyed(sym) {
|
|
2516
2439
|
return sym.getName().startsWith("__@");
|
|
@@ -2671,11 +2594,11 @@ function acceptsUndefined(types) {
|
|
|
2671
2594
|
}
|
|
2672
2595
|
|
|
2673
2596
|
// src/discovery/zod-ast-to-ts.ts
|
|
2674
|
-
import { Node as
|
|
2597
|
+
import { Node as Node7, SyntaxKind as SyntaxKind2 } from "ts-morph";
|
|
2675
2598
|
function zodAstToTs(node) {
|
|
2676
|
-
if (!
|
|
2599
|
+
if (!Node7.isCallExpression(node)) return "unknown";
|
|
2677
2600
|
const expr = node.getExpression();
|
|
2678
|
-
if (
|
|
2601
|
+
if (Node7.isPropertyAccessExpression(expr)) {
|
|
2679
2602
|
const methodName = expr.getName();
|
|
2680
2603
|
const receiver = expr.getExpression();
|
|
2681
2604
|
if (methodName === "optional") {
|
|
@@ -2699,17 +2622,17 @@ function zodAstToTs(node) {
|
|
|
2699
2622
|
case "literal": {
|
|
2700
2623
|
const lit = args[0];
|
|
2701
2624
|
if (!lit) return "unknown";
|
|
2702
|
-
if (
|
|
2703
|
-
if (
|
|
2704
|
-
if (lit.getKind() ===
|
|
2705
|
-
if (lit.getKind() ===
|
|
2625
|
+
if (Node7.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
|
|
2626
|
+
if (Node7.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
|
|
2627
|
+
if (lit.getKind() === SyntaxKind2.TrueKeyword) return "true";
|
|
2628
|
+
if (lit.getKind() === SyntaxKind2.FalseKeyword) return "false";
|
|
2706
2629
|
return "unknown";
|
|
2707
2630
|
}
|
|
2708
2631
|
case "enum": {
|
|
2709
2632
|
const arrArg = args[0];
|
|
2710
|
-
if (!arrArg || !
|
|
2633
|
+
if (!arrArg || !Node7.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2711
2634
|
const members = arrArg.getElements().map(
|
|
2712
|
-
(el) =>
|
|
2635
|
+
(el) => Node7.isStringLiteral(el) ? JSON.stringify(el.getLiteralValue()) : "unknown"
|
|
2713
2636
|
);
|
|
2714
2637
|
return members.join(" | ");
|
|
2715
2638
|
}
|
|
@@ -2720,10 +2643,10 @@ function zodAstToTs(node) {
|
|
|
2720
2643
|
}
|
|
2721
2644
|
case "object": {
|
|
2722
2645
|
const objArg = args[0];
|
|
2723
|
-
if (!objArg || !
|
|
2646
|
+
if (!objArg || !Node7.isObjectLiteralExpression(objArg)) return "unknown";
|
|
2724
2647
|
const lines = [];
|
|
2725
2648
|
for (const prop of objArg.getProperties()) {
|
|
2726
|
-
if (!
|
|
2649
|
+
if (!Node7.isPropertyAssignment(prop)) continue;
|
|
2727
2650
|
const key = prop.getName();
|
|
2728
2651
|
const valNode = prop.getInitializer();
|
|
2729
2652
|
if (!valNode) continue;
|
|
@@ -2735,7 +2658,7 @@ function zodAstToTs(node) {
|
|
|
2735
2658
|
}
|
|
2736
2659
|
case "union": {
|
|
2737
2660
|
const arrArg = args[0];
|
|
2738
|
-
if (!arrArg || !
|
|
2661
|
+
if (!arrArg || !Node7.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2739
2662
|
return arrArg.getElements().map(zodAstToTs).join(" | ");
|
|
2740
2663
|
}
|
|
2741
2664
|
case "record": {
|
|
@@ -2745,7 +2668,7 @@ function zodAstToTs(node) {
|
|
|
2745
2668
|
}
|
|
2746
2669
|
case "tuple": {
|
|
2747
2670
|
const arrArg = args[0];
|
|
2748
|
-
if (!arrArg || !
|
|
2671
|
+
if (!arrArg || !Node7.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2749
2672
|
return `[${arrArg.getElements().map(zodAstToTs).join(", ")}]`;
|
|
2750
2673
|
}
|
|
2751
2674
|
default:
|
|
@@ -2755,9 +2678,9 @@ function zodAstToTs(node) {
|
|
|
2755
2678
|
return "unknown";
|
|
2756
2679
|
}
|
|
2757
2680
|
function isOptionalChain(node) {
|
|
2758
|
-
if (!
|
|
2681
|
+
if (!Node7.isCallExpression(node)) return false;
|
|
2759
2682
|
const expr = node.getExpression();
|
|
2760
|
-
return
|
|
2683
|
+
return Node7.isPropertyAccessExpression(expr) && expr.getName() === "optional";
|
|
2761
2684
|
}
|
|
2762
2685
|
function memberType(val, position, project) {
|
|
2763
2686
|
if (project) {
|
|
@@ -2769,13 +2692,13 @@ function memberType(val, position, project) {
|
|
|
2769
2692
|
return zodAstToTs(val);
|
|
2770
2693
|
}
|
|
2771
2694
|
function parseDefineContractCall(callExpr, project) {
|
|
2772
|
-
if (!
|
|
2695
|
+
if (!Node7.isCallExpression(callExpr)) return null;
|
|
2773
2696
|
const callee = callExpr.getExpression();
|
|
2774
|
-
const calleeName =
|
|
2697
|
+
const calleeName = Node7.isIdentifier(callee) ? callee.getText() : Node7.isPropertyAccessExpression(callee) ? callee.getName() : "";
|
|
2775
2698
|
if (calleeName !== "defineContract") return null;
|
|
2776
2699
|
const args = callExpr.getArguments();
|
|
2777
2700
|
const optsArg = args[0];
|
|
2778
|
-
if (!optsArg || !
|
|
2701
|
+
if (!optsArg || !Node7.isObjectLiteralExpression(optsArg)) return null;
|
|
2779
2702
|
let query = null;
|
|
2780
2703
|
let body = null;
|
|
2781
2704
|
let response = "unknown";
|
|
@@ -2783,7 +2706,7 @@ function parseDefineContractCall(callExpr, project) {
|
|
|
2783
2706
|
let bodyZodText = null;
|
|
2784
2707
|
let queryZodText = null;
|
|
2785
2708
|
for (const prop of optsArg.getProperties()) {
|
|
2786
|
-
if (!
|
|
2709
|
+
if (!Node7.isPropertyAssignment(prop)) continue;
|
|
2787
2710
|
const propName = prop.getName();
|
|
2788
2711
|
const val = prop.getInitializer();
|
|
2789
2712
|
if (!val) continue;
|
|
@@ -2805,7 +2728,7 @@ function parseDefineContractCall(callExpr, project) {
|
|
|
2805
2728
|
// src/discovery/pipe-schema.ts
|
|
2806
2729
|
function pipeSchemaOf(decorator, project) {
|
|
2807
2730
|
for (const pipe of decorator.getArguments()) {
|
|
2808
|
-
if (!
|
|
2731
|
+
if (!Node8.isNewExpression(pipe) && !Node8.isCallExpression(pipe)) continue;
|
|
2809
2732
|
for (const arg of pipe.getArguments()) {
|
|
2810
2733
|
if (!couldBeSchema(arg)) continue;
|
|
2811
2734
|
const inferred = inferSchema(arg, project);
|
|
@@ -2827,15 +2750,15 @@ function pipeSchemaOf(decorator, project) {
|
|
|
2827
2750
|
return null;
|
|
2828
2751
|
}
|
|
2829
2752
|
function couldBeSchema(arg) {
|
|
2830
|
-
return
|
|
2753
|
+
return Node8.isIdentifier(arg) || Node8.isPropertyAccessExpression(arg) || Node8.isCallExpression(arg) || Node8.isAsExpression(arg) || Node8.isSatisfiesExpression(arg) || Node8.isParenthesizedExpression(arg);
|
|
2831
2754
|
}
|
|
2832
2755
|
function syntacticZodType(arg, project) {
|
|
2833
2756
|
let expr = arg;
|
|
2834
|
-
if (
|
|
2757
|
+
if (Node8.isIdentifier(arg)) {
|
|
2835
2758
|
const resolved = resolveImportedVariable(arg.getText(), arg.getSourceFile(), project);
|
|
2836
2759
|
expr = resolved?.decl.getInitializer();
|
|
2837
2760
|
}
|
|
2838
|
-
if (!expr || !
|
|
2761
|
+
if (!expr || !Node8.isCallExpression(expr)) return null;
|
|
2839
2762
|
const type = zodAstToTs(expr);
|
|
2840
2763
|
return type === "unknown" ? null : type;
|
|
2841
2764
|
}
|
|
@@ -2871,22 +2794,22 @@ var PASSTHROUGH_UTILITY = /* @__PURE__ */ new Set([
|
|
|
2871
2794
|
]);
|
|
2872
2795
|
function resolveTypeNodeToString(typeNode, sourceFile, project, depth, subst = /* @__PURE__ */ new Map()) {
|
|
2873
2796
|
if (depth <= 0) return "unknown";
|
|
2874
|
-
if (
|
|
2797
|
+
if (Node9.isArrayTypeNode(typeNode)) {
|
|
2875
2798
|
const elementType = typeNode.getElementTypeNode();
|
|
2876
2799
|
return `Array<${resolveTypeNodeToString(elementType, sourceFile, project, depth, subst)}>`;
|
|
2877
2800
|
}
|
|
2878
|
-
if (
|
|
2801
|
+
if (Node9.isUnionTypeNode(typeNode)) {
|
|
2879
2802
|
return typeNode.getTypeNodes().map((t) => resolveTypeNodeToString(t, sourceFile, project, depth, subst)).join(" | ");
|
|
2880
2803
|
}
|
|
2881
|
-
if (
|
|
2804
|
+
if (Node9.isIntersectionTypeNode(typeNode)) {
|
|
2882
2805
|
return typeNode.getTypeNodes().map((t) => resolveTypeNodeToString(t, sourceFile, project, depth, subst)).join(" & ");
|
|
2883
2806
|
}
|
|
2884
|
-
if (
|
|
2807
|
+
if (Node9.isParenthesizedTypeNode(typeNode)) {
|
|
2885
2808
|
return `(${resolveTypeNodeToString(typeNode.getTypeNode(), sourceFile, project, depth, subst)})`;
|
|
2886
2809
|
}
|
|
2887
|
-
if (
|
|
2810
|
+
if (Node9.isTypeReference(typeNode)) {
|
|
2888
2811
|
const typeName = typeNode.getTypeName();
|
|
2889
|
-
const name =
|
|
2812
|
+
const name = Node9.isIdentifier(typeName) ? typeName.getText() : typeNode.getText();
|
|
2890
2813
|
const bound = subst.get(name);
|
|
2891
2814
|
if (bound !== void 0) return bound;
|
|
2892
2815
|
if (name === "string" || name === "number" || name === "boolean") return name;
|
|
@@ -2909,10 +2832,10 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth, subst = /
|
|
|
2909
2832
|
dbg("unresolvable type:", name, "in", sourceFile.getFilePath());
|
|
2910
2833
|
return "unknown";
|
|
2911
2834
|
}
|
|
2912
|
-
if (
|
|
2835
|
+
if (Node9.isTypeLiteral(typeNode)) {
|
|
2913
2836
|
const members = [];
|
|
2914
2837
|
for (const member of typeNode.getMembers()) {
|
|
2915
|
-
if (
|
|
2838
|
+
if (Node9.isPropertySignature(member)) {
|
|
2916
2839
|
const memberTypeNode = member.getTypeNode();
|
|
2917
2840
|
const memberType2 = memberTypeNode ? resolveTypeNodeToString(memberTypeNode, sourceFile, project, depth, subst) : "unknown";
|
|
2918
2841
|
members.push(`${member.getName()}${member.hasQuestionToken() ? "?" : ""}: ${memberType2}`);
|
|
@@ -2923,11 +2846,11 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth, subst = /
|
|
|
2923
2846
|
return members.length > 0 ? `{ ${members.join("; ")} }` : "{}";
|
|
2924
2847
|
}
|
|
2925
2848
|
const kind = typeNode.getKind();
|
|
2926
|
-
if (kind ===
|
|
2927
|
-
if (kind ===
|
|
2928
|
-
if (kind ===
|
|
2929
|
-
if (kind ===
|
|
2930
|
-
if (kind ===
|
|
2849
|
+
if (kind === SyntaxKind3.StringKeyword) return "string";
|
|
2850
|
+
if (kind === SyntaxKind3.NumberKeyword) return "number";
|
|
2851
|
+
if (kind === SyntaxKind3.BooleanKeyword) return "boolean";
|
|
2852
|
+
if (kind === SyntaxKind3.UnknownKeyword) return "unknown";
|
|
2853
|
+
if (kind === SyntaxKind3.AnyKeyword) return "unknown";
|
|
2931
2854
|
return typeNode.getText();
|
|
2932
2855
|
}
|
|
2933
2856
|
function unwrapFirstTypeArg(typeNode, sourceFile, project, depth, mode, subst = /* @__PURE__ */ new Map()) {
|
|
@@ -2985,7 +2908,7 @@ function resolvePropertied(decl, sourceFile, project, depth, subst = /* @__PURE_
|
|
|
2985
2908
|
}
|
|
2986
2909
|
function namedField(decorator) {
|
|
2987
2910
|
const first = decorator.getArguments()[0];
|
|
2988
|
-
return first &&
|
|
2911
|
+
return first && Node9.isStringLiteral(first) ? first.getLiteralValue() : null;
|
|
2989
2912
|
}
|
|
2990
2913
|
function extractBodyType(method, sourceFile, project) {
|
|
2991
2914
|
for (const param of method.getParameters()) {
|
|
@@ -3035,8 +2958,8 @@ function extractQueryType(method, sourceFile, project) {
|
|
|
3035
2958
|
function isParamOptional(param) {
|
|
3036
2959
|
if (param.hasQuestionToken() || param.hasInitializer()) return true;
|
|
3037
2960
|
const typeNode = param.getTypeNode();
|
|
3038
|
-
if (typeNode &&
|
|
3039
|
-
return typeNode.getTypeNodes().some((t) => t.getKind() ===
|
|
2961
|
+
if (typeNode && Node9.isUnionTypeNode(typeNode)) {
|
|
2962
|
+
return typeNode.getTypeNodes().some((t) => t.getKind() === SyntaxKind3.UndefinedKeyword);
|
|
3040
2963
|
}
|
|
3041
2964
|
return false;
|
|
3042
2965
|
}
|
|
@@ -3048,7 +2971,7 @@ function extractParamsType(method, sourceFile, project) {
|
|
|
3048
2971
|
const paramArgs = paramDecorator.getArguments();
|
|
3049
2972
|
if (paramArgs.length === 0) continue;
|
|
3050
2973
|
const nameArg = paramArgs[0];
|
|
3051
|
-
if (!
|
|
2974
|
+
if (!Node9.isStringLiteral(nameArg)) continue;
|
|
3052
2975
|
const paramName = nameArg.getLiteralValue();
|
|
3053
2976
|
const typeNode = param.getTypeNode();
|
|
3054
2977
|
const paramType = typeNode ? resolveTypeNodeToString(typeNode, sourceFile, project, 3) : "string";
|
|
@@ -3085,28 +3008,28 @@ function extractUploadedFiles(method) {
|
|
|
3085
3008
|
for (const decorator of method.getDecorators()) {
|
|
3086
3009
|
if (decorator.getName() !== "UseInterceptors") continue;
|
|
3087
3010
|
for (const arg of decorator.getArguments()) {
|
|
3088
|
-
if (!
|
|
3011
|
+
if (!Node9.isCallExpression(arg)) continue;
|
|
3089
3012
|
const interceptor = arg.getExpression().getText();
|
|
3090
3013
|
const callArgs = arg.getArguments();
|
|
3091
3014
|
const firstArg2 = callArgs[0];
|
|
3092
3015
|
if (interceptor === "FileInterceptor") {
|
|
3093
|
-
if (firstArg2 &&
|
|
3016
|
+
if (firstArg2 && Node9.isStringLiteral(firstArg2)) {
|
|
3094
3017
|
entries.push(`${firstArg2.getLiteralValue()}: ${FILE}`);
|
|
3095
3018
|
multipart = true;
|
|
3096
3019
|
}
|
|
3097
3020
|
} else if (interceptor === "FilesInterceptor") {
|
|
3098
|
-
if (firstArg2 &&
|
|
3021
|
+
if (firstArg2 && Node9.isStringLiteral(firstArg2)) {
|
|
3099
3022
|
entries.push(`${firstArg2.getLiteralValue()}: Array<${FILE}>`);
|
|
3100
3023
|
multipart = true;
|
|
3101
3024
|
}
|
|
3102
3025
|
} else if (interceptor === "FileFieldsInterceptor") {
|
|
3103
|
-
if (firstArg2 &&
|
|
3026
|
+
if (firstArg2 && Node9.isArrayLiteralExpression(firstArg2)) {
|
|
3104
3027
|
for (const el of firstArg2.getElements()) {
|
|
3105
|
-
if (!
|
|
3028
|
+
if (!Node9.isObjectLiteralExpression(el)) continue;
|
|
3106
3029
|
const nameProp = el.getProperty("name");
|
|
3107
|
-
if (nameProp &&
|
|
3030
|
+
if (nameProp && Node9.isPropertyAssignment(nameProp)) {
|
|
3108
3031
|
const init = nameProp.getInitializer();
|
|
3109
|
-
if (init &&
|
|
3032
|
+
if (init && Node9.isStringLiteral(init)) {
|
|
3110
3033
|
entries.push(`${init.getLiteralValue()}: Array<${FILE}>`);
|
|
3111
3034
|
}
|
|
3112
3035
|
}
|
|
@@ -3126,13 +3049,13 @@ function extractResponseType(method, sourceFile, project) {
|
|
|
3126
3049
|
if (apiResponseDecorator) {
|
|
3127
3050
|
const args = apiResponseDecorator.getArguments();
|
|
3128
3051
|
const optsArg = args[0];
|
|
3129
|
-
if (optsArg &&
|
|
3052
|
+
if (optsArg && Node9.isObjectLiteralExpression(optsArg)) {
|
|
3130
3053
|
for (const prop of optsArg.getProperties()) {
|
|
3131
|
-
if (!
|
|
3054
|
+
if (!Node9.isPropertyAssignment(prop)) continue;
|
|
3132
3055
|
if (prop.getName() !== "type") continue;
|
|
3133
3056
|
const val = prop.getInitializer();
|
|
3134
3057
|
if (!val) continue;
|
|
3135
|
-
if (
|
|
3058
|
+
if (Node9.isArrayLiteralExpression(val)) {
|
|
3136
3059
|
const elements = val.getElements();
|
|
3137
3060
|
const firstEl = elements[0];
|
|
3138
3061
|
if (elements.length > 0 && firstEl !== void 0) {
|
|
@@ -3153,24 +3076,24 @@ function extractResponseType(method, sourceFile, project) {
|
|
|
3153
3076
|
}
|
|
3154
3077
|
function apiResponseStatus(decorator) {
|
|
3155
3078
|
const optsArg = decorator.getArguments()[0];
|
|
3156
|
-
if (!optsArg || !
|
|
3079
|
+
if (!optsArg || !Node9.isObjectLiteralExpression(optsArg)) return null;
|
|
3157
3080
|
for (const prop of optsArg.getProperties()) {
|
|
3158
|
-
if (!
|
|
3081
|
+
if (!Node9.isPropertyAssignment(prop)) continue;
|
|
3159
3082
|
if (prop.getName() !== "status") continue;
|
|
3160
3083
|
const val = prop.getInitializer();
|
|
3161
|
-
if (val &&
|
|
3084
|
+
if (val && Node9.isNumericLiteral(val)) return Number(val.getLiteralValue());
|
|
3162
3085
|
}
|
|
3163
3086
|
return null;
|
|
3164
3087
|
}
|
|
3165
3088
|
function apiResponseTypeNode(decorator) {
|
|
3166
3089
|
const optsArg = decorator.getArguments()[0];
|
|
3167
|
-
if (!optsArg || !
|
|
3090
|
+
if (!optsArg || !Node9.isObjectLiteralExpression(optsArg)) return null;
|
|
3168
3091
|
for (const prop of optsArg.getProperties()) {
|
|
3169
|
-
if (!
|
|
3092
|
+
if (!Node9.isPropertyAssignment(prop)) continue;
|
|
3170
3093
|
if (prop.getName() !== "type") continue;
|
|
3171
3094
|
const val = prop.getInitializer();
|
|
3172
3095
|
if (!val) return null;
|
|
3173
|
-
if (
|
|
3096
|
+
if (Node9.isArrayLiteralExpression(val)) {
|
|
3174
3097
|
const first = val.getElements()[0];
|
|
3175
3098
|
return first ? { node: first, isArray: true } : null;
|
|
3176
3099
|
}
|
|
@@ -3188,7 +3111,7 @@ function extractErrorType(method, sourceFile, project) {
|
|
|
3188
3111
|
const inner = resolveIdentifierToClassType(typeInfo.node, sourceFile, project, 3);
|
|
3189
3112
|
const type = typeInfo.isArray ? `Array<${inner}>` : inner;
|
|
3190
3113
|
let ref = null;
|
|
3191
|
-
if (
|
|
3114
|
+
if (Node9.isIdentifier(typeInfo.node)) {
|
|
3192
3115
|
const name = typeInfo.node.getText();
|
|
3193
3116
|
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
3194
3117
|
if (localDecl?.isExported()) {
|
|
@@ -3205,7 +3128,7 @@ function extractErrorType(method, sourceFile, project) {
|
|
|
3205
3128
|
return null;
|
|
3206
3129
|
}
|
|
3207
3130
|
function resolveIdentifierToClassType(node, sourceFile, project, depth) {
|
|
3208
|
-
if (!
|
|
3131
|
+
if (!Node9.isIdentifier(node)) return "unknown";
|
|
3209
3132
|
const name = node.getText();
|
|
3210
3133
|
const resolved = findType(name, sourceFile, project);
|
|
3211
3134
|
if (resolved) {
|
|
@@ -3225,9 +3148,9 @@ var STREAM_ENVELOPES = /* @__PURE__ */ new Set(["MessageEvent", "MessageEventLik
|
|
|
3225
3148
|
var BINARY_RESPONSE_TYPES = /* @__PURE__ */ new Set(["StreamableFile", "Buffer"]);
|
|
3226
3149
|
function detectBinaryResponse(method) {
|
|
3227
3150
|
const node = unwrapNamedContainer(method.getReturnTypeNode(), /* @__PURE__ */ new Set(["Promise"]));
|
|
3228
|
-
if (!node || !
|
|
3151
|
+
if (!node || !Node9.isTypeReference(node)) return false;
|
|
3229
3152
|
const typeName = node.getTypeName();
|
|
3230
|
-
const name =
|
|
3153
|
+
const name = Node9.isIdentifier(typeName) ? typeName.getText() : "";
|
|
3231
3154
|
return BINARY_RESPONSE_TYPES.has(name);
|
|
3232
3155
|
}
|
|
3233
3156
|
function detectStreamElement(method) {
|
|
@@ -3242,9 +3165,9 @@ function detectStreamElement(method) {
|
|
|
3242
3165
|
return null;
|
|
3243
3166
|
}
|
|
3244
3167
|
function streamContainerElement(node) {
|
|
3245
|
-
if (!node || !
|
|
3168
|
+
if (!node || !Node9.isTypeReference(node)) return null;
|
|
3246
3169
|
const typeName = node.getTypeName();
|
|
3247
|
-
const name =
|
|
3170
|
+
const name = Node9.isIdentifier(typeName) ? typeName.getText() : "";
|
|
3248
3171
|
if (STREAM_CONTAINERS.has(name) || STREAM_CONTAINERS_GENERATOR.has(name)) {
|
|
3249
3172
|
return node.getTypeArguments()[0] ?? null;
|
|
3250
3173
|
}
|
|
@@ -3254,9 +3177,9 @@ function hasAsQueryDecorator(method) {
|
|
|
3254
3177
|
return method.getDecorators().some((d) => d.getName() === "AsQuery");
|
|
3255
3178
|
}
|
|
3256
3179
|
function unwrapNamedContainer(node, names) {
|
|
3257
|
-
if (!node || !
|
|
3180
|
+
if (!node || !Node9.isTypeReference(node)) return node;
|
|
3258
3181
|
const typeName = node.getTypeName();
|
|
3259
|
-
const name =
|
|
3182
|
+
const name = Node9.isIdentifier(typeName) ? typeName.getText() : "";
|
|
3260
3183
|
if (names.has(name)) {
|
|
3261
3184
|
return node.getTypeArguments()[0] ?? node;
|
|
3262
3185
|
}
|
|
@@ -3303,11 +3226,11 @@ function extractDtoContract(method, sourceFile, project, mixin, controllerClass)
|
|
|
3303
3226
|
if (apiResp) {
|
|
3304
3227
|
const args = apiResp.getArguments();
|
|
3305
3228
|
const optsArg = args[0];
|
|
3306
|
-
if (optsArg &&
|
|
3229
|
+
if (optsArg && Node9.isObjectLiteralExpression(optsArg)) {
|
|
3307
3230
|
for (const prop of optsArg.getProperties()) {
|
|
3308
|
-
if (
|
|
3231
|
+
if (Node9.isPropertyAssignment(prop) && prop.getName() === "type") {
|
|
3309
3232
|
const val = prop.getInitializer();
|
|
3310
|
-
if (val &&
|
|
3233
|
+
if (val && Node9.isIdentifier(val)) {
|
|
3311
3234
|
const name = val.getText();
|
|
3312
3235
|
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
3313
3236
|
if (localDecl?.isExported()) {
|
|
@@ -3394,7 +3317,7 @@ async function discoverContractsFast(opts) {
|
|
|
3394
3317
|
return routes;
|
|
3395
3318
|
}
|
|
3396
3319
|
function resolveTsconfigPath(cwd, tsconfig) {
|
|
3397
|
-
return tsconfig ? resolve3(tsconfig) :
|
|
3320
|
+
return tsconfig ? resolve3(tsconfig) : join3(cwd, "tsconfig.json");
|
|
3398
3321
|
}
|
|
3399
3322
|
var NO_TSCONFIG_OPTIONS = {
|
|
3400
3323
|
allowJs: true,
|
|
@@ -3403,25 +3326,25 @@ var NO_TSCONFIG_OPTIONS = {
|
|
|
3403
3326
|
};
|
|
3404
3327
|
function loadDiscoveryTsconfig(tsconfigPath) {
|
|
3405
3328
|
if (!existsSync(tsconfigPath)) return { options: null, files: [] };
|
|
3406
|
-
const read =
|
|
3329
|
+
const read = ts4.readConfigFile(tsconfigPath, (path) => ts4.sys.readFile(path));
|
|
3407
3330
|
if (read.error) {
|
|
3408
3331
|
return {
|
|
3409
3332
|
options: null,
|
|
3410
3333
|
files: [tsconfigPath],
|
|
3411
|
-
error:
|
|
3334
|
+
error: ts4.flattenDiagnosticMessageText(read.error.messageText, " ")
|
|
3412
3335
|
};
|
|
3413
3336
|
}
|
|
3414
3337
|
const host = {
|
|
3415
|
-
useCaseSensitiveFileNames:
|
|
3338
|
+
useCaseSensitiveFileNames: ts4.sys.useCaseSensitiveFileNames,
|
|
3416
3339
|
// The one line that keeps a tsconfig from being a filesystem walk.
|
|
3417
3340
|
readDirectory: () => [],
|
|
3418
|
-
fileExists: (path) =>
|
|
3341
|
+
fileExists: (path) => ts4.sys.fileExists(path),
|
|
3419
3342
|
// `extends` is resolved through the host, so a consumer whose `paths` live
|
|
3420
3343
|
// in a base tsconfig keeps working.
|
|
3421
|
-
readFile: (path) =>
|
|
3344
|
+
readFile: (path) => ts4.sys.readFile(path)
|
|
3422
3345
|
};
|
|
3423
|
-
const sourceFile =
|
|
3424
|
-
const parsed =
|
|
3346
|
+
const sourceFile = ts4.readJsonConfigFile(tsconfigPath, (path) => ts4.sys.readFile(path));
|
|
3347
|
+
const parsed = ts4.parseJsonSourceFileConfigFileContent(
|
|
3425
3348
|
sourceFile,
|
|
3426
3349
|
host,
|
|
3427
3350
|
dirname2(tsconfigPath),
|
|
@@ -3444,7 +3367,7 @@ function createDiscoveryProject(tsconfigPath, tsconfig = loadDiscoveryTsconfig(t
|
|
|
3444
3367
|
`[nestjs-codegen/fast] Could not load ${tsconfigPath}: ${tsconfig.error} \u2014 continuing WITHOUT its compiler options, so path alias imports (e.g. '@/...') will not resolve and controllers extending an alias-imported factory will contribute NO routes to the generated client.`
|
|
3445
3368
|
);
|
|
3446
3369
|
}
|
|
3447
|
-
return new
|
|
3370
|
+
return new Project3({
|
|
3448
3371
|
compilerOptions: tsconfig.options ?? { ...NO_TSCONFIG_OPTIONS },
|
|
3449
3372
|
skipAddingFilesFromTsConfig: true,
|
|
3450
3373
|
skipLoadingLibFiles: true,
|
|
@@ -3486,6 +3409,9 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
3486
3409
|
glob;
|
|
3487
3410
|
/** Absolute paths of the controllers currently loaded as extraction roots. */
|
|
3488
3411
|
controllerPaths = /* @__PURE__ */ new Set();
|
|
3412
|
+
/** Every file in {@link project}, as last read from disk. */
|
|
3413
|
+
files = new DiskFingerprints();
|
|
3414
|
+
unresolved = new UnresolvedImports();
|
|
3489
3415
|
constructor(project, cwd, glob) {
|
|
3490
3416
|
this.project = project;
|
|
3491
3417
|
this.cwd = cwd;
|
|
@@ -3511,6 +3437,8 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
3511
3437
|
}
|
|
3512
3438
|
/** Run the initial extraction (equivalent to a first `discoverContractsFast`). */
|
|
3513
3439
|
discover() {
|
|
3440
|
+
clearMixinTypeProject();
|
|
3441
|
+
clearSchemaTypeProject(this.project);
|
|
3514
3442
|
return this.runExtraction();
|
|
3515
3443
|
}
|
|
3516
3444
|
/**
|
|
@@ -3518,26 +3446,39 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
3518
3446
|
* from disk (controllers AND any lazily-loaded DTO/imported files), re-globs
|
|
3519
3447
|
* to pick up added/removed controllers, clears the per-Project caches, then
|
|
3520
3448
|
* re-extracts. `changedPaths` is a hint; correctness does not depend on it
|
|
3521
|
-
* being exhaustive because re-globbing +
|
|
3449
|
+
* being exhaustive because re-globbing + comparing every loaded file with the
|
|
3450
|
+
* disk covers the set.
|
|
3522
3451
|
*/
|
|
3523
|
-
async rediscover(changedPaths) {
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3452
|
+
async rediscover(changedPaths, options = {}) {
|
|
3453
|
+
const hinted = new Set([...changedPaths ?? []].map((p) => resolve3(p)));
|
|
3454
|
+
const program = this.project.getProgram().compilerObject;
|
|
3455
|
+
const sources = program.getSourceFiles().filter(
|
|
3456
|
+
(file) => !program.isSourceFileDefaultLibrary(file) && !program.isSourceFileFromExternalLibrary(file)
|
|
3457
|
+
).map((file) => ({ path: file.fileName, text: file.text }));
|
|
3458
|
+
for (const path of /* @__PURE__ */ new Set([...hinted, ...this.files.changed(sources)])) {
|
|
3459
|
+
const sf = this.project.getSourceFile(path);
|
|
3460
|
+
if (sf && await sf.refreshFromFileSystem() === FileSystemRefreshResult3.Deleted) {
|
|
3461
|
+
this.files.forget(path);
|
|
3531
3462
|
}
|
|
3532
3463
|
}
|
|
3464
|
+
const inProject = new Set(this.project.getSourceFiles().map((sf) => sf.getFilePath()));
|
|
3465
|
+
const definitions = this.project.getLanguageService().compilerObject.getProgram();
|
|
3466
|
+
for (const path of definitions ? this.unresolved.nowResolving(definitions) : []) {
|
|
3467
|
+
const sf = this.project.getSourceFile(path);
|
|
3468
|
+
if (!sf) continue;
|
|
3469
|
+
this.project.removeSourceFile(sf);
|
|
3470
|
+
if (inProject.has(path)) this.project.addSourceFileAtPathIfExists(path);
|
|
3471
|
+
}
|
|
3533
3472
|
const globbed = new Set(
|
|
3534
3473
|
await fg2(this.glob, { cwd: this.cwd, absolute: true, onlyFiles: true })
|
|
3535
3474
|
);
|
|
3475
|
+
let controllersChanged = false;
|
|
3536
3476
|
for (const f of globbed) {
|
|
3537
3477
|
if (!this.controllerPaths.has(f)) {
|
|
3538
3478
|
try {
|
|
3539
3479
|
this.project.addSourceFileAtPath(f);
|
|
3540
3480
|
this.controllerPaths.add(f);
|
|
3481
|
+
controllersChanged = true;
|
|
3541
3482
|
} catch {
|
|
3542
3483
|
}
|
|
3543
3484
|
}
|
|
@@ -3547,25 +3488,31 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
3547
3488
|
const sf = this.project.getSourceFile(f);
|
|
3548
3489
|
if (sf) this.project.removeSourceFile(sf);
|
|
3549
3490
|
this.controllerPaths.delete(f);
|
|
3491
|
+
controllersChanged = true;
|
|
3550
3492
|
}
|
|
3551
3493
|
}
|
|
3494
|
+
if (controllersChanged || options.filesAddedOrRemoved) {
|
|
3495
|
+
clearMixinTypeProject();
|
|
3496
|
+
clearSchemaTypeProject(this.project);
|
|
3497
|
+
} else {
|
|
3498
|
+
invalidateMixinTypeProject();
|
|
3499
|
+
invalidateSchemaTypeProject(this.project);
|
|
3500
|
+
}
|
|
3552
3501
|
return this.runExtraction();
|
|
3553
3502
|
}
|
|
3554
3503
|
/** Clear stale per-Project caches, then extract over the controller set. */
|
|
3555
3504
|
runExtraction() {
|
|
3556
3505
|
clearTypeResolutionCaches(this.project);
|
|
3557
3506
|
clearEnumCache(this.project);
|
|
3558
|
-
clearMixinTypeProject();
|
|
3559
|
-
clearSchemaTypeProject(this.project);
|
|
3560
3507
|
return extractRoutesFrom(this.project, this.controllerPaths);
|
|
3561
3508
|
}
|
|
3562
3509
|
};
|
|
3563
3510
|
function decoratorStringArg(decoratorExpr) {
|
|
3564
3511
|
if (!decoratorExpr) return void 0;
|
|
3565
|
-
if (
|
|
3566
|
-
if (
|
|
3512
|
+
if (Node10.isStringLiteral(decoratorExpr)) return decoratorExpr.getLiteralValue();
|
|
3513
|
+
if (Node10.isArrayLiteralExpression(decoratorExpr)) {
|
|
3567
3514
|
const first = decoratorExpr.getElements()[0];
|
|
3568
|
-
if (first &&
|
|
3515
|
+
if (first && Node10.isStringLiteral(first)) return first.getLiteralValue();
|
|
3569
3516
|
}
|
|
3570
3517
|
return void 0;
|
|
3571
3518
|
}
|
|
@@ -3686,9 +3633,9 @@ function extractContractRoute(args) {
|
|
|
3686
3633
|
let contractDef = null;
|
|
3687
3634
|
let bodyZodRef = null;
|
|
3688
3635
|
let queryZodRef = null;
|
|
3689
|
-
if (
|
|
3636
|
+
if (Node10.isCallExpression(firstDecoratorArg)) {
|
|
3690
3637
|
contractDef = parseDefineContractCall(firstDecoratorArg, project);
|
|
3691
|
-
} else if (
|
|
3638
|
+
} else if (Node10.isIdentifier(firstDecoratorArg)) {
|
|
3692
3639
|
const identName = firstDecoratorArg.getText();
|
|
3693
3640
|
const resolvedVar = resolveImportedVariable(identName, sourceFile, project);
|
|
3694
3641
|
if (!resolvedVar) {
|
|
@@ -3843,7 +3790,300 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
3843
3790
|
return routes;
|
|
3844
3791
|
}
|
|
3845
3792
|
|
|
3793
|
+
// src/discovery/shared-props.ts
|
|
3794
|
+
function discoverSharedProps(project, moduleEntry) {
|
|
3795
|
+
try {
|
|
3796
|
+
let sourceFile = project.getSourceFile(moduleEntry);
|
|
3797
|
+
if (!sourceFile) {
|
|
3798
|
+
try {
|
|
3799
|
+
sourceFile = project.addSourceFileAtPath(moduleEntry);
|
|
3800
|
+
} catch {
|
|
3801
|
+
return null;
|
|
3802
|
+
}
|
|
3803
|
+
}
|
|
3804
|
+
const forRootCall = findForRootCall(sourceFile);
|
|
3805
|
+
if (!forRootCall) return null;
|
|
3806
|
+
const initializer = findShareInitializer(forRootCall);
|
|
3807
|
+
if (!initializer) return null;
|
|
3808
|
+
return extractShareType(initializer, sourceFile, project);
|
|
3809
|
+
} catch {
|
|
3810
|
+
return null;
|
|
3811
|
+
}
|
|
3812
|
+
}
|
|
3813
|
+
function discoverSharedPropsFromConfig(config) {
|
|
3814
|
+
if (!config.app?.moduleEntry) return null;
|
|
3815
|
+
try {
|
|
3816
|
+
const tsconfigPath = config.app.tsconfig ?? join4(config.codegen.cwd, "tsconfig.json");
|
|
3817
|
+
let project;
|
|
3818
|
+
try {
|
|
3819
|
+
project = sharedPropsProject(tsconfigPath);
|
|
3820
|
+
} catch {
|
|
3821
|
+
project = new Project4({
|
|
3822
|
+
skipAddingFilesFromTsConfig: true,
|
|
3823
|
+
skipLoadingLibFiles: true,
|
|
3824
|
+
skipFileDependencyResolution: true,
|
|
3825
|
+
compilerOptions: { allowJs: true, strict: false }
|
|
3826
|
+
});
|
|
3827
|
+
}
|
|
3828
|
+
return discoverSharedProps(project, config.app.moduleEntry);
|
|
3829
|
+
} catch {
|
|
3830
|
+
return null;
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3833
|
+
var parsedTsconfigs = /* @__PURE__ */ new Map();
|
|
3834
|
+
function readText(file) {
|
|
3835
|
+
try {
|
|
3836
|
+
return readFileSync2(file, "utf8");
|
|
3837
|
+
} catch {
|
|
3838
|
+
return "\0missing";
|
|
3839
|
+
}
|
|
3840
|
+
}
|
|
3841
|
+
function sharedPropsProject(tsconfigPath) {
|
|
3842
|
+
const text = loadDiscoveryTsconfig(tsconfigPath).files.map((file) => `${file}\0${readText(file)}`).join("\0");
|
|
3843
|
+
const flags = {
|
|
3844
|
+
skipAddingFilesFromTsConfig: true,
|
|
3845
|
+
skipLoadingLibFiles: true,
|
|
3846
|
+
skipFileDependencyResolution: true
|
|
3847
|
+
};
|
|
3848
|
+
const parsed = parsedTsconfigs.get(tsconfigPath);
|
|
3849
|
+
if (parsed?.text === text) return new Project4({ ...flags, compilerOptions: parsed.options });
|
|
3850
|
+
const project = new Project4({ ...flags, tsConfigFilePath: tsconfigPath });
|
|
3851
|
+
parsedTsconfigs.set(tsconfigPath, { text, options: project.getCompilerOptions() });
|
|
3852
|
+
return project;
|
|
3853
|
+
}
|
|
3854
|
+
function findForRootCall(sourceFile) {
|
|
3855
|
+
const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind4.CallExpression);
|
|
3856
|
+
for (const call of callExpressions) {
|
|
3857
|
+
const expr = call.getExpression();
|
|
3858
|
+
if (!Node11.isPropertyAccessExpression(expr)) continue;
|
|
3859
|
+
const methodName = expr.getName();
|
|
3860
|
+
const objectExpr = expr.getExpression();
|
|
3861
|
+
if (methodName === "forRoot" && Node11.isIdentifier(objectExpr)) {
|
|
3862
|
+
const name = objectExpr.getText();
|
|
3863
|
+
if (name === "InertiaModule") {
|
|
3864
|
+
return call;
|
|
3865
|
+
}
|
|
3866
|
+
}
|
|
3867
|
+
}
|
|
3868
|
+
return null;
|
|
3869
|
+
}
|
|
3870
|
+
function findShareInitializer(forRootCall) {
|
|
3871
|
+
if (!Node11.isCallExpression(forRootCall)) return null;
|
|
3872
|
+
const args = forRootCall.getArguments();
|
|
3873
|
+
const firstArg2 = args[0];
|
|
3874
|
+
if (!firstArg2 || !Node11.isObjectLiteralExpression(firstArg2)) return null;
|
|
3875
|
+
for (const prop of firstArg2.getProperties()) {
|
|
3876
|
+
if (Node11.isPropertyAssignment(prop) && prop.getName() === "share") {
|
|
3877
|
+
return prop.getInitializer() ?? null;
|
|
3878
|
+
}
|
|
3879
|
+
}
|
|
3880
|
+
return null;
|
|
3881
|
+
}
|
|
3882
|
+
function extractShareType(node, sourceFile, project) {
|
|
3883
|
+
if (Node11.isIdentifier(node)) {
|
|
3884
|
+
const ref = resolveIdentifierToImportRef(node, sourceFile, project);
|
|
3885
|
+
if (ref) {
|
|
3886
|
+
return {
|
|
3887
|
+
typeString: `Awaited<ReturnType<typeof import('${ref.importPath}').${ref.exportName}>>`,
|
|
3888
|
+
properties: null,
|
|
3889
|
+
isImportRef: true
|
|
3890
|
+
};
|
|
3891
|
+
}
|
|
3892
|
+
}
|
|
3893
|
+
if (Node11.isArrowFunction(node)) {
|
|
3894
|
+
const result = extractFromFunctionLike(node, sourceFile);
|
|
3895
|
+
return result ? { ...result, isImportRef: false } : null;
|
|
3896
|
+
}
|
|
3897
|
+
if (Node11.isFunctionExpression(node)) {
|
|
3898
|
+
const result = extractFromFunctionLike(node, sourceFile);
|
|
3899
|
+
return result ? { ...result, isImportRef: false } : null;
|
|
3900
|
+
}
|
|
3901
|
+
if (Node11.isObjectLiteralExpression(node)) {
|
|
3902
|
+
const result = extractFromObjectLiteral(node);
|
|
3903
|
+
return result ? { ...result, isImportRef: false } : null;
|
|
3904
|
+
}
|
|
3905
|
+
return null;
|
|
3906
|
+
}
|
|
3907
|
+
function resolveIdentifierToImportRef(id, sourceFile, project) {
|
|
3908
|
+
if (!Node11.isIdentifier(id)) return null;
|
|
3909
|
+
const name = id.getText();
|
|
3910
|
+
const localFunc = sourceFile.getFunction(name);
|
|
3911
|
+
if (localFunc?.isExported()) {
|
|
3912
|
+
const filePath = sourceFile.getFilePath().replace(/\.ts$/, "");
|
|
3913
|
+
return { importPath: filePath, exportName: name };
|
|
3914
|
+
}
|
|
3915
|
+
const localVar = sourceFile.getVariableDeclaration(name);
|
|
3916
|
+
if (localVar?.isExported()) {
|
|
3917
|
+
const filePath = sourceFile.getFilePath().replace(/\.ts$/, "");
|
|
3918
|
+
return { importPath: filePath, exportName: name };
|
|
3919
|
+
}
|
|
3920
|
+
for (const imp of sourceFile.getImportDeclarations()) {
|
|
3921
|
+
for (const named of imp.getNamedImports()) {
|
|
3922
|
+
const importedName = named.getAliasNode()?.getText() ?? named.getName();
|
|
3923
|
+
if (importedName !== name) continue;
|
|
3924
|
+
const resolvedSource = imp.getModuleSpecifierSourceFile();
|
|
3925
|
+
if (!resolvedSource) continue;
|
|
3926
|
+
const originalName = named.getName();
|
|
3927
|
+
const fn = resolvedSource.getFunction(originalName);
|
|
3928
|
+
if (fn?.isExported()) {
|
|
3929
|
+
const filePath = resolvedSource.getFilePath().replace(/\.ts$/, "");
|
|
3930
|
+
return { importPath: filePath, exportName: originalName };
|
|
3931
|
+
}
|
|
3932
|
+
const v = resolvedSource.getVariableDeclaration(originalName);
|
|
3933
|
+
if (v?.isExported()) {
|
|
3934
|
+
const filePath = resolvedSource.getFilePath().replace(/\.ts$/, "");
|
|
3935
|
+
return { importPath: filePath, exportName: originalName };
|
|
3936
|
+
}
|
|
3937
|
+
}
|
|
3938
|
+
}
|
|
3939
|
+
return null;
|
|
3940
|
+
}
|
|
3941
|
+
function extractFromFunctionLike(node, _sourceFile) {
|
|
3942
|
+
const returnTypeNode = Node11.isArrowFunction(node) || Node11.isFunctionExpression(node) ? node.getReturnTypeNode() : null;
|
|
3943
|
+
if (returnTypeNode) {
|
|
3944
|
+
return extractFromReturnTypeAnnotation(returnTypeNode);
|
|
3945
|
+
}
|
|
3946
|
+
if (Node11.isArrowFunction(node)) {
|
|
3947
|
+
const body = node.getBody();
|
|
3948
|
+
if (Node11.isParenthesizedExpression(body)) {
|
|
3949
|
+
const inner = body.getExpression();
|
|
3950
|
+
if (Node11.isObjectLiteralExpression(inner)) {
|
|
3951
|
+
return extractFromObjectLiteral(inner);
|
|
3952
|
+
}
|
|
3953
|
+
}
|
|
3954
|
+
if (Node11.isObjectLiteralExpression(body)) {
|
|
3955
|
+
return extractFromObjectLiteral(body);
|
|
3956
|
+
}
|
|
3957
|
+
if (Node11.isBlock(body)) {
|
|
3958
|
+
return extractFromBlockReturn(body);
|
|
3959
|
+
}
|
|
3960
|
+
}
|
|
3961
|
+
if (Node11.isFunctionExpression(node)) {
|
|
3962
|
+
const body = node.getBody();
|
|
3963
|
+
if (Node11.isBlock(body)) {
|
|
3964
|
+
return extractFromBlockReturn(body);
|
|
3965
|
+
}
|
|
3966
|
+
}
|
|
3967
|
+
return null;
|
|
3968
|
+
}
|
|
3969
|
+
function extractFromReturnTypeAnnotation(typeNode) {
|
|
3970
|
+
if (Node11.isTypeReference(typeNode)) {
|
|
3971
|
+
const typeName = typeNode.getTypeName();
|
|
3972
|
+
if (Node11.isIdentifier(typeName) && typeName.getText() === "Promise") {
|
|
3973
|
+
const typeArgs = typeNode.getTypeArguments();
|
|
3974
|
+
const firstArg2 = typeArgs[0];
|
|
3975
|
+
if (firstArg2) {
|
|
3976
|
+
return extractFromReturnTypeAnnotation(firstArg2);
|
|
3977
|
+
}
|
|
3978
|
+
return null;
|
|
3979
|
+
}
|
|
3980
|
+
}
|
|
3981
|
+
if (Node11.isTypeLiteral(typeNode)) {
|
|
3982
|
+
const properties = [];
|
|
3983
|
+
for (const member of typeNode.getMembers()) {
|
|
3984
|
+
if (Node11.isPropertySignature(member)) {
|
|
3985
|
+
const name = member.getName();
|
|
3986
|
+
const memberTypeNode = member.getTypeNode();
|
|
3987
|
+
const type = memberTypeNode ? memberTypeNode.getText() : "unknown";
|
|
3988
|
+
properties.push({ name, type });
|
|
3989
|
+
}
|
|
3990
|
+
}
|
|
3991
|
+
if (properties.length === 0) return null;
|
|
3992
|
+
const typeString = `{ ${properties.map((p) => `${p.name}: ${p.type}`).join("; ")} }`;
|
|
3993
|
+
return { typeString, properties, isImportRef: false };
|
|
3994
|
+
}
|
|
3995
|
+
return null;
|
|
3996
|
+
}
|
|
3997
|
+
function extractFromBlockReturn(block) {
|
|
3998
|
+
if (!Node11.isBlock(block)) return null;
|
|
3999
|
+
const statements = block.getStatements();
|
|
4000
|
+
for (let i = statements.length - 1; i >= 0; i--) {
|
|
4001
|
+
const stmt = statements[i];
|
|
4002
|
+
if (!Node11.isReturnStatement(stmt)) continue;
|
|
4003
|
+
const expr = stmt.getExpression();
|
|
4004
|
+
if (!expr) continue;
|
|
4005
|
+
if (Node11.isObjectLiteralExpression(expr)) {
|
|
4006
|
+
return extractFromObjectLiteral(expr);
|
|
4007
|
+
}
|
|
4008
|
+
if (Node11.isParenthesizedExpression(expr)) {
|
|
4009
|
+
const inner = expr.getExpression();
|
|
4010
|
+
if (Node11.isObjectLiteralExpression(inner)) {
|
|
4011
|
+
return extractFromObjectLiteral(inner);
|
|
4012
|
+
}
|
|
4013
|
+
}
|
|
4014
|
+
break;
|
|
4015
|
+
}
|
|
4016
|
+
return null;
|
|
4017
|
+
}
|
|
4018
|
+
function extractFromObjectLiteral(objLiteral) {
|
|
4019
|
+
if (!Node11.isObjectLiteralExpression(objLiteral)) return null;
|
|
4020
|
+
const properties = [];
|
|
4021
|
+
for (const prop of objLiteral.getProperties()) {
|
|
4022
|
+
if (!Node11.isPropertyAssignment(prop)) continue;
|
|
4023
|
+
const name = prop.getName();
|
|
4024
|
+
const initializer = prop.getInitializer();
|
|
4025
|
+
if (!initializer) continue;
|
|
4026
|
+
const type = inferExpressionType(initializer);
|
|
4027
|
+
properties.push({ name, type });
|
|
4028
|
+
}
|
|
4029
|
+
if (properties.length === 0) return null;
|
|
4030
|
+
const typeString = `{ ${properties.map((p) => `${p.name}: ${p.type}`).join("; ")} }`;
|
|
4031
|
+
return { typeString, properties, isImportRef: false };
|
|
4032
|
+
}
|
|
4033
|
+
function inferExpressionType(node) {
|
|
4034
|
+
if (Node11.isStringLiteral(node)) return "string";
|
|
4035
|
+
if (Node11.isTemplateExpression(node) || Node11.isNoSubstitutionTemplateLiteral(node))
|
|
4036
|
+
return "string";
|
|
4037
|
+
if (Node11.isNumericLiteral(node)) return "number";
|
|
4038
|
+
if (node.getKind() === SyntaxKind4.TrueKeyword || node.getKind() === SyntaxKind4.FalseKeyword) {
|
|
4039
|
+
return "boolean";
|
|
4040
|
+
}
|
|
4041
|
+
if (node.getKind() === SyntaxKind4.NullKeyword) return "null";
|
|
4042
|
+
if (Node11.isIdentifier(node) && node.getText() === "undefined") return "undefined";
|
|
4043
|
+
if (Node11.isObjectLiteralExpression(node)) {
|
|
4044
|
+
const props = node.getProperties();
|
|
4045
|
+
if (props.length === 0) return "Record<string, unknown>";
|
|
4046
|
+
const entries = [];
|
|
4047
|
+
for (const prop of props) {
|
|
4048
|
+
if (!Node11.isPropertyAssignment(prop)) continue;
|
|
4049
|
+
const key = prop.getName();
|
|
4050
|
+
const init = prop.getInitializer();
|
|
4051
|
+
if (!init) continue;
|
|
4052
|
+
entries.push(`${key}: ${inferExpressionType(init)}`);
|
|
4053
|
+
}
|
|
4054
|
+
if (entries.length === 0) return "Record<string, unknown>";
|
|
4055
|
+
return `{ ${entries.join("; ")} }`;
|
|
4056
|
+
}
|
|
4057
|
+
if (Node11.isArrayLiteralExpression(node)) {
|
|
4058
|
+
const elements = node.getElements();
|
|
4059
|
+
if (elements.length === 0) return "Array<unknown>";
|
|
4060
|
+
const first = elements[0];
|
|
4061
|
+
if (first) return `Array<${inferExpressionType(first)}>`;
|
|
4062
|
+
return "Array<unknown>";
|
|
4063
|
+
}
|
|
4064
|
+
if (Node11.isConditionalExpression(node)) {
|
|
4065
|
+
const whenTrue = inferExpressionType(node.getWhenTrue());
|
|
4066
|
+
const whenFalse = inferExpressionType(node.getWhenFalse());
|
|
4067
|
+
if (whenTrue === whenFalse) return whenTrue;
|
|
4068
|
+
return `${whenTrue} | ${whenFalse}`;
|
|
4069
|
+
}
|
|
4070
|
+
if (Node11.isParenthesizedExpression(node)) {
|
|
4071
|
+
return inferExpressionType(node.getExpression());
|
|
4072
|
+
}
|
|
4073
|
+
if (Node11.isAsExpression(node)) {
|
|
4074
|
+
const typeNode = node.getTypeNode();
|
|
4075
|
+
if (typeNode) return typeNode.getText();
|
|
4076
|
+
}
|
|
4077
|
+
return "unknown";
|
|
4078
|
+
}
|
|
4079
|
+
|
|
4080
|
+
// src/emit/emit-api.ts
|
|
4081
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
4082
|
+
import { isAbsolute as isAbsolute2, join as join5, relative as relative4 } from "path";
|
|
4083
|
+
|
|
3846
4084
|
// src/extension/registry.ts
|
|
4085
|
+
import { relative as relative3, resolve as resolve4 } from "path";
|
|
4086
|
+
import { Project as Project5 } from "ts-morph";
|
|
3847
4087
|
function resolveApiSlots(extensions) {
|
|
3848
4088
|
let layer;
|
|
3849
4089
|
let layerOwner;
|
|
@@ -5628,36 +5868,64 @@ async function discoverInputFiles(config) {
|
|
|
5628
5868
|
const matched = await fg3(globs, { cwd, absolute: true, onlyFiles: true });
|
|
5629
5869
|
return [...new Set(matched)].sort();
|
|
5630
5870
|
}
|
|
5631
|
-
|
|
5871
|
+
var READ_CONCURRENCY = 64;
|
|
5872
|
+
async function readAll(paths, read) {
|
|
5873
|
+
const out = /* @__PURE__ */ new Map();
|
|
5874
|
+
for (let i = 0; i < paths.length; i += READ_CONCURRENCY) {
|
|
5875
|
+
const batch = paths.slice(i, i + READ_CONCURRENCY);
|
|
5876
|
+
const texts = await Promise.all(batch.map(read));
|
|
5877
|
+
batch.forEach((path, j) => out.set(path, texts[j]));
|
|
5878
|
+
}
|
|
5879
|
+
return out;
|
|
5880
|
+
}
|
|
5881
|
+
async function readInputs(config) {
|
|
5882
|
+
const cwd = config.codegen.cwd;
|
|
5883
|
+
const tsconfigPath = resolveTsconfigPath(cwd, config.app?.tsconfig ?? void 0);
|
|
5884
|
+
const extended = loadDiscoveryTsconfig(tsconfigPath).files;
|
|
5885
|
+
const tsconfigFiles = extended.length > 0 ? extended : [tsconfigPath];
|
|
5886
|
+
const files = await discoverInputFiles(config);
|
|
5887
|
+
const contents = /* @__PURE__ */ new Map();
|
|
5888
|
+
const tsconfigs = await readAll(
|
|
5889
|
+
tsconfigFiles,
|
|
5890
|
+
(file) => readFile2(file, "utf8").catch(() => null)
|
|
5891
|
+
);
|
|
5892
|
+
for (const [file, text] of tsconfigs) contents.set(file, text);
|
|
5893
|
+
for (const [file, text] of await readAll(files, (file2) => readFile2(file2, "utf8"))) {
|
|
5894
|
+
contents.set(file, text);
|
|
5895
|
+
}
|
|
5896
|
+
return { tsconfigFiles, files, contents, tracked: /* @__PURE__ */ new Map() };
|
|
5897
|
+
}
|
|
5898
|
+
async function readInput(snapshot, path) {
|
|
5899
|
+
return snapshot.contents.get(path) ?? readFile2(path, "utf8");
|
|
5900
|
+
}
|
|
5901
|
+
async function computeInputsHash(config, extraInputs = [], snapshot) {
|
|
5902
|
+
const inputs = snapshot ?? await readInputs(config);
|
|
5632
5903
|
const hash = createHash("sha256");
|
|
5633
5904
|
hash.update(`version:${VERSION}
|
|
5634
5905
|
`);
|
|
5635
5906
|
hash.update(`config:${serializeConfig(config)}
|
|
5636
5907
|
`);
|
|
5637
5908
|
const cwd = config.codegen.cwd;
|
|
5638
|
-
const
|
|
5639
|
-
const tsconfigFiles = loadDiscoveryTsconfig(tsconfigPath).files;
|
|
5640
|
-
for (const file of tsconfigFiles.length > 0 ? tsconfigFiles : [tsconfigPath]) {
|
|
5641
|
-
const contents = await readFile2(file, "utf8").catch(() => null);
|
|
5909
|
+
for (const file of inputs.tsconfigFiles) {
|
|
5642
5910
|
hash.update(`tsconfig:${relative7(cwd, file)}
|
|
5643
5911
|
`);
|
|
5644
|
-
hash.update(contents ?? " missing");
|
|
5912
|
+
hash.update(inputs.contents.get(file) ?? " missing");
|
|
5645
5913
|
hash.update("\n");
|
|
5646
5914
|
}
|
|
5647
|
-
const
|
|
5648
|
-
const globbedRelative = new Set(globbed.map((file) => relative7(cwd, file)));
|
|
5915
|
+
const globbedRelative = new Set(inputs.files.map((file) => relative7(cwd, file)));
|
|
5649
5916
|
const extra = [...new Set(extraInputs)].filter((file) => !globbedRelative.has(file)).sort();
|
|
5650
|
-
for (const file of
|
|
5651
|
-
const contents = await readFile2(file, "utf8");
|
|
5917
|
+
for (const file of inputs.files) {
|
|
5652
5918
|
hash.update(`file:${relative7(cwd, file)}
|
|
5653
5919
|
`);
|
|
5654
|
-
hash.update(contents);
|
|
5920
|
+
hash.update(inputs.contents.get(file));
|
|
5655
5921
|
hash.update("\n");
|
|
5656
5922
|
}
|
|
5923
|
+
const unread = extra.map((file) => join13(cwd, file)).filter((abs) => !inputs.tracked.has(abs));
|
|
5924
|
+
for (const [abs, text] of await readAll(unread, readTrackedInput)) inputs.tracked.set(abs, text);
|
|
5657
5925
|
for (const file of extra) {
|
|
5658
5926
|
hash.update(`extra:${file}
|
|
5659
5927
|
`);
|
|
5660
|
-
hash.update(
|
|
5928
|
+
hash.update(inputs.tracked.get(join13(cwd, file)));
|
|
5661
5929
|
hash.update("\n");
|
|
5662
5930
|
}
|
|
5663
5931
|
return hash.digest("hex");
|
|
@@ -5738,14 +6006,15 @@ function driftGuardMessage(outDir, previousEntryPoint, currentEntryPoint, differ
|
|
|
5738
6006
|
async function generate(config, inputRoutes = [], entryPoint = "cli") {
|
|
5739
6007
|
setCodegenDebug(config.debug);
|
|
5740
6008
|
const manifest = await readManifest(config.codegen.outDir);
|
|
5741
|
-
const
|
|
6009
|
+
const inputs = await readInputs(config);
|
|
6010
|
+
const inputsHash = await computeInputsHash(config, manifest?.extraInputs ?? [], inputs);
|
|
5742
6011
|
if (await isManifestFresh(config.codegen.outDir, manifest, inputsHash)) {
|
|
5743
6012
|
console.log(`[nestjs-codegen] ${config.codegen.outDir} up to date, skipped`);
|
|
5744
6013
|
return;
|
|
5745
6014
|
}
|
|
5746
6015
|
const configHash = computeConfigHash(config);
|
|
5747
6016
|
const configKeyHashes = computeConfigKeyHashes(config);
|
|
5748
|
-
if (config.driftGuard && manifest?.entryPoint && manifest.entryPoint !== entryPoint && manifest.configHash && manifest.configHash !== configHash) {
|
|
6017
|
+
if (config.driftGuard && manifest?.version === VERSION && manifest?.entryPoint && manifest.entryPoint !== entryPoint && manifest.configHash && manifest.configHash !== configHash) {
|
|
5749
6018
|
throw new DriftGuardError(
|
|
5750
6019
|
driftGuardMessage(
|
|
5751
6020
|
config.codegen.outDir,
|
|
@@ -5766,7 +6035,7 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
|
|
|
5766
6035
|
}
|
|
5767
6036
|
if (config.pages) {
|
|
5768
6037
|
const pagesConfig = config.pages;
|
|
5769
|
-
const pages = await discoverPageScopes(config);
|
|
6038
|
+
const pages = await discoverPageScopes(config, (path) => readInput(inputs, path));
|
|
5770
6039
|
const sharedProps = discoverSharedPropsFromConfig(config);
|
|
5771
6040
|
await emitPages(pages, config.codegen.outDir, {
|
|
5772
6041
|
propsExport: pagesConfig.propsExport,
|
|
@@ -5816,7 +6085,7 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
|
|
|
5816
6085
|
}
|
|
5817
6086
|
const outputFiles = await listOutputFiles(config.codegen.outDir);
|
|
5818
6087
|
const extraInputs = [...trackedInputs].sort();
|
|
5819
|
-
const recordedHash = extraInputs.length > 0 ? await computeInputsHash(config, extraInputs) : inputsHash;
|
|
6088
|
+
const recordedHash = extraInputs.length > 0 ? await computeInputsHash(config, extraInputs, inputs) : inputsHash;
|
|
5820
6089
|
await writeManifest(config.codegen.outDir, {
|
|
5821
6090
|
version: VERSION,
|
|
5822
6091
|
hash: recordedHash,
|
|
@@ -5999,13 +6268,15 @@ async function watch(config, onChange, options = {}) {
|
|
|
5999
6268
|
pagesWatcher.on("unlink", schedulePagesRegenerate);
|
|
6000
6269
|
let contractsDebounceTimer;
|
|
6001
6270
|
const pendingChangedPaths = /* @__PURE__ */ new Set();
|
|
6271
|
+
let pendingAddedOrRemoved = false;
|
|
6002
6272
|
const contractsWatcher = chokidar.watch(join16(config.codegen.cwd, config.contracts.glob), {
|
|
6003
6273
|
ignoreInitial: true,
|
|
6004
6274
|
persistent: true,
|
|
6005
6275
|
awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
|
|
6006
6276
|
});
|
|
6007
|
-
function scheduleContractsRegenerate(changedPath) {
|
|
6277
|
+
function scheduleContractsRegenerate(changedPath, addedOrRemoved = false) {
|
|
6008
6278
|
if (typeof changedPath === "string") pendingChangedPaths.add(changedPath);
|
|
6279
|
+
if (addedOrRemoved) pendingAddedOrRemoved = true;
|
|
6009
6280
|
if (contractsDebounceTimer !== void 0) {
|
|
6010
6281
|
clearTimeout(contractsDebounceTimer);
|
|
6011
6282
|
}
|
|
@@ -6013,8 +6284,12 @@ async function watch(config, onChange, options = {}) {
|
|
|
6013
6284
|
contractsDebounceTimer = void 0;
|
|
6014
6285
|
const changed = [...pendingChangedPaths];
|
|
6015
6286
|
pendingChangedPaths.clear();
|
|
6287
|
+
const filesAddedOrRemoved = pendingAddedOrRemoved;
|
|
6288
|
+
pendingAddedOrRemoved = false;
|
|
6016
6289
|
try {
|
|
6017
|
-
const routes = await (await getDiscovery()).rediscover(changed
|
|
6290
|
+
const routes = await (await getDiscovery()).rediscover(changed, {
|
|
6291
|
+
filesAddedOrRemoved
|
|
6292
|
+
});
|
|
6018
6293
|
lastRoutes = routes;
|
|
6019
6294
|
await generateAndTrack(routes);
|
|
6020
6295
|
} catch (err) {
|
|
@@ -6026,20 +6301,25 @@ async function watch(config, onChange, options = {}) {
|
|
|
6026
6301
|
onChange?.();
|
|
6027
6302
|
}, config.contracts.debounceMs);
|
|
6028
6303
|
}
|
|
6029
|
-
contractsWatcher.on("add", (p) => scheduleContractsRegenerate(p));
|
|
6304
|
+
contractsWatcher.on("add", (p) => scheduleContractsRegenerate(p, true));
|
|
6030
6305
|
contractsWatcher.on("change", (p) => scheduleContractsRegenerate(p));
|
|
6031
|
-
contractsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p));
|
|
6306
|
+
contractsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p, true));
|
|
6032
6307
|
const formsWatcher = chokidar.watch(join16(config.codegen.cwd, config.forms.watch), {
|
|
6033
6308
|
ignoreInitial: true,
|
|
6034
6309
|
persistent: true,
|
|
6035
6310
|
awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
|
|
6036
6311
|
});
|
|
6037
|
-
formsWatcher.on("add", (p) => scheduleContractsRegenerate(p));
|
|
6312
|
+
formsWatcher.on("add", (p) => scheduleContractsRegenerate(p, true));
|
|
6038
6313
|
formsWatcher.on("change", (p) => scheduleContractsRegenerate(p));
|
|
6039
|
-
formsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p));
|
|
6040
|
-
trackedWatcher.on("add", (p) => scheduleContractsRegenerate(p));
|
|
6314
|
+
formsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p, true));
|
|
6315
|
+
trackedWatcher.on("add", (p) => scheduleContractsRegenerate(p, true));
|
|
6041
6316
|
trackedWatcher.on("change", (p) => scheduleContractsRegenerate(p));
|
|
6042
|
-
trackedWatcher.on("unlink", (p) => scheduleContractsRegenerate(p));
|
|
6317
|
+
trackedWatcher.on("unlink", (p) => scheduleContractsRegenerate(p, true));
|
|
6318
|
+
await Promise.all(
|
|
6319
|
+
[pagesWatcher, contractsWatcher, formsWatcher].map(
|
|
6320
|
+
(w) => new Promise((resolveReady) => w.once("ready", () => resolveReady()))
|
|
6321
|
+
)
|
|
6322
|
+
);
|
|
6043
6323
|
return {
|
|
6044
6324
|
close: async () => {
|
|
6045
6325
|
if (pagesDebounceTimer !== void 0) {
|
|
@@ -6143,7 +6423,7 @@ function createChainModuleRenderer(opts) {
|
|
|
6143
6423
|
}
|
|
6144
6424
|
|
|
6145
6425
|
// src/extension/genui-sandbox-kit.ts
|
|
6146
|
-
import { existsSync as existsSync2, statSync } from "fs";
|
|
6426
|
+
import { existsSync as existsSync2, statSync as statSync2 } from "fs";
|
|
6147
6427
|
import { dirname as dirname5, isAbsolute as isAbsolute3, resolve as resolve6 } from "path";
|
|
6148
6428
|
import fg4 from "fast-glob";
|
|
6149
6429
|
var GENUI_KIT_MODULE = "@dudousxd/nestjs-agent-core/genui/kit";
|
|
@@ -6155,11 +6435,6 @@ var DEFAULT_KIT_DIRS = [
|
|
|
6155
6435
|
"app/components/ui",
|
|
6156
6436
|
"components/ui"
|
|
6157
6437
|
];
|
|
6158
|
-
var LEGACY_OPTIONS = {
|
|
6159
|
-
entry: "`entry: X` is now `components: X`",
|
|
6160
|
-
include: "`include: G` is now `components: G` (globs, files, directories or packages)",
|
|
6161
|
-
css: "`css: G` is now `styles: G` (the app's style entry files)"
|
|
6162
|
-
};
|
|
6163
6438
|
var MIN_CORE = "0.53.0";
|
|
6164
6439
|
async function loadKitModule(specifier = GENUI_KIT_MODULE) {
|
|
6165
6440
|
let mod;
|
|
@@ -6210,7 +6485,7 @@ function resolveKitFilesLocally(root, options) {
|
|
|
6210
6485
|
continue;
|
|
6211
6486
|
}
|
|
6212
6487
|
const path = isAbsolute3(spec) ? spec : resolve6(root, spec);
|
|
6213
|
-
if (existsSync2(path) &&
|
|
6488
|
+
if (existsSync2(path) && statSync2(path).isDirectory()) {
|
|
6214
6489
|
files.push(...fg4.sync("*.{tsx,jsx,ts,js}", { cwd: path, absolute: true, onlyFiles: true }));
|
|
6215
6490
|
dirs.add(path);
|
|
6216
6491
|
} else if (existsSync2(path)) {
|
|
@@ -6226,14 +6501,6 @@ function resolveKitFilesLocally(root, options) {
|
|
|
6226
6501
|
return { files: [...new Set(files)].sort(), dirs: [...dirs] };
|
|
6227
6502
|
}
|
|
6228
6503
|
function genuiSandboxKit(options = {}) {
|
|
6229
|
-
const legacy = Object.keys(LEGACY_OPTIONS).filter(
|
|
6230
|
-
(key) => options[key] !== void 0
|
|
6231
|
-
);
|
|
6232
|
-
if (legacy.length > 0) {
|
|
6233
|
-
throw new CodegenError(
|
|
6234
|
-
`genuiSandboxKit(): ${legacy.map((key) => `\`${key}\``).join(", ")} ${legacy.length > 1 ? "were" : "was"} removed \u2014 ${legacy.map((key) => LEGACY_OPTIONS[key]).join("; ")}. The options are now the agent's Vite plugin's: components, styles, wrapper, stylesDocs.`
|
|
6235
|
-
);
|
|
6236
|
-
}
|
|
6237
6504
|
const output = options.output ?? DEFAULT_SANDBOX_KIT_OUTPUT;
|
|
6238
6505
|
const source = {
|
|
6239
6506
|
...options.components !== void 0 ? { components: options.components } : {},
|
|
@@ -6295,7 +6562,7 @@ function genuiSandboxKit(options = {}) {
|
|
|
6295
6562
|
}
|
|
6296
6563
|
|
|
6297
6564
|
// src/index.ts
|
|
6298
|
-
var VERSION = "0.
|
|
6565
|
+
var VERSION = "0.30.0";
|
|
6299
6566
|
export {
|
|
6300
6567
|
CodegenError,
|
|
6301
6568
|
ConfigError,
|