@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 CHANGED
@@ -23,10 +23,14 @@ var external = [
23
23
  "vue-router/**",
24
24
  "@vueuse/**",
25
25
  "vue-**",
26
- "nuxt",
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 };
@@ -21,7 +21,7 @@ function definePlugin(plugin) {
21
21
  function defineRule(rule) {
22
22
  return rule;
23
23
  }
24
- const FILE_CONTEXT = Object.freeze({
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/rules/merge-duplicates.ts
217
- const mergeDuplicatesRule = 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"));
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
- //#endregion
251
- //#region src/plugins/import/rules/no-inline-type-import.ts
252
- const noInlineTypeImportRule = defineRule({
253
- meta: {
254
- type: "layout",
255
- fixable: "code",
256
- docs: {
257
- description: "Prefer top-level `import type` over inline `type` specifiers, including mixed value/type imports.",
258
- url: "https://github.com/soybeanjs/oxc-config/blob/main/src/plugins/import/rules/no-inline-type-import.ts"
259
- },
260
- messages: { noInlineTypeImport: "Inline type import from \"{{source}}\" should use top-level `import type` declarations instead." },
261
- schema: []
262
- },
263
- create(context) {
264
- const sourceCode = context.sourceCode;
265
- return { ImportDeclaration(node) {
266
- if (!canFixInlineTypeImport(node)) return;
267
- context.report({
268
- node,
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
- return fixer.replaceTextRange([getStartWithComments(firstNode, sourceCode), lastNode.range[1]], parts.join(""));
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.5",
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
- "lint-staged": "^17.4.1",
54
- "oxfmt": "^0.66.0",
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": "tsdown",
57
+ "build": "vp pack",
70
58
  "commit": "soy git-commit",
71
- "fmt": "oxfmt",
72
- "lint": "oxlint --fix",
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",