@wistia/oxlint-config 0.3.4 → 0.4.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/README.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  Wistia's Oxlint configurations. This is a shared config package for [oxlint](https://oxc.rs/docs/guide/usage/linter), a fast TypeScript/Javascript linter.
4
4
 
5
+ ## Philosophy & Principles
6
+
7
+ - **Always Error, Never Warn:** Warnings become background noise that developers tune out. A rule should either flag a real problem or stay silent.
8
+ - **Strict, Consistent Code Style:** Where there's more than one way to do something, this configuration picks the strictest and most uniform option, favoring modern syntax and established best practices.
9
+ - **Fast:** Known performance-heavy rules are skipped.
10
+ - **Don't get in the way:** Rules that involve style preferences are intentionally disabled as this is best left to a formatter like `prettier`. Whenever possible, rules that can auto-fix are chosen to minimize friction and save developer time.
11
+
5
12
  ## How to install
6
13
 
7
14
  ```bash
@@ -10,98 +17,136 @@ yarn add -D @wistia/oxlint-config oxlint
10
17
 
11
18
  ## Quick start
12
19
 
13
- Create an `.oxlintrc.json` in your project root:
20
+ Create an `oxlint.config.ts` in your project root:
14
21
 
15
22
  **TypeScript project** (most common):
16
23
 
17
- ```json
18
- {
19
- "$schema": "./node_modules/oxlint/configuration_schema.json",
20
- "extends": ["./node_modules/@wistia/oxlint-config/configs/typescript.jsonc"]
21
- }
24
+ ```ts
25
+ import { defineConfig } from 'oxlint';
26
+ import { typescriptConfig, reactConfig } from '@wistia/oxlint-config';
27
+
28
+ export default defineConfig({
29
+ extends: [typescriptConfig, reactConfig],
30
+ });
22
31
  ```
23
32
 
24
33
  **JavaScript-only project:**
25
34
 
26
- ```json
27
- {
28
- "$schema": "./node_modules/oxlint/configuration_schema.json",
29
- "extends": ["./node_modules/@wistia/oxlint-config/configs/javascript.jsonc"]
30
- }
35
+ ```ts
36
+ import { defineConfig } from 'oxlint';
37
+ import { javascriptConfig } from '@wistia/oxlint-config';
38
+
39
+ export default defineConfig({
40
+ extends: [javascriptConfig],
41
+ });
31
42
  ```
32
43
 
33
44
  ## Available configs
34
45
 
35
46
  ### Base configs
36
47
 
37
- | Config | Description |
38
- | -------------------------- | ---------------------------------------------------------------------- |
39
- | `configs/javascript.jsonc` | Base config for any JavaScript project (base + import + promise rules) |
40
- | `configs/typescript.jsonc` | TypeScript projects (includes all JavaScript rules) |
48
+ | Export | Description |
49
+ | ------------------ | ----------------------------------------------------------------------------- |
50
+ | `javascriptConfig` | Base config for any JS project (base + import + promise + barrel-files rules) |
51
+ | `typescriptConfig` | TypeScript projects (includes all JavaScript rules + TypeScript rules) |
41
52
 
42
53
  ### Feature configs
43
54
 
44
- | Config | Description |
45
- | --------------------------------- | --------------------------------------------------------------- |
46
- | `configs/react.jsonc` | React component and accessibility rules (react + jsx-a11y) |
47
- | `configs/node.jsonc` | Node.js-specific rules (native + eslint-plugin-n via jsPlugins) |
48
- | `configs/vitest.jsonc` | Vitest testing rules (see note below) |
49
- | `configs/playwright.jsonc` | Playwright E2E testing rules (via jsPlugins) |
50
- | `configs/storybook.jsonc` | Storybook story conventions (via jsPlugins) |
51
- | `configs/testing-library.jsonc` | Testing Library + jest-dom rules (via jsPlugins) |
52
- | `configs/styled-components.jsonc` | Styled Components accessibility rules (via jsPlugins) |
53
-
54
- **Note on jsPlugins configs:** Some configs use oxlint's [jsPlugins](https://oxc.rs/docs/guide/usage/linter/js-plugins) feature to load ESLint plugins for rules that don't have native oxlint equivalents. These configs require the corresponding ESLint plugin to be installed as a dev dependency in the consuming project. Check each config's rule file for the required `jsPlugins` specifiers.
55
-
56
- **Note on vitest config:** Oxlint splits vitest-related rules across two internal plugin namespaces -- `vitest/` for vitest-specific rules and `jest/` for rules that originated in eslint-plugin-jest but apply equally to vitest. The `jest/` prefix is an oxlint naming convention; it does **not** mean Jest is required or that these rules are Jest-specific.
57
-
58
- Configs are **additive** -- start with a base (`javascript` or `typescript`), then layer on feature configs as needed:
59
-
60
- ````json
61
- {
62
- "$schema": "./node_modules/oxlint/configuration_schema.json",
63
- "extends": [
64
- "./node_modules/@wistia/oxlint-config/configs/typescript.jsonc",
65
- "./node_modules/@wistia/oxlint-config/configs/react.jsonc",
66
- "./node_modules/@wistia/oxlint-config/configs/vitest.jsonc"
67
- ]
68
- }
55
+ Feature configs wrap their rules in `overrides` with default file patterns, so they scope correctly when extended at the top level.
56
+
57
+ | Export | Default file patterns | Description |
58
+ | ------------------------ | ----------------------------------------- | ------------------------------------- |
59
+ | `reactConfig` | all files (global) | React component + accessibility rules |
60
+ | `styledComponentsConfig` | all files (global) | Styled Components accessibility rules |
61
+ | `vitestConfig` | `**/*.{test,spec,vitest}.{ts,tsx,js,jsx}` | Vitest testing rules |
62
+ | `testingLibraryConfig` | `**/*.{test,spec,vitest}.{ts,tsx,js,jsx}` | Testing Library + jest-dom rules |
63
+ | `storybookConfig` | `**/*.stories.{ts,tsx,js,jsx}` | Storybook story conventions |
64
+ | `nodeConfig` | `**/*.{mts,mjs,cjs}` | Node.js-specific rules |
65
+ | `playwrightConfig` | `**/*.spec.{ts,tsx,js}`, `**/e2e/**/*.ts` | Playwright E2E testing rules |
66
+
67
+ **Composing configs:**
68
+
69
+ ```ts
70
+ import { defineConfig } from 'oxlint';
71
+ import {
72
+ typescriptConfig,
73
+ reactConfig,
74
+ styledComponentsConfig,
75
+ vitestConfig,
76
+ testingLibraryConfig,
77
+ storybookConfig,
78
+ nodeConfig,
79
+ } from '@wistia/oxlint-config';
80
+
81
+ export default defineConfig({
82
+ extends: [
83
+ typescriptConfig,
84
+ reactConfig,
85
+ styledComponentsConfig,
86
+ vitestConfig,
87
+ testingLibraryConfig,
88
+ storybookConfig,
89
+ nodeConfig,
90
+ ],
91
+ });
92
+ ```
69
93
 
70
94
  ## Overriding rules
71
95
 
72
- Add a `rules` section to your `.oxlintrc.json` -- your rules take precedence over the shared config:
96
+ Add a `rules` section your rules take precedence over the shared configs:
73
97
 
74
- ```json
75
- {
76
- "$schema": "./node_modules/oxlint/configuration_schema.json",
77
- "extends": [
78
- "./node_modules/@wistia/oxlint-config/configs/typescript.jsonc"
98
+ ```ts
99
+ export default defineConfig({
100
+ extends: [typescriptConfig, reactConfig],
101
+ rules: {
102
+ 'typescript/no-explicit-any': 'off',
103
+ 'react/no-clone-element': 'off',
104
+ },
105
+ });
106
+ ```
107
+
108
+ File-specific overrides use `overrides`. Note that `extends` is not supported inside `overrides` — use rule imports for those:
109
+
110
+ ```ts
111
+ import { defineConfig } from 'oxlint';
112
+ import { typescriptConfig, vitestRules } from '@wistia/oxlint-config';
113
+
114
+ export default defineConfig({
115
+ extends: [typescriptConfig],
116
+ overrides: [
117
+ {
118
+ files: ['**/*.test.ts', '**/*.vitest.ts'],
119
+ plugins: vitestRules.plugins,
120
+ jsPlugins: vitestRules.jsPlugins,
121
+ rules: {
122
+ ...vitestRules.rules,
123
+ // project-specific overrides
124
+ 'jest/expect-expect': ['error', { assertFunctionNames: ['expect', 'expectValid'] }],
125
+ },
126
+ },
79
127
  ],
80
- "rules": {
81
- "eslint/no-console": "off",
82
- "typescript/no-explicit-any": "off"
83
- }
84
- }
85
- ````
128
+ });
129
+ ```
86
130
 
87
131
  ## Running oxlint
88
132
 
89
- Add a script to your `package.json`:
90
-
91
133
  ```json
92
134
  {
93
135
  "scripts": {
94
- "lint": "oxlint ."
136
+ "lint:oxlint": "oxlint -c oxlint.config.ts ."
95
137
  }
96
138
  }
97
139
  ```
98
140
 
99
- Oxlint will automatically pick up `.oxlintrc.json` from your project root.
100
-
101
141
  ## Guidelines for adding new rules
102
142
 
103
143
  1. Preference given for autofixable rules
104
144
  2. Should not contradict existing rules
105
145
  3. Person/team adding new rules handles upgrading consumers and fixing violations
106
- 4. Rules should always be set to `error`, never `warn` (the latter are never fixed)
107
- 5. Add short description of rule & link to rule definition in code comments
146
+ 4. Rules should always be set to `error`, never `warn`
147
+ 5. Add short description of rule and link to rule documentation in code comments
148
+ 6. Never delete a rule — set it to `off` with a comment explaining why
149
+
150
+ ## ESLint parity
151
+
152
+ See [docs/eslint-parity.md](docs/eslint-parity.md) for a full list of differences between this package and `@wistia/eslint-config`.
@@ -2,21 +2,15 @@ import { defineConfig } from 'oxlint';
2
2
  import { baseRules } from '../rules/base.mjs';
3
3
  import { importRules } from '../rules/import.mjs';
4
4
  import { promiseRules } from '../rules/promise.mjs';
5
- import { filenamesRules } from '../rules/filenames.mjs';
6
5
  import { barrelFilesRules } from '../rules/barrel-files.mjs';
7
6
 
8
7
  export default defineConfig({
9
8
  plugins: [...baseRules.plugins, ...importRules.plugins, ...promiseRules.plugins],
10
- jsPlugins: [
11
- ...(importRules.jsPlugins || []),
12
- ...(filenamesRules.jsPlugins || []),
13
- ...(barrelFilesRules.jsPlugins || []),
14
- ],
9
+ jsPlugins: [...(importRules.jsPlugins || []), ...(barrelFilesRules.jsPlugins || [])],
15
10
  rules: {
16
11
  ...baseRules.rules,
17
12
  ...importRules.rules,
18
13
  ...promiseRules.rules,
19
- ...filenamesRules.rules,
20
14
  ...barrelFilesRules.rules,
21
15
  },
22
16
  });
package/configs/node.mjs CHANGED
@@ -2,7 +2,12 @@ import { defineConfig } from 'oxlint';
2
2
  import { nodeRules } from '../rules/node.mjs';
3
3
 
4
4
  export default defineConfig({
5
- plugins: nodeRules.plugins,
6
- jsPlugins: nodeRules.jsPlugins,
7
- rules: nodeRules.rules,
5
+ overrides: [
6
+ {
7
+ files: ['**/*.mts', '**/*.mjs', '**/*.cjs'],
8
+ plugins: nodeRules.plugins,
9
+ jsPlugins: nodeRules.jsPlugins,
10
+ rules: nodeRules.rules,
11
+ },
12
+ ],
8
13
  });
@@ -2,6 +2,11 @@ import { defineConfig } from 'oxlint';
2
2
  import { playwrightRules } from '../rules/playwright.mjs';
3
3
 
4
4
  export default defineConfig({
5
- jsPlugins: playwrightRules.jsPlugins,
6
- rules: playwrightRules.rules,
5
+ overrides: [
6
+ {
7
+ files: ['**/*.spec.ts', '**/*.spec.tsx', '**/*.spec.js', '**/e2e/**/*.ts'],
8
+ jsPlugins: playwrightRules.jsPlugins,
9
+ rules: playwrightRules.rules,
10
+ },
11
+ ],
7
12
  });
@@ -2,6 +2,11 @@ import { defineConfig } from 'oxlint';
2
2
  import { storybookRules } from '../rules/storybook.mjs';
3
3
 
4
4
  export default defineConfig({
5
- jsPlugins: storybookRules.jsPlugins,
6
- rules: storybookRules.rules,
5
+ overrides: [
6
+ {
7
+ files: ['**/*.stories.ts', '**/*.stories.tsx', '**/*.stories.js', '**/*.stories.jsx'],
8
+ jsPlugins: storybookRules.jsPlugins,
9
+ rules: storybookRules.rules,
10
+ },
11
+ ],
7
12
  });
@@ -2,6 +2,20 @@ import { defineConfig } from 'oxlint';
2
2
  import { testingLibraryRules } from '../rules/testing-library.mjs';
3
3
 
4
4
  export default defineConfig({
5
- jsPlugins: testingLibraryRules.jsPlugins,
6
- rules: testingLibraryRules.rules,
5
+ overrides: [
6
+ {
7
+ files: [
8
+ '**/*.test.ts',
9
+ '**/*.test.tsx',
10
+ '**/*.test.js',
11
+ '**/*.test.jsx',
12
+ '**/*.vitest.ts',
13
+ '**/*.vitest.tsx',
14
+ '**/*.vitest.js',
15
+ '**/*.vitest.jsx',
16
+ ],
17
+ jsPlugins: testingLibraryRules.jsPlugins,
18
+ rules: testingLibraryRules.rules,
19
+ },
20
+ ],
7
21
  });
@@ -3,27 +3,25 @@ import { baseRules } from '../rules/base.mjs';
3
3
  import { importRules } from '../rules/import.mjs';
4
4
  import { promiseRules } from '../rules/promise.mjs';
5
5
  import { typescriptRules } from '../rules/typescript.mjs';
6
- import { filenamesRules } from '../rules/filenames.mjs';
7
6
  import { barrelFilesRules } from '../rules/barrel-files.mjs';
8
7
 
9
8
  export default defineConfig({
9
+ options: {
10
+ typeAware: true,
11
+ typeCheck: true,
12
+ },
10
13
  plugins: [
11
14
  ...baseRules.plugins,
12
15
  ...importRules.plugins,
13
16
  ...promiseRules.plugins,
14
17
  ...typescriptRules.plugins,
15
18
  ],
16
- jsPlugins: [
17
- ...(importRules.jsPlugins || []),
18
- ...(filenamesRules.jsPlugins || []),
19
- ...(barrelFilesRules.jsPlugins || []),
20
- ],
19
+ jsPlugins: [...(importRules.jsPlugins || []), ...(barrelFilesRules.jsPlugins || [])],
21
20
  rules: {
22
21
  ...baseRules.rules,
23
22
  ...importRules.rules,
24
23
  ...promiseRules.rules,
25
24
  ...typescriptRules.rules,
26
- ...filenamesRules.rules,
27
25
  ...barrelFilesRules.rules,
28
26
  },
29
27
  });
@@ -2,7 +2,21 @@ import { defineConfig } from 'oxlint';
2
2
  import { vitestRules } from '../rules/vitest.mjs';
3
3
 
4
4
  export default defineConfig({
5
- plugins: vitestRules.plugins,
6
- jsPlugins: vitestRules.jsPlugins,
7
- rules: vitestRules.rules,
5
+ overrides: [
6
+ {
7
+ files: [
8
+ '**/*.test.ts',
9
+ '**/*.test.tsx',
10
+ '**/*.test.js',
11
+ '**/*.test.jsx',
12
+ '**/*.vitest.ts',
13
+ '**/*.vitest.tsx',
14
+ '**/*.vitest.js',
15
+ '**/*.vitest.jsx',
16
+ ],
17
+ plugins: vitestRules.plugins,
18
+ jsPlugins: vitestRules.jsPlugins,
19
+ rules: vitestRules.rules,
20
+ },
21
+ ],
8
22
  });
package/index.d.mts CHANGED
@@ -19,7 +19,6 @@ export declare const playwrightRules: RuleFile;
19
19
  export declare const storybookRules: RuleFile;
20
20
  export declare const styledComponentsRules: RuleFile;
21
21
  export declare const testingLibraryRules: RuleFile;
22
- export declare const filenamesRules: RuleFile;
23
22
  export declare const barrelFilesRules: RuleFile;
24
23
 
25
24
  // Configs
package/index.mjs CHANGED
@@ -11,7 +11,6 @@ export { playwrightRules } from './rules/playwright.mjs';
11
11
  export { storybookRules } from './rules/storybook.mjs';
12
12
  export { styledComponentsRules } from './rules/styled-components.mjs';
13
13
  export { testingLibraryRules } from './rules/testing-library.mjs';
14
- export { filenamesRules } from './rules/filenames.mjs';
15
14
  export { barrelFilesRules } from './rules/barrel-files.mjs';
16
15
 
17
16
  // Configs
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wistia/oxlint-config",
3
- "version": "0.3.4",
3
+ "version": "0.4.0",
4
4
  "description": "Wistia's Oxlint configurations",
5
5
  "packageManager": "yarn@4.13.0",
6
6
  "type": "module",
@@ -12,12 +12,12 @@
12
12
  ],
13
13
  "exports": {
14
14
  "./package.json": "./package.json",
15
- "./configs/*": "./configs/*",
16
- "./rules/*": "./rules/*",
17
15
  ".": {
18
16
  "types": "./index.d.mts",
19
17
  "default": "./index.mjs"
20
18
  },
19
+ "./configs/*": "./configs/*",
20
+ "./rules/*": "./rules/*",
21
21
  "./javascript": "./configs/javascript.mjs",
22
22
  "./typescript": "./configs/typescript.mjs",
23
23
  "./react": "./configs/react.mjs",
@@ -34,33 +34,34 @@
34
34
  "validate": "vitest run test/validate-configs.test.mjs"
35
35
  },
36
36
  "peerDependencies": {
37
- "oxlint": ">= 1.0.0"
37
+ "oxlint": ">= 1.0.0",
38
+ "oxlint-tsgolint": ">= 1.0.0"
38
39
  },
39
40
  "dependencies": {
40
- "@vitest/eslint-plugin": "^1.6.14",
41
+ "@vitest/eslint-plugin": "^1.6.16",
41
42
  "eslint-plugin-barrel-files": "^3.0.1",
42
- "eslint-plugin-filenames": "^1.3.2",
43
43
  "eslint-plugin-import-x": "^4.16.2",
44
44
  "eslint-plugin-jest-dom": "^5.5.0",
45
45
  "eslint-plugin-n": "^17.24.0",
46
46
  "eslint-plugin-no-only-tests": "^3.3.0",
47
- "eslint-plugin-playwright": "^2.10.1",
48
- "eslint-plugin-storybook": "^10.3.4",
47
+ "eslint-plugin-playwright": "^2.10.2",
48
+ "eslint-plugin-storybook": "^10.3.5",
49
49
  "eslint-plugin-styled-components": "^0.0.0",
50
50
  "eslint-plugin-styled-components-a11y": "^2.2.1",
51
51
  "eslint-plugin-testing-library": "^7.16.2"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@changesets/changelog-github": "^0.6.0",
55
- "@changesets/cli": "^2.30.0",
56
- "eslint": "^10.2.0",
57
- "oxfmt": "^0.43.0",
58
- "oxlint": "^1.58.0",
59
- "storybook": "^10.3.4",
60
- "vitest": "^4.1.2"
55
+ "@changesets/cli": "^2.31.0",
56
+ "eslint": "^10.2.1",
57
+ "oxfmt": "^0.46.0",
58
+ "oxlint": "^1.61.0",
59
+ "oxlint-tsgolint": "^0.21.1",
60
+ "storybook": "^10.3.5",
61
+ "vitest": "^4.1.5"
61
62
  },
62
63
  "engines": {
63
- "node": "^20.19.0 || ^22.13.0 || >=24"
64
+ "node": "^20.19.0 || ^22.13.0 || >=24 || >=26"
64
65
  },
65
66
  "keywords": [
66
67
  "oxlint",
package/rules/import.mjs CHANGED
@@ -92,7 +92,10 @@ export const importRules = {
92
92
 
93
93
  // Ensure imports point to files/modules that can be resolved
94
94
  // https://github.com/un-ts/eslint-plugin-import-x/blob/master/docs/rules/no-unresolved.md
95
- 'import-x-js/no-unresolved': ['error', { caseSensitive: true }],
95
+ // NOTE: disabled — requires eslint-import-resolver-typescript to resolve TS paths.
96
+ // oxlint's jsPlugin runner does not pass ESLint settings to plugins, so the resolver
97
+ // config never reaches the plugin.
98
+ 'import-x-js/no-unresolved': 'off',
96
99
 
97
100
  // Disallow invalid exports, e.g. multiple defaults
98
101
  // https://github.com/un-ts/eslint-plugin-import-x/blob/master/docs/rules/export.md
@@ -126,21 +129,13 @@ export const importRules = {
126
129
 
127
130
  // Reports if a default export is renamed during import
128
131
  // https://github.com/un-ts/eslint-plugin-import-x/blob/master/docs/rules/no-rename-default.md
129
- 'import-x-js/no-rename-default': 'error',
132
+ // NOTE: disabled — parser errors on TS modules without eslint-import-resolver-typescript.
133
+ 'import-x-js/no-rename-default': 'off',
130
134
 
131
135
  // Ensure consistent use of file extension within the import path
132
136
  // https://github.com/un-ts/eslint-plugin-import-x/blob/master/docs/rules/extensions.md
133
- 'import-x-js/extensions': [
134
- 'error',
135
- 'ignorePackages',
136
- {
137
- js: 'never',
138
- mjs: 'never',
139
- jsx: 'never',
140
- ts: 'never',
141
- tsx: 'never',
142
- },
143
- ],
137
+ // NOTE: disabled — cannot determine file existence without eslint-import-resolver-typescript.
138
+ 'import-x-js/extensions': 'off',
144
139
 
145
140
  // Ensure absolute imports are above relative imports
146
141
  // https://github.com/un-ts/eslint-plugin-import-x/blob/master/docs/rules/order.md
@@ -1,18 +0,0 @@
1
- // All filenames rules loaded via jsPlugins (no native oxlint support).
2
- // see: https://github.com/selaux/eslint-plugin-filenames#rules
3
- export const filenamesRules = {
4
- jsPlugins: ['eslint-plugin-filenames'],
5
- rules: {
6
- // Match Exported Values
7
- // https://github.com/selaux/eslint-plugin-filenames#matching-exported-values-match-exported
8
- 'eslint-plugin-filenames/match-exported': 'error',
9
-
10
- // Consistent Filenames via regex
11
- // https://github.com/selaux/eslint-plugin-filenames#consistent-filenames-via-regex-match-regex
12
- 'eslint-plugin-filenames/match-regex': 'off',
13
-
14
- // Don't allow index files
15
- // https://github.com/selaux/eslint-plugin-filenames#consistent-filenames-via-regex-match-regex
16
- 'eslint-plugin-filenames/no-index': 'off',
17
- },
18
- };