mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-09-16 06:42:02 +02:00
Add scripts for running Yaak in a browser (#664)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
881cff472d
commit
48889b6550
@@ -3,6 +3,9 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="icon" href="/favicon.ico" sizes="any" />
|
||||||
|
<link rel="icon" type="image/png" href="/icon-128.png" sizes="128x128" />
|
||||||
|
<link rel="apple-touch-icon" href="/icon-128.png" />
|
||||||
<title>Yaak App</title>
|
<title>Yaak App</title>
|
||||||
<!-- <script src="http://localhost:8097"></script>-->
|
<!-- <script src="http://localhost:8097"></script>-->
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,12 @@ const standardFontsDir = normalizePath(
|
|||||||
path.join(path.dirname(require.resolve("pdfjs-dist/package.json")), "standard_fonts"),
|
path.join(path.dirname(require.resolve("pdfjs-dist/package.json")), "standard_fonts"),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// The app's own icons, served as the page icon rather than copied into the client:
|
||||||
|
// the bundled app and the tab should not be able to disagree about what Yaak looks like.
|
||||||
|
const iconsDir = normalizePath(
|
||||||
|
path.join(import.meta.dirname, "../../crates-tauri/yaak-app-client/icons/release"),
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Which host the platform package installs. `web` builds Yaak to run in a plain
|
* Which host the platform package installs. `web` builds Yaak to run in a plain
|
||||||
* browser tab, with its own IndexedDB store instead of the Rust engine; anything
|
* browser tab, with its own IndexedDB store instead of the Rust engine; anything
|
||||||
@@ -62,6 +68,10 @@ export default defineConfig(async () => {
|
|||||||
targets: [
|
targets: [
|
||||||
{ src: cMapsDir, dest: "" },
|
{ src: cMapsDir, dest: "" },
|
||||||
{ src: standardFontsDir, dest: "" },
|
{ src: standardFontsDir, dest: "" },
|
||||||
|
// `/favicon.ico` is requested by browsers whether or not anything links to it,
|
||||||
|
// so it is served under that name to keep a 404 out of every console.
|
||||||
|
{ src: `${iconsDir}/icon.ico`, dest: "", rename: "favicon.ico" },
|
||||||
|
{ src: `${iconsDir}/128x128.png`, dest: "", rename: "icon-128.png" },
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -56,24 +56,14 @@ execSync("wasm-pack build --target bundler", {
|
|||||||
env: {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
CC_wasm32_unknown_unknown: clang,
|
CC_wasm32_unknown_unknown: clang,
|
||||||
AR_wasm32_unknown_unknown: fs.existsSync(ar) ? ar : (process.env.AR_wasm32_unknown_unknown ?? ""),
|
AR_wasm32_unknown_unknown: fs.existsSync(ar)
|
||||||
|
? ar
|
||||||
|
: (process.env.AR_wasm32_unknown_unknown ?? ""),
|
||||||
RUSTFLAGS: `--remap-path-prefix=${cargoHome}=/cargo --remap-path-prefix=${sysroot}=/rustc`,
|
RUSTFLAGS: `--remap-path-prefix=${cargoHome}=/cargo --remap-path-prefix=${sysroot}=/rustc`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Rewrite the generated entry to use Vite's ?init import style instead of
|
// No post-processing: wasm-pack's own entry (pkg/yaak_wasm.js) is what the app
|
||||||
// the ES Module Integration style that wasm-pack generates, which Vite/rolldown
|
// imports, and `vite-plugin-wasm` handles its ES Module Integration form in both
|
||||||
// does not support in production builds.
|
// dev and production builds. A rewrite used to live here for a Vite that could
|
||||||
const entry = path.join(__dirname, "pkg", "yaak_web.js");
|
// not, and it outlived both that Vite and the crate name it hardcoded.
|
||||||
fs.writeFileSync(
|
|
||||||
entry,
|
|
||||||
[
|
|
||||||
'import init from "./yaak_web_bg.wasm?init";',
|
|
||||||
'export * from "./yaak_web_bg.js";',
|
|
||||||
'import * as bg from "./yaak_web_bg.js";',
|
|
||||||
'const instance = await init({ "./yaak_web_bg.js": bg });',
|
|
||||||
"bg.__wbg_set_wasm(instance.exports);",
|
|
||||||
"instance.exports.__wbindgen_start();",
|
|
||||||
"",
|
|
||||||
].join("\n"),
|
|
||||||
);
|
|
||||||
|
|||||||
@@ -83,6 +83,11 @@
|
|||||||
"client:bundle": "node scripts/run-build.mjs client --config crates-tauri/yaak-app-client/tauri.release.conf.json --no-sign",
|
"client:bundle": "node scripts/run-build.mjs client --config crates-tauri/yaak-app-client/tauri.release.conf.json --no-sign",
|
||||||
"proxy:build": "node scripts/run-build.mjs proxy",
|
"proxy:build": "node scripts/run-build.mjs proxy",
|
||||||
"proxy:dev": "node scripts/run-dev.mjs proxy",
|
"proxy:dev": "node scripts/run-dev.mjs proxy",
|
||||||
|
"web:dev": "run-p web:dev:*",
|
||||||
|
"web:dev:app": "node scripts/run-web.mjs dev",
|
||||||
|
"web:dev:proxy": "node scripts/run-web.mjs proxy",
|
||||||
|
"web:build": "node scripts/run-web.mjs build",
|
||||||
|
"web:serve": "node scripts/run-web.mjs serve",
|
||||||
"migration": "node scripts/create-migration.cjs",
|
"migration": "node scripts/create-migration.cjs",
|
||||||
"build": "npm run --workspaces --if-present build",
|
"build": "npm run --workspaces --if-present build",
|
||||||
"test": "npm run --workspaces --if-present test",
|
"test": "npm run --workspaces --if-present test",
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run Yaak in a browser: the client built for the web target, and the server
|
||||||
|
* that executes its sends.
|
||||||
|
*
|
||||||
|
* `YAAK_TARGET=web` is what resolves `@yaakapp-internal/platform` to its browser
|
||||||
|
* entry (see apps/yaak-client/vite.config.ts). Setting it in a node script rather
|
||||||
|
* than inline in an npm script keeps this working on Windows.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { spawn, spawnSync } from "child_process";
|
||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
const rootDir = path.join(__dirname, "..");
|
||||||
|
|
||||||
|
// The post-checkout hook writes a per-worktree dev port in here, and the client's
|
||||||
|
// vite config reads YAAK_CLIENT_DEV_PORT from it. Without this, two worktrees both
|
||||||
|
// try to serve on 1420. (run-dev.mjs reads the same file, but with Object.assign,
|
||||||
|
// so there an explicit YAAK_CLIENT_DEV_PORT is silently ignored.)
|
||||||
|
const envLocalPath = path.join(rootDir, ".env.local");
|
||||||
|
if (fs.existsSync(envLocalPath)) {
|
||||||
|
for (const line of fs.readFileSync(envLocalPath, "utf8").split("\n")) {
|
||||||
|
if (!line || line.startsWith("#")) continue;
|
||||||
|
const [key, value] = line.split("=");
|
||||||
|
if (!key || !value) continue;
|
||||||
|
// Defaults, not overrides: a variable set on the command line is the more
|
||||||
|
// deliberate of the two and has to win, or `PORT=x vp run web:dev` silently
|
||||||
|
// lands somewhere else.
|
||||||
|
process.env[key.trim()] ??= value.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const DIST = "dist/apps/yaak-client";
|
||||||
|
|
||||||
|
const [mode] = process.argv.slice(2);
|
||||||
|
|
||||||
|
// Invoke the Vite+ CLI JS entry point directly via node, the way run-dev.mjs invokes
|
||||||
|
// the Tauri CLI. The `.bin/vp` shim is a POSIX script that npm pairs with `vp.cmd` and
|
||||||
|
// `vp.ps1` on Windows, so spawning it without a shell only works on one platform.
|
||||||
|
const vp = path.join(rootDir, "node_modules", "vite-plus", "bin", "vp");
|
||||||
|
|
||||||
|
const runVite = (args) => [process.execPath, [vp, ...args]];
|
||||||
|
|
||||||
|
const env = (extra = {}) => ({ ...process.env, YAAK_TARGET: "web", ...extra });
|
||||||
|
|
||||||
|
/** Run a build step to completion. A failure ends the script. */
|
||||||
|
function run(command, args, extra = {}) {
|
||||||
|
const result = spawnSync(command, args, { cwd: rootDir, stdio: "inherit", env: env(extra) });
|
||||||
|
if (result.status !== 0) process.exit(result.status ?? 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a server until it stops, and take it down when this process is stopped.
|
||||||
|
*
|
||||||
|
* Servers are started as a direct child rather than through `cargo run`, and
|
||||||
|
* signals are forwarded to them, because neither happens by itself: `cargo run`
|
||||||
|
* is a wrapper that does not pass a terminating signal on to the binary it
|
||||||
|
* spawned, and a child is not killed by its parent exiting. Either one on its
|
||||||
|
* own leaves a server holding the port after the terminal that started it has
|
||||||
|
* gone, and the next run fails with "address already in use".
|
||||||
|
*/
|
||||||
|
function serve(command, args, extra = {}) {
|
||||||
|
const child = spawn(command, args, { cwd: rootDir, stdio: "inherit", env: env(extra) });
|
||||||
|
|
||||||
|
const stop = (signal) => child.killed || child.kill(signal);
|
||||||
|
for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) process.on(signal, () => stop(signal));
|
||||||
|
process.on("exit", () => stop("SIGTERM"));
|
||||||
|
|
||||||
|
child.on("exit", (code, signal) => process.exit(code ?? (signal ? 1 : 0)));
|
||||||
|
child.on("error", (err) => {
|
||||||
|
console.error(`Failed to start ${command}: ${err.message}`);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Build the server and hand back the binary, so it can be run without a wrapper. */
|
||||||
|
function buildServer() {
|
||||||
|
run("cargo", ["build", "-p", "yaak-web"]);
|
||||||
|
const target = process.env.CARGO_TARGET_DIR ?? path.join(rootDir, "target");
|
||||||
|
return path.join(target, "debug", "yaak-web");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
// The app, with hot reload, on one origin: its own `/v1` is passed through to
|
||||||
|
// the server `web:dev` starts alongside it. Nothing to build first and one
|
||||||
|
// address to open.
|
||||||
|
case "dev":
|
||||||
|
serve(...runVite(["-C", "apps/yaak-client", "dev", "--force"]));
|
||||||
|
break;
|
||||||
|
|
||||||
|
// The send executor behind the dev server, on the port a dev build looks for.
|
||||||
|
case "proxy":
|
||||||
|
serve(buildServer(), []);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "build":
|
||||||
|
run(...runVite(["-C", "apps/yaak-client", "build"]));
|
||||||
|
break;
|
||||||
|
|
||||||
|
// One process serving both, the shape the Docker image runs. The build comes
|
||||||
|
// first because serving a stale `dist` silently tests the last change but one.
|
||||||
|
case "serve": {
|
||||||
|
const server = buildServer();
|
||||||
|
run(...runVite(["-C", "apps/yaak-client", "build"]));
|
||||||
|
serve(server, ["--serve", DIST]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.error(`Unknown mode ${JSON.stringify(mode)}; expected dev, proxy, build or serve`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user