Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions packages/ui/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,42 @@ import tailwindcss from '@tailwindcss/vite';
import { visualizer } from 'rollup-plugin-visualizer';
import { readFileSync } from 'fs';
import { resolve } from 'path';
import type { Plugin } from 'vite';

/**
* Fail the build if the emitted CSS is suspiciously small.
*
* Tailwind v4 scans source files with the native `@tailwindcss/oxide` addon.
* If that native binary fails to load (e.g. a corrupt platform package on
* Windows), oxide silently falls back to its WASM build, which scans nothing
* and emits a utility-less stylesheet (~13 KB instead of ~250 KB) — with no
* error. That ships an entirely unstyled UI, including in Docker images.
* A healthy build is hundreds of KB; 80 KB is far above the broken case and
* far below any legitimate output, so it cleanly distinguishes the two.
*/
function cssSizeGuard(minBytes = 80_000): Plugin {
return {
name: 'css-size-guard',
apply: 'build',
writeBundle(_options, bundle) {
let total = 0;
for (const [name, asset] of Object.entries(bundle)) {
if (name.endsWith('.css') && asset.type === 'asset') {
const src = asset.source;
total += typeof src === 'string' ? Buffer.byteLength(src) : src.byteLength;
}
}
if (total < minBytes) {
throw new Error(
`[css-size-guard] Emitted CSS is only ${total} bytes (< ${minBytes}). ` +
`Tailwind likely generated no utilities — the native @tailwindcss/oxide ` +
`scanner probably failed and fell back to WASM. Repair the install ` +
`(pnpm install --force) before shipping.`
);
}
},
};
}

export default defineConfig(({ mode }) => {
// Load env from monorepo root (two levels up from packages/ui)
Expand Down Expand Up @@ -37,6 +73,7 @@ export default defineConfig(({ mode }) => {
plugins: [
react(),
tailwindcss(),
cssSizeGuard(),
...(enableVisualizer
? [
visualizer({
Expand Down
Loading