@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.
@@ -1589,6 +1589,55 @@ function extractParamsType(method, sourceFile, project) {
1589
1589
  }
1590
1590
  return entries.length > 0 ? `{ ${entries.join("; ")} }` : null;
1591
1591
  }
1592
+ function extractUploadedFiles(method) {
1593
+ const FILE = "File | Blob";
1594
+ const entries = [];
1595
+ let multipart = false;
1596
+ const hasUploadedFileParam = method.getParameters().some(
1597
+ (p) => p.getDecorators().some((d) => {
1598
+ const name = d.getName();
1599
+ return name === "UploadedFile" || name === "UploadedFiles";
1600
+ })
1601
+ );
1602
+ for (const decorator of method.getDecorators()) {
1603
+ if (decorator.getName() !== "UseInterceptors") continue;
1604
+ for (const arg of decorator.getArguments()) {
1605
+ if (!import_ts_morph5.Node.isCallExpression(arg)) continue;
1606
+ const interceptor = arg.getExpression().getText();
1607
+ const callArgs = arg.getArguments();
1608
+ const firstArg2 = callArgs[0];
1609
+ if (interceptor === "FileInterceptor") {
1610
+ if (firstArg2 && import_ts_morph5.Node.isStringLiteral(firstArg2)) {
1611
+ entries.push(`${firstArg2.getLiteralValue()}: ${FILE}`);
1612
+ multipart = true;
1613
+ }
1614
+ } else if (interceptor === "FilesInterceptor") {
1615
+ if (firstArg2 && import_ts_morph5.Node.isStringLiteral(firstArg2)) {
1616
+ entries.push(`${firstArg2.getLiteralValue()}: Array<${FILE}>`);
1617
+ multipart = true;
1618
+ }
1619
+ } else if (interceptor === "FileFieldsInterceptor") {
1620
+ if (firstArg2 && import_ts_morph5.Node.isArrayLiteralExpression(firstArg2)) {
1621
+ for (const el of firstArg2.getElements()) {
1622
+ if (!import_ts_morph5.Node.isObjectLiteralExpression(el)) continue;
1623
+ const nameProp = el.getProperty("name");
1624
+ if (nameProp && import_ts_morph5.Node.isPropertyAssignment(nameProp)) {
1625
+ const init = nameProp.getInitializer();
1626
+ if (init && import_ts_morph5.Node.isStringLiteral(init)) {
1627
+ entries.push(`${init.getLiteralValue()}: Array<${FILE}>`);
1628
+ }
1629
+ }
1630
+ }
1631
+ multipart = true;
1632
+ }
1633
+ } else if (interceptor === "AnyFilesInterceptor") {
1634
+ multipart = true;
1635
+ }
1636
+ }
1637
+ }
1638
+ if (hasUploadedFileParam) multipart = true;
1639
+ return { fields: entries.length > 0 ? entries.join("; ") : null, multipart };
1640
+ }
1592
1641
  function extractResponseType(method, sourceFile, project) {
1593
1642
  const apiResponseDecorator = method.getDecorators().find((d) => d.getName() === "ApiResponse" && (apiResponseStatus(d) ?? 0) < 400);
1594
1643
  if (apiResponseDecorator) {
@@ -1723,6 +1772,8 @@ function extractDtoContract(method, sourceFile, project) {
1723
1772
  let body = extractBodyType(method, sourceFile, project);
1724
1773
  const filterInfo = extractApplyFilterInfo(method, sourceFile, project);
1725
1774
  const query = extractQueryType(method, sourceFile, project);
1775
+ const uploads = extractUploadedFiles(method);
1776
+ const multipartBody = uploads.fields ? `{ ${uploads.fields} }` : null;
1726
1777
  const streamElement = detectStreamElement(method);
1727
1778
  const isStream = streamElement !== null;
1728
1779
  if (filterInfo && filterInfo.source === "body") {
@@ -1732,7 +1783,7 @@ function extractDtoContract(method, sourceFile, project) {
1732
1783
  const paramsType = extractParamsType(method, sourceFile, project);
1733
1784
  const response = isStream ? resolveTypeNodeToString(streamElement, sourceFile, project, 3) : extractResponseType(method, sourceFile, project);
1734
1785
  const errorInfo = extractErrorType(method, sourceFile, project);
1735
- if (body === null && query === null && paramsType === null && response === "unknown" && errorInfo === null && filterInfo === null && !isStream) {
1786
+ if (body === null && query === null && paramsType === null && response === "unknown" && errorInfo === null && filterInfo === null && !isStream && !uploads.multipart) {
1736
1787
  return null;
1737
1788
  }
1738
1789
  let bodyRef = null;
@@ -1805,7 +1856,9 @@ function extractDtoContract(method, sourceFile, project) {
1805
1856
  formWarnings,
1806
1857
  bodySchema,
1807
1858
  querySchema,
1808
- stream: isStream
1859
+ stream: isStream,
1860
+ multipart: uploads.multipart,
1861
+ multipartBody
1809
1862
  };
1810
1863
  }
1811
1864
  function resolveParamClass(method, decoratorName, sourceFile, project) {
@@ -2272,7 +2325,9 @@ function extractDtoRoute(args) {
2272
2325
  formWarnings: dtoContract?.formWarnings ?? [],
2273
2326
  bodySchema: dtoContract?.bodySchema ?? null,
2274
2327
  querySchema: dtoContract?.querySchema ?? null,
2275
- stream: dtoContract?.stream ?? false
2328
+ stream: dtoContract?.stream ?? false,
2329
+ multipart: dtoContract?.multipart ?? false,
2330
+ multipartBody: dtoContract?.multipartBody ?? null
2276
2331
  }
2277
2332
  });
2278
2333
  }
@@ -2883,7 +2938,15 @@ function emitRouterTypeBlock(tree, indent, outDir, serialization) {
2883
2938
  const isFilterQuery = c.contractSource.filterSource === "query" && !!c.contractSource.filterFields?.length;
2884
2939
  const query = queryRef ? queryRef.isArray ? `Array<${queryRef.name}>` : queryRef.name : isFilterQuery ? emitFilterQueryType(c) : c.contractSource.query ?? "never";
2885
2940
  const bodyRef = c.contractSource.bodyRef;
2886
- const body = method === "GET" ? "never" : bodyRef ? bodyRef.isArray ? `Array<${bodyRef.name}>` : bodyRef.name : c.contractSource.body ?? "never";
2941
+ let body = method === "GET" ? "never" : bodyRef ? bodyRef.isArray ? `Array<${bodyRef.name}>` : bodyRef.name : c.contractSource.body ?? "never";
2942
+ const multipartBody = c.contractSource.multipartBody;
2943
+ if (c.contractSource.multipart && multipartBody) {
2944
+ if (body === "never") {
2945
+ body = multipartBody;
2946
+ } else if (!bodyAcceptsAnything(body)) {
2947
+ body = `(${body}) & ${multipartBody}`;
2948
+ }
2949
+ }
2887
2950
  const response = buildResponseType(c, outDir, serialization);
2888
2951
  const error = buildErrorType(c);
2889
2952
  const params = buildParamsType(c.params);
@@ -2902,6 +2965,25 @@ function emitRouterTypeBlock(tree, indent, outDir, serialization) {
2902
2965
  }
2903
2966
  return lines;
2904
2967
  }
2968
+ function topLevelUnionArms(type) {
2969
+ const arms = [];
2970
+ let depth = 0;
2971
+ let start = 0;
2972
+ for (let i = 0; i < type.length; i++) {
2973
+ const ch = type[i];
2974
+ if (ch === "{" || ch === "[" || ch === "<" || ch === "(") depth++;
2975
+ else if (ch === "}" || ch === "]" || ch === ">" || ch === ")") depth--;
2976
+ else if (ch === "|" && depth === 0) {
2977
+ arms.push(type.slice(start, i).trim());
2978
+ start = i + 1;
2979
+ }
2980
+ }
2981
+ arms.push(type.slice(start).trim());
2982
+ return arms;
2983
+ }
2984
+ function bodyAcceptsAnything(body) {
2985
+ return topLevelUnionArms(body).some((arm) => arm === "unknown" || arm === "any");
2986
+ }
2905
2987
  function buildRequestModel(c) {
2906
2988
  const m = c.method.toLowerCase();
2907
2989
  const flat = JSON.stringify(c.name);
@@ -2918,6 +3000,7 @@ function buildRequestModel(c) {
2918
3000
  const optsParts = [];
2919
3001
  if (hasQuery) optsParts.push("query: input?.query as Record<string, unknown> | undefined");
2920
3002
  if (hasBody) optsParts.push("body: input?.body");
3003
+ if (hasBody && c.contractSource.multipart) optsParts.push("multipart: true");
2921
3004
  const optsExpr = optsParts.length ? `{ ${optsParts.join(", ")} }` : "{}";
2922
3005
  return {
2923
3006
  routeName: c.name,
@@ -4264,7 +4347,7 @@ async function acquireLock(outDir) {
4264
4347
  }
4265
4348
 
4266
4349
  // src/index.ts
4267
- var VERSION = "0.12.0";
4350
+ var VERSION = "0.13.1";
4268
4351
 
4269
4352
  // src/generate-manifest.ts
4270
4353
  var MANIFEST_FILE = ".codegen-manifest.json";