@soybeanjs/oxc-config 0.2.5 → 0.2.6
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/dist/index.js +6 -5
- package/dist/plugins/import.js +119 -135
- package/package.json +6 -18
package/dist/index.js
CHANGED
|
@@ -23,10 +23,14 @@ var external = [
|
|
|
23
23
|
"vue-router/**",
|
|
24
24
|
"@vueuse/**",
|
|
25
25
|
"vue-**",
|
|
26
|
-
"
|
|
26
|
+
"ubean",
|
|
27
|
+
"ubean/**",
|
|
28
|
+
"@ubean/**",
|
|
27
29
|
"hono",
|
|
28
30
|
"@hono/**",
|
|
29
31
|
"hono-**",
|
|
32
|
+
"nuxt",
|
|
33
|
+
"nuxt/**",
|
|
30
34
|
"@nuxt/**",
|
|
31
35
|
"nuxt-**",
|
|
32
36
|
"react",
|
|
@@ -125,7 +129,6 @@ var internal = [
|
|
|
125
129
|
"@/types",
|
|
126
130
|
"@/types/**"
|
|
127
131
|
];
|
|
128
|
-
|
|
129
132
|
//#endregion
|
|
130
133
|
//#region src/oxfmt.ts
|
|
131
134
|
const customGroups = [];
|
|
@@ -203,7 +206,6 @@ const fmt = {
|
|
|
203
206
|
]
|
|
204
207
|
}
|
|
205
208
|
};
|
|
206
|
-
|
|
207
209
|
//#endregion
|
|
208
210
|
//#region src/oxlint.ts
|
|
209
211
|
const lint = {
|
|
@@ -248,6 +250,5 @@ const lint = {
|
|
|
248
250
|
"vue/prefer-import-from-vue": "off"
|
|
249
251
|
}
|
|
250
252
|
};
|
|
251
|
-
|
|
252
253
|
//#endregion
|
|
253
|
-
export { fmt, lint };
|
|
254
|
+
export { fmt, lint };
|
package/dist/plugins/import.js
CHANGED
|
@@ -21,7 +21,7 @@ function definePlugin(plugin) {
|
|
|
21
21
|
function defineRule(rule) {
|
|
22
22
|
return rule;
|
|
23
23
|
}
|
|
24
|
-
|
|
24
|
+
Object.freeze({
|
|
25
25
|
get filename() {
|
|
26
26
|
throw Error("Cannot access `context.filename` in `createOnce`");
|
|
27
27
|
},
|
|
@@ -62,7 +62,6 @@ const FILE_CONTEXT = Object.freeze({
|
|
|
62
62
|
throw Error("Cannot access `context.parserPath` in `createOnce`");
|
|
63
63
|
}
|
|
64
64
|
});
|
|
65
|
-
|
|
66
65
|
//#endregion
|
|
67
66
|
//#region src/plugins/import/shared/imports.ts
|
|
68
67
|
function isTypeKind(kind) {
|
|
@@ -203,7 +202,6 @@ function collectImportGroups(sourceCode) {
|
|
|
203
202
|
groups.push(currentGroup);
|
|
204
203
|
return groups;
|
|
205
204
|
}
|
|
206
|
-
|
|
207
205
|
//#endregion
|
|
208
206
|
//#region src/plugins/import/shared/source.ts
|
|
209
207
|
function getStartWithComments(node, sourceCode) {
|
|
@@ -211,144 +209,130 @@ function getStartWithComments(node, sourceCode) {
|
|
|
211
209
|
if (comments.length > 0) return comments[0].range[0];
|
|
212
210
|
return node.range[0];
|
|
213
211
|
}
|
|
214
|
-
|
|
215
212
|
//#endregion
|
|
216
|
-
//#region src/plugins/import/
|
|
217
|
-
const
|
|
218
|
-
meta: {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
213
|
+
//#region src/plugins/import/index.ts
|
|
214
|
+
const plugin = definePlugin({
|
|
215
|
+
meta: { name: "soybeanjs-import" },
|
|
216
|
+
rules: {
|
|
217
|
+
"merge-duplicates": defineRule({
|
|
218
|
+
meta: {
|
|
219
|
+
type: "layout",
|
|
220
|
+
fixable: "code",
|
|
221
|
+
docs: {
|
|
222
|
+
description: "Merge duplicate imports from the same module into at most one value import and one type import.",
|
|
223
|
+
url: "https://github.com/soybeanjs/oxc-config/blob/main/src/plugins/import/rules/merge-duplicates.ts"
|
|
224
|
+
},
|
|
225
|
+
messages: { mergeDuplicates: "Imports from \"{{source}}\" should be merged into at most one value import and one type import." },
|
|
226
|
+
schema: []
|
|
227
|
+
},
|
|
228
|
+
create(context) {
|
|
229
|
+
const sourceCode = context.sourceCode;
|
|
230
|
+
return { "Program:exit"() {
|
|
231
|
+
const groups = collectImportGroups(sourceCode);
|
|
232
|
+
for (const group of groups) {
|
|
233
|
+
const mergedStatements = mergeImportGroup(group, sourceCode);
|
|
234
|
+
if (!mergedStatements) continue;
|
|
235
|
+
const firstNode = group[0].node;
|
|
236
|
+
const lastNode = group[group.length - 1].node;
|
|
237
|
+
context.report({
|
|
238
|
+
node: firstNode,
|
|
239
|
+
messageId: "mergeDuplicates",
|
|
240
|
+
data: { source: firstNode.source.value },
|
|
241
|
+
fix(fixer) {
|
|
242
|
+
return fixer.replaceTextRange([getStartWithComments(firstNode, sourceCode), lastNode.range[1]], mergedStatements.join("\n"));
|
|
243
|
+
}
|
|
244
|
+
});
|
|
243
245
|
}
|
|
244
|
-
}
|
|
246
|
+
} };
|
|
245
247
|
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
messageId: "noInlineTypeImport",
|
|
270
|
-
data: { source: node.source.value },
|
|
271
|
-
fix(fixer) {
|
|
272
|
-
const parts = [buildValueImportStatement(node, sourceCode), buildTypeImportStatement(node, sourceCode)].filter(Boolean);
|
|
273
|
-
return fixer.replaceText(node, parts.join("\n"));
|
|
274
|
-
}
|
|
275
|
-
});
|
|
276
|
-
} };
|
|
277
|
-
}
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
//#endregion
|
|
281
|
-
//#region src/plugins/import/rules/type-after-value.ts
|
|
282
|
-
const typeAfterValueRule = defineRule({
|
|
283
|
-
meta: {
|
|
284
|
-
type: "layout",
|
|
285
|
-
fixable: "code",
|
|
286
|
-
docs: {
|
|
287
|
-
description: "Ensure value imports come before type imports for the same module source.",
|
|
288
|
-
url: "https://github.com/soybeanjs/oxc-config/blob/main/src/plugins/import/rules/type-after-value.ts"
|
|
289
|
-
},
|
|
290
|
-
messages: { typeBeforeValue: "Type import from \"{{source}}\" must be placed after its value import." },
|
|
291
|
-
schema: []
|
|
292
|
-
},
|
|
293
|
-
create(context) {
|
|
294
|
-
const sourceCode = context.sourceCode;
|
|
295
|
-
return { "Program:exit"() {
|
|
296
|
-
const groups = collectImportGroups(sourceCode);
|
|
297
|
-
for (const group of groups) {
|
|
298
|
-
if (group.length < 2) continue;
|
|
299
|
-
if (!group.some((item, index) => {
|
|
300
|
-
if (!isTypeImport(item.node)) return false;
|
|
301
|
-
for (let cursor = index + 1; cursor < group.length; cursor += 1) if (!isTypeImport(group[cursor].node)) return true;
|
|
302
|
-
return false;
|
|
303
|
-
})) continue;
|
|
304
|
-
const sorted = [...group].sort((left, right) => {
|
|
305
|
-
const leftIsType = isTypeImport(left.node);
|
|
306
|
-
if (leftIsType === isTypeImport(right.node)) return 0;
|
|
307
|
-
return leftIsType ? 1 : -1;
|
|
308
|
-
});
|
|
309
|
-
const firstNode = group[0].node;
|
|
310
|
-
const lastNode = group[group.length - 1].node;
|
|
311
|
-
context.report({
|
|
312
|
-
node: firstNode,
|
|
313
|
-
messageId: "typeBeforeValue",
|
|
314
|
-
data: { source: firstNode.source.value },
|
|
315
|
-
fix(fixer) {
|
|
316
|
-
const parts = [];
|
|
317
|
-
for (let index = 0; index < sorted.length; index += 1) {
|
|
318
|
-
const item = sorted[index];
|
|
319
|
-
const previousItem = index > 0 ? sorted[index - 1] : null;
|
|
320
|
-
const start = getStartWithComments(item.node, sourceCode);
|
|
321
|
-
const text = sourceCode.text.slice(start, item.node.range[1]);
|
|
322
|
-
if (index > 0 && previousItem) {
|
|
323
|
-
const previousOriginalIndex = group.findIndex((candidate) => candidate === previousItem);
|
|
324
|
-
const currentOriginalIndex = group.findIndex((candidate) => candidate === item);
|
|
325
|
-
if (Math.abs(currentOriginalIndex - previousOriginalIndex) === 1) {
|
|
326
|
-
const earlier = group[Math.min(previousOriginalIndex, currentOriginalIndex)];
|
|
327
|
-
const later = group[Math.max(previousOriginalIndex, currentOriginalIndex)];
|
|
328
|
-
const separator = sourceCode.text.slice(earlier.node.range[1], getStartWithComments(later.node, sourceCode));
|
|
329
|
-
parts.push(separator);
|
|
330
|
-
} else parts.push("\n");
|
|
331
|
-
}
|
|
332
|
-
parts.push(text);
|
|
248
|
+
}),
|
|
249
|
+
"no-inline-type-import": defineRule({
|
|
250
|
+
meta: {
|
|
251
|
+
type: "layout",
|
|
252
|
+
fixable: "code",
|
|
253
|
+
docs: {
|
|
254
|
+
description: "Prefer top-level `import type` over inline `type` specifiers, including mixed value/type imports.",
|
|
255
|
+
url: "https://github.com/soybeanjs/oxc-config/blob/main/src/plugins/import/rules/no-inline-type-import.ts"
|
|
256
|
+
},
|
|
257
|
+
messages: { noInlineTypeImport: "Inline type import from \"{{source}}\" should use top-level `import type` declarations instead." },
|
|
258
|
+
schema: []
|
|
259
|
+
},
|
|
260
|
+
create(context) {
|
|
261
|
+
const sourceCode = context.sourceCode;
|
|
262
|
+
return { ImportDeclaration(node) {
|
|
263
|
+
if (!canFixInlineTypeImport(node)) return;
|
|
264
|
+
context.report({
|
|
265
|
+
node,
|
|
266
|
+
messageId: "noInlineTypeImport",
|
|
267
|
+
data: { source: node.source.value },
|
|
268
|
+
fix(fixer) {
|
|
269
|
+
const parts = [buildValueImportStatement(node, sourceCode), buildTypeImportStatement(node, sourceCode)].filter(Boolean);
|
|
270
|
+
return fixer.replaceText(node, parts.join("\n"));
|
|
333
271
|
}
|
|
334
|
-
|
|
272
|
+
});
|
|
273
|
+
} };
|
|
274
|
+
}
|
|
275
|
+
}),
|
|
276
|
+
"type-after-value": defineRule({
|
|
277
|
+
meta: {
|
|
278
|
+
type: "layout",
|
|
279
|
+
fixable: "code",
|
|
280
|
+
docs: {
|
|
281
|
+
description: "Ensure value imports come before type imports for the same module source.",
|
|
282
|
+
url: "https://github.com/soybeanjs/oxc-config/blob/main/src/plugins/import/rules/type-after-value.ts"
|
|
283
|
+
},
|
|
284
|
+
messages: { typeBeforeValue: "Type import from \"{{source}}\" must be placed after its value import." },
|
|
285
|
+
schema: []
|
|
286
|
+
},
|
|
287
|
+
create(context) {
|
|
288
|
+
const sourceCode = context.sourceCode;
|
|
289
|
+
return { "Program:exit"() {
|
|
290
|
+
const groups = collectImportGroups(sourceCode);
|
|
291
|
+
for (const group of groups) {
|
|
292
|
+
if (group.length < 2) continue;
|
|
293
|
+
if (!group.some((item, index) => {
|
|
294
|
+
if (!isTypeImport(item.node)) return false;
|
|
295
|
+
for (let cursor = index + 1; cursor < group.length; cursor += 1) if (!isTypeImport(group[cursor].node)) return true;
|
|
296
|
+
return false;
|
|
297
|
+
})) continue;
|
|
298
|
+
const sorted = [...group].sort((left, right) => {
|
|
299
|
+
const leftIsType = isTypeImport(left.node);
|
|
300
|
+
if (leftIsType === isTypeImport(right.node)) return 0;
|
|
301
|
+
return leftIsType ? 1 : -1;
|
|
302
|
+
});
|
|
303
|
+
const firstNode = group[0].node;
|
|
304
|
+
const lastNode = group[group.length - 1].node;
|
|
305
|
+
context.report({
|
|
306
|
+
node: firstNode,
|
|
307
|
+
messageId: "typeBeforeValue",
|
|
308
|
+
data: { source: firstNode.source.value },
|
|
309
|
+
fix(fixer) {
|
|
310
|
+
const parts = [];
|
|
311
|
+
for (let index = 0; index < sorted.length; index += 1) {
|
|
312
|
+
const item = sorted[index];
|
|
313
|
+
const previousItem = index > 0 ? sorted[index - 1] : null;
|
|
314
|
+
const start = getStartWithComments(item.node, sourceCode);
|
|
315
|
+
const text = sourceCode.text.slice(start, item.node.range[1]);
|
|
316
|
+
if (index > 0 && previousItem) {
|
|
317
|
+
const previousOriginalIndex = group.findIndex((candidate) => candidate === previousItem);
|
|
318
|
+
const currentOriginalIndex = group.findIndex((candidate) => candidate === item);
|
|
319
|
+
if (Math.abs(currentOriginalIndex - previousOriginalIndex) === 1) {
|
|
320
|
+
const earlier = group[Math.min(previousOriginalIndex, currentOriginalIndex)];
|
|
321
|
+
const later = group[Math.max(previousOriginalIndex, currentOriginalIndex)];
|
|
322
|
+
const separator = sourceCode.text.slice(earlier.node.range[1], getStartWithComments(later.node, sourceCode));
|
|
323
|
+
parts.push(separator);
|
|
324
|
+
} else parts.push("\n");
|
|
325
|
+
}
|
|
326
|
+
parts.push(text);
|
|
327
|
+
}
|
|
328
|
+
return fixer.replaceTextRange([getStartWithComments(firstNode, sourceCode), lastNode.range[1]], parts.join(""));
|
|
329
|
+
}
|
|
330
|
+
});
|
|
335
331
|
}
|
|
336
|
-
}
|
|
332
|
+
} };
|
|
337
333
|
}
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
//#endregion
|
|
343
|
-
//#region src/plugins/import/index.ts
|
|
344
|
-
const plugin = definePlugin({
|
|
345
|
-
meta: { name: "soybeanjs-import" },
|
|
346
|
-
rules: {
|
|
347
|
-
"merge-duplicates": mergeDuplicatesRule,
|
|
348
|
-
"no-inline-type-import": noInlineTypeImportRule,
|
|
349
|
-
"type-after-value": typeAfterValueRule
|
|
334
|
+
})
|
|
350
335
|
}
|
|
351
336
|
});
|
|
352
|
-
|
|
353
337
|
//#endregion
|
|
354
|
-
export { plugin as default };
|
|
338
|
+
export { plugin as default };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soybeanjs/oxc-config",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"description": "SoybeanJS's oxlint and oxfmt config",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eslint",
|
|
@@ -50,26 +50,14 @@
|
|
|
50
50
|
"@soybeanjs/cli": "^1.8.2",
|
|
51
51
|
"@soybeanjs/oxc-config": "link:dist",
|
|
52
52
|
"@types/node": "^26.4.1",
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"oxlint": "^1.81.0",
|
|
56
|
-
"simple-git-hooks": "^2.14.0",
|
|
57
|
-
"tsdown": "^0.22.14",
|
|
58
|
-
"tsx": "^4.23.13",
|
|
59
|
-
"typescript": "^7.0.2"
|
|
60
|
-
},
|
|
61
|
-
"simple-git-hooks": {
|
|
62
|
-
"commit-msg": "pnpm soy git-commit-verify",
|
|
63
|
-
"pre-commit": "pnpm typecheck && pnpm lint-staged"
|
|
64
|
-
},
|
|
65
|
-
"lint-staged": {
|
|
66
|
-
"*": "oxlint . --fix && oxfmt"
|
|
53
|
+
"typescript": "^7.0.2",
|
|
54
|
+
"vite-plus": "latest"
|
|
67
55
|
},
|
|
68
56
|
"scripts": {
|
|
69
|
-
"build": "
|
|
57
|
+
"build": "vp pack",
|
|
70
58
|
"commit": "soy git-commit",
|
|
71
|
-
"fmt": "
|
|
72
|
-
"lint": "
|
|
59
|
+
"fmt": "vp fmt",
|
|
60
|
+
"lint": "vp lint . --fix",
|
|
73
61
|
"publish-pkg": "pnpm publish --access public",
|
|
74
62
|
"release": "soy release",
|
|
75
63
|
"typecheck": "tsc --noEmit --skipLibCheck",
|