@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.
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 } from './index-_qRai4M3.cjs';
2
- export { A as AdapterUsage, f as ContractDescriptor, g as ContractSource, h as ControllerRef, N as NumberCheck, i as ScopeConfig, j as StringCheck, T as TypeRef, k as ValidationOption, r as resolveAdapter } from './index-_qRai4M3.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-DgIAN5k5.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-DgIAN5k5.cjs';
3
3
  import { ClassDeclaration, SourceFile, Project } from 'ts-morph';
4
4
 
5
5
  declare function defineConfig(c: UserConfig): UserConfig;
@@ -160,13 +160,20 @@ declare function emitForms(routes: RouteDescriptor[], outDir: string, config: Re
160
160
  * output: an `apiClientLayer` (e.g. `@dudousxd/nestjs-codegen-tanstack`) turns leaves into
161
161
  * handles wrapping the neutral fetcher request; `apiMembers` add handle members; `apiHeader`
162
162
  * contributes top-level imports/statements.
163
+ *
164
+ * `serialization` controls how response types are emitted (default `'json'`):
165
+ * in `'json'` mode each `response` type is wrapped in `Jsonify<...>` (so the
166
+ * generated type reflects the JSON wire shape, e.g. `Date` → `string`); in
167
+ * `'superjson'` mode the raw controller return type is emitted unchanged.
163
168
  */
164
169
  interface ApiEmitOptions {
165
- fetcherImportPath?: string;
170
+ fetcherImportPath?: string | undefined;
166
171
  /** Registered extensions. Their api.ts hooks (transport/layer/members/header) are applied. */
167
- extensions?: CodegenExtension[];
172
+ extensions?: CodegenExtension[] | undefined;
168
173
  /** Shared extension context (from `generate()`). When omitted, a minimal one is built from routes. */
169
- ctx?: ExtensionContext;
174
+ ctx?: ExtensionContext | undefined;
175
+ /** How response payloads deserialize on the client. Default `'json'`. */
176
+ serialization?: SerializationMode | undefined;
170
177
  }
171
178
  declare function emitApi(routes: RouteDescriptor[], outDir: string, opts?: ApiEmitOptions): Promise<void>;
172
179
 
@@ -289,6 +296,6 @@ interface FastDiscoveryOptions {
289
296
  }
290
297
  declare function discoverContractsFast(opts: FastDiscoveryOptions): Promise<RouteDescriptor[]>;
291
298
 
292
- declare const VERSION = "0.6.0";
299
+ declare const VERSION = "0.7.0";
293
300
 
294
301
  export { type ChainModuleRendererOptions, CodegenError, ConfigError, type FastDiscoveryOptions, type JsonSchema, type MocksEmitOptions, type OpenApiDocument, type OpenApiEmitOptions, type OpenApiInfo, RenderContext, RenderedModule, ResolvedConfig, RouteDescriptor, SchemaModule, SchemaNode, type TsTypeContext, UserConfig, VERSION, ValidationAdapter, 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 } from './index-_qRai4M3.js';
2
- export { A as AdapterUsage, f as ContractDescriptor, g as ContractSource, h as ControllerRef, N as NumberCheck, i as ScopeConfig, j as StringCheck, T as TypeRef, k as ValidationOption, r as resolveAdapter } from './index-_qRai4M3.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-DgIAN5k5.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-DgIAN5k5.js';
3
3
  import { ClassDeclaration, SourceFile, Project } from 'ts-morph';
4
4
 
5
5
  declare function defineConfig(c: UserConfig): UserConfig;
@@ -160,13 +160,20 @@ declare function emitForms(routes: RouteDescriptor[], outDir: string, config: Re
160
160
  * output: an `apiClientLayer` (e.g. `@dudousxd/nestjs-codegen-tanstack`) turns leaves into
161
161
  * handles wrapping the neutral fetcher request; `apiMembers` add handle members; `apiHeader`
162
162
  * contributes top-level imports/statements.
163
+ *
164
+ * `serialization` controls how response types are emitted (default `'json'`):
165
+ * in `'json'` mode each `response` type is wrapped in `Jsonify<...>` (so the
166
+ * generated type reflects the JSON wire shape, e.g. `Date` → `string`); in
167
+ * `'superjson'` mode the raw controller return type is emitted unchanged.
163
168
  */
164
169
  interface ApiEmitOptions {
165
- fetcherImportPath?: string;
170
+ fetcherImportPath?: string | undefined;
166
171
  /** Registered extensions. Their api.ts hooks (transport/layer/members/header) are applied. */
167
- extensions?: CodegenExtension[];
172
+ extensions?: CodegenExtension[] | undefined;
168
173
  /** Shared extension context (from `generate()`). When omitted, a minimal one is built from routes. */
169
- ctx?: ExtensionContext;
174
+ ctx?: ExtensionContext | undefined;
175
+ /** How response payloads deserialize on the client. Default `'json'`. */
176
+ serialization?: SerializationMode | undefined;
170
177
  }
171
178
  declare function emitApi(routes: RouteDescriptor[], outDir: string, opts?: ApiEmitOptions): Promise<void>;
172
179
 
@@ -289,6 +296,6 @@ interface FastDiscoveryOptions {
289
296
  }
290
297
  declare function discoverContractsFast(opts: FastDiscoveryOptions): Promise<RouteDescriptor[]>;
291
298
 
292
- declare const VERSION = "0.6.0";
299
+ declare const VERSION = "0.7.0";
293
300
 
294
301
  export { type ChainModuleRendererOptions, CodegenError, ConfigError, type FastDiscoveryOptions, type JsonSchema, type MocksEmitOptions, type OpenApiDocument, type OpenApiEmitOptions, type OpenApiInfo, RenderContext, RenderedModule, ResolvedConfig, RouteDescriptor, SchemaModule, SchemaNode, type TsTypeContext, UserConfig, VERSION, ValidationAdapter, 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
@@ -127,6 +127,7 @@ function applyDefaults(userConfig, cwd) {
127
127
  },
128
128
  app,
129
129
  fetcher: userConfig.fetcher ?? null,
130
+ serialization: userConfig.serialization ?? "json",
130
131
  forms: {
131
132
  enabled: userConfig.forms?.enabled ?? true,
132
133
  watch: userConfig.forms?.watch ?? "src/**/*.dto.ts",
@@ -710,7 +711,11 @@ function emitFilterQueryTypeArgs(c) {
710
711
  function emitFilterQueryType(c) {
711
712
  return `import('@dudousxd/nestjs-filter-client').TypedFilterQuery<${emitFilterQueryTypeArgs(c)}>`;
712
713
  }
713
- function buildResponseType(c, outDir) {
714
+ function buildResponseType(c, outDir, serialization) {
715
+ const raw = rawResponseType(c, outDir);
716
+ return serialization === "json" ? `Jsonify<${raw}>` : raw;
717
+ }
718
+ function rawResponseType(c, outDir) {
714
719
  const respRef = c.contractSource.responseRef;
715
720
  if (c.contractSource.stream) {
716
721
  if (respRef) return respRef.isArray ? `Array<${respRef.name}>` : respRef.name;
@@ -733,7 +738,7 @@ function buildErrorType(c) {
733
738
  }
734
739
  return c.contractSource.error ?? "unknown";
735
740
  }
736
- function emitRouterTypeBlock(tree, indent, outDir) {
741
+ function emitRouterTypeBlock(tree, indent, outDir, serialization) {
737
742
  const pad = " ".repeat(indent);
738
743
  const lines = [];
739
744
  for (const [key, node] of tree) {
@@ -746,7 +751,7 @@ function emitRouterTypeBlock(tree, indent, outDir) {
746
751
  const query = queryRef ? queryRef.isArray ? `Array<${queryRef.name}>` : queryRef.name : isFilterQuery ? emitFilterQueryType(c) : c.contractSource.query ?? "never";
747
752
  const bodyRef = c.contractSource.bodyRef;
748
753
  const body = method === "GET" ? "never" : bodyRef ? bodyRef.isArray ? `Array<${bodyRef.name}>` : bodyRef.name : c.contractSource.body ?? "never";
749
- const response = buildResponseType(c, outDir);
754
+ const response = buildResponseType(c, outDir, serialization);
750
755
  const error = buildErrorType(c);
751
756
  const params = buildParamsType(c.params);
752
757
  const safeMethod = JSON.stringify(method);
@@ -758,7 +763,7 @@ function emitRouterTypeBlock(tree, indent, outDir) {
758
763
  );
759
764
  } else {
760
765
  lines.push(`${pad}${objKey}: {`);
761
- lines.push(...emitRouterTypeBlock(node.children, indent + 2, outDir));
766
+ lines.push(...emitRouterTypeBlock(node.children, indent + 2, outDir, serialization));
762
767
  lines.push(`${pad}};`);
763
768
  }
764
769
  }
@@ -963,6 +968,7 @@ var EMPTY_PATH_NAMESPACE = [
963
968
  ];
964
969
  function buildApiFile(routes, outDir, opts = {}) {
965
970
  const fetcherImportPath = opts.fetcherImportPath;
971
+ const serialization = opts.serialization ?? "json";
966
972
  const extensions = opts.extensions ?? [];
967
973
  const { layer } = resolveApiSlots(extensions);
968
974
  const memberExts = extensions.filter((e) => e.apiMembers);
@@ -1019,6 +1025,9 @@ function buildApiFile(routes, outDir, opts = {}) {
1019
1025
  );
1020
1026
  const runtimeImport = fetcherImportPath ?? "@dudousxd/nestjs-client";
1021
1027
  lines.push(`import type { Fetcher } from '${runtimeImport}';`);
1028
+ if (serialization === "json" && contracted.length > 0) {
1029
+ lines.push(`import type { Jsonify } from '${runtimeImport}';`);
1030
+ }
1022
1031
  if (importsByFile.size > 0 && outDir) {
1023
1032
  lines.push("");
1024
1033
  const emittedNames = /* @__PURE__ */ new Set();
@@ -1078,7 +1087,7 @@ function buildApiFile(routes, outDir, opts = {}) {
1078
1087
  insertIntoTree(tree, segments, leaf, name);
1079
1088
  }
1080
1089
  lines.push("export type ApiRouter = {");
1081
- lines.push(...emitRouterTypeBlock(tree, 2, outDir ?? ""));
1090
+ lines.push(...emitRouterTypeBlock(tree, 2, outDir ?? "", serialization));
1082
1091
  lines.push("};");
1083
1092
  lines.push("");
1084
1093
  lines.push(...emitReqHelper());
@@ -2099,6 +2108,7 @@ async function generate(config, inputRoutes = []) {
2099
2108
  if (hasContracts) {
2100
2109
  await emitApi(routes, config.codegen.outDir, {
2101
2110
  ...config.fetcher?.importPath ? { fetcherImportPath: config.fetcher.importPath } : {},
2111
+ serialization: config.serialization,
2102
2112
  extensions,
2103
2113
  ctx
2104
2114
  });
@@ -4344,6 +4354,7 @@ async function watch(config, onChange) {
4344
4354
  return NO_OP_WATCHER;
4345
4355
  }
4346
4356
  let discovery = null;
4357
+ let lastRoutes = [];
4347
4358
  async function getDiscovery() {
4348
4359
  if (discovery === null) {
4349
4360
  discovery = await PersistentDiscovery.create({
@@ -4357,13 +4368,14 @@ async function watch(config, onChange) {
4357
4368
  }
4358
4369
  try {
4359
4370
  const initialRoutes = (await getDiscovery()).discover();
4371
+ lastRoutes = initialRoutes;
4360
4372
  await generate(config, initialRoutes);
4361
4373
  } catch (err) {
4362
4374
  console.warn(
4363
4375
  `[nestjs-codegen] Initial route discovery failed, falling back to pages-only: ${err instanceof Error ? err.message : String(err)}`
4364
4376
  );
4365
4377
  try {
4366
- await generate(config);
4378
+ await generate(config, lastRoutes);
4367
4379
  } catch {
4368
4380
  }
4369
4381
  }
@@ -4381,7 +4393,7 @@ async function watch(config, onChange) {
4381
4393
  pagesDebounceTimer = setTimeout(async () => {
4382
4394
  pagesDebounceTimer = void 0;
4383
4395
  try {
4384
- await generate(config);
4396
+ await generate(config, lastRoutes);
4385
4397
  } catch (err) {
4386
4398
  console.error(
4387
4399
  "[nestjs-codegen] Pages generation failed:",
@@ -4412,6 +4424,7 @@ async function watch(config, onChange) {
4412
4424
  pendingChangedPaths.clear();
4413
4425
  try {
4414
4426
  const routes = await (await getDiscovery()).rediscover(changed);
4427
+ lastRoutes = routes;
4415
4428
  await generate(config, routes);
4416
4429
  } catch (err) {
4417
4430
  console.error(
@@ -4535,7 +4548,7 @@ function createChainModuleRenderer(opts) {
4535
4548
  }
4536
4549
 
4537
4550
  // src/index.ts
4538
- var VERSION = "0.6.0";
4551
+ var VERSION = "0.7.0";
4539
4552
  export {
4540
4553
  CodegenError,
4541
4554
  ConfigError,