Files
paseo/packages/app/scripts/build-source-editor-webview-html.mjs
Mohamed Boudra 95b7ef823a feat(app): edit source files on mobile
Use one source-editor contract and shared editing runtime across browser and native hosts so file-pane persistence, conflicts, and line endings keep the same semantics on every platform.
2026-08-30 21:21:48 +02:00

96 lines
3.0 KiB
JavaScript

import esbuild from "esbuild";
import fs from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
const scriptDirectory = path.dirname(fileURLToPath(import.meta.url));
const appRoot = path.resolve(scriptDirectory, "..");
const repoRoot = path.resolve(appRoot, "../..");
const entry = path.join(appRoot, "src/source-editor/codemirror/webview-entry.ts");
const output = path.join(appRoot, "src/source-editor/codemirror/webview-html.ts");
const checkOnly = process.argv.includes("--check");
async function resolveTsPath(basePath) {
const candidates = [
basePath,
`${basePath}.ts`,
`${basePath}.tsx`,
`${basePath}.js`,
`${basePath}.jsx`,
path.join(basePath, "index.ts"),
path.join(basePath, "index.tsx"),
];
for (const candidate of candidates) {
try {
if ((await fs.stat(candidate)).isFile()) return candidate;
} catch {
// Try the next TypeScript resolution candidate.
}
}
return basePath;
}
const aliasPlugin = {
name: "paseo-alias",
setup(build) {
build.onResolve({ filter: /^@\// }, async (args) => ({
path: await resolveTsPath(path.join(appRoot, "src", args.path.slice(2))),
}));
},
};
const result = await esbuild.build({
entryPoints: [entry],
bundle: true,
write: false,
format: "iife",
platform: "browser",
target: ["ios15", "chrome100"],
plugins: [aliasPlugin],
logLevel: "info",
});
const javascript = result.outputFiles[0]?.text;
if (!javascript) throw new Error("source editor WebView bundle produced no JavaScript");
const html = `<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data:" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover, interactive-widget=resizes-content" />
<style>
html, body, #source-editor-root, #source-editor-host {
width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden;
}
html, body { overscroll-behavior: none; }
#source-editor-root, #source-editor-host { display: flex; min-width: 0; min-height: 0; }
#source-editor-host { flex: 1; }
* { -webkit-tap-highlight-color: transparent; }
</style>
</head>
<body>
<script>${javascript}</script>
</body>
</html>`;
const contents = `// Generated by packages/app/scripts/build-source-editor-webview-html.mjs.
// Do not edit by hand.
export const sourceEditorWebViewHtml =
${JSON.stringify(html)};
`;
if (checkOnly) {
const existing = await fs.readFile(output, "utf8").catch(() => null);
if (existing !== contents) {
throw new Error(
`${path.relative(repoRoot, output)} is stale. Run npm --prefix packages/app run build:source-editor-webview.`,
);
}
console.log(`${path.relative(repoRoot, output)} is current (${html.length} bytes)`);
} else {
await fs.writeFile(output, contents);
console.log(`Wrote ${path.relative(repoRoot, output)} (${html.length} bytes)`);
}