@rollup/plugin-node-resolve

Плагин @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:

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';

Плагин выполняет последовательность действий:

  1. Ищет каталог:
node_modules/my-library
  1. Анализирует файл package.json.

  2. Проверяет доступные точки входа.

Например:

{
  "main": "dist/index.cjs.js",
  "module": "dist/index.esm.js"
}
  1. Выбирает наиболее подходящий файл.

  2. Возвращает Rollup абсолютный путь к модулю.


Поле module и tree shaking

Одной из причин популярности 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

Большая часть старых пакетов публикуется в формате 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()

Такой вариант считается стандартной конфигурацией большинства проектов.


Параметр browser

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

Пример:

{
  "main": "index.js",
  "browser": "browser.js"
}

Для использования браузерной версии применяется настройка:

nodeResolve({
    browser: true
});

Теперь будет выбран файл:

browser.js

вместо:

index.js

Это особенно важно для фронтенд-проектов.


Использование browser field mappings

Поле browser может содержать карту замен.

Например:

{
  "browser": {
    "./server.js": "./browser.js"
  }
}

Тогда импорт:

import './server.js';

автоматически будет перенаправлен на:

import './browser.js';

при использовании:

nodeResolve({
    browser: true
});

Параметр preferBuiltins

Node.js содержит встроенные модули:

fs
path
crypto
stream
os

По умолчанию плагин отдаёт предпочтение встроенным версиям.

Пример:

import path from 'path';

Настройка по умолчанию:

nodeResolve({
    preferBuiltins: true
});

означает использование встроенного модуля Node.js.


Отключение preferBuiltins

Иногда требуется использовать npm-полифилл.

Тогда настройка меняется:

nodeResolve({
    preferBuiltins: false
});

Теперь будет найден пакет:

node_modules/path

если он существует.

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


Параметр extensions

По умолчанию плагин ищет несколько расширений файлов.

Например:

import utils from './utils';

Плагин проверяет варианты:

utils.js
utils.mjs
utils.json
utils.node

Список можно изменить:

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

Теперь поиск будет учитывать TypeScript и React-компоненты.


Разрешение TypeScript-модулей

Часто встречается следующая структура:

src/
├── app.ts
├── utils.ts
└── index.ts

Импорт:

import { format } from './utils';

Корректно разрешается при наличии:

nodeResolve({
    extensions: ['.js', '.ts']
});

Хотя фактическую компиляцию TypeScript выполняет другой плагин, поиск файлов производится именно через node-resolve.


Параметр dedupe

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

Например:

react
node_modules/react

some-library
└── node_modules/react

В результате могут возникать проблемы:

Invalid Hook Call

или

Multiple React Instances

Для устранения используется:

nodeResolve({
    dedupe: ['react']
});

Теперь все импорты React будут ссылаться на одну копию библиотеки.


Использование функции dedupe

Допускается динамическое определение:

nodeResolve({
    dedupe(importee) {
        return importee === 'react';
    }
});

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


Параметр moduleDirectories

По умолчанию поиск осуществляется в:

node_modules

Список каталогов можно расширить:

nodeResolve({
    moduleDirectories: [
        'node_modules',
        'shared_modules'
    ]
});

Теперь Rollup будет искать пакеты и в каталоге:

shared_modules

Параметр modulePaths

Позволяет указывать дополнительные пути поиска.

Пример:

nodeResolve({
    modulePaths: [
        '/opt/custom-packages'
    ]
});

Поиск модулей будет производиться и в этом каталоге.


Параметр resolveOnly

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

Например:

nodeResolve({
    resolveOnly: [
        /^@company\//
    ]
});

Теперь будут разрешаться только пакеты:

@company/ui
@company/core
@company/utils

Остальные зависимости будут игнорироваться.


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

Допускается и более простой вариант:

nodeResolve({
    resolveOnly: [
        'react',
        'react-dom'
    ]
});

Параметр jail

Настройка ограничивает область поиска.

Пример:

nodeResolve({
    jail: '/project/src'
});

Разрешение модулей за пределами указанного каталога запрещается.

Подобный механизм полезен для:

  • песочниц;
  • систем плагинов;
  • изолированных сборок;
  • внутренних инструментов разработки.

Работа с package exports

Современные пакеты всё чаще используют поле:

{
  "exports": {
    ".": "./dist/index.js"
  }
}

или:

{
  "exports": {
    "import": "./dist/index.mjs",
    "require": "./dist/index.cjs"
  }
}

Плагин полностью поддерживает такую схему.

При импорте:

import pkg from 'library';

будет выбран путь, соответствующий условиям разрешения.


Параметр exportConditions

Позволяет явно указывать условия для поля 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

Типичная конфигурация 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()
    ]
};

Такой набор обеспечивает:

  • корректное разрешение React;
  • поддержку браузерных версий зависимостей;
  • работу CommonJS-пакетов;
  • эффективный tree shaking.

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

Для 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()
]

Каждый этап выполняет свою задачу:

  1. nodeResolve находит модули.
  2. commonjs преобразует CommonJS.
  3. babel транспилирует код.
  4. terser выполняет минификацию.

Нарушение порядка способно привести к ошибкам сборки или некорректному tree shaking.


Ключевые возможности @rollup/plugin-node-resolve

Основные функции плагина:

  • поиск пакетов в node_modules;
  • поддержка алгоритма разрешения Node.js;
  • обработка полей main, module, browser;
  • поддержка exports;
  • интеграция с tree shaking Rollup;
  • работа с ESM и CommonJS;
  • устранение дубликатов зависимостей через dedupe;
  • настройка расширений файлов;
  • поддержка монорепозиториев;
  • выбор специальных условий через exportConditions;
  • ограничение областей поиска через resolveOnly и jail.

Благодаря этим возможностям @rollup/plugin-node-resolve является одним из базовых и практически обязательных плагинов в большинстве проектов на Rollup, обеспечивая корректное подключение и обработку внешних зависимостей из экосистемы npm.