Skip to content

Commit

Permalink
Merge pull request #18 from daniel7grant/feature/relation-wrapper
Browse files Browse the repository at this point in the history
Add support for ESM Relation wrapper
  • Loading branch information
daniel7grant authored Nov 24, 2024
2 parents 9de5032 + 2308791 commit 410798f
Show file tree
Hide file tree
Showing 7 changed files with 362 additions and 18 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## [Unreleased]

### Added

- Add support for [ESM Relation wrapper](https://typeorm.io/#relations-in-esm-projects) ([#16](https://github.com/daniel7grant/eslint-plugin-typeorm-typescript/pull/16), [#17](https://github.com/daniel7grant/eslint-plugin-typeorm-typescript/issues/17), thanks to @lmeysel for initial implementation)
- Add option to `enforce-relation-type` to make sure that relation wrapper is specified everywhere

## [0.4.0] - 2024-08-09

### Added
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,18 @@ TypeORM relation types and TypeScript types should be consistent. Because the na
that `ManyToOne` should be singular, and `OneToMany` an array. Additionally, `OneToOne` and `ManyToOne` are nullable,
which is an easy mistake to make.

This rule also supports the [ESM Relation wrapper](https://typeorm.io/#relations-in-esm-projects). If you are using ES Modules
and want to avoid circular dependency issues, you can set `specifyRelation` to always to make sure that relations are always
wrapped with `Relation<...>`.

#### Configuration

```json
{
"rules": {
"typeorm-typescript/enforce-relation-types": "error"
"typeorm-typescript/enforce-relation-types": "error",
// If you want to force setting Relation<...> everywhere
"typeorm-typescript/enforce-relation-types": ["error", { "specifyRelation": "always" }],
}
}
```
Expand Down
19 changes: 17 additions & 2 deletions examples/legacy/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
import { Entity, Column } from 'typeorm';
import { Entity, Column, OneToMany, PrimaryGeneratedColumn } from 'typeorm';

@Entity({ name: 'User' })
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number;

@Column({ type: 'number' })
userId: number;
}

@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;

@Column({ type: 'string' })
name: number;

@OneToMany(() => Post, (post) => post.userId)
posts: Post;
}
19 changes: 17 additions & 2 deletions examples/recommended/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
import { Entity, Column } from 'typeorm';
import { Entity, Column, OneToMany, PrimaryGeneratedColumn } from 'typeorm';

@Entity({ name: 'User' })
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number;

@Column({ type: 'number' })
userId: number;
}

@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;

@Column({ type: 'string' })
name: number;

@OneToMany(() => Post, (post) => post.userId)
posts: Post;
}
229 changes: 227 additions & 2 deletions src/rules/enforce-relation-types.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,22 @@ ruleTester.run('enforce-relation-types', enforceRelationTypes, {
other: Promise<Other>;
}`,
},
{
name: 'should allow valid wrapped one-to-one relations',
code: `class Entity {
@OneToOne(() => Other)
@JoinColumn()
other: Relation<Other | null>;
}`,
},
{
name: 'should allow valid wrapped non-nullable one-to-one relations',
code: `class Entity {
@OneToOne(() => Other, { nullable: false })
@JoinColumn()
other: Relation<Other>;
}`,
},
{
name: 'should allow valid one-to-many relations',
code: `class Entity {
Expand All @@ -83,6 +99,13 @@ ruleTester.run('enforce-relation-types', enforceRelationTypes, {
others: Promise<Other[]>;
}`,
},
{
name: 'should allow wrapped one-to-many relations',
code: `class Entity {
@OneToMany(() => Other, (other) => other.entity)
others: Relation<Other[]>;
}`,
},
{
name: 'should allow valid nullable many-to-one relations',
code: `class Entity {
Expand Down Expand Up @@ -125,6 +148,20 @@ ruleTester.run('enforce-relation-types', enforceRelationTypes, {
other: Promise<Other>;
}`,
},
{
name: 'should allow valid wrapped many-to-one relations',
code: `class Entity {
@ManyToOne(() => Other)
other: Relation<Other | null>;
}`,
},
{
name: 'should allow valid wrapped non-nullable many-to-one relations',
code: `class Entity {
@ManyToOne(() => Other, { nullable: false })
other: Relation<Other>;
}`,
},
{
name: 'should allow valid many-to-many relations',
code: `class Entity {
Expand All @@ -141,6 +178,14 @@ ruleTester.run('enforce-relation-types', enforceRelationTypes, {
others: Promise<Other[]>;
}`,
},
{
name: 'should allow wrapped many-to-many relations',
code: `class Entity {
@ManyToMany(() => Other)
@JoinTable()
others: Relation<Other[]>;
}`,
},
],
invalid: [
{
Expand Down Expand Up @@ -234,7 +279,7 @@ ruleTester.run('enforce-relation-types', enforceRelationTypes, {
],
},
{
name: 'should fail on mismatched one-to-one promise relations',
name: 'should fail on mismatched one-to-one lazy relations',
code: `class Entity {
@OneToOne(() => Other)
@JoinColumn()
Expand All @@ -256,6 +301,53 @@ ruleTester.run('enforce-relation-types', enforceRelationTypes, {
},
],
},
{
name: 'should fail on mismatched one-to-one wrapped relations',
code: `class Entity {
@OneToOne(() => Other)
@JoinColumn()
other: Relation<Another | null>;
}`,
errors: [
{
messageId: 'typescript_typeorm_relation_mismatch',
suggestions: [
{
messageId: 'typescript_typeorm_relation_suggestion',
output: `class Entity {
@OneToOne(() => Other)
@JoinColumn()
other: Relation<Other | null>;
}`,
},
],
},
],
},
{
name: 'should fail on mismatched one-to-one wrapped relations',
options: [{ specifyRelation: 'always' }],
code: `class Entity {
@OneToOne(() => Other)
@JoinColumn()
other: Other | null;
}`,
errors: [
{
messageId: 'typescript_typeorm_relation_specify_relation_always',
suggestions: [
{
messageId: 'typescript_typeorm_relation_suggestion',
output: `class Entity {
@OneToOne(() => Other)
@JoinColumn()
other: Relation<Other | null>;
}`,
},
],
},
],
},
{
name: 'should fail on primitive one-to-one relations',
code: `class Entity {
Expand Down Expand Up @@ -388,6 +480,49 @@ ruleTester.run('enforce-relation-types', enforceRelationTypes, {
},
],
},
{
name: 'should fail on mismatched wrapped one-to-many relations',
code: `class Entity {
@OneToMany(() => Other, (other) => other.entity)
others: Relation<Another[]>;
}`,
errors: [
{
messageId: 'typescript_typeorm_relation_mismatch',
suggestions: [
{
messageId: 'typescript_typeorm_relation_suggestion',
output: `class Entity {
@OneToMany(() => Other, (other) => other.entity)
others: Relation<Other[]>;
}`,
},
],
},
],
},
{
name: 'should fail on mismatched wrapped one-to-many relations',
options: [{ specifyRelation: 'always' }],
code: `class Entity {
@OneToMany(() => Other, (other) => other.entity)
others: Other[];
}`,
errors: [
{
messageId: 'typescript_typeorm_relation_specify_relation_always',
suggestions: [
{
messageId: 'typescript_typeorm_relation_suggestion',
output: `class Entity {
@OneToMany(() => Other, (other) => other.entity)
others: Relation<Other[]>;
}`,
},
],
},
],
},
{
name: 'should fail on primitive one-to-many relations',
code: `class Entity {
Expand Down Expand Up @@ -480,7 +615,7 @@ ruleTester.run('enforce-relation-types', enforceRelationTypes, {
],
},
{
name: 'should fail on mismatched many-to-one relations',
name: 'should fail on mismatched lazy many-to-one relations',
code: `class Entity {
@ManyToOne(() => Other)
other: Promise<Another | null>;
Expand All @@ -500,6 +635,49 @@ ruleTester.run('enforce-relation-types', enforceRelationTypes, {
},
],
},
{
name: 'should fail on mismatched wrapped many-to-one relations',
code: `class Entity {
@ManyToOne(() => Other)
other: Relation<Another | null>;
}`,
errors: [
{
messageId: 'typescript_typeorm_relation_mismatch',
suggestions: [
{
messageId: 'typescript_typeorm_relation_suggestion',
output: `class Entity {
@ManyToOne(() => Other)
other: Relation<Other | null>;
}`,
},
],
},
],
},
{
name: 'should fail on mismatched wrapped many-to-one relations',
options: [{ specifyRelation: 'always' }],
code: `class Entity {
@ManyToOne(() => Other)
other: Other | null;
}`,
errors: [
{
messageId: 'typescript_typeorm_relation_specify_relation_always',
suggestions: [
{
messageId: 'typescript_typeorm_relation_suggestion',
output: `class Entity {
@ManyToOne(() => Other)
other: Relation<Other | null>;
}`,
},
],
},
],
},
{
name: 'should fail on primitive many-to-one relations',
code: `class Entity {
Expand Down Expand Up @@ -611,6 +789,53 @@ ruleTester.run('enforce-relation-types', enforceRelationTypes, {
},
],
},
{
name: 'should fail on mismatched wrapped many-to-many relations',
code: `class Entity {
@ManyToMany(() => Other)
@JoinTable()
others: Relation<Another[]>;
}`,
errors: [
{
messageId: 'typescript_typeorm_relation_mismatch',
suggestions: [
{
messageId: 'typescript_typeorm_relation_suggestion',
output: `class Entity {
@ManyToMany(() => Other)
@JoinTable()
others: Relation<Other[]>;
}`,
},
],
},
],
},
{
name: 'should fail on specify relation always many-to-many relations',
options: [{ specifyRelation: 'always' }],
code: `class Entity {
@ManyToMany(() => Other)
@JoinTable()
others: Other[];
}`,
errors: [
{
messageId: 'typescript_typeorm_relation_specify_relation_always',
suggestions: [
{
messageId: 'typescript_typeorm_relation_suggestion',
output: `class Entity {
@ManyToMany(() => Other)
@JoinTable()
others: Relation<Other[]>;
}`,
},
],
},
],
},
{
name: 'should fail on primitive many-to-many relations',
code: `class Entity {
Expand Down
Loading

0 comments on commit 410798f

Please sign in to comment.