open-standards/wai/wai-studio/build.mjs
Transaction Science f85a0a7273 WAI Studio on the web: one front end, its core in WebAssembly, served by wai dev at /_wai/
wai/wai-studio: the panels (Inspect, Verify and receipts, Encode/decode/package, Rooms,
Run, Quantum) and the command palette; actions generated from the operation catalogue
with a drift check; the core (wai-studio-core) binding the wai crate for the tab. wai dev
serves Studio at /_wai/ on its own origin, with the dev stack's routes for it under
/_wai/api/.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 15:22:51 -04:00

85 lines
4.7 KiB
JavaScript

// Build WAI Studio into dist/, one front end for both hosts:
//
// node build.mjs [--core-pkg DIR] [--quantum DIR]
//
// wai-studio.js the interface, bundled
// wai-studio-worker.js the core's worker
// wai-studio.css Studio's styles; tokens.css, the site's design tokens, for a page alone
// index.html the local host: the page `wai dev` serves at /_wai/
// hosted.html the hosted host standing alone, on an origin that serves the room
// client at /room/ and its modules at /moq/ and /pkg/ (as `wai dev` does)
// pkg/ --core-pkg: the core built for the web (core/, wasm-bindgen --target
// web --out-name wai_studio_core)
// quantum/ --quantum: Quantum Studio's dist (wai/quantum-studio: node build.mjs
// --pkg <its engine package>), its engine in quantum/pkg/
// BUILD.json each file's SHA-256
//
// The site hosts the same files at /studio/ (its scripts/sync-wai-studio.mjs) with a page of
// its own. Every served name is distinct from the site's other files: its deploy names files
// by their content, keyed by basename.
import { build } from 'esbuild';
import { createHash } from 'node:crypto';
import { copyFileSync, cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
import { dirname, join, relative } from 'node:path';
import { fileURLToPath } from 'node:url';
const here = dirname(fileURLToPath(import.meta.url));
const dist = join(here, 'dist');
const arg = (name) => { const i = process.argv.indexOf(name); return i > 0 ? process.argv[i + 1] : null; };
const corePkg = arg('--core-pkg');
const quantum = arg('--quantum');
rmSync(dist, { recursive: true, force: true });
mkdirSync(dist, { recursive: true });
const common = { bundle: true, format: 'esm', target: ['es2022'], minify: true, legalComments: 'eof', logLevel: 'warning' };
await build({ ...common, entryPoints: [join(here, 'src/main.js')], outfile: join(dist, 'wai-studio.js') });
await build({ ...common, entryPoints: [join(here, 'src/worker.js')], outfile: join(dist, 'wai-studio-worker.js') });
copyFileSync(join(here, 'src/studio.css'), join(dist, 'wai-studio.css'));
copyFileSync(join(here, 'src/tokens.css'), join(dist, 'tokens.css'));
if (corePkg) {
mkdirSync(join(dist, 'pkg'));
for (const f of ['wai_studio_core.js', 'wai_studio_core_bg.wasm']) copyFileSync(join(corePkg, f), join(dist, 'pkg', f));
}
if (quantum) {
mkdirSync(join(dist, 'quantum'));
for (const f of ['studio.js', 'worker.js', 'studio.css']) copyFileSync(join(quantum, f), join(dist, 'quantum', f));
if (existsSync(join(quantum, 'pkg'))) cpSync(join(quantum, 'pkg'), join(dist, 'quantum', 'pkg'), { recursive: true });
}
// A page: the mount element with its host's attributes. Quantum's attributes only where its
// files are, so a build without them shows no Quantum panel.
const page = (title, host, extra) => {
const q = quantum
? ` data-quantum-script="quantum/studio.js" data-quantum-css="quantum/studio.css" data-quantum-pkg="quantum/pkg/wai_quantum_ide.js" data-quantum-worker="quantum/worker.js"`
: '';
return `<!doctype html>
<html lang="en" data-theme="auto">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>${title}</title>
<link rel="icon" href="data:," />
<link rel="stylesheet" href="tokens.css" />
<link rel="stylesheet" href="wai-studio.css" />
<style>
html, body { margin: 0; background: var(--bg-base); color: var(--text-primary); }
body { padding: 12px; }
</style>
</head>
<body>
<main id="wai-studio" data-host="${host}" data-core-pkg="pkg/wai_studio_core.js" data-worker="wai-studio-worker.js"${extra}${q}></main>
<script type="module" src="wai-studio.js"></script>
</body>
</html>
`;
};
writeFileSync(join(dist, 'index.html'), page('WAI Studio', 'local', ' data-api="/_wai/api" data-service="/room-api" data-room-page="/room/"'));
writeFileSync(join(dist, 'hosted.html'), page('WAI Studio', 'hosted', ' data-service="/room-api" data-room-page="/room/"'));
const files = [];
const walk = (d) => { for (const n of readdirSync(d)) { const p = join(d, n); if (statSync(p).isDirectory()) walk(p); else files.push(p); } };
walk(dist);
const sha256 = Object.fromEntries(files.sort().map((p) => [relative(dist, p), createHash('sha256').update(readFileSync(p)).digest('hex')]));
writeFileSync(join(dist, 'BUILD.json'), `${JSON.stringify({ source: 'open-standards wai/wai-studio', core: !!corePkg, quantum: !!quantum, sha256 }, null, 2)}\n`);
console.log(`built ${dist}${corePkg ? ', with the core' : ''}${quantum ? ', with Quantum Studio' : ''}`);