После создания собственного правила следующим этапом становится его публикация в составе ESLint-плагина. Такой подход позволяет распространять сразу набор правил, конфигураций и вспомогательных компонентов через единый npm-пакет.
Типичная структура проекта выглядит следующим образом:
eslint-plugin-myplugin/
├── lib/
│ ├── rules/
│ │ ├── no-console-log.js
│ │ ├── require-foo.js
│ │ └── index.js
│ └── configs/
│ ├── recommended.js
│ └── strict.js
├── package.json
├── README.md
└── LICENSE
В современных версиях ESLint структура может быть упрощена, однако логическое разделение на правила и конфигурации сохраняется практически всегда.
Пример отдельного правила:
module.exports = {
meta: {
type: "problem",
docs: {
description: "Запрещает использование console.log"
},
schema: []
},
create(context) {
return {
CallEx * pression(node) {
if (
node.callee.object &&
node.callee.object.name === "console" &&
node.callee.property &&
node.callee.property.name === "log"
) {
context.report({
node,
message: "Использование console.log запрещено"
});
}
}
};
}
};
Для того чтобы ESLint мог получить доступ к правилам плагина, они должны быть экспортированы через основной объект плагина.
Файл:
// lib/rules/index.js
module.exports = {
"no-console-log": require("./no-console-log"),
"require-foo": require("./require-foo")
};
В современных версиях часто используется непосредственный экспорт из главного файла:
const noConsoleLog = require("./lib/rules/no-console-log");
const requireFoo = require("./lib/rules/require-foo");
module.exports = {
rules: {
"no-console-log": noConsoleLog,
"require-foo": requireFoo
}
};
После публикации пользователь сможет активировать правило следующим образом:
{
"plugins": ["myplugin"],
"rules": {
"myplugin/no-console-log": "error"
}
}
Префикс имени правила всегда соответствует имени плагина.
Главный экспорт npm-пакета представляет собой объект, содержащий различные расширения ESLint.
Пример:
module.exports = {
rules: {
"no-console-log": require("./lib/rules/no-console-log"),
"require-foo": require("./lib/rules/require-foo")
},
configs: {
recommended: require("./lib/configs/recommended")
}
};
Свойство rules содержит все пользовательские
правила.
Свойство configs содержит готовые наборы настроек.
Дополнительно могут присутствовать:
module.exports = {
rules: {},
configs: {},
processors: {},
environments: {}
};
Каждая секция расширяет функциональность ESLint определённым образом.
Большинство популярных плагинов поставляются вместе с рекомендованным набором правил.
Пример:
// lib/configs/recommended.js
module.exports = {
plugins: ["myplugin"],
rules: {
"myplugin/no-console-log": "error",
"myplugin/require-foo": "warn"
}
};
После этого конфигурация подключается к экспорту плагина:
module.exports = {
rules: {
"no-console-log": require("../rules/no-console-log"),
"require-foo": require("../rules/require-foo")
},
configs: {
recommended: require("./recommended")
}
};
Использование:
{
"extends": [
"plugin:myplugin/recommended"
]
}
Подобный подход позволяет централизованно управлять настройками и обеспечивает единообразное использование правил.
ESLint использует специальные соглашения для поиска плагинов.
Для неименованных пакетов:
eslint-plugin-myplugin
Подключение:
{
"plugins": ["myplugin"]
}
Для scoped-пакетов:
@company/eslint-plugin-myplugin
Подключение:
{
"plugins": ["@company/myplugin"]
}
ESLint автоматически убирает префикс eslint-plugin- при
регистрации.
Корректное именование крайне важно, поскольку именно по нему ESLint определяет принадлежность пакета к категории плагинов.
Минимальный вариант:
{
"name": "eslint-plugin-myplugin",
"version": "1.0.0",
"main": "index.js",
"keywords": [
"eslint",
"eslintplugin"
]
}
Более полный пример:
{
"name": "eslint-plugin-myplugin",
"version": "1.0.0",
"description": "Набор пользовательских правил ESLint",
"main": "index.js",
"license": "MIT",
"keywords": [
"eslint",
"eslint-plugin",
"lint",
"javascript"
],
"peerDependencies": {
"eslint": "^9.0.0"
}
}
Особое внимание следует уделять разделу
peerDependencies.
Он показывает, с какими версиями ESLint протестирован плагин.
Начиная с ESLint 9 основной системой конфигурации становится Flat Config.
Плагин может предоставлять готовую конфигурацию для нового формата.
Пример:
const noConsoleLog = require("./rules/no-console-log");
module.exports = {
rules: {
"no-console-log": noConsoleLog
},
configs: {
recommended: {
plugins: {
myplugin: {
rules: {
"no-console-log": noConsoleLog
}
}
},
rules: {
"myplugin/no-console-log": "error"
}
}
}
};
Использование:
const myplugin = require("eslint-plugin-myplugin");
module.exports = [
myplugin.configs.recommended
];
Поддержка Flat Config становится обязательным требованием для современных ESLint-плагинов.
Каждое опубликованное правило должно сопровождаться документацией.
Обычно создаётся отдельный каталог:
docs/
├── no-console-log.md
├── require-foo.md
Типичная структура документа:
# no-console-log
Запрещает использование console.log.
## Неправильно
```js
console.log("debug");
logger.info("debug");
Документация должна содержать:
- назначение правила;
- мотивацию использования;
- примеры корректного кода;
- примеры некорректного кода;
- описание параметров настройки;
- информацию об автоматическом исправлении.
---
### Связь документации с правилом
Поле `docs` внутри `meta` используется для описания правила.
Пример:
```javascript
meta: {
type: "problem",
docs: {
description: "Запрещает использование console.log",
recommended: true
},
schema: []
}
В старых версиях ESLint некоторые инструменты автоматически извлекали данные из этого объекта для генерации документации.
Даже если автоматическая генерация не используется, наличие
заполненного блока docs считается хорошей практикой.
Перед публикацией необходимо проверить несколько аспектов:
Локальная проверка может выполняться через тестовый проект:
npm pack
Затем:
npm install ../eslint-plugin-myplugin-1.0.0.tgz
После установки производится тестирование так же, как и с обычным внешним пакетом.
Авторизация:
npm login
Проверка содержимого пакета:
npm pack
Публикация:
npm publish
Для scoped-пакетов может потребоваться:
npm publish --access public
После публикации пакет становится доступным через npm-реестр.
Установка пользователями:
npm install eslint-plugin-myplugin --save-dev
Либо:
npm install @company/eslint-plugin-myplugin --save-dev
Изменения в правилах должны соответствовать принципам Semantic Versioning.
Patch (1.0.1)
Исправления ошибок:
1.0.0 → 1.0.1
Примеры:
Minor (1.1.0)
Добавление новых возможностей без нарушения совместимости:
1.0.0 → 1.1.0
Примеры:
Major (2.0.0)
Ломающие изменения:
1.0.0 → 2.0.0
Примеры:
По мере роста плагина количество правил может значительно увеличиваться.
Распространённая структура:
lib/
├── rules/
│ ├── best-practices/
│ ├── style/
│ ├── security/
│ └── performance/
Примеры групп:
Для крупных проектов часто создаются несколько конфигураций:
configs: {
recommended,
strict,
style,
security
}
Это позволяет подключать только необходимые группы правил.
При большом количестве правил ручное перечисление становится неудобным.
Пример автоматической регистрации:
const fs = require("fs");
const path = require("path");
const rules = {};
for (const file of fs.readdirSync(__dirname)) {
if (file === "index.js") {
continue;
}
const name = path.basename(file, ".js");
rules[name] = require(`./${file}`);
}
module.exports = rules;
Подобный подход уменьшает объём служебного кода и упрощает сопровождение крупных ESLint-плагинов.
const noConsoleLog = require("./lib/rules/no-console-log");
const requireFoo = require("./lib/rules/require-foo");
module.exports = {
rules: {
"no-console-log": noConsoleLog,
"require-foo": requireFoo
},
configs: {
recommended: {
plugins: ["myplugin"],
rules: {
"myplugin/no-console-log": "error",
"myplugin/require-foo": "warn"
}
}
}
};
Такой пакет представляет собой полностью готовый ESLint-плагин, содержащий собственные правила, наборы конфигураций и инфраструктуру для распространения через npm.