@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 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
- See [docs/eslint-parity.md](docs/eslint-parity.md) for a full list of differences between this package and `@wistia/eslint-config`.
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.7.5",
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.7.6",
59
- "@vitest/eslint-plugin": "^1.6.16",
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.2",
67
- "eslint-plugin-storybook": "^10.3.6",
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.3.0",
77
- "oxfmt": "^0.48.0",
78
- "oxlint": "^1.64.0",
79
- "oxlint-tsgolint": "^0.22.1",
80
- "storybook": "^10.3.6",
81
- "vitest": "^4.1.5"
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
- 'import-x-js/no-extraneous-dependencies': [
175
- 'error',
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
- 'import-x-js/newline-after-import': 'error',
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
@@ -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
- '@eslint-react/no-nested-component-definitions': 'error',
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
- '@eslint-react/no-unstable-default-props': 'error',
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',