@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/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 = {
|
|
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
|
|
367
|
-
var
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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
|
-
|
|
384
|
+
const stat2 = (0, import_node_fs.statSync)(path);
|
|
385
|
+
key = `${stat2.mtimeMs}:${stat2.size}`;
|
|
374
386
|
} catch {
|
|
375
|
-
|
|
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
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
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
|
-
|
|
550
|
-
|
|
551
|
-
|
|
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
|
-
|
|
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
|
-
|
|
623
|
-
|
|
624
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
669
|
-
var
|
|
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,
|
|
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,
|
|
716
|
-
...flavour === "" ? [(0,
|
|
717
|
-
(0,
|
|
718
|
-
(0,
|
|
719
|
-
(0,
|
|
720
|
-
...ext === void 0 ? [(0,
|
|
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,
|
|
743
|
-
candidates.push(`${resolved}.ts`, (0,
|
|
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 &&
|
|
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 (!
|
|
699
|
+
if (!import_ts_morph.Node.isTypeReference(typeNode)) return null;
|
|
939
700
|
const typeName = typeNode.getTypeName();
|
|
940
|
-
const refName =
|
|
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 &&
|
|
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 &&
|
|
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 &&
|
|
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 (
|
|
1060
|
+
if (import_ts_morph2.Node.isObjectLiteralExpression(arg)) {
|
|
1300
1061
|
for (const prop of arg.getProperties()) {
|
|
1301
|
-
if (
|
|
1062
|
+
if (import_ts_morph2.Node.isPropertyAssignment(prop) && prop.getName() === "message") {
|
|
1302
1063
|
const init = prop.getInitializer();
|
|
1303
|
-
if (init &&
|
|
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 || !
|
|
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 (
|
|
1076
|
+
if (import_ts_morph2.Node.isPropertyAssignment(prop) && prop.getName() === "discriminator") {
|
|
1316
1077
|
discProp = prop.getInitializer();
|
|
1317
1078
|
}
|
|
1318
1079
|
}
|
|
1319
|
-
if (!discProp || !
|
|
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 (!
|
|
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" &&
|
|
1088
|
+
if (name === "property" && import_ts_morph2.Node.isStringLiteral(init)) {
|
|
1328
1089
|
property = init.getLiteralValue();
|
|
1329
|
-
} else if (name === "subTypes" &&
|
|
1090
|
+
} else if (name === "subTypes" && import_ts_morph2.Node.isArrayLiteralExpression(init)) {
|
|
1330
1091
|
for (const el of init.getElements()) {
|
|
1331
|
-
if (!
|
|
1092
|
+
if (!import_ts_morph2.Node.isObjectLiteralExpression(el)) continue;
|
|
1332
1093
|
for (const p of el.getProperties()) {
|
|
1333
|
-
if (!
|
|
1094
|
+
if (!import_ts_morph2.Node.isPropertyAssignment(p) || p.getName() !== "name") continue;
|
|
1334
1095
|
const nameInit = p.getInitializer();
|
|
1335
|
-
if (nameInit &&
|
|
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 (
|
|
1107
|
+
if (import_ts_morph2.Node.isArrowFunction(arg)) {
|
|
1347
1108
|
const body = arg.getBody();
|
|
1348
|
-
if (
|
|
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 || !
|
|
1118
|
+
if (!typeNode || !import_ts_morph2.Node.isTypeReference(typeNode)) return [];
|
|
1358
1119
|
const names = [];
|
|
1359
1120
|
for (const arg of typeNode.getTypeArguments()) {
|
|
1360
|
-
if (!
|
|
1121
|
+
if (!import_ts_morph2.Node.isTypeReference(arg)) return [];
|
|
1361
1122
|
const tn = arg.getTypeName();
|
|
1362
|
-
if (!
|
|
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 (
|
|
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 (
|
|
1145
|
+
if (import_ts_morph2.Node.isObjectLiteralExpression(arg)) {
|
|
1385
1146
|
const values = [];
|
|
1386
1147
|
for (const p of arg.getProperties()) {
|
|
1387
|
-
if (!
|
|
1148
|
+
if (!import_ts_morph2.Node.isPropertyAssignment(p)) continue;
|
|
1388
1149
|
const init = p.getInitializer();
|
|
1389
|
-
if (init &&
|
|
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 &&
|
|
1158
|
+
if (arg && import_ts_morph2.Node.isArrayLiteralExpression(arg)) {
|
|
1398
1159
|
const elements = arg.getElements();
|
|
1399
|
-
const allStrings = elements.every((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
|
|
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 (
|
|
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 ===
|
|
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 (
|
|
1242
|
+
if (import_ts_morph3.Node.isLiteralTypeNode(member)) {
|
|
1482
1243
|
const lit = member.getLiteral();
|
|
1483
|
-
if (
|
|
1244
|
+
if (import_ts_morph3.Node.isStringLiteral(lit)) {
|
|
1484
1245
|
stringLits.push(lit.getLiteralValue());
|
|
1485
1246
|
continue;
|
|
1486
1247
|
}
|
|
1487
|
-
if (
|
|
1248
|
+
if (import_ts_morph3.Node.isNumericLiteral(lit)) {
|
|
1488
1249
|
numberLits.push(lit.getText());
|
|
1489
1250
|
continue;
|
|
1490
1251
|
}
|
|
1491
|
-
if (lit.getKind() ===
|
|
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
|
|
1272
|
+
case import_ts_morph3.SyntaxKind.StringKeyword:
|
|
1512
1273
|
return { kind: "string" };
|
|
1513
|
-
case
|
|
1274
|
+
case import_ts_morph3.SyntaxKind.NumberKeyword:
|
|
1514
1275
|
return { kind: "number" };
|
|
1515
|
-
case
|
|
1276
|
+
case import_ts_morph3.SyntaxKind.BooleanKeyword:
|
|
1516
1277
|
return { kind: "boolean" };
|
|
1517
|
-
case
|
|
1518
|
-
case
|
|
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 (
|
|
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 (
|
|
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 (
|
|
1307
|
+
if (import_ts_morph3.Node.isArrowFunction(arg)) {
|
|
1547
1308
|
const body = arg.getBody();
|
|
1548
|
-
if (
|
|
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 (
|
|
1314
|
+
if (import_ts_morph3.Node.isObjectLiteralExpression(arg)) {
|
|
1554
1315
|
const itemsProp = arg.getProperty("items");
|
|
1555
|
-
if (itemsProp &&
|
|
1316
|
+
if (itemsProp && import_ts_morph3.Node.isPropertyAssignment(itemsProp)) {
|
|
1556
1317
|
const init = itemsProp.getInitializer();
|
|
1557
|
-
if (init &&
|
|
1318
|
+
if (init && import_ts_morph3.Node.isArrowFunction(init)) {
|
|
1558
1319
|
const body = init.getBody();
|
|
1559
|
-
if (
|
|
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 (
|
|
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 (
|
|
1351
|
+
if (import_ts_morph3.Node.isObjectLiteralExpression(arg)) {
|
|
1591
1352
|
const enumProp = arg.getProperty("enum");
|
|
1592
|
-
if (enumProp &&
|
|
1353
|
+
if (enumProp && import_ts_morph3.Node.isPropertyAssignment(enumProp)) {
|
|
1593
1354
|
const init = enumProp.getInitializer();
|
|
1594
|
-
if (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 || !
|
|
1365
|
+
if (!typeProp || !import_ts_morph3.Node.isPropertyAssignment(typeProp)) continue;
|
|
1605
1366
|
const init = typeProp.getInitializer();
|
|
1606
1367
|
if (!init) continue;
|
|
1607
|
-
if (
|
|
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 (
|
|
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
|
|
1856
|
-
|
|
1857
|
-
|
|
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
|
-
|
|
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)
|
|
2275
|
-
|
|
2276
|
-
|
|
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
|
-
*
|
|
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 =
|
|
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
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
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 =
|
|
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
|
-
|
|
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)
|
|
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
|
|
2387
|
-
* package (read unpruned by the program itself)
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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 +
|
|
3455
|
+
* being exhaustive because re-globbing + comparing every loaded file with the
|
|
3456
|
+
* disk covers the set.
|
|
3533
3457
|
*/
|
|
3534
|
-
async rediscover(changedPaths) {
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
6897
|
-
if ((0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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");
|