Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(objection): fix decorators to allow to work with circular imports in esbuild #2974

Merged
merged 1 commit into from
Jan 31, 2025

Conversation

LonguCodes
Copy link
Contributor

@LonguCodes LonguCodes commented Jan 29, 2025

Information

Fixes issue, where esbuild does not put decorators at the end, which results in usage of types (module references) before their initialization.

Type Breaking change
Fix No

Todos

  • Tests
  • Coverage
  • Example
  • Documentation

Summary by CodeRabbit

  • Tests

    • Added new test cases for @BelongsToOne decorator to validate:
      • Custom relationship model specification
      • Custom relationship path configuration
      • Functionality with options supplied in different configurations
  • Improvements

    • Enhanced flexibility of the BelongsToOne decorator to accept various parameter types
    • Improved type-checking for model class specifications
    • Updated relationship mapping logic for better handling of options
    • Simplified logic in the RelatesTo function for direct use of opts.type

Copy link

coderabbitai bot commented Jan 29, 2025

Walkthrough

The pull request introduces enhancements to the @BelongsToOne decorator in the Objection ORM package. The changes focus on improving the flexibility of defining relationships between models by allowing more dynamic configuration. This includes supporting custom model specifications, relationship paths, and more flexible parameter handling. The modifications span multiple files, including test cases, decorator implementation, and utility functions, while maintaining backward compatibility.

Changes

File Change Summary
packages/orm/objection/src/decorators/belongsToOne.spec.ts Added four new test cases for @BelongsToOne decorator, including tests for custom relationship models and paths, and handling options supplied in different configurations.
packages/orm/objection/src/decorators/belongsToOne.ts Updated function signature to support multiple overloads for flexible parameters, including model class specifier and relationship options; added backward compatibility logic.
packages/orm/objection/src/decorators/relatesTo.ts Removed conditional block for dynamic resolution of opts.type, simplifying the logic.
packages/orm/objection/src/domain/RelationshipOpts.ts Added isModelClassSpecifier utility function for type checking.
packages/orm/objection/src/utils/createRelationshipMapping.ts Modified handling of opts parameter to support dynamic model class resolution while ensuring robust property updates.

Sequence Diagram

sequenceDiagram
    participant Developer
    participant BelongsToOne
    participant RelatesTo
    participant CreateRelationshipMapping

    Developer->>BelongsToOne: Define relationship
    BelongsToOne->>RelatesTo: Pass model and options
    RelatesTo->>CreateRelationshipMapping: Create relationship mapping
    CreateRelationshipMapping-->>RelatesTo: Return mapping configuration
    RelatesTo-->>BelongsToOne: Apply relationship
    BelongsToOne-->>Developer: Configured relationship
Loading

This sequence diagram illustrates the high-level flow of how the enhanced @BelongsToOne decorator processes relationship configurations, showing the interaction between different components during relationship definition.

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🔭 Outside diff range comments (1)
packages/orm/objection/src/decorators/belongsToOne.spec.ts (1)

Line range hint 1-111: Consider enhancing test coverage for robustness.

While the current tests effectively cover the happy path scenarios, consider adding the following test categories for more comprehensive coverage:

  1. Runtime Type Validation:
// Test invalid model types
@BelongsToOne(() => String) // Should fail
@BelongsToOne(() => {}) // Should fail
  1. Circular Dependency Resolution:
// Test circular dependencies
@Entity("author")
class Author {
  @BelongsToOne(() => Book)
  book?: Book;
}

@Entity("book")
class Book {
  @BelongsToOne(() => Author)
  author?: Author;
}
  1. Error Handling:
// Test decorator application on invalid property types
@BelongsToOne(() => User)
user: string; // Should fail, not a Model type
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 30f0567 and 1e35834.

📒 Files selected for processing (5)
  • packages/orm/objection/src/decorators/belongsToOne.spec.ts (1 hunks)
  • packages/orm/objection/src/decorators/belongsToOne.ts (1 hunks)
  • packages/orm/objection/src/decorators/relatesTo.ts (0 hunks)
  • packages/orm/objection/src/domain/RelationshipOpts.ts (1 hunks)
  • packages/orm/objection/src/utils/createRelationshipMapping.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/orm/objection/src/decorators/relatesTo.ts
🧰 Additional context used
🪛 Biome (1.9.4)
packages/orm/objection/src/utils/createRelationshipMapping.ts

[error] 12-12: Reassigning a function parameter is confusing.

The parameter is declared here:

Use a local variable instead.

(lint/style/noParameterAssign)

packages/orm/objection/src/decorators/belongsToOne.ts

[error] 44-44: Reassigning a function parameter is confusing.

The parameter is declared here:

Use a local variable instead.

(lint/style/noParameterAssign)


[error] 45-45: Reassigning a function parameter is confusing.

The parameter is declared here:

Use a local variable instead.

(lint/style/noParameterAssign)

🔇 Additional comments (9)
packages/orm/objection/src/decorators/belongsToOne.ts (7)

1-1: No immediate concerns about importing from "node:os".
It doesn’t appear that type is used below. If this import is unnecessary, consider removing it.


3-5: Imports look good.
These imports reflect the new references needed by the revised decorator logic.


10-13: Documentation block check.
These doc comments for the @BelongsToOne decorator param are concise and clear.


14-21: Function overload definitions for BelongsToOne.
Providing separate overloads for different parameter types helps ensure clarity. Everything looks good here.


22-29: Additional function overload for BelongsToOne.
This overload continues to maintain backward compatibility while adding extra flexibility.


30-37: Documentation block check.
The doc comments offer good clarity on the parameters typeOrOpts and opts.


48-54: Final computation of options looks good.
Merging opts with typeOrOpts to handle multiple argument forms appears correct, and using isModelClassSpecifier is a well-structured approach.

packages/orm/objection/src/utils/createRelationshipMapping.ts (1)

4-4: New import for isModelClassFactory and RelationshipOpts.
This aligns with the approach of resolving opts.type dynamically.

packages/orm/objection/src/domain/RelationshipOpts.ts (1)

15-18: Type guard utility is well-structured.
isModelClassSpecifier neatly handles both string-based and function-based specifiers.

@LonguCodes LonguCodes force-pushed the fix-objection-esbuild-esm branch from 1e35834 to ad2bfde Compare January 30, 2025 09:58
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1e35834 and ad2bfde.

📒 Files selected for processing (5)
  • packages/orm/objection/src/decorators/belongsToOne.spec.ts (1 hunks)
  • packages/orm/objection/src/decorators/belongsToOne.ts (1 hunks)
  • packages/orm/objection/src/decorators/relatesTo.ts (0 hunks)
  • packages/orm/objection/src/domain/RelationshipOpts.ts (1 hunks)
  • packages/orm/objection/src/utils/createRelationshipMapping.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/orm/objection/src/decorators/relatesTo.ts
🧰 Additional context used
📓 Learnings (2)
packages/orm/objection/src/decorators/belongsToOne.spec.ts (2)
Learnt from: LonguCodes
PR: tsedio/tsed#2974
File: packages/orm/objection/src/decorators/belongsToOne.spec.ts:60-84
Timestamp: 2025-01-29T23:20:16.323Z
Learning: Testing circular dependencies in TypeScript/JavaScript requires specific compiler settings and a live runtime environment, as it cannot be effectively tested through unit tests alone. This is particularly relevant for bundler-specific behaviors like esbuild's handling of circular imports.
Learnt from: LonguCodes
PR: tsedio/tsed#2974
File: packages/orm/objection/src/decorators/belongsToOne.spec.ts:86-111
Timestamp: 2025-01-29T23:19:32.209Z
Learning: The test suite for `@BelongsToOne` decorator in `belongsToOne.spec.ts` focuses on positive cases of relationship mapping configuration. Error cases are not applicable to this test suite as they are likely handled at TypeScript compilation time or in separate validation layers.
packages/orm/objection/src/decorators/belongsToOne.ts (1)
Learnt from: LonguCodes
PR: tsedio/tsed#2974
File: packages/orm/objection/src/decorators/belongsToOne.spec.ts:86-111
Timestamp: 2025-01-29T23:19:32.209Z
Learning: The test suite for `@BelongsToOne` decorator in `belongsToOne.spec.ts` focuses on positive cases of relationship mapping configuration. Error cases are not applicable to this test suite as they are likely handled at TypeScript compilation time or in separate validation layers.
🪛 Biome (1.9.4)
packages/orm/objection/src/utils/createRelationshipMapping.ts

[error] 12-12: Reassigning a function parameter is confusing.

The parameter is declared here:

Use a local variable instead.

(lint/style/noParameterAssign)

packages/orm/objection/src/decorators/belongsToOne.ts

[error] 42-42: Reassigning a function parameter is confusing.

The parameter is declared here:

Use a local variable instead.

(lint/style/noParameterAssign)


[error] 43-43: Reassigning a function parameter is confusing.

The parameter is declared here:

Use a local variable instead.

(lint/style/noParameterAssign)

🔇 Additional comments (6)
packages/orm/objection/src/decorators/belongsToOne.ts (2)

35-38: Overloads look good

The multiple overloads ensure flexible usage, and the overall approach adequately covers different argument types.


42-43: Parameter reassignment confusing

Similar to a previous comment noted in past reviews, reassigning typeOrOpts and opts could reduce readability and risk unintended side effects. Consider using local variables for clarity.

🧰 Tools
🪛 Biome (1.9.4)

[error] 42-42: Reassigning a function parameter is confusing.

The parameter is declared here:

Use a local variable instead.

(lint/style/noParameterAssign)


[error] 43-43: Reassigning a function parameter is confusing.

The parameter is declared here:

Use a local variable instead.

(lint/style/noParameterAssign)

packages/orm/objection/src/utils/createRelationshipMapping.ts (1)

12-17: Avoid parameter reassignment

Reassigning opts makes it harder to follow the flow of data. A best practice is to store the transformed value in a new variable rather than overriding the existing parameter.

🧰 Tools
🪛 Biome (1.9.4)

[error] 12-12: Reassigning a function parameter is confusing.

The parameter is declared here:

Use a local variable instead.

(lint/style/noParameterAssign)

packages/orm/objection/src/domain/RelationshipOpts.ts (1)

16-18: Logic is correct

This utility function accurately checks whether the input is a valid model class specifier.

packages/orm/objection/src/decorators/belongsToOne.spec.ts (2)

60-84: LGTM! Test case effectively validates the circular import fix.

The test case properly validates the new function-style model specification syntax () => User, which directly addresses the PR's objective of fixing circular import issues in esbuild.


86-111: LGTM! Test case provides comprehensive coverage.

The test case effectively validates the combined usage of function-style model specification with custom relationship paths, ensuring both features work together seamlessly.

Comment on lines +60 to +111
it("should set custom relationship model", () => {
@Entity("user")
class User extends Model {
@IdColumn()
id!: string;
}
@Entity("movie")
class Movie extends Model {
@IdColumn()
id!: string;
userId!: string;
@BelongsToOne(() => User)
user?: User;
}
expect(Movie.relationMappings).toEqual({
user: {
relation: Model.BelongsToOneRelation,
modelClass: User,
join: {
from: "movie.userId",
to: "user.id"
}
}
});
});

it("should set custom relationship model and relationship path", () => {
@Entity("user")
class User extends Model {
@IdColumn()
id!: string;
userId!: string;
}
@Entity("movie")
class Movie extends Model {
@IdColumn()
id!: string;
ownerId!: string;
@BelongsToOne(() => User, {from: "ownerId", to: "userId"})
user?: User;
}
expect(Movie.relationMappings).toEqual({
user: {
relation: Model.BelongsToOneRelation,
modelClass: User,
join: {
from: "movie.ownerId",
to: "user.userId"
}
}
});
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider enhancing test descriptions for clarity.

The test descriptions could be more specific about the features being tested. Consider:

  • "should set custom relationship model using function syntax for circular dependency support"
  • "should combine function syntax with custom relationship path configuration"

@LonguCodes LonguCodes force-pushed the fix-objection-esbuild-esm branch from ad2bfde to fbc0ed8 Compare January 30, 2025 15:48
@LonguCodes
Copy link
Contributor Author

@Romakita Would you be able to run the CI again? I've added coverage for the missing lines

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ad2bfde and fbc0ed8.

📒 Files selected for processing (5)
  • packages/orm/objection/src/decorators/belongsToOne.spec.ts (1 hunks)
  • packages/orm/objection/src/decorators/belongsToOne.ts (1 hunks)
  • packages/orm/objection/src/decorators/relatesTo.ts (0 hunks)
  • packages/orm/objection/src/domain/RelationshipOpts.ts (1 hunks)
  • packages/orm/objection/src/utils/createRelationshipMapping.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/orm/objection/src/decorators/relatesTo.ts
🧰 Additional context used
📓 Learnings (2)
packages/orm/objection/src/decorators/belongsToOne.ts (1)
Learnt from: LonguCodes
PR: tsedio/tsed#2974
File: packages/orm/objection/src/decorators/belongsToOne.spec.ts:86-111
Timestamp: 2025-01-29T23:19:32.209Z
Learning: The test suite for `@BelongsToOne` decorator in `belongsToOne.spec.ts` focuses on positive cases of relationship mapping configuration. Error cases are not applicable to this test suite as they are likely handled at TypeScript compilation time or in separate validation layers.
packages/orm/objection/src/decorators/belongsToOne.spec.ts (2)
Learnt from: LonguCodes
PR: tsedio/tsed#2974
File: packages/orm/objection/src/decorators/belongsToOne.spec.ts:60-84
Timestamp: 2025-01-29T23:20:16.323Z
Learning: Testing circular dependencies in TypeScript/JavaScript requires specific compiler settings and a live runtime environment, as it cannot be effectively tested through unit tests alone. This is particularly relevant for bundler-specific behaviors like esbuild's handling of circular imports.
Learnt from: LonguCodes
PR: tsedio/tsed#2974
File: packages/orm/objection/src/decorators/belongsToOne.spec.ts:86-111
Timestamp: 2025-01-29T23:19:32.209Z
Learning: The test suite for `@BelongsToOne` decorator in `belongsToOne.spec.ts` focuses on positive cases of relationship mapping configuration. Error cases are not applicable to this test suite as they are likely handled at TypeScript compilation time or in separate validation layers.
🪛 Biome (1.9.4)
packages/orm/objection/src/utils/createRelationshipMapping.ts

[error] 12-12: Reassigning a function parameter is confusing.

The parameter is declared here:

Use a local variable instead.

(lint/style/noParameterAssign)

packages/orm/objection/src/decorators/belongsToOne.ts

[error] 42-42: Reassigning a function parameter is confusing.

The parameter is declared here:

Use a local variable instead.

(lint/style/noParameterAssign)


[error] 43-43: Reassigning a function parameter is confusing.

The parameter is declared here:

Use a local variable instead.

(lint/style/noParameterAssign)

🔇 Additional comments (10)
packages/orm/objection/src/decorators/belongsToOne.ts (4)

1-3: Imports look good.
No issues noted with the updated imports of Model, ModelClassSpecifier, isModelClassSpecifier, RelationshipOpts, and RelationshipOptsWithoutThrough.


12-27: Overload signatures are well-defined.
The multiple overloads for BelongsToOne add clarity and flexibility without breaking existing usage. Nice work keeping them consistent.


35-44: Avoid reassigning function parameters.
Reassigning typeOrOpts and opts (lines 42–43) is confusing and flagged by the lint rule.

Use a local variable instead, for instance:

if (!typeOrOpts && opts) {
-  typeOrOpts = opts;
-  opts = undefined;
+  const newOpts = opts;
+  return RelatesTo(Model.BelongsToOneRelation, newOpts);
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 42-42: Reassigning a function parameter is confusing.

The parameter is declared here:

Use a local variable instead.

(lint/style/noParameterAssign)


[error] 43-43: Reassigning a function parameter is confusing.

The parameter is declared here:

Use a local variable instead.

(lint/style/noParameterAssign)


46-50: Dynamic options assembly is clear.
The logic concisely merges typeOrOpts into opts when it’s a ModelClassSpecifier. The approach is clean and easy to maintain.

packages/orm/objection/src/utils/createRelationshipMapping.ts (3)

4-4: Import statement is aligned with domain usage.
Kudos for maintaining consistency in your import references for RelationshipOpts.


11-17: Avoid reassigning the opts parameter.
This practice is flagged by your linter (lint/style/noParameterAssign) and can degrade clarity.

Use a local variable to refine or transform opts, for example:

-  opts = opts
-    ? {
-        ...opts,
-        type: isModelClassFactory(opts.type) ? opts.type() : opts.type
-      }
-    : undefined;
+  const resolvedOpts = opts
+    ? {
+        ...opts,
+        type: isModelClassFactory(opts.type) ? opts.type() : opts.type
+      }
+    : undefined;

  return {
    ...
    modelClass: resolvedOpts?.type || ...
  };
🧰 Tools
🪛 Biome (1.9.4)

[error] 12-12: Reassigning a function parameter is confusing.

The parameter is declared here:

Use a local variable instead.

(lint/style/noParameterAssign)


19-28: Overall mapping structure is coherent.
Good job returning a clear configuration object for the relationship with minimal nesting.

packages/orm/objection/src/domain/RelationshipOpts.ts (1)

16-18: Type predicate is well-crafted.
The isModelClassSpecifier function cleanly checks for string or function types. This will improve type safety in downstream logic.

packages/orm/objection/src/decorators/belongsToOne.spec.ts (2)

60-84: LGTM! Test case effectively validates function syntax for model specification.

The test case properly validates the new @BelongsToOne(() => User) syntax, which is crucial for supporting circular imports in esbuild. The assertions comprehensively verify both the relationship type and mapping configuration.


112-137: LGTM! Test case effectively validates backward compatibility.

The test case properly validates that the decorator maintains backward compatibility by supporting undefined as the first argument while still correctly processing the options in the second argument.

Comment on lines +6 to +11
/**
*
* @param type
* @decorator
* @objection
*/
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider including usage examples in the JSDoc.
While these lines provide a brief overview, adding a small code snippet demonstrating how to use @BelongsToOne would aid consumers of this decorator.

Comment on lines +86 to +111
it("should set custom relationship model and relationship path", () => {
@Entity("user")
class User extends Model {
@IdColumn()
id!: string;
userId!: string;
}
@Entity("movie")
class Movie extends Model {
@IdColumn()
id!: string;
ownerId!: string;
@BelongsToOne(() => User, {from: "ownerId", to: "userId"})
user?: User;
}
expect(Movie.relationMappings).toEqual({
user: {
relation: Model.BelongsToOneRelation,
modelClass: User,
join: {
from: "movie.ownerId",
to: "user.userId"
}
}
});
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider enhancing the test description for clarity.

While the test effectively validates the combined usage of function syntax and custom path configuration, consider making the test description more explicit about the features being tested:

it("should support circular imports by combining function syntax with custom relationship path", () => {

@Romakita Romakita merged commit 4e347cc into tsedio:production Jan 31, 2025
12 checks passed
@Romakita
Copy link
Collaborator

🎉 This PR is included in version 8.4.6 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants