various bundle optimizations (#1549)

* fix: missing "@element-plus/icons-vue" dependency

* fix: update vite (40% faster builds)

* fix: don't include sentry in non-nightly builds

* fix: swap to variable fonts & don't include font files in repo

* fix: lazy load languages to not keep them in memory

* nit: revert vite to stable

* nit: retain `.json` message files in bundle

* nit: remove bundle analyzer

* fix: availableLocales does not include unloaded locales
This commit is contained in:
Aries
2026-01-03 23:51:00 -07:00
committed by GitHub
parent 327e7d9b58
commit b02d287190
38 changed files with 574 additions and 619 deletions

View File

@@ -1,77 +1,128 @@
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { resolve } from 'node:path';
import fs from 'node:fs';
import { defineConfig } from 'vite';
import { sentryVitePlugin } from '@sentry/vite-plugin';
import { defineConfig, loadEnv } from 'vite';
import tailwindcss from '@tailwindcss/vite';
import vue from '@vitejs/plugin-vue';
const __dirname = dirname(fileURLToPath(import.meta.url));
import { languageCodes } from './localization/locales';
const authToken = process.env.SENTRY_AUTH_TOKEN;
const buildAndUploadSourceMaps = authToken ? true : false;
const vrcxVersion = fs
.readFileSync(resolve(__dirname, '../Version'), 'utf-8')
.trim();
if (buildAndUploadSourceMaps) {
console.log('Source maps will be built and uploaded to Sentry');
function getAssetLanguage(assetId) {
if (!assetId) return null;
if (assetId.endsWith('.json')) {
const language = assetId.split('.json')[0];
if (languageCodes.includes(language)) return language;
}
const language =
assetId.split('element-plus/es/locale/lang/')[1]?.split('.')[0] ||
// Font assets, e.g., noto-sans-jp-regular.woff2 mapped to language code.
{
jp: 'ja',
sc: 'zh-CN',
tc: 'zh-TW',
kr: 'ko'
}[assetId.split('noto-sans-')[1]?.split('-')[0]];
return language || null;
}
// @ts-ignore
export default defineConfig(() => ({
base: '',
plugins: [
vue(),
tailwindcss(),
buildAndUploadSourceMaps &&
sentryVitePlugin({
authToken,
project: 'vrcx-web',
release: {
name: vrcxVersion
},
sourcemaps: {
assets: './build/html/**',
filesToDeleteAfterUpload: './build/html/**/*.js.map'
function getManualChunk(moduleId) {
const language = getAssetLanguage(moduleId);
if (!language) return;
return `languages/${language}`;
}
const defaultAssetName = '[hash][extname]';
function getAssetFilename({ name }) {
const language = getAssetLanguage(name);
if (!language) return `assets/${defaultAssetName}`;
return `assets/languages/${language}-${defaultAssetName}`;
}
export default defineConfig(({ mode }) => {
const { SENTRY_AUTH_TOKEN: sentryAuthToken } = loadEnv(
mode,
process.cwd(),
''
);
const buildAndUploadSourceMaps = !!sentryAuthToken;
const version = fs
.readFileSync(new URL('../Version', import.meta.url), 'utf-8')
.trim();
const nightly = version.split('-').at(-1).length === 7;
return {
base: '',
plugins: [
vue(),
tailwindcss(),
buildAndUploadSourceMaps &&
import('@sentry/vite-plugin').then(({ sentryVitePlugin }) =>
sentryVitePlugin({
authToken: sentryAuthToken,
project: 'vrcx-web',
release: {
name: version
},
sourcemaps: {
assets: './build/html/**',
filesToDeleteAfterUpload: './build/html/**/*.js.map'
}
})
)
],
css: {
transformer: 'lightningcss',
lightningcss: {
minify: true,
targets: {
chrome: 140
}
})
],
css: {
transformer: 'lightningcss',
lightningcss: {
minify: true,
targets: {
chrome: 140
}
}
},
define: {
LINUX: JSON.stringify(process.env.PLATFORM === 'linux'),
WINDOWS: JSON.stringify(process.env.PLATFORM === 'windows')
},
server: {
port: 9000,
strictPort: true
},
build: {
target: 'chrome140',
outDir: '../build/html',
cssMinify: 'lightningcss',
license: true,
emptyOutDir: true,
reportCompressedSize: false,
chunkSizeWarningLimit: 5000,
modulePreload: true,
assetsInlineLimit: 0,
rollupOptions: {
input: {
index: resolve(__dirname, 'index.html'),
vr: resolve(__dirname, 'vr.html')
}
},
sourcemap: buildAndUploadSourceMaps
}
}));
define: {
LINUX: JSON.stringify(process.env.PLATFORM === 'linux'),
WINDOWS: JSON.stringify(process.env.PLATFORM === 'windows'),
VERSION: JSON.stringify(version),
NIGHTLY: JSON.stringify(nightly)
},
server: {
port: 9000,
strictPort: true
},
build: {
target: 'chrome140',
outDir: '../build/html',
cssMinify: 'lightningcss',
license: true,
emptyOutDir: true,
copyPublicDir: false,
// reportCompressedSize: false,
// chunkSizeWarningLimit: 5000,
sourcemap: buildAndUploadSourceMaps,
assetsInlineLimit: 0,
rollupOptions: {
preserveEntrySignatures: false,
input: {
index: resolve(import.meta.dirname, './index.html'),
vr: resolve(import.meta.dirname, './vr.html')
},
output: {
assetFileNames: getAssetFilename,
manualChunks: getManualChunk
}
}
}
};
});