import { readdirSync, readFileSync, rmSync, statSync } from 'node:fs'; import { join, resolve } from 'node:path'; import { gzipSync } from 'node:zlib'; import { svelte } from '@sveltejs/vite-plugin-svelte'; import { build } from 'vite'; const root = process.cwd(); const outDir = resolve(root, 'build/.bundle-smoke'); const virtualId = 'virtual:active-app-smoke'; const resolvedVirtualId = `\0${virtualId}`; const maxGzipKb = Number(process.env.ACTIVE_BUNDLE_GZIP_LIMIT_KB ?? 70); // Aliases mirror `svelte.config.js`. Specific subpaths must come before // the parent alias — Vite's resolver matches by prefix. const alias = { '$active-app/services': resolve(root, 'src/arts/active-app/service-factories'), '$active-app/presets': resolve(root, 'src/arts/active-app/presets'), '$active-app': resolve(root, 'src/arts/active-app'), $adom: resolve(root, 'src/arts/adom'), $auth: resolve(root, 'src/arts/auth'), $bus: resolve(root, 'src/arts/bus'), $cache: resolve(root, 'src/arts/cache'), $connection: resolve(root, 'src/arts/connection'), $frontend: resolve(root, 'src/arts/frontend'), $libs: resolve(root, 'src/libs'), $locale: resolve(root, 'src/libs/locale'), $reactive: resolve(root, 'src/libs/reactive'), $format: resolve(root, 'src/arts/format'), $http: resolve(root, 'src/arts/http'), $lang: resolve(root, 'src/arts/lang'), $logger: resolve(root, 'src/arts/logger'), $orca: resolve(root, 'src/arts/orca'), $perm: resolve(root, 'src/arts/perm'), $prefs: resolve(root, 'src/arts/prefs'), $session: resolve(root, 'src/arts/session'), $sium: resolve(root, 'src/arts/sium'), $storage: resolve(root, 'src/arts/storage'), $svrs: resolve(root, 'src/svrs'), $timer: resolve(root, 'src/arts/timer') }; rmSync(outDir, { recursive: true, force: true }); await build({ root, configFile: false, logLevel: 'silent', plugins: [ svelte({ compilerOptions: { runes: true } }), { name: 'active-app-smoke-entry', resolveId(id) { return id === virtualId ? resolvedVirtualId : undefined; }, load(id) { if (id !== resolvedVirtualId) return undefined; return [ "import { createActiveApp } from '$active-app/active-app.svelte';", "import { LogLevel } from '$logger/types';", 'const App = createActiveApp({ logger: { level: LogLevel.NONE, transports: [] } });', 'App.dispose();', 'export default App;' ].join('\n'); } } ], resolve: { alias }, build: { emptyOutDir: true, minify: 'oxc', outDir, rollupOptions: { input: virtualId, output: { chunkFileNames: 'chunks/[name]-[hash].js', entryFileNames: 'entry.js', format: 'es' } }, sourcemap: false } }); const jsFiles = listFiles(outDir).filter((file) => file.endsWith('.js')); const rawBytes = jsFiles.reduce((sum, file) => sum + statSync(file).size, 0); const gzBytes = gzipSync(Buffer.concat(jsFiles.map((file) => readFileSync(file)))).byteLength; const rawKb = bytesToKb(rawBytes); const gzKb = bytesToKb(gzBytes); console.log( `active bundle smoke: ${gzKb.toFixed(2)} KB gzip (${rawKb.toFixed(2)} KB raw, ${jsFiles.length} JS file)` ); if (gzKb > maxGzipKb) { console.error( `active bundle smoke exceeded ${maxGzipKb.toFixed(2)} KB gzip. Current: ${gzKb.toFixed(2)} KB.` ); process.exitCode = 1; } function listFiles(dir) { return readdirSync(dir, { withFileTypes: true }).flatMap((entry) => { const path = join(dir, entry.name); return entry.isDirectory() ? listFiles(path) : [path]; }); } function bytesToKb(bytes) { return bytes / 1024; }