Поле external в конфигурации Rollup определяет
зависимости, которые не должны попадать в итоговый бандл. Это один из
ключевых механизмов управления границами сборки, позволяющий разделять
внутренний код проекта и внешние библиотеки, которые будут
предоставляться окружением выполнения (браузером, Node.js, сторонним
рантаймом или системой модулей).
Основная задача external — исключить указанные модули из
процесса бандлинга. Rollup, обнаружив импорт, помеченный как внешний, не
включает его код в выходной файл, а оставляет ссылку на него в виде
import (ESM) или require
(CJS-плагины/вывод).
Ключевая идея:
Это особенно важно при разработке библиотек, где:
node_modules;Наиболее распространённый вариант — использование массива строк:
export default {
input: 'src/index.js',
external: ['react', 'lodash', 'axios']
};
Каждая строка в массиве — это имя модуля, которое Rollup будет считать внешним.
Если в коде встречается импорт:
import axios from 'axios';
Rollup:
axios в бандл;Массив external может содержать не только строки, но и
более гибкие формы сопоставления:
external: ['react']
Совпадает только с точным именем импорта.
external: [/^@org\//]
Любой импорт, начинающийся с @org/, будет считаться
внешним:
import utils from '@org/utils';
import core from '@org/core';
Оба будут исключены из бандла.
Наиболее гибкий вариант — функция, принимающая идентификатор модуля:
external: (id) => {
return id === 'react';
}
Rollup вызывает эту функцию для каждого import или
require. Если возвращается true, модуль
считается внешним.
Rollup обрабатывает граф зависимостей следующим образом:
Строит дерево импортов начиная с input.
Для каждого узла проверяет external.
Если модуль совпадает с правилом:
external влияет не только на финальный код, но и на
процесс анализа:
import React from 'react';
Остаётся без изменений.
const React = require('react');
Rollup оставляет require как ссылку на внешнюю зависимость.
Во встроенных форматах ситуация сложнее:
globals.Пример:
external: ['react'],
output: {
format: 'umd',
globals: {
react: 'React'
}
}
Результат:
(function (React) { ... }(window.React));
Функциональный вариант позволяет реализовать сложную логику исключения:
external: (id) => id.includes('node_modules')
На практике чаще используется более точное правило:
external: (id) => !id.startsWith('.') && !id.startsWith('/')
Здесь:
./, ../) остаются
внутри бандла;external: (id) => {
return /^@company\//.test(id);
}
Подходит для монорепозиториев, где внутренние пакеты не должны попадать в сборку.
Плагины типа @rollup/plugin-node-resolve сначала
пытаются разрешить модуль, после чего Rollup проверяет
external.
Важно:
external применяется к уже разрешённому
id;Пример:
external: ['react']
Импорт:
import React from 'react';
После резолва Rollup всё равно сопоставляет строку
react.
external полностью отключает tree-shaking для этих
модулей, так как:
Это делает external важным инструментом баланса между
размером бандла и границами ответственности.
external: (id) => true
Результат — пустой бандл с одними импортами.
external: ['react']
Но код:
import React from 'react/index.js';
Такой импорт не будет распознан как внешний.
Если используется alias-плагин:
resolve({
alias: {
'@': './src'
}
})
и одновременно:
external: ['@/utils']
Важно учитывать, что external проверяется после
разрешения путей.
В реальных библиотеках часто используется комбинированная стратегия:
external: [
'react',
'react-dom',
/^lodash/
]
или функция:
external: (id) => {
if (id === 'react' || id === 'react-dom') return true;
if (id.startsWith('lodash')) return true;
return false;
}
Такой подход позволяет:
При создании библиотек external фактически определяет
публичный контракт:
От корректности настройки зависит: