Skip to content

Commit 87fadd2

Browse files
[gui] Speed up the web UI build
A clean build of the web UI (`npm run build`, also run by `make package`) took about 76 seconds. It now takes about 6 seconds. Root causes ----------- 1. The whole Vuetify stylesheet was compiled again for every component. `src/variables.scss` started with `@use 'vuetify/styles'`. Webpack adds this file to the top of every SCSS/Sass `<style>` block, so the full Vuetify CSS (about 350 KB) was compiled and bundled once per component, about 56 times. This was most of the build time. It also made the output twice as big (32 MB instead of 15 MB) and `main.js` 7 MB instead of 1 MB. `main.js` already imports `vuetify/styles` once, so these copies were not needed. 2. The JavaScript minifier was slow. Webpack uses Terser by default, which runs in JavaScript and makes two passes over the code. 3. Babel did more work than needed. Without a browser target it converted all code down to old ES5 syntax, and it also processed the whole Vuetify library from node_modules. 4. ESLint ran inside every production build, even though CI already runs `npm run test:lint` separately. Fixes ----- - `variables.scss` now only contains Sass variables, so it no longer outputs any CSS. The `:root` color variables moved to a new `src/global.css` file, imported once from `main.js`. A comment in `variables.scss` explains why CSS must not be put there. - Minify with esbuild (through `minimizer-webpack-plugin`, which webpack already uses) instead of Terser. - Add a "browserslist" target ("defaults") to package.json, and stop running Babel on packages in node_modules. - Run the ESLint webpack plugin only in the development config. - Let webpack clean the `dist` folder (`output.clean`) instead of running `rimraf` before the build. - Clear the webpack cache when any build config file, package.json or .babelrc changes, not only when `webpack.common.js` changes. - Makefile: do not run `npm install` twice when the UI has to be rebuilt. - Remove `vue.config.js`, a leftover from Vue CLI that nothing reads. - Fix `v-model` on a constant in `RunList.vue` and `ListReviewStatusRules.vue` (`itemsPerPageOptions`). esbuild warned that this assignment would throw. It is now a one-way `:items-per-page-options` binding. Results ------- Clean build with an empty cache, measured on the same machine. | | Before | After | | ----------------------- | ------ | ------ | | `npm run build` (total) | 76 s | 6 s | | webpack compile time | 72 s | 4.3 s | | `dist` size | 32 MB | 15 MB | This PR was mainly generated by AI.
1 parent 451b0c5 commit 87fadd2

12 files changed

Lines changed: 572 additions & 51 deletions

File tree

‎web/Makefile‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,10 @@ standalone_package: venv package
129129

130130
# This target will check if the 'dist' directory exists and the vue-cli folder
131131
# is changed. In this case it will remove the dist directory so the UI will
132-
# be built again.
132+
# be built again. Dependencies are installed by the $(DIST_DIR) target.
133133
check_dist:
134134
if [ "$(BUILD_UI_DIST)" = "YES" ] && [ -d $(DIST_DIR) ] && [ "$(LATEST_COMMIT)" != "$(LATEST_BUILD_COMMIT)" ]; then \
135-
cd server/vue-cli && npm install && rm -rf $(DIST_DIR); \
135+
rm -rf $(DIST_DIR); \
136136
fi
137137

138138
clean_package:

‎web/server/vue-cli/config/webpack.common.js‎

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ const CopyPlugin = require("copy-webpack-plugin");
22
const HTMLWebpackPlugin = require("html-webpack-plugin");
33
const { VueLoaderPlugin } = require("vue-loader");
44
const { DefinePlugin, ProvidePlugin } = require("webpack");
5-
const ESLintPlugin = require("eslint-webpack-plugin");
65

76
const { join } = require("path");
87
const helpers = require("./helpers");
@@ -94,11 +93,14 @@ module.exports = {
9493
{
9594
test: /\.js$/,
9695
loader: "babel-loader",
96+
// Dependencies already ship code for the browsers listed in the
97+
// "browserslist" field of package.json, so only our own sources go
98+
// through babel.
9799
// The Thrift API stubs (in `codechecker_api/js`) are plain CommonJS.
98100
// Exclude them like node_modules so babel doesn't turn them into ES
99101
// modules and break their `module.exports`.
100102
exclude: [
101-
/node_modules\/(?!vuetify)/,
103+
/[\\/]node_modules[\\/]/,
102104
/[\\/]codechecker_api[\\/]js[\\/]/
103105
],
104106
options: {
@@ -203,10 +205,6 @@ module.exports = {
203205
new ProvidePlugin({
204206
Buffer: [ "buffer", "Buffer" ],
205207
}),
206-
new ESLintPlugin({
207-
extensions: [ "js", "vue" ]//,
208-
//overrideConfigFile: "./.eslintrc.js"
209-
}),
210208
new VueLoaderPlugin(),
211209
new HTMLWebpackPlugin({
212210
showErrors: true,
@@ -244,8 +242,15 @@ module.exports = {
244242
},
245243
cache: {
246244
type: "filesystem",
245+
// Invalidate the cache when any of the build configuration changes.
247246
buildDependencies: {
248-
config: [ __filename ]
247+
config: [
248+
__filename,
249+
helpers.root("config", "webpack.dev.js"),
250+
helpers.root("config", "webpack.prod.js"),
251+
helpers.root("package.json"),
252+
helpers.root(".babelrc")
253+
]
249254
}
250255
},
251256
snapshot: {

‎web/server/vue-cli/config/webpack.dev.js‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const { DefinePlugin } = require('webpack');
22
const { merge } = require('webpack-merge');
3+
const ESLintPlugin = require('eslint-webpack-plugin');
34

45
const common = require('./webpack.common.js');
56

@@ -58,6 +59,11 @@ module.exports = merge(common, {
5859
]
5960
},
6061
plugins: [
62+
// Lint only in development. Production builds skip it to save time;
63+
// linting is checked separately by `npm run test:lint` (also in CI).
64+
new ESLintPlugin({
65+
extensions: [ "js", "vue" ]
66+
}),
6167
new DefinePlugin({
6268
'process.env': {
6369
'CC_SERVER_HOST': process.env.CC_SERVER_HOST || null,

‎web/server/vue-cli/config/webpack.prod.js‎

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const { DefinePlugin } = require('webpack');
22
const { merge } = require('webpack-merge');
3+
const MinimizerPlugin = require('minimizer-webpack-plugin');
34

45
const common = require('./webpack.common.js');
56
const helpers = require('./helpers');
@@ -9,7 +10,29 @@ module.exports = merge(common, {
910
output: {
1011
path: helpers.root('dist'),
1112
filename: '[name].[contenthash].js',
12-
publicPath: "/"
13+
publicPath: "/",
14+
// Remove the previous build output before emitting the new one.
15+
clean: true
16+
},
17+
optimization: {
18+
minimizer: [
19+
// esbuild is many times faster than the default Terser minifier.
20+
new MinimizerPlugin({
21+
minify: MinimizerPlugin.esbuildMinify,
22+
minimizerOptions: {
23+
// Keep in sync with the "browserslist" field of package.json, so
24+
// the minifier doesn't introduce syntax unsupported by those
25+
// browsers.
26+
target: 'es2020',
27+
logOverride: {
28+
// The generated Thrift API stubs contain duplicate `case 0:`
29+
// clauses. They are harmless and our own sources are checked for
30+
// this by ESLint (`no-duplicate-case`).
31+
'duplicate-case': 'silent'
32+
}
33+
}
34+
})
35+
]
1336
},
1437
plugins: [
1538
new DefinePlugin({

0 commit comments

Comments
 (0)