@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/cli/main.js
CHANGED
|
@@ -247,7 +247,7 @@ async function discoverPages(opts) {
|
|
|
247
247
|
const rel = relative2(opts.cwd, file);
|
|
248
248
|
const nameRel = relative2(pagesBase, file).split(sep2).join("/");
|
|
249
249
|
const name = `${prefix}${computeName(nameRel, opts.componentNameStrategy)}`;
|
|
250
|
-
const source = await readFile(file, "utf8");
|
|
250
|
+
const source = await (opts.read ? opts.read(file) : readFile(file, "utf8"));
|
|
251
251
|
const propsSource = extractPropsSource(source, opts.propsExport);
|
|
252
252
|
out.push({ name, absolutePath: file, relativePath: rel, propsSource, scope });
|
|
253
253
|
}
|
|
@@ -255,10 +255,14 @@ async function discoverPages(opts) {
|
|
|
255
255
|
assertUniqueNames(out);
|
|
256
256
|
return out;
|
|
257
257
|
}
|
|
258
|
-
async function discoverPageScopes(config) {
|
|
258
|
+
async function discoverPageScopes(config, read) {
|
|
259
259
|
const pages = config.pages;
|
|
260
260
|
if (!pages) return [];
|
|
261
|
-
const common = {
|
|
261
|
+
const common = {
|
|
262
|
+
cwd: config.codegen.cwd,
|
|
263
|
+
propsExport: pages.propsExport,
|
|
264
|
+
...read ? { read } : {}
|
|
265
|
+
};
|
|
262
266
|
const out = await discoverPages({
|
|
263
267
|
...common,
|
|
264
268
|
glob: pages.glob,
|
|
@@ -329,305 +333,69 @@ function extractPropsSource(source, exportName) {
|
|
|
329
333
|
}
|
|
330
334
|
|
|
331
335
|
// src/discovery/shared-props.ts
|
|
332
|
-
import {
|
|
333
|
-
import {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
336
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
337
|
+
import { join as join4 } from "path";
|
|
338
|
+
import { Node as Node11, Project as Project4, SyntaxKind as SyntaxKind4 } from "ts-morph";
|
|
339
|
+
|
|
340
|
+
// src/discovery/contracts-fast.ts
|
|
341
|
+
import { existsSync } from "fs";
|
|
342
|
+
import { dirname as dirname2, join as join3, resolve as resolve3 } from "path";
|
|
343
|
+
import fg2 from "fast-glob";
|
|
344
|
+
import {
|
|
345
|
+
FileSystemRefreshResult as FileSystemRefreshResult3,
|
|
346
|
+
Node as Node10,
|
|
347
|
+
Project as Project3,
|
|
348
|
+
ts as ts4
|
|
349
|
+
} from "ts-morph";
|
|
350
|
+
|
|
351
|
+
// src/discovery/disk-fingerprints.ts
|
|
352
|
+
import { readFileSync, statSync } from "fs";
|
|
353
|
+
var DiskFingerprints = class {
|
|
354
|
+
stats = /* @__PURE__ */ new Map();
|
|
355
|
+
/** The paths whose disk content differs from `text`, or that no longer exist. */
|
|
356
|
+
changed(files) {
|
|
357
|
+
const changed = [];
|
|
358
|
+
for (const { path, text } of files) {
|
|
359
|
+
let key;
|
|
338
360
|
try {
|
|
339
|
-
|
|
361
|
+
const stat2 = statSync(path);
|
|
362
|
+
key = `${stat2.mtimeMs}:${stat2.size}`;
|
|
340
363
|
} catch {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
const forRootCall = findForRootCall(sourceFile);
|
|
345
|
-
if (!forRootCall) return null;
|
|
346
|
-
const initializer = findShareInitializer(forRootCall);
|
|
347
|
-
if (!initializer) return null;
|
|
348
|
-
return extractShareType(initializer, sourceFile, project);
|
|
349
|
-
} catch {
|
|
350
|
-
return null;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
function discoverSharedPropsFromConfig(config) {
|
|
354
|
-
if (!config.app?.moduleEntry) return null;
|
|
355
|
-
try {
|
|
356
|
-
const tsconfigPath = config.app.tsconfig ?? join3(config.codegen.cwd, "tsconfig.json");
|
|
357
|
-
let project;
|
|
358
|
-
try {
|
|
359
|
-
project = new Project({
|
|
360
|
-
tsConfigFilePath: tsconfigPath,
|
|
361
|
-
skipAddingFilesFromTsConfig: true,
|
|
362
|
-
skipLoadingLibFiles: true,
|
|
363
|
-
skipFileDependencyResolution: true
|
|
364
|
-
});
|
|
365
|
-
} catch {
|
|
366
|
-
project = new Project({
|
|
367
|
-
skipAddingFilesFromTsConfig: true,
|
|
368
|
-
skipLoadingLibFiles: true,
|
|
369
|
-
skipFileDependencyResolution: true,
|
|
370
|
-
compilerOptions: { allowJs: true, strict: false }
|
|
371
|
-
});
|
|
372
|
-
}
|
|
373
|
-
return discoverSharedProps(project, config.app.moduleEntry);
|
|
374
|
-
} catch {
|
|
375
|
-
return null;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
function findForRootCall(sourceFile) {
|
|
379
|
-
const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
|
|
380
|
-
for (const call of callExpressions) {
|
|
381
|
-
const expr = call.getExpression();
|
|
382
|
-
if (!Node.isPropertyAccessExpression(expr)) continue;
|
|
383
|
-
const methodName = expr.getName();
|
|
384
|
-
const objectExpr = expr.getExpression();
|
|
385
|
-
if (methodName === "forRoot" && Node.isIdentifier(objectExpr)) {
|
|
386
|
-
const name = objectExpr.getText();
|
|
387
|
-
if (name === "InertiaModule") {
|
|
388
|
-
return call;
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
return null;
|
|
393
|
-
}
|
|
394
|
-
function findShareInitializer(forRootCall) {
|
|
395
|
-
if (!Node.isCallExpression(forRootCall)) return null;
|
|
396
|
-
const args = forRootCall.getArguments();
|
|
397
|
-
const firstArg2 = args[0];
|
|
398
|
-
if (!firstArg2 || !Node.isObjectLiteralExpression(firstArg2)) return null;
|
|
399
|
-
for (const prop of firstArg2.getProperties()) {
|
|
400
|
-
if (Node.isPropertyAssignment(prop) && prop.getName() === "share") {
|
|
401
|
-
return prop.getInitializer() ?? null;
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
return null;
|
|
405
|
-
}
|
|
406
|
-
function extractShareType(node, sourceFile, project) {
|
|
407
|
-
if (Node.isIdentifier(node)) {
|
|
408
|
-
const ref = resolveIdentifierToImportRef(node, sourceFile, project);
|
|
409
|
-
if (ref) {
|
|
410
|
-
return {
|
|
411
|
-
typeString: `Awaited<ReturnType<typeof import('${ref.importPath}').${ref.exportName}>>`,
|
|
412
|
-
properties: null,
|
|
413
|
-
isImportRef: true
|
|
414
|
-
};
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
if (Node.isArrowFunction(node)) {
|
|
418
|
-
const result = extractFromFunctionLike(node, sourceFile);
|
|
419
|
-
return result ? { ...result, isImportRef: false } : null;
|
|
420
|
-
}
|
|
421
|
-
if (Node.isFunctionExpression(node)) {
|
|
422
|
-
const result = extractFromFunctionLike(node, sourceFile);
|
|
423
|
-
return result ? { ...result, isImportRef: false } : null;
|
|
424
|
-
}
|
|
425
|
-
if (Node.isObjectLiteralExpression(node)) {
|
|
426
|
-
const result = extractFromObjectLiteral(node);
|
|
427
|
-
return result ? { ...result, isImportRef: false } : null;
|
|
428
|
-
}
|
|
429
|
-
return null;
|
|
430
|
-
}
|
|
431
|
-
function resolveIdentifierToImportRef(id, sourceFile, project) {
|
|
432
|
-
if (!Node.isIdentifier(id)) return null;
|
|
433
|
-
const name = id.getText();
|
|
434
|
-
const localFunc = sourceFile.getFunction(name);
|
|
435
|
-
if (localFunc?.isExported()) {
|
|
436
|
-
const filePath = sourceFile.getFilePath().replace(/\.ts$/, "");
|
|
437
|
-
return { importPath: filePath, exportName: name };
|
|
438
|
-
}
|
|
439
|
-
const localVar = sourceFile.getVariableDeclaration(name);
|
|
440
|
-
if (localVar?.isExported()) {
|
|
441
|
-
const filePath = sourceFile.getFilePath().replace(/\.ts$/, "");
|
|
442
|
-
return { importPath: filePath, exportName: name };
|
|
443
|
-
}
|
|
444
|
-
for (const imp of sourceFile.getImportDeclarations()) {
|
|
445
|
-
for (const named of imp.getNamedImports()) {
|
|
446
|
-
const importedName = named.getAliasNode()?.getText() ?? named.getName();
|
|
447
|
-
if (importedName !== name) continue;
|
|
448
|
-
const resolvedSource = imp.getModuleSpecifierSourceFile();
|
|
449
|
-
if (!resolvedSource) continue;
|
|
450
|
-
const originalName = named.getName();
|
|
451
|
-
const fn = resolvedSource.getFunction(originalName);
|
|
452
|
-
if (fn?.isExported()) {
|
|
453
|
-
const filePath = resolvedSource.getFilePath().replace(/\.ts$/, "");
|
|
454
|
-
return { importPath: filePath, exportName: originalName };
|
|
455
|
-
}
|
|
456
|
-
const v = resolvedSource.getVariableDeclaration(originalName);
|
|
457
|
-
if (v?.isExported()) {
|
|
458
|
-
const filePath = resolvedSource.getFilePath().replace(/\.ts$/, "");
|
|
459
|
-
return { importPath: filePath, exportName: originalName };
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
return null;
|
|
464
|
-
}
|
|
465
|
-
function extractFromFunctionLike(node, _sourceFile) {
|
|
466
|
-
const returnTypeNode = Node.isArrowFunction(node) || Node.isFunctionExpression(node) ? node.getReturnTypeNode() : null;
|
|
467
|
-
if (returnTypeNode) {
|
|
468
|
-
return extractFromReturnTypeAnnotation(returnTypeNode);
|
|
469
|
-
}
|
|
470
|
-
if (Node.isArrowFunction(node)) {
|
|
471
|
-
const body = node.getBody();
|
|
472
|
-
if (Node.isParenthesizedExpression(body)) {
|
|
473
|
-
const inner = body.getExpression();
|
|
474
|
-
if (Node.isObjectLiteralExpression(inner)) {
|
|
475
|
-
return extractFromObjectLiteral(inner);
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
if (Node.isObjectLiteralExpression(body)) {
|
|
479
|
-
return extractFromObjectLiteral(body);
|
|
480
|
-
}
|
|
481
|
-
if (Node.isBlock(body)) {
|
|
482
|
-
return extractFromBlockReturn(body);
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
if (Node.isFunctionExpression(node)) {
|
|
486
|
-
const body = node.getBody();
|
|
487
|
-
if (Node.isBlock(body)) {
|
|
488
|
-
return extractFromBlockReturn(body);
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
return null;
|
|
492
|
-
}
|
|
493
|
-
function extractFromReturnTypeAnnotation(typeNode) {
|
|
494
|
-
if (Node.isTypeReference(typeNode)) {
|
|
495
|
-
const typeName = typeNode.getTypeName();
|
|
496
|
-
if (Node.isIdentifier(typeName) && typeName.getText() === "Promise") {
|
|
497
|
-
const typeArgs = typeNode.getTypeArguments();
|
|
498
|
-
const firstArg2 = typeArgs[0];
|
|
499
|
-
if (firstArg2) {
|
|
500
|
-
return extractFromReturnTypeAnnotation(firstArg2);
|
|
364
|
+
this.stats.delete(path);
|
|
365
|
+
changed.push(path);
|
|
366
|
+
continue;
|
|
501
367
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
if (Node.isPropertySignature(member)) {
|
|
509
|
-
const name = member.getName();
|
|
510
|
-
const memberTypeNode = member.getTypeNode();
|
|
511
|
-
const type = memberTypeNode ? memberTypeNode.getText() : "unknown";
|
|
512
|
-
properties.push({ name, type });
|
|
368
|
+
if (this.stats.get(path) === key) continue;
|
|
369
|
+
let disk;
|
|
370
|
+
try {
|
|
371
|
+
disk = readFileSync(path, "utf-8");
|
|
372
|
+
} catch {
|
|
373
|
+
disk = void 0;
|
|
513
374
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
return null;
|
|
520
|
-
}
|
|
521
|
-
function extractFromBlockReturn(block) {
|
|
522
|
-
if (!Node.isBlock(block)) return null;
|
|
523
|
-
const statements = block.getStatements();
|
|
524
|
-
for (let i = statements.length - 1; i >= 0; i--) {
|
|
525
|
-
const stmt = statements[i];
|
|
526
|
-
if (!Node.isReturnStatement(stmt)) continue;
|
|
527
|
-
const expr = stmt.getExpression();
|
|
528
|
-
if (!expr) continue;
|
|
529
|
-
if (Node.isObjectLiteralExpression(expr)) {
|
|
530
|
-
return extractFromObjectLiteral(expr);
|
|
531
|
-
}
|
|
532
|
-
if (Node.isParenthesizedExpression(expr)) {
|
|
533
|
-
const inner = expr.getExpression();
|
|
534
|
-
if (Node.isObjectLiteralExpression(inner)) {
|
|
535
|
-
return extractFromObjectLiteral(inner);
|
|
375
|
+
if (disk === text) {
|
|
376
|
+
this.stats.set(path, key);
|
|
377
|
+
} else {
|
|
378
|
+
this.stats.delete(path);
|
|
379
|
+
changed.push(path);
|
|
536
380
|
}
|
|
537
381
|
}
|
|
538
|
-
|
|
382
|
+
return changed;
|
|
539
383
|
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
if (!Node.isObjectLiteralExpression(objLiteral)) return null;
|
|
544
|
-
const properties = [];
|
|
545
|
-
for (const prop of objLiteral.getProperties()) {
|
|
546
|
-
if (!Node.isPropertyAssignment(prop)) continue;
|
|
547
|
-
const name = prop.getName();
|
|
548
|
-
const initializer = prop.getInitializer();
|
|
549
|
-
if (!initializer) continue;
|
|
550
|
-
const type = inferExpressionType(initializer);
|
|
551
|
-
properties.push({ name, type });
|
|
552
|
-
}
|
|
553
|
-
if (properties.length === 0) return null;
|
|
554
|
-
const typeString = `{ ${properties.map((p) => `${p.name}: ${p.type}`).join("; ")} }`;
|
|
555
|
-
return { typeString, properties, isImportRef: false };
|
|
556
|
-
}
|
|
557
|
-
function inferExpressionType(node) {
|
|
558
|
-
if (Node.isStringLiteral(node)) return "string";
|
|
559
|
-
if (Node.isTemplateExpression(node) || Node.isNoSubstitutionTemplateLiteral(node))
|
|
560
|
-
return "string";
|
|
561
|
-
if (Node.isNumericLiteral(node)) return "number";
|
|
562
|
-
if (node.getKind() === SyntaxKind.TrueKeyword || node.getKind() === SyntaxKind.FalseKeyword) {
|
|
563
|
-
return "boolean";
|
|
564
|
-
}
|
|
565
|
-
if (node.getKind() === SyntaxKind.NullKeyword) return "null";
|
|
566
|
-
if (Node.isIdentifier(node) && node.getText() === "undefined") return "undefined";
|
|
567
|
-
if (Node.isObjectLiteralExpression(node)) {
|
|
568
|
-
const props = node.getProperties();
|
|
569
|
-
if (props.length === 0) return "Record<string, unknown>";
|
|
570
|
-
const entries = [];
|
|
571
|
-
for (const prop of props) {
|
|
572
|
-
if (!Node.isPropertyAssignment(prop)) continue;
|
|
573
|
-
const key = prop.getName();
|
|
574
|
-
const init = prop.getInitializer();
|
|
575
|
-
if (!init) continue;
|
|
576
|
-
entries.push(`${key}: ${inferExpressionType(init)}`);
|
|
577
|
-
}
|
|
578
|
-
if (entries.length === 0) return "Record<string, unknown>";
|
|
579
|
-
return `{ ${entries.join("; ")} }`;
|
|
580
|
-
}
|
|
581
|
-
if (Node.isArrayLiteralExpression(node)) {
|
|
582
|
-
const elements = node.getElements();
|
|
583
|
-
if (elements.length === 0) return "Array<unknown>";
|
|
584
|
-
const first = elements[0];
|
|
585
|
-
if (first) return `Array<${inferExpressionType(first)}>`;
|
|
586
|
-
return "Array<unknown>";
|
|
587
|
-
}
|
|
588
|
-
if (Node.isConditionalExpression(node)) {
|
|
589
|
-
const whenTrue = inferExpressionType(node.getWhenTrue());
|
|
590
|
-
const whenFalse = inferExpressionType(node.getWhenFalse());
|
|
591
|
-
if (whenTrue === whenFalse) return whenTrue;
|
|
592
|
-
return `${whenTrue} | ${whenFalse}`;
|
|
593
|
-
}
|
|
594
|
-
if (Node.isParenthesizedExpression(node)) {
|
|
595
|
-
return inferExpressionType(node.getExpression());
|
|
596
|
-
}
|
|
597
|
-
if (Node.isAsExpression(node)) {
|
|
598
|
-
const typeNode = node.getTypeNode();
|
|
599
|
-
if (typeNode) return typeNode.getText();
|
|
384
|
+
/** Forget `path`, so its next check compares content again. */
|
|
385
|
+
forget(path) {
|
|
386
|
+
this.stats.delete(path);
|
|
600
387
|
}
|
|
601
|
-
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
// src/emit/emit-api.ts
|
|
605
|
-
import { mkdir, writeFile } from "fs/promises";
|
|
606
|
-
import { isAbsolute as isAbsolute2, join as join5, relative as relative4 } from "path";
|
|
607
|
-
|
|
608
|
-
// src/extension/registry.ts
|
|
609
|
-
import { relative as relative3, resolve as resolve4 } from "path";
|
|
610
|
-
import { Project as Project5 } from "ts-morph";
|
|
611
|
-
|
|
612
|
-
// src/discovery/contracts-fast.ts
|
|
613
|
-
import { existsSync } from "fs";
|
|
614
|
-
import { dirname as dirname2, join as join4, resolve as resolve3 } from "path";
|
|
615
|
-
import fg2 from "fast-glob";
|
|
616
|
-
import {
|
|
617
|
-
Node as Node11,
|
|
618
|
-
Project as Project4,
|
|
619
|
-
ts as ts3
|
|
620
|
-
} from "ts-morph";
|
|
388
|
+
};
|
|
621
389
|
|
|
622
390
|
// src/discovery/dto-type-resolver.ts
|
|
623
391
|
import {
|
|
624
|
-
Node as
|
|
625
|
-
SyntaxKind as
|
|
392
|
+
Node as Node9,
|
|
393
|
+
SyntaxKind as SyntaxKind3
|
|
626
394
|
} from "ts-morph";
|
|
627
395
|
|
|
628
396
|
// src/discovery/dto-to-ir.ts
|
|
629
397
|
import {
|
|
630
|
-
Node as
|
|
398
|
+
Node as Node2
|
|
631
399
|
} from "ts-morph";
|
|
632
400
|
|
|
633
401
|
// src/util/debug-log.ts
|
|
@@ -642,7 +410,7 @@ function debugWarn(message) {
|
|
|
642
410
|
// src/discovery/type-ref-resolution.ts
|
|
643
411
|
import { dirname, resolve as resolve2 } from "path";
|
|
644
412
|
import {
|
|
645
|
-
Node
|
|
413
|
+
Node
|
|
646
414
|
} from "ts-morph";
|
|
647
415
|
var _EMPTY_CTX = { projectRoot: "", tsconfigPaths: null };
|
|
648
416
|
var _ctxByProject = /* @__PURE__ */ new WeakMap();
|
|
@@ -908,13 +676,13 @@ function resolveTypeRef(nodeOrName, sourceFile, project, opts) {
|
|
|
908
676
|
name = nodeOrName;
|
|
909
677
|
} else {
|
|
910
678
|
const typeNode = nodeOrName;
|
|
911
|
-
if (opts.unwrapContainers &&
|
|
679
|
+
if (opts.unwrapContainers && Node.isArrayTypeNode(typeNode)) {
|
|
912
680
|
const inner = resolveTypeRef(typeNode.getElementTypeNode(), sourceFile, project, opts);
|
|
913
681
|
return inner ? { ...inner, isArray: true } : null;
|
|
914
682
|
}
|
|
915
|
-
if (!
|
|
683
|
+
if (!Node.isTypeReference(typeNode)) return null;
|
|
916
684
|
const typeName = typeNode.getTypeName();
|
|
917
|
-
const refName =
|
|
685
|
+
const refName = Node.isIdentifier(typeName) ? typeName.getText() : null;
|
|
918
686
|
if (!refName) return null;
|
|
919
687
|
if (opts.unwrapContainers && refName === "Promise") {
|
|
920
688
|
const first = typeNode.getTypeArguments()[0];
|
|
@@ -1048,7 +816,7 @@ function buildProperty(prop, classFile, ctx) {
|
|
|
1048
816
|
const dec = (n) => decorators.get(n);
|
|
1049
817
|
const typeNode = prop.getTypeNode();
|
|
1050
818
|
const typeText = typeNode?.getText() ?? "unknown";
|
|
1051
|
-
const isArrayType = !!typeNode &&
|
|
819
|
+
const isArrayType = !!typeNode && Node2.isArrayTypeNode(typeNode);
|
|
1052
820
|
const asField = (child) => applyPresence(
|
|
1053
821
|
has("IsArray") || isArrayType ? { kind: "array", element: child } : child,
|
|
1054
822
|
decorators
|
|
@@ -1262,22 +1030,22 @@ function firstArgText(decorator) {
|
|
|
1262
1030
|
}
|
|
1263
1031
|
function numericArg(decorator) {
|
|
1264
1032
|
const arg = firstArg(decorator);
|
|
1265
|
-
if (arg &&
|
|
1033
|
+
if (arg && Node2.isNumericLiteral(arg)) return arg.getText();
|
|
1266
1034
|
return null;
|
|
1267
1035
|
}
|
|
1268
1036
|
function numericArgs(decorator) {
|
|
1269
1037
|
const args = decorator?.getArguments() ?? [];
|
|
1270
|
-
const num = (n) => n &&
|
|
1038
|
+
const num = (n) => n && Node2.isNumericLiteral(n) ? n.getText() : null;
|
|
1271
1039
|
return [num(args[0]), num(args[1])];
|
|
1272
1040
|
}
|
|
1273
1041
|
function messageRaw(decorator) {
|
|
1274
1042
|
const args = decorator?.getArguments() ?? [];
|
|
1275
1043
|
for (const arg of args) {
|
|
1276
|
-
if (
|
|
1044
|
+
if (Node2.isObjectLiteralExpression(arg)) {
|
|
1277
1045
|
for (const prop of arg.getProperties()) {
|
|
1278
|
-
if (
|
|
1046
|
+
if (Node2.isPropertyAssignment(prop) && prop.getName() === "message") {
|
|
1279
1047
|
const init = prop.getInitializer();
|
|
1280
|
-
if (init &&
|
|
1048
|
+
if (init && Node2.isStringLiteral(init)) return init.getText();
|
|
1281
1049
|
}
|
|
1282
1050
|
}
|
|
1283
1051
|
}
|
|
@@ -1286,30 +1054,30 @@ function messageRaw(decorator) {
|
|
|
1286
1054
|
}
|
|
1287
1055
|
function resolveDiscriminator(decorator) {
|
|
1288
1056
|
const optsArg = decorator?.getArguments()[1];
|
|
1289
|
-
if (!optsArg || !
|
|
1057
|
+
if (!optsArg || !Node2.isObjectLiteralExpression(optsArg)) return null;
|
|
1290
1058
|
let discProp;
|
|
1291
1059
|
for (const prop of optsArg.getProperties()) {
|
|
1292
|
-
if (
|
|
1060
|
+
if (Node2.isPropertyAssignment(prop) && prop.getName() === "discriminator") {
|
|
1293
1061
|
discProp = prop.getInitializer();
|
|
1294
1062
|
}
|
|
1295
1063
|
}
|
|
1296
|
-
if (!discProp || !
|
|
1064
|
+
if (!discProp || !Node2.isObjectLiteralExpression(discProp)) return null;
|
|
1297
1065
|
let property = null;
|
|
1298
1066
|
const subTypes = [];
|
|
1299
1067
|
for (const prop of discProp.getProperties()) {
|
|
1300
|
-
if (!
|
|
1068
|
+
if (!Node2.isPropertyAssignment(prop)) continue;
|
|
1301
1069
|
const name = prop.getName();
|
|
1302
1070
|
const init = prop.getInitializer();
|
|
1303
1071
|
if (!init) continue;
|
|
1304
|
-
if (name === "property" &&
|
|
1072
|
+
if (name === "property" && Node2.isStringLiteral(init)) {
|
|
1305
1073
|
property = init.getLiteralValue();
|
|
1306
|
-
} else if (name === "subTypes" &&
|
|
1074
|
+
} else if (name === "subTypes" && Node2.isArrayLiteralExpression(init)) {
|
|
1307
1075
|
for (const el of init.getElements()) {
|
|
1308
|
-
if (!
|
|
1076
|
+
if (!Node2.isObjectLiteralExpression(el)) continue;
|
|
1309
1077
|
for (const p of el.getProperties()) {
|
|
1310
|
-
if (!
|
|
1078
|
+
if (!Node2.isPropertyAssignment(p) || p.getName() !== "name") continue;
|
|
1311
1079
|
const nameInit = p.getInitializer();
|
|
1312
|
-
if (nameInit &&
|
|
1080
|
+
if (nameInit && Node2.isIdentifier(nameInit)) subTypes.push(nameInit.getText());
|
|
1313
1081
|
}
|
|
1314
1082
|
}
|
|
1315
1083
|
}
|
|
@@ -1320,9 +1088,9 @@ function resolveDiscriminator(decorator) {
|
|
|
1320
1088
|
function resolveTypeFactoryName(decorator) {
|
|
1321
1089
|
const arg = firstArg(decorator);
|
|
1322
1090
|
if (!arg) return null;
|
|
1323
|
-
if (
|
|
1091
|
+
if (Node2.isArrowFunction(arg)) {
|
|
1324
1092
|
const body = arg.getBody();
|
|
1325
|
-
if (
|
|
1093
|
+
if (Node2.isIdentifier(body)) return body.getText();
|
|
1326
1094
|
}
|
|
1327
1095
|
return null;
|
|
1328
1096
|
}
|
|
@@ -1331,12 +1099,12 @@ function singularClassName(typeText) {
|
|
|
1331
1099
|
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(inner) ? inner : null;
|
|
1332
1100
|
}
|
|
1333
1101
|
function genericTypeArgNames(typeNode) {
|
|
1334
|
-
if (!typeNode || !
|
|
1102
|
+
if (!typeNode || !Node2.isTypeReference(typeNode)) return [];
|
|
1335
1103
|
const names = [];
|
|
1336
1104
|
for (const arg of typeNode.getTypeArguments()) {
|
|
1337
|
-
if (!
|
|
1105
|
+
if (!Node2.isTypeReference(arg)) return [];
|
|
1338
1106
|
const tn = arg.getTypeName();
|
|
1339
|
-
if (!
|
|
1107
|
+
if (!Node2.isIdentifier(tn)) return [];
|
|
1340
1108
|
names.push(tn.getText());
|
|
1341
1109
|
}
|
|
1342
1110
|
return names;
|
|
@@ -1344,7 +1112,7 @@ function genericTypeArgNames(typeNode) {
|
|
|
1344
1112
|
function enumSchemaFromDecorator(decorator, classFile, ctx) {
|
|
1345
1113
|
const arg = firstArg(decorator);
|
|
1346
1114
|
if (!arg) return null;
|
|
1347
|
-
if (
|
|
1115
|
+
if (Node2.isIdentifier(arg)) {
|
|
1348
1116
|
const name = arg.getText();
|
|
1349
1117
|
const resolved = findType(name, classFile, ctx.project);
|
|
1350
1118
|
if (resolved && resolved.kind === "enum") {
|
|
@@ -1358,12 +1126,12 @@ function enumSchemaFromDecorator(decorator, classFile, ctx) {
|
|
|
1358
1126
|
}
|
|
1359
1127
|
return { kind: "unknown", note: `@IsEnum(${name}): enum not resolvable to literals` };
|
|
1360
1128
|
}
|
|
1361
|
-
if (
|
|
1129
|
+
if (Node2.isObjectLiteralExpression(arg)) {
|
|
1362
1130
|
const values = [];
|
|
1363
1131
|
for (const p of arg.getProperties()) {
|
|
1364
|
-
if (!
|
|
1132
|
+
if (!Node2.isPropertyAssignment(p)) continue;
|
|
1365
1133
|
const init = p.getInitializer();
|
|
1366
|
-
if (init &&
|
|
1134
|
+
if (init && Node2.isStringLiteral(init)) values.push(init.getText());
|
|
1367
1135
|
}
|
|
1368
1136
|
if (values.length > 0) return { kind: "enum", literals: values };
|
|
1369
1137
|
}
|
|
@@ -1371,9 +1139,9 @@ function enumSchemaFromDecorator(decorator, classFile, ctx) {
|
|
|
1371
1139
|
}
|
|
1372
1140
|
function inSchemaFromDecorator(decorator) {
|
|
1373
1141
|
const arg = firstArg(decorator);
|
|
1374
|
-
if (arg &&
|
|
1142
|
+
if (arg && Node2.isArrayLiteralExpression(arg)) {
|
|
1375
1143
|
const elements = arg.getElements();
|
|
1376
|
-
const allStrings = elements.every((e) =>
|
|
1144
|
+
const allStrings = elements.every((e) => Node2.isStringLiteral(e));
|
|
1377
1145
|
if (allStrings && elements.length > 0) {
|
|
1378
1146
|
return { kind: "enum", literals: elements.map((e) => e.getText()) };
|
|
1379
1147
|
}
|
|
@@ -1389,13 +1157,13 @@ function inSchemaFromDecorator(decorator) {
|
|
|
1389
1157
|
|
|
1390
1158
|
// src/discovery/filter-for.ts
|
|
1391
1159
|
import {
|
|
1392
|
-
Node as
|
|
1160
|
+
Node as Node5
|
|
1393
1161
|
} from "ts-morph";
|
|
1394
1162
|
|
|
1395
1163
|
// src/discovery/filter-field-types.ts
|
|
1396
1164
|
import {
|
|
1397
|
-
Node as
|
|
1398
|
-
SyntaxKind
|
|
1165
|
+
Node as Node3,
|
|
1166
|
+
SyntaxKind
|
|
1399
1167
|
} from "ts-morph";
|
|
1400
1168
|
|
|
1401
1169
|
// src/discovery/enum-resolution.ts
|
|
@@ -1449,28 +1217,28 @@ function markNullable(r, nullable) {
|
|
|
1449
1217
|
}
|
|
1450
1218
|
var TRANSPARENT_TYPE_BRANDS = /* @__PURE__ */ new Set(["Opt", "Hidden"]);
|
|
1451
1219
|
function classifyTypeNode(typeNode, sourceFile, project, opts) {
|
|
1452
|
-
if (
|
|
1220
|
+
if (Node3.isUnionTypeNode(typeNode)) {
|
|
1453
1221
|
let nullable = false;
|
|
1454
1222
|
const stringLits = [];
|
|
1455
1223
|
const numberLits = [];
|
|
1456
1224
|
const others = [];
|
|
1457
1225
|
for (const member of typeNode.getTypeNodes()) {
|
|
1458
1226
|
const kind = member.getKind();
|
|
1459
|
-
if (kind ===
|
|
1227
|
+
if (kind === SyntaxKind.NullKeyword || kind === SyntaxKind.UndefinedKeyword) {
|
|
1460
1228
|
nullable = true;
|
|
1461
1229
|
continue;
|
|
1462
1230
|
}
|
|
1463
|
-
if (
|
|
1231
|
+
if (Node3.isLiteralTypeNode(member)) {
|
|
1464
1232
|
const lit = member.getLiteral();
|
|
1465
|
-
if (
|
|
1233
|
+
if (Node3.isStringLiteral(lit)) {
|
|
1466
1234
|
stringLits.push(lit.getLiteralValue());
|
|
1467
1235
|
continue;
|
|
1468
1236
|
}
|
|
1469
|
-
if (
|
|
1237
|
+
if (Node3.isNumericLiteral(lit)) {
|
|
1470
1238
|
numberLits.push(lit.getText());
|
|
1471
1239
|
continue;
|
|
1472
1240
|
}
|
|
1473
|
-
if (lit.getKind() ===
|
|
1241
|
+
if (lit.getKind() === SyntaxKind.NullKeyword) {
|
|
1474
1242
|
nullable = true;
|
|
1475
1243
|
continue;
|
|
1476
1244
|
}
|
|
@@ -1490,19 +1258,19 @@ function classifyTypeNode(typeNode, sourceFile, project, opts) {
|
|
|
1490
1258
|
return markNullable({ kind: "unknown" }, nullable);
|
|
1491
1259
|
}
|
|
1492
1260
|
switch (typeNode.getKind()) {
|
|
1493
|
-
case
|
|
1261
|
+
case SyntaxKind.StringKeyword:
|
|
1494
1262
|
return { kind: "string" };
|
|
1495
|
-
case
|
|
1263
|
+
case SyntaxKind.NumberKeyword:
|
|
1496
1264
|
return { kind: "number" };
|
|
1497
|
-
case
|
|
1265
|
+
case SyntaxKind.BooleanKeyword:
|
|
1498
1266
|
return { kind: "boolean" };
|
|
1499
|
-
case
|
|
1500
|
-
case
|
|
1267
|
+
case SyntaxKind.AnyKeyword:
|
|
1268
|
+
case SyntaxKind.UnknownKeyword:
|
|
1501
1269
|
return { kind: "unknown" };
|
|
1502
1270
|
default:
|
|
1503
1271
|
break;
|
|
1504
1272
|
}
|
|
1505
|
-
if (
|
|
1273
|
+
if (Node3.isTypeReference(typeNode)) {
|
|
1506
1274
|
const refName = typeNode.getTypeName().getText();
|
|
1507
1275
|
if (refName === "Date") return { kind: "date" };
|
|
1508
1276
|
if (refName === "Record" || refName === "Object") return { kind: "json" };
|
|
@@ -1520,25 +1288,25 @@ function classifyTypeNode(typeNode, sourceFile, project, opts) {
|
|
|
1520
1288
|
if (typeRef) return { kind: "unknown", typeRef };
|
|
1521
1289
|
return { kind: "unknown" };
|
|
1522
1290
|
}
|
|
1523
|
-
if (
|
|
1291
|
+
if (Node3.isTypeLiteral(typeNode)) return { kind: "json" };
|
|
1524
1292
|
return { kind: "unknown" };
|
|
1525
1293
|
}
|
|
1526
1294
|
function enumFromDecoratorArgs(args, sourceFile, project) {
|
|
1527
1295
|
for (const arg of args) {
|
|
1528
|
-
if (
|
|
1296
|
+
if (Node3.isArrowFunction(arg)) {
|
|
1529
1297
|
const body = arg.getBody();
|
|
1530
|
-
if (
|
|
1298
|
+
if (Node3.isIdentifier(body)) {
|
|
1531
1299
|
const en = resolveEnumValues(body.getText(), sourceFile, project);
|
|
1532
1300
|
if (en) return en;
|
|
1533
1301
|
}
|
|
1534
1302
|
}
|
|
1535
|
-
if (
|
|
1303
|
+
if (Node3.isObjectLiteralExpression(arg)) {
|
|
1536
1304
|
const itemsProp = arg.getProperty("items");
|
|
1537
|
-
if (itemsProp &&
|
|
1305
|
+
if (itemsProp && Node3.isPropertyAssignment(itemsProp)) {
|
|
1538
1306
|
const init = itemsProp.getInitializer();
|
|
1539
|
-
if (init &&
|
|
1307
|
+
if (init && Node3.isArrowFunction(init)) {
|
|
1540
1308
|
const body = init.getBody();
|
|
1541
|
-
if (
|
|
1309
|
+
if (Node3.isIdentifier(body)) {
|
|
1542
1310
|
const en = resolveEnumValues(body.getText(), sourceFile, project);
|
|
1543
1311
|
if (en) return en;
|
|
1544
1312
|
}
|
|
@@ -1561,7 +1329,7 @@ function classifyFromColumnDecorator(prop, sourceFile, project) {
|
|
|
1561
1329
|
return { kind: "string" };
|
|
1562
1330
|
}
|
|
1563
1331
|
for (const arg of args) {
|
|
1564
|
-
if (
|
|
1332
|
+
if (Node3.isStringLiteral(arg)) {
|
|
1565
1333
|
const raw = arg.getLiteralValue();
|
|
1566
1334
|
const kind = classifyTypeKeyword(raw);
|
|
1567
1335
|
if (kind) {
|
|
@@ -1569,11 +1337,11 @@ function classifyFromColumnDecorator(prop, sourceFile, project) {
|
|
|
1569
1337
|
return { kind };
|
|
1570
1338
|
}
|
|
1571
1339
|
}
|
|
1572
|
-
if (
|
|
1340
|
+
if (Node3.isObjectLiteralExpression(arg)) {
|
|
1573
1341
|
const enumProp = arg.getProperty("enum");
|
|
1574
|
-
if (enumProp &&
|
|
1342
|
+
if (enumProp && Node3.isPropertyAssignment(enumProp)) {
|
|
1575
1343
|
const init = enumProp.getInitializer();
|
|
1576
|
-
if (init &&
|
|
1344
|
+
if (init && Node3.isIdentifier(init)) {
|
|
1577
1345
|
const en = resolveEnumValues(init.getText(), sourceFile, project);
|
|
1578
1346
|
if (en) {
|
|
1579
1347
|
return en.numeric ? { kind: "number", enumValues: en.values, numericEnum: true } : { kind: "string", enumValues: en.values };
|
|
@@ -1583,15 +1351,15 @@ function classifyFromColumnDecorator(prop, sourceFile, project) {
|
|
|
1583
1351
|
}
|
|
1584
1352
|
for (const key of ["columnType", "type"]) {
|
|
1585
1353
|
const typeProp = arg.getProperty(key);
|
|
1586
|
-
if (!typeProp || !
|
|
1354
|
+
if (!typeProp || !Node3.isPropertyAssignment(typeProp)) continue;
|
|
1587
1355
|
const init = typeProp.getInitializer();
|
|
1588
1356
|
if (!init) continue;
|
|
1589
|
-
if (
|
|
1357
|
+
if (Node3.isStringLiteral(init)) {
|
|
1590
1358
|
const kind = classifyTypeKeyword(init.getLiteralValue());
|
|
1591
1359
|
if (kind) return { kind };
|
|
1592
1360
|
continue;
|
|
1593
1361
|
}
|
|
1594
|
-
if (
|
|
1362
|
+
if (Node3.isIdentifier(init)) {
|
|
1595
1363
|
const kind = classifyTypeKeyword(init.getText());
|
|
1596
1364
|
if (kind) return { kind };
|
|
1597
1365
|
}
|
|
@@ -1633,23 +1401,70 @@ function toFilterFieldType(name, r) {
|
|
|
1633
1401
|
}
|
|
1634
1402
|
|
|
1635
1403
|
// src/discovery/heritage.ts
|
|
1636
|
-
import {
|
|
1404
|
+
import {
|
|
1405
|
+
FileSystemRefreshResult,
|
|
1406
|
+
Node as Node4,
|
|
1407
|
+
Project
|
|
1408
|
+
} from "ts-morph";
|
|
1409
|
+
|
|
1410
|
+
// src/discovery/unresolved-imports.ts
|
|
1411
|
+
import { ts } from "ts-morph";
|
|
1412
|
+
var UnresolvedImports = class {
|
|
1413
|
+
read;
|
|
1414
|
+
/**
|
|
1415
|
+
* The files with an import that failed to resolve in `program` and now resolves
|
|
1416
|
+
* to a TypeScript file the program does not hold.
|
|
1417
|
+
*/
|
|
1418
|
+
nowResolving(program) {
|
|
1419
|
+
if (this.read?.program !== program) {
|
|
1420
|
+
this.read = { program, imports: failedImports(program) };
|
|
1421
|
+
}
|
|
1422
|
+
const options = program.getCompilerOptions();
|
|
1423
|
+
const files = /* @__PURE__ */ new Set();
|
|
1424
|
+
for (const { specifier, from } of this.read.imports) {
|
|
1425
|
+
const file = ts.resolveModuleName(specifier, from, options, ts.sys).resolvedModule?.resolvedFileName;
|
|
1426
|
+
if (file !== void 0 && /\.(m|c)?tsx?$/.test(file) && !program.getSourceFile(file)) {
|
|
1427
|
+
files.add(from);
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
return [...files];
|
|
1431
|
+
}
|
|
1432
|
+
};
|
|
1433
|
+
function failedImports(program) {
|
|
1434
|
+
const checker = program.getTypeChecker();
|
|
1435
|
+
const imports = [];
|
|
1436
|
+
for (const file of program.getSourceFiles()) {
|
|
1437
|
+
if (program.isSourceFileDefaultLibrary(file) || file.isDeclarationFile) continue;
|
|
1438
|
+
if (program.isSourceFileFromExternalLibrary(file)) continue;
|
|
1439
|
+
for (const statement of file.statements) {
|
|
1440
|
+
if (!ts.isImportDeclaration(statement) && !ts.isExportDeclaration(statement)) continue;
|
|
1441
|
+
const specifier = statement.moduleSpecifier;
|
|
1442
|
+
if (!specifier || !ts.isStringLiteral(specifier)) continue;
|
|
1443
|
+
if (!checker.getSymbolAtLocation(specifier)) {
|
|
1444
|
+
imports.push({ specifier: specifier.text, from: file.fileName });
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
return imports;
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
// src/discovery/heritage.ts
|
|
1637
1452
|
function resolveHeritageCall(node) {
|
|
1638
1453
|
if (!node) return void 0;
|
|
1639
1454
|
const expr = unwrapExpression(node);
|
|
1640
|
-
if (
|
|
1641
|
-
if (!
|
|
1455
|
+
if (Node4.isCallExpression(expr)) return expr;
|
|
1456
|
+
if (!Node4.isIdentifier(expr)) return void 0;
|
|
1642
1457
|
const decl = expr.getDefinitions().map((d) => d.getDeclarationNode()).find(
|
|
1643
|
-
(n) => n !== void 0 &&
|
|
1458
|
+
(n) => n !== void 0 && Node4.isVariableDeclaration(n)
|
|
1644
1459
|
);
|
|
1645
1460
|
const init = decl?.getInitializer();
|
|
1646
1461
|
if (!init) return void 0;
|
|
1647
1462
|
const unwrapped = unwrapExpression(init);
|
|
1648
|
-
return
|
|
1463
|
+
return Node4.isCallExpression(unwrapped) ? unwrapped : void 0;
|
|
1649
1464
|
}
|
|
1650
1465
|
function unwrapExpression(node) {
|
|
1651
1466
|
let current = node;
|
|
1652
|
-
while (
|
|
1467
|
+
while (Node4.isAsExpression(current) || Node4.isSatisfiesExpression(current) || Node4.isParenthesizedExpression(current) || Node4.isTypeAssertion(current)) {
|
|
1653
1468
|
current = current.getExpression();
|
|
1654
1469
|
}
|
|
1655
1470
|
return current;
|
|
@@ -1657,17 +1472,17 @@ function unwrapExpression(node) {
|
|
|
1657
1472
|
function resolveReturnedClass(factoryDecl) {
|
|
1658
1473
|
const body = factoryDecl.getBody();
|
|
1659
1474
|
if (!body) return void 0;
|
|
1660
|
-
const returns = body.getDescendants().filter((n) =>
|
|
1475
|
+
const returns = body.getDescendants().filter((n) => Node4.isReturnStatement(n));
|
|
1661
1476
|
for (const ret of returns) {
|
|
1662
1477
|
const expr = ret.getExpression();
|
|
1663
1478
|
if (!expr) continue;
|
|
1664
1479
|
const inner = unwrapExpression(expr);
|
|
1665
|
-
if (
|
|
1480
|
+
if (Node4.isClassExpression(inner)) {
|
|
1666
1481
|
return inner;
|
|
1667
1482
|
}
|
|
1668
|
-
if (
|
|
1483
|
+
if (Node4.isIdentifier(inner)) {
|
|
1669
1484
|
const name = inner.getText();
|
|
1670
|
-
const decl = body.getDescendants().find((n) =>
|
|
1485
|
+
const decl = body.getDescendants().find((n) => Node4.isClassDeclaration(n) && n.getName() === name);
|
|
1671
1486
|
if (decl) return decl;
|
|
1672
1487
|
}
|
|
1673
1488
|
}
|
|
@@ -1675,10 +1490,10 @@ function resolveReturnedClass(factoryDecl) {
|
|
|
1675
1490
|
}
|
|
1676
1491
|
function factoryCalleeName(call) {
|
|
1677
1492
|
const callee = unwrapExpression(call.getExpression());
|
|
1678
|
-
if (
|
|
1679
|
-
if (
|
|
1493
|
+
if (Node4.isIdentifier(callee)) return callee;
|
|
1494
|
+
if (Node4.isPropertyAccessExpression(callee)) {
|
|
1680
1495
|
const name = callee.getNameNode();
|
|
1681
|
-
return
|
|
1496
|
+
return Node4.isIdentifier(name) ? name : void 0;
|
|
1682
1497
|
}
|
|
1683
1498
|
return void 0;
|
|
1684
1499
|
}
|
|
@@ -1691,19 +1506,19 @@ function resolveFactoryFromName(name, seen) {
|
|
|
1691
1506
|
seen.add(name);
|
|
1692
1507
|
const decls = name.getDefinitions().map((d) => d.getDeclarationNode()).filter((n) => n !== void 0);
|
|
1693
1508
|
for (const decl of decls) {
|
|
1694
|
-
if (
|
|
1695
|
-
if (
|
|
1509
|
+
if (Node4.isFunctionDeclaration(decl) || Node4.isMethodDeclaration(decl)) return decl;
|
|
1510
|
+
if (Node4.isPropertyAssignment(decl)) {
|
|
1696
1511
|
const init = decl.getInitializer();
|
|
1697
1512
|
const inner = init ? unwrapExpression(init) : void 0;
|
|
1698
|
-
if (inner &&
|
|
1513
|
+
if (inner && Node4.isIdentifier(inner)) {
|
|
1699
1514
|
const resolved = resolveFactoryFromName(inner, seen);
|
|
1700
1515
|
if (resolved) return resolved;
|
|
1701
1516
|
}
|
|
1702
1517
|
continue;
|
|
1703
1518
|
}
|
|
1704
|
-
if (
|
|
1519
|
+
if (Node4.isShorthandPropertyAssignment(decl)) {
|
|
1705
1520
|
const nameNode = decl.getNameNode();
|
|
1706
|
-
const resolved =
|
|
1521
|
+
const resolved = Node4.isIdentifier(nameNode) ? resolveFactoryFromName(nameNode, seen) : void 0;
|
|
1707
1522
|
if (resolved) return resolved;
|
|
1708
1523
|
}
|
|
1709
1524
|
}
|
|
@@ -1715,7 +1530,7 @@ function warnUnresolvedFactory(cls, calleeText, reason) {
|
|
|
1715
1530
|
);
|
|
1716
1531
|
}
|
|
1717
1532
|
function resolveFactoryStaticClass(node) {
|
|
1718
|
-
if (!
|
|
1533
|
+
if (!Node4.isPropertyAccessExpression(node)) return void 0;
|
|
1719
1534
|
const call = resolveHeritageCall(node.getExpression());
|
|
1720
1535
|
if (!call) return void 0;
|
|
1721
1536
|
const factoryDecl = resolveFactoryDeclaration(call);
|
|
@@ -1723,13 +1538,13 @@ function resolveFactoryStaticClass(node) {
|
|
|
1723
1538
|
const returnedClass = resolveReturnedClass(factoryDecl);
|
|
1724
1539
|
if (!returnedClass) return void 0;
|
|
1725
1540
|
const staticProp = returnedClass.getStaticProperties().find((p) => p.getName() === node.getName());
|
|
1726
|
-
if (!staticProp || !
|
|
1541
|
+
if (!staticProp || !Node4.isPropertyDeclaration(staticProp)) return void 0;
|
|
1727
1542
|
const init = staticProp.getInitializer();
|
|
1728
1543
|
if (!init) return void 0;
|
|
1729
1544
|
const inner = unwrapExpression(init);
|
|
1730
|
-
if (
|
|
1731
|
-
if (!
|
|
1732
|
-
return inner.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 &&
|
|
1545
|
+
if (Node4.isClassExpression(inner)) return inner;
|
|
1546
|
+
if (!Node4.isIdentifier(inner)) return void 0;
|
|
1547
|
+
return inner.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 && Node4.isClassDeclaration(n));
|
|
1733
1548
|
}
|
|
1734
1549
|
function resolveBaseClass(cls) {
|
|
1735
1550
|
const heritage = cls.getExtends()?.getExpression();
|
|
@@ -1740,8 +1555,8 @@ function resolveBaseClass(cls) {
|
|
|
1740
1555
|
return factoryDecl ? resolveReturnedClass(factoryDecl) : void 0;
|
|
1741
1556
|
}
|
|
1742
1557
|
const expr = unwrapExpression(heritage);
|
|
1743
|
-
if (!
|
|
1744
|
-
return expr.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 &&
|
|
1558
|
+
if (!Node4.isIdentifier(expr)) return void 0;
|
|
1559
|
+
return expr.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 && Node4.isClassDeclaration(n));
|
|
1745
1560
|
}
|
|
1746
1561
|
function collectMethodsThroughChain(returnedClass) {
|
|
1747
1562
|
const byName = /* @__PURE__ */ new Map();
|
|
@@ -1788,7 +1603,7 @@ function resolveInheritedMethods(cls) {
|
|
|
1788
1603
|
classArgs.push(positional);
|
|
1789
1604
|
continue;
|
|
1790
1605
|
}
|
|
1791
|
-
if (!
|
|
1606
|
+
if (!Node4.isObjectLiteralExpression(arg)) continue;
|
|
1792
1607
|
for (const prop of arg.getProperties()) {
|
|
1793
1608
|
const named = resolvePropertyClassReference(prop);
|
|
1794
1609
|
if (!named) continue;
|
|
@@ -1810,8 +1625,8 @@ function resolveInheritedMethods(cls) {
|
|
|
1810
1625
|
}
|
|
1811
1626
|
function resolveClassReference(node) {
|
|
1812
1627
|
const expr = unwrapExpression(node);
|
|
1813
|
-
if (!
|
|
1814
|
-
const decl = expr.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 &&
|
|
1628
|
+
if (!Node4.isIdentifier(expr)) return void 0;
|
|
1629
|
+
const decl = expr.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 && Node4.isClassDeclaration(n));
|
|
1815
1630
|
if (!decl) return void 0;
|
|
1816
1631
|
return {
|
|
1817
1632
|
name: decl.getName() ?? expr.getText(),
|
|
@@ -1819,29 +1634,56 @@ function resolveClassReference(node) {
|
|
|
1819
1634
|
};
|
|
1820
1635
|
}
|
|
1821
1636
|
function resolvePropertyClassReference(prop) {
|
|
1822
|
-
if (
|
|
1637
|
+
if (Node4.isShorthandPropertyAssignment(prop)) {
|
|
1823
1638
|
const ref2 = resolveClassReference(prop.getNameNode());
|
|
1824
1639
|
return ref2 ? { key: prop.getName(), ref: ref2 } : void 0;
|
|
1825
1640
|
}
|
|
1826
|
-
if (!
|
|
1641
|
+
if (!Node4.isPropertyAssignment(prop)) return void 0;
|
|
1827
1642
|
const init = prop.getInitializer();
|
|
1828
1643
|
if (!init) return void 0;
|
|
1829
1644
|
const ref = resolveClassReference(init);
|
|
1830
1645
|
if (!ref) return void 0;
|
|
1831
1646
|
const nameNode = prop.getNameNode();
|
|
1832
|
-
const key =
|
|
1647
|
+
const key = Node4.isStringLiteral(nameNode) ? nameNode.getLiteralValue() : prop.getName();
|
|
1833
1648
|
return { key, ref };
|
|
1834
1649
|
}
|
|
1835
1650
|
var mixinTypeProject;
|
|
1651
|
+
var mixinFiles = new DiskFingerprints();
|
|
1652
|
+
var mixinStale = false;
|
|
1653
|
+
var mixinUnresolved = new UnresolvedImports();
|
|
1836
1654
|
function getMixinTypeProject() {
|
|
1837
|
-
mixinTypeProject
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1655
|
+
if (mixinTypeProject && mixinStale && !refreshMixinTypeProject(mixinTypeProject)) {
|
|
1656
|
+
mixinTypeProject = void 0;
|
|
1657
|
+
}
|
|
1658
|
+
mixinStale = false;
|
|
1659
|
+
if (!mixinTypeProject) {
|
|
1660
|
+
mixinTypeProject = new Project({
|
|
1661
|
+
skipAddingFilesFromTsConfig: true,
|
|
1662
|
+
compilerOptions: { strict: true }
|
|
1663
|
+
});
|
|
1664
|
+
mixinFiles = new DiskFingerprints();
|
|
1665
|
+
mixinUnresolved = new UnresolvedImports();
|
|
1666
|
+
}
|
|
1841
1667
|
return mixinTypeProject;
|
|
1842
1668
|
}
|
|
1843
1669
|
function clearMixinTypeProject() {
|
|
1844
1670
|
mixinTypeProject = void 0;
|
|
1671
|
+
mixinStale = false;
|
|
1672
|
+
}
|
|
1673
|
+
function invalidateMixinTypeProject() {
|
|
1674
|
+
mixinStale = true;
|
|
1675
|
+
}
|
|
1676
|
+
function refreshMixinTypeProject(project) {
|
|
1677
|
+
const program = project.getProgram().compilerObject;
|
|
1678
|
+
const files = program.getSourceFiles().filter((file) => !program.isSourceFileDefaultLibrary(file)).map((file) => ({ path: file.fileName, text: file.text }));
|
|
1679
|
+
const changed = mixinFiles.changed(files);
|
|
1680
|
+
if (changed.length === 0) return mixinUnresolved.nowResolving(program).length === 0;
|
|
1681
|
+
for (const path of changed) {
|
|
1682
|
+
const sf = project.getSourceFile(path);
|
|
1683
|
+
if (sf?.refreshFromFileSystemSync() !== FileSystemRefreshResult.Updated) return false;
|
|
1684
|
+
}
|
|
1685
|
+
project.resolveSourceFileDependencies();
|
|
1686
|
+
return true;
|
|
1845
1687
|
}
|
|
1846
1688
|
function resolveInstantiatedReturnType(cls, methodName) {
|
|
1847
1689
|
const filePath = cls.getSourceFile().getFilePath();
|
|
@@ -1872,7 +1714,7 @@ function resolveClassRef(ref, project) {
|
|
|
1872
1714
|
return file?.getClass(ref.name);
|
|
1873
1715
|
}
|
|
1874
1716
|
function classifyFilterForHint(typeInit) {
|
|
1875
|
-
if (
|
|
1717
|
+
if (Node5.isStringLiteral(typeInit)) {
|
|
1876
1718
|
switch (typeInit.getLiteralValue()) {
|
|
1877
1719
|
case "string":
|
|
1878
1720
|
return { kind: "string" };
|
|
@@ -1886,10 +1728,10 @@ function classifyFilterForHint(typeInit) {
|
|
|
1886
1728
|
return null;
|
|
1887
1729
|
}
|
|
1888
1730
|
}
|
|
1889
|
-
if (
|
|
1731
|
+
if (Node5.isArrayLiteralExpression(typeInit)) {
|
|
1890
1732
|
const values = [];
|
|
1891
1733
|
for (const el of typeInit.getElements()) {
|
|
1892
|
-
if (!
|
|
1734
|
+
if (!Node5.isStringLiteral(el)) return null;
|
|
1893
1735
|
values.push(el.getLiteralValue());
|
|
1894
1736
|
}
|
|
1895
1737
|
if (values.length === 0) return null;
|
|
@@ -1924,11 +1766,11 @@ function extractFilterForHints(classDecl, project) {
|
|
|
1924
1766
|
if (!filterForDec) continue;
|
|
1925
1767
|
const args = filterForDec.getArguments();
|
|
1926
1768
|
const keyArg = args[0];
|
|
1927
|
-
const inputKey = keyArg &&
|
|
1769
|
+
const inputKey = keyArg && Node5.isStringLiteral(keyArg) ? keyArg.getLiteralValue() : method.getName();
|
|
1928
1770
|
const optsArg = args[1];
|
|
1929
|
-
if (optsArg &&
|
|
1771
|
+
if (optsArg && Node5.isObjectLiteralExpression(optsArg)) {
|
|
1930
1772
|
const typeProp = optsArg.getProperty("type");
|
|
1931
|
-
if (typeProp &&
|
|
1773
|
+
if (typeProp && Node5.isPropertyAssignment(typeProp)) {
|
|
1932
1774
|
const typeInit = typeProp.getInitializer();
|
|
1933
1775
|
if (typeInit) {
|
|
1934
1776
|
const classified = classifyFilterForHint(typeInit);
|
|
@@ -1955,14 +1797,14 @@ function extractApplyFilterInfo(method, sourceFile, project, mixin, controllerCl
|
|
|
1955
1797
|
const filterClassArg = args[0];
|
|
1956
1798
|
if (!filterClassArg) continue;
|
|
1957
1799
|
const factoryStaticClass = resolveFactoryStaticClass(filterClassArg);
|
|
1958
|
-
if (!factoryStaticClass && !
|
|
1800
|
+
if (!factoryStaticClass && !Node5.isIdentifier(filterClassArg)) continue;
|
|
1959
1801
|
let source = "query";
|
|
1960
1802
|
const optionsArg = args[1];
|
|
1961
|
-
if (optionsArg &&
|
|
1803
|
+
if (optionsArg && Node5.isObjectLiteralExpression(optionsArg)) {
|
|
1962
1804
|
const sourceProp = optionsArg.getProperty("source");
|
|
1963
|
-
if (sourceProp &&
|
|
1805
|
+
if (sourceProp && Node5.isPropertyAssignment(sourceProp)) {
|
|
1964
1806
|
const init = sourceProp.getInitializer();
|
|
1965
|
-
if (init &&
|
|
1807
|
+
if (init && Node5.isStringLiteral(init) && init.getLiteralValue() === "body") {
|
|
1966
1808
|
source = "body";
|
|
1967
1809
|
}
|
|
1968
1810
|
}
|
|
@@ -1971,7 +1813,7 @@ function extractApplyFilterInfo(method, sourceFile, project, mixin, controllerCl
|
|
|
1971
1813
|
const resolved = factoryStaticClass ? void 0 : findType(filterClassName, declFile, project);
|
|
1972
1814
|
const declaredClass = factoryStaticClass ?? (resolved?.kind === "class" ? resolved.decl : resolveLocalClassDeclaration(filterClassArg));
|
|
1973
1815
|
const ownRoute = !mixin || (controllerClass ? method.getParent() === controllerClass : declFile.getFilePath() !== mixin.factoryFilePath);
|
|
1974
|
-
const namedByRoute = ownRoute &&
|
|
1816
|
+
const namedByRoute = ownRoute && Node5.isIdentifier(filterClassArg) ? declaredClass : void 0;
|
|
1975
1817
|
for (const candidate of orderFilterCandidates({
|
|
1976
1818
|
namedByRoute,
|
|
1977
1819
|
supplied: resolveMixinFilterClass(mixin, project),
|
|
@@ -2049,22 +1891,22 @@ function resolveRelationEntity(prop, sourceFile, project) {
|
|
|
2049
1891
|
const args = dec.getArguments();
|
|
2050
1892
|
if (args.length === 0) continue;
|
|
2051
1893
|
const arg = args[0];
|
|
2052
|
-
if (
|
|
1894
|
+
if (Node5.isObjectLiteralExpression(arg)) {
|
|
2053
1895
|
const entityProp = arg.getProperty("entity");
|
|
2054
|
-
if (entityProp &&
|
|
1896
|
+
if (entityProp && Node5.isPropertyAssignment(entityProp)) {
|
|
2055
1897
|
const init = entityProp.getInitializer();
|
|
2056
|
-
if (init &&
|
|
1898
|
+
if (init && Node5.isArrowFunction(init)) {
|
|
2057
1899
|
const body = init.getBody();
|
|
2058
|
-
if (
|
|
1900
|
+
if (Node5.isIdentifier(body)) {
|
|
2059
1901
|
const resolved = findType(body.getText(), prop.getSourceFile(), project);
|
|
2060
1902
|
if (resolved?.kind === "class") return resolved.decl;
|
|
2061
1903
|
}
|
|
2062
1904
|
}
|
|
2063
1905
|
}
|
|
2064
1906
|
}
|
|
2065
|
-
if (
|
|
1907
|
+
if (Node5.isArrowFunction(arg)) {
|
|
2066
1908
|
const body = arg.getBody();
|
|
2067
|
-
if (
|
|
1909
|
+
if (Node5.isIdentifier(body)) {
|
|
2068
1910
|
const resolved = findType(body.getText(), prop.getSourceFile(), project);
|
|
2069
1911
|
if (resolved?.kind === "class") return resolved.decl;
|
|
2070
1912
|
}
|
|
@@ -2083,36 +1925,36 @@ function extractClassPropertyTypes(classDecl, project) {
|
|
|
2083
1925
|
return fields;
|
|
2084
1926
|
}
|
|
2085
1927
|
function resolveLocalClassDeclaration(identifier) {
|
|
2086
|
-
if (!
|
|
2087
|
-
return identifier.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 &&
|
|
1928
|
+
if (!Node5.isIdentifier(identifier)) return void 0;
|
|
1929
|
+
return identifier.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 && Node5.isClassDeclaration(n));
|
|
2088
1930
|
}
|
|
2089
1931
|
function resolveDeclaredEntity(optionsArg, filterClass, project) {
|
|
2090
|
-
if (!
|
|
1932
|
+
if (!Node5.isObjectLiteralExpression(optionsArg)) return void 0;
|
|
2091
1933
|
const entityProp = optionsArg.getProperty("entity");
|
|
2092
|
-
if (!entityProp || !
|
|
1934
|
+
if (!entityProp || !Node5.isPropertyAssignment(entityProp)) return void 0;
|
|
2093
1935
|
const entityInit = entityProp.getInitializer();
|
|
2094
|
-
if (!entityInit || !
|
|
1936
|
+
if (!entityInit || !Node5.isIdentifier(entityInit)) return void 0;
|
|
2095
1937
|
const resolved = findType(entityInit.getText(), filterClass.getSourceFile(), project);
|
|
2096
1938
|
if (!resolved || resolved.kind !== "class") return void 0;
|
|
2097
1939
|
return resolved.decl;
|
|
2098
1940
|
}
|
|
2099
1941
|
function readFieldNameList(optionsArg, key) {
|
|
2100
|
-
if (!
|
|
1942
|
+
if (!Node5.isObjectLiteralExpression(optionsArg)) return void 0;
|
|
2101
1943
|
const prop = optionsArg.getProperty(key);
|
|
2102
|
-
if (!prop || !
|
|
1944
|
+
if (!prop || !Node5.isPropertyAssignment(prop)) return void 0;
|
|
2103
1945
|
const init = prop.getInitializer();
|
|
2104
|
-
if (!init || !
|
|
1946
|
+
if (!init || !Node5.isArrayLiteralExpression(init)) return void 0;
|
|
2105
1947
|
const names = /* @__PURE__ */ new Set();
|
|
2106
1948
|
for (const el of init.getElements()) {
|
|
2107
|
-
if (
|
|
1949
|
+
if (Node5.isStringLiteral(el)) {
|
|
2108
1950
|
names.add(el.getLiteralValue());
|
|
2109
1951
|
continue;
|
|
2110
1952
|
}
|
|
2111
|
-
if (
|
|
1953
|
+
if (Node5.isObjectLiteralExpression(el)) {
|
|
2112
1954
|
const fieldProp = el.getProperty("field");
|
|
2113
|
-
if (fieldProp &&
|
|
1955
|
+
if (fieldProp && Node5.isPropertyAssignment(fieldProp)) {
|
|
2114
1956
|
const fieldInit = fieldProp.getInitializer();
|
|
2115
|
-
if (fieldInit &&
|
|
1957
|
+
if (fieldInit && Node5.isStringLiteral(fieldInit)) {
|
|
2116
1958
|
names.add(fieldInit.getLiteralValue());
|
|
2117
1959
|
continue;
|
|
2118
1960
|
}
|
|
@@ -2136,7 +1978,7 @@ function extractFilterableEntityFields(filterClass, project, entityOverride, pre
|
|
|
2136
1978
|
const args = filterableDecorator.getArguments();
|
|
2137
1979
|
if (args.length === 0) return [];
|
|
2138
1980
|
const optionsArg = args[0];
|
|
2139
|
-
if (!
|
|
1981
|
+
if (!Node5.isObjectLiteralExpression(optionsArg)) return [];
|
|
2140
1982
|
const declaredEntity = resolveDeclaredEntity(optionsArg, filterClass, project);
|
|
2141
1983
|
const entityDecl = preferDeclaredEntity ? declaredEntity ?? entityOverride : entityOverride ?? declaredEntity;
|
|
2142
1984
|
if (!entityDecl) return [];
|
|
@@ -2147,17 +1989,17 @@ function extractFilterableEntityFields(filterClass, project, entityOverride, pre
|
|
|
2147
1989
|
const relationsDecorator = filterClass.getDecorators().find((d) => d.getName() === "Relations");
|
|
2148
1990
|
if (relationsDecorator) {
|
|
2149
1991
|
const relArgs = relationsDecorator.getArguments();
|
|
2150
|
-
if (relArgs.length > 0 &&
|
|
1992
|
+
if (relArgs.length > 0 && Node5.isObjectLiteralExpression(relArgs[0])) {
|
|
2151
1993
|
for (const relProp of relArgs[0].getProperties()) {
|
|
2152
|
-
if (!
|
|
1994
|
+
if (!Node5.isPropertyAssignment(relProp)) continue;
|
|
2153
1995
|
const relInit = relProp.getInitializer();
|
|
2154
|
-
if (!relInit || !
|
|
1996
|
+
if (!relInit || !Node5.isObjectLiteralExpression(relInit)) continue;
|
|
2155
1997
|
const keysProp = relInit.getProperty("keys");
|
|
2156
|
-
if (!keysProp || !
|
|
1998
|
+
if (!keysProp || !Node5.isPropertyAssignment(keysProp)) continue;
|
|
2157
1999
|
const keysInit = keysProp.getInitializer();
|
|
2158
|
-
if (!keysInit || !
|
|
2000
|
+
if (!keysInit || !Node5.isArrayLiteralExpression(keysInit)) continue;
|
|
2159
2001
|
for (const el of keysInit.getElements()) {
|
|
2160
|
-
if (
|
|
2002
|
+
if (Node5.isStringLiteral(el)) {
|
|
2161
2003
|
fields.push(toFilterFieldType(el.getLiteralValue(), { kind: "unknown" }));
|
|
2162
2004
|
}
|
|
2163
2005
|
}
|
|
@@ -2168,15 +2010,15 @@ function extractFilterableEntityFields(filterClass, project, entityOverride, pre
|
|
|
2168
2010
|
}
|
|
2169
2011
|
|
|
2170
2012
|
// src/discovery/pipe-schema.ts
|
|
2171
|
-
import { Node as
|
|
2013
|
+
import { Node as Node8 } from "ts-morph";
|
|
2172
2014
|
|
|
2173
2015
|
// src/discovery/schema-type-inference.ts
|
|
2174
2016
|
import {
|
|
2175
|
-
ts as
|
|
2017
|
+
ts as ts3
|
|
2176
2018
|
} from "ts-morph";
|
|
2177
2019
|
|
|
2178
2020
|
// src/discovery/schema-type-program.ts
|
|
2179
|
-
import { Node as
|
|
2021
|
+
import { FileSystemRefreshResult as FileSystemRefreshResult2, Node as Node6, Project as Project2, ts as ts2 } from "ts-morph";
|
|
2180
2022
|
var PARAM_DECORATORS = /* @__PURE__ */ new Set(["Body", "Query", "Param"]);
|
|
2181
2023
|
var SchemaTypeProgram = class {
|
|
2182
2024
|
constructor(discovery) {
|
|
@@ -2186,10 +2028,10 @@ var SchemaTypeProgram = class {
|
|
|
2186
2028
|
...base,
|
|
2187
2029
|
// A tsconfig-less consumer gets ES5 by default, whose lib has no `Map`/`Set`/
|
|
2188
2030
|
// `Promise` for the schema libraries' declarations to lean on.
|
|
2189
|
-
target: base.target ??
|
|
2031
|
+
target: base.target ?? ts2.ScriptTarget.ES2022,
|
|
2190
2032
|
// With neither set, resolve the way a bundler does: package `exports`
|
|
2191
2033
|
// (`zod/v4`) and extensionless or `.js` relative imports all resolve.
|
|
2192
|
-
...base.module === void 0 && base.moduleResolution === void 0 ? { module:
|
|
2034
|
+
...base.module === void 0 && base.moduleResolution === void 0 ? { module: ts2.ModuleKind.ESNext, moduleResolution: ts2.ModuleResolutionKind.Bundler } : {},
|
|
2193
2035
|
// Without it `.nullable()` and `.optional()` would erase to the bare type.
|
|
2194
2036
|
strictNullChecks: true,
|
|
2195
2037
|
// No ambient `@types/*` (a tsconfig's `types: ['node']`): a schema does not
|
|
@@ -2197,17 +2039,16 @@ var SchemaTypeProgram = class {
|
|
|
2197
2039
|
types: [],
|
|
2198
2040
|
noEmit: true
|
|
2199
2041
|
};
|
|
2200
|
-
this.project = new
|
|
2042
|
+
this.project = new Project2({
|
|
2201
2043
|
skipAddingFilesFromTsConfig: true,
|
|
2202
2044
|
compilerOptions: this.options
|
|
2203
2045
|
});
|
|
2204
|
-
|
|
2205
|
-
this.flush();
|
|
2046
|
+
this.requestDiscoveryFiles();
|
|
2206
2047
|
}
|
|
2207
2048
|
project;
|
|
2208
2049
|
options;
|
|
2209
2050
|
/** Parses a file that is not in discovery's Project, without adding it there. */
|
|
2210
|
-
scratch = new
|
|
2051
|
+
scratch = new Project2({
|
|
2211
2052
|
skipAddingFilesFromTsConfig: true,
|
|
2212
2053
|
skipLoadingLibFiles: true,
|
|
2213
2054
|
skipFileDependencyResolution: true
|
|
@@ -2215,6 +2056,53 @@ var SchemaTypeProgram = class {
|
|
|
2215
2056
|
needs = /* @__PURE__ */ new Map();
|
|
2216
2057
|
/** Pruned text per file, as last written into {@link project}. */
|
|
2217
2058
|
written = /* @__PURE__ */ new Map();
|
|
2059
|
+
/** What {@link project} read from disk unpruned (packages, declaration files). */
|
|
2060
|
+
externalFiles = new DiskFingerprints();
|
|
2061
|
+
scratchFiles = new DiskFingerprints();
|
|
2062
|
+
/** The last pruning of each file, with the source text and needs it was pruned for. */
|
|
2063
|
+
pruned = /* @__PURE__ */ new Map();
|
|
2064
|
+
/**
|
|
2065
|
+
* Bring the program up to date with discovery's files and the disk, keeping the
|
|
2066
|
+
* lib and package declarations it has already parsed and checked. Leaves it
|
|
2067
|
+
* holding exactly the files a new program built now would hold, with the same
|
|
2068
|
+
* text. Returns false when a file the program read unpruned has changed on disk
|
|
2069
|
+
* (a package was reinstalled, a `.d.ts` was edited): the caller builds a new
|
|
2070
|
+
* program instead.
|
|
2071
|
+
*/
|
|
2072
|
+
refresh() {
|
|
2073
|
+
const program = this.project.getProgram().compilerObject;
|
|
2074
|
+
const external = program.getSourceFiles().filter(
|
|
2075
|
+
(file) => !program.isSourceFileDefaultLibrary(file) && !this.written.has(file.fileName)
|
|
2076
|
+
).map((file) => ({ path: file.fileName, text: file.text }));
|
|
2077
|
+
if (this.externalFiles.changed(external).length > 0) return false;
|
|
2078
|
+
const scratch = this.scratch.getSourceFiles().map((sf) => ({ path: sf.getFilePath(), text: sf.getFullText() }));
|
|
2079
|
+
for (const path of this.scratchFiles.changed(scratch)) {
|
|
2080
|
+
const sf = this.scratch.getSourceFile(path);
|
|
2081
|
+
if (sf?.refreshFromFileSystemSync() === FileSystemRefreshResult2.Deleted) {
|
|
2082
|
+
this.scratchFiles.forget(path);
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
this.needs.clear();
|
|
2086
|
+
this.requestDiscoveryFiles();
|
|
2087
|
+
for (const path of [...this.written.keys()]) {
|
|
2088
|
+
if (this.needs.has(path)) continue;
|
|
2089
|
+
const sf = this.project.getSourceFile(path);
|
|
2090
|
+
if (sf) this.project.removeSourceFile(sf);
|
|
2091
|
+
this.written.delete(path);
|
|
2092
|
+
}
|
|
2093
|
+
return true;
|
|
2094
|
+
}
|
|
2095
|
+
/**
|
|
2096
|
+
* Everything discovery holds now (the controllers) goes in before the first
|
|
2097
|
+
* query: a file added afterwards discards the program and its checker, and
|
|
2098
|
+
* rebuilding them once per controller is exactly the cost this avoids.
|
|
2099
|
+
*/
|
|
2100
|
+
requestDiscoveryFiles() {
|
|
2101
|
+
for (const sf of this.discovery.getSourceFiles()) {
|
|
2102
|
+
this.request(sf.getFilePath(), { schema: true });
|
|
2103
|
+
}
|
|
2104
|
+
this.flush();
|
|
2105
|
+
}
|
|
2218
2106
|
/**
|
|
2219
2107
|
* The typed copy of a discovery file, with its schema positions kept. Adding one
|
|
2220
2108
|
* that was not there yet (a contract file discovery reached lazily) rebuilds the
|
|
@@ -2255,12 +2143,21 @@ var SchemaTypeProgram = class {
|
|
|
2255
2143
|
while (this.queue.length > 0) {
|
|
2256
2144
|
const path = this.queue.pop();
|
|
2257
2145
|
const original = this.original(path);
|
|
2258
|
-
if (!original)
|
|
2259
|
-
|
|
2260
|
-
|
|
2146
|
+
if (!original) {
|
|
2147
|
+
if (this.written.has(path)) changed.set(path, void 0);
|
|
2148
|
+
continue;
|
|
2149
|
+
}
|
|
2150
|
+
const { text, requests } = this.pruneCached(path, original, this.needs.get(path));
|
|
2151
|
+
for (const { path: target, want } of requests) this.request(target, want);
|
|
2261
2152
|
if (this.written.get(path) !== text) changed.set(path, text);
|
|
2262
2153
|
}
|
|
2263
2154
|
for (const [path, text] of changed) {
|
|
2155
|
+
if (text === void 0) {
|
|
2156
|
+
const sf = this.project.getSourceFile(path);
|
|
2157
|
+
if (sf) this.project.removeSourceFile(sf);
|
|
2158
|
+
this.written.delete(path);
|
|
2159
|
+
continue;
|
|
2160
|
+
}
|
|
2264
2161
|
this.project.createSourceFile(path, text, { overwrite: true });
|
|
2265
2162
|
this.written.set(path, text);
|
|
2266
2163
|
}
|
|
@@ -2269,11 +2166,29 @@ var SchemaTypeProgram = class {
|
|
|
2269
2166
|
original(path) {
|
|
2270
2167
|
return this.discovery.getSourceFile(path) ?? this.scratch.getSourceFile(path) ?? this.scratch.addSourceFileAtPathIfExists(path);
|
|
2271
2168
|
}
|
|
2169
|
+
/** {@link prune}, reusing the last result while the file's text and needs are the same. */
|
|
2170
|
+
pruneCached(path, sf, needs) {
|
|
2171
|
+
const source = sf.getFullText();
|
|
2172
|
+
const key = `${needs.schema}:${needs.all}:${[...needs.names].sort().join(",")}`;
|
|
2173
|
+
const last = this.pruned.get(path);
|
|
2174
|
+
if (last && last.source === source && last.needs === key) return last;
|
|
2175
|
+
const result = this.prune(sf, needs);
|
|
2176
|
+
if (result.resolved) this.pruned.set(path, { ...result, source, needs: key });
|
|
2177
|
+
else this.pruned.delete(path);
|
|
2178
|
+
return result;
|
|
2179
|
+
}
|
|
2272
2180
|
/**
|
|
2273
2181
|
* `sf`'s text with every import/re-export nothing in `needs` references blanked,
|
|
2274
|
-
*
|
|
2182
|
+
* and the names each kept one brings in from its target.
|
|
2275
2183
|
*/
|
|
2276
2184
|
prune(sf, needs) {
|
|
2185
|
+
const requests = [];
|
|
2186
|
+
let resolved = true;
|
|
2187
|
+
const resolveFrom = (specifier) => {
|
|
2188
|
+
const target = this.resolve(specifier, sf);
|
|
2189
|
+
if (target === null) resolved = false;
|
|
2190
|
+
return target ?? void 0;
|
|
2191
|
+
};
|
|
2277
2192
|
const locals = topLevelDeclarations(sf);
|
|
2278
2193
|
const roots = [];
|
|
2279
2194
|
if (needs.all) roots.push(...sf.getStatements());
|
|
@@ -2307,7 +2222,7 @@ var SchemaTypeProgram = class {
|
|
|
2307
2222
|
const seen = new Set(queue);
|
|
2308
2223
|
while (queue.length > 0) {
|
|
2309
2224
|
const node = queue.pop();
|
|
2310
|
-
const ids =
|
|
2225
|
+
const ids = Node6.isIdentifier(node) ? [node] : node.getDescendantsOfKind(ts2.SyntaxKind.Identifier);
|
|
2311
2226
|
for (const id of ids) {
|
|
2312
2227
|
const name = id.getText();
|
|
2313
2228
|
if (referenced.has(name)) continue;
|
|
@@ -2326,7 +2241,7 @@ var SchemaTypeProgram = class {
|
|
|
2326
2241
|
}
|
|
2327
2242
|
};
|
|
2328
2243
|
for (const decl of sf.getImportDeclarations()) {
|
|
2329
|
-
const target =
|
|
2244
|
+
const target = resolveFrom(decl.getModuleSpecifierValue());
|
|
2330
2245
|
const namespace = decl.getNamespaceImport()?.getText();
|
|
2331
2246
|
const def = decl.getDefaultImport()?.getText();
|
|
2332
2247
|
const named = decl.getNamedImports().map((n) => ({
|
|
@@ -2340,21 +2255,24 @@ var SchemaTypeProgram = class {
|
|
|
2340
2255
|
continue;
|
|
2341
2256
|
}
|
|
2342
2257
|
if (target) {
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2258
|
+
requests.push({
|
|
2259
|
+
path: target,
|
|
2260
|
+
want: {
|
|
2261
|
+
all: wantsNamespace,
|
|
2262
|
+
names: [...wantsDefault ? ["default"] : [], ...named.map((n) => n.imported)]
|
|
2263
|
+
}
|
|
2346
2264
|
});
|
|
2347
2265
|
}
|
|
2348
2266
|
}
|
|
2349
2267
|
for (const exp of sf.getExportDeclarations()) {
|
|
2350
2268
|
const spec = exp.getModuleSpecifierValue();
|
|
2351
2269
|
if (spec === void 0) continue;
|
|
2352
|
-
const target =
|
|
2270
|
+
const target = resolveFrom(spec);
|
|
2353
2271
|
if (exp.isNamespaceExport() || exp.getNamedExports().length === 0) {
|
|
2354
2272
|
if (!needs.all && unresolvedNames.length === 0) {
|
|
2355
2273
|
blank(exp);
|
|
2356
2274
|
} else if (target) {
|
|
2357
|
-
|
|
2275
|
+
requests.push({ path: target, want: { all: needs.all, names: unresolvedNames } });
|
|
2358
2276
|
}
|
|
2359
2277
|
continue;
|
|
2360
2278
|
}
|
|
@@ -2362,22 +2280,24 @@ var SchemaTypeProgram = class {
|
|
|
2362
2280
|
(n) => needs.all || needs.names.has((n.getAliasNode() ?? n.getNameNode()).getText())
|
|
2363
2281
|
).map((n) => n.getNameNode().getText());
|
|
2364
2282
|
if (wanted.length === 0) blank(exp);
|
|
2365
|
-
else if (target)
|
|
2283
|
+
else if (target) requests.push({ path: target, want: { names: wanted } });
|
|
2366
2284
|
}
|
|
2367
|
-
return chars.join("");
|
|
2285
|
+
return { text: chars.join(""), requests, resolved };
|
|
2368
2286
|
}
|
|
2369
2287
|
/**
|
|
2370
|
-
* The project source file a module specifier resolves to
|
|
2371
|
-
* package (read unpruned by the program itself)
|
|
2288
|
+
* The project source file a module specifier resolves to; undefined for a
|
|
2289
|
+
* package or a declaration file (read unpruned by the program itself), null for
|
|
2290
|
+
* one that does not resolve.
|
|
2372
2291
|
*/
|
|
2373
2292
|
resolve(specifier, from) {
|
|
2374
|
-
const resolved =
|
|
2293
|
+
const resolved = ts2.resolveModuleName(
|
|
2375
2294
|
specifier,
|
|
2376
2295
|
from.getFilePath(),
|
|
2377
2296
|
this.options,
|
|
2378
|
-
|
|
2297
|
+
ts2.sys
|
|
2379
2298
|
).resolvedModule;
|
|
2380
|
-
if (!resolved
|
|
2299
|
+
if (!resolved) return null;
|
|
2300
|
+
if (resolved.isExternalLibraryImport) return void 0;
|
|
2381
2301
|
const file = resolved.resolvedFileName;
|
|
2382
2302
|
if (file.endsWith(".d.ts") || !/\.(m|c)?tsx?$/.test(file)) return void 0;
|
|
2383
2303
|
return file;
|
|
@@ -2386,20 +2306,20 @@ var SchemaTypeProgram = class {
|
|
|
2386
2306
|
function schemaPositions(sf) {
|
|
2387
2307
|
const roots = [];
|
|
2388
2308
|
sf.forEachDescendant((node) => {
|
|
2389
|
-
if (
|
|
2309
|
+
if (Node6.isDecorator(node)) {
|
|
2390
2310
|
const name = node.getName();
|
|
2391
2311
|
if (name === "ApplyContract") {
|
|
2392
2312
|
roots.push(...node.getArguments());
|
|
2393
2313
|
} else if (PARAM_DECORATORS.has(name)) {
|
|
2394
2314
|
for (const arg of node.getArguments()) {
|
|
2395
|
-
if (
|
|
2315
|
+
if (Node6.isNewExpression(arg) || Node6.isCallExpression(arg)) {
|
|
2396
2316
|
roots.push(...arg.getArguments());
|
|
2397
2317
|
}
|
|
2398
2318
|
}
|
|
2399
2319
|
}
|
|
2400
|
-
} else if (
|
|
2320
|
+
} else if (Node6.isCallExpression(node)) {
|
|
2401
2321
|
const callee = node.getExpression();
|
|
2402
|
-
const name =
|
|
2322
|
+
const name = Node6.isIdentifier(callee) ? callee.getText() : Node6.isPropertyAccessExpression(callee) ? callee.getName() : void 0;
|
|
2403
2323
|
if (name === "defineContract") roots.push(...node.getArguments());
|
|
2404
2324
|
}
|
|
2405
2325
|
});
|
|
@@ -2432,8 +2352,13 @@ function defaultExport(sf) {
|
|
|
2432
2352
|
|
|
2433
2353
|
// src/discovery/schema-type-inference.ts
|
|
2434
2354
|
var programs = /* @__PURE__ */ new WeakMap();
|
|
2355
|
+
var stale = /* @__PURE__ */ new WeakSet();
|
|
2435
2356
|
function programFor(discovery) {
|
|
2436
2357
|
let program = programs.get(discovery);
|
|
2358
|
+
if (program && stale.has(discovery)) {
|
|
2359
|
+
stale.delete(discovery);
|
|
2360
|
+
if (!program.refresh()) program = void 0;
|
|
2361
|
+
}
|
|
2437
2362
|
if (!program) {
|
|
2438
2363
|
program = new SchemaTypeProgram(discovery);
|
|
2439
2364
|
programs.set(discovery, program);
|
|
@@ -2442,6 +2367,10 @@ function programFor(discovery) {
|
|
|
2442
2367
|
}
|
|
2443
2368
|
function clearSchemaTypeProject(discovery) {
|
|
2444
2369
|
programs.delete(discovery);
|
|
2370
|
+
stale.delete(discovery);
|
|
2371
|
+
}
|
|
2372
|
+
function invalidateSchemaTypeProject(discovery) {
|
|
2373
|
+
if (programs.has(discovery)) stale.add(discovery);
|
|
2445
2374
|
}
|
|
2446
2375
|
function counterpart(typedSf, node) {
|
|
2447
2376
|
const start = node.getStart();
|
|
@@ -2505,7 +2434,7 @@ function objectKey(name) {
|
|
|
2505
2434
|
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
|
|
2506
2435
|
}
|
|
2507
2436
|
function isOptionalSymbol(sym) {
|
|
2508
|
-
return (sym.getFlags() &
|
|
2437
|
+
return (sym.getFlags() & ts3.SymbolFlags.Optional) !== 0;
|
|
2509
2438
|
}
|
|
2510
2439
|
function isSymbolKeyed(sym) {
|
|
2511
2440
|
return sym.getName().startsWith("__@");
|
|
@@ -2666,11 +2595,11 @@ function acceptsUndefined(types) {
|
|
|
2666
2595
|
}
|
|
2667
2596
|
|
|
2668
2597
|
// src/discovery/zod-ast-to-ts.ts
|
|
2669
|
-
import { Node as
|
|
2598
|
+
import { Node as Node7, SyntaxKind as SyntaxKind2 } from "ts-morph";
|
|
2670
2599
|
function zodAstToTs(node) {
|
|
2671
|
-
if (!
|
|
2600
|
+
if (!Node7.isCallExpression(node)) return "unknown";
|
|
2672
2601
|
const expr = node.getExpression();
|
|
2673
|
-
if (
|
|
2602
|
+
if (Node7.isPropertyAccessExpression(expr)) {
|
|
2674
2603
|
const methodName = expr.getName();
|
|
2675
2604
|
const receiver = expr.getExpression();
|
|
2676
2605
|
if (methodName === "optional") {
|
|
@@ -2694,17 +2623,17 @@ function zodAstToTs(node) {
|
|
|
2694
2623
|
case "literal": {
|
|
2695
2624
|
const lit = args[0];
|
|
2696
2625
|
if (!lit) return "unknown";
|
|
2697
|
-
if (
|
|
2698
|
-
if (
|
|
2699
|
-
if (lit.getKind() ===
|
|
2700
|
-
if (lit.getKind() ===
|
|
2626
|
+
if (Node7.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
|
|
2627
|
+
if (Node7.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
|
|
2628
|
+
if (lit.getKind() === SyntaxKind2.TrueKeyword) return "true";
|
|
2629
|
+
if (lit.getKind() === SyntaxKind2.FalseKeyword) return "false";
|
|
2701
2630
|
return "unknown";
|
|
2702
2631
|
}
|
|
2703
2632
|
case "enum": {
|
|
2704
2633
|
const arrArg = args[0];
|
|
2705
|
-
if (!arrArg || !
|
|
2634
|
+
if (!arrArg || !Node7.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2706
2635
|
const members = arrArg.getElements().map(
|
|
2707
|
-
(el) =>
|
|
2636
|
+
(el) => Node7.isStringLiteral(el) ? JSON.stringify(el.getLiteralValue()) : "unknown"
|
|
2708
2637
|
);
|
|
2709
2638
|
return members.join(" | ");
|
|
2710
2639
|
}
|
|
@@ -2715,10 +2644,10 @@ function zodAstToTs(node) {
|
|
|
2715
2644
|
}
|
|
2716
2645
|
case "object": {
|
|
2717
2646
|
const objArg = args[0];
|
|
2718
|
-
if (!objArg || !
|
|
2647
|
+
if (!objArg || !Node7.isObjectLiteralExpression(objArg)) return "unknown";
|
|
2719
2648
|
const lines = [];
|
|
2720
2649
|
for (const prop of objArg.getProperties()) {
|
|
2721
|
-
if (!
|
|
2650
|
+
if (!Node7.isPropertyAssignment(prop)) continue;
|
|
2722
2651
|
const key = prop.getName();
|
|
2723
2652
|
const valNode = prop.getInitializer();
|
|
2724
2653
|
if (!valNode) continue;
|
|
@@ -2730,7 +2659,7 @@ function zodAstToTs(node) {
|
|
|
2730
2659
|
}
|
|
2731
2660
|
case "union": {
|
|
2732
2661
|
const arrArg = args[0];
|
|
2733
|
-
if (!arrArg || !
|
|
2662
|
+
if (!arrArg || !Node7.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2734
2663
|
return arrArg.getElements().map(zodAstToTs).join(" | ");
|
|
2735
2664
|
}
|
|
2736
2665
|
case "record": {
|
|
@@ -2740,7 +2669,7 @@ function zodAstToTs(node) {
|
|
|
2740
2669
|
}
|
|
2741
2670
|
case "tuple": {
|
|
2742
2671
|
const arrArg = args[0];
|
|
2743
|
-
if (!arrArg || !
|
|
2672
|
+
if (!arrArg || !Node7.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2744
2673
|
return `[${arrArg.getElements().map(zodAstToTs).join(", ")}]`;
|
|
2745
2674
|
}
|
|
2746
2675
|
default:
|
|
@@ -2750,9 +2679,9 @@ function zodAstToTs(node) {
|
|
|
2750
2679
|
return "unknown";
|
|
2751
2680
|
}
|
|
2752
2681
|
function isOptionalChain(node) {
|
|
2753
|
-
if (!
|
|
2682
|
+
if (!Node7.isCallExpression(node)) return false;
|
|
2754
2683
|
const expr = node.getExpression();
|
|
2755
|
-
return
|
|
2684
|
+
return Node7.isPropertyAccessExpression(expr) && expr.getName() === "optional";
|
|
2756
2685
|
}
|
|
2757
2686
|
function memberType(val, position, project) {
|
|
2758
2687
|
if (project) {
|
|
@@ -2764,13 +2693,13 @@ function memberType(val, position, project) {
|
|
|
2764
2693
|
return zodAstToTs(val);
|
|
2765
2694
|
}
|
|
2766
2695
|
function parseDefineContractCall(callExpr, project) {
|
|
2767
|
-
if (!
|
|
2696
|
+
if (!Node7.isCallExpression(callExpr)) return null;
|
|
2768
2697
|
const callee = callExpr.getExpression();
|
|
2769
|
-
const calleeName =
|
|
2698
|
+
const calleeName = Node7.isIdentifier(callee) ? callee.getText() : Node7.isPropertyAccessExpression(callee) ? callee.getName() : "";
|
|
2770
2699
|
if (calleeName !== "defineContract") return null;
|
|
2771
2700
|
const args = callExpr.getArguments();
|
|
2772
2701
|
const optsArg = args[0];
|
|
2773
|
-
if (!optsArg || !
|
|
2702
|
+
if (!optsArg || !Node7.isObjectLiteralExpression(optsArg)) return null;
|
|
2774
2703
|
let query = null;
|
|
2775
2704
|
let body = null;
|
|
2776
2705
|
let response = "unknown";
|
|
@@ -2778,7 +2707,7 @@ function parseDefineContractCall(callExpr, project) {
|
|
|
2778
2707
|
let bodyZodText = null;
|
|
2779
2708
|
let queryZodText = null;
|
|
2780
2709
|
for (const prop of optsArg.getProperties()) {
|
|
2781
|
-
if (!
|
|
2710
|
+
if (!Node7.isPropertyAssignment(prop)) continue;
|
|
2782
2711
|
const propName = prop.getName();
|
|
2783
2712
|
const val = prop.getInitializer();
|
|
2784
2713
|
if (!val) continue;
|
|
@@ -2800,7 +2729,7 @@ function parseDefineContractCall(callExpr, project) {
|
|
|
2800
2729
|
// src/discovery/pipe-schema.ts
|
|
2801
2730
|
function pipeSchemaOf(decorator, project) {
|
|
2802
2731
|
for (const pipe of decorator.getArguments()) {
|
|
2803
|
-
if (!
|
|
2732
|
+
if (!Node8.isNewExpression(pipe) && !Node8.isCallExpression(pipe)) continue;
|
|
2804
2733
|
for (const arg of pipe.getArguments()) {
|
|
2805
2734
|
if (!couldBeSchema(arg)) continue;
|
|
2806
2735
|
const inferred = inferSchema(arg, project);
|
|
@@ -2822,15 +2751,15 @@ function pipeSchemaOf(decorator, project) {
|
|
|
2822
2751
|
return null;
|
|
2823
2752
|
}
|
|
2824
2753
|
function couldBeSchema(arg) {
|
|
2825
|
-
return
|
|
2754
|
+
return Node8.isIdentifier(arg) || Node8.isPropertyAccessExpression(arg) || Node8.isCallExpression(arg) || Node8.isAsExpression(arg) || Node8.isSatisfiesExpression(arg) || Node8.isParenthesizedExpression(arg);
|
|
2826
2755
|
}
|
|
2827
2756
|
function syntacticZodType(arg, project) {
|
|
2828
2757
|
let expr = arg;
|
|
2829
|
-
if (
|
|
2758
|
+
if (Node8.isIdentifier(arg)) {
|
|
2830
2759
|
const resolved = resolveImportedVariable(arg.getText(), arg.getSourceFile(), project);
|
|
2831
2760
|
expr = resolved?.decl.getInitializer();
|
|
2832
2761
|
}
|
|
2833
|
-
if (!expr || !
|
|
2762
|
+
if (!expr || !Node8.isCallExpression(expr)) return null;
|
|
2834
2763
|
const type = zodAstToTs(expr);
|
|
2835
2764
|
return type === "unknown" ? null : type;
|
|
2836
2765
|
}
|
|
@@ -2866,22 +2795,22 @@ var PASSTHROUGH_UTILITY = /* @__PURE__ */ new Set([
|
|
|
2866
2795
|
]);
|
|
2867
2796
|
function resolveTypeNodeToString(typeNode, sourceFile, project, depth, subst = /* @__PURE__ */ new Map()) {
|
|
2868
2797
|
if (depth <= 0) return "unknown";
|
|
2869
|
-
if (
|
|
2798
|
+
if (Node9.isArrayTypeNode(typeNode)) {
|
|
2870
2799
|
const elementType = typeNode.getElementTypeNode();
|
|
2871
2800
|
return `Array<${resolveTypeNodeToString(elementType, sourceFile, project, depth, subst)}>`;
|
|
2872
2801
|
}
|
|
2873
|
-
if (
|
|
2802
|
+
if (Node9.isUnionTypeNode(typeNode)) {
|
|
2874
2803
|
return typeNode.getTypeNodes().map((t) => resolveTypeNodeToString(t, sourceFile, project, depth, subst)).join(" | ");
|
|
2875
2804
|
}
|
|
2876
|
-
if (
|
|
2805
|
+
if (Node9.isIntersectionTypeNode(typeNode)) {
|
|
2877
2806
|
return typeNode.getTypeNodes().map((t) => resolveTypeNodeToString(t, sourceFile, project, depth, subst)).join(" & ");
|
|
2878
2807
|
}
|
|
2879
|
-
if (
|
|
2808
|
+
if (Node9.isParenthesizedTypeNode(typeNode)) {
|
|
2880
2809
|
return `(${resolveTypeNodeToString(typeNode.getTypeNode(), sourceFile, project, depth, subst)})`;
|
|
2881
2810
|
}
|
|
2882
|
-
if (
|
|
2811
|
+
if (Node9.isTypeReference(typeNode)) {
|
|
2883
2812
|
const typeName = typeNode.getTypeName();
|
|
2884
|
-
const name =
|
|
2813
|
+
const name = Node9.isIdentifier(typeName) ? typeName.getText() : typeNode.getText();
|
|
2885
2814
|
const bound = subst.get(name);
|
|
2886
2815
|
if (bound !== void 0) return bound;
|
|
2887
2816
|
if (name === "string" || name === "number" || name === "boolean") return name;
|
|
@@ -2904,10 +2833,10 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth, subst = /
|
|
|
2904
2833
|
dbg("unresolvable type:", name, "in", sourceFile.getFilePath());
|
|
2905
2834
|
return "unknown";
|
|
2906
2835
|
}
|
|
2907
|
-
if (
|
|
2836
|
+
if (Node9.isTypeLiteral(typeNode)) {
|
|
2908
2837
|
const members = [];
|
|
2909
2838
|
for (const member of typeNode.getMembers()) {
|
|
2910
|
-
if (
|
|
2839
|
+
if (Node9.isPropertySignature(member)) {
|
|
2911
2840
|
const memberTypeNode = member.getTypeNode();
|
|
2912
2841
|
const memberType2 = memberTypeNode ? resolveTypeNodeToString(memberTypeNode, sourceFile, project, depth, subst) : "unknown";
|
|
2913
2842
|
members.push(`${member.getName()}${member.hasQuestionToken() ? "?" : ""}: ${memberType2}`);
|
|
@@ -2918,11 +2847,11 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth, subst = /
|
|
|
2918
2847
|
return members.length > 0 ? `{ ${members.join("; ")} }` : "{}";
|
|
2919
2848
|
}
|
|
2920
2849
|
const kind = typeNode.getKind();
|
|
2921
|
-
if (kind ===
|
|
2922
|
-
if (kind ===
|
|
2923
|
-
if (kind ===
|
|
2924
|
-
if (kind ===
|
|
2925
|
-
if (kind ===
|
|
2850
|
+
if (kind === SyntaxKind3.StringKeyword) return "string";
|
|
2851
|
+
if (kind === SyntaxKind3.NumberKeyword) return "number";
|
|
2852
|
+
if (kind === SyntaxKind3.BooleanKeyword) return "boolean";
|
|
2853
|
+
if (kind === SyntaxKind3.UnknownKeyword) return "unknown";
|
|
2854
|
+
if (kind === SyntaxKind3.AnyKeyword) return "unknown";
|
|
2926
2855
|
return typeNode.getText();
|
|
2927
2856
|
}
|
|
2928
2857
|
function unwrapFirstTypeArg(typeNode, sourceFile, project, depth, mode, subst = /* @__PURE__ */ new Map()) {
|
|
@@ -2980,7 +2909,7 @@ function resolvePropertied(decl, sourceFile, project, depth, subst = /* @__PURE_
|
|
|
2980
2909
|
}
|
|
2981
2910
|
function namedField(decorator) {
|
|
2982
2911
|
const first = decorator.getArguments()[0];
|
|
2983
|
-
return first &&
|
|
2912
|
+
return first && Node9.isStringLiteral(first) ? first.getLiteralValue() : null;
|
|
2984
2913
|
}
|
|
2985
2914
|
function extractBodyType(method, sourceFile, project) {
|
|
2986
2915
|
for (const param of method.getParameters()) {
|
|
@@ -3030,8 +2959,8 @@ function extractQueryType(method, sourceFile, project) {
|
|
|
3030
2959
|
function isParamOptional(param) {
|
|
3031
2960
|
if (param.hasQuestionToken() || param.hasInitializer()) return true;
|
|
3032
2961
|
const typeNode = param.getTypeNode();
|
|
3033
|
-
if (typeNode &&
|
|
3034
|
-
return typeNode.getTypeNodes().some((t) => t.getKind() ===
|
|
2962
|
+
if (typeNode && Node9.isUnionTypeNode(typeNode)) {
|
|
2963
|
+
return typeNode.getTypeNodes().some((t) => t.getKind() === SyntaxKind3.UndefinedKeyword);
|
|
3035
2964
|
}
|
|
3036
2965
|
return false;
|
|
3037
2966
|
}
|
|
@@ -3043,7 +2972,7 @@ function extractParamsType(method, sourceFile, project) {
|
|
|
3043
2972
|
const paramArgs = paramDecorator.getArguments();
|
|
3044
2973
|
if (paramArgs.length === 0) continue;
|
|
3045
2974
|
const nameArg = paramArgs[0];
|
|
3046
|
-
if (!
|
|
2975
|
+
if (!Node9.isStringLiteral(nameArg)) continue;
|
|
3047
2976
|
const paramName = nameArg.getLiteralValue();
|
|
3048
2977
|
const typeNode = param.getTypeNode();
|
|
3049
2978
|
const paramType = typeNode ? resolveTypeNodeToString(typeNode, sourceFile, project, 3) : "string";
|
|
@@ -3080,28 +3009,28 @@ function extractUploadedFiles(method) {
|
|
|
3080
3009
|
for (const decorator of method.getDecorators()) {
|
|
3081
3010
|
if (decorator.getName() !== "UseInterceptors") continue;
|
|
3082
3011
|
for (const arg of decorator.getArguments()) {
|
|
3083
|
-
if (!
|
|
3012
|
+
if (!Node9.isCallExpression(arg)) continue;
|
|
3084
3013
|
const interceptor = arg.getExpression().getText();
|
|
3085
3014
|
const callArgs = arg.getArguments();
|
|
3086
3015
|
const firstArg2 = callArgs[0];
|
|
3087
3016
|
if (interceptor === "FileInterceptor") {
|
|
3088
|
-
if (firstArg2 &&
|
|
3017
|
+
if (firstArg2 && Node9.isStringLiteral(firstArg2)) {
|
|
3089
3018
|
entries.push(`${firstArg2.getLiteralValue()}: ${FILE}`);
|
|
3090
3019
|
multipart = true;
|
|
3091
3020
|
}
|
|
3092
3021
|
} else if (interceptor === "FilesInterceptor") {
|
|
3093
|
-
if (firstArg2 &&
|
|
3022
|
+
if (firstArg2 && Node9.isStringLiteral(firstArg2)) {
|
|
3094
3023
|
entries.push(`${firstArg2.getLiteralValue()}: Array<${FILE}>`);
|
|
3095
3024
|
multipart = true;
|
|
3096
3025
|
}
|
|
3097
3026
|
} else if (interceptor === "FileFieldsInterceptor") {
|
|
3098
|
-
if (firstArg2 &&
|
|
3027
|
+
if (firstArg2 && Node9.isArrayLiteralExpression(firstArg2)) {
|
|
3099
3028
|
for (const el of firstArg2.getElements()) {
|
|
3100
|
-
if (!
|
|
3029
|
+
if (!Node9.isObjectLiteralExpression(el)) continue;
|
|
3101
3030
|
const nameProp = el.getProperty("name");
|
|
3102
|
-
if (nameProp &&
|
|
3031
|
+
if (nameProp && Node9.isPropertyAssignment(nameProp)) {
|
|
3103
3032
|
const init = nameProp.getInitializer();
|
|
3104
|
-
if (init &&
|
|
3033
|
+
if (init && Node9.isStringLiteral(init)) {
|
|
3105
3034
|
entries.push(`${init.getLiteralValue()}: Array<${FILE}>`);
|
|
3106
3035
|
}
|
|
3107
3036
|
}
|
|
@@ -3121,13 +3050,13 @@ function extractResponseType(method, sourceFile, project) {
|
|
|
3121
3050
|
if (apiResponseDecorator) {
|
|
3122
3051
|
const args = apiResponseDecorator.getArguments();
|
|
3123
3052
|
const optsArg = args[0];
|
|
3124
|
-
if (optsArg &&
|
|
3053
|
+
if (optsArg && Node9.isObjectLiteralExpression(optsArg)) {
|
|
3125
3054
|
for (const prop of optsArg.getProperties()) {
|
|
3126
|
-
if (!
|
|
3055
|
+
if (!Node9.isPropertyAssignment(prop)) continue;
|
|
3127
3056
|
if (prop.getName() !== "type") continue;
|
|
3128
3057
|
const val = prop.getInitializer();
|
|
3129
3058
|
if (!val) continue;
|
|
3130
|
-
if (
|
|
3059
|
+
if (Node9.isArrayLiteralExpression(val)) {
|
|
3131
3060
|
const elements = val.getElements();
|
|
3132
3061
|
const firstEl = elements[0];
|
|
3133
3062
|
if (elements.length > 0 && firstEl !== void 0) {
|
|
@@ -3148,24 +3077,24 @@ function extractResponseType(method, sourceFile, project) {
|
|
|
3148
3077
|
}
|
|
3149
3078
|
function apiResponseStatus(decorator) {
|
|
3150
3079
|
const optsArg = decorator.getArguments()[0];
|
|
3151
|
-
if (!optsArg || !
|
|
3080
|
+
if (!optsArg || !Node9.isObjectLiteralExpression(optsArg)) return null;
|
|
3152
3081
|
for (const prop of optsArg.getProperties()) {
|
|
3153
|
-
if (!
|
|
3082
|
+
if (!Node9.isPropertyAssignment(prop)) continue;
|
|
3154
3083
|
if (prop.getName() !== "status") continue;
|
|
3155
3084
|
const val = prop.getInitializer();
|
|
3156
|
-
if (val &&
|
|
3085
|
+
if (val && Node9.isNumericLiteral(val)) return Number(val.getLiteralValue());
|
|
3157
3086
|
}
|
|
3158
3087
|
return null;
|
|
3159
3088
|
}
|
|
3160
3089
|
function apiResponseTypeNode(decorator) {
|
|
3161
3090
|
const optsArg = decorator.getArguments()[0];
|
|
3162
|
-
if (!optsArg || !
|
|
3091
|
+
if (!optsArg || !Node9.isObjectLiteralExpression(optsArg)) return null;
|
|
3163
3092
|
for (const prop of optsArg.getProperties()) {
|
|
3164
|
-
if (!
|
|
3093
|
+
if (!Node9.isPropertyAssignment(prop)) continue;
|
|
3165
3094
|
if (prop.getName() !== "type") continue;
|
|
3166
3095
|
const val = prop.getInitializer();
|
|
3167
3096
|
if (!val) return null;
|
|
3168
|
-
if (
|
|
3097
|
+
if (Node9.isArrayLiteralExpression(val)) {
|
|
3169
3098
|
const first = val.getElements()[0];
|
|
3170
3099
|
return first ? { node: first, isArray: true } : null;
|
|
3171
3100
|
}
|
|
@@ -3183,7 +3112,7 @@ function extractErrorType(method, sourceFile, project) {
|
|
|
3183
3112
|
const inner = resolveIdentifierToClassType(typeInfo.node, sourceFile, project, 3);
|
|
3184
3113
|
const type = typeInfo.isArray ? `Array<${inner}>` : inner;
|
|
3185
3114
|
let ref = null;
|
|
3186
|
-
if (
|
|
3115
|
+
if (Node9.isIdentifier(typeInfo.node)) {
|
|
3187
3116
|
const name = typeInfo.node.getText();
|
|
3188
3117
|
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
3189
3118
|
if (localDecl?.isExported()) {
|
|
@@ -3200,7 +3129,7 @@ function extractErrorType(method, sourceFile, project) {
|
|
|
3200
3129
|
return null;
|
|
3201
3130
|
}
|
|
3202
3131
|
function resolveIdentifierToClassType(node, sourceFile, project, depth) {
|
|
3203
|
-
if (!
|
|
3132
|
+
if (!Node9.isIdentifier(node)) return "unknown";
|
|
3204
3133
|
const name = node.getText();
|
|
3205
3134
|
const resolved = findType(name, sourceFile, project);
|
|
3206
3135
|
if (resolved) {
|
|
@@ -3220,9 +3149,9 @@ var STREAM_ENVELOPES = /* @__PURE__ */ new Set(["MessageEvent", "MessageEventLik
|
|
|
3220
3149
|
var BINARY_RESPONSE_TYPES = /* @__PURE__ */ new Set(["StreamableFile", "Buffer"]);
|
|
3221
3150
|
function detectBinaryResponse(method) {
|
|
3222
3151
|
const node = unwrapNamedContainer(method.getReturnTypeNode(), /* @__PURE__ */ new Set(["Promise"]));
|
|
3223
|
-
if (!node || !
|
|
3152
|
+
if (!node || !Node9.isTypeReference(node)) return false;
|
|
3224
3153
|
const typeName = node.getTypeName();
|
|
3225
|
-
const name =
|
|
3154
|
+
const name = Node9.isIdentifier(typeName) ? typeName.getText() : "";
|
|
3226
3155
|
return BINARY_RESPONSE_TYPES.has(name);
|
|
3227
3156
|
}
|
|
3228
3157
|
function detectStreamElement(method) {
|
|
@@ -3237,9 +3166,9 @@ function detectStreamElement(method) {
|
|
|
3237
3166
|
return null;
|
|
3238
3167
|
}
|
|
3239
3168
|
function streamContainerElement(node) {
|
|
3240
|
-
if (!node || !
|
|
3169
|
+
if (!node || !Node9.isTypeReference(node)) return null;
|
|
3241
3170
|
const typeName = node.getTypeName();
|
|
3242
|
-
const name =
|
|
3171
|
+
const name = Node9.isIdentifier(typeName) ? typeName.getText() : "";
|
|
3243
3172
|
if (STREAM_CONTAINERS.has(name) || STREAM_CONTAINERS_GENERATOR.has(name)) {
|
|
3244
3173
|
return node.getTypeArguments()[0] ?? null;
|
|
3245
3174
|
}
|
|
@@ -3249,9 +3178,9 @@ function hasAsQueryDecorator(method) {
|
|
|
3249
3178
|
return method.getDecorators().some((d) => d.getName() === "AsQuery");
|
|
3250
3179
|
}
|
|
3251
3180
|
function unwrapNamedContainer(node, names) {
|
|
3252
|
-
if (!node || !
|
|
3181
|
+
if (!node || !Node9.isTypeReference(node)) return node;
|
|
3253
3182
|
const typeName = node.getTypeName();
|
|
3254
|
-
const name =
|
|
3183
|
+
const name = Node9.isIdentifier(typeName) ? typeName.getText() : "";
|
|
3255
3184
|
if (names.has(name)) {
|
|
3256
3185
|
return node.getTypeArguments()[0] ?? node;
|
|
3257
3186
|
}
|
|
@@ -3298,11 +3227,11 @@ function extractDtoContract(method, sourceFile, project, mixin, controllerClass)
|
|
|
3298
3227
|
if (apiResp) {
|
|
3299
3228
|
const args = apiResp.getArguments();
|
|
3300
3229
|
const optsArg = args[0];
|
|
3301
|
-
if (optsArg &&
|
|
3230
|
+
if (optsArg && Node9.isObjectLiteralExpression(optsArg)) {
|
|
3302
3231
|
for (const prop of optsArg.getProperties()) {
|
|
3303
|
-
if (
|
|
3232
|
+
if (Node9.isPropertyAssignment(prop) && prop.getName() === "type") {
|
|
3304
3233
|
const val = prop.getInitializer();
|
|
3305
|
-
if (val &&
|
|
3234
|
+
if (val && Node9.isIdentifier(val)) {
|
|
3306
3235
|
const name = val.getText();
|
|
3307
3236
|
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
3308
3237
|
if (localDecl?.isExported()) {
|
|
@@ -3389,7 +3318,7 @@ async function discoverContractsFast(opts) {
|
|
|
3389
3318
|
return routes;
|
|
3390
3319
|
}
|
|
3391
3320
|
function resolveTsconfigPath(cwd, tsconfig) {
|
|
3392
|
-
return tsconfig ? resolve3(tsconfig) :
|
|
3321
|
+
return tsconfig ? resolve3(tsconfig) : join3(cwd, "tsconfig.json");
|
|
3393
3322
|
}
|
|
3394
3323
|
var NO_TSCONFIG_OPTIONS = {
|
|
3395
3324
|
allowJs: true,
|
|
@@ -3398,25 +3327,25 @@ var NO_TSCONFIG_OPTIONS = {
|
|
|
3398
3327
|
};
|
|
3399
3328
|
function loadDiscoveryTsconfig(tsconfigPath) {
|
|
3400
3329
|
if (!existsSync(tsconfigPath)) return { options: null, files: [] };
|
|
3401
|
-
const read =
|
|
3330
|
+
const read = ts4.readConfigFile(tsconfigPath, (path) => ts4.sys.readFile(path));
|
|
3402
3331
|
if (read.error) {
|
|
3403
3332
|
return {
|
|
3404
3333
|
options: null,
|
|
3405
3334
|
files: [tsconfigPath],
|
|
3406
|
-
error:
|
|
3335
|
+
error: ts4.flattenDiagnosticMessageText(read.error.messageText, " ")
|
|
3407
3336
|
};
|
|
3408
3337
|
}
|
|
3409
3338
|
const host = {
|
|
3410
|
-
useCaseSensitiveFileNames:
|
|
3339
|
+
useCaseSensitiveFileNames: ts4.sys.useCaseSensitiveFileNames,
|
|
3411
3340
|
// The one line that keeps a tsconfig from being a filesystem walk.
|
|
3412
3341
|
readDirectory: () => [],
|
|
3413
|
-
fileExists: (path) =>
|
|
3342
|
+
fileExists: (path) => ts4.sys.fileExists(path),
|
|
3414
3343
|
// `extends` is resolved through the host, so a consumer whose `paths` live
|
|
3415
3344
|
// in a base tsconfig keeps working.
|
|
3416
|
-
readFile: (path) =>
|
|
3345
|
+
readFile: (path) => ts4.sys.readFile(path)
|
|
3417
3346
|
};
|
|
3418
|
-
const sourceFile =
|
|
3419
|
-
const parsed =
|
|
3347
|
+
const sourceFile = ts4.readJsonConfigFile(tsconfigPath, (path) => ts4.sys.readFile(path));
|
|
3348
|
+
const parsed = ts4.parseJsonSourceFileConfigFileContent(
|
|
3420
3349
|
sourceFile,
|
|
3421
3350
|
host,
|
|
3422
3351
|
dirname2(tsconfigPath),
|
|
@@ -3439,7 +3368,7 @@ function createDiscoveryProject(tsconfigPath, tsconfig = loadDiscoveryTsconfig(t
|
|
|
3439
3368
|
`[nestjs-codegen/fast] Could not load ${tsconfigPath}: ${tsconfig.error} \u2014 continuing WITHOUT its compiler options, so path alias imports (e.g. '@/...') will not resolve and controllers extending an alias-imported factory will contribute NO routes to the generated client.`
|
|
3440
3369
|
);
|
|
3441
3370
|
}
|
|
3442
|
-
return new
|
|
3371
|
+
return new Project3({
|
|
3443
3372
|
compilerOptions: tsconfig.options ?? { ...NO_TSCONFIG_OPTIONS },
|
|
3444
3373
|
skipAddingFilesFromTsConfig: true,
|
|
3445
3374
|
skipLoadingLibFiles: true,
|
|
@@ -3481,6 +3410,9 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
3481
3410
|
glob;
|
|
3482
3411
|
/** Absolute paths of the controllers currently loaded as extraction roots. */
|
|
3483
3412
|
controllerPaths = /* @__PURE__ */ new Set();
|
|
3413
|
+
/** Every file in {@link project}, as last read from disk. */
|
|
3414
|
+
files = new DiskFingerprints();
|
|
3415
|
+
unresolved = new UnresolvedImports();
|
|
3484
3416
|
constructor(project, cwd, glob) {
|
|
3485
3417
|
this.project = project;
|
|
3486
3418
|
this.cwd = cwd;
|
|
@@ -3506,6 +3438,8 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
3506
3438
|
}
|
|
3507
3439
|
/** Run the initial extraction (equivalent to a first `discoverContractsFast`). */
|
|
3508
3440
|
discover() {
|
|
3441
|
+
clearMixinTypeProject();
|
|
3442
|
+
clearSchemaTypeProject(this.project);
|
|
3509
3443
|
return this.runExtraction();
|
|
3510
3444
|
}
|
|
3511
3445
|
/**
|
|
@@ -3513,26 +3447,39 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
3513
3447
|
* from disk (controllers AND any lazily-loaded DTO/imported files), re-globs
|
|
3514
3448
|
* to pick up added/removed controllers, clears the per-Project caches, then
|
|
3515
3449
|
* re-extracts. `changedPaths` is a hint; correctness does not depend on it
|
|
3516
|
-
* being exhaustive because re-globbing +
|
|
3450
|
+
* being exhaustive because re-globbing + comparing every loaded file with the
|
|
3451
|
+
* disk covers the set.
|
|
3517
3452
|
*/
|
|
3518
|
-
async rediscover(changedPaths) {
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3453
|
+
async rediscover(changedPaths, options = {}) {
|
|
3454
|
+
const hinted = new Set([...changedPaths ?? []].map((p) => resolve3(p)));
|
|
3455
|
+
const program = this.project.getProgram().compilerObject;
|
|
3456
|
+
const sources = program.getSourceFiles().filter(
|
|
3457
|
+
(file) => !program.isSourceFileDefaultLibrary(file) && !program.isSourceFileFromExternalLibrary(file)
|
|
3458
|
+
).map((file) => ({ path: file.fileName, text: file.text }));
|
|
3459
|
+
for (const path of /* @__PURE__ */ new Set([...hinted, ...this.files.changed(sources)])) {
|
|
3460
|
+
const sf = this.project.getSourceFile(path);
|
|
3461
|
+
if (sf && await sf.refreshFromFileSystem() === FileSystemRefreshResult3.Deleted) {
|
|
3462
|
+
this.files.forget(path);
|
|
3526
3463
|
}
|
|
3527
3464
|
}
|
|
3465
|
+
const inProject = new Set(this.project.getSourceFiles().map((sf) => sf.getFilePath()));
|
|
3466
|
+
const definitions = this.project.getLanguageService().compilerObject.getProgram();
|
|
3467
|
+
for (const path of definitions ? this.unresolved.nowResolving(definitions) : []) {
|
|
3468
|
+
const sf = this.project.getSourceFile(path);
|
|
3469
|
+
if (!sf) continue;
|
|
3470
|
+
this.project.removeSourceFile(sf);
|
|
3471
|
+
if (inProject.has(path)) this.project.addSourceFileAtPathIfExists(path);
|
|
3472
|
+
}
|
|
3528
3473
|
const globbed = new Set(
|
|
3529
3474
|
await fg2(this.glob, { cwd: this.cwd, absolute: true, onlyFiles: true })
|
|
3530
3475
|
);
|
|
3476
|
+
let controllersChanged = false;
|
|
3531
3477
|
for (const f of globbed) {
|
|
3532
3478
|
if (!this.controllerPaths.has(f)) {
|
|
3533
3479
|
try {
|
|
3534
3480
|
this.project.addSourceFileAtPath(f);
|
|
3535
3481
|
this.controllerPaths.add(f);
|
|
3482
|
+
controllersChanged = true;
|
|
3536
3483
|
} catch {
|
|
3537
3484
|
}
|
|
3538
3485
|
}
|
|
@@ -3542,25 +3489,31 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
3542
3489
|
const sf = this.project.getSourceFile(f);
|
|
3543
3490
|
if (sf) this.project.removeSourceFile(sf);
|
|
3544
3491
|
this.controllerPaths.delete(f);
|
|
3492
|
+
controllersChanged = true;
|
|
3545
3493
|
}
|
|
3546
3494
|
}
|
|
3495
|
+
if (controllersChanged || options.filesAddedOrRemoved) {
|
|
3496
|
+
clearMixinTypeProject();
|
|
3497
|
+
clearSchemaTypeProject(this.project);
|
|
3498
|
+
} else {
|
|
3499
|
+
invalidateMixinTypeProject();
|
|
3500
|
+
invalidateSchemaTypeProject(this.project);
|
|
3501
|
+
}
|
|
3547
3502
|
return this.runExtraction();
|
|
3548
3503
|
}
|
|
3549
3504
|
/** Clear stale per-Project caches, then extract over the controller set. */
|
|
3550
3505
|
runExtraction() {
|
|
3551
3506
|
clearTypeResolutionCaches(this.project);
|
|
3552
3507
|
clearEnumCache(this.project);
|
|
3553
|
-
clearMixinTypeProject();
|
|
3554
|
-
clearSchemaTypeProject(this.project);
|
|
3555
3508
|
return extractRoutesFrom(this.project, this.controllerPaths);
|
|
3556
3509
|
}
|
|
3557
3510
|
};
|
|
3558
3511
|
function decoratorStringArg(decoratorExpr) {
|
|
3559
3512
|
if (!decoratorExpr) return void 0;
|
|
3560
|
-
if (
|
|
3561
|
-
if (
|
|
3513
|
+
if (Node10.isStringLiteral(decoratorExpr)) return decoratorExpr.getLiteralValue();
|
|
3514
|
+
if (Node10.isArrayLiteralExpression(decoratorExpr)) {
|
|
3562
3515
|
const first = decoratorExpr.getElements()[0];
|
|
3563
|
-
if (first &&
|
|
3516
|
+
if (first && Node10.isStringLiteral(first)) return first.getLiteralValue();
|
|
3564
3517
|
}
|
|
3565
3518
|
return void 0;
|
|
3566
3519
|
}
|
|
@@ -3681,9 +3634,9 @@ function extractContractRoute(args) {
|
|
|
3681
3634
|
let contractDef = null;
|
|
3682
3635
|
let bodyZodRef = null;
|
|
3683
3636
|
let queryZodRef = null;
|
|
3684
|
-
if (
|
|
3637
|
+
if (Node10.isCallExpression(firstDecoratorArg)) {
|
|
3685
3638
|
contractDef = parseDefineContractCall(firstDecoratorArg, project);
|
|
3686
|
-
} else if (
|
|
3639
|
+
} else if (Node10.isIdentifier(firstDecoratorArg)) {
|
|
3687
3640
|
const identName = firstDecoratorArg.getText();
|
|
3688
3641
|
const resolvedVar = resolveImportedVariable(identName, sourceFile, project);
|
|
3689
3642
|
if (!resolvedVar) {
|
|
@@ -3838,7 +3791,300 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
3838
3791
|
return routes;
|
|
3839
3792
|
}
|
|
3840
3793
|
|
|
3794
|
+
// src/discovery/shared-props.ts
|
|
3795
|
+
function discoverSharedProps(project, moduleEntry) {
|
|
3796
|
+
try {
|
|
3797
|
+
let sourceFile = project.getSourceFile(moduleEntry);
|
|
3798
|
+
if (!sourceFile) {
|
|
3799
|
+
try {
|
|
3800
|
+
sourceFile = project.addSourceFileAtPath(moduleEntry);
|
|
3801
|
+
} catch {
|
|
3802
|
+
return null;
|
|
3803
|
+
}
|
|
3804
|
+
}
|
|
3805
|
+
const forRootCall = findForRootCall(sourceFile);
|
|
3806
|
+
if (!forRootCall) return null;
|
|
3807
|
+
const initializer = findShareInitializer(forRootCall);
|
|
3808
|
+
if (!initializer) return null;
|
|
3809
|
+
return extractShareType(initializer, sourceFile, project);
|
|
3810
|
+
} catch {
|
|
3811
|
+
return null;
|
|
3812
|
+
}
|
|
3813
|
+
}
|
|
3814
|
+
function discoverSharedPropsFromConfig(config) {
|
|
3815
|
+
if (!config.app?.moduleEntry) return null;
|
|
3816
|
+
try {
|
|
3817
|
+
const tsconfigPath = config.app.tsconfig ?? join4(config.codegen.cwd, "tsconfig.json");
|
|
3818
|
+
let project;
|
|
3819
|
+
try {
|
|
3820
|
+
project = sharedPropsProject(tsconfigPath);
|
|
3821
|
+
} catch {
|
|
3822
|
+
project = new Project4({
|
|
3823
|
+
skipAddingFilesFromTsConfig: true,
|
|
3824
|
+
skipLoadingLibFiles: true,
|
|
3825
|
+
skipFileDependencyResolution: true,
|
|
3826
|
+
compilerOptions: { allowJs: true, strict: false }
|
|
3827
|
+
});
|
|
3828
|
+
}
|
|
3829
|
+
return discoverSharedProps(project, config.app.moduleEntry);
|
|
3830
|
+
} catch {
|
|
3831
|
+
return null;
|
|
3832
|
+
}
|
|
3833
|
+
}
|
|
3834
|
+
var parsedTsconfigs = /* @__PURE__ */ new Map();
|
|
3835
|
+
function readText(file) {
|
|
3836
|
+
try {
|
|
3837
|
+
return readFileSync2(file, "utf8");
|
|
3838
|
+
} catch {
|
|
3839
|
+
return "\0missing";
|
|
3840
|
+
}
|
|
3841
|
+
}
|
|
3842
|
+
function sharedPropsProject(tsconfigPath) {
|
|
3843
|
+
const text = loadDiscoveryTsconfig(tsconfigPath).files.map((file) => `${file}\0${readText(file)}`).join("\0");
|
|
3844
|
+
const flags = {
|
|
3845
|
+
skipAddingFilesFromTsConfig: true,
|
|
3846
|
+
skipLoadingLibFiles: true,
|
|
3847
|
+
skipFileDependencyResolution: true
|
|
3848
|
+
};
|
|
3849
|
+
const parsed = parsedTsconfigs.get(tsconfigPath);
|
|
3850
|
+
if (parsed?.text === text) return new Project4({ ...flags, compilerOptions: parsed.options });
|
|
3851
|
+
const project = new Project4({ ...flags, tsConfigFilePath: tsconfigPath });
|
|
3852
|
+
parsedTsconfigs.set(tsconfigPath, { text, options: project.getCompilerOptions() });
|
|
3853
|
+
return project;
|
|
3854
|
+
}
|
|
3855
|
+
function findForRootCall(sourceFile) {
|
|
3856
|
+
const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind4.CallExpression);
|
|
3857
|
+
for (const call of callExpressions) {
|
|
3858
|
+
const expr = call.getExpression();
|
|
3859
|
+
if (!Node11.isPropertyAccessExpression(expr)) continue;
|
|
3860
|
+
const methodName = expr.getName();
|
|
3861
|
+
const objectExpr = expr.getExpression();
|
|
3862
|
+
if (methodName === "forRoot" && Node11.isIdentifier(objectExpr)) {
|
|
3863
|
+
const name = objectExpr.getText();
|
|
3864
|
+
if (name === "InertiaModule") {
|
|
3865
|
+
return call;
|
|
3866
|
+
}
|
|
3867
|
+
}
|
|
3868
|
+
}
|
|
3869
|
+
return null;
|
|
3870
|
+
}
|
|
3871
|
+
function findShareInitializer(forRootCall) {
|
|
3872
|
+
if (!Node11.isCallExpression(forRootCall)) return null;
|
|
3873
|
+
const args = forRootCall.getArguments();
|
|
3874
|
+
const firstArg2 = args[0];
|
|
3875
|
+
if (!firstArg2 || !Node11.isObjectLiteralExpression(firstArg2)) return null;
|
|
3876
|
+
for (const prop of firstArg2.getProperties()) {
|
|
3877
|
+
if (Node11.isPropertyAssignment(prop) && prop.getName() === "share") {
|
|
3878
|
+
return prop.getInitializer() ?? null;
|
|
3879
|
+
}
|
|
3880
|
+
}
|
|
3881
|
+
return null;
|
|
3882
|
+
}
|
|
3883
|
+
function extractShareType(node, sourceFile, project) {
|
|
3884
|
+
if (Node11.isIdentifier(node)) {
|
|
3885
|
+
const ref = resolveIdentifierToImportRef(node, sourceFile, project);
|
|
3886
|
+
if (ref) {
|
|
3887
|
+
return {
|
|
3888
|
+
typeString: `Awaited<ReturnType<typeof import('${ref.importPath}').${ref.exportName}>>`,
|
|
3889
|
+
properties: null,
|
|
3890
|
+
isImportRef: true
|
|
3891
|
+
};
|
|
3892
|
+
}
|
|
3893
|
+
}
|
|
3894
|
+
if (Node11.isArrowFunction(node)) {
|
|
3895
|
+
const result = extractFromFunctionLike(node, sourceFile);
|
|
3896
|
+
return result ? { ...result, isImportRef: false } : null;
|
|
3897
|
+
}
|
|
3898
|
+
if (Node11.isFunctionExpression(node)) {
|
|
3899
|
+
const result = extractFromFunctionLike(node, sourceFile);
|
|
3900
|
+
return result ? { ...result, isImportRef: false } : null;
|
|
3901
|
+
}
|
|
3902
|
+
if (Node11.isObjectLiteralExpression(node)) {
|
|
3903
|
+
const result = extractFromObjectLiteral(node);
|
|
3904
|
+
return result ? { ...result, isImportRef: false } : null;
|
|
3905
|
+
}
|
|
3906
|
+
return null;
|
|
3907
|
+
}
|
|
3908
|
+
function resolveIdentifierToImportRef(id, sourceFile, project) {
|
|
3909
|
+
if (!Node11.isIdentifier(id)) return null;
|
|
3910
|
+
const name = id.getText();
|
|
3911
|
+
const localFunc = sourceFile.getFunction(name);
|
|
3912
|
+
if (localFunc?.isExported()) {
|
|
3913
|
+
const filePath = sourceFile.getFilePath().replace(/\.ts$/, "");
|
|
3914
|
+
return { importPath: filePath, exportName: name };
|
|
3915
|
+
}
|
|
3916
|
+
const localVar = sourceFile.getVariableDeclaration(name);
|
|
3917
|
+
if (localVar?.isExported()) {
|
|
3918
|
+
const filePath = sourceFile.getFilePath().replace(/\.ts$/, "");
|
|
3919
|
+
return { importPath: filePath, exportName: name };
|
|
3920
|
+
}
|
|
3921
|
+
for (const imp of sourceFile.getImportDeclarations()) {
|
|
3922
|
+
for (const named of imp.getNamedImports()) {
|
|
3923
|
+
const importedName = named.getAliasNode()?.getText() ?? named.getName();
|
|
3924
|
+
if (importedName !== name) continue;
|
|
3925
|
+
const resolvedSource = imp.getModuleSpecifierSourceFile();
|
|
3926
|
+
if (!resolvedSource) continue;
|
|
3927
|
+
const originalName = named.getName();
|
|
3928
|
+
const fn = resolvedSource.getFunction(originalName);
|
|
3929
|
+
if (fn?.isExported()) {
|
|
3930
|
+
const filePath = resolvedSource.getFilePath().replace(/\.ts$/, "");
|
|
3931
|
+
return { importPath: filePath, exportName: originalName };
|
|
3932
|
+
}
|
|
3933
|
+
const v = resolvedSource.getVariableDeclaration(originalName);
|
|
3934
|
+
if (v?.isExported()) {
|
|
3935
|
+
const filePath = resolvedSource.getFilePath().replace(/\.ts$/, "");
|
|
3936
|
+
return { importPath: filePath, exportName: originalName };
|
|
3937
|
+
}
|
|
3938
|
+
}
|
|
3939
|
+
}
|
|
3940
|
+
return null;
|
|
3941
|
+
}
|
|
3942
|
+
function extractFromFunctionLike(node, _sourceFile) {
|
|
3943
|
+
const returnTypeNode = Node11.isArrowFunction(node) || Node11.isFunctionExpression(node) ? node.getReturnTypeNode() : null;
|
|
3944
|
+
if (returnTypeNode) {
|
|
3945
|
+
return extractFromReturnTypeAnnotation(returnTypeNode);
|
|
3946
|
+
}
|
|
3947
|
+
if (Node11.isArrowFunction(node)) {
|
|
3948
|
+
const body = node.getBody();
|
|
3949
|
+
if (Node11.isParenthesizedExpression(body)) {
|
|
3950
|
+
const inner = body.getExpression();
|
|
3951
|
+
if (Node11.isObjectLiteralExpression(inner)) {
|
|
3952
|
+
return extractFromObjectLiteral(inner);
|
|
3953
|
+
}
|
|
3954
|
+
}
|
|
3955
|
+
if (Node11.isObjectLiteralExpression(body)) {
|
|
3956
|
+
return extractFromObjectLiteral(body);
|
|
3957
|
+
}
|
|
3958
|
+
if (Node11.isBlock(body)) {
|
|
3959
|
+
return extractFromBlockReturn(body);
|
|
3960
|
+
}
|
|
3961
|
+
}
|
|
3962
|
+
if (Node11.isFunctionExpression(node)) {
|
|
3963
|
+
const body = node.getBody();
|
|
3964
|
+
if (Node11.isBlock(body)) {
|
|
3965
|
+
return extractFromBlockReturn(body);
|
|
3966
|
+
}
|
|
3967
|
+
}
|
|
3968
|
+
return null;
|
|
3969
|
+
}
|
|
3970
|
+
function extractFromReturnTypeAnnotation(typeNode) {
|
|
3971
|
+
if (Node11.isTypeReference(typeNode)) {
|
|
3972
|
+
const typeName = typeNode.getTypeName();
|
|
3973
|
+
if (Node11.isIdentifier(typeName) && typeName.getText() === "Promise") {
|
|
3974
|
+
const typeArgs = typeNode.getTypeArguments();
|
|
3975
|
+
const firstArg2 = typeArgs[0];
|
|
3976
|
+
if (firstArg2) {
|
|
3977
|
+
return extractFromReturnTypeAnnotation(firstArg2);
|
|
3978
|
+
}
|
|
3979
|
+
return null;
|
|
3980
|
+
}
|
|
3981
|
+
}
|
|
3982
|
+
if (Node11.isTypeLiteral(typeNode)) {
|
|
3983
|
+
const properties = [];
|
|
3984
|
+
for (const member of typeNode.getMembers()) {
|
|
3985
|
+
if (Node11.isPropertySignature(member)) {
|
|
3986
|
+
const name = member.getName();
|
|
3987
|
+
const memberTypeNode = member.getTypeNode();
|
|
3988
|
+
const type = memberTypeNode ? memberTypeNode.getText() : "unknown";
|
|
3989
|
+
properties.push({ name, type });
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
3992
|
+
if (properties.length === 0) return null;
|
|
3993
|
+
const typeString = `{ ${properties.map((p) => `${p.name}: ${p.type}`).join("; ")} }`;
|
|
3994
|
+
return { typeString, properties, isImportRef: false };
|
|
3995
|
+
}
|
|
3996
|
+
return null;
|
|
3997
|
+
}
|
|
3998
|
+
function extractFromBlockReturn(block) {
|
|
3999
|
+
if (!Node11.isBlock(block)) return null;
|
|
4000
|
+
const statements = block.getStatements();
|
|
4001
|
+
for (let i = statements.length - 1; i >= 0; i--) {
|
|
4002
|
+
const stmt = statements[i];
|
|
4003
|
+
if (!Node11.isReturnStatement(stmt)) continue;
|
|
4004
|
+
const expr = stmt.getExpression();
|
|
4005
|
+
if (!expr) continue;
|
|
4006
|
+
if (Node11.isObjectLiteralExpression(expr)) {
|
|
4007
|
+
return extractFromObjectLiteral(expr);
|
|
4008
|
+
}
|
|
4009
|
+
if (Node11.isParenthesizedExpression(expr)) {
|
|
4010
|
+
const inner = expr.getExpression();
|
|
4011
|
+
if (Node11.isObjectLiteralExpression(inner)) {
|
|
4012
|
+
return extractFromObjectLiteral(inner);
|
|
4013
|
+
}
|
|
4014
|
+
}
|
|
4015
|
+
break;
|
|
4016
|
+
}
|
|
4017
|
+
return null;
|
|
4018
|
+
}
|
|
4019
|
+
function extractFromObjectLiteral(objLiteral) {
|
|
4020
|
+
if (!Node11.isObjectLiteralExpression(objLiteral)) return null;
|
|
4021
|
+
const properties = [];
|
|
4022
|
+
for (const prop of objLiteral.getProperties()) {
|
|
4023
|
+
if (!Node11.isPropertyAssignment(prop)) continue;
|
|
4024
|
+
const name = prop.getName();
|
|
4025
|
+
const initializer = prop.getInitializer();
|
|
4026
|
+
if (!initializer) continue;
|
|
4027
|
+
const type = inferExpressionType(initializer);
|
|
4028
|
+
properties.push({ name, type });
|
|
4029
|
+
}
|
|
4030
|
+
if (properties.length === 0) return null;
|
|
4031
|
+
const typeString = `{ ${properties.map((p) => `${p.name}: ${p.type}`).join("; ")} }`;
|
|
4032
|
+
return { typeString, properties, isImportRef: false };
|
|
4033
|
+
}
|
|
4034
|
+
function inferExpressionType(node) {
|
|
4035
|
+
if (Node11.isStringLiteral(node)) return "string";
|
|
4036
|
+
if (Node11.isTemplateExpression(node) || Node11.isNoSubstitutionTemplateLiteral(node))
|
|
4037
|
+
return "string";
|
|
4038
|
+
if (Node11.isNumericLiteral(node)) return "number";
|
|
4039
|
+
if (node.getKind() === SyntaxKind4.TrueKeyword || node.getKind() === SyntaxKind4.FalseKeyword) {
|
|
4040
|
+
return "boolean";
|
|
4041
|
+
}
|
|
4042
|
+
if (node.getKind() === SyntaxKind4.NullKeyword) return "null";
|
|
4043
|
+
if (Node11.isIdentifier(node) && node.getText() === "undefined") return "undefined";
|
|
4044
|
+
if (Node11.isObjectLiteralExpression(node)) {
|
|
4045
|
+
const props = node.getProperties();
|
|
4046
|
+
if (props.length === 0) return "Record<string, unknown>";
|
|
4047
|
+
const entries = [];
|
|
4048
|
+
for (const prop of props) {
|
|
4049
|
+
if (!Node11.isPropertyAssignment(prop)) continue;
|
|
4050
|
+
const key = prop.getName();
|
|
4051
|
+
const init = prop.getInitializer();
|
|
4052
|
+
if (!init) continue;
|
|
4053
|
+
entries.push(`${key}: ${inferExpressionType(init)}`);
|
|
4054
|
+
}
|
|
4055
|
+
if (entries.length === 0) return "Record<string, unknown>";
|
|
4056
|
+
return `{ ${entries.join("; ")} }`;
|
|
4057
|
+
}
|
|
4058
|
+
if (Node11.isArrayLiteralExpression(node)) {
|
|
4059
|
+
const elements = node.getElements();
|
|
4060
|
+
if (elements.length === 0) return "Array<unknown>";
|
|
4061
|
+
const first = elements[0];
|
|
4062
|
+
if (first) return `Array<${inferExpressionType(first)}>`;
|
|
4063
|
+
return "Array<unknown>";
|
|
4064
|
+
}
|
|
4065
|
+
if (Node11.isConditionalExpression(node)) {
|
|
4066
|
+
const whenTrue = inferExpressionType(node.getWhenTrue());
|
|
4067
|
+
const whenFalse = inferExpressionType(node.getWhenFalse());
|
|
4068
|
+
if (whenTrue === whenFalse) return whenTrue;
|
|
4069
|
+
return `${whenTrue} | ${whenFalse}`;
|
|
4070
|
+
}
|
|
4071
|
+
if (Node11.isParenthesizedExpression(node)) {
|
|
4072
|
+
return inferExpressionType(node.getExpression());
|
|
4073
|
+
}
|
|
4074
|
+
if (Node11.isAsExpression(node)) {
|
|
4075
|
+
const typeNode = node.getTypeNode();
|
|
4076
|
+
if (typeNode) return typeNode.getText();
|
|
4077
|
+
}
|
|
4078
|
+
return "unknown";
|
|
4079
|
+
}
|
|
4080
|
+
|
|
4081
|
+
// src/emit/emit-api.ts
|
|
4082
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
4083
|
+
import { isAbsolute as isAbsolute2, join as join5, relative as relative4 } from "path";
|
|
4084
|
+
|
|
3841
4085
|
// src/extension/registry.ts
|
|
4086
|
+
import { relative as relative3, resolve as resolve4 } from "path";
|
|
4087
|
+
import { Project as Project5 } from "ts-morph";
|
|
3842
4088
|
function resolveApiSlots(extensions) {
|
|
3843
4089
|
let layer;
|
|
3844
4090
|
let layerOwner;
|
|
@@ -5620,36 +5866,64 @@ async function discoverInputFiles(config) {
|
|
|
5620
5866
|
const matched = await fg3(globs, { cwd, absolute: true, onlyFiles: true });
|
|
5621
5867
|
return [...new Set(matched)].sort();
|
|
5622
5868
|
}
|
|
5623
|
-
|
|
5869
|
+
var READ_CONCURRENCY = 64;
|
|
5870
|
+
async function readAll(paths, read) {
|
|
5871
|
+
const out = /* @__PURE__ */ new Map();
|
|
5872
|
+
for (let i = 0; i < paths.length; i += READ_CONCURRENCY) {
|
|
5873
|
+
const batch = paths.slice(i, i + READ_CONCURRENCY);
|
|
5874
|
+
const texts = await Promise.all(batch.map(read));
|
|
5875
|
+
batch.forEach((path, j) => out.set(path, texts[j]));
|
|
5876
|
+
}
|
|
5877
|
+
return out;
|
|
5878
|
+
}
|
|
5879
|
+
async function readInputs(config) {
|
|
5880
|
+
const cwd = config.codegen.cwd;
|
|
5881
|
+
const tsconfigPath = resolveTsconfigPath(cwd, config.app?.tsconfig ?? void 0);
|
|
5882
|
+
const extended = loadDiscoveryTsconfig(tsconfigPath).files;
|
|
5883
|
+
const tsconfigFiles = extended.length > 0 ? extended : [tsconfigPath];
|
|
5884
|
+
const files = await discoverInputFiles(config);
|
|
5885
|
+
const contents = /* @__PURE__ */ new Map();
|
|
5886
|
+
const tsconfigs = await readAll(
|
|
5887
|
+
tsconfigFiles,
|
|
5888
|
+
(file) => readFile2(file, "utf8").catch(() => null)
|
|
5889
|
+
);
|
|
5890
|
+
for (const [file, text] of tsconfigs) contents.set(file, text);
|
|
5891
|
+
for (const [file, text] of await readAll(files, (file2) => readFile2(file2, "utf8"))) {
|
|
5892
|
+
contents.set(file, text);
|
|
5893
|
+
}
|
|
5894
|
+
return { tsconfigFiles, files, contents, tracked: /* @__PURE__ */ new Map() };
|
|
5895
|
+
}
|
|
5896
|
+
async function readInput(snapshot, path) {
|
|
5897
|
+
return snapshot.contents.get(path) ?? readFile2(path, "utf8");
|
|
5898
|
+
}
|
|
5899
|
+
async function computeInputsHash(config, extraInputs = [], snapshot) {
|
|
5900
|
+
const inputs = snapshot ?? await readInputs(config);
|
|
5624
5901
|
const hash = createHash("sha256");
|
|
5625
5902
|
hash.update(`version:${VERSION}
|
|
5626
5903
|
`);
|
|
5627
5904
|
hash.update(`config:${serializeConfig(config)}
|
|
5628
5905
|
`);
|
|
5629
5906
|
const cwd = config.codegen.cwd;
|
|
5630
|
-
const
|
|
5631
|
-
const tsconfigFiles = loadDiscoveryTsconfig(tsconfigPath).files;
|
|
5632
|
-
for (const file of tsconfigFiles.length > 0 ? tsconfigFiles : [tsconfigPath]) {
|
|
5633
|
-
const contents = await readFile2(file, "utf8").catch(() => null);
|
|
5907
|
+
for (const file of inputs.tsconfigFiles) {
|
|
5634
5908
|
hash.update(`tsconfig:${relative7(cwd, file)}
|
|
5635
5909
|
`);
|
|
5636
|
-
hash.update(contents ?? " missing");
|
|
5910
|
+
hash.update(inputs.contents.get(file) ?? " missing");
|
|
5637
5911
|
hash.update("\n");
|
|
5638
5912
|
}
|
|
5639
|
-
const
|
|
5640
|
-
const globbedRelative = new Set(globbed.map((file) => relative7(cwd, file)));
|
|
5913
|
+
const globbedRelative = new Set(inputs.files.map((file) => relative7(cwd, file)));
|
|
5641
5914
|
const extra = [...new Set(extraInputs)].filter((file) => !globbedRelative.has(file)).sort();
|
|
5642
|
-
for (const file of
|
|
5643
|
-
const contents = await readFile2(file, "utf8");
|
|
5915
|
+
for (const file of inputs.files) {
|
|
5644
5916
|
hash.update(`file:${relative7(cwd, file)}
|
|
5645
5917
|
`);
|
|
5646
|
-
hash.update(contents);
|
|
5918
|
+
hash.update(inputs.contents.get(file));
|
|
5647
5919
|
hash.update("\n");
|
|
5648
5920
|
}
|
|
5921
|
+
const unread = extra.map((file) => join13(cwd, file)).filter((abs) => !inputs.tracked.has(abs));
|
|
5922
|
+
for (const [abs, text] of await readAll(unread, readTrackedInput)) inputs.tracked.set(abs, text);
|
|
5649
5923
|
for (const file of extra) {
|
|
5650
5924
|
hash.update(`extra:${file}
|
|
5651
5925
|
`);
|
|
5652
|
-
hash.update(
|
|
5926
|
+
hash.update(inputs.tracked.get(join13(cwd, file)));
|
|
5653
5927
|
hash.update("\n");
|
|
5654
5928
|
}
|
|
5655
5929
|
return hash.digest("hex");
|
|
@@ -5730,7 +6004,8 @@ function driftGuardMessage(outDir, previousEntryPoint, currentEntryPoint, differ
|
|
|
5730
6004
|
async function generate(config, inputRoutes = [], entryPoint = "cli") {
|
|
5731
6005
|
setCodegenDebug(config.debug);
|
|
5732
6006
|
const manifest = await readManifest(config.codegen.outDir);
|
|
5733
|
-
const
|
|
6007
|
+
const inputs = await readInputs(config);
|
|
6008
|
+
const inputsHash = await computeInputsHash(config, manifest?.extraInputs ?? [], inputs);
|
|
5734
6009
|
if (await isManifestFresh(config.codegen.outDir, manifest, inputsHash)) {
|
|
5735
6010
|
console.log(`[nestjs-codegen] ${config.codegen.outDir} up to date, skipped`);
|
|
5736
6011
|
return;
|
|
@@ -5758,7 +6033,7 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
|
|
|
5758
6033
|
}
|
|
5759
6034
|
if (config.pages) {
|
|
5760
6035
|
const pagesConfig = config.pages;
|
|
5761
|
-
const pages = await discoverPageScopes(config);
|
|
6036
|
+
const pages = await discoverPageScopes(config, (path) => readInput(inputs, path));
|
|
5762
6037
|
const sharedProps = discoverSharedPropsFromConfig(config);
|
|
5763
6038
|
await emitPages(pages, config.codegen.outDir, {
|
|
5764
6039
|
propsExport: pagesConfig.propsExport,
|
|
@@ -5808,7 +6083,7 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
|
|
|
5808
6083
|
}
|
|
5809
6084
|
const outputFiles = await listOutputFiles(config.codegen.outDir);
|
|
5810
6085
|
const extraInputs = [...trackedInputs].sort();
|
|
5811
|
-
const recordedHash = extraInputs.length > 0 ? await computeInputsHash(config, extraInputs) : inputsHash;
|
|
6086
|
+
const recordedHash = extraInputs.length > 0 ? await computeInputsHash(config, extraInputs, inputs) : inputsHash;
|
|
5812
6087
|
await writeManifest(config.codegen.outDir, {
|
|
5813
6088
|
version: VERSION,
|
|
5814
6089
|
hash: recordedHash,
|
|
@@ -5991,13 +6266,15 @@ async function watch(config, onChange, options = {}) {
|
|
|
5991
6266
|
pagesWatcher.on("unlink", schedulePagesRegenerate);
|
|
5992
6267
|
let contractsDebounceTimer;
|
|
5993
6268
|
const pendingChangedPaths = /* @__PURE__ */ new Set();
|
|
6269
|
+
let pendingAddedOrRemoved = false;
|
|
5994
6270
|
const contractsWatcher = chokidar.watch(join16(config.codegen.cwd, config.contracts.glob), {
|
|
5995
6271
|
ignoreInitial: true,
|
|
5996
6272
|
persistent: true,
|
|
5997
6273
|
awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
|
|
5998
6274
|
});
|
|
5999
|
-
function scheduleContractsRegenerate(changedPath) {
|
|
6275
|
+
function scheduleContractsRegenerate(changedPath, addedOrRemoved = false) {
|
|
6000
6276
|
if (typeof changedPath === "string") pendingChangedPaths.add(changedPath);
|
|
6277
|
+
if (addedOrRemoved) pendingAddedOrRemoved = true;
|
|
6001
6278
|
if (contractsDebounceTimer !== void 0) {
|
|
6002
6279
|
clearTimeout(contractsDebounceTimer);
|
|
6003
6280
|
}
|
|
@@ -6005,8 +6282,12 @@ async function watch(config, onChange, options = {}) {
|
|
|
6005
6282
|
contractsDebounceTimer = void 0;
|
|
6006
6283
|
const changed = [...pendingChangedPaths];
|
|
6007
6284
|
pendingChangedPaths.clear();
|
|
6285
|
+
const filesAddedOrRemoved = pendingAddedOrRemoved;
|
|
6286
|
+
pendingAddedOrRemoved = false;
|
|
6008
6287
|
try {
|
|
6009
|
-
const routes = await (await getDiscovery()).rediscover(changed
|
|
6288
|
+
const routes = await (await getDiscovery()).rediscover(changed, {
|
|
6289
|
+
filesAddedOrRemoved
|
|
6290
|
+
});
|
|
6010
6291
|
lastRoutes = routes;
|
|
6011
6292
|
await generateAndTrack(routes);
|
|
6012
6293
|
} catch (err) {
|
|
@@ -6018,20 +6299,20 @@ async function watch(config, onChange, options = {}) {
|
|
|
6018
6299
|
onChange?.();
|
|
6019
6300
|
}, config.contracts.debounceMs);
|
|
6020
6301
|
}
|
|
6021
|
-
contractsWatcher.on("add", (p) => scheduleContractsRegenerate(p));
|
|
6302
|
+
contractsWatcher.on("add", (p) => scheduleContractsRegenerate(p, true));
|
|
6022
6303
|
contractsWatcher.on("change", (p) => scheduleContractsRegenerate(p));
|
|
6023
|
-
contractsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p));
|
|
6304
|
+
contractsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p, true));
|
|
6024
6305
|
const formsWatcher = chokidar.watch(join16(config.codegen.cwd, config.forms.watch), {
|
|
6025
6306
|
ignoreInitial: true,
|
|
6026
6307
|
persistent: true,
|
|
6027
6308
|
awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
|
|
6028
6309
|
});
|
|
6029
|
-
formsWatcher.on("add", (p) => scheduleContractsRegenerate(p));
|
|
6310
|
+
formsWatcher.on("add", (p) => scheduleContractsRegenerate(p, true));
|
|
6030
6311
|
formsWatcher.on("change", (p) => scheduleContractsRegenerate(p));
|
|
6031
|
-
formsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p));
|
|
6032
|
-
trackedWatcher.on("add", (p) => scheduleContractsRegenerate(p));
|
|
6312
|
+
formsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p, true));
|
|
6313
|
+
trackedWatcher.on("add", (p) => scheduleContractsRegenerate(p, true));
|
|
6033
6314
|
trackedWatcher.on("change", (p) => scheduleContractsRegenerate(p));
|
|
6034
|
-
trackedWatcher.on("unlink", (p) => scheduleContractsRegenerate(p));
|
|
6315
|
+
trackedWatcher.on("unlink", (p) => scheduleContractsRegenerate(p, true));
|
|
6035
6316
|
return {
|
|
6036
6317
|
close: async () => {
|
|
6037
6318
|
if (pagesDebounceTimer !== void 0) {
|
|
@@ -6052,7 +6333,7 @@ async function watch(config, onChange, options = {}) {
|
|
|
6052
6333
|
}
|
|
6053
6334
|
|
|
6054
6335
|
// src/index.ts
|
|
6055
|
-
var VERSION = "0.
|
|
6336
|
+
var VERSION = "0.29.1";
|
|
6056
6337
|
|
|
6057
6338
|
// src/cli/codegen.ts
|
|
6058
6339
|
async function runCodegen(opts = {}) {
|
|
@@ -6080,22 +6361,22 @@ async function runCodegen(opts = {}) {
|
|
|
6080
6361
|
|
|
6081
6362
|
// src/cli/doctor.ts
|
|
6082
6363
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
6083
|
-
import { appendFileSync, existsSync as existsSync2, readFileSync as
|
|
6364
|
+
import { appendFileSync, existsSync as existsSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
6084
6365
|
import { join as join18 } from "path";
|
|
6085
6366
|
|
|
6086
6367
|
// src/cli/init.ts
|
|
6087
6368
|
import { execFileSync } from "child_process";
|
|
6088
|
-
import { readFileSync as
|
|
6369
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
6089
6370
|
import { access as access2, mkdir as mkdir11, readFile as readFile5, writeFile as writeFile11 } from "fs/promises";
|
|
6090
6371
|
import { join as join17 } from "path";
|
|
6091
6372
|
import { createInterface } from "readline";
|
|
6092
6373
|
|
|
6093
6374
|
// src/cli/patch-utils.ts
|
|
6094
|
-
import { readFileSync, writeFileSync } from "fs";
|
|
6375
|
+
import { readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
6095
6376
|
function patchJsonFile(filePath, mutator, parse = (raw) => raw) {
|
|
6096
6377
|
let raw;
|
|
6097
6378
|
try {
|
|
6098
|
-
raw =
|
|
6379
|
+
raw = readFileSync3(filePath, "utf8");
|
|
6099
6380
|
} catch {
|
|
6100
6381
|
return "skipped";
|
|
6101
6382
|
}
|
|
@@ -6313,7 +6594,7 @@ async function patchPackageJsonScripts(cwd, scripts) {
|
|
|
6313
6594
|
function patchAppModule(filePath, rootView) {
|
|
6314
6595
|
let content;
|
|
6315
6596
|
try {
|
|
6316
|
-
content =
|
|
6597
|
+
content = readFileSync4(filePath, "utf8");
|
|
6317
6598
|
} catch {
|
|
6318
6599
|
return "skipped";
|
|
6319
6600
|
}
|
|
@@ -6354,7 +6635,7 @@ ${indent}HomeController,${content.slice(bracketPos)}`;
|
|
|
6354
6635
|
function patchMainTs(filePath) {
|
|
6355
6636
|
let content;
|
|
6356
6637
|
try {
|
|
6357
|
-
content =
|
|
6638
|
+
content = readFileSync4(filePath, "utf8");
|
|
6358
6639
|
} catch {
|
|
6359
6640
|
return "skipped";
|
|
6360
6641
|
}
|
|
@@ -6838,14 +7119,14 @@ function checkFileExists(cwd, file) {
|
|
|
6838
7119
|
}
|
|
6839
7120
|
function readJson(path) {
|
|
6840
7121
|
try {
|
|
6841
|
-
const raw =
|
|
7122
|
+
const raw = readFileSync5(path, "utf8").replace(/\/\/.*$/gm, "");
|
|
6842
7123
|
return JSON.parse(raw);
|
|
6843
7124
|
} catch {
|
|
6844
7125
|
return null;
|
|
6845
7126
|
}
|
|
6846
7127
|
}
|
|
6847
7128
|
function writeJsonField(filePath, dotPath, value) {
|
|
6848
|
-
const raw =
|
|
7129
|
+
const raw = readFileSync5(filePath, "utf8");
|
|
6849
7130
|
const stripped = raw.replace(/\/\/.*$/gm, "");
|
|
6850
7131
|
const obj = JSON.parse(stripped);
|
|
6851
7132
|
let target = obj;
|
|
@@ -7054,7 +7335,7 @@ async function runDoctor(opts) {
|
|
|
7054
7335
|
}
|
|
7055
7336
|
});
|
|
7056
7337
|
if (checkFileExists(cwd, "vite.config.ts")) {
|
|
7057
|
-
const viteContent =
|
|
7338
|
+
const viteContent = readFileSync5(join18(cwd, "vite.config.ts"), "utf8");
|
|
7058
7339
|
checks.push({
|
|
7059
7340
|
name: "vite.config.ts has resolve.alias",
|
|
7060
7341
|
pass: viteContent.includes("resolve") && viteContent.includes("alias"),
|
|
@@ -7120,7 +7401,7 @@ async function runDoctor(opts) {
|
|
|
7120
7401
|
}
|
|
7121
7402
|
if (checkFileExists(cwd, ".gitignore")) {
|
|
7122
7403
|
const gitignorePath = join18(cwd, ".gitignore");
|
|
7123
|
-
const gitignore =
|
|
7404
|
+
const gitignore = readFileSync5(gitignorePath, "utf8");
|
|
7124
7405
|
checks.push({
|
|
7125
7406
|
name: ".gitignore includes .nestjs-inertia/",
|
|
7126
7407
|
pass: gitignore.includes(".nestjs-inertia"),
|