Migrating from Gulp+Webpack configuration to Webpack-only configuration.

Amit Joki
3 min readMay 27, 2020

--

Webpack

Gulp is a task runner. It has been around for a long time. Up until recently, we at WikiEduDashboard were using it to manage some parts of the asset pipeline.

On the other hand, Webpack is a pure bundle-spitting beast. Whatever you can do with Webpack, it can be done with Gulp. But whatever you can do with Gulp cannot be done with the Webpack.

The problem with Gulp was that there were too many dependencies. And some of the Gulp-plugins were no longer actively maintained.

As part of Google Summer of Code 2020, with Wikimedia, I ventured out into an adventure of finding if we could have everything done with Webpack alone? Would it be possible to get rid of Gulp entirely?

Spoilers ahead, YES WE CAN. Here’s how:

The code snippets have been dumbed down to maintain brevity.

Gulp Task 1: Clean any leftover assets.

import { task } from 'gulp';
import del from 'del';
task('clean', () => del(['path1', 'path2']));

The above Gulp task basically cleaned/deleted any leftover assets from previous builds. We got rid of this task by CleanWebpackPlugin

new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns: ['path1', 'path2']
});

Gulp Task 2: Copy static assets.

import { task, parallel, dest, src } from 'gulp';task('copy-image', () => {
// ... copy static images to the build/dist folder
});
task('copy-fonts', () => {
// ... copy fonts to build/dist folder
});
task('copy-statics', parallel('copy-image', 'copy-fonts'));

We got rid of this task by CopyWebpackPlugin

new CopyPlugin({
patterns: [{
from: 'from_image_path', to: 'to_image_path'
}, {
from: 'from_font_path', to: 'to_font_path'
}]
});

Gulp Task 3: Execute a command to generate i18n translation files.

import { task } from 'gulp';
import gutil from 'gulp-util';
import { exec } from 'child_process';

task('i18n', cb =>
exec('bundle exec rails i18n:js:export', (_ ,stdout, stderr) => {
if (stdout) {
gutil.log(stdout);
}
if (stderr) {
gutil.log(stderr);
}
return cb(err);
})
);

Now there are various ways to migrate from the above task. You could just run a npm script . You could run, exec or spawn and pipe its output. We did the latter.

plugins.push({
apply: (compiler) => {
compiler.hooks.afterEmit.tap('AfterEmitPlugin', (_) => {
spawn('bundle', ['exec', 'rails', 'i18n:js:export'], {
stdio: 'inherit'
});
});
},
});

We can directly tap into the events emitted by the Webpack and create our own nifty-little plugin like the above.

Gulp Task 4: Linting Javascript files.

We had a whole complex Gulp task to watch the JS, JSX files and lint them whenever any changes occured.

We got rid of that using eslint-loader. All we needed to do was add a rule in the modules of webpack.config.js like so:

{
test: /\.jsx?$/,
exclude: [/vendor/, /node_modules(?!\/striptags)/],
loader: 'eslint-loader',
options: {
cache: true,
failOnError: !!env.production
},
}

Gulp Task 5: Emitting Stylesheets.

We had another complex Gulp task to compile and emit the Stylus files we used for styling the elements.

We got rid of that using a combination of stylus-native-loader which converts Stylus files *.stylto *.css files and css-loader to handle the compiled CSS files and mini-css-extract-plugin to extract the CSS into separate files.

{
test: /\.styl$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: !!env.development
}
},
'css-loader',
{
loader: 'stylus-native-loader',
options: {
includeCSS: true,
vendors: true
}
}
]
}

Overall, we were able to remove all the Gulp tasks and make do with a single Webpack Configuration.

The results are amazing as well. The previous Gulp+Webpack Configuration used to take nearly ~120 seconds to build prodcution assets.

The new Webpack-only configuration takes just ~30 seconds.

It is nearly 4x faster than Gulp + Webpack configuration!

You can see the whole migration in all its glorious details in this Pull Request.

--

--