microsoft / BotFramework-WebChat

A highly-customizable web-based client for Azure Bot Services.
https://www.botframework.com/
MIT License
1.6k stars 1.55k forks source link

Update bundler to esbuild #3914

Open compulim opened 3 years ago

compulim commented 3 years ago

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 react@16.8.6, 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:

  1. Babel with @babel/plugin-transform-runtime and inject core-js-pure for usage
  2. Bundle with esbuild
  3. Babel again with @babel/preset-env to convert to ES5
  4. Minify with terser
compulim commented 3 years ago

Some updates:

We should try something outside of Web Chat, few goals:

As SDK, the problem is uniquely challenged because we want to use ponyfills instead of polyfills.

compulim commented 2 years ago

Updates:

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.