eslint-plugin-barrel-rules 1.1.1 → 1.2.0
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 +70 -10
- package/README.md +63 -6
- package/dist/index.cjs +180 -54
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +181 -55
- package/package.json +4 -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.2.0-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,6 +28,10 @@ JavaScript/TypeScript 프로젝트에서 Barrel Pattern(배럴 패턴)을 강제
|
|
|
28
28
|
내부 파일을 직접 import하는 것을 차단하여
|
|
29
29
|
**모듈화, 추상화, 유지보수성, 확장성**을 극대화합니다.
|
|
30
30
|
|
|
31
|
+
> 💡 Tip:
|
|
32
|
+
> 코드 품질을 더욱 강화하고 싶다면, 이 플러그인과 함께 [eslint-plugin-import](https://github.com/import-js/eslint-plugin-import)의 `no-cycle` 룰을 사용하는 것을 추천합니다.
|
|
33
|
+
> 이를 통해 프로젝트 내의 순환 참조(Import Cycle)도 효과적으로 감지하고 방지할 수 있습니다.
|
|
34
|
+
|
|
31
35
|
---
|
|
32
36
|
|
|
33
37
|
## 지원 환경
|
|
@@ -35,7 +39,12 @@ JavaScript/TypeScript 프로젝트에서 Barrel Pattern(배럴 패턴)을 강제
|
|
|
35
39
|
- ESLint 9
|
|
36
40
|
> Flat config(eslint.config.js), TypeScript 지원 시 "typescript-eslint" config 사용 필요
|
|
37
41
|
- ESLint 8
|
|
42
|
+
|
|
38
43
|
> Legacy config(.eslintrc.js), TypeScript 지원 시 "@typescript-eslint/parser"를 parser로 지정하고, "@typescript-eslint"를 plugin에 추가해야 함
|
|
44
|
+
|
|
45
|
+
- TypeScript Alias Import 지원
|
|
46
|
+
> Import 구문에서 TypeScript 경로 별칭(예: `@ts/barrel/inner`)을 `tsconfig.json` 기반으로 자동 해석합니다.
|
|
47
|
+
> 단, ESLint 플러그인 설정에서는 alias 사용 불가능 - 상대경로나 절대경로만 사용하세요.
|
|
39
48
|
- Node.js (ES2015 이상)
|
|
40
49
|
- ES 모듈, CommonJS 모듈 모두 지원
|
|
41
50
|
|
|
@@ -48,6 +57,17 @@ JavaScript/TypeScript 프로젝트에서 Barrel Pattern(배럴 패턴)을 강제
|
|
|
48
57
|
(예: `import ... from "../domains/foo"`는 허용,
|
|
49
58
|
`import ... from "../domains/foo/components/Bar"`는 차단)
|
|
50
59
|
|
|
60
|
+
- **Isolation Barrel Module**
|
|
61
|
+
지정한 barrel path 외부의 모듈이 내부 파일을 직접 import하지 못하도록 막을 수 있습니다.
|
|
62
|
+
`isolated: true` 옵션을 사용하면 같은 barrel path 내부에서는 자유롭게 import가 가능하고,
|
|
63
|
+
외부에서는 해당 barrel path로의 import가 모두 차단됩니다. (barrel(index) 파일을 통한 접근도 불가)
|
|
64
|
+
만약 특정 공유 import 경로만 허용하고 싶다면 `allowedImportPaths` 옵션을 사용할 수 있습니다.
|
|
65
|
+
이를 통해 각 모듈의 경계를 엄격하게 보호하고, 모듈의 독립성을 유지할 수 있습니다.
|
|
66
|
+
|
|
67
|
+
- **와일드카드 import/export 방지**
|
|
68
|
+
`import * as foo from "module"` 또는 `export * from "./module"`과 같은 와일드카드(네임스페이스) import/export를 금지합니다.
|
|
69
|
+
명시적인 이름 기반 import/export만 허용하여 트리쉐이킹과 코드 명확성을 높입니다.
|
|
70
|
+
|
|
51
71
|
- **고성능 glob 매칭**
|
|
52
72
|
`src/domains/*`처럼 glob 패턴으로 여러 디렉토리 지정 가능
|
|
53
73
|
|
|
@@ -56,16 +76,22 @@ JavaScript/TypeScript 프로젝트에서 Barrel Pattern(배럴 패턴)을 강제
|
|
|
56
76
|
## 규칙(Rules)
|
|
57
77
|
|
|
58
78
|
1. **enforce-barrel-pattern**
|
|
59
|
-
모듈
|
|
60
|
-
지정한
|
|
79
|
+
모듈 import 시 barrel 패턴을 강제합니다.
|
|
80
|
+
지정한 barrel 파일(예: index.ts)로만 import를 허용하고, 내부 모듈에 대한 직접 접근을 차단합니다.
|
|
81
|
+
`isolated: true` 옵션을 사용하면 같은 barrel path 내부 파일끼리만 import가 가능하며, 외부에서의 import는 barrel 파일을 통한 접근도 모두 차단됩니다.
|
|
82
|
+
`allowedImportPaths` 옵션을 사용하면 특정 공유 import 경로만 예외적으로 허용할 수 있습니다.
|
|
61
83
|
|
|
62
84
|
- **옵션:**
|
|
63
|
-
- `paths`:
|
|
64
|
-
- `baseDir` (선택): `paths`
|
|
85
|
+
- `paths`: barrel 패턴을 적용할 디렉토리 목록(`baseDir` 기준 상대경로)
|
|
86
|
+
- `baseDir` (선택): `paths` 기준이 되는 베이스 디렉토리 (기본값: ESLint 실행 디렉토리)
|
|
87
|
+
- `isolated` (선택): `true`일 경우, barrel path 외부에서의 모든 import를 차단합니다(barrel 파일 통한 접근 포함). 같은 barrel path 내부 또는 `allowedImportPaths`만 허용.
|
|
88
|
+
- `allowedImportPaths` (선택): isolation 모드에서도 직접 import를 허용할 경로 배열
|
|
65
89
|
|
|
66
90
|
2. **no-wildcard**
|
|
67
|
-
`import * as foo from "module"
|
|
68
|
-
|
|
91
|
+
`import * as foo from "module"` 또는 `export * from "./module"`과 같은 와일드카드(네임스페이스) import/export를 금지합니다.
|
|
92
|
+
`enforce-barrel-pattern` 룰과 함께 사용하는 것을 적극 추천합니다.
|
|
93
|
+
두 룰을 함께 적용하면 모듈 경계를 엄격하게 지킬 수 있을 뿐만 아니라,
|
|
94
|
+
트리쉐이킹을 통한 성능 향상과 코드 추적 및 유지보수의 용이성까지 모두 얻을 수 있습니다.
|
|
69
95
|
|
|
70
96
|
---
|
|
71
97
|
|
|
@@ -100,6 +126,11 @@ module.exports = {
|
|
|
100
126
|
// (옵션) 설정하지 않으면 기본값은 ESLint를 실행한 위치(작업 디렉토리)입니다.
|
|
101
127
|
// 예: `npx eslint .`처럼 실행하면, 실행 시점의 현재 디렉토리가 기본값이 됩니다.
|
|
102
128
|
baseDir: __dirname,
|
|
129
|
+
// isolation 모드 활성화: barrel path 외부에서의 모든 import를 차단합니다.
|
|
130
|
+
isolated: true,
|
|
131
|
+
// "shared" 디렉토리만 직접 import를 허용합니다.
|
|
132
|
+
// 필요에 따라 이 배열에 "node_modules/*" 등 원하는 경로를 자유롭게 추가할 수 있습니다.
|
|
133
|
+
allowedImportPaths: ["src/typescript/shared", "node_modules/*"],
|
|
103
134
|
},
|
|
104
135
|
],
|
|
105
136
|
// import * 또는 export * 금지
|
|
@@ -149,6 +180,11 @@ export default tseslint.config([
|
|
|
149
180
|
// (옵션) 설정하지 않으면 기본값은 ESLint를 실행한 위치(작업 디렉토리)입니다.
|
|
150
181
|
// 예: `npx eslint .`처럼 실행하면, 실행 시점의 현재 디렉토리가 기본값이 됩니다.
|
|
151
182
|
baseDir: __dirname,
|
|
183
|
+
// isolation 모드 활성화: barrel path 외부에서의 모든 import를 차단합니다.
|
|
184
|
+
isolated: true,
|
|
185
|
+
// "shared" 디렉토리만 직접 import를 허용합니다.
|
|
186
|
+
// 필요에 따라 이 배열에 "node_modules/*" 등 원하는 경로를 자유롭게 추가할 수 있습니다.
|
|
187
|
+
allowedImportPaths: ["src/typescript/shared", "node_modules/*"],
|
|
152
188
|
},
|
|
153
189
|
],
|
|
154
190
|
// import * 또는 export * 금지
|
|
@@ -162,25 +198,49 @@ export default tseslint.config([
|
|
|
162
198
|
|
|
163
199
|
## 예시
|
|
164
200
|
|
|
201
|
+
### 1. 배럴 내부파일 직접 접근
|
|
202
|
+
|
|
165
203
|
```ts
|
|
166
|
-
|
|
204
|
+
file(src / index.ts);
|
|
205
|
+
|
|
206
|
+
// ❌ 내부 파일 직접 import 차단
|
|
167
207
|
import { Test } from "../domains/foo/components/Test";
|
|
168
208
|
|
|
169
|
-
// ✅
|
|
209
|
+
// ✅ barrel(index) 파일을 통한 import만 허용
|
|
170
210
|
import { Test } from "../domains/foo";
|
|
171
211
|
```
|
|
172
212
|
|
|
213
|
+
### 2. 격리된 모듈 접근 (TypeScript Alias 지원)
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
file(src / domains / foo / index.ts);
|
|
217
|
+
|
|
218
|
+
// ❌ 격리된 barrel로의 외부 import 차단 (alias 사용해도 차단)
|
|
219
|
+
// barrel 외부에서 접근 (bar의 경로는 src/domains/bar/)
|
|
220
|
+
import { Test } from "@domains/bar/components/Test";
|
|
221
|
+
// 또는
|
|
222
|
+
import { Test } from "../domains/bar";
|
|
223
|
+
|
|
224
|
+
// ✅ 같은 barrel 내부에서의 import는 허용 (alias 지원)
|
|
225
|
+
import { Hook } from "@domains/foo/hooks/useTest"; // 같은 barrel 내부에서
|
|
226
|
+
import { Utils } from "./utils/helper"; // 같은 barrel 내부에서
|
|
227
|
+
|
|
228
|
+
// ✅ 허용된 import 경로는 사용 가능 (alias 지원)
|
|
229
|
+
import { SharedUtil } from "@shared/utils"; // allowedImportPaths에 "src/shared/*"가 있는 경우
|
|
230
|
+
```
|
|
231
|
+
|
|
173
232
|
---
|
|
174
233
|
|
|
175
234
|
## 앞으로의 계획
|
|
176
235
|
|
|
177
236
|
- 더 다양한 모듈 경계/추상화 관련 룰 추가 예정 (~Ing)
|
|
178
|
-
- Alias/tsconfig 지원: TypeScript의 paths
|
|
237
|
+
- **Alias/tsconfig 지원: TypeScript의 paths 맵핑 완벽하게 지원** (OK)
|
|
179
238
|
- **CJS 지원** (OK)
|
|
180
239
|
- **ESLint 8 지원** (OK)
|
|
181
240
|
- **번들 플러그인(플러그인 내 모든 기능 통합)** (OK)
|
|
182
241
|
- **잘못된 경로 설정 검증 기능** (OK)
|
|
183
242
|
- **와일드카드 import/export 제한 규칙** (OK)
|
|
243
|
+
- **지정한 Barrel 경로 격리** (OK)
|
|
184
244
|
|
|
185
245
|
---
|
|
186
246
|
|
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.2.0-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>
|
|
@@ -31,6 +31,10 @@ internal implementation details must only be accessed via the directory’s **in
|
|
|
31
31
|
Direct imports from internal files are blocked, maximizing
|
|
32
32
|
**modularity, abstraction, maintainability, and scalability**.
|
|
33
33
|
|
|
34
|
+
> 💡 Tip:
|
|
35
|
+
> 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
|
+
> This allows you to detect and prevent circular dependencies (import cycles) in your project.
|
|
37
|
+
|
|
34
38
|
---
|
|
35
39
|
|
|
36
40
|
## Supports
|
|
@@ -39,6 +43,9 @@ Direct imports from internal files are blocked, maximizing
|
|
|
39
43
|
> Flat config(eslint.config.js), for TypeScript support, use the "typescript-eslint" config
|
|
40
44
|
- ESLint 8
|
|
41
45
|
> Legacy config(.eslintrc.js), for TypeScript support, set "@typescript-eslint/parser" as the parser and add "@typescript-eslint" as a plugin
|
|
46
|
+
- TypeScript Alias Import Support
|
|
47
|
+
> Automatically resolves TypeScript path aliases (e.g., `@ts/barrel/inner`) in import statements based on your `tsconfig.json`.
|
|
48
|
+
> Note: ESLint plugin configuration does not support aliases - use relative or absolute paths only.
|
|
42
49
|
- Node.js (ES2015+)
|
|
43
50
|
- Supports both ES Modules and CommonJS
|
|
44
51
|
|
|
@@ -51,6 +58,17 @@ Direct imports from internal files are blocked, maximizing
|
|
|
51
58
|
(e.g., `import ... from "../domains/foo"` is allowed,
|
|
52
59
|
but `import ... from "../domains/foo/components/Bar"` is blocked)
|
|
53
60
|
|
|
61
|
+
- **Isolation Barrel Module**
|
|
62
|
+
You can prevent modules outside the specified barrel path from directly importing internal files.
|
|
63
|
+
By enabling `isolated: true`, only files within the same barrel path can freely import each other.
|
|
64
|
+
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 `allowedImportPaths` option.
|
|
66
|
+
This helps you strictly protect your module boundaries and keep each module truly independent.
|
|
67
|
+
|
|
68
|
+
- **Prevent Wildcard Import/Export**
|
|
69
|
+
Disallows wildcard (namespace) imports and exports such as `import * as foo from "module"` or `export * from "./module"`.
|
|
70
|
+
This enforces the use of named imports/exports for better tree-shaking and code clarity.
|
|
71
|
+
|
|
54
72
|
- **High-performance glob matching**
|
|
55
73
|
Specify multiple directories using glob patterns like `src/domains/*`
|
|
56
74
|
|
|
@@ -61,14 +79,20 @@ Direct imports from internal files are blocked, maximizing
|
|
|
61
79
|
1. **enforce-barrel-pattern**
|
|
62
80
|
Enforces the barrel pattern for module imports.
|
|
63
81
|
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.
|
|
64
84
|
|
|
65
85
|
- **Options:**
|
|
66
86
|
- `paths`: The directories to be protected by the barrel pattern (relative to `baseDir`).
|
|
67
87
|
- `baseDir` (optional): The base directory for resolving `paths`. Defaults to the ESLint execution directory.
|
|
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.
|
|
68
90
|
|
|
69
91
|
2. **no-wildcard**
|
|
70
|
-
Disallows wildcard (namespace) imports such as `import * as foo from "module"
|
|
71
|
-
|
|
92
|
+
Disallows wildcard (namespace) imports such as `import * as foo from "module"` or `export * from "./module"`.
|
|
93
|
+
We highly recommend enabling this rule together with the `enforce-barrel-pattern` rule.
|
|
94
|
+
Using both rules together not only enforces strict module boundaries,
|
|
95
|
+
but also improves performance through better tree-shaking and makes code tracing and maintenance much easier.
|
|
72
96
|
|
|
73
97
|
---
|
|
74
98
|
|
|
@@ -103,6 +127,11 @@ module.exports = {
|
|
|
103
127
|
// Optional config. The default value is the directory where ESLint is executed.
|
|
104
128
|
// For example, if you run `npx eslint .`, the default will be the current working directory at the time of execution.
|
|
105
129
|
baseDir: __dirname,
|
|
130
|
+
// Enable isolation mode: block all imports from outside the barrel path
|
|
131
|
+
isolated: true,
|
|
132
|
+
// Allow direct imports only from the "shared" directory.
|
|
133
|
+
// You can customize this array as needed, e.g., add "node_modules/..." or any other path you want to allow.
|
|
134
|
+
allowedImportPaths: ["src/typescript/shared", "node_modules/*"],
|
|
106
135
|
},
|
|
107
136
|
],
|
|
108
137
|
// Disallow wildcard (namespace) import/export.
|
|
@@ -152,6 +181,11 @@ export default tseslint.config([
|
|
|
152
181
|
// Optional config. The default value is the directory where ESLint is executed.
|
|
153
182
|
// For example, if you run `npx eslint .`, the default will be the current working directory at the time of execution.
|
|
154
183
|
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/*"],
|
|
155
189
|
},
|
|
156
190
|
],
|
|
157
191
|
// Disallow wildcard (namespace) import/export.
|
|
@@ -163,9 +197,13 @@ export default tseslint.config([
|
|
|
163
197
|
|
|
164
198
|
---
|
|
165
199
|
|
|
166
|
-
##
|
|
200
|
+
## Examples
|
|
201
|
+
|
|
202
|
+
### 1. Direct Access
|
|
167
203
|
|
|
168
204
|
```ts
|
|
205
|
+
file(src / index.ts);
|
|
206
|
+
|
|
169
207
|
// ❌ Direct import from internal file is blocked
|
|
170
208
|
import { Test } from "../domains/foo/components/Test";
|
|
171
209
|
|
|
@@ -173,6 +211,25 @@ import { Test } from "../domains/foo/components/Test";
|
|
|
173
211
|
import { Test } from "../domains/foo";
|
|
174
212
|
```
|
|
175
213
|
|
|
214
|
+
### 2. Isolated Module Access (with TypeScript Alias Support)
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
file(src / domains / foo / index.ts);
|
|
218
|
+
|
|
219
|
+
// ❌ External import to isolated barrel is blocked (even with alias)
|
|
220
|
+
// from outside barrel (bar's path is src/domains/bar/)
|
|
221
|
+
import { Test } from "@domains/bar/components/Test";
|
|
222
|
+
// or
|
|
223
|
+
import { Test } from "../domains/bar";
|
|
224
|
+
|
|
225
|
+
// ✅ Internal imports within same barrel are allowed (alias supported)
|
|
226
|
+
import { Hook } from "@domains/foo/hooks/useTest"; // from inside same barrel
|
|
227
|
+
import { Utils } from "./utils/helper"; // from inside same barrel
|
|
228
|
+
|
|
229
|
+
// ✅ Allowed import paths are permitted (alias supported)
|
|
230
|
+
import { SharedUtil } from "@shㅇㅇared/utils"; // if "src/shared/*" is in allowedImportPaths
|
|
231
|
+
```
|
|
232
|
+
|
|
176
233
|
---
|
|
177
234
|
|
|
178
235
|
## Future Work
|
|
@@ -180,14 +237,14 @@ import { Test } from "../domains/foo";
|
|
|
180
237
|
- More rules for module boundaries and abstraction (~Ing)
|
|
181
238
|
|
|
182
239
|
- **Alias/tsconfig Support**
|
|
183
|
-
Fully supports TypeScript `paths
|
|
184
|
-
|
|
240
|
+
Fully supports TypeScript `paths` (OK)
|
|
185
241
|
- **CJS Support** (OK)
|
|
186
242
|
- **Eslint8 Support** (OK)
|
|
187
243
|
- **Bundle Plugin(capsure any features in plugin)**
|
|
188
244
|
(OK)
|
|
189
245
|
- **Wrong Path Setup Validator** (OK)
|
|
190
246
|
- **Wildcard Import/Export Protection Rule** (OK)
|
|
247
|
+
- **Isolation Barrel Module** (OK)
|
|
191
248
|
|
|
192
249
|
---
|
|
193
250
|
|
package/dist/index.cjs
CHANGED
|
@@ -36,9 +36,101 @@ module.exports = __toCommonJS(index_exports);
|
|
|
36
36
|
|
|
37
37
|
// src/rules/enforce-barrel-pattern.ts
|
|
38
38
|
var import_utils = require("@typescript-eslint/utils");
|
|
39
|
-
var
|
|
39
|
+
var import_path2 = __toESM(require("path"), 1);
|
|
40
40
|
var import_resolve = __toESM(require("resolve"), 1);
|
|
41
|
+
|
|
42
|
+
// src/utils/glob.ts
|
|
41
43
|
var import_fast_glob = __toESM(require("fast-glob"), 1);
|
|
44
|
+
var Glob = class {
|
|
45
|
+
static resolvePath(path3, baseDir) {
|
|
46
|
+
const globResult = import_fast_glob.default.sync(path3, {
|
|
47
|
+
cwd: baseDir,
|
|
48
|
+
onlyDirectories: true,
|
|
49
|
+
absolute: true
|
|
50
|
+
});
|
|
51
|
+
if (globResult.length === 0) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`[Glob] In baseDir: ${baseDir}, path: ${path3}, any directory was not found`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
return globResult;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// src/utils/alias.ts
|
|
61
|
+
var import_tsconfig_paths = require("tsconfig-paths");
|
|
62
|
+
var import_path = __toESM(require("path"), 1);
|
|
63
|
+
var Alias = class {
|
|
64
|
+
constructor() {
|
|
65
|
+
}
|
|
66
|
+
static resolvePath(rawPath, currentFileDir) {
|
|
67
|
+
try {
|
|
68
|
+
const configResult = (0, import_tsconfig_paths.loadConfig)(currentFileDir);
|
|
69
|
+
if (configResult.resultType === "success") {
|
|
70
|
+
for (const [pattern, targets] of Object.entries(configResult.paths)) {
|
|
71
|
+
const origin = targets[0];
|
|
72
|
+
if (pattern.includes("*")) {
|
|
73
|
+
const patternRegex = new RegExp(
|
|
74
|
+
`^${pattern.replace("*", "(.*)")}$`
|
|
75
|
+
);
|
|
76
|
+
const match = rawPath.match(patternRegex);
|
|
77
|
+
if (match) {
|
|
78
|
+
const [, matchedPath] = match;
|
|
79
|
+
const extendedOrigin = origin.replace("*", matchedPath);
|
|
80
|
+
const absolutePath = import_path.default.resolve(
|
|
81
|
+
`${configResult.absoluteBaseUrl}/${extendedOrigin}`
|
|
82
|
+
);
|
|
83
|
+
return {
|
|
84
|
+
absolutePath,
|
|
85
|
+
type: "success"
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
if (rawPath === pattern) {
|
|
90
|
+
const absolutePath = import_path.default.resolve(
|
|
91
|
+
`${configResult.absoluteBaseUrl}/${origin}`
|
|
92
|
+
);
|
|
93
|
+
return {
|
|
94
|
+
absolutePath,
|
|
95
|
+
type: "success"
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
absolutePath: rawPath,
|
|
103
|
+
type: "fail"
|
|
104
|
+
};
|
|
105
|
+
} catch (e) {
|
|
106
|
+
return {
|
|
107
|
+
absolutePath: rawPath,
|
|
108
|
+
type: "fail"
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// src/rules/enforce-barrel-pattern.ts
|
|
115
|
+
var BARREL_ENTRY_POINT_FILE_NAMES = [
|
|
116
|
+
"index.ts",
|
|
117
|
+
"index.tsx",
|
|
118
|
+
"index.js",
|
|
119
|
+
"index.jsx",
|
|
120
|
+
"index.cjs",
|
|
121
|
+
"index.mjs",
|
|
122
|
+
"index.d.ts"
|
|
123
|
+
];
|
|
124
|
+
var RESOLVE_EXTENSIONS = [
|
|
125
|
+
".ts",
|
|
126
|
+
".tsx",
|
|
127
|
+
".js",
|
|
128
|
+
".jsx",
|
|
129
|
+
".json",
|
|
130
|
+
".d.ts",
|
|
131
|
+
".mjs",
|
|
132
|
+
".cjs"
|
|
133
|
+
];
|
|
42
134
|
var enforceBarrelPattern = {
|
|
43
135
|
meta: {
|
|
44
136
|
type: "problem",
|
|
@@ -50,33 +142,36 @@ var enforceBarrelPattern = {
|
|
|
50
142
|
type: "object",
|
|
51
143
|
properties: {
|
|
52
144
|
paths: { type: "array", items: { type: "string" } },
|
|
53
|
-
baseDir: { type: "string" }
|
|
145
|
+
baseDir: { type: "string" },
|
|
146
|
+
isolated: { type: "boolean" },
|
|
147
|
+
allowedImportPaths: { type: "array", items: { type: "string" } }
|
|
54
148
|
},
|
|
55
149
|
required: ["paths"],
|
|
56
150
|
additionalProperties: false
|
|
57
151
|
}
|
|
58
152
|
],
|
|
59
153
|
messages: {
|
|
60
|
-
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."
|
|
154
|
+
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
|
+
IsolatedBarrelImportDisallowed: "This barrel file is isolated. external import is not allowed. if you want to import outside of the barrel file, please add 'allowedImportPaths' to the plugin options."
|
|
61
156
|
}
|
|
62
157
|
},
|
|
63
158
|
//default options(baseDir is current working directory. almost user execute eslint in project root)
|
|
64
|
-
defaultOptions: [
|
|
159
|
+
defaultOptions: [
|
|
160
|
+
{
|
|
161
|
+
paths: [],
|
|
162
|
+
baseDir: process.cwd(),
|
|
163
|
+
isolated: false,
|
|
164
|
+
allowedImportPaths: []
|
|
165
|
+
}
|
|
166
|
+
],
|
|
65
167
|
create(context) {
|
|
66
168
|
const option = context.options[0];
|
|
67
169
|
const baseDir = option.baseDir;
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
});
|
|
74
|
-
if (globResult.length === 0) {
|
|
75
|
-
throw new Error(
|
|
76
|
-
`[enforce-barrel-pattern] In baseDir: ${baseDir}, path: ${_path}, any directory was not found`
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
return globResult;
|
|
170
|
+
const absoluteTargetPaths = option.paths.flatMap((_path) => {
|
|
171
|
+
return Glob.resolvePath(_path, baseDir);
|
|
172
|
+
});
|
|
173
|
+
const allowedImportPaths = option.allowedImportPaths.flatMap((_path) => {
|
|
174
|
+
return Glob.resolvePath(_path, baseDir);
|
|
80
175
|
});
|
|
81
176
|
return {
|
|
82
177
|
//check only import declaration(ESM)
|
|
@@ -85,48 +180,79 @@ var enforceBarrelPattern = {
|
|
|
85
180
|
const absoluteCurrentFilePath = context.getFilename();
|
|
86
181
|
let absoluteImportPath = null;
|
|
87
182
|
try {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
});
|
|
183
|
+
const aliasResult = Alias.resolvePath(
|
|
184
|
+
rawImportPath,
|
|
185
|
+
import_path2.default.dirname(absoluteCurrentFilePath)
|
|
186
|
+
);
|
|
187
|
+
if (aliasResult.type === "success") {
|
|
188
|
+
absoluteImportPath = aliasResult.absolutePath;
|
|
189
|
+
} else {
|
|
190
|
+
absoluteImportPath = import_resolve.default.sync(rawImportPath, {
|
|
191
|
+
basedir: import_path2.default.dirname(absoluteCurrentFilePath),
|
|
192
|
+
extensions: RESOLVE_EXTENSIONS
|
|
193
|
+
});
|
|
194
|
+
}
|
|
101
195
|
} catch (e) {
|
|
102
196
|
return;
|
|
103
197
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
198
|
+
{
|
|
199
|
+
let matchedLatestTargetPath = null;
|
|
200
|
+
const invalidDirectedImport = absoluteTargetPaths.some(
|
|
201
|
+
(absoluteTargetPath) => {
|
|
202
|
+
const targetPathEntryPoints = BARREL_ENTRY_POINT_FILE_NAMES.map(
|
|
203
|
+
(entry) => import_path2.default.resolve(absoluteTargetPath, entry)
|
|
204
|
+
);
|
|
205
|
+
const closedTargetPath = absoluteTargetPath + "/";
|
|
206
|
+
const targetPathEntryPointed = targetPathEntryPoints.includes(absoluteImportPath);
|
|
207
|
+
const importedEnforceBarrelFile = absoluteImportPath.startsWith(closedTargetPath);
|
|
208
|
+
const currentFileInEnforceBarrel = absoluteCurrentFilePath.startsWith(closedTargetPath);
|
|
209
|
+
const importedOutsideOfTargetPath = !currentFileInEnforceBarrel && importedEnforceBarrelFile;
|
|
210
|
+
const invalidImported = !targetPathEntryPointed && importedOutsideOfTargetPath;
|
|
211
|
+
if (invalidImported) {
|
|
212
|
+
matchedLatestTargetPath = absoluteTargetPath;
|
|
213
|
+
}
|
|
214
|
+
return invalidImported;
|
|
215
|
+
}
|
|
216
|
+
);
|
|
217
|
+
if (invalidDirectedImport) {
|
|
218
|
+
context.report({
|
|
219
|
+
node,
|
|
220
|
+
messageId: "DirectImportDisallowed",
|
|
221
|
+
data: {
|
|
222
|
+
rawImportPath,
|
|
223
|
+
matchedTargetPath: matchedLatestTargetPath
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
{
|
|
229
|
+
if (option.isolated) {
|
|
230
|
+
const currentFileInEnforceBarrel = absoluteTargetPaths.some(
|
|
231
|
+
(absoluteTargetPath) => {
|
|
232
|
+
const closedTargetPath = absoluteTargetPath + "/";
|
|
233
|
+
return absoluteCurrentFilePath.startsWith(closedTargetPath);
|
|
234
|
+
}
|
|
235
|
+
);
|
|
236
|
+
const sameBarrel = absoluteTargetPaths.some(
|
|
237
|
+
(absoluteTargetPath) => {
|
|
238
|
+
const closedTargetPath = absoluteTargetPath + "/";
|
|
239
|
+
const importedEnforceBarrelFile = absoluteImportPath.startsWith(closedTargetPath);
|
|
240
|
+
const currentFileInEnforceBarrel2 = absoluteCurrentFilePath.startsWith(closedTargetPath);
|
|
241
|
+
return importedEnforceBarrelFile && currentFileInEnforceBarrel2;
|
|
242
|
+
}
|
|
243
|
+
);
|
|
244
|
+
const allowedImportPath = allowedImportPaths.some(
|
|
245
|
+
(allowedImportPath2) => {
|
|
246
|
+
return absoluteImportPath.startsWith(allowedImportPath2);
|
|
247
|
+
}
|
|
248
|
+
);
|
|
249
|
+
if (!allowedImportPath && !sameBarrel && currentFileInEnforceBarrel) {
|
|
250
|
+
context.report({
|
|
251
|
+
node,
|
|
252
|
+
messageId: "IsolatedBarrelImportDisallowed"
|
|
253
|
+
});
|
|
254
|
+
}
|
|
121
255
|
}
|
|
122
|
-
return invalidImported;
|
|
123
|
-
});
|
|
124
|
-
if (invalidDirectedImport) {
|
|
125
|
-
context.report({
|
|
126
|
-
node,
|
|
127
|
-
messageId: "DirectImportDisallowed",
|
|
128
|
-
data: { rawImportPath, matchedTargetPath: matchedLatestTargetPath }
|
|
129
|
-
});
|
|
130
256
|
}
|
|
131
257
|
}
|
|
132
258
|
};
|
package/dist/index.d.cts
CHANGED
|
@@ -2,9 +2,11 @@ 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" | "IsolatedBarrelImportDisallowed", {
|
|
6
6
|
paths: string[];
|
|
7
7
|
baseDir: string;
|
|
8
|
+
isolated: boolean;
|
|
9
|
+
allowedImportPaths: string[];
|
|
8
10
|
}[], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
9
11
|
"no-wildcard": _typescript_eslint_utils_ts_eslint.RuleModule<"NoWildcardImport" | "NoExportAll", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
10
12
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -2,9 +2,11 @@ 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" | "IsolatedBarrelImportDisallowed", {
|
|
6
6
|
paths: string[];
|
|
7
7
|
baseDir: string;
|
|
8
|
+
isolated: boolean;
|
|
9
|
+
allowedImportPaths: string[];
|
|
8
10
|
}[], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
9
11
|
"no-wildcard": _typescript_eslint_utils_ts_eslint.RuleModule<"NoWildcardImport" | "NoExportAll", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
10
12
|
};
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,100 @@
|
|
|
1
1
|
// src/rules/enforce-barrel-pattern.ts
|
|
2
2
|
import "@typescript-eslint/utils";
|
|
3
|
-
import
|
|
3
|
+
import path2 from "path";
|
|
4
4
|
import resolve from "resolve";
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
// src/utils/glob.ts
|
|
7
|
+
import FastGlob from "fast-glob";
|
|
8
|
+
var Glob = class {
|
|
9
|
+
static resolvePath(path3, baseDir) {
|
|
10
|
+
const globResult = FastGlob.sync(path3, {
|
|
11
|
+
cwd: baseDir,
|
|
12
|
+
onlyDirectories: true,
|
|
13
|
+
absolute: true
|
|
14
|
+
});
|
|
15
|
+
if (globResult.length === 0) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
`[Glob] In baseDir: ${baseDir}, path: ${path3}, any directory was not found`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
return globResult;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// src/utils/alias.ts
|
|
25
|
+
import { loadConfig } from "tsconfig-paths";
|
|
26
|
+
import path from "path";
|
|
27
|
+
var Alias = class {
|
|
28
|
+
constructor() {
|
|
29
|
+
}
|
|
30
|
+
static resolvePath(rawPath, currentFileDir) {
|
|
31
|
+
try {
|
|
32
|
+
const configResult = loadConfig(currentFileDir);
|
|
33
|
+
if (configResult.resultType === "success") {
|
|
34
|
+
for (const [pattern, targets] of Object.entries(configResult.paths)) {
|
|
35
|
+
const origin = targets[0];
|
|
36
|
+
if (pattern.includes("*")) {
|
|
37
|
+
const patternRegex = new RegExp(
|
|
38
|
+
`^${pattern.replace("*", "(.*)")}$`
|
|
39
|
+
);
|
|
40
|
+
const match = rawPath.match(patternRegex);
|
|
41
|
+
if (match) {
|
|
42
|
+
const [, matchedPath] = match;
|
|
43
|
+
const extendedOrigin = origin.replace("*", matchedPath);
|
|
44
|
+
const absolutePath = path.resolve(
|
|
45
|
+
`${configResult.absoluteBaseUrl}/${extendedOrigin}`
|
|
46
|
+
);
|
|
47
|
+
return {
|
|
48
|
+
absolutePath,
|
|
49
|
+
type: "success"
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
if (rawPath === pattern) {
|
|
54
|
+
const absolutePath = path.resolve(
|
|
55
|
+
`${configResult.absoluteBaseUrl}/${origin}`
|
|
56
|
+
);
|
|
57
|
+
return {
|
|
58
|
+
absolutePath,
|
|
59
|
+
type: "success"
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
absolutePath: rawPath,
|
|
67
|
+
type: "fail"
|
|
68
|
+
};
|
|
69
|
+
} catch (e) {
|
|
70
|
+
return {
|
|
71
|
+
absolutePath: rawPath,
|
|
72
|
+
type: "fail"
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// src/rules/enforce-barrel-pattern.ts
|
|
79
|
+
var BARREL_ENTRY_POINT_FILE_NAMES = [
|
|
80
|
+
"index.ts",
|
|
81
|
+
"index.tsx",
|
|
82
|
+
"index.js",
|
|
83
|
+
"index.jsx",
|
|
84
|
+
"index.cjs",
|
|
85
|
+
"index.mjs",
|
|
86
|
+
"index.d.ts"
|
|
87
|
+
];
|
|
88
|
+
var RESOLVE_EXTENSIONS = [
|
|
89
|
+
".ts",
|
|
90
|
+
".tsx",
|
|
91
|
+
".js",
|
|
92
|
+
".jsx",
|
|
93
|
+
".json",
|
|
94
|
+
".d.ts",
|
|
95
|
+
".mjs",
|
|
96
|
+
".cjs"
|
|
97
|
+
];
|
|
6
98
|
var enforceBarrelPattern = {
|
|
7
99
|
meta: {
|
|
8
100
|
type: "problem",
|
|
@@ -14,33 +106,36 @@ var enforceBarrelPattern = {
|
|
|
14
106
|
type: "object",
|
|
15
107
|
properties: {
|
|
16
108
|
paths: { type: "array", items: { type: "string" } },
|
|
17
|
-
baseDir: { type: "string" }
|
|
109
|
+
baseDir: { type: "string" },
|
|
110
|
+
isolated: { type: "boolean" },
|
|
111
|
+
allowedImportPaths: { type: "array", items: { type: "string" } }
|
|
18
112
|
},
|
|
19
113
|
required: ["paths"],
|
|
20
114
|
additionalProperties: false
|
|
21
115
|
}
|
|
22
116
|
],
|
|
23
117
|
messages: {
|
|
24
|
-
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."
|
|
118
|
+
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
|
+
IsolatedBarrelImportDisallowed: "This barrel file is isolated. external import is not allowed. if you want to import outside of the barrel file, please add 'allowedImportPaths' to the plugin options."
|
|
25
120
|
}
|
|
26
121
|
},
|
|
27
122
|
//default options(baseDir is current working directory. almost user execute eslint in project root)
|
|
28
|
-
defaultOptions: [
|
|
123
|
+
defaultOptions: [
|
|
124
|
+
{
|
|
125
|
+
paths: [],
|
|
126
|
+
baseDir: process.cwd(),
|
|
127
|
+
isolated: false,
|
|
128
|
+
allowedImportPaths: []
|
|
129
|
+
}
|
|
130
|
+
],
|
|
29
131
|
create(context) {
|
|
30
132
|
const option = context.options[0];
|
|
31
133
|
const baseDir = option.baseDir;
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
});
|
|
38
|
-
if (globResult.length === 0) {
|
|
39
|
-
throw new Error(
|
|
40
|
-
`[enforce-barrel-pattern] In baseDir: ${baseDir}, path: ${_path}, any directory was not found`
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
return globResult;
|
|
134
|
+
const absoluteTargetPaths = option.paths.flatMap((_path) => {
|
|
135
|
+
return Glob.resolvePath(_path, baseDir);
|
|
136
|
+
});
|
|
137
|
+
const allowedImportPaths = option.allowedImportPaths.flatMap((_path) => {
|
|
138
|
+
return Glob.resolvePath(_path, baseDir);
|
|
44
139
|
});
|
|
45
140
|
return {
|
|
46
141
|
//check only import declaration(ESM)
|
|
@@ -49,48 +144,79 @@ var enforceBarrelPattern = {
|
|
|
49
144
|
const absoluteCurrentFilePath = context.getFilename();
|
|
50
145
|
let absoluteImportPath = null;
|
|
51
146
|
try {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
});
|
|
147
|
+
const aliasResult = Alias.resolvePath(
|
|
148
|
+
rawImportPath,
|
|
149
|
+
path2.dirname(absoluteCurrentFilePath)
|
|
150
|
+
);
|
|
151
|
+
if (aliasResult.type === "success") {
|
|
152
|
+
absoluteImportPath = aliasResult.absolutePath;
|
|
153
|
+
} else {
|
|
154
|
+
absoluteImportPath = resolve.sync(rawImportPath, {
|
|
155
|
+
basedir: path2.dirname(absoluteCurrentFilePath),
|
|
156
|
+
extensions: RESOLVE_EXTENSIONS
|
|
157
|
+
});
|
|
158
|
+
}
|
|
65
159
|
} catch (e) {
|
|
66
160
|
return;
|
|
67
161
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
162
|
+
{
|
|
163
|
+
let matchedLatestTargetPath = null;
|
|
164
|
+
const invalidDirectedImport = absoluteTargetPaths.some(
|
|
165
|
+
(absoluteTargetPath) => {
|
|
166
|
+
const targetPathEntryPoints = BARREL_ENTRY_POINT_FILE_NAMES.map(
|
|
167
|
+
(entry) => path2.resolve(absoluteTargetPath, entry)
|
|
168
|
+
);
|
|
169
|
+
const closedTargetPath = absoluteTargetPath + "/";
|
|
170
|
+
const targetPathEntryPointed = targetPathEntryPoints.includes(absoluteImportPath);
|
|
171
|
+
const importedEnforceBarrelFile = absoluteImportPath.startsWith(closedTargetPath);
|
|
172
|
+
const currentFileInEnforceBarrel = absoluteCurrentFilePath.startsWith(closedTargetPath);
|
|
173
|
+
const importedOutsideOfTargetPath = !currentFileInEnforceBarrel && importedEnforceBarrelFile;
|
|
174
|
+
const invalidImported = !targetPathEntryPointed && importedOutsideOfTargetPath;
|
|
175
|
+
if (invalidImported) {
|
|
176
|
+
matchedLatestTargetPath = absoluteTargetPath;
|
|
177
|
+
}
|
|
178
|
+
return invalidImported;
|
|
179
|
+
}
|
|
180
|
+
);
|
|
181
|
+
if (invalidDirectedImport) {
|
|
182
|
+
context.report({
|
|
183
|
+
node,
|
|
184
|
+
messageId: "DirectImportDisallowed",
|
|
185
|
+
data: {
|
|
186
|
+
rawImportPath,
|
|
187
|
+
matchedTargetPath: matchedLatestTargetPath
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
{
|
|
193
|
+
if (option.isolated) {
|
|
194
|
+
const currentFileInEnforceBarrel = absoluteTargetPaths.some(
|
|
195
|
+
(absoluteTargetPath) => {
|
|
196
|
+
const closedTargetPath = absoluteTargetPath + "/";
|
|
197
|
+
return absoluteCurrentFilePath.startsWith(closedTargetPath);
|
|
198
|
+
}
|
|
199
|
+
);
|
|
200
|
+
const sameBarrel = absoluteTargetPaths.some(
|
|
201
|
+
(absoluteTargetPath) => {
|
|
202
|
+
const closedTargetPath = absoluteTargetPath + "/";
|
|
203
|
+
const importedEnforceBarrelFile = absoluteImportPath.startsWith(closedTargetPath);
|
|
204
|
+
const currentFileInEnforceBarrel2 = absoluteCurrentFilePath.startsWith(closedTargetPath);
|
|
205
|
+
return importedEnforceBarrelFile && currentFileInEnforceBarrel2;
|
|
206
|
+
}
|
|
207
|
+
);
|
|
208
|
+
const allowedImportPath = allowedImportPaths.some(
|
|
209
|
+
(allowedImportPath2) => {
|
|
210
|
+
return absoluteImportPath.startsWith(allowedImportPath2);
|
|
211
|
+
}
|
|
212
|
+
);
|
|
213
|
+
if (!allowedImportPath && !sameBarrel && currentFileInEnforceBarrel) {
|
|
214
|
+
context.report({
|
|
215
|
+
node,
|
|
216
|
+
messageId: "IsolatedBarrelImportDisallowed"
|
|
217
|
+
});
|
|
218
|
+
}
|
|
85
219
|
}
|
|
86
|
-
return invalidImported;
|
|
87
|
-
});
|
|
88
|
-
if (invalidDirectedImport) {
|
|
89
|
-
context.report({
|
|
90
|
-
node,
|
|
91
|
-
messageId: "DirectImportDisallowed",
|
|
92
|
-
data: { rawImportPath, matchedTargetPath: matchedLatestTargetPath }
|
|
93
|
-
});
|
|
94
220
|
}
|
|
95
221
|
}
|
|
96
222
|
};
|
package/package.json
CHANGED
|
@@ -21,9 +21,11 @@
|
|
|
21
21
|
"barrel-module",
|
|
22
22
|
"barrel pattern",
|
|
23
23
|
"encapsulation directory",
|
|
24
|
-
"enforce barrel pattern"
|
|
24
|
+
"enforce barrel pattern",
|
|
25
|
+
"isolated barrel module",
|
|
26
|
+
"no-wildcard"
|
|
25
27
|
],
|
|
26
|
-
"version": "1.
|
|
28
|
+
"version": "1.2.0",
|
|
27
29
|
"type": "module",
|
|
28
30
|
"main": "dist/index.cjs",
|
|
29
31
|
"module": "dist/index.js",
|