@dudousxd/nestjs-codegen 0.6.0 → 0.7.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.
@@ -1,2 +1,2 @@
1
- export { l as ApiClientLayer, m as ApiHeaderContribution, n as ApiModuleDeps, C as CodegenExtension, o as EmittedFile, E as ExtensionContext, L as LeafModel, p as RequestModel, q as RequestShape, s as defineExtension, t as requestShape } from '../index-_qRai4M3.cjs';
1
+ export { m as ApiClientLayer, n as ApiHeaderContribution, o as ApiModuleDeps, C as CodegenExtension, p as EmittedFile, E as ExtensionContext, L as LeafModel, q as RequestModel, s as RequestShape, t as defineExtension, u as requestShape } from '../index-DgIAN5k5.cjs';
2
2
  import 'ts-morph';
@@ -1,2 +1,2 @@
1
- export { l as ApiClientLayer, m as ApiHeaderContribution, n as ApiModuleDeps, C as CodegenExtension, o as EmittedFile, E as ExtensionContext, L as LeafModel, p as RequestModel, q as RequestShape, s as defineExtension, t as requestShape } from '../index-_qRai4M3.js';
1
+ export { m as ApiClientLayer, n as ApiHeaderContribution, o as ApiModuleDeps, C as CodegenExtension, p as EmittedFile, E as ExtensionContext, L as LeafModel, q as RequestModel, s as RequestShape, t as defineExtension, u as requestShape } from '../index-DgIAN5k5.js';
2
2
  import 'ts-morph';
@@ -254,6 +254,18 @@ interface UserConfig {
254
254
  fetcher?: {
255
255
  importPath: string;
256
256
  };
257
+ /**
258
+ * How response payloads are deserialized on the client, which determines the
259
+ * generated `response` type shape.
260
+ *
261
+ * - `'json'` (default): responses cross the wire as plain JSON, so the
262
+ * generated type is wrapped in `Jsonify<...>` (e.g. `Date` → `string`).
263
+ * - `'superjson'`: responses are revived (Dates/Maps/Sets restored), so the
264
+ * raw controller return type is emitted unchanged.
265
+ *
266
+ * @default 'json'
267
+ */
268
+ serialization?: 'json' | 'superjson';
257
269
  /**
258
270
  * Typed-form schema emit (`forms.ts`). Re-exports / translates contract and
259
271
  * class-validator-decorated DTO schemas into zod schemas for client-side
@@ -319,6 +331,8 @@ interface ResolvedCodegenConfig {
319
331
  outDir: string;
320
332
  cwd: string;
321
333
  }
334
+ /** How response payloads are deserialized on the client. */
335
+ type SerializationMode = 'json' | 'superjson';
322
336
  interface ResolvedAppConfig {
323
337
  moduleEntry: string;
324
338
  tsconfig: string | null;
@@ -352,6 +366,7 @@ interface ResolvedConfig {
352
366
  fetcher: {
353
367
  importPath: string;
354
368
  } | null;
369
+ serialization: SerializationMode;
355
370
  forms: ResolvedFormsConfig;
356
371
  openapi: ResolvedOpenApiConfig;
357
372
  mocks: ResolvedMocksConfig;
@@ -631,4 +646,4 @@ declare function requestShape(route: RouteDescriptor): RequestShape;
631
646
  /** Identity helper for authoring extensions with full type inference. */
632
647
  declare function defineExtension(ext: CodegenExtension): CodegenExtension;
633
648
 
634
- export { type AdapterUsage as A, type CodegenExtension as C, type ExtensionContext as E, type LeafModel as L, type NumberCheck as N, type ResolvedConfig as R, type SchemaNode as S, type TypeRef as T, type UserConfig as U, type ValidationAdapter as V, type RouteDescriptor as a, type RenderContext as b, type SchemaModule as c, type RenderedModule as d, type ResolvedFormsConfig as e, type ContractDescriptor as f, type ContractSource as g, type ControllerRef as h, type ScopeConfig as i, type StringCheck as j, type ValidationOption as k, type ApiClientLayer as l, type ApiHeaderContribution as m, type ApiModuleDeps as n, type EmittedFile as o, type RequestModel as p, type RequestShape as q, resolveAdapter as r, defineExtension as s, requestShape as t };
649
+ export { type AdapterUsage as A, type CodegenExtension as C, type ExtensionContext as E, type LeafModel as L, type NumberCheck as N, type ResolvedConfig as R, type SchemaNode as S, type TypeRef as T, type UserConfig as U, type ValidationAdapter as V, type RouteDescriptor as a, type RenderContext as b, type SchemaModule as c, type RenderedModule as d, type ResolvedFormsConfig as e, type SerializationMode as f, type ContractDescriptor as g, type ContractSource as h, type ControllerRef as i, type ScopeConfig as j, type StringCheck as k, type ValidationOption as l, type ApiClientLayer as m, type ApiHeaderContribution as n, type ApiModuleDeps as o, type EmittedFile as p, type RequestModel as q, resolveAdapter as r, type RequestShape as s, defineExtension as t, requestShape as u };
@@ -254,6 +254,18 @@ interface UserConfig {
254
254
  fetcher?: {
255
255
  importPath: string;
256
256
  };
257
+ /**
258
+ * How response payloads are deserialized on the client, which determines the
259
+ * generated `response` type shape.
260
+ *
261
+ * - `'json'` (default): responses cross the wire as plain JSON, so the
262
+ * generated type is wrapped in `Jsonify<...>` (e.g. `Date` → `string`).
263
+ * - `'superjson'`: responses are revived (Dates/Maps/Sets restored), so the
264
+ * raw controller return type is emitted unchanged.
265
+ *
266
+ * @default 'json'
267
+ */
268
+ serialization?: 'json' | 'superjson';
257
269
  /**
258
270
  * Typed-form schema emit (`forms.ts`). Re-exports / translates contract and
259
271
  * class-validator-decorated DTO schemas into zod schemas for client-side
@@ -319,6 +331,8 @@ interface ResolvedCodegenConfig {
319
331
  outDir: string;
320
332
  cwd: string;
321
333
  }
334
+ /** How response payloads are deserialized on the client. */
335
+ type SerializationMode = 'json' | 'superjson';
322
336
  interface ResolvedAppConfig {
323
337
  moduleEntry: string;
324
338
  tsconfig: string | null;
@@ -352,6 +366,7 @@ interface ResolvedConfig {
352
366
  fetcher: {
353
367
  importPath: string;
354
368
  } | null;
369
+ serialization: SerializationMode;
355
370
  forms: ResolvedFormsConfig;
356
371
  openapi: ResolvedOpenApiConfig;
357
372
  mocks: ResolvedMocksConfig;
@@ -631,4 +646,4 @@ declare function requestShape(route: RouteDescriptor): RequestShape;
631
646
  /** Identity helper for authoring extensions with full type inference. */
632
647
  declare function defineExtension(ext: CodegenExtension): CodegenExtension;
633
648
 
634
- export { type AdapterUsage as A, type CodegenExtension as C, type ExtensionContext as E, type LeafModel as L, type NumberCheck as N, type ResolvedConfig as R, type SchemaNode as S, type TypeRef as T, type UserConfig as U, type ValidationAdapter as V, type RouteDescriptor as a, type RenderContext as b, type SchemaModule as c, type RenderedModule as d, type ResolvedFormsConfig as e, type ContractDescriptor as f, type ContractSource as g, type ControllerRef as h, type ScopeConfig as i, type StringCheck as j, type ValidationOption as k, type ApiClientLayer as l, type ApiHeaderContribution as m, type ApiModuleDeps as n, type EmittedFile as o, type RequestModel as p, type RequestShape as q, resolveAdapter as r, defineExtension as s, requestShape as t };
649
+ export { type AdapterUsage as A, type CodegenExtension as C, type ExtensionContext as E, type LeafModel as L, type NumberCheck as N, type ResolvedConfig as R, type SchemaNode as S, type TypeRef as T, type UserConfig as U, type ValidationAdapter as V, type RouteDescriptor as a, type RenderContext as b, type SchemaModule as c, type RenderedModule as d, type ResolvedFormsConfig as e, type SerializationMode as f, type ContractDescriptor as g, type ContractSource as h, type ControllerRef as i, type ScopeConfig as j, type StringCheck as k, type ValidationOption as l, type ApiClientLayer as m, type ApiHeaderContribution as n, type ApiModuleDeps as o, type EmittedFile as p, type RequestModel as q, resolveAdapter as r, type RequestShape as s, defineExtension as t, requestShape as u };
package/dist/index.cjs CHANGED
@@ -187,6 +187,7 @@ function applyDefaults(userConfig, cwd) {
187
187
  },
188
188
  app,
189
189
  fetcher: userConfig.fetcher ?? null,
190
+ serialization: userConfig.serialization ?? "json",
190
191
  forms: {
191
192
  enabled: userConfig.forms?.enabled ?? true,
192
193
  watch: userConfig.forms?.watch ?? "src/**/*.dto.ts",
@@ -770,7 +771,11 @@ function emitFilterQueryTypeArgs(c) {
770
771
  function emitFilterQueryType(c) {
771
772
  return `import('@dudousxd/nestjs-filter-client').TypedFilterQuery<${emitFilterQueryTypeArgs(c)}>`;
772
773
  }
773
- function buildResponseType(c, outDir) {
774
+ function buildResponseType(c, outDir, serialization) {
775
+ const raw = rawResponseType(c, outDir);
776
+ return serialization === "json" ? `Jsonify<${raw}>` : raw;
777
+ }
778
+ function rawResponseType(c, outDir) {
774
779
  const respRef = c.contractSource.responseRef;
775
780
  if (c.contractSource.stream) {
776
781
  if (respRef) return respRef.isArray ? `Array<${respRef.name}>` : respRef.name;
@@ -793,7 +798,7 @@ function buildErrorType(c) {
793
798
  }
794
799
  return c.contractSource.error ?? "unknown";
795
800
  }
796
- function emitRouterTypeBlock(tree, indent, outDir) {
801
+ function emitRouterTypeBlock(tree, indent, outDir, serialization) {
797
802
  const pad = " ".repeat(indent);
798
803
  const lines = [];
799
804
  for (const [key, node] of tree) {
@@ -806,7 +811,7 @@ function emitRouterTypeBlock(tree, indent, outDir) {
806
811
  const query = queryRef ? queryRef.isArray ? `Array<${queryRef.name}>` : queryRef.name : isFilterQuery ? emitFilterQueryType(c) : c.contractSource.query ?? "never";
807
812
  const bodyRef = c.contractSource.bodyRef;
808
813
  const body = method === "GET" ? "never" : bodyRef ? bodyRef.isArray ? `Array<${bodyRef.name}>` : bodyRef.name : c.contractSource.body ?? "never";
809
- const response = buildResponseType(c, outDir);
814
+ const response = buildResponseType(c, outDir, serialization);
810
815
  const error = buildErrorType(c);
811
816
  const params = buildParamsType(c.params);
812
817
  const safeMethod = JSON.stringify(method);
@@ -818,7 +823,7 @@ function emitRouterTypeBlock(tree, indent, outDir) {
818
823
  );
819
824
  } else {
820
825
  lines.push(`${pad}${objKey}: {`);
821
- lines.push(...emitRouterTypeBlock(node.children, indent + 2, outDir));
826
+ lines.push(...emitRouterTypeBlock(node.children, indent + 2, outDir, serialization));
822
827
  lines.push(`${pad}};`);
823
828
  }
824
829
  }
@@ -1023,6 +1028,7 @@ var EMPTY_PATH_NAMESPACE = [
1023
1028
  ];
1024
1029
  function buildApiFile(routes, outDir, opts = {}) {
1025
1030
  const fetcherImportPath = opts.fetcherImportPath;
1031
+ const serialization = opts.serialization ?? "json";
1026
1032
  const extensions = opts.extensions ?? [];
1027
1033
  const { layer } = resolveApiSlots(extensions);
1028
1034
  const memberExts = extensions.filter((e) => e.apiMembers);
@@ -1079,6 +1085,9 @@ function buildApiFile(routes, outDir, opts = {}) {
1079
1085
  );
1080
1086
  const runtimeImport = fetcherImportPath ?? "@dudousxd/nestjs-client";
1081
1087
  lines.push(`import type { Fetcher } from '${runtimeImport}';`);
1088
+ if (serialization === "json" && contracted.length > 0) {
1089
+ lines.push(`import type { Jsonify } from '${runtimeImport}';`);
1090
+ }
1082
1091
  if (importsByFile.size > 0 && outDir) {
1083
1092
  lines.push("");
1084
1093
  const emittedNames = /* @__PURE__ */ new Set();
@@ -1138,7 +1147,7 @@ function buildApiFile(routes, outDir, opts = {}) {
1138
1147
  insertIntoTree(tree, segments, leaf, name);
1139
1148
  }
1140
1149
  lines.push("export type ApiRouter = {");
1141
- lines.push(...emitRouterTypeBlock(tree, 2, outDir ?? ""));
1150
+ lines.push(...emitRouterTypeBlock(tree, 2, outDir ?? "", serialization));
1142
1151
  lines.push("};");
1143
1152
  lines.push("");
1144
1153
  lines.push(...emitReqHelper());
@@ -2159,6 +2168,7 @@ async function generate(config, inputRoutes = []) {
2159
2168
  if (hasContracts) {
2160
2169
  await emitApi(routes, config.codegen.outDir, {
2161
2170
  ...config.fetcher?.importPath ? { fetcherImportPath: config.fetcher.importPath } : {},
2171
+ serialization: config.serialization,
2162
2172
  extensions,
2163
2173
  ctx
2164
2174
  });
@@ -4389,6 +4399,7 @@ async function watch(config, onChange) {
4389
4399
  return NO_OP_WATCHER;
4390
4400
  }
4391
4401
  let discovery = null;
4402
+ let lastRoutes = [];
4392
4403
  async function getDiscovery() {
4393
4404
  if (discovery === null) {
4394
4405
  discovery = await PersistentDiscovery.create({
@@ -4402,13 +4413,14 @@ async function watch(config, onChange) {
4402
4413
  }
4403
4414
  try {
4404
4415
  const initialRoutes = (await getDiscovery()).discover();
4416
+ lastRoutes = initialRoutes;
4405
4417
  await generate(config, initialRoutes);
4406
4418
  } catch (err) {
4407
4419
  console.warn(
4408
4420
  `[nestjs-codegen] Initial route discovery failed, falling back to pages-only: ${err instanceof Error ? err.message : String(err)}`
4409
4421
  );
4410
4422
  try {
4411
- await generate(config);
4423
+ await generate(config, lastRoutes);
4412
4424
  } catch {
4413
4425
  }
4414
4426
  }
@@ -4426,7 +4438,7 @@ async function watch(config, onChange) {
4426
4438
  pagesDebounceTimer = setTimeout(async () => {
4427
4439
  pagesDebounceTimer = void 0;
4428
4440
  try {
4429
- await generate(config);
4441
+ await generate(config, lastRoutes);
4430
4442
  } catch (err) {
4431
4443
  console.error(
4432
4444
  "[nestjs-codegen] Pages generation failed:",
@@ -4457,6 +4469,7 @@ async function watch(config, onChange) {
4457
4469
  pendingChangedPaths.clear();
4458
4470
  try {
4459
4471
  const routes = await (await getDiscovery()).rediscover(changed);
4472
+ lastRoutes = routes;
4460
4473
  await generate(config, routes);
4461
4474
  } catch (err) {
4462
4475
  console.error(
@@ -4580,7 +4593,7 @@ function createChainModuleRenderer(opts) {
4580
4593
  }
4581
4594
 
4582
4595
  // src/index.ts
4583
- var VERSION = "0.6.0";
4596
+ var VERSION = "0.7.0";
4584
4597
  // Annotate the CommonJS export names for ESM import in node:
4585
4598
  0 && (module.exports = {
4586
4599
  CodegenError,