@dudousxd/nestjs-codegen 0.18.0 → 0.20.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 +102 -0
- package/dist/cli/main.cjs +154 -38
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.js +154 -38
- package/dist/cli/main.js.map +1 -1
- package/dist/index.cjs +154 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +154 -38
- package/dist/index.js.map +1 -1
- package/dist/nest/index.cjs +154 -38
- package/dist/nest/index.cjs.map +1 -1
- package/dist/nest/index.js +154 -38
- package/dist/nest/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/main.js
CHANGED
|
@@ -3472,10 +3472,46 @@ function resolveReturnedClass(factoryDecl) {
|
|
|
3472
3472
|
}
|
|
3473
3473
|
return void 0;
|
|
3474
3474
|
}
|
|
3475
|
+
function factoryCalleeName(call) {
|
|
3476
|
+
const callee = unwrapExpression(call.getExpression());
|
|
3477
|
+
if (Node5.isIdentifier(callee)) return callee;
|
|
3478
|
+
if (Node5.isPropertyAccessExpression(callee)) {
|
|
3479
|
+
const name = callee.getNameNode();
|
|
3480
|
+
return Node5.isIdentifier(name) ? name : void 0;
|
|
3481
|
+
}
|
|
3482
|
+
return void 0;
|
|
3483
|
+
}
|
|
3475
3484
|
function resolveFactoryDeclaration(call) {
|
|
3476
|
-
const
|
|
3477
|
-
|
|
3478
|
-
|
|
3485
|
+
const name = factoryCalleeName(call);
|
|
3486
|
+
return name ? resolveFactoryFromName(name, /* @__PURE__ */ new Set()) : void 0;
|
|
3487
|
+
}
|
|
3488
|
+
function resolveFactoryFromName(name, seen) {
|
|
3489
|
+
if (seen.has(name)) return void 0;
|
|
3490
|
+
seen.add(name);
|
|
3491
|
+
const decls = name.getDefinitions().map((d) => d.getDeclarationNode()).filter((n) => n !== void 0);
|
|
3492
|
+
for (const decl of decls) {
|
|
3493
|
+
if (Node5.isFunctionDeclaration(decl) || Node5.isMethodDeclaration(decl)) return decl;
|
|
3494
|
+
if (Node5.isPropertyAssignment(decl)) {
|
|
3495
|
+
const init = decl.getInitializer();
|
|
3496
|
+
const inner = init ? unwrapExpression(init) : void 0;
|
|
3497
|
+
if (inner && Node5.isIdentifier(inner)) {
|
|
3498
|
+
const resolved = resolveFactoryFromName(inner, seen);
|
|
3499
|
+
if (resolved) return resolved;
|
|
3500
|
+
}
|
|
3501
|
+
continue;
|
|
3502
|
+
}
|
|
3503
|
+
if (Node5.isShorthandPropertyAssignment(decl)) {
|
|
3504
|
+
const nameNode = decl.getNameNode();
|
|
3505
|
+
const resolved = Node5.isIdentifier(nameNode) ? resolveFactoryFromName(nameNode, seen) : void 0;
|
|
3506
|
+
if (resolved) return resolved;
|
|
3507
|
+
}
|
|
3508
|
+
}
|
|
3509
|
+
return void 0;
|
|
3510
|
+
}
|
|
3511
|
+
function warnUnresolvedFactory(cls, calleeText, reason) {
|
|
3512
|
+
console.warn(
|
|
3513
|
+
`[nestjs-codegen/fast] ${cls.getName() ?? "<anonymous class>"} in ${cls.getSourceFile().getFilePath()} extends ${calleeText}(...) but ${reason} \u2014 it contributes NO routes to the generated client. Resolvable factory callees: a name (\`factory(...)\`), a namespace or object property (\`ns.factory(...)\`), or a static method (\`Factory.create(...)\`), each resolving to a function or method declaration that returns a class.`
|
|
3514
|
+
);
|
|
3479
3515
|
}
|
|
3480
3516
|
function resolveFactoryStaticClass(node) {
|
|
3481
3517
|
if (!Node5.isPropertyAccessExpression(node)) return void 0;
|
|
@@ -3497,12 +3533,26 @@ function resolveFactoryStaticClass(node) {
|
|
|
3497
3533
|
function resolveInheritedMethods(cls) {
|
|
3498
3534
|
const expr = resolveHeritageCall(cls.getExtends()?.getExpression());
|
|
3499
3535
|
if (!expr) return void 0;
|
|
3500
|
-
const
|
|
3501
|
-
|
|
3536
|
+
const isController = cls.getDecorator("Controller") !== void 0;
|
|
3537
|
+
const calleeText = expr.getExpression().getText();
|
|
3502
3538
|
const factoryDecl = resolveFactoryDeclaration(expr);
|
|
3503
|
-
if (!factoryDecl)
|
|
3539
|
+
if (!factoryDecl) {
|
|
3540
|
+
if (isController) {
|
|
3541
|
+
warnUnresolvedFactory(
|
|
3542
|
+
cls,
|
|
3543
|
+
calleeText,
|
|
3544
|
+
factoryCalleeName(expr) ? "its callee does not resolve to a function or method declaration" : "its callee is not a name that can be resolved statically"
|
|
3545
|
+
);
|
|
3546
|
+
}
|
|
3547
|
+
return void 0;
|
|
3548
|
+
}
|
|
3504
3549
|
const returnedClass = resolveReturnedClass(factoryDecl);
|
|
3505
|
-
if (!returnedClass)
|
|
3550
|
+
if (!returnedClass) {
|
|
3551
|
+
if (isController) {
|
|
3552
|
+
warnUnresolvedFactory(cls, calleeText, "that factory does not return a class declaration");
|
|
3553
|
+
}
|
|
3554
|
+
return void 0;
|
|
3555
|
+
}
|
|
3506
3556
|
const classArgs = [];
|
|
3507
3557
|
const namedClassArgs = {};
|
|
3508
3558
|
for (const arg of expr.getArguments()) {
|
|
@@ -3520,7 +3570,12 @@ function resolveInheritedMethods(cls) {
|
|
|
3520
3570
|
}
|
|
3521
3571
|
return {
|
|
3522
3572
|
methods: returnedClass.getMethods(),
|
|
3523
|
-
|
|
3573
|
+
// The DECLARATION's name, not the call-site text: consumers look the factory
|
|
3574
|
+
// back up by name inside `factoryFilePath`, which a qualified
|
|
3575
|
+
// `tables.createTableController` would never match. For a bare identifier
|
|
3576
|
+
// the two coincide (bar an import alias, where the declaration name is the
|
|
3577
|
+
// one that resolves anyway).
|
|
3578
|
+
factoryName: factoryDecl.getName() ?? calleeText,
|
|
3524
3579
|
factoryFilePath: factoryDecl.getSourceFile().getFilePath(),
|
|
3525
3580
|
classArgs,
|
|
3526
3581
|
namedClassArgs
|
|
@@ -3579,10 +3634,15 @@ function resolveInstantiatedReturnType(cls, methodName) {
|
|
|
3579
3634
|
|
|
3580
3635
|
// src/discovery/filter-for.ts
|
|
3581
3636
|
function resolveMixinEntityClass(mixin, project) {
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
return
|
|
3637
|
+
return resolveClassRef(mixin?.namedClassArgs?.entity ?? mixin?.classArgs[0], project);
|
|
3638
|
+
}
|
|
3639
|
+
function resolveMixinFilterClass(mixin, project) {
|
|
3640
|
+
return resolveClassRef(mixin?.namedClassArgs?.filter, project);
|
|
3641
|
+
}
|
|
3642
|
+
function resolveClassRef(ref, project) {
|
|
3643
|
+
if (!ref) return void 0;
|
|
3644
|
+
const file = project.getSourceFile(ref.filePath) ?? project.addSourceFileAtPathIfExists(ref.filePath);
|
|
3645
|
+
return file?.getClass(ref.name);
|
|
3586
3646
|
}
|
|
3587
3647
|
function classifyFilterForHint(typeInit) {
|
|
3588
3648
|
if (Node6.isStringLiteral(typeInit)) {
|
|
@@ -3682,31 +3742,55 @@ function extractApplyFilterInfo(method, sourceFile, project, mixin) {
|
|
|
3682
3742
|
}
|
|
3683
3743
|
const filterClassName = filterClassArg.getText();
|
|
3684
3744
|
const resolved = factoryStaticClass ? void 0 : findType(filterClassName, declFile, project);
|
|
3685
|
-
const
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
byName.set(key, toFilterFieldType(key, classified));
|
|
3696
|
-
}
|
|
3697
|
-
fieldTypes = [...byName.values()];
|
|
3698
|
-
}
|
|
3699
|
-
if (fieldTypes.length === 0) return null;
|
|
3700
|
-
const fieldNames = fieldTypes.map((f) => f.name);
|
|
3745
|
+
const declaredClass = factoryStaticClass ?? (resolved?.kind === "class" ? resolved.decl : resolveLocalClassDeclaration(filterClassArg));
|
|
3746
|
+
const ownRoute = !mixin || declFile.getFilePath() !== mixin.factoryFilePath;
|
|
3747
|
+
const namedByRoute = ownRoute && Node6.isIdentifier(filterClassArg) ? declaredClass : void 0;
|
|
3748
|
+
for (const candidate of orderFilterCandidates({
|
|
3749
|
+
namedByRoute,
|
|
3750
|
+
supplied: resolveMixinFilterClass(mixin, project),
|
|
3751
|
+
declared: declaredClass
|
|
3752
|
+
})) {
|
|
3753
|
+
const fieldTypes = collectFilterFields(candidate, project, mixinEntity);
|
|
3754
|
+
if (fieldTypes.length === 0) continue;
|
|
3701
3755
|
return {
|
|
3702
|
-
fieldNames,
|
|
3756
|
+
fieldNames: fieldTypes.map((f) => f.name),
|
|
3703
3757
|
fieldTypes,
|
|
3704
3758
|
source
|
|
3705
3759
|
};
|
|
3706
3760
|
}
|
|
3761
|
+
if (declaredClass) return null;
|
|
3707
3762
|
}
|
|
3708
3763
|
return null;
|
|
3709
3764
|
}
|
|
3765
|
+
function orderFilterCandidates(sources) {
|
|
3766
|
+
const ordered = [];
|
|
3767
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3768
|
+
const push = (decl, preferDeclaredEntity) => {
|
|
3769
|
+
if (!decl || seen.has(decl)) return;
|
|
3770
|
+
seen.add(decl);
|
|
3771
|
+
ordered.push({ decl, preferDeclaredEntity });
|
|
3772
|
+
};
|
|
3773
|
+
push(sources.namedByRoute, true);
|
|
3774
|
+
push(sources.supplied, true);
|
|
3775
|
+
push(sources.declared, false);
|
|
3776
|
+
return ordered;
|
|
3777
|
+
}
|
|
3778
|
+
function collectFilterFields(candidate, project, mixinEntity) {
|
|
3779
|
+
const { decl, preferDeclaredEntity } = candidate;
|
|
3780
|
+
let fieldTypes = extractClassPropertyTypes(decl, project);
|
|
3781
|
+
if (fieldTypes.length === 0) {
|
|
3782
|
+
fieldTypes = extractFilterableEntityFields(decl, project, mixinEntity, preferDeclaredEntity);
|
|
3783
|
+
}
|
|
3784
|
+
const filterForHints = extractFilterForHints(decl, project);
|
|
3785
|
+
if (filterForHints.size > 0) {
|
|
3786
|
+
const byName = new Map(fieldTypes.map((f) => [f.name, f]));
|
|
3787
|
+
for (const [key, classified] of filterForHints) {
|
|
3788
|
+
byName.set(key, toFilterFieldType(key, classified));
|
|
3789
|
+
}
|
|
3790
|
+
fieldTypes = [...byName.values()];
|
|
3791
|
+
}
|
|
3792
|
+
return fieldTypes;
|
|
3793
|
+
}
|
|
3710
3794
|
var RELATION_DECORATORS = /* @__PURE__ */ new Set(["OneToMany", "ManyToOne", "ManyToMany", "OneToOne"]);
|
|
3711
3795
|
function collectEntityFields(entityDecl, sourceFile, project, prefix, visited) {
|
|
3712
3796
|
const entityName = entityDecl.getName() ?? "";
|
|
@@ -3785,21 +3869,53 @@ function resolveDeclaredEntity(optionsArg, filterClass, project) {
|
|
|
3785
3869
|
if (!resolved || resolved.kind !== "class") return void 0;
|
|
3786
3870
|
return resolved.decl;
|
|
3787
3871
|
}
|
|
3788
|
-
function
|
|
3872
|
+
function readFieldNameList(optionsArg, key) {
|
|
3873
|
+
if (!Node6.isObjectLiteralExpression(optionsArg)) return void 0;
|
|
3874
|
+
const prop = optionsArg.getProperty(key);
|
|
3875
|
+
if (!prop || !Node6.isPropertyAssignment(prop)) return void 0;
|
|
3876
|
+
const init = prop.getInitializer();
|
|
3877
|
+
if (!init || !Node6.isArrayLiteralExpression(init)) return void 0;
|
|
3878
|
+
const names = /* @__PURE__ */ new Set();
|
|
3879
|
+
for (const el of init.getElements()) {
|
|
3880
|
+
if (Node6.isStringLiteral(el)) {
|
|
3881
|
+
names.add(el.getLiteralValue());
|
|
3882
|
+
continue;
|
|
3883
|
+
}
|
|
3884
|
+
if (Node6.isObjectLiteralExpression(el)) {
|
|
3885
|
+
const fieldProp = el.getProperty("field");
|
|
3886
|
+
if (fieldProp && Node6.isPropertyAssignment(fieldProp)) {
|
|
3887
|
+
const fieldInit = fieldProp.getInitializer();
|
|
3888
|
+
if (fieldInit && Node6.isStringLiteral(fieldInit)) {
|
|
3889
|
+
names.add(fieldInit.getLiteralValue());
|
|
3890
|
+
continue;
|
|
3891
|
+
}
|
|
3892
|
+
}
|
|
3893
|
+
}
|
|
3894
|
+
return void 0;
|
|
3895
|
+
}
|
|
3896
|
+
return names;
|
|
3897
|
+
}
|
|
3898
|
+
function narrowToDeclaredFields(fields, optionsArg) {
|
|
3899
|
+
const allowed = readFieldNameList(optionsArg, "allowed");
|
|
3900
|
+
const blocked = readFieldNameList(optionsArg, "blocked");
|
|
3901
|
+
if (!allowed && !blocked) return fields;
|
|
3902
|
+
return fields.filter(
|
|
3903
|
+
(f) => (!allowed || allowed.has(f.name)) && !(blocked?.has(f.name) ?? false)
|
|
3904
|
+
);
|
|
3905
|
+
}
|
|
3906
|
+
function extractFilterableEntityFields(filterClass, project, entityOverride, preferDeclaredEntity = false) {
|
|
3789
3907
|
const filterableDecorator = filterClass.getDecorators().find((d) => d.getName() === "Filterable");
|
|
3790
3908
|
if (!filterableDecorator) return [];
|
|
3791
3909
|
const args = filterableDecorator.getArguments();
|
|
3792
3910
|
if (args.length === 0) return [];
|
|
3793
3911
|
const optionsArg = args[0];
|
|
3794
3912
|
if (!Node6.isObjectLiteralExpression(optionsArg)) return [];
|
|
3795
|
-
const
|
|
3913
|
+
const declaredEntity = resolveDeclaredEntity(optionsArg, filterClass, project);
|
|
3914
|
+
const entityDecl = preferDeclaredEntity ? declaredEntity ?? entityOverride : entityOverride ?? declaredEntity;
|
|
3796
3915
|
if (!entityDecl) return [];
|
|
3797
|
-
const fields =
|
|
3798
|
-
entityDecl,
|
|
3799
|
-
|
|
3800
|
-
project,
|
|
3801
|
-
"",
|
|
3802
|
-
/* @__PURE__ */ new Set()
|
|
3916
|
+
const fields = narrowToDeclaredFields(
|
|
3917
|
+
collectEntityFields(entityDecl, entityDecl.getSourceFile(), project, "", /* @__PURE__ */ new Set()),
|
|
3918
|
+
optionsArg
|
|
3803
3919
|
);
|
|
3804
3920
|
const relationsDecorator = filterClass.getDecorators().find((d) => d.getName() === "Relations");
|
|
3805
3921
|
if (relationsDecorator) {
|
|
@@ -5064,7 +5180,7 @@ async function watch(config, onChange, options = {}) {
|
|
|
5064
5180
|
}
|
|
5065
5181
|
|
|
5066
5182
|
// src/index.ts
|
|
5067
|
-
var VERSION = "0.
|
|
5183
|
+
var VERSION = "0.20.0";
|
|
5068
5184
|
|
|
5069
5185
|
// src/cli/codegen.ts
|
|
5070
5186
|
async function runCodegen(opts = {}) {
|