@rollup/plugin-babel

Плагин @rollup/plugin-babel обеспечивает интеграцию Rollup и Babel, позволяя выполнять транспиляцию JavaScript-кода во время сборки. Он используется для преобразования современного синтаксиса ECMAScript, JSX, TypeScript и других расширений языка в код, совместимый с требуемыми версиями браузеров или сред выполнения.

Без использования Babel Rollup занимается преимущественно анализом модулей, объединением зависимостей и оптимизацией итогового бандла. Поддержка старых браузеров и преобразование нестандартного синтаксиса являются задачей Babel.

Основные возможности плагина:

  • транспиляция современного JavaScript;
  • поддержка JSX;
  • интеграция с React;
  • использование Babel Presets;
  • использование Babel Plugins;
  • генерация совместимого кода для различных браузеров;
  • работа с TypeScript через Babel;
  • комбинирование с другими плагинами Rollup.

Установка

Установка выполняется через npm:

npm install --save-dev @rollup/plugin-babel @babel/core

Минимальный набор всегда включает:

npm install --save-dev \
@rollup/plugin-babel \
@babel/core

Для поддержки современных возможностей обычно дополнительно устанавливаются пресеты:

npm install --save-dev \
@babel/preset-env

Для React:

npm install --save-dev \
@babel/preset-react

Для TypeScript:

npm install --save-dev \
@babel/preset-typescript

Базовое подключение

Простейшая конфигурация Rollup:

import babel from '@rollup/plugin-babel';

export default {
    input: 'src/index.js',

    output: {
        file: 'dist/bundle.js',
        format: 'esm'
    },

    plugins: [
        babel({
            babelHelpers: 'bundled'
        })
    ]
};

Ключевым параметром является:

babelHelpers

Без него плагин работать не будет.


Параметр babelHelpers

Babel может добавлять в код различные вспомогательные функции.

Например:

class User {}

может превратиться в:

function _classCallCheck(instance, Constructor) {
    // ...
}

Способ подключения таких функций определяется параметром babelHelpers.

Поддерживаются несколько режимов.

bundled

Наиболее распространённый вариант.

babel({
    babelHelpers: 'bundled'
})

Все helper-функции включаются внутрь итогового бандла.

Преимущества:

  • простая настройка;
  • не требует дополнительных зависимостей.

Недостаток:

  • при сборке нескольких файлов возможны дублирования helper-кода.

runtime

Использует пакет Babel Runtime.

Установка:

npm install --save @babel/runtime

Конфигурация:

babel({
    babelHelpers: 'runtime'
})

Пример настройки Babel:

{
    "plugins": [
        "@babel/plugin-transform-runtime"
    ]
}

Преимущества:

  • отсутствие дублирования;
  • меньший размер сборок.

Недостаток:

  • появляется внешняя зависимость.

inline

Все helper-функции внедряются непосредственно в место использования.

babel({
    babelHelpers: 'inline'
})

Используется редко.

Недостаток:

  • значительное дублирование кода.

external

Предполагает подключение helper-функций извне.

babel({
    babelHelpers: 'external'
})

Используется главным образом при разработке библиотек и специальных сценариях сборки.


Использование preset-env

Самый популярный пресет Babel — @babel/preset-env.

Создание файла:

{
    "presets": [
        "@babel/preset-env"
    ]
}

Либо:

babel({
    babelHelpers: 'bundled',
    presets: ['@babel/preset-env']
})

Пример исходного кода:

const sum = (a, b) => a + b;

После транспиляции:

var sum = function (a, b) {
    return a + b;
};

Настройка целевых браузеров

Babel позволяет определять список поддерживаемых платформ.

Файл .browserslistrc:

> 0.5%
last 2 versions
not dead

Либо:

{
    "presets": [
        [
            "@babel/preset-env",
            {
                "targets": {
                    "chrome": "90",
                    "firefox": "88"
                }
            }
        ]
    ]
}

В этом случае Babel будет выполнять только необходимые преобразования.


Исключение файлов

Плагин поддерживает фильтрацию файлов через include и exclude.

include

babel({
    babelHelpers: 'bundled',
    include: ['src/**/*.js']
})

Будут обработаны только файлы из каталога src.


exclude

babel({
    babelHelpers: 'bundled',
    exclude: 'node_modules/**'
})

Наиболее распространённая настройка.


Комбинированное использование

babel({
    babelHelpers: 'bundled',

    include: [
        'src/**/*.js'
    ],

    exclude: [
        'src/vendor/**'
    ]
})

Работа с React

Для React необходим пресет:

npm install --save-dev @babel/preset-react

Конфигурация:

babel({
    babelHelpers: 'bundled',
    presets: ['@babel/preset-react']
})

Файл:

function App() {
    return <h1>Hello</h1>;
}

После обработки JSX преобразуется в вызовы React API.


Автоматический JSX Runtime

Современный React использует автоматический runtime.

Настройка:

{
    "presets": [
        [
            "@babel/preset-react",
            {
                "runtime": "automatic"
            }
        ]
    ]
}

Пример:

export default function App() {
    return <div>Application</div>;
}

Импорт React вручную больше не требуется.


Работа с TypeScript

Babel способен обрабатывать TypeScript без использования компилятора TypeScript для генерации JavaScript.

Установка:

npm install --save-dev \
@babel/preset-typescript

Конфигурация:

babel({
    babelHelpers: 'bundled',
    presets: ['@babel/preset-typescript']
})

Пример:

interface User {
    name: string;
}

const user: User = {
    name: 'Alex'
};

После транспиляции типы удаляются.


Расширения файлов

По умолчанию Babel работает не со всеми типами файлов.

Для поддержки TypeScript и JSX часто используется настройка:

babel({
    babelHelpers: 'bundled',

    extensions: [
        '.js',
        '.jsx',
        '.ts',
        '.tsx'
    ]
})

Полный пример:

import babel from '@rollup/plugin-babel';

export default {
    input: 'src/index.tsx',

    output: {
        dir: 'dist',
        format: 'esm'
    },

    plugins: [
        babel({
            babelHelpers: 'bundled',

            extensions: [
                '.js',
                '.jsx',
                '.ts',
                '.tsx'
            ]
        })
    ]
};

Использование Babel Plugins

Кроме пресетов Babel поддерживает большое количество отдельных плагинов.

Установка:

npm install --save-dev \
@babel/plugin-proposal-decorators

Подключение:

babel({
    babelHelpers: 'bundled',

    plugins: [
        ['@babel/plugin-proposal-decorators', {
            legacy: true
        }]
    ]
})

Плагины позволяют добавлять поддержку:

  • декораторов;
  • optional chaining;
  • nullish coalescing;
  • class properties;
  • pipeline operator;
  • других экспериментальных возможностей.

Использование конфигурационного файла Babel

Плагин автоматически ищет:

.babelrc

или

babel.config.json

Пример:

{
    "presets": [
        "@babel/preset-env"
    ]
}

После этого конфигурация Rollup может оставаться минимальной:

babel({
    babelHelpers: 'bundled'
})

Такой подход удобен для крупных проектов.


Настройка babelrc

По умолчанию поиск конфигурации Babel включён.

Явное указание:

babel({
    babelHelpers: 'bundled',
    babelrc: true
})

Отключение:

babel({
    babelHelpers: 'bundled',
    babelrc: false
})

В этом случае используются только параметры из Rollup-конфигурации.


Использование configFile

Можно указать конкретный конфигурационный файл:

babel({
    babelHelpers: 'bundled',

    configFile: './config/babel.config.json'
})

Это удобно для монорепозиториев и сложных корпоративных проектов.


Совместное использование с node-resolve

Очень часто Babel применяется вместе с плагином разрешения модулей.

import resolve from '@rollup/plugin-node-resolve';
import babel from '@rollup/plugin-babel';

export default {
    plugins: [
        resolve(),

        babel({
            babelHelpers: 'bundled'
        })
    ]
};

Порядок подключения важен.

Сначала происходит поиск модулей:

resolve()

Затем найденные файлы передаются в Babel.


Совместное использование с commonjs

Для работы со старыми пакетами:

import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import babel from '@rollup/plugin-babel';

export default {
    plugins: [
        resolve(),

        commonjs(),

        babel({
            babelHelpers: 'bundled'
        })
    ]
};

Типичная последовательность выглядит именно так.


Использование при разработке библиотек

Для библиотек часто применяется конфигурация:

babel({
    babelHelpers: 'runtime'
})

Внешние зависимости:

export default {
    external: [
        /@babel\/runtime/
    ]
};

Такой подход позволяет уменьшить размер публикуемых пакетов.


Генерация нескольких форматов

Rollup может создавать несколько вариантов сборки.

export default {
    input: 'src/index.js',

    output: [
        {
            file: 'dist/index.esm.js',
            format: 'esm'
        },
        {
            file: 'dist/index.cjs.js',
            format: 'cjs'
        }
    ],

    plugins: [
        babel({
            babelHelpers: 'bundled'
        })
    ]
};

Babel выполняет транспиляцию до формирования каждого выходного файла.


Производительность сборки

На скорость работы плагина влияют:

  • количество файлов;
  • число Babel-плагинов;
  • число Babel-пресетов;
  • объём исходного кода;
  • количество генерируемых helper-функций.

Для ускорения сборки обычно применяются:

exclude: 'node_modules/**'

и

include: 'src/**'

Это предотвращает обработку лишних файлов.


Типичные ошибки

Отсутствует babelHelpers

Ошибка:

You must specify babelHelpers option

Причина:

babel()

Исправление:

babel({
    babelHelpers: 'bundled'
})

Не установлен @babel/core

Ошибка:

Cannot find module '@babel/core'

Исправление:

npm install --save-dev @babel/core

Не обрабатываются TypeScript-файлы

Причина:

extensions

не содержит нужные расширения.

Исправление:

extensions: [
    '.js',
    '.ts',
    '.tsx'
]

Не работает JSX

Причина:

@babel/preset-react

не установлен либо не подключён.

Исправление:

presets: [
    '@babel/preset-react'
]

Практическая конфигурация для современного проекта

import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import babel from '@rollup/plugin-babel';

export default {
    input: 'src/index.js',

    output: {
        dir: 'dist',
        format: 'esm',
        sourcemap: true
    },

    plugins: [
        resolve(),

        commonjs(),

        babel({
            babelHelpers: 'bundled',

            exclude: 'node_modules/**',

            extensions: [
                '.js',
                '.jsx',
                '.ts',
                '.tsx'
            ]
        })
    ]
};

Подобная конфигурация обеспечивает корректную работу современных возможностей JavaScript, JSX и TypeScript, сохраняет преимущества модульного анализа Rollup и позволяет получать оптимизированные сборки, совместимые с широким спектром браузеров и сред выполнения.