Skip to content

Commit

Permalink
WIP: Add code for typed rule
Browse files Browse the repository at this point in the history
  • Loading branch information
daniel7grant committed Jun 14, 2024
1 parent 0c1f8b3 commit ba5e3b5
Show file tree
Hide file tree
Showing 9 changed files with 285 additions and 53 deletions.
25 changes: 16 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@
"eslint-plugin-prettier": "^5.0.1",
"prettier": "^3.1.0",
"ts-node": "^10.9.1",
"typescript": "^5.1.3",
"typescript": "^5.4.5",
"vitest": "^1.1.0"
},
"dependencies": {
"@typescript-eslint/parser": "^6.10.0",
"@typescript-eslint/utils": "^6.11.0"
"@typescript-eslint/utils": "^6.11.0",
"ts-api-utils": "^1.3.0"
}
}
33 changes: 21 additions & 12 deletions src/rules/enforce-column-types.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import path from 'path';
import * as vitest from 'vitest';
import { RuleTester } from '@typescript-eslint/rule-tester';
import enforceColumnTypes from './enforce-column-types';
Expand All @@ -9,6 +10,10 @@ RuleTester.describe = vitest.describe;

const ruleTester = new RuleTester({
parser: '@typescript-eslint/parser',
parserOptions: {
project: './tsconfig.json',
tsconfigRootDir: path.join(__dirname, '../../tests'),
},
});

ruleTester.run('enforce-column-types', enforceColumnTypes, {
Expand Down Expand Up @@ -157,7 +162,7 @@ ruleTester.run('enforce-column-types', enforceColumnTypes, {
name: 'should allow nullable unset types',
code: `class Entity {
@Column({ nullable: true })
unsetNullable: unset | null;
unsetNullable: number | null;
}`,
},
{
Expand All @@ -167,23 +172,18 @@ ruleTester.run('enforce-column-types', enforceColumnTypes, {
unknownField: unknown;
}`,
},
{
name: 'should allow nullable unknown types',
code: `class Entity {
@Column({ type: 'json', nullable: true })
unknownNullable: unknown | null;
}`,
},
{
name: 'should ignore reference types',
code: `class Entity {
code: `type JsonObject = {};
class Entity {
@Column({ type: 'json' })
reference: JsonObject;
}`,
},
{
name: 'should allow nullable reference types',
code: `class Entity {
code: `type JsonObject = {};
class Entity {
@Column({ type: 'json', nullable: true })
referenceNullable: JsonObject | null;
}`,
Expand Down Expand Up @@ -265,6 +265,15 @@ ruleTester.run('enforce-column-types', enforceColumnTypes, {
deletedAtDate: Date | null;
}`,
},
{
name: 'should handle aliases',
code: `
type StringAlias = string;
class Entity {
@Column({ type: 'string' })
alias: StringAlias;
}`,
},
],
invalid: [
{
Expand Down Expand Up @@ -352,7 +361,7 @@ ruleTester.run('enforce-column-types', enforceColumnTypes, {
],
},
{
name: 'should fail on nullable TypeScript type',
name: 'should fail on non-nullable string TypeScript type',
code: `class Entity {
@Column({ type: 'string' })
str: string | null;
Expand Down Expand Up @@ -415,7 +424,7 @@ ruleTester.run('enforce-column-types', enforceColumnTypes, {
],
},
{
name: 'should fail on nullable TypeORM type',
name: 'should fail on nullable literal TypeORM type',
code: `class Entity {
@Column({ type: 'string', nullable: true })
str: 'one' | 'true';
Expand Down
36 changes: 32 additions & 4 deletions src/rules/enforce-column-types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils';
import {
AST_NODE_TYPES,
ESLintUtils,
ParserServicesWithTypeInformation,
} from '@typescript-eslint/utils';
import {
ColumnType,
convertArgumentToColumnType,
convertTsTypeToColumnType,
convertTypeToColumnType,
isTypesEqual,
typeToString,
Expand All @@ -10,6 +16,7 @@ import {
findEitherDecoratorArguments,
findParentClass,
} from '../utils/treeTraversal';
import { NodeBuilderFlags, TypeChecker, TypeNode } from 'typescript';

const createRule = ESLintUtils.RuleCreator(
(name) =>
Expand Down Expand Up @@ -49,11 +56,32 @@ const enforceColumnTypes = createRule({
if (!columnArguments || !node.typeAnnotation) {
return;
}

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

const typeAnnotation = node.typeAnnotation.typeAnnotation;
let typescriptType: ColumnType;
let services: ParserServicesWithTypeInformation | undefined;
let checker: TypeChecker | undefined;
try {
services = ESLintUtils.getParserServices(context);
checker = services.program.getTypeChecker();
} catch {
// No typechecker found, continue with typeless
}

if (services && checker) {
const type = services.getTypeAtLocation(node);
const typeNode = checker.typeToTypeNode(type, undefined, NodeBuilderFlags.None);

if (typeNode) {
typescriptType = convertTsTypeToColumnType(typeNode);
} else {
typescriptType = convertTypeToColumnType(typeAnnotation);
}
} else {
typescriptType = convertTypeToColumnType(typeAnnotation);
}

if (!isTypesEqual(typeormType, typescriptType)) {
const fixReplace = typeToString(typeormType, typescriptType);
Expand Down
Loading

0 comments on commit ba5e3b5

Please sign in to comment.