Skip to content

Commit 0d56d8a

Browse files
authored
Merge pull request #368 from Commencis/feature/consistent-type-imports
feat(eslint-config): enable consistent type imports
2 parents cad574e + 081453d commit 0d56d8a

File tree

4 files changed

+101
-37
lines changed

4 files changed

+101
-37
lines changed

.changeset/itchy-tires-mate.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@commencis/eslint-config': major
3+
---
4+
5+
feat: enable consistent type imports

packages/eslint-config/src/plugins/importSortPlugin.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import simpleImportSortPlugin from 'eslint-plugin-simple-import-sort';
22

3-
import { FlatConfig } from '@/types';
4-
53
import { importSortRules } from '@/rules';
4+
import type { FlatConfig } from '@/types';
65

76
export const importSortPluginConfig: FlatConfig = {
87
name: 'commencis/plugin:simple-import-sort',

packages/eslint-config/src/rules/importSortRules.ts

Lines changed: 93 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,106 @@
1-
import { Linter } from '@typescript-eslint/utils/ts-eslint';
1+
import type { Linter } from '@typescript-eslint/utils/ts-eslint';
22

3-
export const importSortRules: Linter.RulesRecord = {
4-
'simple-import-sort/imports': [
5-
'error',
6-
{
7-
groups: [
8-
// Side effects
9-
['^\\u0000'],
3+
/**
4+
* Turn a normal "from" regex into a “type-only” variant that matches
5+
* the same sources *when* they’re type-only imports.
6+
* simple-import-sort appends \u0000 to type-only import sources.
7+
*/
8+
function asTypeOnly(pattern: string): string {
9+
const base = pattern.endsWith('$') ? pattern.slice(0, -1) : pattern;
10+
return `${base}\\u0000$`;
11+
}
12+
13+
// Put type-only variants first inside the same group.
14+
function withTypeFirst(group: string[]): string[] {
15+
return group.flatMap((pattern) => [asTypeOnly(pattern), pattern]);
16+
}
17+
18+
// Helpers to generate exact and subpath regex for @/ aliases
19+
function exact(p: string): string {
20+
return `^@/${p}$`;
21+
}
22+
23+
function subpath(p: string): string {
24+
return `^@/${p}/.+$`;
25+
}
26+
27+
// Expand an array of folder names into [exact, subpath] for each
28+
function expandFolders(folders: string[]): string[] {
29+
return folders.flatMap((name) => [exact(name), subpath(name)]);
30+
}
1031

11-
// Main frameworks & libraries
12-
[
13-
'^(react(-native|-dom)?(/.*)?)$',
14-
'^next',
15-
'^vue',
16-
'^nuxt',
17-
'^@angular(/.*|$)',
18-
'^expo',
19-
'^node',
20-
],
32+
const GROUPS: Record<string, string[]> = {
33+
// Side effects (simple-import-sort prefixes side-effects with \u0000 at the start)
34+
SIDE_EFFECTS: ['^\\u0000'],
2135

22-
// External packages
23-
['^@commencis', '^@?\\w'],
36+
// Main frameworks & libraries
37+
FRAMEWORKS: [
38+
'^(react(-native|-dom)?(/.*)?)$',
39+
'^next',
40+
'^vue',
41+
'^nuxt',
42+
'^@angular(/.*|$)',
43+
'^expo',
44+
'^node',
45+
],
2446

25-
// Internal common directories
26-
['^@?/?(config|types|interfaces|constants|helpers|utils|lib)(/.*|$)'],
47+
// External packages
48+
EXTERNAL: ['^@commencis', '^@?\\w'],
2749

28-
// Internal directories
29-
['^@/'],
50+
// Internal common directories
51+
INTERNAL_COMMON: expandFolders([
52+
'config',
53+
'types',
54+
'interfaces',
55+
'constants',
56+
'helpers',
57+
'utils',
58+
'lib',
59+
]),
3060

31-
// Components
32-
['((.*)/)?(providers|layouts|pages|modules|features|components)/?'],
61+
// Component directories
62+
COMPONENTS: expandFolders([
63+
'providers',
64+
'layouts',
65+
'pages',
66+
'modules',
67+
'features',
68+
'components',
69+
]),
3370

34-
// Relative parent imports: '../' comes last
35-
['^\\.\\.(?!/?$)', '^\\.\\./?$'],
71+
// Internal root alias (catch-all leftover @/ imports)
72+
INTERNAL_ROOT: [exact(''), subpath('')],
3673

37-
// Relative imports: './' comes last
38-
['^\\./(?=.*/)(?!/?$)', '^\\.(?!/?$)', '^\\./?$'],
74+
// Relative parent imports then same-dir relatives
75+
RELATIVE_PARENT: ['^\\.\\.(?!/?$)', '^\\.\\./?$'],
76+
RELATIVE_SAME: ['^\\./(?=.*/)(?!/?$)', '^\\.(?!/?$)', '^\\./?$'],
3977

40-
// Styles
41-
['^.+\\.(s?css|(style(s)?)\\..+)$'],
78+
// Styles
79+
STYLES: ['^.+\\.(s?css|(style(s)?)\\..+)$'],
4280

43-
// Static assets
44-
['(asset(s?)|public|static|images)(/.*|$)', '^.+\\.svg$', '^.+\\.png$'],
81+
// Assets
82+
ASSETS: [
83+
'(asset(s?)|public|static|images)(/.*|$)',
84+
'^.+\\.svg$',
85+
'^.+\\.png$',
86+
],
87+
};
88+
89+
export const importSortRules: Linter.RulesRecord = {
90+
'simple-import-sort/imports': [
91+
'error',
92+
{
93+
groups: [
94+
GROUPS.SIDE_EFFECTS,
95+
withTypeFirst(GROUPS.FRAMEWORKS),
96+
withTypeFirst(GROUPS.EXTERNAL),
97+
withTypeFirst(GROUPS.INTERNAL_COMMON),
98+
withTypeFirst(GROUPS.COMPONENTS),
99+
withTypeFirst(GROUPS.INTERNAL_ROOT),
100+
withTypeFirst(GROUPS.RELATIVE_PARENT),
101+
withTypeFirst(GROUPS.RELATIVE_SAME),
102+
GROUPS.STYLES,
103+
GROUPS.ASSETS,
45104
],
46105
},
47106
],

packages/eslint-config/src/rules/typescriptRules.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { Linter } from '@typescript-eslint/utils/ts-eslint';
1+
import type { Linter } from '@typescript-eslint/utils/ts-eslint';
22

33
export const typescriptRules: Linter.RulesRecord = {
44
'@typescript-eslint/consistent-type-definitions': 'off',
55
'@typescript-eslint/no-empty-function': 'off',
66
'@typescript-eslint/array-type': 'off',
77

8+
'@typescript-eslint/consistent-type-imports': 'error',
89
'@typescript-eslint/explicit-function-return-type': 'error',
910
'@typescript-eslint/no-unused-vars': [
1011
'error',

0 commit comments

Comments
 (0)