How to start moving legacy codebase to webpack
I'm JS developer with 13 years of professional experience. I'm always happy to teach my craft.
Search for a command to run...
I'm JS developer with 13 years of professional experience. I'm always happy to teach my craft.
No comments yet. Be the first to comment.
I’ve been working in programming for the last 16 years. Let’s see what changes I see in our industry that are enabled by generative AI. Rapid prototyping The last few months, I’ve been working on a set of WordPress plugins, related to event organizat...
Writing unit tests takes time and effort. Nonetheless, many teams insist on writing them anyway—that’s because of the benefits they bring to a project. Those benefits are mainly the following: fast feedback—unit tests speed up each iteration of twea...

Pure functions are the perfect case for unit testing. For a given input, we always expect the same output—there is no internal state involved. Let’s take a look at a few examples and some simple tests that check if the methods work as expected. Jasmi...

Let’s say you have a job interview in a few days. How should you prepare for it so that you can make an informed decision about joining the company, as well as make sure that your interests are taken care of? Prepare your questions An interview is a ...

Creating example projects is a common way of showing your skills to potential employers. Let’s take a look at what’s important to keep in mind when building personal projects with an eye toward impressing prospective employers. Simplicity Building ap...

Let's say we inherit a legacy JavaScript project, and it's our job to keep it alive. Often in those situations:
In this article, I'll show you how you can start migrating a codebase like this to webpack - so we can replace the old build infrastructure based on half-abandoned projects like Grunt; maybe achieve smaller files to be transferred to the user & keep ourselves up to date with the industry standards.
To simplify, our legacy application is index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>webpack</title>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link
rel="stylesheet"
href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css"
/>
<link rel="stylesheet" href="/resources/demos/style.css" />
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script>
$(function () {
$("#datepicker").datepicker();
});
</script>
</head>
<body>
<p>Date: <input type="text" id="datepicker" /></p>
</body>
</html>
It is an input that uses jQuery UI Datepicker, and it looks like:

The way it's written has few features that JavaScript bundlers let us move away from:
index.html - so we need to keep it up to date to our JS files <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
would not workWe could most likely migrate my simple example to webpack in one go. For bigger projects, it's not an option - too many things are interconnected, and it could take a really long time to do it all. It's unlikely we would get approval for spending a week or two on code setup only. Another problem is that this big-bang approach offers very little feedback along the way. We could learn very late that one of the 3rd party libraries we've been using has some problems when built with wepback.
Let's take the smallest step possible to avoid those issues - add webpack, and move jQuery import there.
First, let's turn the folder we keep our index.html into npm package:
$ npm init -y
Wrote to /home/marcin/workspace/github/tmp/webpack-expose-loader/package.json:
{
"name": "webpack-expose-loader",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
add webpack as a dependency
npm install --save-dev webpack webpack-cli
+ webpack-cli@4.8.0
+ webpack@5.52.1
updated 2 packages and audited 121 packages in 10.088s
1 package is looking for funding
run `npm fund` for details
found 0 vulnerabilities
To avoid creating wepback.config.js, we make sure we keep on using default locations for the source & output files. So we will have src/index.js:
console.log("Hello!");
and we add to index.html:
<script src="dist/main.js"></script>
Before all the other JS imports. Then, we add a build script to package.json:
{
...
"scripts": {
...
"build": "webpack --mode=production"
...
and we can build with:
$ npm run build
> webpack-starter@1.0.0 build /home/marcin/workspace/github/webpack-expose-loader
> webpack --mode=production
asset main.js 22 bytes [compared for emit] [minimized] (name: main)
./src/index.js 23 bytes [built] [code generated]
webpack 5.52.1 compiled successfully in 163 ms
The application should work as before, but with a 'test' log in a console.
To start our migration, let's install jQuery in version 1.12.4 as a dependency:
$ npm install --save jquery@1.12.4
+ jquery@1.12.4
added 1 package from 1 contributor and audited 122 packages in 1.399s
Now, we can import jquery from our src/index.js:
import jQuery from "jquery";
console.log(jQuery);
When we rebuild:
npm run build
> webpack-starter@1.0.0 build /home/marcin/workspace/github/webpack-expose-loader
> webpack --mode=production
asset main.js 95.3 KiB [compared for emit] [minimized] (name: main) 1 related asset
runtime modules 663 bytes 3 modules
cacheable modules 287 KiB
./src/index.js 51 bytes [built] [code generated]
./node_modules/jquery/dist/jquery.js 287 KiB [built] [code generated]
webpack 5.52.1 compiled successfully in 2557 ms
We can see the output main.js is much bigger 95.3 KiB, so we clearly include jQuery in our code. But if we remove from index.html:
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
our date picker will be broken, and we will see in the console log:

That is because webpack is isolating modules. The imports are not polluting the global scope, and each module can access only things it explicitly imported. It's a good thing in the long term - it helps us avoid invisible coupling between modules, which can be very confusing. But in baby-step refactoring, we need to work around it.
expose-loader is a webpack loader that allows us to pollute a global scope with the import from a given file. To use it first, we need to install it as a dev dependency:
$ npm install --save-dev expose-loader
+ expose-loader@3.0.0
added 1 package from 1 contributor and audited 123 packages in 1.926s
Then we should change the import line in our src/index.js:
import jQuery from "expose-loader?exposes=$,jQuery!jquery";
console.log(jQuery);
The code means as follow:
import jQuery from is a part of the import that makes it usable in the current file"expose-loader! is a special import syntax understood by wepback. It picks the loader to be used for the import that is specified after !?exposes=$,jQuery option provided to the loader. In this case, we want the default export of the file after ! to be put on the global scope as $ (for the script in index.html) and as jQuery - for the plugin!jquery what we are importingWith those changes in place, after building, the application should work as before.
We have walked through an example of how to start using a webpack in a legacy project. Let me know in the comment what experience with migration to webpack you have had so far.