@dudousxd/nestjs-codegen 0.11.0 → 0.12.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 +28 -0
- package/dist/cli/main.cjs +204 -88
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.js +188 -72
- package/dist/cli/main.js.map +1 -1
- package/dist/index.cjs +162 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -3
- package/dist/index.d.ts +29 -3
- package/dist/index.js +149 -33
- package/dist/index.js.map +1 -1
- package/dist/nest/index.cjs +207 -72
- package/dist/nest/index.cjs.map +1 -1
- package/dist/nest/index.d.cts +15 -4
- package/dist/nest/index.d.ts +15 -4
- package/dist/nest/index.js +207 -72
- package/dist/nest/index.js.map +1 -1
- package/package.json +1 -1
package/dist/nest/index.cjs
CHANGED
|
@@ -174,8 +174,8 @@ function applyDefaults(userConfig, cwd) {
|
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
// src/watch/watcher.ts
|
|
177
|
-
var
|
|
178
|
-
var
|
|
177
|
+
var import_promises15 = require("fs/promises");
|
|
178
|
+
var import_node_path17 = require("path");
|
|
179
179
|
var import_chokidar = __toESM(require("chokidar"), 1);
|
|
180
180
|
|
|
181
181
|
// src/discovery/contracts-fast.ts
|
|
@@ -2315,8 +2315,8 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
2315
2315
|
}
|
|
2316
2316
|
|
|
2317
2317
|
// src/generate.ts
|
|
2318
|
-
var
|
|
2319
|
-
var
|
|
2318
|
+
var import_promises14 = require("fs/promises");
|
|
2319
|
+
var import_node_path16 = require("path");
|
|
2320
2320
|
|
|
2321
2321
|
// src/discovery/pages.ts
|
|
2322
2322
|
var import_promises2 = require("fs/promises");
|
|
@@ -4211,9 +4211,159 @@ function buildEmpty() {
|
|
|
4211
4211
|
].join("\n");
|
|
4212
4212
|
}
|
|
4213
4213
|
|
|
4214
|
+
// src/generate-manifest.ts
|
|
4215
|
+
var import_node_crypto = require("crypto");
|
|
4216
|
+
var import_promises13 = require("fs/promises");
|
|
4217
|
+
var import_node_path15 = require("path");
|
|
4218
|
+
var import_fast_glob3 = __toESM(require("fast-glob"), 1);
|
|
4219
|
+
|
|
4220
|
+
// src/watch/lock-file.ts
|
|
4221
|
+
var import_promises11 = require("fs/promises");
|
|
4222
|
+
var import_promises12 = require("fs/promises");
|
|
4223
|
+
var import_node_path14 = require("path");
|
|
4224
|
+
var LOCK_FILE = ".watcher.lock";
|
|
4225
|
+
function isProcessAlive(pid) {
|
|
4226
|
+
try {
|
|
4227
|
+
process.kill(pid, 0);
|
|
4228
|
+
return true;
|
|
4229
|
+
} catch {
|
|
4230
|
+
return false;
|
|
4231
|
+
}
|
|
4232
|
+
}
|
|
4233
|
+
async function acquireLock(outDir) {
|
|
4234
|
+
await (0, import_promises12.mkdir)(outDir, { recursive: true });
|
|
4235
|
+
const lockPath = (0, import_node_path14.join)(outDir, LOCK_FILE);
|
|
4236
|
+
const lockData = { pid: process.pid, startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
4237
|
+
try {
|
|
4238
|
+
const fd = await (0, import_promises11.open)(lockPath, "wx");
|
|
4239
|
+
await fd.writeFile(`${JSON.stringify(lockData, null, 2)}
|
|
4240
|
+
`, "utf8");
|
|
4241
|
+
await fd.close();
|
|
4242
|
+
} catch (err) {
|
|
4243
|
+
if (err.code === "EEXIST") {
|
|
4244
|
+
try {
|
|
4245
|
+
const raw = await (0, import_promises12.readFile)(lockPath, "utf8");
|
|
4246
|
+
const existing = JSON.parse(raw);
|
|
4247
|
+
if (isProcessAlive(existing.pid)) return null;
|
|
4248
|
+
await (0, import_promises12.unlink)(lockPath);
|
|
4249
|
+
return acquireLock(outDir);
|
|
4250
|
+
} catch {
|
|
4251
|
+
return null;
|
|
4252
|
+
}
|
|
4253
|
+
}
|
|
4254
|
+
return null;
|
|
4255
|
+
}
|
|
4256
|
+
return {
|
|
4257
|
+
release: async () => {
|
|
4258
|
+
try {
|
|
4259
|
+
await (0, import_promises12.unlink)(lockPath);
|
|
4260
|
+
} catch {
|
|
4261
|
+
}
|
|
4262
|
+
}
|
|
4263
|
+
};
|
|
4264
|
+
}
|
|
4265
|
+
|
|
4266
|
+
// src/index.ts
|
|
4267
|
+
var VERSION = "0.12.0";
|
|
4268
|
+
|
|
4269
|
+
// src/generate-manifest.ts
|
|
4270
|
+
var MANIFEST_FILE = ".codegen-manifest.json";
|
|
4271
|
+
var LOCK_FILE2 = ".watcher.lock";
|
|
4272
|
+
function isManifestShape(value) {
|
|
4273
|
+
if (typeof value !== "object" || value === null) return false;
|
|
4274
|
+
const candidate = value;
|
|
4275
|
+
if (typeof candidate.version !== "string") return false;
|
|
4276
|
+
if (typeof candidate.hash !== "string") return false;
|
|
4277
|
+
if (!Array.isArray(candidate.files)) return false;
|
|
4278
|
+
return candidate.files.every((entry) => typeof entry === "string");
|
|
4279
|
+
}
|
|
4280
|
+
function serializeConfig(config) {
|
|
4281
|
+
try {
|
|
4282
|
+
return JSON.stringify(config, (_key, value) => {
|
|
4283
|
+
if (typeof value === "function") return `[fn:${value.name}]${value.toString()}`;
|
|
4284
|
+
return value;
|
|
4285
|
+
});
|
|
4286
|
+
} catch {
|
|
4287
|
+
return `unserializable:${config.codegen.outDir}:${config.contracts.glob}`;
|
|
4288
|
+
}
|
|
4289
|
+
}
|
|
4290
|
+
async function discoverInputFiles(config) {
|
|
4291
|
+
const globs = [config.contracts.glob, config.forms.watch];
|
|
4292
|
+
if (config.pages) globs.push(config.pages.glob);
|
|
4293
|
+
const cwd = config.codegen.cwd;
|
|
4294
|
+
const matched = await (0, import_fast_glob3.default)(globs, { cwd, absolute: true, onlyFiles: true });
|
|
4295
|
+
return [...new Set(matched)].sort();
|
|
4296
|
+
}
|
|
4297
|
+
async function computeInputsHash(config) {
|
|
4298
|
+
const hash = (0, import_node_crypto.createHash)("sha256");
|
|
4299
|
+
hash.update(`version:${VERSION}
|
|
4300
|
+
`);
|
|
4301
|
+
hash.update(`config:${serializeConfig(config)}
|
|
4302
|
+
`);
|
|
4303
|
+
const inputFiles = await discoverInputFiles(config);
|
|
4304
|
+
const cwd = config.codegen.cwd;
|
|
4305
|
+
for (const file of inputFiles) {
|
|
4306
|
+
const contents = await (0, import_promises13.readFile)(file, "utf8");
|
|
4307
|
+
hash.update(`file:${(0, import_node_path15.relative)(cwd, file)}
|
|
4308
|
+
`);
|
|
4309
|
+
hash.update(contents);
|
|
4310
|
+
hash.update("\n");
|
|
4311
|
+
}
|
|
4312
|
+
return hash.digest("hex");
|
|
4313
|
+
}
|
|
4314
|
+
async function readManifest(outDir) {
|
|
4315
|
+
try {
|
|
4316
|
+
const raw = await (0, import_promises13.readFile)((0, import_node_path15.join)(outDir, MANIFEST_FILE), "utf8");
|
|
4317
|
+
const parsed = JSON.parse(raw);
|
|
4318
|
+
if (!isManifestShape(parsed)) return null;
|
|
4319
|
+
return { version: parsed.version, hash: parsed.hash, files: parsed.files };
|
|
4320
|
+
} catch {
|
|
4321
|
+
return null;
|
|
4322
|
+
}
|
|
4323
|
+
}
|
|
4324
|
+
async function writeManifest(outDir, manifest) {
|
|
4325
|
+
await (0, import_promises13.writeFile)((0, import_node_path15.join)(outDir, MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}
|
|
4326
|
+
`, "utf8");
|
|
4327
|
+
}
|
|
4328
|
+
async function listOutputFiles(outDir) {
|
|
4329
|
+
const found = [];
|
|
4330
|
+
async function walk(dir) {
|
|
4331
|
+
const entries = await (0, import_promises13.readdir)(dir, { withFileTypes: true }).catch(() => []);
|
|
4332
|
+
for (const entry of entries) {
|
|
4333
|
+
const abs = (0, import_node_path15.join)(dir, entry.name);
|
|
4334
|
+
if (entry.isDirectory()) {
|
|
4335
|
+
await walk(abs);
|
|
4336
|
+
} else if (entry.isFile()) {
|
|
4337
|
+
const rel = (0, import_node_path15.relative)(outDir, abs);
|
|
4338
|
+
if (rel === MANIFEST_FILE || rel === LOCK_FILE2) continue;
|
|
4339
|
+
found.push(rel);
|
|
4340
|
+
}
|
|
4341
|
+
}
|
|
4342
|
+
}
|
|
4343
|
+
await walk(outDir);
|
|
4344
|
+
return found.sort();
|
|
4345
|
+
}
|
|
4346
|
+
async function allOutputsExist(outDir, files) {
|
|
4347
|
+
const present = new Set(await listOutputFiles(outDir));
|
|
4348
|
+
return files.every((file) => present.has(file));
|
|
4349
|
+
}
|
|
4350
|
+
async function isManifestFresh(outDir, manifest, inputsHash) {
|
|
4351
|
+
if (manifest === null) return false;
|
|
4352
|
+
if (manifest.version !== VERSION) return false;
|
|
4353
|
+
if (manifest.hash !== inputsHash) return false;
|
|
4354
|
+
if (manifest.files.length === 0) return false;
|
|
4355
|
+
return allOutputsExist(outDir, manifest.files);
|
|
4356
|
+
}
|
|
4357
|
+
|
|
4214
4358
|
// src/generate.ts
|
|
4215
4359
|
async function generate(config, inputRoutes = []) {
|
|
4216
4360
|
setCodegenDebug(config.debug);
|
|
4361
|
+
const inputsHash = await computeInputsHash(config);
|
|
4362
|
+
const manifest = await readManifest(config.codegen.outDir);
|
|
4363
|
+
if (await isManifestFresh(config.codegen.outDir, manifest, inputsHash)) {
|
|
4364
|
+
console.log(`[nestjs-codegen] ${config.codegen.outDir} up to date, skipped`);
|
|
4365
|
+
return;
|
|
4366
|
+
}
|
|
4217
4367
|
const extensions = config.extensions ?? [];
|
|
4218
4368
|
let routes = inputRoutes;
|
|
4219
4369
|
const ctx = createExtensionContext(config, () => routes);
|
|
@@ -4270,69 +4420,29 @@ async function generate(config, inputRoutes = []) {
|
|
|
4270
4420
|
if (extensions.length > 0) {
|
|
4271
4421
|
const extraFiles = await collectEmittedFiles(extensions, ctx);
|
|
4272
4422
|
for (const file of extraFiles) {
|
|
4273
|
-
const dest = (0,
|
|
4274
|
-
await (0,
|
|
4275
|
-
await (0,
|
|
4276
|
-
}
|
|
4277
|
-
}
|
|
4278
|
-
}
|
|
4279
|
-
|
|
4280
|
-
// src/watch/lock-file.ts
|
|
4281
|
-
var import_promises12 = require("fs/promises");
|
|
4282
|
-
var import_promises13 = require("fs/promises");
|
|
4283
|
-
var import_node_path15 = require("path");
|
|
4284
|
-
var LOCK_FILE = ".watcher.lock";
|
|
4285
|
-
function isProcessAlive(pid) {
|
|
4286
|
-
try {
|
|
4287
|
-
process.kill(pid, 0);
|
|
4288
|
-
return true;
|
|
4289
|
-
} catch {
|
|
4290
|
-
return false;
|
|
4291
|
-
}
|
|
4292
|
-
}
|
|
4293
|
-
async function acquireLock(outDir) {
|
|
4294
|
-
await (0, import_promises13.mkdir)(outDir, { recursive: true });
|
|
4295
|
-
const lockPath = (0, import_node_path15.join)(outDir, LOCK_FILE);
|
|
4296
|
-
const lockData = { pid: process.pid, startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
4297
|
-
try {
|
|
4298
|
-
const fd = await (0, import_promises12.open)(lockPath, "wx");
|
|
4299
|
-
await fd.writeFile(`${JSON.stringify(lockData, null, 2)}
|
|
4300
|
-
`, "utf8");
|
|
4301
|
-
await fd.close();
|
|
4302
|
-
} catch (err) {
|
|
4303
|
-
if (err.code === "EEXIST") {
|
|
4304
|
-
try {
|
|
4305
|
-
const raw = await (0, import_promises13.readFile)(lockPath, "utf8");
|
|
4306
|
-
const existing = JSON.parse(raw);
|
|
4307
|
-
if (isProcessAlive(existing.pid)) return null;
|
|
4308
|
-
await (0, import_promises13.unlink)(lockPath);
|
|
4309
|
-
return acquireLock(outDir);
|
|
4310
|
-
} catch {
|
|
4311
|
-
return null;
|
|
4312
|
-
}
|
|
4423
|
+
const dest = (0, import_node_path16.join)(config.codegen.outDir, file.path);
|
|
4424
|
+
await (0, import_promises14.mkdir)((0, import_node_path16.dirname)(dest), { recursive: true });
|
|
4425
|
+
await (0, import_promises14.writeFile)(dest, file.contents, "utf8");
|
|
4313
4426
|
}
|
|
4314
|
-
return null;
|
|
4315
4427
|
}
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
}
|
|
4323
|
-
};
|
|
4428
|
+
const outputFiles = await listOutputFiles(config.codegen.outDir);
|
|
4429
|
+
await writeManifest(config.codegen.outDir, {
|
|
4430
|
+
version: VERSION,
|
|
4431
|
+
hash: inputsHash,
|
|
4432
|
+
files: outputFiles
|
|
4433
|
+
});
|
|
4324
4434
|
}
|
|
4325
4435
|
|
|
4326
4436
|
// src/watch/watcher.ts
|
|
4327
4437
|
var PAGES_DEBOUNCE_MS = 150;
|
|
4328
4438
|
var NO_OP_WATCHER = { close: async () => {
|
|
4329
4439
|
} };
|
|
4330
|
-
async function watch(config, onChange) {
|
|
4440
|
+
async function watch(config, onChange, options = {}) {
|
|
4331
4441
|
const lock = await acquireLock(config.codegen.outDir);
|
|
4332
4442
|
if (lock === null) {
|
|
4333
4443
|
let holderPid = "unknown";
|
|
4334
4444
|
try {
|
|
4335
|
-
const raw = await (0,
|
|
4445
|
+
const raw = await (0, import_promises15.readFile)((0, import_node_path17.join)(config.codegen.outDir, ".watcher.lock"), "utf8");
|
|
4336
4446
|
const data = JSON.parse(raw);
|
|
4337
4447
|
if (data.pid !== void 0) holderPid = String(data.pid);
|
|
4338
4448
|
} catch {
|
|
@@ -4355,22 +4465,33 @@ async function watch(config, onChange) {
|
|
|
4355
4465
|
}
|
|
4356
4466
|
return discovery;
|
|
4357
4467
|
}
|
|
4358
|
-
|
|
4359
|
-
const initialRoutes = (await getDiscovery()).discover();
|
|
4360
|
-
lastRoutes = initialRoutes;
|
|
4361
|
-
await generate(config, initialRoutes);
|
|
4362
|
-
} catch (err) {
|
|
4363
|
-
console.warn(
|
|
4364
|
-
`[nestjs-codegen] Initial route discovery failed, falling back to pages-only: ${err instanceof Error ? err.message : String(err)}`
|
|
4365
|
-
);
|
|
4468
|
+
async function runInitialPass() {
|
|
4366
4469
|
try {
|
|
4367
|
-
await
|
|
4368
|
-
|
|
4470
|
+
const initialRoutes = (await getDiscovery()).discover();
|
|
4471
|
+
lastRoutes = initialRoutes;
|
|
4472
|
+
await generate(config, initialRoutes);
|
|
4473
|
+
} catch (err) {
|
|
4474
|
+
console.warn(
|
|
4475
|
+
`[nestjs-codegen] Initial route discovery failed, falling back to pages-only: ${err instanceof Error ? err.message : String(err)}`
|
|
4476
|
+
);
|
|
4477
|
+
try {
|
|
4478
|
+
await generate(config, lastRoutes);
|
|
4479
|
+
} catch {
|
|
4480
|
+
}
|
|
4369
4481
|
}
|
|
4370
4482
|
}
|
|
4483
|
+
if (options.deferInitialGenerate) {
|
|
4484
|
+
void runInitialPass().catch((err) => {
|
|
4485
|
+
console.warn(
|
|
4486
|
+
`[nestjs-codegen] Background initial generate failed: ${err instanceof Error ? err.message : String(err)}`
|
|
4487
|
+
);
|
|
4488
|
+
});
|
|
4489
|
+
} else {
|
|
4490
|
+
await runInitialPass();
|
|
4491
|
+
}
|
|
4371
4492
|
let pagesDebounceTimer;
|
|
4372
4493
|
const pagesGlob = config.pages?.glob ?? ".nestjs-codegen-no-pages";
|
|
4373
|
-
const pagesWatcher = import_chokidar.default.watch((0,
|
|
4494
|
+
const pagesWatcher = import_chokidar.default.watch((0, import_node_path17.join)(config.codegen.cwd, pagesGlob), {
|
|
4374
4495
|
ignoreInitial: true,
|
|
4375
4496
|
persistent: true,
|
|
4376
4497
|
awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
|
|
@@ -4397,7 +4518,7 @@ async function watch(config, onChange) {
|
|
|
4397
4518
|
pagesWatcher.on("unlink", schedulePagesRegenerate);
|
|
4398
4519
|
let contractsDebounceTimer;
|
|
4399
4520
|
const pendingChangedPaths = /* @__PURE__ */ new Set();
|
|
4400
|
-
const contractsWatcher = import_chokidar.default.watch((0,
|
|
4521
|
+
const contractsWatcher = import_chokidar.default.watch((0, import_node_path17.join)(config.codegen.cwd, config.contracts.glob), {
|
|
4401
4522
|
ignoreInitial: true,
|
|
4402
4523
|
persistent: true,
|
|
4403
4524
|
awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
|
|
@@ -4427,7 +4548,7 @@ async function watch(config, onChange) {
|
|
|
4427
4548
|
contractsWatcher.on("add", (p) => scheduleContractsRegenerate(p));
|
|
4428
4549
|
contractsWatcher.on("change", (p) => scheduleContractsRegenerate(p));
|
|
4429
4550
|
contractsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p));
|
|
4430
|
-
const formsWatcher = import_chokidar.default.watch((0,
|
|
4551
|
+
const formsWatcher = import_chokidar.default.watch((0, import_node_path17.join)(config.codegen.cwd, config.forms.watch), {
|
|
4431
4552
|
ignoreInitial: true,
|
|
4432
4553
|
persistent: true,
|
|
4433
4554
|
awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
|
|
@@ -4455,9 +4576,13 @@ async function watch(config, onChange) {
|
|
|
4455
4576
|
|
|
4456
4577
|
// src/nest/module.ts
|
|
4457
4578
|
var CODEGEN_MODULE_OPTIONS = Symbol("NESTJS_CODEGEN_MODULE_OPTIONS");
|
|
4579
|
+
function isProductionEnv(env) {
|
|
4580
|
+
return (env ?? "").trim().toLowerCase() === "production";
|
|
4581
|
+
}
|
|
4458
4582
|
function shouldRun(options, env) {
|
|
4459
4583
|
if (options.enabled !== void 0) return options.enabled;
|
|
4460
|
-
|
|
4584
|
+
if (isProductionEnv(env)) return options.runInProduction === true;
|
|
4585
|
+
return true;
|
|
4461
4586
|
}
|
|
4462
4587
|
var NestjsCodegenService = class {
|
|
4463
4588
|
constructor(options) {
|
|
@@ -4466,11 +4591,21 @@ var NestjsCodegenService = class {
|
|
|
4466
4591
|
logger = new import_common.Logger("NestjsCodegen");
|
|
4467
4592
|
watcher = null;
|
|
4468
4593
|
async onApplicationBootstrap() {
|
|
4469
|
-
if (!shouldRun(this.options, process.env.NODE_ENV))
|
|
4470
|
-
|
|
4594
|
+
if (!shouldRun(this.options, process.env.NODE_ENV)) {
|
|
4595
|
+
if (this.options.enabled === void 0 && isProductionEnv(process.env.NODE_ENV)) {
|
|
4596
|
+
this.logger.log("Skipped in production (set runInProduction: true to override).");
|
|
4597
|
+
}
|
|
4598
|
+
return;
|
|
4599
|
+
}
|
|
4600
|
+
const {
|
|
4601
|
+
enabled: _enabled,
|
|
4602
|
+
runInProduction: _runInProduction,
|
|
4603
|
+
cwd,
|
|
4604
|
+
...userConfig
|
|
4605
|
+
} = this.options;
|
|
4471
4606
|
try {
|
|
4472
4607
|
const config = resolveConfig(userConfig, cwd ?? process.cwd());
|
|
4473
|
-
this.watcher = await watch(config);
|
|
4608
|
+
this.watcher = await watch(config, void 0, { deferInitialGenerate: true });
|
|
4474
4609
|
this.logger.log(`Watching ${config.contracts.glob} \u2192 ${config.codegen.outDir}`);
|
|
4475
4610
|
} catch (err) {
|
|
4476
4611
|
this.logger.warn(
|