Сборка библиотеки в Webpack отличается от сборки приложения тем, что результат должен быть универсальным артефактом, пригодным для использования в разных окружениях: Node.js (CommonJS), ESM-сборках, браузере через глобальную переменную, а также в системах модульной загрузки AMD. Это требует явной настройки экспорта, формата модуля и управления внешними зависимостями.
Основная цель конфигурации — сформировать дистрибутив, который:
Ключевой блок настройки — output, определяющий формат и
способ публикации результата сборки.
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js',
clean: true,
}
Однако для библиотек этого недостаточно. Необходимо явно указать поведение экспорта.
В Webpack 4 использовались libraryTarget, в Webpack 5 он
заменён на output.library.type.
Современная конфигурация:
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js',
library: {
name: 'MyLibrary',
type: 'umd',
},
globalObject: 'globalThis',
clean: true,
}
library.typemodule.exports"type": "module")UMD остаётся стандартом для библиотек, которые должны работать в любом окружении.
output: {
filename: 'my-lib.js',
library: {
name: 'MyLib',
type: 'umd',
export: 'default',
},
globalObject: 'globalThis',
}
Особенности:
requiredefine)globalObject, чтобы избежать ошибок в
Node.jsexportПараметр library.export управляет тем, какая часть
модуля становится публичным API.
library: {
name: 'MyLib',
type: 'umd',
export: 'default',
}
Возможные варианты:
default — экспортируется
export defaultnamed — экспортируются именованные экспорты['default', 'utils'] — выборочный экспортСовременные пакеты всё чаще публикуются как ES Modules.
output: {
filename: 'index.mjs',
library: {
type: 'module',
},
module: true,
environment: {
module: true,
},
}
Обязательные условия:
"type": "module" в package.json или использование
.mjsexperiments.outputModule (в некоторых версиях
Webpack)experiments: {
outputModule: true,
}
Webpack-сборка библиотеки тесно связана с корректным описанием пакета.
{
"name": "my-lib",
"version": "1.0.0",
"main": "dist/index.js",
"module": "dist/index.mjs",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js"
}
}
}
Параметр exports обеспечивает:
Часто применяется стратегия двойной сборки:
module.exports = [
{
mode: 'production',
entry: './src/index.js',
output: {
filename: 'index.js',
path: path.resolve(__dirname, 'dist'),
library: {
type: 'commonjs2',
},
clean: true,
},
},
{
mode: 'production',
entry: './src/index.js',
output: {
filename: 'index.mjs',
path: path.resolve(__dirname, 'dist'),
library: {
type: 'module',
},
module: true,
clean: false,
},
experiments: {
outputModule: true,
},
},
];
Для библиотек критично не включать зависимости вроде React, Lodash и аналогов в бандл.
externals: {
react: 'react',
'react-dom': 'react-dom',
}
Или универсальный вариант:
externals: {
lodash: {
commonjs: 'lodash',
commonjs2: 'lodash',
amd: 'lodash',
root: '_',
},
}
Результат:
Используется для крупных библиотек:
externals: /^(react|react-dom|lodash)$/i
Или через функцию:
externals: ({ request }, callback) => {
if (/^@?lodash/.test(request)) {
return callback(null, 'commonjs ' + request);
}
callback();
}
Для библиотек важна стабильность и предсказуемость:
optimization: {
minimize: true,
usedExports: true,
sideEffects: false,
}
Особое значение имеет sideEffects в package.json:
{
"sideEffects": false
}
Это позволяет tree-shaking на стороне потребителя.
При публикации UMD-библиотеки в браузерном окружении важно контролировать имя глобального объекта:
output: {
globalObject: 'globalThis',
}
Причины:
window не работает в Node.jsself не универсаленglobalThis поддерживает все окруженияРазные режимы влияют на итоговый пакет.
Production:
mode: 'production',
devtool: 'source-map',
Development:
mode: 'development',
devtool: 'eval-source-map',
Для библиотек development-сборка часто используется только локально, а в npm публикуется production-версия.
Source maps критичны для отладки библиотек:
devtool: 'source-map'
Дополнительно возможно разделение:
.map файлы публикуются вместе с пакетом.npmignoreПри использовании TypeScript обычно добавляется отдельный процесс генерации типов:
{
"types": "dist/index.d.ts"
}
Webpack сам по себе не генерирует .d.ts, поэтому
используется tsc:
{
"compilerOptions": {
"declaration": true,
"emitDeclarationOnly": true,
"outDir": "dist"
}
}
Типичная структура:
dist/
index.js
index.mjs
index.js.map
index.mjs.map
index.d.ts
Поддержание стабильной структуры важно для:
exports mappingДобавление метаинформации в начало файла:
const webpack = require('webpack');
plugins: [
new webpack.BannerPlugin({
banner: 'MyLib v1.0.0',
}),
]
Чтобы избежать конфликтов в глобальной области:
library: {
name: ['MyScope', 'MyLib'],
type: 'umd',
}
В этом случае библиотека публикуется как:
window.MyScope.MyLib
const path = require('path');
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js',
library: {
name: 'MyLib',
type: 'umd',
export: 'default',
},
globalObject: 'globalThis',
clean: true,
},
externals: {
react: 'react',
},
optimization: {
minimize: true,
sideEffects: false,
},
};