alchemy 0.74.2 → 0.75.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bin/alchemy.js +1760 -1725
- package/bin/alchemy.ts +1 -1
- package/bin/commands/create.ts +5 -4
- package/bin/commands/init.ts +109 -65
- package/bin/constants.ts +2 -2
- package/bin/services/dependencies.ts +4 -4
- package/bin/services/execute-alchemy.ts +1 -1
- package/bin/services/find-workspace-root.ts +1 -1
- package/bin/services/get-package-version.ts +4 -4
- package/bin/services/github-workflow.ts +7 -7
- package/bin/services/template-manager.ts +20 -13
- package/bin/services/vibe-rules.ts +6 -5
- package/bin/trpc.ts +7 -0
- package/bin/types.ts +1 -0
- package/lib/alchemy.d.ts.map +1 -1
- package/lib/alchemy.js +29 -1
- package/lib/alchemy.js.map +1 -1
- package/lib/auth.js +1 -1
- package/lib/auth.js.map +1 -1
- package/lib/cloudflare/astro/astro.js +1 -1
- package/lib/cloudflare/astro/astro.js.map +1 -1
- package/lib/cloudflare/bun-spa/bun-spa.d.ts +1 -1
- package/lib/cloudflare/bun-spa/bun-spa.d.ts.map +1 -1
- package/lib/cloudflare/bun-spa/bun-spa.js +10 -11
- package/lib/cloudflare/bun-spa/bun-spa.js.map +1 -1
- package/lib/cloudflare/bun-spa/get-bun-spa-backend.d.ts +11 -0
- package/lib/cloudflare/bun-spa/get-bun-spa-backend.d.ts.map +1 -0
- package/lib/cloudflare/bun-spa/get-bun-spa-backend.js +26 -0
- package/lib/cloudflare/bun-spa/get-bun-spa-backend.js.map +1 -0
- package/lib/cloudflare/bun-spa/index.d.ts +2 -0
- package/lib/cloudflare/bun-spa/index.d.ts.map +1 -0
- package/lib/cloudflare/bun-spa/index.js +2 -0
- package/lib/cloudflare/bun-spa/index.js.map +1 -0
- package/lib/cloudflare/bundle/internal-worker-bundle.js +1 -1
- package/lib/cloudflare/bundle/internal-worker-bundle.js.map +1 -1
- package/lib/cloudflare/bundle/plugin-hybrid-node-compat.js +1 -1
- package/lib/cloudflare/bundle/plugin-hybrid-node-compat.js.map +1 -1
- package/lib/cloudflare/bundle/plugin-node-compat.js +1 -1
- package/lib/cloudflare/bundle/plugin-node-compat.js.map +1 -1
- package/lib/cloudflare/bundle/plugin-wasm.js +1 -1
- package/lib/cloudflare/bundle/plugin-wasm.js.map +1 -1
- package/lib/cloudflare/d1-migrations.js +1 -1
- package/lib/cloudflare/d1-migrations.js.map +1 -1
- package/lib/cloudflare/kv-namespace.d.ts +5 -0
- package/lib/cloudflare/kv-namespace.d.ts.map +1 -1
- package/lib/cloudflare/kv-namespace.js +44 -19
- package/lib/cloudflare/kv-namespace.js.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-controller.js +1 -1
- package/lib/cloudflare/miniflare/miniflare-controller.js.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.js +34 -4
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.js.map +1 -1
- package/lib/cloudflare/miniflare/paths.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/paths.js +2 -1
- package/lib/cloudflare/miniflare/paths.js.map +1 -1
- package/lib/cloudflare/miniflare/symlink-miniflare-state.js +1 -1
- package/lib/cloudflare/miniflare/symlink-miniflare-state.js.map +1 -1
- package/lib/cloudflare/next.js +1 -1
- package/lib/cloudflare/next.js.map +1 -1
- package/lib/cloudflare/nuxt/nuxt.js +1 -1
- package/lib/cloudflare/nuxt/nuxt.js.map +1 -1
- package/lib/cloudflare/react-router/plugin.d.ts.map +1 -1
- package/lib/cloudflare/react-router/plugin.js +2 -1
- package/lib/cloudflare/react-router/plugin.js.map +1 -1
- package/lib/cloudflare/react-router/react-router.js +1 -1
- package/lib/cloudflare/react-router/react-router.js.map +1 -1
- package/lib/cloudflare/redwood/redwood.js +1 -1
- package/lib/cloudflare/redwood/redwood.js.map +1 -1
- package/lib/cloudflare/sveltekit/sveltekit.js +1 -1
- package/lib/cloudflare/sveltekit/sveltekit.js.map +1 -1
- package/lib/cloudflare/tanstack-start/tanstack-start.js +1 -1
- package/lib/cloudflare/tanstack-start/tanstack-start.js.map +1 -1
- package/lib/cloudflare/tunnel.d.ts.map +1 -1
- package/lib/cloudflare/tunnel.js +25 -1
- package/lib/cloudflare/tunnel.js.map +1 -1
- package/lib/cloudflare/vite/plugin.js +1 -1
- package/lib/cloudflare/vite/plugin.js.map +1 -1
- package/lib/cloudflare/website.js +1 -1
- package/lib/cloudflare/website.js.map +1 -1
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +1 -1
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/cloudflare/wrangler.json.js +1 -1
- package/lib/cloudflare/wrangler.json.js.map +1 -1
- package/lib/coinbase/faucet.js +2 -2
- package/lib/coinbase/faucet.js.map +1 -1
- package/lib/docker/api.js +1 -1
- package/lib/docker/api.js.map +1 -1
- package/lib/docker/image.js +1 -1
- package/lib/docker/image.js.map +1 -1
- package/lib/esbuild/bundle.js +1 -1
- package/lib/esbuild/bundle.js.map +1 -1
- package/lib/fs/file.js +1 -1
- package/lib/fs/file.js.map +1 -1
- package/lib/llms.js +1 -1
- package/lib/llms.js.map +1 -1
- package/lib/os/exec.js +1 -1
- package/lib/os/exec.js.map +1 -1
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +1 -1
- package/lib/scope.js.map +1 -1
- package/lib/state/file-system-state-store.js +1 -1
- package/lib/state/file-system-state-store.js.map +1 -1
- package/lib/state/migrations.js +1 -1
- package/lib/state/migrations.js.map +1 -1
- package/lib/state/sqlite-state-store.js +1 -1
- package/lib/state/sqlite-state-store.js.map +1 -1
- package/lib/stripe/file.d.ts +3 -3
- package/lib/stripe/file.js +3 -3
- package/lib/test/bun.js +1 -1
- package/lib/test/bun.js.map +1 -1
- package/lib/test/prune.js +1 -1
- package/lib/test/prune.js.map +1 -1
- package/lib/test/vitest.js +1 -1
- package/lib/test/vitest.js.map +1 -1
- package/lib/util/content-type.js +1 -1
- package/lib/util/content-type.js.map +1 -1
- package/lib/util/idempotent-spawn.js +1 -1
- package/lib/util/idempotent-spawn.js.map +1 -1
- package/lib/util/lock.js +1 -1
- package/lib/util/lock.js.map +1 -1
- package/package.json +6 -1
- package/src/alchemy.ts +56 -1
- package/src/auth.ts +1 -1
- package/src/cloudflare/astro/astro.ts +1 -1
- package/src/cloudflare/bun-spa/bun-spa.ts +13 -13
- package/src/cloudflare/bun-spa/get-bun-spa-backend.ts +29 -0
- package/src/cloudflare/bun-spa/index.ts +1 -0
- package/src/cloudflare/bundle/internal-worker-bundle.ts +1 -1
- package/src/cloudflare/bundle/plugin-hybrid-node-compat.ts +1 -1
- package/src/cloudflare/bundle/plugin-node-compat.ts +1 -1
- package/src/cloudflare/bundle/plugin-wasm.ts +1 -1
- package/src/cloudflare/d1-migrations.ts +1 -1
- package/src/cloudflare/kv-namespace.ts +52 -20
- package/src/cloudflare/miniflare/miniflare-controller.ts +1 -1
- package/src/cloudflare/miniflare/miniflare-worker-proxy.ts +39 -4
- package/src/cloudflare/miniflare/paths.ts +2 -1
- package/src/cloudflare/miniflare/symlink-miniflare-state.ts +1 -1
- package/src/cloudflare/next.ts +1 -1
- package/src/cloudflare/nuxt/nuxt.ts +1 -1
- package/src/cloudflare/react-router/plugin.ts +2 -1
- package/src/cloudflare/react-router/react-router.ts +1 -1
- package/src/cloudflare/redwood/redwood.ts +1 -1
- package/src/cloudflare/sveltekit/sveltekit.ts +1 -1
- package/src/cloudflare/tanstack-start/tanstack-start.ts +1 -1
- package/src/cloudflare/tunnel.ts +36 -1
- package/src/cloudflare/vite/plugin.ts +1 -1
- package/src/cloudflare/website.ts +1 -1
- package/src/cloudflare/worker.ts +1 -3
- package/src/cloudflare/wrangler.json.ts +1 -1
- package/src/coinbase/faucet.ts +2 -2
- package/src/docker/api.ts +1 -1
- package/src/docker/image.ts +1 -1
- package/src/esbuild/bundle.ts +1 -1
- package/src/fs/file.ts +1 -1
- package/src/llms.ts +1 -1
- package/src/os/exec.ts +1 -1
- package/src/scope.ts +1 -1
- package/src/state/file-system-state-store.ts +1 -1
- package/src/state/migrations.ts +1 -1
- package/src/state/sqlite-state-store.ts +1 -1
- package/src/stripe/file.ts +3 -3
- package/src/test/bun.ts +1 -1
- package/src/test/prune.ts +1 -1
- package/src/test/vitest.ts +1 -1
- package/src/util/content-type.ts +1 -1
- package/src/util/idempotent-spawn.ts +1 -1
- package/src/util/lock.ts +1 -1
- package/templates/astro/alchemy.run.ts +0 -2
- package/templates/bun-spa/README.md +94 -0
- package/templates/bun-spa/_env +1 -0
- package/templates/bun-spa/_env.example +1 -0
- package/templates/bun-spa/_gitignore +29 -0
- package/templates/bun-spa/alchemy.run.ts +21 -0
- package/templates/bun-spa/bun-env.d.ts +26 -0
- package/templates/bun-spa/eslint.config.js +27 -0
- package/templates/bun-spa/package.json +30 -0
- package/templates/bun-spa/src/App.css +46 -0
- package/templates/bun-spa/src/App.tsx +54 -0
- package/templates/bun-spa/src/assets/logo.svg +1 -0
- package/templates/bun-spa/src/assets/react.svg +1 -0
- package/templates/bun-spa/src/frontend.tsx +15 -0
- package/templates/bun-spa/src/index.css +69 -0
- package/templates/bun-spa/src/index.html +14 -0
- package/templates/bun-spa/src/worker.ts +51 -0
- package/templates/bun-spa/tsconfig.json +24 -0
- package/templates/hono/alchemy.run.ts +0 -2
- package/templates/nextjs/alchemy.run.ts +0 -2
- package/templates/nuxt/alchemy.run.ts +0 -2
- package/templates/react-router/alchemy.run.ts +0 -2
- package/templates/react-router/package.json +1 -1
- package/templates/rwsdk/alchemy.run.ts +0 -1
- package/templates/rwsdk/package.json +1 -1
- package/templates/sveltekit/alchemy.run.ts +0 -2
- package/templates/sveltekit/package.json +1 -1
- package/templates/tanstack-start/alchemy.run.ts +0 -2
- package/templates/tanstack-start/package.json +1 -1
- package/templates/typescript/alchemy.run.ts +0 -2
- package/templates/vite/alchemy.run.ts +0 -2
- package/templates/vite/package.json +1 -1
- package/workers/tunnel-proxy.js +1 -1
package/bin/alchemy.ts
CHANGED
|
@@ -32,7 +32,7 @@ export type AppRouter = typeof router;
|
|
|
32
32
|
const cli = createCli({
|
|
33
33
|
router,
|
|
34
34
|
name: "alchemy",
|
|
35
|
-
version: getPackageVersion(),
|
|
35
|
+
version: await getPackageVersion(),
|
|
36
36
|
description:
|
|
37
37
|
"🧪 Welcome to Alchemy! Creating infrastructure as code with JavaScript and TypeScript.",
|
|
38
38
|
});
|
package/bin/commands/create.ts
CHANGED
|
@@ -10,11 +10,12 @@ import {
|
|
|
10
10
|
spinner,
|
|
11
11
|
text,
|
|
12
12
|
} from "@clack/prompts";
|
|
13
|
-
import
|
|
14
|
-
import { resolve } from "
|
|
13
|
+
import { rm } from "fs-extra";
|
|
14
|
+
import { resolve } from "pathe";
|
|
15
15
|
import pc from "picocolors";
|
|
16
16
|
import z from "zod";
|
|
17
17
|
import { detectPackageManager } from "../../src/util/detect-package-manager.ts";
|
|
18
|
+
import { exists } from "../../src/util/exists.ts";
|
|
18
19
|
import { throwWithContext } from "../errors.ts";
|
|
19
20
|
import { initializeGitRepo, isGitInstalled } from "../services/git.ts";
|
|
20
21
|
import { addGitHubWorkflowToAlchemy } from "../services/github-workflow.ts";
|
|
@@ -209,7 +210,7 @@ async function getInstallPreference(
|
|
|
209
210
|
async function handleDirectoryOverwrite(
|
|
210
211
|
context: ProjectContext,
|
|
211
212
|
): Promise<void> {
|
|
212
|
-
if (!
|
|
213
|
+
if (!(await exists(context.path))) {
|
|
213
214
|
return;
|
|
214
215
|
}
|
|
215
216
|
|
|
@@ -249,7 +250,7 @@ async function removeExistingDirectory(context: ProjectContext): Promise<void> {
|
|
|
249
250
|
s.start(`Removing existing directory: ${pc.yellow(context.path)}`);
|
|
250
251
|
|
|
251
252
|
try {
|
|
252
|
-
await
|
|
253
|
+
await rm(context.path, { recursive: true, force: true });
|
|
253
254
|
s.stop(`Directory ${pc.yellow(context.path)} removed.`);
|
|
254
255
|
} catch (error) {
|
|
255
256
|
s.stop(pc.red(`Failed to remove directory ${pc.yellow(context.path)}.`));
|
package/bin/commands/init.ts
CHANGED
|
@@ -8,9 +8,15 @@ import {
|
|
|
8
8
|
outro,
|
|
9
9
|
select,
|
|
10
10
|
} from "@clack/prompts";
|
|
11
|
-
import
|
|
11
|
+
import {
|
|
12
|
+
ensureDir,
|
|
13
|
+
ensureFile,
|
|
14
|
+
readFile,
|
|
15
|
+
writeFile,
|
|
16
|
+
writeJson,
|
|
17
|
+
} from "fs-extra";
|
|
12
18
|
import { parse as parseJsonc } from "jsonc-parse";
|
|
13
|
-
import { dirname, relative, resolve } from "
|
|
19
|
+
import { dirname, relative, resolve } from "pathe";
|
|
14
20
|
import pc from "picocolors";
|
|
15
21
|
import {
|
|
16
22
|
IndentationText,
|
|
@@ -21,6 +27,7 @@ import {
|
|
|
21
27
|
} from "ts-morph";
|
|
22
28
|
import z from "zod";
|
|
23
29
|
import { detectPackageManager } from "../../src/util/detect-package-manager.ts";
|
|
30
|
+
import { exists } from "../../src/util/exists.ts";
|
|
24
31
|
import type { DependencyVersionMap } from "../constants.ts";
|
|
25
32
|
import { throwWithContext } from "../errors.ts";
|
|
26
33
|
import { addPackageDependencies } from "../services/dependencies.ts";
|
|
@@ -89,7 +96,7 @@ function sanitizeProjectName(name: string): string {
|
|
|
89
96
|
|
|
90
97
|
async function readJsonc(filePath: string): Promise<any> {
|
|
91
98
|
try {
|
|
92
|
-
const content = await
|
|
99
|
+
const content = await readFile(filePath, "utf-8");
|
|
93
100
|
return parseJsonc(content);
|
|
94
101
|
} catch (error) {
|
|
95
102
|
throw new Error(`Failed to read or parse ${filePath}: ${error}`);
|
|
@@ -97,7 +104,7 @@ async function readJsonc(filePath: string): Promise<any> {
|
|
|
97
104
|
}
|
|
98
105
|
|
|
99
106
|
async function writeJsonWithSpaces(filePath: string, data: any): Promise<void> {
|
|
100
|
-
await
|
|
107
|
+
await writeJson(filePath, data, { spaces: 2 });
|
|
101
108
|
}
|
|
102
109
|
|
|
103
110
|
async function safelyUpdateJson(
|
|
@@ -107,7 +114,7 @@ async function safelyUpdateJson(
|
|
|
107
114
|
): Promise<void> {
|
|
108
115
|
try {
|
|
109
116
|
let data = fallbackData;
|
|
110
|
-
if (await
|
|
117
|
+
if (await exists(filePath)) {
|
|
111
118
|
data = await readJsonc(filePath);
|
|
112
119
|
}
|
|
113
120
|
updater(data);
|
|
@@ -123,7 +130,7 @@ async function createInitContext(options: {
|
|
|
123
130
|
}): Promise<InitContext> {
|
|
124
131
|
const cwd = resolve(process.cwd());
|
|
125
132
|
const packageJsonPath = resolve(cwd, "package.json");
|
|
126
|
-
const hasPackageJson = await
|
|
133
|
+
const hasPackageJson = await exists(packageJsonPath);
|
|
127
134
|
|
|
128
135
|
let projectName = "my-alchemy-app";
|
|
129
136
|
if (hasPackageJson) {
|
|
@@ -135,7 +142,7 @@ async function createInitContext(options: {
|
|
|
135
142
|
} catch (_error) {}
|
|
136
143
|
}
|
|
137
144
|
|
|
138
|
-
const useTypeScript = await
|
|
145
|
+
const useTypeScript = await exists(resolve(cwd, "tsconfig.json"));
|
|
139
146
|
const framework =
|
|
140
147
|
options.framework ||
|
|
141
148
|
(await detectFramework(cwd, hasPackageJson, options.yes));
|
|
@@ -160,6 +167,7 @@ const FRAMEWORK_DETECTION_MAP: Record<string, TemplateType> = {
|
|
|
160
167
|
"@sveltejs/kit": "sveltekit",
|
|
161
168
|
"@tanstack/react-start": "tanstack-start",
|
|
162
169
|
vite: "vite",
|
|
170
|
+
"@types/bun": "bun-spa",
|
|
163
171
|
};
|
|
164
172
|
|
|
165
173
|
async function detectFramework(
|
|
@@ -182,6 +190,7 @@ async function detectFramework(
|
|
|
182
190
|
options: [
|
|
183
191
|
{ label: "TypeScript Worker", value: "typescript" },
|
|
184
192
|
{ label: "Vite", value: "vite" },
|
|
193
|
+
{ label: "Bun SPA", value: "bun-spa" },
|
|
185
194
|
{ label: "Astro", value: "astro" },
|
|
186
195
|
{ label: "React Router", value: "react-router" },
|
|
187
196
|
{ label: "SvelteKit", value: "sveltekit" },
|
|
@@ -226,9 +235,13 @@ async function detectFrameworkFromPackageJson(
|
|
|
226
235
|
|
|
227
236
|
async function checkExistingAlchemyFiles(context: InitContext): Promise<void> {
|
|
228
237
|
const alchemyFiles = ["alchemy.run.ts", "alchemy.run.js"];
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
238
|
+
let existingFile: string | undefined;
|
|
239
|
+
for (const file of alchemyFiles) {
|
|
240
|
+
if (await exists(resolve(context.cwd, file))) {
|
|
241
|
+
existingFile = file;
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
232
245
|
|
|
233
246
|
if (existingFile) {
|
|
234
247
|
const overwriteResult = await confirm({
|
|
@@ -247,9 +260,7 @@ const ALCHEMY_RUN_TEMPLATES: Record<
|
|
|
247
260
|
Exclude<TemplateType, "hono">,
|
|
248
261
|
(context: InitContext) => string
|
|
249
262
|
> = {
|
|
250
|
-
typescript: (context) =>
|
|
251
|
-
|
|
252
|
-
import alchemy from "alchemy";
|
|
263
|
+
typescript: (context) => `import alchemy from "alchemy";
|
|
253
264
|
import { Worker } from "alchemy/cloudflare";
|
|
254
265
|
|
|
255
266
|
const app = await alchemy("${context.projectName}");
|
|
@@ -283,9 +294,25 @@ console.log({
|
|
|
283
294
|
await app.finalize();
|
|
284
295
|
`,
|
|
285
296
|
|
|
286
|
-
|
|
297
|
+
"bun-spa": (context) => `import alchemy from "alchemy";
|
|
298
|
+
import { BunSPA } from "alchemy/cloudflare";
|
|
299
|
+
|
|
300
|
+
const app = await alchemy("${context.projectName}");
|
|
301
|
+
|
|
302
|
+
export const bunsite = await BunSPA("website", {
|
|
303
|
+
frontend: "src/index.html", // adjust to match your HTML entrypoint(s)
|
|
304
|
+
entrypoint: "src/server.ts", // your backend API entrypoint
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
console.log({
|
|
308
|
+
url: bunsite.url,
|
|
309
|
+
apiUrl: bunsite.apiUrl,
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
await app.finalize();
|
|
313
|
+
`,
|
|
287
314
|
|
|
288
|
-
import alchemy from "alchemy";
|
|
315
|
+
astro: (context) => `import alchemy from "alchemy";
|
|
289
316
|
import { Astro } from "alchemy/cloudflare";
|
|
290
317
|
|
|
291
318
|
const app = await alchemy("${context.projectName}");
|
|
@@ -305,9 +332,7 @@ console.log({
|
|
|
305
332
|
await app.finalize();
|
|
306
333
|
`,
|
|
307
334
|
|
|
308
|
-
"react-router": (context) =>
|
|
309
|
-
|
|
310
|
-
import alchemy from "alchemy";
|
|
335
|
+
"react-router": (context) => `import alchemy from "alchemy";
|
|
311
336
|
import { ReactRouter } from "alchemy/cloudflare";
|
|
312
337
|
|
|
313
338
|
const app = await alchemy("${context.projectName}");
|
|
@@ -328,9 +353,7 @@ console.log({
|
|
|
328
353
|
await app.finalize();
|
|
329
354
|
`,
|
|
330
355
|
|
|
331
|
-
sveltekit: (context) =>
|
|
332
|
-
|
|
333
|
-
import alchemy from "alchemy";
|
|
356
|
+
sveltekit: (context) => `import alchemy from "alchemy";
|
|
334
357
|
import { SvelteKit } from "alchemy/cloudflare";
|
|
335
358
|
|
|
336
359
|
const app = await alchemy("${context.projectName}");
|
|
@@ -350,9 +373,7 @@ console.log({
|
|
|
350
373
|
await app.finalize();
|
|
351
374
|
`,
|
|
352
375
|
|
|
353
|
-
"tanstack-start": (context) =>
|
|
354
|
-
|
|
355
|
-
import alchemy from "alchemy";
|
|
376
|
+
"tanstack-start": (context) => `import alchemy from "alchemy";
|
|
356
377
|
import { TanStackStart } from "alchemy/cloudflare";
|
|
357
378
|
|
|
358
379
|
const app = await alchemy("${context.projectName}");
|
|
@@ -372,9 +393,7 @@ console.log({
|
|
|
372
393
|
await app.finalize();
|
|
373
394
|
`,
|
|
374
395
|
|
|
375
|
-
rwsdk: (context) =>
|
|
376
|
-
|
|
377
|
-
import alchemy from "alchemy";
|
|
396
|
+
rwsdk: (context) => `import alchemy from "alchemy";
|
|
378
397
|
import { D1Database, DurableObjectNamespace, Redwood } from "alchemy/cloudflare";
|
|
379
398
|
|
|
380
399
|
const app = await alchemy("${context.projectName}");
|
|
@@ -405,9 +424,7 @@ console.log({
|
|
|
405
424
|
await app.finalize();
|
|
406
425
|
`,
|
|
407
426
|
|
|
408
|
-
nextjs: (context) =>
|
|
409
|
-
|
|
410
|
-
import alchemy from "alchemy";
|
|
427
|
+
nextjs: (context) => `import alchemy from "alchemy";
|
|
411
428
|
import { Nextjs } from "alchemy/cloudflare";
|
|
412
429
|
|
|
413
430
|
const app = await alchemy("${context.projectName}");
|
|
@@ -422,9 +439,7 @@ console.log({
|
|
|
422
439
|
await app.finalize();
|
|
423
440
|
`,
|
|
424
441
|
|
|
425
|
-
nuxt: (context) =>
|
|
426
|
-
|
|
427
|
-
import alchemy from "alchemy";
|
|
442
|
+
nuxt: (context) => `import alchemy from "alchemy";
|
|
428
443
|
import { Nuxt } from "alchemy/cloudflare";
|
|
429
444
|
|
|
430
445
|
const app = await alchemy("${context.projectName}");
|
|
@@ -452,7 +467,7 @@ async function createAlchemyRunFile(context: InitContext): Promise<void> {
|
|
|
452
467
|
? "alchemy.run.ts"
|
|
453
468
|
: "alchemy.run.js";
|
|
454
469
|
const outputPath = resolve(context.cwd, outputFileName);
|
|
455
|
-
await
|
|
470
|
+
await writeFile(outputPath, content, "utf-8");
|
|
456
471
|
} catch (error) {
|
|
457
472
|
throwWithContext(error, "Failed to create alchemy.run file");
|
|
458
473
|
}
|
|
@@ -465,6 +480,7 @@ const FRAMEWORK_DEPENDENCIES: Record<TemplateType, DependencyVersionMap[]> = {
|
|
|
465
480
|
sveltekit: ["alchemy", "@sveltejs/adapter-cloudflare"],
|
|
466
481
|
typescript: ["alchemy"],
|
|
467
482
|
vite: ["alchemy"],
|
|
483
|
+
"bun-spa": ["alchemy"],
|
|
468
484
|
astro: ["alchemy", "@astrojs/cloudflare"],
|
|
469
485
|
"react-router": ["alchemy", "@cloudflare/vite-plugin"],
|
|
470
486
|
"tanstack-start": ["alchemy"],
|
|
@@ -507,11 +523,11 @@ async function updateGitignore(context: InitContext) {
|
|
|
507
523
|
try {
|
|
508
524
|
const gitignorePath = resolve(context.cwd, ".gitignore");
|
|
509
525
|
|
|
510
|
-
await
|
|
526
|
+
await ensureFile(gitignorePath);
|
|
511
527
|
|
|
512
528
|
let gitignoreContent = "";
|
|
513
|
-
if (await
|
|
514
|
-
gitignoreContent = await
|
|
529
|
+
if (await exists(gitignorePath)) {
|
|
530
|
+
gitignoreContent = await readFile(gitignorePath, "utf-8");
|
|
515
531
|
}
|
|
516
532
|
|
|
517
533
|
const lines = gitignoreContent.split("\n").map((line) => line.trim());
|
|
@@ -534,7 +550,7 @@ async function updateGitignore(context: InitContext) {
|
|
|
534
550
|
}
|
|
535
551
|
}
|
|
536
552
|
|
|
537
|
-
await
|
|
553
|
+
await writeFile(gitignorePath, lines.join("\n"), "utf-8");
|
|
538
554
|
} catch (error) {
|
|
539
555
|
throwWithContext(error, "Failed to update .gitignore");
|
|
540
556
|
}
|
|
@@ -589,6 +605,7 @@ async function updateProjectConfiguration(context: InitContext): Promise<{
|
|
|
589
605
|
return {
|
|
590
606
|
typescript: () => updateTypescriptProject(context),
|
|
591
607
|
vite: () => updateViteProject(context),
|
|
608
|
+
"bun-spa": () => updateBunSpaProject(context),
|
|
592
609
|
astro: () => updateAstroProject(context),
|
|
593
610
|
"react-router": () => updateReactRouterProject(context),
|
|
594
611
|
sveltekit: () => updateSvelteKitProject(context),
|
|
@@ -601,7 +618,7 @@ async function updateProjectConfiguration(context: InitContext): Promise<{
|
|
|
601
618
|
|
|
602
619
|
async function updateTypescriptProject(context: InitContext): Promise<void> {
|
|
603
620
|
const tsConfigPath = resolve(context.cwd, "tsconfig.json");
|
|
604
|
-
if (await
|
|
621
|
+
if (await exists(tsConfigPath)) {
|
|
605
622
|
await updateTsConfig(tsConfigPath, {
|
|
606
623
|
include: ["alchemy.run.ts"],
|
|
607
624
|
});
|
|
@@ -611,18 +628,43 @@ async function updateTypescriptProject(context: InitContext): Promise<void> {
|
|
|
611
628
|
async function updateViteProject(_context: InitContext): Promise<void> {
|
|
612
629
|
// const tsConfigPath = resolve(context.cwd, "tsconfig.json");
|
|
613
630
|
// const tsConfigNodePath = resolve(context.cwd, "tsconfig.node.json");
|
|
614
|
-
// if (await
|
|
631
|
+
// if (await exists(tsConfigPath)) {
|
|
615
632
|
// await updateTsConfig(tsConfigPath, {
|
|
616
633
|
// exclude: ["alchemy.run.ts", "./types/env.d.ts"],
|
|
617
634
|
// });
|
|
618
635
|
// }
|
|
619
|
-
// if ((await
|
|
636
|
+
// if ((await exists(tsConfigNodePath)) || context.framework === "vite") {
|
|
620
637
|
// await updateTsConfig(tsConfigNodePath, {
|
|
621
638
|
// include: ["alchemy.run.ts"],
|
|
622
639
|
// });
|
|
623
640
|
// }
|
|
624
641
|
}
|
|
625
642
|
|
|
643
|
+
async function updateBunSpaProject(context: InitContext): Promise<void> {
|
|
644
|
+
// Validate bunfig.toml exists or create it
|
|
645
|
+
const bunfigPath = resolve(context.cwd, "bunfig.toml");
|
|
646
|
+
if (!(await exists(bunfigPath))) {
|
|
647
|
+
// Create bunfig.toml with required config
|
|
648
|
+
await writeFile(bunfigPath, `[serve.static]\nenv='BUN_PUBLIC_*'\n`);
|
|
649
|
+
} else {
|
|
650
|
+
// Validate bunfig.toml has required config
|
|
651
|
+
const bunfigContent = await readFile(bunfigPath, "utf8");
|
|
652
|
+
const hasBunPublicEnv =
|
|
653
|
+
bunfigContent.includes("env") &&
|
|
654
|
+
(bunfigContent.includes("BUN_PUBLIC_*") ||
|
|
655
|
+
bunfigContent.includes("PUBLIC_*"));
|
|
656
|
+
|
|
657
|
+
if (!hasBunPublicEnv) {
|
|
658
|
+
throw new Error(
|
|
659
|
+
"bunfig.toml must contain the following configuration:\n\n" +
|
|
660
|
+
"[serve.static]\n" +
|
|
661
|
+
"env='BUN_PUBLIC_*'\n\n" +
|
|
662
|
+
"This is required for Alchemy to work with Bun SPA.",
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
626
668
|
async function updateSvelteKitProject(context: InitContext): Promise<void> {
|
|
627
669
|
await updateSvelteConfig(context);
|
|
628
670
|
|
|
@@ -667,10 +709,12 @@ async function updateNextjsProject(context: InitContext): Promise<void> {
|
|
|
667
709
|
const candidates = ["ts", "js", "cjs", "mjs"].map((ext) =>
|
|
668
710
|
resolve(context.cwd, `${name}.${ext}`),
|
|
669
711
|
);
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
712
|
+
const existenceChecks = await Promise.all(
|
|
713
|
+
candidates.map((candidate) => exists(candidate)),
|
|
714
|
+
);
|
|
715
|
+
const foundIdx = existenceChecks.findIndex((exists) => exists);
|
|
716
|
+
if (foundIdx !== -1) {
|
|
717
|
+
return { path: candidates[foundIdx], exists: true };
|
|
674
718
|
}
|
|
675
719
|
return { path: candidates[0], exists: false };
|
|
676
720
|
};
|
|
@@ -679,7 +723,7 @@ async function updateNextjsProject(context: InitContext): Promise<void> {
|
|
|
679
723
|
const openNextConfig = await resolveFile("open-next.config");
|
|
680
724
|
|
|
681
725
|
if (nextConfig.exists) {
|
|
682
|
-
const fileContent = await
|
|
726
|
+
const fileContent = await readFile(nextConfig.path, "utf-8");
|
|
683
727
|
let updated = fileContent;
|
|
684
728
|
if (
|
|
685
729
|
!fileContent.includes(
|
|
@@ -691,9 +735,9 @@ async function updateNextjsProject(context: InitContext): Promise<void> {
|
|
|
691
735
|
if (!fileContent.includes("initOpenNextCloudflareForDev()")) {
|
|
692
736
|
updated += "\ninitOpenNextCloudflareForDev();\n";
|
|
693
737
|
}
|
|
694
|
-
await
|
|
738
|
+
await writeFile(nextConfig.path, updated);
|
|
695
739
|
} else {
|
|
696
|
-
await
|
|
740
|
+
await writeFile(
|
|
697
741
|
nextConfig.path,
|
|
698
742
|
`import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare";
|
|
699
743
|
import type { NextConfig } from "next";
|
|
@@ -710,7 +754,7 @@ initOpenNextCloudflareForDev();
|
|
|
710
754
|
}
|
|
711
755
|
|
|
712
756
|
if (!openNextConfig.exists) {
|
|
713
|
-
await
|
|
757
|
+
await writeFile(
|
|
714
758
|
openNextConfig.path,
|
|
715
759
|
`import { defineCloudflareConfig } from "@opennextjs/cloudflare";
|
|
716
760
|
|
|
@@ -724,7 +768,7 @@ export default defineCloudflareConfig({
|
|
|
724
768
|
`,
|
|
725
769
|
);
|
|
726
770
|
}
|
|
727
|
-
await
|
|
771
|
+
await writeFile(
|
|
728
772
|
resolve(context.cwd, "./env.d.ts"),
|
|
729
773
|
`// Auto-generated Cloudflare binding types.
|
|
730
774
|
// @see https://alchemy.run/concepts/bindings/#type-safe-bindings
|
|
@@ -759,17 +803,17 @@ async function updateReactRouterProject(context: InitContext): Promise<{
|
|
|
759
803
|
const wranglerJsonPath = resolve(context.cwd, "wrangler.json");
|
|
760
804
|
const envTsPath = resolve(workersDir, "env.ts");
|
|
761
805
|
|
|
762
|
-
await
|
|
806
|
+
await ensureDir(workersDir);
|
|
763
807
|
|
|
764
808
|
let main: string | undefined;
|
|
765
|
-
if (await
|
|
809
|
+
if (await exists(wranglerJsonCPath)) {
|
|
766
810
|
const wranglerJsonC = await readJsonc(wranglerJsonCPath);
|
|
767
811
|
main = wranglerJsonC.main;
|
|
768
|
-
} else if (await
|
|
812
|
+
} else if (await exists(wranglerJsonPath)) {
|
|
769
813
|
const wranglerJson = await readJsonc(wranglerJsonPath);
|
|
770
814
|
main = wranglerJson.main;
|
|
771
815
|
} else {
|
|
772
|
-
await
|
|
816
|
+
await writeFile(
|
|
773
817
|
resolve(workersDir, "app.ts"),
|
|
774
818
|
`
|
|
775
819
|
import { createRequestHandler } from "react-router";
|
|
@@ -799,7 +843,7 @@ export default {
|
|
|
799
843
|
);
|
|
800
844
|
}
|
|
801
845
|
|
|
802
|
-
await
|
|
846
|
+
await writeFile(
|
|
803
847
|
envTsPath,
|
|
804
848
|
`
|
|
805
849
|
import type { website } from "../alchemy.run.ts";
|
|
@@ -837,7 +881,7 @@ declare module "cloudflare:workers" {
|
|
|
837
881
|
|
|
838
882
|
async function updateViteConfig(context: InitContext): Promise<void> {
|
|
839
883
|
const viteConfigPath = resolve(context.cwd, "vite.config.ts");
|
|
840
|
-
if (!(await
|
|
884
|
+
if (!(await exists(viteConfigPath))) return;
|
|
841
885
|
|
|
842
886
|
try {
|
|
843
887
|
const project = new Project({
|
|
@@ -922,7 +966,7 @@ async function updateViteConfig(context: InitContext): Promise<void> {
|
|
|
922
966
|
|
|
923
967
|
async function updateReactRouterConfigTS(context: InitContext): Promise<void> {
|
|
924
968
|
const configPath = resolve(context.cwd, "react-router.config.ts");
|
|
925
|
-
if (!(await
|
|
969
|
+
if (!(await exists(configPath))) return;
|
|
926
970
|
|
|
927
971
|
try {
|
|
928
972
|
const project = new Project({
|
|
@@ -1005,7 +1049,7 @@ async function updateTanStackStartProject(context: InitContext): Promise<void> {
|
|
|
1005
1049
|
|
|
1006
1050
|
async function updateSvelteConfig(context: InitContext): Promise<void> {
|
|
1007
1051
|
const svelteConfigPath = resolve(context.cwd, "svelte.config.js");
|
|
1008
|
-
if (!(await
|
|
1052
|
+
if (!(await exists(svelteConfigPath))) return;
|
|
1009
1053
|
|
|
1010
1054
|
try {
|
|
1011
1055
|
const project = new Project({
|
|
@@ -1079,7 +1123,7 @@ function updateAdapterInConfig(configObject: Node): void {
|
|
|
1079
1123
|
|
|
1080
1124
|
async function updateEnvFile(context: InitContext): Promise<void> {
|
|
1081
1125
|
const envPath = resolve(context.cwd, ".env");
|
|
1082
|
-
await
|
|
1126
|
+
await ensureFile(envPath);
|
|
1083
1127
|
|
|
1084
1128
|
const envVars = ["ALCHEMY_PASSWORD=change-me"];
|
|
1085
1129
|
if (context.framework === "rwsdk") {
|
|
@@ -1087,9 +1131,9 @@ async function updateEnvFile(context: InitContext): Promise<void> {
|
|
|
1087
1131
|
}
|
|
1088
1132
|
|
|
1089
1133
|
let envContent = "";
|
|
1090
|
-
if (await
|
|
1134
|
+
if (await exists(envPath)) {
|
|
1091
1135
|
try {
|
|
1092
|
-
envContent = await
|
|
1136
|
+
envContent = await readFile(envPath, "utf-8");
|
|
1093
1137
|
} catch (error) {
|
|
1094
1138
|
console.warn("Failed to read .env:", error);
|
|
1095
1139
|
}
|
|
@@ -1109,7 +1153,7 @@ async function updateEnvFile(context: InitContext): Promise<void> {
|
|
|
1109
1153
|
|
|
1110
1154
|
if (needsUpdate) {
|
|
1111
1155
|
try {
|
|
1112
|
-
await
|
|
1156
|
+
await writeFile(envPath, envContent, "utf-8");
|
|
1113
1157
|
} catch (error) {
|
|
1114
1158
|
console.warn("Failed to update .env:", error);
|
|
1115
1159
|
}
|
|
@@ -1118,7 +1162,7 @@ async function updateEnvFile(context: InitContext): Promise<void> {
|
|
|
1118
1162
|
|
|
1119
1163
|
async function updateNuxtConfig(context: InitContext): Promise<void> {
|
|
1120
1164
|
const nuxtConfigPath = resolve(context.cwd, "nuxt.config.ts");
|
|
1121
|
-
if (!(await
|
|
1165
|
+
if (!(await exists(nuxtConfigPath))) return;
|
|
1122
1166
|
|
|
1123
1167
|
try {
|
|
1124
1168
|
const project = new Project({
|
|
@@ -1193,7 +1237,7 @@ async function updateNuxtConfig(context: InitContext): Promise<void> {
|
|
|
1193
1237
|
|
|
1194
1238
|
async function updateAstroConfig(context: InitContext): Promise<void> {
|
|
1195
1239
|
const astroConfigPath = resolve(context.cwd, "astro.config.mjs");
|
|
1196
|
-
if (!(await
|
|
1240
|
+
if (!(await exists(astroConfigPath))) return;
|
|
1197
1241
|
|
|
1198
1242
|
try {
|
|
1199
1243
|
const project = new Project({
|
|
@@ -1251,7 +1295,7 @@ async function updateAstroConfig(context: InitContext): Promise<void> {
|
|
|
1251
1295
|
|
|
1252
1296
|
async function updateTanStackViteConfig(context: InitContext): Promise<void> {
|
|
1253
1297
|
const viteConfigPath = resolve(context.cwd, "vite.config.ts");
|
|
1254
|
-
if (!(await
|
|
1298
|
+
if (!(await exists(viteConfigPath))) return;
|
|
1255
1299
|
|
|
1256
1300
|
try {
|
|
1257
1301
|
const project = new Project({
|
package/bin/constants.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import path from "
|
|
1
|
+
import path from "pathe";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
3
|
import { getPackageVersion } from "./services/get-package-version.ts";
|
|
4
4
|
|
|
@@ -10,7 +10,7 @@ export const dependencyVersionMap = {
|
|
|
10
10
|
alchemy:
|
|
11
11
|
process.env.NODE_ENV === "test"
|
|
12
12
|
? `file:${path.resolve(PKG_ROOT)}`
|
|
13
|
-
: getPackageVersion(),
|
|
13
|
+
: await getPackageVersion(),
|
|
14
14
|
|
|
15
15
|
miniflare: "^4.20250617.3",
|
|
16
16
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import path from "node:path";
|
|
1
|
+
import path from "pathe";
|
|
3
2
|
|
|
3
|
+
import { readJson, writeJson } from "fs-extra";
|
|
4
4
|
import {
|
|
5
5
|
type DependencyVersionMap,
|
|
6
6
|
dependencyVersionMap,
|
|
@@ -15,7 +15,7 @@ export const addPackageDependencies = async (opts: {
|
|
|
15
15
|
|
|
16
16
|
const pkgJsonPath = path.join(projectDir, "package.json");
|
|
17
17
|
|
|
18
|
-
const pkgJson = await
|
|
18
|
+
const pkgJson = await readJson(pkgJsonPath);
|
|
19
19
|
|
|
20
20
|
if (!pkgJson.dependencies) pkgJson.dependencies = {};
|
|
21
21
|
if (!pkgJson.devDependencies) pkgJson.devDependencies = {};
|
|
@@ -34,7 +34,7 @@ export const addPackageDependencies = async (opts: {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
await
|
|
37
|
+
await writeJson(pkgJsonPath, pkgJson, {
|
|
38
38
|
spaces: 2,
|
|
39
39
|
});
|
|
40
40
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { log } from "@clack/prompts";
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
3
|
import { once } from "node:events";
|
|
4
|
-
import { resolve } from "
|
|
4
|
+
import { resolve } from "pathe";
|
|
5
5
|
import pc from "picocolors";
|
|
6
6
|
import z from "zod";
|
|
7
7
|
import { detectRuntime } from "../../src/util/detect-node-runtime.ts";
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import path from "
|
|
1
|
+
import { readJSON } from "fs-extra";
|
|
2
|
+
import path from "pathe";
|
|
3
3
|
import { PKG_ROOT } from "../constants.ts";
|
|
4
4
|
|
|
5
|
-
export const getPackageVersion = () => {
|
|
5
|
+
export const getPackageVersion = async () => {
|
|
6
6
|
const packageJsonPath = path.join(PKG_ROOT, "package.json");
|
|
7
7
|
|
|
8
|
-
const packageJsonContent =
|
|
8
|
+
const packageJsonContent = await readJSON(packageJsonPath);
|
|
9
9
|
|
|
10
10
|
return packageJsonContent.version ?? "1.0.0";
|
|
11
11
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** biome-ignore-all lint/suspicious/noTemplateCurlyInString: we are building a github template workflow */
|
|
2
2
|
import { spinner } from "@clack/prompts";
|
|
3
|
-
import
|
|
4
|
-
import path from "
|
|
3
|
+
import { ensureDir, readFile, writeFile } from "fs-extra";
|
|
4
|
+
import path from "pathe";
|
|
5
5
|
import YAML from "yaml";
|
|
6
6
|
import { throwWithContext } from "../errors.ts";
|
|
7
7
|
import type { ProjectContext } from "../types.ts";
|
|
@@ -17,7 +17,7 @@ export async function addGitHubWorkflowToAlchemy(
|
|
|
17
17
|
|
|
18
18
|
try {
|
|
19
19
|
const workflowDir = path.join(context.path, ".github", "workflows");
|
|
20
|
-
await
|
|
20
|
+
await ensureDir(workflowDir);
|
|
21
21
|
|
|
22
22
|
const pmCommands =
|
|
23
23
|
PackageManager[context.packageManager] ?? PackageManager.bun;
|
|
@@ -69,7 +69,7 @@ export async function addGitHubWorkflowToAlchemy(
|
|
|
69
69
|
run: installCmd,
|
|
70
70
|
};
|
|
71
71
|
|
|
72
|
-
await
|
|
72
|
+
await writeFile(
|
|
73
73
|
path.join(workflowDir, "pr-preview.yml"),
|
|
74
74
|
YAML.stringify({
|
|
75
75
|
name: "Preview",
|
|
@@ -146,7 +146,7 @@ export async function addGitHubWorkflowToAlchemy(
|
|
|
146
146
|
},
|
|
147
147
|
} as const),
|
|
148
148
|
);
|
|
149
|
-
await
|
|
149
|
+
await writeFile(
|
|
150
150
|
path.join(workflowDir, "publish.yml"),
|
|
151
151
|
YAML.stringify({
|
|
152
152
|
name: "Publish",
|
|
@@ -220,7 +220,7 @@ export async function addGitHubWorkflowToAlchemy(
|
|
|
220
220
|
} as const),
|
|
221
221
|
);
|
|
222
222
|
|
|
223
|
-
let code = await
|
|
223
|
+
let code = await readFile(alchemyFilePath, "utf-8");
|
|
224
224
|
|
|
225
225
|
const alchemyImportRegex = /(import alchemy from "alchemy";)/;
|
|
226
226
|
const alchemyImportMatch = code.match(alchemyImportRegex);
|
|
@@ -279,7 +279,7 @@ This preview was built from commit \${process.env.GITHUB_SHA}
|
|
|
279
279
|
code = code.replace(finalizeRegex, `${githubWorkflowCode}$1`);
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
-
await
|
|
282
|
+
await writeFile(alchemyFilePath, code, "utf-8");
|
|
283
283
|
|
|
284
284
|
s.stop("GitHub Actions configured");
|
|
285
285
|
} catch (error) {
|