@dudousxd/nestjs-codegen 0.11.0 → 0.13.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 +59 -0
- package/dist/cli/main.cjs +264 -91
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.js +248 -75
- package/dist/cli/main.js.map +1 -1
- package/dist/extension/index.d.cts +1 -1
- package/dist/extension/index.d.ts +1 -1
- package/dist/{index-CxkGbILp.d.cts → index-DvUzPXdh.d.cts} +7 -0
- package/dist/{index-CxkGbILp.d.ts → index-DvUzPXdh.d.ts} +7 -0
- package/dist/index.cjs +222 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -5
- package/dist/index.d.ts +31 -5
- package/dist/index.js +209 -36
- package/dist/index.js.map +1 -1
- package/dist/nest/index.cjs +267 -75
- package/dist/nest/index.cjs.map +1 -1
- package/dist/nest/index.d.cts +16 -5
- package/dist/nest/index.d.ts +16 -5
- package/dist/nest/index.js +267 -75
- package/dist/nest/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/main.js
CHANGED
|
@@ -183,8 +183,8 @@ Run \`nestjs-codegen init\` to create a starter config.`
|
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
// src/generate.ts
|
|
186
|
-
import { mkdir as mkdir9, writeFile as
|
|
187
|
-
import { dirname as dirname2, join as
|
|
186
|
+
import { mkdir as mkdir9, writeFile as writeFile10 } from "fs/promises";
|
|
187
|
+
import { dirname as dirname2, join as join13 } from "path";
|
|
188
188
|
|
|
189
189
|
// src/discovery/pages.ts
|
|
190
190
|
import { readFile } from "fs/promises";
|
|
@@ -786,6 +786,7 @@ function buildRequestModel(c) {
|
|
|
786
786
|
const optsParts = [];
|
|
787
787
|
if (hasQuery) optsParts.push("query: input?.query as Record<string, unknown> | undefined");
|
|
788
788
|
if (hasBody) optsParts.push("body: input?.body");
|
|
789
|
+
if (hasBody && c.contractSource.multipart) optsParts.push("multipart: true");
|
|
789
790
|
const optsExpr = optsParts.length ? `{ ${optsParts.join(", ")} }` : "{}";
|
|
790
791
|
return {
|
|
791
792
|
routeName: c.name,
|
|
@@ -2079,6 +2080,99 @@ function buildEmpty() {
|
|
|
2079
2080
|
].join("\n");
|
|
2080
2081
|
}
|
|
2081
2082
|
|
|
2083
|
+
// src/generate-manifest.ts
|
|
2084
|
+
import { createHash } from "crypto";
|
|
2085
|
+
import { readFile as readFile2, readdir, writeFile as writeFile9 } from "fs/promises";
|
|
2086
|
+
import { join as join12, relative as relative6 } from "path";
|
|
2087
|
+
import fg2 from "fast-glob";
|
|
2088
|
+
var MANIFEST_FILE = ".codegen-manifest.json";
|
|
2089
|
+
var LOCK_FILE = ".watcher.lock";
|
|
2090
|
+
function isManifestShape(value) {
|
|
2091
|
+
if (typeof value !== "object" || value === null) return false;
|
|
2092
|
+
const candidate = value;
|
|
2093
|
+
if (typeof candidate.version !== "string") return false;
|
|
2094
|
+
if (typeof candidate.hash !== "string") return false;
|
|
2095
|
+
if (!Array.isArray(candidate.files)) return false;
|
|
2096
|
+
return candidate.files.every((entry) => typeof entry === "string");
|
|
2097
|
+
}
|
|
2098
|
+
function serializeConfig(config) {
|
|
2099
|
+
try {
|
|
2100
|
+
return JSON.stringify(config, (_key, value) => {
|
|
2101
|
+
if (typeof value === "function") return `[fn:${value.name}]${value.toString()}`;
|
|
2102
|
+
return value;
|
|
2103
|
+
});
|
|
2104
|
+
} catch {
|
|
2105
|
+
return `unserializable:${config.codegen.outDir}:${config.contracts.glob}`;
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
async function discoverInputFiles(config) {
|
|
2109
|
+
const globs = [config.contracts.glob, config.forms.watch];
|
|
2110
|
+
if (config.pages) globs.push(config.pages.glob);
|
|
2111
|
+
const cwd = config.codegen.cwd;
|
|
2112
|
+
const matched = await fg2(globs, { cwd, absolute: true, onlyFiles: true });
|
|
2113
|
+
return [...new Set(matched)].sort();
|
|
2114
|
+
}
|
|
2115
|
+
async function computeInputsHash(config) {
|
|
2116
|
+
const hash = createHash("sha256");
|
|
2117
|
+
hash.update(`version:${VERSION}
|
|
2118
|
+
`);
|
|
2119
|
+
hash.update(`config:${serializeConfig(config)}
|
|
2120
|
+
`);
|
|
2121
|
+
const inputFiles = await discoverInputFiles(config);
|
|
2122
|
+
const cwd = config.codegen.cwd;
|
|
2123
|
+
for (const file of inputFiles) {
|
|
2124
|
+
const contents = await readFile2(file, "utf8");
|
|
2125
|
+
hash.update(`file:${relative6(cwd, file)}
|
|
2126
|
+
`);
|
|
2127
|
+
hash.update(contents);
|
|
2128
|
+
hash.update("\n");
|
|
2129
|
+
}
|
|
2130
|
+
return hash.digest("hex");
|
|
2131
|
+
}
|
|
2132
|
+
async function readManifest(outDir) {
|
|
2133
|
+
try {
|
|
2134
|
+
const raw = await readFile2(join12(outDir, MANIFEST_FILE), "utf8");
|
|
2135
|
+
const parsed = JSON.parse(raw);
|
|
2136
|
+
if (!isManifestShape(parsed)) return null;
|
|
2137
|
+
return { version: parsed.version, hash: parsed.hash, files: parsed.files };
|
|
2138
|
+
} catch {
|
|
2139
|
+
return null;
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
async function writeManifest(outDir, manifest) {
|
|
2143
|
+
await writeFile9(join12(outDir, MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}
|
|
2144
|
+
`, "utf8");
|
|
2145
|
+
}
|
|
2146
|
+
async function listOutputFiles(outDir) {
|
|
2147
|
+
const found = [];
|
|
2148
|
+
async function walk(dir) {
|
|
2149
|
+
const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
2150
|
+
for (const entry of entries) {
|
|
2151
|
+
const abs = join12(dir, entry.name);
|
|
2152
|
+
if (entry.isDirectory()) {
|
|
2153
|
+
await walk(abs);
|
|
2154
|
+
} else if (entry.isFile()) {
|
|
2155
|
+
const rel = relative6(outDir, abs);
|
|
2156
|
+
if (rel === MANIFEST_FILE || rel === LOCK_FILE) continue;
|
|
2157
|
+
found.push(rel);
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
await walk(outDir);
|
|
2162
|
+
return found.sort();
|
|
2163
|
+
}
|
|
2164
|
+
async function allOutputsExist(outDir, files) {
|
|
2165
|
+
const present = new Set(await listOutputFiles(outDir));
|
|
2166
|
+
return files.every((file) => present.has(file));
|
|
2167
|
+
}
|
|
2168
|
+
async function isManifestFresh(outDir, manifest, inputsHash) {
|
|
2169
|
+
if (manifest === null) return false;
|
|
2170
|
+
if (manifest.version !== VERSION) return false;
|
|
2171
|
+
if (manifest.hash !== inputsHash) return false;
|
|
2172
|
+
if (manifest.files.length === 0) return false;
|
|
2173
|
+
return allOutputsExist(outDir, manifest.files);
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2082
2176
|
// src/util/debug-log.ts
|
|
2083
2177
|
var debugEnabled = false;
|
|
2084
2178
|
function setCodegenDebug(enabled) {
|
|
@@ -2091,6 +2185,12 @@ function debugWarn(message) {
|
|
|
2091
2185
|
// src/generate.ts
|
|
2092
2186
|
async function generate(config, inputRoutes = []) {
|
|
2093
2187
|
setCodegenDebug(config.debug);
|
|
2188
|
+
const inputsHash = await computeInputsHash(config);
|
|
2189
|
+
const manifest = await readManifest(config.codegen.outDir);
|
|
2190
|
+
if (await isManifestFresh(config.codegen.outDir, manifest, inputsHash)) {
|
|
2191
|
+
console.log(`[nestjs-codegen] ${config.codegen.outDir} up to date, skipped`);
|
|
2192
|
+
return;
|
|
2193
|
+
}
|
|
2094
2194
|
const extensions = config.extensions ?? [];
|
|
2095
2195
|
let routes = inputRoutes;
|
|
2096
2196
|
const ctx = createExtensionContext(config, () => routes);
|
|
@@ -2147,21 +2247,27 @@ async function generate(config, inputRoutes = []) {
|
|
|
2147
2247
|
if (extensions.length > 0) {
|
|
2148
2248
|
const extraFiles = await collectEmittedFiles(extensions, ctx);
|
|
2149
2249
|
for (const file of extraFiles) {
|
|
2150
|
-
const dest =
|
|
2250
|
+
const dest = join13(config.codegen.outDir, file.path);
|
|
2151
2251
|
await mkdir9(dirname2(dest), { recursive: true });
|
|
2152
|
-
await
|
|
2252
|
+
await writeFile10(dest, file.contents, "utf8");
|
|
2153
2253
|
}
|
|
2154
2254
|
}
|
|
2255
|
+
const outputFiles = await listOutputFiles(config.codegen.outDir);
|
|
2256
|
+
await writeManifest(config.codegen.outDir, {
|
|
2257
|
+
version: VERSION,
|
|
2258
|
+
hash: inputsHash,
|
|
2259
|
+
files: outputFiles
|
|
2260
|
+
});
|
|
2155
2261
|
}
|
|
2156
2262
|
|
|
2157
2263
|
// src/watch/watcher.ts
|
|
2158
|
-
import { readFile as
|
|
2159
|
-
import { join as
|
|
2264
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
2265
|
+
import { join as join16 } from "path";
|
|
2160
2266
|
import chokidar from "chokidar";
|
|
2161
2267
|
|
|
2162
2268
|
// src/discovery/contracts-fast.ts
|
|
2163
|
-
import { join as
|
|
2164
|
-
import
|
|
2269
|
+
import { join as join14, resolve as resolve3 } from "path";
|
|
2270
|
+
import fg3 from "fast-glob";
|
|
2165
2271
|
import {
|
|
2166
2272
|
Node as Node8,
|
|
2167
2273
|
Project as Project3
|
|
@@ -3576,6 +3682,55 @@ function extractParamsType(method, sourceFile, project) {
|
|
|
3576
3682
|
}
|
|
3577
3683
|
return entries.length > 0 ? `{ ${entries.join("; ")} }` : null;
|
|
3578
3684
|
}
|
|
3685
|
+
function extractUploadedFiles(method) {
|
|
3686
|
+
const FILE = "File | Blob";
|
|
3687
|
+
const entries = [];
|
|
3688
|
+
let multipart = false;
|
|
3689
|
+
const hasUploadedFileParam = method.getParameters().some(
|
|
3690
|
+
(p) => p.getDecorators().some((d) => {
|
|
3691
|
+
const name = d.getName();
|
|
3692
|
+
return name === "UploadedFile" || name === "UploadedFiles";
|
|
3693
|
+
})
|
|
3694
|
+
);
|
|
3695
|
+
for (const decorator of method.getDecorators()) {
|
|
3696
|
+
if (decorator.getName() !== "UseInterceptors") continue;
|
|
3697
|
+
for (const arg of decorator.getArguments()) {
|
|
3698
|
+
if (!Node6.isCallExpression(arg)) continue;
|
|
3699
|
+
const interceptor = arg.getExpression().getText();
|
|
3700
|
+
const callArgs = arg.getArguments();
|
|
3701
|
+
const firstArg2 = callArgs[0];
|
|
3702
|
+
if (interceptor === "FileInterceptor") {
|
|
3703
|
+
if (firstArg2 && Node6.isStringLiteral(firstArg2)) {
|
|
3704
|
+
entries.push(`${firstArg2.getLiteralValue()}: ${FILE}`);
|
|
3705
|
+
multipart = true;
|
|
3706
|
+
}
|
|
3707
|
+
} else if (interceptor === "FilesInterceptor") {
|
|
3708
|
+
if (firstArg2 && Node6.isStringLiteral(firstArg2)) {
|
|
3709
|
+
entries.push(`${firstArg2.getLiteralValue()}: Array<${FILE}>`);
|
|
3710
|
+
multipart = true;
|
|
3711
|
+
}
|
|
3712
|
+
} else if (interceptor === "FileFieldsInterceptor") {
|
|
3713
|
+
if (firstArg2 && Node6.isArrayLiteralExpression(firstArg2)) {
|
|
3714
|
+
for (const el of firstArg2.getElements()) {
|
|
3715
|
+
if (!Node6.isObjectLiteralExpression(el)) continue;
|
|
3716
|
+
const nameProp = el.getProperty("name");
|
|
3717
|
+
if (nameProp && Node6.isPropertyAssignment(nameProp)) {
|
|
3718
|
+
const init = nameProp.getInitializer();
|
|
3719
|
+
if (init && Node6.isStringLiteral(init)) {
|
|
3720
|
+
entries.push(`${init.getLiteralValue()}: Array<${FILE}>`);
|
|
3721
|
+
}
|
|
3722
|
+
}
|
|
3723
|
+
}
|
|
3724
|
+
multipart = true;
|
|
3725
|
+
}
|
|
3726
|
+
} else if (interceptor === "AnyFilesInterceptor") {
|
|
3727
|
+
multipart = true;
|
|
3728
|
+
}
|
|
3729
|
+
}
|
|
3730
|
+
}
|
|
3731
|
+
if (hasUploadedFileParam) multipart = true;
|
|
3732
|
+
return { fields: entries.length > 0 ? entries.join("; ") : null, multipart };
|
|
3733
|
+
}
|
|
3579
3734
|
function extractResponseType(method, sourceFile, project) {
|
|
3580
3735
|
const apiResponseDecorator = method.getDecorators().find((d) => d.getName() === "ApiResponse" && (apiResponseStatus(d) ?? 0) < 400);
|
|
3581
3736
|
if (apiResponseDecorator) {
|
|
@@ -3710,6 +3865,11 @@ function extractDtoContract(method, sourceFile, project) {
|
|
|
3710
3865
|
let body = extractBodyType(method, sourceFile, project);
|
|
3711
3866
|
const filterInfo = extractApplyFilterInfo(method, sourceFile, project);
|
|
3712
3867
|
const query = extractQueryType(method, sourceFile, project);
|
|
3868
|
+
const uploads = extractUploadedFiles(method);
|
|
3869
|
+
if (uploads.fields) {
|
|
3870
|
+
const fileObject = `{ ${uploads.fields} }`;
|
|
3871
|
+
body = body ? `(${body}) & ${fileObject}` : fileObject;
|
|
3872
|
+
}
|
|
3713
3873
|
const streamElement = detectStreamElement(method);
|
|
3714
3874
|
const isStream = streamElement !== null;
|
|
3715
3875
|
if (filterInfo && filterInfo.source === "body") {
|
|
@@ -3719,7 +3879,7 @@ function extractDtoContract(method, sourceFile, project) {
|
|
|
3719
3879
|
const paramsType = extractParamsType(method, sourceFile, project);
|
|
3720
3880
|
const response = isStream ? resolveTypeNodeToString(streamElement, sourceFile, project, 3) : extractResponseType(method, sourceFile, project);
|
|
3721
3881
|
const errorInfo = extractErrorType(method, sourceFile, project);
|
|
3722
|
-
if (body === null && query === null && paramsType === null && response === "unknown" && errorInfo === null && filterInfo === null && !isStream) {
|
|
3882
|
+
if (body === null && query === null && paramsType === null && response === "unknown" && errorInfo === null && filterInfo === null && !isStream && !uploads.multipart) {
|
|
3723
3883
|
return null;
|
|
3724
3884
|
}
|
|
3725
3885
|
let bodyRef = null;
|
|
@@ -3792,7 +3952,8 @@ function extractDtoContract(method, sourceFile, project) {
|
|
|
3792
3952
|
formWarnings,
|
|
3793
3953
|
bodySchema,
|
|
3794
3954
|
querySchema,
|
|
3795
|
-
stream: isStream
|
|
3955
|
+
stream: isStream,
|
|
3956
|
+
multipart: uploads.multipart
|
|
3796
3957
|
};
|
|
3797
3958
|
}
|
|
3798
3959
|
function resolveParamClass(method, decoratorName, sourceFile, project) {
|
|
@@ -3938,7 +4099,7 @@ async function discoverContractsFast(opts) {
|
|
|
3938
4099
|
const { cwd, glob, tsconfig } = opts;
|
|
3939
4100
|
const tsconfigPath = resolveTsconfigPath(cwd, tsconfig);
|
|
3940
4101
|
const project = createDiscoveryProject(tsconfigPath);
|
|
3941
|
-
const files = await
|
|
4102
|
+
const files = await fg3(glob, { cwd, absolute: true, onlyFiles: true });
|
|
3942
4103
|
for (const f of files) {
|
|
3943
4104
|
project.addSourceFileAtPath(f);
|
|
3944
4105
|
}
|
|
@@ -3946,7 +4107,7 @@ async function discoverContractsFast(opts) {
|
|
|
3946
4107
|
return extractAllRoutes(project);
|
|
3947
4108
|
}
|
|
3948
4109
|
function resolveTsconfigPath(cwd, tsconfig) {
|
|
3949
|
-
return tsconfig ? resolve3(tsconfig) :
|
|
4110
|
+
return tsconfig ? resolve3(tsconfig) : join14(cwd, "tsconfig.json");
|
|
3950
4111
|
}
|
|
3951
4112
|
function createDiscoveryProject(tsconfigPath) {
|
|
3952
4113
|
try {
|
|
@@ -4011,7 +4172,7 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
4011
4172
|
const project = createDiscoveryProject(tsconfigPath);
|
|
4012
4173
|
bindDiscoveryContext(project, cwd, tsconfigPath);
|
|
4013
4174
|
const instance = new _PersistentDiscovery(project, cwd, glob);
|
|
4014
|
-
const files = await
|
|
4175
|
+
const files = await fg3(glob, { cwd, absolute: true, onlyFiles: true });
|
|
4015
4176
|
for (const f of files) {
|
|
4016
4177
|
project.addSourceFileAtPath(f);
|
|
4017
4178
|
instance.controllerPaths.add(f);
|
|
@@ -4040,7 +4201,7 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
4040
4201
|
}
|
|
4041
4202
|
}
|
|
4042
4203
|
const globbed = new Set(
|
|
4043
|
-
await
|
|
4204
|
+
await fg3(this.glob, { cwd: this.cwd, absolute: true, onlyFiles: true })
|
|
4044
4205
|
);
|
|
4045
4206
|
for (const f of globbed) {
|
|
4046
4207
|
if (!this.controllerPaths.has(f)) {
|
|
@@ -4277,7 +4438,8 @@ function extractDtoRoute(args) {
|
|
|
4277
4438
|
formWarnings: dtoContract?.formWarnings ?? [],
|
|
4278
4439
|
bodySchema: dtoContract?.bodySchema ?? null,
|
|
4279
4440
|
querySchema: dtoContract?.querySchema ?? null,
|
|
4280
|
-
stream: dtoContract?.stream ?? false
|
|
4441
|
+
stream: dtoContract?.stream ?? false,
|
|
4442
|
+
multipart: dtoContract?.multipart ?? false
|
|
4281
4443
|
}
|
|
4282
4444
|
});
|
|
4283
4445
|
}
|
|
@@ -4321,9 +4483,9 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
4321
4483
|
|
|
4322
4484
|
// src/watch/lock-file.ts
|
|
4323
4485
|
import { open } from "fs/promises";
|
|
4324
|
-
import { mkdir as mkdir10, readFile as
|
|
4325
|
-
import { join as
|
|
4326
|
-
var
|
|
4486
|
+
import { mkdir as mkdir10, readFile as readFile3, unlink } from "fs/promises";
|
|
4487
|
+
import { join as join15 } from "path";
|
|
4488
|
+
var LOCK_FILE2 = ".watcher.lock";
|
|
4327
4489
|
function isProcessAlive(pid) {
|
|
4328
4490
|
try {
|
|
4329
4491
|
process.kill(pid, 0);
|
|
@@ -4334,7 +4496,7 @@ function isProcessAlive(pid) {
|
|
|
4334
4496
|
}
|
|
4335
4497
|
async function acquireLock(outDir) {
|
|
4336
4498
|
await mkdir10(outDir, { recursive: true });
|
|
4337
|
-
const lockPath =
|
|
4499
|
+
const lockPath = join15(outDir, LOCK_FILE2);
|
|
4338
4500
|
const lockData = { pid: process.pid, startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
4339
4501
|
try {
|
|
4340
4502
|
const fd = await open(lockPath, "wx");
|
|
@@ -4344,7 +4506,7 @@ async function acquireLock(outDir) {
|
|
|
4344
4506
|
} catch (err) {
|
|
4345
4507
|
if (err.code === "EEXIST") {
|
|
4346
4508
|
try {
|
|
4347
|
-
const raw = await
|
|
4509
|
+
const raw = await readFile3(lockPath, "utf8");
|
|
4348
4510
|
const existing = JSON.parse(raw);
|
|
4349
4511
|
if (isProcessAlive(existing.pid)) return null;
|
|
4350
4512
|
await unlink(lockPath);
|
|
@@ -4369,12 +4531,12 @@ async function acquireLock(outDir) {
|
|
|
4369
4531
|
var PAGES_DEBOUNCE_MS = 150;
|
|
4370
4532
|
var NO_OP_WATCHER = { close: async () => {
|
|
4371
4533
|
} };
|
|
4372
|
-
async function watch(config, onChange) {
|
|
4534
|
+
async function watch(config, onChange, options = {}) {
|
|
4373
4535
|
const lock = await acquireLock(config.codegen.outDir);
|
|
4374
4536
|
if (lock === null) {
|
|
4375
4537
|
let holderPid = "unknown";
|
|
4376
4538
|
try {
|
|
4377
|
-
const raw = await
|
|
4539
|
+
const raw = await readFile4(join16(config.codegen.outDir, ".watcher.lock"), "utf8");
|
|
4378
4540
|
const data = JSON.parse(raw);
|
|
4379
4541
|
if (data.pid !== void 0) holderPid = String(data.pid);
|
|
4380
4542
|
} catch {
|
|
@@ -4397,22 +4559,33 @@ async function watch(config, onChange) {
|
|
|
4397
4559
|
}
|
|
4398
4560
|
return discovery;
|
|
4399
4561
|
}
|
|
4400
|
-
|
|
4401
|
-
const initialRoutes = (await getDiscovery()).discover();
|
|
4402
|
-
lastRoutes = initialRoutes;
|
|
4403
|
-
await generate(config, initialRoutes);
|
|
4404
|
-
} catch (err) {
|
|
4405
|
-
console.warn(
|
|
4406
|
-
`[nestjs-codegen] Initial route discovery failed, falling back to pages-only: ${err instanceof Error ? err.message : String(err)}`
|
|
4407
|
-
);
|
|
4562
|
+
async function runInitialPass() {
|
|
4408
4563
|
try {
|
|
4409
|
-
await
|
|
4410
|
-
|
|
4564
|
+
const initialRoutes = (await getDiscovery()).discover();
|
|
4565
|
+
lastRoutes = initialRoutes;
|
|
4566
|
+
await generate(config, initialRoutes);
|
|
4567
|
+
} catch (err) {
|
|
4568
|
+
console.warn(
|
|
4569
|
+
`[nestjs-codegen] Initial route discovery failed, falling back to pages-only: ${err instanceof Error ? err.message : String(err)}`
|
|
4570
|
+
);
|
|
4571
|
+
try {
|
|
4572
|
+
await generate(config, lastRoutes);
|
|
4573
|
+
} catch {
|
|
4574
|
+
}
|
|
4411
4575
|
}
|
|
4412
4576
|
}
|
|
4577
|
+
if (options.deferInitialGenerate) {
|
|
4578
|
+
void runInitialPass().catch((err) => {
|
|
4579
|
+
console.warn(
|
|
4580
|
+
`[nestjs-codegen] Background initial generate failed: ${err instanceof Error ? err.message : String(err)}`
|
|
4581
|
+
);
|
|
4582
|
+
});
|
|
4583
|
+
} else {
|
|
4584
|
+
await runInitialPass();
|
|
4585
|
+
}
|
|
4413
4586
|
let pagesDebounceTimer;
|
|
4414
4587
|
const pagesGlob = config.pages?.glob ?? ".nestjs-codegen-no-pages";
|
|
4415
|
-
const pagesWatcher = chokidar.watch(
|
|
4588
|
+
const pagesWatcher = chokidar.watch(join16(config.codegen.cwd, pagesGlob), {
|
|
4416
4589
|
ignoreInitial: true,
|
|
4417
4590
|
persistent: true,
|
|
4418
4591
|
awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
|
|
@@ -4439,7 +4612,7 @@ async function watch(config, onChange) {
|
|
|
4439
4612
|
pagesWatcher.on("unlink", schedulePagesRegenerate);
|
|
4440
4613
|
let contractsDebounceTimer;
|
|
4441
4614
|
const pendingChangedPaths = /* @__PURE__ */ new Set();
|
|
4442
|
-
const contractsWatcher = chokidar.watch(
|
|
4615
|
+
const contractsWatcher = chokidar.watch(join16(config.codegen.cwd, config.contracts.glob), {
|
|
4443
4616
|
ignoreInitial: true,
|
|
4444
4617
|
persistent: true,
|
|
4445
4618
|
awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
|
|
@@ -4469,7 +4642,7 @@ async function watch(config, onChange) {
|
|
|
4469
4642
|
contractsWatcher.on("add", (p) => scheduleContractsRegenerate(p));
|
|
4470
4643
|
contractsWatcher.on("change", (p) => scheduleContractsRegenerate(p));
|
|
4471
4644
|
contractsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p));
|
|
4472
|
-
const formsWatcher = chokidar.watch(
|
|
4645
|
+
const formsWatcher = chokidar.watch(join16(config.codegen.cwd, config.forms.watch), {
|
|
4473
4646
|
ignoreInitial: true,
|
|
4474
4647
|
persistent: true,
|
|
4475
4648
|
awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
|
|
@@ -4496,7 +4669,7 @@ async function watch(config, onChange) {
|
|
|
4496
4669
|
}
|
|
4497
4670
|
|
|
4498
4671
|
// src/index.ts
|
|
4499
|
-
var VERSION = "0.
|
|
4672
|
+
var VERSION = "0.13.0";
|
|
4500
4673
|
|
|
4501
4674
|
// src/cli/codegen.ts
|
|
4502
4675
|
async function runCodegen(opts = {}) {
|
|
@@ -4525,13 +4698,13 @@ async function runCodegen(opts = {}) {
|
|
|
4525
4698
|
// src/cli/doctor.ts
|
|
4526
4699
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
4527
4700
|
import { appendFileSync, existsSync, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
4528
|
-
import { join as
|
|
4701
|
+
import { join as join18 } from "path";
|
|
4529
4702
|
|
|
4530
4703
|
// src/cli/init.ts
|
|
4531
4704
|
import { execFileSync } from "child_process";
|
|
4532
4705
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
4533
|
-
import { access as access2, mkdir as mkdir11, readFile as
|
|
4534
|
-
import { join as
|
|
4706
|
+
import { access as access2, mkdir as mkdir11, readFile as readFile5, writeFile as writeFile11 } from "fs/promises";
|
|
4707
|
+
import { join as join17 } from "path";
|
|
4535
4708
|
import { createInterface } from "readline";
|
|
4536
4709
|
|
|
4537
4710
|
// src/cli/patch-utils.ts
|
|
@@ -4593,7 +4766,7 @@ ${bold(title)}`);
|
|
|
4593
4766
|
}
|
|
4594
4767
|
async function readPackageJson(cwd) {
|
|
4595
4768
|
try {
|
|
4596
|
-
const raw = await
|
|
4769
|
+
const raw = await readFile5(join17(cwd, "package.json"), "utf8");
|
|
4597
4770
|
return JSON.parse(raw);
|
|
4598
4771
|
} catch {
|
|
4599
4772
|
return {};
|
|
@@ -4624,7 +4797,7 @@ async function detectTemplateEngine(cwd) {
|
|
|
4624
4797
|
async function detectPackageManager(cwd) {
|
|
4625
4798
|
async function exists(file) {
|
|
4626
4799
|
try {
|
|
4627
|
-
await access2(
|
|
4800
|
+
await access2(join17(cwd, file));
|
|
4628
4801
|
return true;
|
|
4629
4802
|
} catch {
|
|
4630
4803
|
return false;
|
|
@@ -4667,13 +4840,13 @@ async function writeIfNotExists(filePath, content, label) {
|
|
|
4667
4840
|
if (dir) {
|
|
4668
4841
|
await mkdir11(dir, { recursive: true });
|
|
4669
4842
|
}
|
|
4670
|
-
await
|
|
4843
|
+
await writeFile11(filePath, content, "utf8");
|
|
4671
4844
|
logCreated(label);
|
|
4672
4845
|
}
|
|
4673
4846
|
async function handleViteConfig(cwd, framework) {
|
|
4674
|
-
const filePath =
|
|
4847
|
+
const filePath = join17(cwd, "vite.config.ts");
|
|
4675
4848
|
if (await fileExists2(filePath)) {
|
|
4676
|
-
const existing = await
|
|
4849
|
+
const existing = await readFile5(filePath, "utf8");
|
|
4677
4850
|
const hasPlugin = existing.includes("nestInertia") || existing.includes("nestjs-inertia-vite/plugin");
|
|
4678
4851
|
if (!hasPlugin) {
|
|
4679
4852
|
logSkipped("vite.config.ts");
|
|
@@ -4691,13 +4864,13 @@ async function handleViteConfig(cwd, framework) {
|
|
|
4691
4864
|
if (dir) {
|
|
4692
4865
|
await mkdir11(dir, { recursive: true });
|
|
4693
4866
|
}
|
|
4694
|
-
await
|
|
4867
|
+
await writeFile11(filePath, viteConfigTemplate(framework), "utf8");
|
|
4695
4868
|
logCreated("vite.config.ts");
|
|
4696
4869
|
}
|
|
4697
4870
|
async function patchGitignore(gitignorePath) {
|
|
4698
4871
|
let existing = "";
|
|
4699
4872
|
if (await fileExists2(gitignorePath)) {
|
|
4700
|
-
existing = await
|
|
4873
|
+
existing = await readFile5(gitignorePath, "utf8");
|
|
4701
4874
|
}
|
|
4702
4875
|
if (existing.split("\n").some((line) => line.trim() === GITIGNORE_ENTRY)) {
|
|
4703
4876
|
console.log(` ${cyan("\u2192")} .gitignore ${dim("(already contains .nestjs-inertia/, skipped)")}`);
|
|
@@ -4707,7 +4880,7 @@ async function patchGitignore(gitignorePath) {
|
|
|
4707
4880
|
` : `${existing}
|
|
4708
4881
|
${GITIGNORE_ENTRY}
|
|
4709
4882
|
`;
|
|
4710
|
-
await
|
|
4883
|
+
await writeFile11(gitignorePath, newContent, "utf8");
|
|
4711
4884
|
logPatched(".gitignore", "added .nestjs-inertia/");
|
|
4712
4885
|
}
|
|
4713
4886
|
function installDeps(pkgManager, deps, dev) {
|
|
@@ -4729,10 +4902,10 @@ function installDeps(pkgManager, deps, dev) {
|
|
|
4729
4902
|
}
|
|
4730
4903
|
}
|
|
4731
4904
|
async function patchPackageJsonScripts(cwd, scripts) {
|
|
4732
|
-
const pkgPath =
|
|
4905
|
+
const pkgPath = join17(cwd, "package.json");
|
|
4733
4906
|
let pkg = {};
|
|
4734
4907
|
try {
|
|
4735
|
-
pkg = JSON.parse(await
|
|
4908
|
+
pkg = JSON.parse(await readFile5(pkgPath, "utf8"));
|
|
4736
4909
|
} catch {
|
|
4737
4910
|
return;
|
|
4738
4911
|
}
|
|
@@ -4751,7 +4924,7 @@ async function patchPackageJsonScripts(cwd, scripts) {
|
|
|
4751
4924
|
return;
|
|
4752
4925
|
}
|
|
4753
4926
|
pkg.scripts = existing;
|
|
4754
|
-
await
|
|
4927
|
+
await writeFile11(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
4755
4928
|
`, "utf8");
|
|
4756
4929
|
}
|
|
4757
4930
|
function patchAppModule(filePath, rootView) {
|
|
@@ -5046,7 +5219,7 @@ export class HomeController {
|
|
|
5046
5219
|
}
|
|
5047
5220
|
`;
|
|
5048
5221
|
function patchTsconfigExclude(cwd, dir, filename = "tsconfig.json") {
|
|
5049
|
-
const filePath =
|
|
5222
|
+
const filePath = join17(cwd, filename);
|
|
5050
5223
|
return patchJsonFile(
|
|
5051
5224
|
filePath,
|
|
5052
5225
|
(json) => {
|
|
@@ -5061,7 +5234,7 @@ function patchTsconfigExclude(cwd, dir, filename = "tsconfig.json") {
|
|
|
5061
5234
|
);
|
|
5062
5235
|
}
|
|
5063
5236
|
function patchNestCliJson(cwd, shellDir) {
|
|
5064
|
-
const filePath =
|
|
5237
|
+
const filePath = join17(cwd, "nest-cli.json");
|
|
5065
5238
|
return patchJsonFile(filePath, (json) => {
|
|
5066
5239
|
const compiler = json.compilerOptions ?? {};
|
|
5067
5240
|
const assets = compiler.assets ?? [];
|
|
@@ -5084,46 +5257,46 @@ async function scaffoldFiles(ctx) {
|
|
|
5084
5257
|
const { cwd, framework, engine, shellFileName, entryExt, pageExt } = ctx;
|
|
5085
5258
|
logSection("Scaffold files");
|
|
5086
5259
|
await writeIfNotExists(
|
|
5087
|
-
|
|
5260
|
+
join17(cwd, "nestjs-inertia.config.ts"),
|
|
5088
5261
|
configTemplate(framework),
|
|
5089
5262
|
"nestjs-inertia.config.ts"
|
|
5090
5263
|
);
|
|
5091
|
-
await writeIfNotExists(
|
|
5264
|
+
await writeIfNotExists(join17(cwd, "nestjs-inertia.d.ts"), DTS_TEMPLATE, "nestjs-inertia.d.ts");
|
|
5092
5265
|
await writeIfNotExists(
|
|
5093
|
-
|
|
5266
|
+
join17(cwd, "tsconfig.inertia.json"),
|
|
5094
5267
|
TSCONFIG_INERTIA_TEMPLATE,
|
|
5095
5268
|
"tsconfig.inertia.json"
|
|
5096
5269
|
);
|
|
5097
5270
|
await writeIfNotExists(
|
|
5098
|
-
|
|
5271
|
+
join17(cwd, "inertia", "tsconfig.json"),
|
|
5099
5272
|
INERTIA_TSCONFIG_TEMPLATE,
|
|
5100
5273
|
"inertia/tsconfig.json"
|
|
5101
5274
|
);
|
|
5102
5275
|
await writeIfNotExists(
|
|
5103
|
-
|
|
5276
|
+
join17(cwd, "inertia", shellFileName),
|
|
5104
5277
|
htmlShellTemplate(framework, engine),
|
|
5105
5278
|
`inertia/${shellFileName}`
|
|
5106
5279
|
);
|
|
5107
5280
|
await handleViteConfig(cwd, framework);
|
|
5108
5281
|
await writeIfNotExists(
|
|
5109
|
-
|
|
5282
|
+
join17(cwd, "inertia", "app", `client.${entryExt}`),
|
|
5110
5283
|
entryPointTemplate(framework),
|
|
5111
5284
|
`inertia/app/client.${entryExt}`
|
|
5112
5285
|
);
|
|
5113
5286
|
await writeIfNotExists(
|
|
5114
|
-
|
|
5287
|
+
join17(cwd, "inertia", "pages", `Home.${pageExt}`),
|
|
5115
5288
|
samplePageTemplate(framework),
|
|
5116
5289
|
`inertia/pages/Home.${pageExt}`
|
|
5117
5290
|
);
|
|
5118
5291
|
await writeIfNotExists(
|
|
5119
|
-
|
|
5292
|
+
join17(cwd, "src", "home.controller.ts"),
|
|
5120
5293
|
SAMPLE_CONTROLLER,
|
|
5121
5294
|
"src/home.controller.ts"
|
|
5122
5295
|
);
|
|
5123
5296
|
}
|
|
5124
5297
|
function patchServerAppModule(ctx) {
|
|
5125
5298
|
const { cwd, rootView } = ctx;
|
|
5126
|
-
const appModulePath =
|
|
5299
|
+
const appModulePath = join17(cwd, "src", "app.module.ts");
|
|
5127
5300
|
const appModuleResult = patchAppModule(appModulePath, rootView);
|
|
5128
5301
|
if (appModuleResult === "patched") {
|
|
5129
5302
|
logPatched("src/app.module.ts", "added InertiaModule.forRoot");
|
|
@@ -5137,7 +5310,7 @@ function patchServerAppModule(ctx) {
|
|
|
5137
5310
|
}
|
|
5138
5311
|
}
|
|
5139
5312
|
function patchServerMainTs(ctx) {
|
|
5140
|
-
const mainTsPath =
|
|
5313
|
+
const mainTsPath = join17(ctx.cwd, "src", "main.ts");
|
|
5141
5314
|
const mainTsResult = patchMainTs(mainTsPath);
|
|
5142
5315
|
if (mainTsResult === "patched") {
|
|
5143
5316
|
logPatched("src/main.ts", "added setupInertiaVite after NestFactory.create");
|
|
@@ -5172,7 +5345,7 @@ function patchBuildConfigs(ctx) {
|
|
|
5172
5345
|
}
|
|
5173
5346
|
async function patchGitignoreAndDist(ctx) {
|
|
5174
5347
|
const { cwd } = ctx;
|
|
5175
|
-
await patchGitignore(
|
|
5348
|
+
await patchGitignore(join17(cwd, ".gitignore"));
|
|
5176
5349
|
const tsconfigDistResult = patchTsconfigExclude(cwd, "dist", "tsconfig.json");
|
|
5177
5350
|
if (tsconfigDistResult === "patched") {
|
|
5178
5351
|
logPatched("tsconfig.json", "excluded dist/ from server compilation");
|
|
@@ -5278,7 +5451,7 @@ ${green("\u2713")} Setup complete! Run: ${bold("nest start --watch")}
|
|
|
5278
5451
|
|
|
5279
5452
|
// src/cli/doctor.ts
|
|
5280
5453
|
function checkFileExists(cwd, file) {
|
|
5281
|
-
return existsSync(
|
|
5454
|
+
return existsSync(join18(cwd, file));
|
|
5282
5455
|
}
|
|
5283
5456
|
function readJson(path) {
|
|
5284
5457
|
try {
|
|
@@ -5314,15 +5487,15 @@ function writeJsonField(filePath, dotPath, value) {
|
|
|
5314
5487
|
}
|
|
5315
5488
|
function getPackageVersion(cwd, pkg) {
|
|
5316
5489
|
try {
|
|
5317
|
-
const pkgJson = readJson(
|
|
5490
|
+
const pkgJson = readJson(join18(cwd, "node_modules", pkg, "package.json"));
|
|
5318
5491
|
return pkgJson?.version ?? null;
|
|
5319
5492
|
} catch {
|
|
5320
5493
|
return null;
|
|
5321
5494
|
}
|
|
5322
5495
|
}
|
|
5323
5496
|
function detectPkgManager(cwd) {
|
|
5324
|
-
if (existsSync(
|
|
5325
|
-
if (existsSync(
|
|
5497
|
+
if (existsSync(join18(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
5498
|
+
if (existsSync(join18(cwd, "yarn.lock"))) return "yarn";
|
|
5326
5499
|
return "npm";
|
|
5327
5500
|
}
|
|
5328
5501
|
async function runDoctor(opts) {
|
|
@@ -5360,7 +5533,7 @@ async function runDoctor(opts) {
|
|
|
5360
5533
|
autoFix: () => runInit({ cwd })
|
|
5361
5534
|
});
|
|
5362
5535
|
if (foundShellDir) {
|
|
5363
|
-
const nestCliPath =
|
|
5536
|
+
const nestCliPath = join18(cwd, "nest-cli.json");
|
|
5364
5537
|
const nestCli = readJson(nestCliPath);
|
|
5365
5538
|
const compiler = nestCli?.compilerOptions ?? {};
|
|
5366
5539
|
const assets = compiler.assets ?? [];
|
|
@@ -5399,7 +5572,7 @@ async function runDoctor(opts) {
|
|
|
5399
5572
|
fix: "Run: nestjs-codegen codegen",
|
|
5400
5573
|
autoFix: () => runCodegen({ cwd })
|
|
5401
5574
|
});
|
|
5402
|
-
const tsconfigPath =
|
|
5575
|
+
const tsconfigPath = join18(cwd, "tsconfig.json");
|
|
5403
5576
|
const tsconfig = readJson(tsconfigPath);
|
|
5404
5577
|
const paths = tsconfig?.compilerOptions?.paths;
|
|
5405
5578
|
checks.push({
|
|
@@ -5410,7 +5583,7 @@ async function runDoctor(opts) {
|
|
|
5410
5583
|
});
|
|
5411
5584
|
const inertiaDir = foundShellDir ?? "inertia";
|
|
5412
5585
|
for (const tsconfigFile of ["tsconfig.json", "tsconfig.build.json"]) {
|
|
5413
|
-
const tsc = readJson(
|
|
5586
|
+
const tsc = readJson(join18(cwd, tsconfigFile));
|
|
5414
5587
|
if (!tsc) continue;
|
|
5415
5588
|
const excl = tsc.exclude ?? [];
|
|
5416
5589
|
const excludesIt = excl.includes(inertiaDir);
|
|
@@ -5436,7 +5609,7 @@ async function runDoctor(opts) {
|
|
|
5436
5609
|
}
|
|
5437
5610
|
});
|
|
5438
5611
|
}
|
|
5439
|
-
const inertiaTsconfigPath =
|
|
5612
|
+
const inertiaTsconfigPath = join18(cwd, "tsconfig.inertia.json");
|
|
5440
5613
|
const inertiaTsconfig = readJson(inertiaTsconfigPath);
|
|
5441
5614
|
checks.push({
|
|
5442
5615
|
name: "tsconfig.inertia.json exists",
|
|
@@ -5488,7 +5661,7 @@ async function runDoctor(opts) {
|
|
|
5488
5661
|
fix: 'Add "nestjs-inertia.d.ts" to include array (resolves InertiaRegistry augmentation)'
|
|
5489
5662
|
});
|
|
5490
5663
|
}
|
|
5491
|
-
const innerTsconfigPath =
|
|
5664
|
+
const innerTsconfigPath = join18(cwd, "inertia", "tsconfig.json");
|
|
5492
5665
|
checks.push({
|
|
5493
5666
|
name: "inertia/tsconfig.json exists (VSCode picks up ~codegen alias)",
|
|
5494
5667
|
pass: existsSync(innerTsconfigPath),
|
|
@@ -5498,7 +5671,7 @@ async function runDoctor(opts) {
|
|
|
5498
5671
|
}
|
|
5499
5672
|
});
|
|
5500
5673
|
if (checkFileExists(cwd, "vite.config.ts")) {
|
|
5501
|
-
const viteContent = readFileSync4(
|
|
5674
|
+
const viteContent = readFileSync4(join18(cwd, "vite.config.ts"), "utf8");
|
|
5502
5675
|
checks.push({
|
|
5503
5676
|
name: "vite.config.ts has resolve.alias",
|
|
5504
5677
|
pass: viteContent.includes("resolve") && viteContent.includes("alias"),
|
|
@@ -5563,7 +5736,7 @@ async function runDoctor(opts) {
|
|
|
5563
5736
|
});
|
|
5564
5737
|
}
|
|
5565
5738
|
if (checkFileExists(cwd, ".gitignore")) {
|
|
5566
|
-
const gitignorePath =
|
|
5739
|
+
const gitignorePath = join18(cwd, ".gitignore");
|
|
5567
5740
|
const gitignore = readFileSync4(gitignorePath, "utf8");
|
|
5568
5741
|
checks.push({
|
|
5569
5742
|
name: ".gitignore includes .nestjs-inertia/",
|
|
@@ -5572,7 +5745,7 @@ async function runDoctor(opts) {
|
|
|
5572
5745
|
autoFix: () => appendFileSync(gitignorePath, "\n.nestjs-inertia/\n")
|
|
5573
5746
|
});
|
|
5574
5747
|
}
|
|
5575
|
-
const pkgJsonPath =
|
|
5748
|
+
const pkgJsonPath = join18(cwd, "package.json");
|
|
5576
5749
|
const pkgJson = readJson(pkgJsonPath);
|
|
5577
5750
|
const scripts = pkgJson?.scripts ?? {};
|
|
5578
5751
|
checks.push({
|