@dudousxd/nestjs-codegen 0.14.2 → 0.16.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.
- package/CHANGELOG.md +49 -0
- package/dist/cli/main.cjs +207 -57
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.js +206 -56
- package/dist/cli/main.js.map +1 -1
- package/dist/extension/index.cjs.map +1 -1
- package/dist/extension/index.d.cts +1 -1
- package/dist/extension/index.d.ts +1 -1
- package/dist/extension/index.js.map +1 -1
- package/dist/{index-DT8SgPxp.d.cts → index-LQNP7Ms3.d.cts} +30 -0
- package/dist/{index-DT8SgPxp.d.ts → index-LQNP7Ms3.d.ts} +30 -0
- package/dist/index.cjs +207 -57
- 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 +206 -56
- package/dist/index.js.map +1 -1
- package/dist/nest/index.cjs +227 -59
- package/dist/nest/index.cjs.map +1 -1
- package/dist/nest/index.d.cts +56 -3
- package/dist/nest/index.d.ts +56 -3
- package/dist/nest/index.js +222 -57
- package/dist/nest/index.js.map +1 -1
- package/package.json +1 -1
package/dist/nest/index.js
CHANGED
|
@@ -702,6 +702,9 @@ function buildErrorType(c) {
|
|
|
702
702
|
}
|
|
703
703
|
return c.contractSource.error ?? "unknown";
|
|
704
704
|
}
|
|
705
|
+
function filterFieldLiterals(fields) {
|
|
706
|
+
return fields?.length ? fields.map((f) => JSON.stringify(f)) : [];
|
|
707
|
+
}
|
|
705
708
|
function emitRouterTypeBlock(tree, indent, outDir, serialization) {
|
|
706
709
|
const pad = " ".repeat(indent);
|
|
707
710
|
const lines = [];
|
|
@@ -728,7 +731,8 @@ function emitRouterTypeBlock(tree, indent, outDir, serialization) {
|
|
|
728
731
|
const params = buildParamsType(c.params);
|
|
729
732
|
const safeMethod = JSON.stringify(method);
|
|
730
733
|
const safeUrl = JSON.stringify(c.path);
|
|
731
|
-
const
|
|
734
|
+
const filterLiterals = filterFieldLiterals(c.contractSource.filterFields);
|
|
735
|
+
const filterFields = filterLiterals.length ? filterLiterals.join(" | ") : "never";
|
|
732
736
|
const stream = c.contractSource.stream ? "true" : "false";
|
|
733
737
|
const binary = c.contractSource.binaryResponse ? "true" : "false";
|
|
734
738
|
lines.push(
|
|
@@ -768,6 +772,7 @@ function buildRequestModel(c) {
|
|
|
768
772
|
const TA = buildRouterTypeAccess(c.name);
|
|
769
773
|
const withParams = hasPathParams(c.params);
|
|
770
774
|
const { isGet, isQuery, hasBody, hasQuery } = requestShape(c.route);
|
|
775
|
+
const filterLiterals = filterFieldLiterals(c.contractSource.filterFields);
|
|
771
776
|
const fields = [];
|
|
772
777
|
if (withParams) fields.push(`params: ${TA}['params']`);
|
|
773
778
|
if (hasQuery) fields.push(`query?: ${TA}['query']`);
|
|
@@ -797,7 +802,12 @@ function buildRequestModel(c) {
|
|
|
797
802
|
// (`[name]` rather than `[name, undefined]`) so the bare `.queryKey()` is a
|
|
798
803
|
// clean prefix that partial-matches every parametrized variant — making it
|
|
799
804
|
// directly usable for `invalidateQueries`.
|
|
800
|
-
queryKeyExpr: `(input === undefined ? [${flat}] as const : [${flat}, input] as const)
|
|
805
|
+
queryKeyExpr: `(input === undefined ? [${flat}] as const : [${flat}, input] as const)`,
|
|
806
|
+
// Runtime counterpart to the type-level `filterFields` union: the same
|
|
807
|
+
// discovered field list, emitted as a literal `[...] as const` so apps can
|
|
808
|
+
// validate a dynamic/user-supplied field string with `isFilterField(...)`
|
|
809
|
+
// instead of casting. Omitted for routes with no filter.
|
|
810
|
+
...filterLiterals.length ? { filterFieldsExpr: `[${filterLiterals.join(", ")}] as const` } : {}
|
|
801
811
|
};
|
|
802
812
|
}
|
|
803
813
|
function renderFetcherRequest(req, binaryResponse) {
|
|
@@ -832,9 +842,24 @@ function emitReqHelper() {
|
|
|
832
842
|
""
|
|
833
843
|
];
|
|
834
844
|
}
|
|
845
|
+
function emitFilterFieldGuard() {
|
|
846
|
+
return [
|
|
847
|
+
"/** Runtime guard: narrows `value` to one of the leaf's `filterFields` (a `readonly K[] as const`), so a dynamic field string can be passed to `.where()` without a cast. */",
|
|
848
|
+
"export function isFilterField<const K extends string>(",
|
|
849
|
+
" fields: readonly K[],",
|
|
850
|
+
" value: string,",
|
|
851
|
+
"): value is K {",
|
|
852
|
+
" return (fields as readonly string[]).includes(value);",
|
|
853
|
+
"}",
|
|
854
|
+
""
|
|
855
|
+
];
|
|
856
|
+
}
|
|
835
857
|
function renderLeaf(pad, objKey, req, requestExpr, members, streamExpr) {
|
|
836
858
|
const lines = [`${pad}${objKey}: (input?: ${req.inputType}) => ({`];
|
|
837
859
|
lines.push(`${pad} ...__req<${req.responseType}>(() => ${requestExpr}),`);
|
|
860
|
+
if (req.filterFieldsExpr) {
|
|
861
|
+
lines.push(`${pad} filterFields: ${req.filterFieldsExpr},`);
|
|
862
|
+
}
|
|
838
863
|
if (streamExpr) {
|
|
839
864
|
lines.push(`${pad} stream: () => ${streamExpr},`);
|
|
840
865
|
}
|
|
@@ -1106,6 +1131,9 @@ function buildApiFile(routes, outDir, opts = {}) {
|
|
|
1106
1131
|
lines.push("};");
|
|
1107
1132
|
lines.push("");
|
|
1108
1133
|
lines.push(...emitReqHelper());
|
|
1134
|
+
if (contracted.some((r) => r.contract?.contractSource.filterFields?.length)) {
|
|
1135
|
+
lines.push(...emitFilterFieldGuard());
|
|
1136
|
+
}
|
|
1109
1137
|
lines.push("export function createApi(fetcher: Fetcher) {");
|
|
1110
1138
|
lines.push(" return {");
|
|
1111
1139
|
lines.push(
|
|
@@ -2206,8 +2234,8 @@ import chokidar from "chokidar";
|
|
|
2206
2234
|
import { join as join13, resolve as resolve3 } from "path";
|
|
2207
2235
|
import fg2 from "fast-glob";
|
|
2208
2236
|
import {
|
|
2209
|
-
Node as
|
|
2210
|
-
Project as
|
|
2237
|
+
Node as Node9,
|
|
2238
|
+
Project as Project4
|
|
2211
2239
|
} from "ts-morph";
|
|
2212
2240
|
|
|
2213
2241
|
// src/discovery/dto-type-resolver.ts
|
|
@@ -3199,6 +3227,12 @@ function toFilterFieldType(name, r) {
|
|
|
3199
3227
|
}
|
|
3200
3228
|
|
|
3201
3229
|
// src/discovery/filter-for.ts
|
|
3230
|
+
function resolveMixinEntityClass(mixin, project) {
|
|
3231
|
+
const entityArg = mixin?.classArgs[0];
|
|
3232
|
+
if (!entityArg) return void 0;
|
|
3233
|
+
const file = project.getSourceFile(entityArg.filePath) ?? project.addSourceFileAtPathIfExists(entityArg.filePath);
|
|
3234
|
+
return file?.getClass(entityArg.name);
|
|
3235
|
+
}
|
|
3202
3236
|
function classifyFilterForHint(typeInit) {
|
|
3203
3237
|
if (Node5.isStringLiteral(typeInit)) {
|
|
3204
3238
|
switch (typeInit.getLiteralValue()) {
|
|
@@ -3272,7 +3306,9 @@ function extractFilterForHints(classDecl, project) {
|
|
|
3272
3306
|
}
|
|
3273
3307
|
return hints;
|
|
3274
3308
|
}
|
|
3275
|
-
function extractApplyFilterInfo(method, sourceFile, project) {
|
|
3309
|
+
function extractApplyFilterInfo(method, sourceFile, project, mixin) {
|
|
3310
|
+
const declFile = method.getSourceFile();
|
|
3311
|
+
const mixinEntity = resolveMixinEntityClass(mixin, project);
|
|
3276
3312
|
for (const param of method.getParameters()) {
|
|
3277
3313
|
const filterDecorator = param.getDecorators().find((d) => d.getName() === "ApplyFilter");
|
|
3278
3314
|
if (!filterDecorator) continue;
|
|
@@ -3292,12 +3328,12 @@ function extractApplyFilterInfo(method, sourceFile, project) {
|
|
|
3292
3328
|
}
|
|
3293
3329
|
}
|
|
3294
3330
|
const filterClassName = filterClassArg.getText();
|
|
3295
|
-
const resolved = findType(filterClassName,
|
|
3296
|
-
|
|
3297
|
-
|
|
3331
|
+
const resolved = findType(filterClassName, declFile, project);
|
|
3332
|
+
const classDecl = resolved?.kind === "class" ? resolved.decl : resolveLocalClassDeclaration(filterClassArg);
|
|
3333
|
+
if (classDecl) {
|
|
3298
3334
|
let fieldTypes = extractClassPropertyTypes(classDecl, project);
|
|
3299
3335
|
if (fieldTypes.length === 0) {
|
|
3300
|
-
fieldTypes = extractFilterableEntityFields(classDecl, project);
|
|
3336
|
+
fieldTypes = extractFilterableEntityFields(classDecl, project, mixinEntity);
|
|
3301
3337
|
}
|
|
3302
3338
|
const filterForHints = extractFilterForHints(classDecl, project);
|
|
3303
3339
|
if (filterForHints.size > 0) {
|
|
@@ -3382,22 +3418,29 @@ function extractClassPropertyTypes(classDecl, project) {
|
|
|
3382
3418
|
}
|
|
3383
3419
|
return fields;
|
|
3384
3420
|
}
|
|
3385
|
-
function
|
|
3421
|
+
function resolveLocalClassDeclaration(identifier) {
|
|
3422
|
+
if (!Node5.isIdentifier(identifier)) return void 0;
|
|
3423
|
+
return identifier.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 && Node5.isClassDeclaration(n));
|
|
3424
|
+
}
|
|
3425
|
+
function resolveDeclaredEntity(optionsArg, filterClass, project) {
|
|
3426
|
+
if (!Node5.isObjectLiteralExpression(optionsArg)) return void 0;
|
|
3427
|
+
const entityProp = optionsArg.getProperty("entity");
|
|
3428
|
+
if (!entityProp || !Node5.isPropertyAssignment(entityProp)) return void 0;
|
|
3429
|
+
const entityInit = entityProp.getInitializer();
|
|
3430
|
+
if (!entityInit || !Node5.isIdentifier(entityInit)) return void 0;
|
|
3431
|
+
const resolved = findType(entityInit.getText(), filterClass.getSourceFile(), project);
|
|
3432
|
+
if (!resolved || resolved.kind !== "class") return void 0;
|
|
3433
|
+
return resolved.decl;
|
|
3434
|
+
}
|
|
3435
|
+
function extractFilterableEntityFields(filterClass, project, entityOverride) {
|
|
3386
3436
|
const filterableDecorator = filterClass.getDecorators().find((d) => d.getName() === "Filterable");
|
|
3387
3437
|
if (!filterableDecorator) return [];
|
|
3388
3438
|
const args = filterableDecorator.getArguments();
|
|
3389
3439
|
if (args.length === 0) return [];
|
|
3390
3440
|
const optionsArg = args[0];
|
|
3391
3441
|
if (!Node5.isObjectLiteralExpression(optionsArg)) return [];
|
|
3392
|
-
const
|
|
3393
|
-
if (!
|
|
3394
|
-
const entityInit = entityProp.getInitializer();
|
|
3395
|
-
if (!entityInit || !Node5.isIdentifier(entityInit)) return [];
|
|
3396
|
-
const entityName = entityInit.getText();
|
|
3397
|
-
const filterSourceFile = filterClass.getSourceFile();
|
|
3398
|
-
const resolvedEntity = findType(entityName, filterSourceFile, project);
|
|
3399
|
-
if (!resolvedEntity || resolvedEntity.kind !== "class") return [];
|
|
3400
|
-
const entityDecl = resolvedEntity.decl;
|
|
3442
|
+
const entityDecl = entityOverride ?? resolveDeclaredEntity(optionsArg, filterClass, project);
|
|
3443
|
+
if (!entityDecl) return [];
|
|
3401
3444
|
const fields = collectEntityFields(
|
|
3402
3445
|
entityDecl,
|
|
3403
3446
|
entityDecl.getSourceFile(),
|
|
@@ -3822,9 +3865,9 @@ function unwrapNamedContainer(node, names) {
|
|
|
3822
3865
|
}
|
|
3823
3866
|
return node;
|
|
3824
3867
|
}
|
|
3825
|
-
function extractDtoContract(method, sourceFile, project) {
|
|
3868
|
+
function extractDtoContract(method, sourceFile, project, mixin) {
|
|
3826
3869
|
let body = extractBodyType(method, sourceFile, project);
|
|
3827
|
-
const filterInfo = extractApplyFilterInfo(method, sourceFile, project);
|
|
3870
|
+
const filterInfo = extractApplyFilterInfo(method, sourceFile, project, mixin);
|
|
3828
3871
|
const query = extractQueryType(method, sourceFile, project);
|
|
3829
3872
|
const uploads = extractUploadedFiles(method);
|
|
3830
3873
|
const multipartBody = uploads.fields ? `{ ${uploads.fields} }` : null;
|
|
@@ -3934,12 +3977,94 @@ function resolveParamClass(method, decoratorName, sourceFile, project) {
|
|
|
3934
3977
|
return null;
|
|
3935
3978
|
}
|
|
3936
3979
|
|
|
3980
|
+
// src/discovery/heritage.ts
|
|
3981
|
+
import { Node as Node7, Project as Project3 } from "ts-morph";
|
|
3982
|
+
function unwrapExpression(node) {
|
|
3983
|
+
let current = node;
|
|
3984
|
+
while (Node7.isAsExpression(current) || Node7.isSatisfiesExpression(current) || Node7.isParenthesizedExpression(current) || Node7.isTypeAssertion(current)) {
|
|
3985
|
+
current = current.getExpression();
|
|
3986
|
+
}
|
|
3987
|
+
return current;
|
|
3988
|
+
}
|
|
3989
|
+
function resolveReturnedClass(factoryDecl) {
|
|
3990
|
+
const body = factoryDecl.getBody();
|
|
3991
|
+
if (!body) return void 0;
|
|
3992
|
+
const returns = body.getDescendants().filter((n) => Node7.isReturnStatement(n));
|
|
3993
|
+
for (const ret of returns) {
|
|
3994
|
+
const expr = ret.getExpression();
|
|
3995
|
+
if (!expr) continue;
|
|
3996
|
+
const inner = unwrapExpression(expr);
|
|
3997
|
+
if (Node7.isClassExpression(inner)) {
|
|
3998
|
+
return inner;
|
|
3999
|
+
}
|
|
4000
|
+
if (Node7.isIdentifier(inner)) {
|
|
4001
|
+
const name = inner.getText();
|
|
4002
|
+
const decl = body.getDescendants().find((n) => Node7.isClassDeclaration(n) && n.getName() === name);
|
|
4003
|
+
if (decl) return decl;
|
|
4004
|
+
}
|
|
4005
|
+
}
|
|
4006
|
+
return void 0;
|
|
4007
|
+
}
|
|
4008
|
+
function resolveInheritedMethods(cls) {
|
|
4009
|
+
const expr = cls.getExtends()?.getExpression();
|
|
4010
|
+
if (!expr || !Node7.isCallExpression(expr)) return void 0;
|
|
4011
|
+
const callee = expr.getExpression();
|
|
4012
|
+
if (!Node7.isIdentifier(callee)) return void 0;
|
|
4013
|
+
const factoryDecl = callee.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => Node7.isFunctionDeclaration(n));
|
|
4014
|
+
if (!factoryDecl) return void 0;
|
|
4015
|
+
const returnedClass = resolveReturnedClass(factoryDecl);
|
|
4016
|
+
if (!returnedClass) return void 0;
|
|
4017
|
+
const classArgs = [];
|
|
4018
|
+
for (const arg of expr.getArguments()) {
|
|
4019
|
+
if (!Node7.isIdentifier(arg)) continue;
|
|
4020
|
+
const decl = arg.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => Node7.isClassDeclaration(n));
|
|
4021
|
+
if (decl) {
|
|
4022
|
+
classArgs.push({
|
|
4023
|
+
name: decl.getName() ?? arg.getText(),
|
|
4024
|
+
filePath: decl.getSourceFile().getFilePath()
|
|
4025
|
+
});
|
|
4026
|
+
}
|
|
4027
|
+
}
|
|
4028
|
+
return {
|
|
4029
|
+
methods: returnedClass.getMethods(),
|
|
4030
|
+
factoryName: callee.getText(),
|
|
4031
|
+
factoryFilePath: factoryDecl.getSourceFile().getFilePath(),
|
|
4032
|
+
classArgs
|
|
4033
|
+
};
|
|
4034
|
+
}
|
|
4035
|
+
var mixinTypeProject;
|
|
4036
|
+
function getMixinTypeProject() {
|
|
4037
|
+
mixinTypeProject ??= new Project3({
|
|
4038
|
+
skipAddingFilesFromTsConfig: true,
|
|
4039
|
+
compilerOptions: { strict: true }
|
|
4040
|
+
});
|
|
4041
|
+
return mixinTypeProject;
|
|
4042
|
+
}
|
|
4043
|
+
function clearMixinTypeProject() {
|
|
4044
|
+
mixinTypeProject = void 0;
|
|
4045
|
+
}
|
|
4046
|
+
function resolveInstantiatedReturnType(cls, methodName) {
|
|
4047
|
+
const filePath = cls.getSourceFile().getFilePath();
|
|
4048
|
+
const className = cls.getName();
|
|
4049
|
+
if (!className) return void 0;
|
|
4050
|
+
const project = getMixinTypeProject();
|
|
4051
|
+
const sourceFile = project.getSourceFile(filePath) ?? project.addSourceFileAtPathIfExists(filePath);
|
|
4052
|
+
const typedCls = sourceFile?.getClass(className);
|
|
4053
|
+
if (!typedCls) return void 0;
|
|
4054
|
+
const prop = typedCls.getType().getProperty(methodName);
|
|
4055
|
+
if (!prop) return void 0;
|
|
4056
|
+
const returnType = prop.getTypeAtLocation(typedCls).getCallSignatures()[0]?.getReturnType();
|
|
4057
|
+
if (!returnType) return void 0;
|
|
4058
|
+
const unwrapped = returnType.getSymbol()?.getName() === "Promise" ? returnType.getTypeArguments()[0] ?? returnType : returnType;
|
|
4059
|
+
return unwrapped.getText(typedCls);
|
|
4060
|
+
}
|
|
4061
|
+
|
|
3937
4062
|
// src/discovery/zod-ast-to-ts.ts
|
|
3938
|
-
import { Node as
|
|
4063
|
+
import { Node as Node8, SyntaxKind as SyntaxKind4 } from "ts-morph";
|
|
3939
4064
|
function zodAstToTs(node) {
|
|
3940
|
-
if (!
|
|
4065
|
+
if (!Node8.isCallExpression(node)) return "unknown";
|
|
3941
4066
|
const expr = node.getExpression();
|
|
3942
|
-
if (
|
|
4067
|
+
if (Node8.isPropertyAccessExpression(expr)) {
|
|
3943
4068
|
const methodName = expr.getName();
|
|
3944
4069
|
const receiver = expr.getExpression();
|
|
3945
4070
|
if (methodName === "optional") {
|
|
@@ -3963,17 +4088,17 @@ function zodAstToTs(node) {
|
|
|
3963
4088
|
case "literal": {
|
|
3964
4089
|
const lit = args[0];
|
|
3965
4090
|
if (!lit) return "unknown";
|
|
3966
|
-
if (
|
|
3967
|
-
if (
|
|
4091
|
+
if (Node8.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
|
|
4092
|
+
if (Node8.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
|
|
3968
4093
|
if (lit.getKind() === SyntaxKind4.TrueKeyword) return "true";
|
|
3969
4094
|
if (lit.getKind() === SyntaxKind4.FalseKeyword) return "false";
|
|
3970
4095
|
return "unknown";
|
|
3971
4096
|
}
|
|
3972
4097
|
case "enum": {
|
|
3973
4098
|
const arrArg = args[0];
|
|
3974
|
-
if (!arrArg || !
|
|
4099
|
+
if (!arrArg || !Node8.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
3975
4100
|
const members = arrArg.getElements().map(
|
|
3976
|
-
(el) =>
|
|
4101
|
+
(el) => Node8.isStringLiteral(el) ? JSON.stringify(el.getLiteralValue()) : "unknown"
|
|
3977
4102
|
);
|
|
3978
4103
|
return members.join(" | ");
|
|
3979
4104
|
}
|
|
@@ -3984,10 +4109,10 @@ function zodAstToTs(node) {
|
|
|
3984
4109
|
}
|
|
3985
4110
|
case "object": {
|
|
3986
4111
|
const objArg = args[0];
|
|
3987
|
-
if (!objArg || !
|
|
4112
|
+
if (!objArg || !Node8.isObjectLiteralExpression(objArg)) return "unknown";
|
|
3988
4113
|
const lines = [];
|
|
3989
4114
|
for (const prop of objArg.getProperties()) {
|
|
3990
|
-
if (!
|
|
4115
|
+
if (!Node8.isPropertyAssignment(prop)) continue;
|
|
3991
4116
|
const key = prop.getName();
|
|
3992
4117
|
const valNode = prop.getInitializer();
|
|
3993
4118
|
if (!valNode) continue;
|
|
@@ -3999,7 +4124,7 @@ function zodAstToTs(node) {
|
|
|
3999
4124
|
}
|
|
4000
4125
|
case "union": {
|
|
4001
4126
|
const arrArg = args[0];
|
|
4002
|
-
if (!arrArg || !
|
|
4127
|
+
if (!arrArg || !Node8.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
4003
4128
|
return arrArg.getElements().map(zodAstToTs).join(" | ");
|
|
4004
4129
|
}
|
|
4005
4130
|
case "record": {
|
|
@@ -4009,7 +4134,7 @@ function zodAstToTs(node) {
|
|
|
4009
4134
|
}
|
|
4010
4135
|
case "tuple": {
|
|
4011
4136
|
const arrArg = args[0];
|
|
4012
|
-
if (!arrArg || !
|
|
4137
|
+
if (!arrArg || !Node8.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
4013
4138
|
return `[${arrArg.getElements().map(zodAstToTs).join(", ")}]`;
|
|
4014
4139
|
}
|
|
4015
4140
|
default:
|
|
@@ -4019,18 +4144,18 @@ function zodAstToTs(node) {
|
|
|
4019
4144
|
return "unknown";
|
|
4020
4145
|
}
|
|
4021
4146
|
function isOptionalChain(node) {
|
|
4022
|
-
if (!
|
|
4147
|
+
if (!Node8.isCallExpression(node)) return false;
|
|
4023
4148
|
const expr = node.getExpression();
|
|
4024
|
-
return
|
|
4149
|
+
return Node8.isPropertyAccessExpression(expr) && expr.getName() === "optional";
|
|
4025
4150
|
}
|
|
4026
4151
|
function parseDefineContractCall(callExpr) {
|
|
4027
|
-
if (!
|
|
4152
|
+
if (!Node8.isCallExpression(callExpr)) return null;
|
|
4028
4153
|
const callee = callExpr.getExpression();
|
|
4029
|
-
const calleeName =
|
|
4154
|
+
const calleeName = Node8.isIdentifier(callee) ? callee.getText() : Node8.isPropertyAccessExpression(callee) ? callee.getName() : "";
|
|
4030
4155
|
if (calleeName !== "defineContract") return null;
|
|
4031
4156
|
const args = callExpr.getArguments();
|
|
4032
4157
|
const optsArg = args[0];
|
|
4033
|
-
if (!optsArg || !
|
|
4158
|
+
if (!optsArg || !Node8.isObjectLiteralExpression(optsArg)) return null;
|
|
4034
4159
|
let query = null;
|
|
4035
4160
|
let body = null;
|
|
4036
4161
|
let response = "unknown";
|
|
@@ -4038,7 +4163,7 @@ function parseDefineContractCall(callExpr) {
|
|
|
4038
4163
|
let bodyZodText = null;
|
|
4039
4164
|
let queryZodText = null;
|
|
4040
4165
|
for (const prop of optsArg.getProperties()) {
|
|
4041
|
-
if (!
|
|
4166
|
+
if (!Node8.isPropertyAssignment(prop)) continue;
|
|
4042
4167
|
const propName = prop.getName();
|
|
4043
4168
|
const val = prop.getInitializer();
|
|
4044
4169
|
if (!val) continue;
|
|
@@ -4063,14 +4188,14 @@ function resolveTsconfigPath(cwd, tsconfig) {
|
|
|
4063
4188
|
}
|
|
4064
4189
|
function createDiscoveryProject(tsconfigPath) {
|
|
4065
4190
|
try {
|
|
4066
|
-
return new
|
|
4191
|
+
return new Project4({
|
|
4067
4192
|
tsConfigFilePath: tsconfigPath,
|
|
4068
4193
|
skipAddingFilesFromTsConfig: true,
|
|
4069
4194
|
skipLoadingLibFiles: true,
|
|
4070
4195
|
skipFileDependencyResolution: true
|
|
4071
4196
|
});
|
|
4072
4197
|
} catch {
|
|
4073
|
-
return new
|
|
4198
|
+
return new Project4({
|
|
4074
4199
|
skipAddingFilesFromTsConfig: true,
|
|
4075
4200
|
skipLoadingLibFiles: true,
|
|
4076
4201
|
skipFileDependencyResolution: true,
|
|
@@ -4170,15 +4295,16 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
4170
4295
|
runExtraction() {
|
|
4171
4296
|
clearTypeResolutionCaches(this.project);
|
|
4172
4297
|
clearEnumCache(this.project);
|
|
4298
|
+
clearMixinTypeProject();
|
|
4173
4299
|
return extractRoutesFrom(this.project, this.controllerPaths);
|
|
4174
4300
|
}
|
|
4175
4301
|
};
|
|
4176
4302
|
function decoratorStringArg(decoratorExpr) {
|
|
4177
4303
|
if (!decoratorExpr) return void 0;
|
|
4178
|
-
if (
|
|
4179
|
-
if (
|
|
4304
|
+
if (Node9.isStringLiteral(decoratorExpr)) return decoratorExpr.getLiteralValue();
|
|
4305
|
+
if (Node9.isArrayLiteralExpression(decoratorExpr)) {
|
|
4180
4306
|
const first = decoratorExpr.getElements()[0];
|
|
4181
|
-
if (first &&
|
|
4307
|
+
if (first && Node9.isStringLiteral(first)) return first.getLiteralValue();
|
|
4182
4308
|
}
|
|
4183
4309
|
return void 0;
|
|
4184
4310
|
}
|
|
@@ -4200,7 +4326,8 @@ function joinPaths(prefix, suffix) {
|
|
|
4200
4326
|
if (!prefix && !suffix) return "/";
|
|
4201
4327
|
if (!prefix) return suffix.startsWith("/") ? suffix : `/${suffix}`;
|
|
4202
4328
|
if (!suffix) return prefix.startsWith("/") ? prefix : `/${prefix}`;
|
|
4203
|
-
const
|
|
4329
|
+
const withLeadingSlash = prefix.startsWith("/") ? prefix : `/${prefix}`;
|
|
4330
|
+
const p = withLeadingSlash.endsWith("/") ? withLeadingSlash.slice(0, -1) : withLeadingSlash;
|
|
4204
4331
|
const s = suffix.startsWith("/") ? suffix : `/${suffix}`;
|
|
4205
4332
|
const combined = p + s;
|
|
4206
4333
|
return combined === "" ? "/" : combined;
|
|
@@ -4254,7 +4381,8 @@ function buildRoute(args) {
|
|
|
4254
4381
|
methodAs,
|
|
4255
4382
|
sourceFile,
|
|
4256
4383
|
seenNames,
|
|
4257
|
-
contractSource
|
|
4384
|
+
contractSource,
|
|
4385
|
+
mixin
|
|
4258
4386
|
} = args;
|
|
4259
4387
|
const routeName = resolveRouteName(className, methodName, classAs, methodAs);
|
|
4260
4388
|
const qualifiedRef = `${className}.${methodName}`;
|
|
@@ -4270,7 +4398,12 @@ function buildRoute(args) {
|
|
|
4270
4398
|
path: combinedPath,
|
|
4271
4399
|
name: routeName,
|
|
4272
4400
|
params: extractParams(combinedPath),
|
|
4273
|
-
controllerRef: {
|
|
4401
|
+
controllerRef: {
|
|
4402
|
+
className,
|
|
4403
|
+
methodName,
|
|
4404
|
+
filePath: sourceFile.getFilePath(),
|
|
4405
|
+
...mixin ? { mixin } : {}
|
|
4406
|
+
},
|
|
4274
4407
|
contract: { contractSource }
|
|
4275
4408
|
};
|
|
4276
4409
|
}
|
|
@@ -4284,16 +4417,17 @@ function extractContractRoute(args) {
|
|
|
4284
4417
|
className,
|
|
4285
4418
|
sourceFile,
|
|
4286
4419
|
project,
|
|
4287
|
-
seenNames
|
|
4420
|
+
seenNames,
|
|
4421
|
+
mixin
|
|
4288
4422
|
} = args;
|
|
4289
4423
|
const firstDecoratorArg = applyContractDecorator.getArguments()[0];
|
|
4290
4424
|
if (!firstDecoratorArg) return null;
|
|
4291
4425
|
let contractDef = null;
|
|
4292
4426
|
let bodyZodRef = null;
|
|
4293
4427
|
let queryZodRef = null;
|
|
4294
|
-
if (
|
|
4428
|
+
if (Node9.isCallExpression(firstDecoratorArg)) {
|
|
4295
4429
|
contractDef = parseDefineContractCall(firstDecoratorArg);
|
|
4296
|
-
} else if (
|
|
4430
|
+
} else if (Node9.isIdentifier(firstDecoratorArg)) {
|
|
4297
4431
|
const identName = firstDecoratorArg.getText();
|
|
4298
4432
|
const resolvedVar = resolveImportedVariable(identName, sourceFile, project);
|
|
4299
4433
|
if (!resolvedVar) {
|
|
@@ -4336,6 +4470,7 @@ function extractContractRoute(args) {
|
|
|
4336
4470
|
methodAs,
|
|
4337
4471
|
sourceFile,
|
|
4338
4472
|
seenNames,
|
|
4473
|
+
mixin,
|
|
4339
4474
|
contractSource: {
|
|
4340
4475
|
query: contractDef.query,
|
|
4341
4476
|
body: contractDef.body,
|
|
@@ -4352,13 +4487,14 @@ function extractContractRoute(args) {
|
|
|
4352
4487
|
});
|
|
4353
4488
|
}
|
|
4354
4489
|
function extractDtoRoute(args) {
|
|
4355
|
-
const { cls, method, verb, prefix, className, sourceFile, project, seenNames } = args;
|
|
4490
|
+
const { cls, method, verb, prefix, className, sourceFile, project, seenNames, mixin } = args;
|
|
4356
4491
|
if (!verb) return null;
|
|
4357
4492
|
const combined = joinPaths(prefix, verb.handlerPath);
|
|
4358
4493
|
const methodName = method.getName();
|
|
4359
4494
|
const classAs = readAsDecorator(cls, `class ${className}`);
|
|
4360
4495
|
const methodAs = readAsDecorator(method, `${className}.${methodName}`);
|
|
4361
|
-
const dtoContract = extractDtoContract(method, sourceFile, project);
|
|
4496
|
+
const dtoContract = extractDtoContract(method, sourceFile, project, mixin);
|
|
4497
|
+
const mixinResponse = mixin ? resolveInstantiatedReturnType(cls, methodName) : void 0;
|
|
4362
4498
|
return buildRoute({
|
|
4363
4499
|
className,
|
|
4364
4500
|
methodName,
|
|
@@ -4368,10 +4504,11 @@ function extractDtoRoute(args) {
|
|
|
4368
4504
|
methodAs,
|
|
4369
4505
|
sourceFile,
|
|
4370
4506
|
seenNames,
|
|
4507
|
+
mixin,
|
|
4371
4508
|
contractSource: {
|
|
4372
4509
|
query: dtoContract?.query ?? null,
|
|
4373
4510
|
body: dtoContract?.body ?? null,
|
|
4374
|
-
response: dtoContract?.response ?? "unknown",
|
|
4511
|
+
response: mixinResponse ?? dtoContract?.response ?? "unknown",
|
|
4375
4512
|
error: dtoContract?.error ?? null,
|
|
4376
4513
|
queryRef: dtoContract?.queryRef ?? null,
|
|
4377
4514
|
bodyRef: dtoContract?.bodyRef ?? null,
|
|
@@ -4400,7 +4537,17 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
4400
4537
|
const firstArg2 = controllerDecorator.getArguments()[0];
|
|
4401
4538
|
const prefix = decoratorStringArg(firstArg2) ?? "";
|
|
4402
4539
|
const className = cls.getName() ?? "Unknown";
|
|
4403
|
-
|
|
4540
|
+
const inherited = resolveInheritedMethods(cls);
|
|
4541
|
+
const mixinBinding = inherited ? {
|
|
4542
|
+
factoryName: inherited.factoryName,
|
|
4543
|
+
factoryFilePath: inherited.factoryFilePath,
|
|
4544
|
+
classArgs: inherited.classArgs
|
|
4545
|
+
} : void 0;
|
|
4546
|
+
const methods = [
|
|
4547
|
+
...cls.getMethods().map((method) => ({ method })),
|
|
4548
|
+
...(inherited?.methods ?? []).map((method) => ({ method, mixin: mixinBinding }))
|
|
4549
|
+
];
|
|
4550
|
+
for (const { method, mixin } of methods) {
|
|
4404
4551
|
const verb = resolveVerb(method);
|
|
4405
4552
|
const applyContractDecorator = method.getDecorator("ApplyContract");
|
|
4406
4553
|
const route = applyContractDecorator ? extractContractRoute({
|
|
@@ -4412,7 +4559,8 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
4412
4559
|
className,
|
|
4413
4560
|
sourceFile,
|
|
4414
4561
|
project,
|
|
4415
|
-
seenNames
|
|
4562
|
+
seenNames,
|
|
4563
|
+
mixin
|
|
4416
4564
|
}) : extractDtoRoute({
|
|
4417
4565
|
cls,
|
|
4418
4566
|
method,
|
|
@@ -4421,7 +4569,8 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
4421
4569
|
className,
|
|
4422
4570
|
sourceFile,
|
|
4423
4571
|
project,
|
|
4424
|
-
seenNames
|
|
4572
|
+
seenNames,
|
|
4573
|
+
mixin
|
|
4425
4574
|
});
|
|
4426
4575
|
if (route) routes.push(route);
|
|
4427
4576
|
}
|
|
@@ -4622,7 +4771,7 @@ async function watch(config, onChange, options = {}) {
|
|
|
4622
4771
|
}
|
|
4623
4772
|
|
|
4624
4773
|
// src/index.ts
|
|
4625
|
-
var VERSION = "0.
|
|
4774
|
+
var VERSION = "0.16.0";
|
|
4626
4775
|
|
|
4627
4776
|
// src/generate-manifest.ts
|
|
4628
4777
|
var MANIFEST_FILE = ".codegen-manifest.json";
|
|
@@ -4822,10 +4971,26 @@ var NestjsCodegenModule = class {
|
|
|
4822
4971
|
NestjsCodegenModule = __decorateClass([
|
|
4823
4972
|
Module({})
|
|
4824
4973
|
], NestjsCodegenModule);
|
|
4974
|
+
|
|
4975
|
+
// src/nest/query-list.ts
|
|
4976
|
+
import { createParamDecorator } from "@nestjs/common";
|
|
4977
|
+
function toStringList(raw) {
|
|
4978
|
+
if (raw === void 0 || raw === null) return [];
|
|
4979
|
+
const arr = Array.isArray(raw) ? raw : String(raw).split(",");
|
|
4980
|
+
return arr.map((entry) => String(entry).trim()).filter((entry) => entry.length > 0);
|
|
4981
|
+
}
|
|
4982
|
+
function resolveQueryList(key, ctx) {
|
|
4983
|
+
const request = ctx.switchToHttp().getRequest();
|
|
4984
|
+
return toStringList(key ? request.query?.[key] : void 0);
|
|
4985
|
+
}
|
|
4986
|
+
var QueryList = createParamDecorator(resolveQueryList);
|
|
4825
4987
|
export {
|
|
4826
4988
|
CODEGEN_MODULE_OPTIONS,
|
|
4827
4989
|
NestjsCodegenModule,
|
|
4828
4990
|
NestjsCodegenService,
|
|
4829
|
-
|
|
4991
|
+
QueryList,
|
|
4992
|
+
resolveQueryList,
|
|
4993
|
+
shouldRun,
|
|
4994
|
+
toStringList
|
|
4830
4995
|
};
|
|
4831
4996
|
//# sourceMappingURL=index.js.map
|