@dudousxd/nestjs-codegen 0.12.0 → 0.13.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 { 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-CxkGbILp.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-DvUzPXdh.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-CxkGbILp.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-DvUzPXdh.js';
2
2
  import 'ts-morph';
@@ -489,6 +489,13 @@ interface ContractSource {
489
489
  * `AsyncIterable<T>` stream rather than a single awaited value.
490
490
  */
491
491
  stream?: boolean;
492
+ /**
493
+ * True when the route consumes `multipart/form-data` — its handler takes an
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.
497
+ */
498
+ multipart?: boolean;
492
499
  }
493
500
  interface ContractDescriptor {
494
501
  contractSource: ContractSource;
@@ -489,6 +489,13 @@ interface ContractSource {
489
489
  * `AsyncIterable<T>` stream rather than a single awaited value.
490
490
  */
491
491
  stream?: boolean;
492
+ /**
493
+ * True when the route consumes `multipart/form-data` — its handler takes an
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.
497
+ */
498
+ multipart?: boolean;
492
499
  }
493
500
  interface ContractDescriptor {
494
501
  contractSource: ContractSource;
package/dist/index.cjs CHANGED
@@ -851,6 +851,7 @@ function buildRequestModel(c) {
851
851
  const optsParts = [];
852
852
  if (hasQuery) optsParts.push("query: input?.query as Record<string, unknown> | undefined");
853
853
  if (hasBody) optsParts.push("body: input?.body");
854
+ if (hasBody && c.contractSource.multipart) optsParts.push("multipart: true");
854
855
  const optsExpr = optsParts.length ? `{ ${optsParts.join(", ")} }` : "{}";
855
856
  return {
856
857
  routeName: c.name,
@@ -3734,6 +3735,55 @@ function extractParamsType(method, sourceFile, project) {
3734
3735
  }
3735
3736
  return entries.length > 0 ? `{ ${entries.join("; ")} }` : null;
3736
3737
  }
3738
+ function extractUploadedFiles(method) {
3739
+ const FILE = "File | Blob";
3740
+ const entries = [];
3741
+ let multipart = false;
3742
+ const hasUploadedFileParam = method.getParameters().some(
3743
+ (p) => p.getDecorators().some((d) => {
3744
+ const name = d.getName();
3745
+ return name === "UploadedFile" || name === "UploadedFiles";
3746
+ })
3747
+ );
3748
+ for (const decorator of method.getDecorators()) {
3749
+ if (decorator.getName() !== "UseInterceptors") continue;
3750
+ for (const arg of decorator.getArguments()) {
3751
+ if (!import_ts_morph7.Node.isCallExpression(arg)) continue;
3752
+ const interceptor = arg.getExpression().getText();
3753
+ const callArgs = arg.getArguments();
3754
+ const firstArg2 = callArgs[0];
3755
+ if (interceptor === "FileInterceptor") {
3756
+ if (firstArg2 && import_ts_morph7.Node.isStringLiteral(firstArg2)) {
3757
+ entries.push(`${firstArg2.getLiteralValue()}: ${FILE}`);
3758
+ multipart = true;
3759
+ }
3760
+ } else if (interceptor === "FilesInterceptor") {
3761
+ if (firstArg2 && import_ts_morph7.Node.isStringLiteral(firstArg2)) {
3762
+ entries.push(`${firstArg2.getLiteralValue()}: Array<${FILE}>`);
3763
+ multipart = true;
3764
+ }
3765
+ } else if (interceptor === "FileFieldsInterceptor") {
3766
+ if (firstArg2 && import_ts_morph7.Node.isArrayLiteralExpression(firstArg2)) {
3767
+ for (const el of firstArg2.getElements()) {
3768
+ if (!import_ts_morph7.Node.isObjectLiteralExpression(el)) continue;
3769
+ const nameProp = el.getProperty("name");
3770
+ if (nameProp && import_ts_morph7.Node.isPropertyAssignment(nameProp)) {
3771
+ const init = nameProp.getInitializer();
3772
+ if (init && import_ts_morph7.Node.isStringLiteral(init)) {
3773
+ entries.push(`${init.getLiteralValue()}: Array<${FILE}>`);
3774
+ }
3775
+ }
3776
+ }
3777
+ multipart = true;
3778
+ }
3779
+ } else if (interceptor === "AnyFilesInterceptor") {
3780
+ multipart = true;
3781
+ }
3782
+ }
3783
+ }
3784
+ if (hasUploadedFileParam) multipart = true;
3785
+ return { fields: entries.length > 0 ? entries.join("; ") : null, multipart };
3786
+ }
3737
3787
  function extractResponseType(method, sourceFile, project) {
3738
3788
  const apiResponseDecorator = method.getDecorators().find((d) => d.getName() === "ApiResponse" && (apiResponseStatus(d) ?? 0) < 400);
3739
3789
  if (apiResponseDecorator) {
@@ -3868,6 +3918,11 @@ function extractDtoContract(method, sourceFile, project) {
3868
3918
  let body = extractBodyType(method, sourceFile, project);
3869
3919
  const filterInfo = extractApplyFilterInfo(method, sourceFile, project);
3870
3920
  const query = extractQueryType(method, sourceFile, project);
3921
+ const uploads = extractUploadedFiles(method);
3922
+ if (uploads.fields) {
3923
+ const fileObject = `{ ${uploads.fields} }`;
3924
+ body = body ? `(${body}) & ${fileObject}` : fileObject;
3925
+ }
3871
3926
  const streamElement = detectStreamElement(method);
3872
3927
  const isStream = streamElement !== null;
3873
3928
  if (filterInfo && filterInfo.source === "body") {
@@ -3877,7 +3932,7 @@ function extractDtoContract(method, sourceFile, project) {
3877
3932
  const paramsType = extractParamsType(method, sourceFile, project);
3878
3933
  const response = isStream ? resolveTypeNodeToString(streamElement, sourceFile, project, 3) : extractResponseType(method, sourceFile, project);
3879
3934
  const errorInfo = extractErrorType(method, sourceFile, project);
3880
- if (body === null && query === null && paramsType === null && response === "unknown" && errorInfo === null && filterInfo === null && !isStream) {
3935
+ if (body === null && query === null && paramsType === null && response === "unknown" && errorInfo === null && filterInfo === null && !isStream && !uploads.multipart) {
3881
3936
  return null;
3882
3937
  }
3883
3938
  let bodyRef = null;
@@ -3950,7 +4005,8 @@ function extractDtoContract(method, sourceFile, project) {
3950
4005
  formWarnings,
3951
4006
  bodySchema,
3952
4007
  querySchema,
3953
- stream: isStream
4008
+ stream: isStream,
4009
+ multipart: uploads.multipart
3954
4010
  };
3955
4011
  }
3956
4012
  function resolveParamClass(method, decoratorName, sourceFile, project) {
@@ -4435,7 +4491,8 @@ function extractDtoRoute(args) {
4435
4491
  formWarnings: dtoContract?.formWarnings ?? [],
4436
4492
  bodySchema: dtoContract?.bodySchema ?? null,
4437
4493
  querySchema: dtoContract?.querySchema ?? null,
4438
- stream: dtoContract?.stream ?? false
4494
+ stream: dtoContract?.stream ?? false,
4495
+ multipart: dtoContract?.multipart ?? false
4439
4496
  }
4440
4497
  });
4441
4498
  }
@@ -4748,7 +4805,7 @@ function createChainModuleRenderer(opts) {
4748
4805
  }
4749
4806
 
4750
4807
  // src/index.ts
4751
- var VERSION = "0.12.0";
4808
+ var VERSION = "0.13.0";
4752
4809
  // Annotate the CommonJS export names for ESM import in node:
4753
4810
  0 && (module.exports = {
4754
4811
  CodegenError,