Skip to content

Commit eff6ab9

Browse files
fix(cli): dynamically inject alchemy env.d.ts imports using ts-morph and remove db:generate Exec (#699)
1 parent 9a958d3 commit eff6ab9

File tree

5 files changed

+102
-28
lines changed

5 files changed

+102
-28
lines changed

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -266,13 +266,11 @@ async function getDatabaseInstructions(
266266
if (dbSetup === "d1" && serverDeploy === "alchemy") {
267267
if (orm === "drizzle") {
268268
instructions.push(
269-
`${pc.yellow(
270-
"NOTE:",
271-
)} D1 migrations are automatically handled by Alchemy`,
269+
`${pc.cyan("•")} Generate migrations: ${`${runCmd} db:generate`}`,
272270
);
273271
} else if (orm === "prisma") {
274272
instructions.push(
275-
`${pc.cyan("•")} Generate migrations: ${`${runCmd} db:generate`}`,
273+
`${pc.cyan("•")} Generate Prisma client: ${`${runCmd} db:generate`}`,
276274
);
277275
instructions.push(
278276
`${pc.cyan("•")} Apply migrations: ${`${runCmd} db:migrate`}`,

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

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { glob } from "tinyglobby";
44
import { PKG_ROOT } from "../../constants";
55
import type { ProjectConfig } from "../../types";
66
import { processTemplate } from "../../utils/template-processor";
7+
import { setupEnvDtsImport } from "../deployment/alchemy/env-dts-setup";
78

89
export async function processAndCopyFiles(
910
sourcePattern: string | string[],
@@ -1203,12 +1204,13 @@ export async function setupDeploymentTemplates(
12031204
serverAppDir,
12041205
context,
12051206
);
1206-
await processAndCopyFiles(
1207-
"env.d.ts.hbs",
1208-
alchemyTemplateSrc,
1209-
serverAppDir,
1207+
const envDtsPath = path.join(serverAppDir, "env.d.ts");
1208+
await processTemplate(
1209+
path.join(alchemyTemplateSrc, "env.d.ts.hbs"),
1210+
envDtsPath,
12101211
context,
12111212
);
1213+
await setupEnvDtsImport(envDtsPath, projectDir, context);
12121214

12131215
await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
12141216
}
@@ -1283,22 +1285,24 @@ async function addEnvDtsToPackages(
12831285
for (const packageName of packages) {
12841286
const packageDir = path.join(projectDir, packageName);
12851287
if (await fs.pathExists(packageDir)) {
1286-
await processAndCopyFiles(
1287-
"env.d.ts.hbs",
1288-
alchemyTemplateSrc,
1289-
packageDir,
1288+
const envDtsPath = path.join(packageDir, "env.d.ts");
1289+
await processTemplate(
1290+
path.join(alchemyTemplateSrc, "env.d.ts.hbs"),
1291+
envDtsPath,
12901292
context,
12911293
);
1294+
await setupEnvDtsImport(envDtsPath, projectDir, context);
12921295
}
12931296
}
12941297

12951298
const serverAppDir = path.join(projectDir, "apps/server");
12961299
if (await fs.pathExists(serverAppDir)) {
1297-
await processAndCopyFiles(
1298-
"env.d.ts.hbs",
1299-
alchemyTemplateSrc,
1300-
serverAppDir,
1300+
const envDtsPath = path.join(serverAppDir, "env.d.ts");
1301+
await processTemplate(
1302+
path.join(alchemyTemplateSrc, "env.d.ts.hbs"),
1303+
envDtsPath,
13011304
context,
13021305
);
1306+
await setupEnvDtsImport(envDtsPath, projectDir, context);
13031307
}
13041308
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import path from "node:path";
2+
import fs from "fs-extra";
3+
import { Project } from "ts-morph";
4+
import type { ProjectConfig } from "../../../types";
5+
6+
const tsProject = new Project({
7+
useInMemoryFileSystem: false,
8+
skipAddingFilesFromTsConfig: true,
9+
});
10+
11+
function determineImportPath(
12+
envDtsPath: string,
13+
projectDir: string,
14+
config: ProjectConfig,
15+
): string {
16+
const { webDeploy, serverDeploy, backend } = config;
17+
const isBackendSelf = backend === "self";
18+
19+
let alchemyRunPath: string;
20+
21+
if (
22+
webDeploy === "alchemy" &&
23+
(serverDeploy === "alchemy" || isBackendSelf)
24+
) {
25+
// Both web and server are alchemy, or web + backend=self
26+
if (isBackendSelf) {
27+
alchemyRunPath = path.join(projectDir, "apps/web/alchemy.run.ts");
28+
} else {
29+
alchemyRunPath = path.join(projectDir, "alchemy.run.ts");
30+
}
31+
} else if (webDeploy === "alchemy") {
32+
// Only web is alchemy
33+
alchemyRunPath = path.join(projectDir, "apps/web/alchemy.run.ts");
34+
} else if (serverDeploy === "alchemy") {
35+
// Only server is alchemy
36+
alchemyRunPath = path.join(projectDir, "apps/server/alchemy.run.ts");
37+
} else {
38+
// Should not happen, but fallback
39+
alchemyRunPath = path.join(projectDir, "alchemy.run.ts");
40+
}
41+
42+
// Calculate relative path from env.d.ts to alchemy.run.ts
43+
const relativePath = path.relative(
44+
path.dirname(envDtsPath),
45+
alchemyRunPath.replace(/\.ts$/, ""),
46+
);
47+
48+
// Normalize the path for imports (use forward slashes, handle relative paths)
49+
const importPath = relativePath.startsWith(".")
50+
? relativePath
51+
: `./${relativePath}`;
52+
53+
return importPath.replace(/\\/g, "/");
54+
}
55+
56+
export async function setupEnvDtsImport(
57+
envDtsPath: string,
58+
projectDir: string,
59+
config: ProjectConfig,
60+
) {
61+
if (!(await fs.pathExists(envDtsPath))) {
62+
return;
63+
}
64+
65+
const importPath = determineImportPath(envDtsPath, projectDir, config);
66+
67+
const sourceFile = tsProject.addSourceFileAtPath(envDtsPath);
68+
69+
const existingImports = sourceFile.getImportDeclarations();
70+
const alreadyHasImport = existingImports.some(
71+
(imp) =>
72+
imp.getModuleSpecifierValue() === importPath &&
73+
imp.getNamedImports().some((named) => named.getName() === "server"),
74+
);
75+
76+
if (!alreadyHasImport) {
77+
sourceFile.insertImportDeclaration(0, {
78+
moduleSpecifier: importPath,
79+
namedImports: [{ name: "server", isTypeOnly: true }],
80+
});
81+
}
82+
83+
await sourceFile.save();
84+
}

apps/cli/templates/deploy/alchemy/alchemy.run.ts.hbs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import { Worker } from "alchemy/cloudflare";
2121
{{/if}}
2222
{{#if (and (or (eq serverDeploy "alchemy") (and (eq webDeploy "alchemy") (eq backend "self"))) (eq dbSetup "d1"))}}
2323
import { D1Database } from "alchemy/cloudflare";
24-
import { Exec } from "alchemy/os";
2524
{{/if}}
2625
import { config } from "dotenv";
2726

@@ -36,11 +35,6 @@ config({ path: "./.env" });
3635
const app = await alchemy("{{projectName}}");
3736

3837
{{#if (and (or (eq serverDeploy "alchemy") (and (eq webDeploy "alchemy") (eq backend "self"))) (eq dbSetup "d1"))}}
39-
await Exec("db-generate", {
40-
{{#if (and (eq webDeploy "alchemy") (eq serverDeploy "alchemy"))}}cwd: "packages/db",{{/if}}
41-
command: "{{packageManager}} run db:generate",
42-
});
43-
4438
const db = await D1Database("database", {
4539
{{#if (eq orm "prisma")}}
4640
migrationsDir: "packages/db/prisma/migrations",

apps/cli/templates/deploy/alchemy/env.d.ts.hbs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
// This file infers types for the cloudflare:workers environment from your Alchemy Worker.
22
// @see https://alchemy.run/concepts/bindings/#type-safe-bindings
33

4-
{{#if (eq webDeploy "alchemy")}}
5-
import type { server } from "../../alchemy.run";
6-
{{else}}
7-
import type { server } from "./alchemy.run";
8-
{{/if}}
9-
104
export type CloudflareEnv = typeof server.Env;
115

126
declare global {

0 commit comments

Comments
 (0)