Using tools like Esbuild can make builds faster. However, if you have invested in WebPack and still want to take advantage of a faster build, there is a way.
In this tutorial, we’ll show you how to use esbuild and Webpack and esBuild-Loader.
The world of Web development is constantly evolving
With apologies to those suffering from JavaScript fatigue, the world of Web development is evolving again. It has long been common to run your JavaScript and TypeScript through some node.js-based build tool such as Webpack or rollup.js. These tools are all written in the same language, JavaScript or TypeScript.
The new kids on the blog are tools like Esbuild, Vite and SWC. The significant difference between these tools and their predecessors is that the new ones are written in languages like Go and Rust. Go and Rust perform far better than JavaScript. This means that the build speed is much faster.
These new tools are transformative and may represent the future of web building tools. In the long run, things like EsBuild, Vite, and Friends will likely replace the current standard build tools — Webpacks, Rollups, etc.
However, this is only for the long term. There are many projects that have invested heavily in their current build tools — mainly WebPack. Migrating to a new build tool is no small task. New projects may start with Vite, but existing projects are unlikely to be ported. Webpack is so popular for a reason; It does a lot of things really well. It’s been tested in large projects, it’s mature, and it can handle a wide range of uses.
So what can you do if your team wants to have a faster build, but doesn’t have time for a massive migration? Yes, there is a middle ground to explore.
There is a relatively new project called esbuild-Loader. Developed by Hiroki Osame, esbuild-Loader is a WebPack loader built on top of EsBuild. It allows users to replace ts-Loader or Babel-Loader with their own, which greatly improves build speed.
For full disclosure, I am the main maintainer of TS-Loader, a popular TypeScript loader that is often used with WebPack. However, I feel strongly that the most important thing here is developer productivity. As Node.js-based projects, TS-Loader and Babel-Loader will never be able to compete in the same way as Esbuild-Loader. As a language, Go is really, er, good!
While esbuild may not be suitable for all use cases, it can be suitable for most tasks. Esbuild-loader therefore represents a middle ground — and an early way to get the higher build speeds esBuild offers without saying goodbye to WebPack. This walkthrough will explore using esbuild-Loader in your Webpack setup.
Migrate an existing project to esbuild
Migrating a project that uses babel-Loader or TS-Loader to esbuild-Loader is very simple. First, install the dependency.
npm i -D esbuild-loader
Copy the code
If you are currently using babel-loader, please make the following changes to your webpack.config.js.
module.exports = { module: { rules: [ - { - test: /\.js$/, - use: 'babel-loader', - }, + { + test: /\.js$/, + loader: 'esbuild-loader', + options: { + loader: 'jsx', // Remove this if you're not using JSX + target: 'es2015' // Syntax to compile to (see options below for possible values) + } + }, ... ] ,}}Copy the code
Alternatively, if you are using TS-Loader, please make the following changes to your webpack.config.js.
module.exports = { module: { rules: [ - { - test: /\.tsx?$/, - use: 'ts-loader' - }, + { + test: /\.tsx?$/, + loader: 'esbuild-loader', + options: { + loader: 'tsx', // Or 'ts' if you don't need tsx + target: 'es2015' + } + }, ... ] }},Copy the code
Create a baseline application
Let’s see how esbuild-Loader works in practice. We will use the Create React App to Create a new React application.
npx create-react-app my-app --template typescript
Copy the code
This will build a new React application using TypeScript in the my-app directory. It’s worth mentioning that the Create React App uses the Babel-Loader behind the scenes.
CRA also uses the Fork TS Checker Webpack Plugin to provide TypeScript type checking. This is useful because esBuild is just a translation and is not designed to provide type checking support. So luckily, we still have this plugin out there. Otherwise, we lose type checking.
Now that you understand the benefits of moving to ESbuild, we first need a baseline to understand the performance of using Babel-Loader. We will run time NPM run Build to perform the build of our simple application.
Our full build, TypeScript type checking, translation, minimization, and so on all took 22.08 seconds. Now the question is, what happens if we put esbuild in there?
introduceesbuild-loader
One way to customize the Create React App build is to run NPM Run eject and then customize the CRA output. That’s fine, but it means you can’t keep track of CRA development. Another approach is to use a tool like Create React App Configuration Override (CRACO), which allows you to adjust configurations locally. CRACO describes itself as “a straightforward configuration layer for create-React-app.”
Let’s add esbuild-Loader and CRACO as dependencies.
npm install @craco/craco esbuild-loader --save-dev
Copy the code
Then, we’ll replace the various scripts in our package.json with CRACO.
"start": "craco start",
"build": "craco build",
"test": "craco test",
Copy the code
Our application now uses CRACO, but we haven’t configured it yet. Therefore, we will add a craco.config.js file at the root of our project. This is where we replace babel-loader with esbuild-loader.
const { addAfterLoader, removeLoaders, loaderByName, getLoaders, throwUnexpectedConfigError } = require('@craco/craco'); const { ESBuildMinifyPlugin } = require('esbuild-loader'); const throwError = (message) => throwUnexpectedConfigError({ packageName: 'craco', githubRepo: 'gsoft-inc/craco', message, githubIssueQuery: 'webpack', }); module.exports = { webpack: { configure: (webpackConfig, { paths }) => { const { hasFoundAny, matches } = getLoaders(webpackConfig, loaderByName('babel-loader')); if (! hasFoundAny) throwError('failed to find babel-loader'); console.log('removing babel-loader'); const { hasRemovedAny, removedCount } = removeLoaders(webpackConfig, loaderByName('babel-loader')); if (! hasRemovedAny) throwError('no babel-loader to remove'); if (removedCount ! == 2) throwError('had expected to remove 2 babel loader instances'); console.log('adding esbuild-loader'); const tsLoader = { test: /\.(js|mjs|jsx|ts|tsx)$/, include: paths.appSrc, loader: require.resolve('esbuild-loader'), options: { loader: 'tsx', target: 'es2015' }, }; const { isAdded: tsLoaderIsAdded } = addAfterLoader(webpackConfig, loaderByName('url-loader'), tsLoader); if (! tsLoaderIsAdded) throwError('failed to add esbuild-loader'); console.log('added esbuild-loader'); console.log('adding non-application JS babel-loader back'); const { isAdded: babelLoaderIsAdded } = addAfterLoader( webpackConfig, loaderByName('esbuild-loader'), matches[1].loader // babel-loader ); if (! babelLoaderIsAdded) throwError('failed to add back babel-loader for non-application JS'); console.log('added non-application JS babel-loader back'); console.log('replacing TerserPlugin with ESBuildMinifyPlugin'); webpackConfig.optimization.minimizer = [ new ESBuildMinifyPlugin({ target: 'es2015' }) ]; return webpackConfig; ,}}};Copy the code
So what’s going on here? The script looks for the use of babel-Loader in the default Create React App configuration. There will be two: the TypeScript/JavaScript application code (which we’ll replace) and the non-application JavaScript code. It’s not clear what the non-app JavaScript code is, so we’ll leave it where it is; It could be important. The code we really care about is the code of the application.
You can’t use CRACO to remove a single loader, so we’ll remove both and re-add a non-applied javascriptBabel-Loader. We’ll also add esbuild-Loader and set the {loader: ‘TSX ‘, target:’ ES2015 ‘} option to make sure we can handle JJSX/TSX.
Finally, we’ll switch to shredding esbuild JavaScript using Terser.
Huge performance improvements
Our migration is complete. Next time we build, we’ll use the esbuild-Loader to run the Create React App without popping up. Once again, we will run the Time NPM run build to perform the build of our simple application and determine how long it will take.
Our full build, TypeScript type checking, translation, minimization, and so on all take 13.85 seconds. By moving to esbuild-Loader, our overall compile time was reduced by about a third. This is a huge step forward!
As your code base expands and your application grows, compilation times rise dramatically. With esbuild-Loader, you should get a consistent benefit in build time.
The PostwebPack or Esbuild: First appeared on The LogRocket blog.