Самый быстрый форматер кода

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Всем привет! Меня зовут Андрей, я работаю в БФТ-Холдинге на должности ведущий инженер-разработчик и занимаюсь frontend-разработкой.

В статье подробно поговорим о самом быстром форматере кода. Подробно покажем, как интегрировать форматер в любой проект, настроим форматирование по сохранению в редакторах кода VSCode/VSCodium и посоревнуемся в скорости форматирования с prettier. На фоне хайпа нейросетей я решил разбавить статью изображениями форматера в представлении различных нейросетей. Надеюсь, будет интересно, погнали!

Несмотря на то, что статья с акцентом на frontend, а приведённые настройки конфигурации и примеры кода относятся к JavaScript, TypeScript, Vue или React, статья будет полезна всем разработчикам, так как форматер не ограничивается форматированием вышеуказанного.

Неформатированный код от ruDALL-E. Модель Kandinsky 2.1
Неформатированный код от ruDALL-E. Модель Kandinsky 2.1

План статьи

  • Погружение

  • Знакомьтесь, dprint

  • Интеграция в проект

  • Соревнования в скорости форматирования

    • Подготовка

    • В директории с форматером dprint

    • В директории с форматером prettier

    • Форматируем

  • И ещё раз посоревнуемся

    • Подготовка

    • В директории с форматером dprint

    • В директории с форматером prettier

    • Форматируем

  • Итоги

Погружение

Кратко расскажу о себе. За плечами у меня более 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
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 от 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 init

Для примера выберем следующие плагины:

 * dprint-plugin-typescript
 * dprint-plugin-json
 * dprint-plugin-markdown

Нажимая пробел, выбираем плагины: dprint-plugin-typescript, 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
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 и поддержки вложенных тернарных операторов (скоро появится)

  • Небольшое сообщество и всё отсюда вытекающее.

На этом всё. Надеюсь, вы попробуете в работе данный инструмент. Буду рад любым комментариям, и спасибо за уделённое время.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
У вас в проекте за форматирование отвечает ESLint?
0% Да 0
25% Нет 2
62.5% ESLint + Форматер 5
12.5% Другой вариант 1
Проголосовали 8 пользователей. Воздержались 2 пользователя.
Источник: https://habr.com/ru/companies/bft/articles/738320/


Интересные статьи

Интересные статьи

Рассказываем, откуда взялось название «Запуск Завтра», кто вдохновил Самата попробовать формат подкаста и что общего у собственного проекта и гончарной мастерской.
Всем привет! Хочу рассказать о том, как в мире хаоса и бардака упорядочить жизнь,  сохранить/повысить продуктивность, успеть сделать многое, не разочароваться, оглядываясь назад, систематизироват...
https://www.shadertoy.com/view/fs23WtКод игры написан целиком на языке математики.Разборка кода далее в публикации. Читать далее
С 2019 года в России работает система быстрых платежей (СБП) — возможность переслать деньги в любой банк — участник системы по номеру телефона и, что не менее важно, новы...
Нас часто спрашивают, насколько хорошо те или иные популярные мессенджеры хранят тайны своих пользователей — переписку и пересылаемые файлы, существуют ли риски взлома самих сервисов,...