@storm-software/eslint-plugin-bun 0.0.6 → 0.0.7
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.md +1 -1
- package/dist/configs/index.d.mts +5 -0
- package/dist/configs/index.mjs +4 -943
- package/dist/configs/recommended.d.mts +5 -0
- package/dist/configs/recommended.mjs +33 -939
- package/dist/helpers/bun-workspace.d.mts +49 -0
- package/dist/helpers/bun-workspace.mjs +197 -244
- package/dist/helpers/{create-rule.d.ts → create-rule.d.mts} +6 -6
- package/dist/helpers/create-rule.mjs +37 -38
- package/dist/helpers/{iterate-dependencies.d.ts → iterate-dependencies.d.mts} +7 -7
- package/dist/helpers/iterate-dependencies.mjs +36 -56
- package/dist/index.d.mts +52 -0
- package/dist/index.mjs +29 -951
- package/dist/plugin-9WjNsG8_.mjs +20 -0
- package/dist/plugin.d.mts +31 -0
- package/dist/plugin.mjs +1 -807
- package/dist/rules/json-enforce-catalog.d.mts +14 -0
- package/dist/rules/json-enforce-catalog.mjs +102 -444
- package/dist/rules/json-no-duplicate-catalog-item.d.mts +8 -0
- package/dist/rules/json-no-duplicate-catalog-item.mjs +72 -386
- package/dist/rules/json-no-unused-catalog-item.d.mts +5 -0
- package/dist/rules/json-no-unused-catalog-item.mjs +70 -423
- package/dist/rules/json-valid-catalog.d.mts +11 -0
- package/dist/rules/json-valid-catalog.mjs +80 -422
- package/package.json +15 -22
- package/dist/configs/index.d.ts +0 -5
- package/dist/configs/recommended.d.ts +0 -5
- package/dist/helpers/bun-workspace.d.ts +0 -48
- package/dist/index.d.ts +0 -58
- package/dist/plugin.d.ts +0 -33
- package/dist/rules/json-enforce-catalog.d.ts +0 -18
- package/dist/rules/json-no-duplicate-catalog-item.d.ts +0 -12
- package/dist/rules/json-no-unused-catalog-item.d.ts +0 -7
- package/dist/rules/json-valid-catalog.d.ts +0 -15
|
@@ -1,391 +1,77 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import 'tinyglobby';
|
|
7
|
-
|
|
8
|
-
// src/helpers/create-rule.ts
|
|
9
|
-
function RuleCreator(urlCreator) {
|
|
10
|
-
return function createNamedRule({
|
|
11
|
-
name,
|
|
12
|
-
meta,
|
|
13
|
-
...rule
|
|
14
|
-
}) {
|
|
15
|
-
return createRuleInfo({
|
|
16
|
-
meta: {
|
|
17
|
-
...meta,
|
|
18
|
-
docs: {
|
|
19
|
-
...meta.docs,
|
|
20
|
-
url: urlCreator(name)
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
...rule
|
|
24
|
-
});
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
function createRuleInfo({
|
|
28
|
-
create,
|
|
29
|
-
defaultOptions,
|
|
30
|
-
meta
|
|
31
|
-
}) {
|
|
32
|
-
return {
|
|
33
|
-
create: ((context) => {
|
|
34
|
-
const optionsWithDefault = context.options.map((options, index) => {
|
|
35
|
-
return defu(defaultOptions?.[index] ?? {}, options);
|
|
36
|
-
});
|
|
37
|
-
return create(context, optionsWithDefault);
|
|
38
|
-
}),
|
|
39
|
-
defaultOptions: defaultOptions ?? {},
|
|
40
|
-
meta
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
var createRule = RuleCreator(
|
|
44
|
-
(ruleName) => `https://docs.stormsoftware.com/projects/storm-ops/eslint-plugin-bun/rules/${ruleName}.md`
|
|
45
|
-
);
|
|
46
|
-
function isWorkspaceObject(workspaces) {
|
|
47
|
-
return Boolean(workspaces) && typeof workspaces === "object" && !Array.isArray(workspaces);
|
|
48
|
-
}
|
|
49
|
-
function detectCatalogRoot(data) {
|
|
50
|
-
if (data.catalog || data.catalogs) {
|
|
51
|
-
return "top";
|
|
52
|
-
}
|
|
53
|
-
if (isWorkspaceObject(data.workspaces) && (data.workspaces.catalog || data.workspaces.catalogs)) {
|
|
54
|
-
return "workspaces";
|
|
55
|
-
}
|
|
56
|
-
return isWorkspaceObject(data.workspaces) ? "workspaces" : "top";
|
|
57
|
-
}
|
|
58
|
-
function normalizeCatalogName(name) {
|
|
59
|
-
return name.replace(/\^/g, "h").replace(/~/g, "t").replace(/\./g, "_").replace(/</g, "l").replace(/>/g, "g").replace(/=/g, "e").replace(/\*/g, "s").replace(/@/g, "a").replace(/\|/g, "p").replace(/&/g, "n").replace(/-/g, "m").replace(/\+/g, "u").replace(/\s/g, "_");
|
|
60
|
-
}
|
|
61
|
-
function sortObjectKeys(input) {
|
|
62
|
-
return Object.fromEntries(
|
|
63
|
-
Object.entries(input).sort(([left], [right]) => left.localeCompare(right))
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
function isWorkspaceRootPackageJson(data) {
|
|
67
|
-
return Boolean(
|
|
68
|
-
data.workspaces || data.catalog || data.catalogs
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
function readBunWorkspace(cwd = process.cwd()) {
|
|
72
|
-
const filepath = findUpSync("package.json", {
|
|
73
|
-
cwd,
|
|
74
|
-
type: "file"
|
|
75
|
-
});
|
|
76
|
-
if (!filepath) {
|
|
77
|
-
throw new Error("package.json not found");
|
|
78
|
-
}
|
|
79
|
-
let current = filepath;
|
|
80
|
-
let content = fs.readFileSync(current, "utf8");
|
|
81
|
-
let data = JSON.parse(content);
|
|
82
|
-
while (!isWorkspaceRootPackageJson(data)) {
|
|
83
|
-
const searchFrom = dirname(dirname(current));
|
|
84
|
-
const next = findUpSync("package.json", {
|
|
85
|
-
cwd: searchFrom,
|
|
86
|
-
type: "file"
|
|
87
|
-
});
|
|
88
|
-
if (!next || normalize(next) === normalize(current)) {
|
|
89
|
-
break;
|
|
90
|
-
}
|
|
91
|
-
current = next;
|
|
92
|
-
content = fs.readFileSync(current, "utf8");
|
|
93
|
-
data = JSON.parse(content);
|
|
94
|
-
}
|
|
95
|
-
if (!isWorkspaceRootPackageJson(data)) {
|
|
96
|
-
throw new Error(
|
|
97
|
-
"Bun workspace root package.json with workspaces/catalog not found"
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
let hasChanged = false;
|
|
101
|
-
let queueTimer;
|
|
102
|
-
const queue = [];
|
|
103
|
-
let catalogRoot = detectCatalogRoot(data);
|
|
104
|
-
const write = () => {
|
|
105
|
-
if (!fs.existsSync(current)) {
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
fs.writeFileSync(current, `${JSON.stringify(data, null, 2)}
|
|
109
|
-
`);
|
|
110
|
-
};
|
|
111
|
-
const clearQueue = () => {
|
|
112
|
-
if (queueTimer != null) {
|
|
113
|
-
clearTimeout(queueTimer);
|
|
114
|
-
queueTimer = void 0;
|
|
115
|
-
}
|
|
116
|
-
queue.length = 0;
|
|
117
|
-
};
|
|
118
|
-
const getCatalogContainer = () => {
|
|
119
|
-
if (catalogRoot === "workspaces") {
|
|
120
|
-
if (!isWorkspaceObject(data.workspaces)) {
|
|
121
|
-
data.workspaces = {
|
|
122
|
-
packages: Array.isArray(data.workspaces) ? data.workspaces : [],
|
|
123
|
-
catalog: {},
|
|
124
|
-
catalogs: {}
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
return data.workspaces;
|
|
128
|
-
}
|
|
129
|
-
return data;
|
|
130
|
-
};
|
|
131
|
-
const getCatalogData = () => {
|
|
132
|
-
const container = getCatalogContainer();
|
|
133
|
-
return {
|
|
134
|
-
catalog: container.catalog,
|
|
135
|
-
catalogs: container.catalogs
|
|
136
|
-
};
|
|
137
|
-
};
|
|
138
|
-
const setPackage = (catalogName, packageName, specifier) => {
|
|
139
|
-
const container = getCatalogContainer();
|
|
140
|
-
if (catalogName === "default") {
|
|
141
|
-
container.catalog ??= {};
|
|
142
|
-
if (container.catalog[packageName] !== specifier) {
|
|
143
|
-
container.catalog[packageName] = specifier;
|
|
144
|
-
container.catalog = sortObjectKeys(container.catalog);
|
|
145
|
-
hasChanged = true;
|
|
146
|
-
}
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
container.catalogs ??= {};
|
|
150
|
-
container.catalogs[catalogName] ??= {};
|
|
151
|
-
if (container.catalogs[catalogName][packageName] !== specifier) {
|
|
152
|
-
container.catalogs[catalogName][packageName] = specifier;
|
|
153
|
-
container.catalogs[catalogName] = sortObjectKeys(
|
|
154
|
-
container.catalogs[catalogName]
|
|
155
|
-
);
|
|
156
|
-
container.catalogs = sortObjectKeys(container.catalogs);
|
|
157
|
-
hasChanged = true;
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
const hasSpecifierConflicts = (catalogName, packageName, specifier) => {
|
|
161
|
-
const { catalog, catalogs } = getCatalogData();
|
|
162
|
-
const existingSpecifier = catalogName === "default" ? catalog?.[packageName] : catalogs?.[catalogName]?.[packageName];
|
|
163
|
-
if (existingSpecifier === specifier) {
|
|
164
|
-
return {
|
|
165
|
-
conflicts: false,
|
|
166
|
-
newCatalogName: catalogName,
|
|
167
|
-
existingSpecifier
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
if (existingSpecifier) {
|
|
171
|
-
const versionSuffix = normalizeCatalogName(specifier);
|
|
172
|
-
const newCatalogName = catalogName === "default" ? `conflicts_${packageName}_${versionSuffix}` : `conflicts_${catalogName}_${versionSuffix}`;
|
|
173
|
-
return {
|
|
174
|
-
conflicts: true,
|
|
175
|
-
existingSpecifier,
|
|
176
|
-
newCatalogName
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
return {
|
|
180
|
-
conflicts: false,
|
|
181
|
-
newCatalogName: catalogName
|
|
182
|
-
};
|
|
183
|
-
};
|
|
184
|
-
const getPackageCatalogs = (packageName) => {
|
|
185
|
-
const found = [];
|
|
186
|
-
const { catalog, catalogs } = getCatalogData();
|
|
187
|
-
if (catalogs) {
|
|
188
|
-
for (const name of Object.keys(catalogs)) {
|
|
189
|
-
if (catalogs[name]?.[packageName]) {
|
|
190
|
-
found.push(name);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
if (catalog?.[packageName]) {
|
|
195
|
-
found.push("default");
|
|
196
|
-
}
|
|
197
|
-
return found;
|
|
198
|
-
};
|
|
199
|
-
const workspace2 = {
|
|
200
|
-
filepath: current,
|
|
201
|
-
setContent(newContent) {
|
|
202
|
-
content = newContent;
|
|
203
|
-
data = JSON.parse(newContent);
|
|
204
|
-
catalogRoot = detectCatalogRoot(data);
|
|
205
|
-
hasChanged = false;
|
|
206
|
-
},
|
|
207
|
-
hasChanged: () => hasChanged,
|
|
208
|
-
hasQueue: () => queueTimer != null,
|
|
209
|
-
clearQueue,
|
|
210
|
-
queueChange(fn, order) {
|
|
211
|
-
if (order === "pre") {
|
|
212
|
-
queue.unshift(fn);
|
|
213
|
-
} else {
|
|
214
|
-
queue.push(fn);
|
|
215
|
-
}
|
|
216
|
-
if (queueTimer != null) {
|
|
217
|
-
clearTimeout(queueTimer);
|
|
218
|
-
}
|
|
219
|
-
queueTimer = setTimeout(() => {
|
|
220
|
-
queueTimer = void 0;
|
|
221
|
-
const clone = [...queue];
|
|
222
|
-
queue.length = 0;
|
|
223
|
-
for (const queued of clone) {
|
|
224
|
-
queued(workspace2);
|
|
225
|
-
}
|
|
226
|
-
if (hasChanged) {
|
|
227
|
-
write();
|
|
228
|
-
}
|
|
229
|
-
}, 1e3);
|
|
230
|
-
},
|
|
231
|
-
toJSON: () => data,
|
|
232
|
-
toString: () => `${JSON.stringify(data, null, 2)}
|
|
233
|
-
`,
|
|
234
|
-
setPackage,
|
|
235
|
-
setPackageNoConflicts(catalogName, packageName, specifier) {
|
|
236
|
-
const { newCatalogName } = hasSpecifierConflicts(
|
|
237
|
-
catalogName,
|
|
238
|
-
packageName,
|
|
239
|
-
specifier
|
|
240
|
-
);
|
|
241
|
-
setPackage(newCatalogName, packageName, specifier);
|
|
242
|
-
},
|
|
243
|
-
getPackageCatalogs,
|
|
244
|
-
hasSpecifierConflicts,
|
|
245
|
-
getCatalogData,
|
|
246
|
-
getWorkspacePackagePatterns() {
|
|
247
|
-
if (Array.isArray(data.workspaces)) {
|
|
248
|
-
return data.workspaces;
|
|
249
|
-
}
|
|
250
|
-
if (isWorkspaceObject(data.workspaces) && data.workspaces.packages) {
|
|
251
|
-
return data.workspaces.packages;
|
|
252
|
-
}
|
|
253
|
-
return [];
|
|
254
|
-
}
|
|
255
|
-
};
|
|
256
|
-
return workspace2;
|
|
257
|
-
}
|
|
258
|
-
var WORKSPACE_CACHE_TIME = 1e4;
|
|
259
|
-
var workspaceLastRead;
|
|
260
|
-
var workspace;
|
|
261
|
-
function getBunWorkspace(cwd = process.cwd()) {
|
|
262
|
-
if (workspaceLastRead && workspace && !workspace.hasQueue() && Date.now() - workspaceLastRead > WORKSPACE_CACHE_TIME) {
|
|
263
|
-
workspace = void 0;
|
|
264
|
-
}
|
|
265
|
-
if (!workspace) {
|
|
266
|
-
workspace = readBunWorkspace(cwd);
|
|
267
|
-
workspaceLastRead = Date.now();
|
|
268
|
-
}
|
|
269
|
-
return workspace;
|
|
270
|
-
}
|
|
271
|
-
function isRootWorkspacePackageJson(filename, cwd = process.cwd()) {
|
|
272
|
-
try {
|
|
273
|
-
const root = getBunWorkspace(cwd);
|
|
274
|
-
return normalize(root.filepath) === normalize(filename);
|
|
275
|
-
} catch {
|
|
276
|
-
return false;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// src/helpers/iterate-dependencies.ts
|
|
281
|
-
function getPackageJsonRootNode(context) {
|
|
282
|
-
if (!context.filename.endsWith("package.json")) {
|
|
283
|
-
return void 0;
|
|
284
|
-
}
|
|
285
|
-
const ast = context.sourceCode.ast;
|
|
286
|
-
const root = ast.body[0];
|
|
287
|
-
if (root?.expression?.type === "JSONObjectExpression") {
|
|
288
|
-
return root.expression;
|
|
289
|
-
}
|
|
290
|
-
return void 0;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// src/rules/json-no-duplicate-catalog-item.ts
|
|
294
|
-
var RULE_NAME = "json-no-duplicate-catalog-item";
|
|
1
|
+
import { createRule } from "../helpers/create-rule.mjs";
|
|
2
|
+
import { getBunWorkspace, isRootWorkspacePackageJson } from "../helpers/bun-workspace.mjs";
|
|
3
|
+
import { getPackageJsonRootNode } from "../helpers/iterate-dependencies.mjs";
|
|
4
|
+
//#region src/rules/json-no-duplicate-catalog-item.ts
|
|
5
|
+
const RULE_NAME = "json-no-duplicate-catalog-item";
|
|
295
6
|
function getProperty(node, key) {
|
|
296
|
-
|
|
297
|
-
(property) => property.key.type === "JSONLiteral" && property.key.value === key
|
|
298
|
-
);
|
|
7
|
+
return node?.properties?.find((property) => property.key.type === "JSONLiteral" && property.key.value === key);
|
|
299
8
|
}
|
|
300
9
|
var json_no_duplicate_catalog_item_default = createRule({
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
for (const { catalogName, property } of catalogEntries) {
|
|
369
|
-
const key = String(property.key.value);
|
|
370
|
-
if (allow.includes(key)) {
|
|
371
|
-
continue;
|
|
372
|
-
}
|
|
373
|
-
if (exists.has(key)) {
|
|
374
|
-
context.report({
|
|
375
|
-
node: property.value,
|
|
376
|
-
messageId: "duplicateCatalogItem",
|
|
377
|
-
data: {
|
|
378
|
-
name: key,
|
|
379
|
-
currentCatalog: catalogName,
|
|
380
|
-
existingCatalog: exists.get(key)
|
|
381
|
-
}
|
|
382
|
-
});
|
|
383
|
-
} else {
|
|
384
|
-
exists.set(key, catalogName);
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
return {};
|
|
388
|
-
}
|
|
10
|
+
name: RULE_NAME,
|
|
11
|
+
meta: {
|
|
12
|
+
type: "problem",
|
|
13
|
+
docs: { description: "Disallow duplicate catalog items in the Bun workspace root `package.json`" },
|
|
14
|
+
schema: [{
|
|
15
|
+
type: "object",
|
|
16
|
+
properties: { allow: {
|
|
17
|
+
type: "array",
|
|
18
|
+
items: { type: "string" }
|
|
19
|
+
} },
|
|
20
|
+
additionalProperties: false
|
|
21
|
+
}],
|
|
22
|
+
messages: { duplicateCatalogItem: "Catalog item \"{{name}}\" is already defined in the \"{{existingCatalog}}\" catalog. You may want to remove one of them." }
|
|
23
|
+
},
|
|
24
|
+
defaultOptions: [{}],
|
|
25
|
+
create(context, [options = {}]) {
|
|
26
|
+
if (!isRootWorkspacePackageJson(context.filename)) return {};
|
|
27
|
+
let workspace;
|
|
28
|
+
try {
|
|
29
|
+
workspace = getBunWorkspace();
|
|
30
|
+
} catch {
|
|
31
|
+
return {};
|
|
32
|
+
}
|
|
33
|
+
if (workspace.hasChanged() || workspace.hasQueue()) return {};
|
|
34
|
+
workspace.setContent(context.sourceCode.text);
|
|
35
|
+
const root = getPackageJsonRootNode(context);
|
|
36
|
+
if (!root) return {};
|
|
37
|
+
const { allow = [] } = options;
|
|
38
|
+
const topCatalog = getProperty(root, "catalog");
|
|
39
|
+
const topCatalogs = getProperty(root, "catalogs");
|
|
40
|
+
const workspaces = getProperty(root, "workspaces");
|
|
41
|
+
const workspacesCatalog = workspaces?.value?.type === "JSONObjectExpression" ? getProperty(workspaces.value, "catalog") : void 0;
|
|
42
|
+
const workspacesCatalogs = workspaces?.value?.type === "JSONObjectExpression" ? getProperty(workspaces.value, "catalogs") : void 0;
|
|
43
|
+
const catalogNode = topCatalog ?? workspacesCatalog;
|
|
44
|
+
const catalogsNode = topCatalogs ?? workspacesCatalogs;
|
|
45
|
+
const exists = /* @__PURE__ */ new Map();
|
|
46
|
+
const catalogEntries = [];
|
|
47
|
+
if (catalogNode?.value?.type === "JSONObjectExpression") for (const property of catalogNode.value.properties || []) catalogEntries.push({
|
|
48
|
+
catalogName: "default",
|
|
49
|
+
property
|
|
50
|
+
});
|
|
51
|
+
if (catalogsNode?.value?.type === "JSONObjectExpression") for (const named of catalogsNode.value.properties || []) {
|
|
52
|
+
if (named.value.type !== "JSONObjectExpression") continue;
|
|
53
|
+
const catalogName = String(named.key.value);
|
|
54
|
+
for (const property of named.value.properties || []) catalogEntries.push({
|
|
55
|
+
catalogName,
|
|
56
|
+
property
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
for (const { catalogName, property } of catalogEntries) {
|
|
60
|
+
const key = String(property.key.value);
|
|
61
|
+
if (allow.includes(key)) continue;
|
|
62
|
+
if (exists.has(key)) context.report({
|
|
63
|
+
node: property.value,
|
|
64
|
+
messageId: "duplicateCatalogItem",
|
|
65
|
+
data: {
|
|
66
|
+
name: key,
|
|
67
|
+
currentCatalog: catalogName,
|
|
68
|
+
existingCatalog: exists.get(key)
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
else exists.set(key, catalogName);
|
|
72
|
+
}
|
|
73
|
+
return {};
|
|
74
|
+
}
|
|
389
75
|
});
|
|
390
|
-
|
|
76
|
+
//#endregion
|
|
391
77
|
export { json_no_duplicate_catalog_item_default as default };
|