Skip to content

Commit 4d73181

Browse files
committed
feat(cli): allow passing custom path to app file (#842)
1 parent 210beae commit 4d73181

File tree

5 files changed

+93
-64
lines changed

5 files changed

+93
-64
lines changed

packages/actor-core-cli/src/commands/deploy.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,24 @@ export const deploy = new Command()
2424
.addArgument(
2525
new Argument("<platform>", "The platform to deploy to").choices(["rivet"]),
2626
)
27-
.addArgument(new Argument("[path]", "Location of the project").default("./"))
27+
.addOption(new Option("-r, --root [path]", "Location of the project").default("./"))
28+
.addOption(new Option("-p, --path [path]", "Location of the app.ts file"))
2829
.addOption(new Option("--skip-manager", "Skip deploying ActorCore manager"))
2930
.addOption(new Option("--env <env>", "Specify environment to deploy to"))
3031
.addOption(new Option("-v [version]", "Specify version of actor-core"))
3132
.addHelpText(
3233
"afterAll",
3334
"\nMissing your favorite platform?\nLet us know! https://github.com/rivet-gg/actor-core/issues/new",
3435
)
35-
.action(async (platform, wd, opts) => {
36-
const cwd = path.join(process.cwd(), wd);
36+
.action(async (platform, opts: {
37+
root: string;
38+
path?: string;
39+
port?: string;
40+
skipManager: boolean,
41+
env?: string,
42+
version?: string,
43+
}) => {
44+
const cwd = path.join(process.cwd(), opts.root);
3745

3846
const exec = $({
3947
cwd,
@@ -46,7 +54,7 @@ export const deploy = new Command()
4654
const { config, cli } = yield* ctx.task(
4755
"Prepare",
4856
async function* (ctx) {
49-
const config = yield* validateConfigTask(ctx, cwd);
57+
const config = yield* validateConfigTask(ctx, cwd, opts.path);
5058

5159
const cli = yield* ctx.task(
5260
"Locale rivet-cli",

packages/actor-core-cli/src/commands/dev.tsx

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@ import { spawn } from "node:child_process";
1212
export const dev = new Command()
1313
.name("dev")
1414
.description("Run locally your ActorCore project.")
15-
.addArgument(new Argument("[path]", "Location of the project"))
1615
.addOption(
17-
new Option("-p, --port [port]", "Specify which platform to use").default(
16+
new Option("-r, --root [path]", "Location of the project").default("./"),
17+
)
18+
.addOption(new Option("-p, --path [path]", "Location of the app.ts file"))
19+
.addOption(
20+
new Option("--port [port]", "Specify which platform to use").default(
1821
"6420",
1922
),
2023
)
@@ -26,15 +29,15 @@ export const dev = new Command()
2629
.option("--no-open", "Do not open the browser with ActorCore Studio")
2730
.action(action);
2831

29-
export async function action(
30-
cmdPath = ".",
31-
opts: {
32-
port?: string;
33-
open?: boolean;
34-
} = {},
35-
) {
36-
const cwd = path.join(process.cwd(), cmdPath);
37-
await workflow("Run locally your ActorCore project", async function* (ctx) {
32+
export async function action(opts: {
33+
root: string;
34+
path?: string;
35+
port?: string;
36+
open: boolean;
37+
}) {
38+
const cwd = path.join(process.cwd(), opts.root);
39+
40+
await workflow("Run locally your ActorCore project", async function*(ctx) {
3841
if (opts.open) {
3942
open(
4043
process.env._ACTOR_CORE_CLI_DEV
@@ -58,7 +61,7 @@ export async function action(
5861
"server-entry.js",
5962
),
6063
],
61-
{ env: { ...process.env, PORT: opts.port }, cwd },
64+
{ env: { ...process.env, PORT: opts.port, PATH: opts.path }, cwd },
6265
);
6366
}
6467

@@ -79,7 +82,7 @@ export async function action(
7982
});
8083

8184
while (true) {
82-
yield* validateConfigTask(ctx, cwd);
85+
yield* validateConfigTask(ctx, cwd, opts.path);
8386
server = createServer();
8487
createLock();
8588

packages/actor-core-cli/src/server-entry.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { validateConfig } from "./utils/config";
22
import { serve } from "@actor-core/nodejs";
33

44
async function run() {
5-
const config = await validateConfig(process.cwd());
5+
const config = await validateConfig(process.cwd(), process.env.PATH);
66
config.app.config.inspector = {
77
enabled: true,
88
};

packages/actor-core-cli/src/utils/config.ts

Lines changed: 51 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -13,49 +13,55 @@ const ActorCoreConfig = z.object({
1313

1414
export async function loadConfig(
1515
cwd: string,
16+
appPath?: string,
1617
): Promise<{ path: string; data: z.infer<typeof ActorCoreConfig> } | null> {
1718
const configJoycon = new JoyCon();
1819

19-
const configPath = await configJoycon.resolve({
20-
files: [
21-
"src/app.ts",
22-
"src/app.tsx",
23-
"src/app.mts",
24-
"src/app.js",
25-
"src/app.cjs",
26-
"src/app.mjs",
27-
],
28-
cwd,
29-
stopDir: path.parse(cwd).root,
30-
});
31-
32-
if (configPath) {
33-
try {
34-
const config = await bundleRequire({
35-
filepath: configPath,
36-
});
37-
return {
38-
path: configPath,
39-
data: config.mod.default || config.mod,
40-
};
41-
} catch (error) {
42-
throw { isBundleError: true, details: error };
43-
}
20+
// Attempt to auto-resolve app path
21+
let resolvedAppPath: string;
22+
if (appPath) {
23+
resolvedAppPath = appPath;
24+
} else {
25+
// Auto-resolve app path
26+
const resolved = await configJoycon.resolve({
27+
files: [
28+
"src/app.ts",
29+
"src/app.tsx",
30+
"src/app.mts",
31+
"src/app.js",
32+
"src/app.cjs",
33+
"src/app.mjs",
34+
],
35+
cwd,
36+
stopDir: path.parse(cwd).root,
37+
});
38+
if (!resolved) return null;
39+
resolvedAppPath = resolved;
4440
}
4541

46-
return null;
42+
try {
43+
const config = await bundleRequire({
44+
filepath: resolvedAppPath,
45+
});
46+
return {
47+
path: resolvedAppPath,
48+
data: config.mod.default || config.mod,
49+
};
50+
} catch (error) {
51+
throw { isBundleError: true, path: resolvedAppPath, details: error };
52+
}
4753
}
4854

49-
export async function requireConfig(cwd: string) {
50-
const config = await loadConfig(cwd);
55+
export async function requireConfig(cwd: string, appPath?: string) {
56+
const config = await loadConfig(cwd, appPath);
5157
if (!config || !config.data) {
52-
throw { isNotFoundError: true };
58+
throw { isNotFoundError: true, cwd, appPath };
5359
}
5460
return config;
5561
}
5662

57-
export async function validateConfig(cwd: string) {
58-
const config = await requireConfig(cwd);
63+
export async function validateConfig(cwd: string, appPath?: string) {
64+
const config = await requireConfig(cwd, appPath);
5965
return await ActorCoreConfig.parseAsync({
6066
...config.data,
6167
cwd: path.dirname(config.path),
@@ -64,15 +70,24 @@ export async function validateConfig(cwd: string) {
6470

6571
export const isNotFoundError = (
6672
error: unknown,
67-
): error is { isNotFoundError: true } => {
68-
return z.object({ isNotFoundError: z.literal(true) }).safeParse(error)
69-
.success;
73+
): error is { isNotFoundError: true; cwd: string; path?: string } => {
74+
return z
75+
.object({
76+
isNotFoundError: z.literal(true),
77+
cwd: z.string(),
78+
appPath: z.string().optional(),
79+
})
80+
.safeParse(error).success;
7081
};
7182

7283
export const isBundleError = (
7384
error: unknown,
74-
): error is { isBundleError: true; details: unknown } => {
85+
): error is { isBundleError: true; path: string; details: unknown } => {
7586
return z
76-
.object({ isBundleError: z.literal(true), details: z.any() })
87+
.object({
88+
isBundleError: z.literal(true),
89+
path: z.string(),
90+
details: z.any(),
91+
})
7792
.safeParse(error).success;
7893
};

packages/actor-core-cli/src/workflows/validate-config.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,29 @@ import {
66
import path from "node:path";
77
import type { Context } from "../workflow";
88

9-
export function validateConfigTask(ctx: Context, cwd: string) {
9+
export function validateConfigTask(
10+
ctx: Context,
11+
cwd: string,
12+
appPath?: string,
13+
) {
1014
return ctx.task("Build project", async () => {
1115
try {
12-
return await validateConfig(cwd);
16+
return await validateConfig(cwd, appPath);
1317
} catch (error) {
14-
const indexFile = path.relative(
15-
process.cwd(),
16-
path.join(cwd, "src", "index.ts"),
17-
);
1818
if (isBundleError(error)) {
1919
throw ctx.error(
20-
`Could not parse Actors index file (${indexFile})\n${error.details}`,
20+
`Could not parse Actors index file (${error.path})\n${error.details}`,
2121
{
2222
hint: "Please make sure that the file exists and does not have any syntax errors.",
2323
},
2424
);
2525
} else if (isNotFoundError(error)) {
26-
throw ctx.error(`Could not find Actors index file (${indexFile})`, {
27-
hint: "Please make sure that the file exists and not empty.",
28-
});
26+
throw ctx.error(
27+
`Could not find Actors index file (${error.path ? error.path : path.join(error.cwd, "src/app.{ts,tsx,mts,js,cjs,mjs}")})`,
28+
{
29+
hint: "Please make sure that the file exists and not empty.",
30+
},
31+
);
2932
} else {
3033
console.error(error);
3134
throw ctx.error("Failed to build project config.", {

0 commit comments

Comments
 (0)