Пишем плагин к SWC на Rust
• build, rust
Введение
В недавнем посте про Babel обсуждали тему трансформации кода и какие инструменты для этого есть. Один из путей – кастомный лпагин к SWC.
Конечно, на эту тему есть официальная документация. Однако во-первых, документация подустарела и требует исправления, а во-вторых, хотелось показать, что всё не так сложно, как может показаться. Поехали?
Опишем задачу
Задачу выбрал выдуманную, тривиальную, но вполне реальную.
Часто бывает так, что в коде могут содержаться отладочные вызовы, параметры и так далее. В случае отдельной функции удалить это из бандла тривиально (должно хватить просто разметки функции, как “чистой”), а вот в случае параметров всё немного сложнее. Рассмотрим код:
run('smth', 'Секретная отладка');Подобных вызовов в коде может быть порядочно, информация – не очень публичной, поэтому вполне резонно может появиться желание удалить это в процессе продовой сборки.
Стартуем создание плагина
Я буду корректировать доку. Информация актуальна на момент написания.
Начинаем с установки Rust. На сайте есть инструкция, лучше следовать ей.
Дальше устанавливаем swc_cli и добавляем новый таргет для сборки:
cargo install swc_cli
rustup target add wasm32-wasip1Ставим @swc/cli и @swc/core. На всякий случай приведу версии, которые были у меня:
@swc/cli@0.8.1
@swc/core@1.15.43Затем зовём swcx и создаём плагин:
npx swcx plugin new --target-type wasm32-wasip1 plugin-dirЭто не swc. а swcx – новая консольная тулза, сейчас в бете
Дальнейшие манипуляции будут в plugin-dir/src/lib.rs.
Правим код на Rust
В эпоху всевозможных ии и агентов сделать плагин с простыми манипуляциями будет не сильно сложно:
// Импорты
use swc_core::plugin::{plugin_transform, proxies::TransformPluginProgramMetadata};
use swc_core::ecma::{
ast::*,
visit::{visit_mut_pass, VisitMut, VisitMutWith},
transforms::testing::test,
};
pub struct TransformVisitor;
// Реализация обхода элементов
impl VisitMut for TransformVisitor {
// Обход вызовов
fn visit_mut_call_expr(&mut self, e: &mut CallExpr) {
e.visit_mut_children_with(self);
// Проверяем, что это нужная нам функция
let is_run_call = match &e.callee {
Callee::Expr(callee_expr) => match &**callee_expr {
Expr::Ident(ident) => ident.sym == *"run",
_ => false,
},
_ => false,
};
if is_run_call {
if e.args.len() > 1 {
// Удаляем второй аргумент
e.args.truncate(1);
}
}
}
}
// Входная точка плагина
#[plugin_transform]
pub fn process_transform(mut program: Program, _metadata: TransformPluginProgramMetadata) -> Program {
program.visit_mut_with(&mut TransformVisitor);
program
}
// Минимальный тест со снепшотом
test!(
Default::default(),
|_| visit_mut_pass(TransformVisitor),
boo,
r#"run(a, b);"#
);Я не являюсь хоть сколько-нибудь знающим разработчиком на Rust, это просто минимальный рабочий пример.
Собираем и используем плагин
Собираем с помощью команды:
cargo build-wasip1 --releaseПлагин соберётся по пути plugin-dir/target/wasm32-wasip1/release/plugin-dir.wasm. Путь зависит от настроек, которые вы указывали ранее.
Использование достаточно простое (и для простоты же используется синхронный transformSync), но почему-то в документации этого тоже нет:
// test.mjs
import { transformSync } from '@swc/core';
const transformCode = (code) => {
const result = transformSync(code, {
jsc: {
experimental: {
plugins: [
['<путь до собранного wasm файла>', {}]
]
}
}
});
return result.code;
};
console.log(transformCode('run(a, b)'));
// -> run(a);Итого
У меня нет хорошего бенчмарка – насколько данный подход быстрее того же самого Babel. По всей видимости, при каждом вызове загрузка плагина происходит заново? Но я подумаю, что с этим можно сделать :)
Цель поста – показать, что подобный код заводится без особых усилий и осиливается за 10 минут. Возможно, я кому-то подскажу и сэкономлю пару минут в процессе.
Да, добавляется сложность со сборкой и доставкой плагина, в этом плане путь на JavaScript проще. А в промышленных “Enterprise”-проектах отпугнёт секция “experimental” из примера. Получается, всё это – на свой страх и риск?
В каком-то смысле, но нужно же с чего-то начинать) А кто-то скажет, что скорость стоит того.
Обсудить в Telegram



