@dudousxd/nestjs-codegen 0.15.0 → 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 +42 -0
- package/dist/cli/main.cjs +177 -55
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.js +176 -54
- 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-CjIDPMsV.d.cts → index-LQNP7Ms3.d.cts} +23 -0
- package/dist/{index-CjIDPMsV.d.ts → index-LQNP7Ms3.d.ts} +23 -0
- package/dist/index.cjs +177 -55
- 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 +176 -54
- package/dist/index.js.map +1 -1
- package/dist/nest/index.cjs +176 -55
- 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 +175 -54
- 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-LQNP7Ms3.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-LQNP7Ms3.js';
|
|
2
2
|
import 'ts-morph';
|
|
@@ -540,10 +540,33 @@ interface ContractSource {
|
|
|
540
540
|
interface ContractDescriptor {
|
|
541
541
|
contractSource: ContractSource;
|
|
542
542
|
}
|
|
543
|
+
/**
|
|
544
|
+
* Set when a route was contributed by a base class produced by a factory call
|
|
545
|
+
* (`class X extends createTableController(Entity) {}`). Carries the call-site
|
|
546
|
+
* arguments so downstream consumers (e.g. `@dudousxd/nestjs-filter-codegen`) can
|
|
547
|
+
* resolve what the factory was parameterised with — the decorator arguments
|
|
548
|
+
* inside the factory reference its own parameters, not literal identifiers.
|
|
549
|
+
*/
|
|
550
|
+
interface MixinBinding {
|
|
551
|
+
factoryName: string;
|
|
552
|
+
factoryFilePath: string;
|
|
553
|
+
/** Call-site arguments that resolve to a class declaration, in argument order. */
|
|
554
|
+
classArgs: Array<{
|
|
555
|
+
name: string;
|
|
556
|
+
filePath: string;
|
|
557
|
+
}>;
|
|
558
|
+
}
|
|
543
559
|
interface ControllerRef {
|
|
544
560
|
className: string;
|
|
545
561
|
methodName: string;
|
|
546
562
|
filePath: string;
|
|
563
|
+
/**
|
|
564
|
+
* Present only for routes inherited from a factory-produced base class.
|
|
565
|
+
* `className`/`filePath` still point at the DERIVED class (the call site);
|
|
566
|
+
* `methodName` names a method that exists only on the base, so consumers must
|
|
567
|
+
* branch on this before attempting `controllerClass.getMethod(methodName)`.
|
|
568
|
+
*/
|
|
569
|
+
mixin?: MixinBinding;
|
|
547
570
|
}
|
|
548
571
|
interface RouteDescriptor {
|
|
549
572
|
method: string;
|
|
@@ -540,10 +540,33 @@ interface ContractSource {
|
|
|
540
540
|
interface ContractDescriptor {
|
|
541
541
|
contractSource: ContractSource;
|
|
542
542
|
}
|
|
543
|
+
/**
|
|
544
|
+
* Set when a route was contributed by a base class produced by a factory call
|
|
545
|
+
* (`class X extends createTableController(Entity) {}`). Carries the call-site
|
|
546
|
+
* arguments so downstream consumers (e.g. `@dudousxd/nestjs-filter-codegen`) can
|
|
547
|
+
* resolve what the factory was parameterised with — the decorator arguments
|
|
548
|
+
* inside the factory reference its own parameters, not literal identifiers.
|
|
549
|
+
*/
|
|
550
|
+
interface MixinBinding {
|
|
551
|
+
factoryName: string;
|
|
552
|
+
factoryFilePath: string;
|
|
553
|
+
/** Call-site arguments that resolve to a class declaration, in argument order. */
|
|
554
|
+
classArgs: Array<{
|
|
555
|
+
name: string;
|
|
556
|
+
filePath: string;
|
|
557
|
+
}>;
|
|
558
|
+
}
|
|
543
559
|
interface ControllerRef {
|
|
544
560
|
className: string;
|
|
545
561
|
methodName: string;
|
|
546
562
|
filePath: string;
|
|
563
|
+
/**
|
|
564
|
+
* Present only for routes inherited from a factory-produced base class.
|
|
565
|
+
* `className`/`filePath` still point at the DERIVED class (the call site);
|
|
566
|
+
* `methodName` names a method that exists only on the base, so consumers must
|
|
567
|
+
* branch on this before attempting `controllerClass.getMethod(methodName)`.
|
|
568
|
+
*/
|
|
569
|
+
mixin?: MixinBinding;
|
|
547
570
|
}
|
|
548
571
|
interface RouteDescriptor {
|
|
549
572
|
method: string;
|
package/dist/index.cjs
CHANGED
|
@@ -2477,7 +2477,7 @@ var import_chokidar = __toESM(require("chokidar"), 1);
|
|
|
2477
2477
|
// src/discovery/contracts-fast.ts
|
|
2478
2478
|
var import_node_path15 = require("path");
|
|
2479
2479
|
var import_fast_glob3 = __toESM(require("fast-glob"), 1);
|
|
2480
|
-
var
|
|
2480
|
+
var import_ts_morph10 = require("ts-morph");
|
|
2481
2481
|
|
|
2482
2482
|
// src/discovery/dto-type-resolver.ts
|
|
2483
2483
|
var import_ts_morph7 = require("ts-morph");
|
|
@@ -3456,6 +3456,12 @@ function toFilterFieldType(name, r) {
|
|
|
3456
3456
|
}
|
|
3457
3457
|
|
|
3458
3458
|
// src/discovery/filter-for.ts
|
|
3459
|
+
function resolveMixinEntityClass(mixin, project) {
|
|
3460
|
+
const entityArg = mixin?.classArgs[0];
|
|
3461
|
+
if (!entityArg) return void 0;
|
|
3462
|
+
const file = project.getSourceFile(entityArg.filePath) ?? project.addSourceFileAtPathIfExists(entityArg.filePath);
|
|
3463
|
+
return file?.getClass(entityArg.name);
|
|
3464
|
+
}
|
|
3459
3465
|
function classifyFilterForHint(typeInit) {
|
|
3460
3466
|
if (import_ts_morph6.Node.isStringLiteral(typeInit)) {
|
|
3461
3467
|
switch (typeInit.getLiteralValue()) {
|
|
@@ -3529,7 +3535,9 @@ function extractFilterForHints(classDecl, project) {
|
|
|
3529
3535
|
}
|
|
3530
3536
|
return hints;
|
|
3531
3537
|
}
|
|
3532
|
-
function extractApplyFilterInfo(method, sourceFile, project) {
|
|
3538
|
+
function extractApplyFilterInfo(method, sourceFile, project, mixin) {
|
|
3539
|
+
const declFile = method.getSourceFile();
|
|
3540
|
+
const mixinEntity = resolveMixinEntityClass(mixin, project);
|
|
3533
3541
|
for (const param of method.getParameters()) {
|
|
3534
3542
|
const filterDecorator = param.getDecorators().find((d) => d.getName() === "ApplyFilter");
|
|
3535
3543
|
if (!filterDecorator) continue;
|
|
@@ -3549,12 +3557,12 @@ function extractApplyFilterInfo(method, sourceFile, project) {
|
|
|
3549
3557
|
}
|
|
3550
3558
|
}
|
|
3551
3559
|
const filterClassName = filterClassArg.getText();
|
|
3552
|
-
const resolved = findType(filterClassName,
|
|
3553
|
-
|
|
3554
|
-
|
|
3560
|
+
const resolved = findType(filterClassName, declFile, project);
|
|
3561
|
+
const classDecl = resolved?.kind === "class" ? resolved.decl : resolveLocalClassDeclaration(filterClassArg);
|
|
3562
|
+
if (classDecl) {
|
|
3555
3563
|
let fieldTypes = extractClassPropertyTypes(classDecl, project);
|
|
3556
3564
|
if (fieldTypes.length === 0) {
|
|
3557
|
-
fieldTypes = extractFilterableEntityFields(classDecl, project);
|
|
3565
|
+
fieldTypes = extractFilterableEntityFields(classDecl, project, mixinEntity);
|
|
3558
3566
|
}
|
|
3559
3567
|
const filterForHints = extractFilterForHints(classDecl, project);
|
|
3560
3568
|
if (filterForHints.size > 0) {
|
|
@@ -3639,22 +3647,29 @@ function extractClassPropertyTypes(classDecl, project) {
|
|
|
3639
3647
|
}
|
|
3640
3648
|
return fields;
|
|
3641
3649
|
}
|
|
3642
|
-
function
|
|
3650
|
+
function resolveLocalClassDeclaration(identifier) {
|
|
3651
|
+
if (!import_ts_morph6.Node.isIdentifier(identifier)) return void 0;
|
|
3652
|
+
return identifier.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 && import_ts_morph6.Node.isClassDeclaration(n));
|
|
3653
|
+
}
|
|
3654
|
+
function resolveDeclaredEntity(optionsArg, filterClass, project) {
|
|
3655
|
+
if (!import_ts_morph6.Node.isObjectLiteralExpression(optionsArg)) return void 0;
|
|
3656
|
+
const entityProp = optionsArg.getProperty("entity");
|
|
3657
|
+
if (!entityProp || !import_ts_morph6.Node.isPropertyAssignment(entityProp)) return void 0;
|
|
3658
|
+
const entityInit = entityProp.getInitializer();
|
|
3659
|
+
if (!entityInit || !import_ts_morph6.Node.isIdentifier(entityInit)) return void 0;
|
|
3660
|
+
const resolved = findType(entityInit.getText(), filterClass.getSourceFile(), project);
|
|
3661
|
+
if (!resolved || resolved.kind !== "class") return void 0;
|
|
3662
|
+
return resolved.decl;
|
|
3663
|
+
}
|
|
3664
|
+
function extractFilterableEntityFields(filterClass, project, entityOverride) {
|
|
3643
3665
|
const filterableDecorator = filterClass.getDecorators().find((d) => d.getName() === "Filterable");
|
|
3644
3666
|
if (!filterableDecorator) return [];
|
|
3645
3667
|
const args = filterableDecorator.getArguments();
|
|
3646
3668
|
if (args.length === 0) return [];
|
|
3647
3669
|
const optionsArg = args[0];
|
|
3648
3670
|
if (!import_ts_morph6.Node.isObjectLiteralExpression(optionsArg)) return [];
|
|
3649
|
-
const
|
|
3650
|
-
if (!
|
|
3651
|
-
const entityInit = entityProp.getInitializer();
|
|
3652
|
-
if (!entityInit || !import_ts_morph6.Node.isIdentifier(entityInit)) return [];
|
|
3653
|
-
const entityName = entityInit.getText();
|
|
3654
|
-
const filterSourceFile = filterClass.getSourceFile();
|
|
3655
|
-
const resolvedEntity = findType(entityName, filterSourceFile, project);
|
|
3656
|
-
if (!resolvedEntity || resolvedEntity.kind !== "class") return [];
|
|
3657
|
-
const entityDecl = resolvedEntity.decl;
|
|
3671
|
+
const entityDecl = entityOverride ?? resolveDeclaredEntity(optionsArg, filterClass, project);
|
|
3672
|
+
if (!entityDecl) return [];
|
|
3658
3673
|
const fields = collectEntityFields(
|
|
3659
3674
|
entityDecl,
|
|
3660
3675
|
entityDecl.getSourceFile(),
|
|
@@ -4079,9 +4094,9 @@ function unwrapNamedContainer(node, names) {
|
|
|
4079
4094
|
}
|
|
4080
4095
|
return node;
|
|
4081
4096
|
}
|
|
4082
|
-
function extractDtoContract(method, sourceFile, project) {
|
|
4097
|
+
function extractDtoContract(method, sourceFile, project, mixin) {
|
|
4083
4098
|
let body = extractBodyType(method, sourceFile, project);
|
|
4084
|
-
const filterInfo = extractApplyFilterInfo(method, sourceFile, project);
|
|
4099
|
+
const filterInfo = extractApplyFilterInfo(method, sourceFile, project, mixin);
|
|
4085
4100
|
const query = extractQueryType(method, sourceFile, project);
|
|
4086
4101
|
const uploads = extractUploadedFiles(method);
|
|
4087
4102
|
const multipartBody = uploads.fields ? `{ ${uploads.fields} }` : null;
|
|
@@ -4191,12 +4206,94 @@ function resolveParamClass(method, decoratorName, sourceFile, project) {
|
|
|
4191
4206
|
return null;
|
|
4192
4207
|
}
|
|
4193
4208
|
|
|
4194
|
-
// src/discovery/
|
|
4209
|
+
// src/discovery/heritage.ts
|
|
4195
4210
|
var import_ts_morph8 = require("ts-morph");
|
|
4211
|
+
function unwrapExpression(node) {
|
|
4212
|
+
let current = node;
|
|
4213
|
+
while (import_ts_morph8.Node.isAsExpression(current) || import_ts_morph8.Node.isSatisfiesExpression(current) || import_ts_morph8.Node.isParenthesizedExpression(current) || import_ts_morph8.Node.isTypeAssertion(current)) {
|
|
4214
|
+
current = current.getExpression();
|
|
4215
|
+
}
|
|
4216
|
+
return current;
|
|
4217
|
+
}
|
|
4218
|
+
function resolveReturnedClass(factoryDecl) {
|
|
4219
|
+
const body = factoryDecl.getBody();
|
|
4220
|
+
if (!body) return void 0;
|
|
4221
|
+
const returns = body.getDescendants().filter((n) => import_ts_morph8.Node.isReturnStatement(n));
|
|
4222
|
+
for (const ret of returns) {
|
|
4223
|
+
const expr = ret.getExpression();
|
|
4224
|
+
if (!expr) continue;
|
|
4225
|
+
const inner = unwrapExpression(expr);
|
|
4226
|
+
if (import_ts_morph8.Node.isClassExpression(inner)) {
|
|
4227
|
+
return inner;
|
|
4228
|
+
}
|
|
4229
|
+
if (import_ts_morph8.Node.isIdentifier(inner)) {
|
|
4230
|
+
const name = inner.getText();
|
|
4231
|
+
const decl = body.getDescendants().find((n) => import_ts_morph8.Node.isClassDeclaration(n) && n.getName() === name);
|
|
4232
|
+
if (decl) return decl;
|
|
4233
|
+
}
|
|
4234
|
+
}
|
|
4235
|
+
return void 0;
|
|
4236
|
+
}
|
|
4237
|
+
function resolveInheritedMethods(cls) {
|
|
4238
|
+
const expr = cls.getExtends()?.getExpression();
|
|
4239
|
+
if (!expr || !import_ts_morph8.Node.isCallExpression(expr)) return void 0;
|
|
4240
|
+
const callee = expr.getExpression();
|
|
4241
|
+
if (!import_ts_morph8.Node.isIdentifier(callee)) return void 0;
|
|
4242
|
+
const factoryDecl = callee.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => import_ts_morph8.Node.isFunctionDeclaration(n));
|
|
4243
|
+
if (!factoryDecl) return void 0;
|
|
4244
|
+
const returnedClass = resolveReturnedClass(factoryDecl);
|
|
4245
|
+
if (!returnedClass) return void 0;
|
|
4246
|
+
const classArgs = [];
|
|
4247
|
+
for (const arg of expr.getArguments()) {
|
|
4248
|
+
if (!import_ts_morph8.Node.isIdentifier(arg)) continue;
|
|
4249
|
+
const decl = arg.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => import_ts_morph8.Node.isClassDeclaration(n));
|
|
4250
|
+
if (decl) {
|
|
4251
|
+
classArgs.push({
|
|
4252
|
+
name: decl.getName() ?? arg.getText(),
|
|
4253
|
+
filePath: decl.getSourceFile().getFilePath()
|
|
4254
|
+
});
|
|
4255
|
+
}
|
|
4256
|
+
}
|
|
4257
|
+
return {
|
|
4258
|
+
methods: returnedClass.getMethods(),
|
|
4259
|
+
factoryName: callee.getText(),
|
|
4260
|
+
factoryFilePath: factoryDecl.getSourceFile().getFilePath(),
|
|
4261
|
+
classArgs
|
|
4262
|
+
};
|
|
4263
|
+
}
|
|
4264
|
+
var mixinTypeProject;
|
|
4265
|
+
function getMixinTypeProject() {
|
|
4266
|
+
mixinTypeProject ??= new import_ts_morph8.Project({
|
|
4267
|
+
skipAddingFilesFromTsConfig: true,
|
|
4268
|
+
compilerOptions: { strict: true }
|
|
4269
|
+
});
|
|
4270
|
+
return mixinTypeProject;
|
|
4271
|
+
}
|
|
4272
|
+
function clearMixinTypeProject() {
|
|
4273
|
+
mixinTypeProject = void 0;
|
|
4274
|
+
}
|
|
4275
|
+
function resolveInstantiatedReturnType(cls, methodName) {
|
|
4276
|
+
const filePath = cls.getSourceFile().getFilePath();
|
|
4277
|
+
const className = cls.getName();
|
|
4278
|
+
if (!className) return void 0;
|
|
4279
|
+
const project = getMixinTypeProject();
|
|
4280
|
+
const sourceFile = project.getSourceFile(filePath) ?? project.addSourceFileAtPathIfExists(filePath);
|
|
4281
|
+
const typedCls = sourceFile?.getClass(className);
|
|
4282
|
+
if (!typedCls) return void 0;
|
|
4283
|
+
const prop = typedCls.getType().getProperty(methodName);
|
|
4284
|
+
if (!prop) return void 0;
|
|
4285
|
+
const returnType = prop.getTypeAtLocation(typedCls).getCallSignatures()[0]?.getReturnType();
|
|
4286
|
+
if (!returnType) return void 0;
|
|
4287
|
+
const unwrapped = returnType.getSymbol()?.getName() === "Promise" ? returnType.getTypeArguments()[0] ?? returnType : returnType;
|
|
4288
|
+
return unwrapped.getText(typedCls);
|
|
4289
|
+
}
|
|
4290
|
+
|
|
4291
|
+
// src/discovery/zod-ast-to-ts.ts
|
|
4292
|
+
var import_ts_morph9 = require("ts-morph");
|
|
4196
4293
|
function zodAstToTs(node) {
|
|
4197
|
-
if (!
|
|
4294
|
+
if (!import_ts_morph9.Node.isCallExpression(node)) return "unknown";
|
|
4198
4295
|
const expr = node.getExpression();
|
|
4199
|
-
if (
|
|
4296
|
+
if (import_ts_morph9.Node.isPropertyAccessExpression(expr)) {
|
|
4200
4297
|
const methodName = expr.getName();
|
|
4201
4298
|
const receiver = expr.getExpression();
|
|
4202
4299
|
if (methodName === "optional") {
|
|
@@ -4220,17 +4317,17 @@ function zodAstToTs(node) {
|
|
|
4220
4317
|
case "literal": {
|
|
4221
4318
|
const lit = args[0];
|
|
4222
4319
|
if (!lit) return "unknown";
|
|
4223
|
-
if (
|
|
4224
|
-
if (
|
|
4225
|
-
if (lit.getKind() ===
|
|
4226
|
-
if (lit.getKind() ===
|
|
4320
|
+
if (import_ts_morph9.Node.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
|
|
4321
|
+
if (import_ts_morph9.Node.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
|
|
4322
|
+
if (lit.getKind() === import_ts_morph9.SyntaxKind.TrueKeyword) return "true";
|
|
4323
|
+
if (lit.getKind() === import_ts_morph9.SyntaxKind.FalseKeyword) return "false";
|
|
4227
4324
|
return "unknown";
|
|
4228
4325
|
}
|
|
4229
4326
|
case "enum": {
|
|
4230
4327
|
const arrArg = args[0];
|
|
4231
|
-
if (!arrArg || !
|
|
4328
|
+
if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
4232
4329
|
const members = arrArg.getElements().map(
|
|
4233
|
-
(el) =>
|
|
4330
|
+
(el) => import_ts_morph9.Node.isStringLiteral(el) ? JSON.stringify(el.getLiteralValue()) : "unknown"
|
|
4234
4331
|
);
|
|
4235
4332
|
return members.join(" | ");
|
|
4236
4333
|
}
|
|
@@ -4241,10 +4338,10 @@ function zodAstToTs(node) {
|
|
|
4241
4338
|
}
|
|
4242
4339
|
case "object": {
|
|
4243
4340
|
const objArg = args[0];
|
|
4244
|
-
if (!objArg || !
|
|
4341
|
+
if (!objArg || !import_ts_morph9.Node.isObjectLiteralExpression(objArg)) return "unknown";
|
|
4245
4342
|
const lines = [];
|
|
4246
4343
|
for (const prop of objArg.getProperties()) {
|
|
4247
|
-
if (!
|
|
4344
|
+
if (!import_ts_morph9.Node.isPropertyAssignment(prop)) continue;
|
|
4248
4345
|
const key = prop.getName();
|
|
4249
4346
|
const valNode = prop.getInitializer();
|
|
4250
4347
|
if (!valNode) continue;
|
|
@@ -4256,7 +4353,7 @@ function zodAstToTs(node) {
|
|
|
4256
4353
|
}
|
|
4257
4354
|
case "union": {
|
|
4258
4355
|
const arrArg = args[0];
|
|
4259
|
-
if (!arrArg || !
|
|
4356
|
+
if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
4260
4357
|
return arrArg.getElements().map(zodAstToTs).join(" | ");
|
|
4261
4358
|
}
|
|
4262
4359
|
case "record": {
|
|
@@ -4266,7 +4363,7 @@ function zodAstToTs(node) {
|
|
|
4266
4363
|
}
|
|
4267
4364
|
case "tuple": {
|
|
4268
4365
|
const arrArg = args[0];
|
|
4269
|
-
if (!arrArg || !
|
|
4366
|
+
if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
4270
4367
|
return `[${arrArg.getElements().map(zodAstToTs).join(", ")}]`;
|
|
4271
4368
|
}
|
|
4272
4369
|
default:
|
|
@@ -4276,18 +4373,18 @@ function zodAstToTs(node) {
|
|
|
4276
4373
|
return "unknown";
|
|
4277
4374
|
}
|
|
4278
4375
|
function isOptionalChain(node) {
|
|
4279
|
-
if (!
|
|
4376
|
+
if (!import_ts_morph9.Node.isCallExpression(node)) return false;
|
|
4280
4377
|
const expr = node.getExpression();
|
|
4281
|
-
return
|
|
4378
|
+
return import_ts_morph9.Node.isPropertyAccessExpression(expr) && expr.getName() === "optional";
|
|
4282
4379
|
}
|
|
4283
4380
|
function parseDefineContractCall(callExpr) {
|
|
4284
|
-
if (!
|
|
4381
|
+
if (!import_ts_morph9.Node.isCallExpression(callExpr)) return null;
|
|
4285
4382
|
const callee = callExpr.getExpression();
|
|
4286
|
-
const calleeName =
|
|
4383
|
+
const calleeName = import_ts_morph9.Node.isIdentifier(callee) ? callee.getText() : import_ts_morph9.Node.isPropertyAccessExpression(callee) ? callee.getName() : "";
|
|
4287
4384
|
if (calleeName !== "defineContract") return null;
|
|
4288
4385
|
const args = callExpr.getArguments();
|
|
4289
4386
|
const optsArg = args[0];
|
|
4290
|
-
if (!optsArg || !
|
|
4387
|
+
if (!optsArg || !import_ts_morph9.Node.isObjectLiteralExpression(optsArg)) return null;
|
|
4291
4388
|
let query = null;
|
|
4292
4389
|
let body = null;
|
|
4293
4390
|
let response = "unknown";
|
|
@@ -4295,7 +4392,7 @@ function parseDefineContractCall(callExpr) {
|
|
|
4295
4392
|
let bodyZodText = null;
|
|
4296
4393
|
let queryZodText = null;
|
|
4297
4394
|
for (const prop of optsArg.getProperties()) {
|
|
4298
|
-
if (!
|
|
4395
|
+
if (!import_ts_morph9.Node.isPropertyAssignment(prop)) continue;
|
|
4299
4396
|
const propName = prop.getName();
|
|
4300
4397
|
const val = prop.getInitializer();
|
|
4301
4398
|
if (!val) continue;
|
|
@@ -4324,6 +4421,7 @@ async function discoverContractsFast(opts) {
|
|
|
4324
4421
|
project.addSourceFileAtPath(f);
|
|
4325
4422
|
}
|
|
4326
4423
|
bindDiscoveryContext(project, cwd, tsconfigPath);
|
|
4424
|
+
clearMixinTypeProject();
|
|
4327
4425
|
return extractAllRoutes(project);
|
|
4328
4426
|
}
|
|
4329
4427
|
function resolveTsconfigPath(cwd, tsconfig) {
|
|
@@ -4331,14 +4429,14 @@ function resolveTsconfigPath(cwd, tsconfig) {
|
|
|
4331
4429
|
}
|
|
4332
4430
|
function createDiscoveryProject(tsconfigPath) {
|
|
4333
4431
|
try {
|
|
4334
|
-
return new
|
|
4432
|
+
return new import_ts_morph10.Project({
|
|
4335
4433
|
tsConfigFilePath: tsconfigPath,
|
|
4336
4434
|
skipAddingFilesFromTsConfig: true,
|
|
4337
4435
|
skipLoadingLibFiles: true,
|
|
4338
4436
|
skipFileDependencyResolution: true
|
|
4339
4437
|
});
|
|
4340
4438
|
} catch {
|
|
4341
|
-
return new
|
|
4439
|
+
return new import_ts_morph10.Project({
|
|
4342
4440
|
skipAddingFilesFromTsConfig: true,
|
|
4343
4441
|
skipLoadingLibFiles: true,
|
|
4344
4442
|
skipFileDependencyResolution: true,
|
|
@@ -4445,15 +4543,16 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
4445
4543
|
runExtraction() {
|
|
4446
4544
|
clearTypeResolutionCaches(this.project);
|
|
4447
4545
|
clearEnumCache(this.project);
|
|
4546
|
+
clearMixinTypeProject();
|
|
4448
4547
|
return extractRoutesFrom(this.project, this.controllerPaths);
|
|
4449
4548
|
}
|
|
4450
4549
|
};
|
|
4451
4550
|
function decoratorStringArg(decoratorExpr) {
|
|
4452
4551
|
if (!decoratorExpr) return void 0;
|
|
4453
|
-
if (
|
|
4454
|
-
if (
|
|
4552
|
+
if (import_ts_morph10.Node.isStringLiteral(decoratorExpr)) return decoratorExpr.getLiteralValue();
|
|
4553
|
+
if (import_ts_morph10.Node.isArrayLiteralExpression(decoratorExpr)) {
|
|
4455
4554
|
const first = decoratorExpr.getElements()[0];
|
|
4456
|
-
if (first &&
|
|
4555
|
+
if (first && import_ts_morph10.Node.isStringLiteral(first)) return first.getLiteralValue();
|
|
4457
4556
|
}
|
|
4458
4557
|
return void 0;
|
|
4459
4558
|
}
|
|
@@ -4475,7 +4574,8 @@ function joinPaths(prefix, suffix) {
|
|
|
4475
4574
|
if (!prefix && !suffix) return "/";
|
|
4476
4575
|
if (!prefix) return suffix.startsWith("/") ? suffix : `/${suffix}`;
|
|
4477
4576
|
if (!suffix) return prefix.startsWith("/") ? prefix : `/${prefix}`;
|
|
4478
|
-
const
|
|
4577
|
+
const withLeadingSlash = prefix.startsWith("/") ? prefix : `/${prefix}`;
|
|
4578
|
+
const p = withLeadingSlash.endsWith("/") ? withLeadingSlash.slice(0, -1) : withLeadingSlash;
|
|
4479
4579
|
const s = suffix.startsWith("/") ? suffix : `/${suffix}`;
|
|
4480
4580
|
const combined = p + s;
|
|
4481
4581
|
return combined === "" ? "/" : combined;
|
|
@@ -4529,7 +4629,8 @@ function buildRoute(args) {
|
|
|
4529
4629
|
methodAs,
|
|
4530
4630
|
sourceFile,
|
|
4531
4631
|
seenNames,
|
|
4532
|
-
contractSource
|
|
4632
|
+
contractSource,
|
|
4633
|
+
mixin
|
|
4533
4634
|
} = args;
|
|
4534
4635
|
const routeName = resolveRouteName(className, methodName, classAs, methodAs);
|
|
4535
4636
|
const qualifiedRef = `${className}.${methodName}`;
|
|
@@ -4545,7 +4646,12 @@ function buildRoute(args) {
|
|
|
4545
4646
|
path: combinedPath,
|
|
4546
4647
|
name: routeName,
|
|
4547
4648
|
params: extractParams(combinedPath),
|
|
4548
|
-
controllerRef: {
|
|
4649
|
+
controllerRef: {
|
|
4650
|
+
className,
|
|
4651
|
+
methodName,
|
|
4652
|
+
filePath: sourceFile.getFilePath(),
|
|
4653
|
+
...mixin ? { mixin } : {}
|
|
4654
|
+
},
|
|
4549
4655
|
contract: { contractSource }
|
|
4550
4656
|
};
|
|
4551
4657
|
}
|
|
@@ -4559,16 +4665,17 @@ function extractContractRoute(args) {
|
|
|
4559
4665
|
className,
|
|
4560
4666
|
sourceFile,
|
|
4561
4667
|
project,
|
|
4562
|
-
seenNames
|
|
4668
|
+
seenNames,
|
|
4669
|
+
mixin
|
|
4563
4670
|
} = args;
|
|
4564
4671
|
const firstDecoratorArg = applyContractDecorator.getArguments()[0];
|
|
4565
4672
|
if (!firstDecoratorArg) return null;
|
|
4566
4673
|
let contractDef = null;
|
|
4567
4674
|
let bodyZodRef = null;
|
|
4568
4675
|
let queryZodRef = null;
|
|
4569
|
-
if (
|
|
4676
|
+
if (import_ts_morph10.Node.isCallExpression(firstDecoratorArg)) {
|
|
4570
4677
|
contractDef = parseDefineContractCall(firstDecoratorArg);
|
|
4571
|
-
} else if (
|
|
4678
|
+
} else if (import_ts_morph10.Node.isIdentifier(firstDecoratorArg)) {
|
|
4572
4679
|
const identName = firstDecoratorArg.getText();
|
|
4573
4680
|
const resolvedVar = resolveImportedVariable(identName, sourceFile, project);
|
|
4574
4681
|
if (!resolvedVar) {
|
|
@@ -4611,6 +4718,7 @@ function extractContractRoute(args) {
|
|
|
4611
4718
|
methodAs,
|
|
4612
4719
|
sourceFile,
|
|
4613
4720
|
seenNames,
|
|
4721
|
+
mixin,
|
|
4614
4722
|
contractSource: {
|
|
4615
4723
|
query: contractDef.query,
|
|
4616
4724
|
body: contractDef.body,
|
|
@@ -4627,13 +4735,14 @@ function extractContractRoute(args) {
|
|
|
4627
4735
|
});
|
|
4628
4736
|
}
|
|
4629
4737
|
function extractDtoRoute(args) {
|
|
4630
|
-
const { cls, method, verb, prefix, className, sourceFile, project, seenNames } = args;
|
|
4738
|
+
const { cls, method, verb, prefix, className, sourceFile, project, seenNames, mixin } = args;
|
|
4631
4739
|
if (!verb) return null;
|
|
4632
4740
|
const combined = joinPaths(prefix, verb.handlerPath);
|
|
4633
4741
|
const methodName = method.getName();
|
|
4634
4742
|
const classAs = readAsDecorator(cls, `class ${className}`);
|
|
4635
4743
|
const methodAs = readAsDecorator(method, `${className}.${methodName}`);
|
|
4636
|
-
const dtoContract = extractDtoContract(method, sourceFile, project);
|
|
4744
|
+
const dtoContract = extractDtoContract(method, sourceFile, project, mixin);
|
|
4745
|
+
const mixinResponse = mixin ? resolveInstantiatedReturnType(cls, methodName) : void 0;
|
|
4637
4746
|
return buildRoute({
|
|
4638
4747
|
className,
|
|
4639
4748
|
methodName,
|
|
@@ -4643,10 +4752,11 @@ function extractDtoRoute(args) {
|
|
|
4643
4752
|
methodAs,
|
|
4644
4753
|
sourceFile,
|
|
4645
4754
|
seenNames,
|
|
4755
|
+
mixin,
|
|
4646
4756
|
contractSource: {
|
|
4647
4757
|
query: dtoContract?.query ?? null,
|
|
4648
4758
|
body: dtoContract?.body ?? null,
|
|
4649
|
-
response: dtoContract?.response ?? "unknown",
|
|
4759
|
+
response: mixinResponse ?? dtoContract?.response ?? "unknown",
|
|
4650
4760
|
error: dtoContract?.error ?? null,
|
|
4651
4761
|
queryRef: dtoContract?.queryRef ?? null,
|
|
4652
4762
|
bodyRef: dtoContract?.bodyRef ?? null,
|
|
@@ -4675,7 +4785,17 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
4675
4785
|
const firstArg2 = controllerDecorator.getArguments()[0];
|
|
4676
4786
|
const prefix = decoratorStringArg(firstArg2) ?? "";
|
|
4677
4787
|
const className = cls.getName() ?? "Unknown";
|
|
4678
|
-
|
|
4788
|
+
const inherited = resolveInheritedMethods(cls);
|
|
4789
|
+
const mixinBinding = inherited ? {
|
|
4790
|
+
factoryName: inherited.factoryName,
|
|
4791
|
+
factoryFilePath: inherited.factoryFilePath,
|
|
4792
|
+
classArgs: inherited.classArgs
|
|
4793
|
+
} : void 0;
|
|
4794
|
+
const methods = [
|
|
4795
|
+
...cls.getMethods().map((method) => ({ method })),
|
|
4796
|
+
...(inherited?.methods ?? []).map((method) => ({ method, mixin: mixinBinding }))
|
|
4797
|
+
];
|
|
4798
|
+
for (const { method, mixin } of methods) {
|
|
4679
4799
|
const verb = resolveVerb(method);
|
|
4680
4800
|
const applyContractDecorator = method.getDecorator("ApplyContract");
|
|
4681
4801
|
const route = applyContractDecorator ? extractContractRoute({
|
|
@@ -4687,7 +4807,8 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
4687
4807
|
className,
|
|
4688
4808
|
sourceFile,
|
|
4689
4809
|
project,
|
|
4690
|
-
seenNames
|
|
4810
|
+
seenNames,
|
|
4811
|
+
mixin
|
|
4691
4812
|
}) : extractDtoRoute({
|
|
4692
4813
|
cls,
|
|
4693
4814
|
method,
|
|
@@ -4696,7 +4817,8 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
4696
4817
|
className,
|
|
4697
4818
|
sourceFile,
|
|
4698
4819
|
project,
|
|
4699
|
-
seenNames
|
|
4820
|
+
seenNames,
|
|
4821
|
+
mixin
|
|
4700
4822
|
});
|
|
4701
4823
|
if (route) routes.push(route);
|
|
4702
4824
|
}
|
|
@@ -4980,7 +5102,7 @@ function createChainModuleRenderer(opts) {
|
|
|
4980
5102
|
}
|
|
4981
5103
|
|
|
4982
5104
|
// src/index.ts
|
|
4983
|
-
var VERSION = "0.
|
|
5105
|
+
var VERSION = "0.16.0";
|
|
4984
5106
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4985
5107
|
0 && (module.exports = {
|
|
4986
5108
|
CodegenError,
|