Представьте: вы находитесь в модном ресторане, где меню написано на языке будущего. Вы голодны прямо сейчас, но повар говорит только на диалекте вчерашнего дня. Вступите в игру Babel — космический переводчик, который превращает ваш футуристический JavaScript в нечто, что понял бы даже IE6 (если бы он не был, ну, мёртв). Но что если вы хотите изобрести свой собственный кулинарный синтаксис? Вот тут-то и приходит на помощь волшебство плагинов. Берите лопату для теста, мы займёмся преобразованием AST!
Зачем создавать собственный плагин Babel?
Когда я впервые попробовал себя в разработке плагинов, я спросил: «Почему бы не использовать существующие инструменты?» Затем я попытался написать leftPad()
в 37-й раз и понял: настоящая сила заключается в изобретении собственных абстракций. Плагины позволяют вам:
- Приспосабливать синтаксис JavaScript под свои нужды (например, добавлять операторы конвейера 👀);
- Автоматизировать утомительные шаблоны (прощайте, повторяющиеся проверки ошибок);
- Экспериментировать с функциями языка до их одобрения TC39;
- Путать коллег (шутка… в основном).
Думайте об этом как о том, чтобы дать JavaScript порцию эспрессо, смешанного с радиоактивным паучьим ядом. Вы получаете суперсилы, но можете вырастить дополнительные конечности.
Настройка вашей мастерской плагинов
Прежде чем мы призовём демонов синтаксиса, давайте настроим наш котёл:
- Установите предварительные требования:
npm install --save-dev @babel/core @babel/cli
- Создайте каркас вашего плагина (мой называется
babel-plugin-disappear
):
// disappear-plugin.js
export default function ({ types: t }) {
return {
visitor: {
// Здесь скоро произойдёт наше волшебство...
}
};
}
- Конфигурация для пробного запуска (.babelrc):
{
"plugins": ["./disappear-plugin.js"]
}
Совет: назовите свой плагин как-нибудь драматично. Я чуть было не назвал свой babel-plugin-thanos
, но решил, что исчезновение 50% вашего кода может быть слишком буквально.
AST: Универсальный ключ к вашему коду
Вот где всё становится потрясающе. Весь код — это просто Абстрактные Синтаксические Деревья — вложенные объекты, описывающие каждую запятую, скобку и точку с запятой. Взгляните на AST для foo === bar
:
Эта иерархическая структура — вот что делает плагины Babel похожими на проведение нейрохирургии с помощью садовых ножниц. Вы не редактируете текст — вы переписываете реальность на уровне концепций.
Создание нашего плагина «Исчезающий»
Давайте создадим плагин, который будет удалять операторы console.log
во время сборки — идеально для тех моментов, когда вы случайно оставили отладчики в продакшене.
Шаг 1: Определите свою цель
Мы хотим найти все вызовы console.log
. Их AST выглядит так:
{
type: "CallExpression",
callee: {
type: "MemberExpression",
object: { type: "Identifier", name: "console" },
property: { type: "Identifier", name: "log" }
}
}
Шаг 2: Шаблон посетителя
Плагины работают, проходя по AST и реагируя на типы узлов, как чрезмерно усердные охранники:
visitor: {
CallExpression(path) {
if (
t.isMemberExpression(path.node.callee) &&
t.isIdentifier(path.node.callee.object, { name: "console" }) &&
t.isIdentifier(path.node.callee.property, { name: "log" })
) {
// Уничтожить этот узел!
path.remove();
}
}
}
Шаг 3: Повышение уровня с помощью опций
Захардкоженные поведения остались в 2015 году. Давайте сделаем наш плагин настраиваемым:
export default function ({ types: t }) {
return {
visitor: {
CallExpression(path, state) {
const targets = state.opts.targets || ['log'];
// Проверка по настроенным целям
}
}
};
}
Теперь пользователи могут удалять warn
или error
тоже:
{
"plugins": [
["./disappear-plugin.js", { "targets": ["log", "warn"] }]
]
}
Тестирование: Не выпускайте голодного Годзиллу
Всегда тестируйте свои монстры, преобразующие AST, если не хотите ломать продакшен. Попробуйте @babel/parser
и @babel/traverse
:
import { parse } from '@babel/parser';
import traverse from '@babel/traverse';
const code = `console.log("Oops")`;
const ast = parse(code);
traverse(ast, {
CallExpression: pluginVisitor // Ваша логика посетителя здесь
});
// Должно быть пусто!
console.log(ast.program.body);
Для реальных проектов используйте babel-plugin-tester — это как модульные тесты для вашего искривления реальности:
import pluginTester from 'babel-plugin-tester';
import plugin from '../disappear-plugin';
pluginTester({
plugin,
tests: {
'disappears console.log': {
code: 'console.log("vanish")',
output: '' // Ожидаемый пустой вывод
}
}
});
Продвинутое колдовство
Как только вы освоите основы, попробуйте эти мощные приёмы:
Манипуляция путями
Меняйте переменные как мошенник:
BinaryExpression(path) {
if (path.node.operator === "===") {
// Поменяйте местами left/right как котлеты для бургера
const { left, right } = path.node;
path.node.left = right;
path.node.right = left;
}
}
Вход: foo === bar
→ Выход: bar === foo
Хаос достигнут.
Генерация кода с нуля
Когда вам нужно внедрить код как инфузию синтаксиса:
FunctionDeclaration(path) {
// Добавить в начало "console.error('Here be dragons')"
path.get('body').unshiftContainer(
'body',
t.expressionStatement(
t.callExpression(
t.memberExpression(
t.identifier('console'),
t.identifier('error')
),
[t.stringLiteral('Here be dragons')]
)
)
);
}
Когда на самом деле стоит создать плагин?
Серьёзный разговор: Не каждая проблема требует решения с помощью AST. Но когда вы видите повторяющиеся шаблоны, которые можно сделать чище на уровне синтаксиса? Это ваш золотой билет. Вот несколько законных случаев использования:
- Доменно-специфичные языки (например, пользовательские построители запросов);
- Автоматическая инструментация производительности;
- Переключатели поведения для разработки и продакшена;
- Навязывание архитектурных паттернов (например, все сетевые вызовы через шлюз).
Просто помните: с великой силой приходит великая ответственность не создавать плагин, который заменяет все точки с запятой на эмодзи. (Если только это не пятница. Тогда можно.)
Полная шпаргалка
Так что в следующий раз, когда вы будете проклинать конфигурацию Babel, помните: вы не настраиваете инструмент — вы держите генератор синтаксических червоточин. Теперь идите и создайте плагин, который заменяет undefined
на ¯\_(ツ)_/¯
— я верю в вас!