About
❤️ JavaScript/TypeScript linter (ESLint wrapper) with great defaults
> JavaScript/TypeScript linter (ESLint wrapper) with great defaults
Opinionated but configurable ESLint wrapper with lots of goodies included. Enforces strict and readable code. Never discuss code style on a pull request again! No decision-making. No `eslint.config.js` to manage. It just works!
It uses [ESLint](https://eslint.org) underneath, so issues regarding built-in rules should be opened over [there](https://github.com/eslint/eslint/issues).
**XO requires your project to be [ESM](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c).**
## Highlights
- Beautiful output.
- Zero-config, but [configurable when needed](#config).
- Enforces readable code, because you read more code than you write.
- No need to specify file paths to lint as it lints all JS/TS files except for [commonly ignored paths](#ignores).
- [Flat config customization.](#config)
- [TypeScript supported by default.](#typescript)
- Includes many useful ESLint plugins, like [`unicorn`](https://github.com/sindresorhus/eslint-plugin-unicorn), [`import-x`](https://github.com/un-ts/eslint-plugin-import-x), [`ava`](https://github.com/avajs/eslint-plugin-ava), [`n`](https://github.com/eslint-community/eslint-plugin-n) and more.
- Caches results between runs for much better performance.
- Super simple to add XO to a project with [`$ npm init xo`](https://github.com/xojs/create-xo).
- Fix many issues automagically with `$ xo --fix`.
- Open all files with errors at the correct line in your editor with `$ xo --open`.
- Specify [indent](#space) and [semicolon](#semicolon) preferences easily without messing with the rule config.
- Optionally use the [Prettier](https://github.com/prettier/prettier) code style or turn off all Prettier rules with the `compat` option.
- Optionally use with ESLint [directly](#usage-as-an-eslint-configuration)
- Great [editor plugins](#editor-plugins).
## Install
```sh
npm install xo --save-dev
```
*You must install XO locally. You can run it directly with `$ npx xo`.*
*For framework-specific linting, see [Astro](#astro), [React](#react), [Svelte](#svelte), and [Vue](#vue).*
## Usage
```
…
```
## Default code style
*Any of these can be [overridden](#shareable-configs) if necessary.*
- Tab indentation *[(or space)](#space)*
- Semicolons *[(or not)](#semicolon)*
- Single-quotes
- [Trailing comma](https://medium.com/@nikgraf/why-you-should-enforce-dangling-commas-for-multiline-statements-d034c98e36f8) for multiline statements
- No unused variables
- Space after keyword `if (condition) {}`
- Always `===` instead of `==`
Check out an [example](index.ts) and the [ESLint rules](https://github.com/xojs/eslint-config-xo/blob/main/index.js).
## Workflow
The recommended workflow is to add XO locally to your project and run it with the tests.
Simply run `$ npm init xo` (with any options) to add XO to create an `xo.config.js`.
## Config
You can configure XO options by creating an `xo.config.js` or an `xo.config.ts` file in the root directory of your project, or you can add an `xo` field to your `package.json`. XO supports all js/ts file extensions (js,cjs,mjs,ts,cts,mts) and popular framework extensions (vue,svelte,astro) automatically. A XO config is an extension of ESLint's Flat Config. Like ESLint, an XO config exports an array of XO config objects. XO config objects extend [ESLint Configuration Objects](https://eslint.org/docs/latest/use/configure/configuration-files#configuration-objects). This means all the available configuration params for ESLint also work for `XO`. However, `XO` enhances and adds extra params to the configuration objects to make them easier to work with.
### Config types
XO exports the types `FlatXoConfig`, `XoConfigItem`, and other types for you to get TypeScript validation on your config files.
examples:
`xo.config.js`
```js
/** @type {import('xo').FlatXoConfig} */
const xoConfig = [...]
```
`xo.config.ts`
```ts
import {type FlatXoConfig} from 'xo';
const xoConfig: FlatXoConfig = [...]
```
```ts
export default [...] satisfies import('xo').FlatXoConfig
```
### files
Type: `string | (string | string[])[]`\
Default: `**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx,vue,svelte,astro}`
A glob string, array of globs, or ESLint's native format (where nested arrays create AND patterns) indicating which files the config object applies to. By default `XO` will apply the configuration to [all files](lib/constants.ts). This is compatible with ESLint plugin configs, so you can spread them directly into your XO config.
> Tip: If you are adding additional `@typescript-eslint` rules to your config, these rules will apply to JS files as well unless you separate them appropriately with the `files` option. `@typescript-eslint` rules set to `'off'` or `0`, however, will have no effect on JS linting.
### ignores
Type: `string | string[]`
Some [paths](lib/constants.ts) are ignored by default, including paths in `.gitignore`. Additional ignores can be added here.
> Tip: For *global* ignores, keep `ignores` as the only key in the config item. You can optionally set a `name` property. Adding more properties will cause ignores to be scoped down to your files selection, which may have unexpected effects.
Global negated ignores are supported in both config files and the CLI for reopening XO's built-in ignored paths. This includes directory globs like `!dist/**`, file globs like `!**/*.min.js`, and literal file paths like `!dist/src/index.js`.
When global ignores are involved, XO uses them in this order:
1. Built-in default ignores
2. Global config `ignores`
3. CLI `ignores`
XO keeps positive ignores for fast file discovery and only rechecks XO's own default-ignored paths. ESLint makes the final ignore decision.
### space
Type: `boolean | number`\
Default: `false` *(tab indentation)*
Set it to `true` to get 2-space indentation or specify the number of spaces.
Global only. For file-specific indentation, use the [`@stylistic/indent`](https://eslint.style/rules/indent) and [`@html-eslint/indent`](https://html-eslint.org/docs/rules/indent) rules.
This option exists for pragmatic reasons, but I would strongly recommend you read [“Why tabs are superior”](http://lea.verou.me/2012/01/why-tabs-are-clearly-superior/).
### semicolon
Type: `boolean`\
Default: `true` *(Semicolons required)*
Set it to `false` to enforce no-semicolon style.
Global only. For file-specific semicolon rules, use the [`@stylistic/semi`](https://eslint.style/rules/semi) rule.
### prettier
Type: `boolean | 'compat'`\
Default: `false`
Format code with [Prettier](https://github.com/prettier/prettier).
Global only. For file-specific Prettier overrides, configure the [`prettier/prettier`](https://github.com/prettier/eslint-plugin-prettier#options) rule.
XO applies its own [Prettier options](https://prettier.io/docs/en/options.html):
- [semi](https://prettier.io/docs/en/options.html#semicolons): based on [semicolon](#semicolon) option
- [useTabs](https://prettier.io/docs/en/options.html#tabs): based on [space](#space) option
- [tabWidth](https://prettier.io/docs/en/options.html#tab-width): based on [space](#space) option
- [singleQuote](https://prettier.io/docs/en/options.html#quotes): `true`
- [bracketSpacing](https://prettier.io/docs/en/options.html#bracket-spacing): `false`
- [bracketSameLine](https://prettier.io/docs/en/options.html#bracket-line): `false`
- [trailingComma](https://prettier.io/docs/en/options.html#trailing-commas): `all`
Any options you set in a [Prettier config](https://prettier.io/docs/en/configuration.html) still apply for anything XO does not configure (like `printWidth` or plugins), but XO's own style settings take precedence.
#### Compat
If the Prettier option is set to `compat`, instead of formatting your code automatically, XO will turn off all rules that conflict with Prettier code style and allow you to pass your formatting to the Prettier tool directly.
### Astro
To lint [Astro](https://astro.build) files, install [`eslint-plugin-astro`](https://github.com/ota-meshi/eslint-plugin-astro):
```sh
npm install --save-dev eslint-plugin-astro
```
Then spread its recommended config in your `xo.config.js`:
```js
import astroPlugin from 'eslint-plugin-astro';
const xoConfig = [
...astroPlugin.configs.recommended,
];
export default xoConfig;
```
### React
To lint [React](https://react.dev) files, install [`eslint-config-xo-react`](https://github.com/xojs/eslint-config-xo-react):
```sh
npm install --save-dev eslint-config-xo-react
```
Then spread it in your `xo.config.js`:
```js
import xoReact from 'eslint-config-xo-react';
const xoConfig = [
...xoReact(),
];
export default xoConfig;
```
> [!NOTE]
> Until `eslint-plugin-react` supports ESLint 10 natively, you may need to wrap the config with [`fixupConfigRules`](https://github.com/eslint/rewrite/tree/main/packages/compat) from `@eslint/compat`.
### Svelte
To lint [Svelte](https://svelte.dev) files, install [`eslint-plugin-svelte`](https://github.com/sveltejs/eslint-plugin-svelte):
```sh
npm install --save-dev eslint-plugin-svelte
```
Then spread its recommended config in your `xo.config.js`:
```js
import sveltePlugin from 'eslint-plugin-svelte';
const xoConfig = [
...sveltePlugin.configs.recommended,
];
export default xoConfig;
```
### Vue
To lint [Vue](https://vuejs.org) files, install [`eslint-plugin-vue`](https://github.com/vuejs/eslint-plugin-vue):
```sh
npm install --save-dev eslint-plugin-vue
```
Then spread its recommended config in your `xo.config.js`:
```js
import vuePlugin from 'eslint-plugin-vue';
const xoConfig = [
...vuePlugin.configs['flat/recommended'],
];
export default xoConfig;
```
### Shareable configs
If you want to extend a [shareable ESLint config](https://eslint.org/docs/latest/extend/shareable-configs) or any other npm package, use `xo.config.js` instead of `package.json`, since `package.json` only supports serializable values and cannot `import`.
`xo.config.js`
```js
export {default} from 'my-shareable-config';
```
You can also extend and override:
```js
import myConfig from 'my-shareable-config';
export default [
...myConfig,
{
rules: {
// Your overrides
},
},
];
```
## TypeScript
XO will automatically lint TypeScript files (`.ts`, `.mts`, `.cts`, and `.tsx`) with the rules defined in [eslint-config-xo-typescript#use-with-xo](https://github.com/xojs/eslint-config-xo-typescript#use-with-xo).
XO will handle the [@typescript-eslint/parser `project` option](https://typescript-eslint.io/packages/parser/#project) automatically even if you don't have a `tsconfig.json` in your project.
You can opt out of XO's automatic tsconfig handling by specifying your own `languageOptions.parserOptions.project`, `languageOptions.parserOptions.projectService`, or `languageOptions.parserOptions.tsconfigRootDir`. Files in a config with these properties will be excluded from automatic tsconfig handling.
## Usage as an ESLint Configuration
There are two different ways to use XO's rules with ESLint directly, depending on whether you use the `xo` CLI.
### Without the `xo` CLI
If you don't use the `xo` CLI and just want XO's rules in ESLint, use [`eslint-config-xo`](https://github.com/xojs/eslint-config-xo). It accepts the same core style options as XO, including Prettier integration:
`eslint.config.js`
```js
import eslintConfigXo from 'eslint-config-xo';
export default [
...eslintConfigXo({space: true, prettier: true}),
];
```
> [!NOTE]
> This replaces the old `xoToEslintConfig` helper. For example, `xoToEslintConfig([{space: true, prettier: true}])` becomes `eslintConfigXo({space: true, prettier: true})`. For per-file overrides, add normal ESLint config objects alongside it.
### With the `xo` CLI (editor integration)
If you use the `xo` CLI but your editor only has the ESLint extension (not [XO's](#editor-plugins)), add an `eslint.config.js` that re-exports the adapter. It reads your `xo.config.js` a