102 lines
3.1 KiB
TypeScript
102 lines
3.1 KiB
TypeScript
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<string, CapabilityPackage> = {
|
|
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<void> {
|
|
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 <capability...>");
|
|
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 <list|add <capability...>>");
|
|
}
|