Самый быстрый форматер кода
Всем привет! Меня зовут Андрей, я работаю в БФТ-Холдинге на должности ведущий инженер-разработчик и занимаюсь frontend-разработкой.
В статье подробно поговорим о самом быстром форматере кода. Подробно покажем, как интегрировать форматер в любой проект, настроим форматирование по сохранению в редакторах кода VSCode/VSCodium и посоревнуемся в скорости форматирования с prettier. На фоне хайпа нейросетей я решил разбавить статью изображениями форматера в представлении различных нейросетей. Надеюсь, будет интересно, погнали!
Несмотря на то, что статья с акцентом на frontend, а приведённые настройки конфигурации и примеры кода относятся к JavaScript, TypeScript, Vue или React, статья будет полезна всем разработчикам, так как форматер не ограничивается форматированием вышеуказанного.
Неформатированный код от ruDALL-E. Модель Kandinsky 2.1
План статьи
Погружение
Кратко расскажу о себе. За плечами у меня более 5 лет опыта коммерческой разработки, где большую часть времени я работал в качестве Fullstack-разработчика. Мой основной стек — Vue. В свободное время я делаю вклад в open source и веду активности в профильных сообществах.
В БФТ-Холдинге я разрабатываю четыре проекта, в два из которых удалось внедрить архитектурную методологию Feature-Sliced Design. Основной стек проектов — Quasar 2 + Pinia и, конечно, TypeScript.
Так как я разрабатываю frontend, то начнём погружение в плоскости JavaScript (TypeScript). JavaScript не ограничивает вас в правилах табуляции, пробелах, переносах строк, именовании переменных и т.д. при написании программного кода. Иными словами, человек может писать в своём неповторимом стиле, не следуя какому-то определённому code style. И для проекта, в котором работает только он один, скорее всего, это не вызывает никаких проблем и никак не влияет на скорость выполнения задач, но это не точно. А если в проекте задействована целая команда разработки, начинаются трудности. Например, с чтением и пониманием кодовой базы.
И так сойдёт :)
Есть объективные причины, почему необходимо придерживаться единого code style при разработке, и поэтому компании следят, чтобы разработка кодовой базы велась в соответствии с принятыми стандартами. На Хабр есть отличная статья, в которой подробно раскрывается эта тема.
Почти в любом проекте контроль за соблюдением принятого code style автоматизирован. С очень большой вероятностью разработчиков по рукам бьёт строгий и суровый ESLint, накаченный плагинами, а форматирование осуществляют форматеры кода. Сразу оговоримся, что ESLint тоже может выполнять форматирование.
Конечно, всё зависит от конкретной ситуации, и порой нет другого выхода, кроме как настроить форматирование с помощью линтера. И если так случилось, то в такой ситуации лучше, чтобы за весь процесс форматирования отвечал только линтер. У себя в проектах я придерживаюсь правила: анализ — отдельно, форматирование — отдельно.
Dprint от Midjourney
И вот мы плавно подобрались к форматерам. Сегодня на рынке доминирует prettier, так как он имеет большой набор плагинов, что позволяет ему форматировать большое количество форматов файлов. Также есть менее известные форматеры, такие как jscodeshift (форматирует по codemode скриптам), js-beautify, jsfmt, tsfmt и dprint. Это только те форматеры, которые поддерживаются или активно разрабатываются, и мы можем установить их в качестве dev-зависимости. Помимо этого, есть большое количество расширений для различных IDE и редакторов кода.
Если я забыл упомянуть какой-либо ещё живой форматер, пожалуйста, напишите об этом в комментариях.
Знакомьтесь, dprint
Dprint — это open source форматер кода, написанный на языке Rust. Форматирование выполняется очень быстро за счёт своего собственного алгоритма форматирования и WebAssembly-плагинов. Из коробки доступны плагины для форматирования javascript и typescript, json, markdown, toml и dockerfile. Плагины для форматирования javascript и typescript, json, markdown встроены в Deno cli. На момент написания статьи осуществляется поддержка process-плагинов — плагинов-обёрток (prettier, roslyn (C#/VB), exec). Плагины и конфигурации к ним можно подключать как локально, так и из удаленного источника. Написаны расширения для VSCode/VSCodium и IDE от компании JetBrains.
Dprint — это единственный известный мне форматер кода, который имеет возможность определения количества используемых форматером ядер центрального процессора во время форматирования. По правде сказать, за всё время использования мне ни разу не понадобилась такая возможность.
Также, нельзя не отметить дружелюбный CLI и отличную документацию.
Dprint от ruDALL-E. Модель Kandinsky 2.1
Говоря о dprint, нельзя не упомянуть автора инструмента. Это David Sherret, разработчик из Канады, который занимается программированием уже более 17 лет. В настоящее время он работает в компании Deno, до этого разрабатывал ПО в области медицины. Dprint не единственный его проект, он также разработал статический анализатор и генератор кода (ts-morph) и TypeScript AST Viewer.
Интеграция в проект
В качестве проекта создадим простенький react-проект при помощи Vite. Так как статья не про то, как создавать проекты с Vite, просто приведу команды с минимальными комментариями:
/* создаём проект */
yarn create vite react-dprint --template react-ts
/* Переходим в директорию и ставим пакеты */
cd react-dprint && yarn
/* Запускаем, проверяя работоспособность */
yarn dev
Готовый проект со всеми настройками можно пощупать, клонировав его из репозитория
Отлично — проект готов! Для того, чтобы dprint начал нам что-то форматировать, его необходимо установить и создать правильную конфигурацию. Dprint предлагает целых девять вариантов установки:
Shell (Mac, Linux, WSL)
Windows Installer
Powershell (Windows)
Scoop (Windows)
Homebrew (Mac)
Cargo
Deno
npm
asdf-vm (asdf-dprint)
Устанавливаем: npm install dprint --save-dev
.
Как говорилось ранее, у dprint дружелюбный CLI, поэтому пишем npx dprint --help
и получаем отличное руководство для старта. Разберём основные команды:
init
— создание конфигурационного файла для текущей директорииoutput-resolved-config
— напечатать все доступные конфигурации для выбранных плагиновcheck
— поиск неформатированных файловfmt
— форматировать
Создаём конфигурационный файл командой npx dprint init
. CLI предложит нам выбор из списка нативных плагинов:
Выбор после выполнения команды dprint init
Для примера выберем следующие плагины:
* dprint-plugin-typescript
* dprint-plugin-json
* dprint-plugin-markdown
Нажимая пробел, выбираем плагины: dprint-plugin-typescrip
t, dprint-plugin-json
, dprint-plugin-markdown
. Заглянем в бережно созданный для нас конфигурационный файл:
Секции
typescript
,json
иmarkdown
предназначены для указания правил к форматированиюincludes
— указываем какие директории и типы файлов необходимо отформатироватьexcludes
— указываем, что не надо форматироватьplugins
— пути к плагинам
Это не все настройки, но для старта их более чем достаточно. Теперь мы можем добавить команды в секцию scripts
файла package.json
.
Добавим команды для форматирования в секцию scripts
:
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"format": "npx dprint fmt",
"format:diff": "npx dprint fmt --diff"
}
Но на практике вы будете редко запускать форматирование через NPM (исключение — CI), поэтому настроим форматирование по сохранению файла в VSCode/VSCodium. Для этого необходимо в корне проекта создать (если отсутствует) директорию .vscode
, в ней создать (если отсутствует) файл settings.json
и положить туда следующее содержимое:
{
"[typescript]": {
"editor.defaultFormatter": "dprint.dprint",
"editor.formatOnSave": true
},
"[typescriptreact]": {
"editor.defaultFormatter": "dprint.dprint",
"editor.formatOnSave": true
},
"[json]": {
"editor.defaultFormatter": "dprint.dprint",
"editor.formatOnSave": true
}
}
Замечание: на момент написания статьи над инструментом ведётся активная разработка и нет стабильной версии, поэтому следует фиксировать внесённые изменения перед форматированием. Также стоит отметить, что за более чем полугодовой опыт использования не случилось ничего критического, т. е. того, что могло бы привести к необратимой потере незафиксированных изменений.
Приятной работы! Ну, а мы продолжим.
Dprint от Stable diffusion
Dprint от Stable diffusion
Dprint от Stable diffusion
Соревнования в скорости форматирования
Теперь, когда мы познакомились с форматером, настало время соревнований! В качестве плацдарма возьмём репозиторий фреймворка Quasar 2, а соревноваться в скорости будем c prettier.
Также необходимо сказать о параметрах тачки, на которой мы будем устраивать соревнования:
Система и параметры тачки
Скрипты будем запускать, используя WSL 2.
Подготовка
Создадим две директории с клоном репозитория Quasar 2. В первой директории настроим dprint, а во второй prettier. Также необходимо создать скрипт для каждой из директорий, который запустит форматирование, измерит время форматирования и подсчитает количество изменённых файлов.
В директории с форматером dprint
Скрипт:
# /bin/bash
echo "dprint:"
START=$(date + %s%N)
npx dprint fmt --incremental=false
END=$(date + %s%N)
milliseconds=$((($END - $START)/1000000))
seconds=$(($milliseconds/1000))
minutes=$(($seconds/1000))
echo "Количество миллисекунд: $milliseconds"
echo "Количество секунд: $seconds"
echo "Количество минут: $minutes"
echo "Количество затронутых файлов:"
git diff --shortstat | tail -n1
Мы используем флаг incremental
со значением false
для того, чтобы отключить пропуск тех файлов, которые не были изменены с момента последнего форматирования (на всякий пожарный).
Конфигурационный файл (dprint.json
):
{
"typescript": {
"indentWidth": 2,
"lineWidth": 120,
"quoteStyle": "alwaysDouble",
"semiColons": "always",
"trailingCommas": "never"
},
"includes": ["**/*.{ts,tsx,js,jsx,cjs,mjs}"],
"excludes": [
"**/node_modules",
"**/templates",
"**/test"
],
"plugins": [
"https://plugins.dprint.dev/typescript-0.75.0.wasm"
]
}
В директории с форматером prettier
Скрипт, измеряющий скорость форматирования в директории с prettier, ничем не отличается, кроме заголовка (echo "prettier:"
) вызова самого форматера:
npx prettier --write --loglevel=silent
(--loglevel=silent
— отключаем вывод логов). В отличие от dprint, который использует только один конфигурационный файл, для prettier необходим конфигурационный файл и ignore-файл.
Конфигурационный файл (.prettierrc.json
):
{
"semi": true,
"singleQuote": false,
"printWidth": 120,
"trailingComma": "none"
}
ignore-файл (.prettierignore
):
**/node_modules
**/templates
**/test
**/*.vue
**/*.md
**/*.json
**/*.xml
**/*.yml
**/*.css
**/*.html
**/*.prettierrc
Форматируем
Внимательный читатель заметил, что форматировать мы будет только javascript-файлы и typescript-файлы. В процессе форматирования мы будем заменять одинарные кавычки на двойные.
Замечание: перед запуском скриптов каждый репозиторий отформатирован соответствующим форматером! На момент произведения замеров в директориях содержалось 25132 файла (не считая конфигурационных файлов форматеров) и 3560 директорий. Также были удалены те файлы, на которых спотыкались форматеры.
Скриптиус форматиус
Результаты форматирования двух форматеров представлены в таблице ниже:
prettier | dprint | |
Количество изменённых файлов | 1103 | 1103 |
Затраченное время (в секундах) | 41 | 5 |
Как видим, dprint справился с задачей не просто быстро, а очень быстро и быстрее prettier почти в 9 раз. Если повторить такой сценарий, то значения времени будут не сильно отличаться. У dprint значения варьировались от 5 секунд до 7, а у prettier от 41 до 46 секунд.
И ещё раз посоревнуемся
У dprint есть потрясающий плагин-обёртка для prettier, который по заявлениям автора выполняет форматирование быстрее, чем сам prettier:
Цитата: Additionally it’s much faster. This plugin will format files in parallel and you can take advantage of the speed of dprint’s incremental formatting if enabled.
Подготовка
Чтобы это проверить, также создадим две директории и в каждую из них клонируем один большой проект из проектов компании, написанный на Vue. Скрипты для запуска форматирования будем использовать из предыдущего сравнения. А вот конфигурационные файлы форматеров потерпят изменения.
В директории с форматером dprint
Конфигурационный файл (dprint.json
):
{
"prettier": {
"singleQuote": true,
"tabWidth": 2,
"vueIndentScriptAndStyle": true
},
"includes": ["**/*.vue"],
"excludes": [
"**/node_modules",
"**/templates",
"**/test"
],
"plugins": [
"https://plugins.dprint.dev/prettier-0.13.0.json@dc5d12b7c1bf1a4683eff317c2c87350e75a5a3dfcc127f3d5628931bfb534b1"
]
}
В директории с форматером prettier
Конфигурационный файл (.prettierrc.json
):
{
"singleQuote": true,
"tabWidth": 2,
"vueIndentScriptAndStyle": true
}
ignore-файл (.prettierignore
):
**/node_modules
**/templates
**/test
**/*.vue
**/*.md
**/*.json
**/*.xml
**/*.yml
**/*.css
**/*.html
**/*.ts
**/*.tsx
**/*.jsx
**/*.js
**/*.prettierrc
Форматируем
Тест на внимательность: если вам кажется, что у вас дежавю, то советую вам отдохнуть. А пока следите за цифрами
В процессе форматирования мы будем заменять одинарные кавычки на двойные в файлах .vue
.
Замечание: перед запуском скриптов каждый репозиторий отформатирован соответствующим форматером! На момент произведения замеров в директориях содержалось 1029 файлов (не считая конфигурационных файлов форматеров) и 305 директорий. Также были удалены те файлы, на которых спотыкались форматеры.
Скриптиус форматиус
Результаты форматирования двух форматеров представлены в таблице ниже:
prettier | dprint | |
Количество изменённых файлов | 151 | 151 |
Затраченное время (в секундах) | 11 | 6 |
Магия
Итоги
Сегодня мы познакомились с замечательным инструментом для форматирования. Увидели, как просто и быстро его можно интегрировать в любой проект. А благодаря плагину-обёртке над prettier можно ускорить форматирование, просто перенеся правила в конфигурационный файл dprint.json
. Давайте теперь подведём итоги в виде плюсов и минусов данного форматера.
Плюсы:
Open source (MIT license)
Быстрый, как говорилось ранее, за счёт своего собственного алгоритма форматирования и WebAssembly-плагинов
Поддержка плагинов-обёрток (process-плагины), например, prettier
Подключение конфигураций как локально, так и из удалённого источника
Управление многопоточностью
CI
Отличный DX (developer experience), а именно: дружелюбный и функциональный CLI, отличная документация на официальном сайте и наличие playground.
Минусы:
Отсутствует стабильная версия
Нет нативной поддержки Vue, Svelte и других форматов
Недостаточный набор правил форматирования. Например, для javascript нет замены var на
let
илиconst
и поддержки вложенных тернарных операторов (скоро появится)Небольшое сообщество и всё отсюда вытекающее.
На этом всё. Надеюсь, вы попробуете в работе данный инструмент. Буду рад любым комментариям, и спасибо за уделённое время.