@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/CHANGELOG.md +51 -0
- package/dist/cli/main.cjs +88 -5
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.js +88 -5
- package/dist/cli/main.js.map +1 -1
- package/dist/extension/index.d.cts +1 -1
- package/dist/extension/index.d.ts +1 -1
- package/dist/{index-CxkGbILp.d.cts → index-D8RIMVpU.d.cts} +15 -0
- package/dist/{index-CxkGbILp.d.ts → index-D8RIMVpU.d.ts} +15 -0
- package/dist/index.cjs +88 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +88 -5
- package/dist/index.js.map +1 -1
- package/dist/nest/index.cjs +88 -5
- package/dist/nest/index.cjs.map +1 -1
- package/dist/nest/index.d.cts +1 -1
- package/dist/nest/index.d.ts +1 -1
- package/dist/nest/index.js +88 -5
- package/dist/nest/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -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-
|
|
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-
|
|
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';
|
|
@@ -489,6 +489,21 @@ 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 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.
|
|
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;
|
|
492
507
|
}
|
|
493
508
|
interface ContractDescriptor {
|
|
494
509
|
contractSource: ContractSource;
|
|
@@ -489,6 +489,21 @@ 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 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.
|
|
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;
|
|
492
507
|
}
|
|
493
508
|
interface ContractDescriptor {
|
|
494
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
|
-
|
|
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);
|
|
@@ -851,6 +878,7 @@ function buildRequestModel(c) {
|
|
|
851
878
|
const optsParts = [];
|
|
852
879
|
if (hasQuery) optsParts.push("query: input?.query as Record<string, unknown> | undefined");
|
|
853
880
|
if (hasBody) optsParts.push("body: input?.body");
|
|
881
|
+
if (hasBody && c.contractSource.multipart) optsParts.push("multipart: true");
|
|
854
882
|
const optsExpr = optsParts.length ? `{ ${optsParts.join(", ")} }` : "{}";
|
|
855
883
|
return {
|
|
856
884
|
routeName: c.name,
|
|
@@ -3734,6 +3762,55 @@ function extractParamsType(method, sourceFile, project) {
|
|
|
3734
3762
|
}
|
|
3735
3763
|
return entries.length > 0 ? `{ ${entries.join("; ")} }` : null;
|
|
3736
3764
|
}
|
|
3765
|
+
function extractUploadedFiles(method) {
|
|
3766
|
+
const FILE = "File | Blob";
|
|
3767
|
+
const entries = [];
|
|
3768
|
+
let multipart = false;
|
|
3769
|
+
const hasUploadedFileParam = method.getParameters().some(
|
|
3770
|
+
(p) => p.getDecorators().some((d) => {
|
|
3771
|
+
const name = d.getName();
|
|
3772
|
+
return name === "UploadedFile" || name === "UploadedFiles";
|
|
3773
|
+
})
|
|
3774
|
+
);
|
|
3775
|
+
for (const decorator of method.getDecorators()) {
|
|
3776
|
+
if (decorator.getName() !== "UseInterceptors") continue;
|
|
3777
|
+
for (const arg of decorator.getArguments()) {
|
|
3778
|
+
if (!import_ts_morph7.Node.isCallExpression(arg)) continue;
|
|
3779
|
+
const interceptor = arg.getExpression().getText();
|
|
3780
|
+
const callArgs = arg.getArguments();
|
|
3781
|
+
const firstArg2 = callArgs[0];
|
|
3782
|
+
if (interceptor === "FileInterceptor") {
|
|
3783
|
+
if (firstArg2 && import_ts_morph7.Node.isStringLiteral(firstArg2)) {
|
|
3784
|
+
entries.push(`${firstArg2.getLiteralValue()}: ${FILE}`);
|
|
3785
|
+
multipart = true;
|
|
3786
|
+
}
|
|
3787
|
+
} else if (interceptor === "FilesInterceptor") {
|
|
3788
|
+
if (firstArg2 && import_ts_morph7.Node.isStringLiteral(firstArg2)) {
|
|
3789
|
+
entries.push(`${firstArg2.getLiteralValue()}: Array<${FILE}>`);
|
|
3790
|
+
multipart = true;
|
|
3791
|
+
}
|
|
3792
|
+
} else if (interceptor === "FileFieldsInterceptor") {
|
|
3793
|
+
if (firstArg2 && import_ts_morph7.Node.isArrayLiteralExpression(firstArg2)) {
|
|
3794
|
+
for (const el of firstArg2.getElements()) {
|
|
3795
|
+
if (!import_ts_morph7.Node.isObjectLiteralExpression(el)) continue;
|
|
3796
|
+
const nameProp = el.getProperty("name");
|
|
3797
|
+
if (nameProp && import_ts_morph7.Node.isPropertyAssignment(nameProp)) {
|
|
3798
|
+
const init = nameProp.getInitializer();
|
|
3799
|
+
if (init && import_ts_morph7.Node.isStringLiteral(init)) {
|
|
3800
|
+
entries.push(`${init.getLiteralValue()}: Array<${FILE}>`);
|
|
3801
|
+
}
|
|
3802
|
+
}
|
|
3803
|
+
}
|
|
3804
|
+
multipart = true;
|
|
3805
|
+
}
|
|
3806
|
+
} else if (interceptor === "AnyFilesInterceptor") {
|
|
3807
|
+
multipart = true;
|
|
3808
|
+
}
|
|
3809
|
+
}
|
|
3810
|
+
}
|
|
3811
|
+
if (hasUploadedFileParam) multipart = true;
|
|
3812
|
+
return { fields: entries.length > 0 ? entries.join("; ") : null, multipart };
|
|
3813
|
+
}
|
|
3737
3814
|
function extractResponseType(method, sourceFile, project) {
|
|
3738
3815
|
const apiResponseDecorator = method.getDecorators().find((d) => d.getName() === "ApiResponse" && (apiResponseStatus(d) ?? 0) < 400);
|
|
3739
3816
|
if (apiResponseDecorator) {
|
|
@@ -3868,6 +3945,8 @@ function extractDtoContract(method, sourceFile, project) {
|
|
|
3868
3945
|
let body = extractBodyType(method, sourceFile, project);
|
|
3869
3946
|
const filterInfo = extractApplyFilterInfo(method, sourceFile, project);
|
|
3870
3947
|
const query = extractQueryType(method, sourceFile, project);
|
|
3948
|
+
const uploads = extractUploadedFiles(method);
|
|
3949
|
+
const multipartBody = uploads.fields ? `{ ${uploads.fields} }` : null;
|
|
3871
3950
|
const streamElement = detectStreamElement(method);
|
|
3872
3951
|
const isStream = streamElement !== null;
|
|
3873
3952
|
if (filterInfo && filterInfo.source === "body") {
|
|
@@ -3877,7 +3956,7 @@ function extractDtoContract(method, sourceFile, project) {
|
|
|
3877
3956
|
const paramsType = extractParamsType(method, sourceFile, project);
|
|
3878
3957
|
const response = isStream ? resolveTypeNodeToString(streamElement, sourceFile, project, 3) : extractResponseType(method, sourceFile, project);
|
|
3879
3958
|
const errorInfo = extractErrorType(method, sourceFile, project);
|
|
3880
|
-
if (body === null && query === null && paramsType === null && response === "unknown" && errorInfo === null && filterInfo === null && !isStream) {
|
|
3959
|
+
if (body === null && query === null && paramsType === null && response === "unknown" && errorInfo === null && filterInfo === null && !isStream && !uploads.multipart) {
|
|
3881
3960
|
return null;
|
|
3882
3961
|
}
|
|
3883
3962
|
let bodyRef = null;
|
|
@@ -3950,7 +4029,9 @@ function extractDtoContract(method, sourceFile, project) {
|
|
|
3950
4029
|
formWarnings,
|
|
3951
4030
|
bodySchema,
|
|
3952
4031
|
querySchema,
|
|
3953
|
-
stream: isStream
|
|
4032
|
+
stream: isStream,
|
|
4033
|
+
multipart: uploads.multipart,
|
|
4034
|
+
multipartBody
|
|
3954
4035
|
};
|
|
3955
4036
|
}
|
|
3956
4037
|
function resolveParamClass(method, decoratorName, sourceFile, project) {
|
|
@@ -4435,7 +4516,9 @@ function extractDtoRoute(args) {
|
|
|
4435
4516
|
formWarnings: dtoContract?.formWarnings ?? [],
|
|
4436
4517
|
bodySchema: dtoContract?.bodySchema ?? null,
|
|
4437
4518
|
querySchema: dtoContract?.querySchema ?? null,
|
|
4438
|
-
stream: dtoContract?.stream ?? false
|
|
4519
|
+
stream: dtoContract?.stream ?? false,
|
|
4520
|
+
multipart: dtoContract?.multipart ?? false,
|
|
4521
|
+
multipartBody: dtoContract?.multipartBody ?? null
|
|
4439
4522
|
}
|
|
4440
4523
|
});
|
|
4441
4524
|
}
|
|
@@ -4748,7 +4831,7 @@ function createChainModuleRenderer(opts) {
|
|
|
4748
4831
|
}
|
|
4749
4832
|
|
|
4750
4833
|
// src/index.ts
|
|
4751
|
-
var VERSION = "0.
|
|
4834
|
+
var VERSION = "0.13.1";
|
|
4752
4835
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4753
4836
|
0 && (module.exports = {
|
|
4754
4837
|
CodegenError,
|