@dudousxd/nestjs-codegen 0.13.0 → 0.13.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.
@@ -1,2 +1,2 @@
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-DvUzPXdh.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-D8RIMVpU.cjs';
2
2
  import 'ts-morph';
@@ -1,2 +1,2 @@
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-DvUzPXdh.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-D8RIMVpU.js';
2
2
  import 'ts-morph';
@@ -492,10 +492,18 @@ interface ContractSource {
492
492
  /**
493
493
  * True when the route consumes `multipart/form-data` — its handler takes an
494
494
  * `@UploadedFile()` / `@UploadedFiles()` (via a Multer interceptor). The
495
- * uploaded-file field(s) are merged into `body` as `File | Blob`, and the
496
- * generated client serializes the body to a `FormData` instead of JSON.
495
+ * uploaded-file field(s) are carried in `multipartBody` (typed `File | Blob`)
496
+ * and intersected onto `body` at emit time, and the generated client
497
+ * serializes the body to a `FormData` instead of JSON.
497
498
  */
498
499
  multipart?: boolean;
500
+ /**
501
+ * The uploaded-file field(s) for a multipart route, as an object-type string
502
+ * (e.g. `{ file: File | Blob }`). Kept separate from `body` so the emitter can
503
+ * intersect it onto whichever body representation it picks — a named `bodyRef`
504
+ * or the inline `body` text — preserving the import when there is one.
505
+ */
506
+ multipartBody?: string | null;
499
507
  }
500
508
  interface ContractDescriptor {
501
509
  contractSource: ContractSource;
@@ -492,10 +492,18 @@ interface ContractSource {
492
492
  /**
493
493
  * True when the route consumes `multipart/form-data` — its handler takes an
494
494
  * `@UploadedFile()` / `@UploadedFiles()` (via a Multer interceptor). The
495
- * uploaded-file field(s) are merged into `body` as `File | Blob`, and the
496
- * generated client serializes the body to a `FormData` instead of JSON.
495
+ * uploaded-file field(s) are carried in `multipartBody` (typed `File | Blob`)
496
+ * and intersected onto `body` at emit time, and the generated client
497
+ * serializes the body to a `FormData` instead of JSON.
497
498
  */
498
499
  multipart?: boolean;
500
+ /**
501
+ * The uploaded-file field(s) for a multipart route, as an object-type string
502
+ * (e.g. `{ file: File | Blob }`). Kept separate from `body` so the emitter can
503
+ * intersect it onto whichever body representation it picks — a named `bodyRef`
504
+ * or the inline `body` text — preserving the import when there is one.
505
+ */
506
+ multipartBody?: string | null;
499
507
  }
500
508
  interface ContractDescriptor {
501
509
  contractSource: ContractSource;
package/dist/index.cjs CHANGED
@@ -816,7 +816,15 @@ function emitRouterTypeBlock(tree, indent, outDir, serialization) {
816
816
  const isFilterQuery = c.contractSource.filterSource === "query" && !!c.contractSource.filterFields?.length;
817
817
  const query = queryRef ? queryRef.isArray ? `Array<${queryRef.name}>` : queryRef.name : isFilterQuery ? emitFilterQueryType(c) : c.contractSource.query ?? "never";
818
818
  const bodyRef = c.contractSource.bodyRef;
819
- const body = method === "GET" ? "never" : bodyRef ? bodyRef.isArray ? `Array<${bodyRef.name}>` : bodyRef.name : c.contractSource.body ?? "never";
819
+ let body = method === "GET" ? "never" : bodyRef ? bodyRef.isArray ? `Array<${bodyRef.name}>` : bodyRef.name : c.contractSource.body ?? "never";
820
+ const multipartBody = c.contractSource.multipartBody;
821
+ if (c.contractSource.multipart && multipartBody) {
822
+ if (body === "never") {
823
+ body = multipartBody;
824
+ } else if (!bodyAcceptsAnything(body)) {
825
+ body = `(${body}) & ${multipartBody}`;
826
+ }
827
+ }
820
828
  const response = buildResponseType(c, outDir, serialization);
821
829
  const error = buildErrorType(c);
822
830
  const params = buildParamsType(c.params);
@@ -835,6 +843,25 @@ function emitRouterTypeBlock(tree, indent, outDir, serialization) {
835
843
  }
836
844
  return lines;
837
845
  }
846
+ function topLevelUnionArms(type) {
847
+ const arms = [];
848
+ let depth = 0;
849
+ let start = 0;
850
+ for (let i = 0; i < type.length; i++) {
851
+ const ch = type[i];
852
+ if (ch === "{" || ch === "[" || ch === "<" || ch === "(") depth++;
853
+ else if (ch === "}" || ch === "]" || ch === ">" || ch === ")") depth--;
854
+ else if (ch === "|" && depth === 0) {
855
+ arms.push(type.slice(start, i).trim());
856
+ start = i + 1;
857
+ }
858
+ }
859
+ arms.push(type.slice(start).trim());
860
+ return arms;
861
+ }
862
+ function bodyAcceptsAnything(body) {
863
+ return topLevelUnionArms(body).some((arm) => arm === "unknown" || arm === "any");
864
+ }
838
865
  function buildRequestModel(c) {
839
866
  const m = c.method.toLowerCase();
840
867
  const flat = JSON.stringify(c.name);
@@ -3919,10 +3946,7 @@ function extractDtoContract(method, sourceFile, project) {
3919
3946
  const filterInfo = extractApplyFilterInfo(method, sourceFile, project);
3920
3947
  const query = extractQueryType(method, sourceFile, project);
3921
3948
  const uploads = extractUploadedFiles(method);
3922
- if (uploads.fields) {
3923
- const fileObject = `{ ${uploads.fields} }`;
3924
- body = body ? `(${body}) & ${fileObject}` : fileObject;
3925
- }
3949
+ const multipartBody = uploads.fields ? `{ ${uploads.fields} }` : null;
3926
3950
  const streamElement = detectStreamElement(method);
3927
3951
  const isStream = streamElement !== null;
3928
3952
  if (filterInfo && filterInfo.source === "body") {
@@ -4006,7 +4030,8 @@ function extractDtoContract(method, sourceFile, project) {
4006
4030
  bodySchema,
4007
4031
  querySchema,
4008
4032
  stream: isStream,
4009
- multipart: uploads.multipart
4033
+ multipart: uploads.multipart,
4034
+ multipartBody
4010
4035
  };
4011
4036
  }
4012
4037
  function resolveParamClass(method, decoratorName, sourceFile, project) {
@@ -4492,7 +4517,8 @@ function extractDtoRoute(args) {
4492
4517
  bodySchema: dtoContract?.bodySchema ?? null,
4493
4518
  querySchema: dtoContract?.querySchema ?? null,
4494
4519
  stream: dtoContract?.stream ?? false,
4495
- multipart: dtoContract?.multipart ?? false
4520
+ multipart: dtoContract?.multipart ?? false,
4521
+ multipartBody: dtoContract?.multipartBody ?? null
4496
4522
  }
4497
4523
  });
4498
4524
  }
@@ -4805,7 +4831,7 @@ function createChainModuleRenderer(opts) {
4805
4831
  }
4806
4832
 
4807
4833
  // src/index.ts
4808
- var VERSION = "0.13.0";
4834
+ var VERSION = "0.13.1";
4809
4835
  // Annotate the CommonJS export names for ESM import in node:
4810
4836
  0 && (module.exports = {
4811
4837
  CodegenError,