@dudousxd/nestjs-codegen 0.28.0 → 0.29.1
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 +16 -0
- package/README.md +10 -8
- package/dist/cli/main.cjs +705 -429
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.js +856 -575
- package/dist/cli/main.js.map +1 -1
- package/dist/index.cjs +889 -562
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -14
- package/dist/index.d.ts +27 -14
- package/dist/index.js +1057 -725
- package/dist/index.js.map +1 -1
- package/dist/nest/index.cjs +553 -277
- package/dist/nest/index.cjs.map +1 -1
- package/dist/nest/index.js +363 -82
- package/dist/nest/index.js.map +1 -1
- package/package.json +4 -3
- package/skills/codegen-setup/SKILL.md +5 -1
package/dist/index.cjs
CHANGED
|
@@ -315,7 +315,7 @@ async function discoverPages(opts) {
|
|
|
315
315
|
const rel = (0, import_node_path2.relative)(opts.cwd, file);
|
|
316
316
|
const nameRel = (0, import_node_path2.relative)(pagesBase, file).split(import_node_path2.sep).join("/");
|
|
317
317
|
const name = `${prefix}${computeName(nameRel, opts.componentNameStrategy)}`;
|
|
318
|
-
const source = await (0, import_promises2.readFile)(file, "utf8");
|
|
318
|
+
const source = await (opts.read ? opts.read(file) : (0, import_promises2.readFile)(file, "utf8"));
|
|
319
319
|
const propsSource = extractPropsSource(source, opts.propsExport);
|
|
320
320
|
out.push({ name, absolutePath: file, relativePath: rel, propsSource, scope });
|
|
321
321
|
}
|
|
@@ -323,10 +323,14 @@ async function discoverPages(opts) {
|
|
|
323
323
|
assertUniqueNames(out);
|
|
324
324
|
return out;
|
|
325
325
|
}
|
|
326
|
-
async function discoverPageScopes(config) {
|
|
326
|
+
async function discoverPageScopes(config, read) {
|
|
327
327
|
const pages = config.pages;
|
|
328
328
|
if (!pages) return [];
|
|
329
|
-
const common = {
|
|
329
|
+
const common = {
|
|
330
|
+
cwd: config.codegen.cwd,
|
|
331
|
+
propsExport: pages.propsExport,
|
|
332
|
+
...read ? { read } : {}
|
|
333
|
+
};
|
|
330
334
|
const out = await discoverPages({
|
|
331
335
|
...common,
|
|
332
336
|
glob: pages.glob,
|
|
@@ -397,297 +401,60 @@ function extractPropsSource(source, exportName) {
|
|
|
397
401
|
}
|
|
398
402
|
|
|
399
403
|
// src/discovery/shared-props.ts
|
|
400
|
-
var
|
|
401
|
-
var
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
404
|
+
var import_node_fs3 = require("fs");
|
|
405
|
+
var import_node_path5 = require("path");
|
|
406
|
+
var import_ts_morph13 = require("ts-morph");
|
|
407
|
+
|
|
408
|
+
// src/discovery/contracts-fast.ts
|
|
409
|
+
var import_node_fs2 = require("fs");
|
|
410
|
+
var import_node_path4 = require("path");
|
|
411
|
+
var import_fast_glob2 = __toESM(require("fast-glob"), 1);
|
|
412
|
+
var import_ts_morph12 = require("ts-morph");
|
|
413
|
+
|
|
414
|
+
// src/discovery/disk-fingerprints.ts
|
|
415
|
+
var import_node_fs = require("fs");
|
|
416
|
+
var DiskFingerprints = class {
|
|
417
|
+
stats = /* @__PURE__ */ new Map();
|
|
418
|
+
/** The paths whose disk content differs from `text`, or that no longer exist. */
|
|
419
|
+
changed(files) {
|
|
420
|
+
const changed = [];
|
|
421
|
+
for (const { path, text } of files) {
|
|
422
|
+
let key;
|
|
406
423
|
try {
|
|
407
|
-
|
|
424
|
+
const stat2 = (0, import_node_fs.statSync)(path);
|
|
425
|
+
key = `${stat2.mtimeMs}:${stat2.size}`;
|
|
408
426
|
} catch {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
const forRootCall = findForRootCall(sourceFile);
|
|
413
|
-
if (!forRootCall) return null;
|
|
414
|
-
const initializer = findShareInitializer(forRootCall);
|
|
415
|
-
if (!initializer) return null;
|
|
416
|
-
return extractShareType(initializer, sourceFile, project);
|
|
417
|
-
} catch {
|
|
418
|
-
return null;
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
function discoverSharedPropsFromConfig(config) {
|
|
422
|
-
if (!config.app?.moduleEntry) return null;
|
|
423
|
-
try {
|
|
424
|
-
const tsconfigPath = config.app.tsconfig ?? (0, import_node_path3.join)(config.codegen.cwd, "tsconfig.json");
|
|
425
|
-
let project;
|
|
426
|
-
try {
|
|
427
|
-
project = new import_ts_morph.Project({
|
|
428
|
-
tsConfigFilePath: tsconfigPath,
|
|
429
|
-
skipAddingFilesFromTsConfig: true,
|
|
430
|
-
skipLoadingLibFiles: true,
|
|
431
|
-
skipFileDependencyResolution: true
|
|
432
|
-
});
|
|
433
|
-
} catch {
|
|
434
|
-
project = new import_ts_morph.Project({
|
|
435
|
-
skipAddingFilesFromTsConfig: true,
|
|
436
|
-
skipLoadingLibFiles: true,
|
|
437
|
-
skipFileDependencyResolution: true,
|
|
438
|
-
compilerOptions: { allowJs: true, strict: false }
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
return discoverSharedProps(project, config.app.moduleEntry);
|
|
442
|
-
} catch {
|
|
443
|
-
return null;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
function findForRootCall(sourceFile) {
|
|
447
|
-
const callExpressions = sourceFile.getDescendantsOfKind(import_ts_morph.SyntaxKind.CallExpression);
|
|
448
|
-
for (const call of callExpressions) {
|
|
449
|
-
const expr = call.getExpression();
|
|
450
|
-
if (!import_ts_morph.Node.isPropertyAccessExpression(expr)) continue;
|
|
451
|
-
const methodName = expr.getName();
|
|
452
|
-
const objectExpr = expr.getExpression();
|
|
453
|
-
if (methodName === "forRoot" && import_ts_morph.Node.isIdentifier(objectExpr)) {
|
|
454
|
-
const name = objectExpr.getText();
|
|
455
|
-
if (name === "InertiaModule") {
|
|
456
|
-
return call;
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
return null;
|
|
461
|
-
}
|
|
462
|
-
function findShareInitializer(forRootCall) {
|
|
463
|
-
if (!import_ts_morph.Node.isCallExpression(forRootCall)) return null;
|
|
464
|
-
const args = forRootCall.getArguments();
|
|
465
|
-
const firstArg2 = args[0];
|
|
466
|
-
if (!firstArg2 || !import_ts_morph.Node.isObjectLiteralExpression(firstArg2)) return null;
|
|
467
|
-
for (const prop of firstArg2.getProperties()) {
|
|
468
|
-
if (import_ts_morph.Node.isPropertyAssignment(prop) && prop.getName() === "share") {
|
|
469
|
-
return prop.getInitializer() ?? null;
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
return null;
|
|
473
|
-
}
|
|
474
|
-
function extractShareType(node, sourceFile, project) {
|
|
475
|
-
if (import_ts_morph.Node.isIdentifier(node)) {
|
|
476
|
-
const ref = resolveIdentifierToImportRef(node, sourceFile, project);
|
|
477
|
-
if (ref) {
|
|
478
|
-
return {
|
|
479
|
-
typeString: `Awaited<ReturnType<typeof import('${ref.importPath}').${ref.exportName}>>`,
|
|
480
|
-
properties: null,
|
|
481
|
-
isImportRef: true
|
|
482
|
-
};
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
if (import_ts_morph.Node.isArrowFunction(node)) {
|
|
486
|
-
const result = extractFromFunctionLike(node, sourceFile);
|
|
487
|
-
return result ? { ...result, isImportRef: false } : null;
|
|
488
|
-
}
|
|
489
|
-
if (import_ts_morph.Node.isFunctionExpression(node)) {
|
|
490
|
-
const result = extractFromFunctionLike(node, sourceFile);
|
|
491
|
-
return result ? { ...result, isImportRef: false } : null;
|
|
492
|
-
}
|
|
493
|
-
if (import_ts_morph.Node.isObjectLiteralExpression(node)) {
|
|
494
|
-
const result = extractFromObjectLiteral(node);
|
|
495
|
-
return result ? { ...result, isImportRef: false } : null;
|
|
496
|
-
}
|
|
497
|
-
return null;
|
|
498
|
-
}
|
|
499
|
-
function resolveIdentifierToImportRef(id, sourceFile, project) {
|
|
500
|
-
if (!import_ts_morph.Node.isIdentifier(id)) return null;
|
|
501
|
-
const name = id.getText();
|
|
502
|
-
const localFunc = sourceFile.getFunction(name);
|
|
503
|
-
if (localFunc?.isExported()) {
|
|
504
|
-
const filePath = sourceFile.getFilePath().replace(/\.ts$/, "");
|
|
505
|
-
return { importPath: filePath, exportName: name };
|
|
506
|
-
}
|
|
507
|
-
const localVar = sourceFile.getVariableDeclaration(name);
|
|
508
|
-
if (localVar?.isExported()) {
|
|
509
|
-
const filePath = sourceFile.getFilePath().replace(/\.ts$/, "");
|
|
510
|
-
return { importPath: filePath, exportName: name };
|
|
511
|
-
}
|
|
512
|
-
for (const imp of sourceFile.getImportDeclarations()) {
|
|
513
|
-
for (const named of imp.getNamedImports()) {
|
|
514
|
-
const importedName = named.getAliasNode()?.getText() ?? named.getName();
|
|
515
|
-
if (importedName !== name) continue;
|
|
516
|
-
const resolvedSource = imp.getModuleSpecifierSourceFile();
|
|
517
|
-
if (!resolvedSource) continue;
|
|
518
|
-
const originalName = named.getName();
|
|
519
|
-
const fn = resolvedSource.getFunction(originalName);
|
|
520
|
-
if (fn?.isExported()) {
|
|
521
|
-
const filePath = resolvedSource.getFilePath().replace(/\.ts$/, "");
|
|
522
|
-
return { importPath: filePath, exportName: originalName };
|
|
523
|
-
}
|
|
524
|
-
const v = resolvedSource.getVariableDeclaration(originalName);
|
|
525
|
-
if (v?.isExported()) {
|
|
526
|
-
const filePath = resolvedSource.getFilePath().replace(/\.ts$/, "");
|
|
527
|
-
return { importPath: filePath, exportName: originalName };
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
return null;
|
|
532
|
-
}
|
|
533
|
-
function extractFromFunctionLike(node, _sourceFile) {
|
|
534
|
-
const returnTypeNode = import_ts_morph.Node.isArrowFunction(node) || import_ts_morph.Node.isFunctionExpression(node) ? node.getReturnTypeNode() : null;
|
|
535
|
-
if (returnTypeNode) {
|
|
536
|
-
return extractFromReturnTypeAnnotation(returnTypeNode);
|
|
537
|
-
}
|
|
538
|
-
if (import_ts_morph.Node.isArrowFunction(node)) {
|
|
539
|
-
const body = node.getBody();
|
|
540
|
-
if (import_ts_morph.Node.isParenthesizedExpression(body)) {
|
|
541
|
-
const inner = body.getExpression();
|
|
542
|
-
if (import_ts_morph.Node.isObjectLiteralExpression(inner)) {
|
|
543
|
-
return extractFromObjectLiteral(inner);
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
if (import_ts_morph.Node.isObjectLiteralExpression(body)) {
|
|
547
|
-
return extractFromObjectLiteral(body);
|
|
548
|
-
}
|
|
549
|
-
if (import_ts_morph.Node.isBlock(body)) {
|
|
550
|
-
return extractFromBlockReturn(body);
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
if (import_ts_morph.Node.isFunctionExpression(node)) {
|
|
554
|
-
const body = node.getBody();
|
|
555
|
-
if (import_ts_morph.Node.isBlock(body)) {
|
|
556
|
-
return extractFromBlockReturn(body);
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
return null;
|
|
560
|
-
}
|
|
561
|
-
function extractFromReturnTypeAnnotation(typeNode) {
|
|
562
|
-
if (import_ts_morph.Node.isTypeReference(typeNode)) {
|
|
563
|
-
const typeName = typeNode.getTypeName();
|
|
564
|
-
if (import_ts_morph.Node.isIdentifier(typeName) && typeName.getText() === "Promise") {
|
|
565
|
-
const typeArgs = typeNode.getTypeArguments();
|
|
566
|
-
const firstArg2 = typeArgs[0];
|
|
567
|
-
if (firstArg2) {
|
|
568
|
-
return extractFromReturnTypeAnnotation(firstArg2);
|
|
427
|
+
this.stats.delete(path);
|
|
428
|
+
changed.push(path);
|
|
429
|
+
continue;
|
|
569
430
|
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
if (import_ts_morph.Node.isPropertySignature(member)) {
|
|
577
|
-
const name = member.getName();
|
|
578
|
-
const memberTypeNode = member.getTypeNode();
|
|
579
|
-
const type = memberTypeNode ? memberTypeNode.getText() : "unknown";
|
|
580
|
-
properties.push({ name, type });
|
|
431
|
+
if (this.stats.get(path) === key) continue;
|
|
432
|
+
let disk;
|
|
433
|
+
try {
|
|
434
|
+
disk = (0, import_node_fs.readFileSync)(path, "utf-8");
|
|
435
|
+
} catch {
|
|
436
|
+
disk = void 0;
|
|
581
437
|
}
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
return null;
|
|
588
|
-
}
|
|
589
|
-
function extractFromBlockReturn(block) {
|
|
590
|
-
if (!import_ts_morph.Node.isBlock(block)) return null;
|
|
591
|
-
const statements = block.getStatements();
|
|
592
|
-
for (let i = statements.length - 1; i >= 0; i--) {
|
|
593
|
-
const stmt = statements[i];
|
|
594
|
-
if (!import_ts_morph.Node.isReturnStatement(stmt)) continue;
|
|
595
|
-
const expr = stmt.getExpression();
|
|
596
|
-
if (!expr) continue;
|
|
597
|
-
if (import_ts_morph.Node.isObjectLiteralExpression(expr)) {
|
|
598
|
-
return extractFromObjectLiteral(expr);
|
|
599
|
-
}
|
|
600
|
-
if (import_ts_morph.Node.isParenthesizedExpression(expr)) {
|
|
601
|
-
const inner = expr.getExpression();
|
|
602
|
-
if (import_ts_morph.Node.isObjectLiteralExpression(inner)) {
|
|
603
|
-
return extractFromObjectLiteral(inner);
|
|
438
|
+
if (disk === text) {
|
|
439
|
+
this.stats.set(path, key);
|
|
440
|
+
} else {
|
|
441
|
+
this.stats.delete(path);
|
|
442
|
+
changed.push(path);
|
|
604
443
|
}
|
|
605
444
|
}
|
|
606
|
-
|
|
607
|
-
}
|
|
608
|
-
return null;
|
|
609
|
-
}
|
|
610
|
-
function extractFromObjectLiteral(objLiteral) {
|
|
611
|
-
if (!import_ts_morph.Node.isObjectLiteralExpression(objLiteral)) return null;
|
|
612
|
-
const properties = [];
|
|
613
|
-
for (const prop of objLiteral.getProperties()) {
|
|
614
|
-
if (!import_ts_morph.Node.isPropertyAssignment(prop)) continue;
|
|
615
|
-
const name = prop.getName();
|
|
616
|
-
const initializer = prop.getInitializer();
|
|
617
|
-
if (!initializer) continue;
|
|
618
|
-
const type = inferExpressionType(initializer);
|
|
619
|
-
properties.push({ name, type });
|
|
620
|
-
}
|
|
621
|
-
if (properties.length === 0) return null;
|
|
622
|
-
const typeString = `{ ${properties.map((p) => `${p.name}: ${p.type}`).join("; ")} }`;
|
|
623
|
-
return { typeString, properties, isImportRef: false };
|
|
624
|
-
}
|
|
625
|
-
function inferExpressionType(node) {
|
|
626
|
-
if (import_ts_morph.Node.isStringLiteral(node)) return "string";
|
|
627
|
-
if (import_ts_morph.Node.isTemplateExpression(node) || import_ts_morph.Node.isNoSubstitutionTemplateLiteral(node))
|
|
628
|
-
return "string";
|
|
629
|
-
if (import_ts_morph.Node.isNumericLiteral(node)) return "number";
|
|
630
|
-
if (node.getKind() === import_ts_morph.SyntaxKind.TrueKeyword || node.getKind() === import_ts_morph.SyntaxKind.FalseKeyword) {
|
|
631
|
-
return "boolean";
|
|
632
|
-
}
|
|
633
|
-
if (node.getKind() === import_ts_morph.SyntaxKind.NullKeyword) return "null";
|
|
634
|
-
if (import_ts_morph.Node.isIdentifier(node) && node.getText() === "undefined") return "undefined";
|
|
635
|
-
if (import_ts_morph.Node.isObjectLiteralExpression(node)) {
|
|
636
|
-
const props = node.getProperties();
|
|
637
|
-
if (props.length === 0) return "Record<string, unknown>";
|
|
638
|
-
const entries = [];
|
|
639
|
-
for (const prop of props) {
|
|
640
|
-
if (!import_ts_morph.Node.isPropertyAssignment(prop)) continue;
|
|
641
|
-
const key = prop.getName();
|
|
642
|
-
const init = prop.getInitializer();
|
|
643
|
-
if (!init) continue;
|
|
644
|
-
entries.push(`${key}: ${inferExpressionType(init)}`);
|
|
645
|
-
}
|
|
646
|
-
if (entries.length === 0) return "Record<string, unknown>";
|
|
647
|
-
return `{ ${entries.join("; ")} }`;
|
|
648
|
-
}
|
|
649
|
-
if (import_ts_morph.Node.isArrayLiteralExpression(node)) {
|
|
650
|
-
const elements = node.getElements();
|
|
651
|
-
if (elements.length === 0) return "Array<unknown>";
|
|
652
|
-
const first = elements[0];
|
|
653
|
-
if (first) return `Array<${inferExpressionType(first)}>`;
|
|
654
|
-
return "Array<unknown>";
|
|
655
|
-
}
|
|
656
|
-
if (import_ts_morph.Node.isConditionalExpression(node)) {
|
|
657
|
-
const whenTrue = inferExpressionType(node.getWhenTrue());
|
|
658
|
-
const whenFalse = inferExpressionType(node.getWhenFalse());
|
|
659
|
-
if (whenTrue === whenFalse) return whenTrue;
|
|
660
|
-
return `${whenTrue} | ${whenFalse}`;
|
|
661
|
-
}
|
|
662
|
-
if (import_ts_morph.Node.isParenthesizedExpression(node)) {
|
|
663
|
-
return inferExpressionType(node.getExpression());
|
|
445
|
+
return changed;
|
|
664
446
|
}
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
447
|
+
/** Forget `path`, so its next check compares content again. */
|
|
448
|
+
forget(path) {
|
|
449
|
+
this.stats.delete(path);
|
|
668
450
|
}
|
|
669
|
-
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
// src/emit/emit-api.ts
|
|
673
|
-
var import_promises3 = require("fs/promises");
|
|
674
|
-
var import_node_path7 = require("path");
|
|
675
|
-
|
|
676
|
-
// src/extension/registry.ts
|
|
677
|
-
var import_node_path6 = require("path");
|
|
678
|
-
var import_ts_morph13 = require("ts-morph");
|
|
679
|
-
|
|
680
|
-
// src/discovery/contracts-fast.ts
|
|
681
|
-
var import_node_fs = require("fs");
|
|
682
|
-
var import_node_path5 = require("path");
|
|
683
|
-
var import_fast_glob2 = __toESM(require("fast-glob"), 1);
|
|
684
|
-
var import_ts_morph12 = require("ts-morph");
|
|
451
|
+
};
|
|
685
452
|
|
|
686
453
|
// src/discovery/dto-type-resolver.ts
|
|
687
454
|
var import_ts_morph11 = require("ts-morph");
|
|
688
455
|
|
|
689
456
|
// src/discovery/dto-to-ir.ts
|
|
690
|
-
var
|
|
457
|
+
var import_ts_morph2 = require("ts-morph");
|
|
691
458
|
|
|
692
459
|
// src/util/debug-log.ts
|
|
693
460
|
var debugEnabled = false;
|
|
@@ -699,8 +466,8 @@ function debugWarn(message) {
|
|
|
699
466
|
}
|
|
700
467
|
|
|
701
468
|
// src/discovery/type-ref-resolution.ts
|
|
702
|
-
var
|
|
703
|
-
var
|
|
469
|
+
var import_node_path3 = require("path");
|
|
470
|
+
var import_ts_morph = require("ts-morph");
|
|
704
471
|
var _EMPTY_CTX = { projectRoot: "", tsconfigPaths: null };
|
|
705
472
|
var _ctxByProject = /* @__PURE__ */ new WeakMap();
|
|
706
473
|
function setDiscoveryContext(project, ctx) {
|
|
@@ -741,17 +508,17 @@ function findTypeInFile(name, file) {
|
|
|
741
508
|
}
|
|
742
509
|
function resolveModuleSpecifier(moduleSpecifier, sourceFile, project) {
|
|
743
510
|
if (moduleSpecifier.startsWith(".")) {
|
|
744
|
-
const dir = (0,
|
|
511
|
+
const dir = (0, import_node_path3.dirname)(sourceFile.getFilePath());
|
|
745
512
|
const ext = /\.(c|m)?(j|t)sx?$/.exec(moduleSpecifier)?.[0];
|
|
746
513
|
const base = ext ? moduleSpecifier.slice(0, -ext.length) : moduleSpecifier;
|
|
747
514
|
const flavour = ext?.startsWith(".c") ? "c" : ext?.startsWith(".m") ? "m" : "";
|
|
748
515
|
const candidates = [
|
|
749
|
-
(0,
|
|
750
|
-
...flavour === "" ? [(0,
|
|
751
|
-
(0,
|
|
752
|
-
(0,
|
|
753
|
-
(0,
|
|
754
|
-
...ext === void 0 ? [(0,
|
|
516
|
+
(0, import_node_path3.resolve)(dir, `${base}.${flavour}ts`),
|
|
517
|
+
...flavour === "" ? [(0, import_node_path3.resolve)(dir, `${base}.tsx`)] : [],
|
|
518
|
+
(0, import_node_path3.resolve)(dir, `${moduleSpecifier}.ts`),
|
|
519
|
+
(0, import_node_path3.resolve)(dir, moduleSpecifier, "index.ts"),
|
|
520
|
+
(0, import_node_path3.resolve)(dir, `${base}.d.${flavour}ts`),
|
|
521
|
+
...ext === void 0 ? [(0, import_node_path3.resolve)(dir, moduleSpecifier, "index.d.ts")] : []
|
|
755
522
|
];
|
|
756
523
|
return [...new Set(candidates)];
|
|
757
524
|
}
|
|
@@ -773,8 +540,8 @@ function resolveModuleSpecifier(moduleSpecifier, sourceFile, project) {
|
|
|
773
540
|
const rest = moduleSpecifier.slice(prefix.length);
|
|
774
541
|
const candidates = [];
|
|
775
542
|
for (const mapping of mappings) {
|
|
776
|
-
const resolved = (0,
|
|
777
|
-
candidates.push(`${resolved}.ts`, (0,
|
|
543
|
+
const resolved = (0, import_node_path3.resolve)(baseUrl, mapping.replace("*", rest));
|
|
544
|
+
candidates.push(`${resolved}.ts`, (0, import_node_path3.resolve)(resolved, "index.ts"));
|
|
778
545
|
}
|
|
779
546
|
dbg(" resolved candidates:", candidates);
|
|
780
547
|
return candidates;
|
|
@@ -965,13 +732,13 @@ function resolveTypeRef(nodeOrName, sourceFile, project, opts) {
|
|
|
965
732
|
name = nodeOrName;
|
|
966
733
|
} else {
|
|
967
734
|
const typeNode = nodeOrName;
|
|
968
|
-
if (opts.unwrapContainers &&
|
|
735
|
+
if (opts.unwrapContainers && import_ts_morph.Node.isArrayTypeNode(typeNode)) {
|
|
969
736
|
const inner = resolveTypeRef(typeNode.getElementTypeNode(), sourceFile, project, opts);
|
|
970
737
|
return inner ? { ...inner, isArray: true } : null;
|
|
971
738
|
}
|
|
972
|
-
if (!
|
|
739
|
+
if (!import_ts_morph.Node.isTypeReference(typeNode)) return null;
|
|
973
740
|
const typeName = typeNode.getTypeName();
|
|
974
|
-
const refName =
|
|
741
|
+
const refName = import_ts_morph.Node.isIdentifier(typeName) ? typeName.getText() : null;
|
|
975
742
|
if (!refName) return null;
|
|
976
743
|
if (opts.unwrapContainers && refName === "Promise") {
|
|
977
744
|
const first = typeNode.getTypeArguments()[0];
|
|
@@ -1105,7 +872,7 @@ function buildProperty(prop, classFile, ctx) {
|
|
|
1105
872
|
const dec = (n) => decorators.get(n);
|
|
1106
873
|
const typeNode = prop.getTypeNode();
|
|
1107
874
|
const typeText = typeNode?.getText() ?? "unknown";
|
|
1108
|
-
const isArrayType = !!typeNode &&
|
|
875
|
+
const isArrayType = !!typeNode && import_ts_morph2.Node.isArrayTypeNode(typeNode);
|
|
1109
876
|
const asField = (child) => applyPresence(
|
|
1110
877
|
has("IsArray") || isArrayType ? { kind: "array", element: child } : child,
|
|
1111
878
|
decorators
|
|
@@ -1319,22 +1086,22 @@ function firstArgText(decorator) {
|
|
|
1319
1086
|
}
|
|
1320
1087
|
function numericArg(decorator) {
|
|
1321
1088
|
const arg = firstArg(decorator);
|
|
1322
|
-
if (arg &&
|
|
1089
|
+
if (arg && import_ts_morph2.Node.isNumericLiteral(arg)) return arg.getText();
|
|
1323
1090
|
return null;
|
|
1324
1091
|
}
|
|
1325
1092
|
function numericArgs(decorator) {
|
|
1326
1093
|
const args = decorator?.getArguments() ?? [];
|
|
1327
|
-
const num = (n) => n &&
|
|
1094
|
+
const num = (n) => n && import_ts_morph2.Node.isNumericLiteral(n) ? n.getText() : null;
|
|
1328
1095
|
return [num(args[0]), num(args[1])];
|
|
1329
1096
|
}
|
|
1330
1097
|
function messageRaw(decorator) {
|
|
1331
1098
|
const args = decorator?.getArguments() ?? [];
|
|
1332
1099
|
for (const arg of args) {
|
|
1333
|
-
if (
|
|
1100
|
+
if (import_ts_morph2.Node.isObjectLiteralExpression(arg)) {
|
|
1334
1101
|
for (const prop of arg.getProperties()) {
|
|
1335
|
-
if (
|
|
1102
|
+
if (import_ts_morph2.Node.isPropertyAssignment(prop) && prop.getName() === "message") {
|
|
1336
1103
|
const init = prop.getInitializer();
|
|
1337
|
-
if (init &&
|
|
1104
|
+
if (init && import_ts_morph2.Node.isStringLiteral(init)) return init.getText();
|
|
1338
1105
|
}
|
|
1339
1106
|
}
|
|
1340
1107
|
}
|
|
@@ -1343,30 +1110,30 @@ function messageRaw(decorator) {
|
|
|
1343
1110
|
}
|
|
1344
1111
|
function resolveDiscriminator(decorator) {
|
|
1345
1112
|
const optsArg = decorator?.getArguments()[1];
|
|
1346
|
-
if (!optsArg || !
|
|
1113
|
+
if (!optsArg || !import_ts_morph2.Node.isObjectLiteralExpression(optsArg)) return null;
|
|
1347
1114
|
let discProp;
|
|
1348
1115
|
for (const prop of optsArg.getProperties()) {
|
|
1349
|
-
if (
|
|
1116
|
+
if (import_ts_morph2.Node.isPropertyAssignment(prop) && prop.getName() === "discriminator") {
|
|
1350
1117
|
discProp = prop.getInitializer();
|
|
1351
1118
|
}
|
|
1352
1119
|
}
|
|
1353
|
-
if (!discProp || !
|
|
1120
|
+
if (!discProp || !import_ts_morph2.Node.isObjectLiteralExpression(discProp)) return null;
|
|
1354
1121
|
let property = null;
|
|
1355
1122
|
const subTypes = [];
|
|
1356
1123
|
for (const prop of discProp.getProperties()) {
|
|
1357
|
-
if (!
|
|
1124
|
+
if (!import_ts_morph2.Node.isPropertyAssignment(prop)) continue;
|
|
1358
1125
|
const name = prop.getName();
|
|
1359
1126
|
const init = prop.getInitializer();
|
|
1360
1127
|
if (!init) continue;
|
|
1361
|
-
if (name === "property" &&
|
|
1128
|
+
if (name === "property" && import_ts_morph2.Node.isStringLiteral(init)) {
|
|
1362
1129
|
property = init.getLiteralValue();
|
|
1363
|
-
} else if (name === "subTypes" &&
|
|
1130
|
+
} else if (name === "subTypes" && import_ts_morph2.Node.isArrayLiteralExpression(init)) {
|
|
1364
1131
|
for (const el of init.getElements()) {
|
|
1365
|
-
if (!
|
|
1132
|
+
if (!import_ts_morph2.Node.isObjectLiteralExpression(el)) continue;
|
|
1366
1133
|
for (const p of el.getProperties()) {
|
|
1367
|
-
if (!
|
|
1134
|
+
if (!import_ts_morph2.Node.isPropertyAssignment(p) || p.getName() !== "name") continue;
|
|
1368
1135
|
const nameInit = p.getInitializer();
|
|
1369
|
-
if (nameInit &&
|
|
1136
|
+
if (nameInit && import_ts_morph2.Node.isIdentifier(nameInit)) subTypes.push(nameInit.getText());
|
|
1370
1137
|
}
|
|
1371
1138
|
}
|
|
1372
1139
|
}
|
|
@@ -1377,9 +1144,9 @@ function resolveDiscriminator(decorator) {
|
|
|
1377
1144
|
function resolveTypeFactoryName(decorator) {
|
|
1378
1145
|
const arg = firstArg(decorator);
|
|
1379
1146
|
if (!arg) return null;
|
|
1380
|
-
if (
|
|
1147
|
+
if (import_ts_morph2.Node.isArrowFunction(arg)) {
|
|
1381
1148
|
const body = arg.getBody();
|
|
1382
|
-
if (
|
|
1149
|
+
if (import_ts_morph2.Node.isIdentifier(body)) return body.getText();
|
|
1383
1150
|
}
|
|
1384
1151
|
return null;
|
|
1385
1152
|
}
|
|
@@ -1388,12 +1155,12 @@ function singularClassName(typeText) {
|
|
|
1388
1155
|
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(inner) ? inner : null;
|
|
1389
1156
|
}
|
|
1390
1157
|
function genericTypeArgNames(typeNode) {
|
|
1391
|
-
if (!typeNode || !
|
|
1158
|
+
if (!typeNode || !import_ts_morph2.Node.isTypeReference(typeNode)) return [];
|
|
1392
1159
|
const names = [];
|
|
1393
1160
|
for (const arg of typeNode.getTypeArguments()) {
|
|
1394
|
-
if (!
|
|
1161
|
+
if (!import_ts_morph2.Node.isTypeReference(arg)) return [];
|
|
1395
1162
|
const tn = arg.getTypeName();
|
|
1396
|
-
if (!
|
|
1163
|
+
if (!import_ts_morph2.Node.isIdentifier(tn)) return [];
|
|
1397
1164
|
names.push(tn.getText());
|
|
1398
1165
|
}
|
|
1399
1166
|
return names;
|
|
@@ -1401,7 +1168,7 @@ function genericTypeArgNames(typeNode) {
|
|
|
1401
1168
|
function enumSchemaFromDecorator(decorator, classFile, ctx) {
|
|
1402
1169
|
const arg = firstArg(decorator);
|
|
1403
1170
|
if (!arg) return null;
|
|
1404
|
-
if (
|
|
1171
|
+
if (import_ts_morph2.Node.isIdentifier(arg)) {
|
|
1405
1172
|
const name = arg.getText();
|
|
1406
1173
|
const resolved = findType(name, classFile, ctx.project);
|
|
1407
1174
|
if (resolved && resolved.kind === "enum") {
|
|
@@ -1415,12 +1182,12 @@ function enumSchemaFromDecorator(decorator, classFile, ctx) {
|
|
|
1415
1182
|
}
|
|
1416
1183
|
return { kind: "unknown", note: `@IsEnum(${name}): enum not resolvable to literals` };
|
|
1417
1184
|
}
|
|
1418
|
-
if (
|
|
1185
|
+
if (import_ts_morph2.Node.isObjectLiteralExpression(arg)) {
|
|
1419
1186
|
const values = [];
|
|
1420
1187
|
for (const p of arg.getProperties()) {
|
|
1421
|
-
if (!
|
|
1188
|
+
if (!import_ts_morph2.Node.isPropertyAssignment(p)) continue;
|
|
1422
1189
|
const init = p.getInitializer();
|
|
1423
|
-
if (init &&
|
|
1190
|
+
if (init && import_ts_morph2.Node.isStringLiteral(init)) values.push(init.getText());
|
|
1424
1191
|
}
|
|
1425
1192
|
if (values.length > 0) return { kind: "enum", literals: values };
|
|
1426
1193
|
}
|
|
@@ -1428,9 +1195,9 @@ function enumSchemaFromDecorator(decorator, classFile, ctx) {
|
|
|
1428
1195
|
}
|
|
1429
1196
|
function inSchemaFromDecorator(decorator) {
|
|
1430
1197
|
const arg = firstArg(decorator);
|
|
1431
|
-
if (arg &&
|
|
1198
|
+
if (arg && import_ts_morph2.Node.isArrayLiteralExpression(arg)) {
|
|
1432
1199
|
const elements = arg.getElements();
|
|
1433
|
-
const allStrings = elements.every((e) =>
|
|
1200
|
+
const allStrings = elements.every((e) => import_ts_morph2.Node.isStringLiteral(e));
|
|
1434
1201
|
if (allStrings && elements.length > 0) {
|
|
1435
1202
|
return { kind: "enum", literals: elements.map((e) => e.getText()) };
|
|
1436
1203
|
}
|
|
@@ -1448,7 +1215,7 @@ function inSchemaFromDecorator(decorator) {
|
|
|
1448
1215
|
var import_ts_morph6 = require("ts-morph");
|
|
1449
1216
|
|
|
1450
1217
|
// src/discovery/filter-field-types.ts
|
|
1451
|
-
var
|
|
1218
|
+
var import_ts_morph3 = require("ts-morph");
|
|
1452
1219
|
|
|
1453
1220
|
// src/discovery/enum-resolution.ts
|
|
1454
1221
|
var _enumCache = /* @__PURE__ */ new WeakMap();
|
|
@@ -1501,28 +1268,28 @@ function markNullable(r, nullable) {
|
|
|
1501
1268
|
}
|
|
1502
1269
|
var TRANSPARENT_TYPE_BRANDS = /* @__PURE__ */ new Set(["Opt", "Hidden"]);
|
|
1503
1270
|
function classifyTypeNode(typeNode, sourceFile, project, opts) {
|
|
1504
|
-
if (
|
|
1271
|
+
if (import_ts_morph3.Node.isUnionTypeNode(typeNode)) {
|
|
1505
1272
|
let nullable = false;
|
|
1506
1273
|
const stringLits = [];
|
|
1507
1274
|
const numberLits = [];
|
|
1508
1275
|
const others = [];
|
|
1509
1276
|
for (const member of typeNode.getTypeNodes()) {
|
|
1510
1277
|
const kind = member.getKind();
|
|
1511
|
-
if (kind ===
|
|
1278
|
+
if (kind === import_ts_morph3.SyntaxKind.NullKeyword || kind === import_ts_morph3.SyntaxKind.UndefinedKeyword) {
|
|
1512
1279
|
nullable = true;
|
|
1513
1280
|
continue;
|
|
1514
1281
|
}
|
|
1515
|
-
if (
|
|
1282
|
+
if (import_ts_morph3.Node.isLiteralTypeNode(member)) {
|
|
1516
1283
|
const lit = member.getLiteral();
|
|
1517
|
-
if (
|
|
1284
|
+
if (import_ts_morph3.Node.isStringLiteral(lit)) {
|
|
1518
1285
|
stringLits.push(lit.getLiteralValue());
|
|
1519
1286
|
continue;
|
|
1520
1287
|
}
|
|
1521
|
-
if (
|
|
1288
|
+
if (import_ts_morph3.Node.isNumericLiteral(lit)) {
|
|
1522
1289
|
numberLits.push(lit.getText());
|
|
1523
1290
|
continue;
|
|
1524
1291
|
}
|
|
1525
|
-
if (lit.getKind() ===
|
|
1292
|
+
if (lit.getKind() === import_ts_morph3.SyntaxKind.NullKeyword) {
|
|
1526
1293
|
nullable = true;
|
|
1527
1294
|
continue;
|
|
1528
1295
|
}
|
|
@@ -1542,19 +1309,19 @@ function classifyTypeNode(typeNode, sourceFile, project, opts) {
|
|
|
1542
1309
|
return markNullable({ kind: "unknown" }, nullable);
|
|
1543
1310
|
}
|
|
1544
1311
|
switch (typeNode.getKind()) {
|
|
1545
|
-
case
|
|
1312
|
+
case import_ts_morph3.SyntaxKind.StringKeyword:
|
|
1546
1313
|
return { kind: "string" };
|
|
1547
|
-
case
|
|
1314
|
+
case import_ts_morph3.SyntaxKind.NumberKeyword:
|
|
1548
1315
|
return { kind: "number" };
|
|
1549
|
-
case
|
|
1316
|
+
case import_ts_morph3.SyntaxKind.BooleanKeyword:
|
|
1550
1317
|
return { kind: "boolean" };
|
|
1551
|
-
case
|
|
1552
|
-
case
|
|
1318
|
+
case import_ts_morph3.SyntaxKind.AnyKeyword:
|
|
1319
|
+
case import_ts_morph3.SyntaxKind.UnknownKeyword:
|
|
1553
1320
|
return { kind: "unknown" };
|
|
1554
1321
|
default:
|
|
1555
1322
|
break;
|
|
1556
1323
|
}
|
|
1557
|
-
if (
|
|
1324
|
+
if (import_ts_morph3.Node.isTypeReference(typeNode)) {
|
|
1558
1325
|
const refName = typeNode.getTypeName().getText();
|
|
1559
1326
|
if (refName === "Date") return { kind: "date" };
|
|
1560
1327
|
if (refName === "Record" || refName === "Object") return { kind: "json" };
|
|
@@ -1572,25 +1339,25 @@ function classifyTypeNode(typeNode, sourceFile, project, opts) {
|
|
|
1572
1339
|
if (typeRef) return { kind: "unknown", typeRef };
|
|
1573
1340
|
return { kind: "unknown" };
|
|
1574
1341
|
}
|
|
1575
|
-
if (
|
|
1342
|
+
if (import_ts_morph3.Node.isTypeLiteral(typeNode)) return { kind: "json" };
|
|
1576
1343
|
return { kind: "unknown" };
|
|
1577
1344
|
}
|
|
1578
1345
|
function enumFromDecoratorArgs(args, sourceFile, project) {
|
|
1579
1346
|
for (const arg of args) {
|
|
1580
|
-
if (
|
|
1347
|
+
if (import_ts_morph3.Node.isArrowFunction(arg)) {
|
|
1581
1348
|
const body = arg.getBody();
|
|
1582
|
-
if (
|
|
1349
|
+
if (import_ts_morph3.Node.isIdentifier(body)) {
|
|
1583
1350
|
const en = resolveEnumValues(body.getText(), sourceFile, project);
|
|
1584
1351
|
if (en) return en;
|
|
1585
1352
|
}
|
|
1586
1353
|
}
|
|
1587
|
-
if (
|
|
1354
|
+
if (import_ts_morph3.Node.isObjectLiteralExpression(arg)) {
|
|
1588
1355
|
const itemsProp = arg.getProperty("items");
|
|
1589
|
-
if (itemsProp &&
|
|
1356
|
+
if (itemsProp && import_ts_morph3.Node.isPropertyAssignment(itemsProp)) {
|
|
1590
1357
|
const init = itemsProp.getInitializer();
|
|
1591
|
-
if (init &&
|
|
1358
|
+
if (init && import_ts_morph3.Node.isArrowFunction(init)) {
|
|
1592
1359
|
const body = init.getBody();
|
|
1593
|
-
if (
|
|
1360
|
+
if (import_ts_morph3.Node.isIdentifier(body)) {
|
|
1594
1361
|
const en = resolveEnumValues(body.getText(), sourceFile, project);
|
|
1595
1362
|
if (en) return en;
|
|
1596
1363
|
}
|
|
@@ -1613,7 +1380,7 @@ function classifyFromColumnDecorator(prop, sourceFile, project) {
|
|
|
1613
1380
|
return { kind: "string" };
|
|
1614
1381
|
}
|
|
1615
1382
|
for (const arg of args) {
|
|
1616
|
-
if (
|
|
1383
|
+
if (import_ts_morph3.Node.isStringLiteral(arg)) {
|
|
1617
1384
|
const raw = arg.getLiteralValue();
|
|
1618
1385
|
const kind = classifyTypeKeyword(raw);
|
|
1619
1386
|
if (kind) {
|
|
@@ -1621,11 +1388,11 @@ function classifyFromColumnDecorator(prop, sourceFile, project) {
|
|
|
1621
1388
|
return { kind };
|
|
1622
1389
|
}
|
|
1623
1390
|
}
|
|
1624
|
-
if (
|
|
1391
|
+
if (import_ts_morph3.Node.isObjectLiteralExpression(arg)) {
|
|
1625
1392
|
const enumProp = arg.getProperty("enum");
|
|
1626
|
-
if (enumProp &&
|
|
1393
|
+
if (enumProp && import_ts_morph3.Node.isPropertyAssignment(enumProp)) {
|
|
1627
1394
|
const init = enumProp.getInitializer();
|
|
1628
|
-
if (init &&
|
|
1395
|
+
if (init && import_ts_morph3.Node.isIdentifier(init)) {
|
|
1629
1396
|
const en = resolveEnumValues(init.getText(), sourceFile, project);
|
|
1630
1397
|
if (en) {
|
|
1631
1398
|
return en.numeric ? { kind: "number", enumValues: en.values, numericEnum: true } : { kind: "string", enumValues: en.values };
|
|
@@ -1635,15 +1402,15 @@ function classifyFromColumnDecorator(prop, sourceFile, project) {
|
|
|
1635
1402
|
}
|
|
1636
1403
|
for (const key of ["columnType", "type"]) {
|
|
1637
1404
|
const typeProp = arg.getProperty(key);
|
|
1638
|
-
if (!typeProp || !
|
|
1405
|
+
if (!typeProp || !import_ts_morph3.Node.isPropertyAssignment(typeProp)) continue;
|
|
1639
1406
|
const init = typeProp.getInitializer();
|
|
1640
1407
|
if (!init) continue;
|
|
1641
|
-
if (
|
|
1408
|
+
if (import_ts_morph3.Node.isStringLiteral(init)) {
|
|
1642
1409
|
const kind = classifyTypeKeyword(init.getLiteralValue());
|
|
1643
1410
|
if (kind) return { kind };
|
|
1644
1411
|
continue;
|
|
1645
1412
|
}
|
|
1646
|
-
if (
|
|
1413
|
+
if (import_ts_morph3.Node.isIdentifier(init)) {
|
|
1647
1414
|
const kind = classifyTypeKeyword(init.getText());
|
|
1648
1415
|
if (kind) return { kind };
|
|
1649
1416
|
}
|
|
@@ -1686,6 +1453,49 @@ function toFilterFieldType(name, r) {
|
|
|
1686
1453
|
|
|
1687
1454
|
// src/discovery/heritage.ts
|
|
1688
1455
|
var import_ts_morph5 = require("ts-morph");
|
|
1456
|
+
|
|
1457
|
+
// src/discovery/unresolved-imports.ts
|
|
1458
|
+
var import_ts_morph4 = require("ts-morph");
|
|
1459
|
+
var UnresolvedImports = class {
|
|
1460
|
+
read;
|
|
1461
|
+
/**
|
|
1462
|
+
* The files with an import that failed to resolve in `program` and now resolves
|
|
1463
|
+
* to a TypeScript file the program does not hold.
|
|
1464
|
+
*/
|
|
1465
|
+
nowResolving(program) {
|
|
1466
|
+
if (this.read?.program !== program) {
|
|
1467
|
+
this.read = { program, imports: failedImports(program) };
|
|
1468
|
+
}
|
|
1469
|
+
const options = program.getCompilerOptions();
|
|
1470
|
+
const files = /* @__PURE__ */ new Set();
|
|
1471
|
+
for (const { specifier, from } of this.read.imports) {
|
|
1472
|
+
const file = import_ts_morph4.ts.resolveModuleName(specifier, from, options, import_ts_morph4.ts.sys).resolvedModule?.resolvedFileName;
|
|
1473
|
+
if (file !== void 0 && /\.(m|c)?tsx?$/.test(file) && !program.getSourceFile(file)) {
|
|
1474
|
+
files.add(from);
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
return [...files];
|
|
1478
|
+
}
|
|
1479
|
+
};
|
|
1480
|
+
function failedImports(program) {
|
|
1481
|
+
const checker = program.getTypeChecker();
|
|
1482
|
+
const imports = [];
|
|
1483
|
+
for (const file of program.getSourceFiles()) {
|
|
1484
|
+
if (program.isSourceFileDefaultLibrary(file) || file.isDeclarationFile) continue;
|
|
1485
|
+
if (program.isSourceFileFromExternalLibrary(file)) continue;
|
|
1486
|
+
for (const statement of file.statements) {
|
|
1487
|
+
if (!import_ts_morph4.ts.isImportDeclaration(statement) && !import_ts_morph4.ts.isExportDeclaration(statement)) continue;
|
|
1488
|
+
const specifier = statement.moduleSpecifier;
|
|
1489
|
+
if (!specifier || !import_ts_morph4.ts.isStringLiteral(specifier)) continue;
|
|
1490
|
+
if (!checker.getSymbolAtLocation(specifier)) {
|
|
1491
|
+
imports.push({ specifier: specifier.text, from: file.fileName });
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
return imports;
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
// src/discovery/heritage.ts
|
|
1689
1499
|
function resolveHeritageCall(node) {
|
|
1690
1500
|
if (!node) return void 0;
|
|
1691
1501
|
const expr = unwrapExpression(node);
|
|
@@ -1885,15 +1695,42 @@ function resolvePropertyClassReference(prop) {
|
|
|
1885
1695
|
return { key, ref };
|
|
1886
1696
|
}
|
|
1887
1697
|
var mixinTypeProject;
|
|
1698
|
+
var mixinFiles = new DiskFingerprints();
|
|
1699
|
+
var mixinStale = false;
|
|
1700
|
+
var mixinUnresolved = new UnresolvedImports();
|
|
1888
1701
|
function getMixinTypeProject() {
|
|
1889
|
-
mixinTypeProject
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1702
|
+
if (mixinTypeProject && mixinStale && !refreshMixinTypeProject(mixinTypeProject)) {
|
|
1703
|
+
mixinTypeProject = void 0;
|
|
1704
|
+
}
|
|
1705
|
+
mixinStale = false;
|
|
1706
|
+
if (!mixinTypeProject) {
|
|
1707
|
+
mixinTypeProject = new import_ts_morph5.Project({
|
|
1708
|
+
skipAddingFilesFromTsConfig: true,
|
|
1709
|
+
compilerOptions: { strict: true }
|
|
1710
|
+
});
|
|
1711
|
+
mixinFiles = new DiskFingerprints();
|
|
1712
|
+
mixinUnresolved = new UnresolvedImports();
|
|
1713
|
+
}
|
|
1893
1714
|
return mixinTypeProject;
|
|
1894
1715
|
}
|
|
1895
1716
|
function clearMixinTypeProject() {
|
|
1896
1717
|
mixinTypeProject = void 0;
|
|
1718
|
+
mixinStale = false;
|
|
1719
|
+
}
|
|
1720
|
+
function invalidateMixinTypeProject() {
|
|
1721
|
+
mixinStale = true;
|
|
1722
|
+
}
|
|
1723
|
+
function refreshMixinTypeProject(project) {
|
|
1724
|
+
const program = project.getProgram().compilerObject;
|
|
1725
|
+
const files = program.getSourceFiles().filter((file) => !program.isSourceFileDefaultLibrary(file)).map((file) => ({ path: file.fileName, text: file.text }));
|
|
1726
|
+
const changed = mixinFiles.changed(files);
|
|
1727
|
+
if (changed.length === 0) return mixinUnresolved.nowResolving(program).length === 0;
|
|
1728
|
+
for (const path of changed) {
|
|
1729
|
+
const sf = project.getSourceFile(path);
|
|
1730
|
+
if (sf?.refreshFromFileSystemSync() !== import_ts_morph5.FileSystemRefreshResult.Updated) return false;
|
|
1731
|
+
}
|
|
1732
|
+
project.resolveSourceFileDependencies();
|
|
1733
|
+
return true;
|
|
1897
1734
|
}
|
|
1898
1735
|
function resolveInstantiatedReturnType(cls, methodName) {
|
|
1899
1736
|
const filePath = cls.getSourceFile().getFilePath();
|
|
@@ -2251,8 +2088,7 @@ var SchemaTypeProgram = class {
|
|
|
2251
2088
|
skipAddingFilesFromTsConfig: true,
|
|
2252
2089
|
compilerOptions: this.options
|
|
2253
2090
|
});
|
|
2254
|
-
|
|
2255
|
-
this.flush();
|
|
2091
|
+
this.requestDiscoveryFiles();
|
|
2256
2092
|
}
|
|
2257
2093
|
project;
|
|
2258
2094
|
options;
|
|
@@ -2265,6 +2101,53 @@ var SchemaTypeProgram = class {
|
|
|
2265
2101
|
needs = /* @__PURE__ */ new Map();
|
|
2266
2102
|
/** Pruned text per file, as last written into {@link project}. */
|
|
2267
2103
|
written = /* @__PURE__ */ new Map();
|
|
2104
|
+
/** What {@link project} read from disk unpruned (packages, declaration files). */
|
|
2105
|
+
externalFiles = new DiskFingerprints();
|
|
2106
|
+
scratchFiles = new DiskFingerprints();
|
|
2107
|
+
/** The last pruning of each file, with the source text and needs it was pruned for. */
|
|
2108
|
+
pruned = /* @__PURE__ */ new Map();
|
|
2109
|
+
/**
|
|
2110
|
+
* Bring the program up to date with discovery's files and the disk, keeping the
|
|
2111
|
+
* lib and package declarations it has already parsed and checked. Leaves it
|
|
2112
|
+
* holding exactly the files a new program built now would hold, with the same
|
|
2113
|
+
* text. Returns false when a file the program read unpruned has changed on disk
|
|
2114
|
+
* (a package was reinstalled, a `.d.ts` was edited): the caller builds a new
|
|
2115
|
+
* program instead.
|
|
2116
|
+
*/
|
|
2117
|
+
refresh() {
|
|
2118
|
+
const program = this.project.getProgram().compilerObject;
|
|
2119
|
+
const external = program.getSourceFiles().filter(
|
|
2120
|
+
(file) => !program.isSourceFileDefaultLibrary(file) && !this.written.has(file.fileName)
|
|
2121
|
+
).map((file) => ({ path: file.fileName, text: file.text }));
|
|
2122
|
+
if (this.externalFiles.changed(external).length > 0) return false;
|
|
2123
|
+
const scratch = this.scratch.getSourceFiles().map((sf) => ({ path: sf.getFilePath(), text: sf.getFullText() }));
|
|
2124
|
+
for (const path of this.scratchFiles.changed(scratch)) {
|
|
2125
|
+
const sf = this.scratch.getSourceFile(path);
|
|
2126
|
+
if (sf?.refreshFromFileSystemSync() === import_ts_morph7.FileSystemRefreshResult.Deleted) {
|
|
2127
|
+
this.scratchFiles.forget(path);
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
this.needs.clear();
|
|
2131
|
+
this.requestDiscoveryFiles();
|
|
2132
|
+
for (const path of [...this.written.keys()]) {
|
|
2133
|
+
if (this.needs.has(path)) continue;
|
|
2134
|
+
const sf = this.project.getSourceFile(path);
|
|
2135
|
+
if (sf) this.project.removeSourceFile(sf);
|
|
2136
|
+
this.written.delete(path);
|
|
2137
|
+
}
|
|
2138
|
+
return true;
|
|
2139
|
+
}
|
|
2140
|
+
/**
|
|
2141
|
+
* Everything discovery holds now (the controllers) goes in before the first
|
|
2142
|
+
* query: a file added afterwards discards the program and its checker, and
|
|
2143
|
+
* rebuilding them once per controller is exactly the cost this avoids.
|
|
2144
|
+
*/
|
|
2145
|
+
requestDiscoveryFiles() {
|
|
2146
|
+
for (const sf of this.discovery.getSourceFiles()) {
|
|
2147
|
+
this.request(sf.getFilePath(), { schema: true });
|
|
2148
|
+
}
|
|
2149
|
+
this.flush();
|
|
2150
|
+
}
|
|
2268
2151
|
/**
|
|
2269
2152
|
* The typed copy of a discovery file, with its schema positions kept. Adding one
|
|
2270
2153
|
* that was not there yet (a contract file discovery reached lazily) rebuilds the
|
|
@@ -2305,12 +2188,21 @@ var SchemaTypeProgram = class {
|
|
|
2305
2188
|
while (this.queue.length > 0) {
|
|
2306
2189
|
const path = this.queue.pop();
|
|
2307
2190
|
const original = this.original(path);
|
|
2308
|
-
if (!original)
|
|
2309
|
-
|
|
2310
|
-
|
|
2191
|
+
if (!original) {
|
|
2192
|
+
if (this.written.has(path)) changed.set(path, void 0);
|
|
2193
|
+
continue;
|
|
2194
|
+
}
|
|
2195
|
+
const { text, requests } = this.pruneCached(path, original, this.needs.get(path));
|
|
2196
|
+
for (const { path: target, want } of requests) this.request(target, want);
|
|
2311
2197
|
if (this.written.get(path) !== text) changed.set(path, text);
|
|
2312
2198
|
}
|
|
2313
2199
|
for (const [path, text] of changed) {
|
|
2200
|
+
if (text === void 0) {
|
|
2201
|
+
const sf = this.project.getSourceFile(path);
|
|
2202
|
+
if (sf) this.project.removeSourceFile(sf);
|
|
2203
|
+
this.written.delete(path);
|
|
2204
|
+
continue;
|
|
2205
|
+
}
|
|
2314
2206
|
this.project.createSourceFile(path, text, { overwrite: true });
|
|
2315
2207
|
this.written.set(path, text);
|
|
2316
2208
|
}
|
|
@@ -2319,11 +2211,29 @@ var SchemaTypeProgram = class {
|
|
|
2319
2211
|
original(path) {
|
|
2320
2212
|
return this.discovery.getSourceFile(path) ?? this.scratch.getSourceFile(path) ?? this.scratch.addSourceFileAtPathIfExists(path);
|
|
2321
2213
|
}
|
|
2214
|
+
/** {@link prune}, reusing the last result while the file's text and needs are the same. */
|
|
2215
|
+
pruneCached(path, sf, needs) {
|
|
2216
|
+
const source = sf.getFullText();
|
|
2217
|
+
const key = `${needs.schema}:${needs.all}:${[...needs.names].sort().join(",")}`;
|
|
2218
|
+
const last = this.pruned.get(path);
|
|
2219
|
+
if (last && last.source === source && last.needs === key) return last;
|
|
2220
|
+
const result = this.prune(sf, needs);
|
|
2221
|
+
if (result.resolved) this.pruned.set(path, { ...result, source, needs: key });
|
|
2222
|
+
else this.pruned.delete(path);
|
|
2223
|
+
return result;
|
|
2224
|
+
}
|
|
2322
2225
|
/**
|
|
2323
2226
|
* `sf`'s text with every import/re-export nothing in `needs` references blanked,
|
|
2324
|
-
*
|
|
2227
|
+
* and the names each kept one brings in from its target.
|
|
2325
2228
|
*/
|
|
2326
2229
|
prune(sf, needs) {
|
|
2230
|
+
const requests = [];
|
|
2231
|
+
let resolved = true;
|
|
2232
|
+
const resolveFrom = (specifier) => {
|
|
2233
|
+
const target = this.resolve(specifier, sf);
|
|
2234
|
+
if (target === null) resolved = false;
|
|
2235
|
+
return target ?? void 0;
|
|
2236
|
+
};
|
|
2327
2237
|
const locals = topLevelDeclarations(sf);
|
|
2328
2238
|
const roots = [];
|
|
2329
2239
|
if (needs.all) roots.push(...sf.getStatements());
|
|
@@ -2376,7 +2286,7 @@ var SchemaTypeProgram = class {
|
|
|
2376
2286
|
}
|
|
2377
2287
|
};
|
|
2378
2288
|
for (const decl of sf.getImportDeclarations()) {
|
|
2379
|
-
const target =
|
|
2289
|
+
const target = resolveFrom(decl.getModuleSpecifierValue());
|
|
2380
2290
|
const namespace = decl.getNamespaceImport()?.getText();
|
|
2381
2291
|
const def = decl.getDefaultImport()?.getText();
|
|
2382
2292
|
const named = decl.getNamedImports().map((n) => ({
|
|
@@ -2390,21 +2300,24 @@ var SchemaTypeProgram = class {
|
|
|
2390
2300
|
continue;
|
|
2391
2301
|
}
|
|
2392
2302
|
if (target) {
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2303
|
+
requests.push({
|
|
2304
|
+
path: target,
|
|
2305
|
+
want: {
|
|
2306
|
+
all: wantsNamespace,
|
|
2307
|
+
names: [...wantsDefault ? ["default"] : [], ...named.map((n) => n.imported)]
|
|
2308
|
+
}
|
|
2396
2309
|
});
|
|
2397
2310
|
}
|
|
2398
2311
|
}
|
|
2399
2312
|
for (const exp of sf.getExportDeclarations()) {
|
|
2400
2313
|
const spec = exp.getModuleSpecifierValue();
|
|
2401
2314
|
if (spec === void 0) continue;
|
|
2402
|
-
const target =
|
|
2315
|
+
const target = resolveFrom(spec);
|
|
2403
2316
|
if (exp.isNamespaceExport() || exp.getNamedExports().length === 0) {
|
|
2404
2317
|
if (!needs.all && unresolvedNames.length === 0) {
|
|
2405
2318
|
blank(exp);
|
|
2406
2319
|
} else if (target) {
|
|
2407
|
-
|
|
2320
|
+
requests.push({ path: target, want: { all: needs.all, names: unresolvedNames } });
|
|
2408
2321
|
}
|
|
2409
2322
|
continue;
|
|
2410
2323
|
}
|
|
@@ -2412,13 +2325,14 @@ var SchemaTypeProgram = class {
|
|
|
2412
2325
|
(n) => needs.all || needs.names.has((n.getAliasNode() ?? n.getNameNode()).getText())
|
|
2413
2326
|
).map((n) => n.getNameNode().getText());
|
|
2414
2327
|
if (wanted.length === 0) blank(exp);
|
|
2415
|
-
else if (target)
|
|
2328
|
+
else if (target) requests.push({ path: target, want: { names: wanted } });
|
|
2416
2329
|
}
|
|
2417
|
-
return chars.join("");
|
|
2330
|
+
return { text: chars.join(""), requests, resolved };
|
|
2418
2331
|
}
|
|
2419
2332
|
/**
|
|
2420
|
-
* The project source file a module specifier resolves to
|
|
2421
|
-
* package (read unpruned by the program itself)
|
|
2333
|
+
* The project source file a module specifier resolves to; undefined for a
|
|
2334
|
+
* package or a declaration file (read unpruned by the program itself), null for
|
|
2335
|
+
* one that does not resolve.
|
|
2422
2336
|
*/
|
|
2423
2337
|
resolve(specifier, from) {
|
|
2424
2338
|
const resolved = import_ts_morph7.ts.resolveModuleName(
|
|
@@ -2427,7 +2341,8 @@ var SchemaTypeProgram = class {
|
|
|
2427
2341
|
this.options,
|
|
2428
2342
|
import_ts_morph7.ts.sys
|
|
2429
2343
|
).resolvedModule;
|
|
2430
|
-
if (!resolved
|
|
2344
|
+
if (!resolved) return null;
|
|
2345
|
+
if (resolved.isExternalLibraryImport) return void 0;
|
|
2431
2346
|
const file = resolved.resolvedFileName;
|
|
2432
2347
|
if (file.endsWith(".d.ts") || !/\.(m|c)?tsx?$/.test(file)) return void 0;
|
|
2433
2348
|
return file;
|
|
@@ -2482,8 +2397,13 @@ function defaultExport(sf) {
|
|
|
2482
2397
|
|
|
2483
2398
|
// src/discovery/schema-type-inference.ts
|
|
2484
2399
|
var programs = /* @__PURE__ */ new WeakMap();
|
|
2400
|
+
var stale = /* @__PURE__ */ new WeakSet();
|
|
2485
2401
|
function programFor(discovery) {
|
|
2486
2402
|
let program = programs.get(discovery);
|
|
2403
|
+
if (program && stale.has(discovery)) {
|
|
2404
|
+
stale.delete(discovery);
|
|
2405
|
+
if (!program.refresh()) program = void 0;
|
|
2406
|
+
}
|
|
2487
2407
|
if (!program) {
|
|
2488
2408
|
program = new SchemaTypeProgram(discovery);
|
|
2489
2409
|
programs.set(discovery, program);
|
|
@@ -2492,6 +2412,10 @@ function programFor(discovery) {
|
|
|
2492
2412
|
}
|
|
2493
2413
|
function clearSchemaTypeProject(discovery) {
|
|
2494
2414
|
programs.delete(discovery);
|
|
2415
|
+
stale.delete(discovery);
|
|
2416
|
+
}
|
|
2417
|
+
function invalidateSchemaTypeProject(discovery) {
|
|
2418
|
+
if (programs.has(discovery)) stale.add(discovery);
|
|
2495
2419
|
}
|
|
2496
2420
|
function counterpart(typedSf, node) {
|
|
2497
2421
|
const start = node.getStart();
|
|
@@ -3439,7 +3363,7 @@ async function discoverContractsFast(opts) {
|
|
|
3439
3363
|
return routes;
|
|
3440
3364
|
}
|
|
3441
3365
|
function resolveTsconfigPath(cwd, tsconfig) {
|
|
3442
|
-
return tsconfig ? (0,
|
|
3366
|
+
return tsconfig ? (0, import_node_path4.resolve)(tsconfig) : (0, import_node_path4.join)(cwd, "tsconfig.json");
|
|
3443
3367
|
}
|
|
3444
3368
|
var NO_TSCONFIG_OPTIONS = {
|
|
3445
3369
|
allowJs: true,
|
|
@@ -3447,7 +3371,7 @@ var NO_TSCONFIG_OPTIONS = {
|
|
|
3447
3371
|
strict: false
|
|
3448
3372
|
};
|
|
3449
3373
|
function loadDiscoveryTsconfig(tsconfigPath) {
|
|
3450
|
-
if (!(0,
|
|
3374
|
+
if (!(0, import_node_fs2.existsSync)(tsconfigPath)) return { options: null, files: [] };
|
|
3451
3375
|
const read = import_ts_morph12.ts.readConfigFile(tsconfigPath, (path) => import_ts_morph12.ts.sys.readFile(path));
|
|
3452
3376
|
if (read.error) {
|
|
3453
3377
|
return {
|
|
@@ -3469,7 +3393,7 @@ function loadDiscoveryTsconfig(tsconfigPath) {
|
|
|
3469
3393
|
const parsed = import_ts_morph12.ts.parseJsonSourceFileConfigFileContent(
|
|
3470
3394
|
sourceFile,
|
|
3471
3395
|
host,
|
|
3472
|
-
(0,
|
|
3396
|
+
(0, import_node_path4.dirname)(tsconfigPath),
|
|
3473
3397
|
void 0,
|
|
3474
3398
|
tsconfigPath
|
|
3475
3399
|
);
|
|
@@ -3531,6 +3455,9 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
3531
3455
|
glob;
|
|
3532
3456
|
/** Absolute paths of the controllers currently loaded as extraction roots. */
|
|
3533
3457
|
controllerPaths = /* @__PURE__ */ new Set();
|
|
3458
|
+
/** Every file in {@link project}, as last read from disk. */
|
|
3459
|
+
files = new DiskFingerprints();
|
|
3460
|
+
unresolved = new UnresolvedImports();
|
|
3534
3461
|
constructor(project, cwd, glob) {
|
|
3535
3462
|
this.project = project;
|
|
3536
3463
|
this.cwd = cwd;
|
|
@@ -3556,6 +3483,8 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
3556
3483
|
}
|
|
3557
3484
|
/** Run the initial extraction (equivalent to a first `discoverContractsFast`). */
|
|
3558
3485
|
discover() {
|
|
3486
|
+
clearMixinTypeProject();
|
|
3487
|
+
clearSchemaTypeProject(this.project);
|
|
3559
3488
|
return this.runExtraction();
|
|
3560
3489
|
}
|
|
3561
3490
|
/**
|
|
@@ -3563,26 +3492,39 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
3563
3492
|
* from disk (controllers AND any lazily-loaded DTO/imported files), re-globs
|
|
3564
3493
|
* to pick up added/removed controllers, clears the per-Project caches, then
|
|
3565
3494
|
* re-extracts. `changedPaths` is a hint; correctness does not depend on it
|
|
3566
|
-
* being exhaustive because re-globbing +
|
|
3495
|
+
* being exhaustive because re-globbing + comparing every loaded file with the
|
|
3496
|
+
* disk covers the set.
|
|
3567
3497
|
*/
|
|
3568
|
-
async rediscover(changedPaths) {
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3498
|
+
async rediscover(changedPaths, options = {}) {
|
|
3499
|
+
const hinted = new Set([...changedPaths ?? []].map((p) => (0, import_node_path4.resolve)(p)));
|
|
3500
|
+
const program = this.project.getProgram().compilerObject;
|
|
3501
|
+
const sources = program.getSourceFiles().filter(
|
|
3502
|
+
(file) => !program.isSourceFileDefaultLibrary(file) && !program.isSourceFileFromExternalLibrary(file)
|
|
3503
|
+
).map((file) => ({ path: file.fileName, text: file.text }));
|
|
3504
|
+
for (const path of /* @__PURE__ */ new Set([...hinted, ...this.files.changed(sources)])) {
|
|
3505
|
+
const sf = this.project.getSourceFile(path);
|
|
3506
|
+
if (sf && await sf.refreshFromFileSystem() === import_ts_morph12.FileSystemRefreshResult.Deleted) {
|
|
3507
|
+
this.files.forget(path);
|
|
3576
3508
|
}
|
|
3577
3509
|
}
|
|
3510
|
+
const inProject = new Set(this.project.getSourceFiles().map((sf) => sf.getFilePath()));
|
|
3511
|
+
const definitions = this.project.getLanguageService().compilerObject.getProgram();
|
|
3512
|
+
for (const path of definitions ? this.unresolved.nowResolving(definitions) : []) {
|
|
3513
|
+
const sf = this.project.getSourceFile(path);
|
|
3514
|
+
if (!sf) continue;
|
|
3515
|
+
this.project.removeSourceFile(sf);
|
|
3516
|
+
if (inProject.has(path)) this.project.addSourceFileAtPathIfExists(path);
|
|
3517
|
+
}
|
|
3578
3518
|
const globbed = new Set(
|
|
3579
3519
|
await (0, import_fast_glob2.default)(this.glob, { cwd: this.cwd, absolute: true, onlyFiles: true })
|
|
3580
3520
|
);
|
|
3521
|
+
let controllersChanged = false;
|
|
3581
3522
|
for (const f of globbed) {
|
|
3582
3523
|
if (!this.controllerPaths.has(f)) {
|
|
3583
3524
|
try {
|
|
3584
3525
|
this.project.addSourceFileAtPath(f);
|
|
3585
3526
|
this.controllerPaths.add(f);
|
|
3527
|
+
controllersChanged = true;
|
|
3586
3528
|
} catch {
|
|
3587
3529
|
}
|
|
3588
3530
|
}
|
|
@@ -3592,16 +3534,22 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
3592
3534
|
const sf = this.project.getSourceFile(f);
|
|
3593
3535
|
if (sf) this.project.removeSourceFile(sf);
|
|
3594
3536
|
this.controllerPaths.delete(f);
|
|
3537
|
+
controllersChanged = true;
|
|
3595
3538
|
}
|
|
3596
3539
|
}
|
|
3540
|
+
if (controllersChanged || options.filesAddedOrRemoved) {
|
|
3541
|
+
clearMixinTypeProject();
|
|
3542
|
+
clearSchemaTypeProject(this.project);
|
|
3543
|
+
} else {
|
|
3544
|
+
invalidateMixinTypeProject();
|
|
3545
|
+
invalidateSchemaTypeProject(this.project);
|
|
3546
|
+
}
|
|
3597
3547
|
return this.runExtraction();
|
|
3598
3548
|
}
|
|
3599
3549
|
/** Clear stale per-Project caches, then extract over the controller set. */
|
|
3600
3550
|
runExtraction() {
|
|
3601
3551
|
clearTypeResolutionCaches(this.project);
|
|
3602
3552
|
clearEnumCache(this.project);
|
|
3603
|
-
clearMixinTypeProject();
|
|
3604
|
-
clearSchemaTypeProject(this.project);
|
|
3605
3553
|
return extractRoutesFrom(this.project, this.controllerPaths);
|
|
3606
3554
|
}
|
|
3607
3555
|
};
|
|
@@ -3751,144 +3699,437 @@ function extractContractRoute(args) {
|
|
|
3751
3699
|
if (contractDef.body !== null) {
|
|
3752
3700
|
bodyZodRef = { name: `${identName}.body`, filePath };
|
|
3753
3701
|
}
|
|
3754
|
-
if (contractDef.query !== null) {
|
|
3755
|
-
queryZodRef = { name: `${identName}.query`, filePath };
|
|
3702
|
+
if (contractDef.query !== null) {
|
|
3703
|
+
queryZodRef = { name: `${identName}.query`, filePath };
|
|
3704
|
+
}
|
|
3705
|
+
}
|
|
3706
|
+
} else {
|
|
3707
|
+
console.warn(
|
|
3708
|
+
`[nestjs-codegen/fast] @ApplyContract arg is not an identifier or call expression in ${sourceFile.getFilePath()} \u2014 skipping`
|
|
3709
|
+
);
|
|
3710
|
+
return null;
|
|
3711
|
+
}
|
|
3712
|
+
if (!contractDef) return null;
|
|
3713
|
+
if (!verb) return null;
|
|
3714
|
+
const resolvedPath = joinPaths(prefix, verb.handlerPath);
|
|
3715
|
+
const methodName = method.getName();
|
|
3716
|
+
const classAs = readAsDecorator(cls, `class ${className}`);
|
|
3717
|
+
const methodAs = readAsDecorator(method, `${className}.${methodName}`);
|
|
3718
|
+
return buildRoute({
|
|
3719
|
+
className,
|
|
3720
|
+
methodName,
|
|
3721
|
+
resolvedMethod: verb.httpMethod,
|
|
3722
|
+
combinedPath: resolvedPath,
|
|
3723
|
+
classAs,
|
|
3724
|
+
methodAs,
|
|
3725
|
+
sourceFile,
|
|
3726
|
+
seenNames,
|
|
3727
|
+
mixin,
|
|
3728
|
+
contractSource: {
|
|
3729
|
+
query: contractDef.query,
|
|
3730
|
+
body: contractDef.body,
|
|
3731
|
+
response: contractDef.response,
|
|
3732
|
+
error: contractDef.error,
|
|
3733
|
+
// Path A: capture both the importable ref and the raw text. The emitter
|
|
3734
|
+
// prefers inlining the text (client-safe — re-exporting from a controller
|
|
3735
|
+
// would drag server-only deps into the client bundle).
|
|
3736
|
+
bodyZodRef,
|
|
3737
|
+
bodyZodText: contractDef.bodyZodText,
|
|
3738
|
+
queryZodRef,
|
|
3739
|
+
queryZodText: contractDef.queryZodText
|
|
3740
|
+
}
|
|
3741
|
+
});
|
|
3742
|
+
}
|
|
3743
|
+
function extractDtoRoute(args) {
|
|
3744
|
+
const { cls, method, verb, prefix, className, sourceFile, project, seenNames, mixin } = args;
|
|
3745
|
+
if (!verb) return null;
|
|
3746
|
+
const combined = joinPaths(prefix, verb.handlerPath);
|
|
3747
|
+
const methodName = method.getName();
|
|
3748
|
+
const classAs = readAsDecorator(cls, `class ${className}`);
|
|
3749
|
+
const methodAs = readAsDecorator(method, `${className}.${methodName}`);
|
|
3750
|
+
const dtoContract = extractDtoContract(method, method.getSourceFile(), project, mixin, cls);
|
|
3751
|
+
const mixinResponse = mixin ? resolveInstantiatedReturnType(cls, methodName) : void 0;
|
|
3752
|
+
return buildRoute({
|
|
3753
|
+
className,
|
|
3754
|
+
methodName,
|
|
3755
|
+
resolvedMethod: verb.httpMethod,
|
|
3756
|
+
combinedPath: combined,
|
|
3757
|
+
classAs,
|
|
3758
|
+
methodAs,
|
|
3759
|
+
sourceFile,
|
|
3760
|
+
seenNames,
|
|
3761
|
+
mixin,
|
|
3762
|
+
contractSource: {
|
|
3763
|
+
query: dtoContract?.query ?? null,
|
|
3764
|
+
body: dtoContract?.body ?? null,
|
|
3765
|
+
response: mixinResponse ?? dtoContract?.response ?? "unknown",
|
|
3766
|
+
error: dtoContract?.error ?? null,
|
|
3767
|
+
paramTypes: dtoContract?.paramTypes ?? null,
|
|
3768
|
+
queryRef: dtoContract?.queryRef ?? null,
|
|
3769
|
+
bodyRef: dtoContract?.bodyRef ?? null,
|
|
3770
|
+
responseRef: dtoContract?.responseRef ?? null,
|
|
3771
|
+
errorRef: dtoContract?.errorRef ?? null,
|
|
3772
|
+
filterFields: dtoContract?.filterFields ?? null,
|
|
3773
|
+
filterFieldTypes: dtoContract?.filterFieldTypes ?? null,
|
|
3774
|
+
filterSource: dtoContract?.filterSource ?? null,
|
|
3775
|
+
formWarnings: dtoContract?.formWarnings ?? [],
|
|
3776
|
+
bodySchema: dtoContract?.bodySchema ?? null,
|
|
3777
|
+
querySchema: dtoContract?.querySchema ?? null,
|
|
3778
|
+
stream: dtoContract?.stream ?? false,
|
|
3779
|
+
multipart: dtoContract?.multipart ?? false,
|
|
3780
|
+
multipartBody: dtoContract?.multipartBody ?? null,
|
|
3781
|
+
binaryResponse: dtoContract?.binaryResponse ?? false,
|
|
3782
|
+
asQuery: dtoContract?.asQuery ?? false
|
|
3783
|
+
}
|
|
3784
|
+
});
|
|
3785
|
+
}
|
|
3786
|
+
function extractFromSourceFile(sourceFile, project) {
|
|
3787
|
+
const routes = [];
|
|
3788
|
+
const seenNames = /* @__PURE__ */ new Map();
|
|
3789
|
+
for (const cls of sourceFile.getClasses()) {
|
|
3790
|
+
const controllerDecorator = cls.getDecorator("Controller");
|
|
3791
|
+
if (!controllerDecorator) continue;
|
|
3792
|
+
const firstArg2 = controllerDecorator.getArguments()[0];
|
|
3793
|
+
const prefix = decoratorStringArg(firstArg2) ?? "";
|
|
3794
|
+
const className = cls.getName() ?? "Unknown";
|
|
3795
|
+
const inherited = resolveInheritedMethods(cls);
|
|
3796
|
+
const mixinBinding = inherited ? {
|
|
3797
|
+
factoryName: inherited.factoryName,
|
|
3798
|
+
factoryFilePath: inherited.factoryFilePath,
|
|
3799
|
+
classArgs: inherited.classArgs,
|
|
3800
|
+
namedClassArgs: inherited.namedClassArgs
|
|
3801
|
+
} : void 0;
|
|
3802
|
+
const ownMethods = cls.getMethods();
|
|
3803
|
+
const ownNames = new Set(ownMethods.map((m) => m.getName()));
|
|
3804
|
+
const methods = [
|
|
3805
|
+
...ownMethods.map((method) => ({ method, mixin: mixinBinding })),
|
|
3806
|
+
...(inherited?.methods ?? []).filter((method) => !ownNames.has(method.getName())).map((method) => ({ method, mixin: mixinBinding }))
|
|
3807
|
+
];
|
|
3808
|
+
for (const { method, mixin } of methods) {
|
|
3809
|
+
const verb = resolveVerb(method);
|
|
3810
|
+
const applyContractDecorator = method.getDecorator("ApplyContract");
|
|
3811
|
+
const route = applyContractDecorator ? extractContractRoute({
|
|
3812
|
+
cls,
|
|
3813
|
+
method,
|
|
3814
|
+
applyContractDecorator,
|
|
3815
|
+
verb,
|
|
3816
|
+
prefix,
|
|
3817
|
+
className,
|
|
3818
|
+
sourceFile,
|
|
3819
|
+
project,
|
|
3820
|
+
seenNames,
|
|
3821
|
+
mixin
|
|
3822
|
+
}) : extractDtoRoute({
|
|
3823
|
+
cls,
|
|
3824
|
+
method,
|
|
3825
|
+
verb,
|
|
3826
|
+
prefix,
|
|
3827
|
+
className,
|
|
3828
|
+
sourceFile,
|
|
3829
|
+
project,
|
|
3830
|
+
seenNames,
|
|
3831
|
+
mixin
|
|
3832
|
+
});
|
|
3833
|
+
if (route) routes.push(route);
|
|
3834
|
+
}
|
|
3835
|
+
}
|
|
3836
|
+
return routes;
|
|
3837
|
+
}
|
|
3838
|
+
|
|
3839
|
+
// src/discovery/shared-props.ts
|
|
3840
|
+
function discoverSharedProps(project, moduleEntry) {
|
|
3841
|
+
try {
|
|
3842
|
+
let sourceFile = project.getSourceFile(moduleEntry);
|
|
3843
|
+
if (!sourceFile) {
|
|
3844
|
+
try {
|
|
3845
|
+
sourceFile = project.addSourceFileAtPath(moduleEntry);
|
|
3846
|
+
} catch {
|
|
3847
|
+
return null;
|
|
3848
|
+
}
|
|
3849
|
+
}
|
|
3850
|
+
const forRootCall = findForRootCall(sourceFile);
|
|
3851
|
+
if (!forRootCall) return null;
|
|
3852
|
+
const initializer = findShareInitializer(forRootCall);
|
|
3853
|
+
if (!initializer) return null;
|
|
3854
|
+
return extractShareType(initializer, sourceFile, project);
|
|
3855
|
+
} catch {
|
|
3856
|
+
return null;
|
|
3857
|
+
}
|
|
3858
|
+
}
|
|
3859
|
+
function discoverSharedPropsFromConfig(config) {
|
|
3860
|
+
if (!config.app?.moduleEntry) return null;
|
|
3861
|
+
try {
|
|
3862
|
+
const tsconfigPath = config.app.tsconfig ?? (0, import_node_path5.join)(config.codegen.cwd, "tsconfig.json");
|
|
3863
|
+
let project;
|
|
3864
|
+
try {
|
|
3865
|
+
project = sharedPropsProject(tsconfigPath);
|
|
3866
|
+
} catch {
|
|
3867
|
+
project = new import_ts_morph13.Project({
|
|
3868
|
+
skipAddingFilesFromTsConfig: true,
|
|
3869
|
+
skipLoadingLibFiles: true,
|
|
3870
|
+
skipFileDependencyResolution: true,
|
|
3871
|
+
compilerOptions: { allowJs: true, strict: false }
|
|
3872
|
+
});
|
|
3873
|
+
}
|
|
3874
|
+
return discoverSharedProps(project, config.app.moduleEntry);
|
|
3875
|
+
} catch {
|
|
3876
|
+
return null;
|
|
3877
|
+
}
|
|
3878
|
+
}
|
|
3879
|
+
var parsedTsconfigs = /* @__PURE__ */ new Map();
|
|
3880
|
+
function readText(file) {
|
|
3881
|
+
try {
|
|
3882
|
+
return (0, import_node_fs3.readFileSync)(file, "utf8");
|
|
3883
|
+
} catch {
|
|
3884
|
+
return "\0missing";
|
|
3885
|
+
}
|
|
3886
|
+
}
|
|
3887
|
+
function sharedPropsProject(tsconfigPath) {
|
|
3888
|
+
const text = loadDiscoveryTsconfig(tsconfigPath).files.map((file) => `${file}\0${readText(file)}`).join("\0");
|
|
3889
|
+
const flags = {
|
|
3890
|
+
skipAddingFilesFromTsConfig: true,
|
|
3891
|
+
skipLoadingLibFiles: true,
|
|
3892
|
+
skipFileDependencyResolution: true
|
|
3893
|
+
};
|
|
3894
|
+
const parsed = parsedTsconfigs.get(tsconfigPath);
|
|
3895
|
+
if (parsed?.text === text) return new import_ts_morph13.Project({ ...flags, compilerOptions: parsed.options });
|
|
3896
|
+
const project = new import_ts_morph13.Project({ ...flags, tsConfigFilePath: tsconfigPath });
|
|
3897
|
+
parsedTsconfigs.set(tsconfigPath, { text, options: project.getCompilerOptions() });
|
|
3898
|
+
return project;
|
|
3899
|
+
}
|
|
3900
|
+
function findForRootCall(sourceFile) {
|
|
3901
|
+
const callExpressions = sourceFile.getDescendantsOfKind(import_ts_morph13.SyntaxKind.CallExpression);
|
|
3902
|
+
for (const call of callExpressions) {
|
|
3903
|
+
const expr = call.getExpression();
|
|
3904
|
+
if (!import_ts_morph13.Node.isPropertyAccessExpression(expr)) continue;
|
|
3905
|
+
const methodName = expr.getName();
|
|
3906
|
+
const objectExpr = expr.getExpression();
|
|
3907
|
+
if (methodName === "forRoot" && import_ts_morph13.Node.isIdentifier(objectExpr)) {
|
|
3908
|
+
const name = objectExpr.getText();
|
|
3909
|
+
if (name === "InertiaModule") {
|
|
3910
|
+
return call;
|
|
3911
|
+
}
|
|
3912
|
+
}
|
|
3913
|
+
}
|
|
3914
|
+
return null;
|
|
3915
|
+
}
|
|
3916
|
+
function findShareInitializer(forRootCall) {
|
|
3917
|
+
if (!import_ts_morph13.Node.isCallExpression(forRootCall)) return null;
|
|
3918
|
+
const args = forRootCall.getArguments();
|
|
3919
|
+
const firstArg2 = args[0];
|
|
3920
|
+
if (!firstArg2 || !import_ts_morph13.Node.isObjectLiteralExpression(firstArg2)) return null;
|
|
3921
|
+
for (const prop of firstArg2.getProperties()) {
|
|
3922
|
+
if (import_ts_morph13.Node.isPropertyAssignment(prop) && prop.getName() === "share") {
|
|
3923
|
+
return prop.getInitializer() ?? null;
|
|
3924
|
+
}
|
|
3925
|
+
}
|
|
3926
|
+
return null;
|
|
3927
|
+
}
|
|
3928
|
+
function extractShareType(node, sourceFile, project) {
|
|
3929
|
+
if (import_ts_morph13.Node.isIdentifier(node)) {
|
|
3930
|
+
const ref = resolveIdentifierToImportRef(node, sourceFile, project);
|
|
3931
|
+
if (ref) {
|
|
3932
|
+
return {
|
|
3933
|
+
typeString: `Awaited<ReturnType<typeof import('${ref.importPath}').${ref.exportName}>>`,
|
|
3934
|
+
properties: null,
|
|
3935
|
+
isImportRef: true
|
|
3936
|
+
};
|
|
3937
|
+
}
|
|
3938
|
+
}
|
|
3939
|
+
if (import_ts_morph13.Node.isArrowFunction(node)) {
|
|
3940
|
+
const result = extractFromFunctionLike(node, sourceFile);
|
|
3941
|
+
return result ? { ...result, isImportRef: false } : null;
|
|
3942
|
+
}
|
|
3943
|
+
if (import_ts_morph13.Node.isFunctionExpression(node)) {
|
|
3944
|
+
const result = extractFromFunctionLike(node, sourceFile);
|
|
3945
|
+
return result ? { ...result, isImportRef: false } : null;
|
|
3946
|
+
}
|
|
3947
|
+
if (import_ts_morph13.Node.isObjectLiteralExpression(node)) {
|
|
3948
|
+
const result = extractFromObjectLiteral(node);
|
|
3949
|
+
return result ? { ...result, isImportRef: false } : null;
|
|
3950
|
+
}
|
|
3951
|
+
return null;
|
|
3952
|
+
}
|
|
3953
|
+
function resolveIdentifierToImportRef(id, sourceFile, project) {
|
|
3954
|
+
if (!import_ts_morph13.Node.isIdentifier(id)) return null;
|
|
3955
|
+
const name = id.getText();
|
|
3956
|
+
const localFunc = sourceFile.getFunction(name);
|
|
3957
|
+
if (localFunc?.isExported()) {
|
|
3958
|
+
const filePath = sourceFile.getFilePath().replace(/\.ts$/, "");
|
|
3959
|
+
return { importPath: filePath, exportName: name };
|
|
3960
|
+
}
|
|
3961
|
+
const localVar = sourceFile.getVariableDeclaration(name);
|
|
3962
|
+
if (localVar?.isExported()) {
|
|
3963
|
+
const filePath = sourceFile.getFilePath().replace(/\.ts$/, "");
|
|
3964
|
+
return { importPath: filePath, exportName: name };
|
|
3965
|
+
}
|
|
3966
|
+
for (const imp of sourceFile.getImportDeclarations()) {
|
|
3967
|
+
for (const named of imp.getNamedImports()) {
|
|
3968
|
+
const importedName = named.getAliasNode()?.getText() ?? named.getName();
|
|
3969
|
+
if (importedName !== name) continue;
|
|
3970
|
+
const resolvedSource = imp.getModuleSpecifierSourceFile();
|
|
3971
|
+
if (!resolvedSource) continue;
|
|
3972
|
+
const originalName = named.getName();
|
|
3973
|
+
const fn = resolvedSource.getFunction(originalName);
|
|
3974
|
+
if (fn?.isExported()) {
|
|
3975
|
+
const filePath = resolvedSource.getFilePath().replace(/\.ts$/, "");
|
|
3976
|
+
return { importPath: filePath, exportName: originalName };
|
|
3977
|
+
}
|
|
3978
|
+
const v = resolvedSource.getVariableDeclaration(originalName);
|
|
3979
|
+
if (v?.isExported()) {
|
|
3980
|
+
const filePath = resolvedSource.getFilePath().replace(/\.ts$/, "");
|
|
3981
|
+
return { importPath: filePath, exportName: originalName };
|
|
3982
|
+
}
|
|
3983
|
+
}
|
|
3984
|
+
}
|
|
3985
|
+
return null;
|
|
3986
|
+
}
|
|
3987
|
+
function extractFromFunctionLike(node, _sourceFile) {
|
|
3988
|
+
const returnTypeNode = import_ts_morph13.Node.isArrowFunction(node) || import_ts_morph13.Node.isFunctionExpression(node) ? node.getReturnTypeNode() : null;
|
|
3989
|
+
if (returnTypeNode) {
|
|
3990
|
+
return extractFromReturnTypeAnnotation(returnTypeNode);
|
|
3991
|
+
}
|
|
3992
|
+
if (import_ts_morph13.Node.isArrowFunction(node)) {
|
|
3993
|
+
const body = node.getBody();
|
|
3994
|
+
if (import_ts_morph13.Node.isParenthesizedExpression(body)) {
|
|
3995
|
+
const inner = body.getExpression();
|
|
3996
|
+
if (import_ts_morph13.Node.isObjectLiteralExpression(inner)) {
|
|
3997
|
+
return extractFromObjectLiteral(inner);
|
|
3998
|
+
}
|
|
3999
|
+
}
|
|
4000
|
+
if (import_ts_morph13.Node.isObjectLiteralExpression(body)) {
|
|
4001
|
+
return extractFromObjectLiteral(body);
|
|
4002
|
+
}
|
|
4003
|
+
if (import_ts_morph13.Node.isBlock(body)) {
|
|
4004
|
+
return extractFromBlockReturn(body);
|
|
4005
|
+
}
|
|
4006
|
+
}
|
|
4007
|
+
if (import_ts_morph13.Node.isFunctionExpression(node)) {
|
|
4008
|
+
const body = node.getBody();
|
|
4009
|
+
if (import_ts_morph13.Node.isBlock(body)) {
|
|
4010
|
+
return extractFromBlockReturn(body);
|
|
4011
|
+
}
|
|
4012
|
+
}
|
|
4013
|
+
return null;
|
|
4014
|
+
}
|
|
4015
|
+
function extractFromReturnTypeAnnotation(typeNode) {
|
|
4016
|
+
if (import_ts_morph13.Node.isTypeReference(typeNode)) {
|
|
4017
|
+
const typeName = typeNode.getTypeName();
|
|
4018
|
+
if (import_ts_morph13.Node.isIdentifier(typeName) && typeName.getText() === "Promise") {
|
|
4019
|
+
const typeArgs = typeNode.getTypeArguments();
|
|
4020
|
+
const firstArg2 = typeArgs[0];
|
|
4021
|
+
if (firstArg2) {
|
|
4022
|
+
return extractFromReturnTypeAnnotation(firstArg2);
|
|
3756
4023
|
}
|
|
4024
|
+
return null;
|
|
3757
4025
|
}
|
|
3758
|
-
} else {
|
|
3759
|
-
console.warn(
|
|
3760
|
-
`[nestjs-codegen/fast] @ApplyContract arg is not an identifier or call expression in ${sourceFile.getFilePath()} \u2014 skipping`
|
|
3761
|
-
);
|
|
3762
|
-
return null;
|
|
3763
4026
|
}
|
|
3764
|
-
if (
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
resolvedMethod: verb.httpMethod,
|
|
3774
|
-
combinedPath: resolvedPath,
|
|
3775
|
-
classAs,
|
|
3776
|
-
methodAs,
|
|
3777
|
-
sourceFile,
|
|
3778
|
-
seenNames,
|
|
3779
|
-
mixin,
|
|
3780
|
-
contractSource: {
|
|
3781
|
-
query: contractDef.query,
|
|
3782
|
-
body: contractDef.body,
|
|
3783
|
-
response: contractDef.response,
|
|
3784
|
-
error: contractDef.error,
|
|
3785
|
-
// Path A: capture both the importable ref and the raw text. The emitter
|
|
3786
|
-
// prefers inlining the text (client-safe — re-exporting from a controller
|
|
3787
|
-
// would drag server-only deps into the client bundle).
|
|
3788
|
-
bodyZodRef,
|
|
3789
|
-
bodyZodText: contractDef.bodyZodText,
|
|
3790
|
-
queryZodRef,
|
|
3791
|
-
queryZodText: contractDef.queryZodText
|
|
4027
|
+
if (import_ts_morph13.Node.isTypeLiteral(typeNode)) {
|
|
4028
|
+
const properties = [];
|
|
4029
|
+
for (const member of typeNode.getMembers()) {
|
|
4030
|
+
if (import_ts_morph13.Node.isPropertySignature(member)) {
|
|
4031
|
+
const name = member.getName();
|
|
4032
|
+
const memberTypeNode = member.getTypeNode();
|
|
4033
|
+
const type = memberTypeNode ? memberTypeNode.getText() : "unknown";
|
|
4034
|
+
properties.push({ name, type });
|
|
4035
|
+
}
|
|
3792
4036
|
}
|
|
3793
|
-
|
|
4037
|
+
if (properties.length === 0) return null;
|
|
4038
|
+
const typeString = `{ ${properties.map((p) => `${p.name}: ${p.type}`).join("; ")} }`;
|
|
4039
|
+
return { typeString, properties, isImportRef: false };
|
|
4040
|
+
}
|
|
4041
|
+
return null;
|
|
3794
4042
|
}
|
|
3795
|
-
function
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
className,
|
|
3806
|
-
methodName,
|
|
3807
|
-
resolvedMethod: verb.httpMethod,
|
|
3808
|
-
combinedPath: combined,
|
|
3809
|
-
classAs,
|
|
3810
|
-
methodAs,
|
|
3811
|
-
sourceFile,
|
|
3812
|
-
seenNames,
|
|
3813
|
-
mixin,
|
|
3814
|
-
contractSource: {
|
|
3815
|
-
query: dtoContract?.query ?? null,
|
|
3816
|
-
body: dtoContract?.body ?? null,
|
|
3817
|
-
response: mixinResponse ?? dtoContract?.response ?? "unknown",
|
|
3818
|
-
error: dtoContract?.error ?? null,
|
|
3819
|
-
paramTypes: dtoContract?.paramTypes ?? null,
|
|
3820
|
-
queryRef: dtoContract?.queryRef ?? null,
|
|
3821
|
-
bodyRef: dtoContract?.bodyRef ?? null,
|
|
3822
|
-
responseRef: dtoContract?.responseRef ?? null,
|
|
3823
|
-
errorRef: dtoContract?.errorRef ?? null,
|
|
3824
|
-
filterFields: dtoContract?.filterFields ?? null,
|
|
3825
|
-
filterFieldTypes: dtoContract?.filterFieldTypes ?? null,
|
|
3826
|
-
filterSource: dtoContract?.filterSource ?? null,
|
|
3827
|
-
formWarnings: dtoContract?.formWarnings ?? [],
|
|
3828
|
-
bodySchema: dtoContract?.bodySchema ?? null,
|
|
3829
|
-
querySchema: dtoContract?.querySchema ?? null,
|
|
3830
|
-
stream: dtoContract?.stream ?? false,
|
|
3831
|
-
multipart: dtoContract?.multipart ?? false,
|
|
3832
|
-
multipartBody: dtoContract?.multipartBody ?? null,
|
|
3833
|
-
binaryResponse: dtoContract?.binaryResponse ?? false,
|
|
3834
|
-
asQuery: dtoContract?.asQuery ?? false
|
|
4043
|
+
function extractFromBlockReturn(block) {
|
|
4044
|
+
if (!import_ts_morph13.Node.isBlock(block)) return null;
|
|
4045
|
+
const statements = block.getStatements();
|
|
4046
|
+
for (let i = statements.length - 1; i >= 0; i--) {
|
|
4047
|
+
const stmt = statements[i];
|
|
4048
|
+
if (!import_ts_morph13.Node.isReturnStatement(stmt)) continue;
|
|
4049
|
+
const expr = stmt.getExpression();
|
|
4050
|
+
if (!expr) continue;
|
|
4051
|
+
if (import_ts_morph13.Node.isObjectLiteralExpression(expr)) {
|
|
4052
|
+
return extractFromObjectLiteral(expr);
|
|
3835
4053
|
}
|
|
3836
|
-
|
|
4054
|
+
if (import_ts_morph13.Node.isParenthesizedExpression(expr)) {
|
|
4055
|
+
const inner = expr.getExpression();
|
|
4056
|
+
if (import_ts_morph13.Node.isObjectLiteralExpression(inner)) {
|
|
4057
|
+
return extractFromObjectLiteral(inner);
|
|
4058
|
+
}
|
|
4059
|
+
}
|
|
4060
|
+
break;
|
|
4061
|
+
}
|
|
4062
|
+
return null;
|
|
3837
4063
|
}
|
|
3838
|
-
function
|
|
3839
|
-
|
|
3840
|
-
const
|
|
3841
|
-
for (const
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
const
|
|
3845
|
-
|
|
3846
|
-
const
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
mixin
|
|
3874
|
-
}) : extractDtoRoute({
|
|
3875
|
-
cls,
|
|
3876
|
-
method,
|
|
3877
|
-
verb,
|
|
3878
|
-
prefix,
|
|
3879
|
-
className,
|
|
3880
|
-
sourceFile,
|
|
3881
|
-
project,
|
|
3882
|
-
seenNames,
|
|
3883
|
-
mixin
|
|
3884
|
-
});
|
|
3885
|
-
if (route) routes.push(route);
|
|
4064
|
+
function extractFromObjectLiteral(objLiteral) {
|
|
4065
|
+
if (!import_ts_morph13.Node.isObjectLiteralExpression(objLiteral)) return null;
|
|
4066
|
+
const properties = [];
|
|
4067
|
+
for (const prop of objLiteral.getProperties()) {
|
|
4068
|
+
if (!import_ts_morph13.Node.isPropertyAssignment(prop)) continue;
|
|
4069
|
+
const name = prop.getName();
|
|
4070
|
+
const initializer = prop.getInitializer();
|
|
4071
|
+
if (!initializer) continue;
|
|
4072
|
+
const type = inferExpressionType(initializer);
|
|
4073
|
+
properties.push({ name, type });
|
|
4074
|
+
}
|
|
4075
|
+
if (properties.length === 0) return null;
|
|
4076
|
+
const typeString = `{ ${properties.map((p) => `${p.name}: ${p.type}`).join("; ")} }`;
|
|
4077
|
+
return { typeString, properties, isImportRef: false };
|
|
4078
|
+
}
|
|
4079
|
+
function inferExpressionType(node) {
|
|
4080
|
+
if (import_ts_morph13.Node.isStringLiteral(node)) return "string";
|
|
4081
|
+
if (import_ts_morph13.Node.isTemplateExpression(node) || import_ts_morph13.Node.isNoSubstitutionTemplateLiteral(node))
|
|
4082
|
+
return "string";
|
|
4083
|
+
if (import_ts_morph13.Node.isNumericLiteral(node)) return "number";
|
|
4084
|
+
if (node.getKind() === import_ts_morph13.SyntaxKind.TrueKeyword || node.getKind() === import_ts_morph13.SyntaxKind.FalseKeyword) {
|
|
4085
|
+
return "boolean";
|
|
4086
|
+
}
|
|
4087
|
+
if (node.getKind() === import_ts_morph13.SyntaxKind.NullKeyword) return "null";
|
|
4088
|
+
if (import_ts_morph13.Node.isIdentifier(node) && node.getText() === "undefined") return "undefined";
|
|
4089
|
+
if (import_ts_morph13.Node.isObjectLiteralExpression(node)) {
|
|
4090
|
+
const props = node.getProperties();
|
|
4091
|
+
if (props.length === 0) return "Record<string, unknown>";
|
|
4092
|
+
const entries = [];
|
|
4093
|
+
for (const prop of props) {
|
|
4094
|
+
if (!import_ts_morph13.Node.isPropertyAssignment(prop)) continue;
|
|
4095
|
+
const key = prop.getName();
|
|
4096
|
+
const init = prop.getInitializer();
|
|
4097
|
+
if (!init) continue;
|
|
4098
|
+
entries.push(`${key}: ${inferExpressionType(init)}`);
|
|
3886
4099
|
}
|
|
4100
|
+
if (entries.length === 0) return "Record<string, unknown>";
|
|
4101
|
+
return `{ ${entries.join("; ")} }`;
|
|
3887
4102
|
}
|
|
3888
|
-
|
|
4103
|
+
if (import_ts_morph13.Node.isArrayLiteralExpression(node)) {
|
|
4104
|
+
const elements = node.getElements();
|
|
4105
|
+
if (elements.length === 0) return "Array<unknown>";
|
|
4106
|
+
const first = elements[0];
|
|
4107
|
+
if (first) return `Array<${inferExpressionType(first)}>`;
|
|
4108
|
+
return "Array<unknown>";
|
|
4109
|
+
}
|
|
4110
|
+
if (import_ts_morph13.Node.isConditionalExpression(node)) {
|
|
4111
|
+
const whenTrue = inferExpressionType(node.getWhenTrue());
|
|
4112
|
+
const whenFalse = inferExpressionType(node.getWhenFalse());
|
|
4113
|
+
if (whenTrue === whenFalse) return whenTrue;
|
|
4114
|
+
return `${whenTrue} | ${whenFalse}`;
|
|
4115
|
+
}
|
|
4116
|
+
if (import_ts_morph13.Node.isParenthesizedExpression(node)) {
|
|
4117
|
+
return inferExpressionType(node.getExpression());
|
|
4118
|
+
}
|
|
4119
|
+
if (import_ts_morph13.Node.isAsExpression(node)) {
|
|
4120
|
+
const typeNode = node.getTypeNode();
|
|
4121
|
+
if (typeNode) return typeNode.getText();
|
|
4122
|
+
}
|
|
4123
|
+
return "unknown";
|
|
3889
4124
|
}
|
|
3890
4125
|
|
|
4126
|
+
// src/emit/emit-api.ts
|
|
4127
|
+
var import_promises3 = require("fs/promises");
|
|
4128
|
+
var import_node_path7 = require("path");
|
|
4129
|
+
|
|
3891
4130
|
// src/extension/registry.ts
|
|
4131
|
+
var import_node_path6 = require("path");
|
|
4132
|
+
var import_ts_morph14 = require("ts-morph");
|
|
3892
4133
|
function resolveApiSlots(extensions) {
|
|
3893
4134
|
let layer;
|
|
3894
4135
|
let layerOwner;
|
|
@@ -3939,7 +4180,7 @@ function createExtensionContext(config, getRoutes, trackedInputs) {
|
|
|
3939
4180
|
},
|
|
3940
4181
|
project() {
|
|
3941
4182
|
if (!project) {
|
|
3942
|
-
project = new
|
|
4183
|
+
project = new import_ts_morph14.Project({
|
|
3943
4184
|
skipAddingFilesFromTsConfig: true,
|
|
3944
4185
|
skipLoadingLibFiles: true,
|
|
3945
4186
|
skipFileDependencyResolution: true,
|
|
@@ -5673,36 +5914,64 @@ async function discoverInputFiles(config) {
|
|
|
5673
5914
|
const matched = await (0, import_fast_glob3.default)(globs, { cwd, absolute: true, onlyFiles: true });
|
|
5674
5915
|
return [...new Set(matched)].sort();
|
|
5675
5916
|
}
|
|
5676
|
-
|
|
5917
|
+
var READ_CONCURRENCY = 64;
|
|
5918
|
+
async function readAll(paths, read) {
|
|
5919
|
+
const out = /* @__PURE__ */ new Map();
|
|
5920
|
+
for (let i = 0; i < paths.length; i += READ_CONCURRENCY) {
|
|
5921
|
+
const batch = paths.slice(i, i + READ_CONCURRENCY);
|
|
5922
|
+
const texts = await Promise.all(batch.map(read));
|
|
5923
|
+
batch.forEach((path, j) => out.set(path, texts[j]));
|
|
5924
|
+
}
|
|
5925
|
+
return out;
|
|
5926
|
+
}
|
|
5927
|
+
async function readInputs(config) {
|
|
5928
|
+
const cwd = config.codegen.cwd;
|
|
5929
|
+
const tsconfigPath = resolveTsconfigPath(cwd, config.app?.tsconfig ?? void 0);
|
|
5930
|
+
const extended = loadDiscoveryTsconfig(tsconfigPath).files;
|
|
5931
|
+
const tsconfigFiles = extended.length > 0 ? extended : [tsconfigPath];
|
|
5932
|
+
const files = await discoverInputFiles(config);
|
|
5933
|
+
const contents = /* @__PURE__ */ new Map();
|
|
5934
|
+
const tsconfigs = await readAll(
|
|
5935
|
+
tsconfigFiles,
|
|
5936
|
+
(file) => (0, import_promises11.readFile)(file, "utf8").catch(() => null)
|
|
5937
|
+
);
|
|
5938
|
+
for (const [file, text] of tsconfigs) contents.set(file, text);
|
|
5939
|
+
for (const [file, text] of await readAll(files, (file2) => (0, import_promises11.readFile)(file2, "utf8"))) {
|
|
5940
|
+
contents.set(file, text);
|
|
5941
|
+
}
|
|
5942
|
+
return { tsconfigFiles, files, contents, tracked: /* @__PURE__ */ new Map() };
|
|
5943
|
+
}
|
|
5944
|
+
async function readInput(snapshot, path) {
|
|
5945
|
+
return snapshot.contents.get(path) ?? (0, import_promises11.readFile)(path, "utf8");
|
|
5946
|
+
}
|
|
5947
|
+
async function computeInputsHash(config, extraInputs = [], snapshot) {
|
|
5948
|
+
const inputs = snapshot ?? await readInputs(config);
|
|
5677
5949
|
const hash = (0, import_node_crypto.createHash)("sha256");
|
|
5678
5950
|
hash.update(`version:${VERSION}
|
|
5679
5951
|
`);
|
|
5680
5952
|
hash.update(`config:${serializeConfig(config)}
|
|
5681
5953
|
`);
|
|
5682
5954
|
const cwd = config.codegen.cwd;
|
|
5683
|
-
const
|
|
5684
|
-
const tsconfigFiles = loadDiscoveryTsconfig(tsconfigPath).files;
|
|
5685
|
-
for (const file of tsconfigFiles.length > 0 ? tsconfigFiles : [tsconfigPath]) {
|
|
5686
|
-
const contents = await (0, import_promises11.readFile)(file, "utf8").catch(() => null);
|
|
5955
|
+
for (const file of inputs.tsconfigFiles) {
|
|
5687
5956
|
hash.update(`tsconfig:${(0, import_node_path15.relative)(cwd, file)}
|
|
5688
5957
|
`);
|
|
5689
|
-
hash.update(contents ?? " missing");
|
|
5958
|
+
hash.update(inputs.contents.get(file) ?? " missing");
|
|
5690
5959
|
hash.update("\n");
|
|
5691
5960
|
}
|
|
5692
|
-
const
|
|
5693
|
-
const globbedRelative = new Set(globbed.map((file) => (0, import_node_path15.relative)(cwd, file)));
|
|
5961
|
+
const globbedRelative = new Set(inputs.files.map((file) => (0, import_node_path15.relative)(cwd, file)));
|
|
5694
5962
|
const extra = [...new Set(extraInputs)].filter((file) => !globbedRelative.has(file)).sort();
|
|
5695
|
-
for (const file of
|
|
5696
|
-
const contents = await (0, import_promises11.readFile)(file, "utf8");
|
|
5963
|
+
for (const file of inputs.files) {
|
|
5697
5964
|
hash.update(`file:${(0, import_node_path15.relative)(cwd, file)}
|
|
5698
5965
|
`);
|
|
5699
|
-
hash.update(contents);
|
|
5966
|
+
hash.update(inputs.contents.get(file));
|
|
5700
5967
|
hash.update("\n");
|
|
5701
5968
|
}
|
|
5969
|
+
const unread = extra.map((file) => (0, import_node_path15.join)(cwd, file)).filter((abs) => !inputs.tracked.has(abs));
|
|
5970
|
+
for (const [abs, text] of await readAll(unread, readTrackedInput)) inputs.tracked.set(abs, text);
|
|
5702
5971
|
for (const file of extra) {
|
|
5703
5972
|
hash.update(`extra:${file}
|
|
5704
5973
|
`);
|
|
5705
|
-
hash.update(
|
|
5974
|
+
hash.update(inputs.tracked.get((0, import_node_path15.join)(cwd, file)));
|
|
5706
5975
|
hash.update("\n");
|
|
5707
5976
|
}
|
|
5708
5977
|
return hash.digest("hex");
|
|
@@ -5783,7 +6052,8 @@ function driftGuardMessage(outDir, previousEntryPoint, currentEntryPoint, differ
|
|
|
5783
6052
|
async function generate(config, inputRoutes = [], entryPoint = "cli") {
|
|
5784
6053
|
setCodegenDebug(config.debug);
|
|
5785
6054
|
const manifest = await readManifest(config.codegen.outDir);
|
|
5786
|
-
const
|
|
6055
|
+
const inputs = await readInputs(config);
|
|
6056
|
+
const inputsHash = await computeInputsHash(config, manifest?.extraInputs ?? [], inputs);
|
|
5787
6057
|
if (await isManifestFresh(config.codegen.outDir, manifest, inputsHash)) {
|
|
5788
6058
|
console.log(`[nestjs-codegen] ${config.codegen.outDir} up to date, skipped`);
|
|
5789
6059
|
return;
|
|
@@ -5811,7 +6081,7 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
|
|
|
5811
6081
|
}
|
|
5812
6082
|
if (config.pages) {
|
|
5813
6083
|
const pagesConfig = config.pages;
|
|
5814
|
-
const pages = await discoverPageScopes(config);
|
|
6084
|
+
const pages = await discoverPageScopes(config, (path) => readInput(inputs, path));
|
|
5815
6085
|
const sharedProps = discoverSharedPropsFromConfig(config);
|
|
5816
6086
|
await emitPages(pages, config.codegen.outDir, {
|
|
5817
6087
|
propsExport: pagesConfig.propsExport,
|
|
@@ -5861,7 +6131,7 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
|
|
|
5861
6131
|
}
|
|
5862
6132
|
const outputFiles = await listOutputFiles(config.codegen.outDir);
|
|
5863
6133
|
const extraInputs = [...trackedInputs].sort();
|
|
5864
|
-
const recordedHash = extraInputs.length > 0 ? await computeInputsHash(config, extraInputs) : inputsHash;
|
|
6134
|
+
const recordedHash = extraInputs.length > 0 ? await computeInputsHash(config, extraInputs, inputs) : inputsHash;
|
|
5865
6135
|
await writeManifest(config.codegen.outDir, {
|
|
5866
6136
|
version: VERSION,
|
|
5867
6137
|
hash: recordedHash,
|
|
@@ -6044,13 +6314,15 @@ async function watch(config, onChange, options = {}) {
|
|
|
6044
6314
|
pagesWatcher.on("unlink", schedulePagesRegenerate);
|
|
6045
6315
|
let contractsDebounceTimer;
|
|
6046
6316
|
const pendingChangedPaths = /* @__PURE__ */ new Set();
|
|
6317
|
+
let pendingAddedOrRemoved = false;
|
|
6047
6318
|
const contractsWatcher = import_chokidar.default.watch((0, import_node_path18.join)(config.codegen.cwd, config.contracts.glob), {
|
|
6048
6319
|
ignoreInitial: true,
|
|
6049
6320
|
persistent: true,
|
|
6050
6321
|
awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
|
|
6051
6322
|
});
|
|
6052
|
-
function scheduleContractsRegenerate(changedPath) {
|
|
6323
|
+
function scheduleContractsRegenerate(changedPath, addedOrRemoved = false) {
|
|
6053
6324
|
if (typeof changedPath === "string") pendingChangedPaths.add(changedPath);
|
|
6325
|
+
if (addedOrRemoved) pendingAddedOrRemoved = true;
|
|
6054
6326
|
if (contractsDebounceTimer !== void 0) {
|
|
6055
6327
|
clearTimeout(contractsDebounceTimer);
|
|
6056
6328
|
}
|
|
@@ -6058,8 +6330,12 @@ async function watch(config, onChange, options = {}) {
|
|
|
6058
6330
|
contractsDebounceTimer = void 0;
|
|
6059
6331
|
const changed = [...pendingChangedPaths];
|
|
6060
6332
|
pendingChangedPaths.clear();
|
|
6333
|
+
const filesAddedOrRemoved = pendingAddedOrRemoved;
|
|
6334
|
+
pendingAddedOrRemoved = false;
|
|
6061
6335
|
try {
|
|
6062
|
-
const routes = await (await getDiscovery()).rediscover(changed
|
|
6336
|
+
const routes = await (await getDiscovery()).rediscover(changed, {
|
|
6337
|
+
filesAddedOrRemoved
|
|
6338
|
+
});
|
|
6063
6339
|
lastRoutes = routes;
|
|
6064
6340
|
await generateAndTrack(routes);
|
|
6065
6341
|
} catch (err) {
|
|
@@ -6071,20 +6347,20 @@ async function watch(config, onChange, options = {}) {
|
|
|
6071
6347
|
onChange?.();
|
|
6072
6348
|
}, config.contracts.debounceMs);
|
|
6073
6349
|
}
|
|
6074
|
-
contractsWatcher.on("add", (p) => scheduleContractsRegenerate(p));
|
|
6350
|
+
contractsWatcher.on("add", (p) => scheduleContractsRegenerate(p, true));
|
|
6075
6351
|
contractsWatcher.on("change", (p) => scheduleContractsRegenerate(p));
|
|
6076
|
-
contractsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p));
|
|
6352
|
+
contractsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p, true));
|
|
6077
6353
|
const formsWatcher = import_chokidar.default.watch((0, import_node_path18.join)(config.codegen.cwd, config.forms.watch), {
|
|
6078
6354
|
ignoreInitial: true,
|
|
6079
6355
|
persistent: true,
|
|
6080
6356
|
awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
|
|
6081
6357
|
});
|
|
6082
|
-
formsWatcher.on("add", (p) => scheduleContractsRegenerate(p));
|
|
6358
|
+
formsWatcher.on("add", (p) => scheduleContractsRegenerate(p, true));
|
|
6083
6359
|
formsWatcher.on("change", (p) => scheduleContractsRegenerate(p));
|
|
6084
|
-
formsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p));
|
|
6085
|
-
trackedWatcher.on("add", (p) => scheduleContractsRegenerate(p));
|
|
6360
|
+
formsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p, true));
|
|
6361
|
+
trackedWatcher.on("add", (p) => scheduleContractsRegenerate(p, true));
|
|
6086
6362
|
trackedWatcher.on("change", (p) => scheduleContractsRegenerate(p));
|
|
6087
|
-
trackedWatcher.on("unlink", (p) => scheduleContractsRegenerate(p));
|
|
6363
|
+
trackedWatcher.on("unlink", (p) => scheduleContractsRegenerate(p, true));
|
|
6088
6364
|
return {
|
|
6089
6365
|
close: async () => {
|
|
6090
6366
|
if (pagesDebounceTimer !== void 0) {
|
|
@@ -6188,7 +6464,7 @@ function createChainModuleRenderer(opts) {
|
|
|
6188
6464
|
}
|
|
6189
6465
|
|
|
6190
6466
|
// src/extension/genui-sandbox-kit.ts
|
|
6191
|
-
var
|
|
6467
|
+
var import_node_fs4 = require("fs");
|
|
6192
6468
|
var import_node_path19 = require("path");
|
|
6193
6469
|
var import_fast_glob4 = __toESM(require("fast-glob"), 1);
|
|
6194
6470
|
var GENUI_KIT_MODULE = "@dudousxd/nestjs-agent-core/genui/kit";
|
|
@@ -6200,6 +6476,12 @@ var DEFAULT_KIT_DIRS = [
|
|
|
6200
6476
|
"app/components/ui",
|
|
6201
6477
|
"components/ui"
|
|
6202
6478
|
];
|
|
6479
|
+
var LEGACY_OPTIONS = {
|
|
6480
|
+
entry: "`entry: X` is now `components: X`",
|
|
6481
|
+
include: "`include: G` is now `components: G` (globs, files, directories or packages)",
|
|
6482
|
+
css: "`css: G` is now `styles: G` (the app's style entry files)"
|
|
6483
|
+
};
|
|
6484
|
+
var MIN_CORE = "0.53.0";
|
|
6203
6485
|
async function loadKitModule(specifier = GENUI_KIT_MODULE) {
|
|
6204
6486
|
let mod;
|
|
6205
6487
|
try {
|
|
@@ -6208,19 +6490,24 @@ async function loadKitModule(specifier = GENUI_KIT_MODULE) {
|
|
|
6208
6490
|
const code = err.code;
|
|
6209
6491
|
if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") {
|
|
6210
6492
|
throw new CodegenError(
|
|
6211
|
-
`genuiSandboxKit() needs "${GENUI_KIT_MODULE}" \u2014 install @dudousxd/nestjs-agent-core
|
|
6493
|
+
`genuiSandboxKit() needs "${GENUI_KIT_MODULE}" \u2014 install @dudousxd/nestjs-agent-core@>=${MIN_CORE}, or pass \`generate\` to genuiSandboxKit().`
|
|
6212
6494
|
);
|
|
6213
6495
|
}
|
|
6214
6496
|
if (code === "ERR_PACKAGE_PATH_NOT_EXPORTED") {
|
|
6215
6497
|
throw new CodegenError(
|
|
6216
|
-
`genuiSandboxKit() needs "${GENUI_KIT_MODULE}", which the installed @dudousxd/nestjs-agent-core does not export \u2014 upgrade it to
|
|
6498
|
+
`genuiSandboxKit() needs "${GENUI_KIT_MODULE}", which the installed @dudousxd/nestjs-agent-core does not export \u2014 upgrade it to >=${MIN_CORE}.`
|
|
6217
6499
|
);
|
|
6218
6500
|
}
|
|
6219
6501
|
throw err;
|
|
6220
6502
|
}
|
|
6221
6503
|
if (typeof mod.writeSandboxKitDocs !== "function") {
|
|
6222
6504
|
throw new CodegenError(
|
|
6223
|
-
`"${GENUI_KIT_MODULE}" has no writeSandboxKitDocs export \u2014 upgrade @dudousxd/nestjs-agent-core to
|
|
6505
|
+
`"${GENUI_KIT_MODULE}" has no writeSandboxKitDocs export \u2014 upgrade @dudousxd/nestjs-agent-core to >=${MIN_CORE}.`
|
|
6506
|
+
);
|
|
6507
|
+
}
|
|
6508
|
+
if (typeof mod.resolveStyleEntries !== "function") {
|
|
6509
|
+
throw new CodegenError(
|
|
6510
|
+
`The installed @dudousxd/nestjs-agent-core predates genuiSandboxKit({ components, styles, wrapper }) \u2014 upgrade it to >=${MIN_CORE}.`
|
|
6224
6511
|
);
|
|
6225
6512
|
}
|
|
6226
6513
|
return mod;
|
|
@@ -6234,52 +6521,92 @@ function globBase(glob) {
|
|
|
6234
6521
|
return base.join("/");
|
|
6235
6522
|
}
|
|
6236
6523
|
function resolveKitFilesLocally(root, options) {
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6524
|
+
const specs = options.components === void 0 ? DEFAULT_KIT_DIRS.filter((dir) => (0, import_node_fs4.existsSync)((0, import_node_path19.resolve)(root, dir))).slice(0, 1) : typeof options.components === "string" ? [options.components] : [...options.components];
|
|
6525
|
+
const files = [];
|
|
6526
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
6527
|
+
for (const spec of specs) {
|
|
6528
|
+
if (/[*?{]/.test(spec)) {
|
|
6529
|
+
files.push(...import_fast_glob4.default.sync(spec, { cwd: root, absolute: true, onlyFiles: true }));
|
|
6530
|
+
dirs.add((0, import_node_path19.resolve)(root, globBase(spec)));
|
|
6531
|
+
continue;
|
|
6532
|
+
}
|
|
6533
|
+
const path = (0, import_node_path19.isAbsolute)(spec) ? spec : (0, import_node_path19.resolve)(root, spec);
|
|
6534
|
+
if ((0, import_node_fs4.existsSync)(path) && (0, import_node_fs4.statSync)(path).isDirectory()) {
|
|
6535
|
+
files.push(...import_fast_glob4.default.sync("*.{tsx,jsx,ts,js}", { cwd: path, absolute: true, onlyFiles: true }));
|
|
6536
|
+
dirs.add(path);
|
|
6537
|
+
} else if ((0, import_node_fs4.existsSync)(path)) {
|
|
6538
|
+
files.push(path);
|
|
6539
|
+
dirs.add((0, import_node_path19.dirname)(path));
|
|
6540
|
+
}
|
|
6541
|
+
}
|
|
6542
|
+
if (options.wrapper !== void 0) {
|
|
6543
|
+
const wrapper = (0, import_node_path19.isAbsolute)(options.wrapper) ? options.wrapper : (0, import_node_path19.resolve)(root, options.wrapper);
|
|
6544
|
+
if ((0, import_node_fs4.existsSync)(wrapper)) files.push(wrapper);
|
|
6545
|
+
}
|
|
6546
|
+
if (files.length === 0 && dirs.size === 0) return null;
|
|
6547
|
+
return { files: [...new Set(files)].sort(), dirs: [...dirs] };
|
|
6248
6548
|
}
|
|
6249
6549
|
function genuiSandboxKit(options = {}) {
|
|
6550
|
+
const legacy = Object.keys(LEGACY_OPTIONS).filter(
|
|
6551
|
+
(key) => options[key] !== void 0
|
|
6552
|
+
);
|
|
6553
|
+
if (legacy.length > 0) {
|
|
6554
|
+
throw new CodegenError(
|
|
6555
|
+
`genuiSandboxKit(): ${legacy.map((key) => `\`${key}\``).join(", ")} ${legacy.length > 1 ? "were" : "was"} removed \u2014 ${legacy.map((key) => LEGACY_OPTIONS[key]).join("; ")}. The options are now the agent's Vite plugin's: components, styles, wrapper, stylesDocs.`
|
|
6556
|
+
);
|
|
6557
|
+
}
|
|
6250
6558
|
const output = options.output ?? DEFAULT_SANDBOX_KIT_OUTPUT;
|
|
6251
6559
|
const source = {
|
|
6252
|
-
...options.
|
|
6253
|
-
...options.
|
|
6560
|
+
...options.components !== void 0 ? { components: options.components } : {},
|
|
6561
|
+
...options.wrapper !== void 0 ? { wrapper: options.wrapper } : {}
|
|
6254
6562
|
};
|
|
6255
6563
|
const extension = {
|
|
6256
6564
|
name: "genui-sandbox-kit",
|
|
6257
6565
|
// Not read by the host: serialized into the config hash, so changing an option
|
|
6258
6566
|
// invalidates skip-when-unchanged.
|
|
6259
|
-
options: {
|
|
6567
|
+
options: {
|
|
6568
|
+
...source,
|
|
6569
|
+
styles: options.styles,
|
|
6570
|
+
stylesDocs: options.stylesDocs,
|
|
6571
|
+
tsconfig: options.tsconfig,
|
|
6572
|
+
output
|
|
6573
|
+
},
|
|
6260
6574
|
async emitFiles(ctx) {
|
|
6261
6575
|
const root = ctx.cwd;
|
|
6262
6576
|
let generate2 = options.generate;
|
|
6263
|
-
let
|
|
6577
|
+
let mod;
|
|
6264
6578
|
if (!generate2) {
|
|
6265
|
-
|
|
6579
|
+
mod = await loadKitModule(options.kitModule);
|
|
6266
6580
|
generate2 = mod.writeSandboxKitDocs;
|
|
6267
|
-
if (typeof mod.resolveSandboxKitFiles === "function") {
|
|
6268
|
-
resolveFiles = mod.resolveSandboxKitFiles;
|
|
6269
|
-
}
|
|
6270
6581
|
}
|
|
6271
|
-
const
|
|
6582
|
+
const resolveFiles = mod?.resolveSandboxKitFiles ?? resolveKitFilesLocally;
|
|
6583
|
+
let kit = null;
|
|
6584
|
+
try {
|
|
6585
|
+
kit = resolveFiles(root, source);
|
|
6586
|
+
} catch {
|
|
6587
|
+
kit = null;
|
|
6588
|
+
}
|
|
6272
6589
|
if (kit) ctx.trackInput(...kit.files, ...kit.dirs);
|
|
6273
|
-
if (options.
|
|
6274
|
-
const
|
|
6275
|
-
|
|
6590
|
+
if (options.styles !== void 0) {
|
|
6591
|
+
const entries = mod?.resolveStyleEntries ? mod.resolveStyleEntries(root, options.styles).files : import_fast_glob4.default.sync(typeof options.styles === "string" ? [options.styles] : [...options.styles], {
|
|
6592
|
+
cwd: root,
|
|
6593
|
+
absolute: true,
|
|
6594
|
+
onlyFiles: true
|
|
6595
|
+
});
|
|
6596
|
+
const files = mod?.collectStyleSources ? mod.collectStyleSources(entries).map((source2) => source2.file) : entries;
|
|
6597
|
+
ctx.trackInput(.../* @__PURE__ */ new Set([...entries, ...files]));
|
|
6598
|
+
}
|
|
6599
|
+
if (options.stylesDocs !== void 0 && !options.stylesDocs.includes("\n")) {
|
|
6600
|
+
const guide = (0, import_node_path19.isAbsolute)(options.stylesDocs) ? options.stylesDocs : (0, import_node_path19.resolve)(root, options.stylesDocs);
|
|
6601
|
+
if ((0, import_node_fs4.existsSync)(guide)) ctx.trackInput(guide);
|
|
6276
6602
|
}
|
|
6277
6603
|
ctx.trackInput((0, import_node_path19.isAbsolute)(output) ? output : (0, import_node_path19.resolve)(root, output));
|
|
6278
6604
|
await generate2({
|
|
6279
6605
|
root,
|
|
6280
6606
|
output,
|
|
6281
6607
|
...source,
|
|
6282
|
-
...options.
|
|
6608
|
+
...options.styles !== void 0 ? { styles: options.styles } : {},
|
|
6609
|
+
...options.stylesDocs !== void 0 ? { stylesDocs: options.stylesDocs } : {},
|
|
6283
6610
|
...options.tsconfig !== void 0 ? { tsconfig: options.tsconfig } : {}
|
|
6284
6611
|
});
|
|
6285
6612
|
return [];
|
|
@@ -6289,7 +6616,7 @@ function genuiSandboxKit(options = {}) {
|
|
|
6289
6616
|
}
|
|
6290
6617
|
|
|
6291
6618
|
// src/index.ts
|
|
6292
|
-
var VERSION = "0.
|
|
6619
|
+
var VERSION = "0.29.1";
|
|
6293
6620
|
// Annotate the CommonJS export names for ESM import in node:
|
|
6294
6621
|
0 && (module.exports = {
|
|
6295
6622
|
CodegenError,
|