XO — это инструмент для статического анализа кода JavaScript и TypeScript, построенный поверх ESLint. Его основная идея заключается в предоставлении готового набора правил и единого стандарта качества кода без необходимости вручную собирать десятки ESLint-плагинов и конфигураций.
В отличие от классической настройки ESLint, где разработчик самостоятельно выбирает правила, плагины и параметры, XO предлагает подход «из коробки». При этом система конфигурации остается достаточно гибкой и позволяет адаптировать стандарт под требования конкретного проекта.
Конфигурация XO строится вокруг нескольких ключевых механизмов:
package.json;XO поддерживает несколько вариантов хранения настроек.
Наиболее распространённый способ — использование раздела
xo внутри файла package.json.
Пример:
{
"name": "my-project",
"xo": {
"space": 4,
"semicolon": true
}
}
Такой подход удобен для небольших проектов, поскольку все основные настройки находятся в одном месте.
Для крупных проектов часто используется отдельный файл:
xo.config.js
или
xo.config.cjs
Пример:
export default {
space: 4,
semicolon: true
};
Либо для CommonJS:
module.exports = {
space: 4,
semicolon: true
};
Отдельный файл позволяет:
Несмотря на использование ESLint, XO содержит собственные параметры для наиболее популярных настроек форматирования.
По умолчанию XO требует использование точек с запятой.
Включение:
{
"xo": {
"semicolon": true
}
}
Отключение:
{
"xo": {
"semicolon": false
}
}
Пример:
const name = 'John'
или
const name = 'John';
в зависимости от выбранной конфигурации.
Настройка отступов производится через параметр
space.
Два пробела:
{
"xo": {
"space": 2
}
}
Четыре пробела:
{
"xo": {
"space": 4
}
}
Пример:
function calculate() {
return 42;
}
В большинстве проектов существуют файлы, которые не должны анализироваться линтером:
Для этого используется параметр ignores.
Пример:
{
"xo": {
"ignores": [
"dist/**",
"coverage/**",
"vendor/**"
]
}
}
После этого XO не будет проверять указанные пути.
{
"xo": {
"ignores": [
"src/generated.js"
]
}
}
{
"xo": {
"ignores": [
"**/*.min.js"
]
}
}
Это особенно полезно для минифицированных файлов.
По умолчанию XO анализирует JavaScript-файлы, однако список можно расширить.
Пример:
{
"xo": {
"extensions": [
"js",
"mjs",
"cjs"
]
}
}
После этого линтер будет учитывать все перечисленные типы файлов.
Многие ESLint-правила зависят от среды выполнения программы.
Например:
Для этого используется параметр envs.
{
"xo": {
"envs": [
"node"
]
}
}
Теперь глобальные объекты Node.js будут считаться допустимыми:
console.log(process.version);
{
"xo": {
"envs": [
"browser"
]
}
}
После этого корректно распознаются:
window.location.href;
document.title;
{
"xo": {
"envs": [
"browser",
"node"
]
}
}
Такой вариант встречается в универсальных библиотеках.
Иногда проект использует глобальные объекты, создаваемые сторонними инструментами.
Для их объявления применяется параметр globals.
Пример:
{
"xo": {
"globals": [
"APP_CONFIG",
"VERSION"
]
}
}
Теперь обращения к этим идентификаторам не будут вызывать предупреждений:
console.log(APP_CONFIG.apiUrl);
Одним из наиболее важных элементов конфигурации XO является свойство
rules.
Оно позволяет изменять встроенные правила ESLint.
Пример:
{
"xo": {
"rules": {
"no-console": "off"
}
}
}
Теперь использование:
console.log('Debug');
не будет считаться ошибкой.
ESLint поддерживает три уровня:
| Значение | Описание |
|---|---|
| off | правило отключено |
| warn | предупреждение |
| error | ошибка |
Пример:
{
"xo": {
"rules": {
"eqeqeq": "warn"
}
}
}
Многие правила принимают дополнительные настройки.
Пример:
{
"xo": {
"rules": {
"max-depth": [
"error",
3
]
}
}
}
Код с вложенностью более трёх уровней будет вызывать ошибку.
Разные части проекта могут требовать различных наборов правил.
Для этого применяется механизм overrides.
Пример:
export default {
overrides: [
{
files: [
"test/**/*.js"
],
rules: {
"no-unused-expressions": "off"
}
}
]
};
Теперь правило будет отключено только для тестов.
export default {
overrides: [
{
files: ["src/**/*.js"],
rules: {
"no-console": "error"
}
},
{
files: ["scripts/**/*.js"],
rules: {
"no-console": "off"
}
}
]
};
Такой подход часто применяется для разделения производственного и служебного кода.
XO имеет встроенную поддержку TypeScript.
Для её включения обычно достаточно установить необходимые зависимости.
После этого становятся доступны проверки файлов:
.ts
.tsx
Пример конфигурации:
{
"xo": {
"typescript": true
}
}
При наличии нескольких конфигураций TypeScript можно явно указать нужный файл.
{
"xo": {
"typescript": {
"project": "./tsconfig.json"
}
}
}
{
"xo": {
"typescript": {
"project": [
"packages/*/tsconfig.json"
]
}
}
}
Подобная схема характерна для монорепозиториев.
XO может использоваться совместно с Prettier.
Типичная схема выглядит следующим образом:
Пример:
{
"scripts": {
"lint": "xo",
"format": "prettier --write ."
}
}
Такое разделение позволяет избежать конфликтов между инструментами.
Несмотря на наличие готового набора правил, XO допускает подключение дополнительных плагинов ESLint.
Пример:
export default {
plugins: [
"eslint-plugin-unicorn"
]
};
После подключения становятся доступны правила выбранного плагина.
export default {
rules: {
"unicorn/filename-case": [
"error",
{
case: "kebabCase"
}
]
}
};
Теперь XO будет контролировать стиль именования файлов.
В монорепозиториях часто используется единая конфигурация для всех пакетов.
Структура может выглядеть следующим образом:
root/
├── package.json
├── xo.config.js
├── packages/
│ ├── api/
│ ├── ui/
│ └── core/
Общая конфигурация:
export default {
space: 4,
semicolon: true,
ignores: [
"**/dist/**"
]
};
Каждый пакет автоматически наследует эти настройки.
Иногда возникает необходимость отключить правило только для конкретного участка кода.
Отключение следующей строки:
// eslint-disable-next-line no-console
console.log('Debug');
Отключение блока:
/* eslint-disable no-console */
console.log('One');
console.log('Two');
/* eslint-enable no-console */
Поскольку XO использует ESLint, все подобные механизмы полностью поддерживаются.
Конфигурация может ограничивать анализ конкретными шаблонами.
Пример:
export default {
files: [
"src/**/*.js"
]
};
В этом случае проверка будет выполняться только внутри каталога
src.
Часто используется корпоративный стандарт кодирования, общий для нескольких проектов.
Пример:
import companyConfig from '@company/xo-config';
export default {
...companyConfig,
rules: {
...companyConfig.rules,
'no-console': 'off'
}
};
Такой подход позволяет централизованно поддерживать единый стиль разработки и при необходимости локально изменять отдельные правила.
export default {
space: 4,
semicolon: true,
envs: [
'node'
],
ignores: [
'dist/**',
'coverage/**'
],
rules: {
'no-console': 'off',
'max-depth': [
'error',
4
],
'max-lines': [
'warn',
500
]
}
};
Особенности такой конфигурации:
export default {
envs: [
'browser'
],
space: 2,
ignores: [
'build/**'
],
globals: [
'APP_VERSION'
],
rules: {
'no-alert': 'error',
'no-console': 'warn'
}
};
Подобная конфигурация ориентирована на браузерную среду и обеспечивает более строгий контроль пользовательского интерфейса.
Для небольших проектов:
package.json;Для средних проектов:
overrides;Для крупных проектов и монорепозиториев:
tsconfig;Грамотно настроенная конфигурация XO позволяет получить единый стиль кодирования, автоматический контроль качества, предсказуемое поведение линтера и значительно снизить количество ошибок ещё до запуска приложения.