feat(FN-1093): add mobile build, PWA, and live reload workflow
- Optimize dashboard Vite output for mobile and include vite/client types in app typecheck - Add PWA support with manifest, service worker, icons, and client registration hooks - Add a mobile workspace package with Capacitor config and live-reload scripts for local development - Add a dedicated GitHub Actions mobile pipeline and update mobile workflow documentation - Add dashboard tests for build output, mobile scripts, and PWA asset coverage
This commit is contained in:
118
packages/mobile/scripts/live-reload.ts
Normal file
118
packages/mobile/scripts/live-reload.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import { spawn, type ChildProcess } from "node:child_process";
|
||||
import { setTimeout as delay } from "node:timers/promises";
|
||||
|
||||
type MobilePlatform = "ios" | "android";
|
||||
|
||||
interface Args {
|
||||
platform: MobilePlatform;
|
||||
serverUrl: string;
|
||||
}
|
||||
|
||||
function parseArgs(argv: string[]): Args {
|
||||
let platform: MobilePlatform | undefined;
|
||||
let serverUrl = process.env.FUSION_SERVER_URL || "http://localhost:5173";
|
||||
|
||||
for (let i = 0; i < argv.length; i += 1) {
|
||||
const arg = argv[i];
|
||||
|
||||
if (arg === "--platform") {
|
||||
const value = argv[i + 1];
|
||||
if (value !== "ios" && value !== "android") {
|
||||
throw new Error("--platform must be either 'ios' or 'android'");
|
||||
}
|
||||
platform = value;
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === "--server-url") {
|
||||
const value = argv[i + 1];
|
||||
if (!value) {
|
||||
throw new Error("--server-url requires a value");
|
||||
}
|
||||
serverUrl = value;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!platform) {
|
||||
throw new Error("Missing required --platform argument");
|
||||
}
|
||||
|
||||
return { platform, serverUrl };
|
||||
}
|
||||
|
||||
async function waitForServer(serverUrl: string, timeoutMs = 120_000): Promise<void> {
|
||||
const start = Date.now();
|
||||
const healthUrl = new URL("/", serverUrl).toString();
|
||||
|
||||
while (Date.now() - start < timeoutMs) {
|
||||
try {
|
||||
const response = await fetch(healthUrl, { method: "GET" });
|
||||
if (response.ok || response.status === 404) {
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
// server not ready yet
|
||||
}
|
||||
|
||||
await delay(1_000);
|
||||
}
|
||||
|
||||
throw new Error(`Timed out waiting for dev server at ${serverUrl}`);
|
||||
}
|
||||
|
||||
function command(name: string): string {
|
||||
return process.platform === "win32" ? `${name}.cmd` : name;
|
||||
}
|
||||
|
||||
function spawnWithInheritedIo(bin: string, args: string[], env: NodeJS.ProcessEnv): ChildProcess {
|
||||
return spawn(bin, args, {
|
||||
stdio: "inherit",
|
||||
env,
|
||||
});
|
||||
}
|
||||
|
||||
async function run(): Promise<void> {
|
||||
const { platform, serverUrl } = parseArgs(process.argv.slice(2));
|
||||
const env: NodeJS.ProcessEnv = {
|
||||
...process.env,
|
||||
FUSION_LIVE_RELOAD: "true",
|
||||
FUSION_SERVER_URL: serverUrl,
|
||||
};
|
||||
|
||||
const devServer = spawnWithInheritedIo(command("pnpm"), ["--filter", "@fusion/dashboard", "dev:serve"], env);
|
||||
|
||||
const cleanup = () => {
|
||||
if (!devServer.killed) {
|
||||
devServer.kill("SIGTERM");
|
||||
}
|
||||
};
|
||||
|
||||
process.on("SIGINT", cleanup);
|
||||
process.on("SIGTERM", cleanup);
|
||||
|
||||
try {
|
||||
await waitForServer(serverUrl);
|
||||
|
||||
const capRun = spawnWithInheritedIo(command("npx"), ["cap", "run", platform], env);
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
capRun.on("error", reject);
|
||||
capRun.on("exit", (code) => {
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
reject(new Error(`cap run ${platform} exited with code ${code ?? "unknown"}`));
|
||||
});
|
||||
});
|
||||
} finally {
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
run().catch((error) => {
|
||||
console.error("[mobile live-reload]", error instanceof Error ? error.message : error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
Reference in New Issue
Block a user