@dudousxd/nestjs-codegen 0.6.1 → 0.7.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.
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.1";
299
+ declare const VERSION = "0.7.1";
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.1";
299
+ declare const VERSION = "0.7.1";
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
@@ -46,18 +46,23 @@ async function fileExists(filePath) {
46
46
  }
47
47
  }
48
48
  async function importTs(filePath) {
49
- let tsImport;
49
+ const fileUrl = pathToFileURL(filePath).href;
50
50
  try {
51
- const tsxEsm = await import("tsx/esm/api");
52
- tsImport = tsxEsm.tsImport;
53
- } catch {
54
- throw new ConfigError(
55
- "Failed to load config: `tsx` is required for loading TypeScript config files. Install it as a dev dependency: pnpm add -D tsx"
56
- );
51
+ return await import(fileUrl);
52
+ } catch (nativeError) {
53
+ let tsImport;
54
+ try {
55
+ const tsxEsm = await import("tsx/esm/api");
56
+ tsImport = tsxEsm.tsImport;
57
+ } catch {
58
+ throw new ConfigError(
59
+ "Failed to load config: `tsx` is required for loading TypeScript config files. Install it as a dev dependency: pnpm add -D tsx",
60
+ { cause: nativeError }
61
+ );
62
+ }
63
+ const parentURL = pathToFileURL(`${filePath}__parent__`).href;
64
+ return tsImport(fileUrl, { parentURL });
57
65
  }
58
- const parentURL = pathToFileURL(`${filePath}__parent__`).href;
59
- const fileUrl = pathToFileURL(filePath).href;
60
- return tsImport(fileUrl, { parentURL });
61
66
  }
62
67
  function resolveAbsolute(cwd, p) {
63
68
  if (isAbsolute(p)) return p;
@@ -127,6 +132,7 @@ function applyDefaults(userConfig, cwd) {
127
132
  },
128
133
  app,
129
134
  fetcher: userConfig.fetcher ?? null,
135
+ serialization: userConfig.serialization ?? "json",
130
136
  forms: {
131
137
  enabled: userConfig.forms?.enabled ?? true,
132
138
  watch: userConfig.forms?.watch ?? "src/**/*.dto.ts",
@@ -710,7 +716,11 @@ function emitFilterQueryTypeArgs(c) {
710
716
  function emitFilterQueryType(c) {
711
717
  return `import('@dudousxd/nestjs-filter-client').TypedFilterQuery<${emitFilterQueryTypeArgs(c)}>`;
712
718
  }
713
- function buildResponseType(c, outDir) {
719
+ function buildResponseType(c, outDir, serialization) {
720
+ const raw = rawResponseType(c, outDir);
721
+ return serialization === "json" ? `Jsonify<${raw}>` : raw;
722
+ }
723
+ function rawResponseType(c, outDir) {
714
724
  const respRef = c.contractSource.responseRef;
715
725
  if (c.contractSource.stream) {
716
726
  if (respRef) return respRef.isArray ? `Array<${respRef.name}>` : respRef.name;
@@ -733,7 +743,7 @@ function buildErrorType(c) {
733
743
  }
734
744
  return c.contractSource.error ?? "unknown";
735
745
  }
736
- function emitRouterTypeBlock(tree, indent, outDir) {
746
+ function emitRouterTypeBlock(tree, indent, outDir, serialization) {
737
747
  const pad = " ".repeat(indent);
738
748
  const lines = [];
739
749
  for (const [key, node] of tree) {
@@ -746,7 +756,7 @@ function emitRouterTypeBlock(tree, indent, outDir) {
746
756
  const query = queryRef ? queryRef.isArray ? `Array<${queryRef.name}>` : queryRef.name : isFilterQuery ? emitFilterQueryType(c) : c.contractSource.query ?? "never";
747
757
  const bodyRef = c.contractSource.bodyRef;
748
758
  const body = method === "GET" ? "never" : bodyRef ? bodyRef.isArray ? `Array<${bodyRef.name}>` : bodyRef.name : c.contractSource.body ?? "never";
749
- const response = buildResponseType(c, outDir);
759
+ const response = buildResponseType(c, outDir, serialization);
750
760
  const error = buildErrorType(c);
751
761
  const params = buildParamsType(c.params);
752
762
  const safeMethod = JSON.stringify(method);
@@ -758,7 +768,7 @@ function emitRouterTypeBlock(tree, indent, outDir) {
758
768
  );
759
769
  } else {
760
770
  lines.push(`${pad}${objKey}: {`);
761
- lines.push(...emitRouterTypeBlock(node.children, indent + 2, outDir));
771
+ lines.push(...emitRouterTypeBlock(node.children, indent + 2, outDir, serialization));
762
772
  lines.push(`${pad}};`);
763
773
  }
764
774
  }
@@ -963,6 +973,7 @@ var EMPTY_PATH_NAMESPACE = [
963
973
  ];
964
974
  function buildApiFile(routes, outDir, opts = {}) {
965
975
  const fetcherImportPath = opts.fetcherImportPath;
976
+ const serialization = opts.serialization ?? "json";
966
977
  const extensions = opts.extensions ?? [];
967
978
  const { layer } = resolveApiSlots(extensions);
968
979
  const memberExts = extensions.filter((e) => e.apiMembers);
@@ -1019,6 +1030,9 @@ function buildApiFile(routes, outDir, opts = {}) {
1019
1030
  );
1020
1031
  const runtimeImport = fetcherImportPath ?? "@dudousxd/nestjs-client";
1021
1032
  lines.push(`import type { Fetcher } from '${runtimeImport}';`);
1033
+ if (serialization === "json" && contracted.length > 0) {
1034
+ lines.push(`import type { Jsonify } from '${runtimeImport}';`);
1035
+ }
1022
1036
  if (importsByFile.size > 0 && outDir) {
1023
1037
  lines.push("");
1024
1038
  const emittedNames = /* @__PURE__ */ new Set();
@@ -1078,7 +1092,7 @@ function buildApiFile(routes, outDir, opts = {}) {
1078
1092
  insertIntoTree(tree, segments, leaf, name);
1079
1093
  }
1080
1094
  lines.push("export type ApiRouter = {");
1081
- lines.push(...emitRouterTypeBlock(tree, 2, outDir ?? ""));
1095
+ lines.push(...emitRouterTypeBlock(tree, 2, outDir ?? "", serialization));
1082
1096
  lines.push("};");
1083
1097
  lines.push("");
1084
1098
  lines.push(...emitReqHelper());
@@ -2099,6 +2113,7 @@ async function generate(config, inputRoutes = []) {
2099
2113
  if (hasContracts) {
2100
2114
  await emitApi(routes, config.codegen.outDir, {
2101
2115
  ...config.fetcher?.importPath ? { fetcherImportPath: config.fetcher.importPath } : {},
2116
+ serialization: config.serialization,
2102
2117
  extensions,
2103
2118
  ctx
2104
2119
  });
@@ -4538,7 +4553,7 @@ function createChainModuleRenderer(opts) {
4538
4553
  }
4539
4554
 
4540
4555
  // src/index.ts
4541
- var VERSION = "0.6.1";
4556
+ var VERSION = "0.7.1";
4542
4557
  export {
4543
4558
  CodegenError,
4544
4559
  ConfigError,