@wistia/oxlint-config 0.7.5 → 0.8.1
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 +32 -2
- package/package.json +11 -11
- package/rules/base.mjs +26 -0
- package/rules/import.mjs +8 -24
- package/rules/react-a11y.mjs +51 -0
- package/rules/react.mjs +12 -2
- package/rules/vitest.mjs +5 -0
package/README.md
CHANGED
|
@@ -12,9 +12,23 @@ Wistia's Oxlint configurations. This is a shared config package for [oxlint](htt
|
|
|
12
12
|
## How to install
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
|
-
yarn add -D @wistia/oxlint-config oxlint
|
|
15
|
+
yarn add -D @wistia/oxlint-config oxlint eslint
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
> **Why `eslint`?** This package uses oxlint's [jsPlugins](https://oxc.rs/docs/guide/usage/linter/js-plugins) feature to run ESLint plugins (e.g. `eslint-plugin-import-x`, `@eslint-react/eslint-plugin`, `eslint-plugin-testing-library`, etc.) natively inside oxlint. These plugins import utilities from the `eslint` package at runtime, so it must be installed even though you won't run `eslint` directly.
|
|
19
|
+
>
|
|
20
|
+
> **Note:** jsPlugins do not support rules that rely on TypeScript type information. Any type-aware rules in this config use native oxlint rules instead.
|
|
21
|
+
|
|
22
|
+
### Type-aware linting
|
|
23
|
+
|
|
24
|
+
The `typescriptConfig` enables [type-aware linting](https://oxc.rs/docs/guide/usage/linter/type-aware), which requires the `oxlint-tsgolint` package:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
yarn add -D oxlint-tsgolint
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`oxlint-tsgolint` is a separate Go-based tool that builds your TypeScript program using `typescript-go` and runs type-aware rules (e.g. detecting unhandled promises, unsafe assignments). Without it installed, type-aware rules will not run.
|
|
31
|
+
|
|
18
32
|
## Quick start
|
|
19
33
|
|
|
20
34
|
Create an `oxlint.config.ts` in your project root:
|
|
@@ -149,4 +163,20 @@ export default defineConfig({
|
|
|
149
163
|
|
|
150
164
|
## ESLint parity
|
|
151
165
|
|
|
152
|
-
|
|
166
|
+
Differences between `@wistia/oxlint-config` and `@wistia/eslint-config`.
|
|
167
|
+
|
|
168
|
+
oxlint and ESLint use different rule name prefixes. This document uses the eslint-config names. See [Rule Name Mapping](#rule-name-mapping) at the bottom for prefix translations.
|
|
169
|
+
|
|
170
|
+
### Why some rules are ESLint-only
|
|
171
|
+
|
|
172
|
+
oxlint's jsPlugins feature can load ESLint plugins that export a standard `{ rules }` object. This works for packages like `eslint-plugin-import-x`, `eslint-plugin-storybook`, etc.
|
|
173
|
+
|
|
174
|
+
Several categories of rules cannot use this approach:
|
|
175
|
+
|
|
176
|
+
1. **Core ESLint rules** (e.g. `no-restricted-globals`, `camelcase`, `object-shorthand`). These are built into the `eslint` package, not exported as a plugin with a `{ rules }` object. `@eslint/js` only exports configs, not rules. There is no standalone ESLint plugin that re-exports core rules in the format oxlint's jsPlugins expect.
|
|
177
|
+
|
|
178
|
+
2. **Rules requiring ESLint parser services** (e.g. `@eslint-react/*`, `@typescript-eslint/*`). These call `getParserServices()` from `@typescript-eslint/utils` internally, which requires ESLint's parser infrastructure. oxlint's jsPlugin runner does not provide this.
|
|
179
|
+
|
|
180
|
+
3. **Rules requiring module resolution** (e.g. `import-x/no-unresolved`, `import-x/extensions`, `import-x/no-rename-default`). In ESLint, these rules use `eslint-import-resolver-typescript` to resolve TypeScript path aliases, barrel re-exports, and extensionless imports. The resolver is configured via ESLint's `settings['import-x/resolver']` — but oxlint's jsPlugin runner does not pass ESLint settings to plugins. Without a resolver, these rules can't find modules and produce thousands of false positives on every import.
|
|
181
|
+
|
|
182
|
+
4. **Old-style ESLint plugins** (e.g. `eslint-plugin-filenames`). These export rules as plain functions instead of objects with a `create` method. oxlint's jsPlugin runner requires the modern format (`{ meta, create }`).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wistia/oxlint-config",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Wistia's Oxlint configurations",
|
|
5
5
|
"packageManager": "yarn@4.14.1",
|
|
6
6
|
"type": "module",
|
|
@@ -55,16 +55,16 @@
|
|
|
55
55
|
"oxlint-tsgolint": ">= 1.0.0"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@eslint-react/eslint-plugin": "^5.
|
|
59
|
-
"@vitest/eslint-plugin": "^1.6.
|
|
58
|
+
"@eslint-react/eslint-plugin": "^5.8.3",
|
|
59
|
+
"@vitest/eslint-plugin": "^1.6.17",
|
|
60
60
|
"confusing-browser-globals": "^1.0.11",
|
|
61
61
|
"eslint-plugin-barrel-files": "^3.0.1",
|
|
62
62
|
"eslint-plugin-import-x": "^4.16.2",
|
|
63
63
|
"eslint-plugin-jest-dom": "^5.5.0",
|
|
64
64
|
"eslint-plugin-n": "^18.0.1",
|
|
65
65
|
"eslint-plugin-no-only-tests": "^3.4.0",
|
|
66
|
-
"eslint-plugin-playwright": "^2.10.
|
|
67
|
-
"eslint-plugin-storybook": "^10.
|
|
66
|
+
"eslint-plugin-playwright": "^2.10.4",
|
|
67
|
+
"eslint-plugin-storybook": "^10.4.0",
|
|
68
68
|
"eslint-plugin-styled-components": "^0.0.0",
|
|
69
69
|
"eslint-plugin-styled-components-a11y": "^2.2.1",
|
|
70
70
|
"eslint-plugin-testing-library": "^7.16.2",
|
|
@@ -73,12 +73,12 @@
|
|
|
73
73
|
"devDependencies": {
|
|
74
74
|
"@changesets/changelog-github": "^0.7.0",
|
|
75
75
|
"@changesets/cli": "^2.31.0",
|
|
76
|
-
"eslint": "^10.
|
|
77
|
-
"oxfmt": "^0.
|
|
78
|
-
"oxlint": "^1.
|
|
79
|
-
"oxlint-tsgolint": "^0.
|
|
80
|
-
"storybook": "^10.
|
|
81
|
-
"vitest": "^4.1.
|
|
76
|
+
"eslint": "^10.4.0",
|
|
77
|
+
"oxfmt": "^0.51.0",
|
|
78
|
+
"oxlint": "^1.66.0",
|
|
79
|
+
"oxlint-tsgolint": "^0.23.0",
|
|
80
|
+
"storybook": "^10.4.0",
|
|
81
|
+
"vitest": "^4.1.7"
|
|
82
82
|
},
|
|
83
83
|
"engines": {
|
|
84
84
|
"node": "^20.19.0 || ^22.13.0 || >=24 || >=26"
|
package/rules/base.mjs
CHANGED
|
@@ -272,6 +272,11 @@ export const baseRules = {
|
|
|
272
272
|
// https://oxc.rs/docs/guide/usage/linter/rules/eslint/grouped-accessor-pairs.html
|
|
273
273
|
'eslint/grouped-accessor-pairs': 'error',
|
|
274
274
|
|
|
275
|
+
// Require identifiers to match a specified regular expression
|
|
276
|
+
// https://oxc.rs/docs/guide/usage/linter/rules/eslint/id-match.html
|
|
277
|
+
// Decision: too opinionated for general use
|
|
278
|
+
'eslint/id-match': 'off',
|
|
279
|
+
|
|
275
280
|
// Enforce minimum and maximum identifier lengths
|
|
276
281
|
// https://oxc.rs/docs/guide/usage/linter/rules/eslint/id-length.html
|
|
277
282
|
'eslint/id-length': [
|
|
@@ -320,6 +325,8 @@ export const baseRules = {
|
|
|
320
325
|
|
|
321
326
|
// Disallow the use of console
|
|
322
327
|
// https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-console.html
|
|
328
|
+
// Decision: allow console methods that are commonly used for logging and error reporting,
|
|
329
|
+
// but disallow others that are often left in accidentally
|
|
323
330
|
'eslint/no-console': ['error', { allow: ['log', 'warn', 'error'] }],
|
|
324
331
|
|
|
325
332
|
// Disallow continue statements
|
|
@@ -374,6 +381,14 @@ export const baseRules = {
|
|
|
374
381
|
// https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-implicit-coercion.html
|
|
375
382
|
'eslint/no-implicit-coercion': 'error',
|
|
376
383
|
|
|
384
|
+
// Disallow declarations in the global scope
|
|
385
|
+
// https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-implicit-globals.html
|
|
386
|
+
'eslint/no-implicit-globals': 'error',
|
|
387
|
+
|
|
388
|
+
// Disallow the use of eval()-like methods
|
|
389
|
+
// https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-implied-eval.html
|
|
390
|
+
'eslint/no-implied-eval': 'error',
|
|
391
|
+
|
|
377
392
|
// Disallow the use of the __iterator__ property
|
|
378
393
|
// https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-iterator.html
|
|
379
394
|
'eslint/no-iterator': 'error',
|
|
@@ -577,6 +592,13 @@ export const baseRules = {
|
|
|
577
592
|
// https://oxc.rs/docs/guide/usage/linter/rules/eslint/operator-assignment.html
|
|
578
593
|
'eslint/operator-assignment': ['error', 'always'],
|
|
579
594
|
|
|
595
|
+
// Require using arrow functions for callbacks
|
|
596
|
+
// https://oxc.rs/docs/guide/usage/linter/rules/eslint/prefer-arrow-callback.html
|
|
597
|
+
'eslint/prefer-arrow-callback': [
|
|
598
|
+
'error',
|
|
599
|
+
{ allowNamedFunctions: false, allowUnboundThis: true },
|
|
600
|
+
],
|
|
601
|
+
|
|
580
602
|
// Require const declarations for variables that are never reassigned after declared
|
|
581
603
|
// https://oxc.rs/docs/guide/usage/linter/rules/eslint/prefer-const.html
|
|
582
604
|
'eslint/prefer-const': ['error', { destructuring: 'any', ignoreReadBeforeAssign: true }],
|
|
@@ -608,6 +630,10 @@ export const baseRules = {
|
|
|
608
630
|
// https://oxc.rs/docs/guide/usage/linter/rules/eslint/prefer-object-spread.html
|
|
609
631
|
'eslint/prefer-object-spread': 'error',
|
|
610
632
|
|
|
633
|
+
// Disallow use of the RegExp constructor in favor of regular expression literals
|
|
634
|
+
// https://oxc.rs/docs/guide/usage/linter/rules/eslint/prefer-regex-literals.html
|
|
635
|
+
'eslint/prefer-regex-literals': ['error', { disallowRedundantWrapping: true }],
|
|
636
|
+
|
|
611
637
|
// Require using Error objects as Promise rejection reasons
|
|
612
638
|
// https://oxc.rs/docs/guide/usage/linter/rules/eslint/prefer-promise-reject-errors.html
|
|
613
639
|
'eslint/prefer-promise-reject-errors': ['error', { allowEmptyReject: true }],
|
package/rules/import.mjs
CHANGED
|
@@ -115,6 +115,10 @@ export const importRules = {
|
|
|
115
115
|
// Decision: too opinionated for general use
|
|
116
116
|
'import/max-dependencies': 'off',
|
|
117
117
|
|
|
118
|
+
// Require a newline after the last import/require in a group
|
|
119
|
+
// https://oxc.rs/docs/guide/usage/linter/rules/import/newline-after-import.html
|
|
120
|
+
'import/newline-after-import': 'error',
|
|
121
|
+
|
|
118
122
|
// Ensure named imports coupled with named exports
|
|
119
123
|
// https://oxc.rs/docs/guide/usage/linter/rules/import/named.html
|
|
120
124
|
// Decision: handled by TypeScript
|
|
@@ -171,29 +175,8 @@ export const importRules = {
|
|
|
171
175
|
|
|
172
176
|
// Forbid the use of extraneous packages
|
|
173
177
|
// https://github.com/un-ts/eslint-plugin-import-x/blob/master/docs/rules/no-extraneous-dependencies.md
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
{
|
|
177
|
-
devDependencies: [
|
|
178
|
-
'**/*{.,_}{test,vitest,spec}.{js,jsx,ts,tsx}',
|
|
179
|
-
'**/vite.config.{ts,js,cjs,mjs,mts}',
|
|
180
|
-
'**/vitest.config.{ts,js,cjs,mjs,mts}',
|
|
181
|
-
'**/jest.config.{js,cjs,mjs,mts}',
|
|
182
|
-
'**/jest.setup.{js,cjs,mjs,mts}',
|
|
183
|
-
'**/eslint.config.{js,cjs,mjs,mts}',
|
|
184
|
-
'**/.eslintrc.{js,cjs}',
|
|
185
|
-
'**/.stylelintrc.{cjs,js,json,yaml,yml}',
|
|
186
|
-
'**/stylelint.config.{cjs,mjs,mts,js}',
|
|
187
|
-
'**/esbuild.config.{js,cjs,mjs,mts}',
|
|
188
|
-
'**/tsup.config.{js,cjs,mjs,mts}',
|
|
189
|
-
'**/webpack.config.{js,cjs,mjs,mts}',
|
|
190
|
-
'**/webpack.config.*.{js,cjs,mjs,mts}',
|
|
191
|
-
'**/rollup.config.{js,cjs,mjs,mts}',
|
|
192
|
-
'**/rollup.config.*.{js,cjs,mjs,mts}',
|
|
193
|
-
],
|
|
194
|
-
optionalDependencies: false,
|
|
195
|
-
},
|
|
196
|
-
],
|
|
178
|
+
// Decision: this is better handled by knip
|
|
179
|
+
'import-x-js/no-extraneous-dependencies': 'off',
|
|
197
180
|
|
|
198
181
|
// Reports if a default export is renamed during import
|
|
199
182
|
// https://github.com/un-ts/eslint-plugin-import-x/blob/master/docs/rules/no-rename-default.md
|
|
@@ -219,7 +202,8 @@ export const importRules = {
|
|
|
219
202
|
|
|
220
203
|
// Require a newline after the last import/require in a group
|
|
221
204
|
// https://github.com/un-ts/eslint-plugin-import-x/blob/master/docs/rules/newline-after-import.md
|
|
222
|
-
|
|
205
|
+
// Decision: handled by native import/newline-after-import
|
|
206
|
+
'import-x-js/newline-after-import': 'off',
|
|
223
207
|
|
|
224
208
|
// Ensures that there are no useless path segments
|
|
225
209
|
// https://github.com/un-ts/eslint-plugin-import-x/blob/master/docs/rules/no-useless-path-segments.md
|
package/rules/react-a11y.mjs
CHANGED
|
@@ -53,6 +53,30 @@ export const reactA11yRules = {
|
|
|
53
53
|
// Decision: too opinionated for general use
|
|
54
54
|
'jsx_a11y/autocomplete-valid': 'off',
|
|
55
55
|
|
|
56
|
+
// Enforce that a control (an interactive element) has a text label
|
|
57
|
+
// https://oxc.rs/docs/guide/usage/linter/rules/jsx-a11y/control-has-associated-label.html
|
|
58
|
+
'jsx_a11y/control-has-associated-label': [
|
|
59
|
+
'error',
|
|
60
|
+
{
|
|
61
|
+
labelAttributes: ['label'],
|
|
62
|
+
controlComponents: [],
|
|
63
|
+
ignoreElements: ['audio', 'canvas', 'embed', 'input', 'textarea', 'tr', 'video'],
|
|
64
|
+
ignoreRoles: [
|
|
65
|
+
'grid',
|
|
66
|
+
'listbox',
|
|
67
|
+
'menu',
|
|
68
|
+
'menubar',
|
|
69
|
+
'radiogroup',
|
|
70
|
+
'row',
|
|
71
|
+
'tablist',
|
|
72
|
+
'toolbar',
|
|
73
|
+
'tree',
|
|
74
|
+
'treegrid',
|
|
75
|
+
],
|
|
76
|
+
depth: 5,
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
|
|
56
80
|
// Enforce a clickable non-interactive element has at least one keyboard event listener
|
|
57
81
|
// https://oxc.rs/docs/guide/usage/linter/rules/jsx-a11y/click-events-have-key-events.html
|
|
58
82
|
'jsx_a11y/click-events-have-key-events': 'error',
|
|
@@ -128,6 +152,33 @@ export const reactA11yRules = {
|
|
|
128
152
|
},
|
|
129
153
|
],
|
|
130
154
|
|
|
155
|
+
// Ensure interactive elements are not assigned non-interactive roles
|
|
156
|
+
// https://oxc.rs/docs/guide/usage/linter/rules/jsx-a11y/no-interactive-element-to-noninteractive-role.html
|
|
157
|
+
'jsx_a11y/no-interactive-element-to-noninteractive-role': [
|
|
158
|
+
'error',
|
|
159
|
+
{ tr: ['none', 'presentation'] },
|
|
160
|
+
],
|
|
161
|
+
|
|
162
|
+
// Enforce that non-interactive elements do not have interaction handlers
|
|
163
|
+
// https://oxc.rs/docs/guide/usage/linter/rules/jsx-a11y/no-noninteractive-element-interactions.html
|
|
164
|
+
'jsx_a11y/no-noninteractive-element-interactions': [
|
|
165
|
+
'error',
|
|
166
|
+
{ handlers: ['onClick', 'onMouseDown', 'onMouseUp', 'onKeyPress', 'onKeyDown', 'onKeyUp'] },
|
|
167
|
+
],
|
|
168
|
+
|
|
169
|
+
// Enforce that non-interactive elements are not assigned interactive roles
|
|
170
|
+
// https://oxc.rs/docs/guide/usage/linter/rules/jsx-a11y/no-noninteractive-element-to-interactive-role.html
|
|
171
|
+
'jsx_a11y/no-noninteractive-element-to-interactive-role': [
|
|
172
|
+
'error',
|
|
173
|
+
{
|
|
174
|
+
ul: ['listbox', 'menu', 'menubar', 'radiogroup', 'tablist', 'tree', 'treegrid'],
|
|
175
|
+
ol: ['listbox', 'menu', 'menubar', 'radiogroup', 'tablist', 'tree', 'treegrid'],
|
|
176
|
+
li: ['menuitem', 'option', 'row', 'tab', 'treeitem'],
|
|
177
|
+
table: ['grid'],
|
|
178
|
+
td: ['gridcell'],
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
|
|
131
182
|
// Enforce explicit role is not redundant with implicit role of the element
|
|
132
183
|
// https://oxc.rs/docs/guide/usage/linter/rules/jsx-a11y/no-redundant-roles.html
|
|
133
184
|
'jsx_a11y/no-redundant-roles': 'error',
|
package/rules/react.mjs
CHANGED
|
@@ -105,6 +105,14 @@ export const reactRules = {
|
|
|
105
105
|
// Decision: this is a decision best left to the implementer
|
|
106
106
|
'react/no-multi-comp': 'off',
|
|
107
107
|
|
|
108
|
+
// Disallow object types as default props
|
|
109
|
+
// https://oxc.rs/docs/guide/usage/linter/rules/react/no-object-type-as-default-prop.html
|
|
110
|
+
'react/no-object-type-as-default-prop': 'error',
|
|
111
|
+
|
|
112
|
+
// Disallow creating unstable components inside components
|
|
113
|
+
// https://oxc.rs/docs/guide/usage/linter/rules/react/no-unstable-nested-components.html
|
|
114
|
+
'react/no-unstable-nested-components': 'error',
|
|
115
|
+
|
|
108
116
|
// Disallow usage of the return value of ReactDOM.render
|
|
109
117
|
// https://oxc.rs/docs/guide/usage/linter/rules/react/no-render-return-value.html
|
|
110
118
|
'react/no-render-return-value': 'error',
|
|
@@ -430,7 +438,8 @@ export const reactRules = {
|
|
|
430
438
|
|
|
431
439
|
// Prevent creating unstable components inside components
|
|
432
440
|
// https://eslint-react.xyz/docs/rules/no-nested-component-definitions
|
|
433
|
-
|
|
441
|
+
// Decision: handled by native react/no-unstable-nested-components
|
|
442
|
+
'@eslint-react/no-nested-component-definitions': 'off',
|
|
434
443
|
|
|
435
444
|
// Prevent creating lazy components inside components
|
|
436
445
|
// https://eslint-react.xyz/docs/rules/no-nested-lazy-component-declarations
|
|
@@ -473,7 +482,8 @@ export const reactRules = {
|
|
|
473
482
|
|
|
474
483
|
// Disallow referential-type variables as default props
|
|
475
484
|
// https://eslint-react.xyz/docs/rules/no-unstable-default-props
|
|
476
|
-
|
|
485
|
+
// Decision: handled by native react/no-object-type-as-default-prop
|
|
486
|
+
'@eslint-react/no-unstable-default-props': 'off',
|
|
477
487
|
|
|
478
488
|
// Prevent declaring unused methods of component class
|
|
479
489
|
// https://eslint-react.xyz/docs/rules/no-unused-class-component-members
|
package/rules/vitest.mjs
CHANGED
|
@@ -30,6 +30,11 @@ export const vitestRules = {
|
|
|
30
30
|
// https://oxc.rs/docs/guide/usage/linter/rules/vitest/no-import-node-test.html
|
|
31
31
|
'vitest/no-import-node-test': 'error',
|
|
32
32
|
|
|
33
|
+
// Enforce padding around afterAll blocks
|
|
34
|
+
// https://oxc.rs/docs/guide/usage/linter/rules/vitest/padding-around-after-all-blocks.html
|
|
35
|
+
// Decision: stylistic formatting rule, left to formatter
|
|
36
|
+
'vitest/padding-around-after-all-blocks': 'off',
|
|
37
|
+
|
|
33
38
|
// Disallow conditional tests
|
|
34
39
|
// https://oxc.rs/docs/guide/usage/linter/rules/vitest/no-conditional-in-test.html
|
|
35
40
|
'vitest/no-conditional-in-test': 'error',
|