From 7f5cb0ea1ce5d8a1db8fc39bedb93d0a7fcd79a3 Mon Sep 17 00:00:00 2001 From: Mohamad Mohebifar Date: Sun, 3 Mar 2024 11:38:57 -0500 Subject: [PATCH] chore: Add lint rule for type imports --- packages/babel-plugin/.eslintrc.cjs | 7 ++++++ packages/babel-plugin/package.json | 2 +- .../babel-plugin/src/classes/Component.ts | 5 ++-- .../src/classes/ComponentMutableSegment.ts | 9 +++++-- .../src/classes/ComponentRunnableSegment.ts | 13 +++++----- .../src/classes/ComponentSegmentDependency.ts | 22 +++++----------- .../src/classes/ComponentVariable.ts | 15 +++++------ .../make-dependency-condition.ts | 2 +- .../src/utils/ast-tools/is-accessor-node.ts | 16 ++++++++++++ .../src/utils/errors/RightmostIdNotFound.ts | 2 +- .../unwrap-generic-expression.ts | 2 +- .../micro-transformers/unwrap-jsx-elements.ts | 4 +-- .../unwrap-jsx-expressions.ts | 4 +-- .../src/utils/path-tools/find-components.ts | 2 +- .../path-tools/find-mutating-expression.ts | 2 +- .../is-in-the-same-function-scope.ts | 4 +-- .../utils/path-tools/reorder-by-topology.ts | 2 +- .../is-in-the-same-function-scope.test.ts | 2 +- .../scope-tools/is-scope-descendant-of.ts | 2 +- packages/babel-plugin/src/utils/testing.ts | 4 +-- packages/eslint-config/library.js | 25 ++++++++++++++----- packages/eslint-config/package.json | 1 + yarn.lock | 2 +- 23 files changed, 91 insertions(+), 58 deletions(-) create mode 100644 packages/babel-plugin/src/utils/ast-tools/is-accessor-node.ts diff --git a/packages/babel-plugin/.eslintrc.cjs b/packages/babel-plugin/.eslintrc.cjs index e024dce..aa1d3bf 100644 --- a/packages/babel-plugin/.eslintrc.cjs +++ b/packages/babel-plugin/.eslintrc.cjs @@ -6,5 +6,12 @@ module.exports = { project: true, tsConfigRootDir: __dirname, }, + settings: { + "import/resolver": { + typescript: { + project: __dirname, + }, + }, + }, ignorePatterns: ["src/tests/fixtures/**/*.tsx"], }; diff --git a/packages/babel-plugin/package.json b/packages/babel-plugin/package.json index afad039..69843a7 100644 --- a/packages/babel-plugin/package.json +++ b/packages/babel-plugin/package.json @@ -21,7 +21,7 @@ "dev": "yarn build --watch", "build": "tsup", "test": "jest", - "lint": "eslint src/**/*.{ts,tsx} --max-warnings 15" + "lint": "eslint src/ --max-warnings 15" }, "devDependencies": { "@babel/plugin-syntax-jsx": "^7.23.3", diff --git a/packages/babel-plugin/src/classes/Component.ts b/packages/babel-plugin/src/classes/Component.ts index ded7759..75e57e4 100644 --- a/packages/babel-plugin/src/classes/Component.ts +++ b/packages/babel-plugin/src/classes/Component.ts @@ -1,5 +1,5 @@ import type * as babel from "@babel/core"; -import { Binding } from "@babel/traverse"; +import type { Binding } from "@babel/traverse"; import * as t from "@babel/types"; import { DEFAULT_CACHE_COMMIT_VARIABLE_NAME, @@ -10,8 +10,7 @@ import { import { isControlFlowStatement } from "~/utils/path-tools/control-flow-utils"; import { isInTheSameFunctionScope } from "~/utils/path-tools/is-in-the-same-function-scope"; import { isChildOfScope } from "~/utils/scope-tools/is-scope-descendant-of"; -import { getFunctionParent } from "~/utils/path-tools/get-function-parent"; -import { ComponentMutableSegment } from "./ComponentMutableSegment"; +import type { ComponentMutableSegment } from "./ComponentMutableSegment"; import { ComponentRunnableSegment } from "./ComponentRunnableSegment"; import { ComponentVariable } from "./ComponentVariable"; diff --git a/packages/babel-plugin/src/classes/ComponentMutableSegment.ts b/packages/babel-plugin/src/classes/ComponentMutableSegment.ts index 89deb6e..acbe027 100644 --- a/packages/babel-plugin/src/classes/ComponentMutableSegment.ts +++ b/packages/babel-plugin/src/classes/ComponentMutableSegment.ts @@ -3,7 +3,8 @@ import * as t from "@babel/types"; import { makeDependencyCondition } from "~/utils/ast-factories/make-dependency-condition"; import { DEFAULT_SEGMENT_CALLABLE_VARIABLE_NAME } from "~/utils/constants"; import { hasHookCall } from "~/utils/path-tools/has-hook-call"; -import { Component } from "./Component"; +import { isAccessorNode } from "~/utils/ast-tools/is-accessor-node"; +import type { Component } from "./Component"; import type { ComponentRunnableSegment } from "./ComponentRunnableSegment"; import { ComponentSegmentDependency } from "./ComponentSegmentDependency"; import type { ComponentVariable } from "./ComponentVariable"; @@ -79,9 +80,13 @@ export abstract class ComponentMutableSegment { return; } + if (!isAccessorNode(accessorNode)) { + return; + } + const componentSegmentDependency = new ComponentSegmentDependency( componentVariable, - accessorNode as any + accessorNode ); let alreadyHasDependency = false; diff --git a/packages/babel-plugin/src/classes/ComponentRunnableSegment.ts b/packages/babel-plugin/src/classes/ComponentRunnableSegment.ts index ed4e9b2..58625a8 100644 --- a/packages/babel-plugin/src/classes/ComponentRunnableSegment.ts +++ b/packages/babel-plugin/src/classes/ComponentRunnableSegment.ts @@ -1,4 +1,4 @@ -import * as babel from "@babel/core"; +import type * as babel from "@babel/core"; import * as t from "@babel/types"; import { convertStatementToSegmentCallable } from "~/utils/micro-transformers/convert-statement-to-segment-callable"; import { convertDeclarationToAssignments } from "~/utils/micro-transformers/convert-declaration-to-assignments"; @@ -11,13 +11,14 @@ import { getReferencedVariablesInside } from "~/utils/path-tools/get-referenced- import { reorderByTopology } from "~/utils/path-tools/reorder-by-topology"; import { unwrapJsxElements } from "~/utils/micro-transformers/unwrap-jsx-elements"; import { unwrapJsxExpressions } from "~/utils/micro-transformers/unwrap-jsx-expressions"; -import { Component } from "./Component"; +import type { Component } from "./Component"; +import type { + SegmentTransformationResult} from "./ComponentMutableSegment"; import { - ComponentMutableSegment, - SegmentTransformationResult, + ComponentMutableSegment } from "./ComponentMutableSegment"; -import { ComponentSegmentDependency } from "./ComponentSegmentDependency"; -import { ComponentVariable } from "./ComponentVariable"; +import type { ComponentSegmentDependency } from "./ComponentSegmentDependency"; +import type { ComponentVariable } from "./ComponentVariable"; export class ComponentRunnableSegment extends ComponentMutableSegment { private blockReturnStatement: babel.NodePath | null = diff --git a/packages/babel-plugin/src/classes/ComponentSegmentDependency.ts b/packages/babel-plugin/src/classes/ComponentSegmentDependency.ts index 2b07d85..d54c530 100644 --- a/packages/babel-plugin/src/classes/ComponentSegmentDependency.ts +++ b/packages/babel-plugin/src/classes/ComponentSegmentDependency.ts @@ -1,12 +1,11 @@ import * as t from "@babel/types"; -import { ComponentVariable } from "./ComponentVariable"; - -type AccessorNode = - | t.MemberExpression - | t.OptionalMemberExpression - | t.Identifier - | t.PrivateName; +import type { AccessorNode } from "~/utils/ast-tools/is-accessor-node"; +import { isAccessorNode } from "~/utils/ast-tools/is-accessor-node"; +import type { ComponentVariable } from "./ComponentVariable"; +/** + * A singly linked list of member expressions + */ export class AccessChainItem { public nextComputed = false; public right?: AccessChainItem = undefined; @@ -27,15 +26,6 @@ export class AccessChainItem { } } -function isAccessorNode(node: t.Node): node is AccessorNode { - return ( - t.isMemberExpression(node) || - t.isOptionalMemberExpression(node) || - t.isIdentifier(node) || - t.isPrivateName(node) - ); -} - // When the variable is used in a member expression, we should optimize comparisons to the last member of member expression as well export class ComponentSegmentDependency { private root: AccessChainItem; diff --git a/packages/babel-plugin/src/classes/ComponentVariable.ts b/packages/babel-plugin/src/classes/ComponentVariable.ts index 8ca4664..e9e3fe1 100644 --- a/packages/babel-plugin/src/classes/ComponentVariable.ts +++ b/packages/babel-plugin/src/classes/ComponentVariable.ts @@ -1,5 +1,5 @@ import type * as babel from "@babel/core"; -import { Binding } from "@babel/traverse"; +import type { Binding } from "@babel/traverse"; import * as t from "@babel/types"; import { convertStatementToSegmentCallable } from "~/utils/micro-transformers/convert-statement-to-segment-callable"; import { makeCacheEnqueueCallStatement } from "~/utils/ast-factories/make-cache-enqueue-call-statement"; @@ -12,16 +12,17 @@ import { } from "~/utils/constants"; import { findMutatingExpression } from "~/utils/path-tools/find-mutating-expression"; import { getReferencedVariablesInside } from "~/utils/path-tools/get-referenced-variables-inside"; -import { UnwrappedAssignmentEntry } from "~/utils/micro-transformers/unwrap-pattern-assignment"; +import type { UnwrappedAssignmentEntry } from "~/utils/micro-transformers/unwrap-pattern-assignment"; +import { isForStatementInit } from "~/utils/path-tools/control-flow-utils"; import { getDeclaredIdentifiersInLVal } from "../utils/path-tools/get-declared-identifiers-in-lval"; -import { Component } from "./Component"; +import type { Component } from "./Component"; +import type { + SegmentTransformationResult} from "./ComponentMutableSegment"; import { - ComponentMutableSegment, - SegmentTransformationResult, + ComponentMutableSegment } from "./ComponentMutableSegment"; import type { ComponentRunnableSegment } from "./ComponentRunnableSegment"; -import { ComponentSegmentDependency } from "./ComponentSegmentDependency"; -import { isForStatementInit } from "~/utils/path-tools/control-flow-utils"; +import type { ComponentSegmentDependency } from "./ComponentSegmentDependency"; export class ComponentVariable extends ComponentMutableSegment { private runnableSegmentsMutatingThis = new Set(); diff --git a/packages/babel-plugin/src/utils/ast-factories/make-dependency-condition.ts b/packages/babel-plugin/src/utils/ast-factories/make-dependency-condition.ts index 9871abe..6c2ad4a 100644 --- a/packages/babel-plugin/src/utils/ast-factories/make-dependency-condition.ts +++ b/packages/babel-plugin/src/utils/ast-factories/make-dependency-condition.ts @@ -1,5 +1,5 @@ import * as t from "@babel/types"; -import { ComponentMutableSegment } from "~/classes/ComponentMutableSegment"; +import type { ComponentMutableSegment } from "~/classes/ComponentMutableSegment"; export function makeDependencyCondition( mutableSegment: ComponentMutableSegment diff --git a/packages/babel-plugin/src/utils/ast-tools/is-accessor-node.ts b/packages/babel-plugin/src/utils/ast-tools/is-accessor-node.ts new file mode 100644 index 0000000..c0d7eea --- /dev/null +++ b/packages/babel-plugin/src/utils/ast-tools/is-accessor-node.ts @@ -0,0 +1,16 @@ +import * as t from "@babel/types"; + +export type AccessorNode = + | t.MemberExpression + | t.OptionalMemberExpression + | t.Identifier + | t.PrivateName; + +export function isAccessorNode(node: t.Node): node is AccessorNode { + return ( + t.isMemberExpression(node) || + t.isOptionalMemberExpression(node) || + t.isIdentifier(node) || + t.isPrivateName(node) + ); +} diff --git a/packages/babel-plugin/src/utils/errors/RightmostIdNotFound.ts b/packages/babel-plugin/src/utils/errors/RightmostIdNotFound.ts index 4ed1263..d0ddb1f 100644 --- a/packages/babel-plugin/src/utils/errors/RightmostIdNotFound.ts +++ b/packages/babel-plugin/src/utils/errors/RightmostIdNotFound.ts @@ -1,4 +1,4 @@ -import * as t from "@babel/types"; +import type * as t from "@babel/types"; export class RightmostIdNotFound extends Error { constructor(path: t.Node) { diff --git a/packages/babel-plugin/src/utils/micro-transformers/unwrap-generic-expression.ts b/packages/babel-plugin/src/utils/micro-transformers/unwrap-generic-expression.ts index d9238f0..cc239b6 100644 --- a/packages/babel-plugin/src/utils/micro-transformers/unwrap-generic-expression.ts +++ b/packages/babel-plugin/src/utils/micro-transformers/unwrap-generic-expression.ts @@ -1,4 +1,4 @@ -import * as babel from "@babel/core"; +import type * as babel from "@babel/core"; import * as t from "@babel/types"; export function unwrapGenericExpression( diff --git a/packages/babel-plugin/src/utils/micro-transformers/unwrap-jsx-elements.ts b/packages/babel-plugin/src/utils/micro-transformers/unwrap-jsx-elements.ts index 940dad8..593625c 100644 --- a/packages/babel-plugin/src/utils/micro-transformers/unwrap-jsx-elements.ts +++ b/packages/babel-plugin/src/utils/micro-transformers/unwrap-jsx-elements.ts @@ -1,6 +1,6 @@ -import * as babel from "@babel/core"; +import type * as babel from "@babel/core"; import * as t from "@babel/types"; -import { Component } from "~/classes/Component"; +import type { Component } from "~/classes/Component"; import { DEFAULT_UNWRAPPED_JSX_ELEMENT_VARIABLE_NAME } from "../constants"; import { getParentBlockStatement } from "../path-tools/get-parent-block-statement"; import { isInTheSameFunctionScope } from "../path-tools/is-in-the-same-function-scope"; diff --git a/packages/babel-plugin/src/utils/micro-transformers/unwrap-jsx-expressions.ts b/packages/babel-plugin/src/utils/micro-transformers/unwrap-jsx-expressions.ts index 839c06d..a141dc2 100644 --- a/packages/babel-plugin/src/utils/micro-transformers/unwrap-jsx-expressions.ts +++ b/packages/babel-plugin/src/utils/micro-transformers/unwrap-jsx-expressions.ts @@ -1,6 +1,6 @@ -import * as babel from "@babel/core"; +import type * as babel from "@babel/core"; import * as t from "@babel/types"; -import { Component } from "~/classes/Component"; +import type { Component } from "~/classes/Component"; import { DEFAULT_UNWRAPPED_JSX_EXPRESSION_VARIABLE_NAME } from "../constants"; import { getParentBlockStatement } from "../path-tools/get-parent-block-statement"; import { isInTheSameFunctionScope } from "../path-tools/is-in-the-same-function-scope"; diff --git a/packages/babel-plugin/src/utils/path-tools/find-components.ts b/packages/babel-plugin/src/utils/path-tools/find-components.ts index fee6848..ba64958 100644 --- a/packages/babel-plugin/src/utils/path-tools/find-components.ts +++ b/packages/babel-plugin/src/utils/path-tools/find-components.ts @@ -1,9 +1,9 @@ import type * as babel from "@babel/core"; import * as t from "@babel/types"; import { Component } from "../../classes/Component"; -import { getReturnsOfFunction } from "./get-returns-of-function"; import { doesMatchHookName } from "../ast-tools/is-hook-call"; import { expandArrowFunctionToBlockStatement } from "../expand-arrow-function-to-block-statement"; +import { getReturnsOfFunction } from "./get-returns-of-function"; export function findComponents(program: babel.NodePath) { const components: Component[] = []; diff --git a/packages/babel-plugin/src/utils/path-tools/find-mutating-expression.ts b/packages/babel-plugin/src/utils/path-tools/find-mutating-expression.ts index c8309b6..10b5933 100644 --- a/packages/babel-plugin/src/utils/path-tools/find-mutating-expression.ts +++ b/packages/babel-plugin/src/utils/path-tools/find-mutating-expression.ts @@ -1,7 +1,7 @@ import { MUTATING_METHODS } from "../constants"; -import { getDeclaredIdentifiersInLVal } from "./get-declared-identifiers-in-lval"; import { getLeftmostIdName } from "../ast-tools/get-leftmost-id-name"; import { getRightmostIdName } from "../ast-tools/get-rightmost-id-name"; +import { getDeclaredIdentifiersInLVal } from "./get-declared-identifiers-in-lval"; export function findMutatingExpression( path: babel.NodePath, diff --git a/packages/babel-plugin/src/utils/path-tools/is-in-the-same-function-scope.ts b/packages/babel-plugin/src/utils/path-tools/is-in-the-same-function-scope.ts index 6674653..c422293 100644 --- a/packages/babel-plugin/src/utils/path-tools/is-in-the-same-function-scope.ts +++ b/packages/babel-plugin/src/utils/path-tools/is-in-the-same-function-scope.ts @@ -1,6 +1,6 @@ -import * as babel from "@babel/core"; +import type * as babel from "@babel/core"; import * as t from "@babel/types"; -import { Scope } from "@babel/traverse"; +import type { Scope } from "@babel/traverse"; // We need this to properly detect if return statements belong to the same function export function isInTheSameFunctionScope( diff --git a/packages/babel-plugin/src/utils/path-tools/reorder-by-topology.ts b/packages/babel-plugin/src/utils/path-tools/reorder-by-topology.ts index c0ae8fb..69e6032 100644 --- a/packages/babel-plugin/src/utils/path-tools/reorder-by-topology.ts +++ b/packages/babel-plugin/src/utils/path-tools/reorder-by-topology.ts @@ -1,4 +1,4 @@ -import { ComponentMutableSegment } from "~/classes/ComponentMutableSegment"; +import type { ComponentMutableSegment } from "~/classes/ComponentMutableSegment"; import { CircularDependencyError } from "../errors/CircularDependencyError"; type StatementPath = babel.NodePath; diff --git a/packages/babel-plugin/src/utils/path-tools/tests/is-in-the-same-function-scope.test.ts b/packages/babel-plugin/src/utils/path-tools/tests/is-in-the-same-function-scope.test.ts index 04a2dd9..4d45178 100644 --- a/packages/babel-plugin/src/utils/path-tools/tests/is-in-the-same-function-scope.test.ts +++ b/packages/babel-plugin/src/utils/path-tools/tests/is-in-the-same-function-scope.test.ts @@ -1,4 +1,4 @@ -import * as babel from "@babel/core"; +import type * as babel from "@babel/core"; import { isInTheSameFunctionScope } from "../is-in-the-same-function-scope"; import { parse } from "../../testing"; diff --git a/packages/babel-plugin/src/utils/scope-tools/is-scope-descendant-of.ts b/packages/babel-plugin/src/utils/scope-tools/is-scope-descendant-of.ts index d8a6224..e2bfb84 100644 --- a/packages/babel-plugin/src/utils/scope-tools/is-scope-descendant-of.ts +++ b/packages/babel-plugin/src/utils/scope-tools/is-scope-descendant-of.ts @@ -1,4 +1,4 @@ -import { Scope } from "@babel/traverse"; +import type { Scope } from "@babel/traverse"; export function isChildOfScope(parent: Scope, child: Scope) { let currentScope = child; diff --git a/packages/babel-plugin/src/utils/testing.ts b/packages/babel-plugin/src/utils/testing.ts index ba6dcf8..b7bae53 100644 --- a/packages/babel-plugin/src/utils/testing.ts +++ b/packages/babel-plugin/src/utils/testing.ts @@ -1,9 +1,9 @@ +import * as fs from "fs"; +import * as path from "path"; import * as babel from "@babel/core"; import * as generateBase from "@babel/generator"; import traverse from "@babel/traverse"; import type * as t from "@babel/types"; -import * as fs from "fs"; -import * as path from "path"; import { visitProgram } from "~/visit-program"; import babelPlugin from "../main"; diff --git a/packages/eslint-config/library.js b/packages/eslint-config/library.js index 8a77a4f..44e87aa 100644 --- a/packages/eslint-config/library.js +++ b/packages/eslint-config/library.js @@ -4,12 +4,7 @@ const project = resolve(process.cwd(), "tsconfig.json"); /** @type {import("eslint").Linter.Config} */ module.exports = { - extends: [ - "plugin:@typescript-eslint/recommended", - "prettier", - "eslint-config-turbo", - ], - plugins: ["only-warn"], + extends: ["prettier", "eslint-config-turbo"], globals: { React: true, JSX: true, @@ -34,6 +29,24 @@ module.exports = { overrides: [ { files: ["*.js?(x)", "*.ts?(x)"], + extends: [ + "plugin:@typescript-eslint/recommended", + "plugin:import/recommended", + ], + rules: { + "@typescript-eslint/consistent-type-imports": "error", + "import/order": [ + "error", + { + pathGroups: [ + { + pattern: "~/**", + group: "external", + }, + ], + }, + ], + }, }, { files: ["**/tests/**/*"], diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index a9483cc..d86daaf 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -13,6 +13,7 @@ "@vercel/style-guide": "^5.1.0", "eslint-config-prettier": "^9.1.0", "eslint-config-turbo": "^1.11.3", + "eslint-plugin-import": "^2.29.1", "eslint-plugin-only-warn": "^1.1.0", "typescript": "^5.3.3" } diff --git a/yarn.lock b/yarn.lock index f36ee1f..8e7c209 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4747,7 +4747,7 @@ eslint-plugin-eslint-comments@^3.2.0: escape-string-regexp "^1.0.5" ignore "^5.0.5" -eslint-plugin-import@^2.28.1: +eslint-plugin-import@^2.28.1, eslint-plugin-import@^2.29.1: version "2.29.1" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz#d45b37b5ef5901d639c15270d74d46d161150643" integrity sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==