@wistia/oxlint-config
v0.8.1
Published
Wistia's Oxlint configurations
Readme
@wistia/oxlint-config
Wistia's Oxlint configurations. This is a shared config package for oxlint, a fast TypeScript/Javascript linter.
Philosophy & Principles
- Always Error, Never Warn: Warnings become background noise that developers tune out. A rule should either flag a real problem or stay silent.
- 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.
- Fast: Known performance-heavy rules are skipped.
- 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.
How to install
yarn add -D @wistia/oxlint-config oxlint eslintWhy
eslint? This package uses oxlint's jsPlugins 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 theeslintpackage at runtime, so it must be installed even though you won't runeslintdirectly.Note: jsPlugins do not support rules that rely on TypeScript type information. Any type-aware rules in this config use native oxlint rules instead.
Type-aware linting
The typescriptConfig enables type-aware linting, which requires the oxlint-tsgolint package:
yarn add -D oxlint-tsgolintoxlint-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.
Quick start
Create an oxlint.config.ts in your project root:
TypeScript & React project (most common):
import { defineConfig } from 'oxlint';
import { typescriptConfig, reactConfig } from '@wistia/oxlint-config';
export default defineConfig({
extends: [typescriptConfig, reactConfig],
});JavaScript-only project:
import { defineConfig } from 'oxlint';
import { javascriptConfig } from '@wistia/oxlint-config';
export default defineConfig({
extends: [javascriptConfig],
});Available configs
Base configs
| Export | Description |
| ------------------ | ----------------------------------------------------------------------------- |
| javascriptConfig | Base config for any JS project (base + import + promise + barrel-files rules) |
| typescriptConfig | TypeScript projects (includes all JavaScript rules + TypeScript rules) |
Feature configs
Feature configs wrap their rules in overrides with default file patterns, so they scope correctly when extended at the top level.
| Export | Default file patterns | Description |
| ------------------------ | ----------------------------------------- | ------------------------------------- |
| reactConfig | all files (global) | React component + accessibility rules |
| styledComponentsConfig | all files (global) | Styled Components accessibility rules |
| nodeConfig | **/*.{mts,mjs,cjs} | Node.js-specific rules |
| vitestConfig | **/*.{test,vitest}.{ts,tsx,js,jsx} | Vitest testing rules |
| testingLibraryConfig | **/*.{test,vitest}.{ts,tsx,js,jsx} | Testing Library + jest-dom rules |
| playwrightConfig | **/*.spec.{ts,tsx,js}, **/e2e/**/*.ts | Playwright E2E testing rules |
| storybookConfig | **/*.stories.{ts,tsx,js,jsx} | Storybook story conventions |
Composing configs:
import { defineConfig } from 'oxlint';
import {
typescriptConfig,
reactConfig,
styledComponentsConfig,
vitestConfig,
testingLibraryConfig,
storybookConfig,
nodeConfig,
} from '@wistia/oxlint-config';
export default defineConfig({
extends: [
typescriptConfig,
reactConfig,
styledComponentsConfig,
vitestConfig,
testingLibraryConfig,
storybookConfig,
nodeConfig,
],
});Overriding rules
Add a rules section — your rules take precedence over the shared configs:
export default defineConfig({
extends: [typescriptConfig, reactConfig],
rules: {
'typescript/no-explicit-any': 'off',
'react/no-clone-element': 'off',
},
});File-specific overrides use overrides. Note that extends is not supported inside overrides — use rule imports for those:
import { defineConfig } from 'oxlint';
import { typescriptConfig, vitestRules } from '@wistia/oxlint-config';
export default defineConfig({
extends: [typescriptConfig],
overrides: [
{
files: ['**/*.test.ts', '**/*.vitest.ts'],
plugins: vitestRules.plugins,
jsPlugins: vitestRules.jsPlugins,
rules: {
...vitestRules.rules,
// project-specific overrides
'jest/expect-expect': ['error', { assertFunctionNames: ['expect', 'expectValid'] }],
},
},
],
});Running oxlint
{
"scripts": {
"lint:oxlint": "oxlint -c oxlint.config.ts ."
}
}Guidelines for adding new rules
- Preference given for autofixable rules
- Should not contradict existing rules
- Person/team adding new rules handles upgrading consumers and fixing violations
- Rules should always be set to
error, neverwarn - Add short description of rule and link to rule documentation in code comments
- Never delete a rule — set it to
offwith a comment explaining why
ESLint parity
Differences between @wistia/oxlint-config and @wistia/eslint-config.
oxlint and ESLint use different rule name prefixes. This document uses the eslint-config names. See Rule Name Mapping at the bottom for prefix translations.
Why some rules are ESLint-only
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.
Several categories of rules cannot use this approach:
Core ESLint rules (e.g.
no-restricted-globals,camelcase,object-shorthand). These are built into theeslintpackage, not exported as a plugin with a{ rules }object.@eslint/jsonly exports configs, not rules. There is no standalone ESLint plugin that re-exports core rules in the format oxlint's jsPlugins expect.Rules requiring ESLint parser services (e.g.
@eslint-react/*,@typescript-eslint/*). These callgetParserServices()from@typescript-eslint/utilsinternally, which requires ESLint's parser infrastructure. oxlint's jsPlugin runner does not provide this.Rules requiring module resolution (e.g.
import-x/no-unresolved,import-x/extensions,import-x/no-rename-default). In ESLint, these rules useeslint-import-resolver-typescriptto resolve TypeScript path aliases, barrel re-exports, and extensionless imports. The resolver is configured via ESLint'ssettings['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.Old-style ESLint plugins (e.g.
eslint-plugin-filenames). These export rules as plain functions instead of objects with acreatemethod. oxlint's jsPlugin runner requires the modern format ({ meta, create }).
