Improve lazy loading (#12393)

Creating manual chunk was a bad idea, we should always solve lazy
loading problem at the source instance.

Setting a 4.5MB for the index bundle size, CI will fail if we go above.

There is still a lot of room for optimizations!
- More agressive lazy loading (e.g. xyflow and tiptap are still loaded
in index!)
- Add a  prefetch mechanism
- Add stronger CI checks to make sure libraries we've set asides are not
added back
- Fix AllIcons component with does not work as intended (loaded on
initial load)
This commit is contained in:
Félix Malfait
2025-06-01 09:33:16 +02:00
committed by GitHub
parent c74d7fe986
commit f6bfec882a
37 changed files with 577 additions and 277 deletions

View File

@ -37,6 +37,14 @@ export default defineConfig(({ command, mode }) => {
? path.resolve(__dirname, './tsconfig.build.json')
: path.resolve(__dirname, './tsconfig.dev.json');
const CHUNK_SIZE_WARNING_LIMIT = 1024 * 1024; // 1MB
// Please don't increase this limit for main index chunk
// If it gets too big then find modules in the code base
// that can be loaded lazily, there are more!
const MAIN_CHUNK_SIZE_LIMIT = 4.1 * 1024 * 1024; // 4.1MB for main index chunk
const OTHER_CHUNK_SIZE_LIMIT = 15 * 1024 * 1024; // 5MB for other chunks
const checkers: Checkers = {
overlay: false,
};
@ -167,20 +175,82 @@ export default defineConfig(({ command, mode }) => {
outDir: 'build',
sourcemap: VITE_BUILD_SOURCEMAP === 'true',
rollupOptions: {
// Don't use manual chunks as it causes many issue
// including this one we wasted a lot of time on:
// https://github.com/rollup/rollup/issues/2793
output: {
manualChunks: (id) => {
if (id.includes('@scalar')) {
return 'scalar';
}
// Set chunk size warning limit (in bytes) - warns at 1MB
chunkSizeWarningLimit: CHUNK_SIZE_WARNING_LIMIT,
// Custom plugin to fail build if chunks exceed max size
plugins: [
{
name: 'chunk-size-limit',
generateBundle(_options, bundle) {
const oversizedChunks: string[] = [];
Object.entries(bundle).forEach(([fileName, chunk]) => {
if (chunk.type === 'chunk' && chunk.code) {
const size = Buffer.byteLength(chunk.code, 'utf8');
const isMainChunk = fileName.includes('index') && chunk.isEntry;
const sizeLimit = isMainChunk ? MAIN_CHUNK_SIZE_LIMIT : OTHER_CHUNK_SIZE_LIMIT;
const limitType = isMainChunk ? 'main' : 'other';
if (size > sizeLimit) {
oversizedChunks.push(`${fileName} (${limitType}): ${(size / 1024 / 1024).toFixed(2)}MB (limit: ${(sizeLimit / 1024 / 1024).toFixed(0)}MB)`);
}
}
});
if (oversizedChunks.length > 0) {
const errorMessage = `Build failed: The following chunks exceed their size limits:\n${oversizedChunks.map(chunk => ` - ${chunk}`).join('\n')}`;
this.error(errorMessage);
}
}
},
// TODO; later - think about prefetching modules such
// as date time picker, phone input etc...
/*
{
name: 'add-prefetched-modules',
transformIndexHtml(html: string,
ctx: {
path: string;
filename: string;
server?: ViteDevServer;
bundle?: import('rollup').OutputBundle;
chunk?: import('rollup').OutputChunk;
}) {
return null;
},
},
},
modulePreload: {
resolveDependencies: (filename, deps, { hostId }) => {
return deps.filter(dep => !dep.includes('scalar'));
},
const bundles = Object.keys(ctx.bundle ?? {});
let modernBundles = bundles.filter(
(bundle) => bundle.endsWith('.map') === false
);
// Remove existing files and concatenate them into link tags
const prefechBundlesString = modernBundles
.filter((bundle) => html.includes(bundle) === false)
.map((bundle) => `<link rel="prefetch" href="${ctx.server?.config.base}${bundle}">`)
.join('');
// Use regular expression to get the content within <head> </head>
const headContent = html.match(/<head>([\s\S]*)<\/head>/)?.[1] ?? '';
// Insert the content of prefetch into the head
const newHeadContent = `${headContent}${prefechBundlesString}`;
// Replace the original head
html = html.replace(
/<head>([\s\S]*)<\/head>/,
`<head>${newHeadContent}</head>`
);
return html;
},
}*/
]
}
},
},