В системе плагинов Rollup хук resolveDynamicImport
отвечает за разрешение динамических импортов, создаваемых через
конструкцию import(). Он используется в тех случаях, когда
необходимо явно контролировать процесс определения модуля, загружаемого
динамически, и вмешиваться в стандартный алгоритм разрешения
зависимостей.
Динамические импорты отличаются от статических тем, что их путь может быть вычисляемым выражением, а сам модуль загружается асинхронно. Это создаёт дополнительный уровень сложности для бандлера, поскольку заранее невозможно полностью проанализировать граф зависимостей.
Хук resolveDynamicImport вызывается для каждого
динамического выражения import(...), которое Rollup
обнаруживает во время анализа исходного кода.
Сигнатура плагина выглядит следующим образом:
resolveDynamicImport(specifier, importer) {
return null;
}
Параметры:
import(). Может быть строкой, шаблонной строкой или сложным
выражением.Возвращаемое значение может принимать несколько форм:
null — использовать стандартное поведение Rollupstring — заменить путь модуля{ id, external } — явное указание идентификатора модуля
и его внешнего статусаfalse — полностью отменить обработку импортаPromise с одним из указанных значений — асинхронное
разрешениеХук resolveDynamicImport вызывается после анализа
синтаксического дерева модуля, но до формирования окончательного графа
зависимостей. Он находится между стадиями:
import() выраженийresolveDynamicImportЭтот хук отличается от resolveId, который работает со
статическими импортами. В отличие от него,
resolveDynamicImport предназначен исключительно для
runtime-зависимостей.
Ключевое различие между resolveId и
resolveDynamicImport заключается в типе импортов:
resolveId обрабатывает:
import x from 'module'export * from 'module'resolveDynamicImport обрабатывает:
import('module')import(variable)import(./${name}.js)При этом динамический импорт может вообще не иметь статически вычисляемого значения, что делает его обработку более гибкой и сложной.
export default function plugin() {
return {
name: 'dynamic-import-rewrite',
resolveDynamicImport(specifier) {
if (specifier === 'legacy-module') {
return 'modern-module';
}
return null;
}
};
}
В данном случае любой динамический импорт legacy-module
будет заменён на modern-module.
resolveDynamicImport(specifier) {
if (specifier.startsWith('http')) {
return { id: specifier, external: true };
}
return null;
}
Такой подход используется для CDN-загрузки модулей или микрофронтенд-архитектур, где часть зависимостей не включается в бандл.
resolveDynamicImport(specifier) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(`resolved/${specifier}`);
}, 10);
});
}
Асинхронный вариант полезен при обращении к файловой системе, метаданным или внешним сервисам.
Динамический импорт часто используется с шаблонными строками:
import(`./modules/${name}.js`);
В таких случаях specifier может быть не строкой, а
выражением AST. Rollup передаёт структуру, которую плагин может
анализировать или частично интерпретировать.
Плагин может использовать дополнительные хуки для анализа:
onDynamicImport (внутренний анализ AST)transformЕсли динамический импорт содержит переменную:
import(moduleName);
Rollup не может определить зависимость заранее. В этом случае
resolveDynamicImport становится единственной точкой
контроля.
Неправильная обработка динамических импортов может привести к:
Динамические импорты напрямую влияют на разбиение кода на чанки.
Каждый разрешённый import() потенциально создаёт отдельный
chunk:
external: true → исключение из сборкиИспользуется для перенаправления импортов в зависимости от окружения:
resolveDynamicImport(specifier) {
if (specifier.includes('env=prod')) {
return specifier.replace('env=prod', 'env=production');
}
}
При работе с виртуальными модулями (virtual:
namespace):
resolveDynamicImport(specifier) {
if (specifier.startsWith('virtual:')) {
return specifier;
}
}
resolveDynamicImport(specifier) {
if (specifier.startsWith('cdn:')) {
return {
id: `https://cdn.example.com/${specifier.slice(4)}.js`,
external: true
};
}
}
resolveDynamicImport часто используется совместно с:
resolveId — для статических импортовload — для подмены содержимого модулейtransform — для анализа AST перед сборкойshouldTransformCachedModule — для оптимизации повторных
сборокТипичный поток обработки:
resolveId обрабатывает статические зависимостиresolveDynamicImport обрабатывает runtime
зависимостиload загружает содержимоеtransform модифицирует кодВ реальных плагинах важно учитывать, что specifier может
быть:
Поэтому безопасная обработка включает проверки:
if (typeof specifier !== 'string') {
return null;
}
Если несколько плагинов реализуют resolveDynamicImport,
их порядок имеет значение. Rollup применяет их последовательно до
первого не-null результата, что позволяет:
Динамические импорты поддерживаются только в ESM-контексте. При взаимодействии с CommonJS:
resolveDynamicImport всё равно вызывается на этапе
анализаresolveDynamicImport является ключевым механизмом
контроля runtime-зависимостей. В отличие от статических импортов, он
позволяет управлять поведением приложения в момент исполнения, а не
только компиляции.
Его использование критично в системах:
Он расширяет модель Rollup от статического анализа к частично динамическому управлению графом модулей, сохраняя при этом предсказуемость сборки.