@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/dist/cli/main.cjs CHANGED
@@ -134,11 +134,6 @@ function validateUserConfig(userConfig) {
134
134
  );
135
135
  }
136
136
  }
137
- if (userConfig.scopes && Object.keys(userConfig.scopes).length > 0) {
138
- console.warn(
139
- "[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)."
140
- );
141
- }
142
137
  }
143
138
  function isGlobInput(value) {
144
139
  if (typeof value === "string") return true;
@@ -186,7 +181,6 @@ function applyDefaults(userConfig, cwd) {
186
181
  glob: userConfig.contracts?.glob ?? "src/**/*.controller.ts",
187
182
  debounceMs: userConfig.contracts?.debounceMs ?? 500
188
183
  },
189
- scopes: userConfig.scopes ?? {},
190
184
  codegen: {
191
185
  outDir,
192
186
  cwd: resolvedCwd
@@ -281,7 +275,7 @@ async function discoverPages(opts) {
281
275
  const rel = (0, import_node_path2.relative)(opts.cwd, file);
282
276
  const nameRel = (0, import_node_path2.relative)(pagesBase, file).split(import_node_path2.sep).join("/");
283
277
  const name = `${prefix}${computeName(nameRel, opts.componentNameStrategy)}`;
284
- const source = await (0, import_promises2.readFile)(file, "utf8");
278
+ const source = await (opts.read ? opts.read(file) : (0, import_promises2.readFile)(file, "utf8"));
285
279
  const propsSource = extractPropsSource(source, opts.propsExport);
286
280
  out.push({ name, absolutePath: file, relativePath: rel, propsSource, scope });
287
281
  }
@@ -289,10 +283,14 @@ async function discoverPages(opts) {
289
283
  assertUniqueNames(out);
290
284
  return out;
291
285
  }
292
- async function discoverPageScopes(config) {
286
+ async function discoverPageScopes(config, read) {
293
287
  const pages = config.pages;
294
288
  if (!pages) return [];
295
- const common = { cwd: config.codegen.cwd, propsExport: pages.propsExport };
289
+ const common = {
290
+ cwd: config.codegen.cwd,
291
+ propsExport: pages.propsExport,
292
+ ...read ? { read } : {}
293
+ };
296
294
  const out = await discoverPages({
297
295
  ...common,
298
296
  glob: pages.glob,
@@ -363,297 +361,60 @@ function extractPropsSource(source, exportName) {
363
361
  }
364
362
 
365
363
  // src/discovery/shared-props.ts
366
- var import_node_path3 = require("path");
367
- var import_ts_morph = require("ts-morph");
368
- function discoverSharedProps(project, moduleEntry) {
369
- try {
370
- let sourceFile = project.getSourceFile(moduleEntry);
371
- if (!sourceFile) {
364
+ var import_node_fs3 = require("fs");
365
+ var import_node_path5 = require("path");
366
+ var import_ts_morph13 = require("ts-morph");
367
+
368
+ // src/discovery/contracts-fast.ts
369
+ var import_node_fs2 = require("fs");
370
+ var import_node_path4 = require("path");
371
+ var import_fast_glob2 = __toESM(require("fast-glob"), 1);
372
+ var import_ts_morph12 = require("ts-morph");
373
+
374
+ // src/discovery/disk-fingerprints.ts
375
+ var import_node_fs = require("fs");
376
+ var DiskFingerprints = class {
377
+ stats = /* @__PURE__ */ new Map();
378
+ /** The paths whose disk content differs from `text`, or that no longer exist. */
379
+ changed(files) {
380
+ const changed = [];
381
+ for (const { path, text } of files) {
382
+ let key;
372
383
  try {
373
- sourceFile = project.addSourceFileAtPath(moduleEntry);
384
+ const stat2 = (0, import_node_fs.statSync)(path);
385
+ key = `${stat2.mtimeMs}:${stat2.size}`;
374
386
  } catch {
375
- return null;
376
- }
377
- }
378
- const forRootCall = findForRootCall(sourceFile);
379
- if (!forRootCall) return null;
380
- const initializer = findShareInitializer(forRootCall);
381
- if (!initializer) return null;
382
- return extractShareType(initializer, sourceFile, project);
383
- } catch {
384
- return null;
385
- }
386
- }
387
- function discoverSharedPropsFromConfig(config) {
388
- if (!config.app?.moduleEntry) return null;
389
- try {
390
- const tsconfigPath = config.app.tsconfig ?? (0, import_node_path3.join)(config.codegen.cwd, "tsconfig.json");
391
- let project;
392
- try {
393
- project = new import_ts_morph.Project({
394
- tsConfigFilePath: tsconfigPath,
395
- skipAddingFilesFromTsConfig: true,
396
- skipLoadingLibFiles: true,
397
- skipFileDependencyResolution: true
398
- });
399
- } catch {
400
- project = new import_ts_morph.Project({
401
- skipAddingFilesFromTsConfig: true,
402
- skipLoadingLibFiles: true,
403
- skipFileDependencyResolution: true,
404
- compilerOptions: { allowJs: true, strict: false }
405
- });
406
- }
407
- return discoverSharedProps(project, config.app.moduleEntry);
408
- } catch {
409
- return null;
410
- }
411
- }
412
- function findForRootCall(sourceFile) {
413
- const callExpressions = sourceFile.getDescendantsOfKind(import_ts_morph.SyntaxKind.CallExpression);
414
- for (const call of callExpressions) {
415
- const expr = call.getExpression();
416
- if (!import_ts_morph.Node.isPropertyAccessExpression(expr)) continue;
417
- const methodName = expr.getName();
418
- const objectExpr = expr.getExpression();
419
- if (methodName === "forRoot" && import_ts_morph.Node.isIdentifier(objectExpr)) {
420
- const name = objectExpr.getText();
421
- if (name === "InertiaModule") {
422
- return call;
423
- }
424
- }
425
- }
426
- return null;
427
- }
428
- function findShareInitializer(forRootCall) {
429
- if (!import_ts_morph.Node.isCallExpression(forRootCall)) return null;
430
- const args = forRootCall.getArguments();
431
- const firstArg2 = args[0];
432
- if (!firstArg2 || !import_ts_morph.Node.isObjectLiteralExpression(firstArg2)) return null;
433
- for (const prop of firstArg2.getProperties()) {
434
- if (import_ts_morph.Node.isPropertyAssignment(prop) && prop.getName() === "share") {
435
- return prop.getInitializer() ?? null;
436
- }
437
- }
438
- return null;
439
- }
440
- function extractShareType(node, sourceFile, project) {
441
- if (import_ts_morph.Node.isIdentifier(node)) {
442
- const ref = resolveIdentifierToImportRef(node, sourceFile, project);
443
- if (ref) {
444
- return {
445
- typeString: `Awaited<ReturnType<typeof import('${ref.importPath}').${ref.exportName}>>`,
446
- properties: null,
447
- isImportRef: true
448
- };
449
- }
450
- }
451
- if (import_ts_morph.Node.isArrowFunction(node)) {
452
- const result = extractFromFunctionLike(node, sourceFile);
453
- return result ? { ...result, isImportRef: false } : null;
454
- }
455
- if (import_ts_morph.Node.isFunctionExpression(node)) {
456
- const result = extractFromFunctionLike(node, sourceFile);
457
- return result ? { ...result, isImportRef: false } : null;
458
- }
459
- if (import_ts_morph.Node.isObjectLiteralExpression(node)) {
460
- const result = extractFromObjectLiteral(node);
461
- return result ? { ...result, isImportRef: false } : null;
462
- }
463
- return null;
464
- }
465
- function resolveIdentifierToImportRef(id, sourceFile, project) {
466
- if (!import_ts_morph.Node.isIdentifier(id)) return null;
467
- const name = id.getText();
468
- const localFunc = sourceFile.getFunction(name);
469
- if (localFunc?.isExported()) {
470
- const filePath = sourceFile.getFilePath().replace(/\.ts$/, "");
471
- return { importPath: filePath, exportName: name };
472
- }
473
- const localVar = sourceFile.getVariableDeclaration(name);
474
- if (localVar?.isExported()) {
475
- const filePath = sourceFile.getFilePath().replace(/\.ts$/, "");
476
- return { importPath: filePath, exportName: name };
477
- }
478
- for (const imp of sourceFile.getImportDeclarations()) {
479
- for (const named of imp.getNamedImports()) {
480
- const importedName = named.getAliasNode()?.getText() ?? named.getName();
481
- if (importedName !== name) continue;
482
- const resolvedSource = imp.getModuleSpecifierSourceFile();
483
- if (!resolvedSource) continue;
484
- const originalName = named.getName();
485
- const fn = resolvedSource.getFunction(originalName);
486
- if (fn?.isExported()) {
487
- const filePath = resolvedSource.getFilePath().replace(/\.ts$/, "");
488
- return { importPath: filePath, exportName: originalName };
489
- }
490
- const v = resolvedSource.getVariableDeclaration(originalName);
491
- if (v?.isExported()) {
492
- const filePath = resolvedSource.getFilePath().replace(/\.ts$/, "");
493
- return { importPath: filePath, exportName: originalName };
494
- }
495
- }
496
- }
497
- return null;
498
- }
499
- function extractFromFunctionLike(node, _sourceFile) {
500
- const returnTypeNode = import_ts_morph.Node.isArrowFunction(node) || import_ts_morph.Node.isFunctionExpression(node) ? node.getReturnTypeNode() : null;
501
- if (returnTypeNode) {
502
- return extractFromReturnTypeAnnotation(returnTypeNode);
503
- }
504
- if (import_ts_morph.Node.isArrowFunction(node)) {
505
- const body = node.getBody();
506
- if (import_ts_morph.Node.isParenthesizedExpression(body)) {
507
- const inner = body.getExpression();
508
- if (import_ts_morph.Node.isObjectLiteralExpression(inner)) {
509
- return extractFromObjectLiteral(inner);
510
- }
511
- }
512
- if (import_ts_morph.Node.isObjectLiteralExpression(body)) {
513
- return extractFromObjectLiteral(body);
514
- }
515
- if (import_ts_morph.Node.isBlock(body)) {
516
- return extractFromBlockReturn(body);
517
- }
518
- }
519
- if (import_ts_morph.Node.isFunctionExpression(node)) {
520
- const body = node.getBody();
521
- if (import_ts_morph.Node.isBlock(body)) {
522
- return extractFromBlockReturn(body);
523
- }
524
- }
525
- return null;
526
- }
527
- function extractFromReturnTypeAnnotation(typeNode) {
528
- if (import_ts_morph.Node.isTypeReference(typeNode)) {
529
- const typeName = typeNode.getTypeName();
530
- if (import_ts_morph.Node.isIdentifier(typeName) && typeName.getText() === "Promise") {
531
- const typeArgs = typeNode.getTypeArguments();
532
- const firstArg2 = typeArgs[0];
533
- if (firstArg2) {
534
- return extractFromReturnTypeAnnotation(firstArg2);
387
+ this.stats.delete(path);
388
+ changed.push(path);
389
+ continue;
535
390
  }
536
- return null;
537
- }
538
- }
539
- if (import_ts_morph.Node.isTypeLiteral(typeNode)) {
540
- const properties = [];
541
- for (const member of typeNode.getMembers()) {
542
- if (import_ts_morph.Node.isPropertySignature(member)) {
543
- const name = member.getName();
544
- const memberTypeNode = member.getTypeNode();
545
- const type = memberTypeNode ? memberTypeNode.getText() : "unknown";
546
- properties.push({ name, type });
391
+ if (this.stats.get(path) === key) continue;
392
+ let disk;
393
+ try {
394
+ disk = (0, import_node_fs.readFileSync)(path, "utf-8");
395
+ } catch {
396
+ disk = void 0;
547
397
  }
548
- }
549
- if (properties.length === 0) return null;
550
- const typeString = `{ ${properties.map((p) => `${p.name}: ${p.type}`).join("; ")} }`;
551
- return { typeString, properties, isImportRef: false };
552
- }
553
- return null;
554
- }
555
- function extractFromBlockReturn(block) {
556
- if (!import_ts_morph.Node.isBlock(block)) return null;
557
- const statements = block.getStatements();
558
- for (let i = statements.length - 1; i >= 0; i--) {
559
- const stmt = statements[i];
560
- if (!import_ts_morph.Node.isReturnStatement(stmt)) continue;
561
- const expr = stmt.getExpression();
562
- if (!expr) continue;
563
- if (import_ts_morph.Node.isObjectLiteralExpression(expr)) {
564
- return extractFromObjectLiteral(expr);
565
- }
566
- if (import_ts_morph.Node.isParenthesizedExpression(expr)) {
567
- const inner = expr.getExpression();
568
- if (import_ts_morph.Node.isObjectLiteralExpression(inner)) {
569
- return extractFromObjectLiteral(inner);
398
+ if (disk === text) {
399
+ this.stats.set(path, key);
400
+ } else {
401
+ this.stats.delete(path);
402
+ changed.push(path);
570
403
  }
571
404
  }
572
- break;
573
- }
574
- return null;
575
- }
576
- function extractFromObjectLiteral(objLiteral) {
577
- if (!import_ts_morph.Node.isObjectLiteralExpression(objLiteral)) return null;
578
- const properties = [];
579
- for (const prop of objLiteral.getProperties()) {
580
- if (!import_ts_morph.Node.isPropertyAssignment(prop)) continue;
581
- const name = prop.getName();
582
- const initializer = prop.getInitializer();
583
- if (!initializer) continue;
584
- const type = inferExpressionType(initializer);
585
- properties.push({ name, type });
586
- }
587
- if (properties.length === 0) return null;
588
- const typeString = `{ ${properties.map((p) => `${p.name}: ${p.type}`).join("; ")} }`;
589
- return { typeString, properties, isImportRef: false };
590
- }
591
- function inferExpressionType(node) {
592
- if (import_ts_morph.Node.isStringLiteral(node)) return "string";
593
- if (import_ts_morph.Node.isTemplateExpression(node) || import_ts_morph.Node.isNoSubstitutionTemplateLiteral(node))
594
- return "string";
595
- if (import_ts_morph.Node.isNumericLiteral(node)) return "number";
596
- if (node.getKind() === import_ts_morph.SyntaxKind.TrueKeyword || node.getKind() === import_ts_morph.SyntaxKind.FalseKeyword) {
597
- return "boolean";
598
- }
599
- if (node.getKind() === import_ts_morph.SyntaxKind.NullKeyword) return "null";
600
- if (import_ts_morph.Node.isIdentifier(node) && node.getText() === "undefined") return "undefined";
601
- if (import_ts_morph.Node.isObjectLiteralExpression(node)) {
602
- const props = node.getProperties();
603
- if (props.length === 0) return "Record<string, unknown>";
604
- const entries = [];
605
- for (const prop of props) {
606
- if (!import_ts_morph.Node.isPropertyAssignment(prop)) continue;
607
- const key = prop.getName();
608
- const init = prop.getInitializer();
609
- if (!init) continue;
610
- entries.push(`${key}: ${inferExpressionType(init)}`);
611
- }
612
- if (entries.length === 0) return "Record<string, unknown>";
613
- return `{ ${entries.join("; ")} }`;
614
- }
615
- if (import_ts_morph.Node.isArrayLiteralExpression(node)) {
616
- const elements = node.getElements();
617
- if (elements.length === 0) return "Array<unknown>";
618
- const first = elements[0];
619
- if (first) return `Array<${inferExpressionType(first)}>`;
620
- return "Array<unknown>";
405
+ return changed;
621
406
  }
622
- if (import_ts_morph.Node.isConditionalExpression(node)) {
623
- const whenTrue = inferExpressionType(node.getWhenTrue());
624
- const whenFalse = inferExpressionType(node.getWhenFalse());
625
- if (whenTrue === whenFalse) return whenTrue;
626
- return `${whenTrue} | ${whenFalse}`;
627
- }
628
- if (import_ts_morph.Node.isParenthesizedExpression(node)) {
629
- return inferExpressionType(node.getExpression());
630
- }
631
- if (import_ts_morph.Node.isAsExpression(node)) {
632
- const typeNode = node.getTypeNode();
633
- if (typeNode) return typeNode.getText();
407
+ /** Forget `path`, so its next check compares content again. */
408
+ forget(path) {
409
+ this.stats.delete(path);
634
410
  }
635
- return "unknown";
636
- }
637
-
638
- // src/emit/emit-api.ts
639
- var import_promises3 = require("fs/promises");
640
- var import_node_path7 = require("path");
641
-
642
- // src/extension/registry.ts
643
- var import_node_path6 = require("path");
644
- var import_ts_morph13 = require("ts-morph");
645
-
646
- // src/discovery/contracts-fast.ts
647
- var import_node_fs = require("fs");
648
- var import_node_path5 = require("path");
649
- var import_fast_glob2 = __toESM(require("fast-glob"), 1);
650
- var import_ts_morph12 = require("ts-morph");
411
+ };
651
412
 
652
413
  // src/discovery/dto-type-resolver.ts
653
414
  var import_ts_morph11 = require("ts-morph");
654
415
 
655
416
  // src/discovery/dto-to-ir.ts
656
- var import_ts_morph3 = require("ts-morph");
417
+ var import_ts_morph2 = require("ts-morph");
657
418
 
658
419
  // src/util/debug-log.ts
659
420
  var debugEnabled = false;
@@ -665,8 +426,8 @@ function debugWarn(message) {
665
426
  }
666
427
 
667
428
  // src/discovery/type-ref-resolution.ts
668
- var import_node_path4 = require("path");
669
- var import_ts_morph2 = require("ts-morph");
429
+ var import_node_path3 = require("path");
430
+ var import_ts_morph = require("ts-morph");
670
431
  var _EMPTY_CTX = { projectRoot: "", tsconfigPaths: null };
671
432
  var _ctxByProject = /* @__PURE__ */ new WeakMap();
672
433
  function setDiscoveryContext(project, ctx) {
@@ -707,17 +468,17 @@ function findTypeInFile(name, file) {
707
468
  }
708
469
  function resolveModuleSpecifier(moduleSpecifier, sourceFile, project) {
709
470
  if (moduleSpecifier.startsWith(".")) {
710
- const dir = (0, import_node_path4.dirname)(sourceFile.getFilePath());
471
+ const dir = (0, import_node_path3.dirname)(sourceFile.getFilePath());
711
472
  const ext = /\.(c|m)?(j|t)sx?$/.exec(moduleSpecifier)?.[0];
712
473
  const base = ext ? moduleSpecifier.slice(0, -ext.length) : moduleSpecifier;
713
474
  const flavour = ext?.startsWith(".c") ? "c" : ext?.startsWith(".m") ? "m" : "";
714
475
  const candidates = [
715
- (0, import_node_path4.resolve)(dir, `${base}.${flavour}ts`),
716
- ...flavour === "" ? [(0, import_node_path4.resolve)(dir, `${base}.tsx`)] : [],
717
- (0, import_node_path4.resolve)(dir, `${moduleSpecifier}.ts`),
718
- (0, import_node_path4.resolve)(dir, moduleSpecifier, "index.ts"),
719
- (0, import_node_path4.resolve)(dir, `${base}.d.${flavour}ts`),
720
- ...ext === void 0 ? [(0, import_node_path4.resolve)(dir, moduleSpecifier, "index.d.ts")] : []
476
+ (0, import_node_path3.resolve)(dir, `${base}.${flavour}ts`),
477
+ ...flavour === "" ? [(0, import_node_path3.resolve)(dir, `${base}.tsx`)] : [],
478
+ (0, import_node_path3.resolve)(dir, `${moduleSpecifier}.ts`),
479
+ (0, import_node_path3.resolve)(dir, moduleSpecifier, "index.ts"),
480
+ (0, import_node_path3.resolve)(dir, `${base}.d.${flavour}ts`),
481
+ ...ext === void 0 ? [(0, import_node_path3.resolve)(dir, moduleSpecifier, "index.d.ts")] : []
721
482
  ];
722
483
  return [...new Set(candidates)];
723
484
  }
@@ -739,8 +500,8 @@ function resolveModuleSpecifier(moduleSpecifier, sourceFile, project) {
739
500
  const rest = moduleSpecifier.slice(prefix.length);
740
501
  const candidates = [];
741
502
  for (const mapping of mappings) {
742
- const resolved = (0, import_node_path4.resolve)(baseUrl, mapping.replace("*", rest));
743
- candidates.push(`${resolved}.ts`, (0, import_node_path4.resolve)(resolved, "index.ts"));
503
+ const resolved = (0, import_node_path3.resolve)(baseUrl, mapping.replace("*", rest));
504
+ candidates.push(`${resolved}.ts`, (0, import_node_path3.resolve)(resolved, "index.ts"));
744
505
  }
745
506
  dbg(" resolved candidates:", candidates);
746
507
  return candidates;
@@ -931,13 +692,13 @@ function resolveTypeRef(nodeOrName, sourceFile, project, opts) {
931
692
  name = nodeOrName;
932
693
  } else {
933
694
  const typeNode = nodeOrName;
934
- if (opts.unwrapContainers && import_ts_morph2.Node.isArrayTypeNode(typeNode)) {
695
+ if (opts.unwrapContainers && import_ts_morph.Node.isArrayTypeNode(typeNode)) {
935
696
  const inner = resolveTypeRef(typeNode.getElementTypeNode(), sourceFile, project, opts);
936
697
  return inner ? { ...inner, isArray: true } : null;
937
698
  }
938
- if (!import_ts_morph2.Node.isTypeReference(typeNode)) return null;
699
+ if (!import_ts_morph.Node.isTypeReference(typeNode)) return null;
939
700
  const typeName = typeNode.getTypeName();
940
- const refName = import_ts_morph2.Node.isIdentifier(typeName) ? typeName.getText() : null;
701
+ const refName = import_ts_morph.Node.isIdentifier(typeName) ? typeName.getText() : null;
941
702
  if (!refName) return null;
942
703
  if (opts.unwrapContainers && refName === "Promise") {
943
704
  const first = typeNode.getTypeArguments()[0];
@@ -1071,7 +832,7 @@ function buildProperty(prop, classFile, ctx) {
1071
832
  const dec = (n) => decorators.get(n);
1072
833
  const typeNode = prop.getTypeNode();
1073
834
  const typeText = typeNode?.getText() ?? "unknown";
1074
- const isArrayType = !!typeNode && import_ts_morph3.Node.isArrayTypeNode(typeNode);
835
+ const isArrayType = !!typeNode && import_ts_morph2.Node.isArrayTypeNode(typeNode);
1075
836
  const asField = (child) => applyPresence(
1076
837
  has("IsArray") || isArrayType ? { kind: "array", element: child } : child,
1077
838
  decorators
@@ -1285,22 +1046,22 @@ function firstArgText(decorator) {
1285
1046
  }
1286
1047
  function numericArg(decorator) {
1287
1048
  const arg = firstArg(decorator);
1288
- if (arg && import_ts_morph3.Node.isNumericLiteral(arg)) return arg.getText();
1049
+ if (arg && import_ts_morph2.Node.isNumericLiteral(arg)) return arg.getText();
1289
1050
  return null;
1290
1051
  }
1291
1052
  function numericArgs(decorator) {
1292
1053
  const args = decorator?.getArguments() ?? [];
1293
- const num = (n) => n && import_ts_morph3.Node.isNumericLiteral(n) ? n.getText() : null;
1054
+ const num = (n) => n && import_ts_morph2.Node.isNumericLiteral(n) ? n.getText() : null;
1294
1055
  return [num(args[0]), num(args[1])];
1295
1056
  }
1296
1057
  function messageRaw(decorator) {
1297
1058
  const args = decorator?.getArguments() ?? [];
1298
1059
  for (const arg of args) {
1299
- if (import_ts_morph3.Node.isObjectLiteralExpression(arg)) {
1060
+ if (import_ts_morph2.Node.isObjectLiteralExpression(arg)) {
1300
1061
  for (const prop of arg.getProperties()) {
1301
- if (import_ts_morph3.Node.isPropertyAssignment(prop) && prop.getName() === "message") {
1062
+ if (import_ts_morph2.Node.isPropertyAssignment(prop) && prop.getName() === "message") {
1302
1063
  const init = prop.getInitializer();
1303
- if (init && import_ts_morph3.Node.isStringLiteral(init)) return init.getText();
1064
+ if (init && import_ts_morph2.Node.isStringLiteral(init)) return init.getText();
1304
1065
  }
1305
1066
  }
1306
1067
  }
@@ -1309,30 +1070,30 @@ function messageRaw(decorator) {
1309
1070
  }
1310
1071
  function resolveDiscriminator(decorator) {
1311
1072
  const optsArg = decorator?.getArguments()[1];
1312
- if (!optsArg || !import_ts_morph3.Node.isObjectLiteralExpression(optsArg)) return null;
1073
+ if (!optsArg || !import_ts_morph2.Node.isObjectLiteralExpression(optsArg)) return null;
1313
1074
  let discProp;
1314
1075
  for (const prop of optsArg.getProperties()) {
1315
- if (import_ts_morph3.Node.isPropertyAssignment(prop) && prop.getName() === "discriminator") {
1076
+ if (import_ts_morph2.Node.isPropertyAssignment(prop) && prop.getName() === "discriminator") {
1316
1077
  discProp = prop.getInitializer();
1317
1078
  }
1318
1079
  }
1319
- if (!discProp || !import_ts_morph3.Node.isObjectLiteralExpression(discProp)) return null;
1080
+ if (!discProp || !import_ts_morph2.Node.isObjectLiteralExpression(discProp)) return null;
1320
1081
  let property = null;
1321
1082
  const subTypes = [];
1322
1083
  for (const prop of discProp.getProperties()) {
1323
- if (!import_ts_morph3.Node.isPropertyAssignment(prop)) continue;
1084
+ if (!import_ts_morph2.Node.isPropertyAssignment(prop)) continue;
1324
1085
  const name = prop.getName();
1325
1086
  const init = prop.getInitializer();
1326
1087
  if (!init) continue;
1327
- if (name === "property" && import_ts_morph3.Node.isStringLiteral(init)) {
1088
+ if (name === "property" && import_ts_morph2.Node.isStringLiteral(init)) {
1328
1089
  property = init.getLiteralValue();
1329
- } else if (name === "subTypes" && import_ts_morph3.Node.isArrayLiteralExpression(init)) {
1090
+ } else if (name === "subTypes" && import_ts_morph2.Node.isArrayLiteralExpression(init)) {
1330
1091
  for (const el of init.getElements()) {
1331
- if (!import_ts_morph3.Node.isObjectLiteralExpression(el)) continue;
1092
+ if (!import_ts_morph2.Node.isObjectLiteralExpression(el)) continue;
1332
1093
  for (const p of el.getProperties()) {
1333
- if (!import_ts_morph3.Node.isPropertyAssignment(p) || p.getName() !== "name") continue;
1094
+ if (!import_ts_morph2.Node.isPropertyAssignment(p) || p.getName() !== "name") continue;
1334
1095
  const nameInit = p.getInitializer();
1335
- if (nameInit && import_ts_morph3.Node.isIdentifier(nameInit)) subTypes.push(nameInit.getText());
1096
+ if (nameInit && import_ts_morph2.Node.isIdentifier(nameInit)) subTypes.push(nameInit.getText());
1336
1097
  }
1337
1098
  }
1338
1099
  }
@@ -1343,9 +1104,9 @@ function resolveDiscriminator(decorator) {
1343
1104
  function resolveTypeFactoryName(decorator) {
1344
1105
  const arg = firstArg(decorator);
1345
1106
  if (!arg) return null;
1346
- if (import_ts_morph3.Node.isArrowFunction(arg)) {
1107
+ if (import_ts_morph2.Node.isArrowFunction(arg)) {
1347
1108
  const body = arg.getBody();
1348
- if (import_ts_morph3.Node.isIdentifier(body)) return body.getText();
1109
+ if (import_ts_morph2.Node.isIdentifier(body)) return body.getText();
1349
1110
  }
1350
1111
  return null;
1351
1112
  }
@@ -1354,12 +1115,12 @@ function singularClassName(typeText) {
1354
1115
  return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(inner) ? inner : null;
1355
1116
  }
1356
1117
  function genericTypeArgNames(typeNode) {
1357
- if (!typeNode || !import_ts_morph3.Node.isTypeReference(typeNode)) return [];
1118
+ if (!typeNode || !import_ts_morph2.Node.isTypeReference(typeNode)) return [];
1358
1119
  const names = [];
1359
1120
  for (const arg of typeNode.getTypeArguments()) {
1360
- if (!import_ts_morph3.Node.isTypeReference(arg)) return [];
1121
+ if (!import_ts_morph2.Node.isTypeReference(arg)) return [];
1361
1122
  const tn = arg.getTypeName();
1362
- if (!import_ts_morph3.Node.isIdentifier(tn)) return [];
1123
+ if (!import_ts_morph2.Node.isIdentifier(tn)) return [];
1363
1124
  names.push(tn.getText());
1364
1125
  }
1365
1126
  return names;
@@ -1367,7 +1128,7 @@ function genericTypeArgNames(typeNode) {
1367
1128
  function enumSchemaFromDecorator(decorator, classFile, ctx) {
1368
1129
  const arg = firstArg(decorator);
1369
1130
  if (!arg) return null;
1370
- if (import_ts_morph3.Node.isIdentifier(arg)) {
1131
+ if (import_ts_morph2.Node.isIdentifier(arg)) {
1371
1132
  const name = arg.getText();
1372
1133
  const resolved = findType(name, classFile, ctx.project);
1373
1134
  if (resolved && resolved.kind === "enum") {
@@ -1381,12 +1142,12 @@ function enumSchemaFromDecorator(decorator, classFile, ctx) {
1381
1142
  }
1382
1143
  return { kind: "unknown", note: `@IsEnum(${name}): enum not resolvable to literals` };
1383
1144
  }
1384
- if (import_ts_morph3.Node.isObjectLiteralExpression(arg)) {
1145
+ if (import_ts_morph2.Node.isObjectLiteralExpression(arg)) {
1385
1146
  const values = [];
1386
1147
  for (const p of arg.getProperties()) {
1387
- if (!import_ts_morph3.Node.isPropertyAssignment(p)) continue;
1148
+ if (!import_ts_morph2.Node.isPropertyAssignment(p)) continue;
1388
1149
  const init = p.getInitializer();
1389
- if (init && import_ts_morph3.Node.isStringLiteral(init)) values.push(init.getText());
1150
+ if (init && import_ts_morph2.Node.isStringLiteral(init)) values.push(init.getText());
1390
1151
  }
1391
1152
  if (values.length > 0) return { kind: "enum", literals: values };
1392
1153
  }
@@ -1394,9 +1155,9 @@ function enumSchemaFromDecorator(decorator, classFile, ctx) {
1394
1155
  }
1395
1156
  function inSchemaFromDecorator(decorator) {
1396
1157
  const arg = firstArg(decorator);
1397
- if (arg && import_ts_morph3.Node.isArrayLiteralExpression(arg)) {
1158
+ if (arg && import_ts_morph2.Node.isArrayLiteralExpression(arg)) {
1398
1159
  const elements = arg.getElements();
1399
- const allStrings = elements.every((e) => import_ts_morph3.Node.isStringLiteral(e));
1160
+ const allStrings = elements.every((e) => import_ts_morph2.Node.isStringLiteral(e));
1400
1161
  if (allStrings && elements.length > 0) {
1401
1162
  return { kind: "enum", literals: elements.map((e) => e.getText()) };
1402
1163
  }
@@ -1414,7 +1175,7 @@ function inSchemaFromDecorator(decorator) {
1414
1175
  var import_ts_morph6 = require("ts-morph");
1415
1176
 
1416
1177
  // src/discovery/filter-field-types.ts
1417
- var import_ts_morph4 = require("ts-morph");
1178
+ var import_ts_morph3 = require("ts-morph");
1418
1179
 
1419
1180
  // src/discovery/enum-resolution.ts
1420
1181
  var _enumCache = /* @__PURE__ */ new WeakMap();
@@ -1467,28 +1228,28 @@ function markNullable(r, nullable) {
1467
1228
  }
1468
1229
  var TRANSPARENT_TYPE_BRANDS = /* @__PURE__ */ new Set(["Opt", "Hidden"]);
1469
1230
  function classifyTypeNode(typeNode, sourceFile, project, opts) {
1470
- if (import_ts_morph4.Node.isUnionTypeNode(typeNode)) {
1231
+ if (import_ts_morph3.Node.isUnionTypeNode(typeNode)) {
1471
1232
  let nullable = false;
1472
1233
  const stringLits = [];
1473
1234
  const numberLits = [];
1474
1235
  const others = [];
1475
1236
  for (const member of typeNode.getTypeNodes()) {
1476
1237
  const kind = member.getKind();
1477
- if (kind === import_ts_morph4.SyntaxKind.NullKeyword || kind === import_ts_morph4.SyntaxKind.UndefinedKeyword) {
1238
+ if (kind === import_ts_morph3.SyntaxKind.NullKeyword || kind === import_ts_morph3.SyntaxKind.UndefinedKeyword) {
1478
1239
  nullable = true;
1479
1240
  continue;
1480
1241
  }
1481
- if (import_ts_morph4.Node.isLiteralTypeNode(member)) {
1242
+ if (import_ts_morph3.Node.isLiteralTypeNode(member)) {
1482
1243
  const lit = member.getLiteral();
1483
- if (import_ts_morph4.Node.isStringLiteral(lit)) {
1244
+ if (import_ts_morph3.Node.isStringLiteral(lit)) {
1484
1245
  stringLits.push(lit.getLiteralValue());
1485
1246
  continue;
1486
1247
  }
1487
- if (import_ts_morph4.Node.isNumericLiteral(lit)) {
1248
+ if (import_ts_morph3.Node.isNumericLiteral(lit)) {
1488
1249
  numberLits.push(lit.getText());
1489
1250
  continue;
1490
1251
  }
1491
- if (lit.getKind() === import_ts_morph4.SyntaxKind.NullKeyword) {
1252
+ if (lit.getKind() === import_ts_morph3.SyntaxKind.NullKeyword) {
1492
1253
  nullable = true;
1493
1254
  continue;
1494
1255
  }
@@ -1508,19 +1269,19 @@ function classifyTypeNode(typeNode, sourceFile, project, opts) {
1508
1269
  return markNullable({ kind: "unknown" }, nullable);
1509
1270
  }
1510
1271
  switch (typeNode.getKind()) {
1511
- case import_ts_morph4.SyntaxKind.StringKeyword:
1272
+ case import_ts_morph3.SyntaxKind.StringKeyword:
1512
1273
  return { kind: "string" };
1513
- case import_ts_morph4.SyntaxKind.NumberKeyword:
1274
+ case import_ts_morph3.SyntaxKind.NumberKeyword:
1514
1275
  return { kind: "number" };
1515
- case import_ts_morph4.SyntaxKind.BooleanKeyword:
1276
+ case import_ts_morph3.SyntaxKind.BooleanKeyword:
1516
1277
  return { kind: "boolean" };
1517
- case import_ts_morph4.SyntaxKind.AnyKeyword:
1518
- case import_ts_morph4.SyntaxKind.UnknownKeyword:
1278
+ case import_ts_morph3.SyntaxKind.AnyKeyword:
1279
+ case import_ts_morph3.SyntaxKind.UnknownKeyword:
1519
1280
  return { kind: "unknown" };
1520
1281
  default:
1521
1282
  break;
1522
1283
  }
1523
- if (import_ts_morph4.Node.isTypeReference(typeNode)) {
1284
+ if (import_ts_morph3.Node.isTypeReference(typeNode)) {
1524
1285
  const refName = typeNode.getTypeName().getText();
1525
1286
  if (refName === "Date") return { kind: "date" };
1526
1287
  if (refName === "Record" || refName === "Object") return { kind: "json" };
@@ -1538,25 +1299,25 @@ function classifyTypeNode(typeNode, sourceFile, project, opts) {
1538
1299
  if (typeRef) return { kind: "unknown", typeRef };
1539
1300
  return { kind: "unknown" };
1540
1301
  }
1541
- if (import_ts_morph4.Node.isTypeLiteral(typeNode)) return { kind: "json" };
1302
+ if (import_ts_morph3.Node.isTypeLiteral(typeNode)) return { kind: "json" };
1542
1303
  return { kind: "unknown" };
1543
1304
  }
1544
1305
  function enumFromDecoratorArgs(args, sourceFile, project) {
1545
1306
  for (const arg of args) {
1546
- if (import_ts_morph4.Node.isArrowFunction(arg)) {
1307
+ if (import_ts_morph3.Node.isArrowFunction(arg)) {
1547
1308
  const body = arg.getBody();
1548
- if (import_ts_morph4.Node.isIdentifier(body)) {
1309
+ if (import_ts_morph3.Node.isIdentifier(body)) {
1549
1310
  const en = resolveEnumValues(body.getText(), sourceFile, project);
1550
1311
  if (en) return en;
1551
1312
  }
1552
1313
  }
1553
- if (import_ts_morph4.Node.isObjectLiteralExpression(arg)) {
1314
+ if (import_ts_morph3.Node.isObjectLiteralExpression(arg)) {
1554
1315
  const itemsProp = arg.getProperty("items");
1555
- if (itemsProp && import_ts_morph4.Node.isPropertyAssignment(itemsProp)) {
1316
+ if (itemsProp && import_ts_morph3.Node.isPropertyAssignment(itemsProp)) {
1556
1317
  const init = itemsProp.getInitializer();
1557
- if (init && import_ts_morph4.Node.isArrowFunction(init)) {
1318
+ if (init && import_ts_morph3.Node.isArrowFunction(init)) {
1558
1319
  const body = init.getBody();
1559
- if (import_ts_morph4.Node.isIdentifier(body)) {
1320
+ if (import_ts_morph3.Node.isIdentifier(body)) {
1560
1321
  const en = resolveEnumValues(body.getText(), sourceFile, project);
1561
1322
  if (en) return en;
1562
1323
  }
@@ -1579,7 +1340,7 @@ function classifyFromColumnDecorator(prop, sourceFile, project) {
1579
1340
  return { kind: "string" };
1580
1341
  }
1581
1342
  for (const arg of args) {
1582
- if (import_ts_morph4.Node.isStringLiteral(arg)) {
1343
+ if (import_ts_morph3.Node.isStringLiteral(arg)) {
1583
1344
  const raw = arg.getLiteralValue();
1584
1345
  const kind = classifyTypeKeyword(raw);
1585
1346
  if (kind) {
@@ -1587,11 +1348,11 @@ function classifyFromColumnDecorator(prop, sourceFile, project) {
1587
1348
  return { kind };
1588
1349
  }
1589
1350
  }
1590
- if (import_ts_morph4.Node.isObjectLiteralExpression(arg)) {
1351
+ if (import_ts_morph3.Node.isObjectLiteralExpression(arg)) {
1591
1352
  const enumProp = arg.getProperty("enum");
1592
- if (enumProp && import_ts_morph4.Node.isPropertyAssignment(enumProp)) {
1353
+ if (enumProp && import_ts_morph3.Node.isPropertyAssignment(enumProp)) {
1593
1354
  const init = enumProp.getInitializer();
1594
- if (init && import_ts_morph4.Node.isIdentifier(init)) {
1355
+ if (init && import_ts_morph3.Node.isIdentifier(init)) {
1595
1356
  const en = resolveEnumValues(init.getText(), sourceFile, project);
1596
1357
  if (en) {
1597
1358
  return en.numeric ? { kind: "number", enumValues: en.values, numericEnum: true } : { kind: "string", enumValues: en.values };
@@ -1601,15 +1362,15 @@ function classifyFromColumnDecorator(prop, sourceFile, project) {
1601
1362
  }
1602
1363
  for (const key of ["columnType", "type"]) {
1603
1364
  const typeProp = arg.getProperty(key);
1604
- if (!typeProp || !import_ts_morph4.Node.isPropertyAssignment(typeProp)) continue;
1365
+ if (!typeProp || !import_ts_morph3.Node.isPropertyAssignment(typeProp)) continue;
1605
1366
  const init = typeProp.getInitializer();
1606
1367
  if (!init) continue;
1607
- if (import_ts_morph4.Node.isStringLiteral(init)) {
1368
+ if (import_ts_morph3.Node.isStringLiteral(init)) {
1608
1369
  const kind = classifyTypeKeyword(init.getLiteralValue());
1609
1370
  if (kind) return { kind };
1610
1371
  continue;
1611
1372
  }
1612
- if (import_ts_morph4.Node.isIdentifier(init)) {
1373
+ if (import_ts_morph3.Node.isIdentifier(init)) {
1613
1374
  const kind = classifyTypeKeyword(init.getText());
1614
1375
  if (kind) return { kind };
1615
1376
  }
@@ -1652,6 +1413,49 @@ function toFilterFieldType(name, r) {
1652
1413
 
1653
1414
  // src/discovery/heritage.ts
1654
1415
  var import_ts_morph5 = require("ts-morph");
1416
+
1417
+ // src/discovery/unresolved-imports.ts
1418
+ var import_ts_morph4 = require("ts-morph");
1419
+ var UnresolvedImports = class {
1420
+ read;
1421
+ /**
1422
+ * The files with an import that failed to resolve in `program` and now resolves
1423
+ * to a TypeScript file the program does not hold.
1424
+ */
1425
+ nowResolving(program) {
1426
+ if (this.read?.program !== program) {
1427
+ this.read = { program, imports: failedImports(program) };
1428
+ }
1429
+ const options = program.getCompilerOptions();
1430
+ const files = /* @__PURE__ */ new Set();
1431
+ for (const { specifier, from } of this.read.imports) {
1432
+ const file = import_ts_morph4.ts.resolveModuleName(specifier, from, options, import_ts_morph4.ts.sys).resolvedModule?.resolvedFileName;
1433
+ if (file !== void 0 && /\.(m|c)?tsx?$/.test(file) && !program.getSourceFile(file)) {
1434
+ files.add(from);
1435
+ }
1436
+ }
1437
+ return [...files];
1438
+ }
1439
+ };
1440
+ function failedImports(program) {
1441
+ const checker = program.getTypeChecker();
1442
+ const imports = [];
1443
+ for (const file of program.getSourceFiles()) {
1444
+ if (program.isSourceFileDefaultLibrary(file) || file.isDeclarationFile) continue;
1445
+ if (program.isSourceFileFromExternalLibrary(file)) continue;
1446
+ for (const statement of file.statements) {
1447
+ if (!import_ts_morph4.ts.isImportDeclaration(statement) && !import_ts_morph4.ts.isExportDeclaration(statement)) continue;
1448
+ const specifier = statement.moduleSpecifier;
1449
+ if (!specifier || !import_ts_morph4.ts.isStringLiteral(specifier)) continue;
1450
+ if (!checker.getSymbolAtLocation(specifier)) {
1451
+ imports.push({ specifier: specifier.text, from: file.fileName });
1452
+ }
1453
+ }
1454
+ }
1455
+ return imports;
1456
+ }
1457
+
1458
+ // src/discovery/heritage.ts
1655
1459
  function resolveHeritageCall(node) {
1656
1460
  if (!node) return void 0;
1657
1461
  const expr = unwrapExpression(node);
@@ -1851,15 +1655,42 @@ function resolvePropertyClassReference(prop) {
1851
1655
  return { key, ref };
1852
1656
  }
1853
1657
  var mixinTypeProject;
1658
+ var mixinFiles = new DiskFingerprints();
1659
+ var mixinStale = false;
1660
+ var mixinUnresolved = new UnresolvedImports();
1854
1661
  function getMixinTypeProject() {
1855
- mixinTypeProject ??= new import_ts_morph5.Project({
1856
- skipAddingFilesFromTsConfig: true,
1857
- compilerOptions: { strict: true }
1858
- });
1662
+ if (mixinTypeProject && mixinStale && !refreshMixinTypeProject(mixinTypeProject)) {
1663
+ mixinTypeProject = void 0;
1664
+ }
1665
+ mixinStale = false;
1666
+ if (!mixinTypeProject) {
1667
+ mixinTypeProject = new import_ts_morph5.Project({
1668
+ skipAddingFilesFromTsConfig: true,
1669
+ compilerOptions: { strict: true }
1670
+ });
1671
+ mixinFiles = new DiskFingerprints();
1672
+ mixinUnresolved = new UnresolvedImports();
1673
+ }
1859
1674
  return mixinTypeProject;
1860
1675
  }
1861
1676
  function clearMixinTypeProject() {
1862
1677
  mixinTypeProject = void 0;
1678
+ mixinStale = false;
1679
+ }
1680
+ function invalidateMixinTypeProject() {
1681
+ mixinStale = true;
1682
+ }
1683
+ function refreshMixinTypeProject(project) {
1684
+ const program = project.getProgram().compilerObject;
1685
+ const files = program.getSourceFiles().filter((file) => !program.isSourceFileDefaultLibrary(file)).map((file) => ({ path: file.fileName, text: file.text }));
1686
+ const changed = mixinFiles.changed(files);
1687
+ if (changed.length === 0) return mixinUnresolved.nowResolving(program).length === 0;
1688
+ for (const path of changed) {
1689
+ const sf = project.getSourceFile(path);
1690
+ if (sf?.refreshFromFileSystemSync() !== import_ts_morph5.FileSystemRefreshResult.Updated) return false;
1691
+ }
1692
+ project.resolveSourceFileDependencies();
1693
+ return true;
1863
1694
  }
1864
1695
  function resolveInstantiatedReturnType(cls, methodName) {
1865
1696
  const filePath = cls.getSourceFile().getFilePath();
@@ -2217,8 +2048,7 @@ var SchemaTypeProgram = class {
2217
2048
  skipAddingFilesFromTsConfig: true,
2218
2049
  compilerOptions: this.options
2219
2050
  });
2220
- for (const sf of discovery.getSourceFiles()) this.request(sf.getFilePath(), { schema: true });
2221
- this.flush();
2051
+ this.requestDiscoveryFiles();
2222
2052
  }
2223
2053
  project;
2224
2054
  options;
@@ -2231,6 +2061,53 @@ var SchemaTypeProgram = class {
2231
2061
  needs = /* @__PURE__ */ new Map();
2232
2062
  /** Pruned text per file, as last written into {@link project}. */
2233
2063
  written = /* @__PURE__ */ new Map();
2064
+ /** What {@link project} read from disk unpruned (packages, declaration files). */
2065
+ externalFiles = new DiskFingerprints();
2066
+ scratchFiles = new DiskFingerprints();
2067
+ /** The last pruning of each file, with the source text and needs it was pruned for. */
2068
+ pruned = /* @__PURE__ */ new Map();
2069
+ /**
2070
+ * Bring the program up to date with discovery's files and the disk, keeping the
2071
+ * lib and package declarations it has already parsed and checked. Leaves it
2072
+ * holding exactly the files a new program built now would hold, with the same
2073
+ * text. Returns false when a file the program read unpruned has changed on disk
2074
+ * (a package was reinstalled, a `.d.ts` was edited): the caller builds a new
2075
+ * program instead.
2076
+ */
2077
+ refresh() {
2078
+ const program = this.project.getProgram().compilerObject;
2079
+ const external = program.getSourceFiles().filter(
2080
+ (file) => !program.isSourceFileDefaultLibrary(file) && !this.written.has(file.fileName)
2081
+ ).map((file) => ({ path: file.fileName, text: file.text }));
2082
+ if (this.externalFiles.changed(external).length > 0) return false;
2083
+ const scratch = this.scratch.getSourceFiles().map((sf) => ({ path: sf.getFilePath(), text: sf.getFullText() }));
2084
+ for (const path of this.scratchFiles.changed(scratch)) {
2085
+ const sf = this.scratch.getSourceFile(path);
2086
+ if (sf?.refreshFromFileSystemSync() === import_ts_morph7.FileSystemRefreshResult.Deleted) {
2087
+ this.scratchFiles.forget(path);
2088
+ }
2089
+ }
2090
+ this.needs.clear();
2091
+ this.requestDiscoveryFiles();
2092
+ for (const path of [...this.written.keys()]) {
2093
+ if (this.needs.has(path)) continue;
2094
+ const sf = this.project.getSourceFile(path);
2095
+ if (sf) this.project.removeSourceFile(sf);
2096
+ this.written.delete(path);
2097
+ }
2098
+ return true;
2099
+ }
2100
+ /**
2101
+ * Everything discovery holds now (the controllers) goes in before the first
2102
+ * query: a file added afterwards discards the program and its checker, and
2103
+ * rebuilding them once per controller is exactly the cost this avoids.
2104
+ */
2105
+ requestDiscoveryFiles() {
2106
+ for (const sf of this.discovery.getSourceFiles()) {
2107
+ this.request(sf.getFilePath(), { schema: true });
2108
+ }
2109
+ this.flush();
2110
+ }
2234
2111
  /**
2235
2112
  * The typed copy of a discovery file, with its schema positions kept. Adding one
2236
2113
  * that was not there yet (a contract file discovery reached lazily) rebuilds the
@@ -2271,12 +2148,21 @@ var SchemaTypeProgram = class {
2271
2148
  while (this.queue.length > 0) {
2272
2149
  const path = this.queue.pop();
2273
2150
  const original = this.original(path);
2274
- if (!original) continue;
2275
- const needs = this.needs.get(path);
2276
- const text = this.prune(original, needs);
2151
+ if (!original) {
2152
+ if (this.written.has(path)) changed.set(path, void 0);
2153
+ continue;
2154
+ }
2155
+ const { text, requests } = this.pruneCached(path, original, this.needs.get(path));
2156
+ for (const { path: target, want } of requests) this.request(target, want);
2277
2157
  if (this.written.get(path) !== text) changed.set(path, text);
2278
2158
  }
2279
2159
  for (const [path, text] of changed) {
2160
+ if (text === void 0) {
2161
+ const sf = this.project.getSourceFile(path);
2162
+ if (sf) this.project.removeSourceFile(sf);
2163
+ this.written.delete(path);
2164
+ continue;
2165
+ }
2280
2166
  this.project.createSourceFile(path, text, { overwrite: true });
2281
2167
  this.written.set(path, text);
2282
2168
  }
@@ -2285,11 +2171,29 @@ var SchemaTypeProgram = class {
2285
2171
  original(path) {
2286
2172
  return this.discovery.getSourceFile(path) ?? this.scratch.getSourceFile(path) ?? this.scratch.addSourceFileAtPathIfExists(path);
2287
2173
  }
2174
+ /** {@link prune}, reusing the last result while the file's text and needs are the same. */
2175
+ pruneCached(path, sf, needs) {
2176
+ const source = sf.getFullText();
2177
+ const key = `${needs.schema}:${needs.all}:${[...needs.names].sort().join(",")}`;
2178
+ const last = this.pruned.get(path);
2179
+ if (last && last.source === source && last.needs === key) return last;
2180
+ const result = this.prune(sf, needs);
2181
+ if (result.resolved) this.pruned.set(path, { ...result, source, needs: key });
2182
+ else this.pruned.delete(path);
2183
+ return result;
2184
+ }
2288
2185
  /**
2289
2186
  * `sf`'s text with every import/re-export nothing in `needs` references blanked,
2290
- * requesting from each kept one's target the names it brings in.
2187
+ * and the names each kept one brings in from its target.
2291
2188
  */
2292
2189
  prune(sf, needs) {
2190
+ const requests = [];
2191
+ let resolved = true;
2192
+ const resolveFrom = (specifier) => {
2193
+ const target = this.resolve(specifier, sf);
2194
+ if (target === null) resolved = false;
2195
+ return target ?? void 0;
2196
+ };
2293
2197
  const locals = topLevelDeclarations(sf);
2294
2198
  const roots = [];
2295
2199
  if (needs.all) roots.push(...sf.getStatements());
@@ -2342,7 +2246,7 @@ var SchemaTypeProgram = class {
2342
2246
  }
2343
2247
  };
2344
2248
  for (const decl of sf.getImportDeclarations()) {
2345
- const target = this.resolve(decl.getModuleSpecifierValue(), sf);
2249
+ const target = resolveFrom(decl.getModuleSpecifierValue());
2346
2250
  const namespace = decl.getNamespaceImport()?.getText();
2347
2251
  const def = decl.getDefaultImport()?.getText();
2348
2252
  const named = decl.getNamedImports().map((n) => ({
@@ -2356,21 +2260,24 @@ var SchemaTypeProgram = class {
2356
2260
  continue;
2357
2261
  }
2358
2262
  if (target) {
2359
- this.request(target, {
2360
- all: wantsNamespace,
2361
- names: [...wantsDefault ? ["default"] : [], ...named.map((n) => n.imported)]
2263
+ requests.push({
2264
+ path: target,
2265
+ want: {
2266
+ all: wantsNamespace,
2267
+ names: [...wantsDefault ? ["default"] : [], ...named.map((n) => n.imported)]
2268
+ }
2362
2269
  });
2363
2270
  }
2364
2271
  }
2365
2272
  for (const exp of sf.getExportDeclarations()) {
2366
2273
  const spec = exp.getModuleSpecifierValue();
2367
2274
  if (spec === void 0) continue;
2368
- const target = this.resolve(spec, sf);
2275
+ const target = resolveFrom(spec);
2369
2276
  if (exp.isNamespaceExport() || exp.getNamedExports().length === 0) {
2370
2277
  if (!needs.all && unresolvedNames.length === 0) {
2371
2278
  blank(exp);
2372
2279
  } else if (target) {
2373
- this.request(target, { all: needs.all, names: unresolvedNames });
2280
+ requests.push({ path: target, want: { all: needs.all, names: unresolvedNames } });
2374
2281
  }
2375
2282
  continue;
2376
2283
  }
@@ -2378,13 +2285,14 @@ var SchemaTypeProgram = class {
2378
2285
  (n) => needs.all || needs.names.has((n.getAliasNode() ?? n.getNameNode()).getText())
2379
2286
  ).map((n) => n.getNameNode().getText());
2380
2287
  if (wanted.length === 0) blank(exp);
2381
- else if (target) this.request(target, { names: wanted });
2288
+ else if (target) requests.push({ path: target, want: { names: wanted } });
2382
2289
  }
2383
- return chars.join("");
2290
+ return { text: chars.join(""), requests, resolved };
2384
2291
  }
2385
2292
  /**
2386
- * The project source file a module specifier resolves to, or undefined for a
2387
- * package (read unpruned by the program itself) or an unresolvable one.
2293
+ * The project source file a module specifier resolves to; undefined for a
2294
+ * package or a declaration file (read unpruned by the program itself), null for
2295
+ * one that does not resolve.
2388
2296
  */
2389
2297
  resolve(specifier, from) {
2390
2298
  const resolved = import_ts_morph7.ts.resolveModuleName(
@@ -2393,7 +2301,8 @@ var SchemaTypeProgram = class {
2393
2301
  this.options,
2394
2302
  import_ts_morph7.ts.sys
2395
2303
  ).resolvedModule;
2396
- if (!resolved || resolved.isExternalLibraryImport) return void 0;
2304
+ if (!resolved) return null;
2305
+ if (resolved.isExternalLibraryImport) return void 0;
2397
2306
  const file = resolved.resolvedFileName;
2398
2307
  if (file.endsWith(".d.ts") || !/\.(m|c)?tsx?$/.test(file)) return void 0;
2399
2308
  return file;
@@ -2448,8 +2357,13 @@ function defaultExport(sf) {
2448
2357
 
2449
2358
  // src/discovery/schema-type-inference.ts
2450
2359
  var programs = /* @__PURE__ */ new WeakMap();
2360
+ var stale = /* @__PURE__ */ new WeakSet();
2451
2361
  function programFor(discovery) {
2452
2362
  let program = programs.get(discovery);
2363
+ if (program && stale.has(discovery)) {
2364
+ stale.delete(discovery);
2365
+ if (!program.refresh()) program = void 0;
2366
+ }
2453
2367
  if (!program) {
2454
2368
  program = new SchemaTypeProgram(discovery);
2455
2369
  programs.set(discovery, program);
@@ -2458,6 +2372,10 @@ function programFor(discovery) {
2458
2372
  }
2459
2373
  function clearSchemaTypeProject(discovery) {
2460
2374
  programs.delete(discovery);
2375
+ stale.delete(discovery);
2376
+ }
2377
+ function invalidateSchemaTypeProject(discovery) {
2378
+ if (programs.has(discovery)) stale.add(discovery);
2461
2379
  }
2462
2380
  function counterpart(typedSf, node) {
2463
2381
  const start = node.getStart();
@@ -3405,7 +3323,7 @@ async function discoverContractsFast(opts) {
3405
3323
  return routes;
3406
3324
  }
3407
3325
  function resolveTsconfigPath(cwd, tsconfig) {
3408
- return tsconfig ? (0, import_node_path5.resolve)(tsconfig) : (0, import_node_path5.join)(cwd, "tsconfig.json");
3326
+ return tsconfig ? (0, import_node_path4.resolve)(tsconfig) : (0, import_node_path4.join)(cwd, "tsconfig.json");
3409
3327
  }
3410
3328
  var NO_TSCONFIG_OPTIONS = {
3411
3329
  allowJs: true,
@@ -3413,7 +3331,7 @@ var NO_TSCONFIG_OPTIONS = {
3413
3331
  strict: false
3414
3332
  };
3415
3333
  function loadDiscoveryTsconfig(tsconfigPath) {
3416
- if (!(0, import_node_fs.existsSync)(tsconfigPath)) return { options: null, files: [] };
3334
+ if (!(0, import_node_fs2.existsSync)(tsconfigPath)) return { options: null, files: [] };
3417
3335
  const read = import_ts_morph12.ts.readConfigFile(tsconfigPath, (path) => import_ts_morph12.ts.sys.readFile(path));
3418
3336
  if (read.error) {
3419
3337
  return {
@@ -3435,7 +3353,7 @@ function loadDiscoveryTsconfig(tsconfigPath) {
3435
3353
  const parsed = import_ts_morph12.ts.parseJsonSourceFileConfigFileContent(
3436
3354
  sourceFile,
3437
3355
  host,
3438
- (0, import_node_path5.dirname)(tsconfigPath),
3356
+ (0, import_node_path4.dirname)(tsconfigPath),
3439
3357
  void 0,
3440
3358
  tsconfigPath
3441
3359
  );
@@ -3497,6 +3415,9 @@ var PersistentDiscovery = class _PersistentDiscovery {
3497
3415
  glob;
3498
3416
  /** Absolute paths of the controllers currently loaded as extraction roots. */
3499
3417
  controllerPaths = /* @__PURE__ */ new Set();
3418
+ /** Every file in {@link project}, as last read from disk. */
3419
+ files = new DiskFingerprints();
3420
+ unresolved = new UnresolvedImports();
3500
3421
  constructor(project, cwd, glob) {
3501
3422
  this.project = project;
3502
3423
  this.cwd = cwd;
@@ -3522,6 +3443,8 @@ var PersistentDiscovery = class _PersistentDiscovery {
3522
3443
  }
3523
3444
  /** Run the initial extraction (equivalent to a first `discoverContractsFast`). */
3524
3445
  discover() {
3446
+ clearMixinTypeProject();
3447
+ clearSchemaTypeProject(this.project);
3525
3448
  return this.runExtraction();
3526
3449
  }
3527
3450
  /**
@@ -3529,26 +3452,39 @@ var PersistentDiscovery = class _PersistentDiscovery {
3529
3452
  * from disk (controllers AND any lazily-loaded DTO/imported files), re-globs
3530
3453
  * to pick up added/removed controllers, clears the per-Project caches, then
3531
3454
  * re-extracts. `changedPaths` is a hint; correctness does not depend on it
3532
- * being exhaustive because re-globbing + refresh-on-presence covers the set.
3455
+ * being exhaustive because re-globbing + comparing every loaded file with the
3456
+ * disk covers the set.
3533
3457
  */
3534
- async rediscover(changedPaths) {
3535
- if (changedPaths) {
3536
- for (const p of changedPaths) {
3537
- const abs = (0, import_node_path5.resolve)(p);
3538
- const sf = this.project.getSourceFile(abs);
3539
- if (sf) {
3540
- await sf.refreshFromFileSystem();
3541
- }
3458
+ async rediscover(changedPaths, options = {}) {
3459
+ const hinted = new Set([...changedPaths ?? []].map((p) => (0, import_node_path4.resolve)(p)));
3460
+ const program = this.project.getProgram().compilerObject;
3461
+ const sources = program.getSourceFiles().filter(
3462
+ (file) => !program.isSourceFileDefaultLibrary(file) && !program.isSourceFileFromExternalLibrary(file)
3463
+ ).map((file) => ({ path: file.fileName, text: file.text }));
3464
+ for (const path of /* @__PURE__ */ new Set([...hinted, ...this.files.changed(sources)])) {
3465
+ const sf = this.project.getSourceFile(path);
3466
+ if (sf && await sf.refreshFromFileSystem() === import_ts_morph12.FileSystemRefreshResult.Deleted) {
3467
+ this.files.forget(path);
3542
3468
  }
3543
3469
  }
3470
+ const inProject = new Set(this.project.getSourceFiles().map((sf) => sf.getFilePath()));
3471
+ const definitions = this.project.getLanguageService().compilerObject.getProgram();
3472
+ for (const path of definitions ? this.unresolved.nowResolving(definitions) : []) {
3473
+ const sf = this.project.getSourceFile(path);
3474
+ if (!sf) continue;
3475
+ this.project.removeSourceFile(sf);
3476
+ if (inProject.has(path)) this.project.addSourceFileAtPathIfExists(path);
3477
+ }
3544
3478
  const globbed = new Set(
3545
3479
  await (0, import_fast_glob2.default)(this.glob, { cwd: this.cwd, absolute: true, onlyFiles: true })
3546
3480
  );
3481
+ let controllersChanged = false;
3547
3482
  for (const f of globbed) {
3548
3483
  if (!this.controllerPaths.has(f)) {
3549
3484
  try {
3550
3485
  this.project.addSourceFileAtPath(f);
3551
3486
  this.controllerPaths.add(f);
3487
+ controllersChanged = true;
3552
3488
  } catch {
3553
3489
  }
3554
3490
  }
@@ -3558,16 +3494,22 @@ var PersistentDiscovery = class _PersistentDiscovery {
3558
3494
  const sf = this.project.getSourceFile(f);
3559
3495
  if (sf) this.project.removeSourceFile(sf);
3560
3496
  this.controllerPaths.delete(f);
3497
+ controllersChanged = true;
3561
3498
  }
3562
3499
  }
3500
+ if (controllersChanged || options.filesAddedOrRemoved) {
3501
+ clearMixinTypeProject();
3502
+ clearSchemaTypeProject(this.project);
3503
+ } else {
3504
+ invalidateMixinTypeProject();
3505
+ invalidateSchemaTypeProject(this.project);
3506
+ }
3563
3507
  return this.runExtraction();
3564
3508
  }
3565
3509
  /** Clear stale per-Project caches, then extract over the controller set. */
3566
3510
  runExtraction() {
3567
3511
  clearTypeResolutionCaches(this.project);
3568
3512
  clearEnumCache(this.project);
3569
- clearMixinTypeProject();
3570
- clearSchemaTypeProject(this.project);
3571
3513
  return extractRoutesFrom(this.project, this.controllerPaths);
3572
3514
  }
3573
3515
  };
@@ -3854,7 +3796,300 @@ function extractFromSourceFile(sourceFile, project) {
3854
3796
  return routes;
3855
3797
  }
3856
3798
 
3799
+ // src/discovery/shared-props.ts
3800
+ function discoverSharedProps(project, moduleEntry) {
3801
+ try {
3802
+ let sourceFile = project.getSourceFile(moduleEntry);
3803
+ if (!sourceFile) {
3804
+ try {
3805
+ sourceFile = project.addSourceFileAtPath(moduleEntry);
3806
+ } catch {
3807
+ return null;
3808
+ }
3809
+ }
3810
+ const forRootCall = findForRootCall(sourceFile);
3811
+ if (!forRootCall) return null;
3812
+ const initializer = findShareInitializer(forRootCall);
3813
+ if (!initializer) return null;
3814
+ return extractShareType(initializer, sourceFile, project);
3815
+ } catch {
3816
+ return null;
3817
+ }
3818
+ }
3819
+ function discoverSharedPropsFromConfig(config) {
3820
+ if (!config.app?.moduleEntry) return null;
3821
+ try {
3822
+ const tsconfigPath = config.app.tsconfig ?? (0, import_node_path5.join)(config.codegen.cwd, "tsconfig.json");
3823
+ let project;
3824
+ try {
3825
+ project = sharedPropsProject(tsconfigPath);
3826
+ } catch {
3827
+ project = new import_ts_morph13.Project({
3828
+ skipAddingFilesFromTsConfig: true,
3829
+ skipLoadingLibFiles: true,
3830
+ skipFileDependencyResolution: true,
3831
+ compilerOptions: { allowJs: true, strict: false }
3832
+ });
3833
+ }
3834
+ return discoverSharedProps(project, config.app.moduleEntry);
3835
+ } catch {
3836
+ return null;
3837
+ }
3838
+ }
3839
+ var parsedTsconfigs = /* @__PURE__ */ new Map();
3840
+ function readText(file) {
3841
+ try {
3842
+ return (0, import_node_fs3.readFileSync)(file, "utf8");
3843
+ } catch {
3844
+ return "\0missing";
3845
+ }
3846
+ }
3847
+ function sharedPropsProject(tsconfigPath) {
3848
+ const text = loadDiscoveryTsconfig(tsconfigPath).files.map((file) => `${file}\0${readText(file)}`).join("\0");
3849
+ const flags = {
3850
+ skipAddingFilesFromTsConfig: true,
3851
+ skipLoadingLibFiles: true,
3852
+ skipFileDependencyResolution: true
3853
+ };
3854
+ const parsed = parsedTsconfigs.get(tsconfigPath);
3855
+ if (parsed?.text === text) return new import_ts_morph13.Project({ ...flags, compilerOptions: parsed.options });
3856
+ const project = new import_ts_morph13.Project({ ...flags, tsConfigFilePath: tsconfigPath });
3857
+ parsedTsconfigs.set(tsconfigPath, { text, options: project.getCompilerOptions() });
3858
+ return project;
3859
+ }
3860
+ function findForRootCall(sourceFile) {
3861
+ const callExpressions = sourceFile.getDescendantsOfKind(import_ts_morph13.SyntaxKind.CallExpression);
3862
+ for (const call of callExpressions) {
3863
+ const expr = call.getExpression();
3864
+ if (!import_ts_morph13.Node.isPropertyAccessExpression(expr)) continue;
3865
+ const methodName = expr.getName();
3866
+ const objectExpr = expr.getExpression();
3867
+ if (methodName === "forRoot" && import_ts_morph13.Node.isIdentifier(objectExpr)) {
3868
+ const name = objectExpr.getText();
3869
+ if (name === "InertiaModule") {
3870
+ return call;
3871
+ }
3872
+ }
3873
+ }
3874
+ return null;
3875
+ }
3876
+ function findShareInitializer(forRootCall) {
3877
+ if (!import_ts_morph13.Node.isCallExpression(forRootCall)) return null;
3878
+ const args = forRootCall.getArguments();
3879
+ const firstArg2 = args[0];
3880
+ if (!firstArg2 || !import_ts_morph13.Node.isObjectLiteralExpression(firstArg2)) return null;
3881
+ for (const prop of firstArg2.getProperties()) {
3882
+ if (import_ts_morph13.Node.isPropertyAssignment(prop) && prop.getName() === "share") {
3883
+ return prop.getInitializer() ?? null;
3884
+ }
3885
+ }
3886
+ return null;
3887
+ }
3888
+ function extractShareType(node, sourceFile, project) {
3889
+ if (import_ts_morph13.Node.isIdentifier(node)) {
3890
+ const ref = resolveIdentifierToImportRef(node, sourceFile, project);
3891
+ if (ref) {
3892
+ return {
3893
+ typeString: `Awaited<ReturnType<typeof import('${ref.importPath}').${ref.exportName}>>`,
3894
+ properties: null,
3895
+ isImportRef: true
3896
+ };
3897
+ }
3898
+ }
3899
+ if (import_ts_morph13.Node.isArrowFunction(node)) {
3900
+ const result = extractFromFunctionLike(node, sourceFile);
3901
+ return result ? { ...result, isImportRef: false } : null;
3902
+ }
3903
+ if (import_ts_morph13.Node.isFunctionExpression(node)) {
3904
+ const result = extractFromFunctionLike(node, sourceFile);
3905
+ return result ? { ...result, isImportRef: false } : null;
3906
+ }
3907
+ if (import_ts_morph13.Node.isObjectLiteralExpression(node)) {
3908
+ const result = extractFromObjectLiteral(node);
3909
+ return result ? { ...result, isImportRef: false } : null;
3910
+ }
3911
+ return null;
3912
+ }
3913
+ function resolveIdentifierToImportRef(id, sourceFile, project) {
3914
+ if (!import_ts_morph13.Node.isIdentifier(id)) return null;
3915
+ const name = id.getText();
3916
+ const localFunc = sourceFile.getFunction(name);
3917
+ if (localFunc?.isExported()) {
3918
+ const filePath = sourceFile.getFilePath().replace(/\.ts$/, "");
3919
+ return { importPath: filePath, exportName: name };
3920
+ }
3921
+ const localVar = sourceFile.getVariableDeclaration(name);
3922
+ if (localVar?.isExported()) {
3923
+ const filePath = sourceFile.getFilePath().replace(/\.ts$/, "");
3924
+ return { importPath: filePath, exportName: name };
3925
+ }
3926
+ for (const imp of sourceFile.getImportDeclarations()) {
3927
+ for (const named of imp.getNamedImports()) {
3928
+ const importedName = named.getAliasNode()?.getText() ?? named.getName();
3929
+ if (importedName !== name) continue;
3930
+ const resolvedSource = imp.getModuleSpecifierSourceFile();
3931
+ if (!resolvedSource) continue;
3932
+ const originalName = named.getName();
3933
+ const fn = resolvedSource.getFunction(originalName);
3934
+ if (fn?.isExported()) {
3935
+ const filePath = resolvedSource.getFilePath().replace(/\.ts$/, "");
3936
+ return { importPath: filePath, exportName: originalName };
3937
+ }
3938
+ const v = resolvedSource.getVariableDeclaration(originalName);
3939
+ if (v?.isExported()) {
3940
+ const filePath = resolvedSource.getFilePath().replace(/\.ts$/, "");
3941
+ return { importPath: filePath, exportName: originalName };
3942
+ }
3943
+ }
3944
+ }
3945
+ return null;
3946
+ }
3947
+ function extractFromFunctionLike(node, _sourceFile) {
3948
+ const returnTypeNode = import_ts_morph13.Node.isArrowFunction(node) || import_ts_morph13.Node.isFunctionExpression(node) ? node.getReturnTypeNode() : null;
3949
+ if (returnTypeNode) {
3950
+ return extractFromReturnTypeAnnotation(returnTypeNode);
3951
+ }
3952
+ if (import_ts_morph13.Node.isArrowFunction(node)) {
3953
+ const body = node.getBody();
3954
+ if (import_ts_morph13.Node.isParenthesizedExpression(body)) {
3955
+ const inner = body.getExpression();
3956
+ if (import_ts_morph13.Node.isObjectLiteralExpression(inner)) {
3957
+ return extractFromObjectLiteral(inner);
3958
+ }
3959
+ }
3960
+ if (import_ts_morph13.Node.isObjectLiteralExpression(body)) {
3961
+ return extractFromObjectLiteral(body);
3962
+ }
3963
+ if (import_ts_morph13.Node.isBlock(body)) {
3964
+ return extractFromBlockReturn(body);
3965
+ }
3966
+ }
3967
+ if (import_ts_morph13.Node.isFunctionExpression(node)) {
3968
+ const body = node.getBody();
3969
+ if (import_ts_morph13.Node.isBlock(body)) {
3970
+ return extractFromBlockReturn(body);
3971
+ }
3972
+ }
3973
+ return null;
3974
+ }
3975
+ function extractFromReturnTypeAnnotation(typeNode) {
3976
+ if (import_ts_morph13.Node.isTypeReference(typeNode)) {
3977
+ const typeName = typeNode.getTypeName();
3978
+ if (import_ts_morph13.Node.isIdentifier(typeName) && typeName.getText() === "Promise") {
3979
+ const typeArgs = typeNode.getTypeArguments();
3980
+ const firstArg2 = typeArgs[0];
3981
+ if (firstArg2) {
3982
+ return extractFromReturnTypeAnnotation(firstArg2);
3983
+ }
3984
+ return null;
3985
+ }
3986
+ }
3987
+ if (import_ts_morph13.Node.isTypeLiteral(typeNode)) {
3988
+ const properties = [];
3989
+ for (const member of typeNode.getMembers()) {
3990
+ if (import_ts_morph13.Node.isPropertySignature(member)) {
3991
+ const name = member.getName();
3992
+ const memberTypeNode = member.getTypeNode();
3993
+ const type = memberTypeNode ? memberTypeNode.getText() : "unknown";
3994
+ properties.push({ name, type });
3995
+ }
3996
+ }
3997
+ if (properties.length === 0) return null;
3998
+ const typeString = `{ ${properties.map((p) => `${p.name}: ${p.type}`).join("; ")} }`;
3999
+ return { typeString, properties, isImportRef: false };
4000
+ }
4001
+ return null;
4002
+ }
4003
+ function extractFromBlockReturn(block) {
4004
+ if (!import_ts_morph13.Node.isBlock(block)) return null;
4005
+ const statements = block.getStatements();
4006
+ for (let i = statements.length - 1; i >= 0; i--) {
4007
+ const stmt = statements[i];
4008
+ if (!import_ts_morph13.Node.isReturnStatement(stmt)) continue;
4009
+ const expr = stmt.getExpression();
4010
+ if (!expr) continue;
4011
+ if (import_ts_morph13.Node.isObjectLiteralExpression(expr)) {
4012
+ return extractFromObjectLiteral(expr);
4013
+ }
4014
+ if (import_ts_morph13.Node.isParenthesizedExpression(expr)) {
4015
+ const inner = expr.getExpression();
4016
+ if (import_ts_morph13.Node.isObjectLiteralExpression(inner)) {
4017
+ return extractFromObjectLiteral(inner);
4018
+ }
4019
+ }
4020
+ break;
4021
+ }
4022
+ return null;
4023
+ }
4024
+ function extractFromObjectLiteral(objLiteral) {
4025
+ if (!import_ts_morph13.Node.isObjectLiteralExpression(objLiteral)) return null;
4026
+ const properties = [];
4027
+ for (const prop of objLiteral.getProperties()) {
4028
+ if (!import_ts_morph13.Node.isPropertyAssignment(prop)) continue;
4029
+ const name = prop.getName();
4030
+ const initializer = prop.getInitializer();
4031
+ if (!initializer) continue;
4032
+ const type = inferExpressionType(initializer);
4033
+ properties.push({ name, type });
4034
+ }
4035
+ if (properties.length === 0) return null;
4036
+ const typeString = `{ ${properties.map((p) => `${p.name}: ${p.type}`).join("; ")} }`;
4037
+ return { typeString, properties, isImportRef: false };
4038
+ }
4039
+ function inferExpressionType(node) {
4040
+ if (import_ts_morph13.Node.isStringLiteral(node)) return "string";
4041
+ if (import_ts_morph13.Node.isTemplateExpression(node) || import_ts_morph13.Node.isNoSubstitutionTemplateLiteral(node))
4042
+ return "string";
4043
+ if (import_ts_morph13.Node.isNumericLiteral(node)) return "number";
4044
+ if (node.getKind() === import_ts_morph13.SyntaxKind.TrueKeyword || node.getKind() === import_ts_morph13.SyntaxKind.FalseKeyword) {
4045
+ return "boolean";
4046
+ }
4047
+ if (node.getKind() === import_ts_morph13.SyntaxKind.NullKeyword) return "null";
4048
+ if (import_ts_morph13.Node.isIdentifier(node) && node.getText() === "undefined") return "undefined";
4049
+ if (import_ts_morph13.Node.isObjectLiteralExpression(node)) {
4050
+ const props = node.getProperties();
4051
+ if (props.length === 0) return "Record<string, unknown>";
4052
+ const entries = [];
4053
+ for (const prop of props) {
4054
+ if (!import_ts_morph13.Node.isPropertyAssignment(prop)) continue;
4055
+ const key = prop.getName();
4056
+ const init = prop.getInitializer();
4057
+ if (!init) continue;
4058
+ entries.push(`${key}: ${inferExpressionType(init)}`);
4059
+ }
4060
+ if (entries.length === 0) return "Record<string, unknown>";
4061
+ return `{ ${entries.join("; ")} }`;
4062
+ }
4063
+ if (import_ts_morph13.Node.isArrayLiteralExpression(node)) {
4064
+ const elements = node.getElements();
4065
+ if (elements.length === 0) return "Array<unknown>";
4066
+ const first = elements[0];
4067
+ if (first) return `Array<${inferExpressionType(first)}>`;
4068
+ return "Array<unknown>";
4069
+ }
4070
+ if (import_ts_morph13.Node.isConditionalExpression(node)) {
4071
+ const whenTrue = inferExpressionType(node.getWhenTrue());
4072
+ const whenFalse = inferExpressionType(node.getWhenFalse());
4073
+ if (whenTrue === whenFalse) return whenTrue;
4074
+ return `${whenTrue} | ${whenFalse}`;
4075
+ }
4076
+ if (import_ts_morph13.Node.isParenthesizedExpression(node)) {
4077
+ return inferExpressionType(node.getExpression());
4078
+ }
4079
+ if (import_ts_morph13.Node.isAsExpression(node)) {
4080
+ const typeNode = node.getTypeNode();
4081
+ if (typeNode) return typeNode.getText();
4082
+ }
4083
+ return "unknown";
4084
+ }
4085
+
4086
+ // src/emit/emit-api.ts
4087
+ var import_promises3 = require("fs/promises");
4088
+ var import_node_path7 = require("path");
4089
+
3857
4090
  // src/extension/registry.ts
4091
+ var import_node_path6 = require("path");
4092
+ var import_ts_morph14 = require("ts-morph");
3858
4093
  function resolveApiSlots(extensions) {
3859
4094
  let layer;
3860
4095
  let layerOwner;
@@ -3905,7 +4140,7 @@ function createExtensionContext(config, getRoutes, trackedInputs) {
3905
4140
  },
3906
4141
  project() {
3907
4142
  if (!project) {
3908
- project = new import_ts_morph13.Project({
4143
+ project = new import_ts_morph14.Project({
3909
4144
  skipAddingFilesFromTsConfig: true,
3910
4145
  skipLoadingLibFiles: true,
3911
4146
  skipFileDependencyResolution: true,
@@ -5636,36 +5871,64 @@ async function discoverInputFiles(config) {
5636
5871
  const matched = await (0, import_fast_glob3.default)(globs, { cwd, absolute: true, onlyFiles: true });
5637
5872
  return [...new Set(matched)].sort();
5638
5873
  }
5639
- async function computeInputsHash(config, extraInputs = []) {
5874
+ var READ_CONCURRENCY = 64;
5875
+ async function readAll(paths, read) {
5876
+ const out = /* @__PURE__ */ new Map();
5877
+ for (let i = 0; i < paths.length; i += READ_CONCURRENCY) {
5878
+ const batch = paths.slice(i, i + READ_CONCURRENCY);
5879
+ const texts = await Promise.all(batch.map(read));
5880
+ batch.forEach((path, j) => out.set(path, texts[j]));
5881
+ }
5882
+ return out;
5883
+ }
5884
+ async function readInputs(config) {
5885
+ const cwd = config.codegen.cwd;
5886
+ const tsconfigPath = resolveTsconfigPath(cwd, config.app?.tsconfig ?? void 0);
5887
+ const extended = loadDiscoveryTsconfig(tsconfigPath).files;
5888
+ const tsconfigFiles = extended.length > 0 ? extended : [tsconfigPath];
5889
+ const files = await discoverInputFiles(config);
5890
+ const contents = /* @__PURE__ */ new Map();
5891
+ const tsconfigs = await readAll(
5892
+ tsconfigFiles,
5893
+ (file) => (0, import_promises11.readFile)(file, "utf8").catch(() => null)
5894
+ );
5895
+ for (const [file, text] of tsconfigs) contents.set(file, text);
5896
+ for (const [file, text] of await readAll(files, (file2) => (0, import_promises11.readFile)(file2, "utf8"))) {
5897
+ contents.set(file, text);
5898
+ }
5899
+ return { tsconfigFiles, files, contents, tracked: /* @__PURE__ */ new Map() };
5900
+ }
5901
+ async function readInput(snapshot, path) {
5902
+ return snapshot.contents.get(path) ?? (0, import_promises11.readFile)(path, "utf8");
5903
+ }
5904
+ async function computeInputsHash(config, extraInputs = [], snapshot) {
5905
+ const inputs = snapshot ?? await readInputs(config);
5640
5906
  const hash = (0, import_node_crypto.createHash)("sha256");
5641
5907
  hash.update(`version:${VERSION}
5642
5908
  `);
5643
5909
  hash.update(`config:${serializeConfig(config)}
5644
5910
  `);
5645
5911
  const cwd = config.codegen.cwd;
5646
- const tsconfigPath = resolveTsconfigPath(cwd, config.app?.tsconfig ?? void 0);
5647
- const tsconfigFiles = loadDiscoveryTsconfig(tsconfigPath).files;
5648
- for (const file of tsconfigFiles.length > 0 ? tsconfigFiles : [tsconfigPath]) {
5649
- const contents = await (0, import_promises11.readFile)(file, "utf8").catch(() => null);
5912
+ for (const file of inputs.tsconfigFiles) {
5650
5913
  hash.update(`tsconfig:${(0, import_node_path15.relative)(cwd, file)}
5651
5914
  `);
5652
- hash.update(contents ?? " missing");
5915
+ hash.update(inputs.contents.get(file) ?? " missing");
5653
5916
  hash.update("\n");
5654
5917
  }
5655
- const globbed = await discoverInputFiles(config);
5656
- const globbedRelative = new Set(globbed.map((file) => (0, import_node_path15.relative)(cwd, file)));
5918
+ const globbedRelative = new Set(inputs.files.map((file) => (0, import_node_path15.relative)(cwd, file)));
5657
5919
  const extra = [...new Set(extraInputs)].filter((file) => !globbedRelative.has(file)).sort();
5658
- for (const file of globbed) {
5659
- const contents = await (0, import_promises11.readFile)(file, "utf8");
5920
+ for (const file of inputs.files) {
5660
5921
  hash.update(`file:${(0, import_node_path15.relative)(cwd, file)}
5661
5922
  `);
5662
- hash.update(contents);
5923
+ hash.update(inputs.contents.get(file));
5663
5924
  hash.update("\n");
5664
5925
  }
5926
+ const unread = extra.map((file) => (0, import_node_path15.join)(cwd, file)).filter((abs) => !inputs.tracked.has(abs));
5927
+ for (const [abs, text] of await readAll(unread, readTrackedInput)) inputs.tracked.set(abs, text);
5665
5928
  for (const file of extra) {
5666
5929
  hash.update(`extra:${file}
5667
5930
  `);
5668
- hash.update(await readTrackedInput((0, import_node_path15.join)(cwd, file)));
5931
+ hash.update(inputs.tracked.get((0, import_node_path15.join)(cwd, file)));
5669
5932
  hash.update("\n");
5670
5933
  }
5671
5934
  return hash.digest("hex");
@@ -5746,14 +6009,15 @@ function driftGuardMessage(outDir, previousEntryPoint, currentEntryPoint, differ
5746
6009
  async function generate(config, inputRoutes = [], entryPoint = "cli") {
5747
6010
  setCodegenDebug(config.debug);
5748
6011
  const manifest = await readManifest(config.codegen.outDir);
5749
- const inputsHash = await computeInputsHash(config, manifest?.extraInputs ?? []);
6012
+ const inputs = await readInputs(config);
6013
+ const inputsHash = await computeInputsHash(config, manifest?.extraInputs ?? [], inputs);
5750
6014
  if (await isManifestFresh(config.codegen.outDir, manifest, inputsHash)) {
5751
6015
  console.log(`[nestjs-codegen] ${config.codegen.outDir} up to date, skipped`);
5752
6016
  return;
5753
6017
  }
5754
6018
  const configHash = computeConfigHash(config);
5755
6019
  const configKeyHashes = computeConfigKeyHashes(config);
5756
- if (config.driftGuard && manifest?.entryPoint && manifest.entryPoint !== entryPoint && manifest.configHash && manifest.configHash !== configHash) {
6020
+ if (config.driftGuard && manifest?.version === VERSION && manifest?.entryPoint && manifest.entryPoint !== entryPoint && manifest.configHash && manifest.configHash !== configHash) {
5757
6021
  throw new DriftGuardError(
5758
6022
  driftGuardMessage(
5759
6023
  config.codegen.outDir,
@@ -5774,7 +6038,7 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
5774
6038
  }
5775
6039
  if (config.pages) {
5776
6040
  const pagesConfig = config.pages;
5777
- const pages = await discoverPageScopes(config);
6041
+ const pages = await discoverPageScopes(config, (path) => readInput(inputs, path));
5778
6042
  const sharedProps = discoverSharedPropsFromConfig(config);
5779
6043
  await emitPages(pages, config.codegen.outDir, {
5780
6044
  propsExport: pagesConfig.propsExport,
@@ -5824,7 +6088,7 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
5824
6088
  }
5825
6089
  const outputFiles = await listOutputFiles(config.codegen.outDir);
5826
6090
  const extraInputs = [...trackedInputs].sort();
5827
- const recordedHash = extraInputs.length > 0 ? await computeInputsHash(config, extraInputs) : inputsHash;
6091
+ const recordedHash = extraInputs.length > 0 ? await computeInputsHash(config, extraInputs, inputs) : inputsHash;
5828
6092
  await writeManifest(config.codegen.outDir, {
5829
6093
  version: VERSION,
5830
6094
  hash: recordedHash,
@@ -6007,13 +6271,15 @@ async function watch(config, onChange, options = {}) {
6007
6271
  pagesWatcher.on("unlink", schedulePagesRegenerate);
6008
6272
  let contractsDebounceTimer;
6009
6273
  const pendingChangedPaths = /* @__PURE__ */ new Set();
6274
+ let pendingAddedOrRemoved = false;
6010
6275
  const contractsWatcher = import_chokidar.default.watch((0, import_node_path18.join)(config.codegen.cwd, config.contracts.glob), {
6011
6276
  ignoreInitial: true,
6012
6277
  persistent: true,
6013
6278
  awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
6014
6279
  });
6015
- function scheduleContractsRegenerate(changedPath) {
6280
+ function scheduleContractsRegenerate(changedPath, addedOrRemoved = false) {
6016
6281
  if (typeof changedPath === "string") pendingChangedPaths.add(changedPath);
6282
+ if (addedOrRemoved) pendingAddedOrRemoved = true;
6017
6283
  if (contractsDebounceTimer !== void 0) {
6018
6284
  clearTimeout(contractsDebounceTimer);
6019
6285
  }
@@ -6021,8 +6287,12 @@ async function watch(config, onChange, options = {}) {
6021
6287
  contractsDebounceTimer = void 0;
6022
6288
  const changed = [...pendingChangedPaths];
6023
6289
  pendingChangedPaths.clear();
6290
+ const filesAddedOrRemoved = pendingAddedOrRemoved;
6291
+ pendingAddedOrRemoved = false;
6024
6292
  try {
6025
- const routes = await (await getDiscovery()).rediscover(changed);
6293
+ const routes = await (await getDiscovery()).rediscover(changed, {
6294
+ filesAddedOrRemoved
6295
+ });
6026
6296
  lastRoutes = routes;
6027
6297
  await generateAndTrack(routes);
6028
6298
  } catch (err) {
@@ -6034,20 +6304,25 @@ async function watch(config, onChange, options = {}) {
6034
6304
  onChange?.();
6035
6305
  }, config.contracts.debounceMs);
6036
6306
  }
6037
- contractsWatcher.on("add", (p) => scheduleContractsRegenerate(p));
6307
+ contractsWatcher.on("add", (p) => scheduleContractsRegenerate(p, true));
6038
6308
  contractsWatcher.on("change", (p) => scheduleContractsRegenerate(p));
6039
- contractsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p));
6309
+ contractsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p, true));
6040
6310
  const formsWatcher = import_chokidar.default.watch((0, import_node_path18.join)(config.codegen.cwd, config.forms.watch), {
6041
6311
  ignoreInitial: true,
6042
6312
  persistent: true,
6043
6313
  awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
6044
6314
  });
6045
- formsWatcher.on("add", (p) => scheduleContractsRegenerate(p));
6315
+ formsWatcher.on("add", (p) => scheduleContractsRegenerate(p, true));
6046
6316
  formsWatcher.on("change", (p) => scheduleContractsRegenerate(p));
6047
- formsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p));
6048
- trackedWatcher.on("add", (p) => scheduleContractsRegenerate(p));
6317
+ formsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p, true));
6318
+ trackedWatcher.on("add", (p) => scheduleContractsRegenerate(p, true));
6049
6319
  trackedWatcher.on("change", (p) => scheduleContractsRegenerate(p));
6050
- trackedWatcher.on("unlink", (p) => scheduleContractsRegenerate(p));
6320
+ trackedWatcher.on("unlink", (p) => scheduleContractsRegenerate(p, true));
6321
+ await Promise.all(
6322
+ [pagesWatcher, contractsWatcher, formsWatcher].map(
6323
+ (w) => new Promise((resolveReady) => w.once("ready", () => resolveReady()))
6324
+ )
6325
+ );
6051
6326
  return {
6052
6327
  close: async () => {
6053
6328
  if (pagesDebounceTimer !== void 0) {
@@ -6068,7 +6343,7 @@ async function watch(config, onChange, options = {}) {
6068
6343
  }
6069
6344
 
6070
6345
  // src/index.ts
6071
- var VERSION = "0.29.0";
6346
+ var VERSION = "0.30.0";
6072
6347
 
6073
6348
  // src/cli/codegen.ts
6074
6349
  async function runCodegen(opts = {}) {
@@ -6096,28 +6371,28 @@ async function runCodegen(opts = {}) {
6096
6371
 
6097
6372
  // src/cli/doctor.ts
6098
6373
  var import_node_child_process2 = require("child_process");
6099
- var import_node_fs4 = require("fs");
6374
+ var import_node_fs6 = require("fs");
6100
6375
  var import_node_path20 = require("path");
6101
6376
 
6102
6377
  // src/cli/init.ts
6103
6378
  var import_node_child_process = require("child_process");
6104
- var import_node_fs3 = require("fs");
6379
+ var import_node_fs5 = require("fs");
6105
6380
  var import_promises16 = require("fs/promises");
6106
6381
  var import_node_path19 = require("path");
6107
6382
  var import_node_readline = require("readline");
6108
6383
 
6109
6384
  // src/cli/patch-utils.ts
6110
- var import_node_fs2 = require("fs");
6385
+ var import_node_fs4 = require("fs");
6111
6386
  function patchJsonFile(filePath, mutator, parse = (raw) => raw) {
6112
6387
  let raw;
6113
6388
  try {
6114
- raw = (0, import_node_fs2.readFileSync)(filePath, "utf8");
6389
+ raw = (0, import_node_fs4.readFileSync)(filePath, "utf8");
6115
6390
  } catch {
6116
6391
  return "skipped";
6117
6392
  }
6118
6393
  const json = JSON.parse(parse(raw));
6119
6394
  if (!mutator(json)) return "already";
6120
- (0, import_node_fs2.writeFileSync)(filePath, `${JSON.stringify(json, null, 2)}
6395
+ (0, import_node_fs4.writeFileSync)(filePath, `${JSON.stringify(json, null, 2)}
6121
6396
  `, "utf8");
6122
6397
  return "patched";
6123
6398
  }
@@ -6329,7 +6604,7 @@ async function patchPackageJsonScripts(cwd, scripts) {
6329
6604
  function patchAppModule(filePath, rootView) {
6330
6605
  let content;
6331
6606
  try {
6332
- content = (0, import_node_fs3.readFileSync)(filePath, "utf8");
6607
+ content = (0, import_node_fs5.readFileSync)(filePath, "utf8");
6333
6608
  } catch {
6334
6609
  return "skipped";
6335
6610
  }
@@ -6364,13 +6639,13 @@ ${indent}HomeController,${content.slice(bracketPos)}`;
6364
6639
  }
6365
6640
  }
6366
6641
  if (!changed) return "already";
6367
- (0, import_node_fs3.writeFileSync)(filePath, content, "utf8");
6642
+ (0, import_node_fs5.writeFileSync)(filePath, content, "utf8");
6368
6643
  return "patched";
6369
6644
  }
6370
6645
  function patchMainTs(filePath) {
6371
6646
  let content;
6372
6647
  try {
6373
- content = (0, import_node_fs3.readFileSync)(filePath, "utf8");
6648
+ content = (0, import_node_fs5.readFileSync)(filePath, "utf8");
6374
6649
  } catch {
6375
6650
  return "skipped";
6376
6651
  }
@@ -6395,7 +6670,7 @@ function patchMainTs(filePath) {
6395
6670
  });`;
6396
6671
  content = `${content.slice(0, insertAfterPos)}
6397
6672
  ${viteSetup}${content.slice(insertAfterPos)}`;
6398
- (0, import_node_fs3.writeFileSync)(filePath, content, "utf8");
6673
+ (0, import_node_fs5.writeFileSync)(filePath, content, "utf8");
6399
6674
  return "patched";
6400
6675
  }
6401
6676
  function configTemplate(framework) {
@@ -6850,18 +7125,18 @@ ${green("\u2713")} Setup complete! Run: ${bold("nest start --watch")}
6850
7125
 
6851
7126
  // src/cli/doctor.ts
6852
7127
  function checkFileExists(cwd, file) {
6853
- return (0, import_node_fs4.existsSync)((0, import_node_path20.join)(cwd, file));
7128
+ return (0, import_node_fs6.existsSync)((0, import_node_path20.join)(cwd, file));
6854
7129
  }
6855
7130
  function readJson(path) {
6856
7131
  try {
6857
- const raw = (0, import_node_fs4.readFileSync)(path, "utf8").replace(/\/\/.*$/gm, "");
7132
+ const raw = (0, import_node_fs6.readFileSync)(path, "utf8").replace(/\/\/.*$/gm, "");
6858
7133
  return JSON.parse(raw);
6859
7134
  } catch {
6860
7135
  return null;
6861
7136
  }
6862
7137
  }
6863
7138
  function writeJsonField(filePath, dotPath, value) {
6864
- const raw = (0, import_node_fs4.readFileSync)(filePath, "utf8");
7139
+ const raw = (0, import_node_fs6.readFileSync)(filePath, "utf8");
6865
7140
  const stripped = raw.replace(/\/\/.*$/gm, "");
6866
7141
  const obj = JSON.parse(stripped);
6867
7142
  let target = obj;
@@ -6881,7 +7156,7 @@ function writeJsonField(filePath, dotPath, value) {
6881
7156
  } else {
6882
7157
  target[lastKey] = value;
6883
7158
  }
6884
- (0, import_node_fs4.writeFileSync)(filePath, `${JSON.stringify(obj, null, 2)}
7159
+ (0, import_node_fs6.writeFileSync)(filePath, `${JSON.stringify(obj, null, 2)}
6885
7160
  `, "utf8");
6886
7161
  }
6887
7162
  function getPackageVersion(cwd, pkg) {
@@ -6893,8 +7168,8 @@ function getPackageVersion(cwd, pkg) {
6893
7168
  }
6894
7169
  }
6895
7170
  function detectPkgManager(cwd) {
6896
- if ((0, import_node_fs4.existsSync)((0, import_node_path20.join)(cwd, "pnpm-lock.yaml"))) return "pnpm";
6897
- if ((0, import_node_fs4.existsSync)((0, import_node_path20.join)(cwd, "yarn.lock"))) return "yarn";
7171
+ if ((0, import_node_fs6.existsSync)((0, import_node_path20.join)(cwd, "pnpm-lock.yaml"))) return "pnpm";
7172
+ if ((0, import_node_fs6.existsSync)((0, import_node_path20.join)(cwd, "yarn.lock"))) return "yarn";
6898
7173
  return "npm";
6899
7174
  }
6900
7175
  async function runDoctor(opts) {
@@ -7015,7 +7290,7 @@ async function runDoctor(opts) {
7015
7290
  pass: !!inertiaTsconfig,
7016
7291
  fix: "Create tsconfig.inertia.json (typechecks inertia/ + .nestjs-inertia/)",
7017
7292
  autoFix: () => {
7018
- (0, import_node_fs4.writeFileSync)(inertiaTsconfigPath, TSCONFIG_INERTIA_TEMPLATE, "utf8");
7293
+ (0, import_node_fs6.writeFileSync)(inertiaTsconfigPath, TSCONFIG_INERTIA_TEMPLATE, "utf8");
7019
7294
  }
7020
7295
  });
7021
7296
  if (inertiaTsconfig) {
@@ -7063,14 +7338,14 @@ async function runDoctor(opts) {
7063
7338
  const innerTsconfigPath = (0, import_node_path20.join)(cwd, "inertia", "tsconfig.json");
7064
7339
  checks.push({
7065
7340
  name: "inertia/tsconfig.json exists (VSCode picks up ~codegen alias)",
7066
- pass: (0, import_node_fs4.existsSync)(innerTsconfigPath),
7341
+ pass: (0, import_node_fs6.existsSync)(innerTsconfigPath),
7067
7342
  fix: "Create inertia/tsconfig.json that extends ../tsconfig.inertia.json",
7068
7343
  autoFix: () => {
7069
- (0, import_node_fs4.writeFileSync)(innerTsconfigPath, INERTIA_TSCONFIG_TEMPLATE, "utf8");
7344
+ (0, import_node_fs6.writeFileSync)(innerTsconfigPath, INERTIA_TSCONFIG_TEMPLATE, "utf8");
7070
7345
  }
7071
7346
  });
7072
7347
  if (checkFileExists(cwd, "vite.config.ts")) {
7073
- const viteContent = (0, import_node_fs4.readFileSync)((0, import_node_path20.join)(cwd, "vite.config.ts"), "utf8");
7348
+ const viteContent = (0, import_node_fs6.readFileSync)((0, import_node_path20.join)(cwd, "vite.config.ts"), "utf8");
7074
7349
  checks.push({
7075
7350
  name: "vite.config.ts has resolve.alias",
7076
7351
  pass: viteContent.includes("resolve") && viteContent.includes("alias"),
@@ -7136,12 +7411,12 @@ async function runDoctor(opts) {
7136
7411
  }
7137
7412
  if (checkFileExists(cwd, ".gitignore")) {
7138
7413
  const gitignorePath = (0, import_node_path20.join)(cwd, ".gitignore");
7139
- const gitignore = (0, import_node_fs4.readFileSync)(gitignorePath, "utf8");
7414
+ const gitignore = (0, import_node_fs6.readFileSync)(gitignorePath, "utf8");
7140
7415
  checks.push({
7141
7416
  name: ".gitignore includes .nestjs-inertia/",
7142
7417
  pass: gitignore.includes(".nestjs-inertia"),
7143
7418
  fix: "Add .nestjs-inertia/ to .gitignore",
7144
- autoFix: () => (0, import_node_fs4.appendFileSync)(gitignorePath, "\n.nestjs-inertia/\n")
7419
+ autoFix: () => (0, import_node_fs6.appendFileSync)(gitignorePath, "\n.nestjs-inertia/\n")
7145
7420
  });
7146
7421
  }
7147
7422
  const pkgJsonPath = (0, import_node_path20.join)(cwd, "package.json");