Плагин @rollup/plugin-node-resolve предназначен для
разрешения импортов по алгоритму, совместимому с экосистемой Node.js.
Без него Rollup умеет работать только с относительными и абсолютными
путями файлов, но не способен автоматически находить пакеты внутри
каталога node_modules.
Например, следующий импорт без дополнительной настройки вызовет ошибку:
import lodash from 'lodash';
Rollup не знает, где находится пакет lodash, поскольку
указан только идентификатор модуля, а не путь к файлу. Именно эту задачу
решает @rollup/plugin-node-resolve.
После подключения плагина Rollup начинает:
node_modules;exports;module, main,
browser;Установка выполняется через npm:
npm install @rollup/plugin-node-resolve --save-dev
Либо через Yarn:
yarn add @rollup/plugin-node-resolve --dev
Либо через pnpm:
pnpm add @rollup/plugin-node-resolve -D
Минимальная конфигурация выглядит следующим образом:
import { nodeResolve } from '@rollup/plugin-node-resolve';
export default {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'esm'
},
plugins: [
nodeResolve()
]
};
Теперь можно импортировать пакеты из node_modules:
import axios from 'axios';
Плагин найдёт пакет, определит входной файл и передаст его Rollup для дальнейшей обработки.
Предположим, существует импорт:
import library from 'my-library';
Плагин выполняет последовательность действий:
node_modules/my-library
Анализирует файл package.json.
Проверяет доступные точки входа.
Например:
{
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js"
}
Выбирает наиболее подходящий файл.
Возвращает Rollup абсолютный путь к модулю.
Одной из причин популярности Rollup является эффективный tree shaking.
Рассмотрим пакет:
{
"main": "dist/index.js",
"module": "dist/index.esm.js"
}
Поле module обычно указывает на ESM-сборку.
Плагин старается использовать именно её:
import { nodeResolve } from '@rollup/plugin-node-resolve';
nodeResolve();
В результате Rollup получает ES-модули и может удалять неиспользуемый код.
Например:
import { add } from 'math-library';
Если библиотека экспортирует десятки функций, но используется только
add, лишний код будет исключён из итогового бандла.
Большая часть старых пакетов публикуется в формате CommonJS.
Пример:
const fs = require('fs');
Для поддержки таких пакетов обычно используются два плагина одновременно:
import { nodeResolve } from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
export default {
plugins: [
nodeResolve(),
commonjs()
]
};
Порядок имеет значение.
Сначала выполняется поиск модулей:
nodeResolve()
Затем CommonJS преобразуется в ESM:
commonjs()
Такой вариант считается стандартной конфигурацией большинства проектов.
Многие пакеты содержат специальные версии для браузера.
Пример:
{
"main": "index.js",
"browser": "browser.js"
}
Для использования браузерной версии применяется настройка:
nodeResolve({
browser: true
});
Теперь будет выбран файл:
browser.js
вместо:
index.js
Это особенно важно для фронтенд-проектов.
Поле browser может содержать карту замен.
Например:
{
"browser": {
"./server.js": "./browser.js"
}
}
Тогда импорт:
import './server.js';
автоматически будет перенаправлен на:
import './browser.js';
при использовании:
nodeResolve({
browser: true
});
Node.js содержит встроенные модули:
fs
path
crypto
stream
os
По умолчанию плагин отдаёт предпочтение встроенным версиям.
Пример:
import path from 'path';
Настройка по умолчанию:
nodeResolve({
preferBuiltins: true
});
означает использование встроенного модуля Node.js.
Иногда требуется использовать npm-полифилл.
Тогда настройка меняется:
nodeResolve({
preferBuiltins: false
});
Теперь будет найден пакет:
node_modules/path
если он существует.
Такой подход часто применяется при сборке браузерных приложений.
По умолчанию плагин ищет несколько расширений файлов.
Например:
import utils from './utils';
Плагин проверяет варианты:
utils.js
utils.mjs
utils.json
utils.node
Список можно изменить:
nodeResolve({
extensions: [
'.js',
'.jsx',
'.ts',
'.tsx'
]
});
Теперь поиск будет учитывать TypeScript и React-компоненты.
Часто встречается следующая структура:
src/
├── app.ts
├── utils.ts
└── index.ts
Импорт:
import { format } from './utils';
Корректно разрешается при наличии:
nodeResolve({
extensions: ['.js', '.ts']
});
Хотя фактическую компиляцию TypeScript выполняет другой плагин, поиск
файлов производится именно через node-resolve.
Иногда одна и та же библиотека попадает в проект несколько раз.
Например:
react
node_modules/react
some-library
└── node_modules/react
В результате могут возникать проблемы:
Invalid Hook Call
или
Multiple React Instances
Для устранения используется:
nodeResolve({
dedupe: ['react']
});
Теперь все импорты React будут ссылаться на одну копию библиотеки.
Допускается динамическое определение:
nodeResolve({
dedupe(importee) {
return importee === 'react';
}
});
Это удобно для крупных монорепозиториев.
По умолчанию поиск осуществляется в:
node_modules
Список каталогов можно расширить:
nodeResolve({
moduleDirectories: [
'node_modules',
'shared_modules'
]
});
Теперь Rollup будет искать пакеты и в каталоге:
shared_modules
Позволяет указывать дополнительные пути поиска.
Пример:
nodeResolve({
modulePaths: [
'/opt/custom-packages'
]
});
Поиск модулей будет производиться и в этом каталоге.
Иногда требуется ограничить набор разрешаемых пакетов.
Например:
nodeResolve({
resolveOnly: [
/^@company\//
]
});
Теперь будут разрешаться только пакеты:
@company/ui
@company/core
@company/utils
Остальные зависимости будут игнорироваться.
Допускается и более простой вариант:
nodeResolve({
resolveOnly: [
'react',
'react-dom'
]
});
Настройка ограничивает область поиска.
Пример:
nodeResolve({
jail: '/project/src'
});
Разрешение модулей за пределами указанного каталога запрещается.
Подобный механизм полезен для:
Современные пакеты всё чаще используют поле:
{
"exports": {
".": "./dist/index.js"
}
}
или:
{
"exports": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
}
Плагин полностью поддерживает такую схему.
При импорте:
import pkg from 'library';
будет выбран путь, соответствующий условиям разрешения.
Позволяет явно указывать условия для поля exports.
Пример:
nodeResolve({
exportConditions: [
'development'
]
});
Предположим, пакет содержит:
{
"exports": {
"development": "./dev.js",
"production": "./prod.js"
}
}
Тогда будет выбран файл:
dev.js
Структура монорепозитория может выглядеть так:
packages/
├── core
├── ui
├── utils
└── app
Каждый пакет имеет собственный package.json.
В подобных проектах часто используются:
nodeResolve({
dedupe: ['react']
});
и
nodeResolve({
moduleDirectories: [
'node_modules'
]
});
Это помогает избежать дублирования зависимостей между пакетами.
Типичная конфигурация React-проекта:
import { nodeResolve } from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
export default {
plugins: [
nodeResolve({
browser: true,
dedupe: ['react', 'react-dom']
}),
commonjs()
]
};
Такой набор обеспечивает:
Для Vue-проектов часто применяется конфигурация:
nodeResolve({
browser: true,
extensions: [
'.mjs',
'.js',
'.json',
'.vue'
]
});
Это позволяет находить компоненты Vue наряду с обычными JavaScript-модулями.
При возникновении ошибки:
Could not resolve 'some-package'
обычно причиной является одно из следующих обстоятельств:
node_modules;exports;browser;extensions.Для диагностики полезно проверить:
npm ls some-package
и содержимое:
node_modules/some-package/package.json
Особое внимание следует уделять полям:
{
"main": "...",
"module": "...",
"browser": "...",
"exports": "..."
}
Именно они определяют итоговое поведение
@rollup/plugin-node-resolve.
Наиболее распространённая последовательность выглядит так:
plugins: [
nodeResolve(),
commonjs(),
babel(),
terser()
]
Каждый этап выполняет свою задачу:
nodeResolve находит модули.commonjs преобразует CommonJS.babel транспилирует код.terser выполняет минификацию.Нарушение порядка способно привести к ошибкам сборки или некорректному tree shaking.
Основные функции плагина:
node_modules;main, module,
browser;exports;dedupe;exportConditions;resolveOnly и
jail.Благодаря этим возможностям @rollup/plugin-node-resolve
является одним из базовых и практически обязательных плагинов в
большинстве проектов на Rollup, обеспечивая корректное подключение и
обработку внешних зависимостей из экосистемы npm.