@dudousxd/nestjs-codegen 0.2.0 → 0.3.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 +41 -0
- package/README.md +161 -0
- package/dist/cli/main.cjs +1214 -1613
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.js +1188 -1587
- package/dist/cli/main.js.map +1 -1
- package/dist/extension/index.cjs +12 -2
- package/dist/extension/index.cjs.map +1 -1
- package/dist/extension/index.d.cts +1 -1
- package/dist/extension/index.d.ts +1 -1
- package/dist/extension/index.js +10 -1
- package/dist/extension/index.js.map +1 -1
- package/dist/{index-BwIRjOQA.d.cts → index-oH5t7x4G.d.cts} +56 -41
- package/dist/{index-BwIRjOQA.d.ts → index-oH5t7x4G.d.ts} +56 -41
- package/dist/index.cjs +1003 -1457
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -16
- package/dist/index.d.ts +32 -16
- package/dist/index.js +977 -1430
- package/dist/index.js.map +1 -1
- package/dist/nest/index.cjs +908 -1355
- package/dist/nest/index.cjs.map +1 -1
- package/dist/nest/index.d.cts +9 -2
- package/dist/nest/index.d.ts +9 -2
- package/dist/nest/index.js +893 -1340
- package/dist/nest/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -44,8 +44,7 @@ __export(src_exports, {
|
|
|
44
44
|
loadConfig: () => loadConfig,
|
|
45
45
|
resolveAdapter: () => resolveAdapter,
|
|
46
46
|
resolveConfig: () => resolveConfig,
|
|
47
|
-
watch: () => watch
|
|
48
|
-
zodAdapter: () => zodAdapter
|
|
47
|
+
watch: () => watch
|
|
49
48
|
});
|
|
50
49
|
module.exports = __toCommonJS(src_exports);
|
|
51
50
|
|
|
@@ -73,112 +72,9 @@ var CodegenError = class extends Error {
|
|
|
73
72
|
}
|
|
74
73
|
};
|
|
75
74
|
|
|
76
|
-
// src/adapters/zod.ts
|
|
77
|
-
function toObjectKey(name) {
|
|
78
|
-
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
|
|
79
|
-
}
|
|
80
|
-
function messageSuffix(messageRaw2) {
|
|
81
|
-
return messageRaw2 ? `{ message: ${messageRaw2} }` : "";
|
|
82
|
-
}
|
|
83
|
-
function renderStringChecks(checks) {
|
|
84
|
-
let out = "";
|
|
85
|
-
for (const c of checks) {
|
|
86
|
-
switch (c.check) {
|
|
87
|
-
case "email":
|
|
88
|
-
out += `.email(${messageSuffix(c.messageRaw)})`;
|
|
89
|
-
break;
|
|
90
|
-
case "url":
|
|
91
|
-
out += `.url(${messageSuffix(c.messageRaw)})`;
|
|
92
|
-
break;
|
|
93
|
-
case "uuid":
|
|
94
|
-
out += `.uuid(${messageSuffix(c.messageRaw)})`;
|
|
95
|
-
break;
|
|
96
|
-
case "regex":
|
|
97
|
-
out += `.regex(${c.pattern})`;
|
|
98
|
-
break;
|
|
99
|
-
case "min":
|
|
100
|
-
out += `.min(${c.value})`;
|
|
101
|
-
break;
|
|
102
|
-
case "max":
|
|
103
|
-
out += `.max(${c.value})`;
|
|
104
|
-
break;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return out;
|
|
108
|
-
}
|
|
109
|
-
function render(node, ctx) {
|
|
110
|
-
switch (node.kind) {
|
|
111
|
-
case "string":
|
|
112
|
-
return `z.string()${renderStringChecks(node.checks)}`;
|
|
113
|
-
case "number": {
|
|
114
|
-
let out = "z.number()";
|
|
115
|
-
for (const c of node.checks) {
|
|
116
|
-
if (c.check === "int") out += ".int()";
|
|
117
|
-
else if (c.check === "positive") out += ".positive()";
|
|
118
|
-
else if (c.check === "negative") out += ".negative()";
|
|
119
|
-
else if (c.check === "min") out += `.min(${c.value})`;
|
|
120
|
-
else if (c.check === "max") out += `.max(${c.value})`;
|
|
121
|
-
}
|
|
122
|
-
return out;
|
|
123
|
-
}
|
|
124
|
-
case "boolean":
|
|
125
|
-
return "z.boolean()";
|
|
126
|
-
case "date":
|
|
127
|
-
return "z.coerce.date()";
|
|
128
|
-
case "unknown":
|
|
129
|
-
return node.note ? `z.unknown() /* ${node.note} */` : "z.unknown()";
|
|
130
|
-
case "instanceof":
|
|
131
|
-
return `z.instanceof(${node.ctor})`;
|
|
132
|
-
case "enum":
|
|
133
|
-
return `z.enum([${node.literals.join(", ")}])`;
|
|
134
|
-
case "literal":
|
|
135
|
-
return `z.literal(${node.raw})`;
|
|
136
|
-
case "union":
|
|
137
|
-
return `z.union([${node.options.map((o) => render(o, ctx)).join(", ")}])`;
|
|
138
|
-
case "object": {
|
|
139
|
-
if (node.fields.length === 0) {
|
|
140
|
-
return node.passthrough ? "z.object({}).passthrough()" : "z.object({})";
|
|
141
|
-
}
|
|
142
|
-
const inner = node.fields.map((f) => `${toObjectKey(f.key)}: ${render(f.value, ctx)}`).join(", ");
|
|
143
|
-
return `z.object({ ${inner} })${node.passthrough ? ".passthrough()" : ""}`;
|
|
144
|
-
}
|
|
145
|
-
case "array":
|
|
146
|
-
return `z.array(${render(node.element, ctx)})`;
|
|
147
|
-
case "optional":
|
|
148
|
-
return `${render(node.inner, ctx)}.optional()`;
|
|
149
|
-
case "ref":
|
|
150
|
-
return node.name;
|
|
151
|
-
case "lazyRef":
|
|
152
|
-
return `z.lazy(() => ${node.name})`;
|
|
153
|
-
case "annotated": {
|
|
154
|
-
const comments = node.unmappable.map((n) => `/* @${n}: not translatable to zod (server-only) */`).join(" ");
|
|
155
|
-
return `${render(node.inner, ctx)} ${comments}`;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
var zodAdapter = {
|
|
160
|
-
name: "zod",
|
|
161
|
-
importStatements(usage) {
|
|
162
|
-
return usage.used ? ["import { z } from 'zod';"] : [];
|
|
163
|
-
},
|
|
164
|
-
render,
|
|
165
|
-
inferType(schemaConst) {
|
|
166
|
-
return `z.infer<typeof ${schemaConst}>`;
|
|
167
|
-
},
|
|
168
|
-
renderModule(mod) {
|
|
169
|
-
const ctx = { named: mod.named };
|
|
170
|
-
const namedNestedSchemas = /* @__PURE__ */ new Map();
|
|
171
|
-
for (const [name, node] of mod.named) {
|
|
172
|
-
namedNestedSchemas.set(name, render(node, ctx));
|
|
173
|
-
}
|
|
174
|
-
return { schemaText: render(mod.root, ctx), namedNestedSchemas, warnings: mod.warnings };
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
|
|
178
75
|
// src/adapters/registry.ts
|
|
179
76
|
function resolveAdapter(option) {
|
|
180
77
|
if (typeof option !== "string") return option;
|
|
181
|
-
if (option === "zod") return zodAdapter;
|
|
182
78
|
const pkg = `@dudousxd/nestjs-codegen-${option}`;
|
|
183
79
|
const named = `${option}Adapter`;
|
|
184
80
|
throw new ConfigError(
|
|
@@ -231,8 +127,21 @@ If this is intentional, move the file inside your project directory.`
|
|
|
231
127
|
function resolveConfig(userConfig, cwd) {
|
|
232
128
|
return applyDefaults(userConfig, cwd ?? process.cwd());
|
|
233
129
|
}
|
|
130
|
+
function validateUserConfig(userConfig) {
|
|
131
|
+
if (userConfig.validation == null) {
|
|
132
|
+
throw new ConfigError(
|
|
133
|
+
"validation adapter is required \u2014 install @dudousxd/nestjs-codegen-zod and pass zodAdapter, or use @dudousxd/nestjs-codegen-valibot / -arktype"
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
if (userConfig.pages && typeof userConfig.pages.glob !== "string") {
|
|
137
|
+
throw new ConfigError(
|
|
138
|
+
"Config validation failed: `pages.glob` must be a string when `pages` is set"
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
234
142
|
function applyDefaults(userConfig, cwd) {
|
|
235
|
-
|
|
143
|
+
validateUserConfig(userConfig);
|
|
144
|
+
const outDir = userConfig.codegen?.outDir ? resolveAbsolute(cwd, userConfig.codegen.outDir) : (0, import_node_path.join)(cwd, ".nestjs-codegen");
|
|
236
145
|
const resolvedCwd = userConfig.codegen?.cwd ? resolveAbsolute(cwd, userConfig.codegen.cwd) : cwd;
|
|
237
146
|
let app = null;
|
|
238
147
|
if (userConfig.app) {
|
|
@@ -250,7 +159,8 @@ function applyDefaults(userConfig, cwd) {
|
|
|
250
159
|
}
|
|
251
160
|
return {
|
|
252
161
|
extensions: userConfig.extensions ?? [],
|
|
253
|
-
|
|
162
|
+
// Non-null: validateUserConfig() above throws when `validation` is absent.
|
|
163
|
+
validation: resolveAdapter(userConfig.validation),
|
|
254
164
|
pages: userConfig.pages ? {
|
|
255
165
|
glob: userConfig.pages.glob,
|
|
256
166
|
propsExport: userConfig.pages.propsExport ?? "ComponentProps",
|
|
@@ -304,18 +214,12 @@ Run \`nestjs-codegen init\` to create a starter config.`
|
|
|
304
214
|
`Config file must have a default export. Did you forget \`export default defineConfig({...})\`? (${configPath})`
|
|
305
215
|
);
|
|
306
216
|
}
|
|
307
|
-
if (userConfig.pages && typeof userConfig.pages.glob !== "string") {
|
|
308
|
-
throw new ConfigError(
|
|
309
|
-
`Config validation failed: \`pages.glob\` must be a string when \`pages\` is set (${configPath})`
|
|
310
|
-
);
|
|
311
|
-
}
|
|
312
217
|
return applyDefaults(userConfig, resolvedCwd);
|
|
313
218
|
}
|
|
314
219
|
|
|
315
220
|
// src/generate.ts
|
|
316
221
|
var import_promises9 = require("fs/promises");
|
|
317
|
-
var
|
|
318
|
-
var import_ts_morph3 = require("ts-morph");
|
|
222
|
+
var import_node_path10 = require("path");
|
|
319
223
|
|
|
320
224
|
// src/discovery/pages.ts
|
|
321
225
|
var import_promises2 = require("fs/promises");
|
|
@@ -370,6 +274,7 @@ function extractPropsSource(source, exportName) {
|
|
|
370
274
|
}
|
|
371
275
|
|
|
372
276
|
// src/discovery/shared-props.ts
|
|
277
|
+
var import_node_path3 = require("path");
|
|
373
278
|
var import_ts_morph = require("ts-morph");
|
|
374
279
|
function discoverSharedProps(project, moduleEntry) {
|
|
375
280
|
try {
|
|
@@ -390,6 +295,31 @@ function discoverSharedProps(project, moduleEntry) {
|
|
|
390
295
|
return null;
|
|
391
296
|
}
|
|
392
297
|
}
|
|
298
|
+
function discoverSharedPropsFromConfig(config) {
|
|
299
|
+
if (!config.app?.moduleEntry) return null;
|
|
300
|
+
try {
|
|
301
|
+
const tsconfigPath = config.app.tsconfig ?? (0, import_node_path3.join)(config.codegen.cwd, "tsconfig.json");
|
|
302
|
+
let project;
|
|
303
|
+
try {
|
|
304
|
+
project = new import_ts_morph.Project({
|
|
305
|
+
tsConfigFilePath: tsconfigPath,
|
|
306
|
+
skipAddingFilesFromTsConfig: true,
|
|
307
|
+
skipLoadingLibFiles: true,
|
|
308
|
+
skipFileDependencyResolution: true
|
|
309
|
+
});
|
|
310
|
+
} catch {
|
|
311
|
+
project = new import_ts_morph.Project({
|
|
312
|
+
skipAddingFilesFromTsConfig: true,
|
|
313
|
+
skipLoadingLibFiles: true,
|
|
314
|
+
skipFileDependencyResolution: true,
|
|
315
|
+
compilerOptions: { allowJs: true, strict: false }
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
return discoverSharedProps(project, config.app.moduleEntry);
|
|
319
|
+
} catch {
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
393
323
|
function findForRootCall(sourceFile) {
|
|
394
324
|
const callExpressions = sourceFile.getDescendantsOfKind(import_ts_morph.SyntaxKind.CallExpression);
|
|
395
325
|
for (const call of callExpressions) {
|
|
@@ -409,9 +339,9 @@ function findForRootCall(sourceFile) {
|
|
|
409
339
|
function findShareInitializer(forRootCall) {
|
|
410
340
|
if (!import_ts_morph.Node.isCallExpression(forRootCall)) return null;
|
|
411
341
|
const args = forRootCall.getArguments();
|
|
412
|
-
const
|
|
413
|
-
if (!
|
|
414
|
-
for (const prop of
|
|
342
|
+
const firstArg2 = args[0];
|
|
343
|
+
if (!firstArg2 || !import_ts_morph.Node.isObjectLiteralExpression(firstArg2)) return null;
|
|
344
|
+
for (const prop of firstArg2.getProperties()) {
|
|
415
345
|
if (import_ts_morph.Node.isPropertyAssignment(prop) && prop.getName() === "share") {
|
|
416
346
|
return prop.getInitializer() ?? null;
|
|
417
347
|
}
|
|
@@ -510,9 +440,9 @@ function extractFromReturnTypeAnnotation(typeNode) {
|
|
|
510
440
|
const typeName = typeNode.getTypeName();
|
|
511
441
|
if (import_ts_morph.Node.isIdentifier(typeName) && typeName.getText() === "Promise") {
|
|
512
442
|
const typeArgs = typeNode.getTypeArguments();
|
|
513
|
-
const
|
|
514
|
-
if (
|
|
515
|
-
return extractFromReturnTypeAnnotation(
|
|
443
|
+
const firstArg2 = typeArgs[0];
|
|
444
|
+
if (firstArg2) {
|
|
445
|
+
return extractFromReturnTypeAnnotation(firstArg2);
|
|
516
446
|
}
|
|
517
447
|
return null;
|
|
518
448
|
}
|
|
@@ -618,25 +548,14 @@ function inferExpressionType(node) {
|
|
|
618
548
|
|
|
619
549
|
// src/emit/emit-api.ts
|
|
620
550
|
var import_promises3 = require("fs/promises");
|
|
621
|
-
var
|
|
551
|
+
var import_node_path4 = require("path");
|
|
622
552
|
|
|
623
553
|
// src/extension/registry.ts
|
|
624
554
|
var import_ts_morph2 = require("ts-morph");
|
|
625
555
|
function resolveApiSlots(extensions) {
|
|
626
|
-
let transport;
|
|
627
|
-
let transportOwner;
|
|
628
556
|
let layer;
|
|
629
557
|
let layerOwner;
|
|
630
558
|
for (const ext of extensions) {
|
|
631
|
-
if (ext.apiTransport) {
|
|
632
|
-
if (transport) {
|
|
633
|
-
throw new CodegenError(
|
|
634
|
-
`api transport claimed by both "${transportOwner}" and "${ext.name}" \u2014 only one extension may set apiTransport.`
|
|
635
|
-
);
|
|
636
|
-
}
|
|
637
|
-
transport = ext.apiTransport;
|
|
638
|
-
transportOwner = ext.name;
|
|
639
|
-
}
|
|
640
559
|
if (ext.apiClientLayer) {
|
|
641
560
|
if (layer) {
|
|
642
561
|
throw new CodegenError(
|
|
@@ -648,11 +567,22 @@ function resolveApiSlots(extensions) {
|
|
|
648
567
|
}
|
|
649
568
|
}
|
|
650
569
|
return {
|
|
651
|
-
...transport ? { transport } : {},
|
|
652
570
|
...layer ? { layer } : {}
|
|
653
571
|
};
|
|
654
572
|
}
|
|
655
573
|
var CORE_FILES = /* @__PURE__ */ new Set(["routes.ts", "api.ts", "forms.ts", "index.d.ts", "pages.d.ts"]);
|
|
574
|
+
function mergeExclusive(target, incoming, {
|
|
575
|
+
owner,
|
|
576
|
+
describe
|
|
577
|
+
}) {
|
|
578
|
+
for (const [key, value] of incoming) {
|
|
579
|
+
const prev = target.get(key);
|
|
580
|
+
if (prev !== void 0) {
|
|
581
|
+
throw new CodegenError(describe(key, prev.owner, owner));
|
|
582
|
+
}
|
|
583
|
+
target.set(key, { value, owner });
|
|
584
|
+
}
|
|
585
|
+
}
|
|
656
586
|
function createExtensionContext(config, getRoutes) {
|
|
657
587
|
let project;
|
|
658
588
|
return {
|
|
@@ -697,29 +627,36 @@ async function collectEmittedFiles(extensions, ctx) {
|
|
|
697
627
|
`Extension "${ext.name}" tried to emit the core-owned file "${file.path}". Core files (${[...CORE_FILES].join(", ")}) cannot be produced by extensions.`
|
|
698
628
|
);
|
|
699
629
|
}
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
);
|
|
705
|
-
}
|
|
706
|
-
owners.set(key, ext.name);
|
|
630
|
+
mergeExclusive(owners, [[key, file]], {
|
|
631
|
+
owner: ext.name,
|
|
632
|
+
describe: (_key, prevOwner, owner) => `Output file "${file.path}" is emitted by both "${prevOwner}" and "${owner}". Two extensions cannot write the same file.`
|
|
633
|
+
});
|
|
707
634
|
files.push(file);
|
|
708
635
|
}
|
|
709
636
|
}
|
|
710
637
|
return files;
|
|
711
638
|
}
|
|
712
639
|
|
|
640
|
+
// src/extension/types.ts
|
|
641
|
+
function requestShape(route) {
|
|
642
|
+
const cs = route.contract?.contractSource;
|
|
643
|
+
const isGet = route.method.toUpperCase() === "GET";
|
|
644
|
+
const isQuery = isGet || !!cs?.filterFields?.length;
|
|
645
|
+
const hasBody = !!cs?.bodyRef || cs?.body != null && cs.body !== "never";
|
|
646
|
+
const hasQuery = isGet || !!cs?.queryRef || cs?.query != null && cs.query !== "never";
|
|
647
|
+
return { isGet, isQuery, hasBody, hasQuery };
|
|
648
|
+
}
|
|
649
|
+
|
|
713
650
|
// src/emit/emit-api.ts
|
|
714
651
|
async function emitApi(routes, outDir, opts = {}) {
|
|
715
652
|
await (0, import_promises3.mkdir)(outDir, { recursive: true });
|
|
716
653
|
const content = buildApiFile(routes, outDir, opts);
|
|
717
|
-
await (0, import_promises3.writeFile)((0,
|
|
654
|
+
await (0, import_promises3.writeFile)((0, import_node_path4.join)(outDir, "api.ts"), content, "utf8");
|
|
718
655
|
}
|
|
719
656
|
function splitName(name) {
|
|
720
657
|
return name.split(".");
|
|
721
658
|
}
|
|
722
|
-
function
|
|
659
|
+
function toObjectKey(segment) {
|
|
723
660
|
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(segment)) {
|
|
724
661
|
return segment;
|
|
725
662
|
}
|
|
@@ -812,7 +749,7 @@ function emitFilterQueryType(c) {
|
|
|
812
749
|
}
|
|
813
750
|
function buildResponseType(c, outDir) {
|
|
814
751
|
if (c.controllerRef) {
|
|
815
|
-
let relPath = (0,
|
|
752
|
+
let relPath = (0, import_node_path4.relative)(outDir, c.controllerRef.filePath).replace(/\.ts$/, "");
|
|
816
753
|
if (!relPath.startsWith(".")) relPath = `./${relPath}`;
|
|
817
754
|
return `Awaited<ReturnType<import('${relPath}').${c.controllerRef.className}['${c.controllerRef.methodName}']>>`;
|
|
818
755
|
}
|
|
@@ -826,7 +763,7 @@ function emitRouterTypeBlock(tree, indent, outDir) {
|
|
|
826
763
|
const pad = " ".repeat(indent);
|
|
827
764
|
const lines = [];
|
|
828
765
|
for (const [key, node] of tree) {
|
|
829
|
-
const objKey =
|
|
766
|
+
const objKey = toObjectKey(key);
|
|
830
767
|
if (node.kind === "leaf") {
|
|
831
768
|
const c = node;
|
|
832
769
|
const method = c.method.toUpperCase();
|
|
@@ -852,15 +789,12 @@ function emitRouterTypeBlock(tree, indent, outDir) {
|
|
|
852
789
|
return lines;
|
|
853
790
|
}
|
|
854
791
|
function buildRequestModel(c) {
|
|
855
|
-
const isGet = c.method.toUpperCase() === "GET";
|
|
856
792
|
const m = c.method.toLowerCase();
|
|
857
793
|
const flat = JSON.stringify(c.name);
|
|
858
794
|
const path = JSON.stringify(c.path);
|
|
859
795
|
const TA = buildRouterTypeAccess(c.name);
|
|
860
796
|
const withParams = hasPathParams(c.params);
|
|
861
|
-
const
|
|
862
|
-
const isQuery = isGet || !!c.contractSource.filterFields?.length;
|
|
863
|
-
const hasQuery = isGet || !!c.contractSource.queryRef || c.contractSource.query != null && c.contractSource.query !== "never";
|
|
797
|
+
const { isGet, isQuery, hasBody, hasQuery } = requestShape(c.route);
|
|
864
798
|
const fields = [];
|
|
865
799
|
if (withParams) fields.push(`params: ${TA}['params']`);
|
|
866
800
|
if (hasQuery) fields.push(`query?: ${TA}['query']`);
|
|
@@ -882,7 +816,6 @@ function buildRequestModel(c) {
|
|
|
882
816
|
urlExpr,
|
|
883
817
|
optsExpr,
|
|
884
818
|
responseType: `${TA}['response']`,
|
|
885
|
-
bodyType: `${TA}['body']`,
|
|
886
819
|
queryKeyExpr: `[${flat}, input] as const`
|
|
887
820
|
};
|
|
888
821
|
}
|
|
@@ -930,7 +863,7 @@ function emitApiObjectBlock(tree, indent, p) {
|
|
|
930
863
|
const pad = " ".repeat(indent);
|
|
931
864
|
const lines = [];
|
|
932
865
|
for (const [key, node] of tree) {
|
|
933
|
-
const objKey =
|
|
866
|
+
const objKey = toObjectKey(key);
|
|
934
867
|
if (node.kind === "branch") {
|
|
935
868
|
lines.push(`${pad}${objKey}: {`);
|
|
936
869
|
lines.push(...emitApiObjectBlock(node.children, indent + 2, p));
|
|
@@ -938,30 +871,28 @@ function emitApiObjectBlock(tree, indent, p) {
|
|
|
938
871
|
continue;
|
|
939
872
|
}
|
|
940
873
|
const req = buildRequestModel(node);
|
|
941
|
-
const
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
params: node.params,
|
|
946
|
-
contract: { contractSource: node.contractSource },
|
|
947
|
-
...node.controllerRef ? { controllerRef: node.controllerRef } : {}
|
|
874
|
+
const leaf = {
|
|
875
|
+
route: node.route,
|
|
876
|
+
request: req,
|
|
877
|
+
requestExpr: renderFetcherRequest(req)
|
|
948
878
|
};
|
|
949
|
-
const
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
879
|
+
const owned = /* @__PURE__ */ new Map();
|
|
880
|
+
if (p.layer) {
|
|
881
|
+
mergeExclusive(owned, Object.entries(p.layer.buildMembers(leaf.requestExpr, leaf, p.ctx)), {
|
|
882
|
+
owner: p.layer.name,
|
|
883
|
+
describe: (name, prevOwner, owner) => `api member "${name}" on route "${req.routeName}" is contributed by more than one extension (conflict between "${prevOwner}" and "${owner}").`
|
|
884
|
+
});
|
|
885
|
+
}
|
|
953
886
|
for (const ext of p.memberExts) {
|
|
954
887
|
const extra = ext.apiMembers?.(leaf, p.ctx);
|
|
955
888
|
if (!extra) continue;
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
);
|
|
961
|
-
}
|
|
962
|
-
members[name] = value;
|
|
963
|
-
}
|
|
889
|
+
mergeExclusive(owned, Object.entries(extra), {
|
|
890
|
+
owner: ext.name,
|
|
891
|
+
describe: (name, prevOwner, owner) => `api member "${name}" on route "${req.routeName}" is contributed by more than one extension (conflict between "${prevOwner}" and "${owner}").`
|
|
892
|
+
});
|
|
964
893
|
}
|
|
894
|
+
const members = {};
|
|
895
|
+
for (const [name, { value }] of owned) members[name] = value;
|
|
965
896
|
lines.push(...renderLeaf(pad, objKey, req, leaf.requestExpr, members));
|
|
966
897
|
}
|
|
967
898
|
return lines;
|
|
@@ -970,10 +901,82 @@ function buildRouterTypeAccess(name) {
|
|
|
970
901
|
const segments = splitName(name);
|
|
971
902
|
return `ApiRouter${segments.map((s) => `[${JSON.stringify(s)}]`).join("")}`;
|
|
972
903
|
}
|
|
904
|
+
var RESOLVER_HELPERS = [
|
|
905
|
+
// --- Recursive helper type _RouterAt: walks nested ApiRouter by dot-path ---
|
|
906
|
+
"type _RouterAt<R, P extends string> = P extends `${infer Head}.${infer Tail}`",
|
|
907
|
+
" ? Head extends keyof R ? _RouterAt<R[Head], Tail> : never",
|
|
908
|
+
" : P extends keyof R ? R[P] : never;",
|
|
909
|
+
"",
|
|
910
|
+
// --- ResolveByName: resolve a field from a dot-path name ---
|
|
911
|
+
"type ResolveByName<K extends string, Field extends string> = _RouterAt<ApiRouter, K> extends infer R ? Field extends keyof R ? R[Field] : never : never;",
|
|
912
|
+
"",
|
|
913
|
+
// --- ResolveByPath: scan all leaves for matching method + url ---
|
|
914
|
+
// Flattens ApiRouter recursively and finds the entry whose method === M and url === U.
|
|
915
|
+
"type _LeafValues<T> = T extends { method: string; url: string }",
|
|
916
|
+
" ? T",
|
|
917
|
+
" : T extends object ? _LeafValues<T[keyof T]> : never;",
|
|
918
|
+
"",
|
|
919
|
+
"type ResolveByPath<M extends string, U extends string, Field extends string> = _LeafValues<ApiRouter> extends infer L",
|
|
920
|
+
" ? L extends { method: M; url: U }",
|
|
921
|
+
" ? Field extends keyof L ? L[Field] : never",
|
|
922
|
+
" : never",
|
|
923
|
+
" : never;",
|
|
924
|
+
""
|
|
925
|
+
];
|
|
926
|
+
var ROUTE_NAMESPACE = [
|
|
927
|
+
"export namespace Route {",
|
|
928
|
+
' export type Response<K extends string> = ResolveByName<K, "response">;',
|
|
929
|
+
' export type Body<K extends string> = ResolveByName<K, "body">;',
|
|
930
|
+
' export type Query<K extends string> = ResolveByName<K, "query">;',
|
|
931
|
+
' export type Params<K extends string> = ResolveByName<K, "params">;',
|
|
932
|
+
' export type Error<K extends string> = ResolveByName<K, "error">;',
|
|
933
|
+
' export type FilterFields<K extends string> = ResolveByName<K, "filterFields">;',
|
|
934
|
+
" export type Request<K extends string> = {",
|
|
935
|
+
" body: Body<K>;",
|
|
936
|
+
" query: Query<K>;",
|
|
937
|
+
" params: Params<K>;",
|
|
938
|
+
" };",
|
|
939
|
+
"}",
|
|
940
|
+
""
|
|
941
|
+
];
|
|
942
|
+
var PATH_NAMESPACE = [
|
|
943
|
+
"export namespace Path {",
|
|
944
|
+
' export type Response<M extends string, U extends string> = ResolveByPath<M, U, "response">;',
|
|
945
|
+
' export type Body<M extends string, U extends string> = ResolveByPath<M, U, "body">;',
|
|
946
|
+
' export type Query<M extends string, U extends string> = ResolveByPath<M, U, "query">;',
|
|
947
|
+
' export type Params<M extends string, U extends string> = ResolveByPath<M, U, "params">;',
|
|
948
|
+
' export type Error<M extends string, U extends string> = ResolveByPath<M, U, "error">;',
|
|
949
|
+
' export type FilterFields<M extends string, U extends string> = ResolveByPath<M, U, "filterFields">;',
|
|
950
|
+
"}",
|
|
951
|
+
""
|
|
952
|
+
];
|
|
953
|
+
var EMPTY_ROUTE_NAMESPACE = [
|
|
954
|
+
"export namespace Route {",
|
|
955
|
+
" export type Response<K extends string> = never;",
|
|
956
|
+
" export type Body<K extends string> = never;",
|
|
957
|
+
" export type Query<K extends string> = never;",
|
|
958
|
+
" export type Params<K extends string> = never;",
|
|
959
|
+
" export type Error<K extends string> = never;",
|
|
960
|
+
" export type FilterFields<K extends string> = never;",
|
|
961
|
+
" export type Request<K extends string> = { body: never; query: never; params: never };",
|
|
962
|
+
"}",
|
|
963
|
+
""
|
|
964
|
+
];
|
|
965
|
+
var EMPTY_PATH_NAMESPACE = [
|
|
966
|
+
"export namespace Path {",
|
|
967
|
+
" export type Response<M extends string, U extends string> = never;",
|
|
968
|
+
" export type Body<M extends string, U extends string> = never;",
|
|
969
|
+
" export type Query<M extends string, U extends string> = never;",
|
|
970
|
+
" export type Params<M extends string, U extends string> = never;",
|
|
971
|
+
" export type Error<M extends string, U extends string> = never;",
|
|
972
|
+
" export type FilterFields<M extends string, U extends string> = never;",
|
|
973
|
+
"}",
|
|
974
|
+
""
|
|
975
|
+
];
|
|
973
976
|
function buildApiFile(routes, outDir, opts = {}) {
|
|
974
977
|
const fetcherImportPath = opts.fetcherImportPath;
|
|
975
978
|
const extensions = opts.extensions ?? [];
|
|
976
|
-
const {
|
|
979
|
+
const { layer } = resolveApiSlots(extensions);
|
|
977
980
|
const memberExts = extensions.filter((e) => e.apiMembers);
|
|
978
981
|
const headerExts = extensions.filter((e) => e.apiHeader);
|
|
979
982
|
const contracted = routes.filter((r) => r.contract);
|
|
@@ -1018,7 +1021,6 @@ function buildApiFile(routes, outDir, opts = {}) {
|
|
|
1018
1021
|
seenImports.add(imp);
|
|
1019
1022
|
extImports.push(imp);
|
|
1020
1023
|
};
|
|
1021
|
-
for (const imp of transport?.imports?.(ctx) ?? []) pushImport(imp);
|
|
1022
1024
|
for (const imp of layer?.imports?.(ctx) ?? []) pushImport(imp);
|
|
1023
1025
|
for (const ext of headerExts) {
|
|
1024
1026
|
for (const imp of ext.apiHeader?.(ctx)?.imports ?? []) pushImport(imp);
|
|
@@ -1034,8 +1036,8 @@ function buildApiFile(routes, outDir, opts = {}) {
|
|
|
1034
1036
|
const emittedNames = /* @__PURE__ */ new Set();
|
|
1035
1037
|
for (const [filePath, names] of importsByFile) {
|
|
1036
1038
|
let relPath;
|
|
1037
|
-
if ((0,
|
|
1038
|
-
relPath = (0,
|
|
1039
|
+
if ((0, import_node_path4.isAbsolute)(filePath)) {
|
|
1040
|
+
relPath = (0, import_node_path4.relative)(outDir, filePath).replace(/\.ts$/, "");
|
|
1039
1041
|
if (!relPath.startsWith(".")) relPath = `./${relPath}`;
|
|
1040
1042
|
} else {
|
|
1041
1043
|
relPath = filePath;
|
|
@@ -1063,27 +1065,8 @@ function buildApiFile(routes, outDir, opts = {}) {
|
|
|
1063
1065
|
lines.push("}");
|
|
1064
1066
|
lines.push("export type Api = ReturnType<typeof createApi>;");
|
|
1065
1067
|
lines.push("");
|
|
1066
|
-
lines.push(
|
|
1067
|
-
lines.push(
|
|
1068
|
-
lines.push(" export type Body<K extends string> = never;");
|
|
1069
|
-
lines.push(" export type Query<K extends string> = never;");
|
|
1070
|
-
lines.push(" export type Params<K extends string> = never;");
|
|
1071
|
-
lines.push(" export type Error<K extends string> = never;");
|
|
1072
|
-
lines.push(" export type FilterFields<K extends string> = never;");
|
|
1073
|
-
lines.push(
|
|
1074
|
-
" export type Request<K extends string> = { body: never; query: never; params: never };"
|
|
1075
|
-
);
|
|
1076
|
-
lines.push("}");
|
|
1077
|
-
lines.push("");
|
|
1078
|
-
lines.push("export namespace Path {");
|
|
1079
|
-
lines.push(" export type Response<M extends string, U extends string> = never;");
|
|
1080
|
-
lines.push(" export type Body<M extends string, U extends string> = never;");
|
|
1081
|
-
lines.push(" export type Query<M extends string, U extends string> = never;");
|
|
1082
|
-
lines.push(" export type Params<M extends string, U extends string> = never;");
|
|
1083
|
-
lines.push(" export type Error<M extends string, U extends string> = never;");
|
|
1084
|
-
lines.push(" export type FilterFields<M extends string, U extends string> = never;");
|
|
1085
|
-
lines.push("}");
|
|
1086
|
-
lines.push("");
|
|
1068
|
+
lines.push(...EMPTY_ROUTE_NAMESPACE);
|
|
1069
|
+
lines.push(...EMPTY_PATH_NAMESPACE);
|
|
1087
1070
|
return lines.join("\n");
|
|
1088
1071
|
}
|
|
1089
1072
|
const tree = /* @__PURE__ */ new Map();
|
|
@@ -1101,7 +1084,8 @@ function buildApiFile(routes, outDir, opts = {}) {
|
|
|
1101
1084
|
path: r.path,
|
|
1102
1085
|
params: r.params,
|
|
1103
1086
|
controllerRef: r.controllerRef,
|
|
1104
|
-
contractSource: c.contractSource
|
|
1087
|
+
contractSource: c.contractSource,
|
|
1088
|
+
route: r
|
|
1105
1089
|
};
|
|
1106
1090
|
insertIntoTree(tree, segments, leaf, name);
|
|
1107
1091
|
}
|
|
@@ -1114,7 +1098,6 @@ function buildApiFile(routes, outDir, opts = {}) {
|
|
|
1114
1098
|
lines.push(" return {");
|
|
1115
1099
|
lines.push(
|
|
1116
1100
|
...emitApiObjectBlock(tree, 4, {
|
|
1117
|
-
...transport ? { transport } : {},
|
|
1118
1101
|
...layer ? { layer } : {},
|
|
1119
1102
|
memberExts,
|
|
1120
1103
|
ctx
|
|
@@ -1125,61 +1108,9 @@ function buildApiFile(routes, outDir, opts = {}) {
|
|
|
1125
1108
|
lines.push("");
|
|
1126
1109
|
lines.push("export type Api = ReturnType<typeof createApi>;");
|
|
1127
1110
|
lines.push("");
|
|
1128
|
-
lines.push(
|
|
1129
|
-
lines.push(
|
|
1130
|
-
lines.push(
|
|
1131
|
-
lines.push("");
|
|
1132
|
-
lines.push(
|
|
1133
|
-
"type ResolveByName<K extends string, Field extends string> = _RouterAt<ApiRouter, K> extends infer R ? Field extends keyof R ? R[Field] : never : never;"
|
|
1134
|
-
);
|
|
1135
|
-
lines.push("");
|
|
1136
|
-
lines.push("type _LeafValues<T> = T extends { method: string; url: string }");
|
|
1137
|
-
lines.push(" ? T");
|
|
1138
|
-
lines.push(" : T extends object ? _LeafValues<T[keyof T]> : never;");
|
|
1139
|
-
lines.push("");
|
|
1140
|
-
lines.push(
|
|
1141
|
-
"type ResolveByPath<M extends string, U extends string, Field extends string> = _LeafValues<ApiRouter> extends infer L"
|
|
1142
|
-
);
|
|
1143
|
-
lines.push(" ? L extends { method: M; url: U }");
|
|
1144
|
-
lines.push(" ? Field extends keyof L ? L[Field] : never");
|
|
1145
|
-
lines.push(" : never");
|
|
1146
|
-
lines.push(" : never;");
|
|
1147
|
-
lines.push("");
|
|
1148
|
-
lines.push("export namespace Route {");
|
|
1149
|
-
lines.push(' export type Response<K extends string> = ResolveByName<K, "response">;');
|
|
1150
|
-
lines.push(' export type Body<K extends string> = ResolveByName<K, "body">;');
|
|
1151
|
-
lines.push(' export type Query<K extends string> = ResolveByName<K, "query">;');
|
|
1152
|
-
lines.push(' export type Params<K extends string> = ResolveByName<K, "params">;');
|
|
1153
|
-
lines.push(' export type Error<K extends string> = ResolveByName<K, "error">;');
|
|
1154
|
-
lines.push(' export type FilterFields<K extends string> = ResolveByName<K, "filterFields">;');
|
|
1155
|
-
lines.push(" export type Request<K extends string> = {");
|
|
1156
|
-
lines.push(" body: Body<K>;");
|
|
1157
|
-
lines.push(" query: Query<K>;");
|
|
1158
|
-
lines.push(" params: Params<K>;");
|
|
1159
|
-
lines.push(" };");
|
|
1160
|
-
lines.push("}");
|
|
1161
|
-
lines.push("");
|
|
1162
|
-
lines.push("export namespace Path {");
|
|
1163
|
-
lines.push(
|
|
1164
|
-
' export type Response<M extends string, U extends string> = ResolveByPath<M, U, "response">;'
|
|
1165
|
-
);
|
|
1166
|
-
lines.push(
|
|
1167
|
-
' export type Body<M extends string, U extends string> = ResolveByPath<M, U, "body">;'
|
|
1168
|
-
);
|
|
1169
|
-
lines.push(
|
|
1170
|
-
' export type Query<M extends string, U extends string> = ResolveByPath<M, U, "query">;'
|
|
1171
|
-
);
|
|
1172
|
-
lines.push(
|
|
1173
|
-
' export type Params<M extends string, U extends string> = ResolveByPath<M, U, "params">;'
|
|
1174
|
-
);
|
|
1175
|
-
lines.push(
|
|
1176
|
-
' export type Error<M extends string, U extends string> = ResolveByPath<M, U, "error">;'
|
|
1177
|
-
);
|
|
1178
|
-
lines.push(
|
|
1179
|
-
' export type FilterFields<M extends string, U extends string> = ResolveByPath<M, U, "filterFields">;'
|
|
1180
|
-
);
|
|
1181
|
-
lines.push("}");
|
|
1182
|
-
lines.push("");
|
|
1111
|
+
lines.push(...RESOLVER_HELPERS);
|
|
1112
|
+
lines.push(...ROUTE_NAMESPACE);
|
|
1113
|
+
lines.push(...PATH_NAMESPACE);
|
|
1183
1114
|
for (const ext of headerExts) {
|
|
1184
1115
|
const statements = ext.apiHeader?.(ctx)?.statements;
|
|
1185
1116
|
if (statements?.length) {
|
|
@@ -1191,7 +1122,7 @@ function buildApiFile(routes, outDir, opts = {}) {
|
|
|
1191
1122
|
|
|
1192
1123
|
// src/emit/emit-cache.ts
|
|
1193
1124
|
var import_promises4 = require("fs/promises");
|
|
1194
|
-
var
|
|
1125
|
+
var import_node_path5 = require("path");
|
|
1195
1126
|
async function emitCache(pages, outDir) {
|
|
1196
1127
|
await (0, import_promises4.mkdir)(outDir, { recursive: true });
|
|
1197
1128
|
const entries = await Promise.all(
|
|
@@ -1205,95 +1136,21 @@ async function emitCache(pages, outDir) {
|
|
|
1205
1136
|
})
|
|
1206
1137
|
);
|
|
1207
1138
|
const cache = { pages: entries };
|
|
1208
|
-
await (0, import_promises4.writeFile)((0,
|
|
1139
|
+
await (0, import_promises4.writeFile)((0, import_node_path5.join)(outDir, "components.json"), `${JSON.stringify(cache, null, 2)}
|
|
1209
1140
|
`, "utf8");
|
|
1210
1141
|
}
|
|
1211
1142
|
|
|
1212
1143
|
// src/emit/emit-forms.ts
|
|
1213
1144
|
var import_promises5 = require("fs/promises");
|
|
1214
|
-
var
|
|
1145
|
+
var import_node_path6 = require("path");
|
|
1215
1146
|
async function emitForms(routes, outDir, config, adapter) {
|
|
1216
1147
|
if (config && config.enabled === false) return false;
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
if (content2 === null) return false;
|
|
1220
|
-
await (0, import_promises5.mkdir)(outDir, { recursive: true });
|
|
1221
|
-
await (0, import_promises5.writeFile)((0, import_node_path5.join)(outDir, "forms.ts"), content2, "utf8");
|
|
1222
|
-
return true;
|
|
1223
|
-
}
|
|
1224
|
-
const entries = collectFormEntries(routes);
|
|
1225
|
-
if (entries.length === 0) return false;
|
|
1148
|
+
const content = buildFormsFileWithAdapter(routes, outDir, adapter, config);
|
|
1149
|
+
if (content === null) return false;
|
|
1226
1150
|
await (0, import_promises5.mkdir)(outDir, { recursive: true });
|
|
1227
|
-
|
|
1228
|
-
await (0, import_promises5.writeFile)((0, import_node_path5.join)(outDir, "forms.ts"), content, "utf8");
|
|
1151
|
+
await (0, import_promises5.writeFile)((0, import_node_path6.join)(outDir, "forms.ts"), content, "utf8");
|
|
1229
1152
|
return true;
|
|
1230
1153
|
}
|
|
1231
|
-
function buildFormsFileWithAdapter(routes, adapter) {
|
|
1232
|
-
const sorted = [...routes].filter((r) => r.contract).sort((a, b) => a.name.localeCompare(b.name));
|
|
1233
|
-
const methodNameCounts = /* @__PURE__ */ new Map();
|
|
1234
|
-
for (const route of sorted) {
|
|
1235
|
-
const cs = route.contract.contractSource;
|
|
1236
|
-
if (!cs.bodySchema && !cs.querySchema) continue;
|
|
1237
|
-
methodNameCounts.set(
|
|
1238
|
-
deriveBaseName(route.name).method,
|
|
1239
|
-
(methodNameCounts.get(deriveBaseName(route.name).method) ?? 0) + 1
|
|
1240
|
-
);
|
|
1241
|
-
}
|
|
1242
|
-
const named = /* @__PURE__ */ new Map();
|
|
1243
|
-
const decls = [];
|
|
1244
|
-
const mapEntries = [];
|
|
1245
|
-
let used = false;
|
|
1246
|
-
for (const route of sorted) {
|
|
1247
|
-
const cs = route.contract.contractSource;
|
|
1248
|
-
const { method, full } = deriveBaseName(route.name);
|
|
1249
|
-
const base = (methodNameCounts.get(method) ?? 0) > 1 ? full : method;
|
|
1250
|
-
const block = [];
|
|
1251
|
-
if (cs.bodyZodText && !cs.bodySchema) {
|
|
1252
|
-
block.push(
|
|
1253
|
-
`// warning: ${route.name} body is a defineContract (zod) schema; not translated to ${adapter.name} \u2014 use the zod adapter.`
|
|
1254
|
-
);
|
|
1255
|
-
}
|
|
1256
|
-
let bodyConst;
|
|
1257
|
-
if (cs.bodySchema) {
|
|
1258
|
-
used = true;
|
|
1259
|
-
const r = adapter.renderModule(cs.bodySchema);
|
|
1260
|
-
for (const [n, t] of r.namedNestedSchemas) named.set(n, t);
|
|
1261
|
-
bodyConst = `${base}BodySchema`;
|
|
1262
|
-
block.push(`export const ${bodyConst} = ${r.schemaText};`);
|
|
1263
|
-
block.push(`export type ${base}Body = ${adapter.inferType(bodyConst)};`);
|
|
1264
|
-
}
|
|
1265
|
-
if (cs.querySchema) {
|
|
1266
|
-
used = true;
|
|
1267
|
-
const r = adapter.renderModule(cs.querySchema);
|
|
1268
|
-
for (const [n, t] of r.namedNestedSchemas) named.set(n, t);
|
|
1269
|
-
const queryConst = `${base}QuerySchema`;
|
|
1270
|
-
block.push(`export const ${queryConst} = ${r.schemaText};`);
|
|
1271
|
-
block.push(`export type ${base}Query = ${adapter.inferType(queryConst)};`);
|
|
1272
|
-
}
|
|
1273
|
-
if (block.length === 0) continue;
|
|
1274
|
-
decls.push(`// ${route.name}`, ...block, "");
|
|
1275
|
-
if (bodyConst) mapEntries.push(` ${JSON.stringify(route.name)}: ${bodyConst},`);
|
|
1276
|
-
}
|
|
1277
|
-
if (!used) return null;
|
|
1278
|
-
const lines = ["// Generated by @dudousxd/nestjs-codegen. Do not edit."];
|
|
1279
|
-
for (const imp of adapter.importStatements({ used: true })) lines.push(imp);
|
|
1280
|
-
lines.push("");
|
|
1281
|
-
if (named.size > 0) {
|
|
1282
|
-
lines.push("// Hoisted nested schemas (shared across endpoints).");
|
|
1283
|
-
for (const [n, t] of named) lines.push(`const ${n} = ${t};`);
|
|
1284
|
-
lines.push("");
|
|
1285
|
-
}
|
|
1286
|
-
lines.push(...decls);
|
|
1287
|
-
lines.push("/** Route name \u2192 body schema map. */");
|
|
1288
|
-
lines.push("export const formSchemas = {");
|
|
1289
|
-
lines.push(...mapEntries);
|
|
1290
|
-
lines.push("} as const;");
|
|
1291
|
-
lines.push("");
|
|
1292
|
-
return lines.join("\n");
|
|
1293
|
-
}
|
|
1294
|
-
function hasSchema(src) {
|
|
1295
|
-
return !!src && (src.ref !== null || src.text !== null);
|
|
1296
|
-
}
|
|
1297
1154
|
function pascal(segment) {
|
|
1298
1155
|
return segment.split(/[^a-zA-Z0-9]+/).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
|
|
1299
1156
|
}
|
|
@@ -1303,124 +1160,16 @@ function deriveBaseName(routeName) {
|
|
|
1303
1160
|
const full = segments.map(pascal).join("");
|
|
1304
1161
|
return { method, full };
|
|
1305
1162
|
}
|
|
1306
|
-
function collectFormEntries(routes) {
|
|
1307
|
-
const sorted = [...routes].filter((r) => r.contract).sort((a, b) => a.name.localeCompare(b.name));
|
|
1308
|
-
const methodNameCounts = /* @__PURE__ */ new Map();
|
|
1309
|
-
const candidates = [];
|
|
1310
|
-
for (const route of sorted) {
|
|
1311
|
-
const cs = route.contract.contractSource;
|
|
1312
|
-
const body = { ref: cs.bodyZodRef ?? null, text: cs.bodyZodText ?? null };
|
|
1313
|
-
const query = { ref: cs.queryZodRef ?? null, text: cs.queryZodText ?? null };
|
|
1314
|
-
if (!hasSchema(body) && !hasSchema(query)) continue;
|
|
1315
|
-
const { method, full } = deriveBaseName(route.name);
|
|
1316
|
-
methodNameCounts.set(method, (methodNameCounts.get(method) ?? 0) + 1);
|
|
1317
|
-
candidates.push({ route, method, full });
|
|
1318
|
-
}
|
|
1319
|
-
const entries = [];
|
|
1320
|
-
for (const { route, method, full } of candidates) {
|
|
1321
|
-
const cs = route.contract.contractSource;
|
|
1322
|
-
const collision = (methodNameCounts.get(method) ?? 0) > 1;
|
|
1323
|
-
const baseName = collision ? full : method;
|
|
1324
|
-
const body = { ref: cs.bodyZodRef ?? null, text: cs.bodyZodText ?? null };
|
|
1325
|
-
const query = { ref: cs.queryZodRef ?? null, text: cs.queryZodText ?? null };
|
|
1326
|
-
entries.push({
|
|
1327
|
-
routeName: route.name,
|
|
1328
|
-
baseName,
|
|
1329
|
-
body: hasSchema(body) ? body : void 0,
|
|
1330
|
-
query: hasSchema(query) ? query : void 0,
|
|
1331
|
-
nestedSchemas: cs.formNestedSchemas ?? null,
|
|
1332
|
-
warnings: cs.formWarnings ?? []
|
|
1333
|
-
});
|
|
1334
|
-
}
|
|
1335
|
-
return entries;
|
|
1336
|
-
}
|
|
1337
1163
|
function relImport(outDir, filePath) {
|
|
1338
|
-
let relPath = (0,
|
|
1164
|
+
let relPath = (0, import_node_path6.relative)(outDir, filePath).replace(/\.ts$/, "");
|
|
1339
1165
|
if (!relPath.startsWith(".")) relPath = `./${relPath}`;
|
|
1340
1166
|
return relPath;
|
|
1341
1167
|
}
|
|
1342
1168
|
function refRootIdentifier(refName) {
|
|
1343
1169
|
return refName.split(".")[0] ?? refName;
|
|
1344
1170
|
}
|
|
1345
|
-
function
|
|
1346
|
-
|
|
1347
|
-
const lines = [
|
|
1348
|
-
"// Generated by @dudousxd/nestjs-codegen. Do not edit.",
|
|
1349
|
-
`import { z } from '${zodImport}';`
|
|
1350
|
-
];
|
|
1351
|
-
const importsByFile = /* @__PURE__ */ new Map();
|
|
1352
|
-
const refAlias = /* @__PURE__ */ new Map();
|
|
1353
|
-
for (const entry of entries) {
|
|
1354
|
-
for (const src of [entry.body, entry.query]) {
|
|
1355
|
-
if (src?.ref && !src.text) {
|
|
1356
|
-
const root = refRootIdentifier(src.ref.name);
|
|
1357
|
-
const set = importsByFile.get(src.ref.filePath) ?? /* @__PURE__ */ new Set();
|
|
1358
|
-
set.add(root);
|
|
1359
|
-
importsByFile.set(src.ref.filePath, set);
|
|
1360
|
-
}
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
|
-
if (importsByFile.size > 0) {
|
|
1364
|
-
const emitted = /* @__PURE__ */ new Set();
|
|
1365
|
-
for (const [filePath, roots] of [...importsByFile.entries()].sort()) {
|
|
1366
|
-
const relPath = relImport(outDir, filePath);
|
|
1367
|
-
const specifiers = [];
|
|
1368
|
-
for (const root of [...roots].sort()) {
|
|
1369
|
-
if (emitted.has(root)) {
|
|
1370
|
-
const alias = `${root}_${emitted.size}`;
|
|
1371
|
-
specifiers.push(`${root} as ${alias}`);
|
|
1372
|
-
emitted.add(alias);
|
|
1373
|
-
refAlias.set(`${filePath}\0${root}`, alias);
|
|
1374
|
-
} else {
|
|
1375
|
-
specifiers.push(root);
|
|
1376
|
-
emitted.add(root);
|
|
1377
|
-
refAlias.set(`${filePath}\0${root}`, root);
|
|
1378
|
-
}
|
|
1379
|
-
}
|
|
1380
|
-
lines.push(`import { ${specifiers.join(", ")} } from '${relPath}';`);
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
lines.push("");
|
|
1384
|
-
const { globalSchemas, renamesByEntry } = planNestedSchemas(entries);
|
|
1385
|
-
if (globalSchemas.size > 0) {
|
|
1386
|
-
lines.push("// Hoisted nested schemas (shared across endpoints).");
|
|
1387
|
-
for (const [name, text] of globalSchemas) {
|
|
1388
|
-
lines.push(`const ${name} = ${text};`);
|
|
1389
|
-
}
|
|
1390
|
-
lines.push("");
|
|
1391
|
-
}
|
|
1392
|
-
const mapEntries = [];
|
|
1393
|
-
for (const entry of entries) {
|
|
1394
|
-
lines.push(`// ${entry.routeName}`);
|
|
1395
|
-
if (entry.warnings && entry.warnings.length > 0) {
|
|
1396
|
-
for (const w of entry.warnings) {
|
|
1397
|
-
lines.push(`// warning: ${w}`);
|
|
1398
|
-
}
|
|
1399
|
-
}
|
|
1400
|
-
const rename = renamesByEntry.get(entry) ?? null;
|
|
1401
|
-
if (entry.body) {
|
|
1402
|
-
const schemaName = `${entry.baseName}BodySchema`;
|
|
1403
|
-
const typeName = `${entry.baseName}Body`;
|
|
1404
|
-
const text = applyRenames(renderSchema(entry.body, outDir, refAlias), rename);
|
|
1405
|
-
lines.push(`export const ${schemaName} = ${text};`);
|
|
1406
|
-
lines.push(`export type ${typeName} = z.infer<typeof ${schemaName}>;`);
|
|
1407
|
-
mapEntries.push(` ${JSON.stringify(entry.routeName)}: ${schemaName},`);
|
|
1408
|
-
}
|
|
1409
|
-
if (entry.query) {
|
|
1410
|
-
const schemaName = `${entry.baseName}QuerySchema`;
|
|
1411
|
-
const typeName = `${entry.baseName}Query`;
|
|
1412
|
-
const text = applyRenames(renderSchema(entry.query, outDir, refAlias), rename);
|
|
1413
|
-
lines.push(`export const ${schemaName} = ${text};`);
|
|
1414
|
-
lines.push(`export type ${typeName} = z.infer<typeof ${schemaName}>;`);
|
|
1415
|
-
}
|
|
1416
|
-
lines.push("");
|
|
1417
|
-
}
|
|
1418
|
-
lines.push("/** Route name \u2192 body schema map. */");
|
|
1419
|
-
lines.push("export const formSchemas = {");
|
|
1420
|
-
lines.push(...mapEntries);
|
|
1421
|
-
lines.push("} as const;");
|
|
1422
|
-
lines.push("");
|
|
1423
|
-
return lines.join("\n");
|
|
1171
|
+
function hasSource(src) {
|
|
1172
|
+
return !!(src.schema || src.zodText || src.zodRef);
|
|
1424
1173
|
}
|
|
1425
1174
|
function applyRenames(text, renames) {
|
|
1426
1175
|
if (!renames || renames.size === 0) return text;
|
|
@@ -1486,38 +1235,190 @@ function planNestedSchemas(entries) {
|
|
|
1486
1235
|
}
|
|
1487
1236
|
return { globalSchemas, renamesByEntry };
|
|
1488
1237
|
}
|
|
1489
|
-
function
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1238
|
+
function buildFormsFileWithAdapter(routes, outDir, adapter, config) {
|
|
1239
|
+
const acceptsRawZod = adapter.acceptsRawZodSource === true;
|
|
1240
|
+
const sorted = [...routes].filter((r) => r.contract).sort((a, b) => a.name.localeCompare(b.name));
|
|
1241
|
+
const methodNameCounts = /* @__PURE__ */ new Map();
|
|
1242
|
+
const candidates = [];
|
|
1243
|
+
for (const route of sorted) {
|
|
1244
|
+
const cs = route.contract.contractSource;
|
|
1245
|
+
const body = {
|
|
1246
|
+
schema: cs.bodySchema ?? null,
|
|
1247
|
+
zodText: cs.bodyZodText ?? null,
|
|
1248
|
+
zodRef: cs.bodyZodRef ?? null
|
|
1249
|
+
};
|
|
1250
|
+
const query = {
|
|
1251
|
+
schema: cs.querySchema ?? null,
|
|
1252
|
+
zodText: cs.queryZodText ?? null,
|
|
1253
|
+
zodRef: cs.queryZodRef ?? null
|
|
1254
|
+
};
|
|
1255
|
+
if (!hasSource(body) && !hasSource(query)) continue;
|
|
1256
|
+
const { method, full } = deriveBaseName(route.name);
|
|
1257
|
+
methodNameCounts.set(method, (methodNameCounts.get(method) ?? 0) + 1);
|
|
1258
|
+
candidates.push({
|
|
1259
|
+
routeName: route.name,
|
|
1260
|
+
baseName: full,
|
|
1261
|
+
// resolved below
|
|
1262
|
+
body: hasSource(body) ? body : void 0,
|
|
1263
|
+
query: hasSource(query) ? query : void 0,
|
|
1264
|
+
nestedSchemas: cs.formNestedSchemas ?? null,
|
|
1265
|
+
warnings: cs.formWarnings ?? []
|
|
1266
|
+
});
|
|
1496
1267
|
}
|
|
1497
|
-
|
|
1498
|
-
}
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
const
|
|
1506
|
-
|
|
1507
|
-
|
|
1268
|
+
const entries = candidates.map((c) => {
|
|
1269
|
+
const { method, full } = deriveBaseName(c.routeName);
|
|
1270
|
+
const collision = (methodNameCounts.get(method) ?? 0) > 1;
|
|
1271
|
+
return { ...c, baseName: collision ? full : method };
|
|
1272
|
+
});
|
|
1273
|
+
if (entries.length === 0) return null;
|
|
1274
|
+
const importsByFile = /* @__PURE__ */ new Map();
|
|
1275
|
+
const refAlias = /* @__PURE__ */ new Map();
|
|
1276
|
+
for (const entry of entries) {
|
|
1277
|
+
for (const src of [entry.body, entry.query]) {
|
|
1278
|
+
if (src?.zodRef && !src.zodText && !src.schema) {
|
|
1279
|
+
const root = refRootIdentifier(src.zodRef.name);
|
|
1280
|
+
const set = importsByFile.get(src.zodRef.filePath) ?? /* @__PURE__ */ new Set();
|
|
1281
|
+
set.add(root);
|
|
1282
|
+
importsByFile.set(src.zodRef.filePath, set);
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1508
1285
|
}
|
|
1509
|
-
|
|
1510
|
-
|
|
1286
|
+
const importLines = [];
|
|
1287
|
+
if (importsByFile.size > 0) {
|
|
1288
|
+
const emitted = /* @__PURE__ */ new Set();
|
|
1289
|
+
for (const [filePath, roots] of [...importsByFile.entries()].sort()) {
|
|
1290
|
+
const relPath = relImport(outDir, filePath);
|
|
1291
|
+
const specifiers = [];
|
|
1292
|
+
for (const root of [...roots].sort()) {
|
|
1293
|
+
if (emitted.has(root)) {
|
|
1294
|
+
const alias = `${root}_${emitted.size}`;
|
|
1295
|
+
specifiers.push(`${root} as ${alias}`);
|
|
1296
|
+
emitted.add(alias);
|
|
1297
|
+
refAlias.set(`${filePath}\0${root}`, alias);
|
|
1298
|
+
} else {
|
|
1299
|
+
specifiers.push(root);
|
|
1300
|
+
emitted.add(root);
|
|
1301
|
+
refAlias.set(`${filePath}\0${root}`, root);
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
importLines.push(`import { ${specifiers.join(", ")} } from '${relPath}';`);
|
|
1305
|
+
}
|
|
1511
1306
|
}
|
|
1512
|
-
const
|
|
1513
|
-
|
|
1307
|
+
const { globalSchemas, renamesByEntry } = planNestedSchemas(entries);
|
|
1308
|
+
const irNamed = /* @__PURE__ */ new Map();
|
|
1309
|
+
const decls = [];
|
|
1310
|
+
const mapEntries = [];
|
|
1311
|
+
let used = false;
|
|
1312
|
+
const renderSource = (src, rename) => {
|
|
1313
|
+
if (src.schema) {
|
|
1314
|
+
const r = adapter.renderModule(src.schema);
|
|
1315
|
+
for (const [n, t] of r.namedNestedSchemas) irNamed.set(n, t);
|
|
1316
|
+
return { text: r.schemaText };
|
|
1317
|
+
}
|
|
1318
|
+
if (src.zodText) {
|
|
1319
|
+
if (!acceptsRawZod) {
|
|
1320
|
+
return {
|
|
1321
|
+
text: "",
|
|
1322
|
+
warn: `is a defineContract (zod) schema; not translated to ${adapter.name} \u2014 use the zod adapter.`
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1325
|
+
return { text: applyRenames(src.zodText, rename) };
|
|
1326
|
+
}
|
|
1327
|
+
if (src.zodRef) {
|
|
1328
|
+
if (!acceptsRawZod) {
|
|
1329
|
+
return {
|
|
1330
|
+
text: "",
|
|
1331
|
+
warn: `is a defineContract (zod) schema; not translated to ${adapter.name} \u2014 use the zod adapter.`
|
|
1332
|
+
};
|
|
1333
|
+
}
|
|
1334
|
+
const root = refRootIdentifier(src.zodRef.name);
|
|
1335
|
+
const alias = refAlias.get(`${src.zodRef.filePath}\0${root}`) ?? root;
|
|
1336
|
+
const member = src.zodRef.name.slice(root.length);
|
|
1337
|
+
return { text: `${alias}${member}` };
|
|
1338
|
+
}
|
|
1339
|
+
return null;
|
|
1340
|
+
};
|
|
1341
|
+
for (const entry of entries) {
|
|
1342
|
+
const block = [];
|
|
1343
|
+
const rename = renamesByEntry.get(entry) ?? null;
|
|
1344
|
+
let bodyConst;
|
|
1345
|
+
if (entry.warnings && entry.warnings.length > 0) {
|
|
1346
|
+
for (const w of entry.warnings) block.push(`// warning: ${w}`);
|
|
1347
|
+
}
|
|
1348
|
+
if (entry.body) {
|
|
1349
|
+
const rendered = renderSource(entry.body, rename);
|
|
1350
|
+
if (rendered?.warn) {
|
|
1351
|
+
block.push(`// warning: ${entry.routeName} body ${rendered.warn}`);
|
|
1352
|
+
} else if (rendered) {
|
|
1353
|
+
used = true;
|
|
1354
|
+
bodyConst = `${entry.baseName}BodySchema`;
|
|
1355
|
+
block.push(`export const ${bodyConst} = ${rendered.text};`);
|
|
1356
|
+
block.push(`export type ${entry.baseName}Body = ${adapter.inferType(bodyConst)};`);
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
if (entry.query) {
|
|
1360
|
+
const rendered = renderSource(entry.query, rename);
|
|
1361
|
+
if (rendered?.warn) {
|
|
1362
|
+
block.push(`// warning: ${entry.routeName} query ${rendered.warn}`);
|
|
1363
|
+
} else if (rendered) {
|
|
1364
|
+
used = true;
|
|
1365
|
+
const queryConst = `${entry.baseName}QuerySchema`;
|
|
1366
|
+
block.push(`export const ${queryConst} = ${rendered.text};`);
|
|
1367
|
+
block.push(`export type ${entry.baseName}Query = ${adapter.inferType(queryConst)};`);
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
if (block.length === 0) continue;
|
|
1371
|
+
decls.push(`// ${entry.routeName}`, ...block, "");
|
|
1372
|
+
if (bodyConst) mapEntries.push(` ${JSON.stringify(entry.routeName)}: ${bodyConst},`);
|
|
1373
|
+
}
|
|
1374
|
+
if (!used) return null;
|
|
1375
|
+
const lines = ["// Generated by @dudousxd/nestjs-codegen. Do not edit."];
|
|
1376
|
+
if (acceptsRawZod) {
|
|
1377
|
+
const zodImport = config?.zodImport ?? "zod";
|
|
1378
|
+
lines.push(`import { z } from '${zodImport}';`);
|
|
1379
|
+
} else {
|
|
1380
|
+
for (const imp of adapter.importStatements({ used: true })) lines.push(imp);
|
|
1381
|
+
}
|
|
1382
|
+
lines.push(...importLines);
|
|
1383
|
+
lines.push("");
|
|
1384
|
+
const allNested = /* @__PURE__ */ new Map();
|
|
1385
|
+
for (const [n, t] of globalSchemas) allNested.set(n, t);
|
|
1386
|
+
for (const [n, t] of irNamed) if (!allNested.has(n)) allNested.set(n, t);
|
|
1387
|
+
if (allNested.size > 0) {
|
|
1388
|
+
lines.push("// Hoisted nested schemas (shared across endpoints).");
|
|
1389
|
+
for (const [n, t] of allNested) lines.push(`const ${n} = ${t};`);
|
|
1390
|
+
lines.push("");
|
|
1391
|
+
}
|
|
1392
|
+
lines.push(...decls);
|
|
1393
|
+
lines.push("/** Route name \u2192 body schema map. */");
|
|
1394
|
+
lines.push("export const formSchemas = {");
|
|
1395
|
+
lines.push(...mapEntries);
|
|
1396
|
+
lines.push("} as const;");
|
|
1397
|
+
lines.push("");
|
|
1398
|
+
return lines.join("\n");
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
// src/emit/emit-index.ts
|
|
1402
|
+
var import_promises6 = require("fs/promises");
|
|
1403
|
+
var import_node_path7 = require("path");
|
|
1404
|
+
async function emitIndex(outDir, hasContracts = false, hasForms = false) {
|
|
1405
|
+
await (0, import_promises6.mkdir)(outDir, { recursive: true });
|
|
1406
|
+
const exports2 = ["export * from './pages.js';", "export * from './routes.js';"];
|
|
1407
|
+
if (hasContracts) {
|
|
1408
|
+
exports2.push("export * from './api.js';");
|
|
1409
|
+
}
|
|
1410
|
+
if (hasForms) {
|
|
1411
|
+
exports2.push("export * from './forms.js';");
|
|
1412
|
+
}
|
|
1413
|
+
const content = ["// Generated by @dudousxd/nestjs-codegen. Do not edit.", ...exports2, ""].join(
|
|
1414
|
+
"\n"
|
|
1514
1415
|
);
|
|
1515
|
-
await (0, import_promises6.writeFile)((0,
|
|
1416
|
+
await (0, import_promises6.writeFile)((0, import_node_path7.join)(outDir, "index.d.ts"), content, "utf8");
|
|
1516
1417
|
}
|
|
1517
1418
|
|
|
1518
1419
|
// src/emit/emit-pages.ts
|
|
1519
1420
|
var import_promises7 = require("fs/promises");
|
|
1520
|
-
var
|
|
1421
|
+
var import_node_path8 = require("path");
|
|
1521
1422
|
async function emitPages(pages, outDir, _options = {}) {
|
|
1522
1423
|
await (0, import_promises7.mkdir)(outDir, { recursive: true });
|
|
1523
1424
|
const pageNameUnion = pages.length > 0 ? pages.map((p) => JSON.stringify(p.name)).join(" | ") : "never";
|
|
@@ -1538,7 +1439,7 @@ ${augBody}
|
|
|
1538
1439
|
}
|
|
1539
1440
|
${sharedPropsBlock}}
|
|
1540
1441
|
`;
|
|
1541
|
-
await (0, import_promises7.writeFile)((0,
|
|
1442
|
+
await (0, import_promises7.writeFile)((0, import_node_path8.join)(outDir, "pages.d.ts"), content, "utf8");
|
|
1542
1443
|
}
|
|
1543
1444
|
function buildSharedPropsBlock(sharedProps) {
|
|
1544
1445
|
if (!sharedProps) return "";
|
|
@@ -1557,7 +1458,7 @@ ${propsBody}
|
|
|
1557
1458
|
`;
|
|
1558
1459
|
}
|
|
1559
1460
|
function buildAugmentationType(page, outDir) {
|
|
1560
|
-
let importPath = (0,
|
|
1461
|
+
let importPath = (0, import_node_path8.relative)(outDir, page.absolutePath).replace(/\.(tsx?|vue|svelte)$/, "");
|
|
1561
1462
|
if (!importPath.startsWith(".")) {
|
|
1562
1463
|
importPath = `./${importPath}`;
|
|
1563
1464
|
}
|
|
@@ -1569,11 +1470,11 @@ function needsQuotes(name) {
|
|
|
1569
1470
|
|
|
1570
1471
|
// src/emit/emit-routes.ts
|
|
1571
1472
|
var import_promises8 = require("fs/promises");
|
|
1572
|
-
var
|
|
1473
|
+
var import_node_path9 = require("path");
|
|
1573
1474
|
async function emitRoutes(routes, outDir) {
|
|
1574
1475
|
await (0, import_promises8.mkdir)(outDir, { recursive: true });
|
|
1575
1476
|
const content = buildRoutesFile(routes);
|
|
1576
|
-
await (0, import_promises8.writeFile)((0,
|
|
1477
|
+
await (0, import_promises8.writeFile)((0, import_node_path9.join)(outDir, "routes.ts"), content, "utf8");
|
|
1577
1478
|
}
|
|
1578
1479
|
function buildRoutesFile(routes) {
|
|
1579
1480
|
if (routes.length === 0) {
|
|
@@ -1701,30 +1602,7 @@ async function generate(config, inputRoutes = []) {
|
|
|
1701
1602
|
propsExport: pagesConfig.propsExport,
|
|
1702
1603
|
componentNameStrategy: pagesConfig.componentNameStrategy
|
|
1703
1604
|
});
|
|
1704
|
-
|
|
1705
|
-
if (config.app?.moduleEntry) {
|
|
1706
|
-
try {
|
|
1707
|
-
const tsconfigPath = config.app.tsconfig ?? (0, import_node_path9.join)(config.codegen.cwd, "tsconfig.json");
|
|
1708
|
-
let project;
|
|
1709
|
-
try {
|
|
1710
|
-
project = new import_ts_morph3.Project({
|
|
1711
|
-
tsConfigFilePath: tsconfigPath,
|
|
1712
|
-
skipAddingFilesFromTsConfig: true,
|
|
1713
|
-
skipLoadingLibFiles: true,
|
|
1714
|
-
skipFileDependencyResolution: true
|
|
1715
|
-
});
|
|
1716
|
-
} catch {
|
|
1717
|
-
project = new import_ts_morph3.Project({
|
|
1718
|
-
skipAddingFilesFromTsConfig: true,
|
|
1719
|
-
skipLoadingLibFiles: true,
|
|
1720
|
-
skipFileDependencyResolution: true,
|
|
1721
|
-
compilerOptions: { allowJs: true, strict: false }
|
|
1722
|
-
});
|
|
1723
|
-
}
|
|
1724
|
-
sharedProps = discoverSharedProps(project, config.app.moduleEntry);
|
|
1725
|
-
} catch {
|
|
1726
|
-
}
|
|
1727
|
-
}
|
|
1605
|
+
const sharedProps = discoverSharedPropsFromConfig(config);
|
|
1728
1606
|
await emitPages(pages, config.codegen.outDir, {
|
|
1729
1607
|
propsExport: pagesConfig.propsExport,
|
|
1730
1608
|
sharedProps
|
|
@@ -1748,8 +1626,8 @@ async function generate(config, inputRoutes = []) {
|
|
|
1748
1626
|
if (extensions.length > 0) {
|
|
1749
1627
|
const extraFiles = await collectEmittedFiles(extensions, ctx);
|
|
1750
1628
|
for (const file of extraFiles) {
|
|
1751
|
-
const dest = (0,
|
|
1752
|
-
await (0, import_promises9.mkdir)((0,
|
|
1629
|
+
const dest = (0, import_node_path10.join)(config.codegen.outDir, file.path);
|
|
1630
|
+
await (0, import_promises9.mkdir)((0, import_node_path10.dirname)(dest), { recursive: true });
|
|
1753
1631
|
await (0, import_promises9.writeFile)(dest, file.contents, "utf8");
|
|
1754
1632
|
}
|
|
1755
1633
|
}
|
|
@@ -1757,35 +1635,31 @@ async function generate(config, inputRoutes = []) {
|
|
|
1757
1635
|
|
|
1758
1636
|
// src/watch/watcher.ts
|
|
1759
1637
|
var import_promises12 = require("fs/promises");
|
|
1760
|
-
var
|
|
1638
|
+
var import_node_path14 = require("path");
|
|
1761
1639
|
var import_chokidar = __toESM(require("chokidar"), 1);
|
|
1762
1640
|
|
|
1763
1641
|
// src/discovery/contracts-fast.ts
|
|
1764
|
-
var
|
|
1642
|
+
var import_node_path12 = require("path");
|
|
1765
1643
|
var import_fast_glob2 = __toESM(require("fast-glob"), 1);
|
|
1766
1644
|
var import_ts_morph9 = require("ts-morph");
|
|
1767
1645
|
|
|
1646
|
+
// src/discovery/dto-type-resolver.ts
|
|
1647
|
+
var import_ts_morph7 = require("ts-morph");
|
|
1648
|
+
|
|
1768
1649
|
// src/discovery/dto-to-ir.ts
|
|
1769
|
-
var
|
|
1650
|
+
var import_ts_morph4 = require("ts-morph");
|
|
1770
1651
|
|
|
1771
1652
|
// src/discovery/type-ref-resolution.ts
|
|
1772
1653
|
var import_node_fs = require("fs");
|
|
1773
|
-
var
|
|
1774
|
-
var
|
|
1775
|
-
var
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
return prev;
|
|
1780
|
-
}
|
|
1781
|
-
function restoreDiscoveryContext(ctx) {
|
|
1782
|
-
_ctx = ctx;
|
|
1783
|
-
}
|
|
1784
|
-
function _projectRoot() {
|
|
1785
|
-
return _ctx.projectRoot;
|
|
1654
|
+
var import_node_path11 = require("path");
|
|
1655
|
+
var import_ts_morph3 = require("ts-morph");
|
|
1656
|
+
var _EMPTY_CTX = { projectRoot: "", tsconfigPaths: null };
|
|
1657
|
+
var _ctxByProject = /* @__PURE__ */ new WeakMap();
|
|
1658
|
+
function setDiscoveryContext(project, ctx) {
|
|
1659
|
+
_ctxByProject.set(project, ctx);
|
|
1786
1660
|
}
|
|
1787
|
-
function
|
|
1788
|
-
return
|
|
1661
|
+
function _ctxFor(project) {
|
|
1662
|
+
return _ctxByProject.get(project) ?? _EMPTY_CTX;
|
|
1789
1663
|
}
|
|
1790
1664
|
var _debug = process.env.NESTJS_INERTIA_DEBUG === "1";
|
|
1791
1665
|
function dbg(...args) {
|
|
@@ -1827,18 +1701,19 @@ function findTypeInFile(name, file) {
|
|
|
1827
1701
|
}
|
|
1828
1702
|
return null;
|
|
1829
1703
|
}
|
|
1830
|
-
function resolveModuleSpecifier(moduleSpecifier, sourceFile,
|
|
1704
|
+
function resolveModuleSpecifier(moduleSpecifier, sourceFile, project) {
|
|
1831
1705
|
if (moduleSpecifier.startsWith(".")) {
|
|
1832
|
-
const dir = (0,
|
|
1706
|
+
const dir = (0, import_node_path11.dirname)(sourceFile.getFilePath());
|
|
1833
1707
|
const noExt = moduleSpecifier.replace(/\.(js|ts)$/, "");
|
|
1834
1708
|
return [
|
|
1835
|
-
(0,
|
|
1836
|
-
(0,
|
|
1837
|
-
(0,
|
|
1709
|
+
(0, import_node_path11.resolve)(dir, `${noExt}.ts`),
|
|
1710
|
+
(0, import_node_path11.resolve)(dir, `${moduleSpecifier}.ts`),
|
|
1711
|
+
(0, import_node_path11.resolve)(dir, moduleSpecifier, "index.ts")
|
|
1838
1712
|
];
|
|
1839
1713
|
}
|
|
1840
|
-
const
|
|
1841
|
-
const
|
|
1714
|
+
const ctx = _ctxFor(project);
|
|
1715
|
+
const baseUrl = ctx.projectRoot;
|
|
1716
|
+
const tsconfigPaths = ctx.tsconfigPaths;
|
|
1842
1717
|
dbg(
|
|
1843
1718
|
"resolveModuleSpecifier",
|
|
1844
1719
|
moduleSpecifier,
|
|
@@ -1854,8 +1729,8 @@ function resolveModuleSpecifier(moduleSpecifier, sourceFile, _project) {
|
|
|
1854
1729
|
const rest = moduleSpecifier.slice(prefix.length);
|
|
1855
1730
|
const candidates = [];
|
|
1856
1731
|
for (const mapping of mappings) {
|
|
1857
|
-
const resolved = (0,
|
|
1858
|
-
candidates.push(`${resolved}.ts`, (0,
|
|
1732
|
+
const resolved = (0, import_node_path11.resolve)(baseUrl, mapping.replace("*", rest));
|
|
1733
|
+
candidates.push(`${resolved}.ts`, (0, import_node_path11.resolve)(resolved, "index.ts"));
|
|
1859
1734
|
}
|
|
1860
1735
|
dbg(" resolved candidates:", candidates);
|
|
1861
1736
|
return candidates;
|
|
@@ -1955,13 +1830,13 @@ function resolveTypeRef(nodeOrName, sourceFile, project, opts) {
|
|
|
1955
1830
|
name = nodeOrName;
|
|
1956
1831
|
} else {
|
|
1957
1832
|
const typeNode = nodeOrName;
|
|
1958
|
-
if (opts.unwrapContainers &&
|
|
1833
|
+
if (opts.unwrapContainers && import_ts_morph3.Node.isArrayTypeNode(typeNode)) {
|
|
1959
1834
|
const inner = resolveTypeRef(typeNode.getElementTypeNode(), sourceFile, project, opts);
|
|
1960
1835
|
return inner ? { ...inner, isArray: true } : null;
|
|
1961
1836
|
}
|
|
1962
|
-
if (!
|
|
1837
|
+
if (!import_ts_morph3.Node.isTypeReference(typeNode)) return null;
|
|
1963
1838
|
const typeName = typeNode.getTypeName();
|
|
1964
|
-
const refName =
|
|
1839
|
+
const refName = import_ts_morph3.Node.isIdentifier(typeName) ? typeName.getText() : null;
|
|
1965
1840
|
if (!refName) return null;
|
|
1966
1841
|
if (opts.unwrapContainers && refName === "Promise") {
|
|
1967
1842
|
const first = typeNode.getTypeArguments()[0];
|
|
@@ -1984,7 +1859,7 @@ function resolveTypeRef(nodeOrName, sourceFile, project, opts) {
|
|
|
1984
1859
|
if (!namedImport) continue;
|
|
1985
1860
|
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
1986
1861
|
if (opts.allowBareSpecifier && !moduleSpecifier.startsWith(".") && !moduleSpecifier.startsWith("/")) {
|
|
1987
|
-
const tsconfigPaths =
|
|
1862
|
+
const tsconfigPaths = _ctxFor(project).tsconfigPaths;
|
|
1988
1863
|
const isAlias = tsconfigPaths != null && Object.keys(tsconfigPaths).some((p) => {
|
|
1989
1864
|
const prefix = p.replace("*", "");
|
|
1990
1865
|
return moduleSpecifier.startsWith(prefix);
|
|
@@ -2256,22 +2131,22 @@ function firstArgText(decorator) {
|
|
|
2256
2131
|
}
|
|
2257
2132
|
function numericArg(decorator) {
|
|
2258
2133
|
const arg = firstArg(decorator);
|
|
2259
|
-
if (arg &&
|
|
2134
|
+
if (arg && import_ts_morph4.Node.isNumericLiteral(arg)) return arg.getText();
|
|
2260
2135
|
return null;
|
|
2261
2136
|
}
|
|
2262
2137
|
function numericArgs(decorator) {
|
|
2263
2138
|
const args = decorator?.getArguments() ?? [];
|
|
2264
|
-
const num = (n) => n &&
|
|
2139
|
+
const num = (n) => n && import_ts_morph4.Node.isNumericLiteral(n) ? n.getText() : null;
|
|
2265
2140
|
return [num(args[0]), num(args[1])];
|
|
2266
2141
|
}
|
|
2267
2142
|
function messageRaw(decorator) {
|
|
2268
2143
|
const args = decorator?.getArguments() ?? [];
|
|
2269
2144
|
for (const arg of args) {
|
|
2270
|
-
if (
|
|
2145
|
+
if (import_ts_morph4.Node.isObjectLiteralExpression(arg)) {
|
|
2271
2146
|
for (const prop of arg.getProperties()) {
|
|
2272
|
-
if (
|
|
2147
|
+
if (import_ts_morph4.Node.isPropertyAssignment(prop) && prop.getName() === "message") {
|
|
2273
2148
|
const init = prop.getInitializer();
|
|
2274
|
-
if (init &&
|
|
2149
|
+
if (init && import_ts_morph4.Node.isStringLiteral(init)) return init.getText();
|
|
2275
2150
|
}
|
|
2276
2151
|
}
|
|
2277
2152
|
}
|
|
@@ -2281,9 +2156,9 @@ function messageRaw(decorator) {
|
|
|
2281
2156
|
function resolveTypeFactoryName(decorator) {
|
|
2282
2157
|
const arg = firstArg(decorator);
|
|
2283
2158
|
if (!arg) return null;
|
|
2284
|
-
if (
|
|
2159
|
+
if (import_ts_morph4.Node.isArrowFunction(arg)) {
|
|
2285
2160
|
const body = arg.getBody();
|
|
2286
|
-
if (
|
|
2161
|
+
if (import_ts_morph4.Node.isIdentifier(body)) return body.getText();
|
|
2287
2162
|
}
|
|
2288
2163
|
return null;
|
|
2289
2164
|
}
|
|
@@ -2294,7 +2169,7 @@ function singularClassName(typeText) {
|
|
|
2294
2169
|
function enumSchemaFromDecorator(decorator, classFile, ctx) {
|
|
2295
2170
|
const arg = firstArg(decorator);
|
|
2296
2171
|
if (!arg) return null;
|
|
2297
|
-
if (
|
|
2172
|
+
if (import_ts_morph4.Node.isIdentifier(arg)) {
|
|
2298
2173
|
const name = arg.getText();
|
|
2299
2174
|
const resolved = findType(name, classFile, ctx.project);
|
|
2300
2175
|
if (resolved && resolved.kind === "enum") {
|
|
@@ -2308,12 +2183,12 @@ function enumSchemaFromDecorator(decorator, classFile, ctx) {
|
|
|
2308
2183
|
}
|
|
2309
2184
|
return { kind: "unknown", note: `@IsEnum(${name}): enum not resolvable to literals` };
|
|
2310
2185
|
}
|
|
2311
|
-
if (
|
|
2186
|
+
if (import_ts_morph4.Node.isObjectLiteralExpression(arg)) {
|
|
2312
2187
|
const values = [];
|
|
2313
2188
|
for (const p of arg.getProperties()) {
|
|
2314
|
-
if (!
|
|
2189
|
+
if (!import_ts_morph4.Node.isPropertyAssignment(p)) continue;
|
|
2315
2190
|
const init = p.getInitializer();
|
|
2316
|
-
if (init &&
|
|
2191
|
+
if (init && import_ts_morph4.Node.isStringLiteral(init)) values.push(init.getText());
|
|
2317
2192
|
}
|
|
2318
2193
|
if (values.length > 0) return { kind: "enum", literals: values };
|
|
2319
2194
|
}
|
|
@@ -2321,9 +2196,9 @@ function enumSchemaFromDecorator(decorator, classFile, ctx) {
|
|
|
2321
2196
|
}
|
|
2322
2197
|
function inSchemaFromDecorator(decorator) {
|
|
2323
2198
|
const arg = firstArg(decorator);
|
|
2324
|
-
if (arg &&
|
|
2199
|
+
if (arg && import_ts_morph4.Node.isArrayLiteralExpression(arg)) {
|
|
2325
2200
|
const elements = arg.getElements();
|
|
2326
|
-
const allStrings = elements.every((e) =>
|
|
2201
|
+
const allStrings = elements.every((e) => import_ts_morph4.Node.isStringLiteral(e));
|
|
2327
2202
|
if (allStrings && elements.length > 0) {
|
|
2328
2203
|
return { kind: "enum", literals: elements.map((e) => e.getText()) };
|
|
2329
2204
|
}
|
|
@@ -2337,458 +2212,130 @@ function inSchemaFromDecorator(decorator) {
|
|
|
2337
2212
|
return null;
|
|
2338
2213
|
}
|
|
2339
2214
|
|
|
2340
|
-
// src/discovery/
|
|
2215
|
+
// src/discovery/filter-for.ts
|
|
2341
2216
|
var import_ts_morph6 = require("ts-morph");
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
"
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
"Matches",
|
|
2359
|
-
"IsEnum",
|
|
2360
|
-
"IsIn",
|
|
2361
|
-
"IsOptional",
|
|
2362
|
-
"IsNotEmpty",
|
|
2363
|
-
"IsArray",
|
|
2364
|
-
"ValidateNested",
|
|
2365
|
-
"Type",
|
|
2366
|
-
"IsObject",
|
|
2367
|
-
"Allow",
|
|
2368
|
-
"IsDefined"
|
|
2369
|
-
]);
|
|
2370
|
-
function extractZodFromDto(classDecl, sourceFile, project) {
|
|
2371
|
-
const ctx = {
|
|
2372
|
-
sourceFile,
|
|
2373
|
-
project,
|
|
2374
|
-
namedNestedSchemas: /* @__PURE__ */ new Map(),
|
|
2375
|
-
warnings: [],
|
|
2376
|
-
warnedDecorators: /* @__PURE__ */ new Set(),
|
|
2377
|
-
emittedClasses: /* @__PURE__ */ new Map(),
|
|
2378
|
-
visiting: /* @__PURE__ */ new Set(),
|
|
2379
|
-
recursiveSchemas: /* @__PURE__ */ new Set(),
|
|
2380
|
-
depth: 0
|
|
2381
|
-
};
|
|
2382
|
-
const schemaText = buildObjectSchema(classDecl, sourceFile, ctx);
|
|
2383
|
-
for (const schemaName of ctx.recursiveSchemas) {
|
|
2384
|
-
ctx.namedNestedSchemas.set(schemaName, "z.unknown() /* recursive type \u2014 not expanded */");
|
|
2385
|
-
}
|
|
2386
|
-
return {
|
|
2387
|
-
schemaText,
|
|
2388
|
-
namedNestedSchemas: ctx.namedNestedSchemas,
|
|
2389
|
-
warnings: ctx.warnings
|
|
2390
|
-
};
|
|
2217
|
+
|
|
2218
|
+
// src/discovery/filter-field-types.ts
|
|
2219
|
+
var import_ts_morph5 = require("ts-morph");
|
|
2220
|
+
|
|
2221
|
+
// src/discovery/enum-resolution.ts
|
|
2222
|
+
function resolveEnumValues(name, sourceFile, project) {
|
|
2223
|
+
const resolved = findType(name, sourceFile, project);
|
|
2224
|
+
if (!resolved || resolved.kind !== "enum") return null;
|
|
2225
|
+
let numeric = true;
|
|
2226
|
+
const values = resolved.members.map((m) => {
|
|
2227
|
+
const parsed = JSON.parse(m);
|
|
2228
|
+
if (typeof parsed === "string") numeric = false;
|
|
2229
|
+
return String(parsed);
|
|
2230
|
+
});
|
|
2231
|
+
if (values.length === 0) return null;
|
|
2232
|
+
return { values, numeric };
|
|
2391
2233
|
}
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2234
|
+
|
|
2235
|
+
// src/discovery/filter-field-types.ts
|
|
2236
|
+
var STRING_TYPE_KEYWORDS = ["varchar", "text", "string", "char", "uuid", "enum"];
|
|
2237
|
+
var NUMBER_TYPE_KEYWORDS = ["int", "float", "double", "decimal", "number", "numeric", "real"];
|
|
2238
|
+
var BOOLEAN_TYPE_KEYWORDS = ["bool", "boolean", "bit"];
|
|
2239
|
+
var DATE_TYPE_KEYWORDS = ["date", "time", "timestamp", "datetime"];
|
|
2240
|
+
var JSON_TYPE_KEYWORDS = ["json", "jsonb"];
|
|
2241
|
+
function classifyTypeKeyword(raw) {
|
|
2242
|
+
const t = raw.toLowerCase();
|
|
2243
|
+
if (STRING_TYPE_KEYWORDS.some((s) => t.includes(s))) return "string";
|
|
2244
|
+
if (NUMBER_TYPE_KEYWORDS.some((s) => t.includes(s))) return "number";
|
|
2245
|
+
if (BOOLEAN_TYPE_KEYWORDS.some((s) => t.includes(s))) return "boolean";
|
|
2246
|
+
if (DATE_TYPE_KEYWORDS.some((s) => t.includes(s))) return "date";
|
|
2247
|
+
if (JSON_TYPE_KEYWORDS.some((s) => t.includes(s))) return "json";
|
|
2248
|
+
return null;
|
|
2404
2249
|
}
|
|
2405
|
-
function
|
|
2406
|
-
return
|
|
2250
|
+
function markNullable(r, nullable) {
|
|
2251
|
+
return nullable ? { ...r, nullable: true } : r;
|
|
2407
2252
|
}
|
|
2408
|
-
function
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
if (has("IsInt")) base = "z.number().int()";
|
|
2435
|
-
if (has("IsObject") && !has("ValidateNested")) base = "z.object({}).passthrough()";
|
|
2436
|
-
if (has("Allow")) base = "z.unknown()";
|
|
2437
|
-
if (has("IsEmail")) {
|
|
2438
|
-
base = ensureStringBase(base);
|
|
2439
|
-
refinements.push(`.email(${messageArg(dec("IsEmail"))})`);
|
|
2440
|
-
}
|
|
2441
|
-
if (has("IsUrl")) {
|
|
2442
|
-
base = ensureStringBase(base);
|
|
2443
|
-
refinements.push(`.url(${messageArg(dec("IsUrl"))})`);
|
|
2444
|
-
}
|
|
2445
|
-
if (has("IsUUID")) {
|
|
2446
|
-
base = ensureStringBase(base);
|
|
2447
|
-
refinements.push(`.uuid(${messageArg(dec("IsUUID"))})`);
|
|
2448
|
-
}
|
|
2449
|
-
if (has("Matches")) {
|
|
2450
|
-
const re = firstArgText2(dec("Matches"));
|
|
2451
|
-
if (re) {
|
|
2452
|
-
base = ensureStringBase(base);
|
|
2453
|
-
refinements.push(`.regex(${re})`);
|
|
2454
|
-
}
|
|
2455
|
-
}
|
|
2456
|
-
if (has("MinLength")) {
|
|
2457
|
-
const n = numericArg2(dec("MinLength"));
|
|
2458
|
-
if (n !== null) refinements.push(`.min(${n})`);
|
|
2459
|
-
}
|
|
2460
|
-
if (has("MaxLength")) {
|
|
2461
|
-
const n = numericArg2(dec("MaxLength"));
|
|
2462
|
-
if (n !== null) refinements.push(`.max(${n})`);
|
|
2463
|
-
}
|
|
2464
|
-
if (has("Length")) {
|
|
2465
|
-
const [min, max] = numericArgs2(dec("Length"));
|
|
2466
|
-
if (min !== null) refinements.push(`.min(${min})`);
|
|
2467
|
-
if (max !== null) refinements.push(`.max(${max})`);
|
|
2468
|
-
}
|
|
2469
|
-
if (has("Min")) {
|
|
2470
|
-
const n = numericArg2(dec("Min"));
|
|
2471
|
-
if (n !== null) refinements.push(`.min(${n})`);
|
|
2472
|
-
}
|
|
2473
|
-
if (has("Max")) {
|
|
2474
|
-
const n = numericArg2(dec("Max"));
|
|
2475
|
-
if (n !== null) refinements.push(`.max(${n})`);
|
|
2476
|
-
}
|
|
2477
|
-
if (has("IsPositive")) refinements.push(".positive()");
|
|
2478
|
-
if (has("IsNegative")) refinements.push(".negative()");
|
|
2479
|
-
if (has("IsNotEmpty") && isStringBase(base)) refinements.push(".min(1)");
|
|
2480
|
-
if (has("IsEnum")) {
|
|
2481
|
-
const enumExpr = enumSchemaFromDecorator2(dec("IsEnum"), classFile, ctx);
|
|
2482
|
-
if (enumExpr) base = enumExpr;
|
|
2483
|
-
}
|
|
2484
|
-
if (has("IsIn")) {
|
|
2485
|
-
const inExpr = inSchemaFromDecorator2(dec("IsIn"));
|
|
2486
|
-
if (inExpr) base = inExpr;
|
|
2487
|
-
}
|
|
2488
|
-
for (const name of decorators.keys()) {
|
|
2489
|
-
if (!KNOWN_DECORATORS2.has(name)) {
|
|
2490
|
-
comments.push(`/* @${name}: not translatable to zod (server-only) */`);
|
|
2491
|
-
if (!ctx.warnedDecorators.has(name)) {
|
|
2492
|
-
ctx.warnedDecorators.add(name);
|
|
2493
|
-
const msg = `@${name} is not translatable to zod and was skipped (server-only validation).`;
|
|
2494
|
-
ctx.warnings.push(msg);
|
|
2495
|
-
console.warn(`[nestjs-codegen/forms] ${msg}`);
|
|
2253
|
+
function classifyTypeNode(typeNode, sourceFile, project, opts) {
|
|
2254
|
+
if (import_ts_morph5.Node.isUnionTypeNode(typeNode)) {
|
|
2255
|
+
let nullable = false;
|
|
2256
|
+
const stringLits = [];
|
|
2257
|
+
const numberLits = [];
|
|
2258
|
+
const others = [];
|
|
2259
|
+
for (const member of typeNode.getTypeNodes()) {
|
|
2260
|
+
const kind = member.getKind();
|
|
2261
|
+
if (kind === import_ts_morph5.SyntaxKind.NullKeyword || kind === import_ts_morph5.SyntaxKind.UndefinedKeyword) {
|
|
2262
|
+
nullable = true;
|
|
2263
|
+
continue;
|
|
2264
|
+
}
|
|
2265
|
+
if (import_ts_morph5.Node.isLiteralTypeNode(member)) {
|
|
2266
|
+
const lit = member.getLiteral();
|
|
2267
|
+
if (import_ts_morph5.Node.isStringLiteral(lit)) {
|
|
2268
|
+
stringLits.push(lit.getLiteralValue());
|
|
2269
|
+
continue;
|
|
2270
|
+
}
|
|
2271
|
+
if (import_ts_morph5.Node.isNumericLiteral(lit)) {
|
|
2272
|
+
numberLits.push(lit.getText());
|
|
2273
|
+
continue;
|
|
2274
|
+
}
|
|
2275
|
+
if (lit.getKind() === import_ts_morph5.SyntaxKind.NullKeyword) {
|
|
2276
|
+
nullable = true;
|
|
2277
|
+
continue;
|
|
2278
|
+
}
|
|
2496
2279
|
}
|
|
2280
|
+
others.push(member);
|
|
2281
|
+
}
|
|
2282
|
+
if (others.length === 0 && stringLits.length > 0 && numberLits.length === 0) {
|
|
2283
|
+
return markNullable({ kind: "string", enumValues: stringLits }, nullable);
|
|
2284
|
+
}
|
|
2285
|
+
if (others.length === 0 && numberLits.length > 0 && stringLits.length === 0) {
|
|
2286
|
+
return markNullable({ kind: "number", enumValues: numberLits, numericEnum: true }, nullable);
|
|
2497
2287
|
}
|
|
2288
|
+
if (others.length === 1) {
|
|
2289
|
+
const inner = classifyTypeNode(others[0], sourceFile, project, opts);
|
|
2290
|
+
return markNullable(inner, nullable || inner.nullable === true);
|
|
2291
|
+
}
|
|
2292
|
+
return markNullable({ kind: "unknown" }, nullable);
|
|
2498
2293
|
}
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2294
|
+
switch (typeNode.getKind()) {
|
|
2295
|
+
case import_ts_morph5.SyntaxKind.StringKeyword:
|
|
2296
|
+
return { kind: "string" };
|
|
2297
|
+
case import_ts_morph5.SyntaxKind.NumberKeyword:
|
|
2298
|
+
return { kind: "number" };
|
|
2299
|
+
case import_ts_morph5.SyntaxKind.BooleanKeyword:
|
|
2300
|
+
return { kind: "boolean" };
|
|
2301
|
+
case import_ts_morph5.SyntaxKind.AnyKeyword:
|
|
2302
|
+
case import_ts_morph5.SyntaxKind.UnknownKeyword:
|
|
2303
|
+
return { kind: "unknown" };
|
|
2304
|
+
default:
|
|
2305
|
+
break;
|
|
2502
2306
|
}
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2307
|
+
if (import_ts_morph5.Node.isTypeReference(typeNode)) {
|
|
2308
|
+
const refName = typeNode.getTypeName().getText();
|
|
2309
|
+
if (refName === "Date") return { kind: "date" };
|
|
2310
|
+
if (refName === "Record" || refName === "Object") return { kind: "json" };
|
|
2311
|
+
const typeRef = opts?.resolveRef?.(refName) ?? null;
|
|
2312
|
+
const en = resolveEnumValues(refName, sourceFile, project);
|
|
2313
|
+
if (en) {
|
|
2314
|
+
const base = en.numeric ? { kind: "number", enumValues: en.values, numericEnum: true } : { kind: "string", enumValues: en.values };
|
|
2315
|
+
return typeRef ? { ...base, typeRef } : base;
|
|
2316
|
+
}
|
|
2317
|
+
if (typeRef) return { kind: "unknown", typeRef };
|
|
2318
|
+
return { kind: "unknown" };
|
|
2506
2319
|
}
|
|
2507
|
-
return
|
|
2508
|
-
}
|
|
2509
|
-
function applyPresence2(expr, decorators) {
|
|
2510
|
-
if (decorators.has("IsDefined")) return expr;
|
|
2511
|
-
if (decorators.has("IsOptional")) return `${expr}.optional()`;
|
|
2512
|
-
return expr;
|
|
2513
|
-
}
|
|
2514
|
-
function baseFromType2(typeText, isArrayType, ctx, classFile) {
|
|
2515
|
-
const inner = isArrayType ? typeText.slice(0, -2).trim() : typeText;
|
|
2516
|
-
switch (inner) {
|
|
2517
|
-
case "string":
|
|
2518
|
-
return "z.string()";
|
|
2519
|
-
case "number":
|
|
2520
|
-
return "z.number()";
|
|
2521
|
-
case "boolean":
|
|
2522
|
-
return "z.boolean()";
|
|
2523
|
-
case "Date":
|
|
2524
|
-
return "z.coerce.date()";
|
|
2525
|
-
case "File":
|
|
2526
|
-
case "Express.Multer.File":
|
|
2527
|
-
return "z.instanceof(File)";
|
|
2528
|
-
default:
|
|
2529
|
-
return "z.unknown()";
|
|
2530
|
-
}
|
|
2531
|
-
}
|
|
2532
|
-
function ensureStringBase(base) {
|
|
2533
|
-
return isStringBase(base) ? base : "z.string()";
|
|
2534
|
-
}
|
|
2535
|
-
function isStringBase(base) {
|
|
2536
|
-
return base.startsWith("z.string(");
|
|
2537
|
-
}
|
|
2538
|
-
function buildNestedReference2(className, fromFile, ctx) {
|
|
2539
|
-
if (ctx.visiting.has(className) || ctx.depth >= 8) {
|
|
2540
|
-
const reserved = ctx.emittedClasses.get(className) ?? aliasFor2(className, ctx);
|
|
2541
|
-
ctx.emittedClasses.set(className, reserved);
|
|
2542
|
-
ctx.recursiveSchemas.add(reserved);
|
|
2543
|
-
if (!ctx.warnedDecorators.has(`recursive:${reserved}`)) {
|
|
2544
|
-
ctx.warnedDecorators.add(`recursive:${reserved}`);
|
|
2545
|
-
const msg = `${className} is a recursive type and was not expanded; the generated form schema uses z.unknown() for it.`;
|
|
2546
|
-
ctx.warnings.push(msg);
|
|
2547
|
-
console.warn(`[nestjs-codegen/forms] ${msg}`);
|
|
2548
|
-
}
|
|
2549
|
-
return `z.lazy(() => ${reserved})`;
|
|
2550
|
-
}
|
|
2551
|
-
const existing = ctx.emittedClasses.get(className);
|
|
2552
|
-
if (existing) return existing;
|
|
2553
|
-
const schemaName = aliasFor2(className, ctx);
|
|
2554
|
-
const resolved = findType(className, fromFile, ctx.project);
|
|
2555
|
-
if (!resolved || resolved.kind !== "class") {
|
|
2556
|
-
return "z.object({}).passthrough()";
|
|
2557
|
-
}
|
|
2558
|
-
ctx.emittedClasses.set(className, schemaName);
|
|
2559
|
-
ctx.visiting.add(className);
|
|
2560
|
-
ctx.depth += 1;
|
|
2561
|
-
const childText = buildObjectSchema(resolved.decl, resolved.file, ctx);
|
|
2562
|
-
ctx.depth -= 1;
|
|
2563
|
-
ctx.visiting.delete(className);
|
|
2564
|
-
ctx.namedNestedSchemas.set(schemaName, childText);
|
|
2565
|
-
return schemaName;
|
|
2566
|
-
}
|
|
2567
|
-
function aliasFor2(className, ctx) {
|
|
2568
|
-
const baseName = `${className}Schema`;
|
|
2569
|
-
let candidate = baseName;
|
|
2570
|
-
let i = 1;
|
|
2571
|
-
const used = new Set(ctx.namedNestedSchemas.keys());
|
|
2572
|
-
for (const v of ctx.emittedClasses.values()) used.add(v);
|
|
2573
|
-
while (used.has(candidate)) {
|
|
2574
|
-
candidate = `${baseName}_${i}`;
|
|
2575
|
-
i += 1;
|
|
2576
|
-
}
|
|
2577
|
-
return candidate;
|
|
2578
|
-
}
|
|
2579
|
-
function firstArg2(decorator) {
|
|
2580
|
-
return decorator?.getArguments()[0];
|
|
2581
|
-
}
|
|
2582
|
-
function firstArgText2(decorator) {
|
|
2583
|
-
const arg = firstArg2(decorator);
|
|
2584
|
-
return arg ? arg.getText() : null;
|
|
2585
|
-
}
|
|
2586
|
-
function numericArg2(decorator) {
|
|
2587
|
-
const arg = firstArg2(decorator);
|
|
2588
|
-
if (arg && import_ts_morph6.Node.isNumericLiteral(arg)) return arg.getText();
|
|
2589
|
-
return null;
|
|
2590
|
-
}
|
|
2591
|
-
function numericArgs2(decorator) {
|
|
2592
|
-
const args = decorator?.getArguments() ?? [];
|
|
2593
|
-
const num = (n) => n && import_ts_morph6.Node.isNumericLiteral(n) ? n.getText() : null;
|
|
2594
|
-
return [num(args[0]), num(args[1])];
|
|
2595
|
-
}
|
|
2596
|
-
function messageArg(decorator) {
|
|
2597
|
-
const args = decorator?.getArguments() ?? [];
|
|
2598
|
-
for (const arg of args) {
|
|
2599
|
-
if (import_ts_morph6.Node.isObjectLiteralExpression(arg)) {
|
|
2600
|
-
for (const prop of arg.getProperties()) {
|
|
2601
|
-
if (import_ts_morph6.Node.isPropertyAssignment(prop) && prop.getName() === "message") {
|
|
2602
|
-
const init = prop.getInitializer();
|
|
2603
|
-
if (init && import_ts_morph6.Node.isStringLiteral(init)) {
|
|
2604
|
-
return `{ message: ${init.getText()} }`;
|
|
2605
|
-
}
|
|
2606
|
-
}
|
|
2607
|
-
}
|
|
2608
|
-
}
|
|
2609
|
-
}
|
|
2610
|
-
return "";
|
|
2611
|
-
}
|
|
2612
|
-
function resolveTypeFactoryName2(decorator) {
|
|
2613
|
-
const arg = firstArg2(decorator);
|
|
2614
|
-
if (!arg) return null;
|
|
2615
|
-
if (import_ts_morph6.Node.isArrowFunction(arg)) {
|
|
2616
|
-
const body = arg.getBody();
|
|
2617
|
-
if (import_ts_morph6.Node.isIdentifier(body)) return body.getText();
|
|
2618
|
-
}
|
|
2619
|
-
return null;
|
|
2620
|
-
}
|
|
2621
|
-
function singularClassName2(typeText) {
|
|
2622
|
-
const inner = typeText.endsWith("[]") ? typeText.slice(0, -2).trim() : typeText;
|
|
2623
|
-
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(inner) ? inner : null;
|
|
2624
|
-
}
|
|
2625
|
-
function enumSchemaFromDecorator2(decorator, classFile, ctx) {
|
|
2626
|
-
const arg = firstArg2(decorator);
|
|
2627
|
-
if (!arg) return null;
|
|
2628
|
-
if (import_ts_morph6.Node.isIdentifier(arg)) {
|
|
2629
|
-
const name = arg.getText();
|
|
2630
|
-
const resolved = findType(name, classFile, ctx.project);
|
|
2631
|
-
if (resolved && resolved.kind === "enum") {
|
|
2632
|
-
return `z.enum([${resolved.members.join(", ")}])`;
|
|
2633
|
-
}
|
|
2634
|
-
const msg = `@IsEnum(${name}): enum could not be resolved to literal members and is not importable into the generated form schema; falling back to z.unknown().`;
|
|
2635
|
-
if (!ctx.warnedDecorators.has(`IsEnum:${name}`)) {
|
|
2636
|
-
ctx.warnedDecorators.add(`IsEnum:${name}`);
|
|
2637
|
-
ctx.warnings.push(msg);
|
|
2638
|
-
console.warn(`[nestjs-codegen/forms] ${msg}`);
|
|
2639
|
-
}
|
|
2640
|
-
return `z.unknown() /* @IsEnum(${name}): enum not resolvable to literals */`;
|
|
2641
|
-
}
|
|
2642
|
-
if (import_ts_morph6.Node.isObjectLiteralExpression(arg)) {
|
|
2643
|
-
const values = [];
|
|
2644
|
-
for (const p of arg.getProperties()) {
|
|
2645
|
-
if (!import_ts_morph6.Node.isPropertyAssignment(p)) continue;
|
|
2646
|
-
const init = p.getInitializer();
|
|
2647
|
-
if (init && import_ts_morph6.Node.isStringLiteral(init)) values.push(init.getText());
|
|
2648
|
-
}
|
|
2649
|
-
if (values.length > 0) return `z.enum([${values.join(", ")}])`;
|
|
2650
|
-
}
|
|
2651
|
-
return null;
|
|
2652
|
-
}
|
|
2653
|
-
function inSchemaFromDecorator2(decorator) {
|
|
2654
|
-
const arg = firstArg2(decorator);
|
|
2655
|
-
if (arg && import_ts_morph6.Node.isArrayLiteralExpression(arg)) {
|
|
2656
|
-
const elements = arg.getElements();
|
|
2657
|
-
const allStrings = elements.every((e) => import_ts_morph6.Node.isStringLiteral(e));
|
|
2658
|
-
if (allStrings && elements.length > 0) {
|
|
2659
|
-
return `z.enum([${elements.map((e) => e.getText()).join(", ")}])`;
|
|
2660
|
-
}
|
|
2661
|
-
if (elements.length > 0) {
|
|
2662
|
-
return `z.union([${elements.map((e) => `z.literal(${e.getText()})`).join(", ")}])`;
|
|
2663
|
-
}
|
|
2664
|
-
}
|
|
2665
|
-
return null;
|
|
2666
|
-
}
|
|
2667
|
-
|
|
2668
|
-
// src/discovery/filter-for.ts
|
|
2669
|
-
var import_ts_morph8 = require("ts-morph");
|
|
2670
|
-
|
|
2671
|
-
// src/discovery/filter-field-types.ts
|
|
2672
|
-
var import_ts_morph7 = require("ts-morph");
|
|
2673
|
-
|
|
2674
|
-
// src/discovery/enum-resolution.ts
|
|
2675
|
-
function resolveEnumValues(name, sourceFile, project) {
|
|
2676
|
-
const resolved = findType(name, sourceFile, project);
|
|
2677
|
-
if (!resolved || resolved.kind !== "enum") return null;
|
|
2678
|
-
let numeric = true;
|
|
2679
|
-
const values = resolved.members.map((m) => {
|
|
2680
|
-
const parsed = JSON.parse(m);
|
|
2681
|
-
if (typeof parsed === "string") numeric = false;
|
|
2682
|
-
return String(parsed);
|
|
2683
|
-
});
|
|
2684
|
-
if (values.length === 0) return null;
|
|
2685
|
-
return { values, numeric };
|
|
2686
|
-
}
|
|
2687
|
-
|
|
2688
|
-
// src/discovery/filter-field-types.ts
|
|
2689
|
-
var STRING_TYPE_KEYWORDS = ["varchar", "text", "string", "char", "uuid", "enum"];
|
|
2690
|
-
var NUMBER_TYPE_KEYWORDS = ["int", "float", "double", "decimal", "number", "numeric", "real"];
|
|
2691
|
-
var BOOLEAN_TYPE_KEYWORDS = ["bool", "boolean", "bit"];
|
|
2692
|
-
var DATE_TYPE_KEYWORDS = ["date", "time", "timestamp", "datetime"];
|
|
2693
|
-
var JSON_TYPE_KEYWORDS = ["json", "jsonb"];
|
|
2694
|
-
function classifyTypeKeyword(raw) {
|
|
2695
|
-
const t = raw.toLowerCase();
|
|
2696
|
-
if (STRING_TYPE_KEYWORDS.some((s) => t.includes(s))) return "string";
|
|
2697
|
-
if (NUMBER_TYPE_KEYWORDS.some((s) => t.includes(s))) return "number";
|
|
2698
|
-
if (BOOLEAN_TYPE_KEYWORDS.some((s) => t.includes(s))) return "boolean";
|
|
2699
|
-
if (DATE_TYPE_KEYWORDS.some((s) => t.includes(s))) return "date";
|
|
2700
|
-
if (JSON_TYPE_KEYWORDS.some((s) => t.includes(s))) return "json";
|
|
2701
|
-
return null;
|
|
2702
|
-
}
|
|
2703
|
-
function markNullable(r, nullable) {
|
|
2704
|
-
return nullable ? { ...r, nullable: true } : r;
|
|
2705
|
-
}
|
|
2706
|
-
function classifyTypeNode(typeNode, sourceFile, project, opts) {
|
|
2707
|
-
if (import_ts_morph7.Node.isUnionTypeNode(typeNode)) {
|
|
2708
|
-
let nullable = false;
|
|
2709
|
-
const stringLits = [];
|
|
2710
|
-
const numberLits = [];
|
|
2711
|
-
const others = [];
|
|
2712
|
-
for (const member of typeNode.getTypeNodes()) {
|
|
2713
|
-
const kind = member.getKind();
|
|
2714
|
-
if (kind === import_ts_morph7.SyntaxKind.NullKeyword || kind === import_ts_morph7.SyntaxKind.UndefinedKeyword) {
|
|
2715
|
-
nullable = true;
|
|
2716
|
-
continue;
|
|
2717
|
-
}
|
|
2718
|
-
if (import_ts_morph7.Node.isLiteralTypeNode(member)) {
|
|
2719
|
-
const lit = member.getLiteral();
|
|
2720
|
-
if (import_ts_morph7.Node.isStringLiteral(lit)) {
|
|
2721
|
-
stringLits.push(lit.getLiteralValue());
|
|
2722
|
-
continue;
|
|
2723
|
-
}
|
|
2724
|
-
if (import_ts_morph7.Node.isNumericLiteral(lit)) {
|
|
2725
|
-
numberLits.push(lit.getText());
|
|
2726
|
-
continue;
|
|
2727
|
-
}
|
|
2728
|
-
if (lit.getKind() === import_ts_morph7.SyntaxKind.NullKeyword) {
|
|
2729
|
-
nullable = true;
|
|
2730
|
-
continue;
|
|
2731
|
-
}
|
|
2732
|
-
}
|
|
2733
|
-
others.push(member);
|
|
2734
|
-
}
|
|
2735
|
-
if (others.length === 0 && stringLits.length > 0 && numberLits.length === 0) {
|
|
2736
|
-
return markNullable({ kind: "string", enumValues: stringLits }, nullable);
|
|
2737
|
-
}
|
|
2738
|
-
if (others.length === 0 && numberLits.length > 0 && stringLits.length === 0) {
|
|
2739
|
-
return markNullable({ kind: "number", enumValues: numberLits, numericEnum: true }, nullable);
|
|
2740
|
-
}
|
|
2741
|
-
if (others.length === 1) {
|
|
2742
|
-
const inner = classifyTypeNode(others[0], sourceFile, project, opts);
|
|
2743
|
-
return markNullable(inner, nullable || inner.nullable === true);
|
|
2744
|
-
}
|
|
2745
|
-
return markNullable({ kind: "unknown" }, nullable);
|
|
2746
|
-
}
|
|
2747
|
-
switch (typeNode.getKind()) {
|
|
2748
|
-
case import_ts_morph7.SyntaxKind.StringKeyword:
|
|
2749
|
-
return { kind: "string" };
|
|
2750
|
-
case import_ts_morph7.SyntaxKind.NumberKeyword:
|
|
2751
|
-
return { kind: "number" };
|
|
2752
|
-
case import_ts_morph7.SyntaxKind.BooleanKeyword:
|
|
2753
|
-
return { kind: "boolean" };
|
|
2754
|
-
case import_ts_morph7.SyntaxKind.AnyKeyword:
|
|
2755
|
-
case import_ts_morph7.SyntaxKind.UnknownKeyword:
|
|
2756
|
-
return { kind: "unknown" };
|
|
2757
|
-
default:
|
|
2758
|
-
break;
|
|
2759
|
-
}
|
|
2760
|
-
if (import_ts_morph7.Node.isTypeReference(typeNode)) {
|
|
2761
|
-
const refName = typeNode.getTypeName().getText();
|
|
2762
|
-
if (refName === "Date") return { kind: "date" };
|
|
2763
|
-
if (refName === "Record" || refName === "Object") return { kind: "json" };
|
|
2764
|
-
const typeRef = opts?.resolveRef?.(refName) ?? null;
|
|
2765
|
-
const en = resolveEnumValues(refName, sourceFile, project);
|
|
2766
|
-
if (en) {
|
|
2767
|
-
const base = en.numeric ? { kind: "number", enumValues: en.values, numericEnum: true } : { kind: "string", enumValues: en.values };
|
|
2768
|
-
return typeRef ? { ...base, typeRef } : base;
|
|
2769
|
-
}
|
|
2770
|
-
if (typeRef) return { kind: "unknown", typeRef };
|
|
2771
|
-
return { kind: "unknown" };
|
|
2772
|
-
}
|
|
2773
|
-
if (import_ts_morph7.Node.isTypeLiteral(typeNode)) return { kind: "json" };
|
|
2774
|
-
return { kind: "unknown" };
|
|
2320
|
+
if (import_ts_morph5.Node.isTypeLiteral(typeNode)) return { kind: "json" };
|
|
2321
|
+
return { kind: "unknown" };
|
|
2775
2322
|
}
|
|
2776
2323
|
function enumFromDecoratorArgs(args, sourceFile, project) {
|
|
2777
2324
|
for (const arg of args) {
|
|
2778
|
-
if (
|
|
2325
|
+
if (import_ts_morph5.Node.isArrowFunction(arg)) {
|
|
2779
2326
|
const body = arg.getBody();
|
|
2780
|
-
if (
|
|
2327
|
+
if (import_ts_morph5.Node.isIdentifier(body)) {
|
|
2781
2328
|
const en = resolveEnumValues(body.getText(), sourceFile, project);
|
|
2782
2329
|
if (en) return en;
|
|
2783
2330
|
}
|
|
2784
2331
|
}
|
|
2785
|
-
if (
|
|
2332
|
+
if (import_ts_morph5.Node.isObjectLiteralExpression(arg)) {
|
|
2786
2333
|
const itemsProp = arg.getProperty("items");
|
|
2787
|
-
if (itemsProp &&
|
|
2334
|
+
if (itemsProp && import_ts_morph5.Node.isPropertyAssignment(itemsProp)) {
|
|
2788
2335
|
const init = itemsProp.getInitializer();
|
|
2789
|
-
if (init &&
|
|
2336
|
+
if (init && import_ts_morph5.Node.isArrowFunction(init)) {
|
|
2790
2337
|
const body = init.getBody();
|
|
2791
|
-
if (
|
|
2338
|
+
if (import_ts_morph5.Node.isIdentifier(body)) {
|
|
2792
2339
|
const en = resolveEnumValues(body.getText(), sourceFile, project);
|
|
2793
2340
|
if (en) return en;
|
|
2794
2341
|
}
|
|
@@ -2811,7 +2358,7 @@ function classifyFromColumnDecorator(prop, sourceFile, project) {
|
|
|
2811
2358
|
return { kind: "string" };
|
|
2812
2359
|
}
|
|
2813
2360
|
for (const arg of args) {
|
|
2814
|
-
if (
|
|
2361
|
+
if (import_ts_morph5.Node.isStringLiteral(arg)) {
|
|
2815
2362
|
const raw = arg.getLiteralValue();
|
|
2816
2363
|
const kind = classifyTypeKeyword(raw);
|
|
2817
2364
|
if (kind) {
|
|
@@ -2819,11 +2366,11 @@ function classifyFromColumnDecorator(prop, sourceFile, project) {
|
|
|
2819
2366
|
return { kind };
|
|
2820
2367
|
}
|
|
2821
2368
|
}
|
|
2822
|
-
if (
|
|
2369
|
+
if (import_ts_morph5.Node.isObjectLiteralExpression(arg)) {
|
|
2823
2370
|
const enumProp = arg.getProperty("enum");
|
|
2824
|
-
if (enumProp &&
|
|
2371
|
+
if (enumProp && import_ts_morph5.Node.isPropertyAssignment(enumProp)) {
|
|
2825
2372
|
const init = enumProp.getInitializer();
|
|
2826
|
-
if (init &&
|
|
2373
|
+
if (init && import_ts_morph5.Node.isIdentifier(init)) {
|
|
2827
2374
|
const en = resolveEnumValues(init.getText(), sourceFile, project);
|
|
2828
2375
|
if (en) {
|
|
2829
2376
|
return en.numeric ? { kind: "number", enumValues: en.values, numericEnum: true } : { kind: "string", enumValues: en.values };
|
|
@@ -2832,9 +2379,9 @@ function classifyFromColumnDecorator(prop, sourceFile, project) {
|
|
|
2832
2379
|
}
|
|
2833
2380
|
}
|
|
2834
2381
|
const typeProp = arg.getProperty("type");
|
|
2835
|
-
if (typeProp &&
|
|
2382
|
+
if (typeProp && import_ts_morph5.Node.isPropertyAssignment(typeProp)) {
|
|
2836
2383
|
const init = typeProp.getInitializer();
|
|
2837
|
-
if (init &&
|
|
2384
|
+
if (init && import_ts_morph5.Node.isStringLiteral(init)) {
|
|
2838
2385
|
const kind = classifyTypeKeyword(init.getLiteralValue());
|
|
2839
2386
|
if (kind) return { kind };
|
|
2840
2387
|
}
|
|
@@ -2870,7 +2417,7 @@ function toFilterFieldType(name, r) {
|
|
|
2870
2417
|
|
|
2871
2418
|
// src/discovery/filter-for.ts
|
|
2872
2419
|
function classifyFilterForHint(typeInit) {
|
|
2873
|
-
if (
|
|
2420
|
+
if (import_ts_morph6.Node.isStringLiteral(typeInit)) {
|
|
2874
2421
|
switch (typeInit.getLiteralValue()) {
|
|
2875
2422
|
case "string":
|
|
2876
2423
|
return { kind: "string" };
|
|
@@ -2884,10 +2431,10 @@ function classifyFilterForHint(typeInit) {
|
|
|
2884
2431
|
return null;
|
|
2885
2432
|
}
|
|
2886
2433
|
}
|
|
2887
|
-
if (
|
|
2434
|
+
if (import_ts_morph6.Node.isArrayLiteralExpression(typeInit)) {
|
|
2888
2435
|
const values = [];
|
|
2889
2436
|
for (const el of typeInit.getElements()) {
|
|
2890
|
-
if (!
|
|
2437
|
+
if (!import_ts_morph6.Node.isStringLiteral(el)) return null;
|
|
2891
2438
|
values.push(el.getLiteralValue());
|
|
2892
2439
|
}
|
|
2893
2440
|
if (values.length === 0) return null;
|
|
@@ -2922,11 +2469,11 @@ function extractFilterForHints(classDecl, project) {
|
|
|
2922
2469
|
if (!filterForDec) continue;
|
|
2923
2470
|
const args = filterForDec.getArguments();
|
|
2924
2471
|
const keyArg = args[0];
|
|
2925
|
-
const inputKey = keyArg &&
|
|
2472
|
+
const inputKey = keyArg && import_ts_morph6.Node.isStringLiteral(keyArg) ? keyArg.getLiteralValue() : method.getName();
|
|
2926
2473
|
const optsArg = args[1];
|
|
2927
|
-
if (optsArg &&
|
|
2474
|
+
if (optsArg && import_ts_morph6.Node.isObjectLiteralExpression(optsArg)) {
|
|
2928
2475
|
const typeProp = optsArg.getProperty("type");
|
|
2929
|
-
if (typeProp &&
|
|
2476
|
+
if (typeProp && import_ts_morph6.Node.isPropertyAssignment(typeProp)) {
|
|
2930
2477
|
const typeInit = typeProp.getInitializer();
|
|
2931
2478
|
if (typeInit) {
|
|
2932
2479
|
const classified = classifyFilterForHint(typeInit);
|
|
@@ -2949,14 +2496,14 @@ function extractApplyFilterInfo(method, sourceFile, project) {
|
|
|
2949
2496
|
const args = filterDecorator.getArguments();
|
|
2950
2497
|
if (args.length === 0) continue;
|
|
2951
2498
|
const filterClassArg = args[0];
|
|
2952
|
-
if (!filterClassArg || !
|
|
2499
|
+
if (!filterClassArg || !import_ts_morph6.Node.isIdentifier(filterClassArg)) continue;
|
|
2953
2500
|
let source = "query";
|
|
2954
2501
|
const optionsArg = args[1];
|
|
2955
|
-
if (optionsArg &&
|
|
2502
|
+
if (optionsArg && import_ts_morph6.Node.isObjectLiteralExpression(optionsArg)) {
|
|
2956
2503
|
const sourceProp = optionsArg.getProperty("source");
|
|
2957
|
-
if (sourceProp &&
|
|
2504
|
+
if (sourceProp && import_ts_morph6.Node.isPropertyAssignment(sourceProp)) {
|
|
2958
2505
|
const init = sourceProp.getInitializer();
|
|
2959
|
-
if (init &&
|
|
2506
|
+
if (init && import_ts_morph6.Node.isStringLiteral(init) && init.getLiteralValue() === "body") {
|
|
2960
2507
|
source = "body";
|
|
2961
2508
|
}
|
|
2962
2509
|
}
|
|
@@ -3019,22 +2566,22 @@ function resolveRelationEntity(prop, sourceFile, project) {
|
|
|
3019
2566
|
const args = dec.getArguments();
|
|
3020
2567
|
if (args.length === 0) continue;
|
|
3021
2568
|
const arg = args[0];
|
|
3022
|
-
if (
|
|
2569
|
+
if (import_ts_morph6.Node.isObjectLiteralExpression(arg)) {
|
|
3023
2570
|
const entityProp = arg.getProperty("entity");
|
|
3024
|
-
if (entityProp &&
|
|
2571
|
+
if (entityProp && import_ts_morph6.Node.isPropertyAssignment(entityProp)) {
|
|
3025
2572
|
const init = entityProp.getInitializer();
|
|
3026
|
-
if (init &&
|
|
2573
|
+
if (init && import_ts_morph6.Node.isArrowFunction(init)) {
|
|
3027
2574
|
const body = init.getBody();
|
|
3028
|
-
if (
|
|
2575
|
+
if (import_ts_morph6.Node.isIdentifier(body)) {
|
|
3029
2576
|
const resolved = findType(body.getText(), prop.getSourceFile(), project);
|
|
3030
2577
|
if (resolved?.kind === "class") return resolved.decl;
|
|
3031
2578
|
}
|
|
3032
2579
|
}
|
|
3033
2580
|
}
|
|
3034
2581
|
}
|
|
3035
|
-
if (
|
|
2582
|
+
if (import_ts_morph6.Node.isArrowFunction(arg)) {
|
|
3036
2583
|
const body = arg.getBody();
|
|
3037
|
-
if (
|
|
2584
|
+
if (import_ts_morph6.Node.isIdentifier(body)) {
|
|
3038
2585
|
const resolved = findType(body.getText(), prop.getSourceFile(), project);
|
|
3039
2586
|
if (resolved?.kind === "class") return resolved.decl;
|
|
3040
2587
|
}
|
|
@@ -3058,11 +2605,11 @@ function extractFilterableEntityFields(filterClass, project) {
|
|
|
3058
2605
|
const args = filterableDecorator.getArguments();
|
|
3059
2606
|
if (args.length === 0) return [];
|
|
3060
2607
|
const optionsArg = args[0];
|
|
3061
|
-
if (!
|
|
2608
|
+
if (!import_ts_morph6.Node.isObjectLiteralExpression(optionsArg)) return [];
|
|
3062
2609
|
const entityProp = optionsArg.getProperty("entity");
|
|
3063
|
-
if (!entityProp || !
|
|
2610
|
+
if (!entityProp || !import_ts_morph6.Node.isPropertyAssignment(entityProp)) return [];
|
|
3064
2611
|
const entityInit = entityProp.getInitializer();
|
|
3065
|
-
if (!entityInit || !
|
|
2612
|
+
if (!entityInit || !import_ts_morph6.Node.isIdentifier(entityInit)) return [];
|
|
3066
2613
|
const entityName = entityInit.getText();
|
|
3067
2614
|
const filterSourceFile = filterClass.getSourceFile();
|
|
3068
2615
|
const resolvedEntity = findType(entityName, filterSourceFile, project);
|
|
@@ -3078,17 +2625,17 @@ function extractFilterableEntityFields(filterClass, project) {
|
|
|
3078
2625
|
const relationsDecorator = filterClass.getDecorators().find((d) => d.getName() === "Relations");
|
|
3079
2626
|
if (relationsDecorator) {
|
|
3080
2627
|
const relArgs = relationsDecorator.getArguments();
|
|
3081
|
-
if (relArgs.length > 0 &&
|
|
2628
|
+
if (relArgs.length > 0 && import_ts_morph6.Node.isObjectLiteralExpression(relArgs[0])) {
|
|
3082
2629
|
for (const relProp of relArgs[0].getProperties()) {
|
|
3083
|
-
if (!
|
|
2630
|
+
if (!import_ts_morph6.Node.isPropertyAssignment(relProp)) continue;
|
|
3084
2631
|
const relInit = relProp.getInitializer();
|
|
3085
|
-
if (!relInit || !
|
|
2632
|
+
if (!relInit || !import_ts_morph6.Node.isObjectLiteralExpression(relInit)) continue;
|
|
3086
2633
|
const keysProp = relInit.getProperty("keys");
|
|
3087
|
-
if (!keysProp || !
|
|
2634
|
+
if (!keysProp || !import_ts_morph6.Node.isPropertyAssignment(keysProp)) continue;
|
|
3088
2635
|
const keysInit = keysProp.getInitializer();
|
|
3089
|
-
if (!keysInit || !
|
|
2636
|
+
if (!keysInit || !import_ts_morph6.Node.isArrayLiteralExpression(keysInit)) continue;
|
|
3090
2637
|
for (const el of keysInit.getElements()) {
|
|
3091
|
-
if (
|
|
2638
|
+
if (import_ts_morph6.Node.isStringLiteral(el)) {
|
|
3092
2639
|
fields.push(toFilterFieldType(el.getLiteralValue(), { kind: "unknown" }));
|
|
3093
2640
|
}
|
|
3094
2641
|
}
|
|
@@ -3098,266 +2645,64 @@ function extractFilterableEntityFields(filterClass, project) {
|
|
|
3098
2645
|
return fields;
|
|
3099
2646
|
}
|
|
3100
2647
|
|
|
3101
|
-
// src/discovery/
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
2648
|
+
// src/discovery/dto-type-resolver.ts
|
|
2649
|
+
var WRAPPER_TYPES = {
|
|
2650
|
+
// MikroORM Ref/Reference/LoadedReference/IdentifiedReference are server-side
|
|
2651
|
+
// wrappers around related entities; the wire shape is just the referenced
|
|
2652
|
+
// entity. Unwrap to the type argument.
|
|
2653
|
+
Ref: "unwrap",
|
|
2654
|
+
Reference: "unwrap",
|
|
2655
|
+
LoadedReference: "unwrap",
|
|
2656
|
+
IdentifiedReference: "unwrap",
|
|
2657
|
+
// MikroORM Opt<T> is a marker, Loaded<T, ...> is a wrapper; both reduce to T.
|
|
2658
|
+
Opt: "unwrap",
|
|
2659
|
+
Loaded: "unwrap",
|
|
2660
|
+
// Promise<T> — unwrap
|
|
2661
|
+
Promise: "unwrap",
|
|
2662
|
+
// MikroORM Collection<T> serializes as an array of T on the wire.
|
|
2663
|
+
Collection: "arrayOf",
|
|
2664
|
+
// Array<T> generic form
|
|
2665
|
+
Array: "arrayOf"
|
|
2666
|
+
};
|
|
2667
|
+
var PASSTHROUGH_UTILITY = /* @__PURE__ */ new Set([
|
|
2668
|
+
"Record",
|
|
2669
|
+
"Omit",
|
|
2670
|
+
"Pick",
|
|
2671
|
+
"Partial",
|
|
2672
|
+
"Required",
|
|
2673
|
+
"Readonly",
|
|
2674
|
+
"Map",
|
|
2675
|
+
"Set"
|
|
2676
|
+
]);
|
|
2677
|
+
function resolveTypeNodeToString(typeNode, sourceFile, project, depth) {
|
|
2678
|
+
if (depth <= 0) return "unknown";
|
|
2679
|
+
if (import_ts_morph7.Node.isArrayTypeNode(typeNode)) {
|
|
2680
|
+
const elementType = typeNode.getElementTypeNode();
|
|
2681
|
+
return `Array<${resolveTypeNodeToString(elementType, sourceFile, project, depth)}>`;
|
|
3124
2682
|
}
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
project.addSourceFileAtPath(f);
|
|
2683
|
+
if (import_ts_morph7.Node.isUnionTypeNode(typeNode)) {
|
|
2684
|
+
return typeNode.getTypeNodes().map((t) => resolveTypeNodeToString(t, sourceFile, project, depth)).join(" | ");
|
|
3128
2685
|
}
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
projectRoot: cwd,
|
|
3132
|
-
tsconfigPaths: loadTsconfigPaths(tsconfigPath)
|
|
3133
|
-
});
|
|
3134
|
-
try {
|
|
3135
|
-
for (const sourceFile of project.getSourceFiles()) {
|
|
3136
|
-
routes.push(...extractFromSourceFile(sourceFile, project));
|
|
3137
|
-
}
|
|
3138
|
-
} finally {
|
|
3139
|
-
restoreDiscoveryContext(prevCtx);
|
|
2686
|
+
if (import_ts_morph7.Node.isIntersectionTypeNode(typeNode)) {
|
|
2687
|
+
return typeNode.getTypeNodes().map((t) => resolveTypeNodeToString(t, sourceFile, project, depth)).join(" & ");
|
|
3140
2688
|
}
|
|
3141
|
-
|
|
3142
|
-
}
|
|
3143
|
-
|
|
3144
|
-
if (
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
if (
|
|
3150
|
-
|
|
2689
|
+
if (import_ts_morph7.Node.isParenthesizedTypeNode(typeNode)) {
|
|
2690
|
+
return `(${resolveTypeNodeToString(typeNode.getTypeNode(), sourceFile, project, depth)})`;
|
|
2691
|
+
}
|
|
2692
|
+
if (import_ts_morph7.Node.isTypeReference(typeNode)) {
|
|
2693
|
+
const typeName = typeNode.getTypeName();
|
|
2694
|
+
const name = import_ts_morph7.Node.isIdentifier(typeName) ? typeName.getText() : typeNode.getText();
|
|
2695
|
+
if (name === "string" || name === "number" || name === "boolean") return name;
|
|
2696
|
+
if (name === "Date") return "string";
|
|
2697
|
+
if (name === "unknown" || name === "any" || name === "void") return "unknown";
|
|
2698
|
+
if (name === "StreamableFile" || name === "Observable" || name === "ReadableStream")
|
|
2699
|
+
return "unknown";
|
|
2700
|
+
const wrapperMode = WRAPPER_TYPES[name];
|
|
2701
|
+
if (wrapperMode) {
|
|
2702
|
+
return unwrapFirstTypeArg(typeNode, sourceFile, project, depth, wrapperMode);
|
|
3151
2703
|
}
|
|
3152
|
-
if (
|
|
3153
|
-
return
|
|
3154
|
-
}
|
|
3155
|
-
const args = node.getArguments();
|
|
3156
|
-
switch (methodName) {
|
|
3157
|
-
case "string":
|
|
3158
|
-
return "string";
|
|
3159
|
-
case "number":
|
|
3160
|
-
return "number";
|
|
3161
|
-
case "boolean":
|
|
3162
|
-
return "boolean";
|
|
3163
|
-
case "unknown":
|
|
3164
|
-
return "unknown";
|
|
3165
|
-
case "any":
|
|
3166
|
-
return "unknown";
|
|
3167
|
-
case "literal": {
|
|
3168
|
-
const lit = args[0];
|
|
3169
|
-
if (!lit) return "unknown";
|
|
3170
|
-
if (import_ts_morph9.Node.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
|
|
3171
|
-
if (import_ts_morph9.Node.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
|
|
3172
|
-
if (lit.getKind() === import_ts_morph9.SyntaxKind.TrueKeyword) return "true";
|
|
3173
|
-
if (lit.getKind() === import_ts_morph9.SyntaxKind.FalseKeyword) return "false";
|
|
3174
|
-
return "unknown";
|
|
3175
|
-
}
|
|
3176
|
-
case "enum": {
|
|
3177
|
-
const arrArg = args[0];
|
|
3178
|
-
if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
3179
|
-
const members = arrArg.getElements().map(
|
|
3180
|
-
(el) => import_ts_morph9.Node.isStringLiteral(el) ? JSON.stringify(el.getLiteralValue()) : "unknown"
|
|
3181
|
-
);
|
|
3182
|
-
return members.join(" | ");
|
|
3183
|
-
}
|
|
3184
|
-
case "array": {
|
|
3185
|
-
const inner = args[0];
|
|
3186
|
-
if (!inner) return "unknown";
|
|
3187
|
-
return `Array<${zodAstToTs(inner)}>`;
|
|
3188
|
-
}
|
|
3189
|
-
case "object": {
|
|
3190
|
-
const objArg = args[0];
|
|
3191
|
-
if (!objArg || !import_ts_morph9.Node.isObjectLiteralExpression(objArg)) return "unknown";
|
|
3192
|
-
const lines = [];
|
|
3193
|
-
for (const prop of objArg.getProperties()) {
|
|
3194
|
-
if (!import_ts_morph9.Node.isPropertyAssignment(prop)) continue;
|
|
3195
|
-
const key = prop.getName();
|
|
3196
|
-
const valNode = prop.getInitializer();
|
|
3197
|
-
if (!valNode) continue;
|
|
3198
|
-
const tsType = zodAstToTs(valNode);
|
|
3199
|
-
const isOpt = isOptionalChain(valNode);
|
|
3200
|
-
lines.push(`${key}${isOpt ? "?" : ""}: ${tsType}`);
|
|
3201
|
-
}
|
|
3202
|
-
return `{ ${lines.join("; ")} }`;
|
|
3203
|
-
}
|
|
3204
|
-
case "union": {
|
|
3205
|
-
const arrArg = args[0];
|
|
3206
|
-
if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
3207
|
-
return arrArg.getElements().map(zodAstToTs).join(" | ");
|
|
3208
|
-
}
|
|
3209
|
-
case "record": {
|
|
3210
|
-
const valArg = args.length === 1 ? args[0] : args[1];
|
|
3211
|
-
if (!valArg) return "unknown";
|
|
3212
|
-
return `Record<string, ${zodAstToTs(valArg)}>`;
|
|
3213
|
-
}
|
|
3214
|
-
case "tuple": {
|
|
3215
|
-
const arrArg = args[0];
|
|
3216
|
-
if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
3217
|
-
return `[${arrArg.getElements().map(zodAstToTs).join(", ")}]`;
|
|
3218
|
-
}
|
|
3219
|
-
default:
|
|
3220
|
-
return "unknown";
|
|
3221
|
-
}
|
|
3222
|
-
}
|
|
3223
|
-
return "unknown";
|
|
3224
|
-
}
|
|
3225
|
-
function isOptionalChain(node) {
|
|
3226
|
-
if (!import_ts_morph9.Node.isCallExpression(node)) return false;
|
|
3227
|
-
const expr = node.getExpression();
|
|
3228
|
-
return import_ts_morph9.Node.isPropertyAccessExpression(expr) && expr.getName() === "optional";
|
|
3229
|
-
}
|
|
3230
|
-
function decoratorStringArg(decoratorExpr) {
|
|
3231
|
-
if (!decoratorExpr) return void 0;
|
|
3232
|
-
if (import_ts_morph9.Node.isStringLiteral(decoratorExpr)) return decoratorExpr.getLiteralValue();
|
|
3233
|
-
if (import_ts_morph9.Node.isArrayLiteralExpression(decoratorExpr)) {
|
|
3234
|
-
const first = decoratorExpr.getElements()[0];
|
|
3235
|
-
if (first && import_ts_morph9.Node.isStringLiteral(first)) return first.getLiteralValue();
|
|
3236
|
-
}
|
|
3237
|
-
return void 0;
|
|
3238
|
-
}
|
|
3239
|
-
function parseDefineContractCall(callExpr) {
|
|
3240
|
-
if (!import_ts_morph9.Node.isCallExpression(callExpr)) return null;
|
|
3241
|
-
const callee = callExpr.getExpression();
|
|
3242
|
-
const calleeName = import_ts_morph9.Node.isIdentifier(callee) ? callee.getText() : import_ts_morph9.Node.isPropertyAccessExpression(callee) ? callee.getName() : "";
|
|
3243
|
-
if (calleeName !== "defineContract") return null;
|
|
3244
|
-
const args = callExpr.getArguments();
|
|
3245
|
-
const optsArg = args[0];
|
|
3246
|
-
if (!optsArg || !import_ts_morph9.Node.isObjectLiteralExpression(optsArg)) return null;
|
|
3247
|
-
let query = null;
|
|
3248
|
-
let body = null;
|
|
3249
|
-
let response = "unknown";
|
|
3250
|
-
let bodyZodText = null;
|
|
3251
|
-
let queryZodText = null;
|
|
3252
|
-
for (const prop of optsArg.getProperties()) {
|
|
3253
|
-
if (!import_ts_morph9.Node.isPropertyAssignment(prop)) continue;
|
|
3254
|
-
const propName = prop.getName();
|
|
3255
|
-
const val = prop.getInitializer();
|
|
3256
|
-
if (!val) continue;
|
|
3257
|
-
if (propName === "query") {
|
|
3258
|
-
query = zodAstToTs(val);
|
|
3259
|
-
queryZodText = val.getText();
|
|
3260
|
-
} else if (propName === "body") {
|
|
3261
|
-
body = zodAstToTs(val);
|
|
3262
|
-
bodyZodText = val.getText();
|
|
3263
|
-
} else if (propName === "response") {
|
|
3264
|
-
response = zodAstToTs(val);
|
|
3265
|
-
}
|
|
3266
|
-
}
|
|
3267
|
-
return { query, body, response, bodyZodText, queryZodText };
|
|
3268
|
-
}
|
|
3269
|
-
function deriveClassSegment(className) {
|
|
3270
|
-
const noSuffix = className.replace(/Controller$/, "");
|
|
3271
|
-
if (!noSuffix) {
|
|
3272
|
-
throw new Error(
|
|
3273
|
-
`Controller class name "${className}" derives empty route segment after stripping "Controller". Add an @As(...) override at the class level.`
|
|
3274
|
-
);
|
|
3275
|
-
}
|
|
3276
|
-
return noSuffix.charAt(0).toLowerCase() + noSuffix.slice(1);
|
|
3277
|
-
}
|
|
3278
|
-
function resolveRouteName(className, methodName, classAs, methodAs) {
|
|
3279
|
-
const classPortion = classAs ?? deriveClassSegment(className);
|
|
3280
|
-
const methodPortion = methodAs ?? methodName;
|
|
3281
|
-
return `${classPortion}.${methodPortion}`;
|
|
3282
|
-
}
|
|
3283
|
-
function joinPaths(prefix, suffix) {
|
|
3284
|
-
if (!prefix && !suffix) return "/";
|
|
3285
|
-
if (!prefix) return suffix.startsWith("/") ? suffix : `/${suffix}`;
|
|
3286
|
-
if (!suffix) return prefix.startsWith("/") ? prefix : `/${prefix}`;
|
|
3287
|
-
const p = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
|
|
3288
|
-
const s = suffix.startsWith("/") ? suffix : `/${suffix}`;
|
|
3289
|
-
const combined = p + s;
|
|
3290
|
-
return combined === "" ? "/" : combined;
|
|
3291
|
-
}
|
|
3292
|
-
function extractParams(path) {
|
|
3293
|
-
const matches = path.matchAll(/:(\w+)/g);
|
|
3294
|
-
return Array.from(matches).map((m) => ({ name: m[1], source: "path" }));
|
|
3295
|
-
}
|
|
3296
|
-
function resolveTypeNodeToString(typeNode, sourceFile, project, depth) {
|
|
3297
|
-
if (depth <= 0) return "unknown";
|
|
3298
|
-
if (import_ts_morph9.Node.isArrayTypeNode(typeNode)) {
|
|
3299
|
-
const elementType = typeNode.getElementTypeNode();
|
|
3300
|
-
return `Array<${resolveTypeNodeToString(elementType, sourceFile, project, depth)}>`;
|
|
3301
|
-
}
|
|
3302
|
-
if (import_ts_morph9.Node.isUnionTypeNode(typeNode)) {
|
|
3303
|
-
return typeNode.getTypeNodes().map((t) => resolveTypeNodeToString(t, sourceFile, project, depth)).join(" | ");
|
|
3304
|
-
}
|
|
3305
|
-
if (import_ts_morph9.Node.isIntersectionTypeNode(typeNode)) {
|
|
3306
|
-
return typeNode.getTypeNodes().map((t) => resolveTypeNodeToString(t, sourceFile, project, depth)).join(" & ");
|
|
3307
|
-
}
|
|
3308
|
-
if (import_ts_morph9.Node.isParenthesizedTypeNode(typeNode)) {
|
|
3309
|
-
return `(${resolveTypeNodeToString(typeNode.getTypeNode(), sourceFile, project, depth)})`;
|
|
3310
|
-
}
|
|
3311
|
-
if (import_ts_morph9.Node.isTypeReference(typeNode)) {
|
|
3312
|
-
const typeName = typeNode.getTypeName();
|
|
3313
|
-
const name = import_ts_morph9.Node.isIdentifier(typeName) ? typeName.getText() : typeNode.getText();
|
|
3314
|
-
if (name === "string" || name === "number" || name === "boolean") return name;
|
|
3315
|
-
if (name === "Date") return "string";
|
|
3316
|
-
if (name === "unknown" || name === "any" || name === "void") return "unknown";
|
|
3317
|
-
if (name === "StreamableFile" || name === "Observable" || name === "ReadableStream")
|
|
3318
|
-
return "unknown";
|
|
3319
|
-
if (name === "Ref" || name === "Reference" || name === "LoadedReference" || name === "IdentifiedReference") {
|
|
3320
|
-
const typeArgs = typeNode.getTypeArguments();
|
|
3321
|
-
const firstTypeArg = typeArgs[0];
|
|
3322
|
-
if (typeArgs.length > 0 && firstTypeArg !== void 0) {
|
|
3323
|
-
return resolveTypeNodeToString(firstTypeArg, sourceFile, project, depth);
|
|
3324
|
-
}
|
|
3325
|
-
return "unknown";
|
|
3326
|
-
}
|
|
3327
|
-
if (name === "Collection") {
|
|
3328
|
-
const typeArgs = typeNode.getTypeArguments();
|
|
3329
|
-
const firstTypeArg = typeArgs[0];
|
|
3330
|
-
if (typeArgs.length > 0 && firstTypeArg !== void 0) {
|
|
3331
|
-
return `Array<${resolveTypeNodeToString(firstTypeArg, sourceFile, project, depth)}>`;
|
|
3332
|
-
}
|
|
3333
|
-
return "Array<unknown>";
|
|
3334
|
-
}
|
|
3335
|
-
if (name === "Opt" || name === "Loaded") {
|
|
3336
|
-
const typeArgs = typeNode.getTypeArguments();
|
|
3337
|
-
const firstTypeArg = typeArgs[0];
|
|
3338
|
-
if (typeArgs.length > 0 && firstTypeArg !== void 0) {
|
|
3339
|
-
return resolveTypeNodeToString(firstTypeArg, sourceFile, project, depth);
|
|
3340
|
-
}
|
|
3341
|
-
return "unknown";
|
|
3342
|
-
}
|
|
3343
|
-
if (name === "Array") {
|
|
3344
|
-
const typeArgs = typeNode.getTypeArguments();
|
|
3345
|
-
const firstTypeArg = typeArgs[0];
|
|
3346
|
-
if (typeArgs.length > 0 && firstTypeArg !== void 0) {
|
|
3347
|
-
return `Array<${resolveTypeNodeToString(firstTypeArg, sourceFile, project, depth)}>`;
|
|
3348
|
-
}
|
|
3349
|
-
return "Array<unknown>";
|
|
3350
|
-
}
|
|
3351
|
-
if (["Record", "Omit", "Pick", "Partial", "Required", "Readonly", "Map", "Set"].includes(name)) {
|
|
3352
|
-
return typeNode.getText();
|
|
3353
|
-
}
|
|
3354
|
-
if (name === "Promise") {
|
|
3355
|
-
const typeArgs = typeNode.getTypeArguments();
|
|
3356
|
-
const firstTypeArg = typeArgs[0];
|
|
3357
|
-
if (typeArgs.length > 0 && firstTypeArg !== void 0) {
|
|
3358
|
-
return resolveTypeNodeToString(firstTypeArg, sourceFile, project, depth);
|
|
3359
|
-
}
|
|
3360
|
-
return "unknown";
|
|
2704
|
+
if (PASSTHROUGH_UTILITY.has(name)) {
|
|
2705
|
+
return typeNode.getText();
|
|
3361
2706
|
}
|
|
3362
2707
|
const resolved = findType(name, sourceFile, project);
|
|
3363
2708
|
if (resolved) {
|
|
@@ -3367,13 +2712,22 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth) {
|
|
|
3367
2712
|
return "unknown";
|
|
3368
2713
|
}
|
|
3369
2714
|
const kind = typeNode.getKind();
|
|
3370
|
-
if (kind ===
|
|
3371
|
-
if (kind ===
|
|
3372
|
-
if (kind ===
|
|
3373
|
-
if (kind ===
|
|
3374
|
-
if (kind ===
|
|
2715
|
+
if (kind === import_ts_morph7.SyntaxKind.StringKeyword) return "string";
|
|
2716
|
+
if (kind === import_ts_morph7.SyntaxKind.NumberKeyword) return "number";
|
|
2717
|
+
if (kind === import_ts_morph7.SyntaxKind.BooleanKeyword) return "boolean";
|
|
2718
|
+
if (kind === import_ts_morph7.SyntaxKind.UnknownKeyword) return "unknown";
|
|
2719
|
+
if (kind === import_ts_morph7.SyntaxKind.AnyKeyword) return "unknown";
|
|
3375
2720
|
return typeNode.getText();
|
|
3376
2721
|
}
|
|
2722
|
+
function unwrapFirstTypeArg(typeNode, sourceFile, project, depth, mode) {
|
|
2723
|
+
const typeArgs = typeNode.getTypeArguments();
|
|
2724
|
+
const firstTypeArg = typeArgs[0];
|
|
2725
|
+
if (typeArgs.length > 0 && firstTypeArg !== void 0) {
|
|
2726
|
+
const inner = resolveTypeNodeToString(firstTypeArg, sourceFile, project, depth);
|
|
2727
|
+
return mode === "arrayOf" ? `Array<${inner}>` : inner;
|
|
2728
|
+
}
|
|
2729
|
+
return mode === "arrayOf" ? "Array<unknown>" : "unknown";
|
|
2730
|
+
}
|
|
3377
2731
|
function expandTypeDecl(result, project, depth) {
|
|
3378
2732
|
if (depth < 0) return "unknown";
|
|
3379
2733
|
switch (result.kind) {
|
|
@@ -3439,7 +2793,7 @@ function extractParamsType(method, sourceFile, project) {
|
|
|
3439
2793
|
const paramArgs = paramDecorator.getArguments();
|
|
3440
2794
|
if (paramArgs.length === 0) continue;
|
|
3441
2795
|
const nameArg = paramArgs[0];
|
|
3442
|
-
if (!
|
|
2796
|
+
if (!import_ts_morph7.Node.isStringLiteral(nameArg)) continue;
|
|
3443
2797
|
const paramName = nameArg.getLiteralValue();
|
|
3444
2798
|
const typeNode = param.getTypeNode();
|
|
3445
2799
|
const paramType = typeNode ? resolveTypeNodeToString(typeNode, sourceFile, project, 3) : "string";
|
|
@@ -3452,13 +2806,13 @@ function extractResponseType(method, sourceFile, project) {
|
|
|
3452
2806
|
if (apiResponseDecorator) {
|
|
3453
2807
|
const args = apiResponseDecorator.getArguments();
|
|
3454
2808
|
const optsArg = args[0];
|
|
3455
|
-
if (optsArg &&
|
|
2809
|
+
if (optsArg && import_ts_morph7.Node.isObjectLiteralExpression(optsArg)) {
|
|
3456
2810
|
for (const prop of optsArg.getProperties()) {
|
|
3457
|
-
if (!
|
|
2811
|
+
if (!import_ts_morph7.Node.isPropertyAssignment(prop)) continue;
|
|
3458
2812
|
if (prop.getName() !== "type") continue;
|
|
3459
2813
|
const val = prop.getInitializer();
|
|
3460
2814
|
if (!val) continue;
|
|
3461
|
-
if (
|
|
2815
|
+
if (import_ts_morph7.Node.isArrayLiteralExpression(val)) {
|
|
3462
2816
|
const elements = val.getElements();
|
|
3463
2817
|
const firstEl = elements[0];
|
|
3464
2818
|
if (elements.length > 0 && firstEl !== void 0) {
|
|
@@ -3478,7 +2832,7 @@ function extractResponseType(method, sourceFile, project) {
|
|
|
3478
2832
|
return "unknown";
|
|
3479
2833
|
}
|
|
3480
2834
|
function resolveIdentifierToClassType(node, sourceFile, project, depth) {
|
|
3481
|
-
if (!
|
|
2835
|
+
if (!import_ts_morph7.Node.isIdentifier(node)) return "unknown";
|
|
3482
2836
|
const name = node.getText();
|
|
3483
2837
|
const resolved = findType(name, sourceFile, project);
|
|
3484
2838
|
if (resolved) {
|
|
@@ -3525,11 +2879,11 @@ function extractDtoContract(method, sourceFile, project) {
|
|
|
3525
2879
|
if (apiResp) {
|
|
3526
2880
|
const args = apiResp.getArguments();
|
|
3527
2881
|
const optsArg = args[0];
|
|
3528
|
-
if (optsArg &&
|
|
2882
|
+
if (optsArg && import_ts_morph7.Node.isObjectLiteralExpression(optsArg)) {
|
|
3529
2883
|
for (const prop of optsArg.getProperties()) {
|
|
3530
|
-
if (
|
|
2884
|
+
if (import_ts_morph7.Node.isPropertyAssignment(prop) && prop.getName() === "type") {
|
|
3531
2885
|
const val = prop.getInitializer();
|
|
3532
|
-
if (val &&
|
|
2886
|
+
if (val && import_ts_morph7.Node.isIdentifier(val)) {
|
|
3533
2887
|
const name = val.getText();
|
|
3534
2888
|
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
3535
2889
|
if (localDecl?.isExported()) {
|
|
@@ -3546,27 +2900,18 @@ function extractDtoContract(method, sourceFile, project) {
|
|
|
3546
2900
|
}
|
|
3547
2901
|
}
|
|
3548
2902
|
}
|
|
3549
|
-
let bodyZodText = null;
|
|
3550
|
-
let queryZodText = null;
|
|
3551
2903
|
let bodySchema = null;
|
|
3552
2904
|
let querySchema = null;
|
|
3553
|
-
const formNested = {};
|
|
3554
2905
|
const formWarnings = [];
|
|
3555
2906
|
const bodyClass = resolveParamClass(method, "Body", sourceFile, project);
|
|
3556
2907
|
if (bodyClass) {
|
|
3557
|
-
const result = extractZodFromDto(bodyClass.decl, bodyClass.file, project);
|
|
3558
|
-
bodyZodText = result.schemaText;
|
|
3559
|
-
for (const [k, v] of result.namedNestedSchemas) formNested[k] = v;
|
|
3560
|
-
formWarnings.push(...result.warnings);
|
|
3561
2908
|
bodySchema = extractSchemaFromDto(bodyClass.decl, bodyClass.file, project);
|
|
2909
|
+
formWarnings.push(...bodySchema.warnings);
|
|
3562
2910
|
}
|
|
3563
2911
|
const queryClass = resolveParamClass(method, "Query", sourceFile, project);
|
|
3564
2912
|
if (queryClass) {
|
|
3565
|
-
const result = extractZodFromDto(queryClass.decl, queryClass.file, project);
|
|
3566
|
-
queryZodText = result.schemaText;
|
|
3567
|
-
for (const [k, v] of result.namedNestedSchemas) formNested[k] = v;
|
|
3568
|
-
formWarnings.push(...result.warnings);
|
|
3569
2913
|
querySchema = extractSchemaFromDto(queryClass.decl, queryClass.file, project);
|
|
2914
|
+
formWarnings.push(...querySchema.warnings);
|
|
3570
2915
|
}
|
|
3571
2916
|
return {
|
|
3572
2917
|
query,
|
|
@@ -3579,9 +2924,6 @@ function extractDtoContract(method, sourceFile, project) {
|
|
|
3579
2924
|
filterFields: filterInfo?.fieldNames ?? null,
|
|
3580
2925
|
filterFieldTypes: filterInfo?.fieldTypes ?? null,
|
|
3581
2926
|
filterSource: filterInfo?.source ?? null,
|
|
3582
|
-
bodyZodText,
|
|
3583
|
-
queryZodText,
|
|
3584
|
-
formNestedSchemas: Object.keys(formNested).length > 0 ? formNested : null,
|
|
3585
2927
|
formWarnings,
|
|
3586
2928
|
bodySchema,
|
|
3587
2929
|
querySchema
|
|
@@ -3601,6 +2943,201 @@ function resolveParamClass(method, decoratorName, sourceFile, project) {
|
|
|
3601
2943
|
}
|
|
3602
2944
|
return null;
|
|
3603
2945
|
}
|
|
2946
|
+
|
|
2947
|
+
// src/discovery/zod-ast-to-ts.ts
|
|
2948
|
+
var import_ts_morph8 = require("ts-morph");
|
|
2949
|
+
function zodAstToTs(node) {
|
|
2950
|
+
if (!import_ts_morph8.Node.isCallExpression(node)) return "unknown";
|
|
2951
|
+
const expr = node.getExpression();
|
|
2952
|
+
if (import_ts_morph8.Node.isPropertyAccessExpression(expr)) {
|
|
2953
|
+
const methodName = expr.getName();
|
|
2954
|
+
const receiver = expr.getExpression();
|
|
2955
|
+
if (methodName === "optional") {
|
|
2956
|
+
return `${zodAstToTs(receiver)} | undefined`;
|
|
2957
|
+
}
|
|
2958
|
+
if (methodName === "nullable") {
|
|
2959
|
+
return `${zodAstToTs(receiver)} | null`;
|
|
2960
|
+
}
|
|
2961
|
+
const args = node.getArguments();
|
|
2962
|
+
switch (methodName) {
|
|
2963
|
+
case "string":
|
|
2964
|
+
return "string";
|
|
2965
|
+
case "number":
|
|
2966
|
+
return "number";
|
|
2967
|
+
case "boolean":
|
|
2968
|
+
return "boolean";
|
|
2969
|
+
case "unknown":
|
|
2970
|
+
return "unknown";
|
|
2971
|
+
case "any":
|
|
2972
|
+
return "unknown";
|
|
2973
|
+
case "literal": {
|
|
2974
|
+
const lit = args[0];
|
|
2975
|
+
if (!lit) return "unknown";
|
|
2976
|
+
if (import_ts_morph8.Node.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
|
|
2977
|
+
if (import_ts_morph8.Node.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
|
|
2978
|
+
if (lit.getKind() === import_ts_morph8.SyntaxKind.TrueKeyword) return "true";
|
|
2979
|
+
if (lit.getKind() === import_ts_morph8.SyntaxKind.FalseKeyword) return "false";
|
|
2980
|
+
return "unknown";
|
|
2981
|
+
}
|
|
2982
|
+
case "enum": {
|
|
2983
|
+
const arrArg = args[0];
|
|
2984
|
+
if (!arrArg || !import_ts_morph8.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2985
|
+
const members = arrArg.getElements().map(
|
|
2986
|
+
(el) => import_ts_morph8.Node.isStringLiteral(el) ? JSON.stringify(el.getLiteralValue()) : "unknown"
|
|
2987
|
+
);
|
|
2988
|
+
return members.join(" | ");
|
|
2989
|
+
}
|
|
2990
|
+
case "array": {
|
|
2991
|
+
const inner = args[0];
|
|
2992
|
+
if (!inner) return "unknown";
|
|
2993
|
+
return `Array<${zodAstToTs(inner)}>`;
|
|
2994
|
+
}
|
|
2995
|
+
case "object": {
|
|
2996
|
+
const objArg = args[0];
|
|
2997
|
+
if (!objArg || !import_ts_morph8.Node.isObjectLiteralExpression(objArg)) return "unknown";
|
|
2998
|
+
const lines = [];
|
|
2999
|
+
for (const prop of objArg.getProperties()) {
|
|
3000
|
+
if (!import_ts_morph8.Node.isPropertyAssignment(prop)) continue;
|
|
3001
|
+
const key = prop.getName();
|
|
3002
|
+
const valNode = prop.getInitializer();
|
|
3003
|
+
if (!valNode) continue;
|
|
3004
|
+
const tsType = zodAstToTs(valNode);
|
|
3005
|
+
const isOpt = isOptionalChain(valNode);
|
|
3006
|
+
lines.push(`${key}${isOpt ? "?" : ""}: ${tsType}`);
|
|
3007
|
+
}
|
|
3008
|
+
return `{ ${lines.join("; ")} }`;
|
|
3009
|
+
}
|
|
3010
|
+
case "union": {
|
|
3011
|
+
const arrArg = args[0];
|
|
3012
|
+
if (!arrArg || !import_ts_morph8.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
3013
|
+
return arrArg.getElements().map(zodAstToTs).join(" | ");
|
|
3014
|
+
}
|
|
3015
|
+
case "record": {
|
|
3016
|
+
const valArg = args.length === 1 ? args[0] : args[1];
|
|
3017
|
+
if (!valArg) return "unknown";
|
|
3018
|
+
return `Record<string, ${zodAstToTs(valArg)}>`;
|
|
3019
|
+
}
|
|
3020
|
+
case "tuple": {
|
|
3021
|
+
const arrArg = args[0];
|
|
3022
|
+
if (!arrArg || !import_ts_morph8.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
3023
|
+
return `[${arrArg.getElements().map(zodAstToTs).join(", ")}]`;
|
|
3024
|
+
}
|
|
3025
|
+
default:
|
|
3026
|
+
return "unknown";
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3029
|
+
return "unknown";
|
|
3030
|
+
}
|
|
3031
|
+
function isOptionalChain(node) {
|
|
3032
|
+
if (!import_ts_morph8.Node.isCallExpression(node)) return false;
|
|
3033
|
+
const expr = node.getExpression();
|
|
3034
|
+
return import_ts_morph8.Node.isPropertyAccessExpression(expr) && expr.getName() === "optional";
|
|
3035
|
+
}
|
|
3036
|
+
function parseDefineContractCall(callExpr) {
|
|
3037
|
+
if (!import_ts_morph8.Node.isCallExpression(callExpr)) return null;
|
|
3038
|
+
const callee = callExpr.getExpression();
|
|
3039
|
+
const calleeName = import_ts_morph8.Node.isIdentifier(callee) ? callee.getText() : import_ts_morph8.Node.isPropertyAccessExpression(callee) ? callee.getName() : "";
|
|
3040
|
+
if (calleeName !== "defineContract") return null;
|
|
3041
|
+
const args = callExpr.getArguments();
|
|
3042
|
+
const optsArg = args[0];
|
|
3043
|
+
if (!optsArg || !import_ts_morph8.Node.isObjectLiteralExpression(optsArg)) return null;
|
|
3044
|
+
let query = null;
|
|
3045
|
+
let body = null;
|
|
3046
|
+
let response = "unknown";
|
|
3047
|
+
let bodyZodText = null;
|
|
3048
|
+
let queryZodText = null;
|
|
3049
|
+
for (const prop of optsArg.getProperties()) {
|
|
3050
|
+
if (!import_ts_morph8.Node.isPropertyAssignment(prop)) continue;
|
|
3051
|
+
const propName = prop.getName();
|
|
3052
|
+
const val = prop.getInitializer();
|
|
3053
|
+
if (!val) continue;
|
|
3054
|
+
if (propName === "query") {
|
|
3055
|
+
query = zodAstToTs(val);
|
|
3056
|
+
queryZodText = val.getText();
|
|
3057
|
+
} else if (propName === "body") {
|
|
3058
|
+
body = zodAstToTs(val);
|
|
3059
|
+
bodyZodText = val.getText();
|
|
3060
|
+
} else if (propName === "response") {
|
|
3061
|
+
response = zodAstToTs(val);
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
3064
|
+
return { query, body, response, bodyZodText, queryZodText };
|
|
3065
|
+
}
|
|
3066
|
+
|
|
3067
|
+
// src/discovery/contracts-fast.ts
|
|
3068
|
+
async function discoverContractsFast(opts) {
|
|
3069
|
+
const { cwd, glob, tsconfig } = opts;
|
|
3070
|
+
const tsconfigPath = tsconfig ? (0, import_node_path12.resolve)(tsconfig) : (0, import_node_path12.join)(cwd, "tsconfig.json");
|
|
3071
|
+
let project;
|
|
3072
|
+
try {
|
|
3073
|
+
project = new import_ts_morph9.Project({
|
|
3074
|
+
tsConfigFilePath: tsconfigPath,
|
|
3075
|
+
skipAddingFilesFromTsConfig: true,
|
|
3076
|
+
skipLoadingLibFiles: true,
|
|
3077
|
+
skipFileDependencyResolution: true
|
|
3078
|
+
});
|
|
3079
|
+
} catch {
|
|
3080
|
+
project = new import_ts_morph9.Project({
|
|
3081
|
+
skipAddingFilesFromTsConfig: true,
|
|
3082
|
+
skipLoadingLibFiles: true,
|
|
3083
|
+
skipFileDependencyResolution: true,
|
|
3084
|
+
compilerOptions: {
|
|
3085
|
+
allowJs: true,
|
|
3086
|
+
resolveJsonModule: false,
|
|
3087
|
+
strict: false
|
|
3088
|
+
}
|
|
3089
|
+
});
|
|
3090
|
+
}
|
|
3091
|
+
const files = await (0, import_fast_glob2.default)(glob, { cwd, absolute: true, onlyFiles: true });
|
|
3092
|
+
for (const f of files) {
|
|
3093
|
+
project.addSourceFileAtPath(f);
|
|
3094
|
+
}
|
|
3095
|
+
const routes = [];
|
|
3096
|
+
setDiscoveryContext(project, {
|
|
3097
|
+
projectRoot: cwd,
|
|
3098
|
+
tsconfigPaths: loadTsconfigPaths(tsconfigPath)
|
|
3099
|
+
});
|
|
3100
|
+
for (const sourceFile of project.getSourceFiles()) {
|
|
3101
|
+
routes.push(...extractFromSourceFile(sourceFile, project));
|
|
3102
|
+
}
|
|
3103
|
+
return routes;
|
|
3104
|
+
}
|
|
3105
|
+
function decoratorStringArg(decoratorExpr) {
|
|
3106
|
+
if (!decoratorExpr) return void 0;
|
|
3107
|
+
if (import_ts_morph9.Node.isStringLiteral(decoratorExpr)) return decoratorExpr.getLiteralValue();
|
|
3108
|
+
if (import_ts_morph9.Node.isArrayLiteralExpression(decoratorExpr)) {
|
|
3109
|
+
const first = decoratorExpr.getElements()[0];
|
|
3110
|
+
if (first && import_ts_morph9.Node.isStringLiteral(first)) return first.getLiteralValue();
|
|
3111
|
+
}
|
|
3112
|
+
return void 0;
|
|
3113
|
+
}
|
|
3114
|
+
function deriveClassSegment(className) {
|
|
3115
|
+
const noSuffix = className.replace(/Controller$/, "");
|
|
3116
|
+
if (!noSuffix) {
|
|
3117
|
+
throw new Error(
|
|
3118
|
+
`Controller class name "${className}" derives empty route segment after stripping "Controller". Add an @As(...) override at the class level.`
|
|
3119
|
+
);
|
|
3120
|
+
}
|
|
3121
|
+
return noSuffix.charAt(0).toLowerCase() + noSuffix.slice(1);
|
|
3122
|
+
}
|
|
3123
|
+
function resolveRouteName(className, methodName, classAs, methodAs) {
|
|
3124
|
+
const classPortion = classAs ?? deriveClassSegment(className);
|
|
3125
|
+
const methodPortion = methodAs ?? methodName;
|
|
3126
|
+
return `${classPortion}.${methodPortion}`;
|
|
3127
|
+
}
|
|
3128
|
+
function joinPaths(prefix, suffix) {
|
|
3129
|
+
if (!prefix && !suffix) return "/";
|
|
3130
|
+
if (!prefix) return suffix.startsWith("/") ? suffix : `/${suffix}`;
|
|
3131
|
+
if (!suffix) return prefix.startsWith("/") ? prefix : `/${prefix}`;
|
|
3132
|
+
const p = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
|
|
3133
|
+
const s = suffix.startsWith("/") ? suffix : `/${suffix}`;
|
|
3134
|
+
const combined = p + s;
|
|
3135
|
+
return combined === "" ? "/" : combined;
|
|
3136
|
+
}
|
|
3137
|
+
function extractParams(path) {
|
|
3138
|
+
const matches = path.matchAll(/:(\w+)/g);
|
|
3139
|
+
return Array.from(matches).map((m) => ({ name: m[1], source: "path" }));
|
|
3140
|
+
}
|
|
3604
3141
|
var HTTP_METHOD_DECORATORS = {
|
|
3605
3142
|
Get: "GET",
|
|
3606
3143
|
Post: "POST",
|
|
@@ -3611,176 +3148,186 @@ var HTTP_METHOD_DECORATORS = {
|
|
|
3611
3148
|
Head: "HEAD",
|
|
3612
3149
|
All: "ALL"
|
|
3613
3150
|
};
|
|
3151
|
+
function resolveVerb(method) {
|
|
3152
|
+
for (const [decoratorName, verb] of Object.entries(HTTP_METHOD_DECORATORS)) {
|
|
3153
|
+
const httpDecorator = method.getDecorator(decoratorName);
|
|
3154
|
+
if (httpDecorator) {
|
|
3155
|
+
const httpArgs = httpDecorator.getArguments();
|
|
3156
|
+
const pathArg = httpArgs[0];
|
|
3157
|
+
return { httpMethod: verb, handlerPath: decoratorStringArg(pathArg) ?? "" };
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
3160
|
+
return null;
|
|
3161
|
+
}
|
|
3162
|
+
function readAsDecorator(node, label) {
|
|
3163
|
+
const asDecorator = node.getDecorator("As");
|
|
3164
|
+
if (!asDecorator) return void 0;
|
|
3165
|
+
const asName = decoratorStringArg(asDecorator.getArguments()[0]);
|
|
3166
|
+
if (!asName) {
|
|
3167
|
+
throw new Error(`@As decorator on ${label} must have a non-empty string argument.`);
|
|
3168
|
+
}
|
|
3169
|
+
return asName;
|
|
3170
|
+
}
|
|
3171
|
+
function buildRoute(args) {
|
|
3172
|
+
const {
|
|
3173
|
+
className,
|
|
3174
|
+
methodName,
|
|
3175
|
+
resolvedMethod,
|
|
3176
|
+
combinedPath,
|
|
3177
|
+
classAs,
|
|
3178
|
+
methodAs,
|
|
3179
|
+
sourceFile,
|
|
3180
|
+
seenNames,
|
|
3181
|
+
contractSource
|
|
3182
|
+
} = args;
|
|
3183
|
+
const routeName = resolveRouteName(className, methodName, classAs, methodAs);
|
|
3184
|
+
const qualifiedRef = `${className}.${methodName}`;
|
|
3185
|
+
const existing = seenNames.get(routeName);
|
|
3186
|
+
if (existing !== void 0) {
|
|
3187
|
+
throw new Error(
|
|
3188
|
+
`Route name collision: "${routeName}" is used by both "${existing}" and "${qualifiedRef}". Use @As(...) to give one of them a unique name.`
|
|
3189
|
+
);
|
|
3190
|
+
}
|
|
3191
|
+
seenNames.set(routeName, qualifiedRef);
|
|
3192
|
+
return {
|
|
3193
|
+
method: resolvedMethod,
|
|
3194
|
+
path: combinedPath,
|
|
3195
|
+
name: routeName,
|
|
3196
|
+
params: extractParams(combinedPath),
|
|
3197
|
+
controllerRef: { className, methodName, filePath: sourceFile.getFilePath() },
|
|
3198
|
+
contract: { contractSource }
|
|
3199
|
+
};
|
|
3200
|
+
}
|
|
3201
|
+
function extractContractRoute(args) {
|
|
3202
|
+
const { cls, method, applyContractDecorator, verb, prefix, className, sourceFile, seenNames } = args;
|
|
3203
|
+
const firstDecoratorArg = applyContractDecorator.getArguments()[0];
|
|
3204
|
+
if (!firstDecoratorArg) return null;
|
|
3205
|
+
let contractDef = null;
|
|
3206
|
+
let bodyZodRef = null;
|
|
3207
|
+
let queryZodRef = null;
|
|
3208
|
+
if (import_ts_morph9.Node.isCallExpression(firstDecoratorArg)) {
|
|
3209
|
+
contractDef = parseDefineContractCall(firstDecoratorArg);
|
|
3210
|
+
} else if (import_ts_morph9.Node.isIdentifier(firstDecoratorArg)) {
|
|
3211
|
+
const identName = firstDecoratorArg.getText();
|
|
3212
|
+
const varDecl = sourceFile.getVariableDeclaration(identName);
|
|
3213
|
+
if (!varDecl) {
|
|
3214
|
+
console.warn(
|
|
3215
|
+
`[nestjs-codegen/fast] Cannot resolve '${identName}' in ${sourceFile.getFilePath()} (cross-file imports are out-of-scope for v1) \u2014 skipping`
|
|
3216
|
+
);
|
|
3217
|
+
return null;
|
|
3218
|
+
}
|
|
3219
|
+
const initializer = varDecl.getInitializer();
|
|
3220
|
+
if (!initializer) return null;
|
|
3221
|
+
contractDef = parseDefineContractCall(initializer);
|
|
3222
|
+
if (contractDef && varDecl.isExported()) {
|
|
3223
|
+
const filePath = sourceFile.getFilePath();
|
|
3224
|
+
if (contractDef.body !== null) {
|
|
3225
|
+
bodyZodRef = { name: `${identName}.body`, filePath };
|
|
3226
|
+
}
|
|
3227
|
+
if (contractDef.query !== null) {
|
|
3228
|
+
queryZodRef = { name: `${identName}.query`, filePath };
|
|
3229
|
+
}
|
|
3230
|
+
}
|
|
3231
|
+
} else {
|
|
3232
|
+
console.warn(
|
|
3233
|
+
`[nestjs-codegen/fast] @ApplyContract arg is not an identifier or call expression in ${sourceFile.getFilePath()} \u2014 skipping`
|
|
3234
|
+
);
|
|
3235
|
+
return null;
|
|
3236
|
+
}
|
|
3237
|
+
if (!contractDef) return null;
|
|
3238
|
+
if (!verb) return null;
|
|
3239
|
+
const resolvedPath = joinPaths(prefix, verb.handlerPath);
|
|
3240
|
+
const methodName = method.getName();
|
|
3241
|
+
const classAs = readAsDecorator(cls, `class ${className}`);
|
|
3242
|
+
const methodAs = readAsDecorator(method, `${className}.${methodName}`);
|
|
3243
|
+
return buildRoute({
|
|
3244
|
+
className,
|
|
3245
|
+
methodName,
|
|
3246
|
+
resolvedMethod: verb.httpMethod,
|
|
3247
|
+
combinedPath: resolvedPath,
|
|
3248
|
+
classAs,
|
|
3249
|
+
methodAs,
|
|
3250
|
+
sourceFile,
|
|
3251
|
+
seenNames,
|
|
3252
|
+
contractSource: {
|
|
3253
|
+
query: contractDef.query,
|
|
3254
|
+
body: contractDef.body,
|
|
3255
|
+
response: contractDef.response,
|
|
3256
|
+
// Path A: capture both the importable ref and the raw text. The emitter
|
|
3257
|
+
// prefers inlining the text (client-safe — re-exporting from a controller
|
|
3258
|
+
// would drag server-only deps into the client bundle).
|
|
3259
|
+
bodyZodRef,
|
|
3260
|
+
bodyZodText: contractDef.bodyZodText,
|
|
3261
|
+
queryZodRef,
|
|
3262
|
+
queryZodText: contractDef.queryZodText
|
|
3263
|
+
}
|
|
3264
|
+
});
|
|
3265
|
+
}
|
|
3266
|
+
function extractDtoRoute(args) {
|
|
3267
|
+
const { cls, method, verb, prefix, className, sourceFile, project, seenNames } = args;
|
|
3268
|
+
if (!verb) return null;
|
|
3269
|
+
const combined = joinPaths(prefix, verb.handlerPath);
|
|
3270
|
+
const methodName = method.getName();
|
|
3271
|
+
const classAs = readAsDecorator(cls, `class ${className}`);
|
|
3272
|
+
const methodAs = readAsDecorator(method, `${className}.${methodName}`);
|
|
3273
|
+
const dtoContract = extractDtoContract(method, sourceFile, project);
|
|
3274
|
+
return buildRoute({
|
|
3275
|
+
className,
|
|
3276
|
+
methodName,
|
|
3277
|
+
resolvedMethod: verb.httpMethod,
|
|
3278
|
+
combinedPath: combined,
|
|
3279
|
+
classAs,
|
|
3280
|
+
methodAs,
|
|
3281
|
+
sourceFile,
|
|
3282
|
+
seenNames,
|
|
3283
|
+
contractSource: {
|
|
3284
|
+
query: dtoContract?.query ?? null,
|
|
3285
|
+
body: dtoContract?.body ?? null,
|
|
3286
|
+
response: dtoContract?.response ?? "unknown",
|
|
3287
|
+
queryRef: dtoContract?.queryRef ?? null,
|
|
3288
|
+
bodyRef: dtoContract?.bodyRef ?? null,
|
|
3289
|
+
responseRef: dtoContract?.responseRef ?? null,
|
|
3290
|
+
filterFields: dtoContract?.filterFields ?? null,
|
|
3291
|
+
filterFieldTypes: dtoContract?.filterFieldTypes ?? null,
|
|
3292
|
+
filterSource: dtoContract?.filterSource ?? null,
|
|
3293
|
+
formWarnings: dtoContract?.formWarnings ?? [],
|
|
3294
|
+
bodySchema: dtoContract?.bodySchema ?? null,
|
|
3295
|
+
querySchema: dtoContract?.querySchema ?? null
|
|
3296
|
+
}
|
|
3297
|
+
});
|
|
3298
|
+
}
|
|
3614
3299
|
function extractFromSourceFile(sourceFile, project) {
|
|
3615
3300
|
const routes = [];
|
|
3616
3301
|
const seenNames = /* @__PURE__ */ new Map();
|
|
3617
|
-
const
|
|
3618
|
-
for (const cls of classes) {
|
|
3302
|
+
for (const cls of sourceFile.getClasses()) {
|
|
3619
3303
|
const controllerDecorator = cls.getDecorator("Controller");
|
|
3620
3304
|
if (!controllerDecorator) continue;
|
|
3621
|
-
const
|
|
3622
|
-
const
|
|
3623
|
-
const prefix = decoratorStringArg(firstArg3) ?? "";
|
|
3305
|
+
const firstArg2 = controllerDecorator.getArguments()[0];
|
|
3306
|
+
const prefix = decoratorStringArg(firstArg2) ?? "";
|
|
3624
3307
|
const className = cls.getName() ?? "Unknown";
|
|
3625
3308
|
for (const method of cls.getMethods()) {
|
|
3626
|
-
|
|
3627
|
-
let handlerPath = "";
|
|
3628
|
-
for (const [decoratorName, verb] of Object.entries(HTTP_METHOD_DECORATORS)) {
|
|
3629
|
-
const httpDecorator = method.getDecorator(decoratorName);
|
|
3630
|
-
if (httpDecorator) {
|
|
3631
|
-
httpMethod = verb;
|
|
3632
|
-
const httpArgs = httpDecorator.getArguments();
|
|
3633
|
-
const pathArg = httpArgs[0];
|
|
3634
|
-
handlerPath = decoratorStringArg(pathArg) ?? "";
|
|
3635
|
-
break;
|
|
3636
|
-
}
|
|
3637
|
-
}
|
|
3309
|
+
const verb = resolveVerb(method);
|
|
3638
3310
|
const applyContractDecorator = method.getDecorator("ApplyContract");
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
contractDef = parseDefineContractCall(initializer);
|
|
3660
|
-
if (contractDef && varDecl.isExported()) {
|
|
3661
|
-
const filePath = sourceFile.getFilePath();
|
|
3662
|
-
if (contractDef.body !== null) {
|
|
3663
|
-
bodyZodRef = { name: `${identName}.body`, filePath };
|
|
3664
|
-
}
|
|
3665
|
-
if (contractDef.query !== null) {
|
|
3666
|
-
queryZodRef = { name: `${identName}.query`, filePath };
|
|
3667
|
-
}
|
|
3668
|
-
}
|
|
3669
|
-
} else {
|
|
3670
|
-
console.warn(
|
|
3671
|
-
`[nestjs-codegen/fast] @ApplyContract arg is not an identifier or call expression in ${sourceFile.getFilePath()} \u2014 skipping`
|
|
3672
|
-
);
|
|
3673
|
-
continue;
|
|
3674
|
-
}
|
|
3675
|
-
if (!contractDef) continue;
|
|
3676
|
-
if (!httpMethod) continue;
|
|
3677
|
-
const resolvedMethod = httpMethod;
|
|
3678
|
-
const resolvedPath = joinPaths(prefix, handlerPath);
|
|
3679
|
-
const combined = resolvedPath;
|
|
3680
|
-
const params = extractParams(combined);
|
|
3681
|
-
const methodName = method.getName();
|
|
3682
|
-
const classAsDecorator = cls.getDecorator("As");
|
|
3683
|
-
let classAs;
|
|
3684
|
-
if (classAsDecorator) {
|
|
3685
|
-
const classAsArgs = classAsDecorator.getArguments();
|
|
3686
|
-
const classAsName = decoratorStringArg(classAsArgs[0]);
|
|
3687
|
-
if (!classAsName) {
|
|
3688
|
-
throw new Error(
|
|
3689
|
-
`@As decorator on class ${className} must have a non-empty string argument.`
|
|
3690
|
-
);
|
|
3691
|
-
}
|
|
3692
|
-
classAs = classAsName;
|
|
3693
|
-
}
|
|
3694
|
-
const methodAsDecorator = method.getDecorator("As");
|
|
3695
|
-
let methodAs;
|
|
3696
|
-
if (methodAsDecorator) {
|
|
3697
|
-
const methodAsArgs = methodAsDecorator.getArguments();
|
|
3698
|
-
const methodAsName = decoratorStringArg(methodAsArgs[0]);
|
|
3699
|
-
if (!methodAsName) {
|
|
3700
|
-
throw new Error(
|
|
3701
|
-
`@As decorator on ${className}.${methodName} must have a non-empty string argument.`
|
|
3702
|
-
);
|
|
3703
|
-
}
|
|
3704
|
-
methodAs = methodAsName;
|
|
3705
|
-
}
|
|
3706
|
-
const routeName = resolveRouteName(className, methodName, classAs, methodAs);
|
|
3707
|
-
const qualifiedRef = `${className}.${methodName}`;
|
|
3708
|
-
const existing = seenNames.get(routeName);
|
|
3709
|
-
if (existing !== void 0) {
|
|
3710
|
-
throw new Error(
|
|
3711
|
-
`Route name collision: "${routeName}" is used by both "${existing}" and "${qualifiedRef}". Use @As(...) to give one of them a unique name.`
|
|
3712
|
-
);
|
|
3713
|
-
}
|
|
3714
|
-
seenNames.set(routeName, qualifiedRef);
|
|
3715
|
-
routes.push({
|
|
3716
|
-
method: resolvedMethod,
|
|
3717
|
-
path: combined,
|
|
3718
|
-
name: routeName,
|
|
3719
|
-
params,
|
|
3720
|
-
controllerRef: { className, methodName, filePath: sourceFile.getFilePath() },
|
|
3721
|
-
contract: {
|
|
3722
|
-
contractSource: {
|
|
3723
|
-
query: contractDef.query,
|
|
3724
|
-
body: contractDef.body,
|
|
3725
|
-
response: contractDef.response,
|
|
3726
|
-
// Path A: capture both the importable ref and the raw text. The
|
|
3727
|
-
// emitter prefers inlining the text (client-safe — re-exporting from
|
|
3728
|
-
// a controller would drag server-only deps into the client bundle).
|
|
3729
|
-
bodyZodRef,
|
|
3730
|
-
bodyZodText: contractDef.bodyZodText,
|
|
3731
|
-
queryZodRef,
|
|
3732
|
-
queryZodText: contractDef.queryZodText
|
|
3733
|
-
}
|
|
3734
|
-
}
|
|
3735
|
-
});
|
|
3736
|
-
} else {
|
|
3737
|
-
if (!httpMethod) continue;
|
|
3738
|
-
const combined = joinPaths(prefix, handlerPath);
|
|
3739
|
-
const params = extractParams(combined);
|
|
3740
|
-
const methodName = method.getName();
|
|
3741
|
-
const classAsDecorator = cls.getDecorator("As");
|
|
3742
|
-
let classAs;
|
|
3743
|
-
if (classAsDecorator) {
|
|
3744
|
-
const classAsArgs = classAsDecorator.getArguments();
|
|
3745
|
-
const classAsName = decoratorStringArg(classAsArgs[0]);
|
|
3746
|
-
if (classAsName) classAs = classAsName;
|
|
3747
|
-
}
|
|
3748
|
-
const methodAsDecorator = method.getDecorator("As");
|
|
3749
|
-
let methodAs;
|
|
3750
|
-
if (methodAsDecorator) {
|
|
3751
|
-
const methodAsArgs = methodAsDecorator.getArguments();
|
|
3752
|
-
const methodAsName = decoratorStringArg(methodAsArgs[0]);
|
|
3753
|
-
if (methodAsName) methodAs = methodAsName;
|
|
3754
|
-
}
|
|
3755
|
-
const routeName = resolveRouteName(className, methodName, classAs, methodAs);
|
|
3756
|
-
const dtoContract = extractDtoContract(method, sourceFile, project);
|
|
3757
|
-
routes.push({
|
|
3758
|
-
method: httpMethod,
|
|
3759
|
-
path: combined,
|
|
3760
|
-
name: routeName,
|
|
3761
|
-
params,
|
|
3762
|
-
controllerRef: { className, methodName, filePath: sourceFile.getFilePath() },
|
|
3763
|
-
contract: {
|
|
3764
|
-
contractSource: {
|
|
3765
|
-
query: dtoContract?.query ?? null,
|
|
3766
|
-
body: dtoContract?.body ?? null,
|
|
3767
|
-
response: dtoContract?.response ?? "unknown",
|
|
3768
|
-
queryRef: dtoContract?.queryRef ?? null,
|
|
3769
|
-
bodyRef: dtoContract?.bodyRef ?? null,
|
|
3770
|
-
responseRef: dtoContract?.responseRef ?? null,
|
|
3771
|
-
filterFields: dtoContract?.filterFields ?? null,
|
|
3772
|
-
filterFieldTypes: dtoContract?.filterFieldTypes ?? null,
|
|
3773
|
-
filterSource: dtoContract?.filterSource ?? null,
|
|
3774
|
-
bodyZodText: dtoContract?.bodyZodText ?? null,
|
|
3775
|
-
queryZodText: dtoContract?.queryZodText ?? null,
|
|
3776
|
-
formNestedSchemas: dtoContract?.formNestedSchemas ?? null,
|
|
3777
|
-
formWarnings: dtoContract?.formWarnings ?? [],
|
|
3778
|
-
bodySchema: dtoContract?.bodySchema ?? null,
|
|
3779
|
-
querySchema: dtoContract?.querySchema ?? null
|
|
3780
|
-
}
|
|
3781
|
-
}
|
|
3782
|
-
});
|
|
3783
|
-
}
|
|
3311
|
+
const route = applyContractDecorator ? extractContractRoute({
|
|
3312
|
+
cls,
|
|
3313
|
+
method,
|
|
3314
|
+
applyContractDecorator,
|
|
3315
|
+
verb,
|
|
3316
|
+
prefix,
|
|
3317
|
+
className,
|
|
3318
|
+
sourceFile,
|
|
3319
|
+
seenNames
|
|
3320
|
+
}) : extractDtoRoute({
|
|
3321
|
+
cls,
|
|
3322
|
+
method,
|
|
3323
|
+
verb,
|
|
3324
|
+
prefix,
|
|
3325
|
+
className,
|
|
3326
|
+
sourceFile,
|
|
3327
|
+
project,
|
|
3328
|
+
seenNames
|
|
3329
|
+
});
|
|
3330
|
+
if (route) routes.push(route);
|
|
3784
3331
|
}
|
|
3785
3332
|
}
|
|
3786
3333
|
return routes;
|
|
@@ -3789,7 +3336,7 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
3789
3336
|
// src/watch/lock-file.ts
|
|
3790
3337
|
var import_promises10 = require("fs/promises");
|
|
3791
3338
|
var import_promises11 = require("fs/promises");
|
|
3792
|
-
var
|
|
3339
|
+
var import_node_path13 = require("path");
|
|
3793
3340
|
var LOCK_FILE = ".watcher.lock";
|
|
3794
3341
|
function isProcessAlive(pid) {
|
|
3795
3342
|
try {
|
|
@@ -3801,7 +3348,7 @@ function isProcessAlive(pid) {
|
|
|
3801
3348
|
}
|
|
3802
3349
|
async function acquireLock(outDir) {
|
|
3803
3350
|
await (0, import_promises11.mkdir)(outDir, { recursive: true });
|
|
3804
|
-
const lockPath = (0,
|
|
3351
|
+
const lockPath = (0, import_node_path13.join)(outDir, LOCK_FILE);
|
|
3805
3352
|
const lockData = { pid: process.pid, startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
3806
3353
|
try {
|
|
3807
3354
|
const fd = await (0, import_promises10.open)(lockPath, "wx");
|
|
@@ -3841,7 +3388,7 @@ async function watch(config, onChange) {
|
|
|
3841
3388
|
if (lock === null) {
|
|
3842
3389
|
let holderPid = "unknown";
|
|
3843
3390
|
try {
|
|
3844
|
-
const raw = await (0, import_promises12.readFile)((0,
|
|
3391
|
+
const raw = await (0, import_promises12.readFile)((0, import_node_path14.join)(config.codegen.outDir, ".watcher.lock"), "utf8");
|
|
3845
3392
|
const data = JSON.parse(raw);
|
|
3846
3393
|
if (data.pid !== void 0) holderPid = String(data.pid);
|
|
3847
3394
|
} catch {
|
|
@@ -3869,7 +3416,7 @@ async function watch(config, onChange) {
|
|
|
3869
3416
|
}
|
|
3870
3417
|
let pagesDebounceTimer;
|
|
3871
3418
|
const pagesGlob = config.pages?.glob ?? ".nestjs-codegen-no-pages";
|
|
3872
|
-
const pagesWatcher = import_chokidar.default.watch((0,
|
|
3419
|
+
const pagesWatcher = import_chokidar.default.watch((0, import_node_path14.join)(config.codegen.cwd, pagesGlob), {
|
|
3873
3420
|
ignoreInitial: true,
|
|
3874
3421
|
persistent: true,
|
|
3875
3422
|
awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
|
|
@@ -3895,7 +3442,7 @@ async function watch(config, onChange) {
|
|
|
3895
3442
|
pagesWatcher.on("change", schedulePagesRegenerate);
|
|
3896
3443
|
pagesWatcher.on("unlink", schedulePagesRegenerate);
|
|
3897
3444
|
let contractsDebounceTimer;
|
|
3898
|
-
const contractsWatcher = import_chokidar.default.watch((0,
|
|
3445
|
+
const contractsWatcher = import_chokidar.default.watch((0, import_node_path14.join)(config.codegen.cwd, config.contracts.glob), {
|
|
3899
3446
|
ignoreInitial: true,
|
|
3900
3447
|
persistent: true,
|
|
3901
3448
|
awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
|
|
@@ -3925,7 +3472,7 @@ async function watch(config, onChange) {
|
|
|
3925
3472
|
contractsWatcher.on("add", scheduleContractsRegenerate);
|
|
3926
3473
|
contractsWatcher.on("change", scheduleContractsRegenerate);
|
|
3927
3474
|
contractsWatcher.on("unlink", scheduleContractsRegenerate);
|
|
3928
|
-
const formsWatcher = import_chokidar.default.watch((0,
|
|
3475
|
+
const formsWatcher = import_chokidar.default.watch((0, import_node_path14.join)(config.codegen.cwd, config.forms.watch), {
|
|
3929
3476
|
ignoreInitial: true,
|
|
3930
3477
|
persistent: true,
|
|
3931
3478
|
awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
|
|
@@ -3952,7 +3499,7 @@ async function watch(config, onChange) {
|
|
|
3952
3499
|
}
|
|
3953
3500
|
|
|
3954
3501
|
// src/index.ts
|
|
3955
|
-
var VERSION = "0.
|
|
3502
|
+
var VERSION = "0.3.0";
|
|
3956
3503
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3957
3504
|
0 && (module.exports = {
|
|
3958
3505
|
CodegenError,
|
|
@@ -3969,7 +3516,6 @@ var VERSION = "0.2.0";
|
|
|
3969
3516
|
loadConfig,
|
|
3970
3517
|
resolveAdapter,
|
|
3971
3518
|
resolveConfig,
|
|
3972
|
-
watch
|
|
3973
|
-
zodAdapter
|
|
3519
|
+
watch
|
|
3974
3520
|
});
|
|
3975
3521
|
//# sourceMappingURL=index.cjs.map
|