@dudousxd/nestjs-codegen 0.17.2 → 0.19.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 +76 -0
- package/dist/cli/main.cjs +104 -19
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.js +104 -19
- 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-DCF9QSPY.d.cts → index-Dyf4ttwU.d.cts} +24 -1
- package/dist/{index-DCF9QSPY.d.ts → index-Dyf4ttwU.d.ts} +24 -1
- package/dist/index.cjs +104 -19
- 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 +104 -19
- package/dist/index.js.map +1 -1
- package/dist/nest/index.cjs +104 -19
- 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 +104 -19
- package/dist/nest/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,81 @@
|
|
|
1
1
|
# @dudousxd/nestjs-codegen
|
|
2
2
|
|
|
3
|
+
## 0.19.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 8ffea16: Discover controller factories called through a property, and warn — loudly —
|
|
8
|
+
when a factory heritage clause cannot be followed at all.
|
|
9
|
+
|
|
10
|
+
A controller whose base came from anything other than a bare function name
|
|
11
|
+
contributed ZERO routes to the generated client:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
class A extends tables.createTableController(Entity, {}) {} // namespace import
|
|
15
|
+
class B extends TableFactory.create(Entity, {}) {} // static method
|
|
16
|
+
class C extends factories.table(Entity, {}) {} // re-export object
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Discovery required the callee of the factory call to be an `Identifier`, so each
|
|
20
|
+
of these resolved to nothing — and unlike the earlier filter gaps, this does not
|
|
21
|
+
mutilate a route's contract, it deletes the whole controller. `tsc` green,
|
|
22
|
+
codegen green, no warning: the first sign is a client call that does not exist.
|
|
23
|
+
|
|
24
|
+
The callee is now resolved through its name node, which covers a namespace import
|
|
25
|
+
(`ns.factory(...)`), a static method (`Factory.create(...)`) and a property of a
|
|
26
|
+
re-export object (`factories.table(...)`, including the `{ createTableController }`
|
|
27
|
+
shorthand). Bare identifiers are unchanged. A callee with no name node — an
|
|
28
|
+
element access (`factories['table'](...)`) or a callee that is itself a call
|
|
29
|
+
(`makeFactory()(Entity)`) — would mean evaluating the program to name the
|
|
30
|
+
function, so it stays unresolved.
|
|
31
|
+
|
|
32
|
+
Unresolved is no longer silent. A `@Controller`-decorated class whose heritage is
|
|
33
|
+
a factory-shaped CALL that discovery cannot follow now prints one line naming the
|
|
34
|
+
file, the class, the callee and what it costs:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
[nestjs-codegen/fast] SearchUtilsController in /src/utils/search.controller.ts
|
|
38
|
+
extends makeTableController()(...) but its callee is not a name that can be
|
|
39
|
+
resolved statically — it contributes NO routes to the generated client.
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Scoped tightly on purpose: it is a warning and never a throw, and it stays quiet
|
|
43
|
+
for `extends SomeBaseClass` (not a call) and for any class without `@Controller`
|
|
44
|
+
(extending a call expression is ordinary code). So the next unsupported shape
|
|
45
|
+
costs a line on stderr instead of a day.
|
|
46
|
+
|
|
47
|
+
`MixinBinding.factoryName` now carries the resolved DECLARATION's name rather
|
|
48
|
+
than the call-site text — a qualified `tables.createTableController` would never
|
|
49
|
+
match the by-name lookup consumers do inside `factoryFilePath`. Identical for
|
|
50
|
+
every existing bare-identifier call site, bar an import alias, where the
|
|
51
|
+
declaration name is the one that resolves.
|
|
52
|
+
|
|
53
|
+
## 0.18.0
|
|
54
|
+
|
|
55
|
+
### Minor Changes
|
|
56
|
+
|
|
57
|
+
- 553e503: Resolve the entity of a controller factory called with a single options object.
|
|
58
|
+
|
|
59
|
+
`class X extends createTableController({ entity: Util, dto: UtilDTO })` emitted
|
|
60
|
+
every one of its routes with `body: never` and `filterFields: never` — the typed
|
|
61
|
+
filter builder gone from the client, with `tsc` and codegen both green. Only the
|
|
62
|
+
positional form (`createTableController(Util, { dto })`) worked: discovery
|
|
63
|
+
collected call-site classes by scanning for identifier ARGUMENTS, and an object
|
|
64
|
+
literal is not one, so the entity behind the factory's generated
|
|
65
|
+
`@Filterable({ entity })` was unresolvable. Measured downstream at 22 tables
|
|
66
|
+
losing their filter builder at once.
|
|
67
|
+
|
|
68
|
+
Class-valued properties of an options-object argument are now collected too,
|
|
69
|
+
keyed by property name, and the entity resolver prefers a named `entity` over the
|
|
70
|
+
first positional argument. Both call forms work, so a codebase can migrate table
|
|
71
|
+
by table.
|
|
72
|
+
|
|
73
|
+
`MixinBinding` gains `namedClassArgs: Record<string, { name, filePath }>`
|
|
74
|
+
alongside the unchanged positional `classArgs`. The key is what identifies an
|
|
75
|
+
argument's role once there is no positional order to read it from, and consumers
|
|
76
|
+
want different properties: this package resolves `entity`, while
|
|
77
|
+
`@dudousxd/nestjs-filter-codegen` reads `filter` off the same binding.
|
|
78
|
+
|
|
3
79
|
## 0.17.2
|
|
4
80
|
|
|
5
81
|
### Patch Changes
|
package/dist/cli/main.cjs
CHANGED
|
@@ -3491,10 +3491,46 @@ function resolveReturnedClass(factoryDecl) {
|
|
|
3491
3491
|
}
|
|
3492
3492
|
return void 0;
|
|
3493
3493
|
}
|
|
3494
|
+
function factoryCalleeName(call) {
|
|
3495
|
+
const callee = unwrapExpression(call.getExpression());
|
|
3496
|
+
if (import_ts_morph6.Node.isIdentifier(callee)) return callee;
|
|
3497
|
+
if (import_ts_morph6.Node.isPropertyAccessExpression(callee)) {
|
|
3498
|
+
const name = callee.getNameNode();
|
|
3499
|
+
return import_ts_morph6.Node.isIdentifier(name) ? name : void 0;
|
|
3500
|
+
}
|
|
3501
|
+
return void 0;
|
|
3502
|
+
}
|
|
3494
3503
|
function resolveFactoryDeclaration(call) {
|
|
3495
|
-
const
|
|
3496
|
-
|
|
3497
|
-
|
|
3504
|
+
const name = factoryCalleeName(call);
|
|
3505
|
+
return name ? resolveFactoryFromName(name, /* @__PURE__ */ new Set()) : void 0;
|
|
3506
|
+
}
|
|
3507
|
+
function resolveFactoryFromName(name, seen) {
|
|
3508
|
+
if (seen.has(name)) return void 0;
|
|
3509
|
+
seen.add(name);
|
|
3510
|
+
const decls = name.getDefinitions().map((d) => d.getDeclarationNode()).filter((n) => n !== void 0);
|
|
3511
|
+
for (const decl of decls) {
|
|
3512
|
+
if (import_ts_morph6.Node.isFunctionDeclaration(decl) || import_ts_morph6.Node.isMethodDeclaration(decl)) return decl;
|
|
3513
|
+
if (import_ts_morph6.Node.isPropertyAssignment(decl)) {
|
|
3514
|
+
const init = decl.getInitializer();
|
|
3515
|
+
const inner = init ? unwrapExpression(init) : void 0;
|
|
3516
|
+
if (inner && import_ts_morph6.Node.isIdentifier(inner)) {
|
|
3517
|
+
const resolved = resolveFactoryFromName(inner, seen);
|
|
3518
|
+
if (resolved) return resolved;
|
|
3519
|
+
}
|
|
3520
|
+
continue;
|
|
3521
|
+
}
|
|
3522
|
+
if (import_ts_morph6.Node.isShorthandPropertyAssignment(decl)) {
|
|
3523
|
+
const nameNode = decl.getNameNode();
|
|
3524
|
+
const resolved = import_ts_morph6.Node.isIdentifier(nameNode) ? resolveFactoryFromName(nameNode, seen) : void 0;
|
|
3525
|
+
if (resolved) return resolved;
|
|
3526
|
+
}
|
|
3527
|
+
}
|
|
3528
|
+
return void 0;
|
|
3529
|
+
}
|
|
3530
|
+
function warnUnresolvedFactory(cls, calleeText, reason) {
|
|
3531
|
+
console.warn(
|
|
3532
|
+
`[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.`
|
|
3533
|
+
);
|
|
3498
3534
|
}
|
|
3499
3535
|
function resolveFactoryStaticClass(node) {
|
|
3500
3536
|
if (!import_ts_morph6.Node.isPropertyAccessExpression(node)) return void 0;
|
|
@@ -3516,30 +3552,78 @@ function resolveFactoryStaticClass(node) {
|
|
|
3516
3552
|
function resolveInheritedMethods(cls) {
|
|
3517
3553
|
const expr = resolveHeritageCall(cls.getExtends()?.getExpression());
|
|
3518
3554
|
if (!expr) return void 0;
|
|
3519
|
-
const
|
|
3520
|
-
|
|
3555
|
+
const isController = cls.getDecorator("Controller") !== void 0;
|
|
3556
|
+
const calleeText = expr.getExpression().getText();
|
|
3521
3557
|
const factoryDecl = resolveFactoryDeclaration(expr);
|
|
3522
|
-
if (!factoryDecl)
|
|
3558
|
+
if (!factoryDecl) {
|
|
3559
|
+
if (isController) {
|
|
3560
|
+
warnUnresolvedFactory(
|
|
3561
|
+
cls,
|
|
3562
|
+
calleeText,
|
|
3563
|
+
factoryCalleeName(expr) ? "its callee does not resolve to a function or method declaration" : "its callee is not a name that can be resolved statically"
|
|
3564
|
+
);
|
|
3565
|
+
}
|
|
3566
|
+
return void 0;
|
|
3567
|
+
}
|
|
3523
3568
|
const returnedClass = resolveReturnedClass(factoryDecl);
|
|
3524
|
-
if (!returnedClass)
|
|
3569
|
+
if (!returnedClass) {
|
|
3570
|
+
if (isController) {
|
|
3571
|
+
warnUnresolvedFactory(cls, calleeText, "that factory does not return a class declaration");
|
|
3572
|
+
}
|
|
3573
|
+
return void 0;
|
|
3574
|
+
}
|
|
3525
3575
|
const classArgs = [];
|
|
3576
|
+
const namedClassArgs = {};
|
|
3526
3577
|
for (const arg of expr.getArguments()) {
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3578
|
+
const positional = resolveClassReference(arg);
|
|
3579
|
+
if (positional) {
|
|
3580
|
+
classArgs.push(positional);
|
|
3581
|
+
continue;
|
|
3582
|
+
}
|
|
3583
|
+
if (!import_ts_morph6.Node.isObjectLiteralExpression(arg)) continue;
|
|
3584
|
+
for (const prop of arg.getProperties()) {
|
|
3585
|
+
const named = resolvePropertyClassReference(prop);
|
|
3586
|
+
if (!named) continue;
|
|
3587
|
+
namedClassArgs[named.key] ??= named.ref;
|
|
3534
3588
|
}
|
|
3535
3589
|
}
|
|
3536
3590
|
return {
|
|
3537
3591
|
methods: returnedClass.getMethods(),
|
|
3538
|
-
|
|
3592
|
+
// The DECLARATION's name, not the call-site text: consumers look the factory
|
|
3593
|
+
// back up by name inside `factoryFilePath`, which a qualified
|
|
3594
|
+
// `tables.createTableController` would never match. For a bare identifier
|
|
3595
|
+
// the two coincide (bar an import alias, where the declaration name is the
|
|
3596
|
+
// one that resolves anyway).
|
|
3597
|
+
factoryName: factoryDecl.getName() ?? calleeText,
|
|
3539
3598
|
factoryFilePath: factoryDecl.getSourceFile().getFilePath(),
|
|
3540
|
-
classArgs
|
|
3599
|
+
classArgs,
|
|
3600
|
+
namedClassArgs
|
|
3541
3601
|
};
|
|
3542
3602
|
}
|
|
3603
|
+
function resolveClassReference(node) {
|
|
3604
|
+
const expr = unwrapExpression(node);
|
|
3605
|
+
if (!import_ts_morph6.Node.isIdentifier(expr)) return void 0;
|
|
3606
|
+
const decl = expr.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 && import_ts_morph6.Node.isClassDeclaration(n));
|
|
3607
|
+
if (!decl) return void 0;
|
|
3608
|
+
return {
|
|
3609
|
+
name: decl.getName() ?? expr.getText(),
|
|
3610
|
+
filePath: decl.getSourceFile().getFilePath()
|
|
3611
|
+
};
|
|
3612
|
+
}
|
|
3613
|
+
function resolvePropertyClassReference(prop) {
|
|
3614
|
+
if (import_ts_morph6.Node.isShorthandPropertyAssignment(prop)) {
|
|
3615
|
+
const ref2 = resolveClassReference(prop.getNameNode());
|
|
3616
|
+
return ref2 ? { key: prop.getName(), ref: ref2 } : void 0;
|
|
3617
|
+
}
|
|
3618
|
+
if (!import_ts_morph6.Node.isPropertyAssignment(prop)) return void 0;
|
|
3619
|
+
const init = prop.getInitializer();
|
|
3620
|
+
if (!init) return void 0;
|
|
3621
|
+
const ref = resolveClassReference(init);
|
|
3622
|
+
if (!ref) return void 0;
|
|
3623
|
+
const nameNode = prop.getNameNode();
|
|
3624
|
+
const key = import_ts_morph6.Node.isStringLiteral(nameNode) ? nameNode.getLiteralValue() : prop.getName();
|
|
3625
|
+
return { key, ref };
|
|
3626
|
+
}
|
|
3543
3627
|
var mixinTypeProject;
|
|
3544
3628
|
function getMixinTypeProject() {
|
|
3545
3629
|
mixinTypeProject ??= new import_ts_morph6.Project({
|
|
@@ -3569,7 +3653,7 @@ function resolveInstantiatedReturnType(cls, methodName) {
|
|
|
3569
3653
|
|
|
3570
3654
|
// src/discovery/filter-for.ts
|
|
3571
3655
|
function resolveMixinEntityClass(mixin, project) {
|
|
3572
|
-
const entityArg = mixin?.classArgs[0];
|
|
3656
|
+
const entityArg = mixin?.namedClassArgs?.entity ?? mixin?.classArgs[0];
|
|
3573
3657
|
if (!entityArg) return void 0;
|
|
3574
3658
|
const file = project.getSourceFile(entityArg.filePath) ?? project.addSourceFileAtPathIfExists(entityArg.filePath);
|
|
3575
3659
|
return file?.getClass(entityArg.name);
|
|
@@ -4821,7 +4905,8 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
4821
4905
|
const mixinBinding = inherited ? {
|
|
4822
4906
|
factoryName: inherited.factoryName,
|
|
4823
4907
|
factoryFilePath: inherited.factoryFilePath,
|
|
4824
|
-
classArgs: inherited.classArgs
|
|
4908
|
+
classArgs: inherited.classArgs,
|
|
4909
|
+
namedClassArgs: inherited.namedClassArgs
|
|
4825
4910
|
} : void 0;
|
|
4826
4911
|
const ownMethods = cls.getMethods();
|
|
4827
4912
|
const ownNames = new Set(ownMethods.map((m) => m.getName()));
|
|
@@ -5053,7 +5138,7 @@ async function watch(config, onChange, options = {}) {
|
|
|
5053
5138
|
}
|
|
5054
5139
|
|
|
5055
5140
|
// src/index.ts
|
|
5056
|
-
var VERSION = "0.
|
|
5141
|
+
var VERSION = "0.19.0";
|
|
5057
5142
|
|
|
5058
5143
|
// src/cli/codegen.ts
|
|
5059
5144
|
async function runCodegen(opts = {}) {
|