alchemy 0.37.2 → 0.38.1
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/bin/alchemy.mjs +44306 -48055
- package/bin/alchemy.ts +80 -85
- package/bin/commands/create.ts +263 -0
- package/bin/constants.ts +13 -0
- package/bin/errors.ts +11 -0
- package/bin/services/dependencies.ts +47 -0
- package/bin/services/get-package-version.ts +11 -0
- package/bin/services/package-manager.ts +113 -0
- package/bin/services/template-manager.ts +137 -0
- package/bin/types.ts +83 -0
- package/lib/alchemy.d.ts.map +1 -1
- package/lib/alchemy.js +12 -3
- package/lib/alchemy.js.map +1 -1
- package/lib/apply.d.ts +2 -0
- package/lib/apply.d.ts.map +1 -1
- package/lib/apply.js +47 -12
- package/lib/apply.js.map +1 -1
- package/lib/build-date.d.ts +6 -0
- package/lib/build-date.d.ts.map +1 -0
- package/lib/build-date.js +8 -0
- package/lib/build-date.js.map +1 -0
- package/lib/cloudflare/api.d.ts.map +1 -1
- package/lib/cloudflare/api.js +2 -1
- package/lib/cloudflare/api.js.map +1 -1
- package/lib/cloudflare/do-state-store/internal.js +5 -3
- package/lib/cloudflare/do-state-store/internal.js.map +1 -1
- package/lib/cloudflare/do-state-store/worker.d.ts.map +1 -1
- package/lib/cloudflare/do-state-store/worker.js +6 -1
- package/lib/cloudflare/do-state-store/worker.js.map +1 -1
- package/lib/cloudflare/pipeline.d.ts +1 -1
- package/lib/cloudflare/pipeline.d.ts.map +1 -1
- package/lib/cloudflare/pipeline.js +6 -1
- package/lib/cloudflare/pipeline.js.map +1 -1
- package/lib/cloudflare/worker-stub.d.ts +23 -2
- package/lib/cloudflare/worker-stub.d.ts.map +1 -1
- package/lib/cloudflare/worker-stub.js +16 -2
- package/lib/cloudflare/worker-stub.js.map +1 -1
- package/lib/cloudflare/worker.d.ts +5 -4
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +4 -7
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/context.d.ts +2 -2
- package/lib/context.d.ts.map +1 -1
- package/lib/destroy.d.ts +8 -2
- package/lib/destroy.d.ts.map +1 -1
- package/lib/destroy.js +19 -5
- package/lib/destroy.js.map +1 -1
- package/lib/scope.d.ts +26 -4
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +104 -9
- package/lib/scope.js.map +1 -1
- package/lib/sentry/project.js +1 -1
- package/lib/sentry/project.js.map +1 -1
- package/lib/util/logger.js +1 -1
- package/lib/util/logger.js.map +1 -1
- package/lib/util/mutex.d.ts +9 -0
- package/lib/util/mutex.d.ts.map +1 -0
- package/lib/util/mutex.js +35 -0
- package/lib/util/mutex.js.map +1 -0
- package/package.json +16 -6
- package/src/alchemy.ts +17 -5
- package/src/apply.ts +67 -17
- package/src/build-date.ts +8 -0
- package/src/cloudflare/api.ts +2 -1
- package/src/cloudflare/do-state-store/internal.ts +5 -3
- package/src/cloudflare/do-state-store/worker.ts +13 -3
- package/src/cloudflare/pipeline.ts +6 -0
- package/src/cloudflare/worker-stub.ts +31 -2
- package/src/cloudflare/worker.ts +6 -15
- package/src/context.ts +2 -2
- package/src/destroy.ts +30 -7
- package/src/scope.ts +145 -10
- package/src/sentry/project.ts +1 -1
- package/src/util/logger.ts +1 -1
- package/src/util/mutex.ts +33 -0
- package/templates/astro/.vscode/extensions.json +4 -0
- package/templates/astro/.vscode/launch.json +11 -0
- package/templates/astro/README.md +48 -0
- package/templates/astro/_env.example +1 -0
- package/templates/astro/_gitignore +27 -0
- package/templates/astro/alchemy.run.ts +16 -0
- package/templates/astro/astro.config.mjs +8 -0
- package/templates/astro/package.json +23 -0
- package/templates/astro/public/favicon.svg +9 -0
- package/templates/astro/src/assets/astro.svg +1 -0
- package/templates/astro/src/assets/background.svg +1 -0
- package/templates/astro/src/components/Welcome.astro +210 -0
- package/templates/astro/src/layouts/Layout.astro +22 -0
- package/templates/astro/src/pages/api/hello.ts +19 -0
- package/templates/astro/src/pages/index.astro +11 -0
- package/templates/astro/tsconfig.json +18 -0
- package/templates/astro/types/env.d.ts +16 -0
- package/templates/astro/wrangler.jsonc +6 -0
- package/templates/nuxt/README.md +75 -0
- package/templates/nuxt/_env +1 -0
- package/templates/nuxt/_env.example +1 -0
- package/templates/nuxt/_gitignore +27 -0
- package/templates/nuxt/alchemy.run.ts +16 -0
- package/templates/nuxt/app.vue +6 -0
- package/templates/nuxt/nuxt.config.ts +13 -0
- package/templates/nuxt/package.json +26 -0
- package/templates/nuxt/public/favicon.ico +0 -0
- package/templates/nuxt/public/robots.txt +2 -0
- package/templates/nuxt/server/api/hello.ts +7 -0
- package/templates/nuxt/server/middleware/auth.ts +5 -0
- package/templates/nuxt/server/middleware/hello.ts +5 -0
- package/templates/nuxt/server/tsconfig.json +3 -0
- package/templates/nuxt/tsconfig.json +15 -0
- package/templates/nuxt/types/env.d.ts +16 -0
- package/templates/react-router/.vscode/settings.json +5 -0
- package/templates/react-router/README.md +79 -0
- package/templates/react-router/_env +1 -0
- package/templates/react-router/_env.example +1 -0
- package/templates/react-router/_gitignore +15 -0
- package/templates/react-router/alchemy.run.ts +17 -0
- package/templates/react-router/app/app.css +15 -0
- package/templates/react-router/app/entry.server.tsx +43 -0
- package/templates/react-router/app/root.tsx +75 -0
- package/templates/react-router/app/routes/home.tsx +17 -0
- package/templates/react-router/app/routes.ts +3 -0
- package/templates/react-router/app/welcome/logo-dark.svg +23 -0
- package/templates/react-router/app/welcome/logo-light.svg +23 -0
- package/templates/react-router/app/welcome/welcome.tsx +90 -0
- package/templates/react-router/package.json +34 -0
- package/templates/react-router/public/favicon.ico +0 -0
- package/templates/react-router/react-router.config.ts +8 -0
- package/templates/react-router/tsconfig.cloudflare.json +27 -0
- package/templates/react-router/tsconfig.json +17 -0
- package/templates/react-router/tsconfig.node.json +20 -0
- package/templates/react-router/types/env.d.ts +16 -0
- package/templates/react-router/vite.config.ts +14 -0
- package/templates/react-router/workers/app.ts +23 -0
- package/templates/react-router/wrangler.jsonc +6 -0
- package/templates/rwsdk/.cursor/rules/rwsdk_rwsdk-interruptors.mdc +247 -0
- package/templates/rwsdk/.cursor/rules/rwsdk_rwsdk-middleware.mdc +75 -0
- package/templates/rwsdk/.cursor/rules/rwsdk_rwsdk-react.mdc +110 -0
- package/templates/rwsdk/.cursor/rules/rwsdk_rwsdk-request-response.mdc +213 -0
- package/templates/rwsdk/.devcontainer/Dockerfile +8 -0
- package/templates/rwsdk/.devcontainer/devcontainer.json +21 -0
- package/templates/rwsdk/README.md +62 -0
- package/templates/rwsdk/_env +5 -0
- package/templates/rwsdk/_env.example +5 -0
- package/templates/rwsdk/_gitignore +71 -0
- package/templates/rwsdk/alchemy.run.ts +29 -0
- package/templates/rwsdk/package.json +61 -0
- package/templates/rwsdk/prisma/schema.prisma +43 -0
- package/templates/rwsdk/src/app/Document.tsx +16 -0
- package/templates/rwsdk/src/app/headers.ts +32 -0
- package/templates/rwsdk/src/app/pages/Home.tsx +13 -0
- package/templates/rwsdk/src/app/pages/user/Login.tsx +79 -0
- package/templates/rwsdk/src/app/pages/user/functions.ts +171 -0
- package/templates/rwsdk/src/app/pages/user/routes.ts +17 -0
- package/templates/rwsdk/src/app/shared/links.ts +3 -0
- package/templates/rwsdk/src/client.tsx +3 -0
- package/templates/rwsdk/src/db.ts +26 -0
- package/templates/rwsdk/src/scripts/seed.ts +21 -0
- package/templates/rwsdk/src/session/durableObject.ts +63 -0
- package/templates/rwsdk/src/session/store.ts +13 -0
- package/templates/rwsdk/src/worker.tsx +63 -0
- package/templates/rwsdk/tsconfig.json +47 -0
- package/templates/rwsdk/types/env.d.ts +16 -0
- package/templates/rwsdk/types/rw.d.ts +5 -0
- package/templates/rwsdk/types/vite.d.ts +4 -0
- package/templates/rwsdk/vite.config.mts +6 -0
- package/templates/rwsdk/wrangler.jsonc +28 -0
- package/templates/sveltekit/README.md +38 -0
- package/templates/sveltekit/_env +1 -0
- package/templates/sveltekit/_env.example +1 -0
- package/templates/sveltekit/_gitignore +26 -0
- package/templates/sveltekit/_npmrc +1 -0
- package/templates/sveltekit/_prettierrc +3 -0
- package/templates/sveltekit/alchemy.run.ts +16 -0
- package/templates/sveltekit/bun.lock +1099 -0
- package/templates/sveltekit/package.json +33 -0
- package/templates/sveltekit/src/app.css +1 -0
- package/templates/sveltekit/src/app.d.ts +13 -0
- package/templates/sveltekit/src/app.html +12 -0
- package/templates/sveltekit/src/routes/+layout.svelte +7 -0
- package/templates/sveltekit/src/routes/+page.svelte +2 -0
- package/templates/sveltekit/static/favicon.png +0 -0
- package/templates/sveltekit/svelte.config.js +12 -0
- package/templates/sveltekit/tsconfig.json +27 -0
- package/templates/sveltekit/types/env.d.ts +16 -0
- package/templates/sveltekit/vite.config.ts +6 -0
- package/templates/sveltekit/wrangler.jsonc +7 -0
- package/templates/tanstack-start/.vscode/settings.json +11 -0
- package/templates/tanstack-start/README.md +72 -0
- package/templates/tanstack-start/_env +1 -0
- package/templates/tanstack-start/_env.example +1 -0
- package/templates/tanstack-start/_gitignore +22 -0
- package/templates/tanstack-start/_prettierignore +4 -0
- package/templates/tanstack-start/alchemy.run.ts +16 -0
- package/templates/tanstack-start/package.json +37 -0
- package/templates/tanstack-start/public/android-chrome-192x192.png +0 -0
- package/templates/tanstack-start/public/android-chrome-512x512.png +0 -0
- package/templates/tanstack-start/public/apple-touch-icon.png +0 -0
- package/templates/tanstack-start/public/favicon-16x16.png +0 -0
- package/templates/tanstack-start/public/favicon-32x32.png +0 -0
- package/templates/tanstack-start/public/favicon.ico +0 -0
- package/templates/tanstack-start/public/favicon.png +0 -0
- package/templates/tanstack-start/public/site.webmanifest +19 -0
- package/templates/tanstack-start/src/components/DefaultCatchBoundary.tsx +53 -0
- package/templates/tanstack-start/src/components/NotFound.tsx +25 -0
- package/templates/tanstack-start/src/components/PostError.tsx +5 -0
- package/templates/tanstack-start/src/components/UserError.tsx +5 -0
- package/templates/tanstack-start/src/router.tsx +22 -0
- package/templates/tanstack-start/src/routes/__root.tsx +146 -0
- package/templates/tanstack-start/src/routes/_pathlessLayout/_nested-layout/route-a.tsx +10 -0
- package/templates/tanstack-start/src/routes/_pathlessLayout/_nested-layout/route-b.tsx +10 -0
- package/templates/tanstack-start/src/routes/_pathlessLayout/_nested-layout.tsx +34 -0
- package/templates/tanstack-start/src/routes/_pathlessLayout.tsx +16 -0
- package/templates/tanstack-start/src/routes/api/users.$userId.ts +28 -0
- package/templates/tanstack-start/src/routes/api/users.ts +59 -0
- package/templates/tanstack-start/src/routes/customScript[.]js.ts +10 -0
- package/templates/tanstack-start/src/routes/deferred.tsx +62 -0
- package/templates/tanstack-start/src/routes/index.tsx +12 -0
- package/templates/tanstack-start/src/routes/posts.$postId.tsx +34 -0
- package/templates/tanstack-start/src/routes/posts.index.tsx +8 -0
- package/templates/tanstack-start/src/routes/posts.tsx +38 -0
- package/templates/tanstack-start/src/routes/posts_.$postId.deep.tsx +29 -0
- package/templates/tanstack-start/src/routes/redirect.tsx +9 -0
- package/templates/tanstack-start/src/routes/users.$userId.tsx +44 -0
- package/templates/tanstack-start/src/routes/users.index.tsx +18 -0
- package/templates/tanstack-start/src/routes/users.tsx +49 -0
- package/templates/tanstack-start/src/styles/app.css +12 -0
- package/templates/tanstack-start/src/utils/loggingMiddleware.tsx +41 -0
- package/templates/tanstack-start/src/utils/posts.tsx +40 -0
- package/templates/tanstack-start/src/utils/seo.ts +33 -0
- package/templates/tanstack-start/src/utils/users.tsx +5 -0
- package/templates/tanstack-start/tsconfig.json +31 -0
- package/templates/tanstack-start/types/env.d.ts +16 -0
- package/templates/tanstack-start/vite.config.ts +33 -0
- package/templates/tanstack-start/wrangler.jsonc +7 -0
- package/templates/typescript/README.md +15 -0
- package/templates/typescript/_gitignore +37 -0
- package/templates/typescript/alchemy.run.ts +15 -0
- package/templates/typescript/index.ts +1 -0
- package/templates/typescript/package.json +18 -0
- package/templates/typescript/src/worker.js +5 -0
- package/templates/typescript/src/worker.ts +7 -0
- package/templates/typescript/tsconfig.json +21 -0
- package/templates/typescript/types/env.d.ts +16 -0
- package/templates/vite/README.md +54 -0
- package/templates/vite/_env.example +1 -0
- package/templates/vite/_gitignore +27 -0
- package/templates/vite/alchemy.run.ts +17 -0
- package/templates/vite/eslint.config.js +28 -0
- package/templates/vite/index.html +13 -0
- package/templates/vite/package.json +35 -0
- package/templates/vite/public/vite.svg +1 -0
- package/templates/vite/src/App.css +42 -0
- package/templates/vite/src/App.tsx +35 -0
- package/templates/vite/src/assets/react.svg +1 -0
- package/templates/vite/src/index.css +68 -0
- package/templates/vite/src/main.tsx +10 -0
- package/templates/vite/src/vite-env.d.ts +1 -0
- package/templates/vite/tsconfig.json +34 -0
- package/templates/vite/types/env.d.ts +16 -0
- package/templates/vite/vite.config.ts +9 -0
- package/templates/vite/worker/index.ts +12 -0
- package/templates/vite/wrangler.jsonc +6 -0
- package/bin/create-alchemy.ts +0 -1220
package/bin/create-alchemy.ts
DELETED
|
@@ -1,1220 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { confirm, input, select } from "@inquirer/prompts";
|
|
4
|
-
import { applyEdits, modify } from "jsonc-parser";
|
|
5
|
-
import { execSync } from "node:child_process";
|
|
6
|
-
import { existsSync } from "node:fs";
|
|
7
|
-
import * as fs from "node:fs/promises";
|
|
8
|
-
import { join, resolve } from "node:path";
|
|
9
|
-
|
|
10
|
-
const isTest = process.env.NODE_ENV === "test";
|
|
11
|
-
|
|
12
|
-
// Package manager detection
|
|
13
|
-
type PackageManager = "bun" | "npm" | "pnpm" | "yarn";
|
|
14
|
-
|
|
15
|
-
// CLI options interface
|
|
16
|
-
interface CliOptions {
|
|
17
|
-
name?: string;
|
|
18
|
-
template?: string;
|
|
19
|
-
yes?: boolean;
|
|
20
|
-
overwrite?: boolean;
|
|
21
|
-
help?: boolean;
|
|
22
|
-
version?: boolean;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Mutable state that will be initialised inside `createAlchemy()` and
|
|
26
|
-
// reused by the helper utilities defined later in the file. Keeping these
|
|
27
|
-
// at module scope preserves the existing references inside helper
|
|
28
|
-
// functions without requiring any further changes.
|
|
29
|
-
let options: CliOptions = { yes: isTest };
|
|
30
|
-
let pm: PackageManager;
|
|
31
|
-
let alchemyVersion: string;
|
|
32
|
-
let projectPath: string;
|
|
33
|
-
let projectName: string;
|
|
34
|
-
let template: string;
|
|
35
|
-
|
|
36
|
-
// Define templates
|
|
37
|
-
const templates: Template[] = [
|
|
38
|
-
{
|
|
39
|
-
name: "typescript",
|
|
40
|
-
description: "Basic TypeScript Worker project",
|
|
41
|
-
init: initTypescriptProject,
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
name: "vite",
|
|
45
|
-
description: "React Vite.js application",
|
|
46
|
-
init: initViteProject,
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
name: "astro",
|
|
50
|
-
description: "Astro application with SSR",
|
|
51
|
-
init: initAstroProject,
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
name: "react-router",
|
|
55
|
-
description: "React Router application",
|
|
56
|
-
init: initReactRouterProject,
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
name: "sveltekit",
|
|
60
|
-
description: "SvelteKit application",
|
|
61
|
-
init: initSvelteKitProject,
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
name: "tanstack-start",
|
|
65
|
-
description: "TanStack Start application",
|
|
66
|
-
init: initTanstackStartProject,
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
name: "rwsdk",
|
|
70
|
-
description: "Redwood SDK application",
|
|
71
|
-
init: initRedwoodProject,
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
name: "nuxt",
|
|
75
|
-
description: "Nuxt.js application",
|
|
76
|
-
init: initNuxtProject,
|
|
77
|
-
},
|
|
78
|
-
];
|
|
79
|
-
|
|
80
|
-
// -------------------------------------------------------------------------------------------------
|
|
81
|
-
// Public API – this is invoked by the parent `alchemy` CLI wrapper. It reproduces the original
|
|
82
|
-
// behaviour but relies on the caller to provide the already-parsed command-line options.
|
|
83
|
-
// -------------------------------------------------------------------------------------------------
|
|
84
|
-
|
|
85
|
-
export async function createAlchemy(
|
|
86
|
-
cliOptions: Partial<CliOptions> = {},
|
|
87
|
-
): Promise<void> {
|
|
88
|
-
// Merge with defaults so helper functions can keep referencing the global `options`
|
|
89
|
-
options = { yes: isTest, ...cliOptions };
|
|
90
|
-
|
|
91
|
-
// Handle help / version flags early
|
|
92
|
-
if (options.help) {
|
|
93
|
-
console.log(`
|
|
94
|
-
Usage: alchemy create <project-name> [options]
|
|
95
|
-
|
|
96
|
-
Options:
|
|
97
|
-
-h, --help Show help
|
|
98
|
-
-v, --version Show version
|
|
99
|
-
<project-name> Project name (positional / non-interactive)
|
|
100
|
-
--template=<name> Template name (non-interactive)
|
|
101
|
-
-y, --yes Skip confirmations (non-interactive)
|
|
102
|
-
--overwrite Overwrite existing directory
|
|
103
|
-
|
|
104
|
-
Templates:
|
|
105
|
-
${templates.map((t) => ` ${t.name.padEnd(15)} ${t.description}`).join("\n")}
|
|
106
|
-
`);
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (options.version) {
|
|
111
|
-
console.log("0.28.0");
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
console.log("🧪 Welcome to Alchemy!");
|
|
116
|
-
console.log("Creating a new Alchemy project...\n");
|
|
117
|
-
|
|
118
|
-
pm = detectPackageManager();
|
|
119
|
-
console.log(`Detected package manager: ${pm}\n`);
|
|
120
|
-
|
|
121
|
-
// Acquire project name – prompt if not provided
|
|
122
|
-
if (options.name) {
|
|
123
|
-
projectName = options.name;
|
|
124
|
-
console.log(`Using project name: ${projectName}`);
|
|
125
|
-
} else {
|
|
126
|
-
projectName = await input({
|
|
127
|
-
message: "What is your project name?",
|
|
128
|
-
default: "my-alchemy-app",
|
|
129
|
-
validate: (input) => {
|
|
130
|
-
if (!input.trim()) return "Project name is required";
|
|
131
|
-
if (!/^[a-z0-9-_]+$/i.test(input))
|
|
132
|
-
return "Project name can only contain letters, numbers, hyphens, and underscores";
|
|
133
|
-
return true;
|
|
134
|
-
},
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Validate project name (even if provided non-interactively)
|
|
139
|
-
if (!projectName.trim()) {
|
|
140
|
-
throw new Error("Project name is required");
|
|
141
|
-
}
|
|
142
|
-
if (!/^[a-z0-9-_]+$/i.test(projectName)) {
|
|
143
|
-
throw new Error(
|
|
144
|
-
"Project name can only contain letters, numbers, hyphens, and underscores",
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Select template – prompt if not provided
|
|
149
|
-
if (options.template) {
|
|
150
|
-
template = options.template;
|
|
151
|
-
console.log(`Using template: ${template}`);
|
|
152
|
-
|
|
153
|
-
if (!templates.find((t) => t.name === template)) {
|
|
154
|
-
throw new Error(
|
|
155
|
-
`Template '${template}' not found. Available templates: ${templates.map((t) => t.name).join(", ")}`,
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
} else {
|
|
159
|
-
template = await select({
|
|
160
|
-
message: "Which template would you like to use?",
|
|
161
|
-
choices: templates.map((t) => ({
|
|
162
|
-
name: t.description,
|
|
163
|
-
value: t.name,
|
|
164
|
-
})),
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const selectedTemplate = templates.find((t) => t.name === template)!;
|
|
169
|
-
|
|
170
|
-
// Prepare working directory
|
|
171
|
-
projectPath = resolve(process.cwd(), projectName);
|
|
172
|
-
|
|
173
|
-
if (existsSync(projectPath)) {
|
|
174
|
-
let overwriteConfirmed: boolean;
|
|
175
|
-
if (options.overwrite || options.yes) {
|
|
176
|
-
overwriteConfirmed = true;
|
|
177
|
-
console.log(
|
|
178
|
-
`Directory ${projectName} already exists. Overwriting due to CLI flag.`,
|
|
179
|
-
);
|
|
180
|
-
} else {
|
|
181
|
-
overwriteConfirmed = await confirm({
|
|
182
|
-
message: `Directory ${projectName} already exists. Overwrite?`,
|
|
183
|
-
default: false,
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (!overwriteConfirmed) {
|
|
188
|
-
console.log("Cancelled.");
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
console.log(`\n🔨 Creating ${template} project in ${projectPath}...`);
|
|
194
|
-
|
|
195
|
-
alchemyVersion = `alchemy${isTest ? "@file:../../alchemy" : ""}`;
|
|
196
|
-
|
|
197
|
-
// Execute the template initialisation
|
|
198
|
-
await selectedTemplate.init(projectName, projectPath);
|
|
199
|
-
|
|
200
|
-
// Ensure a .gitignore exists
|
|
201
|
-
const gitignorePath = join(projectPath, ".gitignore");
|
|
202
|
-
if (!existsSync(gitignorePath)) {
|
|
203
|
-
await fs.writeFile(
|
|
204
|
-
gitignorePath,
|
|
205
|
-
"node_modules/\n.env\n.env.local\ndist/\nlib/\n.wrangler/\nwrangler.jsonc\n*.tsbuildinfo\n",
|
|
206
|
-
);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
console.log(`\n✅ Project ${projectName} created successfully!`);
|
|
210
|
-
console.log("\n📁 Navigate to your project:");
|
|
211
|
-
console.log(` cd ${projectName}`);
|
|
212
|
-
console.log("\n🚀 Deploy your project:");
|
|
213
|
-
console.log(` ${pm} run deploy`);
|
|
214
|
-
console.log("\n🧹 Destroy your project:");
|
|
215
|
-
console.log(` ${pm} run destroy`);
|
|
216
|
-
console.log("\n📚 Learn more: https://alchemy.run");
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Template definitions
|
|
220
|
-
interface Template {
|
|
221
|
-
name: string;
|
|
222
|
-
description: string;
|
|
223
|
-
init: (projectName: string, projectPath: string) => Promise<void>;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
async function initTypescriptProject(
|
|
227
|
-
projectName: string,
|
|
228
|
-
projectPath: string,
|
|
229
|
-
): Promise<void> {
|
|
230
|
-
await mkdir(projectPath);
|
|
231
|
-
|
|
232
|
-
const commands = getPackageManagerCommands(pm);
|
|
233
|
-
|
|
234
|
-
// Initialize project
|
|
235
|
-
execCommand(commands.init, projectPath);
|
|
236
|
-
|
|
237
|
-
await createEnvTs(projectPath);
|
|
238
|
-
await initWranglerRunTs(projectPath, {
|
|
239
|
-
entrypoint: "src/worker.ts",
|
|
240
|
-
});
|
|
241
|
-
await appendGitignore(projectPath);
|
|
242
|
-
|
|
243
|
-
// Create basic project structure
|
|
244
|
-
await mkdir(projectPath, "src");
|
|
245
|
-
|
|
246
|
-
// Create worker.ts
|
|
247
|
-
await fs.writeFile(
|
|
248
|
-
join(projectPath, "src", "worker.ts"),
|
|
249
|
-
`import type { worker } from "../alchemy.run.ts";
|
|
250
|
-
|
|
251
|
-
export default {
|
|
252
|
-
async fetch(request: Request, env: typeof worker.Env, ctx: ExecutionContext): Promise<Response> {
|
|
253
|
-
return new Response("Hello World from ${projectName}!");
|
|
254
|
-
},
|
|
255
|
-
};
|
|
256
|
-
`,
|
|
257
|
-
);
|
|
258
|
-
|
|
259
|
-
// Create tsconfig.json
|
|
260
|
-
await writeJsonFile(join(projectPath, "tsconfig.json"), {
|
|
261
|
-
compilerOptions: {
|
|
262
|
-
target: "ESNext",
|
|
263
|
-
module: "ESNext",
|
|
264
|
-
moduleResolution: "Bundler",
|
|
265
|
-
strict: true,
|
|
266
|
-
esModuleInterop: true,
|
|
267
|
-
skipLibCheck: true,
|
|
268
|
-
allowImportingTsExtensions: true,
|
|
269
|
-
rewriteRelativeImportExtensions: true,
|
|
270
|
-
types: ["@cloudflare/workers-types", "@types/node"],
|
|
271
|
-
},
|
|
272
|
-
include: ["src/**/*", "types/**/*", "alchemy.run.ts"],
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
await writeJsonFile(join(projectPath, "package.json"), {
|
|
276
|
-
name: projectName,
|
|
277
|
-
version: "0.0.0",
|
|
278
|
-
description: "Alchemy Typescript Project",
|
|
279
|
-
type: "module",
|
|
280
|
-
scripts: {
|
|
281
|
-
build: "tsc -b",
|
|
282
|
-
deploy: "tsx ./alchemy.run.ts",
|
|
283
|
-
destroy: "tsx ./alchemy.run.ts --destroy",
|
|
284
|
-
},
|
|
285
|
-
devDependencies: {
|
|
286
|
-
"@cloudflare/workers-types": "latest",
|
|
287
|
-
"@types/node": "^24.0.1",
|
|
288
|
-
alchemy: "^0.28.0",
|
|
289
|
-
typescript: "^5.8.3",
|
|
290
|
-
},
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
// Install dependencies
|
|
294
|
-
install({
|
|
295
|
-
devDependencies: [
|
|
296
|
-
alchemyVersion,
|
|
297
|
-
"@cloudflare/workers-types",
|
|
298
|
-
"@types/node",
|
|
299
|
-
"typescript",
|
|
300
|
-
],
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
async function initViteProject(
|
|
305
|
-
projectName: string,
|
|
306
|
-
projectPath: string,
|
|
307
|
-
): Promise<void> {
|
|
308
|
-
npx(`create-vite@6.5.0 ${projectName} --template react-ts`);
|
|
309
|
-
const root = projectPath;
|
|
310
|
-
await rm(join(root, "tsconfig.app.json"));
|
|
311
|
-
await rm(join(root, "tsconfig.node.json"));
|
|
312
|
-
|
|
313
|
-
await initWebsiteProject(projectPath, {
|
|
314
|
-
entrypoint: "worker/index.ts",
|
|
315
|
-
devDependencies: ["@cloudflare/vite-plugin"],
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
await fs.writeFile(
|
|
319
|
-
join(root, "vite.config.ts"),
|
|
320
|
-
`import { defineConfig } from 'vite'
|
|
321
|
-
import react from '@vitejs/plugin-react'
|
|
322
|
-
|
|
323
|
-
import { cloudflare } from "@cloudflare/vite-plugin";
|
|
324
|
-
|
|
325
|
-
// https://vite.dev/config/
|
|
326
|
-
export default defineConfig({
|
|
327
|
-
plugins: [react(), cloudflare()],
|
|
328
|
-
});
|
|
329
|
-
`,
|
|
330
|
-
);
|
|
331
|
-
await writeJsonFile(join(root, "tsconfig.json"), {
|
|
332
|
-
exclude: ["test"],
|
|
333
|
-
include: ["types/**/*.ts", "src/**/*.ts", "alchemy.run.ts"],
|
|
334
|
-
compilerOptions: {
|
|
335
|
-
target: "es2021",
|
|
336
|
-
lib: ["es2021"],
|
|
337
|
-
jsx: "react-jsx",
|
|
338
|
-
module: "es2022",
|
|
339
|
-
moduleResolution: "Bundler",
|
|
340
|
-
resolveJsonModule: true,
|
|
341
|
-
allowJs: true,
|
|
342
|
-
checkJs: false,
|
|
343
|
-
noEmit: true,
|
|
344
|
-
isolatedModules: true,
|
|
345
|
-
allowSyntheticDefaultImports: true,
|
|
346
|
-
forceConsistentCasingInFileNames: true,
|
|
347
|
-
allowImportingTsExtensions: true,
|
|
348
|
-
rewriteRelativeImportExtensions: true,
|
|
349
|
-
strict: true,
|
|
350
|
-
skipLibCheck: true,
|
|
351
|
-
types: ["@cloudflare/workers-types", "./types/env.d.ts"],
|
|
352
|
-
},
|
|
353
|
-
});
|
|
354
|
-
await mkdir(root, "worker");
|
|
355
|
-
await fs.writeFile(
|
|
356
|
-
join(root, "worker", "index.ts"),
|
|
357
|
-
`export default {
|
|
358
|
-
fetch(request) {
|
|
359
|
-
const url = new URL(request.url);
|
|
360
|
-
|
|
361
|
-
if (url.pathname.startsWith("/api/")) {
|
|
362
|
-
return Response.json({
|
|
363
|
-
name: "Cloudflare",
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
return new Response(null, { status: 404 });
|
|
367
|
-
},
|
|
368
|
-
} satisfies ExportedHandler<Env>;
|
|
369
|
-
`,
|
|
370
|
-
);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
async function initAstroProject(
|
|
374
|
-
projectName: string,
|
|
375
|
-
projectPath: string,
|
|
376
|
-
): Promise<void> {
|
|
377
|
-
create(
|
|
378
|
-
pm,
|
|
379
|
-
`astro@latest ${projectName} ${pm === "npm" ? "--" : ""} --no-git --no-deploy --install ${options.yes ? "--yes" : ""}`,
|
|
380
|
-
);
|
|
381
|
-
|
|
382
|
-
await initWebsiteProject(projectPath, {
|
|
383
|
-
scripts: {
|
|
384
|
-
dev: "astro dev",
|
|
385
|
-
build: "astro check && astro build",
|
|
386
|
-
},
|
|
387
|
-
devDependencies: ["@astrojs/cloudflare"],
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
// Update astro.config.mjs
|
|
391
|
-
await fs.writeFile(
|
|
392
|
-
join(projectPath, "astro.config.mjs"),
|
|
393
|
-
`import { defineConfig } from 'astro/config';
|
|
394
|
-
import cloudflare from '@astrojs/cloudflare';
|
|
395
|
-
|
|
396
|
-
// https://astro.build/config
|
|
397
|
-
export default defineConfig({
|
|
398
|
-
output: 'server',
|
|
399
|
-
adapter: cloudflare(),
|
|
400
|
-
});
|
|
401
|
-
`,
|
|
402
|
-
);
|
|
403
|
-
|
|
404
|
-
// Create API route example
|
|
405
|
-
await mkdir(projectPath, "src", "pages", "api");
|
|
406
|
-
await fs.writeFile(
|
|
407
|
-
join(projectPath, "src", "pages", "api", "hello.ts"),
|
|
408
|
-
`import type { APIRoute } from 'astro';
|
|
409
|
-
|
|
410
|
-
export const GET: APIRoute = async ({ request }) => {
|
|
411
|
-
// Access Cloudflare runtime context
|
|
412
|
-
const runtime = request.cf;
|
|
413
|
-
|
|
414
|
-
return new Response(JSON.stringify({
|
|
415
|
-
message: "Hello from Astro API on Cloudflare!",
|
|
416
|
-
timestamp: new Date().toISOString(),
|
|
417
|
-
colo: runtime?.colo || "unknown",
|
|
418
|
-
country: runtime?.country || "unknown",
|
|
419
|
-
city: runtime?.city || "unknown",
|
|
420
|
-
}), {
|
|
421
|
-
status: 200,
|
|
422
|
-
headers: {
|
|
423
|
-
'Content-Type': 'application/json',
|
|
424
|
-
},
|
|
425
|
-
});
|
|
426
|
-
};
|
|
427
|
-
`,
|
|
428
|
-
);
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
async function initReactRouterProject(
|
|
432
|
-
projectName: string,
|
|
433
|
-
projectPath: string,
|
|
434
|
-
): Promise<void> {
|
|
435
|
-
create(
|
|
436
|
-
pm,
|
|
437
|
-
`cloudflare@2.49.3 ${projectName} ${pm === "npm" ? "--" : ""} --framework=react-router --no-git --no-deploy ${options.yes ? "--yes" : ""}`,
|
|
438
|
-
);
|
|
439
|
-
|
|
440
|
-
await initWebsiteProject(projectPath, {
|
|
441
|
-
entrypoint: "workers/app.ts",
|
|
442
|
-
devDependencies: ["@cloudflare/vite-plugin"],
|
|
443
|
-
tsconfig: "tsconfig.node.json",
|
|
444
|
-
});
|
|
445
|
-
|
|
446
|
-
await modifyTsConfig(projectPath, {
|
|
447
|
-
tsconfig: "tsconfig.node.json",
|
|
448
|
-
});
|
|
449
|
-
|
|
450
|
-
await modifyJsoncFile(join(projectPath, "tsconfig.json"), {
|
|
451
|
-
"compilerOptions.types": undefined,
|
|
452
|
-
"compilerOptions.noEmit": undefined,
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
await fs.writeFile(
|
|
456
|
-
join(projectPath, "vite.config.ts"),
|
|
457
|
-
`import { reactRouter } from "@react-router/dev/vite";
|
|
458
|
-
import { cloudflare } from "@cloudflare/vite-plugin";
|
|
459
|
-
import tailwindcss from "@tailwindcss/vite";
|
|
460
|
-
import { defineConfig } from "vite";
|
|
461
|
-
import tsconfigPaths from "vite-tsconfig-paths";
|
|
462
|
-
|
|
463
|
-
export default defineConfig({
|
|
464
|
-
plugins: [
|
|
465
|
-
cloudflare({ viteEnvironment: { name: "ssr" } }),
|
|
466
|
-
tailwindcss(),
|
|
467
|
-
reactRouter(),
|
|
468
|
-
tsconfigPaths(),
|
|
469
|
-
],
|
|
470
|
-
});
|
|
471
|
-
`,
|
|
472
|
-
);
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
async function initSvelteKitProject(
|
|
476
|
-
projectName: string,
|
|
477
|
-
projectPath: string,
|
|
478
|
-
): Promise<void> {
|
|
479
|
-
npx(
|
|
480
|
-
`-y sv@latest create --install=${pm} --types=ts ${options.yes ? "--template minimal --no-add-ons" : ""} ${projectName}`,
|
|
481
|
-
);
|
|
482
|
-
|
|
483
|
-
await initWebsiteProject(projectPath, {
|
|
484
|
-
// entrypoint: "src/routes/index.svelte",
|
|
485
|
-
});
|
|
486
|
-
|
|
487
|
-
install({
|
|
488
|
-
devDependencies: [
|
|
489
|
-
"@sveltejs/adapter-cloudflare",
|
|
490
|
-
"@sveltejs/vite-plugin-svelte",
|
|
491
|
-
],
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
// Update svelte.config.js
|
|
495
|
-
await fs.writeFile(
|
|
496
|
-
join(projectPath, "svelte.config.js"),
|
|
497
|
-
`import adapter from '@sveltejs/adapter-cloudflare';
|
|
498
|
-
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
|
499
|
-
|
|
500
|
-
/** @type {import('@sveltejs/kit').Config} */
|
|
501
|
-
const config = {
|
|
502
|
-
preprocess: vitePreprocess(),
|
|
503
|
-
kit: {
|
|
504
|
-
adapter: adapter()
|
|
505
|
-
}
|
|
506
|
-
};
|
|
507
|
-
|
|
508
|
-
export default config;
|
|
509
|
-
`,
|
|
510
|
-
);
|
|
511
|
-
|
|
512
|
-
// Create vite.config.ts
|
|
513
|
-
await fs.writeFile(
|
|
514
|
-
join(projectPath, "vite.config.ts"),
|
|
515
|
-
`import { sveltekit } from '@sveltejs/kit/vite';
|
|
516
|
-
import { defineConfig } from 'vite';
|
|
517
|
-
|
|
518
|
-
export default defineConfig({
|
|
519
|
-
\tplugins: [sveltekit()],
|
|
520
|
-
});
|
|
521
|
-
`,
|
|
522
|
-
);
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
async function initRedwoodProject(): Promise<void> {
|
|
526
|
-
npx(`-y create-rwsdk@latest ${projectName}`);
|
|
527
|
-
install();
|
|
528
|
-
execCommand(`${getPackageManagerCommands(pm).run} dev:init`, projectPath);
|
|
529
|
-
await initWebsiteProject(projectPath, {
|
|
530
|
-
scripts: {
|
|
531
|
-
deploy: "tsx --env-file .env ./alchemy.run.ts",
|
|
532
|
-
destroy: "tsx --env-file .env ./alchemy.run.ts --destroy",
|
|
533
|
-
},
|
|
534
|
-
});
|
|
535
|
-
await modifyJsoncFile(join(projectPath, "tsconfig.json"), {
|
|
536
|
-
include: undefined,
|
|
537
|
-
"compilerOptions.types": ["@cloudflare/workers-types", "types/**/*.ts"],
|
|
538
|
-
});
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
async function initTanstackStartProject(
|
|
542
|
-
projectName: string,
|
|
543
|
-
projectPath: string,
|
|
544
|
-
): Promise<void> {
|
|
545
|
-
npx(
|
|
546
|
-
`gitpick TanStack/router/tree/main/examples/react/start-basic ${projectName}`,
|
|
547
|
-
);
|
|
548
|
-
|
|
549
|
-
await initWebsiteProject(projectPath);
|
|
550
|
-
|
|
551
|
-
await Promise.all([
|
|
552
|
-
rm(join(projectPath, "postcss.config.mjs")),
|
|
553
|
-
rm(join(projectPath, "tailwind.config.mjs")),
|
|
554
|
-
]);
|
|
555
|
-
|
|
556
|
-
install({
|
|
557
|
-
dependencies: ["tailwind-merge@3"],
|
|
558
|
-
devDependencies: ["tailwindcss@4", "@tailwindcss/vite@latest", "postcss"],
|
|
559
|
-
});
|
|
560
|
-
|
|
561
|
-
await Promise.all([
|
|
562
|
-
fs.writeFile(
|
|
563
|
-
join(projectPath, "vite.config.ts"),
|
|
564
|
-
`import tailwindcss from "@tailwindcss/vite";
|
|
565
|
-
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
|
|
566
|
-
import { cloudflareWorkersDevEnvironmentShim } from "alchemy/cloudflare";
|
|
567
|
-
import { defineConfig, PluginOption } from "vite";
|
|
568
|
-
import tsConfigPaths from "vite-tsconfig-paths";
|
|
569
|
-
|
|
570
|
-
export default defineConfig({
|
|
571
|
-
server: {
|
|
572
|
-
port: 3000,
|
|
573
|
-
},
|
|
574
|
-
build: {
|
|
575
|
-
target: "esnext",
|
|
576
|
-
rollupOptions: {
|
|
577
|
-
external: ["node:async_hooks", "cloudflare:workers"],
|
|
578
|
-
},
|
|
579
|
-
},
|
|
580
|
-
plugins: [
|
|
581
|
-
tailwindcss() as PluginOption,
|
|
582
|
-
cloudflareWorkersDevEnvironmentShim(),
|
|
583
|
-
tsConfigPaths({
|
|
584
|
-
projects: ["./tsconfig.json"],
|
|
585
|
-
}),
|
|
586
|
-
tanstackStart({
|
|
587
|
-
target: "cloudflare-module",
|
|
588
|
-
tsr: {
|
|
589
|
-
routeTreeFileHeader: [
|
|
590
|
-
"/** biome-ignore-all lint/suspicious/noExplicitAny: code generated by @tanstack/react-start */",
|
|
591
|
-
],
|
|
592
|
-
quoteStyle: "double",
|
|
593
|
-
},
|
|
594
|
-
}),
|
|
595
|
-
],
|
|
596
|
-
});
|
|
597
|
-
`,
|
|
598
|
-
),
|
|
599
|
-
|
|
600
|
-
fs.writeFile(
|
|
601
|
-
join(projectPath, "src", "styles", "app.css"),
|
|
602
|
-
`@import "tailwindcss";
|
|
603
|
-
|
|
604
|
-
:root {
|
|
605
|
-
--border: var(--color-zinc-200);
|
|
606
|
-
--popover: var(--color-white);
|
|
607
|
-
--popover-foreground: var(--color-zinc-950);
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
@theme {
|
|
611
|
-
--font-sans: var(--font-sans, Inter), ui-sans-serif, system-ui, sans-serif,
|
|
612
|
-
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
|
613
|
-
}
|
|
614
|
-
`,
|
|
615
|
-
),
|
|
616
|
-
]);
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
async function initNuxtProject(): Promise<void> {
|
|
620
|
-
// create(
|
|
621
|
-
// `cloudflare@latest -- ${projectName} --framework=nuxt --no-git --no-deploy`,
|
|
622
|
-
// );
|
|
623
|
-
npx(
|
|
624
|
-
`nuxi@3.25.1 init nuxt --packageManager npm --no-install --no-gitInit ${isTest ? "--template=ui -M @nuxt/ui" : ""}`,
|
|
625
|
-
);
|
|
626
|
-
// npx nuxi@3.25.1 init nuxt --packageManager npm --no-install --no-gitInit
|
|
627
|
-
/*
|
|
628
|
-
-M, --modules Nuxt modules to install (comma separated without spaces)
|
|
629
|
-
|
|
630
|
-
◻ @nuxt/content – The file-based CMS with support for Markdown, YAML, JSON
|
|
631
|
-
◻ @nuxt/eslint – Project-aware, easy-to-use, extensible and future-proof ESLint integration
|
|
632
|
-
◻ @nuxt/fonts – Add custom web fonts with performance in mind
|
|
633
|
-
◻ @nuxt/icon – Icon module for Nuxt with 200,000+ ready to use icons from Iconify
|
|
634
|
-
◻ @nuxt/image – Add images with progressive processing, lazy-loading, resizing and providers
|
|
635
|
-
support
|
|
636
|
-
◻ @nuxt/scripts – Add 3rd-party scripts without sacrificing performance
|
|
637
|
-
◻ @nuxt/test-utils – Test utilities for Nuxt
|
|
638
|
-
◻ @nuxt/ui – The Intuitive UI Library powered by Reka UI and Tailwind CSS
|
|
639
|
-
*/
|
|
640
|
-
|
|
641
|
-
await initWebsiteProject(projectPath, {
|
|
642
|
-
scripts: {
|
|
643
|
-
build: "nuxt build",
|
|
644
|
-
},
|
|
645
|
-
include: ["server/**/*.ts"],
|
|
646
|
-
});
|
|
647
|
-
|
|
648
|
-
await fs.writeFile(
|
|
649
|
-
join(projectPath, "nuxt.config.ts"),
|
|
650
|
-
`// https://nuxt.com/docs/api/configuration/nuxt-config
|
|
651
|
-
export default defineNuxtConfig({
|
|
652
|
-
compatibilityDate: '2025-05-15',
|
|
653
|
-
devtools: { enabled: true },
|
|
654
|
-
nitro: {
|
|
655
|
-
preset: "cloudflare_module",
|
|
656
|
-
cloudflare: {
|
|
657
|
-
deployConfig: true,
|
|
658
|
-
nodeCompat: true
|
|
659
|
-
}
|
|
660
|
-
},
|
|
661
|
-
modules: ["nitro-cloudflare-dev"],
|
|
662
|
-
});
|
|
663
|
-
`,
|
|
664
|
-
);
|
|
665
|
-
|
|
666
|
-
install({
|
|
667
|
-
devDependencies: ["nitro-cloudflare-dev"],
|
|
668
|
-
});
|
|
669
|
-
|
|
670
|
-
install();
|
|
671
|
-
|
|
672
|
-
await mkdir(projectPath, "server", "api");
|
|
673
|
-
await fs.writeFile(
|
|
674
|
-
join(projectPath, "server", "api", "hello.ts"),
|
|
675
|
-
`// see: https://nuxt.com/docs/guide/directory-structure/server
|
|
676
|
-
|
|
677
|
-
export default defineEventHandler((event) => {
|
|
678
|
-
return {
|
|
679
|
-
hello: "world",
|
|
680
|
-
};
|
|
681
|
-
});
|
|
682
|
-
`,
|
|
683
|
-
);
|
|
684
|
-
|
|
685
|
-
await mkdir(projectPath, "server", "middleware");
|
|
686
|
-
await fs.writeFile(
|
|
687
|
-
join(projectPath, "server", "middleware", "hello.ts"),
|
|
688
|
-
`// see: https://nuxt.com/docs/guide/directory-structure/server#server-middleware
|
|
689
|
-
|
|
690
|
-
export default defineEventHandler((event) => {
|
|
691
|
-
console.log('New request: ' + getRequestURL(event))
|
|
692
|
-
})
|
|
693
|
-
`,
|
|
694
|
-
);
|
|
695
|
-
await fs.writeFile(
|
|
696
|
-
join(projectPath, "server", "middleware", "auth.ts"),
|
|
697
|
-
`// see: https://nuxt.com/docs/guide/directory-structure/server#server-middleware
|
|
698
|
-
|
|
699
|
-
export default defineEventHandler((event) => {
|
|
700
|
-
event.context.auth = { user: 123 }
|
|
701
|
-
});
|
|
702
|
-
`,
|
|
703
|
-
);
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
interface WebsiteOptions {
|
|
707
|
-
entrypoint?: string;
|
|
708
|
-
tsconfig?: string;
|
|
709
|
-
scripts?: Record<string, string>;
|
|
710
|
-
include?: string[];
|
|
711
|
-
types?: string[];
|
|
712
|
-
devDependencies?: string[];
|
|
713
|
-
dependencies?: string[];
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
/**
|
|
717
|
-
* Unified initialization function for website projects that use create-cloudflare
|
|
718
|
-
*/
|
|
719
|
-
async function initWebsiteProject(
|
|
720
|
-
projectPath: string,
|
|
721
|
-
options: WebsiteOptions = {},
|
|
722
|
-
): Promise<void> {
|
|
723
|
-
await createEnvTs(projectPath);
|
|
724
|
-
await cleanupWrangler(projectPath);
|
|
725
|
-
await modifyTsConfig(projectPath, options);
|
|
726
|
-
await modifyPackageJson(projectPath, options?.scripts);
|
|
727
|
-
|
|
728
|
-
// Create alchemy.run.ts
|
|
729
|
-
await initWranglerRunTs(projectPath, options);
|
|
730
|
-
|
|
731
|
-
await appendGitignore(projectPath);
|
|
732
|
-
await appendEnv(projectPath);
|
|
733
|
-
|
|
734
|
-
install({
|
|
735
|
-
dependencies: options.dependencies,
|
|
736
|
-
devDependencies: [
|
|
737
|
-
"@cloudflare/workers-types",
|
|
738
|
-
alchemyVersion,
|
|
739
|
-
...(pm === "bun" ? [] : ["tsx"]),
|
|
740
|
-
"typescript",
|
|
741
|
-
...(options.devDependencies ?? []),
|
|
742
|
-
],
|
|
743
|
-
});
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
async function appendGitignore(projectPath: string): Promise<void> {
|
|
747
|
-
try {
|
|
748
|
-
await fs.writeFile(
|
|
749
|
-
join(projectPath, ".gitignore"),
|
|
750
|
-
[
|
|
751
|
-
await fs.readFile(join(projectPath, ".gitignore"), "utf-8"),
|
|
752
|
-
".alchemy/",
|
|
753
|
-
".env",
|
|
754
|
-
].join("\n"),
|
|
755
|
-
);
|
|
756
|
-
} catch {
|
|
757
|
-
await fs.writeFile(join(projectPath, ".gitignore"), ".alchemy/");
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
async function initWranglerRunTs(
|
|
762
|
-
projectPath: string,
|
|
763
|
-
options?: {
|
|
764
|
-
entrypoint?: string;
|
|
765
|
-
},
|
|
766
|
-
): Promise<void> {
|
|
767
|
-
// Create alchemy.run.ts
|
|
768
|
-
await fs.writeFile(
|
|
769
|
-
join(projectPath, "alchemy.run.ts"),
|
|
770
|
-
createAlchemyRunTs(projectName, options),
|
|
771
|
-
);
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
function createAlchemyRunTs(
|
|
775
|
-
projectName: string,
|
|
776
|
-
options?: {
|
|
777
|
-
entrypoint?: string;
|
|
778
|
-
},
|
|
779
|
-
): string {
|
|
780
|
-
const adopt = isTest ? "\n adopt: true," : "";
|
|
781
|
-
if (template === "typescript") {
|
|
782
|
-
return `/// <reference types="@types/node" />
|
|
783
|
-
|
|
784
|
-
import alchemy from "alchemy";
|
|
785
|
-
import { Worker } from "alchemy/cloudflare";
|
|
786
|
-
|
|
787
|
-
const app = await alchemy("${projectName}");
|
|
788
|
-
|
|
789
|
-
export const worker = await Worker("worker", {
|
|
790
|
-
name: "${projectName}",${adopt}
|
|
791
|
-
entrypoint: "${options?.entrypoint || "./src/worker.ts"}",
|
|
792
|
-
});
|
|
793
|
-
|
|
794
|
-
console.log(worker.url);
|
|
795
|
-
|
|
796
|
-
await app.finalize();
|
|
797
|
-
`;
|
|
798
|
-
} else if (template === "rwsdk") {
|
|
799
|
-
return `/// <reference types="@types/node" />
|
|
800
|
-
import alchemy from "alchemy";
|
|
801
|
-
import { D1Database, DurableObjectNamespace, Redwood } from "alchemy/cloudflare";
|
|
802
|
-
|
|
803
|
-
const app = await alchemy("${projectName}");
|
|
804
|
-
|
|
805
|
-
const database = await D1Database("database", {
|
|
806
|
-
name: "${projectName}-db",${adopt}
|
|
807
|
-
migrationsDir: "migrations",
|
|
808
|
-
});
|
|
809
|
-
|
|
810
|
-
export const worker = await Redwood("website", {
|
|
811
|
-
name: "${projectName}-website",
|
|
812
|
-
command: "${detectPackageManager()} run build",${adopt}
|
|
813
|
-
bindings: {
|
|
814
|
-
AUTH_SECRET_KEY: alchemy.secret(process.env.AUTH_SECRET_KEY),
|
|
815
|
-
DB: database,
|
|
816
|
-
SESSION_DURABLE_OBJECT: new DurableObjectNamespace("session", {
|
|
817
|
-
className: "SessionDurableObject",
|
|
818
|
-
}),
|
|
819
|
-
},
|
|
820
|
-
});
|
|
821
|
-
|
|
822
|
-
console.log({
|
|
823
|
-
url: worker.url,
|
|
824
|
-
});
|
|
825
|
-
|
|
826
|
-
await app.finalize();
|
|
827
|
-
`;
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
// Map template names to their corresponding resource names
|
|
831
|
-
const resourceMap: Record<string, string> = {
|
|
832
|
-
vite: "Vite",
|
|
833
|
-
astro: "Astro",
|
|
834
|
-
"react-router": "ReactRouter",
|
|
835
|
-
sveltekit: "SvelteKit",
|
|
836
|
-
"tanstack-start": "TanStackStart",
|
|
837
|
-
rwsdk: "Redwood",
|
|
838
|
-
nuxt: "Nuxt",
|
|
839
|
-
};
|
|
840
|
-
|
|
841
|
-
const resourceName = resourceMap[template];
|
|
842
|
-
if (!resourceName) {
|
|
843
|
-
throw new Error(`Unknown template: ${template}`);
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
// Special configuration for Vite template
|
|
847
|
-
const config =
|
|
848
|
-
options?.entrypoint !== undefined
|
|
849
|
-
? `{
|
|
850
|
-
main: "${options?.entrypoint || "./src/index.ts"}",
|
|
851
|
-
command: "${detectPackageManager()} run build",${adopt}
|
|
852
|
-
}`
|
|
853
|
-
: `{
|
|
854
|
-
command: "${detectPackageManager()} run build",${adopt}
|
|
855
|
-
}`;
|
|
856
|
-
|
|
857
|
-
return `/// <reference types="@types/node" />
|
|
858
|
-
|
|
859
|
-
import alchemy from "alchemy";
|
|
860
|
-
import { ${resourceName} } from "alchemy/cloudflare";
|
|
861
|
-
|
|
862
|
-
const app = await alchemy("${projectName}");
|
|
863
|
-
|
|
864
|
-
export const worker = await ${resourceName}("website", ${config});
|
|
865
|
-
|
|
866
|
-
console.log({
|
|
867
|
-
url: worker.url,
|
|
868
|
-
});
|
|
869
|
-
|
|
870
|
-
await app.finalize();
|
|
871
|
-
`;
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
async function tryReadFile(path: string): Promise<string | undefined> {
|
|
875
|
-
try {
|
|
876
|
-
return await fs.readFile(path, "utf-8");
|
|
877
|
-
} catch {
|
|
878
|
-
return undefined;
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
async function appendFile(path: string, content: string): Promise<void> {
|
|
883
|
-
const existingContent = await tryReadFile(path);
|
|
884
|
-
await fs.writeFile(
|
|
885
|
-
path,
|
|
886
|
-
`${existingContent ? `${existingContent}\n` : ""}${content}`,
|
|
887
|
-
);
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
async function appendEnv(projectPath: string): Promise<void> {
|
|
891
|
-
await appendFile(join(projectPath, ".env"), "ALCHEMY_PASSWORD=change-me");
|
|
892
|
-
await appendFile(
|
|
893
|
-
join(projectPath, ".env.example"),
|
|
894
|
-
"ALCHEMY_PASSWORD=change-me",
|
|
895
|
-
);
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
async function createEnvTs(
|
|
899
|
-
projectPath: string,
|
|
900
|
-
identifier = "worker",
|
|
901
|
-
): Promise<void> {
|
|
902
|
-
// Create env.d.ts for proper typing
|
|
903
|
-
await mkdir(projectPath, "types");
|
|
904
|
-
await fs.writeFile(
|
|
905
|
-
join(projectPath, "types", "env.d.ts"),
|
|
906
|
-
`// This file infers types for the cloudflare:workers environment from your Alchemy Worker.
|
|
907
|
-
// @see https://alchemy.run/docs/concepts/bindings.html#type-safe-bindings
|
|
908
|
-
|
|
909
|
-
import type { ${identifier} } from "../alchemy.run.ts";
|
|
910
|
-
|
|
911
|
-
export type CloudflareEnv = typeof ${identifier}.Env;
|
|
912
|
-
|
|
913
|
-
declare global {
|
|
914
|
-
type Env = CloudflareEnv;
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
declare module "cloudflare:workers" {
|
|
918
|
-
namespace Cloudflare {
|
|
919
|
-
export interface Env extends CloudflareEnv {}
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
`,
|
|
923
|
-
);
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
async function writeJsonFile(file: string, content: any): Promise<void> {
|
|
927
|
-
await fs.writeFile(file, JSON.stringify(content, null, 2));
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
async function cleanupWrangler(projectPath: string): Promise<void> {
|
|
931
|
-
if (existsSync(join(projectPath, "worker-configuration.d.ts"))) {
|
|
932
|
-
await fs.unlink(join(projectPath, "worker-configuration.d.ts"));
|
|
933
|
-
}
|
|
934
|
-
if (existsSync(join(projectPath, "wrangler.jsonc"))) {
|
|
935
|
-
await fs.unlink(join(projectPath, "wrangler.jsonc"));
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
|
|
939
|
-
/**
|
|
940
|
-
* Modifies a JSON/JSONC file with the given modifications
|
|
941
|
-
*/
|
|
942
|
-
async function modifyJsoncFile(
|
|
943
|
-
file: string,
|
|
944
|
-
modifications: Record<string, unknown>,
|
|
945
|
-
): Promise<void> {
|
|
946
|
-
if (!existsSync(file)) {
|
|
947
|
-
return; // No file to modify
|
|
948
|
-
}
|
|
949
|
-
|
|
950
|
-
const content = await fs.readFile(file, "utf-8");
|
|
951
|
-
let modifiedContent = content;
|
|
952
|
-
|
|
953
|
-
for (const [path, value] of Object.entries(modifications)) {
|
|
954
|
-
const pathArray = path.split(".");
|
|
955
|
-
const edits = modify(modifiedContent, pathArray, value, {
|
|
956
|
-
formattingOptions: {
|
|
957
|
-
tabSize: 2,
|
|
958
|
-
insertSpaces: true,
|
|
959
|
-
eol: "\n",
|
|
960
|
-
},
|
|
961
|
-
});
|
|
962
|
-
modifiedContent = applyEdits(modifiedContent, edits);
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
await fs.writeFile(file, modifiedContent);
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
/**
|
|
969
|
-
* Modifies tsconfig.json to set proper Cloudflare Workers types and remove worker-configuration.d.ts
|
|
970
|
-
*/
|
|
971
|
-
async function modifyTsConfig(
|
|
972
|
-
projectPath: string,
|
|
973
|
-
options: WebsiteOptions = {},
|
|
974
|
-
): Promise<void> {
|
|
975
|
-
const tsconfigPath = join(projectPath, options.tsconfig ?? "tsconfig.json");
|
|
976
|
-
|
|
977
|
-
if (!existsSync(tsconfigPath)) {
|
|
978
|
-
return; // No tsconfig.json to modify
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
const tsconfigContent = await fs.readFile(tsconfigPath, "utf-8");
|
|
982
|
-
|
|
983
|
-
// Set compilerOptions.types to ["@cloudflare/workers-types"]
|
|
984
|
-
const typesEdit = modify(
|
|
985
|
-
tsconfigContent,
|
|
986
|
-
["compilerOptions", "types"],
|
|
987
|
-
["@cloudflare/workers-types", "./types/env.d.ts", ...(options.types ?? [])],
|
|
988
|
-
{
|
|
989
|
-
formattingOptions: {
|
|
990
|
-
tabSize: 2,
|
|
991
|
-
insertSpaces: true,
|
|
992
|
-
eol: "\n",
|
|
993
|
-
},
|
|
994
|
-
},
|
|
995
|
-
);
|
|
996
|
-
|
|
997
|
-
let modifiedContent = applyEdits(tsconfigContent, typesEdit);
|
|
998
|
-
|
|
999
|
-
// Parse the JSON to get the current includes array
|
|
1000
|
-
const { parseTree, getNodeValue, findNodeAtLocation } = await import(
|
|
1001
|
-
"jsonc-parser"
|
|
1002
|
-
);
|
|
1003
|
-
const tree = parseTree(modifiedContent);
|
|
1004
|
-
const includeNode = tree ? findNodeAtLocation(tree, ["include"]) : undefined;
|
|
1005
|
-
const currentIncludes = includeNode ? getNodeValue(includeNode) : [];
|
|
1006
|
-
|
|
1007
|
-
// Filter out worker-configuration.d.ts and ensure required files are included
|
|
1008
|
-
let newIncludes = Array.isArray(currentIncludes) ? [...currentIncludes] : [];
|
|
1009
|
-
|
|
1010
|
-
// Remove worker-configuration.d.ts if it exists
|
|
1011
|
-
newIncludes = newIncludes.filter(
|
|
1012
|
-
(include) =>
|
|
1013
|
-
include !== "worker-configuration.d.ts" &&
|
|
1014
|
-
include !== "./worker-configuration.d.ts",
|
|
1015
|
-
);
|
|
1016
|
-
|
|
1017
|
-
await fs.writeFile(
|
|
1018
|
-
tsconfigPath,
|
|
1019
|
-
applyEdits(
|
|
1020
|
-
modifiedContent,
|
|
1021
|
-
modify(
|
|
1022
|
-
modifiedContent,
|
|
1023
|
-
["include"],
|
|
1024
|
-
Array.from(
|
|
1025
|
-
new Set([
|
|
1026
|
-
"alchemy.run.ts",
|
|
1027
|
-
"types/**/*.ts",
|
|
1028
|
-
...newIncludes.filter(
|
|
1029
|
-
(include) =>
|
|
1030
|
-
include !== "worker-configuration.d.ts" &&
|
|
1031
|
-
include !== "./worker-configuration.d.ts",
|
|
1032
|
-
),
|
|
1033
|
-
...(options.include ?? []),
|
|
1034
|
-
]),
|
|
1035
|
-
),
|
|
1036
|
-
{
|
|
1037
|
-
formattingOptions: {
|
|
1038
|
-
tabSize: 2,
|
|
1039
|
-
insertSpaces: true,
|
|
1040
|
-
eol: "\n",
|
|
1041
|
-
},
|
|
1042
|
-
},
|
|
1043
|
-
),
|
|
1044
|
-
),
|
|
1045
|
-
);
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
|
-
/**
|
|
1049
|
-
* Modifies package.json for website projects to add proper scripts and type: "module"
|
|
1050
|
-
*/
|
|
1051
|
-
async function modifyPackageJson(
|
|
1052
|
-
projectPath: string,
|
|
1053
|
-
scripts?: Record<string, string>,
|
|
1054
|
-
): Promise<void> {
|
|
1055
|
-
const packageJsonPath = join(projectPath, "package.json");
|
|
1056
|
-
|
|
1057
|
-
if (!existsSync(packageJsonPath)) {
|
|
1058
|
-
return; // No package.json to modify
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
const packageJson = {
|
|
1062
|
-
type: "module",
|
|
1063
|
-
...JSON.parse(await fs.readFile(packageJsonPath, "utf-8")),
|
|
1064
|
-
};
|
|
1065
|
-
|
|
1066
|
-
// Determine deploy command based on package manager
|
|
1067
|
-
const deployCommand =
|
|
1068
|
-
pm === "bun"
|
|
1069
|
-
? "bun --env-file=./.env ./alchemy.run.ts"
|
|
1070
|
-
: "tsx --env-file=./.env ./alchemy.run.ts";
|
|
1071
|
-
|
|
1072
|
-
// Add/update scripts
|
|
1073
|
-
if (!packageJson.scripts) {
|
|
1074
|
-
packageJson.scripts = {};
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
packageJson.scripts.build = scripts?.build || "vite build";
|
|
1078
|
-
packageJson.scripts.deploy = scripts?.deploy || deployCommand;
|
|
1079
|
-
packageJson.scripts.destroy =
|
|
1080
|
-
scripts?.destroy || `${deployCommand} --destroy`;
|
|
1081
|
-
|
|
1082
|
-
packageJson.scripts = {
|
|
1083
|
-
...Object.fromEntries(
|
|
1084
|
-
Object.entries(packageJson.scripts).sort(([a], [b]) =>
|
|
1085
|
-
a.localeCompare(b),
|
|
1086
|
-
),
|
|
1087
|
-
),
|
|
1088
|
-
};
|
|
1089
|
-
|
|
1090
|
-
// Write back to file with proper formatting
|
|
1091
|
-
await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
function detectPackageManager(): PackageManager {
|
|
1095
|
-
// Check npm_execpath for bun
|
|
1096
|
-
if (process.env.npm_execpath?.includes("bun")) {
|
|
1097
|
-
return "bun";
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
|
-
// Check npm_config_user_agent
|
|
1101
|
-
const userAgent = process.env.npm_config_user_agent;
|
|
1102
|
-
if (userAgent) {
|
|
1103
|
-
if (userAgent.startsWith("bun")) return "bun";
|
|
1104
|
-
if (userAgent.startsWith("pnpm")) return "pnpm";
|
|
1105
|
-
if (userAgent.startsWith("yarn")) return "yarn";
|
|
1106
|
-
if (userAgent.startsWith("npm")) return "npm";
|
|
1107
|
-
}
|
|
1108
|
-
|
|
1109
|
-
// Default fallback
|
|
1110
|
-
return "npm";
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
|
-
function getPackageManagerCommands(pm: PackageManager) {
|
|
1114
|
-
const commands = {
|
|
1115
|
-
bun: {
|
|
1116
|
-
init: "bun init -y",
|
|
1117
|
-
install: "bun install",
|
|
1118
|
-
add: "bun add",
|
|
1119
|
-
addDev: "bun add -D",
|
|
1120
|
-
run: "bun run",
|
|
1121
|
-
create: "bun create",
|
|
1122
|
-
x: "bunx",
|
|
1123
|
-
},
|
|
1124
|
-
npm: {
|
|
1125
|
-
init: "npm init -y",
|
|
1126
|
-
install: "npm install",
|
|
1127
|
-
add: "npm install",
|
|
1128
|
-
addDev: "npm install --save-dev",
|
|
1129
|
-
run: "npm run",
|
|
1130
|
-
create: "npm create",
|
|
1131
|
-
x: "npx",
|
|
1132
|
-
},
|
|
1133
|
-
pnpm: {
|
|
1134
|
-
init: "pnpm init",
|
|
1135
|
-
install: "pnpm install",
|
|
1136
|
-
add: "pnpm add",
|
|
1137
|
-
addDev: "pnpm add -D",
|
|
1138
|
-
run: "pnpm run",
|
|
1139
|
-
create: "pnpm create",
|
|
1140
|
-
x: "pnpm dlx",
|
|
1141
|
-
},
|
|
1142
|
-
yarn: {
|
|
1143
|
-
init: "yarn init -y",
|
|
1144
|
-
install: "yarn install",
|
|
1145
|
-
add: "yarn add",
|
|
1146
|
-
addDev: "yarn add -D",
|
|
1147
|
-
run: "yarn",
|
|
1148
|
-
create: "yarn create",
|
|
1149
|
-
x: "yarn dlx",
|
|
1150
|
-
},
|
|
1151
|
-
};
|
|
1152
|
-
|
|
1153
|
-
return commands[pm];
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
|
-
async function rm(path: string): Promise<void> {
|
|
1157
|
-
if (existsSync(path)) {
|
|
1158
|
-
await fs.rm(path, { recursive: true });
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
async function mkdir(...path: string[]): Promise<void> {
|
|
1163
|
-
await fs.mkdir(join(...path), {
|
|
1164
|
-
recursive: true,
|
|
1165
|
-
});
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
function execCommand(command: string, cwd: string = process.cwd()): void {
|
|
1169
|
-
console.log(command.replaceAll(/ +/g, " "));
|
|
1170
|
-
try {
|
|
1171
|
-
execSync(command, { stdio: "inherit", cwd });
|
|
1172
|
-
} catch {
|
|
1173
|
-
console.error(`Failed to execute: ${command}`);
|
|
1174
|
-
process.exit(1);
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
function install({
|
|
1179
|
-
dependencies,
|
|
1180
|
-
devDependencies,
|
|
1181
|
-
cwd = projectPath,
|
|
1182
|
-
}: {
|
|
1183
|
-
dependencies?: string[];
|
|
1184
|
-
devDependencies?: string[];
|
|
1185
|
-
cwd?: string;
|
|
1186
|
-
} = {}) {
|
|
1187
|
-
if (!dependencies && !devDependencies) {
|
|
1188
|
-
execCommand(getPackageManagerCommands(pm).install, cwd);
|
|
1189
|
-
}
|
|
1190
|
-
if (dependencies) {
|
|
1191
|
-
execCommand(
|
|
1192
|
-
`${getPackageManagerCommands(pm).add} ${dependencies.join(" ")}`,
|
|
1193
|
-
cwd,
|
|
1194
|
-
);
|
|
1195
|
-
}
|
|
1196
|
-
if (devDependencies) {
|
|
1197
|
-
execCommand(
|
|
1198
|
-
`${getPackageManagerCommands(pm).addDev} ${devDependencies.join(" ")}`,
|
|
1199
|
-
cwd,
|
|
1200
|
-
);
|
|
1201
|
-
}
|
|
1202
|
-
}
|
|
1203
|
-
|
|
1204
|
-
function npx(command: string, cwd: string = process.cwd()): void {
|
|
1205
|
-
execCommand(
|
|
1206
|
-
`${getPackageManagerCommands(pm).x} ${options.yes ? "--yes" : ""} ${command}`,
|
|
1207
|
-
cwd,
|
|
1208
|
-
);
|
|
1209
|
-
}
|
|
1210
|
-
|
|
1211
|
-
function create(
|
|
1212
|
-
pm: PackageManager,
|
|
1213
|
-
command: string,
|
|
1214
|
-
cwd: string = process.cwd(),
|
|
1215
|
-
): void {
|
|
1216
|
-
execCommand(
|
|
1217
|
-
`${getPackageManagerCommands(pm).create} ${options.yes ? "-y" : ""} ${command}`,
|
|
1218
|
-
cwd,
|
|
1219
|
-
);
|
|
1220
|
-
}
|