Skip to content

Commit 37c8e7c

Browse files
feat(cli): add react router support with alchemy (#542)
1 parent 5fe7a73 commit 37c8e7c

20 files changed

+163
-276
lines changed

apps/cli/src/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ export const dependencyVersionMap = {
138138
"@sveltejs/adapter-cloudflare": "^7.2.1",
139139
"@cloudflare/workers-types": "^4.20250822.0",
140140

141-
alchemy: "^0.62.1",
141+
alchemy: "^0.63.0",
142142
// temporary workaround for alchemy + tanstack start
143143
nitropack: "^2.12.4",
144144

apps/cli/src/helpers/core/create-project.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,12 @@ export async function createProject(options: ProjectConfig) {
7676

7777
await handleExtras(projectDir, options);
7878

79+
await setupEnvironmentVariables(options);
80+
await updatePackageConfigurations(projectDir, options);
81+
7982
await setupWebDeploy(options);
8083
await setupServerDeploy(options);
8184

82-
await setupEnvironmentVariables(options);
83-
await updatePackageConfigurations(projectDir, options);
8485
await createReadme(projectDir, options);
8586

8687
await writeBtsConfig(options);

apps/cli/src/helpers/core/create-readme.ts

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ function generateReadmeContent(options: ProjectConfig): string {
3535
frontend = ["tanstack-router"],
3636
backend = "hono",
3737
api = "trpc",
38+
webDeploy,
39+
serverDeploy,
3840
} = options;
3941

4042
const isConvex = backend === "convex";
@@ -103,6 +105,7 @@ Follow the prompts to create a new Convex project and connect it to your applica
103105
packageManagerRunCmd,
104106
orm,
105107
options.dbSetup,
108+
options.serverDeploy,
106109
)
107110
}
108111
@@ -120,6 +123,8 @@ ${
120123
: ""
121124
}
122125
126+
${generateDeploymentCommands(packageManagerRunCmd, webDeploy, serverDeploy)}
127+
123128
## Project Structure
124129
125130
\`\`\`
@@ -475,6 +480,7 @@ function generateDatabaseSetup(
475480
packageManagerRunCmd: string,
476481
orm: ORM,
477482
dbSetup: DatabaseSetup,
483+
serverDeploy?: string,
478484
): string {
479485
if (database === "none") {
480486
return "";
@@ -494,7 +500,9 @@ function generateDatabaseSetup(
494500
1. Start the local SQLite database:
495501
${
496502
dbSetup === "d1"
497-
? "Local development for a Cloudflare D1 database will already be running as part of the `wrangler dev` command."
503+
? serverDeploy === "alchemy"
504+
? "D1 local development and migrations are handled automatically by Alchemy during dev and deploy."
505+
: "Local development for a Cloudflare D1 database will already be running as part of the `wrangler dev` command."
498506
: `\`\`\`bash
499507
cd apps/server && ${packageManagerRunCmd} db:local
500508
\`\`\`
@@ -632,3 +640,51 @@ function generateScriptsList(
632640

633641
return scripts;
634642
}
643+
644+
function generateDeploymentCommands(
645+
packageManagerRunCmd: string,
646+
webDeploy?: string,
647+
serverDeploy?: string,
648+
): string {
649+
const lines: string[] = [];
650+
651+
if (webDeploy === "alchemy" || serverDeploy === "alchemy") {
652+
lines.push("## Deployment (Alchemy)");
653+
if (webDeploy === "alchemy" && serverDeploy !== "alchemy") {
654+
lines.push(
655+
`- Web dev: cd apps/web && ${packageManagerRunCmd} dev`,
656+
`- Web deploy: cd apps/web && ${packageManagerRunCmd} deploy`,
657+
`- Web destroy: cd apps/web && ${packageManagerRunCmd} destroy`,
658+
);
659+
}
660+
if (serverDeploy === "alchemy" && webDeploy !== "alchemy") {
661+
lines.push(
662+
`- Server dev: cd apps/server && ${packageManagerRunCmd} dev`,
663+
`- Server deploy: cd apps/server && ${packageManagerRunCmd} deploy`,
664+
`- Server destroy: cd apps/server && ${packageManagerRunCmd} destroy`,
665+
);
666+
}
667+
if (webDeploy === "alchemy" && serverDeploy === "alchemy") {
668+
lines.push(
669+
`- Dev: ${packageManagerRunCmd} dev`,
670+
`- Deploy: ${packageManagerRunCmd} deploy`,
671+
`- Destroy: ${packageManagerRunCmd} destroy`,
672+
);
673+
}
674+
}
675+
676+
if (webDeploy === "wrangler" || serverDeploy === "wrangler") {
677+
lines.push("\n## Deployment (Cloudflare Wrangler)");
678+
if (webDeploy === "wrangler") {
679+
lines.push(`- Web deploy: cd apps/web && ${packageManagerRunCmd} deploy`);
680+
}
681+
if (serverDeploy === "wrangler") {
682+
lines.push(
683+
`- Server dev: cd apps/server && ${packageManagerRunCmd} dev`,
684+
`- Server deploy: cd apps/server && ${packageManagerRunCmd} deploy`,
685+
);
686+
}
687+
}
688+
689+
return lines.length ? `\n${lines.join("\n")}\n` : "";
690+
}

apps/cli/src/helpers/core/post-installation.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -412,15 +412,15 @@ function getAlchemyDeployInstructions(
412412

413413
if (webDeploy === "alchemy" && serverDeploy !== "alchemy") {
414414
instructions.push(
415-
`${pc.bold("Deploy web with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/web && ${runCmd} alchemy:dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/web && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/web && ${runCmd} destroy`}`,
415+
`${pc.bold("Deploy web with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/web && ${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/web && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/web && ${runCmd} destroy`}`,
416416
);
417417
} else if (serverDeploy === "alchemy" && webDeploy !== "alchemy") {
418418
instructions.push(
419-
`${pc.bold("Deploy server with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/server && ${runCmd} alchemy:dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/server && ${runCmd} destroy`}`,
419+
`${pc.bold("Deploy server with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/server && ${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/server && ${runCmd} destroy`}`,
420420
);
421421
} else if (webDeploy === "alchemy" && serverDeploy === "alchemy") {
422422
instructions.push(
423-
`${pc.bold("Deploy with Alchemy:")}\n${pc.cyan("•")} Dev: ${`${runCmd} alchemy:dev`}\n${pc.cyan("•")} Deploy: ${`${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`${runCmd} destroy`}`,
423+
`${pc.bold("Deploy with Alchemy:")}\n${pc.cyan("•")} Dev: ${`${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`${runCmd} destroy`}`,
424424
);
425425
}
426426

apps/cli/src/helpers/core/template-manager.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -835,12 +835,6 @@ export async function setupDeploymentTemplates(
835835
serverAppDir,
836836
context,
837837
);
838-
await processAndCopyFiles(
839-
"wrangler.jsonc.hbs",
840-
alchemyTemplateSrc,
841-
serverAppDir,
842-
context,
843-
);
844838
}
845839
}
846840
} else {
@@ -885,12 +879,6 @@ export async function setupDeploymentTemplates(
885879
serverAppDir,
886880
context,
887881
);
888-
await processAndCopyFiles(
889-
"wrangler.jsonc.hbs",
890-
alchemyTemplateSrc,
891-
serverAppDir,
892-
context,
893-
);
894882
}
895883
}
896884
}

apps/cli/src/helpers/deployment/alchemy/alchemy-combined-setup.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export async function setupCombinedAlchemyDeploy(
2929
...pkg.scripts,
3030
deploy: "alchemy deploy",
3131
destroy: "alchemy destroy",
32-
"alchemy:dev": "alchemy dev",
32+
dev: "alchemy dev",
3333
};
3434
await fs.writeJson(rootPkgPath, pkg, { spaces: 2 });
3535
}
@@ -49,18 +49,32 @@ export async function setupCombinedAlchemyDeploy(
4949
const isSolid = frontend.includes("solid");
5050

5151
if (isNext) {
52-
await setupNextAlchemyDeploy(projectDir, packageManager);
52+
await setupNextAlchemyDeploy(projectDir, packageManager, {
53+
skipAppScripts: true,
54+
});
5355
} else if (isNuxt) {
54-
await setupNuxtAlchemyDeploy(projectDir, packageManager);
56+
await setupNuxtAlchemyDeploy(projectDir, packageManager, {
57+
skipAppScripts: true,
58+
});
5559
} else if (isSvelte) {
56-
await setupSvelteAlchemyDeploy(projectDir, packageManager);
60+
await setupSvelteAlchemyDeploy(projectDir, packageManager, {
61+
skipAppScripts: true,
62+
});
5763
} else if (isTanstackStart) {
58-
await setupTanStackStartAlchemyDeploy(projectDir, packageManager);
64+
await setupTanStackStartAlchemyDeploy(projectDir, packageManager, {
65+
skipAppScripts: true,
66+
});
5967
} else if (isTanstackRouter) {
60-
await setupTanStackRouterAlchemyDeploy(projectDir, packageManager);
68+
await setupTanStackRouterAlchemyDeploy(projectDir, packageManager, {
69+
skipAppScripts: true,
70+
});
6171
} else if (isReactRouter) {
62-
await setupReactRouterAlchemyDeploy(projectDir, packageManager);
72+
await setupReactRouterAlchemyDeploy(projectDir, packageManager, {
73+
skipAppScripts: true,
74+
});
6375
} else if (isSolid) {
64-
await setupSolidAlchemyDeploy(projectDir, packageManager);
76+
await setupSolidAlchemyDeploy(projectDir, packageManager, {
77+
skipAppScripts: true,
78+
});
6579
}
6680
}

apps/cli/src/helpers/deployment/alchemy/alchemy-next-setup.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { addPackageDependency } from "../../../utils/add-package-deps";
66
export async function setupNextAlchemyDeploy(
77
projectDir: string,
88
_packageManager: PackageManager,
9+
options?: { skipAppScripts?: boolean },
910
) {
1011
const webAppDir = path.join(projectDir, "apps/web");
1112
if (!(await fs.pathExists(webAppDir))) return;
@@ -19,12 +20,14 @@ export async function setupNextAlchemyDeploy(
1920
if (await fs.pathExists(pkgPath)) {
2021
const pkg = await fs.readJson(pkgPath);
2122

22-
pkg.scripts = {
23-
...pkg.scripts,
24-
deploy: "alchemy deploy",
25-
destroy: "alchemy destroy",
26-
"alchemy:dev": "alchemy dev",
27-
};
23+
if (!options?.skipAppScripts) {
24+
pkg.scripts = {
25+
...pkg.scripts,
26+
deploy: "alchemy deploy",
27+
destroy: "alchemy destroy",
28+
dev: "alchemy dev",
29+
};
30+
}
2831
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
2932
}
3033
}

apps/cli/src/helpers/deployment/alchemy/alchemy-nuxt-setup.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { addPackageDependency } from "../../../utils/add-package-deps";
77
export async function setupNuxtAlchemyDeploy(
88
projectDir: string,
99
_packageManager: PackageManager,
10+
options?: { skipAppScripts?: boolean },
1011
) {
1112
const webAppDir = path.join(projectDir, "apps/web");
1213
if (!(await fs.pathExists(webAppDir))) return;
@@ -20,12 +21,14 @@ export async function setupNuxtAlchemyDeploy(
2021
if (await fs.pathExists(pkgPath)) {
2122
const pkg = await fs.readJson(pkgPath);
2223

23-
pkg.scripts = {
24-
...pkg.scripts,
25-
deploy: "alchemy deploy",
26-
destroy: "alchemy destroy",
27-
"alchemy:dev": "alchemy dev",
28-
};
24+
if (!options?.skipAppScripts) {
25+
pkg.scripts = {
26+
...pkg.scripts,
27+
deploy: "alchemy deploy",
28+
destroy: "alchemy destroy",
29+
dev: "alchemy dev",
30+
};
31+
}
2932
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3033
}
3134

0 commit comments

Comments
 (0)