@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.
@@ -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-DCF9QSPY.cjs';
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-Dyf4ttwU.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-DCF9QSPY.js';
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-Dyf4ttwU.js';
2
2
  import 'ts-morph';
@@ -550,11 +550,34 @@ interface ContractDescriptor {
550
550
  interface MixinBinding {
551
551
  factoryName: string;
552
552
  factoryFilePath: string;
553
- /** Call-site arguments that resolve to a class declaration, in argument order. */
553
+ /**
554
+ * POSITIONAL call-site arguments that resolve to a class declaration, in
555
+ * argument order — `createTableController(Entity, { dto })` yields one entry.
556
+ */
554
557
  classArgs: Array<{
555
558
  name: string;
556
559
  filePath: string;
557
560
  }>;
561
+ /**
562
+ * Class-valued properties of an OPTIONS-OBJECT call-site argument, keyed by
563
+ * property name — `createTableController({ entity: Entity, filter: Filter })`
564
+ * yields `entity` and `filter`.
565
+ *
566
+ * Keyed by name rather than flattened into {@link classArgs} because the key
567
+ * is what identifies the argument's role: with a single object argument there
568
+ * is no positional order to read a role out of, and consumers want different
569
+ * properties. This package resolves `entity` (to derive the filter fields of a
570
+ * factory-generated `@Filterable({ entity })`, whose `entity` names the
571
+ * factory's own parameter and resolves to nothing on its own);
572
+ * `@dudousxd/nestjs-filter-codegen` reads `filter` off this same binding to
573
+ * resolve a call-site-supplied filter class. Empty for a purely positional
574
+ * call; both maps are populated independently, so a factory may be called
575
+ * either way.
576
+ */
577
+ namedClassArgs: Record<string, {
578
+ name: string;
579
+ filePath: string;
580
+ }>;
558
581
  }
559
582
  interface ControllerRef {
560
583
  className: string;
@@ -550,11 +550,34 @@ interface ContractDescriptor {
550
550
  interface MixinBinding {
551
551
  factoryName: string;
552
552
  factoryFilePath: string;
553
- /** Call-site arguments that resolve to a class declaration, in argument order. */
553
+ /**
554
+ * POSITIONAL call-site arguments that resolve to a class declaration, in
555
+ * argument order — `createTableController(Entity, { dto })` yields one entry.
556
+ */
554
557
  classArgs: Array<{
555
558
  name: string;
556
559
  filePath: string;
557
560
  }>;
561
+ /**
562
+ * Class-valued properties of an OPTIONS-OBJECT call-site argument, keyed by
563
+ * property name — `createTableController({ entity: Entity, filter: Filter })`
564
+ * yields `entity` and `filter`.
565
+ *
566
+ * Keyed by name rather than flattened into {@link classArgs} because the key
567
+ * is what identifies the argument's role: with a single object argument there
568
+ * is no positional order to read a role out of, and consumers want different
569
+ * properties. This package resolves `entity` (to derive the filter fields of a
570
+ * factory-generated `@Filterable({ entity })`, whose `entity` names the
571
+ * factory's own parameter and resolves to nothing on its own);
572
+ * `@dudousxd/nestjs-filter-codegen` reads `filter` off this same binding to
573
+ * resolve a call-site-supplied filter class. Empty for a purely positional
574
+ * call; both maps are populated independently, so a factory may be called
575
+ * either way.
576
+ */
577
+ namedClassArgs: Record<string, {
578
+ name: string;
579
+ filePath: string;
580
+ }>;
558
581
  }
559
582
  interface ControllerRef {
560
583
  className: string;
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 callee = call.getExpression();
3531
- if (!import_ts_morph6.Node.isIdentifier(callee)) return void 0;
3532
- return callee.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => import_ts_morph6.Node.isFunctionDeclaration(n));
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,30 +3587,78 @@ 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 callee = expr.getExpression();
3555
- if (!import_ts_morph6.Node.isIdentifier(callee)) return void 0;
3590
+ const isController = cls.getDecorator("Controller") !== void 0;
3591
+ const calleeText = expr.getExpression().getText();
3556
3592
  const factoryDecl = resolveFactoryDeclaration(expr);
3557
- if (!factoryDecl) return void 0;
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) return void 0;
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 = [];
3611
+ const namedClassArgs = {};
3561
3612
  for (const arg of expr.getArguments()) {
3562
- if (!import_ts_morph6.Node.isIdentifier(arg)) continue;
3563
- const decl = arg.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => import_ts_morph6.Node.isClassDeclaration(n));
3564
- if (decl) {
3565
- classArgs.push({
3566
- name: decl.getName() ?? arg.getText(),
3567
- filePath: decl.getSourceFile().getFilePath()
3568
- });
3613
+ const positional = resolveClassReference(arg);
3614
+ if (positional) {
3615
+ classArgs.push(positional);
3616
+ continue;
3617
+ }
3618
+ if (!import_ts_morph6.Node.isObjectLiteralExpression(arg)) continue;
3619
+ for (const prop of arg.getProperties()) {
3620
+ const named = resolvePropertyClassReference(prop);
3621
+ if (!named) continue;
3622
+ namedClassArgs[named.key] ??= named.ref;
3569
3623
  }
3570
3624
  }
3571
3625
  return {
3572
3626
  methods: returnedClass.getMethods(),
3573
- factoryName: callee.getText(),
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,
3574
3633
  factoryFilePath: factoryDecl.getSourceFile().getFilePath(),
3575
- classArgs
3634
+ classArgs,
3635
+ namedClassArgs
3576
3636
  };
3577
3637
  }
3638
+ function resolveClassReference(node) {
3639
+ const expr = unwrapExpression(node);
3640
+ if (!import_ts_morph6.Node.isIdentifier(expr)) return void 0;
3641
+ const decl = expr.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 && import_ts_morph6.Node.isClassDeclaration(n));
3642
+ if (!decl) return void 0;
3643
+ return {
3644
+ name: decl.getName() ?? expr.getText(),
3645
+ filePath: decl.getSourceFile().getFilePath()
3646
+ };
3647
+ }
3648
+ function resolvePropertyClassReference(prop) {
3649
+ if (import_ts_morph6.Node.isShorthandPropertyAssignment(prop)) {
3650
+ const ref2 = resolveClassReference(prop.getNameNode());
3651
+ return ref2 ? { key: prop.getName(), ref: ref2 } : void 0;
3652
+ }
3653
+ if (!import_ts_morph6.Node.isPropertyAssignment(prop)) return void 0;
3654
+ const init = prop.getInitializer();
3655
+ if (!init) return void 0;
3656
+ const ref = resolveClassReference(init);
3657
+ if (!ref) return void 0;
3658
+ const nameNode = prop.getNameNode();
3659
+ const key = import_ts_morph6.Node.isStringLiteral(nameNode) ? nameNode.getLiteralValue() : prop.getName();
3660
+ return { key, ref };
3661
+ }
3578
3662
  var mixinTypeProject;
3579
3663
  function getMixinTypeProject() {
3580
3664
  mixinTypeProject ??= new import_ts_morph6.Project({
@@ -3604,7 +3688,7 @@ function resolveInstantiatedReturnType(cls, methodName) {
3604
3688
 
3605
3689
  // src/discovery/filter-for.ts
3606
3690
  function resolveMixinEntityClass(mixin, project) {
3607
- const entityArg = mixin?.classArgs[0];
3691
+ const entityArg = mixin?.namedClassArgs?.entity ?? mixin?.classArgs[0];
3608
3692
  if (!entityArg) return void 0;
3609
3693
  const file = project.getSourceFile(entityArg.filePath) ?? project.addSourceFileAtPathIfExists(entityArg.filePath);
3610
3694
  return file?.getClass(entityArg.name);
@@ -4856,7 +4940,8 @@ function extractFromSourceFile(sourceFile, project) {
4856
4940
  const mixinBinding = inherited ? {
4857
4941
  factoryName: inherited.factoryName,
4858
4942
  factoryFilePath: inherited.factoryFilePath,
4859
- classArgs: inherited.classArgs
4943
+ classArgs: inherited.classArgs,
4944
+ namedClassArgs: inherited.namedClassArgs
4860
4945
  } : void 0;
4861
4946
  const ownMethods = cls.getMethods();
4862
4947
  const ownNames = new Set(ownMethods.map((m) => m.getName()));
@@ -5171,7 +5256,7 @@ function createChainModuleRenderer(opts) {
5171
5256
  }
5172
5257
 
5173
5258
  // src/index.ts
5174
- var VERSION = "0.17.2";
5259
+ var VERSION = "0.19.0";
5175
5260
  // Annotate the CommonJS export names for ESM import in node:
5176
5261
  0 && (module.exports = {
5177
5262
  CodegenError,