eslint-plugin-barrel-rules 1.3.0 → 1.4.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 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.3.0-blue.svg" alt="Version"/>
6
+ <img src="https://img.shields.io/badge/version-1.4.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,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
 
@@ -222,7 +226,7 @@ export default tseslint.config([
222
226
  },
223
227
  ],
224
228
  baseDir: __dirname,
225
- globalAllowPaths: ["src/shares/*", "node_modules/*"],
229
+ globalAllowPaths: ["src/shares/*"],
226
230
  },
227
231
  ],
228
232
 
@@ -278,6 +282,7 @@ import { SharedUtil } from "@shared/utils"; // allowedImportPaths에 "src/shared
278
282
  - **잘못된 경로 설정 검증 기능** (OK)
279
283
  - **와일드카드 import/export 제한 규칙** (OK)
280
284
  - **지정한 Barrel 경로 격리** (OK)
285
+ - **빈 디렉토리 지원** (예: 'src/shares/\*'가 설정되어 있어도 shares 디렉토리가 비어있어도 됨) (OK)
281
286
 
282
287
  ---
283
288
 
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.3.0-blue.svg" alt="Version"/>
6
+ <img src="https://img.shields.io/badge/version-1.4.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>
@@ -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
 
@@ -228,7 +231,7 @@ export default tseslint.config([
228
231
  },
229
232
  ],
230
233
  baseDir: __dirname,
231
- globalAllowPaths: ["src/shares/*", "node_modules/*"],
234
+ globalAllowPaths: ["src/shares/*"],
232
235
  },
233
236
  ],
234
237
 
@@ -288,6 +291,7 @@ import { SharedUtil } from "@shared/utils"; // if "src/shared/*" is in allowedPa
288
291
  - **Wrong Path Setup Validator** (OK)
289
292
  - **Wildcard Import/Export Protection Rule** (OK)
290
293
  - **Isolation Barrel Module** (OK)
294
+ - **Empty Directory Support** (e.g., 'src/shares/\*' can be configured even if the shares directory is empty) (OK)
291
295
 
292
296
  ---
293
297
 
package/dist/index.cjs CHANGED
@@ -48,11 +48,6 @@ var Glob = class {
48
48
  onlyDirectories: true,
49
49
  absolute: true
50
50
  });
51
- if (globResult.length === 0) {
52
- throw new Error(
53
- `[Glob] In baseDir: ${baseDir}, path: ${path4}, any directory was not found`
54
- );
55
- }
56
51
  return globResult;
57
52
  }
58
53
  };
@@ -123,13 +118,20 @@ var BARREL_ENTRY_POINT_FILE_NAMES = [
123
118
  ];
124
119
  var RESOLVE_EXTENSIONS = [
125
120
  ".ts",
126
- ".tsx",
127
121
  ".js",
122
+ ".tsx",
128
123
  ".jsx",
129
124
  ".json",
125
+ ".d.js",
130
126
  ".d.ts",
131
127
  ".mjs",
132
- ".cjs"
128
+ ".cjs",
129
+ ".mts",
130
+ ".cts",
131
+ ".d.mjs",
132
+ ".d.cjs",
133
+ ".d.mts",
134
+ ".d.cts"
133
135
  ];
134
136
  var enforceBarrelPattern = {
135
137
  meta: {
@@ -183,63 +185,78 @@ var enforceBarrelPattern = {
183
185
  const absoluteTargetPaths = option.paths.flatMap((_path) => {
184
186
  return Glob.resolvePath(_path, baseDir);
185
187
  });
186
- return {
187
- //check only import declaration(ESM)
188
- ImportDeclaration(node) {
189
- const rawImportPath = node.source.value;
190
- const absoluteCurrentFilePath = context.getFilename();
191
- let absoluteImportPath = null;
192
- try {
193
- const aliasResult = Alias.resolvePath(
194
- rawImportPath,
195
- import_path2.default.dirname(absoluteCurrentFilePath)
196
- );
197
- if (aliasResult.type === "success") {
198
- absoluteImportPath = aliasResult.absolutePath;
199
- } else {
200
- absoluteImportPath = import_resolve.default.sync(rawImportPath, {
201
- basedir: import_path2.default.dirname(absoluteCurrentFilePath),
202
- extensions: RESOLVE_EXTENSIONS
203
- });
188
+ function checker(node) {
189
+ if (!node.source) {
190
+ return;
191
+ }
192
+ const rawImportPath = node.source.value;
193
+ const absoluteCurrentFilePath = context.getFilename();
194
+ let absoluteImportPath = null;
195
+ try {
196
+ const aliasResult = Alias.resolvePath(
197
+ rawImportPath,
198
+ import_path2.default.dirname(absoluteCurrentFilePath)
199
+ );
200
+ if (aliasResult.type === "success") {
201
+ absoluteImportPath = aliasResult.absolutePath;
202
+ } else {
203
+ if (!rawImportPath.startsWith(".") && !rawImportPath.startsWith("/") || rawImportPath.includes("/node_modules/")) {
204
+ return;
204
205
  }
205
- } catch (e) {
206
- context.report({
207
- node,
208
- messageId: "TransformedAliasResolveFailed"
206
+ absoluteImportPath = import_resolve.default.sync(rawImportPath, {
207
+ basedir: import_path2.default.dirname(absoluteCurrentFilePath),
208
+ extensions: RESOLVE_EXTENSIONS
209
209
  });
210
- return;
211
210
  }
212
- {
213
- let matchedLatestTargetPath = null;
214
- const invalidDirectedImport = absoluteTargetPaths.some(
215
- (absoluteTargetPath) => {
216
- const targetPathEntryPoints = BARREL_ENTRY_POINT_FILE_NAMES.map(
217
- (entry) => import_path2.default.resolve(absoluteTargetPath, entry)
218
- );
219
- const closedTargetPath = absoluteTargetPath + "/";
220
- const targetPathEntryPointed = targetPathEntryPoints.includes(absoluteImportPath);
221
- const importedEnforceBarrelFile = absoluteImportPath.startsWith(closedTargetPath);
222
- const currentFileInEnforceBarrel = absoluteCurrentFilePath.startsWith(closedTargetPath);
223
- const importedOutsideOfTargetPath = !currentFileInEnforceBarrel && importedEnforceBarrelFile;
224
- const invalidImported = !targetPathEntryPointed && importedOutsideOfTargetPath;
225
- if (invalidImported) {
226
- matchedLatestTargetPath = absoluteTargetPath;
227
- }
228
- return invalidImported;
211
+ } catch (e) {
212
+ context.report({
213
+ node,
214
+ messageId: "TransformedAliasResolveFailed"
215
+ });
216
+ return;
217
+ }
218
+ {
219
+ let matchedLatestTargetPath = null;
220
+ const invalidDirectedImport = absoluteTargetPaths.some(
221
+ (absoluteTargetPath) => {
222
+ const targetPathEntryPoints = BARREL_ENTRY_POINT_FILE_NAMES.map(
223
+ (entry) => import_path2.default.resolve(absoluteTargetPath, entry)
224
+ );
225
+ const closedTargetPath = absoluteTargetPath + "/";
226
+ const targetPathEntryPointed = targetPathEntryPoints.includes(absoluteImportPath);
227
+ const importedEnforceBarrelFile = absoluteImportPath.startsWith(closedTargetPath);
228
+ const currentFileInEnforceBarrel = absoluteCurrentFilePath.startsWith(closedTargetPath);
229
+ const importedOutsideOfTargetPath = !currentFileInEnforceBarrel && importedEnforceBarrelFile;
230
+ const invalidImported = !targetPathEntryPointed && importedOutsideOfTargetPath;
231
+ if (invalidImported) {
232
+ matchedLatestTargetPath = absoluteTargetPath;
229
233
  }
230
- );
231
- if (invalidDirectedImport) {
232
- context.report({
233
- node,
234
- messageId: "DirectImportDisallowed",
235
- data: {
236
- rawImportPath,
237
- matchedTargetPath: matchedLatestTargetPath
238
- }
239
- });
234
+ return invalidImported;
240
235
  }
236
+ );
237
+ if (invalidDirectedImport) {
238
+ context.report({
239
+ node,
240
+ messageId: "DirectImportDisallowed",
241
+ data: {
242
+ rawImportPath,
243
+ matchedTargetPath: matchedLatestTargetPath
244
+ }
245
+ });
241
246
  }
242
247
  }
248
+ }
249
+ return {
250
+ ExportNamedDeclaration(node) {
251
+ return checker(node);
252
+ },
253
+ ExportAllDeclaration(node) {
254
+ return checker(node);
255
+ },
256
+ //check only import declaration(ESM)
257
+ ImportDeclaration(node) {
258
+ return checker(node);
259
+ }
243
260
  };
244
261
  }
245
262
  };
@@ -250,12 +267,20 @@ var import_path3 = __toESM(require("path"), 1);
250
267
  var import_resolve2 = __toESM(require("resolve"), 1);
251
268
  var RESOLVE_EXTENSIONS2 = [
252
269
  ".ts",
253
- ".tsx",
254
270
  ".js",
271
+ ".tsx",
255
272
  ".jsx",
273
+ ".json",
274
+ ".d.js",
256
275
  ".d.ts",
257
276
  ".mjs",
258
- ".cjs"
277
+ ".cjs",
278
+ ".mts",
279
+ ".cts",
280
+ ".d.mjs",
281
+ ".d.cjs",
282
+ ".d.mts",
283
+ ".d.cts"
259
284
  ];
260
285
  var isolateBarrelFile = {
261
286
  meta: {
@@ -319,6 +344,9 @@ var isolateBarrelFile = {
319
344
  if (aliasResult.type === "success") {
320
345
  absoluteImportPath = aliasResult.absolutePath;
321
346
  } else {
347
+ if (!rawImportPath.startsWith(".") && !rawImportPath.startsWith("/") || rawImportPath.includes("/node_modules/")) {
348
+ return;
349
+ }
322
350
  absoluteImportPath = import_resolve2.default.sync(rawImportPath, {
323
351
  basedir: import_path3.default.dirname(absoluteCurrentFilePath),
324
352
  extensions: RESOLVE_EXTENSIONS2
package/dist/index.js CHANGED
@@ -12,11 +12,6 @@ var Glob = class {
12
12
  onlyDirectories: true,
13
13
  absolute: true
14
14
  });
15
- if (globResult.length === 0) {
16
- throw new Error(
17
- `[Glob] In baseDir: ${baseDir}, path: ${path4}, any directory was not found`
18
- );
19
- }
20
15
  return globResult;
21
16
  }
22
17
  };
@@ -87,13 +82,20 @@ var BARREL_ENTRY_POINT_FILE_NAMES = [
87
82
  ];
88
83
  var RESOLVE_EXTENSIONS = [
89
84
  ".ts",
90
- ".tsx",
91
85
  ".js",
86
+ ".tsx",
92
87
  ".jsx",
93
88
  ".json",
89
+ ".d.js",
94
90
  ".d.ts",
95
91
  ".mjs",
96
- ".cjs"
92
+ ".cjs",
93
+ ".mts",
94
+ ".cts",
95
+ ".d.mjs",
96
+ ".d.cjs",
97
+ ".d.mts",
98
+ ".d.cts"
97
99
  ];
98
100
  var enforceBarrelPattern = {
99
101
  meta: {
@@ -147,63 +149,78 @@ var enforceBarrelPattern = {
147
149
  const absoluteTargetPaths = option.paths.flatMap((_path) => {
148
150
  return Glob.resolvePath(_path, baseDir);
149
151
  });
150
- return {
151
- //check only import declaration(ESM)
152
- ImportDeclaration(node) {
153
- const rawImportPath = node.source.value;
154
- const absoluteCurrentFilePath = context.getFilename();
155
- let absoluteImportPath = null;
156
- try {
157
- const aliasResult = Alias.resolvePath(
158
- rawImportPath,
159
- path2.dirname(absoluteCurrentFilePath)
160
- );
161
- if (aliasResult.type === "success") {
162
- absoluteImportPath = aliasResult.absolutePath;
163
- } else {
164
- absoluteImportPath = resolve.sync(rawImportPath, {
165
- basedir: path2.dirname(absoluteCurrentFilePath),
166
- extensions: RESOLVE_EXTENSIONS
167
- });
152
+ function checker(node) {
153
+ if (!node.source) {
154
+ return;
155
+ }
156
+ const rawImportPath = node.source.value;
157
+ const absoluteCurrentFilePath = context.getFilename();
158
+ let absoluteImportPath = null;
159
+ try {
160
+ const aliasResult = Alias.resolvePath(
161
+ rawImportPath,
162
+ path2.dirname(absoluteCurrentFilePath)
163
+ );
164
+ if (aliasResult.type === "success") {
165
+ absoluteImportPath = aliasResult.absolutePath;
166
+ } else {
167
+ if (!rawImportPath.startsWith(".") && !rawImportPath.startsWith("/") || rawImportPath.includes("/node_modules/")) {
168
+ return;
168
169
  }
169
- } catch (e) {
170
- context.report({
171
- node,
172
- messageId: "TransformedAliasResolveFailed"
170
+ absoluteImportPath = resolve.sync(rawImportPath, {
171
+ basedir: path2.dirname(absoluteCurrentFilePath),
172
+ extensions: RESOLVE_EXTENSIONS
173
173
  });
174
- return;
175
174
  }
176
- {
177
- let matchedLatestTargetPath = null;
178
- const invalidDirectedImport = absoluteTargetPaths.some(
179
- (absoluteTargetPath) => {
180
- const targetPathEntryPoints = BARREL_ENTRY_POINT_FILE_NAMES.map(
181
- (entry) => path2.resolve(absoluteTargetPath, entry)
182
- );
183
- const closedTargetPath = absoluteTargetPath + "/";
184
- const targetPathEntryPointed = targetPathEntryPoints.includes(absoluteImportPath);
185
- const importedEnforceBarrelFile = absoluteImportPath.startsWith(closedTargetPath);
186
- const currentFileInEnforceBarrel = absoluteCurrentFilePath.startsWith(closedTargetPath);
187
- const importedOutsideOfTargetPath = !currentFileInEnforceBarrel && importedEnforceBarrelFile;
188
- const invalidImported = !targetPathEntryPointed && importedOutsideOfTargetPath;
189
- if (invalidImported) {
190
- matchedLatestTargetPath = absoluteTargetPath;
191
- }
192
- return invalidImported;
175
+ } catch (e) {
176
+ context.report({
177
+ node,
178
+ messageId: "TransformedAliasResolveFailed"
179
+ });
180
+ return;
181
+ }
182
+ {
183
+ let matchedLatestTargetPath = null;
184
+ const invalidDirectedImport = absoluteTargetPaths.some(
185
+ (absoluteTargetPath) => {
186
+ const targetPathEntryPoints = BARREL_ENTRY_POINT_FILE_NAMES.map(
187
+ (entry) => path2.resolve(absoluteTargetPath, entry)
188
+ );
189
+ const closedTargetPath = absoluteTargetPath + "/";
190
+ const targetPathEntryPointed = targetPathEntryPoints.includes(absoluteImportPath);
191
+ const importedEnforceBarrelFile = absoluteImportPath.startsWith(closedTargetPath);
192
+ const currentFileInEnforceBarrel = absoluteCurrentFilePath.startsWith(closedTargetPath);
193
+ const importedOutsideOfTargetPath = !currentFileInEnforceBarrel && importedEnforceBarrelFile;
194
+ const invalidImported = !targetPathEntryPointed && importedOutsideOfTargetPath;
195
+ if (invalidImported) {
196
+ matchedLatestTargetPath = absoluteTargetPath;
193
197
  }
194
- );
195
- if (invalidDirectedImport) {
196
- context.report({
197
- node,
198
- messageId: "DirectImportDisallowed",
199
- data: {
200
- rawImportPath,
201
- matchedTargetPath: matchedLatestTargetPath
202
- }
203
- });
198
+ return invalidImported;
204
199
  }
200
+ );
201
+ if (invalidDirectedImport) {
202
+ context.report({
203
+ node,
204
+ messageId: "DirectImportDisallowed",
205
+ data: {
206
+ rawImportPath,
207
+ matchedTargetPath: matchedLatestTargetPath
208
+ }
209
+ });
205
210
  }
206
211
  }
212
+ }
213
+ return {
214
+ ExportNamedDeclaration(node) {
215
+ return checker(node);
216
+ },
217
+ ExportAllDeclaration(node) {
218
+ return checker(node);
219
+ },
220
+ //check only import declaration(ESM)
221
+ ImportDeclaration(node) {
222
+ return checker(node);
223
+ }
207
224
  };
208
225
  }
209
226
  };
@@ -214,12 +231,20 @@ import path3 from "path";
214
231
  import resolve2 from "resolve";
215
232
  var RESOLVE_EXTENSIONS2 = [
216
233
  ".ts",
217
- ".tsx",
218
234
  ".js",
235
+ ".tsx",
219
236
  ".jsx",
237
+ ".json",
238
+ ".d.js",
220
239
  ".d.ts",
221
240
  ".mjs",
222
- ".cjs"
241
+ ".cjs",
242
+ ".mts",
243
+ ".cts",
244
+ ".d.mjs",
245
+ ".d.cjs",
246
+ ".d.mts",
247
+ ".d.cts"
223
248
  ];
224
249
  var isolateBarrelFile = {
225
250
  meta: {
@@ -283,6 +308,9 @@ var isolateBarrelFile = {
283
308
  if (aliasResult.type === "success") {
284
309
  absoluteImportPath = aliasResult.absolutePath;
285
310
  } else {
311
+ if (!rawImportPath.startsWith(".") && !rawImportPath.startsWith("/") || rawImportPath.includes("/node_modules/")) {
312
+ return;
313
+ }
286
314
  absoluteImportPath = resolve2.sync(rawImportPath, {
287
315
  basedir: path3.dirname(absoluteCurrentFilePath),
288
316
  extensions: RESOLVE_EXTENSIONS2
package/package.json CHANGED
@@ -25,7 +25,7 @@
25
25
  "isolated barrel module",
26
26
  "no-wildcard"
27
27
  ],
28
- "version": "1.3.0",
28
+ "version": "1.4.0",
29
29
  "type": "module",
30
30
  "main": "dist/index.cjs",
31
31
  "module": "dist/index.js",
@@ -49,6 +49,7 @@
49
49
  "typescript": "~5.8.3"
50
50
  },
51
51
  "scripts": {
52
+ "lint": "eslint src/**/*.ts",
52
53
  "build": "pnpm run test && tsup src/index.ts --dts --format cjs,esm",
53
54
  "type-check": "tsc --noEmit",
54
55
  "test": "jest",