@dudousxd/nestjs-codegen 0.15.0 → 0.17.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 +56 -0
- package/dist/cli/main.cjs +299 -147
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.js +239 -87
- package/dist/cli/main.js.map +1 -1
- 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.map +1 -1
- package/dist/{index-CjIDPMsV.d.cts → index-DCF9QSPY.d.cts} +47 -0
- package/dist/{index-CjIDPMsV.d.ts → index-DCF9QSPY.d.ts} +47 -0
- package/dist/index.cjs +262 -110
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -3
- package/dist/index.d.ts +12 -3
- package/dist/index.js +233 -81
- package/dist/index.js.map +1 -1
- package/dist/nest/index.cjs +261 -110
- package/dist/nest/index.cjs.map +1 -1
- package/dist/nest/index.d.cts +1 -1
- package/dist/nest/index.d.ts +1 -1
- package/dist/nest/index.js +232 -81
- package/dist/nest/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -251,7 +251,7 @@ Run \`nestjs-codegen init\` to create a starter config.`
|
|
|
251
251
|
|
|
252
252
|
// src/generate.ts
|
|
253
253
|
var import_promises12 = require("fs/promises");
|
|
254
|
-
var
|
|
254
|
+
var import_node_path14 = require("path");
|
|
255
255
|
|
|
256
256
|
// src/discovery/pages.ts
|
|
257
257
|
var import_promises2 = require("fs/promises");
|
|
@@ -580,9 +580,10 @@ function inferExpressionType(node) {
|
|
|
580
580
|
|
|
581
581
|
// src/emit/emit-api.ts
|
|
582
582
|
var import_promises3 = require("fs/promises");
|
|
583
|
-
var
|
|
583
|
+
var import_node_path5 = require("path");
|
|
584
584
|
|
|
585
585
|
// src/extension/registry.ts
|
|
586
|
+
var import_node_path4 = require("path");
|
|
586
587
|
var import_ts_morph2 = require("ts-morph");
|
|
587
588
|
function resolveApiSlots(extensions) {
|
|
588
589
|
let layer;
|
|
@@ -615,7 +616,7 @@ function mergeExclusive(target, incoming, {
|
|
|
615
616
|
target.set(key, { value, owner });
|
|
616
617
|
}
|
|
617
618
|
}
|
|
618
|
-
function createExtensionContext(config, getRoutes) {
|
|
619
|
+
function createExtensionContext(config, getRoutes, trackedInputs) {
|
|
619
620
|
let project;
|
|
620
621
|
return {
|
|
621
622
|
cwd: config.codegen.cwd,
|
|
@@ -624,6 +625,13 @@ function createExtensionContext(config, getRoutes) {
|
|
|
624
625
|
get routes() {
|
|
625
626
|
return getRoutes();
|
|
626
627
|
},
|
|
628
|
+
trackInput(...paths) {
|
|
629
|
+
if (!trackedInputs) return;
|
|
630
|
+
for (const path of paths) {
|
|
631
|
+
if (!path) continue;
|
|
632
|
+
trackedInputs.add((0, import_node_path4.relative)(config.codegen.cwd, (0, import_node_path4.resolve)(config.codegen.cwd, path)));
|
|
633
|
+
}
|
|
634
|
+
},
|
|
627
635
|
project() {
|
|
628
636
|
if (!project) {
|
|
629
637
|
project = new import_ts_morph2.Project({
|
|
@@ -683,7 +691,7 @@ function requestShape(route) {
|
|
|
683
691
|
async function emitApi(routes, outDir, opts = {}) {
|
|
684
692
|
await (0, import_promises3.mkdir)(outDir, { recursive: true });
|
|
685
693
|
const content = buildApiFile(routes, outDir, opts);
|
|
686
|
-
await (0, import_promises3.writeFile)((0,
|
|
694
|
+
await (0, import_promises3.writeFile)((0, import_node_path5.join)(outDir, "api.ts"), content, "utf8");
|
|
687
695
|
}
|
|
688
696
|
function splitName(name) {
|
|
689
697
|
return name.split(".");
|
|
@@ -791,7 +799,7 @@ function rawResponseType(c, outDir) {
|
|
|
791
799
|
return c.contractSource.response;
|
|
792
800
|
}
|
|
793
801
|
if (c.controllerRef) {
|
|
794
|
-
let relPath = (0,
|
|
802
|
+
let relPath = (0, import_node_path5.relative)(outDir, c.controllerRef.filePath).replace(/\.ts$/, "");
|
|
795
803
|
if (!relPath.startsWith(".")) relPath = `./${relPath}`;
|
|
796
804
|
return `Awaited<ReturnType<import('${relPath}').${c.controllerRef.className}['${c.controllerRef.methodName}']>>`;
|
|
797
805
|
}
|
|
@@ -1117,6 +1125,11 @@ function buildApiFile(routes, outDir, opts = {}) {
|
|
|
1117
1125
|
config: {},
|
|
1118
1126
|
project: () => {
|
|
1119
1127
|
throw new Error("ExtensionContext.project() is unavailable in standalone emitApi.");
|
|
1128
|
+
},
|
|
1129
|
+
// No manifest is written on the standalone path, so there is nothing for
|
|
1130
|
+
// tracked dependencies to be recorded into — accept and discard them
|
|
1131
|
+
// rather than making every caller supply a sink.
|
|
1132
|
+
trackInput: () => {
|
|
1120
1133
|
}
|
|
1121
1134
|
};
|
|
1122
1135
|
const importsByFile = /* @__PURE__ */ new Map();
|
|
@@ -1172,8 +1185,8 @@ function buildApiFile(routes, outDir, opts = {}) {
|
|
|
1172
1185
|
const emittedNames = /* @__PURE__ */ new Set();
|
|
1173
1186
|
for (const [filePath, names] of importsByFile) {
|
|
1174
1187
|
let relPath;
|
|
1175
|
-
if ((0,
|
|
1176
|
-
relPath = (0,
|
|
1188
|
+
if ((0, import_node_path5.isAbsolute)(filePath)) {
|
|
1189
|
+
relPath = (0, import_node_path5.relative)(outDir, filePath).replace(/\.ts$/, "");
|
|
1177
1190
|
if (!relPath.startsWith(".")) relPath = `./${relPath}`;
|
|
1178
1191
|
} else {
|
|
1179
1192
|
relPath = filePath;
|
|
@@ -1267,7 +1280,7 @@ function buildApiFile(routes, outDir, opts = {}) {
|
|
|
1267
1280
|
|
|
1268
1281
|
// src/emit/emit-cache.ts
|
|
1269
1282
|
var import_promises4 = require("fs/promises");
|
|
1270
|
-
var
|
|
1283
|
+
var import_node_path6 = require("path");
|
|
1271
1284
|
async function emitCache(pages, outDir) {
|
|
1272
1285
|
await (0, import_promises4.mkdir)(outDir, { recursive: true });
|
|
1273
1286
|
const entries = await Promise.all(
|
|
@@ -1281,19 +1294,19 @@ async function emitCache(pages, outDir) {
|
|
|
1281
1294
|
})
|
|
1282
1295
|
);
|
|
1283
1296
|
const cache = { pages: entries };
|
|
1284
|
-
await (0, import_promises4.writeFile)((0,
|
|
1297
|
+
await (0, import_promises4.writeFile)((0, import_node_path6.join)(outDir, "components.json"), `${JSON.stringify(cache, null, 2)}
|
|
1285
1298
|
`, "utf8");
|
|
1286
1299
|
}
|
|
1287
1300
|
|
|
1288
1301
|
// src/emit/emit-forms.ts
|
|
1289
1302
|
var import_promises5 = require("fs/promises");
|
|
1290
|
-
var
|
|
1303
|
+
var import_node_path7 = require("path");
|
|
1291
1304
|
async function emitForms(routes, outDir, config, adapter) {
|
|
1292
1305
|
if (config && config.enabled === false) return false;
|
|
1293
1306
|
const content = buildFormsFileWithAdapter(routes, outDir, adapter, config);
|
|
1294
1307
|
if (content === null) return false;
|
|
1295
1308
|
await (0, import_promises5.mkdir)(outDir, { recursive: true });
|
|
1296
|
-
await (0, import_promises5.writeFile)((0,
|
|
1309
|
+
await (0, import_promises5.writeFile)((0, import_node_path7.join)(outDir, "forms.ts"), content, "utf8");
|
|
1297
1310
|
return true;
|
|
1298
1311
|
}
|
|
1299
1312
|
function pascal(segment) {
|
|
@@ -1306,7 +1319,7 @@ function deriveBaseName(routeName) {
|
|
|
1306
1319
|
return { method, full };
|
|
1307
1320
|
}
|
|
1308
1321
|
function relImport(outDir, filePath) {
|
|
1309
|
-
let relPath = (0,
|
|
1322
|
+
let relPath = (0, import_node_path7.relative)(outDir, filePath).replace(/\.ts$/, "");
|
|
1310
1323
|
if (!relPath.startsWith(".")) relPath = `./${relPath}`;
|
|
1311
1324
|
return relPath;
|
|
1312
1325
|
}
|
|
@@ -1580,7 +1593,7 @@ function buildFormsFileWithAdapter(routes, outDir, adapter, config) {
|
|
|
1580
1593
|
|
|
1581
1594
|
// src/emit/emit-index.ts
|
|
1582
1595
|
var import_promises6 = require("fs/promises");
|
|
1583
|
-
var
|
|
1596
|
+
var import_node_path8 = require("path");
|
|
1584
1597
|
async function emitIndex(outDir, hasContracts = false, hasForms = false) {
|
|
1585
1598
|
await (0, import_promises6.mkdir)(outDir, { recursive: true });
|
|
1586
1599
|
const exports2 = ["export * from './pages.js';", "export * from './routes.js';"];
|
|
@@ -1593,12 +1606,12 @@ async function emitIndex(outDir, hasContracts = false, hasForms = false) {
|
|
|
1593
1606
|
const content = ["// Generated by @dudousxd/nestjs-codegen. Do not edit.", ...exports2, ""].join(
|
|
1594
1607
|
"\n"
|
|
1595
1608
|
);
|
|
1596
|
-
await (0, import_promises6.writeFile)((0,
|
|
1609
|
+
await (0, import_promises6.writeFile)((0, import_node_path8.join)(outDir, "index.d.ts"), content, "utf8");
|
|
1597
1610
|
}
|
|
1598
1611
|
|
|
1599
1612
|
// src/emit/emit-mocks.ts
|
|
1600
1613
|
var import_promises7 = require("fs/promises");
|
|
1601
|
-
var
|
|
1614
|
+
var import_node_path9 = require("path");
|
|
1602
1615
|
|
|
1603
1616
|
// src/ir/schema-node-to-json-schema.ts
|
|
1604
1617
|
var DEFAULT_CTX = { refPrefix: "#/components/schemas/" };
|
|
@@ -1925,12 +1938,12 @@ async function emitMocks(routes, outDir, opts = {}) {
|
|
|
1925
1938
|
await (0, import_promises7.mkdir)(outDir, { recursive: true });
|
|
1926
1939
|
const content = buildMocksFile(routes, opts);
|
|
1927
1940
|
const fileName = opts.fileName ?? "mocks.ts";
|
|
1928
|
-
await (0, import_promises7.writeFile)((0,
|
|
1941
|
+
await (0, import_promises7.writeFile)((0, import_node_path9.join)(outDir, fileName), content, "utf8");
|
|
1929
1942
|
}
|
|
1930
1943
|
|
|
1931
1944
|
// src/emit/emit-openapi.ts
|
|
1932
1945
|
var import_promises8 = require("fs/promises");
|
|
1933
|
-
var
|
|
1946
|
+
var import_node_path10 = require("path");
|
|
1934
1947
|
var REF_PREFIX2 = "#/components/schemas/";
|
|
1935
1948
|
function toOpenApiPath(path) {
|
|
1936
1949
|
return path.replace(/:([^/]+)/g, "{$1}");
|
|
@@ -2051,13 +2064,13 @@ async function emitOpenApi(routes, outDir, opts = {}) {
|
|
|
2051
2064
|
await (0, import_promises8.mkdir)(outDir, { recursive: true });
|
|
2052
2065
|
const doc = buildOpenApiSpec(routes, opts);
|
|
2053
2066
|
const fileName = opts.fileName ?? "openapi.json";
|
|
2054
|
-
await (0, import_promises8.writeFile)((0,
|
|
2067
|
+
await (0, import_promises8.writeFile)((0, import_node_path10.join)(outDir, fileName), `${JSON.stringify(doc, null, 2)}
|
|
2055
2068
|
`, "utf8");
|
|
2056
2069
|
}
|
|
2057
2070
|
|
|
2058
2071
|
// src/emit/emit-pages.ts
|
|
2059
2072
|
var import_promises9 = require("fs/promises");
|
|
2060
|
-
var
|
|
2073
|
+
var import_node_path11 = require("path");
|
|
2061
2074
|
async function emitPages(pages, outDir, _options = {}) {
|
|
2062
2075
|
await (0, import_promises9.mkdir)(outDir, { recursive: true });
|
|
2063
2076
|
const pageNameUnion = pages.length > 0 ? pages.map((p) => JSON.stringify(p.name)).join(" | ") : "never";
|
|
@@ -2078,7 +2091,7 @@ ${augBody}
|
|
|
2078
2091
|
}
|
|
2079
2092
|
${sharedPropsBlock}}
|
|
2080
2093
|
`;
|
|
2081
|
-
await (0, import_promises9.writeFile)((0,
|
|
2094
|
+
await (0, import_promises9.writeFile)((0, import_node_path11.join)(outDir, "pages.d.ts"), content, "utf8");
|
|
2082
2095
|
}
|
|
2083
2096
|
function buildSharedPropsBlock(sharedProps) {
|
|
2084
2097
|
if (!sharedProps) return "";
|
|
@@ -2097,7 +2110,7 @@ ${propsBody}
|
|
|
2097
2110
|
`;
|
|
2098
2111
|
}
|
|
2099
2112
|
function buildAugmentationType(page, outDir) {
|
|
2100
|
-
let importPath = (0,
|
|
2113
|
+
let importPath = (0, import_node_path11.relative)(outDir, page.absolutePath).replace(/\.(tsx?|vue|svelte)$/, "");
|
|
2101
2114
|
if (!importPath.startsWith(".")) {
|
|
2102
2115
|
importPath = `./${importPath}`;
|
|
2103
2116
|
}
|
|
@@ -2109,11 +2122,11 @@ function needsQuotes(name) {
|
|
|
2109
2122
|
|
|
2110
2123
|
// src/emit/emit-routes.ts
|
|
2111
2124
|
var import_promises10 = require("fs/promises");
|
|
2112
|
-
var
|
|
2125
|
+
var import_node_path12 = require("path");
|
|
2113
2126
|
async function emitRoutes(routes, outDir) {
|
|
2114
2127
|
await (0, import_promises10.mkdir)(outDir, { recursive: true });
|
|
2115
2128
|
const content = buildRoutesFile(routes);
|
|
2116
|
-
await (0, import_promises10.writeFile)((0,
|
|
2129
|
+
await (0, import_promises10.writeFile)((0, import_node_path12.join)(outDir, "routes.ts"), content, "utf8");
|
|
2117
2130
|
}
|
|
2118
2131
|
function buildRoutesFile(routes) {
|
|
2119
2132
|
if (routes.length === 0) {
|
|
@@ -2228,7 +2241,7 @@ function buildEmpty() {
|
|
|
2228
2241
|
// src/generate-manifest.ts
|
|
2229
2242
|
var import_node_crypto = require("crypto");
|
|
2230
2243
|
var import_promises11 = require("fs/promises");
|
|
2231
|
-
var
|
|
2244
|
+
var import_node_path13 = require("path");
|
|
2232
2245
|
var import_fast_glob2 = __toESM(require("fast-glob"), 1);
|
|
2233
2246
|
var MANIFEST_FILE = ".codegen-manifest.json";
|
|
2234
2247
|
var LOCK_FILE = ".watcher.lock";
|
|
@@ -2251,6 +2264,9 @@ function isManifestShape(value) {
|
|
|
2251
2264
|
if (candidate.configKeyHashes !== void 0 && !isStringRecord(candidate.configKeyHashes)) {
|
|
2252
2265
|
return false;
|
|
2253
2266
|
}
|
|
2267
|
+
if (candidate.extraInputs !== void 0 && (!Array.isArray(candidate.extraInputs) || !candidate.extraInputs.every((entry) => typeof entry === "string"))) {
|
|
2268
|
+
return false;
|
|
2269
|
+
}
|
|
2254
2270
|
if (!Array.isArray(candidate.files)) return false;
|
|
2255
2271
|
return candidate.files.every((entry) => typeof entry === "string");
|
|
2256
2272
|
}
|
|
@@ -2290,26 +2306,35 @@ async function discoverInputFiles(config) {
|
|
|
2290
2306
|
const matched = await (0, import_fast_glob2.default)(globs, { cwd, absolute: true, onlyFiles: true });
|
|
2291
2307
|
return [...new Set(matched)].sort();
|
|
2292
2308
|
}
|
|
2293
|
-
async function computeInputsHash(config) {
|
|
2309
|
+
async function computeInputsHash(config, extraInputs = []) {
|
|
2294
2310
|
const hash = (0, import_node_crypto.createHash)("sha256");
|
|
2295
2311
|
hash.update(`version:${VERSION}
|
|
2296
2312
|
`);
|
|
2297
2313
|
hash.update(`config:${serializeConfig(config)}
|
|
2298
2314
|
`);
|
|
2299
|
-
const inputFiles = await discoverInputFiles(config);
|
|
2300
2315
|
const cwd = config.codegen.cwd;
|
|
2301
|
-
|
|
2316
|
+
const globbed = await discoverInputFiles(config);
|
|
2317
|
+
const globbedRelative = new Set(globbed.map((file) => (0, import_node_path13.relative)(cwd, file)));
|
|
2318
|
+
const extra = [...new Set(extraInputs)].filter((file) => !globbedRelative.has(file)).sort();
|
|
2319
|
+
for (const file of globbed) {
|
|
2302
2320
|
const contents = await (0, import_promises11.readFile)(file, "utf8");
|
|
2303
|
-
hash.update(`file:${(0,
|
|
2321
|
+
hash.update(`file:${(0, import_node_path13.relative)(cwd, file)}
|
|
2304
2322
|
`);
|
|
2305
2323
|
hash.update(contents);
|
|
2306
2324
|
hash.update("\n");
|
|
2307
2325
|
}
|
|
2326
|
+
for (const file of extra) {
|
|
2327
|
+
const contents = await (0, import_promises11.readFile)((0, import_node_path13.join)(cwd, file), "utf8").catch(() => null);
|
|
2328
|
+
hash.update(`extra:${file}
|
|
2329
|
+
`);
|
|
2330
|
+
hash.update(contents ?? "\0missing");
|
|
2331
|
+
hash.update("\n");
|
|
2332
|
+
}
|
|
2308
2333
|
return hash.digest("hex");
|
|
2309
2334
|
}
|
|
2310
2335
|
async function readManifest(outDir) {
|
|
2311
2336
|
try {
|
|
2312
|
-
const raw = await (0, import_promises11.readFile)((0,
|
|
2337
|
+
const raw = await (0, import_promises11.readFile)((0, import_node_path13.join)(outDir, MANIFEST_FILE), "utf8");
|
|
2313
2338
|
const parsed = JSON.parse(raw);
|
|
2314
2339
|
if (!isManifestShape(parsed)) return null;
|
|
2315
2340
|
return {
|
|
@@ -2318,6 +2343,7 @@ async function readManifest(outDir) {
|
|
|
2318
2343
|
...parsed.entryPoint ? { entryPoint: parsed.entryPoint } : {},
|
|
2319
2344
|
...parsed.configHash ? { configHash: parsed.configHash } : {},
|
|
2320
2345
|
...parsed.configKeyHashes ? { configKeyHashes: parsed.configKeyHashes } : {},
|
|
2346
|
+
...parsed.extraInputs ? { extraInputs: parsed.extraInputs } : {},
|
|
2321
2347
|
files: parsed.files
|
|
2322
2348
|
};
|
|
2323
2349
|
} catch {
|
|
@@ -2328,7 +2354,7 @@ function computeConfigHash(config) {
|
|
|
2328
2354
|
return (0, import_node_crypto.createHash)("sha256").update(serializeConfig(config)).digest("hex");
|
|
2329
2355
|
}
|
|
2330
2356
|
async function writeManifest(outDir, manifest) {
|
|
2331
|
-
await (0, import_promises11.writeFile)((0,
|
|
2357
|
+
await (0, import_promises11.writeFile)((0, import_node_path13.join)(outDir, MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}
|
|
2332
2358
|
`, "utf8");
|
|
2333
2359
|
}
|
|
2334
2360
|
async function listOutputFiles(outDir) {
|
|
@@ -2336,11 +2362,11 @@ async function listOutputFiles(outDir) {
|
|
|
2336
2362
|
async function walk(dir) {
|
|
2337
2363
|
const entries = await (0, import_promises11.readdir)(dir, { withFileTypes: true }).catch(() => []);
|
|
2338
2364
|
for (const entry of entries) {
|
|
2339
|
-
const abs = (0,
|
|
2365
|
+
const abs = (0, import_node_path13.join)(dir, entry.name);
|
|
2340
2366
|
if (entry.isDirectory()) {
|
|
2341
2367
|
await walk(abs);
|
|
2342
2368
|
} else if (entry.isFile()) {
|
|
2343
|
-
const rel = (0,
|
|
2369
|
+
const rel = (0, import_node_path13.relative)(outDir, abs);
|
|
2344
2370
|
if (rel === MANIFEST_FILE || rel === LOCK_FILE) continue;
|
|
2345
2371
|
found.push(rel);
|
|
2346
2372
|
}
|
|
@@ -2377,8 +2403,8 @@ function driftGuardMessage(outDir, previousEntryPoint, currentEntryPoint, differ
|
|
|
2377
2403
|
}
|
|
2378
2404
|
async function generate(config, inputRoutes = [], entryPoint = "cli") {
|
|
2379
2405
|
setCodegenDebug(config.debug);
|
|
2380
|
-
const inputsHash = await computeInputsHash(config);
|
|
2381
2406
|
const manifest = await readManifest(config.codegen.outDir);
|
|
2407
|
+
const inputsHash = await computeInputsHash(config, manifest?.extraInputs ?? []);
|
|
2382
2408
|
if (await isManifestFresh(config.codegen.outDir, manifest, inputsHash)) {
|
|
2383
2409
|
console.log(`[nestjs-codegen] ${config.codegen.outDir} up to date, skipped`);
|
|
2384
2410
|
return;
|
|
@@ -2399,7 +2425,8 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
|
|
|
2399
2425
|
}
|
|
2400
2426
|
const extensions = config.extensions ?? [];
|
|
2401
2427
|
let routes = inputRoutes;
|
|
2402
|
-
const
|
|
2428
|
+
const trackedInputs = /* @__PURE__ */ new Set();
|
|
2429
|
+
const ctx = createExtensionContext(config, () => routes, trackedInputs);
|
|
2403
2430
|
if (extensions.length > 0) {
|
|
2404
2431
|
routes = await applyTransformRoutes(routes, extensions, ctx);
|
|
2405
2432
|
}
|
|
@@ -2453,31 +2480,34 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
|
|
|
2453
2480
|
if (extensions.length > 0) {
|
|
2454
2481
|
const extraFiles = await collectEmittedFiles(extensions, ctx);
|
|
2455
2482
|
for (const file of extraFiles) {
|
|
2456
|
-
const dest = (0,
|
|
2457
|
-
await (0, import_promises12.mkdir)((0,
|
|
2483
|
+
const dest = (0, import_node_path14.join)(config.codegen.outDir, file.path);
|
|
2484
|
+
await (0, import_promises12.mkdir)((0, import_node_path14.dirname)(dest), { recursive: true });
|
|
2458
2485
|
await (0, import_promises12.writeFile)(dest, file.contents, "utf8");
|
|
2459
2486
|
}
|
|
2460
2487
|
}
|
|
2461
2488
|
const outputFiles = await listOutputFiles(config.codegen.outDir);
|
|
2489
|
+
const extraInputs = [...trackedInputs].sort();
|
|
2490
|
+
const recordedHash = extraInputs.length > 0 ? await computeInputsHash(config, extraInputs) : inputsHash;
|
|
2462
2491
|
await writeManifest(config.codegen.outDir, {
|
|
2463
2492
|
version: VERSION,
|
|
2464
|
-
hash:
|
|
2493
|
+
hash: recordedHash,
|
|
2465
2494
|
entryPoint,
|
|
2466
2495
|
configHash,
|
|
2467
2496
|
configKeyHashes,
|
|
2468
|
-
files: outputFiles
|
|
2497
|
+
files: outputFiles,
|
|
2498
|
+
...extraInputs.length > 0 ? { extraInputs } : {}
|
|
2469
2499
|
});
|
|
2470
2500
|
}
|
|
2471
2501
|
|
|
2472
2502
|
// src/watch/watcher.ts
|
|
2473
2503
|
var import_promises15 = require("fs/promises");
|
|
2474
|
-
var
|
|
2504
|
+
var import_node_path18 = require("path");
|
|
2475
2505
|
var import_chokidar = __toESM(require("chokidar"), 1);
|
|
2476
2506
|
|
|
2477
2507
|
// src/discovery/contracts-fast.ts
|
|
2478
|
-
var
|
|
2508
|
+
var import_node_path16 = require("path");
|
|
2479
2509
|
var import_fast_glob3 = __toESM(require("fast-glob"), 1);
|
|
2480
|
-
var
|
|
2510
|
+
var import_ts_morph10 = require("ts-morph");
|
|
2481
2511
|
|
|
2482
2512
|
// src/discovery/dto-type-resolver.ts
|
|
2483
2513
|
var import_ts_morph7 = require("ts-morph");
|
|
@@ -2487,7 +2517,7 @@ var import_ts_morph4 = require("ts-morph");
|
|
|
2487
2517
|
|
|
2488
2518
|
// src/discovery/type-ref-resolution.ts
|
|
2489
2519
|
var import_node_fs = require("fs");
|
|
2490
|
-
var
|
|
2520
|
+
var import_node_path15 = require("path");
|
|
2491
2521
|
var import_ts_morph3 = require("ts-morph");
|
|
2492
2522
|
var _EMPTY_CTX = { projectRoot: "", tsconfigPaths: null };
|
|
2493
2523
|
var _ctxByProject = /* @__PURE__ */ new WeakMap();
|
|
@@ -2539,12 +2569,12 @@ function findTypeInFile(name, file) {
|
|
|
2539
2569
|
}
|
|
2540
2570
|
function resolveModuleSpecifier(moduleSpecifier, sourceFile, project) {
|
|
2541
2571
|
if (moduleSpecifier.startsWith(".")) {
|
|
2542
|
-
const dir = (0,
|
|
2572
|
+
const dir = (0, import_node_path15.dirname)(sourceFile.getFilePath());
|
|
2543
2573
|
const noExt = moduleSpecifier.replace(/\.(js|ts)$/, "");
|
|
2544
2574
|
return [
|
|
2545
|
-
(0,
|
|
2546
|
-
(0,
|
|
2547
|
-
(0,
|
|
2575
|
+
(0, import_node_path15.resolve)(dir, `${noExt}.ts`),
|
|
2576
|
+
(0, import_node_path15.resolve)(dir, `${moduleSpecifier}.ts`),
|
|
2577
|
+
(0, import_node_path15.resolve)(dir, moduleSpecifier, "index.ts")
|
|
2548
2578
|
];
|
|
2549
2579
|
}
|
|
2550
2580
|
const ctx = _ctxFor(project);
|
|
@@ -2565,8 +2595,8 @@ function resolveModuleSpecifier(moduleSpecifier, sourceFile, project) {
|
|
|
2565
2595
|
const rest = moduleSpecifier.slice(prefix.length);
|
|
2566
2596
|
const candidates = [];
|
|
2567
2597
|
for (const mapping of mappings) {
|
|
2568
|
-
const resolved = (0,
|
|
2569
|
-
candidates.push(`${resolved}.ts`, (0,
|
|
2598
|
+
const resolved = (0, import_node_path15.resolve)(baseUrl, mapping.replace("*", rest));
|
|
2599
|
+
candidates.push(`${resolved}.ts`, (0, import_node_path15.resolve)(resolved, "index.ts"));
|
|
2570
2600
|
}
|
|
2571
2601
|
dbg(" resolved candidates:", candidates);
|
|
2572
2602
|
return candidates;
|
|
@@ -3456,6 +3486,12 @@ function toFilterFieldType(name, r) {
|
|
|
3456
3486
|
}
|
|
3457
3487
|
|
|
3458
3488
|
// src/discovery/filter-for.ts
|
|
3489
|
+
function resolveMixinEntityClass(mixin, project) {
|
|
3490
|
+
const entityArg = mixin?.classArgs[0];
|
|
3491
|
+
if (!entityArg) return void 0;
|
|
3492
|
+
const file = project.getSourceFile(entityArg.filePath) ?? project.addSourceFileAtPathIfExists(entityArg.filePath);
|
|
3493
|
+
return file?.getClass(entityArg.name);
|
|
3494
|
+
}
|
|
3459
3495
|
function classifyFilterForHint(typeInit) {
|
|
3460
3496
|
if (import_ts_morph6.Node.isStringLiteral(typeInit)) {
|
|
3461
3497
|
switch (typeInit.getLiteralValue()) {
|
|
@@ -3529,7 +3565,9 @@ function extractFilterForHints(classDecl, project) {
|
|
|
3529
3565
|
}
|
|
3530
3566
|
return hints;
|
|
3531
3567
|
}
|
|
3532
|
-
function extractApplyFilterInfo(method, sourceFile, project) {
|
|
3568
|
+
function extractApplyFilterInfo(method, sourceFile, project, mixin) {
|
|
3569
|
+
const declFile = method.getSourceFile();
|
|
3570
|
+
const mixinEntity = resolveMixinEntityClass(mixin, project);
|
|
3533
3571
|
for (const param of method.getParameters()) {
|
|
3534
3572
|
const filterDecorator = param.getDecorators().find((d) => d.getName() === "ApplyFilter");
|
|
3535
3573
|
if (!filterDecorator) continue;
|
|
@@ -3549,12 +3587,12 @@ function extractApplyFilterInfo(method, sourceFile, project) {
|
|
|
3549
3587
|
}
|
|
3550
3588
|
}
|
|
3551
3589
|
const filterClassName = filterClassArg.getText();
|
|
3552
|
-
const resolved = findType(filterClassName,
|
|
3553
|
-
|
|
3554
|
-
|
|
3590
|
+
const resolved = findType(filterClassName, declFile, project);
|
|
3591
|
+
const classDecl = resolved?.kind === "class" ? resolved.decl : resolveLocalClassDeclaration(filterClassArg);
|
|
3592
|
+
if (classDecl) {
|
|
3555
3593
|
let fieldTypes = extractClassPropertyTypes(classDecl, project);
|
|
3556
3594
|
if (fieldTypes.length === 0) {
|
|
3557
|
-
fieldTypes = extractFilterableEntityFields(classDecl, project);
|
|
3595
|
+
fieldTypes = extractFilterableEntityFields(classDecl, project, mixinEntity);
|
|
3558
3596
|
}
|
|
3559
3597
|
const filterForHints = extractFilterForHints(classDecl, project);
|
|
3560
3598
|
if (filterForHints.size > 0) {
|
|
@@ -3639,22 +3677,29 @@ function extractClassPropertyTypes(classDecl, project) {
|
|
|
3639
3677
|
}
|
|
3640
3678
|
return fields;
|
|
3641
3679
|
}
|
|
3642
|
-
function
|
|
3680
|
+
function resolveLocalClassDeclaration(identifier) {
|
|
3681
|
+
if (!import_ts_morph6.Node.isIdentifier(identifier)) return void 0;
|
|
3682
|
+
return identifier.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 && import_ts_morph6.Node.isClassDeclaration(n));
|
|
3683
|
+
}
|
|
3684
|
+
function resolveDeclaredEntity(optionsArg, filterClass, project) {
|
|
3685
|
+
if (!import_ts_morph6.Node.isObjectLiteralExpression(optionsArg)) return void 0;
|
|
3686
|
+
const entityProp = optionsArg.getProperty("entity");
|
|
3687
|
+
if (!entityProp || !import_ts_morph6.Node.isPropertyAssignment(entityProp)) return void 0;
|
|
3688
|
+
const entityInit = entityProp.getInitializer();
|
|
3689
|
+
if (!entityInit || !import_ts_morph6.Node.isIdentifier(entityInit)) return void 0;
|
|
3690
|
+
const resolved = findType(entityInit.getText(), filterClass.getSourceFile(), project);
|
|
3691
|
+
if (!resolved || resolved.kind !== "class") return void 0;
|
|
3692
|
+
return resolved.decl;
|
|
3693
|
+
}
|
|
3694
|
+
function extractFilterableEntityFields(filterClass, project, entityOverride) {
|
|
3643
3695
|
const filterableDecorator = filterClass.getDecorators().find((d) => d.getName() === "Filterable");
|
|
3644
3696
|
if (!filterableDecorator) return [];
|
|
3645
3697
|
const args = filterableDecorator.getArguments();
|
|
3646
3698
|
if (args.length === 0) return [];
|
|
3647
3699
|
const optionsArg = args[0];
|
|
3648
3700
|
if (!import_ts_morph6.Node.isObjectLiteralExpression(optionsArg)) return [];
|
|
3649
|
-
const
|
|
3650
|
-
if (!
|
|
3651
|
-
const entityInit = entityProp.getInitializer();
|
|
3652
|
-
if (!entityInit || !import_ts_morph6.Node.isIdentifier(entityInit)) return [];
|
|
3653
|
-
const entityName = entityInit.getText();
|
|
3654
|
-
const filterSourceFile = filterClass.getSourceFile();
|
|
3655
|
-
const resolvedEntity = findType(entityName, filterSourceFile, project);
|
|
3656
|
-
if (!resolvedEntity || resolvedEntity.kind !== "class") return [];
|
|
3657
|
-
const entityDecl = resolvedEntity.decl;
|
|
3701
|
+
const entityDecl = entityOverride ?? resolveDeclaredEntity(optionsArg, filterClass, project);
|
|
3702
|
+
if (!entityDecl) return [];
|
|
3658
3703
|
const fields = collectEntityFields(
|
|
3659
3704
|
entityDecl,
|
|
3660
3705
|
entityDecl.getSourceFile(),
|
|
@@ -4079,9 +4124,9 @@ function unwrapNamedContainer(node, names) {
|
|
|
4079
4124
|
}
|
|
4080
4125
|
return node;
|
|
4081
4126
|
}
|
|
4082
|
-
function extractDtoContract(method, sourceFile, project) {
|
|
4127
|
+
function extractDtoContract(method, sourceFile, project, mixin) {
|
|
4083
4128
|
let body = extractBodyType(method, sourceFile, project);
|
|
4084
|
-
const filterInfo = extractApplyFilterInfo(method, sourceFile, project);
|
|
4129
|
+
const filterInfo = extractApplyFilterInfo(method, sourceFile, project, mixin);
|
|
4085
4130
|
const query = extractQueryType(method, sourceFile, project);
|
|
4086
4131
|
const uploads = extractUploadedFiles(method);
|
|
4087
4132
|
const multipartBody = uploads.fields ? `{ ${uploads.fields} }` : null;
|
|
@@ -4191,12 +4236,94 @@ function resolveParamClass(method, decoratorName, sourceFile, project) {
|
|
|
4191
4236
|
return null;
|
|
4192
4237
|
}
|
|
4193
4238
|
|
|
4194
|
-
// src/discovery/
|
|
4239
|
+
// src/discovery/heritage.ts
|
|
4195
4240
|
var import_ts_morph8 = require("ts-morph");
|
|
4241
|
+
function unwrapExpression(node) {
|
|
4242
|
+
let current = node;
|
|
4243
|
+
while (import_ts_morph8.Node.isAsExpression(current) || import_ts_morph8.Node.isSatisfiesExpression(current) || import_ts_morph8.Node.isParenthesizedExpression(current) || import_ts_morph8.Node.isTypeAssertion(current)) {
|
|
4244
|
+
current = current.getExpression();
|
|
4245
|
+
}
|
|
4246
|
+
return current;
|
|
4247
|
+
}
|
|
4248
|
+
function resolveReturnedClass(factoryDecl) {
|
|
4249
|
+
const body = factoryDecl.getBody();
|
|
4250
|
+
if (!body) return void 0;
|
|
4251
|
+
const returns = body.getDescendants().filter((n) => import_ts_morph8.Node.isReturnStatement(n));
|
|
4252
|
+
for (const ret of returns) {
|
|
4253
|
+
const expr = ret.getExpression();
|
|
4254
|
+
if (!expr) continue;
|
|
4255
|
+
const inner = unwrapExpression(expr);
|
|
4256
|
+
if (import_ts_morph8.Node.isClassExpression(inner)) {
|
|
4257
|
+
return inner;
|
|
4258
|
+
}
|
|
4259
|
+
if (import_ts_morph8.Node.isIdentifier(inner)) {
|
|
4260
|
+
const name = inner.getText();
|
|
4261
|
+
const decl = body.getDescendants().find((n) => import_ts_morph8.Node.isClassDeclaration(n) && n.getName() === name);
|
|
4262
|
+
if (decl) return decl;
|
|
4263
|
+
}
|
|
4264
|
+
}
|
|
4265
|
+
return void 0;
|
|
4266
|
+
}
|
|
4267
|
+
function resolveInheritedMethods(cls) {
|
|
4268
|
+
const expr = cls.getExtends()?.getExpression();
|
|
4269
|
+
if (!expr || !import_ts_morph8.Node.isCallExpression(expr)) return void 0;
|
|
4270
|
+
const callee = expr.getExpression();
|
|
4271
|
+
if (!import_ts_morph8.Node.isIdentifier(callee)) return void 0;
|
|
4272
|
+
const factoryDecl = callee.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => import_ts_morph8.Node.isFunctionDeclaration(n));
|
|
4273
|
+
if (!factoryDecl) return void 0;
|
|
4274
|
+
const returnedClass = resolveReturnedClass(factoryDecl);
|
|
4275
|
+
if (!returnedClass) return void 0;
|
|
4276
|
+
const classArgs = [];
|
|
4277
|
+
for (const arg of expr.getArguments()) {
|
|
4278
|
+
if (!import_ts_morph8.Node.isIdentifier(arg)) continue;
|
|
4279
|
+
const decl = arg.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => import_ts_morph8.Node.isClassDeclaration(n));
|
|
4280
|
+
if (decl) {
|
|
4281
|
+
classArgs.push({
|
|
4282
|
+
name: decl.getName() ?? arg.getText(),
|
|
4283
|
+
filePath: decl.getSourceFile().getFilePath()
|
|
4284
|
+
});
|
|
4285
|
+
}
|
|
4286
|
+
}
|
|
4287
|
+
return {
|
|
4288
|
+
methods: returnedClass.getMethods(),
|
|
4289
|
+
factoryName: callee.getText(),
|
|
4290
|
+
factoryFilePath: factoryDecl.getSourceFile().getFilePath(),
|
|
4291
|
+
classArgs
|
|
4292
|
+
};
|
|
4293
|
+
}
|
|
4294
|
+
var mixinTypeProject;
|
|
4295
|
+
function getMixinTypeProject() {
|
|
4296
|
+
mixinTypeProject ??= new import_ts_morph8.Project({
|
|
4297
|
+
skipAddingFilesFromTsConfig: true,
|
|
4298
|
+
compilerOptions: { strict: true }
|
|
4299
|
+
});
|
|
4300
|
+
return mixinTypeProject;
|
|
4301
|
+
}
|
|
4302
|
+
function clearMixinTypeProject() {
|
|
4303
|
+
mixinTypeProject = void 0;
|
|
4304
|
+
}
|
|
4305
|
+
function resolveInstantiatedReturnType(cls, methodName) {
|
|
4306
|
+
const filePath = cls.getSourceFile().getFilePath();
|
|
4307
|
+
const className = cls.getName();
|
|
4308
|
+
if (!className) return void 0;
|
|
4309
|
+
const project = getMixinTypeProject();
|
|
4310
|
+
const sourceFile = project.getSourceFile(filePath) ?? project.addSourceFileAtPathIfExists(filePath);
|
|
4311
|
+
const typedCls = sourceFile?.getClass(className);
|
|
4312
|
+
if (!typedCls) return void 0;
|
|
4313
|
+
const prop = typedCls.getType().getProperty(methodName);
|
|
4314
|
+
if (!prop) return void 0;
|
|
4315
|
+
const returnType = prop.getTypeAtLocation(typedCls).getCallSignatures()[0]?.getReturnType();
|
|
4316
|
+
if (!returnType) return void 0;
|
|
4317
|
+
const unwrapped = returnType.getSymbol()?.getName() === "Promise" ? returnType.getTypeArguments()[0] ?? returnType : returnType;
|
|
4318
|
+
return unwrapped.getText(typedCls);
|
|
4319
|
+
}
|
|
4320
|
+
|
|
4321
|
+
// src/discovery/zod-ast-to-ts.ts
|
|
4322
|
+
var import_ts_morph9 = require("ts-morph");
|
|
4196
4323
|
function zodAstToTs(node) {
|
|
4197
|
-
if (!
|
|
4324
|
+
if (!import_ts_morph9.Node.isCallExpression(node)) return "unknown";
|
|
4198
4325
|
const expr = node.getExpression();
|
|
4199
|
-
if (
|
|
4326
|
+
if (import_ts_morph9.Node.isPropertyAccessExpression(expr)) {
|
|
4200
4327
|
const methodName = expr.getName();
|
|
4201
4328
|
const receiver = expr.getExpression();
|
|
4202
4329
|
if (methodName === "optional") {
|
|
@@ -4220,17 +4347,17 @@ function zodAstToTs(node) {
|
|
|
4220
4347
|
case "literal": {
|
|
4221
4348
|
const lit = args[0];
|
|
4222
4349
|
if (!lit) return "unknown";
|
|
4223
|
-
if (
|
|
4224
|
-
if (
|
|
4225
|
-
if (lit.getKind() ===
|
|
4226
|
-
if (lit.getKind() ===
|
|
4350
|
+
if (import_ts_morph9.Node.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
|
|
4351
|
+
if (import_ts_morph9.Node.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
|
|
4352
|
+
if (lit.getKind() === import_ts_morph9.SyntaxKind.TrueKeyword) return "true";
|
|
4353
|
+
if (lit.getKind() === import_ts_morph9.SyntaxKind.FalseKeyword) return "false";
|
|
4227
4354
|
return "unknown";
|
|
4228
4355
|
}
|
|
4229
4356
|
case "enum": {
|
|
4230
4357
|
const arrArg = args[0];
|
|
4231
|
-
if (!arrArg || !
|
|
4358
|
+
if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
4232
4359
|
const members = arrArg.getElements().map(
|
|
4233
|
-
(el) =>
|
|
4360
|
+
(el) => import_ts_morph9.Node.isStringLiteral(el) ? JSON.stringify(el.getLiteralValue()) : "unknown"
|
|
4234
4361
|
);
|
|
4235
4362
|
return members.join(" | ");
|
|
4236
4363
|
}
|
|
@@ -4241,10 +4368,10 @@ function zodAstToTs(node) {
|
|
|
4241
4368
|
}
|
|
4242
4369
|
case "object": {
|
|
4243
4370
|
const objArg = args[0];
|
|
4244
|
-
if (!objArg || !
|
|
4371
|
+
if (!objArg || !import_ts_morph9.Node.isObjectLiteralExpression(objArg)) return "unknown";
|
|
4245
4372
|
const lines = [];
|
|
4246
4373
|
for (const prop of objArg.getProperties()) {
|
|
4247
|
-
if (!
|
|
4374
|
+
if (!import_ts_morph9.Node.isPropertyAssignment(prop)) continue;
|
|
4248
4375
|
const key = prop.getName();
|
|
4249
4376
|
const valNode = prop.getInitializer();
|
|
4250
4377
|
if (!valNode) continue;
|
|
@@ -4256,7 +4383,7 @@ function zodAstToTs(node) {
|
|
|
4256
4383
|
}
|
|
4257
4384
|
case "union": {
|
|
4258
4385
|
const arrArg = args[0];
|
|
4259
|
-
if (!arrArg || !
|
|
4386
|
+
if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
4260
4387
|
return arrArg.getElements().map(zodAstToTs).join(" | ");
|
|
4261
4388
|
}
|
|
4262
4389
|
case "record": {
|
|
@@ -4266,7 +4393,7 @@ function zodAstToTs(node) {
|
|
|
4266
4393
|
}
|
|
4267
4394
|
case "tuple": {
|
|
4268
4395
|
const arrArg = args[0];
|
|
4269
|
-
if (!arrArg || !
|
|
4396
|
+
if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
4270
4397
|
return `[${arrArg.getElements().map(zodAstToTs).join(", ")}]`;
|
|
4271
4398
|
}
|
|
4272
4399
|
default:
|
|
@@ -4276,18 +4403,18 @@ function zodAstToTs(node) {
|
|
|
4276
4403
|
return "unknown";
|
|
4277
4404
|
}
|
|
4278
4405
|
function isOptionalChain(node) {
|
|
4279
|
-
if (!
|
|
4406
|
+
if (!import_ts_morph9.Node.isCallExpression(node)) return false;
|
|
4280
4407
|
const expr = node.getExpression();
|
|
4281
|
-
return
|
|
4408
|
+
return import_ts_morph9.Node.isPropertyAccessExpression(expr) && expr.getName() === "optional";
|
|
4282
4409
|
}
|
|
4283
4410
|
function parseDefineContractCall(callExpr) {
|
|
4284
|
-
if (!
|
|
4411
|
+
if (!import_ts_morph9.Node.isCallExpression(callExpr)) return null;
|
|
4285
4412
|
const callee = callExpr.getExpression();
|
|
4286
|
-
const calleeName =
|
|
4413
|
+
const calleeName = import_ts_morph9.Node.isIdentifier(callee) ? callee.getText() : import_ts_morph9.Node.isPropertyAccessExpression(callee) ? callee.getName() : "";
|
|
4287
4414
|
if (calleeName !== "defineContract") return null;
|
|
4288
4415
|
const args = callExpr.getArguments();
|
|
4289
4416
|
const optsArg = args[0];
|
|
4290
|
-
if (!optsArg || !
|
|
4417
|
+
if (!optsArg || !import_ts_morph9.Node.isObjectLiteralExpression(optsArg)) return null;
|
|
4291
4418
|
let query = null;
|
|
4292
4419
|
let body = null;
|
|
4293
4420
|
let response = "unknown";
|
|
@@ -4295,7 +4422,7 @@ function parseDefineContractCall(callExpr) {
|
|
|
4295
4422
|
let bodyZodText = null;
|
|
4296
4423
|
let queryZodText = null;
|
|
4297
4424
|
for (const prop of optsArg.getProperties()) {
|
|
4298
|
-
if (!
|
|
4425
|
+
if (!import_ts_morph9.Node.isPropertyAssignment(prop)) continue;
|
|
4299
4426
|
const propName = prop.getName();
|
|
4300
4427
|
const val = prop.getInitializer();
|
|
4301
4428
|
if (!val) continue;
|
|
@@ -4324,21 +4451,22 @@ async function discoverContractsFast(opts) {
|
|
|
4324
4451
|
project.addSourceFileAtPath(f);
|
|
4325
4452
|
}
|
|
4326
4453
|
bindDiscoveryContext(project, cwd, tsconfigPath);
|
|
4454
|
+
clearMixinTypeProject();
|
|
4327
4455
|
return extractAllRoutes(project);
|
|
4328
4456
|
}
|
|
4329
4457
|
function resolveTsconfigPath(cwd, tsconfig) {
|
|
4330
|
-
return tsconfig ? (0,
|
|
4458
|
+
return tsconfig ? (0, import_node_path16.resolve)(tsconfig) : (0, import_node_path16.join)(cwd, "tsconfig.json");
|
|
4331
4459
|
}
|
|
4332
4460
|
function createDiscoveryProject(tsconfigPath) {
|
|
4333
4461
|
try {
|
|
4334
|
-
return new
|
|
4462
|
+
return new import_ts_morph10.Project({
|
|
4335
4463
|
tsConfigFilePath: tsconfigPath,
|
|
4336
4464
|
skipAddingFilesFromTsConfig: true,
|
|
4337
4465
|
skipLoadingLibFiles: true,
|
|
4338
4466
|
skipFileDependencyResolution: true
|
|
4339
4467
|
});
|
|
4340
4468
|
} catch {
|
|
4341
|
-
return new
|
|
4469
|
+
return new import_ts_morph10.Project({
|
|
4342
4470
|
skipAddingFilesFromTsConfig: true,
|
|
4343
4471
|
skipLoadingLibFiles: true,
|
|
4344
4472
|
skipFileDependencyResolution: true,
|
|
@@ -4413,7 +4541,7 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
4413
4541
|
async rediscover(changedPaths) {
|
|
4414
4542
|
if (changedPaths) {
|
|
4415
4543
|
for (const p of changedPaths) {
|
|
4416
|
-
const abs = (0,
|
|
4544
|
+
const abs = (0, import_node_path16.resolve)(p);
|
|
4417
4545
|
const sf = this.project.getSourceFile(abs);
|
|
4418
4546
|
if (sf) {
|
|
4419
4547
|
await sf.refreshFromFileSystem();
|
|
@@ -4445,15 +4573,16 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
4445
4573
|
runExtraction() {
|
|
4446
4574
|
clearTypeResolutionCaches(this.project);
|
|
4447
4575
|
clearEnumCache(this.project);
|
|
4576
|
+
clearMixinTypeProject();
|
|
4448
4577
|
return extractRoutesFrom(this.project, this.controllerPaths);
|
|
4449
4578
|
}
|
|
4450
4579
|
};
|
|
4451
4580
|
function decoratorStringArg(decoratorExpr) {
|
|
4452
4581
|
if (!decoratorExpr) return void 0;
|
|
4453
|
-
if (
|
|
4454
|
-
if (
|
|
4582
|
+
if (import_ts_morph10.Node.isStringLiteral(decoratorExpr)) return decoratorExpr.getLiteralValue();
|
|
4583
|
+
if (import_ts_morph10.Node.isArrayLiteralExpression(decoratorExpr)) {
|
|
4455
4584
|
const first = decoratorExpr.getElements()[0];
|
|
4456
|
-
if (first &&
|
|
4585
|
+
if (first && import_ts_morph10.Node.isStringLiteral(first)) return first.getLiteralValue();
|
|
4457
4586
|
}
|
|
4458
4587
|
return void 0;
|
|
4459
4588
|
}
|
|
@@ -4475,7 +4604,8 @@ function joinPaths(prefix, suffix) {
|
|
|
4475
4604
|
if (!prefix && !suffix) return "/";
|
|
4476
4605
|
if (!prefix) return suffix.startsWith("/") ? suffix : `/${suffix}`;
|
|
4477
4606
|
if (!suffix) return prefix.startsWith("/") ? prefix : `/${prefix}`;
|
|
4478
|
-
const
|
|
4607
|
+
const withLeadingSlash = prefix.startsWith("/") ? prefix : `/${prefix}`;
|
|
4608
|
+
const p = withLeadingSlash.endsWith("/") ? withLeadingSlash.slice(0, -1) : withLeadingSlash;
|
|
4479
4609
|
const s = suffix.startsWith("/") ? suffix : `/${suffix}`;
|
|
4480
4610
|
const combined = p + s;
|
|
4481
4611
|
return combined === "" ? "/" : combined;
|
|
@@ -4529,7 +4659,8 @@ function buildRoute(args) {
|
|
|
4529
4659
|
methodAs,
|
|
4530
4660
|
sourceFile,
|
|
4531
4661
|
seenNames,
|
|
4532
|
-
contractSource
|
|
4662
|
+
contractSource,
|
|
4663
|
+
mixin
|
|
4533
4664
|
} = args;
|
|
4534
4665
|
const routeName = resolveRouteName(className, methodName, classAs, methodAs);
|
|
4535
4666
|
const qualifiedRef = `${className}.${methodName}`;
|
|
@@ -4545,7 +4676,12 @@ function buildRoute(args) {
|
|
|
4545
4676
|
path: combinedPath,
|
|
4546
4677
|
name: routeName,
|
|
4547
4678
|
params: extractParams(combinedPath),
|
|
4548
|
-
controllerRef: {
|
|
4679
|
+
controllerRef: {
|
|
4680
|
+
className,
|
|
4681
|
+
methodName,
|
|
4682
|
+
filePath: sourceFile.getFilePath(),
|
|
4683
|
+
...mixin ? { mixin } : {}
|
|
4684
|
+
},
|
|
4549
4685
|
contract: { contractSource }
|
|
4550
4686
|
};
|
|
4551
4687
|
}
|
|
@@ -4559,16 +4695,17 @@ function extractContractRoute(args) {
|
|
|
4559
4695
|
className,
|
|
4560
4696
|
sourceFile,
|
|
4561
4697
|
project,
|
|
4562
|
-
seenNames
|
|
4698
|
+
seenNames,
|
|
4699
|
+
mixin
|
|
4563
4700
|
} = args;
|
|
4564
4701
|
const firstDecoratorArg = applyContractDecorator.getArguments()[0];
|
|
4565
4702
|
if (!firstDecoratorArg) return null;
|
|
4566
4703
|
let contractDef = null;
|
|
4567
4704
|
let bodyZodRef = null;
|
|
4568
4705
|
let queryZodRef = null;
|
|
4569
|
-
if (
|
|
4706
|
+
if (import_ts_morph10.Node.isCallExpression(firstDecoratorArg)) {
|
|
4570
4707
|
contractDef = parseDefineContractCall(firstDecoratorArg);
|
|
4571
|
-
} else if (
|
|
4708
|
+
} else if (import_ts_morph10.Node.isIdentifier(firstDecoratorArg)) {
|
|
4572
4709
|
const identName = firstDecoratorArg.getText();
|
|
4573
4710
|
const resolvedVar = resolveImportedVariable(identName, sourceFile, project);
|
|
4574
4711
|
if (!resolvedVar) {
|
|
@@ -4611,6 +4748,7 @@ function extractContractRoute(args) {
|
|
|
4611
4748
|
methodAs,
|
|
4612
4749
|
sourceFile,
|
|
4613
4750
|
seenNames,
|
|
4751
|
+
mixin,
|
|
4614
4752
|
contractSource: {
|
|
4615
4753
|
query: contractDef.query,
|
|
4616
4754
|
body: contractDef.body,
|
|
@@ -4627,13 +4765,14 @@ function extractContractRoute(args) {
|
|
|
4627
4765
|
});
|
|
4628
4766
|
}
|
|
4629
4767
|
function extractDtoRoute(args) {
|
|
4630
|
-
const { cls, method, verb, prefix, className, sourceFile, project, seenNames } = args;
|
|
4768
|
+
const { cls, method, verb, prefix, className, sourceFile, project, seenNames, mixin } = args;
|
|
4631
4769
|
if (!verb) return null;
|
|
4632
4770
|
const combined = joinPaths(prefix, verb.handlerPath);
|
|
4633
4771
|
const methodName = method.getName();
|
|
4634
4772
|
const classAs = readAsDecorator(cls, `class ${className}`);
|
|
4635
4773
|
const methodAs = readAsDecorator(method, `${className}.${methodName}`);
|
|
4636
|
-
const dtoContract = extractDtoContract(method, sourceFile, project);
|
|
4774
|
+
const dtoContract = extractDtoContract(method, sourceFile, project, mixin);
|
|
4775
|
+
const mixinResponse = mixin ? resolveInstantiatedReturnType(cls, methodName) : void 0;
|
|
4637
4776
|
return buildRoute({
|
|
4638
4777
|
className,
|
|
4639
4778
|
methodName,
|
|
@@ -4643,10 +4782,11 @@ function extractDtoRoute(args) {
|
|
|
4643
4782
|
methodAs,
|
|
4644
4783
|
sourceFile,
|
|
4645
4784
|
seenNames,
|
|
4785
|
+
mixin,
|
|
4646
4786
|
contractSource: {
|
|
4647
4787
|
query: dtoContract?.query ?? null,
|
|
4648
4788
|
body: dtoContract?.body ?? null,
|
|
4649
|
-
response: dtoContract?.response ?? "unknown",
|
|
4789
|
+
response: mixinResponse ?? dtoContract?.response ?? "unknown",
|
|
4650
4790
|
error: dtoContract?.error ?? null,
|
|
4651
4791
|
queryRef: dtoContract?.queryRef ?? null,
|
|
4652
4792
|
bodyRef: dtoContract?.bodyRef ?? null,
|
|
@@ -4675,7 +4815,17 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
4675
4815
|
const firstArg2 = controllerDecorator.getArguments()[0];
|
|
4676
4816
|
const prefix = decoratorStringArg(firstArg2) ?? "";
|
|
4677
4817
|
const className = cls.getName() ?? "Unknown";
|
|
4678
|
-
|
|
4818
|
+
const inherited = resolveInheritedMethods(cls);
|
|
4819
|
+
const mixinBinding = inherited ? {
|
|
4820
|
+
factoryName: inherited.factoryName,
|
|
4821
|
+
factoryFilePath: inherited.factoryFilePath,
|
|
4822
|
+
classArgs: inherited.classArgs
|
|
4823
|
+
} : void 0;
|
|
4824
|
+
const methods = [
|
|
4825
|
+
...cls.getMethods().map((method) => ({ method })),
|
|
4826
|
+
...(inherited?.methods ?? []).map((method) => ({ method, mixin: mixinBinding }))
|
|
4827
|
+
];
|
|
4828
|
+
for (const { method, mixin } of methods) {
|
|
4679
4829
|
const verb = resolveVerb(method);
|
|
4680
4830
|
const applyContractDecorator = method.getDecorator("ApplyContract");
|
|
4681
4831
|
const route = applyContractDecorator ? extractContractRoute({
|
|
@@ -4687,7 +4837,8 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
4687
4837
|
className,
|
|
4688
4838
|
sourceFile,
|
|
4689
4839
|
project,
|
|
4690
|
-
seenNames
|
|
4840
|
+
seenNames,
|
|
4841
|
+
mixin
|
|
4691
4842
|
}) : extractDtoRoute({
|
|
4692
4843
|
cls,
|
|
4693
4844
|
method,
|
|
@@ -4696,7 +4847,8 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
4696
4847
|
className,
|
|
4697
4848
|
sourceFile,
|
|
4698
4849
|
project,
|
|
4699
|
-
seenNames
|
|
4850
|
+
seenNames,
|
|
4851
|
+
mixin
|
|
4700
4852
|
});
|
|
4701
4853
|
if (route) routes.push(route);
|
|
4702
4854
|
}
|
|
@@ -4707,7 +4859,7 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
4707
4859
|
// src/watch/lock-file.ts
|
|
4708
4860
|
var import_promises13 = require("fs/promises");
|
|
4709
4861
|
var import_promises14 = require("fs/promises");
|
|
4710
|
-
var
|
|
4862
|
+
var import_node_path17 = require("path");
|
|
4711
4863
|
var LOCK_FILE2 = ".watcher.lock";
|
|
4712
4864
|
function isProcessAlive(pid) {
|
|
4713
4865
|
try {
|
|
@@ -4719,7 +4871,7 @@ function isProcessAlive(pid) {
|
|
|
4719
4871
|
}
|
|
4720
4872
|
async function acquireLock(outDir) {
|
|
4721
4873
|
await (0, import_promises14.mkdir)(outDir, { recursive: true });
|
|
4722
|
-
const lockPath = (0,
|
|
4874
|
+
const lockPath = (0, import_node_path17.join)(outDir, LOCK_FILE2);
|
|
4723
4875
|
const lockData = { pid: process.pid, startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
4724
4876
|
try {
|
|
4725
4877
|
const fd = await (0, import_promises13.open)(lockPath, "wx");
|
|
@@ -4760,7 +4912,7 @@ async function watch(config, onChange, options = {}) {
|
|
|
4760
4912
|
if (lock === null) {
|
|
4761
4913
|
let holderPid = "unknown";
|
|
4762
4914
|
try {
|
|
4763
|
-
const raw = await (0, import_promises15.readFile)((0,
|
|
4915
|
+
const raw = await (0, import_promises15.readFile)((0, import_node_path18.join)(config.codegen.outDir, ".watcher.lock"), "utf8");
|
|
4764
4916
|
const data = JSON.parse(raw);
|
|
4765
4917
|
if (data.pid !== void 0) holderPid = String(data.pid);
|
|
4766
4918
|
} catch {
|
|
@@ -4813,7 +4965,7 @@ async function watch(config, onChange, options = {}) {
|
|
|
4813
4965
|
}
|
|
4814
4966
|
let pagesDebounceTimer;
|
|
4815
4967
|
const pagesGlob = config.pages?.glob ?? ".nestjs-codegen-no-pages";
|
|
4816
|
-
const pagesWatcher = import_chokidar.default.watch((0,
|
|
4968
|
+
const pagesWatcher = import_chokidar.default.watch((0, import_node_path18.join)(config.codegen.cwd, pagesGlob), {
|
|
4817
4969
|
ignoreInitial: true,
|
|
4818
4970
|
persistent: true,
|
|
4819
4971
|
awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
|
|
@@ -4840,7 +4992,7 @@ async function watch(config, onChange, options = {}) {
|
|
|
4840
4992
|
pagesWatcher.on("unlink", schedulePagesRegenerate);
|
|
4841
4993
|
let contractsDebounceTimer;
|
|
4842
4994
|
const pendingChangedPaths = /* @__PURE__ */ new Set();
|
|
4843
|
-
const contractsWatcher = import_chokidar.default.watch((0,
|
|
4995
|
+
const contractsWatcher = import_chokidar.default.watch((0, import_node_path18.join)(config.codegen.cwd, config.contracts.glob), {
|
|
4844
4996
|
ignoreInitial: true,
|
|
4845
4997
|
persistent: true,
|
|
4846
4998
|
awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
|
|
@@ -4870,7 +5022,7 @@ async function watch(config, onChange, options = {}) {
|
|
|
4870
5022
|
contractsWatcher.on("add", (p) => scheduleContractsRegenerate(p));
|
|
4871
5023
|
contractsWatcher.on("change", (p) => scheduleContractsRegenerate(p));
|
|
4872
5024
|
contractsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p));
|
|
4873
|
-
const formsWatcher = import_chokidar.default.watch((0,
|
|
5025
|
+
const formsWatcher = import_chokidar.default.watch((0, import_node_path18.join)(config.codegen.cwd, config.forms.watch), {
|
|
4874
5026
|
ignoreInitial: true,
|
|
4875
5027
|
persistent: true,
|
|
4876
5028
|
awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
|
|
@@ -4980,7 +5132,7 @@ function createChainModuleRenderer(opts) {
|
|
|
4980
5132
|
}
|
|
4981
5133
|
|
|
4982
5134
|
// src/index.ts
|
|
4983
|
-
var VERSION = "0.
|
|
5135
|
+
var VERSION = "0.17.0";
|
|
4984
5136
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4985
5137
|
0 && (module.exports = {
|
|
4986
5138
|
CodegenError,
|