Параметр output.freeze управляет генерацией вызовов
Object.freeze() для экспортируемых объектов и пространств
имён в итоговом бандле. Настройка влияет на поведение модулей ES при
работе с экспортами, а также на совместимость со средами выполнения и
производительность.
По умолчанию Rollup стремится приблизить поведение сгенерированного кода к стандартной семантике ES Modules. Одним из механизмов такого приближения становится заморозка namespace-объектов.
Object.freezeВ JavaScript функция Object.freeze() запрещает:
Пример:
const user = {
name: 'Alex'
};
Object.freeze(user);
user.name = 'John';
console.log(user.name); // Alex
После заморозки объект становится неизменяемым.
output.freezeПри сборке модулей Rollup может генерировать namespace-объекты:
import * as utils from './utils.js';
Подобные объекты представляют пространство имён модуля. Чтобы сделать их поведение ближе к стандарту ES Modules, Rollup по умолчанию замораживает такие структуры.
Пример генерации:
var utils = /*#__PURE__*/Object.freeze({
__proto__: null,
sum: sum,
multiply: multiply
});
Здесь Rollup:
Object.freeze().output: {
freeze: true
}
Поведение включено автоматически.
freezeexport default {
input: 'src/main.js',
output: {
file: 'dist/bundle.js',
format: 'esm',
freeze: false
}
};
В этом случае Rollup перестанет добавлять
Object.freeze().
Сгенерированный код станет проще:
var utils = {
__proto__: null,
sum: sum,
multiply: multiply
};
output.freezeКаждый вызов Object.freeze() увеличивает итоговый объём
кода.
В небольших проектах разница почти незаметна, однако в крупных библиотеках с большим количеством namespace-объектов объём может увеличиваться ощутимо.
Object.freeze() требует дополнительных операций во время
инициализации.
В большинстве приложений влияние минимально, но:
иногда отключают freeze ради ускорения старта.
Некоторые старые JavaScript-движки или нестандартные embedded-среды
работают с Object.freeze() медленно либо некорректно.
В подобных случаях параметр отключают полностью.
ES Modules предполагают неизменяемость namespace-объектов.
При отключённом freeze появляется возможность
модифицировать экспортированный namespace:
import * as api from './api.js';
api.test = 123;
При freeze: true подобный код вызовет ошибку либо будет
проигнорирован.
При freeze: false изменение станет возможным.
Некоторые инструменты и библиотеки рассчитывают на неизменяемость экспортов.
Особенно это касается:
freeze: trueДля npm-пакетов заморозка почти всегда является правильным решением.
Причины:
Пример:
export default {
input: 'src/index.js',
output: {
dir: 'dist',
format: 'esm',
freeze: true
}
};
Если модуль используется множеством внешних потребителей, неизменяемость экспортов уменьшает вероятность ошибок.
Например:
export const config = {
api: '/v1'
};
Freeze помогает защититься от случайного изменения структуры.
freeze: falseЕсли бандл не публикуется как библиотека и используется только внутри проекта, строгая неизменяемость часто не нужна.
Некоторые проекты минимизируют любую дополнительную работу при инициализации.
Иногда разработчики жертвуют частью корректности ради минимального размера.
output.freeze не влияет напрямую на tree shaking.
Однако отключение freeze:
Но влияние обычно незначительно.
Наиболее актуальный сценарий.
output: {
format: 'esm',
freeze: true
}
Rollup может замораживать namespace-объекты и при генерации CJS.
В UMD/IIFE freeze тоже применяется к объектам экспортов.
import terser from '@rollup/plugin-terser';
export default {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'umd',
name: 'MyLibrary',
freeze: true
},
plugins: [
terser()
]
};
output.esModuleПараметр output.esModule управляет добавлением
специального маркера ES Module в CommonJS-бандлы.
Речь идёт о свойстве:
__esModule
Этот флаг широко используется экосистемой JavaScript для совместимости между:
__esModuleМногие транспайлеры и сборщики добавляют специальное свойство:
exports.__esModule = true;
или:
Object.defineProperty(exports, '__esModule', {
value: true
});
Этот флаг сообщает:
модуль был создан как ES Module либо совместим с ES Module semantics.
__esModuleПроблема возникает из-за различий между:
module.exports = value;
и:
exports.test = value;
export default value;
и:
export const test = value;
Системам совместимости необходимо понимать:
CommonJS-модуль:
module.exports = 'hello';
Импорт:
import value from './module.js';
Без дополнительных механизмов совместимости возможны неоднозначности.
output.esModuleRollup может автоматически добавлять:
Object.defineProperty(exports, '__esModule', {
value: true
});
output.esModuletrueВсегда добавлять __esModule.
output: {
format: 'cjs',
esModule: true
}
Результат:
Object.defineProperty(exports, '__esModule', {
value: true
});
falseНикогда не добавлять.
output: {
format: 'cjs',
esModule: false
}
"if-default-prop"Добавлять только при необходимости.
Это современное рекомендуемое поведение Rollup.
Пример:
output: {
format: 'cjs',
esModule: 'if-default-prop'
}
В современных версиях Rollup:
esModule: "if-default-prop"
Rollup старается избегать лишнего __esModule, но
добавляет его при необходимости совместимости.
__esModuleBabel активно использует этот флаг.
Без него могут появляться конструкции:
module.default.default
или некорректные default imports.
TypeScript при esModuleInterop и
allowSyntheticDefaultImports ориентируется на наличие
__esModule.
Webpack также учитывает этот маркер.
Для npm-библиотек наличие __esModule часто улучшает
совместимость с различными сборщиками.
esModule: false
полезенКаждая дополнительная строка увеличивает размер.
В микро-бандлах иногда отключают:
output: {
esModule: false
}
Некоторые проекты предпочитают самостоятельно управлять interop-логикой.
Иногда legacy-runtime ожидает «чистый» CommonJS без дополнительных свойств.
format: 'esm'Для настоящих ES Modules параметр практически не имеет значения.
__esModule нужен прежде всего для CommonJS
interoperability.
Исходный модуль:
export default function sum(a, b) {
return a + b;
}
Конфиг:
export default {
input: 'src/index.js',
output: {
file: 'dist/index.cjs',
format: 'cjs',
esModule: true
}
};
Rollup может сгенерировать:
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
function sum(a, b) {
return a + b;
}
exports.default = sum;
exports.default и module.exportsexports.default = value;
module.exports = value;
__esModule помогает инструментам понять, как правильно
интерпретировать экспорт.
Параметр тесно связан с:
output.interop
Interop управляет преобразованием импортов между CJS и ESM.
esModule определяет наличие служебного флага
совместимости.
output: {
format: 'cjs',
esModule: 'if-default-prop'
}
или:
output: {
format: 'cjs',
esModule: true
}
output: {
esModule: false
}
если совместимость не требуется.
freeze и esModuleПример:
export default {
input: 'src/index.js',
output: {
file: 'dist/library.cjs',
format: 'cjs',
freeze: true,
esModule: 'if-default-prop'
}
};
Такой конфиг: