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