@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/index.cjs
CHANGED
|
@@ -3526,10 +3526,46 @@ function resolveReturnedClass(factoryDecl) {
|
|
|
3526
3526
|
}
|
|
3527
3527
|
return void 0;
|
|
3528
3528
|
}
|
|
3529
|
+
function factoryCalleeName(call) {
|
|
3530
|
+
const callee = unwrapExpression(call.getExpression());
|
|
3531
|
+
if (import_ts_morph6.Node.isIdentifier(callee)) return callee;
|
|
3532
|
+
if (import_ts_morph6.Node.isPropertyAccessExpression(callee)) {
|
|
3533
|
+
const name = callee.getNameNode();
|
|
3534
|
+
return import_ts_morph6.Node.isIdentifier(name) ? name : void 0;
|
|
3535
|
+
}
|
|
3536
|
+
return void 0;
|
|
3537
|
+
}
|
|
3529
3538
|
function resolveFactoryDeclaration(call) {
|
|
3530
|
-
const
|
|
3531
|
-
|
|
3532
|
-
|
|
3539
|
+
const name = factoryCalleeName(call);
|
|
3540
|
+
return name ? resolveFactoryFromName(name, /* @__PURE__ */ new Set()) : void 0;
|
|
3541
|
+
}
|
|
3542
|
+
function resolveFactoryFromName(name, seen) {
|
|
3543
|
+
if (seen.has(name)) return void 0;
|
|
3544
|
+
seen.add(name);
|
|
3545
|
+
const decls = name.getDefinitions().map((d) => d.getDeclarationNode()).filter((n) => n !== void 0);
|
|
3546
|
+
for (const decl of decls) {
|
|
3547
|
+
if (import_ts_morph6.Node.isFunctionDeclaration(decl) || import_ts_morph6.Node.isMethodDeclaration(decl)) return decl;
|
|
3548
|
+
if (import_ts_morph6.Node.isPropertyAssignment(decl)) {
|
|
3549
|
+
const init = decl.getInitializer();
|
|
3550
|
+
const inner = init ? unwrapExpression(init) : void 0;
|
|
3551
|
+
if (inner && import_ts_morph6.Node.isIdentifier(inner)) {
|
|
3552
|
+
const resolved = resolveFactoryFromName(inner, seen);
|
|
3553
|
+
if (resolved) return resolved;
|
|
3554
|
+
}
|
|
3555
|
+
continue;
|
|
3556
|
+
}
|
|
3557
|
+
if (import_ts_morph6.Node.isShorthandPropertyAssignment(decl)) {
|
|
3558
|
+
const nameNode = decl.getNameNode();
|
|
3559
|
+
const resolved = import_ts_morph6.Node.isIdentifier(nameNode) ? resolveFactoryFromName(nameNode, seen) : void 0;
|
|
3560
|
+
if (resolved) return resolved;
|
|
3561
|
+
}
|
|
3562
|
+
}
|
|
3563
|
+
return void 0;
|
|
3564
|
+
}
|
|
3565
|
+
function warnUnresolvedFactory(cls, calleeText, reason) {
|
|
3566
|
+
console.warn(
|
|
3567
|
+
`[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.`
|
|
3568
|
+
);
|
|
3533
3569
|
}
|
|
3534
3570
|
function resolveFactoryStaticClass(node) {
|
|
3535
3571
|
if (!import_ts_morph6.Node.isPropertyAccessExpression(node)) return void 0;
|
|
@@ -3551,12 +3587,26 @@ function resolveFactoryStaticClass(node) {
|
|
|
3551
3587
|
function resolveInheritedMethods(cls) {
|
|
3552
3588
|
const expr = resolveHeritageCall(cls.getExtends()?.getExpression());
|
|
3553
3589
|
if (!expr) return void 0;
|
|
3554
|
-
const
|
|
3555
|
-
|
|
3590
|
+
const isController = cls.getDecorator("Controller") !== void 0;
|
|
3591
|
+
const calleeText = expr.getExpression().getText();
|
|
3556
3592
|
const factoryDecl = resolveFactoryDeclaration(expr);
|
|
3557
|
-
if (!factoryDecl)
|
|
3593
|
+
if (!factoryDecl) {
|
|
3594
|
+
if (isController) {
|
|
3595
|
+
warnUnresolvedFactory(
|
|
3596
|
+
cls,
|
|
3597
|
+
calleeText,
|
|
3598
|
+
factoryCalleeName(expr) ? "its callee does not resolve to a function or method declaration" : "its callee is not a name that can be resolved statically"
|
|
3599
|
+
);
|
|
3600
|
+
}
|
|
3601
|
+
return void 0;
|
|
3602
|
+
}
|
|
3558
3603
|
const returnedClass = resolveReturnedClass(factoryDecl);
|
|
3559
|
-
if (!returnedClass)
|
|
3604
|
+
if (!returnedClass) {
|
|
3605
|
+
if (isController) {
|
|
3606
|
+
warnUnresolvedFactory(cls, calleeText, "that factory does not return a class declaration");
|
|
3607
|
+
}
|
|
3608
|
+
return void 0;
|
|
3609
|
+
}
|
|
3560
3610
|
const classArgs = [];
|
|
3561
3611
|
const namedClassArgs = {};
|
|
3562
3612
|
for (const arg of expr.getArguments()) {
|
|
@@ -3574,7 +3624,12 @@ function resolveInheritedMethods(cls) {
|
|
|
3574
3624
|
}
|
|
3575
3625
|
return {
|
|
3576
3626
|
methods: returnedClass.getMethods(),
|
|
3577
|
-
|
|
3627
|
+
// The DECLARATION's name, not the call-site text: consumers look the factory
|
|
3628
|
+
// back up by name inside `factoryFilePath`, which a qualified
|
|
3629
|
+
// `tables.createTableController` would never match. For a bare identifier
|
|
3630
|
+
// the two coincide (bar an import alias, where the declaration name is the
|
|
3631
|
+
// one that resolves anyway).
|
|
3632
|
+
factoryName: factoryDecl.getName() ?? calleeText,
|
|
3578
3633
|
factoryFilePath: factoryDecl.getSourceFile().getFilePath(),
|
|
3579
3634
|
classArgs,
|
|
3580
3635
|
namedClassArgs
|
|
@@ -3633,10 +3688,15 @@ function resolveInstantiatedReturnType(cls, methodName) {
|
|
|
3633
3688
|
|
|
3634
3689
|
// src/discovery/filter-for.ts
|
|
3635
3690
|
function resolveMixinEntityClass(mixin, project) {
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
return
|
|
3691
|
+
return resolveClassRef(mixin?.namedClassArgs?.entity ?? mixin?.classArgs[0], project);
|
|
3692
|
+
}
|
|
3693
|
+
function resolveMixinFilterClass(mixin, project) {
|
|
3694
|
+
return resolveClassRef(mixin?.namedClassArgs?.filter, project);
|
|
3695
|
+
}
|
|
3696
|
+
function resolveClassRef(ref, project) {
|
|
3697
|
+
if (!ref) return void 0;
|
|
3698
|
+
const file = project.getSourceFile(ref.filePath) ?? project.addSourceFileAtPathIfExists(ref.filePath);
|
|
3699
|
+
return file?.getClass(ref.name);
|
|
3640
3700
|
}
|
|
3641
3701
|
function classifyFilterForHint(typeInit) {
|
|
3642
3702
|
if (import_ts_morph7.Node.isStringLiteral(typeInit)) {
|
|
@@ -3736,31 +3796,55 @@ function extractApplyFilterInfo(method, sourceFile, project, mixin) {
|
|
|
3736
3796
|
}
|
|
3737
3797
|
const filterClassName = filterClassArg.getText();
|
|
3738
3798
|
const resolved = factoryStaticClass ? void 0 : findType(filterClassName, declFile, project);
|
|
3739
|
-
const
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
byName.set(key, toFilterFieldType(key, classified));
|
|
3750
|
-
}
|
|
3751
|
-
fieldTypes = [...byName.values()];
|
|
3752
|
-
}
|
|
3753
|
-
if (fieldTypes.length === 0) return null;
|
|
3754
|
-
const fieldNames = fieldTypes.map((f) => f.name);
|
|
3799
|
+
const declaredClass = factoryStaticClass ?? (resolved?.kind === "class" ? resolved.decl : resolveLocalClassDeclaration(filterClassArg));
|
|
3800
|
+
const ownRoute = !mixin || declFile.getFilePath() !== mixin.factoryFilePath;
|
|
3801
|
+
const namedByRoute = ownRoute && import_ts_morph7.Node.isIdentifier(filterClassArg) ? declaredClass : void 0;
|
|
3802
|
+
for (const candidate of orderFilterCandidates({
|
|
3803
|
+
namedByRoute,
|
|
3804
|
+
supplied: resolveMixinFilterClass(mixin, project),
|
|
3805
|
+
declared: declaredClass
|
|
3806
|
+
})) {
|
|
3807
|
+
const fieldTypes = collectFilterFields(candidate, project, mixinEntity);
|
|
3808
|
+
if (fieldTypes.length === 0) continue;
|
|
3755
3809
|
return {
|
|
3756
|
-
fieldNames,
|
|
3810
|
+
fieldNames: fieldTypes.map((f) => f.name),
|
|
3757
3811
|
fieldTypes,
|
|
3758
3812
|
source
|
|
3759
3813
|
};
|
|
3760
3814
|
}
|
|
3815
|
+
if (declaredClass) return null;
|
|
3761
3816
|
}
|
|
3762
3817
|
return null;
|
|
3763
3818
|
}
|
|
3819
|
+
function orderFilterCandidates(sources) {
|
|
3820
|
+
const ordered = [];
|
|
3821
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3822
|
+
const push = (decl, preferDeclaredEntity) => {
|
|
3823
|
+
if (!decl || seen.has(decl)) return;
|
|
3824
|
+
seen.add(decl);
|
|
3825
|
+
ordered.push({ decl, preferDeclaredEntity });
|
|
3826
|
+
};
|
|
3827
|
+
push(sources.namedByRoute, true);
|
|
3828
|
+
push(sources.supplied, true);
|
|
3829
|
+
push(sources.declared, false);
|
|
3830
|
+
return ordered;
|
|
3831
|
+
}
|
|
3832
|
+
function collectFilterFields(candidate, project, mixinEntity) {
|
|
3833
|
+
const { decl, preferDeclaredEntity } = candidate;
|
|
3834
|
+
let fieldTypes = extractClassPropertyTypes(decl, project);
|
|
3835
|
+
if (fieldTypes.length === 0) {
|
|
3836
|
+
fieldTypes = extractFilterableEntityFields(decl, project, mixinEntity, preferDeclaredEntity);
|
|
3837
|
+
}
|
|
3838
|
+
const filterForHints = extractFilterForHints(decl, project);
|
|
3839
|
+
if (filterForHints.size > 0) {
|
|
3840
|
+
const byName = new Map(fieldTypes.map((f) => [f.name, f]));
|
|
3841
|
+
for (const [key, classified] of filterForHints) {
|
|
3842
|
+
byName.set(key, toFilterFieldType(key, classified));
|
|
3843
|
+
}
|
|
3844
|
+
fieldTypes = [...byName.values()];
|
|
3845
|
+
}
|
|
3846
|
+
return fieldTypes;
|
|
3847
|
+
}
|
|
3764
3848
|
var RELATION_DECORATORS = /* @__PURE__ */ new Set(["OneToMany", "ManyToOne", "ManyToMany", "OneToOne"]);
|
|
3765
3849
|
function collectEntityFields(entityDecl, sourceFile, project, prefix, visited) {
|
|
3766
3850
|
const entityName = entityDecl.getName() ?? "";
|
|
@@ -3839,21 +3923,53 @@ function resolveDeclaredEntity(optionsArg, filterClass, project) {
|
|
|
3839
3923
|
if (!resolved || resolved.kind !== "class") return void 0;
|
|
3840
3924
|
return resolved.decl;
|
|
3841
3925
|
}
|
|
3842
|
-
function
|
|
3926
|
+
function readFieldNameList(optionsArg, key) {
|
|
3927
|
+
if (!import_ts_morph7.Node.isObjectLiteralExpression(optionsArg)) return void 0;
|
|
3928
|
+
const prop = optionsArg.getProperty(key);
|
|
3929
|
+
if (!prop || !import_ts_morph7.Node.isPropertyAssignment(prop)) return void 0;
|
|
3930
|
+
const init = prop.getInitializer();
|
|
3931
|
+
if (!init || !import_ts_morph7.Node.isArrayLiteralExpression(init)) return void 0;
|
|
3932
|
+
const names = /* @__PURE__ */ new Set();
|
|
3933
|
+
for (const el of init.getElements()) {
|
|
3934
|
+
if (import_ts_morph7.Node.isStringLiteral(el)) {
|
|
3935
|
+
names.add(el.getLiteralValue());
|
|
3936
|
+
continue;
|
|
3937
|
+
}
|
|
3938
|
+
if (import_ts_morph7.Node.isObjectLiteralExpression(el)) {
|
|
3939
|
+
const fieldProp = el.getProperty("field");
|
|
3940
|
+
if (fieldProp && import_ts_morph7.Node.isPropertyAssignment(fieldProp)) {
|
|
3941
|
+
const fieldInit = fieldProp.getInitializer();
|
|
3942
|
+
if (fieldInit && import_ts_morph7.Node.isStringLiteral(fieldInit)) {
|
|
3943
|
+
names.add(fieldInit.getLiteralValue());
|
|
3944
|
+
continue;
|
|
3945
|
+
}
|
|
3946
|
+
}
|
|
3947
|
+
}
|
|
3948
|
+
return void 0;
|
|
3949
|
+
}
|
|
3950
|
+
return names;
|
|
3951
|
+
}
|
|
3952
|
+
function narrowToDeclaredFields(fields, optionsArg) {
|
|
3953
|
+
const allowed = readFieldNameList(optionsArg, "allowed");
|
|
3954
|
+
const blocked = readFieldNameList(optionsArg, "blocked");
|
|
3955
|
+
if (!allowed && !blocked) return fields;
|
|
3956
|
+
return fields.filter(
|
|
3957
|
+
(f) => (!allowed || allowed.has(f.name)) && !(blocked?.has(f.name) ?? false)
|
|
3958
|
+
);
|
|
3959
|
+
}
|
|
3960
|
+
function extractFilterableEntityFields(filterClass, project, entityOverride, preferDeclaredEntity = false) {
|
|
3843
3961
|
const filterableDecorator = filterClass.getDecorators().find((d) => d.getName() === "Filterable");
|
|
3844
3962
|
if (!filterableDecorator) return [];
|
|
3845
3963
|
const args = filterableDecorator.getArguments();
|
|
3846
3964
|
if (args.length === 0) return [];
|
|
3847
3965
|
const optionsArg = args[0];
|
|
3848
3966
|
if (!import_ts_morph7.Node.isObjectLiteralExpression(optionsArg)) return [];
|
|
3849
|
-
const
|
|
3967
|
+
const declaredEntity = resolveDeclaredEntity(optionsArg, filterClass, project);
|
|
3968
|
+
const entityDecl = preferDeclaredEntity ? declaredEntity ?? entityOverride : entityOverride ?? declaredEntity;
|
|
3850
3969
|
if (!entityDecl) return [];
|
|
3851
|
-
const fields =
|
|
3852
|
-
entityDecl,
|
|
3853
|
-
|
|
3854
|
-
project,
|
|
3855
|
-
"",
|
|
3856
|
-
/* @__PURE__ */ new Set()
|
|
3970
|
+
const fields = narrowToDeclaredFields(
|
|
3971
|
+
collectEntityFields(entityDecl, entityDecl.getSourceFile(), project, "", /* @__PURE__ */ new Set()),
|
|
3972
|
+
optionsArg
|
|
3857
3973
|
);
|
|
3858
3974
|
const relationsDecorator = filterClass.getDecorators().find((d) => d.getName() === "Relations");
|
|
3859
3975
|
if (relationsDecorator) {
|
|
@@ -5201,7 +5317,7 @@ function createChainModuleRenderer(opts) {
|
|
|
5201
5317
|
}
|
|
5202
5318
|
|
|
5203
5319
|
// src/index.ts
|
|
5204
|
-
var VERSION = "0.
|
|
5320
|
+
var VERSION = "0.20.0";
|
|
5205
5321
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5206
5322
|
0 && (module.exports = {
|
|
5207
5323
|
CodegenError,
|