December 8, 2021

Новое поколение сборщиков для JavaScript

В последние годы наметился интересный тренд - писать инструменты для JavaScript на других языках. В статье Axel Rauschmayer приводит (не полный) список примеров таких инструментов. О плюсах и минусах этого подхода можно почитать в статье, а я хотел бы подробнее остановиться на двух инструментах из этого списка.

Количество еженедельных скачиваний пакетов esbuild и @swc/core из NPM.

SWC

https://swc.rs/ - задумывался как аналог Babel, но написанный на Rust. Изначально позиционировался как очень быстрый компилятор для JavaScript и TypeScript. Потом стали называть себя быстрым компилятором для веба (чтобы это не значило). А недавно у них произошёл ребрендинг и теперь они позиционируют себя как платформа для веба написанная на Rust. Проект развивается с 2018 года, но популярность к нему стала приходить не так давно. Сначала SWC начали использовать в Deno (который тоже в свою очередь написано на Rust), потом его начали интегрировать во вторую версию Parcel которая зарелизилась осенью. И, наконец, в августе команда разработки Next объявила, что они начали интегрировать SWC вместо Babel и Terser. А сам автор SWC DongYoon Kang присоединился к команде Next.js.

Один из плюсов SWC в том, что консольная команда полностью совместима по аргументам с Babel. В документации обещают, что достаточно поменять название бинарного файла в скрипте запуска и всё будет работать. В моём случае так и произошло.

Если начинался SWC просто как компилятор для JavaScript/TypeScript, то со временем он превратился в целый набор инструментов (не зря теперь называют себя платформой) - теперь это минификатор и бандлер (через отдельную утилиту swcpack), поддерживает официальный лоадер для webpack и трансформер для Jest. Дополнительно есть вариант поставки самого SWC как WebAssembly модуля, так что его можно запускать прямо в браузере. Это открывает интересные возможности для разных браузерных плейграундов.

Поддерживает он и расширение за счёт плагинов на JavaScript. Но в отличии от Babel, у которого практически весь функционал разбит на плагины или пресеты (наборы плагинов), у SWC монолитная архитектура. И плагины тут скорее для конечных пользователей, которые хотят какой-то кастомизации. Проблема в том, что использование JavaScript плагинов очень сильно замедляет работу из-за накладных расходов по передачи данных между JS и Rust контекстами. Хотя и в этом направлении работы идут: #2175 - ищут способ ускорить передачу данных и #2635 - разрабатывают систему плагинов на Rust. Глобально мне не кажется это серьёзной проблемой, потому что сейчас новые предложения для JS появляются не так часто как во времена когда Babel только появился (и который тогда назывался 6to5). Но в общем стоит иметь в виду, что SWC не поддерживает экспериментальные фичи. Вот здесь есть полный список плагинов Babel, функционал которых поддерживает SWC.

Ещё одним минусом SWC является то, что он не поддерживает чтение конфигов tsconfig.json несмотря на то, что компиляция TypeScript в нём работает из коробки. Вместо этого они предлагают настройки через свой конфиг `.swcrc`. Решение не очень удобное и приводит к дублированию некоторых настроек. Не думаю, что там есть технические проблемы, скорее идеологические - поддержать все настройки tsc они никогда не смогут (да и нет такой цели), а объяснять каждому пользователю почему та или иная опция в tsconfig.json не сработала для SWC они, видимо, не хотят.

Благодаря тому, что SWC поставляется как Rust пакет, он легко встраивается в другие инструменты написанные на Rust. Выше уже упоминал про Deno, в котором компиляция и линтинг сделаны на SWC. Ещё интересный проект dprint - аналог Prettier. Интересно что dprint поддерживает плагины на основе Webassembly в отличии от самого SWC.

С тех пор как Babel сделал поддержку компиляции TypeScript сам tsc (TypeScript компилятор) от этого только выиграл. Babel и SWC используются только для компиляции кода, они никак никак не проверяют типы и просто вырезают их в процессе компиляции. Для проверки типов всё равно нужно отдельно запустить tsc, потому что только он умеет это делать. Даже команда Deno, которая на первых порах очень хвалилась тем, что поддерживают TypeScript с проверкой типов из коробки (они собирали tsc в V8 snapshot и компилировали TypeScript в JavaScript на ходу) ради ускорения работы сначала начали компилировать код с помощью SWC, теперь ещё хотят по дефолту отключить проверку типов. Хотя это была одна из самых продающих фичей Deno, когда Райан только начинал рассказывать о первых версиях. Так вот в планах у автора SWC есть задача изменить текущее положение и реализовать проверку типов на Rust - Issue #571. Задача амбициозная, но будет интересно посмотреть что из этого может получиться.

С тех пор как автор перешёл на зарплату в Vercel активность в разработке выросла в разы. Так что похоже у SWC в ближайшей перспективе всё будет хорошо. Экосистема инструментов на Rust в JavaScript экосистеме постепенно растёт, тусовка там дружная, все друг другу контрибьютят.

esbuild

https://esbuild.github.io/ - это уже не просто компилятор для JavaScript/TypeScript, а полноценный web сборщик. Т.е. он конкурент webpack и babel вместе взятым. Первая версия была опубликована в начале 2020 года. Несмотря на то, что он появился почти на два года позже SWC он при этом в разы популярнее (по количеству скачиваний на npm). Это ещё раз говорит о том, что недостаточно сделать хороший инструмент, нужно ещё его грамотно продать пользователям. Например, анимированный график на главной странице esbuild очень хорошо показывает его главное достоинство. Кстати, автор esbuild - Evan Wallace, сооснователь Figma.

Написан на языке Go. Тоже поддерживает запуск через WebAssembly как в браузере так и в Node.js, но рекомендуется такой способ запуска только для браузера из-за особенностей работы Node.js с WebAssemble - падение скорости может быть до 10 раз.

Может работать в двух режимах - transform и bundle. Transform - просто преобразует указанные файлы из одного формата в другой. Т.е. такой аналог Babel. Режим build - это уже полноценная сборка бандла. Указываешь входной файл, esbuild проходит по всем импортам и собирает все зависимости в один файл. Поддерживает watch режим и dev сервер. Нативно поддерживаются только несколько базовых лоадеров - для CSS, JSON и файлов. Для CSS не поддерживаются модули, а про less/sass можно и не вспоминать. К сожалению пока не поддерживается компиляция в es5, поэтому если вам важна поддержка старых браузеров, то esbuild не подойдёт. Но в планах такая поддержка есть.

Зато есть возможность писать свои плагины для esbuild на JavaScript и, несмотря на то, что она помечена как экспериментальная, сообщество уже написало довольно много плагинов. Там и для less есть плагин и штук пять плагинов для sass. Правда похоже что использование этих плагинов может сильно замедлить работу esbuild. Например, плагин для postcss сам читает исходные css файлы, прогоняет их через postcss и сохраняет результат во временные файлы и никак не кеширует результат. Непонятно как в итоге эти плагины дружат между собой, могут ли они последовательно обрабатывать файлы и т.д.

Немного странно выглядит и способ конфигурации esbuild. Всё кроме плагинов можно настроить через аргументы командной строки, но если вам это станет неудобно из-за большого количества опций или захочется подключить плагин, то никакого `.esbuildrc` нет. Предлагается использовать JavaScript API которое предоставляет esbuild и написать произвольный скрипт для запуска.

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

Если SWC интегрируется как rust пакет в другие проекты, то esbuild в основном интегрируется через свой JavaScript API. Наиболее популярные проекты которые его используют Vite и Snowpack. Забавно, что они используют esbuild для противоположных вещей - Vite только в режиме разработки, чтобы предсобирать зависимости, а Snowpack использует его по дефолту для сборки конечного production бандла.

Как видно из графика в начале статьи популярность SWC и esbuild постоянно растёт. Но если добавить на этот график Babel и Webpack, то видно что их популярности пока ничто не угрожает. Картина может сильно измениться по мере того, как другие популярные инструменты, такие как Next.js, начинают их использовать внутри себя. Посмотрим как изменится ситуация через год или два.

Количество еженедельных скачиваний пакетов @babel/core, webpack, esbuild и @swc/core из NPM.