Skip to content

Commit

Permalink
Add special column types
Browse files Browse the repository at this point in the history
  • Loading branch information
daniel7grant committed Jun 5, 2024
1 parent 0a731a9 commit b430142
Show file tree
Hide file tree
Showing 7 changed files with 290 additions and 44 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
### Added

- Allow Column decorator to handle string parameters, multiple parameters
- Add UUID column-type to string types
- Add support for additional Column decorators:
- Primary column decorators `PrimaryColumn`, `PrimaryGeneratedColumn` is number by default,
- Date column decorators `CreateDateColumn`, `UpdateDateColumn` is date by default, `DeleteDateColumn` is nullable,
- Version column decorator `VersionColumn` is number.

### Changed

Expand Down
196 changes: 196 additions & 0 deletions src/rules/enforce-column-types.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,76 @@ ruleTester.run('enforce-column-types', enforceColumnTypes, {
transformed: number;
}`,
},
{
name: 'should allow primary columns',
code: `class Entity {
@PrimaryColumn()
id: number;
}`,
},
{
name: 'should allow generated primary columns',
code: `class Entity {
@PrimaryGeneratedColumn()
idGenerated: number;
}`,
},
{
name: 'should allow uuid primary columns',
code: `class Entity {
@PrimaryGeneratedColumn('uuid')
uuid: string;
}`,
},
{
name: 'should allow version columns',
code: `class Entity {
@VersionColumn()
version: number;
}`,
},
{
name: 'should allow created date string columns',
code: `class Entity {
@CreateDateColumn()
createdAt: string;
}`,
},
{
name: 'should allow created date date columns',
code: `class Entity {
@CreateDateColumn()
createdAtDate: Date;
}`,
},
{
name: 'should allow updated date string columns',
code: `class Entity {
@UpdateDateColumn()
updatedAt: string;
}`,
},
{
name: 'should allow updated date date columns',
code: `class Entity {
@UpdateDateColumn()
updatedAtDate: Date;
}`,
},
{
name: 'should allow deleted date string columns',
code: `class Entity {
@DeleteDateColumn()
deletedAt: string | null;
}`,
},
{
name: 'should allow deleted date date columns',
code: `class Entity {
@DeleteDateColumn()
deletedAtDate: Date | null;
}`,
},
],
invalid: [
{
Expand Down Expand Up @@ -441,5 +511,131 @@ ruleTester.run('enforce-column-types', enforceColumnTypes, {
},
],
},
{
name: 'should fail on non-number id type',
code: `class Entity {
@PrimaryGeneratedColumn()
id: string;
}`,
errors: [
{
messageId: 'typescript_typeorm_column_mismatch',
suggestions: [
{
messageId: 'typescript_typeorm_column_suggestion',
output: `class Entity {
@PrimaryGeneratedColumn()
id: number;
}`,
},
],
},
],
},
{
name: 'should fail on non-string uuid type',
code: `class Entity {
@PrimaryGeneratedColumn('uuid')
id: number;
}`,
errors: [
{
messageId: 'typescript_typeorm_column_mismatch',
suggestions: [
{
messageId: 'typescript_typeorm_column_suggestion',
output: `class Entity {
@PrimaryGeneratedColumn('uuid')
id: string;
}`,
},
],
},
],
},
{
name: 'should fail on nullable id type',
code: `class Entity {
@PrimaryGeneratedColumn()
id: number | null;
}`,
errors: [
{
messageId: 'typescript_typeorm_column_mismatch',
suggestions: [
{
messageId: 'typescript_typeorm_column_suggestion',
output: `class Entity {
@PrimaryGeneratedColumn()
id: number;
}`,
},
],
},
],
},
{
name: 'should fail on nullable create date type',
code: `class Entity {
@CreateDateColumn()
createdAt: Date | null;
}`,
errors: [
{
messageId: 'typescript_typeorm_column_mismatch',
suggestions: [
{
messageId: 'typescript_typeorm_column_suggestion',
output: `class Entity {
@CreateDateColumn()
createdAt: Date;
}`,
},
],
},
],
},
{
name: 'should fail on nullable update date type',
code: `class Entity {
@UpdateDateColumn()
updatedAt: Date | null;
}`,
errors: [
{
messageId: 'typescript_typeorm_column_mismatch',
suggestions: [
{
messageId: 'typescript_typeorm_column_suggestion',
output: `class Entity {
@UpdateDateColumn()
updatedAt: Date;
}`,
},
],
},
],
},
{
name: 'should fail on non-nullable delete date type',
code: `class Entity {
@DeleteDateColumn()
deletedAt: Date;
}`,
errors: [
{
messageId: 'typescript_typeorm_column_mismatch',
suggestions: [
{
messageId: 'typescript_typeorm_column_suggestion',
output: `class Entity {
@DeleteDateColumn()
deletedAt: Date | null;
}`,
},
],
},
],
},
],
});
19 changes: 16 additions & 3 deletions src/rules/enforce-column-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import {
isTypesEqual,
typeToString,
} from '../utils/columnType';
import { findDecoratorArguments, findParentClass } from '../utils/treeTraversal';
import {
findDecoratorArguments,
findEitherDecoratorArguments,
findParentClass,
} from '../utils/treeTraversal';

const createRule = ESLintUtils.RuleCreator(
(name) =>
Expand Down Expand Up @@ -33,12 +37,21 @@ const enforceColumnTypes = createRule({
create(context) {
return {
PropertyDefinition(node) {
const columnArguments = findDecoratorArguments(node.decorators, 'Column');
const columnArguments = findEitherDecoratorArguments(node.decorators, [
'Column',
'PrimaryColumn',
'PrimaryGeneratedColumn',
'CreateDateColumn',
'UpdateDateColumn',
'DeleteDateColumn',
'VersionColumn',
]);
if (!columnArguments || !node.typeAnnotation) {
return;
}

const typeormType = convertArgumentToColumnType(columnArguments);
const [column, colArguments] = columnArguments;
const typeormType = convertArgumentToColumnType(column, colArguments);
const { typeAnnotation } = node.typeAnnotation;
const typescriptType = convertTypeToColumnType(typeAnnotation);

Expand Down
17 changes: 7 additions & 10 deletions src/rules/enforce-relation-types.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { AST_NODE_TYPES, ESLintUtils, TSESTree } from '@typescript-eslint/utils';
import { ReportSuggestionArray } from '@typescript-eslint/utils/ts-eslint';
import {
findDecoratorArguments,
findEitherDecoratorArguments,
findObjectArgument,
findParentClass,
} from '../utils/treeTraversal';
import {
convertArgumentToRelationType,
convertTypeToRelationType,
Relation,
relationTypes,
isTypesEqual,
typeToString,
isTypeMissingNullable,
Expand Down Expand Up @@ -55,13 +53,12 @@ const enforceColumnTypes = createRule({
create(context) {
return {
PropertyDefinition(node) {
const relationsArguments = relationTypes.map(
(relation): [Relation, TSESTree.CallExpressionArgument[] | undefined] => [
relation,
findDecoratorArguments(node.decorators, relation),
],
);
const relationArguments = relationsArguments.find(([, args]) => args);
const relationArguments = findEitherDecoratorArguments(node.decorators, [
'OneToOne',
'OneToMany',
'ManyToOne',
'ManyToMany',
]);
if (!relationArguments) {
return;
}
Expand Down
Loading

0 comments on commit b430142

Please sign in to comment.