@dudousxd/nestjs-codegen 0.26.0 → 0.27.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-Bbw73Kkb.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-Bbw73Kkb.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-Dc4s883u.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-Dc4s883u.cjs';
3
3
  import { ClassDeclaration, SourceFile, Project } from 'ts-morph';
4
4
 
5
5
  declare function defineConfig(c: UserConfig): UserConfig;
@@ -382,6 +382,6 @@ interface FastDiscoveryOptions {
382
382
  }
383
383
  declare function discoverContractsFast(opts: FastDiscoveryOptions): Promise<RouteDescriptor[]>;
384
384
 
385
- declare const VERSION = "0.26.0";
385
+ declare const VERSION = "0.27.0";
386
386
 
387
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-Bbw73Kkb.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-Bbw73Kkb.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-Dc4s883u.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-Dc4s883u.js';
3
3
  import { ClassDeclaration, SourceFile, Project } from 'ts-morph';
4
4
 
5
5
  declare function defineConfig(c: UserConfig): UserConfig;
@@ -382,6 +382,6 @@ interface FastDiscoveryOptions {
382
382
  }
383
383
  declare function discoverContractsFast(opts: FastDiscoveryOptions): Promise<RouteDescriptor[]>;
384
384
 
385
- declare const VERSION = "0.26.0";
385
+ declare const VERSION = "0.27.0";
386
386
 
387
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
@@ -88,11 +88,32 @@ function validateUserConfig(userConfig) {
88
88
  "validation adapter is required \u2014 install @dudousxd/nestjs-codegen-zod and pass zodAdapter, or use @dudousxd/nestjs-codegen-valibot / -arktype"
89
89
  );
90
90
  }
91
- if (userConfig.pages && typeof userConfig.pages.glob !== "string") {
91
+ if (userConfig.pages && !isGlobInput(userConfig.pages.glob)) {
92
92
  throw new ConfigError(
93
- "Config validation failed: `pages.glob` must be a string when `pages` is set"
93
+ "Config validation failed: `pages.glob` must be a string or a non-empty array of strings when `pages` is set"
94
94
  );
95
95
  }
96
+ for (const [scope, scopeConfig] of Object.entries(userConfig.pages?.scopes ?? {})) {
97
+ if (scope === "default") {
98
+ throw new ConfigError(
99
+ 'Config validation failed: `pages.scopes` cannot declare "default" \u2014 the default app is `pages.glob` (InertiaModule.forRoot).'
100
+ );
101
+ }
102
+ if (!isGlobInput(scopeConfig?.glob)) {
103
+ throw new ConfigError(
104
+ `Config validation failed: \`pages.scopes.${scope}.glob\` must be a string or a non-empty array of strings`
105
+ );
106
+ }
107
+ }
108
+ if (userConfig.scopes && Object.keys(userConfig.scopes).length > 0) {
109
+ console.warn(
110
+ "[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)."
111
+ );
112
+ }
113
+ }
114
+ function isGlobInput(value) {
115
+ if (typeof value === "string") return true;
116
+ return Array.isArray(value) && value.length > 0 && value.every((glob) => typeof glob === "string");
96
117
  }
97
118
  function applyDefaults(userConfig, cwd) {
98
119
  validateUserConfig(userConfig);
@@ -120,7 +141,17 @@ function applyDefaults(userConfig, cwd) {
120
141
  pages: userConfig.pages ? {
121
142
  glob: userConfig.pages.glob,
122
143
  propsExport: userConfig.pages.propsExport ?? "ComponentProps",
123
- componentNameStrategy: userConfig.pages.componentNameStrategy ?? "relative-no-ext"
144
+ componentNameStrategy: userConfig.pages.componentNameStrategy ?? "relative-no-ext",
145
+ scopes: Object.fromEntries(
146
+ Object.entries(userConfig.pages.scopes ?? {}).map(([scope, scopeConfig]) => [
147
+ scope,
148
+ {
149
+ glob: scopeConfig.glob,
150
+ prefix: scopeConfig.prefix ?? `${scope}/`,
151
+ componentNameStrategy: scopeConfig.componentNameStrategy ?? userConfig.pages?.componentNameStrategy ?? "relative-no-ext"
152
+ }
153
+ ])
154
+ )
124
155
  } : null,
125
156
  contracts: {
126
157
  glob: userConfig.contracts?.glob ?? "src/**/*.controller.ts",
@@ -194,26 +225,84 @@ import { dirname as dirname4, join as join14 } from "path";
194
225
 
195
226
  // src/discovery/pages.ts
196
227
  import { readFile } from "fs/promises";
197
- import { join as join2, relative as relative2 } from "path";
228
+ import { join as join2, relative as relative2, sep as sep2 } from "path";
198
229
  import fg from "fast-glob";
199
230
  var NON_PAGE_FILE_RE = /\.(?:test|spec|stories|story)\.(?:tsx?|jsx?|vue|svelte)$/i;
231
+ var MAGIC_SEGMENT_RE = /[*?{}[\]()!]/;
232
+ function globStaticBase(glob) {
233
+ const segments = glob.split("/");
234
+ const staticSegments = [];
235
+ for (const segment of segments.slice(0, -1)) {
236
+ if (MAGIC_SEGMENT_RE.test(segment)) break;
237
+ staticSegments.push(segment);
238
+ }
239
+ return staticSegments.join("/");
240
+ }
200
241
  async function discoverPages(opts) {
201
- const allFiles = await fg(opts.glob, { cwd: opts.cwd, absolute: true });
202
- const files = allFiles.filter((f) => !NON_PAGE_FILE_RE.test(f));
203
- files.sort();
204
- const globStatic = opts.glob.split("*")[0]?.replace(/\/$/, "") ?? "";
205
- const pagesBase = join2(opts.cwd, globStatic);
242
+ const globs = typeof opts.glob === "string" ? [opts.glob] : opts.glob;
243
+ const prefix = opts.prefix ?? "";
244
+ const scope = opts.scope ?? "default";
245
+ const seenFiles = /* @__PURE__ */ new Set();
206
246
  const out = [];
207
- for (const file of files) {
208
- const rel = relative2(opts.cwd, file);
209
- const nameRel = relative2(pagesBase, file);
210
- const name = computeName(nameRel, opts.componentNameStrategy);
211
- const source = await readFile(file, "utf8");
212
- const propsSource = extractPropsSource(source, opts.propsExport);
213
- out.push({ name, absolutePath: file, relativePath: rel, propsSource });
247
+ for (const glob of globs) {
248
+ const files = (await fg(glob, { cwd: opts.cwd, absolute: true })).filter((f) => !NON_PAGE_FILE_RE.test(f) && !seenFiles.has(f)).sort();
249
+ const pagesBase = join2(opts.cwd, globStaticBase(glob));
250
+ for (const file of files) {
251
+ seenFiles.add(file);
252
+ const rel = relative2(opts.cwd, file);
253
+ const nameRel = relative2(pagesBase, file).split(sep2).join("/");
254
+ const name = `${prefix}${computeName(nameRel, opts.componentNameStrategy)}`;
255
+ const source = await readFile(file, "utf8");
256
+ const propsSource = extractPropsSource(source, opts.propsExport);
257
+ out.push({ name, absolutePath: file, relativePath: rel, propsSource, scope });
258
+ }
259
+ }
260
+ assertUniqueNames(out);
261
+ return out;
262
+ }
263
+ async function discoverPageScopes(config) {
264
+ const pages = config.pages;
265
+ if (!pages) return [];
266
+ const common = { cwd: config.codegen.cwd, propsExport: pages.propsExport };
267
+ const out = await discoverPages({
268
+ ...common,
269
+ glob: pages.glob,
270
+ componentNameStrategy: pages.componentNameStrategy
271
+ });
272
+ for (const [scope, scopeConfig] of Object.entries(pages.scopes ?? {})) {
273
+ out.push(
274
+ ...await discoverPages({
275
+ ...common,
276
+ glob: scopeConfig.glob,
277
+ componentNameStrategy: scopeConfig.componentNameStrategy,
278
+ prefix: scopeConfig.prefix,
279
+ scope
280
+ })
281
+ );
214
282
  }
283
+ assertUniqueNames(out);
215
284
  return out;
216
285
  }
286
+ function allPageGlobs(config) {
287
+ if (!config.pages) return [];
288
+ const asList = (glob) => typeof glob === "string" ? [glob] : glob;
289
+ return [
290
+ ...asList(config.pages.glob),
291
+ ...Object.values(config.pages.scopes ?? {}).flatMap((scope) => asList(scope.glob))
292
+ ];
293
+ }
294
+ function assertUniqueNames(pages) {
295
+ const byName = /* @__PURE__ */ new Map();
296
+ for (const page of pages) {
297
+ const previous = byName.get(page.name);
298
+ if (previous) {
299
+ throw new Error(
300
+ `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.`
301
+ );
302
+ }
303
+ byName.set(page.name, page);
304
+ }
305
+ }
217
306
  function computeName(rel, strat) {
218
307
  if (typeof strat === "function") return strat(rel);
219
308
  const noExt = rel.replace(/\.(tsx?|vue|svelte)$/, "");
@@ -4102,28 +4191,28 @@ function renderFetcherRequest(req, binaryResponse) {
4102
4191
  }
4103
4192
  function emitReqHelper() {
4104
4193
  return [
4105
- "/** Awaitable request handle. `await api.x.y({...})` runs the fetch; extensions add query/mutation helpers. */",
4106
- "type __Req<R> = {",
4107
- " then<T1 = R, T2 = never>(",
4194
+ "/** An awaitable request handle: a lazy, memoized `Promise` that also carries `fetch()` and the members extensions add. */",
4195
+ "export type RequestHandle<R, M = {}> = Promise<R> & { fetch(): Promise<R> } & M;",
4196
+ "class __Request<R> extends Promise<R> {",
4197
+ " static override get [Symbol.species](): PromiseConstructor {",
4198
+ " return Promise;",
4199
+ " }",
4200
+ " private __run: () => Promise<R>;",
4201
+ " private __p: Promise<R> | undefined;",
4202
+ " constructor(run: () => Promise<R>) {",
4203
+ " super(() => {});",
4204
+ " this.__run = run;",
4205
+ " }",
4206
+ " override then<T1 = R, T2 = never>(",
4108
4207
  " onfulfilled?: ((value: R) => T1 | PromiseLike<T1>) | null,",
4109
4208
  " onrejected?: ((reason: unknown) => T2 | PromiseLike<T2>) | null,",
4110
- " ): Promise<T1 | T2>;",
4111
- " catch<T = never>(onrejected?: ((reason: unknown) => T | PromiseLike<T>) | null): Promise<R | T>;",
4112
- " finally(onfinally?: (() => void) | null): Promise<R>;",
4113
- " fetch(): Promise<R>;",
4114
- "};",
4115
- "function __req<R>(run: () => Promise<R>): __Req<R> {",
4116
- " let __p: Promise<R> | undefined;",
4117
- " const __promise = () => {",
4118
- " __p ??= run();",
4119
- " return __p;",
4120
- " };",
4121
- " return {",
4122
- " then: (onfulfilled, onrejected) => __promise().then(onfulfilled, onrejected),",
4123
- " catch: (onrejected) => __promise().catch(onrejected),",
4124
- " finally: (onfinally) => __promise().finally(onfinally),",
4125
- " fetch: run,",
4126
- " };",
4209
+ " ): Promise<T1 | T2> {",
4210
+ " this.__p ??= this.__run();",
4211
+ " return this.__p.then(onfulfilled, onrejected);",
4212
+ " }",
4213
+ "}",
4214
+ "function __req<R, M extends object = {}>(run: () => Promise<R>, members?: M): RequestHandle<R, M> {",
4215
+ " return Object.assign(new __Request<R>(run), { fetch: run }, members) as RequestHandle<R, M>;",
4127
4216
  "}",
4128
4217
  ""
4129
4218
  ];
@@ -4141,18 +4230,26 @@ function emitFilterFieldGuard() {
4141
4230
  ];
4142
4231
  }
4143
4232
  function renderLeaf(pad, objKey, req, requestExpr, members, streamExpr) {
4144
- const lines = [`${pad}${objKey}: (input?: ${req.inputType}) => ({`];
4145
- lines.push(`${pad} ...__req<${req.responseType}>(() => ${requestExpr}),`);
4233
+ const memberLines = [];
4146
4234
  if (req.filterFieldsExpr) {
4147
- lines.push(`${pad} filterFields: ${req.filterFieldsExpr},`);
4235
+ memberLines.push(`${pad} filterFields: ${req.filterFieldsExpr},`);
4148
4236
  }
4149
4237
  if (streamExpr) {
4150
- lines.push(`${pad} stream: () => ${streamExpr},`);
4238
+ memberLines.push(`${pad} stream: () => ${streamExpr},`);
4151
4239
  }
4152
4240
  for (const [name, value] of Object.entries(members)) {
4153
- lines.push(`${pad} ${name}: ${value},`);
4154
- }
4155
- lines.push(`${pad}}),`);
4241
+ memberLines.push(`${pad} ${name}: ${value},`);
4242
+ }
4243
+ const body = [requestExpr, ...memberLines].join("\n");
4244
+ const param = /\binput\b/.test(body) ? `input?: ${req.inputType}` : "";
4245
+ const lines = [`${pad}${objKey}: (${param}) =>`];
4246
+ if (memberLines.length === 0) {
4247
+ lines.push(`${pad} __req<${req.responseType}>(() => ${requestExpr}),`);
4248
+ return lines;
4249
+ }
4250
+ lines.push(`${pad} __req(() => ${requestExpr}, {`);
4251
+ lines.push(...memberLines);
4252
+ lines.push(`${pad} }),`);
4156
4253
  return lines;
4157
4254
  }
4158
4255
  function renderStreamExpr(req) {
@@ -4286,6 +4383,23 @@ var EMPTY_PATH_NAMESPACE = [
4286
4383
  function isClientCallable(route) {
4287
4384
  return route.method.toUpperCase() !== "ALL";
4288
4385
  }
4386
+ var ROUTES_IMPORT_PLACEHOLDER = "/* @@routes-import@@ */";
4387
+ var ROUTES_EXPORTS = [
4388
+ // Called, never mentioned in prose: match the call so a comment saying "route" doesn't count.
4389
+ { name: "route", typeOnly: false },
4390
+ { name: "ROUTES", typeOnly: false },
4391
+ { name: "RouteName", typeOnly: true },
4392
+ { name: "ExtractParams", typeOnly: true },
4393
+ { name: "RouteParams", typeOnly: true }
4394
+ ];
4395
+ function finishRoutesImport(lines) {
4396
+ const body = lines.filter((l) => l !== ROUTES_IMPORT_PLACEHOLDER).join("\n");
4397
+ const used = ROUTES_EXPORTS.filter(
4398
+ ({ name }) => new RegExp(name === "route" ? "\\broute\\(" : `\\b${name}\\b`).test(body)
4399
+ ).map(({ name, typeOnly }) => typeOnly ? `type ${name}` : name);
4400
+ const importLine = used.length ? `import { ${used.join(", ")} } from './routes.js';` : null;
4401
+ return lines.flatMap((l) => l === ROUTES_IMPORT_PLACEHOLDER ? importLine ? [importLine] : [] : [l]).join("\n");
4402
+ }
4289
4403
  function buildApiFile(routes, outDir, opts = {}) {
4290
4404
  const fetcherImportPath = opts.fetcherImportPath;
4291
4405
  const serialization = opts.serialization ?? "json";
@@ -4345,9 +4459,7 @@ function buildApiFile(routes, outDir, opts = {}) {
4345
4459
  for (const imp of ext.apiHeader?.(ctx)?.imports ?? []) pushImport(imp);
4346
4460
  }
4347
4461
  lines.push(...extImports);
4348
- lines.push(
4349
- "import { route, ROUTES, type RouteName, type ExtractParams, type RouteParams } from './routes.js';"
4350
- );
4462
+ lines.push(ROUTES_IMPORT_PLACEHOLDER);
4351
4463
  const runtimeImport = fetcherImportPath ?? "@dudousxd/nestjs-client";
4352
4464
  lines.push(`import type { Fetcher } from '${runtimeImport}';`);
4353
4465
  if (serialization === "json" && contracted.length > 0) {
@@ -4398,7 +4510,7 @@ function buildApiFile(routes, outDir, opts = {}) {
4398
4510
  lines.push(...statements, "");
4399
4511
  }
4400
4512
  }
4401
- return lines.join("\n");
4513
+ return finishRoutesImport(lines);
4402
4514
  }
4403
4515
  const tree = /* @__PURE__ */ new Map();
4404
4516
  for (const r of contracted) {
@@ -4451,7 +4563,7 @@ function buildApiFile(routes, outDir, opts = {}) {
4451
4563
  lines.push(...statements, "");
4452
4564
  }
4453
4565
  }
4454
- return lines.join("\n");
4566
+ return finishRoutesImport(lines);
4455
4567
  }
4456
4568
 
4457
4569
  // src/emit/emit-cache.ts
@@ -4464,6 +4576,7 @@ async function emitCache(pages, outDir) {
4464
4576
  const s = await stat(p.absolutePath);
4465
4577
  return {
4466
4578
  name: p.name,
4579
+ scope: p.scope ?? "default",
4467
4580
  relativePath: p.relativePath,
4468
4581
  mtime: s.mtime.toISOString()
4469
4582
  };
@@ -5258,10 +5371,11 @@ async function emitPages(pages, outDir, _options = {}) {
5258
5371
  }).join("\n");
5259
5372
  const propsHelper = "\nexport type InertiaProps<K extends InertiaPageName> = import('@dudousxd/nestjs-inertia').InertiaPages[K];\n";
5260
5373
  const sharedPropsBlock = buildSharedPropsBlock(_options.sharedProps ?? null);
5374
+ const scopesBlock = buildScopesBlock(pages);
5261
5375
  const content = `// Generated by @dudousxd/nestjs-codegen. Do not edit.
5262
5376
 
5263
5377
  export type InertiaPageName = ${pageNameUnion};
5264
- ${propsHelper}
5378
+ ${propsHelper}${scopesBlock}
5265
5379
  declare module '@dudousxd/nestjs-inertia' {
5266
5380
  interface InertiaPages {
5267
5381
  ${augBody}
@@ -5270,6 +5384,31 @@ ${sharedPropsBlock}}
5270
5384
  `;
5271
5385
  await writeFile7(join11(outDir, "pages.d.ts"), content, "utf8");
5272
5386
  }
5387
+ function buildScopesBlock(pages) {
5388
+ const byScope = /* @__PURE__ */ new Map();
5389
+ for (const page of pages) {
5390
+ const scope = page.scope ?? "default";
5391
+ const names = byScope.get(scope) ?? [];
5392
+ names.push(JSON.stringify(page.name));
5393
+ byScope.set(scope, names);
5394
+ }
5395
+ if (![...byScope.keys()].some((scope) => scope !== "default")) return "";
5396
+ if (!byScope.has("default")) byScope.set("default", []);
5397
+ const scopes = ["default", ...[...byScope.keys()].filter((s) => s !== "default")];
5398
+ const body = scopes.map((scope) => {
5399
+ const key = needsQuotes(scope) ? JSON.stringify(scope) : scope;
5400
+ const names = byScope.get(scope) ?? [];
5401
+ return ` ${key}: ${names.length ? names.join(" | ") : "never"};`;
5402
+ }).join("\n");
5403
+ return `
5404
+ /** The page names of each Inertia app: \`default\` (forRoot) and every \`pages.scopes\` entry. */
5405
+ export interface InertiaScopePages {
5406
+ ${body}
5407
+ }
5408
+
5409
+ export type InertiaScopePageName<S extends keyof InertiaScopePages> = InertiaScopePages[S];
5410
+ `;
5411
+ }
5273
5412
  function buildSharedPropsBlock(sharedProps) {
5274
5413
  if (!sharedProps) return "";
5275
5414
  if (sharedProps.isImportRef) {
@@ -5477,8 +5616,7 @@ function diffConfigKeyHashes(previous, current) {
5477
5616
  return [...keys].filter((key) => previous[key] !== current[key]).sort();
5478
5617
  }
5479
5618
  async function discoverInputFiles(config) {
5480
- const globs = [config.contracts.glob, config.forms.watch];
5481
- if (config.pages) globs.push(config.pages.glob);
5619
+ const globs = [config.contracts.glob, config.forms.watch, ...allPageGlobs(config)];
5482
5620
  const cwd = config.codegen.cwd;
5483
5621
  const matched = await fg3(globs, { cwd, absolute: true, onlyFiles: true });
5484
5622
  return [...new Set(matched)].sort();
@@ -5609,12 +5747,7 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
5609
5747
  }
5610
5748
  if (config.pages) {
5611
5749
  const pagesConfig = config.pages;
5612
- const pages = await discoverPages({
5613
- glob: pagesConfig.glob,
5614
- cwd: config.codegen.cwd,
5615
- propsExport: pagesConfig.propsExport,
5616
- componentNameStrategy: pagesConfig.componentNameStrategy
5617
- });
5750
+ const pages = await discoverPageScopes(config);
5618
5751
  const sharedProps = discoverSharedPropsFromConfig(config);
5619
5752
  await emitPages(pages, config.codegen.outDir, {
5620
5753
  propsExport: pagesConfig.propsExport,
@@ -5789,12 +5922,17 @@ async function watch(config, onChange, options = {}) {
5789
5922
  await runInitialPass();
5790
5923
  }
5791
5924
  let pagesDebounceTimer;
5792
- const pagesGlob = config.pages?.glob ?? ".nestjs-codegen-no-pages";
5793
- const pagesWatcher = chokidar.watch(join16(config.codegen.cwd, pagesGlob), {
5794
- ignoreInitial: true,
5795
- persistent: true,
5796
- awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
5797
- });
5925
+ const pageGlobs = allPageGlobs(config);
5926
+ const pagesWatcher = chokidar.watch(
5927
+ (pageGlobs.length ? pageGlobs : [".nestjs-codegen-no-pages"]).map(
5928
+ (glob) => join16(config.codegen.cwd, glob)
5929
+ ),
5930
+ {
5931
+ ignoreInitial: true,
5932
+ persistent: true,
5933
+ awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
5934
+ }
5935
+ );
5798
5936
  function schedulePagesRegenerate() {
5799
5937
  if (pagesDebounceTimer !== void 0) {
5800
5938
  clearTimeout(pagesDebounceTimer);
@@ -5957,7 +6095,7 @@ function createChainModuleRenderer(opts) {
5957
6095
  }
5958
6096
 
5959
6097
  // src/index.ts
5960
- var VERSION = "0.26.0";
6098
+ var VERSION = "0.27.0";
5961
6099
  export {
5962
6100
  CodegenError,
5963
6101
  ConfigError,