@dudousxd/nestjs-codegen 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { U as UserConfig, R as ResolvedConfig, a as RouteDescriptor, S as SchemaNode, b as RenderContext, c as SchemaModule, d as RenderedModule, e as ResolvedFormsConfig, V as ValidationAdapter, C as CodegenExtension, E as ExtensionContext, f as SerializationMode } from './index-LQNP7Ms3.cjs';
2
- export { A as AdapterUsage, g as ContractDescriptor, h as ContractSource, i as ControllerRef, N as NumberCheck, j as ScopeConfig, k as StringCheck, T as TypeRef, l as ValidationOption, r as resolveAdapter } from './index-LQNP7Ms3.cjs';
1
+ import { U as UserConfig, R as ResolvedConfig, a as RouteDescriptor, S as SchemaNode, b as RenderContext, c as SchemaModule, d as RenderedModule, e as ResolvedFormsConfig, V as ValidationAdapter, C as CodegenExtension, E as ExtensionContext, f as SerializationMode } from './index-DCF9QSPY.cjs';
2
+ export { A as AdapterUsage, g as ContractDescriptor, h as ContractSource, i as ControllerRef, N as NumberCheck, j as ScopeConfig, k as StringCheck, T as TypeRef, l as ValidationOption, r as resolveAdapter } from './index-DCF9QSPY.cjs';
3
3
  import { ClassDeclaration, SourceFile, Project } from 'ts-morph';
4
4
 
5
5
  declare function defineConfig(c: UserConfig): UserConfig;
@@ -81,6 +81,15 @@ interface CodegenManifest {
81
81
  configKeyHashes?: Record<string, string>;
82
82
  /** Generated output files, relative to `outDir`, recorded after the last run. */
83
83
  files: string[];
84
+ /**
85
+ * Extra input files an extension read that the host's globs don't cover
86
+ * (`ExtensionContext.trackInput`), relative to `codegen.cwd`. Recorded like a
87
+ * compiler depfile: this run's dependencies become the next run's hash
88
+ * inputs, so editing one of them invalidates the manifest. Absent on
89
+ * manifests written before this field existed — treated as "none", which is
90
+ * the pre-existing (under-invalidating) behavior.
91
+ */
92
+ extraInputs?: string[];
84
93
  }
85
94
 
86
95
  /**
@@ -373,6 +382,6 @@ interface FastDiscoveryOptions {
373
382
  }
374
383
  declare function discoverContractsFast(opts: FastDiscoveryOptions): Promise<RouteDescriptor[]>;
375
384
 
376
- declare const VERSION = "0.16.0";
385
+ declare const VERSION = "0.17.0";
377
386
 
378
387
  export { type ChainModuleRendererOptions, CodegenError, type CodegenManifest, ConfigError, DriftGuardError, type EntryPoint, type FastDiscoveryOptions, type JsonSchema, type MocksEmitOptions, type OpenApiDocument, type OpenApiEmitOptions, type OpenApiInfo, RenderContext, RenderedModule, ResolvedConfig, RouteDescriptor, SchemaModule, SchemaNode, type TsTypeContext, UserConfig, VERSION, ValidationAdapter, type WatchOptions, type Watcher, acquireLock, buildMocksFile, buildOpenApiSpec, createChainModuleRenderer, defineConfig, discoverContractsFast, emitApi, emitForms, emitMocks, emitOpenApi, emitRoutes, extractSchemaFromDto, generate, loadConfig, renderTsType, resolveConfig, schemaModuleToJsonSchema, schemaNodeToJsonSchema, toObjectKey, typeNameFor, watch };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { U as UserConfig, R as ResolvedConfig, a as RouteDescriptor, S as SchemaNode, b as RenderContext, c as SchemaModule, d as RenderedModule, e as ResolvedFormsConfig, V as ValidationAdapter, C as CodegenExtension, E as ExtensionContext, f as SerializationMode } from './index-LQNP7Ms3.js';
2
- export { A as AdapterUsage, g as ContractDescriptor, h as ContractSource, i as ControllerRef, N as NumberCheck, j as ScopeConfig, k as StringCheck, T as TypeRef, l as ValidationOption, r as resolveAdapter } from './index-LQNP7Ms3.js';
1
+ import { U as UserConfig, R as ResolvedConfig, a as RouteDescriptor, S as SchemaNode, b as RenderContext, c as SchemaModule, d as RenderedModule, e as ResolvedFormsConfig, V as ValidationAdapter, C as CodegenExtension, E as ExtensionContext, f as SerializationMode } from './index-DCF9QSPY.js';
2
+ export { A as AdapterUsage, g as ContractDescriptor, h as ContractSource, i as ControllerRef, N as NumberCheck, j as ScopeConfig, k as StringCheck, T as TypeRef, l as ValidationOption, r as resolveAdapter } from './index-DCF9QSPY.js';
3
3
  import { ClassDeclaration, SourceFile, Project } from 'ts-morph';
4
4
 
5
5
  declare function defineConfig(c: UserConfig): UserConfig;
@@ -81,6 +81,15 @@ interface CodegenManifest {
81
81
  configKeyHashes?: Record<string, string>;
82
82
  /** Generated output files, relative to `outDir`, recorded after the last run. */
83
83
  files: string[];
84
+ /**
85
+ * Extra input files an extension read that the host's globs don't cover
86
+ * (`ExtensionContext.trackInput`), relative to `codegen.cwd`. Recorded like a
87
+ * compiler depfile: this run's dependencies become the next run's hash
88
+ * inputs, so editing one of them invalidates the manifest. Absent on
89
+ * manifests written before this field existed — treated as "none", which is
90
+ * the pre-existing (under-invalidating) behavior.
91
+ */
92
+ extraInputs?: string[];
84
93
  }
85
94
 
86
95
  /**
@@ -373,6 +382,6 @@ interface FastDiscoveryOptions {
373
382
  }
374
383
  declare function discoverContractsFast(opts: FastDiscoveryOptions): Promise<RouteDescriptor[]>;
375
384
 
376
- declare const VERSION = "0.16.0";
385
+ declare const VERSION = "0.17.0";
377
386
 
378
387
  export { type ChainModuleRendererOptions, CodegenError, type CodegenManifest, ConfigError, DriftGuardError, type EntryPoint, type FastDiscoveryOptions, type JsonSchema, type MocksEmitOptions, type OpenApiDocument, type OpenApiEmitOptions, type OpenApiInfo, RenderContext, RenderedModule, ResolvedConfig, RouteDescriptor, SchemaModule, SchemaNode, type TsTypeContext, UserConfig, VERSION, ValidationAdapter, type WatchOptions, type Watcher, acquireLock, buildMocksFile, buildOpenApiSpec, createChainModuleRenderer, defineConfig, discoverContractsFast, emitApi, emitForms, emitMocks, emitOpenApi, emitRoutes, extractSchemaFromDto, generate, loadConfig, renderTsType, resolveConfig, schemaModuleToJsonSchema, schemaNodeToJsonSchema, toObjectKey, typeNameFor, watch };
package/dist/index.js CHANGED
@@ -519,9 +519,10 @@ function inferExpressionType(node) {
519
519
 
520
520
  // src/emit/emit-api.ts
521
521
  import { mkdir, writeFile } from "fs/promises";
522
- import { isAbsolute as isAbsolute2, join as join4, relative as relative3 } from "path";
522
+ import { isAbsolute as isAbsolute2, join as join4, relative as relative4 } from "path";
523
523
 
524
524
  // src/extension/registry.ts
525
+ import { relative as relative3, resolve as resolve2 } from "path";
525
526
  import { Project as Project2 } from "ts-morph";
526
527
  function resolveApiSlots(extensions) {
527
528
  let layer;
@@ -554,7 +555,7 @@ function mergeExclusive(target, incoming, {
554
555
  target.set(key, { value, owner });
555
556
  }
556
557
  }
557
- function createExtensionContext(config, getRoutes) {
558
+ function createExtensionContext(config, getRoutes, trackedInputs) {
558
559
  let project;
559
560
  return {
560
561
  cwd: config.codegen.cwd,
@@ -563,6 +564,13 @@ function createExtensionContext(config, getRoutes) {
563
564
  get routes() {
564
565
  return getRoutes();
565
566
  },
567
+ trackInput(...paths) {
568
+ if (!trackedInputs) return;
569
+ for (const path of paths) {
570
+ if (!path) continue;
571
+ trackedInputs.add(relative3(config.codegen.cwd, resolve2(config.codegen.cwd, path)));
572
+ }
573
+ },
566
574
  project() {
567
575
  if (!project) {
568
576
  project = new Project2({
@@ -730,7 +738,7 @@ function rawResponseType(c, outDir) {
730
738
  return c.contractSource.response;
731
739
  }
732
740
  if (c.controllerRef) {
733
- let relPath = relative3(outDir, c.controllerRef.filePath).replace(/\.ts$/, "");
741
+ let relPath = relative4(outDir, c.controllerRef.filePath).replace(/\.ts$/, "");
734
742
  if (!relPath.startsWith(".")) relPath = `./${relPath}`;
735
743
  return `Awaited<ReturnType<import('${relPath}').${c.controllerRef.className}['${c.controllerRef.methodName}']>>`;
736
744
  }
@@ -1056,6 +1064,11 @@ function buildApiFile(routes, outDir, opts = {}) {
1056
1064
  config: {},
1057
1065
  project: () => {
1058
1066
  throw new Error("ExtensionContext.project() is unavailable in standalone emitApi.");
1067
+ },
1068
+ // No manifest is written on the standalone path, so there is nothing for
1069
+ // tracked dependencies to be recorded into — accept and discard them
1070
+ // rather than making every caller supply a sink.
1071
+ trackInput: () => {
1059
1072
  }
1060
1073
  };
1061
1074
  const importsByFile = /* @__PURE__ */ new Map();
@@ -1112,7 +1125,7 @@ function buildApiFile(routes, outDir, opts = {}) {
1112
1125
  for (const [filePath, names] of importsByFile) {
1113
1126
  let relPath;
1114
1127
  if (isAbsolute2(filePath)) {
1115
- relPath = relative3(outDir, filePath).replace(/\.ts$/, "");
1128
+ relPath = relative4(outDir, filePath).replace(/\.ts$/, "");
1116
1129
  if (!relPath.startsWith(".")) relPath = `./${relPath}`;
1117
1130
  } else {
1118
1131
  relPath = filePath;
@@ -1226,7 +1239,7 @@ async function emitCache(pages, outDir) {
1226
1239
 
1227
1240
  // src/emit/emit-forms.ts
1228
1241
  import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
1229
- import { join as join6, relative as relative4 } from "path";
1242
+ import { join as join6, relative as relative5 } from "path";
1230
1243
  async function emitForms(routes, outDir, config, adapter) {
1231
1244
  if (config && config.enabled === false) return false;
1232
1245
  const content = buildFormsFileWithAdapter(routes, outDir, adapter, config);
@@ -1245,7 +1258,7 @@ function deriveBaseName(routeName) {
1245
1258
  return { method, full };
1246
1259
  }
1247
1260
  function relImport(outDir, filePath) {
1248
- let relPath = relative4(outDir, filePath).replace(/\.ts$/, "");
1261
+ let relPath = relative5(outDir, filePath).replace(/\.ts$/, "");
1249
1262
  if (!relPath.startsWith(".")) relPath = `./${relPath}`;
1250
1263
  return relPath;
1251
1264
  }
@@ -1996,7 +2009,7 @@ async function emitOpenApi(routes, outDir, opts = {}) {
1996
2009
 
1997
2010
  // src/emit/emit-pages.ts
1998
2011
  import { mkdir as mkdir7, writeFile as writeFile7 } from "fs/promises";
1999
- import { join as join10, relative as relative5 } from "path";
2012
+ import { join as join10, relative as relative6 } from "path";
2000
2013
  async function emitPages(pages, outDir, _options = {}) {
2001
2014
  await mkdir7(outDir, { recursive: true });
2002
2015
  const pageNameUnion = pages.length > 0 ? pages.map((p) => JSON.stringify(p.name)).join(" | ") : "never";
@@ -2036,7 +2049,7 @@ ${propsBody}
2036
2049
  `;
2037
2050
  }
2038
2051
  function buildAugmentationType(page, outDir) {
2039
- let importPath = relative5(outDir, page.absolutePath).replace(/\.(tsx?|vue|svelte)$/, "");
2052
+ let importPath = relative6(outDir, page.absolutePath).replace(/\.(tsx?|vue|svelte)$/, "");
2040
2053
  if (!importPath.startsWith(".")) {
2041
2054
  importPath = `./${importPath}`;
2042
2055
  }
@@ -2167,7 +2180,7 @@ function buildEmpty() {
2167
2180
  // src/generate-manifest.ts
2168
2181
  import { createHash } from "crypto";
2169
2182
  import { readFile as readFile2, readdir, writeFile as writeFile9 } from "fs/promises";
2170
- import { join as join12, relative as relative6 } from "path";
2183
+ import { join as join12, relative as relative7 } from "path";
2171
2184
  import fg2 from "fast-glob";
2172
2185
  var MANIFEST_FILE = ".codegen-manifest.json";
2173
2186
  var LOCK_FILE = ".watcher.lock";
@@ -2190,6 +2203,9 @@ function isManifestShape(value) {
2190
2203
  if (candidate.configKeyHashes !== void 0 && !isStringRecord(candidate.configKeyHashes)) {
2191
2204
  return false;
2192
2205
  }
2206
+ if (candidate.extraInputs !== void 0 && (!Array.isArray(candidate.extraInputs) || !candidate.extraInputs.every((entry) => typeof entry === "string"))) {
2207
+ return false;
2208
+ }
2193
2209
  if (!Array.isArray(candidate.files)) return false;
2194
2210
  return candidate.files.every((entry) => typeof entry === "string");
2195
2211
  }
@@ -2229,21 +2245,30 @@ async function discoverInputFiles(config) {
2229
2245
  const matched = await fg2(globs, { cwd, absolute: true, onlyFiles: true });
2230
2246
  return [...new Set(matched)].sort();
2231
2247
  }
2232
- async function computeInputsHash(config) {
2248
+ async function computeInputsHash(config, extraInputs = []) {
2233
2249
  const hash = createHash("sha256");
2234
2250
  hash.update(`version:${VERSION}
2235
2251
  `);
2236
2252
  hash.update(`config:${serializeConfig(config)}
2237
2253
  `);
2238
- const inputFiles = await discoverInputFiles(config);
2239
2254
  const cwd = config.codegen.cwd;
2240
- for (const file of inputFiles) {
2255
+ const globbed = await discoverInputFiles(config);
2256
+ const globbedRelative = new Set(globbed.map((file) => relative7(cwd, file)));
2257
+ const extra = [...new Set(extraInputs)].filter((file) => !globbedRelative.has(file)).sort();
2258
+ for (const file of globbed) {
2241
2259
  const contents = await readFile2(file, "utf8");
2242
- hash.update(`file:${relative6(cwd, file)}
2260
+ hash.update(`file:${relative7(cwd, file)}
2243
2261
  `);
2244
2262
  hash.update(contents);
2245
2263
  hash.update("\n");
2246
2264
  }
2265
+ for (const file of extra) {
2266
+ const contents = await readFile2(join12(cwd, file), "utf8").catch(() => null);
2267
+ hash.update(`extra:${file}
2268
+ `);
2269
+ hash.update(contents ?? "\0missing");
2270
+ hash.update("\n");
2271
+ }
2247
2272
  return hash.digest("hex");
2248
2273
  }
2249
2274
  async function readManifest(outDir) {
@@ -2257,6 +2282,7 @@ async function readManifest(outDir) {
2257
2282
  ...parsed.entryPoint ? { entryPoint: parsed.entryPoint } : {},
2258
2283
  ...parsed.configHash ? { configHash: parsed.configHash } : {},
2259
2284
  ...parsed.configKeyHashes ? { configKeyHashes: parsed.configKeyHashes } : {},
2285
+ ...parsed.extraInputs ? { extraInputs: parsed.extraInputs } : {},
2260
2286
  files: parsed.files
2261
2287
  };
2262
2288
  } catch {
@@ -2279,7 +2305,7 @@ async function listOutputFiles(outDir) {
2279
2305
  if (entry.isDirectory()) {
2280
2306
  await walk(abs);
2281
2307
  } else if (entry.isFile()) {
2282
- const rel = relative6(outDir, abs);
2308
+ const rel = relative7(outDir, abs);
2283
2309
  if (rel === MANIFEST_FILE || rel === LOCK_FILE) continue;
2284
2310
  found.push(rel);
2285
2311
  }
@@ -2316,8 +2342,8 @@ function driftGuardMessage(outDir, previousEntryPoint, currentEntryPoint, differ
2316
2342
  }
2317
2343
  async function generate(config, inputRoutes = [], entryPoint = "cli") {
2318
2344
  setCodegenDebug(config.debug);
2319
- const inputsHash = await computeInputsHash(config);
2320
2345
  const manifest = await readManifest(config.codegen.outDir);
2346
+ const inputsHash = await computeInputsHash(config, manifest?.extraInputs ?? []);
2321
2347
  if (await isManifestFresh(config.codegen.outDir, manifest, inputsHash)) {
2322
2348
  console.log(`[nestjs-codegen] ${config.codegen.outDir} up to date, skipped`);
2323
2349
  return;
@@ -2338,7 +2364,8 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
2338
2364
  }
2339
2365
  const extensions = config.extensions ?? [];
2340
2366
  let routes = inputRoutes;
2341
- const ctx = createExtensionContext(config, () => routes);
2367
+ const trackedInputs = /* @__PURE__ */ new Set();
2368
+ const ctx = createExtensionContext(config, () => routes, trackedInputs);
2342
2369
  if (extensions.length > 0) {
2343
2370
  routes = await applyTransformRoutes(routes, extensions, ctx);
2344
2371
  }
@@ -2398,13 +2425,16 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
2398
2425
  }
2399
2426
  }
2400
2427
  const outputFiles = await listOutputFiles(config.codegen.outDir);
2428
+ const extraInputs = [...trackedInputs].sort();
2429
+ const recordedHash = extraInputs.length > 0 ? await computeInputsHash(config, extraInputs) : inputsHash;
2401
2430
  await writeManifest(config.codegen.outDir, {
2402
2431
  version: VERSION,
2403
- hash: inputsHash,
2432
+ hash: recordedHash,
2404
2433
  entryPoint,
2405
2434
  configHash,
2406
2435
  configKeyHashes,
2407
- files: outputFiles
2436
+ files: outputFiles,
2437
+ ...extraInputs.length > 0 ? { extraInputs } : {}
2408
2438
  });
2409
2439
  }
2410
2440
 
@@ -2414,7 +2444,7 @@ import { join as join16 } from "path";
2414
2444
  import chokidar from "chokidar";
2415
2445
 
2416
2446
  // src/discovery/contracts-fast.ts
2417
- import { join as join14, resolve as resolve3 } from "path";
2447
+ import { join as join14, resolve as resolve4 } from "path";
2418
2448
  import fg3 from "fast-glob";
2419
2449
  import {
2420
2450
  Node as Node9,
@@ -2434,7 +2464,7 @@ import {
2434
2464
 
2435
2465
  // src/discovery/type-ref-resolution.ts
2436
2466
  import { readFileSync } from "fs";
2437
- import { dirname as dirname3, resolve as resolve2 } from "path";
2467
+ import { dirname as dirname3, resolve as resolve3 } from "path";
2438
2468
  import {
2439
2469
  Node as Node2
2440
2470
  } from "ts-morph";
@@ -2491,9 +2521,9 @@ function resolveModuleSpecifier(moduleSpecifier, sourceFile, project) {
2491
2521
  const dir = dirname3(sourceFile.getFilePath());
2492
2522
  const noExt = moduleSpecifier.replace(/\.(js|ts)$/, "");
2493
2523
  return [
2494
- resolve2(dir, `${noExt}.ts`),
2495
- resolve2(dir, `${moduleSpecifier}.ts`),
2496
- resolve2(dir, moduleSpecifier, "index.ts")
2524
+ resolve3(dir, `${noExt}.ts`),
2525
+ resolve3(dir, `${moduleSpecifier}.ts`),
2526
+ resolve3(dir, moduleSpecifier, "index.ts")
2497
2527
  ];
2498
2528
  }
2499
2529
  const ctx = _ctxFor(project);
@@ -2514,8 +2544,8 @@ function resolveModuleSpecifier(moduleSpecifier, sourceFile, project) {
2514
2544
  const rest = moduleSpecifier.slice(prefix.length);
2515
2545
  const candidates = [];
2516
2546
  for (const mapping of mappings) {
2517
- const resolved = resolve2(baseUrl, mapping.replace("*", rest));
2518
- candidates.push(`${resolved}.ts`, resolve2(resolved, "index.ts"));
2547
+ const resolved = resolve3(baseUrl, mapping.replace("*", rest));
2548
+ candidates.push(`${resolved}.ts`, resolve3(resolved, "index.ts"));
2519
2549
  }
2520
2550
  dbg(" resolved candidates:", candidates);
2521
2551
  return candidates;
@@ -4379,7 +4409,7 @@ async function discoverContractsFast(opts) {
4379
4409
  return extractAllRoutes(project);
4380
4410
  }
4381
4411
  function resolveTsconfigPath(cwd, tsconfig) {
4382
- return tsconfig ? resolve3(tsconfig) : join14(cwd, "tsconfig.json");
4412
+ return tsconfig ? resolve4(tsconfig) : join14(cwd, "tsconfig.json");
4383
4413
  }
4384
4414
  function createDiscoveryProject(tsconfigPath) {
4385
4415
  try {
@@ -4465,7 +4495,7 @@ var PersistentDiscovery = class _PersistentDiscovery {
4465
4495
  async rediscover(changedPaths) {
4466
4496
  if (changedPaths) {
4467
4497
  for (const p of changedPaths) {
4468
- const abs = resolve3(p);
4498
+ const abs = resolve4(p);
4469
4499
  const sf = this.project.getSourceFile(abs);
4470
4500
  if (sf) {
4471
4501
  await sf.refreshFromFileSystem();
@@ -5056,7 +5086,7 @@ function createChainModuleRenderer(opts) {
5056
5086
  }
5057
5087
 
5058
5088
  // src/index.ts
5059
- var VERSION = "0.16.0";
5089
+ var VERSION = "0.17.0";
5060
5090
  export {
5061
5091
  CodegenError,
5062
5092
  ConfigError,