@dudousxd/nestjs-codegen 0.12.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.
package/dist/cli/main.js CHANGED
@@ -751,7 +751,15 @@ function emitRouterTypeBlock(tree, indent, outDir, serialization) {
751
751
  const isFilterQuery = c.contractSource.filterSource === "query" && !!c.contractSource.filterFields?.length;
752
752
  const query = queryRef ? queryRef.isArray ? `Array<${queryRef.name}>` : queryRef.name : isFilterQuery ? emitFilterQueryType(c) : c.contractSource.query ?? "never";
753
753
  const bodyRef = c.contractSource.bodyRef;
754
- const body = method === "GET" ? "never" : bodyRef ? bodyRef.isArray ? `Array<${bodyRef.name}>` : bodyRef.name : c.contractSource.body ?? "never";
754
+ let body = method === "GET" ? "never" : bodyRef ? bodyRef.isArray ? `Array<${bodyRef.name}>` : bodyRef.name : c.contractSource.body ?? "never";
755
+ const multipartBody = c.contractSource.multipartBody;
756
+ if (c.contractSource.multipart && multipartBody) {
757
+ if (body === "never") {
758
+ body = multipartBody;
759
+ } else if (!bodyAcceptsAnything(body)) {
760
+ body = `(${body}) & ${multipartBody}`;
761
+ }
762
+ }
755
763
  const response = buildResponseType(c, outDir, serialization);
756
764
  const error = buildErrorType(c);
757
765
  const params = buildParamsType(c.params);
@@ -770,6 +778,25 @@ function emitRouterTypeBlock(tree, indent, outDir, serialization) {
770
778
  }
771
779
  return lines;
772
780
  }
781
+ function topLevelUnionArms(type) {
782
+ const arms = [];
783
+ let depth = 0;
784
+ let start = 0;
785
+ for (let i = 0; i < type.length; i++) {
786
+ const ch = type[i];
787
+ if (ch === "{" || ch === "[" || ch === "<" || ch === "(") depth++;
788
+ else if (ch === "}" || ch === "]" || ch === ">" || ch === ")") depth--;
789
+ else if (ch === "|" && depth === 0) {
790
+ arms.push(type.slice(start, i).trim());
791
+ start = i + 1;
792
+ }
793
+ }
794
+ arms.push(type.slice(start).trim());
795
+ return arms;
796
+ }
797
+ function bodyAcceptsAnything(body) {
798
+ return topLevelUnionArms(body).some((arm) => arm === "unknown" || arm === "any");
799
+ }
773
800
  function buildRequestModel(c) {
774
801
  const m = c.method.toLowerCase();
775
802
  const flat = JSON.stringify(c.name);
@@ -786,6 +813,7 @@ function buildRequestModel(c) {
786
813
  const optsParts = [];
787
814
  if (hasQuery) optsParts.push("query: input?.query as Record<string, unknown> | undefined");
788
815
  if (hasBody) optsParts.push("body: input?.body");
816
+ if (hasBody && c.contractSource.multipart) optsParts.push("multipart: true");
789
817
  const optsExpr = optsParts.length ? `{ ${optsParts.join(", ")} }` : "{}";
790
818
  return {
791
819
  routeName: c.name,
@@ -3681,6 +3709,55 @@ function extractParamsType(method, sourceFile, project) {
3681
3709
  }
3682
3710
  return entries.length > 0 ? `{ ${entries.join("; ")} }` : null;
3683
3711
  }
3712
+ function extractUploadedFiles(method) {
3713
+ const FILE = "File | Blob";
3714
+ const entries = [];
3715
+ let multipart = false;
3716
+ const hasUploadedFileParam = method.getParameters().some(
3717
+ (p) => p.getDecorators().some((d) => {
3718
+ const name = d.getName();
3719
+ return name === "UploadedFile" || name === "UploadedFiles";
3720
+ })
3721
+ );
3722
+ for (const decorator of method.getDecorators()) {
3723
+ if (decorator.getName() !== "UseInterceptors") continue;
3724
+ for (const arg of decorator.getArguments()) {
3725
+ if (!Node6.isCallExpression(arg)) continue;
3726
+ const interceptor = arg.getExpression().getText();
3727
+ const callArgs = arg.getArguments();
3728
+ const firstArg2 = callArgs[0];
3729
+ if (interceptor === "FileInterceptor") {
3730
+ if (firstArg2 && Node6.isStringLiteral(firstArg2)) {
3731
+ entries.push(`${firstArg2.getLiteralValue()}: ${FILE}`);
3732
+ multipart = true;
3733
+ }
3734
+ } else if (interceptor === "FilesInterceptor") {
3735
+ if (firstArg2 && Node6.isStringLiteral(firstArg2)) {
3736
+ entries.push(`${firstArg2.getLiteralValue()}: Array<${FILE}>`);
3737
+ multipart = true;
3738
+ }
3739
+ } else if (interceptor === "FileFieldsInterceptor") {
3740
+ if (firstArg2 && Node6.isArrayLiteralExpression(firstArg2)) {
3741
+ for (const el of firstArg2.getElements()) {
3742
+ if (!Node6.isObjectLiteralExpression(el)) continue;
3743
+ const nameProp = el.getProperty("name");
3744
+ if (nameProp && Node6.isPropertyAssignment(nameProp)) {
3745
+ const init = nameProp.getInitializer();
3746
+ if (init && Node6.isStringLiteral(init)) {
3747
+ entries.push(`${init.getLiteralValue()}: Array<${FILE}>`);
3748
+ }
3749
+ }
3750
+ }
3751
+ multipart = true;
3752
+ }
3753
+ } else if (interceptor === "AnyFilesInterceptor") {
3754
+ multipart = true;
3755
+ }
3756
+ }
3757
+ }
3758
+ if (hasUploadedFileParam) multipart = true;
3759
+ return { fields: entries.length > 0 ? entries.join("; ") : null, multipart };
3760
+ }
3684
3761
  function extractResponseType(method, sourceFile, project) {
3685
3762
  const apiResponseDecorator = method.getDecorators().find((d) => d.getName() === "ApiResponse" && (apiResponseStatus(d) ?? 0) < 400);
3686
3763
  if (apiResponseDecorator) {
@@ -3815,6 +3892,8 @@ function extractDtoContract(method, sourceFile, project) {
3815
3892
  let body = extractBodyType(method, sourceFile, project);
3816
3893
  const filterInfo = extractApplyFilterInfo(method, sourceFile, project);
3817
3894
  const query = extractQueryType(method, sourceFile, project);
3895
+ const uploads = extractUploadedFiles(method);
3896
+ const multipartBody = uploads.fields ? `{ ${uploads.fields} }` : null;
3818
3897
  const streamElement = detectStreamElement(method);
3819
3898
  const isStream = streamElement !== null;
3820
3899
  if (filterInfo && filterInfo.source === "body") {
@@ -3824,7 +3903,7 @@ function extractDtoContract(method, sourceFile, project) {
3824
3903
  const paramsType = extractParamsType(method, sourceFile, project);
3825
3904
  const response = isStream ? resolveTypeNodeToString(streamElement, sourceFile, project, 3) : extractResponseType(method, sourceFile, project);
3826
3905
  const errorInfo = extractErrorType(method, sourceFile, project);
3827
- if (body === null && query === null && paramsType === null && response === "unknown" && errorInfo === null && filterInfo === null && !isStream) {
3906
+ if (body === null && query === null && paramsType === null && response === "unknown" && errorInfo === null && filterInfo === null && !isStream && !uploads.multipart) {
3828
3907
  return null;
3829
3908
  }
3830
3909
  let bodyRef = null;
@@ -3897,7 +3976,9 @@ function extractDtoContract(method, sourceFile, project) {
3897
3976
  formWarnings,
3898
3977
  bodySchema,
3899
3978
  querySchema,
3900
- stream: isStream
3979
+ stream: isStream,
3980
+ multipart: uploads.multipart,
3981
+ multipartBody
3901
3982
  };
3902
3983
  }
3903
3984
  function resolveParamClass(method, decoratorName, sourceFile, project) {
@@ -4382,7 +4463,9 @@ function extractDtoRoute(args) {
4382
4463
  formWarnings: dtoContract?.formWarnings ?? [],
4383
4464
  bodySchema: dtoContract?.bodySchema ?? null,
4384
4465
  querySchema: dtoContract?.querySchema ?? null,
4385
- stream: dtoContract?.stream ?? false
4466
+ stream: dtoContract?.stream ?? false,
4467
+ multipart: dtoContract?.multipart ?? false,
4468
+ multipartBody: dtoContract?.multipartBody ?? null
4386
4469
  }
4387
4470
  });
4388
4471
  }
@@ -4612,7 +4695,7 @@ async function watch(config, onChange, options = {}) {
4612
4695
  }
4613
4696
 
4614
4697
  // src/index.ts
4615
- var VERSION = "0.12.0";
4698
+ var VERSION = "0.13.1";
4616
4699
 
4617
4700
  // src/cli/codegen.ts
4618
4701
  async function runCodegen(opts = {}) {