JavaScript/TypeScript linter (ESLint wrapper) with great defaults
XO supports multiple configuration methods including flat config files, CLI options, and programmatic configuration. The configuration system is built on top of ESLint's flat config format with XO-specific extensions.
Core configuration options that control XO's linting behavior.
interface XoConfigOptions {
/**
* Use spaces for indentation
* - boolean: true for 2 spaces, false for tabs
* - number: specific number of spaces
* - string: parsed as boolean or number
* - undefined: use default (tabs)
*/
space?: Space;
/** Use semicolons at the end of statements */
semicolon?: boolean;
/**
* Use Prettier to format code
* - true: Enable Prettier formatting
* - 'compat': Disable conflicting rules for separate Prettier usage
*/
prettier?: boolean | 'compat';
/** Add React support and React-specific linting rules */
react?: boolean;
/** Files to ignore, can be a glob pattern or array of patterns */
ignores?: string | string[];
}
// Utility type for space configuration
type Space = boolean | number | string | undefined;XO extends ESLint's flat config format with additional options.
interface XoConfigItem extends XoConfigOptions {
/**
* Glob patterns indicating files this configuration applies to
* If not specified, applies to all files
*/
files?: string | string[];
/**
* Glob patterns indicating files this configuration should NOT apply to
* Takes precedence over files patterns
*/
ignores?: string | string[];
// Inherits all ESLint Linter.Config properties:
// languageOptions, plugins, rules, settings, etc.
}
type FlatXoConfig = XoConfigItem | XoConfigItem[];Options that control the linter's behavior and execution context.
interface LinterOptions {
/** Current working directory to use for relative paths */
cwd: string;
/** Write fixes to the files automatically */
fix?: boolean;
/** Path to the file being linted (required for lintText) */
filePath?: string;
/** Show only errors and NOT warnings */
quiet?: boolean;
/**
* Auto-configure type-aware linting on unincluded TypeScript files
* Ensures TypeScript files are linted with type-aware parser
*/
ts?: boolean;
/** Custom path to config file to use for the linter */
configPath?: string;
}Options specific to linting text content rather than files.
interface LintTextOptions {
/** Path to the file being linted (used for rule context) */
filePath: string;
/** Warn if the file is ignored by configuration */
warnIgnored?: boolean;
}Create a xo.config.js file in your project root:
// xo.config.js
export default [
{
space: 2,
semicolon: true,
prettier: false
},
{
files: ["src/**/*.{js,ts}"],
react: true,
prettier: true
},
{
files: ["test/**/*.js"],
space: 4,
rules: {
// Custom ESLint rules
"no-console": "off"
}
},
{
ignores: ["dist/**", "coverage/**"]
}
];// xo.config.ts
import type { FlatXoConfig } from "xo";
const config: FlatXoConfig = [
{
space: 2,
semicolon: true,
languageOptions: {
parserOptions: {
project: "./tsconfig.json"
}
}
},
{
files: ["**/*.tsx"],
react: true,
prettier: true
}
];
export default config;// xo.config.js
export default [
// Global defaults
{
space: 2,
semicolon: true
},
// Frontend specific
{
files: ["frontend/**/*.{js,ts,jsx,tsx}"],
react: true,
prettier: true,
space: 2
},
// Backend specific
{
files: ["backend/**/*.{js,ts}"],
react: false,
space: 4,
rules: {
"n/no-process-env": "off"
}
},
// Test files
{
files: ["**/*.test.{js,ts}"],
rules: {
"no-console": "off"
}
}
];Override configuration via command-line options:
# Override space configuration
xo --space 4 --semicolon --react
# Use custom config file
xo --config ./custom-xo.config.js
# Ignore additional patterns
xo --ignore "temp/**" --ignore "*.generated.js"Configure XO programmatically when creating instances:
import Xo from "xo";
// Basic configuration
const xo = new Xo(
{
cwd: process.cwd(),
fix: true,
quiet: false
},
{
space: 2,
semicolon: true,
react: true,
ignores: ["dist/**", "coverage/**"]
}
);
// Advanced configuration with custom rules
const advancedXo = new Xo(
{ cwd: "./src", fix: false },
{
space: 4,
prettier: "compat",
// Configuration will be merged with defaults
}
);XO resolves configuration in the following order (later options override earlier ones):
xo.config.js, xo.config.ts, etc.XO automatically handles TypeScript configuration:
// xo.config.ts
export default [
{
// TypeScript files automatically get type-aware linting
files: ["**/*.{ts,tsx}"],
languageOptions: {
parserOptions: {
project: true, // Use nearest tsconfig.json
tsconfigRootDir: import.meta.dirname
}
}
},
{
// Override for specific files
files: ["scripts/**/*.ts"],
languageOptions: {
parserOptions: {
project: "./scripts/tsconfig.json"
}
}
}
];Configure Prettier integration:
// xo.config.js with Prettier
export default [
{
prettier: true,
space: 2,
semicolon: true,
// XO will validate Prettier config matches XO options
},
{
// Compatibility mode - let Prettier handle formatting separately
files: ["formatted/**/*.js"],
prettier: "compat"
}
];Configure file ignoring:
// xo.config.js
export default [
{
// Global ignores (no files specified)
ignores: [
"dist/**",
"coverage/**",
"node_modules/**",
"*.min.js"
]
},
{
// Conditional ignores
files: ["src/**/*.js"],
ignores: ["src/**/*.generated.js"]
}
];XO includes comprehensive defaults:
// Default ignore patterns
const defaultIgnores = [
"**/node_modules/**",
"**/bower_components/**",
"flow-typed/**",
"coverage/**",
"{tmp,temp}/**",
"**/*.min.js",
"vendor/**",
"dist/**",
"tap-snapshots/*.{cjs,js}"
];
// Default file extensions
const jsExtensions = ["js", "jsx", "mjs", "cjs"];
const tsExtensions = ["ts", "tsx", "cts", "mts"];
const allExtensions = [...jsExtensions, ...tsExtensions];Convert XO config to ESLint config:
import { xoToEslintConfig } from "xo";
const xoConfig = [
{ space: 2, semicolon: true, react: true }
];
const eslintConfig = xoToEslintConfig(xoConfig, {
prettierOptions: { semi: true, tabWidth: 2 }
});
// Use with ESLint directly
import { ESLint } from "eslint";
const eslint = new ESLint({ overrideConfig: eslintConfig });// xo.config.js
const isCI = process.env.CI === "true";
const isDevelopment = process.env.NODE_ENV === "development";
export default [
{
space: 2,
semicolon: true,
// Adjust for CI environment
...(isCI && {
quiet: true,
// More strict rules in CI
rules: {
"no-console": "error"
}
}),
// Development-friendly rules
...(isDevelopment && {
rules: {
"no-console": "warn",
"no-debugger": "warn"
}
})
}
];Install with Tessl CLI
npx tessl i tessl/npm-xo