talking-pixel / scripts /postbuild.mjs
cfahlgren1's picture
cfahlgren1 HF Staff
convert talking pixel to a sveltekit app that runs pivo-1.4m tts in the browser
3ec6b9e
Raw History Blame Contribute Delete
3.81 kB
// After `vite build`:
// 1. Make the shell's asset URLs relative. In hash-router mode Kit emits them absolute,
// which would break the page anywhere but the domain root.
// 2. Compute what the home page really downloads from this origin and stamp it into
// build/index.html, which the giant number reads. Google Fonts are not counted.
import { readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs';
import { join, relative } from 'node:path';
const root = new URL('../', import.meta.url).pathname;
const build = join(root, 'build');
const generatedNodes = join(root, '.svelte-kit/generated/build/client-optimized/nodes');
const manifest = JSON.parse(readFileSync(join(root, '.svelte-kit/output/client/.vite/manifest.json'), 'utf8'));
const PLACEHOLDER = '__TP_DOWNLOAD_BYTES__';
const indexPath = join(build, 'index.html');
const shell = readFileSync(indexPath, 'utf8').replaceAll('"/_app/', '"./_app/');
if (!shell.includes(PLACEHOLDER)) throw new Error('download-size placeholder not found in build/index.html');
if (shell.includes('"/_app/')) throw new Error('absolute asset URLs left in build/index.html');
function walk(dir) {
return readdirSync(dir).flatMap((name) => {
const path = join(dir, name);
return statSync(path).isDirectory() ? walk(path) : [path];
});
}
const keyByFile = new Map(Object.entries(manifest).map(([key, entry]) => [entry.file, key]));
const files = new Set();
const seen = new Set();
function visit(key) {
if (seen.has(key)) return;
seen.add(key);
const entry = manifest[key];
if (!entry) throw new Error(`manifest entry missing: ${key}`);
files.add(entry.file);
for (const css of entry.css ?? []) files.add(css);
for (const dep of entry.imports ?? []) visit(dep);
}
// Everything the shell preloads or loads, with its static imports.
const shellRefs = [...shell.matchAll(/["'](\.\/_app\/[^"']+)["']/g)].map((m) => m[1].slice(2));
if (shellRefs.length === 0) throw new Error('no asset references found in the shell');
for (const ref of new Set(shellRefs)) {
if (keyByFile.has(ref)) visit(keyByFile.get(ref));
else files.add(ref); // stylesheet
}
// The layout, error and home page nodes; the benchmark route is split out and not downloaded by the app.
const nodeSources = readdirSync(generatedNodes).map((file) => [file, readFileSync(join(generatedNodes, file), 'utf8')]);
const appNodes = nodeSources.filter(([, source]) => !source.includes('routes/bench/'));
if (!appNodes.some(([, source]) => source.includes('routes/+page.svelte'))) throw new Error('could not find the home page node');
for (const [file] of appNodes) visit(`.svelte-kit/generated/build/client-optimized/nodes/${file}`);
// The synthesis worker; the benchmark worker is not part of the app.
const workers = walk(join(build, '_app/immutable/workers')).filter((path) => !path.includes('bench-worker'));
if (workers.length !== 1) throw new Error(`expected one app worker, found ${workers.length}`);
files.add(relative(build, workers[0]));
const fixed = ['_app/version.json', 'player-worklet.js', ...['encoder', 'decoder', 'conditioning', 'vocoder_step'].map((f) => `models/${f}.onnx`), 'models/cmudict.dict.gz'];
const others = [...files, ...fixed].reduce((sum, file) => sum + statSync(join(build, file)).size, 0);
// index.html contains its own size, so iterate until the digit count settles.
let total = others;
for (let i = 0; i < 4; i++) total = others + Buffer.byteLength(shell.replace(PLACEHOLDER, String(total)));
writeFileSync(indexPath, shell.replace(PLACEHOLDER, String(total)));
writeFileSync(join(root, '.svelte-kit/download-files.json'), JSON.stringify(['index.html', ...files, ...fixed].sort(), null, 1));
console.log(`download size: ${total} bytes (${(total / 1e6).toFixed(1)} MB) across ${files.size + fixed.length + 1} files`);