Научу писать плагин ESLint и пойму, как работает ESLint.

JavaScript ESLint
Научу писать плагин ESLint и пойму, как работает ESLint.

Цель этой статьи — описать, как создать плагин ESLint и создатьESLint rule, чтобы помочь нам лучше понять принцип работы ESLint, и при необходимости мы можем создать правило Lint, которое идеально соответствует нашим потребностям.

Цель плагина

запрещенный предметsetTimeoutВторой аргумент - число.

PS: Если это число, то легко стать числом дьявола Никто не знает, почему это число и что означает это число.

Инициализируйте проект с помощью шаблона:

1. Установите пакет npm

Чтобы облегчить разработчикам разработку плагинов, ESLint официально предоставляет использование шаблонов Yeoman (generator-eslint).

Все, что нам нужно знать о Yeoman, — это то, что это инструмент создания каркаса, который создает спроектированную структуру каталогов, содержащую указанную структуру фреймворка.

npm install -g yo generator-eslint

2. Создайте папку:

mkdir eslint-plugin-demo
cd eslint-plugin-demo

3. Командная строка инициализирует структуру проекта плагина ESLint:

yo eslint:plugin

Далее войдите в процесс взаимодействия с командной строкой.После завершения процесса сгенерируйте каркас проекта плагина ESLint и файлы.

? What is your name? OBKoro1
? What is the plugin ID? korolint   // 这个插件的ID是什么
? Type a short description of this plugin: XX公司的定制ESLint rule // 输入这个插件的描述
? Does this plugin contain custom ESLint rules? Yes // 这个插件包含自定义ESLint规则吗?
? Does this plugin contain one or more processors? No // 这个插件包含一个或多个处理器吗
// 处理器用于处理js以外的文件 比如.vue文件
   create package.json
   create lib/index.js
   create README.md

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

4. Создайте правила

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

yo eslint:rule // 生成 eslint rule的模板文件

Создайте взаимодействие командной строки правила:

? What is your name? OBKoro1
? Where will this rule be published? (Use arrow keys) // 这个规则将在哪里发布?
❯ ESLint Core  // 官方核心规则 (目前有200多个规则)
  ESLint Plugin  // 选择ESLint插件
? What is the rule ID? settimeout-no-number  // 规则的ID
? Type a short description of this rule: setTimeout 第二个参数禁止是数字  // 输入该规则的描述
? Type a short example of the code that will fail:  占位  // 输入一个失败例子的代码
   create docs/rules/settimeout-no-number.md
   create lib/rules/settimeout-no-number.js
   create tests/lib/rules/settimeout-no-number.js

Добавлена ​​структура проекта с определенными файлами правил

.
├── README.md
├── docs // 使用文档
│   └── rules // 所有规则的文档
│       └── settimeout-no-number.md // 具体规则文档
├── lib // eslint 规则开发
│   ├── index.js 引入+导出rules文件夹的规则
│   └── rules // 此目录下可以构建多个规则
│       └── settimeout-no-number.js // 规则细节
├── package.json
└── tests // 单元测试
    └── lib
        └── rules
            └── settimeout-no-number.js // 测试该规则的文件

4. Установите зависимости проекта

npm install

Вышеизложенное является подготовкой к разработке конкретных правил плагинов ESLint.Давайте сначала взглянем на соответствующие знания принципов AST и ESLint и разработаем ESLint для нас.ruleЗаложить основу.

AST — абстрактное синтаксическое дерево

АСТ это:Abstract Syntax TreeАббревиатура, китайское название: Абстрактное синтаксическое дерево.

Роль АСТ

Код абстрагируется в древовидную структуру данных, что удобно для последующего анализа и обнаружения кода.

Код анализируется в AST

astexplorer.net— это инструментальный сайт: он может видеть, как выглядит код, когда он анализируется в AST.

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

Селектор АСТ:

Обведенная кружком часть на рисунке ниже называется селекторами AST.

Роль селекторов AST: Используйте селектор кода, чтобы выбрать определенный фрагмент кода, а затем выполните статический анализ кода.

Существует множество селекторов AST, и у ESLint официально есть репозиторий, в котором перечислены все типы селекторов:estree

ESLint разработан нижеruleВам нужно использовать селекторы, вы поймете, когда будете их использовать позже, и вы будете знать их сейчас.

将代码解析成AST


Как работает ESLint

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

1. Разобрать код в AST

ESLint использует парсер JavaScriptEspreeРазобрать код JS в AST.

PS: Parser: это инструмент для разбора кода в AST. ES6, react и vue разработали соответствующие парсеры, поэтому ESLint может их обнаружить, и ESLint также является унифицированным интерфейсным инструментом Lint.

2. Глубоко изучите AST и отслеживайте процесс сопоставления.

После получения AST ESLint дважды проходит каждый селектор в порядке «сверху вниз», а затем «снизу вверх».

3. Активируйте селектор прослушивателяruleПерезвоните

В процессе глубокого обхода каждое правило, которое вступает в силу, будет отслеживать один или несколько селекторов.

4. Детали, такие как определенные правила обнаружения.


правила разработки

шаблон правила по умолчанию

ОткрытьruleСгенерированный файл шаблонаlib/rules/settimeout-no-number.js, чтобы очистить файл и удалить ненужные параметры:

module.exports = {
    meta: {
        docs: {
            description: "setTimeout 第二个参数禁止是数字",
        },
        fixable: null,  // 修复函数
    },
   // rule 核心
    create: function(context) {
       // 公共变量和函数应该在此定义
        return {
            // 返回事件钩子
        };
    }
};

Некоторые из удаленных элементов конфигурации являются элементами конфигурации, используемыми официальными основными правилами ESLint, а некоторые являются элементами конфигурации, которые временно не нужно понимать.Когда вам нужно их использовать, вы можете проверить их самостоятельно.Документация ESLint

создать метод - селектор прослушивания

Как упоминалось в третьей части принципа ESLint выше: в процессе глубокого обхода каждое действующее правило будет отслеживать один или несколько селекторов, всякий раз, когда селектор совпадет, отслеживание правила селектора вызовет соответствующий обратный вызов.

createВозвращает объект, свойства которого установлены для селекторов. ESLint соберет эти селекторы и выполнит все обратные вызовы, которые прослушивают селекторы во время обхода AST.

// rule 核心
create: function(context) {
    // 公共变量和函数应该在此定义
    return {
        // 返回事件钩子
        Identifier: (node) => {
            // node是选中的内容,是我们监听的部分, 它的值参考AST
        }
    };
}

Соблюдайте АСТ:

Создать ESLintruleВам нужно следить за тем, чтобы код анализировался в AST, выбирать код, который вы хотите обнаружить, а затем выносить некоторые суждения.

передается следующий кодastexplorer.netПроанализировано онлайн.

setTimeout(()=>{
	console.log('settimeout')
}, 1000)

setTimeout第二个参数为数字时的AST

полный файл правила

lib/rules/settimeout-no-number.js:

module.exports = {
    meta: {
        docs: {
            description: "setTimeout 第二个参数禁止是数字",
        },
        fixable: null,  // 修复函数
    },
    // rule 核心
    create: function (context) {
        // 公共变量和函数应该在此定义
        return {
            // 返回事件钩子
            'CallExpression': (node) => {
                if (node.callee.name !== 'setTimeout') return // 不是定时器即过滤
                const timeNode = node.arguments && node.arguments[1] // 获取第二个参数
                if (!timeNode) return // 没有第二个参数
                // 检测报错第二个参数是数字 报错
                if (timeNode.type === 'Literal' && typeof timeNode.value === 'number') {
                    context.report({
                        node,
                        message: 'setTimeout第二个参数禁止是数字'
                    })
                }
            }
        };
    }
};

context.report(): этот метод используется для уведомления ESLint о том, что этот код является предупреждением или ошибкой, как указано выше. существуетздесьПроверитьcontextа такжеcontext.report()документация.

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

Сейчас это может быть немного запутанно, но это не имеет значения, давайте напишем тестовый пример, а затем воспользуемсяdebuggerПосмотрим, как работает код.

Прецедент:

тестовый файлtests/lib/rules/settimeout-no-number.js:

/**
 * @fileoverview setTimeout 第二个参数禁止是数字
 * @author OBKoro1
 */
"use strict";
var rule = require("../../../lib/rules/settimeout-no-number"), // 引入rule
    RuleTester = require("eslint").RuleTester;

var ruleTester = new RuleTester({
    parserOptions: {
        ecmaVersion: 7, // 默认支持语法为es5 
    },
});
// 运行测试用例
ruleTester.run("settimeout-no-number", rule, {
    // 正确的测试用例
    valid: [
        {
            code: 'let someNumber = 1000; setTimeout(()=>{ console.log(11) },someNumber)'
        },
        {
            code: 'setTimeout(()=>{ console.log(11) },someNumber)'
        }
    ],
    // 错误的测试用例
    invalid: [
        {
            code: 'setTimeout(()=>{ console.log(11) },1000)',
            errors: [{
                message: "setTimeout第二个参数禁止是数字", // 与rule抛出的错误保持一致
                type: "CallExpression" // rule监听的对应钩子
            }]
        }
    ]
});

Давайте узнаем, как отлаживать файлы узлов в VSCode для наблюдения.ruleкак это работает.

на самом деле попалconsoleМожно и в виде , но немного тормозит консоль при отладке.Для узлов типа node информация не полная, поэтому все же рекомендую пройтиdebuggerспособ отладкиrule.

Файлы узлов отладки в VSCode

  1. Нажмите кнопку настроек на изображении ниже, и файл будет открытlaunch.json
  2. Заполните следующее содержимое файла для отладки файла узла.
  3. существуетruleпопасть в файлdebuggerИли щелкните маленькую красную точку рядом с количеством строк кода.
  4. Нажмите кнопку запуска на рисунке, чтобы войтиdebugger

vscode 设置

{
    // 使用 IntelliSense 了解相关属性。 
    // 悬停以查看现有属性的描述。
    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "启动程序", // 调试界面的名称
            // 运行项目下的这个文件:
            "program": "${workspaceFolder}/tests/lib/rules/settimeout-no-number.js",
            "args": [] // node 文件的参数
        },
        // 下面是用于调试package.json的命令 之前可以用,貌似vscode出了点bug导致现在用不了了
        {
            "name": "Launch via NPM",
            "type": "node",
            "request": "launch",
            "runtimeExecutable": "npm",
            "runtimeArgs": [
                "run-script", "dev"    //这里的dev就对应package.json中的scripts中的dev
            ],
            "port": 9229    //这个端口是调试的端口,不是项目启动的端口
        },
    ]
}

Запустите тестовый пример и введите точку останова

  1. существуетlib/rules/settimeout-no-number.jsударить кого-нибудьdebugger
  2. Нажмите кнопку «Пуск», чтобы запустить тестовый файл в режиме отладки.tests/lib/rules/settimeout-no-number.js
  3. начать отладкуrule.

Опубликовать плагин

eslint-плагиныnpmНа него ссылаются в виде пакета, поэтому вам нужно опубликовать плагин:

  1. Регистрация: Если вы не зарегистрировали учетную запись npm, вам необходимо перейти нарегистродин раз.

  2. Вход в нпм:npm login

  3. выпускатьnpmМешок:npm publishТо есть ESLint поставилpackage.jsonГотово.

Интегрировать в проект:

УстановитьnpmМешок:npm i eslint-plugin-korolint -D

  1. Обычный метод:引入插件一条条写入规则
// .eslintrc.js
module.exports = {
  plugins: [ 'korolint' ],
  rules: { 
    "korolint/settimeout-no-number": "error"
 }
}
  1. extendsНаследовать конфигурацию плагина:

Когда правил много, пользователям слишком сложно писать их одно за другим, поэтому ESLint можетНаследовать конфигурацию плагина:

немного отремонтироватьlib/rules/index.jsдокумент:

'use strict';
var requireIndex = require('requireindex');
const output = {
  rules: requireIndex(__dirname + '/rules'), // 导出所有规则
  configs: {
    // 导出自定义规则 在项目中直接引用
    koroRule: {
      plugins: ['korolint'], // 引入插件
      rules: {
        // 开启规则
        'korolint/settimeout-no-number': 'error'
      }
    }
  }
};
module.exports = output;

Инструкции:

использоватьextendsчтобы наследовать конфигурацию плагина,extendsМало того, что этот метод наследования, даже если вы передаете в npm-пакете относительный путь к файлу, eslint также может наследовать конфигурацию в нем.

// .eslintrc.js
module.exports = {
  extends: [ 'plugin:korolint/koroRule' ] // 继承插件导出的配置
}

PS: При таком способе использования имя пакета npm не может бытьeslint-plugin-xx-xx, только дляeslint-plugin-xxВ противном случае будет сообщено об ошибке, и эта проблема вызовет головную боль o(╥﹏╥)o

Расширение:

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

Направление обхода:

Как упоминалось выше: после получения AST ESLint будет проходить каждый селектор дважды в порядке «сверху вниз», а затем «снизу вверх».

что мы мониторимСелектор будет срабатывать в процессе «сверху вниз» по умолчанию, если его нужно выполнять в процессе «снизу вверх», нужно добавить:exit, в приведенном вышеCallExpressionстановитсяCallExpression:exit.

Уведомление: Фрагмент кода может содержать один и тот же селектор несколько раз после синтаксического анализа, и хук селектора также будет срабатывать несколько раз.

функция исправления: автоматически исправлять ошибки правил

Ремонтный эффект:

// 修复前
setTimeout(() => {

}, 1000)
// 修复后 变量名故意写错 为了让用户去修改它
const countNumber1 = 1000
setTimeout(() => {

}, countNumber2)
  1. Включите функцию исправления в метаобъекте правила:
// rule文件
module.exports = {
  meta: {
    docs: {
      description: 'setTimeout 第二个参数禁止是数字'
    },
    fixable: 'code' // 打开修复功能
  }
}
  1. существуетcontext.report()обеспечитьfixфункция:

поставить вышеcontext.reportИзмените его, добавьтеfixСпособа достаточно, для более подробного ознакомления можно посмотретьДокументация.

context.report({
    node,
    message: 'setTimeout第二个参数禁止是数字',
    fix(fixer) {
        const numberValue = timeNode.value;
        const statementString = `const countNumber1 = ${numberValue}\n`
        return [
        // 修改数字为变量 变量名故意写错 为了让用户去修改它
        fixer.replaceTextRange(node.arguments[1].range, 'countNumber2'),
        // 在setTimeout之前增加一行声明变量的代码 用户自行修改变量名
        fixer.insertTextBeforeRange(node.range, statementString)
        ];
    }
});

адрес проекта:

eslint-plugin-korolint


Уф~ Этот блог велся и отключался в течение нескольких недель, и, наконец, он готов!

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

Если вы считаете, что мой блог полезен для вас, пожалуйста, подпишитесь на него / лайкните его!

Передовое расширенное накопление,Нет публики,GitHubЯ не хочу Звезду✨ (сумасшедший намек), wx:OBkoro1, email: obkoro1@foxmail.com

Друг возьми меня в полет

Плагин ESLint для Jiyouyeyan1996Обучение, при возникновении проблем, он также указал мне, большое спасибо.

Использованная литература:

Создать правило Обсуждение принципа работы ESLint