diff --git a/packages/actor-core-cli/src/commands/deploy.tsx b/packages/actor-core-cli/src/commands/deploy.tsx index bff233981..877daad60 100644 --- a/packages/actor-core-cli/src/commands/deploy.tsx +++ b/packages/actor-core-cli/src/commands/deploy.tsx @@ -24,7 +24,8 @@ export const deploy = new Command() .addArgument( new Argument("", "The platform to deploy to").choices(["rivet"]), ) - .addArgument(new Argument("[path]", "Location of the project").default("./")) + .addOption(new Option("-r, --root [path]", "Location of the project").default("./")) + .addOption(new Option("-p, --path [path]", "Location of the app.ts file")) .addOption(new Option("--skip-manager", "Skip deploying ActorCore manager")) .addOption(new Option("--env ", "Specify environment to deploy to")) .addOption(new Option("-v [version]", "Specify version of actor-core")) @@ -32,8 +33,15 @@ export const deploy = new Command() "afterAll", "\nMissing your favorite platform?\nLet us know! https://github.com/rivet-gg/actor-core/issues/new", ) - .action(async (platform, wd, opts) => { - const cwd = path.join(process.cwd(), wd); + .action(async (platform, opts: { + root: string; + path?: string; + port?: string; + skipManager: boolean, + env?: string, + version?: string, + }) => { + const cwd = path.join(process.cwd(), opts.root); const exec = $({ cwd, @@ -46,7 +54,7 @@ export const deploy = new Command() const { config, cli } = yield* ctx.task( "Prepare", async function* (ctx) { - const config = yield* validateConfigTask(ctx, cwd); + const config = yield* validateConfigTask(ctx, cwd, opts.path); const cli = yield* ctx.task( "Locale rivet-cli", diff --git a/packages/actor-core-cli/src/commands/dev.tsx b/packages/actor-core-cli/src/commands/dev.tsx index c43252214..49ed05ea3 100644 --- a/packages/actor-core-cli/src/commands/dev.tsx +++ b/packages/actor-core-cli/src/commands/dev.tsx @@ -12,9 +12,12 @@ import { spawn } from "node:child_process"; export const dev = new Command() .name("dev") .description("Run locally your ActorCore project.") - .addArgument(new Argument("[path]", "Location of the project")) .addOption( - new Option("-p, --port [port]", "Specify which platform to use").default( + new Option("-r, --root [path]", "Location of the project").default("./"), + ) + .addOption(new Option("-p, --path [path]", "Location of the app.ts file")) + .addOption( + new Option("--port [port]", "Specify which platform to use").default( "6420", ), ) @@ -26,15 +29,15 @@ export const dev = new Command() .option("--no-open", "Do not open the browser with ActorCore Studio") .action(action); -export async function action( - cmdPath = ".", - opts: { - port?: string; - open?: boolean; - } = {}, -) { - const cwd = path.join(process.cwd(), cmdPath); - await workflow("Run locally your ActorCore project", async function* (ctx) { +export async function action(opts: { + root: string; + path?: string; + port?: string; + open: boolean; +}) { + const cwd = path.join(process.cwd(), opts.root); + + await workflow("Run locally your ActorCore project", async function*(ctx) { if (opts.open) { open( process.env._ACTOR_CORE_CLI_DEV @@ -58,7 +61,7 @@ export async function action( "server-entry.js", ), ], - { env: { ...process.env, PORT: opts.port }, cwd }, + { env: { ...process.env, PORT: opts.port, PATH: opts.path }, cwd }, ); } @@ -79,7 +82,7 @@ export async function action( }); while (true) { - yield* validateConfigTask(ctx, cwd); + yield* validateConfigTask(ctx, cwd, opts.path); server = createServer(); createLock(); diff --git a/packages/actor-core-cli/src/server-entry.ts b/packages/actor-core-cli/src/server-entry.ts index 84a497639..8af3361f0 100644 --- a/packages/actor-core-cli/src/server-entry.ts +++ b/packages/actor-core-cli/src/server-entry.ts @@ -2,7 +2,7 @@ import { validateConfig } from "./utils/config"; import { serve } from "@actor-core/nodejs"; async function run() { - const config = await validateConfig(process.cwd()); + const config = await validateConfig(process.cwd(), process.env.PATH); config.app.config.inspector = { enabled: true, }; diff --git a/packages/actor-core-cli/src/utils/config.ts b/packages/actor-core-cli/src/utils/config.ts index 6d92803b0..6aae873f4 100644 --- a/packages/actor-core-cli/src/utils/config.ts +++ b/packages/actor-core-cli/src/utils/config.ts @@ -13,49 +13,55 @@ const ActorCoreConfig = z.object({ export async function loadConfig( cwd: string, + appPath?: string, ): Promise<{ path: string; data: z.infer } | null> { const configJoycon = new JoyCon(); - const configPath = await configJoycon.resolve({ - files: [ - "src/app.ts", - "src/app.tsx", - "src/app.mts", - "src/app.js", - "src/app.cjs", - "src/app.mjs", - ], - cwd, - stopDir: path.parse(cwd).root, - }); - - if (configPath) { - try { - const config = await bundleRequire({ - filepath: configPath, - }); - return { - path: configPath, - data: config.mod.default || config.mod, - }; - } catch (error) { - throw { isBundleError: true, details: error }; - } + // Attempt to auto-resolve app path + let resolvedAppPath: string; + if (appPath) { + resolvedAppPath = appPath; + } else { + // Auto-resolve app path + const resolved = await configJoycon.resolve({ + files: [ + "src/app.ts", + "src/app.tsx", + "src/app.mts", + "src/app.js", + "src/app.cjs", + "src/app.mjs", + ], + cwd, + stopDir: path.parse(cwd).root, + }); + if (!resolved) return null; + resolvedAppPath = resolved; } - return null; + try { + const config = await bundleRequire({ + filepath: resolvedAppPath, + }); + return { + path: resolvedAppPath, + data: config.mod.default || config.mod, + }; + } catch (error) { + throw { isBundleError: true, path: resolvedAppPath, details: error }; + } } -export async function requireConfig(cwd: string) { - const config = await loadConfig(cwd); +export async function requireConfig(cwd: string, appPath?: string) { + const config = await loadConfig(cwd, appPath); if (!config || !config.data) { - throw { isNotFoundError: true }; + throw { isNotFoundError: true, cwd, appPath }; } return config; } -export async function validateConfig(cwd: string) { - const config = await requireConfig(cwd); +export async function validateConfig(cwd: string, appPath?: string) { + const config = await requireConfig(cwd, appPath); return await ActorCoreConfig.parseAsync({ ...config.data, cwd: path.dirname(config.path), @@ -64,15 +70,24 @@ export async function validateConfig(cwd: string) { export const isNotFoundError = ( error: unknown, -): error is { isNotFoundError: true } => { - return z.object({ isNotFoundError: z.literal(true) }).safeParse(error) - .success; +): error is { isNotFoundError: true; cwd: string; path?: string } => { + return z + .object({ + isNotFoundError: z.literal(true), + cwd: z.string(), + appPath: z.string().optional(), + }) + .safeParse(error).success; }; export const isBundleError = ( error: unknown, -): error is { isBundleError: true; details: unknown } => { +): error is { isBundleError: true; path: string; details: unknown } => { return z - .object({ isBundleError: z.literal(true), details: z.any() }) + .object({ + isBundleError: z.literal(true), + path: z.string(), + details: z.any(), + }) .safeParse(error).success; }; diff --git a/packages/actor-core-cli/src/workflows/validate-config.ts b/packages/actor-core-cli/src/workflows/validate-config.ts index 9d88fe7b0..88c5ab64f 100644 --- a/packages/actor-core-cli/src/workflows/validate-config.ts +++ b/packages/actor-core-cli/src/workflows/validate-config.ts @@ -6,26 +6,29 @@ import { import path from "node:path"; import type { Context } from "../workflow"; -export function validateConfigTask(ctx: Context, cwd: string) { +export function validateConfigTask( + ctx: Context, + cwd: string, + appPath?: string, +) { return ctx.task("Build project", async () => { try { - return await validateConfig(cwd); + return await validateConfig(cwd, appPath); } catch (error) { - const indexFile = path.relative( - process.cwd(), - path.join(cwd, "src", "index.ts"), - ); if (isBundleError(error)) { throw ctx.error( - `Could not parse Actors index file (${indexFile})\n${error.details}`, + `Could not parse Actors index file (${error.path})\n${error.details}`, { hint: "Please make sure that the file exists and does not have any syntax errors.", }, ); } else if (isNotFoundError(error)) { - throw ctx.error(`Could not find Actors index file (${indexFile})`, { - hint: "Please make sure that the file exists and not empty.", - }); + throw ctx.error( + `Could not find Actors index file (${error.path ? error.path : path.join(error.cwd, "src/app.{ts,tsx,mts,js,cjs,mjs}")})`, + { + hint: "Please make sure that the file exists and not empty.", + }, + ); } else { console.error(error); throw ctx.error("Failed to build project config.", {