Интеграция Gulp и Webpack строится вокруг разделения обязанностей: Gulp выступает оркестратором задач, управляющим файловыми потоками и последовательностями сборки, а Webpack выполняет роль компилятора модулей с графом зависимостей, трансформацией и бандлингом.
Ключевая идея заключается в том, что Webpack используется не как CLI-инструмент, а как программный модуль через Node.js API, что позволяет встроить процесс сборки в пайплайн Gulp-задач, управлять жизненным циклом компиляции и получать детализированные результаты сборки.
Webpack предоставляет низкоуровневый API через функцию
webpack(config), возвращающую объект компилятора
(compiler). Этот объект содержит методы управления
сборкой:
compiler.run(callback) — однократная сборкаcompiler.watch(watchOptions, callback) — режим
наблюденияcompiler.close(callback) — завершение watch-сессииcompiler.hooks)Базовый пример создания компилятора:
const webpack = require('webpack');
const config = require('./webpack.config');
const compiler = webpack(config);
Этот объект становится центральной точкой интеграции с Gulp.
Gulp 4 использует функции-задачи, которые могут возвращать Promise,
stream или принимать callback. Самый прямой способ интеграции Webpack —
использование compiler.run.
const gulp = require('gulp');
const webpack = require('webpack');
const config = require('./webpack.config');
function build(done) {
const compiler = webpack(config);
compiler.run((err, stats) => {
if (err) {
done(err);
return;
}
const info = stats.toJson();
if (stats.hasErrors()) {
done(new Error(info.errors));
return;
}
console.log(stats.toString({
colors: true,
chunks: false,
modules: false
}));
compiler.close(() => done());
});
}
gulp.task('build', build);
Особенность модели заключается в необходимости явно завершать
компилятор через close, иначе процесс может удерживать
event loop.
Callback-модель часто заменяется Promise-обёрткой для упрощения композиции задач Gulp.
function runWebpack(config) {
const compiler = webpack(config);
return new Promise((resolve, reject) => {
compiler.run((err, stats) => {
if (err) {
compiler.close(() => reject(err));
return;
}
const info = stats.toJson();
if (stats.hasErrors()) {
compiler.close(() => reject(new Error(info.errors)));
return;
}
compiler.close(() => resolve(stats));
});
});
}
function build() {
return runWebpack(require('./webpack.config'));
}
exports.build = build;
В этой модели Gulp корректно отслеживает завершение задачи через Promise.
Webpack watch API позволяет встроить долгоживущий процесс сборки, который реагирует на изменения файлов.
function watch() {
const compiler = webpack(require('./webpack.config'));
compiler.watch(
{
aggregateTimeout: 300,
poll: undefined
},
(err, stats) => {
if (err) {
console.error(err);
return;
}
console.log(stats.toString({
colors: true
}));
}
);
return () => {
compiler.close(() => {});
};
}
exports.watch = watch;
Важный аспект интеграции: Gulp не должен завершать задачу, поэтому возвращается функция очистки или удерживается процесс через незавершающийся stream/promise-паттерн.
Webpack stats объект является основным источником
информации о сборке. Он содержит:
Приведение к JSON:
const info = stats.toJson({
all: false,
errors: true,
warnings: true
});
Форматированный вывод:
stats.toString({
colors: true,
chunks: false,
modules: false,
entrypoints: true
});
В Gulp-пайплайнах часто используется минимизированный режим stats для ускорения вывода логов.
Webpack ошибки делятся на:
Корректная обработка:
compiler.run((err, stats) => {
if (err) {
throw err;
}
const info = stats.toJson();
if (stats.hasErrors()) {
throw new Error(info.errors.join('\n'));
}
if (stats.hasWarnings()) {
console.warn(info.warnings);
}
});
В Gulp важно пробрасывать ошибки через done(err) или
rejection Promise, иначе задача будет считаться успешно завершённой.
Webpack поддерживает массив конфигураций, что позволяет запускать несколько сборок в одном вызове компилятора.
const configs = [
require('./webpack.client.config'),
require('./webpack.server.config')
];
const compiler = webpack(configs);
Каждая конфигурация компилируется отдельно, а stats
возвращает массив результатов.
Обработка:
compiler.run((err, stats) => {
stats.stats.forEach(s => {
console.log(s.toString());
});
});
Хотя Webpack не является stream-based системой, его можно встроить в поток Gulp через промежуточные задачи.
Типовой подход — использовать Gulp для подготовки файлов, после чего запускать Webpack:
const gulp = require('gulp');
function copyAssets() {
return gulp.src('src/assets/**/*')
.pipe(gulp.dest('dist/assets'));
}
После чего Webpack собирает JS-часть:
function scripts() {
return runWebpack(require('./webpack.config'));
}
Последовательность:
exports.build = gulp.series(copyAssets, scripts);
Gulp позволяет комбинировать задачи Webpack с другими этапами сборки:
exports.build = gulp.series(
clean,
gulp.parallel(styles, images),
scripts
);
Webpack интегрируется как отдельный этап, но может быть запущен параллельно при независимых конфигурациях:
exports.build = gulp.parallel(
scriptsClient,
scriptsAdmin
);
Webpack hooks позволяют вмешиваться в процесс сборки:
compiler.hooks.beforeRun.tap('GulpIntegration', () => {
console.log('Webpack start');
});
compiler.hooks.done.tap('GulpIntegration', (stats) => {
console.log('Webpack finished');
});
Это позволяет синхронизировать Gulp-задачи с жизненным циклом компиляции без polling и внешних проверок.
При интеграции важно учитывать накладные расходы:
Оптимизированный паттерн:
const compiler = webpack(config);
function build(done) {
compiler.run((err, stats) => {
compiler.close(() => done(err));
});
}
Связка Gulp и Webpack применяется в случаях:
Каждая архитектура определяется сложностью проекта и необходимостью контроля над шагами сборки.