@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/nest/index.cjs
CHANGED
|
@@ -3308,10 +3308,46 @@ function resolveReturnedClass(factoryDecl) {
|
|
|
3308
3308
|
}
|
|
3309
3309
|
return void 0;
|
|
3310
3310
|
}
|
|
3311
|
+
function factoryCalleeName(call) {
|
|
3312
|
+
const callee = unwrapExpression(call.getExpression());
|
|
3313
|
+
if (import_ts_morph6.Node.isIdentifier(callee)) return callee;
|
|
3314
|
+
if (import_ts_morph6.Node.isPropertyAccessExpression(callee)) {
|
|
3315
|
+
const name = callee.getNameNode();
|
|
3316
|
+
return import_ts_morph6.Node.isIdentifier(name) ? name : void 0;
|
|
3317
|
+
}
|
|
3318
|
+
return void 0;
|
|
3319
|
+
}
|
|
3311
3320
|
function resolveFactoryDeclaration(call) {
|
|
3312
|
-
const
|
|
3313
|
-
|
|
3314
|
-
|
|
3321
|
+
const name = factoryCalleeName(call);
|
|
3322
|
+
return name ? resolveFactoryFromName(name, /* @__PURE__ */ new Set()) : void 0;
|
|
3323
|
+
}
|
|
3324
|
+
function resolveFactoryFromName(name, seen) {
|
|
3325
|
+
if (seen.has(name)) return void 0;
|
|
3326
|
+
seen.add(name);
|
|
3327
|
+
const decls = name.getDefinitions().map((d) => d.getDeclarationNode()).filter((n) => n !== void 0);
|
|
3328
|
+
for (const decl of decls) {
|
|
3329
|
+
if (import_ts_morph6.Node.isFunctionDeclaration(decl) || import_ts_morph6.Node.isMethodDeclaration(decl)) return decl;
|
|
3330
|
+
if (import_ts_morph6.Node.isPropertyAssignment(decl)) {
|
|
3331
|
+
const init = decl.getInitializer();
|
|
3332
|
+
const inner = init ? unwrapExpression(init) : void 0;
|
|
3333
|
+
if (inner && import_ts_morph6.Node.isIdentifier(inner)) {
|
|
3334
|
+
const resolved = resolveFactoryFromName(inner, seen);
|
|
3335
|
+
if (resolved) return resolved;
|
|
3336
|
+
}
|
|
3337
|
+
continue;
|
|
3338
|
+
}
|
|
3339
|
+
if (import_ts_morph6.Node.isShorthandPropertyAssignment(decl)) {
|
|
3340
|
+
const nameNode = decl.getNameNode();
|
|
3341
|
+
const resolved = import_ts_morph6.Node.isIdentifier(nameNode) ? resolveFactoryFromName(nameNode, seen) : void 0;
|
|
3342
|
+
if (resolved) return resolved;
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3345
|
+
return void 0;
|
|
3346
|
+
}
|
|
3347
|
+
function warnUnresolvedFactory(cls, calleeText, reason) {
|
|
3348
|
+
console.warn(
|
|
3349
|
+
`[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.`
|
|
3350
|
+
);
|
|
3315
3351
|
}
|
|
3316
3352
|
function resolveFactoryStaticClass(node) {
|
|
3317
3353
|
if (!import_ts_morph6.Node.isPropertyAccessExpression(node)) return void 0;
|
|
@@ -3333,12 +3369,26 @@ function resolveFactoryStaticClass(node) {
|
|
|
3333
3369
|
function resolveInheritedMethods(cls) {
|
|
3334
3370
|
const expr = resolveHeritageCall(cls.getExtends()?.getExpression());
|
|
3335
3371
|
if (!expr) return void 0;
|
|
3336
|
-
const
|
|
3337
|
-
|
|
3372
|
+
const isController = cls.getDecorator("Controller") !== void 0;
|
|
3373
|
+
const calleeText = expr.getExpression().getText();
|
|
3338
3374
|
const factoryDecl = resolveFactoryDeclaration(expr);
|
|
3339
|
-
if (!factoryDecl)
|
|
3375
|
+
if (!factoryDecl) {
|
|
3376
|
+
if (isController) {
|
|
3377
|
+
warnUnresolvedFactory(
|
|
3378
|
+
cls,
|
|
3379
|
+
calleeText,
|
|
3380
|
+
factoryCalleeName(expr) ? "its callee does not resolve to a function or method declaration" : "its callee is not a name that can be resolved statically"
|
|
3381
|
+
);
|
|
3382
|
+
}
|
|
3383
|
+
return void 0;
|
|
3384
|
+
}
|
|
3340
3385
|
const returnedClass = resolveReturnedClass(factoryDecl);
|
|
3341
|
-
if (!returnedClass)
|
|
3386
|
+
if (!returnedClass) {
|
|
3387
|
+
if (isController) {
|
|
3388
|
+
warnUnresolvedFactory(cls, calleeText, "that factory does not return a class declaration");
|
|
3389
|
+
}
|
|
3390
|
+
return void 0;
|
|
3391
|
+
}
|
|
3342
3392
|
const classArgs = [];
|
|
3343
3393
|
const namedClassArgs = {};
|
|
3344
3394
|
for (const arg of expr.getArguments()) {
|
|
@@ -3356,7 +3406,12 @@ function resolveInheritedMethods(cls) {
|
|
|
3356
3406
|
}
|
|
3357
3407
|
return {
|
|
3358
3408
|
methods: returnedClass.getMethods(),
|
|
3359
|
-
|
|
3409
|
+
// The DECLARATION's name, not the call-site text: consumers look the factory
|
|
3410
|
+
// back up by name inside `factoryFilePath`, which a qualified
|
|
3411
|
+
// `tables.createTableController` would never match. For a bare identifier
|
|
3412
|
+
// the two coincide (bar an import alias, where the declaration name is the
|
|
3413
|
+
// one that resolves anyway).
|
|
3414
|
+
factoryName: factoryDecl.getName() ?? calleeText,
|
|
3360
3415
|
factoryFilePath: factoryDecl.getSourceFile().getFilePath(),
|
|
3361
3416
|
classArgs,
|
|
3362
3417
|
namedClassArgs
|
|
@@ -3415,10 +3470,15 @@ function resolveInstantiatedReturnType(cls, methodName) {
|
|
|
3415
3470
|
|
|
3416
3471
|
// src/discovery/filter-for.ts
|
|
3417
3472
|
function resolveMixinEntityClass(mixin, project) {
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
return
|
|
3473
|
+
return resolveClassRef(mixin?.namedClassArgs?.entity ?? mixin?.classArgs[0], project);
|
|
3474
|
+
}
|
|
3475
|
+
function resolveMixinFilterClass(mixin, project) {
|
|
3476
|
+
return resolveClassRef(mixin?.namedClassArgs?.filter, project);
|
|
3477
|
+
}
|
|
3478
|
+
function resolveClassRef(ref, project) {
|
|
3479
|
+
if (!ref) return void 0;
|
|
3480
|
+
const file = project.getSourceFile(ref.filePath) ?? project.addSourceFileAtPathIfExists(ref.filePath);
|
|
3481
|
+
return file?.getClass(ref.name);
|
|
3422
3482
|
}
|
|
3423
3483
|
function classifyFilterForHint(typeInit) {
|
|
3424
3484
|
if (import_ts_morph7.Node.isStringLiteral(typeInit)) {
|
|
@@ -3518,31 +3578,55 @@ function extractApplyFilterInfo(method, sourceFile, project, mixin) {
|
|
|
3518
3578
|
}
|
|
3519
3579
|
const filterClassName = filterClassArg.getText();
|
|
3520
3580
|
const resolved = factoryStaticClass ? void 0 : findType(filterClassName, declFile, project);
|
|
3521
|
-
const
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
byName.set(key, toFilterFieldType(key, classified));
|
|
3532
|
-
}
|
|
3533
|
-
fieldTypes = [...byName.values()];
|
|
3534
|
-
}
|
|
3535
|
-
if (fieldTypes.length === 0) return null;
|
|
3536
|
-
const fieldNames = fieldTypes.map((f) => f.name);
|
|
3581
|
+
const declaredClass = factoryStaticClass ?? (resolved?.kind === "class" ? resolved.decl : resolveLocalClassDeclaration(filterClassArg));
|
|
3582
|
+
const ownRoute = !mixin || declFile.getFilePath() !== mixin.factoryFilePath;
|
|
3583
|
+
const namedByRoute = ownRoute && import_ts_morph7.Node.isIdentifier(filterClassArg) ? declaredClass : void 0;
|
|
3584
|
+
for (const candidate of orderFilterCandidates({
|
|
3585
|
+
namedByRoute,
|
|
3586
|
+
supplied: resolveMixinFilterClass(mixin, project),
|
|
3587
|
+
declared: declaredClass
|
|
3588
|
+
})) {
|
|
3589
|
+
const fieldTypes = collectFilterFields(candidate, project, mixinEntity);
|
|
3590
|
+
if (fieldTypes.length === 0) continue;
|
|
3537
3591
|
return {
|
|
3538
|
-
fieldNames,
|
|
3592
|
+
fieldNames: fieldTypes.map((f) => f.name),
|
|
3539
3593
|
fieldTypes,
|
|
3540
3594
|
source
|
|
3541
3595
|
};
|
|
3542
3596
|
}
|
|
3597
|
+
if (declaredClass) return null;
|
|
3543
3598
|
}
|
|
3544
3599
|
return null;
|
|
3545
3600
|
}
|
|
3601
|
+
function orderFilterCandidates(sources) {
|
|
3602
|
+
const ordered = [];
|
|
3603
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3604
|
+
const push = (decl, preferDeclaredEntity) => {
|
|
3605
|
+
if (!decl || seen.has(decl)) return;
|
|
3606
|
+
seen.add(decl);
|
|
3607
|
+
ordered.push({ decl, preferDeclaredEntity });
|
|
3608
|
+
};
|
|
3609
|
+
push(sources.namedByRoute, true);
|
|
3610
|
+
push(sources.supplied, true);
|
|
3611
|
+
push(sources.declared, false);
|
|
3612
|
+
return ordered;
|
|
3613
|
+
}
|
|
3614
|
+
function collectFilterFields(candidate, project, mixinEntity) {
|
|
3615
|
+
const { decl, preferDeclaredEntity } = candidate;
|
|
3616
|
+
let fieldTypes = extractClassPropertyTypes(decl, project);
|
|
3617
|
+
if (fieldTypes.length === 0) {
|
|
3618
|
+
fieldTypes = extractFilterableEntityFields(decl, project, mixinEntity, preferDeclaredEntity);
|
|
3619
|
+
}
|
|
3620
|
+
const filterForHints = extractFilterForHints(decl, project);
|
|
3621
|
+
if (filterForHints.size > 0) {
|
|
3622
|
+
const byName = new Map(fieldTypes.map((f) => [f.name, f]));
|
|
3623
|
+
for (const [key, classified] of filterForHints) {
|
|
3624
|
+
byName.set(key, toFilterFieldType(key, classified));
|
|
3625
|
+
}
|
|
3626
|
+
fieldTypes = [...byName.values()];
|
|
3627
|
+
}
|
|
3628
|
+
return fieldTypes;
|
|
3629
|
+
}
|
|
3546
3630
|
var RELATION_DECORATORS = /* @__PURE__ */ new Set(["OneToMany", "ManyToOne", "ManyToMany", "OneToOne"]);
|
|
3547
3631
|
function collectEntityFields(entityDecl, sourceFile, project, prefix, visited) {
|
|
3548
3632
|
const entityName = entityDecl.getName() ?? "";
|
|
@@ -3621,21 +3705,53 @@ function resolveDeclaredEntity(optionsArg, filterClass, project) {
|
|
|
3621
3705
|
if (!resolved || resolved.kind !== "class") return void 0;
|
|
3622
3706
|
return resolved.decl;
|
|
3623
3707
|
}
|
|
3624
|
-
function
|
|
3708
|
+
function readFieldNameList(optionsArg, key) {
|
|
3709
|
+
if (!import_ts_morph7.Node.isObjectLiteralExpression(optionsArg)) return void 0;
|
|
3710
|
+
const prop = optionsArg.getProperty(key);
|
|
3711
|
+
if (!prop || !import_ts_morph7.Node.isPropertyAssignment(prop)) return void 0;
|
|
3712
|
+
const init = prop.getInitializer();
|
|
3713
|
+
if (!init || !import_ts_morph7.Node.isArrayLiteralExpression(init)) return void 0;
|
|
3714
|
+
const names = /* @__PURE__ */ new Set();
|
|
3715
|
+
for (const el of init.getElements()) {
|
|
3716
|
+
if (import_ts_morph7.Node.isStringLiteral(el)) {
|
|
3717
|
+
names.add(el.getLiteralValue());
|
|
3718
|
+
continue;
|
|
3719
|
+
}
|
|
3720
|
+
if (import_ts_morph7.Node.isObjectLiteralExpression(el)) {
|
|
3721
|
+
const fieldProp = el.getProperty("field");
|
|
3722
|
+
if (fieldProp && import_ts_morph7.Node.isPropertyAssignment(fieldProp)) {
|
|
3723
|
+
const fieldInit = fieldProp.getInitializer();
|
|
3724
|
+
if (fieldInit && import_ts_morph7.Node.isStringLiteral(fieldInit)) {
|
|
3725
|
+
names.add(fieldInit.getLiteralValue());
|
|
3726
|
+
continue;
|
|
3727
|
+
}
|
|
3728
|
+
}
|
|
3729
|
+
}
|
|
3730
|
+
return void 0;
|
|
3731
|
+
}
|
|
3732
|
+
return names;
|
|
3733
|
+
}
|
|
3734
|
+
function narrowToDeclaredFields(fields, optionsArg) {
|
|
3735
|
+
const allowed = readFieldNameList(optionsArg, "allowed");
|
|
3736
|
+
const blocked = readFieldNameList(optionsArg, "blocked");
|
|
3737
|
+
if (!allowed && !blocked) return fields;
|
|
3738
|
+
return fields.filter(
|
|
3739
|
+
(f) => (!allowed || allowed.has(f.name)) && !(blocked?.has(f.name) ?? false)
|
|
3740
|
+
);
|
|
3741
|
+
}
|
|
3742
|
+
function extractFilterableEntityFields(filterClass, project, entityOverride, preferDeclaredEntity = false) {
|
|
3625
3743
|
const filterableDecorator = filterClass.getDecorators().find((d) => d.getName() === "Filterable");
|
|
3626
3744
|
if (!filterableDecorator) return [];
|
|
3627
3745
|
const args = filterableDecorator.getArguments();
|
|
3628
3746
|
if (args.length === 0) return [];
|
|
3629
3747
|
const optionsArg = args[0];
|
|
3630
3748
|
if (!import_ts_morph7.Node.isObjectLiteralExpression(optionsArg)) return [];
|
|
3631
|
-
const
|
|
3749
|
+
const declaredEntity = resolveDeclaredEntity(optionsArg, filterClass, project);
|
|
3750
|
+
const entityDecl = preferDeclaredEntity ? declaredEntity ?? entityOverride : entityOverride ?? declaredEntity;
|
|
3632
3751
|
if (!entityDecl) return [];
|
|
3633
|
-
const fields =
|
|
3634
|
-
entityDecl,
|
|
3635
|
-
|
|
3636
|
-
project,
|
|
3637
|
-
"",
|
|
3638
|
-
/* @__PURE__ */ new Set()
|
|
3752
|
+
const fields = narrowToDeclaredFields(
|
|
3753
|
+
collectEntityFields(entityDecl, entityDecl.getSourceFile(), project, "", /* @__PURE__ */ new Set()),
|
|
3754
|
+
optionsArg
|
|
3639
3755
|
);
|
|
3640
3756
|
const relationsDecorator = filterClass.getDecorators().find((d) => d.getName() === "Relations");
|
|
3641
3757
|
if (relationsDecorator) {
|
|
@@ -4881,7 +4997,7 @@ async function watch(config, onChange, options = {}) {
|
|
|
4881
4997
|
}
|
|
4882
4998
|
|
|
4883
4999
|
// src/index.ts
|
|
4884
|
-
var VERSION = "0.
|
|
5000
|
+
var VERSION = "0.20.0";
|
|
4885
5001
|
|
|
4886
5002
|
// src/generate-manifest.ts
|
|
4887
5003
|
var MANIFEST_FILE = ".codegen-manifest.json";
|