Декораторы представляют собой механизм метапрограммирования, позволяющий модифицировать поведение классов, методов, аксессоров и свойств на этапе определения. В контексте сборки приложений они рассматриваются как синтаксический сахар, требующий трансформации через транспиляторы, поскольку стандарт JavaScript долгое время не включал их в стабильную спецификацию.
Parcel как сборщик модулей не реализует декораторы самостоятельно, а делегирует их обработку соответствующим трансформерам — в первую очередь Babel или TypeScript. Это делает поддержку гибкой и зависящей от выбранного стека.
Декоратор — это функция, принимающая целевой элемент программы и изменяющая его поведение или метаданные.
Простейший пример:
function readonly(target, key, descriptor) {
descriptor.writable = false;
return descriptor;
}
class User {
@readonly
name() {
return "Alex";
}
}
Декоратор применяется во время определения класса, до создания его экземпляров.
Основные виды декораторов:
В экосистеме JavaScript существует две основные реализации:
Они несовместимы по сигнатуре и поведению.
Legacy-версия:
function log(target, key, descriptor) {
const original = descriptor.value;
descriptor.value = function (...args) {
console.log(key, args);
return original.apply(this, args);
};
return descriptor;
}
Новая версия Stage 3:
function log(value, context) {
return function (...args) {
console.log(context.name, args);
return value.apply(this, args);
};
}
Разница критична для конфигурации сборки, так как требует разных трансформеров.
Parcel работает как «оркестратор» трансформаций. Он не интерпретирует декораторы, а передает файлы в соответствующие трансформеры.
Основные пути обработки:
@parcel/transformer-babel@parcel/transformer-typescript-tscParcel автоматически определяет наличие Babel или TypeScript конфигурации и применяет соответствующий pipeline.
Для поддержки legacy-декораторов используется Babel-плагин:
npm install --save-dev @babel/plugin-proposal-decorators
Конфигурация .babelrc или
babel.config.json:
{
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }]
]
}
Parcel подхватывает эту конфигурацию автоматически при использовании Babel-трансформера.
TypeScript поддерживает декораторы на уровне компилятора.
В tsconfig.json:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
Важный момент: TypeScript по умолчанию использует legacy-реализацию. Новые decorators требуют другой стратегии и пока ограниченно поддерживаются через комбинацию TypeScript + Babel.
Parcel использует @parcel/transformer-typescript-tsc,
который вызывает tsc-совместимую трансформацию.
Особенности:
Одновременное использование двух транспилеров может привести к двойной трансформации декораторов.
Типичный проблемный сценарий:
Это приводит к некорректному рантайм-коду.
Для устранения конфликта используется принцип единственного источника трансформации:
Parcel строит граф зависимостей и применяет трансформеры по цепочке:
Декораторы исчезают на этапе трансформации AST, превращаясь в обычные вызовы функций и обертки.
Parcel активно поддерживает новые ECMAScript-фичи через транспиляцию, даже если они не являются частью текущего стандарта движка.
Ключевые категории:
Parcel поддерживает top-level await в ES modules без дополнительной конфигурации в современных режимах.
Пример:
const data = await fetch("/api/data").then(r => r.json());
export default data;
При сборке Parcel:
Эти фичи поддерживаются через Babel/TypeScript без дополнительных настроек.
const value = user?.profile?.name ?? "guest";
Parcel не изменяет семантику, а лишь обеспечивает корректную транспиляцию для старых сред выполнения.
const module = await import("./feature.js");
Parcel воспринимает dynamic import как сигнал к code splitting:
Часто декораторы используются в архитектурах внедрения зависимостей.
Пример упрощённого контейнера:
const registry = new Map();
function Injectable(target) {
registry.set(target.name, new target());
}
@Injectable
class Service {}
Parcel не влияет на семантику DI, но обеспечивает корректную трансформацию синтаксиса.
При использовании TypeScript-опции:
{
"emitDecoratorMetadata": true
}
добавляется зависимость от reflect-metadata:
import "reflect-metadata";
function logType(target, key) {
const type = Reflect.getMetadata("design:type", target, key);
console.log(type);
}
Parcel не генерирует метаданные, но обеспечивает корректную упаковку runtime-библиотеки.
Декораторы увеличивают нагрузку на этап сборки из-за необходимости AST-преобразований.
Факторы влияния:
Parcel компенсирует это за счёт:
Parcel сохраняет результаты трансформации декораторов в кэш:
При изменении исходного кода пересобирается только затронутая часть графа.
Типичные ошибки при работе с декораторами в Parcel:
Пример ошибки:
Unexpected token @
Причина — отсутствие декоратор-трансформера в цепочке обработки.
С точки зрения Parcel декораторы являются:
Они не влияют на граф модулей напрямую, но изменяют структуру генерируемого кода, что может косвенно влиять на оптимизацию и tree-shaking.
Развитие стандарта декораторов напрямую влияет на работу сборщиков.
Ожидаемые изменения:
Parcel адаптируется к этим изменениям через обновление трансформеров и упрощение конфигурационного слоя.