Rome: что умеет новый универсальный инструмент JavaScript

Создатель Yarn и Babel Себастиан Маккензи (Sebastian McKenzie) долго работал над универсальным инструментом для JavaScript-разработчиков. 26 февраля он представил проект Rome. «Все дороги ведут в Рим», — таков девиз создателей инструмента.
Содержание
- Что такое Rome
- Как это выглядит на практике
- Почему Rome — перспективный и удобный инструмент?
- Как устроена сборка в Rome
- Что у Rome с качеством сборки
- Перспективы Rome в больших проектах
- Сокращение размеров сборки в Rome
- Дальнейшая оптимизация
- Разделение кода в Rome
- Использование Rome в CLI
Что такое Rome
Rome — это универсальный набор инструментов для разработки на JavaScript. Он компилирует и собирает JavaScript-проекты, выполняет линтинг и тайп-чекинг, запускает тесты, а также форматирует код.
Как это выглядит на практике
Создатели представили Rome несколько дней назад. Но в CLI уже есть полезная информация об использовании инструмента. Вот некоторые команды:
rome bundle
— собирает модули в проекте JavaScript;rome compile
— компилирует единый файл;rome develop
— запускает локальный сервер;rome parse
— парсит единый файл и выводит абстрактное синтаксическое дерево;rome resolve
— резолвит файл;rome analyzeDependencies
— анализирует и выводит зависимости.
Подробнее об использовании Rome в CLI пойдёт речь ниже.
Почему Rome — перспективный и удобный инструмент?
Rome можно назвать новым подходом к использованию инструментов для разработки на JavaScript. Он больше похож на универсальные решения, которые применяют в крупных компаниях, чем на опенсорсные инструменты, каждый из которых решает какую-то одну задачу. То есть при использовании Rome разработчик использует только его, а не пропускает код через несколько разных инструментов.
Это решает одну из проблем, с которыми сталкиваются разработчики при использовании популярных сборщиков типа Webpack или Rollup. Она заключается в том, что анализ и оптимизация программы становятся слишком «дорогими», так как каждый инструмент независимо от других парсит код и строит своё абстрактное синтаксическое дерево (AST).
Как устроена сборка в Rome
Архитектуру Rome можно назвать уникальной. Компиляция выполняется помодульно. Это позволяет обрабатывать каждый модуль в пуле рабочих потоков. Это хорошо работает на уровне обработки модулей, но создаёт проблемы на уровне сборки. Чтобы избежать необходимости повторного парсинга модулей, необходимо создавать единое пространство имён. То есть все модули в проекте находятся в одной области видимости.
Чтобы сделать сборку возможной с учётом помодульной обработки, Rome добавляет префиксы всем переменным в области видимости. Эти префиксы генерируются из названия файлов. Например, переменная foo
в модуле test.js
превращается в test_js_foo
.
Этот подход применяется к импортируемым и экспортируемым идентификаторам на уровне модулей. Это значит, что при экспорте можно использовать только название файла и идентификатор экспортируемой сущности.
Например, если имя файла — test.js
, экспорт — export const foo = 1;
, на выходе получаем const ___R$test_js$foo = 1;
. Ещё один пример с именем файла index.js
. Импорт выглядит так:
На выходе получаем: console.log(___R$test_js$foo);
.
Что у Rome с качеством сборки
В современной веб-разработке инструменты определяют качество и размер приложения. Это значит, что разработчики должны обращать внимание на содержание сборок Rome. Автор оригинальной публикации всегда проверяет, будет ли инструмент объединять модули в единое замыкание как Rollup, или сохранит границы модулей с помощью замыканий и рантайм-загрузчиков как Webpack.
Rome создаёт сборки с единым замыканием. Это похоже на то, как работает Rollup. Примеры ниже.
Модуль:
Сборка:
Пока Rome не предлагает инструментов для уменьшения размеров сборки. Однако дополнительное использование плагина Terser улучшает результат.
Примеры показывают, что сборки Rome можно серьёзно оптимизировать. В идеале сборщик должен знать режим работы и поддерживать замыкания и директиву strict mode
при работе с модулями ES. Он также мог бы поднимать глобальные декларации на уровень модуля.
Перспективы Rome в больших проектах
Посмотрим на более сложный пример с двумя модулями, в которых есть общие зависимости.
entry.tsx
:
other.tsx
:
react.tsx
:
type VNode = {
type: string;
props: any;
children: Array<VNode|string>
};
function createElement(
type: string,
props: any,
...children: Array<VNode|string>
): VNode {
return { type, props, children };
}
export default { createElement };
Сборка с помощью rome bundle entry.tsx out
генерирует директорию с файлом index.js
.
Пример немного сложнее предыдущего, но структура в нём сохраняется.
Без реализации модулей и «мёртвого кода» три исходных модуля превращаются в одно замыкание.
Сокращение размеров сборки в Rome
Как сказано выше, в данный момент в Rome нет инструментов для уменьшения размеров сборки. Можно пропускать полученный результат через Terser. Если сделать это с кодом из примера выше, на выходе получаем следующее (код отформатирован для удобства чтения):
После минимизации результат выглядит довольно хорошо. Надо учитывать, что это очень простое приложение. Результаты работы с реальным приложением могут быть другими.
Дальнейшая оптимизация
Автор оригинальной публикации работает над инструментом, который оптимизирует сборки JavaScript. В качестве эксперимента он пропустил созданную с помощью Rome сборку через этот инструмент до обработки с помощью Terser. В итоге получился близкий к идеальному результат: без «мёртвого кода» и функций-обёрток, а также с использованием преимуществ современного синтаксиса JavaScript.
Этот пример кажется перспективным.
Разделение кода в Rome
Похоже, Rome не поддерживает динамический импорт и разделение кода. Использование import()
позволяет импортировать модуль, но он ведёт себя как при статическом импорте. Исходный оператор import()
не меняется при сборке, что приводит к ошибке.
Пока неизвестно, как разделение и фрагментирование кода повлияет на качество сборки. Эффективность этих действий зависит от доступа к переменным, которые попадают в одну сборку из другой.
Использование Rome в CLI
Ниже приводится вывод хелпа Rome в CLI.
$ rome --help
Usage: rome [command] [flags]
Options
--benchmark
--benchmark-iterations <num>
--collect-markers
--cwd <input>
--focus <input>
--grep <input>
--inverse-grep
--log-path <input>
--logs
--log-workers
--markers-path <input>
--max-diagnostics <num>
--no-profile-workers
--no-show-all-diagnostics
--profile
--profile-path <input>
--profile-sampling <num>
--profile-timeout <num>
--rage
--rage-path <input>
--resolver-mocks
--resolver-scale <num>
--silent
--temporary-daemon
--verbose
--verbose-diagnostics
--watch
Code Quality Commands
ci install dependencies, run lint and tests
lint run lint against a set of files
test run tests
--no-coverage
--show-all-coverage
--update-snapshots
Internal Commands
evict evict a file from the memory cache
logs
rage
Process Management Commands
restart restart daemon
start start daemon (if none running)
status get the current daemon status
stop stop a running daemon if one exists
web
Project Management Commands
config
publish TODO
run TODO
Source Code Commands
analyzeDependencies analyze and dump the dependencies of a file
--compact
--focus-source <input>
bundle build a standalone js bundle for a package
compile compile a single file
--bundle
develop start a web server
--port <num>
parse parse a single file and dump its ast
--no-compact
--show-despite-diagnostics
resolve resolve a file
Адаптированный перевод статьи Rome, a new JavaScript Toolchain by Jason Miller. Мнение автора оригинальной публикации может не совпадать с мнением администрации Хекслета.
Дмитрий Дементий
5 лет назад