one
Version:
One is a React framework for web, iOS, and Android apps.
465 lines (461 loc) • 11.3 kB
JavaScript
import { defineCommand, runMain, showUsage } from "citty";
import { readFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import colors from "picocolors";
function getPackageVersion() {
let dirname;
if (typeof __dirname !== "undefined") {
dirname = __dirname;
} else {
dirname = path.dirname(fileURLToPath(import.meta.url));
}
const packagePath = path.join(dirname, "..", "..", "package.json");
const packageJson = JSON.parse(readFileSync(packagePath, "utf-8"));
return packageJson.version;
}
const version = getPackageVersion();
const DOCS_BASE = "https://onestack.dev/docs";
const docsLinks = {
dev: `${DOCS_BASE}/one-dev`,
build: `${DOCS_BASE}/one-build`,
serve: `${DOCS_BASE}/one-serve`,
prebuild: `${DOCS_BASE}/guides-ios-native`,
"run:ios": `${DOCS_BASE}/guides-ios-native`,
"run:android": `${DOCS_BASE}/guides-ios-native`,
clean: `${DOCS_BASE}/configuration`,
patch: `${DOCS_BASE}/configuration`,
"generate-routes": `${DOCS_BASE}/routing-typed-routes`,
typegen: `${DOCS_BASE}/routing-typed-routes`,
"metro-eject": `${DOCS_BASE}/guides-ota-updates`,
"updates-publish": "https://onestack.dev/native/updates"
};
function withDocsLink(description, command) {
return `${description}
Docs: ${docsLinks[command]}`;
}
if (path.sep !== "/") {
console.warn(colors.bgYellow("WARNING: UNSUPPORTED OS") + colors.yellow(" - It appears you’re using Windows, which is currently not supported. You may experience unexpected issues."));
}
const modes = {
development: "development",
production: "production"
};
function lastValue(arg) {
if (Array.isArray(arg)) {
return arg[arg.length - 1];
}
return arg;
}
const dev = defineCommand({
meta: {
name: "dev",
version,
description: withDocsLink("Start the dev server", "dev")
},
args: {
clean: { type: "boolean" },
host: { type: "string" },
port: { type: "string" },
mode: {
type: "string",
description: "If set to \"production\" you can run the development server but serve the production bundle"
},
"debug-bundle": {
type: "string",
description: `Will output the bundle to a temp file and then serve it from there afterwards allowing you to easily edit the bundle to debug problems.`
},
debug: {
type: "string",
description: `Pass debug args to Vite`
},
"extra-config": {
type: "string",
description: `Path to an extra vite config file to merge on top of the project config`
}
},
async run({ args }) {
const { dev: dev2 } = await import("./cli/dev.mjs");
await dev2({
...args,
port: lastValue(args.port),
host: lastValue(args.host),
debugBundle: lastValue(args["debug-bundle"]),
mode: modes[lastValue(args.mode)],
extraConfig: lastValue(args["extra-config"])
});
}
});
const buildCommand = defineCommand({
meta: {
name: "build",
version,
description: withDocsLink("Build your app", "build")
},
args: {
step: {
type: "string",
required: false
},
only: {
type: "string",
required: false
},
platform: {
type: "string",
description: `One of: web, ios, android`,
default: "web",
required: false
},
"skip-env": {
type: "boolean",
description: `Skip loading .env files during build`,
required: false
}
},
async run({ args }) {
const { build } = await import("./cli/build.mjs");
const platforms = {
ios: "ios",
web: "web",
android: "android"
};
if (args.platform && !platforms[args.platform]) {
throw new Error(`Invalid platform: ${args.platform}`);
}
const platform = platforms[args.platform] || "web";
await build({
only: args.only,
platform,
step: args.step,
skipEnv: args["skip-env"]
});
process.exit(0);
}
});
const serveCommand = defineCommand({
meta: {
name: "serve",
version,
description: withDocsLink("Serve a built app for production", "serve")
},
args: {
host: { type: "string" },
port: { type: "string" },
compress: { type: "boolean" },
loadEnv: { type: "boolean" },
outDir: {
type: "string",
description: "Build output directory (default: dist)"
},
cluster: {
type: "string",
description: "Enable cluster mode with N workers (default: cpu count)"
}
},
async run({ args }) {
const { serve } = await import("./serve.mjs");
const port = lastValue(args.port);
const host = lastValue(args.host);
const clusterArg = lastValue(args.cluster);
await serve({
port: port ? +port : void 0,
host,
compress: args.compress,
loadEnv: !!args.loadEnv,
outDir: lastValue(args.outDir),
cluster: clusterArg !== void 0 ? clusterArg ? +clusterArg : true : void 0
});
}
});
const prebuild = defineCommand({
meta: {
name: "prebuild",
version,
description: withDocsLink("Prebuild native project", "prebuild")
},
args: {
platform: {
type: "string",
description: "ios or android"
},
"no-install": {
type: "boolean",
description: "skip installing native dependencies",
default: false
}
},
async run({ args }) {
if (args.install === false) args["no-install"] = true;
const { run } = await import("./cli/prebuild.mjs");
await run(args);
}
});
const runIos = defineCommand({
meta: {
name: "run:ios",
version,
description: withDocsLink("Run the iOS app", "run:ios")
},
args: {
simulator: {
type: "string",
description: "explicitly set the simulator to use, e.g. \"iPhone 16\""
},
udid: {
type: "string",
description: "explicitly set the simulator to use by UDID"
}
},
async run({ args }) {
const { run } = await import("./cli/runIos.mjs");
await run(args);
}
});
const runAndroid = defineCommand({
meta: {
name: "run:android",
version,
description: withDocsLink("Run the Android app", "run:android")
},
args: {},
async run({ args }) {
const { run } = await import("./cli/runAndroid.mjs");
await run(args);
}
});
const clean = defineCommand({
meta: {
name: "clean",
version: "0.0.0",
description: withDocsLink("Clean build folders", "clean")
},
args: {},
async run() {
const { clean: vxrnClean } = await import("vxrn");
await vxrnClean({ root: process.cwd() });
}
});
const patch = defineCommand({
meta: {
name: "patch",
version: "0.0.0",
description: withDocsLink("Apply package patches", "patch")
},
args: { force: {
type: "boolean",
description: "Force re-apply all patches"
} },
async run({ args }) {
const { run } = await import("./cli/patch.mjs");
await run(args);
}
});
const generateRoutes = defineCommand({
meta: {
name: "generate-routes",
version,
description: withDocsLink("Generate route type definitions (routes.d.ts)", "generate-routes")
},
args: {
appDir: {
type: "string",
description: "Path to app directory (default: \"app\")"
},
typed: {
type: "string",
description: "Auto-generate route helpers. Options: \"type\" (type-only helpers) or \"runtime\" (runtime helpers)"
}
},
async run({ args }) {
const { run } = await import("./cli/generateRoutes.mjs");
await run(args);
}
});
const typegen = defineCommand({
meta: {
name: "typegen",
version,
description: withDocsLink("Generate routes.d.ts (alias for generate-routes)", "typegen")
},
args: {
appDir: {
type: "string",
description: "Path to app directory (default: \"app\")"
},
typed: {
type: "string",
description: "Auto-generate route helpers. Options: \"type\" (type-only helpers) or \"runtime\" (runtime helpers)"
}
},
async run({ args }) {
const { run } = await import("./cli/generateRoutes.mjs");
await run(args);
}
});
const metroEject = defineCommand({
meta: {
name: "metro-eject",
version,
description: withDocsLink("Write babel.config.cjs + metro.config.cjs so you can own them and customize freely", "metro-eject")
},
args: { force: {
type: "boolean",
description: "Overwrite existing files"
} },
async run({ args }) {
const { generateBundlerConfig } = await import("./cli/generateBundlerConfig.mjs");
const { loadUserOneOptions } = await import("./vite/loadConfig.mjs");
process.env.IS_VXRN_CLI = "true";
const { oneOptions } = await loadUserOneOptions("build");
const { ok } = generateBundlerConfig({
force: !!args.force,
eject: true,
oneOptions
});
if (!ok) process.exit(1);
}
});
const daemonCommand = defineCommand({
meta: {
name: "daemon",
version,
description: "Multi-app development server proxy"
},
args: {
subcommand: {
type: "positional",
description: "Subcommand: start, stop, status, route (default: start)",
required: false
},
port: {
type: "string",
description: "Port to listen on (default: 8081)"
},
host: {
type: "string",
description: "Host to bind to (default: 0.0.0.0)"
},
app: {
type: "string",
description: "Bundle ID for route command"
},
slot: {
type: "string",
description: "Slot number for route command"
},
project: {
type: "string",
description: "Project path for route command"
},
tui: {
type: "boolean",
description: "Show TUI (default: true if TTY)"
}
},
async run({ args }) {
const { daemon } = await import("./cli/daemon.mjs");
await daemon({
...args,
port: lastValue(args.port),
host: lastValue(args.host)
});
}
});
const updatesPublish = defineCommand({
meta: {
name: "updates publish",
version,
description: withDocsLink("Bundle and publish an over-the-air update", "updates-publish")
},
args: {
platform: {
type: "string",
description: "ios or android"
},
out: {
type: "string",
description: "Directory to write manifest.json and assets into"
},
metadata: {
type: "string",
description: "Manifest metadata as key=value (repeatable)"
},
intermediatesOut: {
type: "string",
description: "Separate directory for local debug bundle and composed source map; never upload it"
},
runtimeVersion: {
type: "string",
description: "Runtime version for an app built with expo prebuild, which has no native.app"
}
},
async run({ args }) {
const { runUpdatesPublish } = await import("./cli/updatesPublish.mjs");
await runUpdatesPublish(args);
}
});
const updates = defineCommand({
meta: {
name: "updates",
version,
description: "Over-the-air updates"
},
subCommands: { publish: updatesPublish }
});
const subCommands = {
dev,
clean,
build: buildCommand,
prebuild,
"run:ios": runIos,
"run:android": runAndroid,
patch,
serve: serveCommand,
"generate-routes": generateRoutes,
typegen,
daemon: daemonCommand,
"metro-eject": metroEject,
updates
};
const subMain = defineCommand({
meta: {
name: "one",
version,
description: "Welcome to One"
},
subCommands
});
const main = defineCommand({
meta: {
name: "one",
version,
description: "Welcome to One"
},
args: { name: {
type: "positional",
description: "Folder name to place the app into",
required: false
} },
async run({ args }) {
if (subCommands[args.name]) {
runMain(subMain);
return;
}
const { cliMain } = await import("./cli/main.mjs");
await cliMain(args);
}
});
const helpIndex = process.argv.indexOf("--help");
if (helpIndex > 0) {
const subCommandName = process.argv[helpIndex - 1];
const subCommand = subCommands[subCommandName];
if (subCommand) {
showUsage(subCommand);
} else {
showUsage(subMain);
}
} else {
runMain(main);
}
//# sourceMappingURL=cli.mjs.map