@dudousxd/nestjs-codegen 0.20.0 → 0.21.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +91 -0
- package/dist/cli/main.cjs +490 -451
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.js +369 -329
- package/dist/cli/main.js.map +1 -1
- package/dist/index.cjs +490 -451
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +355 -315
- package/dist/index.js.map +1 -1
- package/dist/nest/index.cjs +4213 -4177
- package/dist/nest/index.cjs.map +1 -1
- package/dist/nest/index.js +4232 -4195
- package/dist/nest/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/main.js
CHANGED
|
@@ -185,7 +185,7 @@ Run \`nestjs-codegen init\` to create a starter config.`
|
|
|
185
185
|
|
|
186
186
|
// src/generate.ts
|
|
187
187
|
import { mkdir as mkdir9, writeFile as writeFile10 } from "fs/promises";
|
|
188
|
-
import { dirname as
|
|
188
|
+
import { dirname as dirname4, join as join14 } from "path";
|
|
189
189
|
|
|
190
190
|
// src/discovery/pages.ts
|
|
191
191
|
import { readFile } from "fs/promises";
|
|
@@ -2172,275 +2172,17 @@ function buildEmpty() {
|
|
|
2172
2172
|
// src/generate-manifest.ts
|
|
2173
2173
|
import { createHash } from "crypto";
|
|
2174
2174
|
import { readFile as readFile2, readdir, writeFile as writeFile9 } from "fs/promises";
|
|
2175
|
-
import { join as
|
|
2176
|
-
import
|
|
2177
|
-
var MANIFEST_FILE = ".codegen-manifest.json";
|
|
2178
|
-
var LOCK_FILE = ".watcher.lock";
|
|
2179
|
-
var DriftGuardError = class extends Error {
|
|
2180
|
-
constructor(message) {
|
|
2181
|
-
super(message);
|
|
2182
|
-
this.name = "DriftGuardError";
|
|
2183
|
-
}
|
|
2184
|
-
};
|
|
2185
|
-
function isEntryPoint(value) {
|
|
2186
|
-
return value === "cli" || value === "module";
|
|
2187
|
-
}
|
|
2188
|
-
function isManifestShape(value) {
|
|
2189
|
-
if (typeof value !== "object" || value === null) return false;
|
|
2190
|
-
const candidate = value;
|
|
2191
|
-
if (typeof candidate.version !== "string") return false;
|
|
2192
|
-
if (typeof candidate.hash !== "string") return false;
|
|
2193
|
-
if (candidate.entryPoint !== void 0 && !isEntryPoint(candidate.entryPoint)) return false;
|
|
2194
|
-
if (candidate.configHash !== void 0 && typeof candidate.configHash !== "string") return false;
|
|
2195
|
-
if (candidate.configKeyHashes !== void 0 && !isStringRecord(candidate.configKeyHashes)) {
|
|
2196
|
-
return false;
|
|
2197
|
-
}
|
|
2198
|
-
if (candidate.extraInputs !== void 0 && (!Array.isArray(candidate.extraInputs) || !candidate.extraInputs.every((entry) => typeof entry === "string"))) {
|
|
2199
|
-
return false;
|
|
2200
|
-
}
|
|
2201
|
-
if (!Array.isArray(candidate.files)) return false;
|
|
2202
|
-
return candidate.files.every((entry) => typeof entry === "string");
|
|
2203
|
-
}
|
|
2204
|
-
function isStringRecord(value) {
|
|
2205
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
2206
|
-
return Object.values(value).every((entry) => typeof entry === "string");
|
|
2207
|
-
}
|
|
2208
|
-
function serializeConfig(config) {
|
|
2209
|
-
return serializeConfigValue(config, `unserializable:${config.codegen.outDir}`);
|
|
2210
|
-
}
|
|
2211
|
-
function serializeConfigValue(value, unserializableMarker) {
|
|
2212
|
-
try {
|
|
2213
|
-
return JSON.stringify(value, (_key, entry) => {
|
|
2214
|
-
if (typeof entry === "function") return `[fn:${entry.name}]`;
|
|
2215
|
-
return entry;
|
|
2216
|
-
});
|
|
2217
|
-
} catch {
|
|
2218
|
-
return unserializableMarker;
|
|
2219
|
-
}
|
|
2220
|
-
}
|
|
2221
|
-
function computeConfigKeyHashes(config) {
|
|
2222
|
-
const hashes = {};
|
|
2223
|
-
for (const [key, value] of Object.entries(config)) {
|
|
2224
|
-
if (value === void 0) continue;
|
|
2225
|
-
hashes[key] = createHash("sha256").update(serializeConfigValue(value, `unserializable:${key}`)).digest("hex");
|
|
2226
|
-
}
|
|
2227
|
-
return hashes;
|
|
2228
|
-
}
|
|
2229
|
-
function diffConfigKeyHashes(previous, current) {
|
|
2230
|
-
const keys = /* @__PURE__ */ new Set([...Object.keys(previous), ...Object.keys(current)]);
|
|
2231
|
-
return [...keys].filter((key) => previous[key] !== current[key]).sort();
|
|
2232
|
-
}
|
|
2233
|
-
async function discoverInputFiles(config) {
|
|
2234
|
-
const globs = [config.contracts.glob, config.forms.watch];
|
|
2235
|
-
if (config.pages) globs.push(config.pages.glob);
|
|
2236
|
-
const cwd = config.codegen.cwd;
|
|
2237
|
-
const matched = await fg2(globs, { cwd, absolute: true, onlyFiles: true });
|
|
2238
|
-
return [...new Set(matched)].sort();
|
|
2239
|
-
}
|
|
2240
|
-
async function computeInputsHash(config, extraInputs = []) {
|
|
2241
|
-
const hash = createHash("sha256");
|
|
2242
|
-
hash.update(`version:${VERSION}
|
|
2243
|
-
`);
|
|
2244
|
-
hash.update(`config:${serializeConfig(config)}
|
|
2245
|
-
`);
|
|
2246
|
-
const cwd = config.codegen.cwd;
|
|
2247
|
-
const globbed = await discoverInputFiles(config);
|
|
2248
|
-
const globbedRelative = new Set(globbed.map((file) => relative7(cwd, file)));
|
|
2249
|
-
const extra = [...new Set(extraInputs)].filter((file) => !globbedRelative.has(file)).sort();
|
|
2250
|
-
for (const file of globbed) {
|
|
2251
|
-
const contents = await readFile2(file, "utf8");
|
|
2252
|
-
hash.update(`file:${relative7(cwd, file)}
|
|
2253
|
-
`);
|
|
2254
|
-
hash.update(contents);
|
|
2255
|
-
hash.update("\n");
|
|
2256
|
-
}
|
|
2257
|
-
for (const file of extra) {
|
|
2258
|
-
const contents = await readFile2(join12(cwd, file), "utf8").catch(() => null);
|
|
2259
|
-
hash.update(`extra:${file}
|
|
2260
|
-
`);
|
|
2261
|
-
hash.update(contents ?? "\0missing");
|
|
2262
|
-
hash.update("\n");
|
|
2263
|
-
}
|
|
2264
|
-
return hash.digest("hex");
|
|
2265
|
-
}
|
|
2266
|
-
async function readManifest(outDir) {
|
|
2267
|
-
try {
|
|
2268
|
-
const raw = await readFile2(join12(outDir, MANIFEST_FILE), "utf8");
|
|
2269
|
-
const parsed = JSON.parse(raw);
|
|
2270
|
-
if (!isManifestShape(parsed)) return null;
|
|
2271
|
-
return {
|
|
2272
|
-
version: parsed.version,
|
|
2273
|
-
hash: parsed.hash,
|
|
2274
|
-
...parsed.entryPoint ? { entryPoint: parsed.entryPoint } : {},
|
|
2275
|
-
...parsed.configHash ? { configHash: parsed.configHash } : {},
|
|
2276
|
-
...parsed.configKeyHashes ? { configKeyHashes: parsed.configKeyHashes } : {},
|
|
2277
|
-
...parsed.extraInputs ? { extraInputs: parsed.extraInputs } : {},
|
|
2278
|
-
files: parsed.files
|
|
2279
|
-
};
|
|
2280
|
-
} catch {
|
|
2281
|
-
return null;
|
|
2282
|
-
}
|
|
2283
|
-
}
|
|
2284
|
-
function computeConfigHash(config) {
|
|
2285
|
-
return createHash("sha256").update(serializeConfig(config)).digest("hex");
|
|
2286
|
-
}
|
|
2287
|
-
async function writeManifest(outDir, manifest) {
|
|
2288
|
-
await writeFile9(join12(outDir, MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}
|
|
2289
|
-
`, "utf8");
|
|
2290
|
-
}
|
|
2291
|
-
async function listOutputFiles(outDir) {
|
|
2292
|
-
const found = [];
|
|
2293
|
-
async function walk(dir) {
|
|
2294
|
-
const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
2295
|
-
for (const entry of entries) {
|
|
2296
|
-
const abs = join12(dir, entry.name);
|
|
2297
|
-
if (entry.isDirectory()) {
|
|
2298
|
-
await walk(abs);
|
|
2299
|
-
} else if (entry.isFile()) {
|
|
2300
|
-
const rel = relative7(outDir, abs);
|
|
2301
|
-
if (rel === MANIFEST_FILE || rel === LOCK_FILE) continue;
|
|
2302
|
-
found.push(rel);
|
|
2303
|
-
}
|
|
2304
|
-
}
|
|
2305
|
-
}
|
|
2306
|
-
await walk(outDir);
|
|
2307
|
-
return found.sort();
|
|
2308
|
-
}
|
|
2309
|
-
async function allOutputsExist(outDir, files) {
|
|
2310
|
-
const present = new Set(await listOutputFiles(outDir));
|
|
2311
|
-
return files.every((file) => present.has(file));
|
|
2312
|
-
}
|
|
2313
|
-
async function isManifestFresh(outDir, manifest, inputsHash) {
|
|
2314
|
-
if (manifest === null) return false;
|
|
2315
|
-
if (manifest.version !== VERSION) return false;
|
|
2316
|
-
if (manifest.hash !== inputsHash) return false;
|
|
2317
|
-
if (manifest.files.length === 0) return false;
|
|
2318
|
-
return allOutputsExist(outDir, manifest.files);
|
|
2319
|
-
}
|
|
2320
|
-
|
|
2321
|
-
// src/util/debug-log.ts
|
|
2322
|
-
var debugEnabled = false;
|
|
2323
|
-
function setCodegenDebug(enabled) {
|
|
2324
|
-
debugEnabled = enabled;
|
|
2325
|
-
}
|
|
2326
|
-
function debugWarn(message) {
|
|
2327
|
-
if (debugEnabled) console.warn(`[nestjs-codegen] ${message}`);
|
|
2328
|
-
}
|
|
2329
|
-
|
|
2330
|
-
// src/generate.ts
|
|
2331
|
-
function driftGuardMessage(outDir, previousEntryPoint, currentEntryPoint, differingKeys) {
|
|
2332
|
-
const differ = differingKeys.length > 0 ? `their resolved configs differ at: ${differingKeys.map((key) => `\`${key}\``).join(", ")}` : "their resolved configs differ (re-run after this generate records per-key hashes to see which keys)";
|
|
2333
|
-
return `[nestjs-codegen] Config drift detected in "${outDir}": the last generate ran from the "${previousEntryPoint}" entry point, this run is from the "${currentEntryPoint}" entry point, and ${differ}. Both entry points must read the SAME config \u2014 export a shared config object (e.g. codegen.config.ts) and import it from BOTH nestjs-codegen.config.ts (CLI) and NestjsCodegenModule.forRoot() (Nest module), or set \`driftGuard: false\` on either config to opt out of this check.`;
|
|
2334
|
-
}
|
|
2335
|
-
async function generate(config, inputRoutes = [], entryPoint = "cli") {
|
|
2336
|
-
setCodegenDebug(config.debug);
|
|
2337
|
-
const manifest = await readManifest(config.codegen.outDir);
|
|
2338
|
-
const inputsHash = await computeInputsHash(config, manifest?.extraInputs ?? []);
|
|
2339
|
-
if (await isManifestFresh(config.codegen.outDir, manifest, inputsHash)) {
|
|
2340
|
-
console.log(`[nestjs-codegen] ${config.codegen.outDir} up to date, skipped`);
|
|
2341
|
-
return;
|
|
2342
|
-
}
|
|
2343
|
-
const configHash = computeConfigHash(config);
|
|
2344
|
-
const configKeyHashes = computeConfigKeyHashes(config);
|
|
2345
|
-
if (config.driftGuard && manifest?.entryPoint && manifest.entryPoint !== entryPoint && manifest.configHash && manifest.configHash !== configHash) {
|
|
2346
|
-
throw new DriftGuardError(
|
|
2347
|
-
driftGuardMessage(
|
|
2348
|
-
config.codegen.outDir,
|
|
2349
|
-
manifest.entryPoint,
|
|
2350
|
-
entryPoint,
|
|
2351
|
-
// A pre-key-hash manifest can't tell us WHICH keys differ — pass none
|
|
2352
|
-
// rather than diffing against {} (which would name every key).
|
|
2353
|
-
manifest.configKeyHashes ? diffConfigKeyHashes(manifest.configKeyHashes, configKeyHashes) : []
|
|
2354
|
-
)
|
|
2355
|
-
);
|
|
2356
|
-
}
|
|
2357
|
-
const extensions = config.extensions ?? [];
|
|
2358
|
-
let routes = inputRoutes;
|
|
2359
|
-
const trackedInputs = /* @__PURE__ */ new Set();
|
|
2360
|
-
const ctx = createExtensionContext(config, () => routes, trackedInputs);
|
|
2361
|
-
if (extensions.length > 0) {
|
|
2362
|
-
routes = await applyTransformRoutes(routes, extensions, ctx);
|
|
2363
|
-
}
|
|
2364
|
-
if (config.pages) {
|
|
2365
|
-
const pagesConfig = config.pages;
|
|
2366
|
-
const pages = await discoverPages({
|
|
2367
|
-
glob: pagesConfig.glob,
|
|
2368
|
-
cwd: config.codegen.cwd,
|
|
2369
|
-
propsExport: pagesConfig.propsExport,
|
|
2370
|
-
componentNameStrategy: pagesConfig.componentNameStrategy
|
|
2371
|
-
});
|
|
2372
|
-
const sharedProps = discoverSharedPropsFromConfig(config);
|
|
2373
|
-
await emitPages(pages, config.codegen.outDir, {
|
|
2374
|
-
propsExport: pagesConfig.propsExport,
|
|
2375
|
-
sharedProps
|
|
2376
|
-
});
|
|
2377
|
-
await emitCache(pages, config.codegen.outDir);
|
|
2378
|
-
}
|
|
2379
|
-
const hasRoutes = routes.length > 0;
|
|
2380
|
-
const hasContracts = routes.some((r) => r.contract);
|
|
2381
|
-
if (hasRoutes) {
|
|
2382
|
-
await emitRoutes(routes, config.codegen.outDir);
|
|
2383
|
-
}
|
|
2384
|
-
if (hasContracts) {
|
|
2385
|
-
await emitApi(routes, config.codegen.outDir, {
|
|
2386
|
-
...config.fetcher?.importPath ? { fetcherImportPath: config.fetcher.importPath } : {},
|
|
2387
|
-
serialization: config.serialization,
|
|
2388
|
-
extensions,
|
|
2389
|
-
ctx
|
|
2390
|
-
});
|
|
2391
|
-
}
|
|
2392
|
-
const hasForms = await emitForms(routes, config.codegen.outDir, config.forms, config.validation);
|
|
2393
|
-
if (hasContracts && config.openapi.enabled) {
|
|
2394
|
-
await emitOpenApi(routes, config.codegen.outDir, {
|
|
2395
|
-
fileName: config.openapi.fileName,
|
|
2396
|
-
info: {
|
|
2397
|
-
title: config.openapi.title,
|
|
2398
|
-
version: config.openapi.version,
|
|
2399
|
-
...config.openapi.description ? { description: config.openapi.description } : {}
|
|
2400
|
-
}
|
|
2401
|
-
});
|
|
2402
|
-
}
|
|
2403
|
-
if (hasContracts && config.mocks.enabled) {
|
|
2404
|
-
await emitMocks(routes, config.codegen.outDir, {
|
|
2405
|
-
fileName: config.mocks.fileName,
|
|
2406
|
-
seed: config.mocks.seed,
|
|
2407
|
-
baseUrl: config.mocks.baseUrl
|
|
2408
|
-
});
|
|
2409
|
-
}
|
|
2410
|
-
await emitIndex(config.codegen.outDir, hasContracts, hasForms);
|
|
2411
|
-
if (extensions.length > 0) {
|
|
2412
|
-
const extraFiles = await collectEmittedFiles(extensions, ctx);
|
|
2413
|
-
for (const file of extraFiles) {
|
|
2414
|
-
const dest = join13(config.codegen.outDir, file.path);
|
|
2415
|
-
await mkdir9(dirname2(dest), { recursive: true });
|
|
2416
|
-
await writeFile10(dest, file.contents, "utf8");
|
|
2417
|
-
}
|
|
2418
|
-
}
|
|
2419
|
-
const outputFiles = await listOutputFiles(config.codegen.outDir);
|
|
2420
|
-
const extraInputs = [...trackedInputs].sort();
|
|
2421
|
-
const recordedHash = extraInputs.length > 0 ? await computeInputsHash(config, extraInputs) : inputsHash;
|
|
2422
|
-
await writeManifest(config.codegen.outDir, {
|
|
2423
|
-
version: VERSION,
|
|
2424
|
-
hash: recordedHash,
|
|
2425
|
-
entryPoint,
|
|
2426
|
-
configHash,
|
|
2427
|
-
configKeyHashes,
|
|
2428
|
-
files: outputFiles,
|
|
2429
|
-
...extraInputs.length > 0 ? { extraInputs } : {}
|
|
2430
|
-
});
|
|
2431
|
-
}
|
|
2432
|
-
|
|
2433
|
-
// src/watch/watcher.ts
|
|
2434
|
-
import { readFile as readFile4 } from "fs/promises";
|
|
2435
|
-
import { join as join16 } from "path";
|
|
2436
|
-
import chokidar from "chokidar";
|
|
2175
|
+
import { join as join13, relative as relative7 } from "path";
|
|
2176
|
+
import fg3 from "fast-glob";
|
|
2437
2177
|
|
|
2438
2178
|
// src/discovery/contracts-fast.ts
|
|
2439
|
-
import {
|
|
2440
|
-
import
|
|
2179
|
+
import { existsSync } from "fs";
|
|
2180
|
+
import { dirname as dirname3, join as join12, resolve as resolve4 } from "path";
|
|
2181
|
+
import fg2 from "fast-glob";
|
|
2441
2182
|
import {
|
|
2442
2183
|
Node as Node9,
|
|
2443
|
-
Project as Project4
|
|
2184
|
+
Project as Project4,
|
|
2185
|
+
ts
|
|
2444
2186
|
} from "ts-morph";
|
|
2445
2187
|
|
|
2446
2188
|
// src/discovery/dto-type-resolver.ts
|
|
@@ -2454,9 +2196,17 @@ import {
|
|
|
2454
2196
|
Node as Node3
|
|
2455
2197
|
} from "ts-morph";
|
|
2456
2198
|
|
|
2199
|
+
// src/util/debug-log.ts
|
|
2200
|
+
var debugEnabled = false;
|
|
2201
|
+
function setCodegenDebug(enabled) {
|
|
2202
|
+
debugEnabled = enabled;
|
|
2203
|
+
}
|
|
2204
|
+
function debugWarn(message) {
|
|
2205
|
+
if (debugEnabled) console.warn(`[nestjs-codegen] ${message}`);
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2457
2208
|
// src/discovery/type-ref-resolution.ts
|
|
2458
|
-
import {
|
|
2459
|
-
import { dirname as dirname3, resolve as resolve3 } from "path";
|
|
2209
|
+
import { dirname as dirname2, resolve as resolve3 } from "path";
|
|
2460
2210
|
import {
|
|
2461
2211
|
Node as Node2
|
|
2462
2212
|
} from "ts-morph";
|
|
@@ -2472,16 +2222,6 @@ var _debug = process.env.NESTJS_INERTIA_DEBUG === "1";
|
|
|
2472
2222
|
function dbg(...args) {
|
|
2473
2223
|
if (_debug) console.log("[codegen:debug]", ...args);
|
|
2474
2224
|
}
|
|
2475
|
-
function loadTsconfigPaths(tsconfigPath) {
|
|
2476
|
-
try {
|
|
2477
|
-
const raw = readFileSync(tsconfigPath, "utf8");
|
|
2478
|
-
const stripped = raw.replace(/\/\/.*$/gm, "");
|
|
2479
|
-
const parsed = JSON.parse(stripped);
|
|
2480
|
-
return parsed.compilerOptions?.paths ?? null;
|
|
2481
|
-
} catch {
|
|
2482
|
-
return null;
|
|
2483
|
-
}
|
|
2484
|
-
}
|
|
2485
2225
|
function findTypeInFile(name, file) {
|
|
2486
2226
|
const cls = file.getClass(name);
|
|
2487
2227
|
if (cls) return { kind: "class", decl: cls, file };
|
|
@@ -2510,7 +2250,7 @@ function findTypeInFile(name, file) {
|
|
|
2510
2250
|
}
|
|
2511
2251
|
function resolveModuleSpecifier(moduleSpecifier, sourceFile, project) {
|
|
2512
2252
|
if (moduleSpecifier.startsWith(".")) {
|
|
2513
|
-
const dir =
|
|
2253
|
+
const dir = dirname2(sourceFile.getFilePath());
|
|
2514
2254
|
const noExt = moduleSpecifier.replace(/\.(js|ts)$/, "");
|
|
2515
2255
|
return [
|
|
2516
2256
|
resolve3(dir, `${noExt}.ts`),
|
|
@@ -2519,7 +2259,7 @@ function resolveModuleSpecifier(moduleSpecifier, sourceFile, project) {
|
|
|
2519
2259
|
];
|
|
2520
2260
|
}
|
|
2521
2261
|
const ctx = _ctxFor(project);
|
|
2522
|
-
const baseUrl = ctx.projectRoot;
|
|
2262
|
+
const baseUrl = ctx.pathsBase ?? ctx.projectRoot;
|
|
2523
2263
|
const tsconfigPaths = ctx.tsconfigPaths;
|
|
2524
2264
|
dbg(
|
|
2525
2265
|
"resolveModuleSpecifier",
|
|
@@ -3717,7 +3457,7 @@ function extractFilterForHints(classDecl, project) {
|
|
|
3717
3457
|
}
|
|
3718
3458
|
return hints;
|
|
3719
3459
|
}
|
|
3720
|
-
function extractApplyFilterInfo(method, sourceFile, project, mixin) {
|
|
3460
|
+
function extractApplyFilterInfo(method, sourceFile, project, mixin, controllerClass) {
|
|
3721
3461
|
const declFile = method.getSourceFile();
|
|
3722
3462
|
const mixinEntity = resolveMixinEntityClass(mixin, project);
|
|
3723
3463
|
for (const param of method.getParameters()) {
|
|
@@ -3743,7 +3483,7 @@ function extractApplyFilterInfo(method, sourceFile, project, mixin) {
|
|
|
3743
3483
|
const filterClassName = filterClassArg.getText();
|
|
3744
3484
|
const resolved = factoryStaticClass ? void 0 : findType(filterClassName, declFile, project);
|
|
3745
3485
|
const declaredClass = factoryStaticClass ?? (resolved?.kind === "class" ? resolved.decl : resolveLocalClassDeclaration(filterClassArg));
|
|
3746
|
-
const ownRoute = !mixin || declFile.getFilePath() !== mixin.factoryFilePath;
|
|
3486
|
+
const ownRoute = !mixin || (controllerClass ? method.getParent() === controllerClass : declFile.getFilePath() !== mixin.factoryFilePath);
|
|
3747
3487
|
const namedByRoute = ownRoute && Node6.isIdentifier(filterClassArg) ? declaredClass : void 0;
|
|
3748
3488
|
for (const candidate of orderFilterCandidates({
|
|
3749
3489
|
namedByRoute,
|
|
@@ -4334,9 +4074,9 @@ function unwrapNamedContainer(node, names) {
|
|
|
4334
4074
|
}
|
|
4335
4075
|
return node;
|
|
4336
4076
|
}
|
|
4337
|
-
function extractDtoContract(method, sourceFile, project, mixin) {
|
|
4077
|
+
function extractDtoContract(method, sourceFile, project, mixin, controllerClass) {
|
|
4338
4078
|
let body = extractBodyType(method, sourceFile, project);
|
|
4339
|
-
const filterInfo = extractApplyFilterInfo(method, sourceFile, project, mixin);
|
|
4079
|
+
const filterInfo = extractApplyFilterInfo(method, sourceFile, project, mixin, controllerClass);
|
|
4340
4080
|
const query = extractQueryType(method, sourceFile, project);
|
|
4341
4081
|
const uploads = extractUploadedFiles(method);
|
|
4342
4082
|
const multipartBody = uploads.fields ? `{ ${uploads.fields} }` : null;
|
|
@@ -4573,43 +4313,80 @@ function parseDefineContractCall(callExpr) {
|
|
|
4573
4313
|
async function discoverContractsFast(opts) {
|
|
4574
4314
|
const { cwd, glob, tsconfig } = opts;
|
|
4575
4315
|
const tsconfigPath = resolveTsconfigPath(cwd, tsconfig);
|
|
4576
|
-
const
|
|
4577
|
-
const
|
|
4316
|
+
const loaded = loadDiscoveryTsconfig(tsconfigPath);
|
|
4317
|
+
const project = createDiscoveryProject(tsconfigPath, loaded);
|
|
4318
|
+
const files = await fg2(glob, { cwd, absolute: true, onlyFiles: true });
|
|
4578
4319
|
for (const f of files) {
|
|
4579
4320
|
project.addSourceFileAtPath(f);
|
|
4580
4321
|
}
|
|
4581
|
-
bindDiscoveryContext(project, cwd, tsconfigPath);
|
|
4322
|
+
bindDiscoveryContext(project, cwd, tsconfigPath, loaded);
|
|
4582
4323
|
clearMixinTypeProject();
|
|
4583
4324
|
return extractAllRoutes(project);
|
|
4584
4325
|
}
|
|
4585
4326
|
function resolveTsconfigPath(cwd, tsconfig) {
|
|
4586
|
-
return tsconfig ? resolve4(tsconfig) :
|
|
4327
|
+
return tsconfig ? resolve4(tsconfig) : join12(cwd, "tsconfig.json");
|
|
4587
4328
|
}
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
return
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4329
|
+
var NO_TSCONFIG_OPTIONS = {
|
|
4330
|
+
allowJs: true,
|
|
4331
|
+
resolveJsonModule: false,
|
|
4332
|
+
strict: false
|
|
4333
|
+
};
|
|
4334
|
+
function loadDiscoveryTsconfig(tsconfigPath) {
|
|
4335
|
+
if (!existsSync(tsconfigPath)) return { options: null, files: [] };
|
|
4336
|
+
const read = ts.readConfigFile(tsconfigPath, (path) => ts.sys.readFile(path));
|
|
4337
|
+
if (read.error) {
|
|
4338
|
+
return {
|
|
4339
|
+
options: null,
|
|
4340
|
+
files: [tsconfigPath],
|
|
4341
|
+
error: ts.flattenDiagnosticMessageText(read.error.messageText, " ")
|
|
4342
|
+
};
|
|
4343
|
+
}
|
|
4344
|
+
const host = {
|
|
4345
|
+
useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
|
|
4346
|
+
// The one line that keeps a tsconfig from being a filesystem walk.
|
|
4347
|
+
readDirectory: () => [],
|
|
4348
|
+
fileExists: (path) => ts.sys.fileExists(path),
|
|
4349
|
+
// `extends` is resolved through the host, so a consumer whose `paths` live
|
|
4350
|
+
// in a base tsconfig keeps working.
|
|
4351
|
+
readFile: (path) => ts.sys.readFile(path)
|
|
4352
|
+
};
|
|
4353
|
+
const sourceFile = ts.readJsonConfigFile(tsconfigPath, (path) => ts.sys.readFile(path));
|
|
4354
|
+
const parsed = ts.parseJsonSourceFileConfigFileContent(
|
|
4355
|
+
sourceFile,
|
|
4356
|
+
host,
|
|
4357
|
+
dirname3(tsconfigPath),
|
|
4358
|
+
void 0,
|
|
4359
|
+
tsconfigPath
|
|
4360
|
+
);
|
|
4361
|
+
return {
|
|
4362
|
+
options: parsed.options,
|
|
4363
|
+
files: [tsconfigPath, ...sourceFile.extendedSourceFiles ?? []]
|
|
4364
|
+
};
|
|
4365
|
+
}
|
|
4366
|
+
function pathsBaseDir(options, fallback) {
|
|
4367
|
+
if (typeof options.baseUrl === "string") return options.baseUrl;
|
|
4368
|
+
if (typeof options.pathsBasePath === "string") return options.pathsBasePath;
|
|
4369
|
+
return fallback;
|
|
4370
|
+
}
|
|
4371
|
+
function createDiscoveryProject(tsconfigPath, tsconfig = loadDiscoveryTsconfig(tsconfigPath)) {
|
|
4372
|
+
if (tsconfig.error) {
|
|
4373
|
+
console.warn(
|
|
4374
|
+
`[nestjs-codegen/fast] Could not load ${tsconfigPath}: ${tsconfig.error} \u2014 continuing WITHOUT its compiler options, so path alias imports (e.g. '@/...') will not resolve and controllers extending an alias-imported factory will contribute NO routes to the generated client.`
|
|
4375
|
+
);
|
|
4607
4376
|
}
|
|
4377
|
+
return new Project4({
|
|
4378
|
+
compilerOptions: tsconfig.options ?? { ...NO_TSCONFIG_OPTIONS },
|
|
4379
|
+
skipAddingFilesFromTsConfig: true,
|
|
4380
|
+
skipLoadingLibFiles: true,
|
|
4381
|
+
skipFileDependencyResolution: true
|
|
4382
|
+
});
|
|
4608
4383
|
}
|
|
4609
|
-
function bindDiscoveryContext(project, cwd, tsconfigPath) {
|
|
4384
|
+
function bindDiscoveryContext(project, cwd, tsconfigPath, tsconfig = loadDiscoveryTsconfig(tsconfigPath)) {
|
|
4385
|
+
const { options } = tsconfig;
|
|
4610
4386
|
setDiscoveryContext(project, {
|
|
4611
4387
|
projectRoot: cwd,
|
|
4612
|
-
tsconfigPaths:
|
|
4388
|
+
tsconfigPaths: options?.paths ?? null,
|
|
4389
|
+
pathsBase: options ? pathsBaseDir(options, cwd) : cwd
|
|
4613
4390
|
});
|
|
4614
4391
|
}
|
|
4615
4392
|
function extractRoutesFrom(project, controllerPaths) {
|
|
@@ -4645,10 +4422,11 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
4645
4422
|
static async create(opts) {
|
|
4646
4423
|
const { cwd, glob, tsconfig } = opts;
|
|
4647
4424
|
const tsconfigPath = resolveTsconfigPath(cwd, tsconfig);
|
|
4648
|
-
const
|
|
4649
|
-
|
|
4425
|
+
const loaded = loadDiscoveryTsconfig(tsconfigPath);
|
|
4426
|
+
const project = createDiscoveryProject(tsconfigPath, loaded);
|
|
4427
|
+
bindDiscoveryContext(project, cwd, tsconfigPath, loaded);
|
|
4650
4428
|
const instance = new _PersistentDiscovery(project, cwd, glob);
|
|
4651
|
-
const files = await
|
|
4429
|
+
const files = await fg2(glob, { cwd, absolute: true, onlyFiles: true });
|
|
4652
4430
|
for (const f of files) {
|
|
4653
4431
|
project.addSourceFileAtPath(f);
|
|
4654
4432
|
instance.controllerPaths.add(f);
|
|
@@ -4677,7 +4455,7 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
4677
4455
|
}
|
|
4678
4456
|
}
|
|
4679
4457
|
const globbed = new Set(
|
|
4680
|
-
await
|
|
4458
|
+
await fg2(this.glob, { cwd: this.cwd, absolute: true, onlyFiles: true })
|
|
4681
4459
|
);
|
|
4682
4460
|
for (const f of globbed) {
|
|
4683
4461
|
if (!this.controllerPaths.has(f)) {
|
|
@@ -4899,7 +4677,7 @@ function extractDtoRoute(args) {
|
|
|
4899
4677
|
const methodName = method.getName();
|
|
4900
4678
|
const classAs = readAsDecorator(cls, `class ${className}`);
|
|
4901
4679
|
const methodAs = readAsDecorator(method, `${className}.${methodName}`);
|
|
4902
|
-
const dtoContract = extractDtoContract(method, sourceFile, project, mixin);
|
|
4680
|
+
const dtoContract = extractDtoContract(method, sourceFile, project, mixin, cls);
|
|
4903
4681
|
const mixinResponse = mixin ? resolveInstantiatedReturnType(cls, methodName) : void 0;
|
|
4904
4682
|
return buildRoute({
|
|
4905
4683
|
className,
|
|
@@ -4987,6 +4765,268 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
4987
4765
|
return routes;
|
|
4988
4766
|
}
|
|
4989
4767
|
|
|
4768
|
+
// src/generate-manifest.ts
|
|
4769
|
+
var MANIFEST_FILE = ".codegen-manifest.json";
|
|
4770
|
+
var LOCK_FILE = ".watcher.lock";
|
|
4771
|
+
var DriftGuardError = class extends Error {
|
|
4772
|
+
constructor(message) {
|
|
4773
|
+
super(message);
|
|
4774
|
+
this.name = "DriftGuardError";
|
|
4775
|
+
}
|
|
4776
|
+
};
|
|
4777
|
+
function isEntryPoint(value) {
|
|
4778
|
+
return value === "cli" || value === "module";
|
|
4779
|
+
}
|
|
4780
|
+
function isManifestShape(value) {
|
|
4781
|
+
if (typeof value !== "object" || value === null) return false;
|
|
4782
|
+
const candidate = value;
|
|
4783
|
+
if (typeof candidate.version !== "string") return false;
|
|
4784
|
+
if (typeof candidate.hash !== "string") return false;
|
|
4785
|
+
if (candidate.entryPoint !== void 0 && !isEntryPoint(candidate.entryPoint)) return false;
|
|
4786
|
+
if (candidate.configHash !== void 0 && typeof candidate.configHash !== "string") return false;
|
|
4787
|
+
if (candidate.configKeyHashes !== void 0 && !isStringRecord(candidate.configKeyHashes)) {
|
|
4788
|
+
return false;
|
|
4789
|
+
}
|
|
4790
|
+
if (candidate.extraInputs !== void 0 && (!Array.isArray(candidate.extraInputs) || !candidate.extraInputs.every((entry) => typeof entry === "string"))) {
|
|
4791
|
+
return false;
|
|
4792
|
+
}
|
|
4793
|
+
if (!Array.isArray(candidate.files)) return false;
|
|
4794
|
+
return candidate.files.every((entry) => typeof entry === "string");
|
|
4795
|
+
}
|
|
4796
|
+
function isStringRecord(value) {
|
|
4797
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
4798
|
+
return Object.values(value).every((entry) => typeof entry === "string");
|
|
4799
|
+
}
|
|
4800
|
+
function serializeConfig(config) {
|
|
4801
|
+
return serializeConfigValue(config, `unserializable:${config.codegen.outDir}`);
|
|
4802
|
+
}
|
|
4803
|
+
function serializeConfigValue(value, unserializableMarker) {
|
|
4804
|
+
try {
|
|
4805
|
+
return JSON.stringify(value, (_key, entry) => {
|
|
4806
|
+
if (typeof entry === "function") return `[fn:${entry.name}]`;
|
|
4807
|
+
return entry;
|
|
4808
|
+
});
|
|
4809
|
+
} catch {
|
|
4810
|
+
return unserializableMarker;
|
|
4811
|
+
}
|
|
4812
|
+
}
|
|
4813
|
+
function computeConfigKeyHashes(config) {
|
|
4814
|
+
const hashes = {};
|
|
4815
|
+
for (const [key, value] of Object.entries(config)) {
|
|
4816
|
+
if (value === void 0) continue;
|
|
4817
|
+
hashes[key] = createHash("sha256").update(serializeConfigValue(value, `unserializable:${key}`)).digest("hex");
|
|
4818
|
+
}
|
|
4819
|
+
return hashes;
|
|
4820
|
+
}
|
|
4821
|
+
function diffConfigKeyHashes(previous, current) {
|
|
4822
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(previous), ...Object.keys(current)]);
|
|
4823
|
+
return [...keys].filter((key) => previous[key] !== current[key]).sort();
|
|
4824
|
+
}
|
|
4825
|
+
async function discoverInputFiles(config) {
|
|
4826
|
+
const globs = [config.contracts.glob, config.forms.watch];
|
|
4827
|
+
if (config.pages) globs.push(config.pages.glob);
|
|
4828
|
+
const cwd = config.codegen.cwd;
|
|
4829
|
+
const matched = await fg3(globs, { cwd, absolute: true, onlyFiles: true });
|
|
4830
|
+
return [...new Set(matched)].sort();
|
|
4831
|
+
}
|
|
4832
|
+
async function computeInputsHash(config, extraInputs = []) {
|
|
4833
|
+
const hash = createHash("sha256");
|
|
4834
|
+
hash.update(`version:${VERSION}
|
|
4835
|
+
`);
|
|
4836
|
+
hash.update(`config:${serializeConfig(config)}
|
|
4837
|
+
`);
|
|
4838
|
+
const cwd = config.codegen.cwd;
|
|
4839
|
+
const tsconfigPath = resolveTsconfigPath(cwd, config.app?.tsconfig ?? void 0);
|
|
4840
|
+
const tsconfigFiles = loadDiscoveryTsconfig(tsconfigPath).files;
|
|
4841
|
+
for (const file of tsconfigFiles.length > 0 ? tsconfigFiles : [tsconfigPath]) {
|
|
4842
|
+
const contents = await readFile2(file, "utf8").catch(() => null);
|
|
4843
|
+
hash.update(`tsconfig:${relative7(cwd, file)}
|
|
4844
|
+
`);
|
|
4845
|
+
hash.update(contents ?? " missing");
|
|
4846
|
+
hash.update("\n");
|
|
4847
|
+
}
|
|
4848
|
+
const globbed = await discoverInputFiles(config);
|
|
4849
|
+
const globbedRelative = new Set(globbed.map((file) => relative7(cwd, file)));
|
|
4850
|
+
const extra = [...new Set(extraInputs)].filter((file) => !globbedRelative.has(file)).sort();
|
|
4851
|
+
for (const file of globbed) {
|
|
4852
|
+
const contents = await readFile2(file, "utf8");
|
|
4853
|
+
hash.update(`file:${relative7(cwd, file)}
|
|
4854
|
+
`);
|
|
4855
|
+
hash.update(contents);
|
|
4856
|
+
hash.update("\n");
|
|
4857
|
+
}
|
|
4858
|
+
for (const file of extra) {
|
|
4859
|
+
const contents = await readFile2(join13(cwd, file), "utf8").catch(() => null);
|
|
4860
|
+
hash.update(`extra:${file}
|
|
4861
|
+
`);
|
|
4862
|
+
hash.update(contents ?? "\0missing");
|
|
4863
|
+
hash.update("\n");
|
|
4864
|
+
}
|
|
4865
|
+
return hash.digest("hex");
|
|
4866
|
+
}
|
|
4867
|
+
async function readManifest(outDir) {
|
|
4868
|
+
try {
|
|
4869
|
+
const raw = await readFile2(join13(outDir, MANIFEST_FILE), "utf8");
|
|
4870
|
+
const parsed = JSON.parse(raw);
|
|
4871
|
+
if (!isManifestShape(parsed)) return null;
|
|
4872
|
+
return {
|
|
4873
|
+
version: parsed.version,
|
|
4874
|
+
hash: parsed.hash,
|
|
4875
|
+
...parsed.entryPoint ? { entryPoint: parsed.entryPoint } : {},
|
|
4876
|
+
...parsed.configHash ? { configHash: parsed.configHash } : {},
|
|
4877
|
+
...parsed.configKeyHashes ? { configKeyHashes: parsed.configKeyHashes } : {},
|
|
4878
|
+
...parsed.extraInputs ? { extraInputs: parsed.extraInputs } : {},
|
|
4879
|
+
files: parsed.files
|
|
4880
|
+
};
|
|
4881
|
+
} catch {
|
|
4882
|
+
return null;
|
|
4883
|
+
}
|
|
4884
|
+
}
|
|
4885
|
+
function computeConfigHash(config) {
|
|
4886
|
+
return createHash("sha256").update(serializeConfig(config)).digest("hex");
|
|
4887
|
+
}
|
|
4888
|
+
async function writeManifest(outDir, manifest) {
|
|
4889
|
+
await writeFile9(join13(outDir, MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}
|
|
4890
|
+
`, "utf8");
|
|
4891
|
+
}
|
|
4892
|
+
async function listOutputFiles(outDir) {
|
|
4893
|
+
const found = [];
|
|
4894
|
+
async function walk(dir) {
|
|
4895
|
+
const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
4896
|
+
for (const entry of entries) {
|
|
4897
|
+
const abs = join13(dir, entry.name);
|
|
4898
|
+
if (entry.isDirectory()) {
|
|
4899
|
+
await walk(abs);
|
|
4900
|
+
} else if (entry.isFile()) {
|
|
4901
|
+
const rel = relative7(outDir, abs);
|
|
4902
|
+
if (rel === MANIFEST_FILE || rel === LOCK_FILE) continue;
|
|
4903
|
+
found.push(rel);
|
|
4904
|
+
}
|
|
4905
|
+
}
|
|
4906
|
+
}
|
|
4907
|
+
await walk(outDir);
|
|
4908
|
+
return found.sort();
|
|
4909
|
+
}
|
|
4910
|
+
async function allOutputsExist(outDir, files) {
|
|
4911
|
+
const present = new Set(await listOutputFiles(outDir));
|
|
4912
|
+
return files.every((file) => present.has(file));
|
|
4913
|
+
}
|
|
4914
|
+
async function isManifestFresh(outDir, manifest, inputsHash) {
|
|
4915
|
+
if (manifest === null) return false;
|
|
4916
|
+
if (manifest.version !== VERSION) return false;
|
|
4917
|
+
if (manifest.hash !== inputsHash) return false;
|
|
4918
|
+
if (manifest.files.length === 0) return false;
|
|
4919
|
+
return allOutputsExist(outDir, manifest.files);
|
|
4920
|
+
}
|
|
4921
|
+
|
|
4922
|
+
// src/generate.ts
|
|
4923
|
+
function driftGuardMessage(outDir, previousEntryPoint, currentEntryPoint, differingKeys) {
|
|
4924
|
+
const differ = differingKeys.length > 0 ? `their resolved configs differ at: ${differingKeys.map((key) => `\`${key}\``).join(", ")}` : "their resolved configs differ (re-run after this generate records per-key hashes to see which keys)";
|
|
4925
|
+
return `[nestjs-codegen] Config drift detected in "${outDir}": the last generate ran from the "${previousEntryPoint}" entry point, this run is from the "${currentEntryPoint}" entry point, and ${differ}. Both entry points must read the SAME config \u2014 export a shared config object (e.g. codegen.config.ts) and import it from BOTH nestjs-codegen.config.ts (CLI) and NestjsCodegenModule.forRoot() (Nest module), or set \`driftGuard: false\` on either config to opt out of this check.`;
|
|
4926
|
+
}
|
|
4927
|
+
async function generate(config, inputRoutes = [], entryPoint = "cli") {
|
|
4928
|
+
setCodegenDebug(config.debug);
|
|
4929
|
+
const manifest = await readManifest(config.codegen.outDir);
|
|
4930
|
+
const inputsHash = await computeInputsHash(config, manifest?.extraInputs ?? []);
|
|
4931
|
+
if (await isManifestFresh(config.codegen.outDir, manifest, inputsHash)) {
|
|
4932
|
+
console.log(`[nestjs-codegen] ${config.codegen.outDir} up to date, skipped`);
|
|
4933
|
+
return;
|
|
4934
|
+
}
|
|
4935
|
+
const configHash = computeConfigHash(config);
|
|
4936
|
+
const configKeyHashes = computeConfigKeyHashes(config);
|
|
4937
|
+
if (config.driftGuard && manifest?.entryPoint && manifest.entryPoint !== entryPoint && manifest.configHash && manifest.configHash !== configHash) {
|
|
4938
|
+
throw new DriftGuardError(
|
|
4939
|
+
driftGuardMessage(
|
|
4940
|
+
config.codegen.outDir,
|
|
4941
|
+
manifest.entryPoint,
|
|
4942
|
+
entryPoint,
|
|
4943
|
+
// A pre-key-hash manifest can't tell us WHICH keys differ — pass none
|
|
4944
|
+
// rather than diffing against {} (which would name every key).
|
|
4945
|
+
manifest.configKeyHashes ? diffConfigKeyHashes(manifest.configKeyHashes, configKeyHashes) : []
|
|
4946
|
+
)
|
|
4947
|
+
);
|
|
4948
|
+
}
|
|
4949
|
+
const extensions = config.extensions ?? [];
|
|
4950
|
+
let routes = inputRoutes;
|
|
4951
|
+
const trackedInputs = /* @__PURE__ */ new Set();
|
|
4952
|
+
const ctx = createExtensionContext(config, () => routes, trackedInputs);
|
|
4953
|
+
if (extensions.length > 0) {
|
|
4954
|
+
routes = await applyTransformRoutes(routes, extensions, ctx);
|
|
4955
|
+
}
|
|
4956
|
+
if (config.pages) {
|
|
4957
|
+
const pagesConfig = config.pages;
|
|
4958
|
+
const pages = await discoverPages({
|
|
4959
|
+
glob: pagesConfig.glob,
|
|
4960
|
+
cwd: config.codegen.cwd,
|
|
4961
|
+
propsExport: pagesConfig.propsExport,
|
|
4962
|
+
componentNameStrategy: pagesConfig.componentNameStrategy
|
|
4963
|
+
});
|
|
4964
|
+
const sharedProps = discoverSharedPropsFromConfig(config);
|
|
4965
|
+
await emitPages(pages, config.codegen.outDir, {
|
|
4966
|
+
propsExport: pagesConfig.propsExport,
|
|
4967
|
+
sharedProps
|
|
4968
|
+
});
|
|
4969
|
+
await emitCache(pages, config.codegen.outDir);
|
|
4970
|
+
}
|
|
4971
|
+
const hasRoutes = routes.length > 0;
|
|
4972
|
+
const hasContracts = routes.some((r) => r.contract);
|
|
4973
|
+
if (hasRoutes) {
|
|
4974
|
+
await emitRoutes(routes, config.codegen.outDir);
|
|
4975
|
+
}
|
|
4976
|
+
if (hasContracts) {
|
|
4977
|
+
await emitApi(routes, config.codegen.outDir, {
|
|
4978
|
+
...config.fetcher?.importPath ? { fetcherImportPath: config.fetcher.importPath } : {},
|
|
4979
|
+
serialization: config.serialization,
|
|
4980
|
+
extensions,
|
|
4981
|
+
ctx
|
|
4982
|
+
});
|
|
4983
|
+
}
|
|
4984
|
+
const hasForms = await emitForms(routes, config.codegen.outDir, config.forms, config.validation);
|
|
4985
|
+
if (hasContracts && config.openapi.enabled) {
|
|
4986
|
+
await emitOpenApi(routes, config.codegen.outDir, {
|
|
4987
|
+
fileName: config.openapi.fileName,
|
|
4988
|
+
info: {
|
|
4989
|
+
title: config.openapi.title,
|
|
4990
|
+
version: config.openapi.version,
|
|
4991
|
+
...config.openapi.description ? { description: config.openapi.description } : {}
|
|
4992
|
+
}
|
|
4993
|
+
});
|
|
4994
|
+
}
|
|
4995
|
+
if (hasContracts && config.mocks.enabled) {
|
|
4996
|
+
await emitMocks(routes, config.codegen.outDir, {
|
|
4997
|
+
fileName: config.mocks.fileName,
|
|
4998
|
+
seed: config.mocks.seed,
|
|
4999
|
+
baseUrl: config.mocks.baseUrl
|
|
5000
|
+
});
|
|
5001
|
+
}
|
|
5002
|
+
await emitIndex(config.codegen.outDir, hasContracts, hasForms);
|
|
5003
|
+
if (extensions.length > 0) {
|
|
5004
|
+
const extraFiles = await collectEmittedFiles(extensions, ctx);
|
|
5005
|
+
for (const file of extraFiles) {
|
|
5006
|
+
const dest = join14(config.codegen.outDir, file.path);
|
|
5007
|
+
await mkdir9(dirname4(dest), { recursive: true });
|
|
5008
|
+
await writeFile10(dest, file.contents, "utf8");
|
|
5009
|
+
}
|
|
5010
|
+
}
|
|
5011
|
+
const outputFiles = await listOutputFiles(config.codegen.outDir);
|
|
5012
|
+
const extraInputs = [...trackedInputs].sort();
|
|
5013
|
+
const recordedHash = extraInputs.length > 0 ? await computeInputsHash(config, extraInputs) : inputsHash;
|
|
5014
|
+
await writeManifest(config.codegen.outDir, {
|
|
5015
|
+
version: VERSION,
|
|
5016
|
+
hash: recordedHash,
|
|
5017
|
+
entryPoint,
|
|
5018
|
+
configHash,
|
|
5019
|
+
configKeyHashes,
|
|
5020
|
+
files: outputFiles,
|
|
5021
|
+
...extraInputs.length > 0 ? { extraInputs } : {}
|
|
5022
|
+
});
|
|
5023
|
+
}
|
|
5024
|
+
|
|
5025
|
+
// src/watch/watcher.ts
|
|
5026
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
5027
|
+
import { join as join16 } from "path";
|
|
5028
|
+
import chokidar from "chokidar";
|
|
5029
|
+
|
|
4990
5030
|
// src/watch/lock-file.ts
|
|
4991
5031
|
import { open } from "fs/promises";
|
|
4992
5032
|
import { mkdir as mkdir10, readFile as readFile3, unlink } from "fs/promises";
|
|
@@ -5180,7 +5220,7 @@ async function watch(config, onChange, options = {}) {
|
|
|
5180
5220
|
}
|
|
5181
5221
|
|
|
5182
5222
|
// src/index.ts
|
|
5183
|
-
var VERSION = "0.
|
|
5223
|
+
var VERSION = "0.21.1";
|
|
5184
5224
|
|
|
5185
5225
|
// src/cli/codegen.ts
|
|
5186
5226
|
async function runCodegen(opts = {}) {
|
|
@@ -5208,22 +5248,22 @@ async function runCodegen(opts = {}) {
|
|
|
5208
5248
|
|
|
5209
5249
|
// src/cli/doctor.ts
|
|
5210
5250
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
5211
|
-
import { appendFileSync, existsSync, readFileSync as
|
|
5251
|
+
import { appendFileSync, existsSync as existsSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
5212
5252
|
import { join as join18 } from "path";
|
|
5213
5253
|
|
|
5214
5254
|
// src/cli/init.ts
|
|
5215
5255
|
import { execFileSync } from "child_process";
|
|
5216
|
-
import { readFileSync as
|
|
5256
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
5217
5257
|
import { access as access2, mkdir as mkdir11, readFile as readFile5, writeFile as writeFile11 } from "fs/promises";
|
|
5218
5258
|
import { join as join17 } from "path";
|
|
5219
5259
|
import { createInterface } from "readline";
|
|
5220
5260
|
|
|
5221
5261
|
// src/cli/patch-utils.ts
|
|
5222
|
-
import { readFileSync
|
|
5262
|
+
import { readFileSync, writeFileSync } from "fs";
|
|
5223
5263
|
function patchJsonFile(filePath, mutator, parse = (raw) => raw) {
|
|
5224
5264
|
let raw;
|
|
5225
5265
|
try {
|
|
5226
|
-
raw =
|
|
5266
|
+
raw = readFileSync(filePath, "utf8");
|
|
5227
5267
|
} catch {
|
|
5228
5268
|
return "skipped";
|
|
5229
5269
|
}
|
|
@@ -5441,7 +5481,7 @@ async function patchPackageJsonScripts(cwd, scripts) {
|
|
|
5441
5481
|
function patchAppModule(filePath, rootView) {
|
|
5442
5482
|
let content;
|
|
5443
5483
|
try {
|
|
5444
|
-
content =
|
|
5484
|
+
content = readFileSync2(filePath, "utf8");
|
|
5445
5485
|
} catch {
|
|
5446
5486
|
return "skipped";
|
|
5447
5487
|
}
|
|
@@ -5482,7 +5522,7 @@ ${indent}HomeController,${content.slice(bracketPos)}`;
|
|
|
5482
5522
|
function patchMainTs(filePath) {
|
|
5483
5523
|
let content;
|
|
5484
5524
|
try {
|
|
5485
|
-
content =
|
|
5525
|
+
content = readFileSync2(filePath, "utf8");
|
|
5486
5526
|
} catch {
|
|
5487
5527
|
return "skipped";
|
|
5488
5528
|
}
|
|
@@ -5962,18 +6002,18 @@ ${green("\u2713")} Setup complete! Run: ${bold("nest start --watch")}
|
|
|
5962
6002
|
|
|
5963
6003
|
// src/cli/doctor.ts
|
|
5964
6004
|
function checkFileExists(cwd, file) {
|
|
5965
|
-
return
|
|
6005
|
+
return existsSync2(join18(cwd, file));
|
|
5966
6006
|
}
|
|
5967
6007
|
function readJson(path) {
|
|
5968
6008
|
try {
|
|
5969
|
-
const raw =
|
|
6009
|
+
const raw = readFileSync3(path, "utf8").replace(/\/\/.*$/gm, "");
|
|
5970
6010
|
return JSON.parse(raw);
|
|
5971
6011
|
} catch {
|
|
5972
6012
|
return null;
|
|
5973
6013
|
}
|
|
5974
6014
|
}
|
|
5975
6015
|
function writeJsonField(filePath, dotPath, value) {
|
|
5976
|
-
const raw =
|
|
6016
|
+
const raw = readFileSync3(filePath, "utf8");
|
|
5977
6017
|
const stripped = raw.replace(/\/\/.*$/gm, "");
|
|
5978
6018
|
const obj = JSON.parse(stripped);
|
|
5979
6019
|
let target = obj;
|
|
@@ -6005,8 +6045,8 @@ function getPackageVersion(cwd, pkg) {
|
|
|
6005
6045
|
}
|
|
6006
6046
|
}
|
|
6007
6047
|
function detectPkgManager(cwd) {
|
|
6008
|
-
if (
|
|
6009
|
-
if (
|
|
6048
|
+
if (existsSync2(join18(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
6049
|
+
if (existsSync2(join18(cwd, "yarn.lock"))) return "yarn";
|
|
6010
6050
|
return "npm";
|
|
6011
6051
|
}
|
|
6012
6052
|
async function runDoctor(opts) {
|
|
@@ -6175,14 +6215,14 @@ async function runDoctor(opts) {
|
|
|
6175
6215
|
const innerTsconfigPath = join18(cwd, "inertia", "tsconfig.json");
|
|
6176
6216
|
checks.push({
|
|
6177
6217
|
name: "inertia/tsconfig.json exists (VSCode picks up ~codegen alias)",
|
|
6178
|
-
pass:
|
|
6218
|
+
pass: existsSync2(innerTsconfigPath),
|
|
6179
6219
|
fix: "Create inertia/tsconfig.json that extends ../tsconfig.inertia.json",
|
|
6180
6220
|
autoFix: () => {
|
|
6181
6221
|
writeFileSync3(innerTsconfigPath, INERTIA_TSCONFIG_TEMPLATE, "utf8");
|
|
6182
6222
|
}
|
|
6183
6223
|
});
|
|
6184
6224
|
if (checkFileExists(cwd, "vite.config.ts")) {
|
|
6185
|
-
const viteContent =
|
|
6225
|
+
const viteContent = readFileSync3(join18(cwd, "vite.config.ts"), "utf8");
|
|
6186
6226
|
checks.push({
|
|
6187
6227
|
name: "vite.config.ts has resolve.alias",
|
|
6188
6228
|
pass: viteContent.includes("resolve") && viteContent.includes("alias"),
|
|
@@ -6248,7 +6288,7 @@ async function runDoctor(opts) {
|
|
|
6248
6288
|
}
|
|
6249
6289
|
if (checkFileExists(cwd, ".gitignore")) {
|
|
6250
6290
|
const gitignorePath = join18(cwd, ".gitignore");
|
|
6251
|
-
const gitignore =
|
|
6291
|
+
const gitignore = readFileSync3(gitignorePath, "utf8");
|
|
6252
6292
|
checks.push({
|
|
6253
6293
|
name: ".gitignore includes .nestjs-inertia/",
|
|
6254
6294
|
pass: gitignore.includes(".nestjs-inertia"),
|