Update bundler to esbuild
Background
We bumped to webpack@5. Should we move to esbuild?
Let's weight which one is better, in terms of size and speed (for DX).
esbuild
Our bundle must be ES5. But esbuild emit bundle with ES6+. Thus, after esbuild, we need to transpile the bundle again.
esbuild config to use for isomorphic-react
In our webchat*.js bundle, although we bundled [email protected], if the environment already have a window.React and window.ReactDOM object available, we will prefer them over our own bundled React. This is called "isomorphic React".
We need to configure esbuild to redirect all import 'react' to this isomorphic React package.
Content of esbuild.base.js
const { resolve } = require('path');
const onResolveReact = {
name: 'isomorphic-react',
setup(build) {
build.onResolve({ filter: /^react$/ }, () => {
return {
path: resolve('node_modules/isomorphic-react/dist/react.js')
};
});
build.onResolve({ filter: /^react-dom$/ }, () => {
return {
path: resolve('node_modules/isomorphic-react/dist/react-dom.js')
};
});
}
};
module.exports = {
bundle: true,
entryPoints: ['src/index.js'],
logLevel: 'info',
plugins: [onResolveReact],
sourcemap: true,
watch: process.argv.slice(2).includes('--watch')
};
Babel config to use on bundle emitted by esbuild
The bundle emitted by esbuild contains many features from ES6+. We need to run Babel again to transpile it back to ES5.
We need to enable a very specific set of plugins. If we enable all plugins via @babel/preset-env, it will cause a conflict with Symbol polyfill from core-js@3.
Content of babel.post.config.json
{
/*
These are plugins that can transpile bundle emitted by ESBuild and also compatible with core-js@3.
Some plugins included by @babel/preset-env will break core-js@3 for Symbol, so we select plugin carefully.
List of plugins included by @babel/preset-env:
https://github.com/babel/babel/blob/master/packages/babel-compat-data/scripts/data/plugin-features.js
If we use @babel/preset-env on the bundle emitted by ESBuild, it will cause this issue.
https://github.com/zloirock/core-js/issues/514
The solution is to skip Babel on node_modules/core-js. But in our case, we can't because it is already in the final bundle.
*/
"plugins": [
"@babel/plugin-proposal-nullish-coalescing-operator", // This is only needed for --minify build, which emit "??" operator.
"@babel/plugin-transform-arrow-functions",
"@babel/plugin-transform-block-scoping",
"@babel/plugin-transform-destructuring",
"@babel/plugin-transform-for-of",
"@babel/plugin-transform-parameters",
"@babel/plugin-transform-shorthand-properties",
"@babel/plugin-transform-spread",
"@babel/plugin-transform-template-literals"
],
"sourceMaps": true
}
Dependencies
jsonwebtoken requires buffer. Since we don't verify the token (we merely check the userId), we should move to jwt-decode instead.
Investigations
Steps to compile:
- Babel with
@babel/plugin-transform-runtimeand injectcore-js-purefor usage - Bundle with
esbuild - Babel again with
@babel/preset-envto convert to ES5 - Minify with
terser
Some updates:
- webpack@5 is much slower than webpack@4 when used in watch mode
- esbuild has an internal template that is written in ES6+
- esbuild/internal/runtime/runtime.go is the "bundle header", it consists of Object.freeze which need to be ponyfilled in IE11
- We should try running Babel on-the-fly in esbuild to reduce repeated transpilation
- corejs@3 is probably needed because we prefer babel/plugin-transform-runtime to scope our polyfills
We should try something outside of Web Chat, few goals:
- The bundled file should run on IE11 with zero polyfills
- All required polyfills should be ponyfill (babel/plugin-transform-runtime and use corejs@3)
- Only 1 or 2 passes of Babel
- Minified production build should run properly on IE11
- Dev build only need to run on Chromium, not necessarily with other browsers
As SDK, the problem is uniquely challenged because we want to use ponyfills instead of polyfills.
Updates:
- Multiple transpilation passes does not work
- Although ESBuild do support some ES5 transpilation with ES5-compatible templates, there are some syntaxes not transpiled yet. We probably need to run Babel in front of ESBuild
- Babel on-the-fly via ESBuild plugin is very slow, we should run CLI side-by-side
- Bundle size by ESBuild is 20-30% larger than Webpack for unknown reasons
Currently in #4330, we are using ESBuild as development server. We should continue to investigate ESBuild for production build with Babel as the transpilation tool.
Also, we should clean up our exports for better treeshaking.