1
1
import js from "@eslint/js" ;
2
2
import globals from "globals" ;
3
+ import importPlugin from "eslint-plugin-import" ;
3
4
import reactHooks from "eslint-plugin-react-hooks" ;
4
5
import reactRefresh from "eslint-plugin-react-refresh" ;
5
6
import tseslint from "typescript-eslint" ;
6
7
import tailwindPlugin from "eslint-plugin-tailwindcss" ;
8
+ import path from "path" ;
9
+ import fs from "fs" ;
10
+
11
+ const FEATURES_DIR = "./src/features" ;
12
+
13
+ /**
14
+ * Traverse the features directory and return an array of restricted paths for
15
+ * use in the `import/no-restricted-paths` rule.
16
+ *
17
+ * @example
18
+ * ```js
19
+ * [
20
+ * {
21
+ * except: [ './dependencies' ],
22
+ * from: './src/features',
23
+ * target: './src/features/dependencies'
24
+ * },
25
+ * {
26
+ * except: [ './versions' ],
27
+ * from: './src/features',
28
+ * target: './src/features/versions'
29
+ * },
30
+ * {
31
+ * except: [ './vulnerabilities' ],
32
+ * from: './src/features',
33
+ * target: './src/features/vulnerabilities'
34
+ * }
35
+ * ]
36
+ * ```
37
+ */
38
+ const getRestrictedPathsForFeatureDir = ( ) => {
39
+ const featureDirPath = path . resolve ( FEATURES_DIR ) ;
40
+ /**
41
+ * @type {Array<{except: `./${string}`[], from: './src/features', target: string}> }
42
+ */
43
+ const restrictedPaths = [ ] ;
44
+
45
+ try {
46
+ const featureDirs = fs . readdirSync ( featureDirPath ) ;
47
+
48
+ featureDirs . forEach ( ( featureDir ) => {
49
+ const subPath = path . join ( featureDirPath , featureDir ) ;
50
+ if ( fs . lstatSync ( subPath ) . isDirectory ( ) ) {
51
+ restrictedPaths . push ( {
52
+ except : [ `./${ featureDir } ` ] ,
53
+ from : FEATURES_DIR ,
54
+ target : path . join ( FEATURES_DIR , featureDir ) ,
55
+ } ) ;
56
+ }
57
+ } ) ;
58
+ } catch ( error ) {
59
+ console . error ( "Error reading features directory:" , error ) ;
60
+ }
61
+
62
+ return restrictedPaths ;
63
+ } ;
7
64
8
65
const restrictedSyntax = {
9
66
reactQuery : {
@@ -24,15 +81,29 @@ export default tseslint.config(
24
81
] ,
25
82
files : [ "**/*.{ts,tsx}" ] ,
26
83
languageOptions : {
27
- ecmaVersion : 2020 ,
28
- globals : globals . browser ,
84
+ parserOptions : {
85
+ projectService : true ,
86
+ tsconfigRootDir : import . meta. dirname ,
87
+ ecmaFeatures : {
88
+ jsx : true ,
89
+ } ,
90
+ } ,
91
+ globals : {
92
+ ...globals . browser ,
93
+ ...globals . node ,
94
+ } ,
29
95
} ,
30
96
plugins : {
31
97
"react-hooks" : reactHooks ,
32
98
"react-refresh" : reactRefresh ,
99
+ import : importPlugin ,
33
100
} ,
34
-
35
101
settings : {
102
+ "import/resolver" : {
103
+ typescript : true ,
104
+ node : true ,
105
+ } ,
106
+
36
107
tailwindcss : {
37
108
callees : [ "tv" , "twMerge" ] ,
38
109
config : "./tailwind.config.ts" ,
@@ -132,6 +203,40 @@ export default tseslint.config(
132
203
] ,
133
204
} ,
134
205
] ,
206
+ "import/no-restricted-paths" : [
207
+ "error" ,
208
+ {
209
+ zones : [
210
+ // disables cross-feature imports:
211
+ // eg. src/features/dashboard-alerts should not import from src/features/dashboard-messages, etc.
212
+ ...getRestrictedPathsForFeatureDir ( ) ,
213
+
214
+ // enforce unidirectional codebase:
215
+ // e.g. src/routes can import from src/features but not the other way around
216
+ {
217
+ from : "./src/routes" ,
218
+ target : "./src/features" ,
219
+ } ,
220
+
221
+ // enforce unidirectional codebase:
222
+ // e.g src/features and src/routes can import from these shared modules but not the other way around
223
+ {
224
+ from : [ "./src/features" , "./src/routes" ] ,
225
+ target : [
226
+ "./src/components" ,
227
+ "./src/constants" ,
228
+ "./src/hooks" ,
229
+ "./src/i18n" ,
230
+ "./src/lib" ,
231
+ "./src/mocks" ,
232
+ "./src/trusty-api" ,
233
+ "./src/types" ,
234
+ "./src/utils" ,
235
+ ] ,
236
+ } ,
237
+ ] ,
238
+ } ,
239
+ ] ,
135
240
} ,
136
- } ,
241
+ }
137
242
) ;
0 commit comments