eslint-plugin-barrel-rules 1.2.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +83 -42
- package/README.md +86 -40
- package/dist/index.cjs +173 -44
- package/dist/index.d.cts +9 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.js +173 -44
- package/package.json +11 -2
package/README.ko.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# **Advanced Barrel Pattern Enforcement for JavaScript/TypeScript Projects**
|
|
4
4
|
|
|
5
5
|
<div align="center">
|
|
6
|
-
<img src="https://img.shields.io/badge/version-1.
|
|
6
|
+
<img src="https://img.shields.io/badge/version-1.3.1-blue.svg" alt="Version"/>
|
|
7
7
|
<img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License"/>
|
|
8
8
|
<img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" alt="PRs Welcome"/>
|
|
9
9
|
</div>
|
|
@@ -28,7 +28,11 @@ JavaScript/TypeScript 프로젝트에서 Barrel Pattern(배럴 패턴)을 강제
|
|
|
28
28
|
내부 파일을 직접 import하는 것을 차단하여
|
|
29
29
|
**모듈화, 추상화, 유지보수성, 확장성**을 극대화합니다.
|
|
30
30
|
|
|
31
|
-
> 💡 Tip:
|
|
31
|
+
> 💡 Tip:
|
|
32
|
+
>
|
|
33
|
+
> > 이 플러그인은 `node_modules`(외부 패키지)의 import는 제한하거나 검사하지 않습니다.
|
|
34
|
+
> > 모든 규칙은 오직 프로젝트 내부(로컬 소스 파일) 경로의 import에만 적용됩니다.
|
|
35
|
+
>
|
|
32
36
|
> 코드 품질을 더욱 강화하고 싶다면, 이 플러그인과 함께 [eslint-plugin-import](https://github.com/import-js/eslint-plugin-import)의 `no-cycle` 룰을 사용하는 것을 추천합니다.
|
|
33
37
|
> 이를 통해 프로젝트 내의 순환 참조(Import Cycle)도 효과적으로 감지하고 방지할 수 있습니다.
|
|
34
38
|
|
|
@@ -58,11 +62,7 @@ JavaScript/TypeScript 프로젝트에서 Barrel Pattern(배럴 패턴)을 강제
|
|
|
58
62
|
`import ... from "../domains/foo/components/Bar"`는 차단)
|
|
59
63
|
|
|
60
64
|
- **Isolation Barrel Module**
|
|
61
|
-
지정한 barrel path 외부의 모듈이 내부 파일을 직접 import하지 못하도록 막을 수 있습니다.
|
|
62
|
-
`isolated: true` 옵션을 사용하면 같은 barrel path 내부에서는 자유롭게 import가 가능하고,
|
|
63
|
-
외부에서는 해당 barrel path로의 import가 모두 차단됩니다. (barrel(index) 파일을 통한 접근도 불가)
|
|
64
|
-
만약 특정 공유 import 경로만 허용하고 싶다면 `allowedImportPaths` 옵션을 사용할 수 있습니다.
|
|
65
|
-
이를 통해 각 모듈의 경계를 엄격하게 보호하고, 모듈의 독립성을 유지할 수 있습니다.
|
|
65
|
+
지정한 barrel path 외부의 모듈이 내부 파일을 직접 import하지 못하도록 막을 수 있습니다.
|
|
66
66
|
|
|
67
67
|
- **와일드카드 import/export 방지**
|
|
68
68
|
`import * as foo from "module"` 또는 `export * from "./module"`과 같은 와일드카드(네임스페이스) import/export를 금지합니다.
|
|
@@ -75,17 +75,13 @@ JavaScript/TypeScript 프로젝트에서 Barrel Pattern(배럴 패턴)을 강제
|
|
|
75
75
|
|
|
76
76
|
## 규칙(Rules)
|
|
77
77
|
|
|
78
|
-
1. **enforce-barrel-pattern**
|
|
78
|
+
1. **enforce-barrel-pattern** (isolate 옵션이 제거되었습니다.)
|
|
79
79
|
모듈 import 시 barrel 패턴을 강제합니다.
|
|
80
80
|
지정한 barrel 파일(예: index.ts)로만 import를 허용하고, 내부 모듈에 대한 직접 접근을 차단합니다.
|
|
81
|
-
`isolated: true` 옵션을 사용하면 같은 barrel path 내부 파일끼리만 import가 가능하며, 외부에서의 import는 barrel 파일을 통한 접근도 모두 차단됩니다.
|
|
82
|
-
`allowedImportPaths` 옵션을 사용하면 특정 공유 import 경로만 예외적으로 허용할 수 있습니다.
|
|
83
81
|
|
|
84
82
|
- **옵션:**
|
|
85
83
|
- `paths`: barrel 패턴을 적용할 디렉토리 목록(`baseDir` 기준 상대경로)
|
|
86
|
-
- `baseDir
|
|
87
|
-
- `isolated` (선택): `true`일 경우, barrel path 외부에서의 모든 import를 차단합니다(barrel 파일 통한 접근 포함). 같은 barrel path 내부 또는 `allowedImportPaths`만 허용.
|
|
88
|
-
- `allowedImportPaths` (선택): isolation 모드에서도 직접 import를 허용할 경로 배열
|
|
84
|
+
- `baseDir`: `paths` 기준이 되는 베이스 디렉토리 (기본값: ESLint 실행 디렉토리)
|
|
89
85
|
|
|
90
86
|
2. **no-wildcard**
|
|
91
87
|
`import * as foo from "module"` 또는 `export * from "./module"`과 같은 와일드카드(네임스페이스) import/export를 금지합니다.
|
|
@@ -93,6 +89,14 @@ JavaScript/TypeScript 프로젝트에서 Barrel Pattern(배럴 패턴)을 강제
|
|
|
93
89
|
두 룰을 함께 적용하면 모듈 경계를 엄격하게 지킬 수 있을 뿐만 아니라,
|
|
94
90
|
트리쉐이킹을 통한 성능 향상과 코드 추적 및 유지보수의 용이성까지 모두 얻을 수 있습니다.
|
|
95
91
|
|
|
92
|
+
3. **isolate-barrel-file** (isolated 기능을 새로운 룰로 제작했습니다.)
|
|
93
|
+
모듈 import 시 barrel 패턴을 강제합니다.
|
|
94
|
+
지정한 barrel 파일(예: index.ts)로만 import를 허용하고, 내부 모듈에 대한 직접 접근을 차단합니다.
|
|
95
|
+
- **옵션:**
|
|
96
|
+
- `isolations(Array<{ path: string, allowedPaths: string[] }>)`: `path`와 `allowedPaths`로 구성된 isolation을 추가할 수 있습니다.
|
|
97
|
+
- `baseDir`: `paths` 기준이 되는 베이스 디렉토리 (기본값: ESLint 실행 디렉토리)
|
|
98
|
+
- `globalAllowedPaths` : 모든 isolations에 공통적으로 허용할 경로를 지정합니다(node_modules ... etc)
|
|
99
|
+
|
|
96
100
|
---
|
|
97
101
|
|
|
98
102
|
## 설치
|
|
@@ -118,23 +122,43 @@ module.exports = {
|
|
|
118
122
|
parser: "@typescript-eslint/parser",
|
|
119
123
|
plugins: ["@typescript-eslint", "barrel-rules"],
|
|
120
124
|
rules: {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
125
|
+
|
|
126
|
+
//barrel-file 캡슐화
|
|
127
|
+
"barrel-rules/enforce-barrel-pattern": [
|
|
128
|
+
"error",
|
|
129
|
+
{
|
|
130
|
+
// encapsulation barrel file
|
|
131
|
+
paths: ["src/pages/*", "src/features/*", "src/entities/*"],
|
|
132
|
+
baseDir: __dirname,
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
|
|
136
|
+
//barrel file내부에서 외부 모듈 사용 제한
|
|
137
|
+
"barrel-rules/isolate-barrel-file": [
|
|
138
|
+
"error",
|
|
139
|
+
{
|
|
140
|
+
//isolation options
|
|
141
|
+
isolations: [
|
|
142
|
+
{
|
|
143
|
+
path: "src/pages/*",
|
|
144
|
+
allowedPaths: ["src/features/*", "src/entities/*"],
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
path: "src/features/*",
|
|
148
|
+
allowedPaths: ["src/entities/*"],
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
path: "src/entities/*",
|
|
152
|
+
allowedPaths: [],
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
baseDir: __dirname,
|
|
156
|
+
globalAllowPaths: ["src/shares/*", "node_modules/*"],
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
|
|
160
|
+
// "*"를 사용한 불 분명한 import/export 방지
|
|
161
|
+
"barrel-rules/no-wildcard": ["error"],
|
|
138
162
|
},
|
|
139
163
|
};
|
|
140
164
|
```
|
|
@@ -172,22 +196,41 @@ export default tseslint.config([
|
|
|
172
196
|
},
|
|
173
197
|
// barrel-rules에 대한 설정만 추가하면 됩니다.
|
|
174
198
|
rules: {
|
|
199
|
+
//barrel-file 캡슐화
|
|
175
200
|
"barrel-rules/enforce-barrel-pattern": [
|
|
176
201
|
"error",
|
|
177
202
|
{
|
|
178
|
-
//
|
|
179
|
-
paths: ["src/
|
|
180
|
-
// (옵션) 설정하지 않으면 기본값은 ESLint를 실행한 위치(작업 디렉토리)입니다.
|
|
181
|
-
// 예: `npx eslint .`처럼 실행하면, 실행 시점의 현재 디렉토리가 기본값이 됩니다.
|
|
203
|
+
// encapsulation barrel file
|
|
204
|
+
paths: ["src/pages/*", "src/features/*", "src/entities/*"],
|
|
182
205
|
baseDir: __dirname,
|
|
183
|
-
// isolation 모드 활성화: barrel path 외부에서의 모든 import를 차단합니다.
|
|
184
|
-
isolated: true,
|
|
185
|
-
// "shared" 디렉토리만 직접 import를 허용합니다.
|
|
186
|
-
// 필요에 따라 이 배열에 "node_modules/*" 등 원하는 경로를 자유롭게 추가할 수 있습니다.
|
|
187
|
-
allowedImportPaths: ["src/typescript/shared", "node_modules/*"],
|
|
188
206
|
},
|
|
189
207
|
],
|
|
190
|
-
|
|
208
|
+
|
|
209
|
+
//barrel file내부에서 외부 모듈 사용 제한
|
|
210
|
+
"barrel-rules/isolate-barrel-file": [
|
|
211
|
+
"error",
|
|
212
|
+
{
|
|
213
|
+
//isolation options
|
|
214
|
+
isolations: [
|
|
215
|
+
{
|
|
216
|
+
path: "src/pages/*",
|
|
217
|
+
allowedPaths: ["src/features/*", "src/entities/*"],
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
path: "src/features/*",
|
|
221
|
+
allowedPaths: ["src/entities/*"],
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
path: "src/entities/*",
|
|
225
|
+
allowedPaths: [],
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
baseDir: __dirname,
|
|
229
|
+
globalAllowPaths: ["src/shares/*"],
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
|
|
233
|
+
// "*"를 사용한 불 분명한 import/export 방지
|
|
191
234
|
"barrel-rules/no-wildcard": ["error"],
|
|
192
235
|
},
|
|
193
236
|
},
|
|
@@ -218,8 +261,6 @@ file(src / domains / foo / index.ts);
|
|
|
218
261
|
// ❌ 격리된 barrel로의 외부 import 차단 (alias 사용해도 차단)
|
|
219
262
|
// barrel 외부에서 접근 (bar의 경로는 src/domains/bar/)
|
|
220
263
|
import { Test } from "@domains/bar/components/Test";
|
|
221
|
-
// 또는
|
|
222
|
-
import { Test } from "../domains/bar";
|
|
223
264
|
|
|
224
265
|
// ✅ 같은 barrel 내부에서의 import는 허용 (alias 지원)
|
|
225
266
|
import { Hook } from "@domains/foo/hooks/useTest"; // 같은 barrel 내부에서
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# **Advanced Barrel Pattern Enforcement for JavaScript/TypeScript Projects**
|
|
4
4
|
|
|
5
5
|
<div align="center">
|
|
6
|
-
<img src="https://img.shields.io/badge/version-1.
|
|
6
|
+
<img src="https://img.shields.io/badge/version-1.3.1-blue.svg" alt="Version"/>
|
|
7
7
|
<img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License"/>
|
|
8
8
|
<img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" alt="PRs Welcome"/>
|
|
9
9
|
</div>
|
|
@@ -32,6 +32,9 @@ Direct imports from internal files are blocked, maximizing
|
|
|
32
32
|
**modularity, abstraction, maintainability, and scalability**.
|
|
33
33
|
|
|
34
34
|
> 💡 Tip:
|
|
35
|
+
> This plugin does not restrict or track imports from `node_modules` (external packages).
|
|
36
|
+
> The rules only apply to imports of internal (local source file) paths within your project.
|
|
37
|
+
>
|
|
35
38
|
> For even stronger code quality, we recommend using the `no-cycle` rule from [eslint-plugin-import](https://github.com/import-js/eslint-plugin-import) together with this plugin.
|
|
36
39
|
> This allows you to detect and prevent circular dependencies (import cycles) in your project.
|
|
37
40
|
|
|
@@ -60,10 +63,9 @@ Direct imports from internal files are blocked, maximizing
|
|
|
60
63
|
|
|
61
64
|
- **Isolation Barrel Module**
|
|
62
65
|
You can prevent modules outside the specified barrel path from directly importing internal files.
|
|
63
|
-
By enabling `
|
|
66
|
+
By enabling `isolate-barrel-file`, only files within the same barrel path can freely import each other.
|
|
64
67
|
Any import from outside the enforced barrel path is completely blocked, even if it tries to import via the barrel (index) file.
|
|
65
|
-
If you want to allow specific shared imports, you can use the `
|
|
66
|
-
This helps you strictly protect your module boundaries and keep each module truly independent.
|
|
68
|
+
If you want to allow specific shared imports, you can use the `allowedPaths` or `globalAllowedPaths` option.
|
|
67
69
|
|
|
68
70
|
- **Prevent Wildcard Import/Export**
|
|
69
71
|
Disallows wildcard (namespace) imports and exports such as `import * as foo from "module"` or `export * from "./module"`.
|
|
@@ -76,17 +78,13 @@ Direct imports from internal files are blocked, maximizing
|
|
|
76
78
|
|
|
77
79
|
## Rules
|
|
78
80
|
|
|
79
|
-
1. **enforce-barrel-pattern**
|
|
81
|
+
1. **enforce-barrel-pattern** (Isolation is exracted as new rule :))
|
|
80
82
|
Enforces the barrel pattern for module imports.
|
|
81
83
|
Only allows imports from designated barrel files and prevents direct access to internal modules.
|
|
82
|
-
When `isolated: true` is set, only files within the same barrel path can import each other, and any import from outside the barrel path is completely blocked (even via the barrel file).
|
|
83
|
-
You can allow specific shared import paths by using the `allowedImportPaths` option.
|
|
84
84
|
|
|
85
85
|
- **Options:**
|
|
86
86
|
- `paths`: The directories to be protected by the barrel pattern (relative to `baseDir`).
|
|
87
|
-
- `baseDir
|
|
88
|
-
- `isolated` (optional): If `true`, blocks all imports from outside the barrel path, even via the barrel file. Only allows imports within the same barrel path or from `allowedImportPaths`.
|
|
89
|
-
- `allowedImportPaths` (optional): Array of paths that are allowed to be imported directly, even in isolation mode.
|
|
87
|
+
- `baseDir`: The base directory for resolving `paths`. Defaults to the ESLint execution directory.
|
|
90
88
|
|
|
91
89
|
2. **no-wildcard**
|
|
92
90
|
Disallows wildcard (namespace) imports such as `import * as foo from "module"` or `export * from "./module"`.
|
|
@@ -94,6 +92,14 @@ Direct imports from internal files are blocked, maximizing
|
|
|
94
92
|
Using both rules together not only enforces strict module boundaries,
|
|
95
93
|
but also improves performance through better tree-shaking and makes code tracing and maintenance much easier.
|
|
96
94
|
|
|
95
|
+
3. **isolate-barrel-file** (New Rules!!!)
|
|
96
|
+
Only files within the same barrel path can import each other, and any import from outside the barrel path is completely blocked (even via the barrel file).
|
|
97
|
+
You can allow specific shared import paths by using the `allowedPaths` option.
|
|
98
|
+
- **Options:**
|
|
99
|
+
- `isolations(Array<{ path: string, allowedPaths: string[] }>)`: If you set isolation path, blocks all imports from outside the barrel path, even via the barrel file. Only allows imports within the same barrel path or from `allowedPaths` or `globalAllowedPaths`.
|
|
100
|
+
- `baseDir`: The base directory for resolving `paths`. Defaults to the ESLint execution directory.
|
|
101
|
+
- `globalAllowedPaths` : Array of paths that are allowed to be imported directly, even in isolation mode.
|
|
102
|
+
|
|
97
103
|
---
|
|
98
104
|
|
|
99
105
|
## Install
|
|
@@ -119,23 +125,44 @@ module.exports = {
|
|
|
119
125
|
parser: "@typescript-eslint/parser",
|
|
120
126
|
plugins: ["@typescript-eslint", "barrel-rules"],
|
|
121
127
|
rules: {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
128
|
+
|
|
129
|
+
//enforce barrel capsuling
|
|
130
|
+
"barrel-rules/enforce-barrel-pattern": [
|
|
131
|
+
"error",
|
|
132
|
+
{
|
|
133
|
+
// encapsulation barrel file
|
|
134
|
+
paths: ["src/pages/*", "src/features/*", "src/entities/*"],
|
|
135
|
+
baseDir: __dirname,
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
|
|
139
|
+
//protect barrel file from outside module
|
|
140
|
+
"barrel-rules/isolate-barrel-file": [
|
|
141
|
+
"error",
|
|
142
|
+
{
|
|
143
|
+
//isolation options
|
|
144
|
+
isolations: [
|
|
145
|
+
{
|
|
146
|
+
path: "src/pages/*",
|
|
147
|
+
allowedPaths: ["src/features/*", "src/entities/*"],
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
path: "src/features/*",
|
|
151
|
+
allowedPaths: ["src/entities/*"],
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
path: "src/entities/*",
|
|
155
|
+
allowedPaths: [],
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
baseDir: __dirname,
|
|
159
|
+
globalAllowPaths: ["src/shares/*", "node_modules/*"],
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
|
|
163
|
+
// protect wildcard import/export
|
|
164
|
+
"barrel-rules/no-wildcard": ["error"],
|
|
165
|
+
|
|
139
166
|
},
|
|
140
167
|
};
|
|
141
168
|
```
|
|
@@ -173,23 +200,44 @@ export default tseslint.config([
|
|
|
173
200
|
},
|
|
174
201
|
//just set your setting for barrel-rules
|
|
175
202
|
rules: {
|
|
203
|
+
|
|
204
|
+
//enforce barrel capsuling
|
|
176
205
|
"barrel-rules/enforce-barrel-pattern": [
|
|
177
206
|
"error",
|
|
178
207
|
{
|
|
179
|
-
//
|
|
180
|
-
paths: ["src/
|
|
181
|
-
// Optional config. The default value is the directory where ESLint is executed.
|
|
182
|
-
// For example, if you run `npx eslint .`, the default will be the current working directory at the time of execution.
|
|
208
|
+
// encapsulation barrel file
|
|
209
|
+
paths: ["src/pages/*", "src/features/*", "src/entities/*"],
|
|
183
210
|
baseDir: __dirname,
|
|
184
|
-
// Enable isolation mode: block all imports from outside the barrel path
|
|
185
|
-
isolated: true,
|
|
186
|
-
// Allow direct imports only from the "shared" directory.
|
|
187
|
-
// You can customize this array as needed, e.g., add "node_modules/*" or any other path you want to allow.
|
|
188
|
-
allowedImportPaths: ["src/typescript/shared", "node_modules/*"],
|
|
189
211
|
},
|
|
190
212
|
],
|
|
191
|
-
|
|
213
|
+
|
|
214
|
+
//protect barrel file from outside module
|
|
215
|
+
"barrel-rules/isolate-barrel-file": [
|
|
216
|
+
"error",
|
|
217
|
+
{
|
|
218
|
+
//isolation options
|
|
219
|
+
isolations: [
|
|
220
|
+
{
|
|
221
|
+
path: "src/pages/*",
|
|
222
|
+
allowedPaths: ["src/features/*", "src/entities/*"],
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
path: "src/features/*",
|
|
226
|
+
allowedPaths: ["src/entities/*"],
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
path: "src/entities/*",
|
|
230
|
+
allowedPaths: [],
|
|
231
|
+
},
|
|
232
|
+
],
|
|
233
|
+
baseDir: __dirname,
|
|
234
|
+
globalAllowPaths: ["src/shares/*"],
|
|
235
|
+
},
|
|
236
|
+
],
|
|
237
|
+
|
|
238
|
+
// protect wildcard import/export
|
|
192
239
|
"barrel-rules/no-wildcard": ["error"],
|
|
240
|
+
|
|
193
241
|
},
|
|
194
242
|
},
|
|
195
243
|
]);
|
|
@@ -219,15 +267,13 @@ file(src / domains / foo / index.ts);
|
|
|
219
267
|
// ❌ External import to isolated barrel is blocked (even with alias)
|
|
220
268
|
// from outside barrel (bar's path is src/domains/bar/)
|
|
221
269
|
import { Test } from "@domains/bar/components/Test";
|
|
222
|
-
// or
|
|
223
|
-
import { Test } from "../domains/bar";
|
|
224
270
|
|
|
225
271
|
// ✅ Internal imports within same barrel are allowed (alias supported)
|
|
226
272
|
import { Hook } from "@domains/foo/hooks/useTest"; // from inside same barrel
|
|
227
273
|
import { Utils } from "./utils/helper"; // from inside same barrel
|
|
228
274
|
|
|
229
275
|
// ✅ Allowed import paths are permitted (alias supported)
|
|
230
|
-
import { SharedUtil } from "@
|
|
276
|
+
import { SharedUtil } from "@shared/utils"; // if "src/shared/*" is in allowedPaths or globalAllowedPaths
|
|
231
277
|
```
|
|
232
278
|
|
|
233
279
|
---
|
package/dist/index.cjs
CHANGED
|
@@ -35,22 +35,22 @@ __export(index_exports, {
|
|
|
35
35
|
module.exports = __toCommonJS(index_exports);
|
|
36
36
|
|
|
37
37
|
// src/rules/enforce-barrel-pattern.ts
|
|
38
|
-
var
|
|
38
|
+
var import_types = require("@typescript-eslint/types");
|
|
39
39
|
var import_path2 = __toESM(require("path"), 1);
|
|
40
40
|
var import_resolve = __toESM(require("resolve"), 1);
|
|
41
41
|
|
|
42
42
|
// src/utils/glob.ts
|
|
43
43
|
var import_fast_glob = __toESM(require("fast-glob"), 1);
|
|
44
44
|
var Glob = class {
|
|
45
|
-
static resolvePath(
|
|
46
|
-
const globResult = import_fast_glob.default.sync(
|
|
45
|
+
static resolvePath(path4, baseDir) {
|
|
46
|
+
const globResult = import_fast_glob.default.sync(path4, {
|
|
47
47
|
cwd: baseDir,
|
|
48
48
|
onlyDirectories: true,
|
|
49
49
|
absolute: true
|
|
50
50
|
});
|
|
51
51
|
if (globResult.length === 0) {
|
|
52
52
|
throw new Error(
|
|
53
|
-
`[Glob] In baseDir: ${baseDir}, path: ${
|
|
53
|
+
`[Glob] In baseDir: ${baseDir}, path: ${path4}, any directory was not found`
|
|
54
54
|
);
|
|
55
55
|
}
|
|
56
56
|
return globResult;
|
|
@@ -123,13 +123,20 @@ var BARREL_ENTRY_POINT_FILE_NAMES = [
|
|
|
123
123
|
];
|
|
124
124
|
var RESOLVE_EXTENSIONS = [
|
|
125
125
|
".ts",
|
|
126
|
-
".tsx",
|
|
127
126
|
".js",
|
|
127
|
+
".tsx",
|
|
128
128
|
".jsx",
|
|
129
129
|
".json",
|
|
130
|
+
".d.js",
|
|
130
131
|
".d.ts",
|
|
131
132
|
".mjs",
|
|
132
|
-
".cjs"
|
|
133
|
+
".cjs",
|
|
134
|
+
".mts",
|
|
135
|
+
".cts",
|
|
136
|
+
".d.mjs",
|
|
137
|
+
".d.cjs",
|
|
138
|
+
".d.mts",
|
|
139
|
+
".d.cts"
|
|
133
140
|
];
|
|
134
141
|
var enforceBarrelPattern = {
|
|
135
142
|
meta: {
|
|
@@ -142,37 +149,47 @@ var enforceBarrelPattern = {
|
|
|
142
149
|
type: "object",
|
|
143
150
|
properties: {
|
|
144
151
|
paths: { type: "array", items: { type: "string" } },
|
|
145
|
-
baseDir: { type: "string" }
|
|
146
|
-
isolated: { type: "boolean" },
|
|
147
|
-
allowedImportPaths: { type: "array", items: { type: "string" } }
|
|
152
|
+
baseDir: { type: "string" }
|
|
148
153
|
},
|
|
149
|
-
required: ["paths"],
|
|
154
|
+
required: ["paths", "baseDir"],
|
|
150
155
|
additionalProperties: false
|
|
151
156
|
}
|
|
152
157
|
],
|
|
153
158
|
messages: {
|
|
159
|
+
TransformedAliasResolveFailed: "Transformed alias resolve failed. please check the alias config.",
|
|
154
160
|
DirectImportDisallowed: "Please import from '{{matchedTargetPath}}'. Direct access to '{{rawImportPath}}' is not allowed. You must use the barrel pattern and only consume APIs exposed externally. This is to ensure encapsulation of internal logic and maintain module boundaries.",
|
|
155
|
-
|
|
161
|
+
EmptyEslintConfig: "Please set the eslint config '{{property}}' to the eslint config. if you want to use this rule, please set the eslint config."
|
|
156
162
|
}
|
|
157
163
|
},
|
|
158
164
|
//default options(baseDir is current working directory. almost user execute eslint in project root)
|
|
159
165
|
defaultOptions: [
|
|
160
166
|
{
|
|
161
167
|
paths: [],
|
|
162
|
-
baseDir: process.cwd()
|
|
163
|
-
isolated: false,
|
|
164
|
-
allowedImportPaths: []
|
|
168
|
+
baseDir: process.cwd()
|
|
165
169
|
}
|
|
166
170
|
],
|
|
167
171
|
create(context) {
|
|
168
172
|
const option = context.options[0];
|
|
173
|
+
if (!option) {
|
|
174
|
+
return {
|
|
175
|
+
Program(node) {
|
|
176
|
+
const option2 = context.options[0];
|
|
177
|
+
if (!option2) {
|
|
178
|
+
return context.report({
|
|
179
|
+
node,
|
|
180
|
+
messageId: "EmptyEslintConfig",
|
|
181
|
+
data: {
|
|
182
|
+
property: "{ path: Array<string>, baseDir: string }"
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
}
|
|
169
189
|
const baseDir = option.baseDir;
|
|
170
190
|
const absoluteTargetPaths = option.paths.flatMap((_path) => {
|
|
171
191
|
return Glob.resolvePath(_path, baseDir);
|
|
172
192
|
});
|
|
173
|
-
const allowedImportPaths = option.allowedImportPaths.flatMap((_path) => {
|
|
174
|
-
return Glob.resolvePath(_path, baseDir);
|
|
175
|
-
});
|
|
176
193
|
return {
|
|
177
194
|
//check only import declaration(ESM)
|
|
178
195
|
ImportDeclaration(node) {
|
|
@@ -187,12 +204,19 @@ var enforceBarrelPattern = {
|
|
|
187
204
|
if (aliasResult.type === "success") {
|
|
188
205
|
absoluteImportPath = aliasResult.absolutePath;
|
|
189
206
|
} else {
|
|
207
|
+
if (!rawImportPath.startsWith(".") && !rawImportPath.startsWith("/") || rawImportPath.includes("/node_modules/")) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
190
210
|
absoluteImportPath = import_resolve.default.sync(rawImportPath, {
|
|
191
211
|
basedir: import_path2.default.dirname(absoluteCurrentFilePath),
|
|
192
212
|
extensions: RESOLVE_EXTENSIONS
|
|
193
213
|
});
|
|
194
214
|
}
|
|
195
215
|
} catch (e) {
|
|
216
|
+
context.report({
|
|
217
|
+
node,
|
|
218
|
+
messageId: "TransformedAliasResolveFailed"
|
|
219
|
+
});
|
|
196
220
|
return;
|
|
197
221
|
}
|
|
198
222
|
{
|
|
@@ -225,34 +249,138 @@ var enforceBarrelPattern = {
|
|
|
225
249
|
});
|
|
226
250
|
}
|
|
227
251
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
// src/rules/isolate-barrel-file.ts
|
|
258
|
+
var import_types2 = require("@typescript-eslint/types");
|
|
259
|
+
var import_path3 = __toESM(require("path"), 1);
|
|
260
|
+
var import_resolve2 = __toESM(require("resolve"), 1);
|
|
261
|
+
var RESOLVE_EXTENSIONS2 = [
|
|
262
|
+
".ts",
|
|
263
|
+
".js",
|
|
264
|
+
".tsx",
|
|
265
|
+
".jsx",
|
|
266
|
+
".json",
|
|
267
|
+
".d.js",
|
|
268
|
+
".d.ts",
|
|
269
|
+
".mjs",
|
|
270
|
+
".cjs",
|
|
271
|
+
".mts",
|
|
272
|
+
".cts",
|
|
273
|
+
".d.mjs",
|
|
274
|
+
".d.cjs",
|
|
275
|
+
".d.mts",
|
|
276
|
+
".d.cts"
|
|
277
|
+
];
|
|
278
|
+
var isolateBarrelFile = {
|
|
279
|
+
meta: {
|
|
280
|
+
type: "problem",
|
|
281
|
+
docs: {
|
|
282
|
+
description: "Isolate barrel file is not allowed to import outside of the barrel file"
|
|
283
|
+
},
|
|
284
|
+
schema: [
|
|
285
|
+
{
|
|
286
|
+
type: "object",
|
|
287
|
+
properties: {
|
|
288
|
+
isolations: { type: "array", items: { type: "object" } },
|
|
289
|
+
baseDir: { type: "string" },
|
|
290
|
+
globalAllowPaths: { type: "array", items: { type: "string" } }
|
|
291
|
+
},
|
|
292
|
+
required: ["isolations", "baseDir", "globalAllowPaths"],
|
|
293
|
+
additionalProperties: false
|
|
294
|
+
}
|
|
295
|
+
],
|
|
296
|
+
messages: {
|
|
297
|
+
TransformedAliasResolveFailed: "Transformed alias resolve failed. please check the alias config.",
|
|
298
|
+
IsolatedBarrelImportDisallowed: "This barrel file is isolated. external import is not allowed. if you want to import outside of the barrel file, please add 'allowedPath' to the plugin options."
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
//default options(baseDir is current working directory. almost user execute eslint in project root)
|
|
302
|
+
defaultOptions: [
|
|
303
|
+
{
|
|
304
|
+
isolations: [],
|
|
305
|
+
baseDir: process.cwd(),
|
|
306
|
+
globalAllowPaths: []
|
|
307
|
+
}
|
|
308
|
+
],
|
|
309
|
+
create(context) {
|
|
310
|
+
const option = context.options[0];
|
|
311
|
+
const baseDir = option.baseDir;
|
|
312
|
+
const absoluteGlobalAllowPaths = option.globalAllowPaths.flatMap((path4) => {
|
|
313
|
+
return Glob.resolvePath(path4, baseDir);
|
|
314
|
+
});
|
|
315
|
+
const absoluteIsolations = option.isolations.flatMap((isolation) => {
|
|
316
|
+
const isolationPaths = Glob.resolvePath(isolation.path, baseDir);
|
|
317
|
+
const allowedPaths = isolation.allowedPaths.flatMap((path4) => {
|
|
318
|
+
return Glob.resolvePath(path4, baseDir);
|
|
319
|
+
});
|
|
320
|
+
return isolationPaths.map((isolationPath) => ({
|
|
321
|
+
isolationPath,
|
|
322
|
+
//self isolatedPath also allowed to be imported
|
|
323
|
+
allowedPaths: [...allowedPaths, isolationPath]
|
|
324
|
+
}));
|
|
325
|
+
});
|
|
326
|
+
return {
|
|
327
|
+
//check only import declaration(ESM)
|
|
328
|
+
ImportDeclaration(node) {
|
|
329
|
+
const rawImportPath = node.source.value;
|
|
330
|
+
const absoluteCurrentFilePath = context.getFilename();
|
|
331
|
+
let absoluteImportPath = null;
|
|
332
|
+
try {
|
|
333
|
+
const aliasResult = Alias.resolvePath(
|
|
334
|
+
rawImportPath,
|
|
335
|
+
import_path3.default.dirname(absoluteCurrentFilePath)
|
|
336
|
+
);
|
|
337
|
+
if (aliasResult.type === "success") {
|
|
338
|
+
absoluteImportPath = aliasResult.absolutePath;
|
|
339
|
+
} else {
|
|
340
|
+
if (!rawImportPath.startsWith(".") && !rawImportPath.startsWith("/") || rawImportPath.includes("/node_modules/")) {
|
|
341
|
+
return;
|
|
254
342
|
}
|
|
343
|
+
absoluteImportPath = import_resolve2.default.sync(rawImportPath, {
|
|
344
|
+
basedir: import_path3.default.dirname(absoluteCurrentFilePath),
|
|
345
|
+
extensions: RESOLVE_EXTENSIONS2
|
|
346
|
+
});
|
|
255
347
|
}
|
|
348
|
+
} catch (e) {
|
|
349
|
+
context.report({
|
|
350
|
+
node,
|
|
351
|
+
messageId: "TransformedAliasResolveFailed"
|
|
352
|
+
});
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
const isolationIndex = absoluteIsolations.findIndex((isolation) => {
|
|
356
|
+
const absoluteIsolationPath = isolation.isolationPath;
|
|
357
|
+
const closedIsolationPath = absoluteIsolationPath + "/";
|
|
358
|
+
return absoluteCurrentFilePath.startsWith(closedIsolationPath);
|
|
359
|
+
});
|
|
360
|
+
const matchedIsolation = absoluteIsolations[isolationIndex];
|
|
361
|
+
if (!matchedIsolation) return;
|
|
362
|
+
const isAllowedImport = matchedIsolation.allowedPaths.some(
|
|
363
|
+
(allowedPath) => {
|
|
364
|
+
const same = absoluteImportPath === allowedPath;
|
|
365
|
+
const closedAllowedPath = allowedPath + "/";
|
|
366
|
+
const sub = absoluteImportPath.startsWith(closedAllowedPath);
|
|
367
|
+
return same || sub;
|
|
368
|
+
}
|
|
369
|
+
);
|
|
370
|
+
const isGlobalAllowedImport = absoluteGlobalAllowPaths.some(
|
|
371
|
+
(allowedPath) => {
|
|
372
|
+
const same = absoluteImportPath === allowedPath;
|
|
373
|
+
const closedAllowedPath = allowedPath + "/";
|
|
374
|
+
const sub = absoluteImportPath.startsWith(closedAllowedPath);
|
|
375
|
+
return same || sub;
|
|
376
|
+
}
|
|
377
|
+
);
|
|
378
|
+
const allowedImport = isAllowedImport || isGlobalAllowedImport;
|
|
379
|
+
if (!allowedImport) {
|
|
380
|
+
context.report({
|
|
381
|
+
node,
|
|
382
|
+
messageId: "IsolatedBarrelImportDisallowed"
|
|
383
|
+
});
|
|
256
384
|
}
|
|
257
385
|
}
|
|
258
386
|
};
|
|
@@ -260,7 +388,7 @@ var enforceBarrelPattern = {
|
|
|
260
388
|
};
|
|
261
389
|
|
|
262
390
|
// src/rules/no-wildcard.ts
|
|
263
|
-
var
|
|
391
|
+
var import_types3 = require("@typescript-eslint/types");
|
|
264
392
|
var noWildcard = {
|
|
265
393
|
meta: {
|
|
266
394
|
type: "problem",
|
|
@@ -300,6 +428,7 @@ var noWildcard = {
|
|
|
300
428
|
// src/index.ts
|
|
301
429
|
var rules = {
|
|
302
430
|
"enforce-barrel-pattern": enforceBarrelPattern,
|
|
431
|
+
"isolate-barrel-file": isolateBarrelFile,
|
|
303
432
|
"no-wildcard": noWildcard
|
|
304
433
|
};
|
|
305
434
|
var index_default = { rules };
|
package/dist/index.d.cts
CHANGED
|
@@ -2,11 +2,17 @@ import * as _typescript_eslint_utils_ts_eslint from '@typescript-eslint/utils/ts
|
|
|
2
2
|
|
|
3
3
|
declare const _default: {
|
|
4
4
|
rules: {
|
|
5
|
-
"enforce-barrel-pattern": _typescript_eslint_utils_ts_eslint.RuleModule<"DirectImportDisallowed" | "
|
|
5
|
+
"enforce-barrel-pattern": _typescript_eslint_utils_ts_eslint.RuleModule<"DirectImportDisallowed" | "TransformedAliasResolveFailed" | "EmptyEslintConfig", {
|
|
6
6
|
paths: string[];
|
|
7
7
|
baseDir: string;
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
}[], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
9
|
+
"isolate-barrel-file": _typescript_eslint_utils_ts_eslint.RuleModule<"TransformedAliasResolveFailed" | "IsolatedBarrelImportDisallowed", {
|
|
10
|
+
isolations: {
|
|
11
|
+
path: string;
|
|
12
|
+
allowedPaths: string[];
|
|
13
|
+
}[];
|
|
14
|
+
baseDir: string;
|
|
15
|
+
globalAllowPaths: string[];
|
|
10
16
|
}[], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
11
17
|
"no-wildcard": _typescript_eslint_utils_ts_eslint.RuleModule<"NoWildcardImport" | "NoExportAll", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
12
18
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -2,11 +2,17 @@ import * as _typescript_eslint_utils_ts_eslint from '@typescript-eslint/utils/ts
|
|
|
2
2
|
|
|
3
3
|
declare const _default: {
|
|
4
4
|
rules: {
|
|
5
|
-
"enforce-barrel-pattern": _typescript_eslint_utils_ts_eslint.RuleModule<"DirectImportDisallowed" | "
|
|
5
|
+
"enforce-barrel-pattern": _typescript_eslint_utils_ts_eslint.RuleModule<"DirectImportDisallowed" | "TransformedAliasResolveFailed" | "EmptyEslintConfig", {
|
|
6
6
|
paths: string[];
|
|
7
7
|
baseDir: string;
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
}[], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
9
|
+
"isolate-barrel-file": _typescript_eslint_utils_ts_eslint.RuleModule<"TransformedAliasResolveFailed" | "IsolatedBarrelImportDisallowed", {
|
|
10
|
+
isolations: {
|
|
11
|
+
path: string;
|
|
12
|
+
allowedPaths: string[];
|
|
13
|
+
}[];
|
|
14
|
+
baseDir: string;
|
|
15
|
+
globalAllowPaths: string[];
|
|
10
16
|
}[], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
11
17
|
"no-wildcard": _typescript_eslint_utils_ts_eslint.RuleModule<"NoWildcardImport" | "NoExportAll", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
12
18
|
};
|
package/dist/index.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
// src/rules/enforce-barrel-pattern.ts
|
|
2
|
-
import "@typescript-eslint/
|
|
2
|
+
import "@typescript-eslint/types";
|
|
3
3
|
import path2 from "path";
|
|
4
4
|
import resolve from "resolve";
|
|
5
5
|
|
|
6
6
|
// src/utils/glob.ts
|
|
7
7
|
import FastGlob from "fast-glob";
|
|
8
8
|
var Glob = class {
|
|
9
|
-
static resolvePath(
|
|
10
|
-
const globResult = FastGlob.sync(
|
|
9
|
+
static resolvePath(path4, baseDir) {
|
|
10
|
+
const globResult = FastGlob.sync(path4, {
|
|
11
11
|
cwd: baseDir,
|
|
12
12
|
onlyDirectories: true,
|
|
13
13
|
absolute: true
|
|
14
14
|
});
|
|
15
15
|
if (globResult.length === 0) {
|
|
16
16
|
throw new Error(
|
|
17
|
-
`[Glob] In baseDir: ${baseDir}, path: ${
|
|
17
|
+
`[Glob] In baseDir: ${baseDir}, path: ${path4}, any directory was not found`
|
|
18
18
|
);
|
|
19
19
|
}
|
|
20
20
|
return globResult;
|
|
@@ -87,13 +87,20 @@ var BARREL_ENTRY_POINT_FILE_NAMES = [
|
|
|
87
87
|
];
|
|
88
88
|
var RESOLVE_EXTENSIONS = [
|
|
89
89
|
".ts",
|
|
90
|
-
".tsx",
|
|
91
90
|
".js",
|
|
91
|
+
".tsx",
|
|
92
92
|
".jsx",
|
|
93
93
|
".json",
|
|
94
|
+
".d.js",
|
|
94
95
|
".d.ts",
|
|
95
96
|
".mjs",
|
|
96
|
-
".cjs"
|
|
97
|
+
".cjs",
|
|
98
|
+
".mts",
|
|
99
|
+
".cts",
|
|
100
|
+
".d.mjs",
|
|
101
|
+
".d.cjs",
|
|
102
|
+
".d.mts",
|
|
103
|
+
".d.cts"
|
|
97
104
|
];
|
|
98
105
|
var enforceBarrelPattern = {
|
|
99
106
|
meta: {
|
|
@@ -106,37 +113,47 @@ var enforceBarrelPattern = {
|
|
|
106
113
|
type: "object",
|
|
107
114
|
properties: {
|
|
108
115
|
paths: { type: "array", items: { type: "string" } },
|
|
109
|
-
baseDir: { type: "string" }
|
|
110
|
-
isolated: { type: "boolean" },
|
|
111
|
-
allowedImportPaths: { type: "array", items: { type: "string" } }
|
|
116
|
+
baseDir: { type: "string" }
|
|
112
117
|
},
|
|
113
|
-
required: ["paths"],
|
|
118
|
+
required: ["paths", "baseDir"],
|
|
114
119
|
additionalProperties: false
|
|
115
120
|
}
|
|
116
121
|
],
|
|
117
122
|
messages: {
|
|
123
|
+
TransformedAliasResolveFailed: "Transformed alias resolve failed. please check the alias config.",
|
|
118
124
|
DirectImportDisallowed: "Please import from '{{matchedTargetPath}}'. Direct access to '{{rawImportPath}}' is not allowed. You must use the barrel pattern and only consume APIs exposed externally. This is to ensure encapsulation of internal logic and maintain module boundaries.",
|
|
119
|
-
|
|
125
|
+
EmptyEslintConfig: "Please set the eslint config '{{property}}' to the eslint config. if you want to use this rule, please set the eslint config."
|
|
120
126
|
}
|
|
121
127
|
},
|
|
122
128
|
//default options(baseDir is current working directory. almost user execute eslint in project root)
|
|
123
129
|
defaultOptions: [
|
|
124
130
|
{
|
|
125
131
|
paths: [],
|
|
126
|
-
baseDir: process.cwd()
|
|
127
|
-
isolated: false,
|
|
128
|
-
allowedImportPaths: []
|
|
132
|
+
baseDir: process.cwd()
|
|
129
133
|
}
|
|
130
134
|
],
|
|
131
135
|
create(context) {
|
|
132
136
|
const option = context.options[0];
|
|
137
|
+
if (!option) {
|
|
138
|
+
return {
|
|
139
|
+
Program(node) {
|
|
140
|
+
const option2 = context.options[0];
|
|
141
|
+
if (!option2) {
|
|
142
|
+
return context.report({
|
|
143
|
+
node,
|
|
144
|
+
messageId: "EmptyEslintConfig",
|
|
145
|
+
data: {
|
|
146
|
+
property: "{ path: Array<string>, baseDir: string }"
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
}
|
|
133
153
|
const baseDir = option.baseDir;
|
|
134
154
|
const absoluteTargetPaths = option.paths.flatMap((_path) => {
|
|
135
155
|
return Glob.resolvePath(_path, baseDir);
|
|
136
156
|
});
|
|
137
|
-
const allowedImportPaths = option.allowedImportPaths.flatMap((_path) => {
|
|
138
|
-
return Glob.resolvePath(_path, baseDir);
|
|
139
|
-
});
|
|
140
157
|
return {
|
|
141
158
|
//check only import declaration(ESM)
|
|
142
159
|
ImportDeclaration(node) {
|
|
@@ -151,12 +168,19 @@ var enforceBarrelPattern = {
|
|
|
151
168
|
if (aliasResult.type === "success") {
|
|
152
169
|
absoluteImportPath = aliasResult.absolutePath;
|
|
153
170
|
} else {
|
|
171
|
+
if (!rawImportPath.startsWith(".") && !rawImportPath.startsWith("/") || rawImportPath.includes("/node_modules/")) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
154
174
|
absoluteImportPath = resolve.sync(rawImportPath, {
|
|
155
175
|
basedir: path2.dirname(absoluteCurrentFilePath),
|
|
156
176
|
extensions: RESOLVE_EXTENSIONS
|
|
157
177
|
});
|
|
158
178
|
}
|
|
159
179
|
} catch (e) {
|
|
180
|
+
context.report({
|
|
181
|
+
node,
|
|
182
|
+
messageId: "TransformedAliasResolveFailed"
|
|
183
|
+
});
|
|
160
184
|
return;
|
|
161
185
|
}
|
|
162
186
|
{
|
|
@@ -189,34 +213,138 @@ var enforceBarrelPattern = {
|
|
|
189
213
|
});
|
|
190
214
|
}
|
|
191
215
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
// src/rules/isolate-barrel-file.ts
|
|
222
|
+
import "@typescript-eslint/types";
|
|
223
|
+
import path3 from "path";
|
|
224
|
+
import resolve2 from "resolve";
|
|
225
|
+
var RESOLVE_EXTENSIONS2 = [
|
|
226
|
+
".ts",
|
|
227
|
+
".js",
|
|
228
|
+
".tsx",
|
|
229
|
+
".jsx",
|
|
230
|
+
".json",
|
|
231
|
+
".d.js",
|
|
232
|
+
".d.ts",
|
|
233
|
+
".mjs",
|
|
234
|
+
".cjs",
|
|
235
|
+
".mts",
|
|
236
|
+
".cts",
|
|
237
|
+
".d.mjs",
|
|
238
|
+
".d.cjs",
|
|
239
|
+
".d.mts",
|
|
240
|
+
".d.cts"
|
|
241
|
+
];
|
|
242
|
+
var isolateBarrelFile = {
|
|
243
|
+
meta: {
|
|
244
|
+
type: "problem",
|
|
245
|
+
docs: {
|
|
246
|
+
description: "Isolate barrel file is not allowed to import outside of the barrel file"
|
|
247
|
+
},
|
|
248
|
+
schema: [
|
|
249
|
+
{
|
|
250
|
+
type: "object",
|
|
251
|
+
properties: {
|
|
252
|
+
isolations: { type: "array", items: { type: "object" } },
|
|
253
|
+
baseDir: { type: "string" },
|
|
254
|
+
globalAllowPaths: { type: "array", items: { type: "string" } }
|
|
255
|
+
},
|
|
256
|
+
required: ["isolations", "baseDir", "globalAllowPaths"],
|
|
257
|
+
additionalProperties: false
|
|
258
|
+
}
|
|
259
|
+
],
|
|
260
|
+
messages: {
|
|
261
|
+
TransformedAliasResolveFailed: "Transformed alias resolve failed. please check the alias config.",
|
|
262
|
+
IsolatedBarrelImportDisallowed: "This barrel file is isolated. external import is not allowed. if you want to import outside of the barrel file, please add 'allowedPath' to the plugin options."
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
//default options(baseDir is current working directory. almost user execute eslint in project root)
|
|
266
|
+
defaultOptions: [
|
|
267
|
+
{
|
|
268
|
+
isolations: [],
|
|
269
|
+
baseDir: process.cwd(),
|
|
270
|
+
globalAllowPaths: []
|
|
271
|
+
}
|
|
272
|
+
],
|
|
273
|
+
create(context) {
|
|
274
|
+
const option = context.options[0];
|
|
275
|
+
const baseDir = option.baseDir;
|
|
276
|
+
const absoluteGlobalAllowPaths = option.globalAllowPaths.flatMap((path4) => {
|
|
277
|
+
return Glob.resolvePath(path4, baseDir);
|
|
278
|
+
});
|
|
279
|
+
const absoluteIsolations = option.isolations.flatMap((isolation) => {
|
|
280
|
+
const isolationPaths = Glob.resolvePath(isolation.path, baseDir);
|
|
281
|
+
const allowedPaths = isolation.allowedPaths.flatMap((path4) => {
|
|
282
|
+
return Glob.resolvePath(path4, baseDir);
|
|
283
|
+
});
|
|
284
|
+
return isolationPaths.map((isolationPath) => ({
|
|
285
|
+
isolationPath,
|
|
286
|
+
//self isolatedPath also allowed to be imported
|
|
287
|
+
allowedPaths: [...allowedPaths, isolationPath]
|
|
288
|
+
}));
|
|
289
|
+
});
|
|
290
|
+
return {
|
|
291
|
+
//check only import declaration(ESM)
|
|
292
|
+
ImportDeclaration(node) {
|
|
293
|
+
const rawImportPath = node.source.value;
|
|
294
|
+
const absoluteCurrentFilePath = context.getFilename();
|
|
295
|
+
let absoluteImportPath = null;
|
|
296
|
+
try {
|
|
297
|
+
const aliasResult = Alias.resolvePath(
|
|
298
|
+
rawImportPath,
|
|
299
|
+
path3.dirname(absoluteCurrentFilePath)
|
|
300
|
+
);
|
|
301
|
+
if (aliasResult.type === "success") {
|
|
302
|
+
absoluteImportPath = aliasResult.absolutePath;
|
|
303
|
+
} else {
|
|
304
|
+
if (!rawImportPath.startsWith(".") && !rawImportPath.startsWith("/") || rawImportPath.includes("/node_modules/")) {
|
|
305
|
+
return;
|
|
218
306
|
}
|
|
307
|
+
absoluteImportPath = resolve2.sync(rawImportPath, {
|
|
308
|
+
basedir: path3.dirname(absoluteCurrentFilePath),
|
|
309
|
+
extensions: RESOLVE_EXTENSIONS2
|
|
310
|
+
});
|
|
219
311
|
}
|
|
312
|
+
} catch (e) {
|
|
313
|
+
context.report({
|
|
314
|
+
node,
|
|
315
|
+
messageId: "TransformedAliasResolveFailed"
|
|
316
|
+
});
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
const isolationIndex = absoluteIsolations.findIndex((isolation) => {
|
|
320
|
+
const absoluteIsolationPath = isolation.isolationPath;
|
|
321
|
+
const closedIsolationPath = absoluteIsolationPath + "/";
|
|
322
|
+
return absoluteCurrentFilePath.startsWith(closedIsolationPath);
|
|
323
|
+
});
|
|
324
|
+
const matchedIsolation = absoluteIsolations[isolationIndex];
|
|
325
|
+
if (!matchedIsolation) return;
|
|
326
|
+
const isAllowedImport = matchedIsolation.allowedPaths.some(
|
|
327
|
+
(allowedPath) => {
|
|
328
|
+
const same = absoluteImportPath === allowedPath;
|
|
329
|
+
const closedAllowedPath = allowedPath + "/";
|
|
330
|
+
const sub = absoluteImportPath.startsWith(closedAllowedPath);
|
|
331
|
+
return same || sub;
|
|
332
|
+
}
|
|
333
|
+
);
|
|
334
|
+
const isGlobalAllowedImport = absoluteGlobalAllowPaths.some(
|
|
335
|
+
(allowedPath) => {
|
|
336
|
+
const same = absoluteImportPath === allowedPath;
|
|
337
|
+
const closedAllowedPath = allowedPath + "/";
|
|
338
|
+
const sub = absoluteImportPath.startsWith(closedAllowedPath);
|
|
339
|
+
return same || sub;
|
|
340
|
+
}
|
|
341
|
+
);
|
|
342
|
+
const allowedImport = isAllowedImport || isGlobalAllowedImport;
|
|
343
|
+
if (!allowedImport) {
|
|
344
|
+
context.report({
|
|
345
|
+
node,
|
|
346
|
+
messageId: "IsolatedBarrelImportDisallowed"
|
|
347
|
+
});
|
|
220
348
|
}
|
|
221
349
|
}
|
|
222
350
|
};
|
|
@@ -224,7 +352,7 @@ var enforceBarrelPattern = {
|
|
|
224
352
|
};
|
|
225
353
|
|
|
226
354
|
// src/rules/no-wildcard.ts
|
|
227
|
-
import "@typescript-eslint/
|
|
355
|
+
import "@typescript-eslint/types";
|
|
228
356
|
var noWildcard = {
|
|
229
357
|
meta: {
|
|
230
358
|
type: "problem",
|
|
@@ -264,6 +392,7 @@ var noWildcard = {
|
|
|
264
392
|
// src/index.ts
|
|
265
393
|
var rules = {
|
|
266
394
|
"enforce-barrel-pattern": enforceBarrelPattern,
|
|
395
|
+
"isolate-barrel-file": isolateBarrelFile,
|
|
267
396
|
"no-wildcard": noWildcard
|
|
268
397
|
};
|
|
269
398
|
var index_default = { rules };
|
package/package.json
CHANGED
|
@@ -25,15 +25,21 @@
|
|
|
25
25
|
"isolated barrel module",
|
|
26
26
|
"no-wildcard"
|
|
27
27
|
],
|
|
28
|
-
"version": "1.
|
|
28
|
+
"version": "1.3.1",
|
|
29
29
|
"type": "module",
|
|
30
30
|
"main": "dist/index.cjs",
|
|
31
31
|
"module": "dist/index.js",
|
|
32
32
|
"types": "dist/index.d.ts",
|
|
33
33
|
"dependencies": {
|
|
34
|
+
"@types/jest": "^30.0.0",
|
|
35
|
+
"@typescript-eslint/parser": "^8.46.2",
|
|
36
|
+
"@typescript-eslint/rule-tester": "^8.46.2",
|
|
37
|
+
"@typescript-eslint/types": "^8.46.2",
|
|
34
38
|
"@typescript-eslint/utils": "^8.36.0",
|
|
35
39
|
"fast-glob": "^3.3.3",
|
|
40
|
+
"jest": "^30.2.0",
|
|
36
41
|
"resolve": "^1.22.10",
|
|
42
|
+
"ts-jest": "^29.4.5",
|
|
37
43
|
"tsconfig-paths": "^4.2.0"
|
|
38
44
|
},
|
|
39
45
|
"devDependencies": {
|
|
@@ -43,8 +49,11 @@
|
|
|
43
49
|
"typescript": "~5.8.3"
|
|
44
50
|
},
|
|
45
51
|
"scripts": {
|
|
46
|
-
"build": "tsup src/index.ts --dts --format cjs,esm",
|
|
52
|
+
"build": "pnpm run test && tsup src/index.ts --dts --format cjs,esm",
|
|
47
53
|
"type-check": "tsc --noEmit",
|
|
54
|
+
"test": "jest",
|
|
55
|
+
"test:watch": "jest --watch",
|
|
56
|
+
"test:coverage": "jest --coverage",
|
|
48
57
|
"release": "pnpm run build && pnpm publish --access=public"
|
|
49
58
|
}
|
|
50
59
|
}
|