Экспорт конфигураций из плагина

Плагин ESLint может содержать не только пользовательские правила, процессоры и наборы окружений, но и готовые конфигурации. Экспорт конфигураций позволяет поставлять вместе с плагином заранее настроенные наборы правил, которые пользователи могут подключать одной строкой вместо ручной настройки десятков параметров.

Такой подход широко используется в экосистеме ESLint. Многие популярные плагины предоставляют несколько конфигураций одновременно:

  • базовую;
  • рекомендуемую;
  • строгую;
  • конфигурацию для TypeScript;
  • конфигурацию для React;
  • конфигурацию для тестов;
  • конфигурацию для форматирования кода.

Основная идея заключается в том, что автор плагина определяет оптимальный набор правил и публикует его как часть пакета.


Структура плагина с экспортируемыми конфигурациями

Типичная структура проекта выглядит следующим образом:

eslint-plugin-example/
├── lib/
│   ├── rules/
│   │   ├── no-console-log.js
│   │   └── require-description.js
│   └── index.js
├── package.json
└── README.md

Файл index.js обычно является точкой входа плагина и экспортирует все доступные сущности.

Пример:

const noConsoleLog = require("./rules/no-console-log");
const requireDescription = require("./rules/require-description");

module.exports = {
    rules: {
        "no-console-log": noConsoleLog,
        "require-description": requireDescription
    },

    configs: {
        recommended: {
            rules: {
                "example/no-console-log": "error",
                "example/require-description": "warn"
            }
        }
    }
};

В объекте configs содержатся все конфигурации, доступные пользователям.


Объект configs

ESLint ожидает, что экспортируемые конфигурации будут находиться в свойстве configs.

Простейший вариант:

module.exports = {
    configs: {
        recommended: {
            rules: {
                "example/no-console-log": "error"
            }
        }
    }
};

После публикации плагина пользователь сможет подключить такую конфигурацию через механизм расширения настроек.

Для старого формата конфигураций:

module.exports = {
    extends: [
        "plugin:example/recommended"
    ]
};

Здесь:

  • example — имя плагина без префикса eslint-plugin-;
  • recommended — имя экспортированной конфигурации.

Именование конфигураций

Наиболее распространённые имена:

Имя Назначение
recommended Рекомендуемый набор правил
all Все правила плагина
strict Максимально строгие проверки
typescript Настройки для TypeScript
react Настройки для React
node Настройки для Node.js
test Настройки для тестовой среды

Плагин может экспортировать любое количество конфигураций.

Пример:

module.exports = {
    configs: {
        recommended: {},
        strict: {},
        all: {}
    }
};

Подключение:

extends: [
    "plugin:example/recommended",
    "plugin:example/strict"
]

Практически каждый серьёзный ESLint-плагин предоставляет конфигурацию recommended.

Её задача — включать правила, которые:

  • полезны большинству проектов;
  • редко дают ложные срабатывания;
  • помогают предотвращать реальные ошибки.

Пример:

module.exports = {
    configs: {
        recommended: {
            rules: {
                "example/no-console-log": "error",
                "example/require-description": "warn"
            }
        }
    }
};

Обычно в такую конфигурацию не включают слишком спорные правила оформления кода.


Конфигурация all

Конфигурация all включает все правила плагина.

Пример:

module.exports = {
    configs: {
        all: {
            rules: {
                "example/no-console-log": "error",
                "example/require-description": "error",
                "example/max-function-lines": "error",
                "example/no-inline-comment": "error"
            }
        }
    }
};

Особенности:

  • используется для тестирования;
  • удобна для изучения возможностей плагина;
  • может быть слишком строгой для реальных проектов.

Во многих популярных плагинах конфигурация all считается нестабильной, поскольку новые версии могут автоматически включать новые правила.


Подключение собственных правил внутри конфигурации

При описании правил необходимо указывать полный идентификатор.

Неправильно:

rules: {
    "no-console-log": "error"
}

Правильно:

rules: {
    "example/no-console-log": "error"
}

Префикс должен совпадать с именем плагина.

Если пакет называется:

eslint-plugin-example

то имя пространства правил будет:

example

Итоговое имя:

example/no-console-log

Экспорт нескольких уровней строгости

Часто один и тот же набор правил публикуется в нескольких вариантах.

Пример:

module.exports = {
    configs: {
        recommended: {
            rules: {
                "example/no-console-log": "warn"
            }
        },

        strict: {
            rules: {
                "example/no-console-log": "error",
                "example/require-description": "error"
            }
        }
    }
};

Пользователь самостоятельно выбирает необходимый уровень контроля.


Использование настроек parserOptions

Конфигурация может содержать любые параметры ESLint.

Например:

module.exports = {
    configs: {
        recommended: {
            parserOptions: {
                ecmaVersion: "latest",
                sourceType: "module"
            },

            rules: {
                "example/no-console-log": "error"
            }
        }
    }
};

Это избавляет пользователей от необходимости повторять типовые настройки.


Использование настроек languageOptions в Flat Config

Современный ESLint использует формат Flat Config.

В этом случае экспортируемая конфигурация может выглядеть так:

const plugin = {
    rules: {
        "no-console-log": noConsoleLog
    },

    configs: {
        recommended: {
            plugins: {
                example: null
            },

            rules: {
                "example/no-console-log": "error"
            }
        }
    }
};

На практике структура зависит от версии ESLint и способа публикации плагина.

Для Flat Config обычно экспортируется полноценный объект конфигурации, совместимый с новым API.


Экспорт конфигураций для Flat Config

Современный подход:

const plugin = {
    rules: {
        "no-console-log": noConsoleLog
    }
};

plugin.configs = {
    recommended: {
        plugins: {
            example: plugin
        },

        rules: {
            "example/no-console-log": "error"
        }
    }
};

module.exports = plugin;

Подключение:

const examplePlugin = require("eslint-plugin-example");

module.exports = [
    examplePlugin.configs.recommended
];

Такой подход становится всё более распространённым после перехода ESLint на плоские конфигурации.


Наследование конфигураций внутри плагина

Чтобы избежать дублирования, одна конфигурация может строиться поверх другой.

Пример:

const recommendedRules = {
    "example/no-console-log": "error"
};

module.exports = {
    configs: {
        recommended: {
            rules: recommendedRules
        },

        strict: {
            rules: {
                ...recommendedRules,
                "example/require-description": "error"
            }
        }
    }
};

Преимущества:

  • отсутствие копирования кода;
  • единая точка изменения настроек;
  • упрощённая поддержка.

Разделение конфигураций по файлам

В крупных плагинах конфигурации обычно выносятся в отдельную директорию.

Структура:

lib/
├── configs/
│   ├── recommended.js
│   ├── strict.js
│   └── all.js
├── rules/
└── index.js

Файл recommended.js:

module.exports = {
    rules: {
        "example/no-console-log": "error"
    }
};

Файл index.js:

const recommended = require("./configs/recommended");
const strict = require("./configs/strict");

module.exports = {
    rules: {},

    configs: {
        recommended,
        strict
    }
};

Такой подход особенно полезен при большом количестве конфигураций.


Добавление сторонних расширений

Конфигурация может использовать другие плагины и расширения.

Пример:

module.exports = {
    configs: {
        recommended: {
            extends: [
                "eslint:recommended"
            ],

            rules: {
                "example/no-console-log": "error"
            }
        }
    }
};

Это позволяет публиковать готовые комплексные решения.


Экспорт конфигурации для определённой технологии

Распространённая практика — выпуск специализированных конфигураций.

Для React:

module.exports = {
    configs: {
        react: {
            rules: {
                "example/no-console-log": "error",
                "example/react-component-name": "warn"
            }
        }
    }
};

Для Node.js:

module.exports = {
    configs: {
        node: {
            rules: {
                "example/no-process-exit": "error"
            }
        }
    }
};

Для тестов:

module.exports = {
    configs: {
        test: {
            rules: {
                "example/no-focused-test": "error"
            }
        }
    }
};

Подобное разделение позволяет адаптировать один плагин под разные типы проектов.


Автоматическое формирование конфигурации all

Если правил много, конфигурацию all можно генерировать программно.

Пример:

const rules = {
    "no-console-log": require("./rules/no-console-log"),
    "require-description": require("./rules/require-description")
};

const allRules = {};

for (const ruleName of Object.keys(rules)) {
    allRules[`example/${ruleName}`] = "error";
}

module.exports = {
    rules,

    configs: {
        all: {
            rules: allRules
        }
    }
};

Преимущества:

  • отсутствует риск забыть новое правило;
  • сокращается объём ручной поддержки;
  • конфигурация всегда соответствует составу плагина.

Документирование экспортируемых конфигураций

Каждая экспортируемая конфигурация должна быть подробно описана.

Обычно документация содержит:

  • название конфигурации;
  • список включённых правил;
  • назначение;
  • примеры подключения;
  • различия между уровнями строгости.

Пример описания:

plugin:example/recommended

Включает:

  • example/no-console-log
  • example/require-description

Пример подключения:

extends: [
    "plugin:example/recommended"
]

Хорошо документированные конфигурации значительно упрощают внедрение плагина и уменьшают количество ошибок при настройке.


Типичные ошибки при экспорте конфигураций

Отсутствие префикса плагина

Неправильно:

rules: {
    "no-console-log": "error"
}

Правильно:

rules: {
    "example/no-console-log": "error"
}

Несовпадение имени плагина

Пакет:

eslint-plugin-example

Правило:

example/no-console-log

а не:

eslint-plugin-example/no-console-log

Использование несуществующих правил

Неправильно:

rules: {
    "example/unknown-rule": "error"
}

Все правила должны присутствовать в секции:

rules: {
    "unknown-rule": ruleImplementation
}

Циклические зависимости

Проблема:

recommended.js

импортирует:

index.js

а index.js импортирует:

recommended.js

Подобные циклы нередко приводят к частично инициализированным объектам и трудноуловимым ошибкам.

Смешивание форматов конфигурации

Не следует одновременно использовать несовместимые подходы для Legacy Config и Flat Config без явного разделения. Для поддержки разных поколений ESLint обычно публикуются отдельные экспортируемые варианты конфигураций с чётко определённым назначением и документацией.