Skip to content

Commit e0ba2f2

Browse files
authored
chore: fast playground build feat. claude (#17808)
The plugin we used to analyze bundle sizes was unnecessarily slow. After isolating the problem, tasked Claude with building a lightweight plugin that just reads the build output and enforcers the limits based on regexp. Works like a charm. Build went from ~180s to 35.
2 parents a137850 + dafbcc9 commit e0ba2f2

File tree

3 files changed

+90
-41
lines changed

3 files changed

+90
-41
lines changed

playground/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@
5858
"typescript": "~5.7.3",
5959
"typescript-eslint": "^8.11.0",
6060
"vite": "^7.1.4",
61-
"vite-plugin-bundlesize": "^0.3.0",
6261
"vite-plugin-node-polyfills": "^0.24.0",
6362
"vite-plugin-static-copy": "^3.1.2"
6463
}

playground/vite.config.ts

Lines changed: 89 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { defineConfig, loadEnv, searchForWorkspaceRoot } from 'vite';
1+
import { defineConfig, loadEnv, searchForWorkspaceRoot, Plugin, ResolvedConfig } from 'vite';
22
import react from '@vitejs/plugin-react-swc';
33
import { PolyfillOptions, nodePolyfills } from 'vite-plugin-node-polyfills';
4-
import bundlesize from 'vite-plugin-bundlesize';
4+
import fs from 'fs';
5+
import path from 'path';
56

67
// Only required for alternative bb wasm file, left as reference
78
//import { viteStaticCopy } from 'vite-plugin-static-copy';
@@ -21,6 +22,79 @@ const nodePolyfillsFix = (options?: PolyfillOptions | undefined): Plugin => {
2122
};
2223
};
2324

25+
/**
26+
* Lightweight chunk size validator plugin
27+
* Checks chunk sizes after build completes and fails if limits are exceeded
28+
*/
29+
interface ChunkSizeLimit {
30+
/** Pattern to match chunk file names (e.g., /assets\/index-.*\.js$/) */
31+
pattern: RegExp;
32+
/** Maximum size in kilobytes */
33+
maxSizeKB: number;
34+
/** Optional description for logging */
35+
description?: string;
36+
}
37+
38+
const chunkSizeValidator = (limits: ChunkSizeLimit[]): Plugin => {
39+
let config: ResolvedConfig;
40+
41+
return {
42+
name: 'chunk-size-validator',
43+
enforce: 'post',
44+
apply: 'build',
45+
configResolved(resolvedConfig) {
46+
config = resolvedConfig;
47+
},
48+
closeBundle() {
49+
const outDir = this.meta?.watchMode ? null : 'dist';
50+
if (!outDir) return; // Skip in watch mode
51+
52+
const logger = config.logger;
53+
const violations: string[] = [];
54+
const checkDir = (dir: string, baseDir: string = '') => {
55+
const files = fs.readdirSync(dir);
56+
57+
for (const file of files) {
58+
const filePath = path.join(dir, file);
59+
const relativePath = path.join(baseDir, file);
60+
const stat = fs.statSync(filePath);
61+
62+
if (stat.isDirectory()) {
63+
checkDir(filePath, relativePath);
64+
} else if (stat.isFile()) {
65+
const sizeKB = stat.size / 1024;
66+
67+
for (const limit of limits) {
68+
if (limit.pattern.test(relativePath)) {
69+
const desc = limit.description ? ` (${limit.description})` : '';
70+
logger.info(` ${relativePath}: ${sizeKB.toFixed(2)} KB / ${limit.maxSizeKB} KB${desc}`);
71+
72+
if (sizeKB > limit.maxSizeKB) {
73+
violations.push(
74+
` ❌ ${relativePath}: ${sizeKB.toFixed(2)} KB exceeds limit of ${limit.maxSizeKB} KB${desc}`,
75+
);
76+
}
77+
}
78+
}
79+
}
80+
}
81+
};
82+
83+
logger.info('\n📦 Validating chunk sizes...');
84+
checkDir(path.resolve(process.cwd(), outDir));
85+
86+
if (violations.length > 0) {
87+
logger.error('\n❌ Chunk size validation failed:\n');
88+
violations.forEach(v => logger.error(v));
89+
logger.error('\n');
90+
throw new Error('Build failed: chunk size limits exceeded');
91+
} else {
92+
logger.info('✅ All chunks within size limits\n');
93+
}
94+
},
95+
};
96+
};
97+
2498
// https://vite.dev/config/
2599
export default defineConfig(({ mode }) => {
26100
const env = loadEnv(mode, process.cwd(), '');
@@ -47,7 +121,7 @@ export default defineConfig(({ mode }) => {
47121
},
48122
plugins: [
49123
react({ jsxImportSource: '@emotion/react' }),
50-
nodePolyfillsFix({ include: ['buffer', 'path'] }),
124+
nodePolyfillsFix({ include: ['buffer', 'path', 'process', 'net', 'tty'] }),
51125
// This is unnecessary unless BB_WASM_PATH is defined (default would be /assets/barretenberg.wasm.gz)
52126
// Left as an example of how to use a different bb wasm file than the default lazily loaded one
53127
// viteStaticCopy({
@@ -58,18 +132,21 @@ export default defineConfig(({ mode }) => {
58132
// },
59133
// ],
60134
// }),
61-
bundlesize({
135+
chunkSizeValidator([
62136
// Bump log:
63137
// - AD: bumped from 1600 => 1680 as we now have a 20kb msgpack lib in bb.js and other logic got us 50kb higher, adding some wiggle room.
64138
// - MW: bumped from 1700 => 1750 after adding the noble curves pkg to foundation required for blob batching calculations.
65-
limits: [
66-
// Main entrypoint, hard limit
67-
{ name: 'assets/index-*', limit: '1750kB' },
68-
// This limit is to detect wheter our json artifacts or bb.js wasm get out of control. At the time
69-
// of writing, all the .js files bundled in the app are below 4MB
70-
{ name: '**/*', limit: '4000kB' },
71-
],
72-
}),
139+
{
140+
pattern: /assets\/index-.*\.js$/,
141+
maxSizeKB: 1750,
142+
description: 'Main entrypoint, hard limit',
143+
},
144+
{
145+
pattern: /.*/,
146+
maxSizeKB: 4000,
147+
description: 'Detect if json artifacts or bb.js wasm get out of control',
148+
},
149+
]),
73150
],
74151
define: {
75152
'process.env': JSON.stringify({
@@ -81,9 +158,5 @@ export default defineConfig(({ mode }) => {
81158
BB_WASM_PATH: env.BB_WASM_PATH,
82159
}),
83160
},
84-
build: {
85-
// Required by vite-plugin-bundle-size
86-
sourcemap: 'hidden',
87-
},
88161
};
89162
});

playground/yarn.lock

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ __metadata:
102102
typescript: "npm:~5.7.3"
103103
typescript-eslint: "npm:^8.11.0"
104104
vite: "npm:^7.1.4"
105-
vite-plugin-bundlesize: "npm:^0.3.0"
106105
vite-plugin-node-polyfills: "npm:^0.24.0"
107106
vite-plugin-static-copy: "npm:^3.1.2"
108107
languageName: unknown
@@ -5708,7 +5707,7 @@ __metadata:
57085707
languageName: node
57095708
linkType: hard
57105709

5711-
"strip-ansi@npm:^7.0.1, strip-ansi@npm:^7.1.0":
5710+
"strip-ansi@npm:^7.0.1":
57125711
version: 7.1.0
57135712
resolution: "strip-ansi@npm:7.1.0"
57145713
dependencies:
@@ -6045,21 +6044,6 @@ __metadata:
60456044
languageName: node
60466045
linkType: hard
60476046

6048-
"vite-plugin-bundlesize@npm:^0.3.0":
6049-
version: 0.3.0
6050-
resolution: "vite-plugin-bundlesize@npm:0.3.0"
6051-
dependencies:
6052-
picomatch: "npm:^4.0.3"
6053-
strip-ansi: "npm:^7.1.0"
6054-
vlq: "npm:^2.0.4"
6055-
peerDependencies:
6056-
vite: ">= 6.0.0"
6057-
bin:
6058-
bundlesize: bin/cli.js
6059-
checksum: 10c0/2c81c710715c86cc6dc0ff71339ff1a0f233e0f6cada71849e2bd7c66cdb2caca0d393d6f67726496fd8aebf62681a41287de72223ce0b1e8061d2e53e6ef8da
6060-
languageName: node
6061-
linkType: hard
6062-
60636047
"vite-plugin-node-polyfills@npm:^0.24.0":
60646048
version: 0.24.0
60656049
resolution: "vite-plugin-node-polyfills@npm:0.24.0"
@@ -6142,13 +6126,6 @@ __metadata:
61426126
languageName: node
61436127
linkType: hard
61446128

6145-
"vlq@npm:^2.0.4":
6146-
version: 2.0.4
6147-
resolution: "vlq@npm:2.0.4"
6148-
checksum: 10c0/7b4b14a724ad21a71a5ea8aabc08e7b766271cbf61b51b5a861c6f8c917446d7b10956ceaa95f6ce21cd80f12468875fc42287d3b7b1a40e627c116400ce663e
6149-
languageName: node
6150-
linkType: hard
6151-
61526129
"vm-browserify@npm:^1.0.1":
61536130
version: 1.1.2
61546131
resolution: "vm-browserify@npm:1.1.2"

0 commit comments

Comments
 (0)