Skip to content

Commit

Permalink
build(ui): improve performance with esbuild-loader
Browse files Browse the repository at this point in the history
- similar to [Argo CD](https://github.com/argoproj/argo-cd/blob/d9df2525c57d4870215a6ce149dbedd08ae05fdb/ui/src/app/webpack.config.js#L37) and many other projects, we can use `esbuild` instead of TS etc to significantly speed up build times
  - (as `esbuild` is written in Go and compiled instead of using interpreted TS/JS)
  - we don't use any Babel or TS plugins, so I think this codebase is a good candidate for that
  - it also simplifies the build process as well, removing & replacing many other deps
  - caveats:
    - `esbuild` does not do type-checking, so we have to run `tsc --noEmit` as part of the `lint` phase
      - `isolatedModules` config is needed to ensure compatibility with this
    - `esbuild` does not currently support React Fast Refresh
      - `react-hot-loader` was replaced by [React Fast Refresh](https://github.com/pmmmwh/react-refresh-webpack-plugin) and is deprecated / hasn't received an update in 2 years
        - remove `module.hot` etc usage of `react-hot-loader` as well
      - that being said, `esbuild` is significantly faster than using Babel or TS for builds, so hot reloading is nice but not necessary
        - also hot reloading can be buggy

- further optimize dev builds by using cheaper `eval` sourcemaps as [recommended by Webpack for performance](https://webpack.js.org/configuration/devtool/)
  - prod builds should always have highest quality sourcemaps, so do not change those

- remove unused `react-paginate` `exclude`
  - it's not a dep we currently use; it doesn't exist in the lockfile
    - (nor in `argo-ui`'s lockfile, of which all prod deps would be in Workflows's lockfile)

some simple performance testing when running natively on macOS, taken directly from Webpack's reported stats:
- before:
  - prod build: ~65s
  - dev build: ~22s
  - dev rebuild: ~1.8s
- after:
  - prod build: ~50s (~25% faster)
  - dev build: ~16s (~25% faster)
  - dev rebuild: ~.8s (~50% faster)
- these are not as good improvements as I would have liked, which mean there are bigger bottlenecks elsewhere
  - e.g. Webpack itself (analysis, source maps, running loaders), sass-loader, etc
  - switching to `esbuild` directly may further improve these stats
    - i.e. instead of Webpack + `esbuild-loader`

Signed-off-by: Anton Gilgur <agilgur5@gmail.com>
  • Loading branch information
agilgur5 committed Jan 22, 2024
1 parent baef485 commit 9d49b09
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 144 deletions.
7 changes: 2 additions & 5 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"scripts": {
"build": "rm -Rf dist && NODE_OPTIONS='--openssl-legacy-provider' NODE_ENV=production webpack --mode production --config ./src/app/webpack.config.js",
"start": "NODE_OPTIONS='--no-experimental-fetch --openssl-legacy-provider' webpack-dev-server --config ./src/app/webpack.config.js",
"lint": "eslint --fix ./src/app",
"lint": "eslint --fix ./src/app && tsc --noEmit",
"test": "jest",
"deduplicate": "yarn-deduplicate -s fewer yarn.lock"
},
Expand Down Expand Up @@ -63,8 +63,8 @@
"@typescript-eslint/eslint-plugin": "^6.18.1",
"@typescript-eslint/parser": "^6.10.0",
"babel-jest": "^29.7.0",
"babel-loader": "^9.1.3",
"copy-webpack-plugin": "^12.0.1",
"esbuild-loader": "^4.0.2",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
Expand All @@ -74,13 +74,10 @@
"monaco-editor-webpack-plugin": "^7.1.0",
"prettier": "^3.2.1",
"raw-loader": "^4.0.2",
"react-hot-loader": "^4.13.1",
"sass": "^1.69.7",
"sass-loader": "^13.3.2",
"source-map-loader": "^4.0.2",
"style-loader": "^1.3.0",
"ts-jest": "^26.4.4",
"ts-loader": "^9.5.1",
"ts-node": "^9.1.1",
"typescript": "^4.6.4",
"webpack": "^5.89.0",
Expand Down
8 changes: 0 additions & 8 deletions ui/src/app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,3 @@ import * as ReactDOM from 'react-dom';
import {App} from './app';

ReactDOM.render(<App />, document.getElementById('app'));

const mdl = module as any;
if (mdl.hot) {
mdl.hot.accept('./app.tsx', () => {
const UpdatedApp = require('./app.tsx').App; // eslint-disable-line @typescript-eslint/no-var-requires
ReactDOM.render(<UpdatedApp />, document.getElementById('app'));
});
}
1 change: 1 addition & 0 deletions ui/src/app/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"outDir": "./../../dist/app",
"sourceMap": true,
"noImplicitAny": true,
"isolatedModules": true, // for compatibility with esbuild
"module": "ES2020", // must be ES2020+ for dynamic imports: https://github.com/Microsoft/TypeScript/issues/16675, https://github.com/webpack/webpack/issues/5703#issuecomment-357512412
"moduleResolution": "node",
"esModuleInterop": true,
Expand Down
12 changes: 4 additions & 8 deletions ui/src/app/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const webpack = require('webpack');
const path = require('path');

const isProd = process.env.NODE_ENV === 'production';
const proxyConf = {
Expand All @@ -25,7 +24,7 @@ const config = {
path: __dirname + '/../../dist/app'
},

devtool: 'source-map',
devtool: isProd ? 'source-map' : 'eval',

resolve: {
extensions: ['.ts', '.tsx', '.js', '.json', '.ttf'],
Expand All @@ -36,16 +35,13 @@ const config = {
rules: [
{
test: /\.tsx?$/,
use: [
...(isProd ? [] : ['react-hot-loader/webpack']),
`ts-loader?transpileOnly=${!isProd}&allowTsInNodeModules=true&configFile=${path.resolve('./src/app/tsconfig.json')}`
]
loader: 'esbuild-loader'
},
{
enforce: 'pre',
exclude: [/node_modules\/react-paginate/, /node_modules\/monaco-editor/],
exclude: [/node_modules\/monaco-editor/],
test: /\.js$/,
use: [...(isProd ? ['babel-loader'] : ['source-map-loader'])]
use: ['esbuild-loader']
},
{
test: /\.scss$/,
Expand Down
Loading

0 comments on commit 9d49b09

Please sign in to comment.