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 +8 -3
- package/README.md +6 -2
- package/dist/index.cjs +87 -59
- package/dist/index.js +87 -59
- package/package.json +2 -1
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.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/*"
|
|
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.
|
|
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/*"
|
|
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
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
)
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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.
|
|
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",
|