Продолжая совершенствовать созданный по материалам прошлых статей проект правил для контроллера Wirenboard, добавим в него поддержку статического анализа кода.
Ранее мы освоили разработку правил на языке TypeScript:
Перед этим подготовили контроллер к работе с Visual Studio Code:
Чтобы защитить себя от случайного удаления важных системных файлов, запретили подключения от имени пользователя root:
Анализатор кода ESLint
Выявить ошибки, уязвимости и нарушения стандартов программирования прямо в процессе написания правил wb-rules позволяют статические анализаторы кода, также называемые линтерами.
Для проверки исходного кода на корректность, не требуется передавать файлы проекта на контроллер и пытаться их там запустить. Более того, инструмент способен препятствовать сборке при обнаружении проблем.
Отдельно отметим необходимость придерживаться общего стиля кодирования при работе над проектом в команде.
Например, один программист убеждён, что перенос фигурной скобки на новую строку нарушает целостность блока (сторонник стиля Stroustrup) и для отступов обязательно нужно использовать два пробела - так компактнее.
Другой же напротив, видит правильным переносить скобки (стиль Allman), а для наглядности делает отступы из четырёх пробелов. Как им между собой договориться?
В этой ситуации приходит владелец проекта и говорит: «будет вот так». Для убедительности ещё и задаёт программе ряд инструкций, нарушение которых будет расценено как грубая ошибка.
ESLint отслеживает соответствие требованиям проекта, подчёркивая всё, что ему не нравится и выдавая всплывающие подсказки, в которых содержится ссылка на страницу с подробным объяснением проблемы и примерами, как должно быть.
Придраться может даже к лишнему пробелу или неуместной точке с запятой.
Инструмент можно настроить на автоматическое исправление всех несоответствий при сохранении файла. Это экономит время и значительно разгружает разработчика.
Однако, попадаются и ситуации, когда код настолько хитрый, что анализатор понятия не имеет, как его трансформировать в правильный. В этом случае он просто наворчит и откажется собирать проект, ожидая ручного устранения проблемы.
Установка и настройка
Первым делом откроем терминал 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.
В новых проектах лучше начать с набора strictTypeChecked, столкнуться с ворчанием линтера и подстроить его под себя, хорошенько изучив причины недовольства. Переопределить или выключить возможно любое мешающее правило, главное разобраться, для чего оно создано и какие опасности предотвращает.
Если у вас уже есть значительные наработки правил wb-rules, можно использовать набор recommendedTypeChecked - понадобится меньше усилий на то, чтобы вникнуть в требования анализатора.
configs.stylisticTypeChecked следит за стилем кода TypeScript - убирает объявление типа там, где оно неуместно и т.п.
Чтобы ESLint не пытался анализировать скомпилированный и подготовленный для передачи на контроллер код, мы добавили секцию globalIgnores, где перечислены места, куда линтеру смотреть не нужно.
Финальным штрихом внесём в секцию «остальные параметры настройки» обязательный для конфигурации элемент с указанием, где искать файл конфигурации TypeScript под названием tsconfig.json:
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname
}
}
Модификация tsconfig.json
Дополнительно потребуется зайти в файл tsconfig.json и в секцию compilerOptions добавить опцию strictNullChecks в значении true.
На этом настройка конфигурации завершена.
Расширение ESLint для VS Code
Если прямо сейчас перейти в любой из файлов папки с исходными кодами src, ничего не произойдёт. А всё потому, что для активации линтера нужно установить специальное расширение VS Code с одноимённым названием ESLint.
Установка и настройка
На панели инструментов редактора переключимся на вкладку расширений и введём в строку поиска идентификатор расширения, dbaeumer.vscode-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 - при сохранении.
Откроем какой-нибудь файл из папки src, где лежат исходники правил wb-rules. В правом нижнем углу редактора найдём значок в виде двух фигурных скобок и нажмём на него. В появившемся списке выберем ESLint и нажмём на булавку, чтобы добавить кнопку на панель:
Теперь мы всегда можем получить доступ к окну вывода сообщений ESLint (1) и ознакомиться с полным списком обнаруженных проблем (2). Если же просто навести курсор на подчёркнутую строку (3), появится описание проблемы. При клике по ссылке в сообщении произойдёт переход на страницу с подробным объяснением, что это за правило и примерами - как нужно делать и как не нужно:
Автоматический форматер Prettier
Чтобы совсем не задумываться о стилизации кода, можно добавить в ESLint плагин Prettier. Об этом инструменте есть отличная статья на Хабре, тут даже нечего добавить:
Разве что, в нашем случае понадобится даже не 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 сразу же структурирует код по своему усмотрению.
Настроек у 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).
Отключение правил
Бывают ситуации, когда правило сильно мешается. В нашем случае, система линтинга мешает использованию объекта dev, являющегося частью стандартного API правил wb-rules.
Прежде чем выключать правила, нужно иметь чёткое представление, для чего это делается. Откроем файл eslint.config.mjs и перейдём в дополнительную секцию, после чего добавим туда пару строк:
rules: {
'@typescript-eslint/dot-notation': 'off',
'@typescript-eslint/no-unsafe-member-access': 'off'
},
После сохранения конфигурации, в файлах правил и модулей правил станет возможным использование конструкции вида dev['deviceId']['control'].
Статический анализ перед сборкой проекта
Чтобы ESLint блокировал сборку в случае ошибок, нужно добавить его в команду build файла package.json перед вызовом tsc:
eslint && tsc && tsc-alias && babel build -d dist
Система статического анализа кода готова к работе.
Шаблон проекта на GitHub
Настроенный проект для разработки правил wb-rules доступен на Github.
Он расположен в отдельной ветке под названием eslint, которая соответствует материалам данной статьи и не содержит «лишнего» кода следующих выпусков.
Послесловие
Читайте далее: рассказ о работе с юнит-тестами - как прямо при сохранении файла убедиться, что очередные внесённые в код правки не нарушили существующие алгоритмы?