@ilyasemenov/oxc-config 0.1.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/LICENSE +21 -0
- package/README.md +146 -0
- package/dist/eslint.d.mts +14 -0
- package/dist/eslint.mjs +52 -0
- package/dist/oxfmt.d.mts +7 -0
- package/dist/oxfmt.mjs +6 -0
- package/dist/oxlint.d.mts +31 -0
- package/dist/oxlint.mjs +48 -0
- package/package.json +89 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ilya Semenov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# @ilyasemenov/oxc-config
|
|
2
|
+
|
|
3
|
+
Reusable oxlint, oxfmt, and ESLint configurations for projects I develop or oversee.
|
|
4
|
+
|
|
5
|
+
Code is linted with oxlint and formatted with oxfmt.
|
|
6
|
+
For Vue, ESLint additionally lints and formats templates, which oxlint cannot do.
|
|
7
|
+
Both HTML and Pug templates are supported.
|
|
8
|
+
|
|
9
|
+
## Setup
|
|
10
|
+
|
|
11
|
+
Install:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npm install -D @ilyasemenov/oxc-config oxlint oxfmt
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Create `oxlint.config.ts`:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import base from "@ilyasemenov/oxc-config/oxlint"
|
|
21
|
+
import { defineConfig } from "oxlint"
|
|
22
|
+
|
|
23
|
+
export default defineConfig({
|
|
24
|
+
extends: [base],
|
|
25
|
+
})
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Create `oxfmt.config.ts`:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
export { default } from "@ilyasemenov/oxc-config/oxfmt"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
To change formatting options, spread the config into your own: `defineConfig({ ...base, printWidth: 100 })`.
|
|
35
|
+
|
|
36
|
+
### Vue
|
|
37
|
+
|
|
38
|
+
For Vue projects, also install:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
npm install -D eslint @antfu/eslint-config typescript-eslint typescript@~6.0.3
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
typescript-eslint doesn't support TypeScript 6.1+ yet.
|
|
45
|
+
|
|
46
|
+
Create `eslint.config.js`:
|
|
47
|
+
|
|
48
|
+
```js
|
|
49
|
+
// @ts-check
|
|
50
|
+
|
|
51
|
+
import config from "@ilyasemenov/oxc-config/eslint"
|
|
52
|
+
|
|
53
|
+
export default await config()
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
It uses the Vue rules from [@antfu/eslint-config](https://github.com/antfu/eslint-config) with my preferences on top, and only checks `.vue` files.
|
|
57
|
+
The oxlint config leaves unused variables in Vue files to ESLint, so use both.
|
|
58
|
+
|
|
59
|
+
To add your own Vue rules:
|
|
60
|
+
|
|
61
|
+
```js
|
|
62
|
+
// @ts-check
|
|
63
|
+
|
|
64
|
+
import config from "@ilyasemenov/oxc-config/eslint"
|
|
65
|
+
import { defineConfig } from "eslint/config"
|
|
66
|
+
|
|
67
|
+
export default defineConfig({
|
|
68
|
+
files: ["**/*.vue"],
|
|
69
|
+
extends: [await config()],
|
|
70
|
+
rules: {
|
|
71
|
+
"vue/require-v-for-key": "error",
|
|
72
|
+
},
|
|
73
|
+
})
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Pug
|
|
77
|
+
|
|
78
|
+
For Pug templates, install:
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
npm install -D eslint-plugin-vue-pug@flat
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
As of 29.09.2026, the `latest` tag still points to the older 0.x version.
|
|
85
|
+
Then enable Pug in `eslint.config.js`:
|
|
86
|
+
|
|
87
|
+
```js
|
|
88
|
+
// @ts-check
|
|
89
|
+
|
|
90
|
+
import config from "@ilyasemenov/oxc-config/eslint"
|
|
91
|
+
|
|
92
|
+
export default await config({ vuePug: true })
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The Pug preset disables some HTML checks for all Vue files, so enable it only in Pug projects.
|
|
96
|
+
|
|
97
|
+
## A few preferences
|
|
98
|
+
|
|
99
|
+
- No semicolons.
|
|
100
|
+
- Imports grouped as side effects, builtins, external, internal, parent, sibling/index, and unknown, with a blank line between groups.
|
|
101
|
+
- `bun:*` imports grouped with `node:*`; `~`, `#`, and `@/` treated as internal prefixes.
|
|
102
|
+
- Package subpaths kept together: `#app/…` before `#app-server/…`.
|
|
103
|
+
- Named imports sorted by their original name, ignoring aliases; type imports before value imports from the same module.
|
|
104
|
+
- Vue component names in kebab-case, for both HTML and Pug.
|
|
105
|
+
- No empty component blocks; `v-for` without a key and string concatenation are allowed.
|
|
106
|
+
|
|
107
|
+
Imports are sorted by Perfectionist in oxlint: oxfmt's `sortImports` doesn't support this order, so keep it disabled.
|
|
108
|
+
|
|
109
|
+
## Running it
|
|
110
|
+
|
|
111
|
+
```sh
|
|
112
|
+
npx oxlint --fix
|
|
113
|
+
npx eslint --fix '**/*.vue'
|
|
114
|
+
npx oxfmt
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Leave out the ESLint command in projects without Vue.
|
|
118
|
+
If you also use Stylelint, run it before oxfmt.
|
|
119
|
+
|
|
120
|
+
If an import needs both moving and sorting inside `{ … }`, oxlint needs a second `--fix` run ([oxc#16118](https://github.com/oxc-project/oxc/issues/16118)).
|
|
121
|
+
|
|
122
|
+
### Monorepos
|
|
123
|
+
|
|
124
|
+
Running oxlint from the root also loads configs of nested packages.
|
|
125
|
+
If they resolve different copies of the same JS plugin, oxlint fails with `Plugin name 'perfectionist' is already registered`.
|
|
126
|
+
If the root config covers all packages, pass it explicitly to skip nested configs:
|
|
127
|
+
|
|
128
|
+
```sh
|
|
129
|
+
npx oxlint --config oxlint.config.ts --fix
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Otherwise, run oxlint from each package directory.
|
|
133
|
+
|
|
134
|
+
## Development
|
|
135
|
+
|
|
136
|
+
```sh
|
|
137
|
+
mise trust
|
|
138
|
+
mise install
|
|
139
|
+
bun install
|
|
140
|
+
bun test
|
|
141
|
+
bun run types
|
|
142
|
+
bun run build
|
|
143
|
+
bun run lint
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Tests run directly against the source configs; there's no need to build first.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Linter } from "eslint";
|
|
2
|
+
//#region src/eslint.d.ts
|
|
3
|
+
/** Options for the Vue SFC configuration. */
|
|
4
|
+
export interface EslintOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Enable Pug templates with the optional eslint-plugin-vue-pug peer.
|
|
7
|
+
* This disables incompatible HTML rules for all Vue files in the config.
|
|
8
|
+
* @default false
|
|
9
|
+
*/
|
|
10
|
+
vuePug?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/** Create Vue SFC rules complementing oxlint, with optional Pug support. */
|
|
13
|
+
export default function createConfig({ vuePug }?: EslintOptions): Promise<Linter.Config[]>;
|
|
14
|
+
//#endregion
|
package/dist/eslint.mjs
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { vue } from "@antfu/eslint-config";
|
|
2
|
+
import { defineConfig } from "eslint/config";
|
|
3
|
+
import tseslint from "typescript-eslint";
|
|
4
|
+
//#region src/eslint.ts
|
|
5
|
+
/** Create Vue SFC rules complementing oxlint, with optional Pug support. */
|
|
6
|
+
async function createConfig({ vuePug = false } = {}) {
|
|
7
|
+
const configs = defineConfig(await vue({
|
|
8
|
+
typescript: true,
|
|
9
|
+
sfcBlocks: false,
|
|
10
|
+
stylistic: { braceStyle: "1tbs" }
|
|
11
|
+
}), {
|
|
12
|
+
name: "ilyasemenov/vue/oxlint",
|
|
13
|
+
plugins: { "@typescript-eslint": tseslint.plugin },
|
|
14
|
+
rules: { "@typescript-eslint/no-unused-vars": ["error", { ignoreRestSiblings: true }] }
|
|
15
|
+
}, {
|
|
16
|
+
name: "ilyasemenov/vue/rules",
|
|
17
|
+
rules: {
|
|
18
|
+
"vue/component-name-in-template-casing": [
|
|
19
|
+
"error",
|
|
20
|
+
"kebab-case",
|
|
21
|
+
{ registeredComponentsOnly: false }
|
|
22
|
+
],
|
|
23
|
+
"vue/no-empty-component-block": "error",
|
|
24
|
+
"vue/prefer-template": "off",
|
|
25
|
+
"vue/require-v-for-key": "off"
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
if (vuePug) {
|
|
29
|
+
const { default: pluginVuePug } = await import("eslint-plugin-vue-pug");
|
|
30
|
+
configs.push(...defineConfig(pluginVuePug.configs["flat/recommended"], {
|
|
31
|
+
name: "ilyasemenov/vue-pug/compat",
|
|
32
|
+
rules: { "vue/html-quotes": [
|
|
33
|
+
"error",
|
|
34
|
+
"double",
|
|
35
|
+
{ avoidEscape: true }
|
|
36
|
+
] }
|
|
37
|
+
}, {
|
|
38
|
+
name: "ilyasemenov/vue-pug/rules",
|
|
39
|
+
rules: { "vue-pug/component-name-in-template-casing": [
|
|
40
|
+
"error",
|
|
41
|
+
"kebab-case",
|
|
42
|
+
{ registeredComponentsOnly: false }
|
|
43
|
+
] }
|
|
44
|
+
}));
|
|
45
|
+
}
|
|
46
|
+
return defineConfig({
|
|
47
|
+
files: ["**/*.vue"],
|
|
48
|
+
extends: configs
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
//#endregion
|
|
52
|
+
export { createConfig as default };
|
package/dist/oxfmt.d.mts
ADDED
package/dist/oxfmt.mjs
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//#region src/oxlint.d.ts
|
|
2
|
+
/** Import sorting and Vue script rules, paired with the ESLint entry point. */
|
|
3
|
+
declare const _default: {
|
|
4
|
+
jsPlugins: string[];
|
|
5
|
+
rules: {
|
|
6
|
+
"perfectionist/sort-imports": ["error", {
|
|
7
|
+
type: "custom";
|
|
8
|
+
alphabet: string;
|
|
9
|
+
environment: "bun";
|
|
10
|
+
groups: (string | string[])[];
|
|
11
|
+
internalPattern: string[];
|
|
12
|
+
newlinesBetween: number;
|
|
13
|
+
fallbackSort: {
|
|
14
|
+
type: string;
|
|
15
|
+
};
|
|
16
|
+
}];
|
|
17
|
+
"perfectionist/sort-named-imports": ["error", {
|
|
18
|
+
type: "custom";
|
|
19
|
+
alphabet: string;
|
|
20
|
+
ignoreAlias: boolean;
|
|
21
|
+
}];
|
|
22
|
+
};
|
|
23
|
+
overrides: {
|
|
24
|
+
files: string[];
|
|
25
|
+
rules: {
|
|
26
|
+
"no-unused-vars": "off";
|
|
27
|
+
};
|
|
28
|
+
}[];
|
|
29
|
+
};
|
|
30
|
+
//#endregion
|
|
31
|
+
export { _default as default };
|
package/dist/oxlint.mjs
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Alphabet } from "eslint-plugin-perfectionist/alphabet";
|
|
2
|
+
import { defineConfig } from "oxlint";
|
|
3
|
+
//#region src/oxlint.ts
|
|
4
|
+
const alphabet = Alphabet.generateRecommendedAlphabet().sortByNaturalSort().placeCharacterBefore({
|
|
5
|
+
characterBefore: "/",
|
|
6
|
+
characterAfter: "-"
|
|
7
|
+
}).placeCharacterBefore({
|
|
8
|
+
characterBefore: ".",
|
|
9
|
+
characterAfter: "/"
|
|
10
|
+
}).getCharacters().replaceAll(/[\uD800-\uDFFF]/g, "");
|
|
11
|
+
/** Import sorting and Vue script rules, paired with the ESLint entry point. */
|
|
12
|
+
var oxlint_default = defineConfig({
|
|
13
|
+
jsPlugins: [import.meta.resolve("eslint-plugin-perfectionist")],
|
|
14
|
+
rules: {
|
|
15
|
+
"perfectionist/sort-imports": ["error", {
|
|
16
|
+
type: "custom",
|
|
17
|
+
alphabet,
|
|
18
|
+
environment: "bun",
|
|
19
|
+
groups: [
|
|
20
|
+
"side-effect",
|
|
21
|
+
"builtin",
|
|
22
|
+
"external",
|
|
23
|
+
"internal",
|
|
24
|
+
"parent",
|
|
25
|
+
["sibling", "index"],
|
|
26
|
+
"unknown"
|
|
27
|
+
],
|
|
28
|
+
internalPattern: [
|
|
29
|
+
"^~",
|
|
30
|
+
"^#",
|
|
31
|
+
"^@/"
|
|
32
|
+
],
|
|
33
|
+
newlinesBetween: 1,
|
|
34
|
+
fallbackSort: { type: "type-import-first" }
|
|
35
|
+
}],
|
|
36
|
+
"perfectionist/sort-named-imports": ["error", {
|
|
37
|
+
type: "custom",
|
|
38
|
+
alphabet,
|
|
39
|
+
ignoreAlias: true
|
|
40
|
+
}]
|
|
41
|
+
},
|
|
42
|
+
overrides: [{
|
|
43
|
+
files: ["**/*.vue"],
|
|
44
|
+
rules: { "no-unused-vars": "off" }
|
|
45
|
+
}]
|
|
46
|
+
});
|
|
47
|
+
//#endregion
|
|
48
|
+
export { oxlint_default as default };
|
package/package.json
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ilyasemenov/oxc-config",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Shared oxlint, oxfmt, and ESLint configurations for JavaScript, TypeScript, and Vue",
|
|
5
|
+
"homepage": "https://github.com/IlyaSemenov/oxc-config#readme",
|
|
6
|
+
"bugs": "https://github.com/IlyaSemenov/oxc-config/issues",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"author": "Ilya Semenov",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/IlyaSemenov/oxc-config.git"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"README.md",
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"type": "module",
|
|
18
|
+
"exports": {
|
|
19
|
+
"./oxlint": {
|
|
20
|
+
"types": "./dist/oxlint.d.mts",
|
|
21
|
+
"import": "./dist/oxlint.mjs"
|
|
22
|
+
},
|
|
23
|
+
"./oxfmt": {
|
|
24
|
+
"types": "./dist/oxfmt.d.mts",
|
|
25
|
+
"import": "./dist/oxfmt.mjs"
|
|
26
|
+
},
|
|
27
|
+
"./eslint": {
|
|
28
|
+
"types": "./dist/eslint.d.mts",
|
|
29
|
+
"import": "./dist/eslint.mjs"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public",
|
|
34
|
+
"provenance": true
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsdown",
|
|
38
|
+
"prepare": "lefthook install",
|
|
39
|
+
"types": "tsc && tsc -p tests/tsconfig.json",
|
|
40
|
+
"test": "bun test",
|
|
41
|
+
"prepublishOnly": "bun run build",
|
|
42
|
+
"lint": "oxlint --fix && oxfmt",
|
|
43
|
+
"lint:check": "oxlint && oxfmt --check"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"eslint-plugin-perfectionist": "^5.12.1"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@antfu/eslint-config": "^9.5.1",
|
|
50
|
+
"@arethetypeswrong/core": "^0.18.5",
|
|
51
|
+
"@changesets/cli": "^3.0.3",
|
|
52
|
+
"@tsconfig/bun": "^1.0.11",
|
|
53
|
+
"@types/bun": "^1.4.2",
|
|
54
|
+
"eslint": "^10.11.0",
|
|
55
|
+
"eslint-plugin-vue-pug": "^1.0.0-alpha.6",
|
|
56
|
+
"oxfmt": "^0.71.0",
|
|
57
|
+
"oxlint": "^1.86.0",
|
|
58
|
+
"publint": "^0.3.24",
|
|
59
|
+
"tsdown": "^0.23.0",
|
|
60
|
+
"typescript": "~6.0.3",
|
|
61
|
+
"typescript-eslint": "^8.71.0"
|
|
62
|
+
},
|
|
63
|
+
"peerDependencies": {
|
|
64
|
+
"@antfu/eslint-config": "^9.5.1",
|
|
65
|
+
"eslint": "^10.11.0",
|
|
66
|
+
"eslint-plugin-vue-pug": "^1.0.0-alpha.6",
|
|
67
|
+
"oxfmt": "^0.71.0",
|
|
68
|
+
"oxlint": "^1.86.0",
|
|
69
|
+
"typescript-eslint": "^8.71.0"
|
|
70
|
+
},
|
|
71
|
+
"peerDependenciesMeta": {
|
|
72
|
+
"eslint": {
|
|
73
|
+
"optional": true
|
|
74
|
+
},
|
|
75
|
+
"@antfu/eslint-config": {
|
|
76
|
+
"optional": true
|
|
77
|
+
},
|
|
78
|
+
"eslint-plugin-vue-pug": {
|
|
79
|
+
"optional": true
|
|
80
|
+
},
|
|
81
|
+
"typescript-eslint": {
|
|
82
|
+
"optional": true
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"engines": {
|
|
86
|
+
"node": "^20.19.0 || ^22.18.0 || >=24.0.0"
|
|
87
|
+
},
|
|
88
|
+
"packageManager": "bun@1.4.2"
|
|
89
|
+
}
|