@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/nest/index.cjs
CHANGED
|
@@ -180,12 +180,12 @@ function applyDefaults(userConfig, cwd) {
|
|
|
180
180
|
// src/generate-manifest.ts
|
|
181
181
|
var import_node_crypto = require("crypto");
|
|
182
182
|
var import_promises15 = require("fs/promises");
|
|
183
|
-
var
|
|
183
|
+
var import_node_path18 = require("path");
|
|
184
184
|
var import_fast_glob3 = __toESM(require("fast-glob"), 1);
|
|
185
185
|
|
|
186
186
|
// src/generate.ts
|
|
187
187
|
var import_promises11 = require("fs/promises");
|
|
188
|
-
var
|
|
188
|
+
var import_node_path13 = require("path");
|
|
189
189
|
|
|
190
190
|
// src/discovery/pages.ts
|
|
191
191
|
var import_promises2 = require("fs/promises");
|
|
@@ -514,9 +514,10 @@ function inferExpressionType(node) {
|
|
|
514
514
|
|
|
515
515
|
// src/emit/emit-api.ts
|
|
516
516
|
var import_promises3 = require("fs/promises");
|
|
517
|
-
var
|
|
517
|
+
var import_node_path5 = require("path");
|
|
518
518
|
|
|
519
519
|
// src/extension/registry.ts
|
|
520
|
+
var import_node_path4 = require("path");
|
|
520
521
|
var import_ts_morph2 = require("ts-morph");
|
|
521
522
|
function resolveApiSlots(extensions) {
|
|
522
523
|
let layer;
|
|
@@ -549,7 +550,7 @@ function mergeExclusive(target, incoming, {
|
|
|
549
550
|
target.set(key, { value, owner });
|
|
550
551
|
}
|
|
551
552
|
}
|
|
552
|
-
function createExtensionContext(config, getRoutes) {
|
|
553
|
+
function createExtensionContext(config, getRoutes, trackedInputs) {
|
|
553
554
|
let project;
|
|
554
555
|
return {
|
|
555
556
|
cwd: config.codegen.cwd,
|
|
@@ -558,6 +559,13 @@ function createExtensionContext(config, getRoutes) {
|
|
|
558
559
|
get routes() {
|
|
559
560
|
return getRoutes();
|
|
560
561
|
},
|
|
562
|
+
trackInput(...paths) {
|
|
563
|
+
if (!trackedInputs) return;
|
|
564
|
+
for (const path of paths) {
|
|
565
|
+
if (!path) continue;
|
|
566
|
+
trackedInputs.add((0, import_node_path4.relative)(config.codegen.cwd, (0, import_node_path4.resolve)(config.codegen.cwd, path)));
|
|
567
|
+
}
|
|
568
|
+
},
|
|
561
569
|
project() {
|
|
562
570
|
if (!project) {
|
|
563
571
|
project = new import_ts_morph2.Project({
|
|
@@ -617,7 +625,7 @@ function requestShape(route) {
|
|
|
617
625
|
async function emitApi(routes, outDir, opts = {}) {
|
|
618
626
|
await (0, import_promises3.mkdir)(outDir, { recursive: true });
|
|
619
627
|
const content = buildApiFile(routes, outDir, opts);
|
|
620
|
-
await (0, import_promises3.writeFile)((0,
|
|
628
|
+
await (0, import_promises3.writeFile)((0, import_node_path5.join)(outDir, "api.ts"), content, "utf8");
|
|
621
629
|
}
|
|
622
630
|
function splitName(name) {
|
|
623
631
|
return name.split(".");
|
|
@@ -725,7 +733,7 @@ function rawResponseType(c, outDir) {
|
|
|
725
733
|
return c.contractSource.response;
|
|
726
734
|
}
|
|
727
735
|
if (c.controllerRef) {
|
|
728
|
-
let relPath = (0,
|
|
736
|
+
let relPath = (0, import_node_path5.relative)(outDir, c.controllerRef.filePath).replace(/\.ts$/, "");
|
|
729
737
|
if (!relPath.startsWith(".")) relPath = `./${relPath}`;
|
|
730
738
|
return `Awaited<ReturnType<import('${relPath}').${c.controllerRef.className}['${c.controllerRef.methodName}']>>`;
|
|
731
739
|
}
|
|
@@ -1051,6 +1059,11 @@ function buildApiFile(routes, outDir, opts = {}) {
|
|
|
1051
1059
|
config: {},
|
|
1052
1060
|
project: () => {
|
|
1053
1061
|
throw new Error("ExtensionContext.project() is unavailable in standalone emitApi.");
|
|
1062
|
+
},
|
|
1063
|
+
// No manifest is written on the standalone path, so there is nothing for
|
|
1064
|
+
// tracked dependencies to be recorded into — accept and discard them
|
|
1065
|
+
// rather than making every caller supply a sink.
|
|
1066
|
+
trackInput: () => {
|
|
1054
1067
|
}
|
|
1055
1068
|
};
|
|
1056
1069
|
const importsByFile = /* @__PURE__ */ new Map();
|
|
@@ -1106,8 +1119,8 @@ function buildApiFile(routes, outDir, opts = {}) {
|
|
|
1106
1119
|
const emittedNames = /* @__PURE__ */ new Set();
|
|
1107
1120
|
for (const [filePath, names] of importsByFile) {
|
|
1108
1121
|
let relPath;
|
|
1109
|
-
if ((0,
|
|
1110
|
-
relPath = (0,
|
|
1122
|
+
if ((0, import_node_path5.isAbsolute)(filePath)) {
|
|
1123
|
+
relPath = (0, import_node_path5.relative)(outDir, filePath).replace(/\.ts$/, "");
|
|
1111
1124
|
if (!relPath.startsWith(".")) relPath = `./${relPath}`;
|
|
1112
1125
|
} else {
|
|
1113
1126
|
relPath = filePath;
|
|
@@ -1201,7 +1214,7 @@ function buildApiFile(routes, outDir, opts = {}) {
|
|
|
1201
1214
|
|
|
1202
1215
|
// src/emit/emit-cache.ts
|
|
1203
1216
|
var import_promises4 = require("fs/promises");
|
|
1204
|
-
var
|
|
1217
|
+
var import_node_path6 = require("path");
|
|
1205
1218
|
async function emitCache(pages, outDir) {
|
|
1206
1219
|
await (0, import_promises4.mkdir)(outDir, { recursive: true });
|
|
1207
1220
|
const entries = await Promise.all(
|
|
@@ -1215,19 +1228,19 @@ async function emitCache(pages, outDir) {
|
|
|
1215
1228
|
})
|
|
1216
1229
|
);
|
|
1217
1230
|
const cache = { pages: entries };
|
|
1218
|
-
await (0, import_promises4.writeFile)((0,
|
|
1231
|
+
await (0, import_promises4.writeFile)((0, import_node_path6.join)(outDir, "components.json"), `${JSON.stringify(cache, null, 2)}
|
|
1219
1232
|
`, "utf8");
|
|
1220
1233
|
}
|
|
1221
1234
|
|
|
1222
1235
|
// src/emit/emit-forms.ts
|
|
1223
1236
|
var import_promises5 = require("fs/promises");
|
|
1224
|
-
var
|
|
1237
|
+
var import_node_path7 = require("path");
|
|
1225
1238
|
async function emitForms(routes, outDir, config, adapter) {
|
|
1226
1239
|
if (config && config.enabled === false) return false;
|
|
1227
1240
|
const content = buildFormsFileWithAdapter(routes, outDir, adapter, config);
|
|
1228
1241
|
if (content === null) return false;
|
|
1229
1242
|
await (0, import_promises5.mkdir)(outDir, { recursive: true });
|
|
1230
|
-
await (0, import_promises5.writeFile)((0,
|
|
1243
|
+
await (0, import_promises5.writeFile)((0, import_node_path7.join)(outDir, "forms.ts"), content, "utf8");
|
|
1231
1244
|
return true;
|
|
1232
1245
|
}
|
|
1233
1246
|
function pascal(segment) {
|
|
@@ -1240,7 +1253,7 @@ function deriveBaseName(routeName) {
|
|
|
1240
1253
|
return { method, full };
|
|
1241
1254
|
}
|
|
1242
1255
|
function relImport(outDir, filePath) {
|
|
1243
|
-
let relPath = (0,
|
|
1256
|
+
let relPath = (0, import_node_path7.relative)(outDir, filePath).replace(/\.ts$/, "");
|
|
1244
1257
|
if (!relPath.startsWith(".")) relPath = `./${relPath}`;
|
|
1245
1258
|
return relPath;
|
|
1246
1259
|
}
|
|
@@ -1514,7 +1527,7 @@ function buildFormsFileWithAdapter(routes, outDir, adapter, config) {
|
|
|
1514
1527
|
|
|
1515
1528
|
// src/emit/emit-index.ts
|
|
1516
1529
|
var import_promises6 = require("fs/promises");
|
|
1517
|
-
var
|
|
1530
|
+
var import_node_path8 = require("path");
|
|
1518
1531
|
async function emitIndex(outDir, hasContracts = false, hasForms = false) {
|
|
1519
1532
|
await (0, import_promises6.mkdir)(outDir, { recursive: true });
|
|
1520
1533
|
const exports2 = ["export * from './pages.js';", "export * from './routes.js';"];
|
|
@@ -1527,12 +1540,12 @@ async function emitIndex(outDir, hasContracts = false, hasForms = false) {
|
|
|
1527
1540
|
const content = ["// Generated by @dudousxd/nestjs-codegen. Do not edit.", ...exports2, ""].join(
|
|
1528
1541
|
"\n"
|
|
1529
1542
|
);
|
|
1530
|
-
await (0, import_promises6.writeFile)((0,
|
|
1543
|
+
await (0, import_promises6.writeFile)((0, import_node_path8.join)(outDir, "index.d.ts"), content, "utf8");
|
|
1531
1544
|
}
|
|
1532
1545
|
|
|
1533
1546
|
// src/emit/emit-mocks.ts
|
|
1534
1547
|
var import_promises7 = require("fs/promises");
|
|
1535
|
-
var
|
|
1548
|
+
var import_node_path9 = require("path");
|
|
1536
1549
|
|
|
1537
1550
|
// src/ir/schema-node-to-json-schema.ts
|
|
1538
1551
|
var DEFAULT_CTX = { refPrefix: "#/components/schemas/" };
|
|
@@ -1856,12 +1869,12 @@ async function emitMocks(routes, outDir, opts = {}) {
|
|
|
1856
1869
|
await (0, import_promises7.mkdir)(outDir, { recursive: true });
|
|
1857
1870
|
const content = buildMocksFile(routes, opts);
|
|
1858
1871
|
const fileName = opts.fileName ?? "mocks.ts";
|
|
1859
|
-
await (0, import_promises7.writeFile)((0,
|
|
1872
|
+
await (0, import_promises7.writeFile)((0, import_node_path9.join)(outDir, fileName), content, "utf8");
|
|
1860
1873
|
}
|
|
1861
1874
|
|
|
1862
1875
|
// src/emit/emit-openapi.ts
|
|
1863
1876
|
var import_promises8 = require("fs/promises");
|
|
1864
|
-
var
|
|
1877
|
+
var import_node_path10 = require("path");
|
|
1865
1878
|
var REF_PREFIX2 = "#/components/schemas/";
|
|
1866
1879
|
function toOpenApiPath(path) {
|
|
1867
1880
|
return path.replace(/:([^/]+)/g, "{$1}");
|
|
@@ -1982,13 +1995,13 @@ async function emitOpenApi(routes, outDir, opts = {}) {
|
|
|
1982
1995
|
await (0, import_promises8.mkdir)(outDir, { recursive: true });
|
|
1983
1996
|
const doc = buildOpenApiSpec(routes, opts);
|
|
1984
1997
|
const fileName = opts.fileName ?? "openapi.json";
|
|
1985
|
-
await (0, import_promises8.writeFile)((0,
|
|
1998
|
+
await (0, import_promises8.writeFile)((0, import_node_path10.join)(outDir, fileName), `${JSON.stringify(doc, null, 2)}
|
|
1986
1999
|
`, "utf8");
|
|
1987
2000
|
}
|
|
1988
2001
|
|
|
1989
2002
|
// src/emit/emit-pages.ts
|
|
1990
2003
|
var import_promises9 = require("fs/promises");
|
|
1991
|
-
var
|
|
2004
|
+
var import_node_path11 = require("path");
|
|
1992
2005
|
async function emitPages(pages, outDir, _options = {}) {
|
|
1993
2006
|
await (0, import_promises9.mkdir)(outDir, { recursive: true });
|
|
1994
2007
|
const pageNameUnion = pages.length > 0 ? pages.map((p) => JSON.stringify(p.name)).join(" | ") : "never";
|
|
@@ -2009,7 +2022,7 @@ ${augBody}
|
|
|
2009
2022
|
}
|
|
2010
2023
|
${sharedPropsBlock}}
|
|
2011
2024
|
`;
|
|
2012
|
-
await (0, import_promises9.writeFile)((0,
|
|
2025
|
+
await (0, import_promises9.writeFile)((0, import_node_path11.join)(outDir, "pages.d.ts"), content, "utf8");
|
|
2013
2026
|
}
|
|
2014
2027
|
function buildSharedPropsBlock(sharedProps) {
|
|
2015
2028
|
if (!sharedProps) return "";
|
|
@@ -2028,7 +2041,7 @@ ${propsBody}
|
|
|
2028
2041
|
`;
|
|
2029
2042
|
}
|
|
2030
2043
|
function buildAugmentationType(page, outDir) {
|
|
2031
|
-
let importPath = (0,
|
|
2044
|
+
let importPath = (0, import_node_path11.relative)(outDir, page.absolutePath).replace(/\.(tsx?|vue|svelte)$/, "");
|
|
2032
2045
|
if (!importPath.startsWith(".")) {
|
|
2033
2046
|
importPath = `./${importPath}`;
|
|
2034
2047
|
}
|
|
@@ -2040,11 +2053,11 @@ function needsQuotes(name) {
|
|
|
2040
2053
|
|
|
2041
2054
|
// src/emit/emit-routes.ts
|
|
2042
2055
|
var import_promises10 = require("fs/promises");
|
|
2043
|
-
var
|
|
2056
|
+
var import_node_path12 = require("path");
|
|
2044
2057
|
async function emitRoutes(routes, outDir) {
|
|
2045
2058
|
await (0, import_promises10.mkdir)(outDir, { recursive: true });
|
|
2046
2059
|
const content = buildRoutesFile(routes);
|
|
2047
|
-
await (0, import_promises10.writeFile)((0,
|
|
2060
|
+
await (0, import_promises10.writeFile)((0, import_node_path12.join)(outDir, "routes.ts"), content, "utf8");
|
|
2048
2061
|
}
|
|
2049
2062
|
function buildRoutesFile(routes) {
|
|
2050
2063
|
if (routes.length === 0) {
|
|
@@ -2172,8 +2185,8 @@ function driftGuardMessage(outDir, previousEntryPoint, currentEntryPoint, differ
|
|
|
2172
2185
|
}
|
|
2173
2186
|
async function generate(config, inputRoutes = [], entryPoint = "cli") {
|
|
2174
2187
|
setCodegenDebug(config.debug);
|
|
2175
|
-
const inputsHash = await computeInputsHash(config);
|
|
2176
2188
|
const manifest = await readManifest(config.codegen.outDir);
|
|
2189
|
+
const inputsHash = await computeInputsHash(config, manifest?.extraInputs ?? []);
|
|
2177
2190
|
if (await isManifestFresh(config.codegen.outDir, manifest, inputsHash)) {
|
|
2178
2191
|
console.log(`[nestjs-codegen] ${config.codegen.outDir} up to date, skipped`);
|
|
2179
2192
|
return;
|
|
@@ -2194,7 +2207,8 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
|
|
|
2194
2207
|
}
|
|
2195
2208
|
const extensions = config.extensions ?? [];
|
|
2196
2209
|
let routes = inputRoutes;
|
|
2197
|
-
const
|
|
2210
|
+
const trackedInputs = /* @__PURE__ */ new Set();
|
|
2211
|
+
const ctx = createExtensionContext(config, () => routes, trackedInputs);
|
|
2198
2212
|
if (extensions.length > 0) {
|
|
2199
2213
|
routes = await applyTransformRoutes(routes, extensions, ctx);
|
|
2200
2214
|
}
|
|
@@ -2248,31 +2262,34 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
|
|
|
2248
2262
|
if (extensions.length > 0) {
|
|
2249
2263
|
const extraFiles = await collectEmittedFiles(extensions, ctx);
|
|
2250
2264
|
for (const file of extraFiles) {
|
|
2251
|
-
const dest = (0,
|
|
2252
|
-
await (0, import_promises11.mkdir)((0,
|
|
2265
|
+
const dest = (0, import_node_path13.join)(config.codegen.outDir, file.path);
|
|
2266
|
+
await (0, import_promises11.mkdir)((0, import_node_path13.dirname)(dest), { recursive: true });
|
|
2253
2267
|
await (0, import_promises11.writeFile)(dest, file.contents, "utf8");
|
|
2254
2268
|
}
|
|
2255
2269
|
}
|
|
2256
2270
|
const outputFiles = await listOutputFiles(config.codegen.outDir);
|
|
2271
|
+
const extraInputs = [...trackedInputs].sort();
|
|
2272
|
+
const recordedHash = extraInputs.length > 0 ? await computeInputsHash(config, extraInputs) : inputsHash;
|
|
2257
2273
|
await writeManifest(config.codegen.outDir, {
|
|
2258
2274
|
version: VERSION,
|
|
2259
|
-
hash:
|
|
2275
|
+
hash: recordedHash,
|
|
2260
2276
|
entryPoint,
|
|
2261
2277
|
configHash,
|
|
2262
2278
|
configKeyHashes,
|
|
2263
|
-
files: outputFiles
|
|
2279
|
+
files: outputFiles,
|
|
2280
|
+
...extraInputs.length > 0 ? { extraInputs } : {}
|
|
2264
2281
|
});
|
|
2265
2282
|
}
|
|
2266
2283
|
|
|
2267
2284
|
// src/watch/watcher.ts
|
|
2268
2285
|
var import_promises14 = require("fs/promises");
|
|
2269
|
-
var
|
|
2286
|
+
var import_node_path17 = require("path");
|
|
2270
2287
|
var import_chokidar = __toESM(require("chokidar"), 1);
|
|
2271
2288
|
|
|
2272
2289
|
// src/discovery/contracts-fast.ts
|
|
2273
|
-
var
|
|
2290
|
+
var import_node_path15 = require("path");
|
|
2274
2291
|
var import_fast_glob2 = __toESM(require("fast-glob"), 1);
|
|
2275
|
-
var
|
|
2292
|
+
var import_ts_morph10 = require("ts-morph");
|
|
2276
2293
|
|
|
2277
2294
|
// src/discovery/dto-type-resolver.ts
|
|
2278
2295
|
var import_ts_morph7 = require("ts-morph");
|
|
@@ -2282,7 +2299,7 @@ var import_ts_morph4 = require("ts-morph");
|
|
|
2282
2299
|
|
|
2283
2300
|
// src/discovery/type-ref-resolution.ts
|
|
2284
2301
|
var import_node_fs = require("fs");
|
|
2285
|
-
var
|
|
2302
|
+
var import_node_path14 = require("path");
|
|
2286
2303
|
var import_ts_morph3 = require("ts-morph");
|
|
2287
2304
|
var _EMPTY_CTX = { projectRoot: "", tsconfigPaths: null };
|
|
2288
2305
|
var _ctxByProject = /* @__PURE__ */ new WeakMap();
|
|
@@ -2334,12 +2351,12 @@ function findTypeInFile(name, file) {
|
|
|
2334
2351
|
}
|
|
2335
2352
|
function resolveModuleSpecifier(moduleSpecifier, sourceFile, project) {
|
|
2336
2353
|
if (moduleSpecifier.startsWith(".")) {
|
|
2337
|
-
const dir = (0,
|
|
2354
|
+
const dir = (0, import_node_path14.dirname)(sourceFile.getFilePath());
|
|
2338
2355
|
const noExt = moduleSpecifier.replace(/\.(js|ts)$/, "");
|
|
2339
2356
|
return [
|
|
2340
|
-
(0,
|
|
2341
|
-
(0,
|
|
2342
|
-
(0,
|
|
2357
|
+
(0, import_node_path14.resolve)(dir, `${noExt}.ts`),
|
|
2358
|
+
(0, import_node_path14.resolve)(dir, `${moduleSpecifier}.ts`),
|
|
2359
|
+
(0, import_node_path14.resolve)(dir, moduleSpecifier, "index.ts")
|
|
2343
2360
|
];
|
|
2344
2361
|
}
|
|
2345
2362
|
const ctx = _ctxFor(project);
|
|
@@ -2360,8 +2377,8 @@ function resolveModuleSpecifier(moduleSpecifier, sourceFile, project) {
|
|
|
2360
2377
|
const rest = moduleSpecifier.slice(prefix.length);
|
|
2361
2378
|
const candidates = [];
|
|
2362
2379
|
for (const mapping of mappings) {
|
|
2363
|
-
const resolved = (0,
|
|
2364
|
-
candidates.push(`${resolved}.ts`, (0,
|
|
2380
|
+
const resolved = (0, import_node_path14.resolve)(baseUrl, mapping.replace("*", rest));
|
|
2381
|
+
candidates.push(`${resolved}.ts`, (0, import_node_path14.resolve)(resolved, "index.ts"));
|
|
2365
2382
|
}
|
|
2366
2383
|
dbg(" resolved candidates:", candidates);
|
|
2367
2384
|
return candidates;
|
|
@@ -3251,6 +3268,12 @@ function toFilterFieldType(name, r) {
|
|
|
3251
3268
|
}
|
|
3252
3269
|
|
|
3253
3270
|
// src/discovery/filter-for.ts
|
|
3271
|
+
function resolveMixinEntityClass(mixin, project) {
|
|
3272
|
+
const entityArg = mixin?.classArgs[0];
|
|
3273
|
+
if (!entityArg) return void 0;
|
|
3274
|
+
const file = project.getSourceFile(entityArg.filePath) ?? project.addSourceFileAtPathIfExists(entityArg.filePath);
|
|
3275
|
+
return file?.getClass(entityArg.name);
|
|
3276
|
+
}
|
|
3254
3277
|
function classifyFilterForHint(typeInit) {
|
|
3255
3278
|
if (import_ts_morph6.Node.isStringLiteral(typeInit)) {
|
|
3256
3279
|
switch (typeInit.getLiteralValue()) {
|
|
@@ -3324,7 +3347,9 @@ function extractFilterForHints(classDecl, project) {
|
|
|
3324
3347
|
}
|
|
3325
3348
|
return hints;
|
|
3326
3349
|
}
|
|
3327
|
-
function extractApplyFilterInfo(method, sourceFile, project) {
|
|
3350
|
+
function extractApplyFilterInfo(method, sourceFile, project, mixin) {
|
|
3351
|
+
const declFile = method.getSourceFile();
|
|
3352
|
+
const mixinEntity = resolveMixinEntityClass(mixin, project);
|
|
3328
3353
|
for (const param of method.getParameters()) {
|
|
3329
3354
|
const filterDecorator = param.getDecorators().find((d) => d.getName() === "ApplyFilter");
|
|
3330
3355
|
if (!filterDecorator) continue;
|
|
@@ -3344,12 +3369,12 @@ function extractApplyFilterInfo(method, sourceFile, project) {
|
|
|
3344
3369
|
}
|
|
3345
3370
|
}
|
|
3346
3371
|
const filterClassName = filterClassArg.getText();
|
|
3347
|
-
const resolved = findType(filterClassName,
|
|
3348
|
-
|
|
3349
|
-
|
|
3372
|
+
const resolved = findType(filterClassName, declFile, project);
|
|
3373
|
+
const classDecl = resolved?.kind === "class" ? resolved.decl : resolveLocalClassDeclaration(filterClassArg);
|
|
3374
|
+
if (classDecl) {
|
|
3350
3375
|
let fieldTypes = extractClassPropertyTypes(classDecl, project);
|
|
3351
3376
|
if (fieldTypes.length === 0) {
|
|
3352
|
-
fieldTypes = extractFilterableEntityFields(classDecl, project);
|
|
3377
|
+
fieldTypes = extractFilterableEntityFields(classDecl, project, mixinEntity);
|
|
3353
3378
|
}
|
|
3354
3379
|
const filterForHints = extractFilterForHints(classDecl, project);
|
|
3355
3380
|
if (filterForHints.size > 0) {
|
|
@@ -3434,22 +3459,29 @@ function extractClassPropertyTypes(classDecl, project) {
|
|
|
3434
3459
|
}
|
|
3435
3460
|
return fields;
|
|
3436
3461
|
}
|
|
3437
|
-
function
|
|
3462
|
+
function resolveLocalClassDeclaration(identifier) {
|
|
3463
|
+
if (!import_ts_morph6.Node.isIdentifier(identifier)) return void 0;
|
|
3464
|
+
return identifier.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 && import_ts_morph6.Node.isClassDeclaration(n));
|
|
3465
|
+
}
|
|
3466
|
+
function resolveDeclaredEntity(optionsArg, filterClass, project) {
|
|
3467
|
+
if (!import_ts_morph6.Node.isObjectLiteralExpression(optionsArg)) return void 0;
|
|
3468
|
+
const entityProp = optionsArg.getProperty("entity");
|
|
3469
|
+
if (!entityProp || !import_ts_morph6.Node.isPropertyAssignment(entityProp)) return void 0;
|
|
3470
|
+
const entityInit = entityProp.getInitializer();
|
|
3471
|
+
if (!entityInit || !import_ts_morph6.Node.isIdentifier(entityInit)) return void 0;
|
|
3472
|
+
const resolved = findType(entityInit.getText(), filterClass.getSourceFile(), project);
|
|
3473
|
+
if (!resolved || resolved.kind !== "class") return void 0;
|
|
3474
|
+
return resolved.decl;
|
|
3475
|
+
}
|
|
3476
|
+
function extractFilterableEntityFields(filterClass, project, entityOverride) {
|
|
3438
3477
|
const filterableDecorator = filterClass.getDecorators().find((d) => d.getName() === "Filterable");
|
|
3439
3478
|
if (!filterableDecorator) return [];
|
|
3440
3479
|
const args = filterableDecorator.getArguments();
|
|
3441
3480
|
if (args.length === 0) return [];
|
|
3442
3481
|
const optionsArg = args[0];
|
|
3443
3482
|
if (!import_ts_morph6.Node.isObjectLiteralExpression(optionsArg)) return [];
|
|
3444
|
-
const
|
|
3445
|
-
if (!
|
|
3446
|
-
const entityInit = entityProp.getInitializer();
|
|
3447
|
-
if (!entityInit || !import_ts_morph6.Node.isIdentifier(entityInit)) return [];
|
|
3448
|
-
const entityName = entityInit.getText();
|
|
3449
|
-
const filterSourceFile = filterClass.getSourceFile();
|
|
3450
|
-
const resolvedEntity = findType(entityName, filterSourceFile, project);
|
|
3451
|
-
if (!resolvedEntity || resolvedEntity.kind !== "class") return [];
|
|
3452
|
-
const entityDecl = resolvedEntity.decl;
|
|
3483
|
+
const entityDecl = entityOverride ?? resolveDeclaredEntity(optionsArg, filterClass, project);
|
|
3484
|
+
if (!entityDecl) return [];
|
|
3453
3485
|
const fields = collectEntityFields(
|
|
3454
3486
|
entityDecl,
|
|
3455
3487
|
entityDecl.getSourceFile(),
|
|
@@ -3874,9 +3906,9 @@ function unwrapNamedContainer(node, names) {
|
|
|
3874
3906
|
}
|
|
3875
3907
|
return node;
|
|
3876
3908
|
}
|
|
3877
|
-
function extractDtoContract(method, sourceFile, project) {
|
|
3909
|
+
function extractDtoContract(method, sourceFile, project, mixin) {
|
|
3878
3910
|
let body = extractBodyType(method, sourceFile, project);
|
|
3879
|
-
const filterInfo = extractApplyFilterInfo(method, sourceFile, project);
|
|
3911
|
+
const filterInfo = extractApplyFilterInfo(method, sourceFile, project, mixin);
|
|
3880
3912
|
const query = extractQueryType(method, sourceFile, project);
|
|
3881
3913
|
const uploads = extractUploadedFiles(method);
|
|
3882
3914
|
const multipartBody = uploads.fields ? `{ ${uploads.fields} }` : null;
|
|
@@ -3986,12 +4018,94 @@ function resolveParamClass(method, decoratorName, sourceFile, project) {
|
|
|
3986
4018
|
return null;
|
|
3987
4019
|
}
|
|
3988
4020
|
|
|
3989
|
-
// src/discovery/
|
|
4021
|
+
// src/discovery/heritage.ts
|
|
3990
4022
|
var import_ts_morph8 = require("ts-morph");
|
|
4023
|
+
function unwrapExpression(node) {
|
|
4024
|
+
let current = node;
|
|
4025
|
+
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)) {
|
|
4026
|
+
current = current.getExpression();
|
|
4027
|
+
}
|
|
4028
|
+
return current;
|
|
4029
|
+
}
|
|
4030
|
+
function resolveReturnedClass(factoryDecl) {
|
|
4031
|
+
const body = factoryDecl.getBody();
|
|
4032
|
+
if (!body) return void 0;
|
|
4033
|
+
const returns = body.getDescendants().filter((n) => import_ts_morph8.Node.isReturnStatement(n));
|
|
4034
|
+
for (const ret of returns) {
|
|
4035
|
+
const expr = ret.getExpression();
|
|
4036
|
+
if (!expr) continue;
|
|
4037
|
+
const inner = unwrapExpression(expr);
|
|
4038
|
+
if (import_ts_morph8.Node.isClassExpression(inner)) {
|
|
4039
|
+
return inner;
|
|
4040
|
+
}
|
|
4041
|
+
if (import_ts_morph8.Node.isIdentifier(inner)) {
|
|
4042
|
+
const name = inner.getText();
|
|
4043
|
+
const decl = body.getDescendants().find((n) => import_ts_morph8.Node.isClassDeclaration(n) && n.getName() === name);
|
|
4044
|
+
if (decl) return decl;
|
|
4045
|
+
}
|
|
4046
|
+
}
|
|
4047
|
+
return void 0;
|
|
4048
|
+
}
|
|
4049
|
+
function resolveInheritedMethods(cls) {
|
|
4050
|
+
const expr = cls.getExtends()?.getExpression();
|
|
4051
|
+
if (!expr || !import_ts_morph8.Node.isCallExpression(expr)) return void 0;
|
|
4052
|
+
const callee = expr.getExpression();
|
|
4053
|
+
if (!import_ts_morph8.Node.isIdentifier(callee)) return void 0;
|
|
4054
|
+
const factoryDecl = callee.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => import_ts_morph8.Node.isFunctionDeclaration(n));
|
|
4055
|
+
if (!factoryDecl) return void 0;
|
|
4056
|
+
const returnedClass = resolveReturnedClass(factoryDecl);
|
|
4057
|
+
if (!returnedClass) return void 0;
|
|
4058
|
+
const classArgs = [];
|
|
4059
|
+
for (const arg of expr.getArguments()) {
|
|
4060
|
+
if (!import_ts_morph8.Node.isIdentifier(arg)) continue;
|
|
4061
|
+
const decl = arg.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => import_ts_morph8.Node.isClassDeclaration(n));
|
|
4062
|
+
if (decl) {
|
|
4063
|
+
classArgs.push({
|
|
4064
|
+
name: decl.getName() ?? arg.getText(),
|
|
4065
|
+
filePath: decl.getSourceFile().getFilePath()
|
|
4066
|
+
});
|
|
4067
|
+
}
|
|
4068
|
+
}
|
|
4069
|
+
return {
|
|
4070
|
+
methods: returnedClass.getMethods(),
|
|
4071
|
+
factoryName: callee.getText(),
|
|
4072
|
+
factoryFilePath: factoryDecl.getSourceFile().getFilePath(),
|
|
4073
|
+
classArgs
|
|
4074
|
+
};
|
|
4075
|
+
}
|
|
4076
|
+
var mixinTypeProject;
|
|
4077
|
+
function getMixinTypeProject() {
|
|
4078
|
+
mixinTypeProject ??= new import_ts_morph8.Project({
|
|
4079
|
+
skipAddingFilesFromTsConfig: true,
|
|
4080
|
+
compilerOptions: { strict: true }
|
|
4081
|
+
});
|
|
4082
|
+
return mixinTypeProject;
|
|
4083
|
+
}
|
|
4084
|
+
function clearMixinTypeProject() {
|
|
4085
|
+
mixinTypeProject = void 0;
|
|
4086
|
+
}
|
|
4087
|
+
function resolveInstantiatedReturnType(cls, methodName) {
|
|
4088
|
+
const filePath = cls.getSourceFile().getFilePath();
|
|
4089
|
+
const className = cls.getName();
|
|
4090
|
+
if (!className) return void 0;
|
|
4091
|
+
const project = getMixinTypeProject();
|
|
4092
|
+
const sourceFile = project.getSourceFile(filePath) ?? project.addSourceFileAtPathIfExists(filePath);
|
|
4093
|
+
const typedCls = sourceFile?.getClass(className);
|
|
4094
|
+
if (!typedCls) return void 0;
|
|
4095
|
+
const prop = typedCls.getType().getProperty(methodName);
|
|
4096
|
+
if (!prop) return void 0;
|
|
4097
|
+
const returnType = prop.getTypeAtLocation(typedCls).getCallSignatures()[0]?.getReturnType();
|
|
4098
|
+
if (!returnType) return void 0;
|
|
4099
|
+
const unwrapped = returnType.getSymbol()?.getName() === "Promise" ? returnType.getTypeArguments()[0] ?? returnType : returnType;
|
|
4100
|
+
return unwrapped.getText(typedCls);
|
|
4101
|
+
}
|
|
4102
|
+
|
|
4103
|
+
// src/discovery/zod-ast-to-ts.ts
|
|
4104
|
+
var import_ts_morph9 = require("ts-morph");
|
|
3991
4105
|
function zodAstToTs(node) {
|
|
3992
|
-
if (!
|
|
4106
|
+
if (!import_ts_morph9.Node.isCallExpression(node)) return "unknown";
|
|
3993
4107
|
const expr = node.getExpression();
|
|
3994
|
-
if (
|
|
4108
|
+
if (import_ts_morph9.Node.isPropertyAccessExpression(expr)) {
|
|
3995
4109
|
const methodName = expr.getName();
|
|
3996
4110
|
const receiver = expr.getExpression();
|
|
3997
4111
|
if (methodName === "optional") {
|
|
@@ -4015,17 +4129,17 @@ function zodAstToTs(node) {
|
|
|
4015
4129
|
case "literal": {
|
|
4016
4130
|
const lit = args[0];
|
|
4017
4131
|
if (!lit) return "unknown";
|
|
4018
|
-
if (
|
|
4019
|
-
if (
|
|
4020
|
-
if (lit.getKind() ===
|
|
4021
|
-
if (lit.getKind() ===
|
|
4132
|
+
if (import_ts_morph9.Node.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
|
|
4133
|
+
if (import_ts_morph9.Node.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
|
|
4134
|
+
if (lit.getKind() === import_ts_morph9.SyntaxKind.TrueKeyword) return "true";
|
|
4135
|
+
if (lit.getKind() === import_ts_morph9.SyntaxKind.FalseKeyword) return "false";
|
|
4022
4136
|
return "unknown";
|
|
4023
4137
|
}
|
|
4024
4138
|
case "enum": {
|
|
4025
4139
|
const arrArg = args[0];
|
|
4026
|
-
if (!arrArg || !
|
|
4140
|
+
if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
4027
4141
|
const members = arrArg.getElements().map(
|
|
4028
|
-
(el) =>
|
|
4142
|
+
(el) => import_ts_morph9.Node.isStringLiteral(el) ? JSON.stringify(el.getLiteralValue()) : "unknown"
|
|
4029
4143
|
);
|
|
4030
4144
|
return members.join(" | ");
|
|
4031
4145
|
}
|
|
@@ -4036,10 +4150,10 @@ function zodAstToTs(node) {
|
|
|
4036
4150
|
}
|
|
4037
4151
|
case "object": {
|
|
4038
4152
|
const objArg = args[0];
|
|
4039
|
-
if (!objArg || !
|
|
4153
|
+
if (!objArg || !import_ts_morph9.Node.isObjectLiteralExpression(objArg)) return "unknown";
|
|
4040
4154
|
const lines = [];
|
|
4041
4155
|
for (const prop of objArg.getProperties()) {
|
|
4042
|
-
if (!
|
|
4156
|
+
if (!import_ts_morph9.Node.isPropertyAssignment(prop)) continue;
|
|
4043
4157
|
const key = prop.getName();
|
|
4044
4158
|
const valNode = prop.getInitializer();
|
|
4045
4159
|
if (!valNode) continue;
|
|
@@ -4051,7 +4165,7 @@ function zodAstToTs(node) {
|
|
|
4051
4165
|
}
|
|
4052
4166
|
case "union": {
|
|
4053
4167
|
const arrArg = args[0];
|
|
4054
|
-
if (!arrArg || !
|
|
4168
|
+
if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
4055
4169
|
return arrArg.getElements().map(zodAstToTs).join(" | ");
|
|
4056
4170
|
}
|
|
4057
4171
|
case "record": {
|
|
@@ -4061,7 +4175,7 @@ function zodAstToTs(node) {
|
|
|
4061
4175
|
}
|
|
4062
4176
|
case "tuple": {
|
|
4063
4177
|
const arrArg = args[0];
|
|
4064
|
-
if (!arrArg || !
|
|
4178
|
+
if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
4065
4179
|
return `[${arrArg.getElements().map(zodAstToTs).join(", ")}]`;
|
|
4066
4180
|
}
|
|
4067
4181
|
default:
|
|
@@ -4071,18 +4185,18 @@ function zodAstToTs(node) {
|
|
|
4071
4185
|
return "unknown";
|
|
4072
4186
|
}
|
|
4073
4187
|
function isOptionalChain(node) {
|
|
4074
|
-
if (!
|
|
4188
|
+
if (!import_ts_morph9.Node.isCallExpression(node)) return false;
|
|
4075
4189
|
const expr = node.getExpression();
|
|
4076
|
-
return
|
|
4190
|
+
return import_ts_morph9.Node.isPropertyAccessExpression(expr) && expr.getName() === "optional";
|
|
4077
4191
|
}
|
|
4078
4192
|
function parseDefineContractCall(callExpr) {
|
|
4079
|
-
if (!
|
|
4193
|
+
if (!import_ts_morph9.Node.isCallExpression(callExpr)) return null;
|
|
4080
4194
|
const callee = callExpr.getExpression();
|
|
4081
|
-
const calleeName =
|
|
4195
|
+
const calleeName = import_ts_morph9.Node.isIdentifier(callee) ? callee.getText() : import_ts_morph9.Node.isPropertyAccessExpression(callee) ? callee.getName() : "";
|
|
4082
4196
|
if (calleeName !== "defineContract") return null;
|
|
4083
4197
|
const args = callExpr.getArguments();
|
|
4084
4198
|
const optsArg = args[0];
|
|
4085
|
-
if (!optsArg || !
|
|
4199
|
+
if (!optsArg || !import_ts_morph9.Node.isObjectLiteralExpression(optsArg)) return null;
|
|
4086
4200
|
let query = null;
|
|
4087
4201
|
let body = null;
|
|
4088
4202
|
let response = "unknown";
|
|
@@ -4090,7 +4204,7 @@ function parseDefineContractCall(callExpr) {
|
|
|
4090
4204
|
let bodyZodText = null;
|
|
4091
4205
|
let queryZodText = null;
|
|
4092
4206
|
for (const prop of optsArg.getProperties()) {
|
|
4093
|
-
if (!
|
|
4207
|
+
if (!import_ts_morph9.Node.isPropertyAssignment(prop)) continue;
|
|
4094
4208
|
const propName = prop.getName();
|
|
4095
4209
|
const val = prop.getInitializer();
|
|
4096
4210
|
if (!val) continue;
|
|
@@ -4111,18 +4225,18 @@ function parseDefineContractCall(callExpr) {
|
|
|
4111
4225
|
|
|
4112
4226
|
// src/discovery/contracts-fast.ts
|
|
4113
4227
|
function resolveTsconfigPath(cwd, tsconfig) {
|
|
4114
|
-
return tsconfig ? (0,
|
|
4228
|
+
return tsconfig ? (0, import_node_path15.resolve)(tsconfig) : (0, import_node_path15.join)(cwd, "tsconfig.json");
|
|
4115
4229
|
}
|
|
4116
4230
|
function createDiscoveryProject(tsconfigPath) {
|
|
4117
4231
|
try {
|
|
4118
|
-
return new
|
|
4232
|
+
return new import_ts_morph10.Project({
|
|
4119
4233
|
tsConfigFilePath: tsconfigPath,
|
|
4120
4234
|
skipAddingFilesFromTsConfig: true,
|
|
4121
4235
|
skipLoadingLibFiles: true,
|
|
4122
4236
|
skipFileDependencyResolution: true
|
|
4123
4237
|
});
|
|
4124
4238
|
} catch {
|
|
4125
|
-
return new
|
|
4239
|
+
return new import_ts_morph10.Project({
|
|
4126
4240
|
skipAddingFilesFromTsConfig: true,
|
|
4127
4241
|
skipLoadingLibFiles: true,
|
|
4128
4242
|
skipFileDependencyResolution: true,
|
|
@@ -4190,7 +4304,7 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
4190
4304
|
async rediscover(changedPaths) {
|
|
4191
4305
|
if (changedPaths) {
|
|
4192
4306
|
for (const p of changedPaths) {
|
|
4193
|
-
const abs = (0,
|
|
4307
|
+
const abs = (0, import_node_path15.resolve)(p);
|
|
4194
4308
|
const sf = this.project.getSourceFile(abs);
|
|
4195
4309
|
if (sf) {
|
|
4196
4310
|
await sf.refreshFromFileSystem();
|
|
@@ -4222,15 +4336,16 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
4222
4336
|
runExtraction() {
|
|
4223
4337
|
clearTypeResolutionCaches(this.project);
|
|
4224
4338
|
clearEnumCache(this.project);
|
|
4339
|
+
clearMixinTypeProject();
|
|
4225
4340
|
return extractRoutesFrom(this.project, this.controllerPaths);
|
|
4226
4341
|
}
|
|
4227
4342
|
};
|
|
4228
4343
|
function decoratorStringArg(decoratorExpr) {
|
|
4229
4344
|
if (!decoratorExpr) return void 0;
|
|
4230
|
-
if (
|
|
4231
|
-
if (
|
|
4345
|
+
if (import_ts_morph10.Node.isStringLiteral(decoratorExpr)) return decoratorExpr.getLiteralValue();
|
|
4346
|
+
if (import_ts_morph10.Node.isArrayLiteralExpression(decoratorExpr)) {
|
|
4232
4347
|
const first = decoratorExpr.getElements()[0];
|
|
4233
|
-
if (first &&
|
|
4348
|
+
if (first && import_ts_morph10.Node.isStringLiteral(first)) return first.getLiteralValue();
|
|
4234
4349
|
}
|
|
4235
4350
|
return void 0;
|
|
4236
4351
|
}
|
|
@@ -4252,7 +4367,8 @@ function joinPaths(prefix, suffix) {
|
|
|
4252
4367
|
if (!prefix && !suffix) return "/";
|
|
4253
4368
|
if (!prefix) return suffix.startsWith("/") ? suffix : `/${suffix}`;
|
|
4254
4369
|
if (!suffix) return prefix.startsWith("/") ? prefix : `/${prefix}`;
|
|
4255
|
-
const
|
|
4370
|
+
const withLeadingSlash = prefix.startsWith("/") ? prefix : `/${prefix}`;
|
|
4371
|
+
const p = withLeadingSlash.endsWith("/") ? withLeadingSlash.slice(0, -1) : withLeadingSlash;
|
|
4256
4372
|
const s = suffix.startsWith("/") ? suffix : `/${suffix}`;
|
|
4257
4373
|
const combined = p + s;
|
|
4258
4374
|
return combined === "" ? "/" : combined;
|
|
@@ -4306,7 +4422,8 @@ function buildRoute(args) {
|
|
|
4306
4422
|
methodAs,
|
|
4307
4423
|
sourceFile,
|
|
4308
4424
|
seenNames,
|
|
4309
|
-
contractSource
|
|
4425
|
+
contractSource,
|
|
4426
|
+
mixin
|
|
4310
4427
|
} = args;
|
|
4311
4428
|
const routeName = resolveRouteName(className, methodName, classAs, methodAs);
|
|
4312
4429
|
const qualifiedRef = `${className}.${methodName}`;
|
|
@@ -4322,7 +4439,12 @@ function buildRoute(args) {
|
|
|
4322
4439
|
path: combinedPath,
|
|
4323
4440
|
name: routeName,
|
|
4324
4441
|
params: extractParams(combinedPath),
|
|
4325
|
-
controllerRef: {
|
|
4442
|
+
controllerRef: {
|
|
4443
|
+
className,
|
|
4444
|
+
methodName,
|
|
4445
|
+
filePath: sourceFile.getFilePath(),
|
|
4446
|
+
...mixin ? { mixin } : {}
|
|
4447
|
+
},
|
|
4326
4448
|
contract: { contractSource }
|
|
4327
4449
|
};
|
|
4328
4450
|
}
|
|
@@ -4336,16 +4458,17 @@ function extractContractRoute(args) {
|
|
|
4336
4458
|
className,
|
|
4337
4459
|
sourceFile,
|
|
4338
4460
|
project,
|
|
4339
|
-
seenNames
|
|
4461
|
+
seenNames,
|
|
4462
|
+
mixin
|
|
4340
4463
|
} = args;
|
|
4341
4464
|
const firstDecoratorArg = applyContractDecorator.getArguments()[0];
|
|
4342
4465
|
if (!firstDecoratorArg) return null;
|
|
4343
4466
|
let contractDef = null;
|
|
4344
4467
|
let bodyZodRef = null;
|
|
4345
4468
|
let queryZodRef = null;
|
|
4346
|
-
if (
|
|
4469
|
+
if (import_ts_morph10.Node.isCallExpression(firstDecoratorArg)) {
|
|
4347
4470
|
contractDef = parseDefineContractCall(firstDecoratorArg);
|
|
4348
|
-
} else if (
|
|
4471
|
+
} else if (import_ts_morph10.Node.isIdentifier(firstDecoratorArg)) {
|
|
4349
4472
|
const identName = firstDecoratorArg.getText();
|
|
4350
4473
|
const resolvedVar = resolveImportedVariable(identName, sourceFile, project);
|
|
4351
4474
|
if (!resolvedVar) {
|
|
@@ -4388,6 +4511,7 @@ function extractContractRoute(args) {
|
|
|
4388
4511
|
methodAs,
|
|
4389
4512
|
sourceFile,
|
|
4390
4513
|
seenNames,
|
|
4514
|
+
mixin,
|
|
4391
4515
|
contractSource: {
|
|
4392
4516
|
query: contractDef.query,
|
|
4393
4517
|
body: contractDef.body,
|
|
@@ -4404,13 +4528,14 @@ function extractContractRoute(args) {
|
|
|
4404
4528
|
});
|
|
4405
4529
|
}
|
|
4406
4530
|
function extractDtoRoute(args) {
|
|
4407
|
-
const { cls, method, verb, prefix, className, sourceFile, project, seenNames } = args;
|
|
4531
|
+
const { cls, method, verb, prefix, className, sourceFile, project, seenNames, mixin } = args;
|
|
4408
4532
|
if (!verb) return null;
|
|
4409
4533
|
const combined = joinPaths(prefix, verb.handlerPath);
|
|
4410
4534
|
const methodName = method.getName();
|
|
4411
4535
|
const classAs = readAsDecorator(cls, `class ${className}`);
|
|
4412
4536
|
const methodAs = readAsDecorator(method, `${className}.${methodName}`);
|
|
4413
|
-
const dtoContract = extractDtoContract(method, sourceFile, project);
|
|
4537
|
+
const dtoContract = extractDtoContract(method, sourceFile, project, mixin);
|
|
4538
|
+
const mixinResponse = mixin ? resolveInstantiatedReturnType(cls, methodName) : void 0;
|
|
4414
4539
|
return buildRoute({
|
|
4415
4540
|
className,
|
|
4416
4541
|
methodName,
|
|
@@ -4420,10 +4545,11 @@ function extractDtoRoute(args) {
|
|
|
4420
4545
|
methodAs,
|
|
4421
4546
|
sourceFile,
|
|
4422
4547
|
seenNames,
|
|
4548
|
+
mixin,
|
|
4423
4549
|
contractSource: {
|
|
4424
4550
|
query: dtoContract?.query ?? null,
|
|
4425
4551
|
body: dtoContract?.body ?? null,
|
|
4426
|
-
response: dtoContract?.response ?? "unknown",
|
|
4552
|
+
response: mixinResponse ?? dtoContract?.response ?? "unknown",
|
|
4427
4553
|
error: dtoContract?.error ?? null,
|
|
4428
4554
|
queryRef: dtoContract?.queryRef ?? null,
|
|
4429
4555
|
bodyRef: dtoContract?.bodyRef ?? null,
|
|
@@ -4452,7 +4578,17 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
4452
4578
|
const firstArg2 = controllerDecorator.getArguments()[0];
|
|
4453
4579
|
const prefix = decoratorStringArg(firstArg2) ?? "";
|
|
4454
4580
|
const className = cls.getName() ?? "Unknown";
|
|
4455
|
-
|
|
4581
|
+
const inherited = resolveInheritedMethods(cls);
|
|
4582
|
+
const mixinBinding = inherited ? {
|
|
4583
|
+
factoryName: inherited.factoryName,
|
|
4584
|
+
factoryFilePath: inherited.factoryFilePath,
|
|
4585
|
+
classArgs: inherited.classArgs
|
|
4586
|
+
} : void 0;
|
|
4587
|
+
const methods = [
|
|
4588
|
+
...cls.getMethods().map((method) => ({ method })),
|
|
4589
|
+
...(inherited?.methods ?? []).map((method) => ({ method, mixin: mixinBinding }))
|
|
4590
|
+
];
|
|
4591
|
+
for (const { method, mixin } of methods) {
|
|
4456
4592
|
const verb = resolveVerb(method);
|
|
4457
4593
|
const applyContractDecorator = method.getDecorator("ApplyContract");
|
|
4458
4594
|
const route = applyContractDecorator ? extractContractRoute({
|
|
@@ -4464,7 +4600,8 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
4464
4600
|
className,
|
|
4465
4601
|
sourceFile,
|
|
4466
4602
|
project,
|
|
4467
|
-
seenNames
|
|
4603
|
+
seenNames,
|
|
4604
|
+
mixin
|
|
4468
4605
|
}) : extractDtoRoute({
|
|
4469
4606
|
cls,
|
|
4470
4607
|
method,
|
|
@@ -4473,7 +4610,8 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
4473
4610
|
className,
|
|
4474
4611
|
sourceFile,
|
|
4475
4612
|
project,
|
|
4476
|
-
seenNames
|
|
4613
|
+
seenNames,
|
|
4614
|
+
mixin
|
|
4477
4615
|
});
|
|
4478
4616
|
if (route) routes.push(route);
|
|
4479
4617
|
}
|
|
@@ -4484,7 +4622,7 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
4484
4622
|
// src/watch/lock-file.ts
|
|
4485
4623
|
var import_promises12 = require("fs/promises");
|
|
4486
4624
|
var import_promises13 = require("fs/promises");
|
|
4487
|
-
var
|
|
4625
|
+
var import_node_path16 = require("path");
|
|
4488
4626
|
var LOCK_FILE = ".watcher.lock";
|
|
4489
4627
|
function isProcessAlive(pid) {
|
|
4490
4628
|
try {
|
|
@@ -4496,7 +4634,7 @@ function isProcessAlive(pid) {
|
|
|
4496
4634
|
}
|
|
4497
4635
|
async function acquireLock(outDir) {
|
|
4498
4636
|
await (0, import_promises13.mkdir)(outDir, { recursive: true });
|
|
4499
|
-
const lockPath = (0,
|
|
4637
|
+
const lockPath = (0, import_node_path16.join)(outDir, LOCK_FILE);
|
|
4500
4638
|
const lockData = { pid: process.pid, startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
4501
4639
|
try {
|
|
4502
4640
|
const fd = await (0, import_promises12.open)(lockPath, "wx");
|
|
@@ -4537,7 +4675,7 @@ async function watch(config, onChange, options = {}) {
|
|
|
4537
4675
|
if (lock === null) {
|
|
4538
4676
|
let holderPid = "unknown";
|
|
4539
4677
|
try {
|
|
4540
|
-
const raw = await (0, import_promises14.readFile)((0,
|
|
4678
|
+
const raw = await (0, import_promises14.readFile)((0, import_node_path17.join)(config.codegen.outDir, ".watcher.lock"), "utf8");
|
|
4541
4679
|
const data = JSON.parse(raw);
|
|
4542
4680
|
if (data.pid !== void 0) holderPid = String(data.pid);
|
|
4543
4681
|
} catch {
|
|
@@ -4590,7 +4728,7 @@ async function watch(config, onChange, options = {}) {
|
|
|
4590
4728
|
}
|
|
4591
4729
|
let pagesDebounceTimer;
|
|
4592
4730
|
const pagesGlob = config.pages?.glob ?? ".nestjs-codegen-no-pages";
|
|
4593
|
-
const pagesWatcher = import_chokidar.default.watch((0,
|
|
4731
|
+
const pagesWatcher = import_chokidar.default.watch((0, import_node_path17.join)(config.codegen.cwd, pagesGlob), {
|
|
4594
4732
|
ignoreInitial: true,
|
|
4595
4733
|
persistent: true,
|
|
4596
4734
|
awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
|
|
@@ -4617,7 +4755,7 @@ async function watch(config, onChange, options = {}) {
|
|
|
4617
4755
|
pagesWatcher.on("unlink", schedulePagesRegenerate);
|
|
4618
4756
|
let contractsDebounceTimer;
|
|
4619
4757
|
const pendingChangedPaths = /* @__PURE__ */ new Set();
|
|
4620
|
-
const contractsWatcher = import_chokidar.default.watch((0,
|
|
4758
|
+
const contractsWatcher = import_chokidar.default.watch((0, import_node_path17.join)(config.codegen.cwd, config.contracts.glob), {
|
|
4621
4759
|
ignoreInitial: true,
|
|
4622
4760
|
persistent: true,
|
|
4623
4761
|
awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
|
|
@@ -4647,7 +4785,7 @@ async function watch(config, onChange, options = {}) {
|
|
|
4647
4785
|
contractsWatcher.on("add", (p) => scheduleContractsRegenerate(p));
|
|
4648
4786
|
contractsWatcher.on("change", (p) => scheduleContractsRegenerate(p));
|
|
4649
4787
|
contractsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p));
|
|
4650
|
-
const formsWatcher = import_chokidar.default.watch((0,
|
|
4788
|
+
const formsWatcher = import_chokidar.default.watch((0, import_node_path17.join)(config.codegen.cwd, config.forms.watch), {
|
|
4651
4789
|
ignoreInitial: true,
|
|
4652
4790
|
persistent: true,
|
|
4653
4791
|
awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
|
|
@@ -4674,7 +4812,7 @@ async function watch(config, onChange, options = {}) {
|
|
|
4674
4812
|
}
|
|
4675
4813
|
|
|
4676
4814
|
// src/index.ts
|
|
4677
|
-
var VERSION = "0.
|
|
4815
|
+
var VERSION = "0.17.0";
|
|
4678
4816
|
|
|
4679
4817
|
// src/generate-manifest.ts
|
|
4680
4818
|
var MANIFEST_FILE = ".codegen-manifest.json";
|
|
@@ -4698,6 +4836,9 @@ function isManifestShape(value) {
|
|
|
4698
4836
|
if (candidate.configKeyHashes !== void 0 && !isStringRecord(candidate.configKeyHashes)) {
|
|
4699
4837
|
return false;
|
|
4700
4838
|
}
|
|
4839
|
+
if (candidate.extraInputs !== void 0 && (!Array.isArray(candidate.extraInputs) || !candidate.extraInputs.every((entry) => typeof entry === "string"))) {
|
|
4840
|
+
return false;
|
|
4841
|
+
}
|
|
4701
4842
|
if (!Array.isArray(candidate.files)) return false;
|
|
4702
4843
|
return candidate.files.every((entry) => typeof entry === "string");
|
|
4703
4844
|
}
|
|
@@ -4737,26 +4878,35 @@ async function discoverInputFiles(config) {
|
|
|
4737
4878
|
const matched = await (0, import_fast_glob3.default)(globs, { cwd, absolute: true, onlyFiles: true });
|
|
4738
4879
|
return [...new Set(matched)].sort();
|
|
4739
4880
|
}
|
|
4740
|
-
async function computeInputsHash(config) {
|
|
4881
|
+
async function computeInputsHash(config, extraInputs = []) {
|
|
4741
4882
|
const hash = (0, import_node_crypto.createHash)("sha256");
|
|
4742
4883
|
hash.update(`version:${VERSION}
|
|
4743
4884
|
`);
|
|
4744
4885
|
hash.update(`config:${serializeConfig(config)}
|
|
4745
4886
|
`);
|
|
4746
|
-
const inputFiles = await discoverInputFiles(config);
|
|
4747
4887
|
const cwd = config.codegen.cwd;
|
|
4748
|
-
|
|
4888
|
+
const globbed = await discoverInputFiles(config);
|
|
4889
|
+
const globbedRelative = new Set(globbed.map((file) => (0, import_node_path18.relative)(cwd, file)));
|
|
4890
|
+
const extra = [...new Set(extraInputs)].filter((file) => !globbedRelative.has(file)).sort();
|
|
4891
|
+
for (const file of globbed) {
|
|
4749
4892
|
const contents = await (0, import_promises15.readFile)(file, "utf8");
|
|
4750
|
-
hash.update(`file:${(0,
|
|
4893
|
+
hash.update(`file:${(0, import_node_path18.relative)(cwd, file)}
|
|
4751
4894
|
`);
|
|
4752
4895
|
hash.update(contents);
|
|
4753
4896
|
hash.update("\n");
|
|
4754
4897
|
}
|
|
4898
|
+
for (const file of extra) {
|
|
4899
|
+
const contents = await (0, import_promises15.readFile)((0, import_node_path18.join)(cwd, file), "utf8").catch(() => null);
|
|
4900
|
+
hash.update(`extra:${file}
|
|
4901
|
+
`);
|
|
4902
|
+
hash.update(contents ?? "\0missing");
|
|
4903
|
+
hash.update("\n");
|
|
4904
|
+
}
|
|
4755
4905
|
return hash.digest("hex");
|
|
4756
4906
|
}
|
|
4757
4907
|
async function readManifest(outDir) {
|
|
4758
4908
|
try {
|
|
4759
|
-
const raw = await (0, import_promises15.readFile)((0,
|
|
4909
|
+
const raw = await (0, import_promises15.readFile)((0, import_node_path18.join)(outDir, MANIFEST_FILE), "utf8");
|
|
4760
4910
|
const parsed = JSON.parse(raw);
|
|
4761
4911
|
if (!isManifestShape(parsed)) return null;
|
|
4762
4912
|
return {
|
|
@@ -4765,6 +4915,7 @@ async function readManifest(outDir) {
|
|
|
4765
4915
|
...parsed.entryPoint ? { entryPoint: parsed.entryPoint } : {},
|
|
4766
4916
|
...parsed.configHash ? { configHash: parsed.configHash } : {},
|
|
4767
4917
|
...parsed.configKeyHashes ? { configKeyHashes: parsed.configKeyHashes } : {},
|
|
4918
|
+
...parsed.extraInputs ? { extraInputs: parsed.extraInputs } : {},
|
|
4768
4919
|
files: parsed.files
|
|
4769
4920
|
};
|
|
4770
4921
|
} catch {
|
|
@@ -4775,7 +4926,7 @@ function computeConfigHash(config) {
|
|
|
4775
4926
|
return (0, import_node_crypto.createHash)("sha256").update(serializeConfig(config)).digest("hex");
|
|
4776
4927
|
}
|
|
4777
4928
|
async function writeManifest(outDir, manifest) {
|
|
4778
|
-
await (0, import_promises15.writeFile)((0,
|
|
4929
|
+
await (0, import_promises15.writeFile)((0, import_node_path18.join)(outDir, MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}
|
|
4779
4930
|
`, "utf8");
|
|
4780
4931
|
}
|
|
4781
4932
|
async function listOutputFiles(outDir) {
|
|
@@ -4783,11 +4934,11 @@ async function listOutputFiles(outDir) {
|
|
|
4783
4934
|
async function walk(dir) {
|
|
4784
4935
|
const entries = await (0, import_promises15.readdir)(dir, { withFileTypes: true }).catch(() => []);
|
|
4785
4936
|
for (const entry of entries) {
|
|
4786
|
-
const abs = (0,
|
|
4937
|
+
const abs = (0, import_node_path18.join)(dir, entry.name);
|
|
4787
4938
|
if (entry.isDirectory()) {
|
|
4788
4939
|
await walk(abs);
|
|
4789
4940
|
} else if (entry.isFile()) {
|
|
4790
|
-
const rel = (0,
|
|
4941
|
+
const rel = (0, import_node_path18.relative)(outDir, abs);
|
|
4791
4942
|
if (rel === MANIFEST_FILE || rel === LOCK_FILE2) continue;
|
|
4792
4943
|
found.push(rel);
|
|
4793
4944
|
}
|