Scope hoisting (также известный как module concatenation) — оптимизация сборки, при которой множество отдельных JavaScript-модулей объединяются в один или несколько крупных файлов с минимальной обёрткой вокруг каждого модуля. Вместо генерации изолированных функций для каждого файла создаётся единый связный модульный контекст, где зависимости инлайнются и выстраиваются в линейный порядок исполнения.
В традиционной модульной сборке каждый файл превращается в отдельную функцию-обёртку:
(function(module, exports, require) {
// код модуля
});
При scope hoisting эти обёртки устраняются, а модули “схлопываются” в одну область видимости:
// объединённый код модулей без лишних обёрток
const a = 1;
const b = a + 2;
Parcel применяет scope hoisting в production-сборках, используя статический анализ графа зависимостей. Основная цель — уменьшение накладных расходов на вызовы функций модулей и ускорение выполнения кода в браузере за счёт более эффективного JIT-оптимизирования.
Scope hoisting возможен только при условии, что граф зависимостей может быть полностью проанализирован на этапе сборки. Parcel строит dependency graph, где каждый модуль рассматривается как узел, а импорты и экспорты — как рёбра.
Ключевым этапом является определение:
importexportЕсли модульный граф является статически разрешимым, Parcel может безопасно объединять модули в единое пространство имён.
Наиболее важное требование — использование стандартной системы ES Modules:
import { sum } from './math.js';
export const result = sum(2, 3);
Parcel опирается на статическую структуру import/export. Только в этом случае возможно безопасное объединение модулей.
Динамические импорты:
import('./module.js');
не блокируют scope hoisting полностью, но переводят соответствующие части графа в асинхронные чанки. Такие модули исключаются из синхронного объединения.
CommonJS снижает эффективность scope hoisting:
const lib = require('lib');
module.exports = function() {};
Причины:
Parcel выполняет трансформацию CommonJS в ES Module-like структуру, но результат часто остаётся частично деоптимизированным, и такие модули могут быть вынесены в отдельные обёртки.
Scope hoisting требует анализа побочных эффектов. Модули с неизвестными side effects ограничивают возможность объединения.
Пример потенциально проблемного кода:
window.globalValue = 42;
console.log('module loaded');
Для управления этим используется поле sideEffects в
package.json:
{
"sideEffects": false
}
или точечная настройка:
{
"sideEffects": ["*.css"]
}
Если модуль помечен как “чистый”, Parcel может безопасно удалять его при tree-shaking и включать в scope hoisting.
Конструкции, которые ломают статический анализ:
export let value = 1;
if (Math.random()) {
value = 2;
}
или:
module.exports = condition ? a : b;
затрудняют предсказуемую компоновку модулей, поэтому такие фрагменты ограничивают степень объединения.
Циклические зависимости не запрещают scope hoisting, но влияют на порядок объединения модулей.
Пример:
// a.js
import { b } from './b.js';
// b.js
import { a } from './a.js';
Parcel разрешает такие зависимости через механизм live bindings, однако:
Scope hoisting тесно связан с tree-shaking. Для эффективного объединения модулей необходимо предварительное удаление неиспользуемого кода.
Условия корректного tree-shaking:
Tree-shaking уменьшает объём графа, после чего scope hoisting выполняет его “схлопывание” в минимальное количество исполняемых единиц.
Parcel использует AST-анализ, поэтому важна предсказуемость синтаксиса:
eval и withПример несовместимого подхода:
const moduleName = './a.js';
import(moduleName);
Такой код переводит модуль в асинхронную область и исключает его из scope hoisting.
Scope hoisting не применяется или частично отключается при наличии:
export * в цепочках
CommonJS-обёртокParcel в таких случаях выбирает безопасную стратегию: частичное объединение или сохранение модулей в отдельных функциях.
Наиболее эффективное применение scope hoisting наблюдается в приложениях, где:
В таких условиях Parcel формирует компактные бандлы с минимальным количеством обёрток, что снижает overhead загрузки и ускоряет выполнение кода в браузере за счёт лучшей оптимизации движком JavaScript.