import { existsSync, readFileSync } from "node:fs"; import { join, resolve } from "node:path"; import { runMobileCommand } from "./mobile-command.ts"; interface CapabilityPackage { browser: string; capacitor?: string; expo?: string; } export const nativeCatalog: Record = { camera: { browser: "MediaDevices / file input", capacitor: "@capacitor/camera", expo: "expo-camera", }, clipboard: { browser: "Clipboard API", capacitor: "@capacitor/clipboard", expo: "expo-clipboard", }, device: { browser: "Browser device information", capacitor: "@capacitor/device", expo: "expo-device", }, filesystem: { browser: "File System Access API", capacitor: "@capacitor/filesystem", expo: "expo-file-system", }, geolocation: { browser: "Geolocation API", capacitor: "@capacitor/geolocation", expo: "expo-location", }, haptics: { browser: "Vibration API", capacitor: "@capacitor/haptics", expo: "expo-haptics" }, network: { browser: "Navigator online status", capacitor: "@capacitor/network", expo: "expo-network", }, notifications: { browser: "Notifications API", capacitor: "@capacitor/local-notifications", expo: "expo-notifications", }, share: { browser: "Web Share API", capacitor: "@capacitor/share" }, storage: { browser: "Web Storage", capacitor: "@capacitor/preferences", expo: "expo-secure-store", }, }; function mode(root: string): "webview" | "native" { const file = join(root, "mobile", "package.json"); if (!existsSync(file)) throw new Error("No mobile project found. Run `wrnexus generate mobile` first."); return ( (JSON.parse(readFileSync(file, "utf8")) as { wrnexus?: { mode?: "webview" | "native" } }) .wrnexus?.mode ?? "webview" ); } export async function runNativeCommand( appRoot: string, subcommand?: string, args: string[] = [], ): Promise { if (subcommand === "list" || !subcommand) { console.log("WrNexus native capabilities:\n"); for (const [name, entry] of Object.entries(nativeCatalog)) { console.log( ` ${name.padEnd(14)} browser: ${entry.browser}; Capacitor: ${entry.capacitor ?? "built in"}; Expo: ${entry.expo ?? "built in"}`, ); } return; } if (subcommand === "add") { const names = args.filter((arg) => !arg.startsWith("--")); if (!names.length) throw new Error("Usage: wrnexus native add "); const unknown = names.filter((name) => !nativeCatalog[name]); if (unknown.length) throw new Error( `Unknown native capability: ${unknown.join(", ")}. Run \`wrnexus native list\`.`, ); const root = resolve(appRoot); const target = mode(root); const packages = names .map((name) => nativeCatalog[name]![target === "native" ? "expo" : "capacitor"]) .filter((value): value is string => !!value); if (!packages.length) { console.log(` ✓ ${names.join(", ")} use built-in APIs; no package installation required`); return; } await runMobileCommand(root, "add", packages); return; } throw new Error("Usage: wrnexus native >"); }