Найти в Дзене

Правила wb-rules и ESLint: статический анализ кода

Продолжая совершенствовать созданный по материалам прошлых статей проект правил для контроллера Wirenboard, добавим в него поддержку статического анализа кода. Ранее мы освоили разработку правил на языке TypeScript: Перед этим подготовили контроллер к работе с Visual Studio Code: Чтобы защитить себя от случайного удаления важных системных файлов, запретили подключения от имени пользователя root: Выявить ошибки, уязвимости и нарушения стандартов программирования прямо в процессе написания правил wb-rules позволяют статические анализаторы кода, также называемые линтерами. Для проверки исходного кода на корректность, не требуется передавать файлы проекта на контроллер и пытаться их там запустить. Более того, инструмент способен препятствовать сборке при обнаружении проблем. Отдельно отметим необходимость придерживаться общего стиля кодирования при работе над проектом в команде. Например, один программист убеждён, что перенос фигурной скобки на новую строку нарушает целостность блока (стор
Оглавление

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

Ранее мы освоили разработку правил на языке TypeScript:

Перед этим подготовили контроллер к работе с Visual Studio Code:

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

Анализатор кода ESLint

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

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

Отдельно отметим необходимость придерживаться общего стиля кодирования при работе над проектом в команде.

Например, один программист убеждён, что перенос фигурной скобки на новую строку нарушает целостность блока (сторонник стиля Stroustrup) и для отступов обязательно нужно использовать два пробела - так компактнее.

Другой же напротив, видит правильным переносить скобки (стиль Allman), а для наглядности делает отступы из четырёх пробелов. Как им между собой договориться?

В этой ситуации приходит владелец проекта и говорит: «будет вот так». Для убедительности ещё и задаёт программе ряд инструкций, нарушение которых будет расценено как грубая ошибка.

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

Придраться может даже к лишнему пробелу или неуместной точке с запятой.

Статический анализ кода на языке TypeScript
Статический анализ кода на языке TypeScript

Инструмент можно настроить на автоматическое исправление всех несоответствий при сохранении файла. Это экономит время и значительно разгружает разработчика.

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

Установка и настройка

Первым делом откроем терминал VS Code и выполним команду для установки дополнительных инструментов:

yarn add --dev eslint @eslint/js typescript-eslint


Как и прежде, с помощью ключа
--dev, установка производится в секцию devDependencies, поэтому вспомогательный код не попадёт в итоговую программу.

Менеджер пакетов Yarn скачает линтер и готовые пресеты правил для языков JavaScript и TypeScript. Обычно не требуется долго и упорно прописывать конфигурацию с нуля - используют рекомендованные (recommended) или строгие (strict) наборы правил, а всё что не вписывается в принятый командой стандарт, переопределяют ниже.

В корне проекта создадим файл eslint.config.mjs, который будет хранить конфигурацию ESLint в новом формате под названием Flat.

Первыми строчками импортируем скачанные ранее пакеты с правилами:

import { globalIgnores } from 'eslint/config'
import eslint from '@eslint/js'
import tseslint from 'typescript-eslint'

Дальше объявим контейнер конфигурации, а также перечислим в нём используемые пресеты:

export default tseslint.config(
eslint.configs.recommended,
tseslint.configs.strictTypeChecked.map((config) => ({
...config,
files: ['**/*.ts'] // Правила TS только для файлов TS.
})),
tseslint.configs.stylisticTypeChecked.map((config) => ({
...config,
files: ['**/*.ts'] // Правила TS только для файлов TS.
})),
globalIgnores(['node_modules/', 'build/', 'dist/', 'tsc-replacers/', 'types/']),
{
// Остальные параметры настройки ESLint.
}
)


Для tselint существует два набора общих правил с проверкой типов -
configs.recommendedTypeChecked и configs.strictTypeChecked.

Базовые настройки ESLint
Базовые настройки ESLint

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

Если у вас уже есть значительные наработки правил wb-rules, можно использовать набор recommendedTypeChecked - понадобится меньше усилий на то, чтобы вникнуть в требования анализатора.

configs.stylisticTypeChecked следит за стилем кода TypeScript - убирает объявление типа там, где оно неуместно и т.п.

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

Финальным штрихом внесём в секцию «остальные параметры настройки» обязательный для конфигурации элемент с указанием, где искать файл конфигурации TypeScript под названием tsconfig.json:

languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname
}
}
Указание на папку с файлом tsconfig.json
Указание на папку с файлом tsconfig.json

Модификация tsconfig.json

Дополнительно потребуется зайти в файл tsconfig.json и в секцию compilerOptions добавить опцию strictNullChecks в значении true.

На этом настройка конфигурации завершена.

Расширение ESLint для VS Code

Если прямо сейчас перейти в любой из файлов папки с исходными кодами src, ничего не произойдёт. А всё потому, что для активации линтера нужно установить специальное расширение VS Code с одноимённым названием ESLint.

Установка и настройка

На панели инструментов редактора переключимся на вкладку расширений и введём в строку поиска идентификатор расширения, dbaeumer.vscode-eslint

Установка расширения ESLint
Установка расширения ESLint

Нажмём кнопку Install и после установки расширения вернёмся в Explorer проекта - потребуется открыть файл .vscode/settings.json, чтобы добавить несколько параметров:

"eslint.useFlatConfig": true,
"eslint.format.enable": true,
"[javascript][typescript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.formatOnType": true,
"editor.formatOnSave": true,
}

eslint.useFlatConfig сообщает расширению ESLint, что нужно использовать файл конфигурации в формате Flat.

eslint.format.enable активирует возможность использования расширения в качестве форматера кода.

Дальше идёт указание, для каких языков использовать форматер ESLint и в какой момент его применять. Возможно несколько установок:

  • editor.formatOnPaste - при вставке;
  • editor.formatOnType - при вводе;
  • editor.formatOnSave - при сохранении.
Параметры расширения ESLint
Параметры расширения ESLint

Откроем какой-нибудь файл из папки src, где лежат исходники правил wb-rules. В правом нижнем углу редактора найдём значок в виде двух фигурных скобок и нажмём на него. В появившемся списке выберем ESLint и нажмём на булавку, чтобы добавить кнопку на панель:

Добавление кнопки ESLint на панель
Добавление кнопки ESLint на панель

Теперь мы всегда можем получить доступ к окну вывода сообщений ESLint (1) и ознакомиться с полным списком обнаруженных проблем (2). Если же просто навести курсор на подчёркнутую строку (3), появится описание проблемы. При клике по ссылке в сообщении произойдёт переход на страницу с подробным объяснением, что это за правило и примерами - как нужно делать и как не нужно:

Окно со списком проблем, обнаруженных ESLint
Окно со списком проблем, обнаруженных ESLint

Автоматический форматер Prettier

Чтобы совсем не задумываться о стилизации кода, можно добавить в ESLint плагин Prettier. Об этом инструменте есть отличная статья на Хабре, тут даже нечего добавить:

Prettier в крупных проектах: тратим 20 минут на настройку, забываем о форматировании навсегда

Разве что, в нашем случае понадобится даже не 20 минут на настройку, а около пяти.

Установка и настройка

Откроем терминал VS Code и выполним последовательно две команды для установки:

yarn add --dev eslint-plugin-prettier eslint-config-prettier
yarn add --dev --exact prettier

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

После завершения установки перейдём в файл esling.config.mjs и добавим Prettier.

Рядом с остальными import добавим ещё один:

import prettierRecommended from 'eslint-plugin-prettier/recommended'

После объявлений tseslint.configs добавим строчку и вставим вызов prettierRecommended.

При первом сохранении файла ничего не произойдёт, поскольку Prettier на момент сохранения был неактивен. Если перезапустить студию, форматер найдёт множество ошибок. Сохраним файл повторно и Prettier сразу же структурирует код по своему усмотрению.

Конфигурация ESLint с добавленным Prettier
Конфигурация ESLint с добавленным Prettier

Настроек у Prettier не так много, как у ESLint. Предполагается, что там уже и так всё настроено «идеально». Создадим в корне проекта файл prettier.config.mjs следующего содержания:

/**
* @see https://prettier.io/docs/configuration
* @type {import("prettier").Config}
*/
const config = {
trailingComma: 'none',
tabWidth: 2,
semi: false,
singleQuote: true
}

export default config


Теперь везде при отсутствии противоречий будет убираться лишняя точка с запятой (
trailingComma), отступ станет равным двум пробелам (tabWith), а в массивах на последнем элементе не будет ставиться запятая (semi). Да, ещё все двойные кавычки поменяются на одинарные (singleQuote).

Дополнительные настройки Prettier
Дополнительные настройки Prettier

Отключение правил

Бывают ситуации, когда правило сильно мешается. В нашем случае, система линтинга мешает использованию объекта dev, являющегося частью стандартного API правил wb-rules.

Прежде чем выключать правила, нужно иметь чёткое представление, для чего это делается. Откроем файл eslint.config.mjs и перейдём в дополнительную секцию, после чего добавим туда пару строк:

rules: {
'@typescript-eslint/dot-notation': 'off',
'@typescript-eslint/no-unsafe-member-access': 'off'
},

После сохранения конфигурации, в файлах правил и модулей правил станет возможным использование конструкции вида dev['deviceId']['control'].

Выключение определённых правил ESLint
Выключение определённых правил ESLint

Статический анализ перед сборкой проекта

Чтобы ESLint блокировал сборку в случае ошибок, нужно добавить его в команду build файла package.json перед вызовом tsc:

eslint && tsc && tsc-alias && babel build -d dist

Система статического анализа кода готова к работе.

Добавление вызова eslint перед сборкой проекта
Добавление вызова eslint перед сборкой проекта

Шаблон проекта на GitHub

Настроенный проект для разработки правил wb-rules доступен на Github.

GitHub - wihome-dev/wb-rules-typescript at intro/eslint

Он расположен в отдельной ветке под названием eslint, которая соответствует материалам данной статьи и не содержит «лишнего» кода следующих выпусков.

Послесловие

Читайте далее: рассказ о работе с юнит-тестами - как прямо при сохранении файла убедиться, что очередные внесённые в код правки не нарушили существующие алгоритмы?