@dudousxd/nestjs-codegen 0.26.0 → 0.27.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.
@@ -109,11 +109,32 @@ function validateUserConfig(userConfig) {
109
109
  "validation adapter is required \u2014 install @dudousxd/nestjs-codegen-zod and pass zodAdapter, or use @dudousxd/nestjs-codegen-valibot / -arktype"
110
110
  );
111
111
  }
112
- if (userConfig.pages && typeof userConfig.pages.glob !== "string") {
112
+ if (userConfig.pages && !isGlobInput(userConfig.pages.glob)) {
113
113
  throw new ConfigError(
114
- "Config validation failed: `pages.glob` must be a string when `pages` is set"
114
+ "Config validation failed: `pages.glob` must be a string or a non-empty array of strings when `pages` is set"
115
115
  );
116
116
  }
117
+ for (const [scope, scopeConfig] of Object.entries(userConfig.pages?.scopes ?? {})) {
118
+ if (scope === "default") {
119
+ throw new ConfigError(
120
+ 'Config validation failed: `pages.scopes` cannot declare "default" \u2014 the default app is `pages.glob` (InertiaModule.forRoot).'
121
+ );
122
+ }
123
+ if (!isGlobInput(scopeConfig?.glob)) {
124
+ throw new ConfigError(
125
+ `Config validation failed: \`pages.scopes.${scope}.glob\` must be a string or a non-empty array of strings`
126
+ );
127
+ }
128
+ }
129
+ if (userConfig.scopes && Object.keys(userConfig.scopes).length > 0) {
130
+ console.warn(
131
+ "[nestjs-codegen] The top-level `scopes` option has no effect and is deprecated. For several Inertia apps use `pages.scopes` (one entry per InertiaModule.forFeature scope); for several controller sets use one `contracts.glob` (e.g. a brace glob)."
132
+ );
133
+ }
134
+ }
135
+ function isGlobInput(value) {
136
+ if (typeof value === "string") return true;
137
+ return Array.isArray(value) && value.length > 0 && value.every((glob) => typeof glob === "string");
117
138
  }
118
139
  function applyDefaults(userConfig, cwd) {
119
140
  validateUserConfig(userConfig);
@@ -141,7 +162,17 @@ function applyDefaults(userConfig, cwd) {
141
162
  pages: userConfig.pages ? {
142
163
  glob: userConfig.pages.glob,
143
164
  propsExport: userConfig.pages.propsExport ?? "ComponentProps",
144
- componentNameStrategy: userConfig.pages.componentNameStrategy ?? "relative-no-ext"
165
+ componentNameStrategy: userConfig.pages.componentNameStrategy ?? "relative-no-ext",
166
+ scopes: Object.fromEntries(
167
+ Object.entries(userConfig.pages.scopes ?? {}).map(([scope, scopeConfig]) => [
168
+ scope,
169
+ {
170
+ glob: scopeConfig.glob,
171
+ prefix: scopeConfig.prefix ?? `${scope}/`,
172
+ componentNameStrategy: scopeConfig.componentNameStrategy ?? userConfig.pages?.componentNameStrategy ?? "relative-no-ext"
173
+ }
174
+ ])
175
+ )
145
176
  } : null,
146
177
  contracts: {
147
178
  glob: userConfig.contracts?.glob ?? "src/**/*.controller.ts",
@@ -283,10 +314,11 @@ function resolveModuleSpecifier(moduleSpecifier, sourceFile, project) {
283
314
  }
284
315
  return [];
285
316
  }
286
- function resolveImportedType(name, sourceFile, project) {
317
+ function resolveImportedType(name, sourceFile, project, seen = /* @__PURE__ */ new Set()) {
287
318
  for (const importDecl of sourceFile.getImportDeclarations()) {
288
- const namedImport = importDecl.getNamedImports().find((n) => n.getName() === name);
319
+ const namedImport = importDecl.getNamedImports().find((n) => (n.getAliasNode()?.getText() ?? n.getName()) === name);
289
320
  if (!namedImport) continue;
321
+ const sourceName = namedImport.getName();
290
322
  const moduleSpecifier = importDecl.getModuleSpecifierValue();
291
323
  const candidates = resolveModuleSpecifier(moduleSpecifier, sourceFile, project);
292
324
  for (const candidate of candidates) {
@@ -298,17 +330,17 @@ function resolveImportedType(name, sourceFile, project) {
298
330
  continue;
299
331
  }
300
332
  }
301
- const result = findTypeInFile(name, importedFile);
333
+ const result = findTypeInFile(sourceName, importedFile);
302
334
  if (result) return result;
303
- const viaReExport = resolveReExportedType(name, importedFile, project, /* @__PURE__ */ new Set());
335
+ const viaReExport = resolveReExportedType(sourceName, importedFile, project, /* @__PURE__ */ new Set());
304
336
  if (viaReExport) return viaReExport;
305
337
  }
306
338
  if (candidates.length === 0) {
307
- const viaCompiler = resolveBareSpecifierType(name, importDecl, project);
339
+ const viaCompiler = resolveBareSpecifierType(sourceName, importDecl, project);
308
340
  if (viaCompiler) return viaCompiler;
309
341
  }
310
342
  }
311
- return resolveReExportedType(name, sourceFile, project, /* @__PURE__ */ new Set());
343
+ return resolveReExportedType(name, sourceFile, project, seen);
312
344
  }
313
345
  function resolveBareSpecifierType(name, importDecl, project) {
314
346
  let target;
@@ -323,9 +355,9 @@ function resolveBareSpecifierType(name, importDecl, project) {
323
355
  return resolveReExportedType(name, target, project, /* @__PURE__ */ new Set());
324
356
  }
325
357
  function resolveReExportedType(name, file, project, seen) {
326
- const filePath = file.getFilePath();
327
- if (seen.has(filePath)) return null;
328
- seen.add(filePath);
358
+ const visit = `${file.getFilePath()}#${name}`;
359
+ if (seen.has(visit)) return null;
360
+ seen.add(visit);
329
361
  for (const exportDecl of file.getExportDeclarations()) {
330
362
  const moduleSpecifier = exportDecl.getModuleSpecifierValue();
331
363
  const namedExports = exportDecl.getNamedExports();
@@ -347,7 +379,7 @@ function resolveReExportedType(name, file, project, seen) {
347
379
  const sourceName = namedExports.find((n) => (n.getAliasNode()?.getText() ?? n.getName()) === name)?.getName() ?? name;
348
380
  const local = findTypeInFile(sourceName, file);
349
381
  if (local) return local;
350
- const imported = resolveImportedType(sourceName, file, project);
382
+ const imported = resolveImportedType(sourceName, file, project, seen);
351
383
  if (imported) return imported;
352
384
  }
353
385
  return null;
@@ -3365,32 +3397,86 @@ function extractFromSourceFile(sourceFile, project) {
3365
3397
  return routes;
3366
3398
  }
3367
3399
 
3368
- // src/generate.ts
3369
- var import_promises11 = require("fs/promises");
3370
- var import_node_path15 = require("path");
3371
-
3372
3400
  // src/discovery/pages.ts
3373
3401
  var import_promises2 = require("fs/promises");
3374
3402
  var import_node_path4 = require("path");
3375
3403
  var import_fast_glob2 = __toESM(require("fast-glob"), 1);
3376
3404
  var NON_PAGE_FILE_RE = /\.(?:test|spec|stories|story)\.(?:tsx?|jsx?|vue|svelte)$/i;
3405
+ var MAGIC_SEGMENT_RE = /[*?{}[\]()!]/;
3406
+ function globStaticBase(glob) {
3407
+ const segments = glob.split("/");
3408
+ const staticSegments = [];
3409
+ for (const segment of segments.slice(0, -1)) {
3410
+ if (MAGIC_SEGMENT_RE.test(segment)) break;
3411
+ staticSegments.push(segment);
3412
+ }
3413
+ return staticSegments.join("/");
3414
+ }
3377
3415
  async function discoverPages(opts) {
3378
- const allFiles = await (0, import_fast_glob2.default)(opts.glob, { cwd: opts.cwd, absolute: true });
3379
- const files = allFiles.filter((f) => !NON_PAGE_FILE_RE.test(f));
3380
- files.sort();
3381
- const globStatic = opts.glob.split("*")[0]?.replace(/\/$/, "") ?? "";
3382
- const pagesBase = (0, import_node_path4.join)(opts.cwd, globStatic);
3416
+ const globs = typeof opts.glob === "string" ? [opts.glob] : opts.glob;
3417
+ const prefix = opts.prefix ?? "";
3418
+ const scope = opts.scope ?? "default";
3419
+ const seenFiles = /* @__PURE__ */ new Set();
3383
3420
  const out = [];
3384
- for (const file of files) {
3385
- const rel = (0, import_node_path4.relative)(opts.cwd, file);
3386
- const nameRel = (0, import_node_path4.relative)(pagesBase, file);
3387
- const name = computeName(nameRel, opts.componentNameStrategy);
3388
- const source = await (0, import_promises2.readFile)(file, "utf8");
3389
- const propsSource = extractPropsSource(source, opts.propsExport);
3390
- out.push({ name, absolutePath: file, relativePath: rel, propsSource });
3421
+ for (const glob of globs) {
3422
+ const files = (await (0, import_fast_glob2.default)(glob, { cwd: opts.cwd, absolute: true })).filter((f) => !NON_PAGE_FILE_RE.test(f) && !seenFiles.has(f)).sort();
3423
+ const pagesBase = (0, import_node_path4.join)(opts.cwd, globStaticBase(glob));
3424
+ for (const file of files) {
3425
+ seenFiles.add(file);
3426
+ const rel = (0, import_node_path4.relative)(opts.cwd, file);
3427
+ const nameRel = (0, import_node_path4.relative)(pagesBase, file).split(import_node_path4.sep).join("/");
3428
+ const name = `${prefix}${computeName(nameRel, opts.componentNameStrategy)}`;
3429
+ const source = await (0, import_promises2.readFile)(file, "utf8");
3430
+ const propsSource = extractPropsSource(source, opts.propsExport);
3431
+ out.push({ name, absolutePath: file, relativePath: rel, propsSource, scope });
3432
+ }
3433
+ }
3434
+ assertUniqueNames(out);
3435
+ return out;
3436
+ }
3437
+ async function discoverPageScopes(config) {
3438
+ const pages = config.pages;
3439
+ if (!pages) return [];
3440
+ const common = { cwd: config.codegen.cwd, propsExport: pages.propsExport };
3441
+ const out = await discoverPages({
3442
+ ...common,
3443
+ glob: pages.glob,
3444
+ componentNameStrategy: pages.componentNameStrategy
3445
+ });
3446
+ for (const [scope, scopeConfig] of Object.entries(pages.scopes ?? {})) {
3447
+ out.push(
3448
+ ...await discoverPages({
3449
+ ...common,
3450
+ glob: scopeConfig.glob,
3451
+ componentNameStrategy: scopeConfig.componentNameStrategy,
3452
+ prefix: scopeConfig.prefix,
3453
+ scope
3454
+ })
3455
+ );
3391
3456
  }
3457
+ assertUniqueNames(out);
3392
3458
  return out;
3393
3459
  }
3460
+ function allPageGlobs(config) {
3461
+ if (!config.pages) return [];
3462
+ const asList = (glob) => typeof glob === "string" ? [glob] : glob;
3463
+ return [
3464
+ ...asList(config.pages.glob),
3465
+ ...Object.values(config.pages.scopes ?? {}).flatMap((scope) => asList(scope.glob))
3466
+ ];
3467
+ }
3468
+ function assertUniqueNames(pages) {
3469
+ const byName = /* @__PURE__ */ new Map();
3470
+ for (const page of pages) {
3471
+ const previous = byName.get(page.name);
3472
+ if (previous) {
3473
+ throw new Error(
3474
+ `Two Inertia pages resolve to the component name "${page.name}": ${previous.relativePath} (scope "${previous.scope}") and ${page.relativePath} (scope "${page.scope}"). Give one of them another name, directory or scope prefix.`
3475
+ );
3476
+ }
3477
+ byName.set(page.name, page);
3478
+ }
3479
+ }
3394
3480
  function computeName(rel, strat) {
3395
3481
  if (typeof strat === "function") return strat(rel);
3396
3482
  const noExt = rel.replace(/\.(tsx?|vue|svelte)$/, "");
@@ -3421,6 +3507,10 @@ function extractPropsSource(source, exportName) {
3421
3507
  return source.slice(start);
3422
3508
  }
3423
3509
 
3510
+ // src/generate.ts
3511
+ var import_promises11 = require("fs/promises");
3512
+ var import_node_path15 = require("path");
3513
+
3424
3514
  // src/discovery/shared-props.ts
3425
3515
  var import_node_path5 = require("path");
3426
3516
  var import_ts_morph12 = require("ts-morph");
@@ -4055,28 +4145,28 @@ function renderFetcherRequest(req, binaryResponse) {
4055
4145
  }
4056
4146
  function emitReqHelper() {
4057
4147
  return [
4058
- "/** Awaitable request handle. `await api.x.y({...})` runs the fetch; extensions add query/mutation helpers. */",
4059
- "type __Req<R> = {",
4060
- " then<T1 = R, T2 = never>(",
4148
+ "/** An awaitable request handle: a lazy, memoized `Promise` that also carries `fetch()` and the members extensions add. */",
4149
+ "export type RequestHandle<R, M = {}> = Promise<R> & { fetch(): Promise<R> } & M;",
4150
+ "class __Request<R> extends Promise<R> {",
4151
+ " static override get [Symbol.species](): PromiseConstructor {",
4152
+ " return Promise;",
4153
+ " }",
4154
+ " private __run: () => Promise<R>;",
4155
+ " private __p: Promise<R> | undefined;",
4156
+ " constructor(run: () => Promise<R>) {",
4157
+ " super(() => {});",
4158
+ " this.__run = run;",
4159
+ " }",
4160
+ " override then<T1 = R, T2 = never>(",
4061
4161
  " onfulfilled?: ((value: R) => T1 | PromiseLike<T1>) | null,",
4062
4162
  " onrejected?: ((reason: unknown) => T2 | PromiseLike<T2>) | null,",
4063
- " ): Promise<T1 | T2>;",
4064
- " catch<T = never>(onrejected?: ((reason: unknown) => T | PromiseLike<T>) | null): Promise<R | T>;",
4065
- " finally(onfinally?: (() => void) | null): Promise<R>;",
4066
- " fetch(): Promise<R>;",
4067
- "};",
4068
- "function __req<R>(run: () => Promise<R>): __Req<R> {",
4069
- " let __p: Promise<R> | undefined;",
4070
- " const __promise = () => {",
4071
- " __p ??= run();",
4072
- " return __p;",
4073
- " };",
4074
- " return {",
4075
- " then: (onfulfilled, onrejected) => __promise().then(onfulfilled, onrejected),",
4076
- " catch: (onrejected) => __promise().catch(onrejected),",
4077
- " finally: (onfinally) => __promise().finally(onfinally),",
4078
- " fetch: run,",
4079
- " };",
4163
+ " ): Promise<T1 | T2> {",
4164
+ " this.__p ??= this.__run();",
4165
+ " return this.__p.then(onfulfilled, onrejected);",
4166
+ " }",
4167
+ "}",
4168
+ "function __req<R, M extends object = {}>(run: () => Promise<R>, members?: M): RequestHandle<R, M> {",
4169
+ " return Object.assign(new __Request<R>(run), { fetch: run }, members) as RequestHandle<R, M>;",
4080
4170
  "}",
4081
4171
  ""
4082
4172
  ];
@@ -4094,18 +4184,26 @@ function emitFilterFieldGuard() {
4094
4184
  ];
4095
4185
  }
4096
4186
  function renderLeaf(pad, objKey, req, requestExpr, members, streamExpr) {
4097
- const lines = [`${pad}${objKey}: (input?: ${req.inputType}) => ({`];
4098
- lines.push(`${pad} ...__req<${req.responseType}>(() => ${requestExpr}),`);
4187
+ const memberLines = [];
4099
4188
  if (req.filterFieldsExpr) {
4100
- lines.push(`${pad} filterFields: ${req.filterFieldsExpr},`);
4189
+ memberLines.push(`${pad} filterFields: ${req.filterFieldsExpr},`);
4101
4190
  }
4102
4191
  if (streamExpr) {
4103
- lines.push(`${pad} stream: () => ${streamExpr},`);
4192
+ memberLines.push(`${pad} stream: () => ${streamExpr},`);
4104
4193
  }
4105
4194
  for (const [name, value] of Object.entries(members)) {
4106
- lines.push(`${pad} ${name}: ${value},`);
4107
- }
4108
- lines.push(`${pad}}),`);
4195
+ memberLines.push(`${pad} ${name}: ${value},`);
4196
+ }
4197
+ const body = [requestExpr, ...memberLines].join("\n");
4198
+ const param = /\binput\b/.test(body) ? `input?: ${req.inputType}` : "";
4199
+ const lines = [`${pad}${objKey}: (${param}) =>`];
4200
+ if (memberLines.length === 0) {
4201
+ lines.push(`${pad} __req<${req.responseType}>(() => ${requestExpr}),`);
4202
+ return lines;
4203
+ }
4204
+ lines.push(`${pad} __req(() => ${requestExpr}, {`);
4205
+ lines.push(...memberLines);
4206
+ lines.push(`${pad} }),`);
4109
4207
  return lines;
4110
4208
  }
4111
4209
  function renderStreamExpr(req) {
@@ -4239,6 +4337,23 @@ var EMPTY_PATH_NAMESPACE = [
4239
4337
  function isClientCallable(route) {
4240
4338
  return route.method.toUpperCase() !== "ALL";
4241
4339
  }
4340
+ var ROUTES_IMPORT_PLACEHOLDER = "/* @@routes-import@@ */";
4341
+ var ROUTES_EXPORTS = [
4342
+ // Called, never mentioned in prose: match the call so a comment saying "route" doesn't count.
4343
+ { name: "route", typeOnly: false },
4344
+ { name: "ROUTES", typeOnly: false },
4345
+ { name: "RouteName", typeOnly: true },
4346
+ { name: "ExtractParams", typeOnly: true },
4347
+ { name: "RouteParams", typeOnly: true }
4348
+ ];
4349
+ function finishRoutesImport(lines) {
4350
+ const body = lines.filter((l) => l !== ROUTES_IMPORT_PLACEHOLDER).join("\n");
4351
+ const used = ROUTES_EXPORTS.filter(
4352
+ ({ name }) => new RegExp(name === "route" ? "\\broute\\(" : `\\b${name}\\b`).test(body)
4353
+ ).map(({ name, typeOnly }) => typeOnly ? `type ${name}` : name);
4354
+ const importLine = used.length ? `import { ${used.join(", ")} } from './routes.js';` : null;
4355
+ return lines.flatMap((l) => l === ROUTES_IMPORT_PLACEHOLDER ? importLine ? [importLine] : [] : [l]).join("\n");
4356
+ }
4242
4357
  function buildApiFile(routes, outDir, opts = {}) {
4243
4358
  const fetcherImportPath = opts.fetcherImportPath;
4244
4359
  const serialization = opts.serialization ?? "json";
@@ -4298,9 +4413,7 @@ function buildApiFile(routes, outDir, opts = {}) {
4298
4413
  for (const imp of ext.apiHeader?.(ctx)?.imports ?? []) pushImport(imp);
4299
4414
  }
4300
4415
  lines.push(...extImports);
4301
- lines.push(
4302
- "import { route, ROUTES, type RouteName, type ExtractParams, type RouteParams } from './routes.js';"
4303
- );
4416
+ lines.push(ROUTES_IMPORT_PLACEHOLDER);
4304
4417
  const runtimeImport = fetcherImportPath ?? "@dudousxd/nestjs-client";
4305
4418
  lines.push(`import type { Fetcher } from '${runtimeImport}';`);
4306
4419
  if (serialization === "json" && contracted.length > 0) {
@@ -4351,7 +4464,7 @@ function buildApiFile(routes, outDir, opts = {}) {
4351
4464
  lines.push(...statements, "");
4352
4465
  }
4353
4466
  }
4354
- return lines.join("\n");
4467
+ return finishRoutesImport(lines);
4355
4468
  }
4356
4469
  const tree = /* @__PURE__ */ new Map();
4357
4470
  for (const r of contracted) {
@@ -4404,7 +4517,7 @@ function buildApiFile(routes, outDir, opts = {}) {
4404
4517
  lines.push(...statements, "");
4405
4518
  }
4406
4519
  }
4407
- return lines.join("\n");
4520
+ return finishRoutesImport(lines);
4408
4521
  }
4409
4522
 
4410
4523
  // src/emit/emit-cache.ts
@@ -4417,6 +4530,7 @@ async function emitCache(pages, outDir) {
4417
4530
  const s = await (0, import_promises4.stat)(p.absolutePath);
4418
4531
  return {
4419
4532
  name: p.name,
4533
+ scope: p.scope ?? "default",
4420
4534
  relativePath: p.relativePath,
4421
4535
  mtime: s.mtime.toISOString()
4422
4536
  };
@@ -5208,10 +5322,11 @@ async function emitPages(pages, outDir, _options = {}) {
5208
5322
  }).join("\n");
5209
5323
  const propsHelper = "\nexport type InertiaProps<K extends InertiaPageName> = import('@dudousxd/nestjs-inertia').InertiaPages[K];\n";
5210
5324
  const sharedPropsBlock = buildSharedPropsBlock(_options.sharedProps ?? null);
5325
+ const scopesBlock = buildScopesBlock(pages);
5211
5326
  const content = `// Generated by @dudousxd/nestjs-codegen. Do not edit.
5212
5327
 
5213
5328
  export type InertiaPageName = ${pageNameUnion};
5214
- ${propsHelper}
5329
+ ${propsHelper}${scopesBlock}
5215
5330
  declare module '@dudousxd/nestjs-inertia' {
5216
5331
  interface InertiaPages {
5217
5332
  ${augBody}
@@ -5220,6 +5335,31 @@ ${sharedPropsBlock}}
5220
5335
  `;
5221
5336
  await (0, import_promises9.writeFile)((0, import_node_path13.join)(outDir, "pages.d.ts"), content, "utf8");
5222
5337
  }
5338
+ function buildScopesBlock(pages) {
5339
+ const byScope = /* @__PURE__ */ new Map();
5340
+ for (const page of pages) {
5341
+ const scope = page.scope ?? "default";
5342
+ const names = byScope.get(scope) ?? [];
5343
+ names.push(JSON.stringify(page.name));
5344
+ byScope.set(scope, names);
5345
+ }
5346
+ if (![...byScope.keys()].some((scope) => scope !== "default")) return "";
5347
+ if (!byScope.has("default")) byScope.set("default", []);
5348
+ const scopes = ["default", ...[...byScope.keys()].filter((s) => s !== "default")];
5349
+ const body = scopes.map((scope) => {
5350
+ const key = needsQuotes(scope) ? JSON.stringify(scope) : scope;
5351
+ const names = byScope.get(scope) ?? [];
5352
+ return ` ${key}: ${names.length ? names.join(" | ") : "never"};`;
5353
+ }).join("\n");
5354
+ return `
5355
+ /** The page names of each Inertia app: \`default\` (forRoot) and every \`pages.scopes\` entry. */
5356
+ export interface InertiaScopePages {
5357
+ ${body}
5358
+ }
5359
+
5360
+ export type InertiaScopePageName<S extends keyof InertiaScopePages> = InertiaScopePages[S];
5361
+ `;
5362
+ }
5223
5363
  function buildSharedPropsBlock(sharedProps) {
5224
5364
  if (!sharedProps) return "";
5225
5365
  if (sharedProps.isImportRef) {
@@ -5401,12 +5541,7 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
5401
5541
  }
5402
5542
  if (config.pages) {
5403
5543
  const pagesConfig = config.pages;
5404
- const pages = await discoverPages({
5405
- glob: pagesConfig.glob,
5406
- cwd: config.codegen.cwd,
5407
- propsExport: pagesConfig.propsExport,
5408
- componentNameStrategy: pagesConfig.componentNameStrategy
5409
- });
5544
+ const pages = await discoverPageScopes(config);
5410
5545
  const sharedProps = discoverSharedPropsFromConfig(config);
5411
5546
  await emitPages(pages, config.codegen.outDir, {
5412
5547
  propsExport: pagesConfig.propsExport,
@@ -5581,12 +5716,17 @@ async function watch(config, onChange, options = {}) {
5581
5716
  await runInitialPass();
5582
5717
  }
5583
5718
  let pagesDebounceTimer;
5584
- const pagesGlob = config.pages?.glob ?? ".nestjs-codegen-no-pages";
5585
- const pagesWatcher = import_chokidar.default.watch((0, import_node_path17.join)(config.codegen.cwd, pagesGlob), {
5586
- ignoreInitial: true,
5587
- persistent: true,
5588
- awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
5589
- });
5719
+ const pageGlobs = allPageGlobs(config);
5720
+ const pagesWatcher = import_chokidar.default.watch(
5721
+ (pageGlobs.length ? pageGlobs : [".nestjs-codegen-no-pages"]).map(
5722
+ (glob) => (0, import_node_path17.join)(config.codegen.cwd, glob)
5723
+ ),
5724
+ {
5725
+ ignoreInitial: true,
5726
+ persistent: true,
5727
+ awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
5728
+ }
5729
+ );
5590
5730
  function schedulePagesRegenerate() {
5591
5731
  if (pagesDebounceTimer !== void 0) {
5592
5732
  clearTimeout(pagesDebounceTimer);
@@ -5666,7 +5806,7 @@ async function watch(config, onChange, options = {}) {
5666
5806
  }
5667
5807
 
5668
5808
  // src/index.ts
5669
- var VERSION = "0.26.0";
5809
+ var VERSION = "0.27.1";
5670
5810
 
5671
5811
  // src/generate-manifest.ts
5672
5812
  var MANIFEST_FILE = ".codegen-manifest.json";
@@ -5726,8 +5866,7 @@ function diffConfigKeyHashes(previous, current) {
5726
5866
  return [...keys].filter((key) => previous[key] !== current[key]).sort();
5727
5867
  }
5728
5868
  async function discoverInputFiles(config) {
5729
- const globs = [config.contracts.glob, config.forms.watch];
5730
- if (config.pages) globs.push(config.pages.glob);
5869
+ const globs = [config.contracts.glob, config.forms.watch, ...allPageGlobs(config)];
5731
5870
  const cwd = config.codegen.cwd;
5732
5871
  const matched = await (0, import_fast_glob3.default)(globs, { cwd, absolute: true, onlyFiles: true });
5733
5872
  return [...new Set(matched)].sort();