1import * as NodeFSP from "node:fs/promises";
2import * as NodePath from "node:path";
3import * as NodeURL from "node:url";
4import { build } from "vite-plus";
5
6const mobileRoot = NodePath.resolve(NodePath.dirname(NodeURL.fileURLToPath(import.meta.url)), "..");
7
8/** Metro embeds each shared browser transport as a small script in a native WebView. */
9async function generateWebViewScript(feature: string, name: string) {
10 const stem = `${name.toLowerCase()}-stream`;
11 const result = await build({
12 configFile: false,
13 logLevel: "silent",
14 build: {
15 write: false,
16 target: "es2022",
17 minify: true,
18 lib: {
19 entry: NodePath.join(mobileRoot, "src/features", feature, `${stem}.browser.ts`),
20 name: `T3${name}Stream`,
21 formats: ["iife"],
22 },
23 },
24 });
25 const bundles = Array.isArray(result) ? result : [result];
26 const chunk = bundles
27 .flatMap((bundle) => ("output" in bundle ? bundle.output : []))
28 .find((output) => output.type === "chunk");
29 if (!chunk) throw new Error(`${name} build did not emit a script.`);
30 const root = NodePath.join(mobileRoot, ".generated", stem);
31 await NodeFSP.mkdir(root, { recursive: true });
32 for (const [file, contents] of [
33 ["index.js", `module.exports = ${JSON.stringify(chunk.code)};\n`],
34 ["package.json", '{"main":"index.js"}\n'],
35 ] as const) {
36 const destination = NodePath.join(root, file);
37 const previous = await NodeFSP.readFile(destination, "utf8").catch(() => null);
38 if (previous !== contents) await NodeFSP.writeFile(destination, contents);
39 }
40}
41
42export const generateDeviceStreamScript = () => generateWebViewScript("devices", "Device");
43export const generatePreviewStreamScript = () => generateWebViewScript("browser", "Preview");