create-better-t-stack 3.11.1 → 3.12.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/dist/chunk-Dt3mZKp0.mjs +24 -0
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +40 -60
- package/dist/index.mjs +2 -2
- package/dist/{src-Dc2OdxbP.mjs → src-DBVnwTkj.mjs} +644 -609
- package/package.json +2 -2
- package/templates/addons/turborepo/turbo.json.hbs +8 -0
- package/templates/api/orpc/native/utils/orpc.ts.hbs +21 -20
- package/templates/api/orpc/web/nuxt/app/plugins/orpc.ts.hbs +3 -5
- package/templates/api/orpc/web/react/base/src/utils/orpc.ts.hbs +73 -67
- package/templates/api/orpc/web/solid/src/utils/orpc.ts.hbs +15 -14
- package/templates/api/trpc/native/utils/trpc.ts.hbs +8 -7
- package/templates/api/trpc/web/react/base/src/utils/trpc.ts.hbs +59 -57
- package/templates/auth/better-auth/convex/native/base/lib/auth-client.ts.hbs +10 -9
- package/templates/auth/better-auth/convex/web/react/next/src/lib/auth-server.ts.hbs +10 -9
- package/templates/auth/better-auth/convex/web/react/tanstack-router/src/lib/auth-client.ts.hbs +5 -4
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/lib/auth-server.ts.hbs +8 -7
- package/templates/auth/better-auth/native/base/lib/auth-client.ts.hbs +9 -8
- package/templates/auth/better-auth/server/base/src/index.ts.hbs +239 -235
- package/templates/auth/better-auth/web/nuxt/app/plugins/auth-client.ts.hbs +2 -3
- package/templates/auth/better-auth/web/react/base/src/lib/auth-client.ts.hbs +9 -11
- package/templates/auth/better-auth/web/solid/src/lib/auth-client.ts.hbs +3 -2
- package/templates/backend/server/elysia/src/index.ts.hbs +71 -71
- package/templates/backend/server/express/src/index.ts.hbs +57 -57
- package/templates/backend/server/fastify/src/index.ts.hbs +107 -107
- package/templates/backend/server/hono/src/index.ts.hbs +75 -85
- package/templates/base/tsconfig.json.hbs +3 -0
- package/templates/db/drizzle/mysql/src/index.ts.hbs +23 -30
- package/templates/db/drizzle/postgres/src/index.ts.hbs +6 -13
- package/templates/db/drizzle/sqlite/src/index.ts.hbs +11 -18
- package/templates/db/mongoose/mongodb/src/index.ts.hbs +3 -2
- package/templates/db/prisma/mongodb/prisma/schema/schema.prisma.hbs +1 -1
- package/templates/db/prisma/mysql/prisma/schema/schema.prisma.hbs +1 -1
- package/templates/db/prisma/mysql/prisma.config.ts.hbs +16 -16
- package/templates/db/prisma/mysql/src/index.ts.hbs +16 -15
- package/templates/db/prisma/postgres/prisma/schema/schema.prisma.hbs +1 -1
- package/templates/db/prisma/postgres/src/index.ts.hbs +10 -9
- package/templates/db/prisma/sqlite/prisma/schema/schema.prisma.hbs +1 -1
- package/templates/db/prisma/sqlite/src/index.ts.hbs +4 -7
- package/templates/examples/ai/native/bare/app/(drawer)/ai.tsx.hbs +2 -1
- package/templates/examples/ai/native/unistyles/app/(drawer)/ai.tsx.hbs +2 -1
- package/templates/examples/ai/native/uniwind/app/(drawer)/ai.tsx.hbs +2 -1
- package/templates/examples/ai/web/nuxt/app/pages/ai.vue.hbs +1 -3
- package/templates/examples/ai/web/react/next/src/app/ai/page.tsx.hbs +4 -3
- package/templates/examples/ai/web/react/react-router/src/routes/ai.tsx.hbs +2 -1
- package/templates/examples/ai/web/react/tanstack-router/src/routes/ai.tsx.hbs +4 -1
- package/templates/examples/ai/web/react/tanstack-start/src/routes/ai.tsx.hbs +4 -1
- package/templates/frontend/native/bare/app/_layout.tsx.hbs +4 -2
- package/templates/frontend/native/unistyles/app/_layout.tsx.hbs +4 -2
- package/templates/frontend/native/uniwind/app/_layout.tsx.hbs +4 -3
- package/templates/frontend/nuxt/nuxt.config.ts.hbs +6 -3
- package/templates/frontend/react/next/next.config.ts.hbs +9 -8
- package/templates/frontend/react/next/src/components/providers.tsx.hbs +4 -1
- package/templates/frontend/react/next/tsconfig.json.hbs +2 -2
- package/templates/frontend/react/react-router/src/root.tsx.hbs +3 -4
- package/templates/frontend/react/tanstack-router/src/main.tsx.hbs +3 -2
- package/templates/frontend/react/tanstack-start/src/router.tsx.hbs +97 -93
- package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +23 -3
- package/templates/packages/config/tsconfig.base.json.hbs +1 -1
- package/templates/{deploy/alchemy → packages/env}/env.d.ts.hbs +6 -4
- package/templates/packages/env/package.json.hbs +7 -0
- package/templates/packages/env/src/native.ts.hbs +21 -0
- package/templates/packages/env/src/server.ts.hbs +38 -0
- package/templates/packages/env/src/web.ts.hbs +89 -0
- package/templates/packages/env/tsconfig.json.hbs +3 -0
- package/templates/{deploy/alchemy → packages/infra}/alchemy.run.ts.hbs +84 -80
- package/templates/packages/infra/package.json.hbs +10 -0
- package/templates/payments/polar/server/base/src/lib/payments.ts.hbs +3 -2
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { t as __reExport } from "./chunk-Dt3mZKp0.mjs";
|
|
2
3
|
import { autocompleteMultiselect, cancel, confirm, group, groupMultiselect, intro, isCancel, log, multiselect, outro, select, spinner, text } from "@clack/prompts";
|
|
3
4
|
import { createRouterClient, os } from "@orpc/server";
|
|
4
5
|
import pc from "picocolors";
|
|
5
6
|
import { createCli } from "trpc-cli";
|
|
6
7
|
import z from "zod";
|
|
7
|
-
import path from "node:path";
|
|
8
8
|
import consola, { consola as consola$1 } from "consola";
|
|
9
9
|
import fs from "fs-extra";
|
|
10
|
+
import path from "node:path";
|
|
10
11
|
import { fileURLToPath } from "node:url";
|
|
11
|
-
import { APISchema, AddonsSchema, AuthSchema, BackendSchema, DatabaseSchema, DatabaseSetupSchema, DirectoryConflictSchema, ExamplesSchema, FrontendSchema, ORMSchema, PackageManagerSchema, PaymentsSchema, ProjectNameSchema, RuntimeSchema, ServerDeploySchema, TemplateSchema, WebDeploySchema } from "@better-t-stack/types";
|
|
12
12
|
import gradient from "gradient-string";
|
|
13
13
|
import * as JSONC from "jsonc-parser";
|
|
14
14
|
import { $, execa } from "execa";
|
|
@@ -155,10 +155,13 @@ const dependencyVersionMap = {
|
|
|
155
155
|
"nitro-cloudflare-dev": "^0.2.2",
|
|
156
156
|
"@sveltejs/adapter-cloudflare": "^7.2.4",
|
|
157
157
|
"@cloudflare/workers-types": "^4.20251213.0",
|
|
158
|
-
alchemy: "^0.
|
|
158
|
+
alchemy: "^0.82.1",
|
|
159
159
|
dotenv: "^17.2.2",
|
|
160
160
|
tsdown: "^0.16.5",
|
|
161
161
|
zod: "^4.1.13",
|
|
162
|
+
"@t3-oss/env-core": "^0.13.1",
|
|
163
|
+
"@t3-oss/env-nextjs": "^0.13.1",
|
|
164
|
+
"@t3-oss/env-nuxt": "^0.13.1",
|
|
162
165
|
srvx: "0.8.15",
|
|
163
166
|
"@polar-sh/better-auth": "^1.1.3",
|
|
164
167
|
"@polar-sh/sdk": "^0.34.16"
|
|
@@ -191,6 +194,12 @@ const ADDON_COMPATIBILITY = {
|
|
|
191
194
|
none: []
|
|
192
195
|
};
|
|
193
196
|
|
|
197
|
+
//#endregion
|
|
198
|
+
//#region src/types.ts
|
|
199
|
+
var types_exports = {};
|
|
200
|
+
import * as import__better_t_stack_types from "@better-t-stack/types";
|
|
201
|
+
__reExport(types_exports, import__better_t_stack_types);
|
|
202
|
+
|
|
194
203
|
//#endregion
|
|
195
204
|
//#region src/utils/compatibility.ts
|
|
196
205
|
const WEB_FRAMEWORKS = [
|
|
@@ -205,24 +214,18 @@ const WEB_FRAMEWORKS = [
|
|
|
205
214
|
|
|
206
215
|
//#endregion
|
|
207
216
|
//#region src/utils/errors.ts
|
|
208
|
-
function isProgrammatic() {
|
|
209
|
-
return process.env.BTS_PROGRAMMATIC === "1";
|
|
210
|
-
}
|
|
211
217
|
function exitWithError(message) {
|
|
212
218
|
consola.error(pc.red(message));
|
|
213
|
-
|
|
214
|
-
process.exit(1);
|
|
219
|
+
throw new Error(message);
|
|
215
220
|
}
|
|
216
221
|
function exitCancelled(message = "Operation cancelled") {
|
|
217
222
|
cancel(pc.red(message));
|
|
218
|
-
|
|
219
|
-
process.exit(0);
|
|
223
|
+
throw new Error(message);
|
|
220
224
|
}
|
|
221
225
|
function handleError(error, fallbackMessage) {
|
|
222
226
|
const message = error instanceof Error ? error.message : fallbackMessage || String(error);
|
|
223
227
|
consola.error(pc.red(message));
|
|
224
|
-
|
|
225
|
-
process.exit(1);
|
|
228
|
+
throw new Error(message);
|
|
226
229
|
}
|
|
227
230
|
|
|
228
231
|
//#endregion
|
|
@@ -435,7 +438,7 @@ const ADDON_GROUPS = {
|
|
|
435
438
|
};
|
|
436
439
|
async function getAddonsChoice(addons, frontends, auth) {
|
|
437
440
|
if (addons !== void 0) return addons;
|
|
438
|
-
const allAddons = AddonsSchema.options.filter((addon) => addon !== "none");
|
|
441
|
+
const allAddons = types_exports.AddonsSchema.options.filter((addon) => addon !== "none");
|
|
439
442
|
const groupedOptions = {
|
|
440
443
|
Documentation: [],
|
|
441
444
|
Linting: [],
|
|
@@ -481,7 +484,7 @@ async function getAddonsToAdd(frontend, existingAddons = [], auth) {
|
|
|
481
484
|
Other: []
|
|
482
485
|
};
|
|
483
486
|
const frontendArray = frontend || [];
|
|
484
|
-
const compatibleAddons = getCompatibleAddons(AddonsSchema.options.filter((addon) => addon !== "none"), frontendArray, existingAddons, auth);
|
|
487
|
+
const compatibleAddons = getCompatibleAddons(types_exports.AddonsSchema.options.filter((addon) => addon !== "none"), frontendArray, existingAddons, auth);
|
|
485
488
|
for (const addon of compatibleAddons) {
|
|
486
489
|
const { label, hint } = getAddonDisplay(addon);
|
|
487
490
|
const option = {
|
|
@@ -1058,8 +1061,8 @@ async function getRuntimeChoice(runtime, backend) {
|
|
|
1058
1061
|
//#endregion
|
|
1059
1062
|
//#region src/prompts/server-deploy.ts
|
|
1060
1063
|
function getDeploymentDisplay$1(deployment) {
|
|
1061
|
-
if (deployment === "
|
|
1062
|
-
label: "
|
|
1064
|
+
if (deployment === "cloudflare") return {
|
|
1065
|
+
label: "Cloudflare",
|
|
1063
1066
|
hint: "Deploy to Cloudflare Workers using Alchemy"
|
|
1064
1067
|
};
|
|
1065
1068
|
return {
|
|
@@ -1071,17 +1074,17 @@ async function getServerDeploymentChoice(deployment, runtime, backend, _webDeplo
|
|
|
1071
1074
|
if (deployment !== void 0) return deployment;
|
|
1072
1075
|
if (backend === "none" || backend === "convex") return "none";
|
|
1073
1076
|
if (backend !== "hono") return "none";
|
|
1074
|
-
if (runtime === "workers") return "
|
|
1077
|
+
if (runtime === "workers") return "cloudflare";
|
|
1075
1078
|
return "none";
|
|
1076
1079
|
}
|
|
1077
1080
|
async function getServerDeploymentToAdd(runtime, existingDeployment, backend) {
|
|
1078
1081
|
if (backend !== "hono") return "none";
|
|
1079
1082
|
const options = [];
|
|
1080
1083
|
if (runtime === "workers") {
|
|
1081
|
-
if (existingDeployment !== "
|
|
1082
|
-
const { label, hint } = getDeploymentDisplay$1("
|
|
1084
|
+
if (existingDeployment !== "cloudflare") {
|
|
1085
|
+
const { label, hint } = getDeploymentDisplay$1("cloudflare");
|
|
1083
1086
|
options.push({
|
|
1084
|
-
value: "
|
|
1087
|
+
value: "cloudflare",
|
|
1085
1088
|
label,
|
|
1086
1089
|
hint
|
|
1087
1090
|
});
|
|
@@ -1104,8 +1107,8 @@ function hasWebFrontend(frontends) {
|
|
|
1104
1107
|
return frontends.some((f) => WEB_FRAMEWORKS.includes(f));
|
|
1105
1108
|
}
|
|
1106
1109
|
function getDeploymentDisplay(deployment) {
|
|
1107
|
-
if (deployment === "
|
|
1108
|
-
label: "
|
|
1110
|
+
if (deployment === "cloudflare") return {
|
|
1111
|
+
label: "Cloudflare",
|
|
1109
1112
|
hint: "Deploy to Cloudflare Workers using Alchemy"
|
|
1110
1113
|
};
|
|
1111
1114
|
return {
|
|
@@ -1118,7 +1121,7 @@ async function getDeploymentChoice(deployment, _runtime, _backend, frontend = []
|
|
|
1118
1121
|
if (!hasWebFrontend(frontend)) return "none";
|
|
1119
1122
|
const response = await select({
|
|
1120
1123
|
message: "Select web deployment",
|
|
1121
|
-
options: ["
|
|
1124
|
+
options: ["cloudflare", "none"].map((deploy) => {
|
|
1122
1125
|
const { label, hint } = getDeploymentDisplay(deploy);
|
|
1123
1126
|
return {
|
|
1124
1127
|
value: deploy,
|
|
@@ -1134,10 +1137,10 @@ async function getDeploymentChoice(deployment, _runtime, _backend, frontend = []
|
|
|
1134
1137
|
async function getDeploymentToAdd(frontend, existingDeployment) {
|
|
1135
1138
|
if (!hasWebFrontend(frontend)) return "none";
|
|
1136
1139
|
const options = [];
|
|
1137
|
-
if (existingDeployment !== "
|
|
1138
|
-
const { label, hint } = getDeploymentDisplay("
|
|
1140
|
+
if (existingDeployment !== "cloudflare") {
|
|
1141
|
+
const { label, hint } = getDeploymentDisplay("cloudflare");
|
|
1139
1142
|
options.push({
|
|
1140
|
-
value: "
|
|
1143
|
+
value: "cloudflare",
|
|
1141
1144
|
label,
|
|
1142
1145
|
hint
|
|
1143
1146
|
});
|
|
@@ -1211,7 +1214,7 @@ function isPathWithinCwd(targetPath) {
|
|
|
1211
1214
|
}
|
|
1212
1215
|
function validateDirectoryName(name) {
|
|
1213
1216
|
if (name === ".") return void 0;
|
|
1214
|
-
const result = ProjectNameSchema.safeParse(name);
|
|
1217
|
+
const result = types_exports.ProjectNameSchema.safeParse(name);
|
|
1215
1218
|
if (!result.success) return result.error.issues[0]?.message || "Invalid project name";
|
|
1216
1219
|
}
|
|
1217
1220
|
async function getProjectName(initialName) {
|
|
@@ -1771,7 +1774,7 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
1771
1774
|
validateSelfBackendCompatibility(providedFlags, options, config);
|
|
1772
1775
|
validateWorkersCompatibility(providedFlags, options, config);
|
|
1773
1776
|
if (config.runtime === "workers" && config.serverDeploy === "none") exitWithError("Cloudflare Workers runtime requires a server deployment. Please choose 'alchemy' for --server-deploy.");
|
|
1774
|
-
if (providedFlags.has("serverDeploy") && config.serverDeploy === "
|
|
1777
|
+
if (providedFlags.has("serverDeploy") && config.serverDeploy === "cloudflare" && config.runtime !== "workers") exitWithError(`Server deployment '${config.serverDeploy}' requires '--runtime workers'. Please use '--runtime workers' or choose a different server deployment.`);
|
|
1775
1778
|
if (config.addons && config.addons.length > 0) {
|
|
1776
1779
|
validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth);
|
|
1777
1780
|
config.addons = [...new Set(config.addons)];
|
|
@@ -1796,11 +1799,11 @@ function validateConfigForProgrammaticUse(config) {
|
|
|
1796
1799
|
//#endregion
|
|
1797
1800
|
//#region src/utils/project-name-validation.ts
|
|
1798
1801
|
function validateProjectName(name) {
|
|
1799
|
-
const result = ProjectNameSchema.safeParse(name);
|
|
1802
|
+
const result = types_exports.ProjectNameSchema.safeParse(name);
|
|
1800
1803
|
if (!result.success) exitWithError(`Invalid project name: ${result.error.issues[0]?.message || "Invalid project name"}`);
|
|
1801
1804
|
}
|
|
1802
1805
|
function validateProjectNameThrow(name) {
|
|
1803
|
-
const result = ProjectNameSchema.safeParse(name);
|
|
1806
|
+
const result = types_exports.ProjectNameSchema.safeParse(name);
|
|
1804
1807
|
if (!result.success) throw new Error(`Invalid project name: ${result.error.issues[0]?.message}`);
|
|
1805
1808
|
}
|
|
1806
1809
|
function extractAndValidateProjectName(projectName, projectDirectory, throwOnError = false) {
|
|
@@ -1998,6 +2001,45 @@ function getPackageExecutionCommand(packageManager, commandWithArgs) {
|
|
|
1998
2001
|
default: return `npx ${commandWithArgs}`;
|
|
1999
2002
|
}
|
|
2000
2003
|
}
|
|
2004
|
+
/**
|
|
2005
|
+
* Returns the command and arguments as an array for use with execa's $ template syntax.
|
|
2006
|
+
* This avoids the need for shell: true and provides better escaping.
|
|
2007
|
+
*
|
|
2008
|
+
* @param packageManager - The selected package manager (e.g., 'npm', 'yarn', 'pnpm', 'bun').
|
|
2009
|
+
* @param commandWithArgs - The command to run, including arguments (e.g., "prisma generate").
|
|
2010
|
+
* @returns An array of [command, ...args] (e.g., ["npx", "prisma", "generate"]).
|
|
2011
|
+
*/
|
|
2012
|
+
function getPackageExecutionArgs(packageManager, commandWithArgs) {
|
|
2013
|
+
const args = commandWithArgs.split(" ");
|
|
2014
|
+
switch (packageManager) {
|
|
2015
|
+
case "pnpm": return [
|
|
2016
|
+
"pnpm",
|
|
2017
|
+
"dlx",
|
|
2018
|
+
...args
|
|
2019
|
+
];
|
|
2020
|
+
case "bun": return ["bunx", ...args];
|
|
2021
|
+
default: return ["npx", ...args];
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
/**
|
|
2025
|
+
* Returns just the runner prefix as an array, for when you already have args built.
|
|
2026
|
+
* Use this when you have complex arguments that shouldn't be split by spaces.
|
|
2027
|
+
*
|
|
2028
|
+
* @param packageManager - The selected package manager.
|
|
2029
|
+
* @returns The runner prefix as an array (e.g., ["npx"] or ["pnpm", "dlx"]).
|
|
2030
|
+
*
|
|
2031
|
+
* @example
|
|
2032
|
+
* const prefix = getPackageRunnerPrefix("bun");
|
|
2033
|
+
* const args = ["@tauri-apps/cli@latest", "init", "--app-name=foo"];
|
|
2034
|
+
* await $`${[...prefix, ...args]}`;
|
|
2035
|
+
*/
|
|
2036
|
+
function getPackageRunnerPrefix(packageManager) {
|
|
2037
|
+
switch (packageManager) {
|
|
2038
|
+
case "pnpm": return ["pnpm", "dlx"];
|
|
2039
|
+
case "bun": return ["bunx"];
|
|
2040
|
+
default: return ["npx"];
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2001
2043
|
|
|
2002
2044
|
//#endregion
|
|
2003
2045
|
//#region src/helpers/addons/fumadocs-setup.ts
|
|
@@ -2042,16 +2084,15 @@ async function setupFumadocs(config) {
|
|
|
2042
2084
|
initialValue: "next-mdx"
|
|
2043
2085
|
});
|
|
2044
2086
|
if (isCancel(template)) return exitCancelled("Operation cancelled");
|
|
2045
|
-
const
|
|
2087
|
+
const args = getPackageExecutionArgs(packageManager, `create-fumadocs-app@latest fumadocs --template ${TEMPLATES$2[template].value} --src --pm ${packageManager} --no-git`);
|
|
2046
2088
|
const appsDir = path.join(projectDir, "apps");
|
|
2047
2089
|
await fs.ensureDir(appsDir);
|
|
2048
2090
|
const s = spinner();
|
|
2049
2091
|
s.start("Running Fumadocs create command...");
|
|
2050
|
-
await
|
|
2092
|
+
await $({
|
|
2051
2093
|
cwd: appsDir,
|
|
2052
|
-
env: { CI: "true" }
|
|
2053
|
-
|
|
2054
|
-
});
|
|
2094
|
+
env: { CI: "true" }
|
|
2095
|
+
})`${args}`;
|
|
2055
2096
|
const fumadocsDir = path.join(projectDir, "apps", "fumadocs");
|
|
2056
2097
|
const packageJsonPath = path.join(fumadocsDir, "package.json");
|
|
2057
2098
|
if (await fs.pathExists(packageJsonPath)) {
|
|
@@ -2084,18 +2125,17 @@ async function setupOxlint(projectDir, packageManager) {
|
|
|
2084
2125
|
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
2085
2126
|
}
|
|
2086
2127
|
const s = spinner();
|
|
2087
|
-
const
|
|
2128
|
+
const oxlintArgs = getPackageExecutionArgs(packageManager, "oxlint@latest --init");
|
|
2088
2129
|
s.start("Initializing oxlint and oxfmt...");
|
|
2089
|
-
await
|
|
2130
|
+
await $({
|
|
2090
2131
|
cwd: projectDir,
|
|
2091
|
-
env: { CI: "true" }
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
await
|
|
2132
|
+
env: { CI: "true" }
|
|
2133
|
+
})`${oxlintArgs}`;
|
|
2134
|
+
const oxfmtArgs = getPackageExecutionArgs(packageManager, "oxfmt@latest --init");
|
|
2135
|
+
await $({
|
|
2095
2136
|
cwd: projectDir,
|
|
2096
|
-
env: { CI: "true" }
|
|
2097
|
-
|
|
2098
|
-
});
|
|
2137
|
+
env: { CI: "true" }
|
|
2138
|
+
})`${oxfmtArgs}`;
|
|
2099
2139
|
s.stop("oxlint and oxfmt initialized successfully!");
|
|
2100
2140
|
}
|
|
2101
2141
|
|
|
@@ -2160,11 +2200,11 @@ async function setupRuler(config) {
|
|
|
2160
2200
|
const s = spinner();
|
|
2161
2201
|
s.start("Applying rules with Ruler...");
|
|
2162
2202
|
try {
|
|
2163
|
-
|
|
2203
|
+
const rulerApplyArgs = getPackageExecutionArgs(packageManager, `@intellectronica/ruler@latest apply --agents ${selectedEditors.join(",")} --local-only`);
|
|
2204
|
+
await $({
|
|
2164
2205
|
cwd: projectDir,
|
|
2165
|
-
env: { CI: "true" }
|
|
2166
|
-
|
|
2167
|
-
});
|
|
2206
|
+
env: { CI: "true" }
|
|
2207
|
+
})`${rulerApplyArgs}`;
|
|
2168
2208
|
s.stop("Applied rules with Ruler");
|
|
2169
2209
|
} catch {
|
|
2170
2210
|
s.stop(pc.red("Failed to apply rules"));
|
|
@@ -2194,7 +2234,7 @@ async function setupStarlight(config) {
|
|
|
2194
2234
|
const s = spinner();
|
|
2195
2235
|
try {
|
|
2196
2236
|
s.start("Setting up Starlight docs...");
|
|
2197
|
-
const
|
|
2237
|
+
const args = getPackageExecutionArgs(packageManager, `create-astro@latest ${[
|
|
2198
2238
|
"docs",
|
|
2199
2239
|
"--template",
|
|
2200
2240
|
"starlight",
|
|
@@ -2206,11 +2246,10 @@ async function setupStarlight(config) {
|
|
|
2206
2246
|
].join(" ")}`);
|
|
2207
2247
|
const appsDir = path.join(projectDir, "apps");
|
|
2208
2248
|
await fs.ensureDir(appsDir);
|
|
2209
|
-
await
|
|
2249
|
+
await $({
|
|
2210
2250
|
cwd: appsDir,
|
|
2211
|
-
env: { CI: "true" }
|
|
2212
|
-
|
|
2213
|
-
});
|
|
2251
|
+
env: { CI: "true" }
|
|
2252
|
+
})`${args}`;
|
|
2214
2253
|
s.stop("Starlight docs setup successfully!");
|
|
2215
2254
|
} catch (error) {
|
|
2216
2255
|
s.stop(pc.red("Failed to set up Starlight docs"));
|
|
@@ -2250,19 +2289,21 @@ async function setupTauri(config) {
|
|
|
2250
2289
|
const hasNext = frontend.includes("next");
|
|
2251
2290
|
const devUrl = hasReactRouter || hasSvelte ? "http://localhost:5173" : hasNext ? "http://localhost:3001" : "http://localhost:3001";
|
|
2252
2291
|
const frontendDist = hasNuxt ? "../.output/public" : hasSvelte ? "../build" : hasNext ? "../.next" : hasReactRouter ? "../build/client" : "../dist";
|
|
2253
|
-
|
|
2292
|
+
const tauriArgs = [
|
|
2293
|
+
"@tauri-apps/cli@latest",
|
|
2254
2294
|
"init",
|
|
2255
2295
|
`--app-name=${path.basename(projectDir)}`,
|
|
2256
2296
|
`--window-title=${path.basename(projectDir)}`,
|
|
2257
2297
|
`--frontend-dist=${frontendDist}`,
|
|
2258
2298
|
`--dev-url=${devUrl}`,
|
|
2259
|
-
`--before-dev-command
|
|
2260
|
-
`--before-build-command
|
|
2261
|
-
]
|
|
2299
|
+
`--before-dev-command=${packageManager} run dev`,
|
|
2300
|
+
`--before-build-command=${packageManager} run build`
|
|
2301
|
+
];
|
|
2302
|
+
const prefix = getPackageRunnerPrefix(packageManager);
|
|
2303
|
+
await $({
|
|
2262
2304
|
cwd: clientPackageDir,
|
|
2263
|
-
env: { CI: "true" }
|
|
2264
|
-
|
|
2265
|
-
});
|
|
2305
|
+
env: { CI: "true" }
|
|
2306
|
+
})`${[...prefix, ...tauriArgs]}`;
|
|
2266
2307
|
s.stop("Tauri desktop app support configured successfully!");
|
|
2267
2308
|
} catch (error) {
|
|
2268
2309
|
s.stop(pc.red("Failed to set up Tauri"));
|
|
@@ -2300,16 +2341,15 @@ async function setupTui(config) {
|
|
|
2300
2341
|
initialValue: "core"
|
|
2301
2342
|
});
|
|
2302
2343
|
if (isCancel(template)) return exitCancelled("Operation cancelled");
|
|
2303
|
-
const
|
|
2344
|
+
const args = getPackageExecutionArgs(packageManager, `create-tui@latest --template ${template} --no-git --no-install tui`);
|
|
2304
2345
|
const appsDir = path.join(projectDir, "apps");
|
|
2305
2346
|
await fs.ensureDir(appsDir);
|
|
2306
2347
|
const s = spinner();
|
|
2307
2348
|
s.start("Running OpenTUI create command...");
|
|
2308
|
-
await
|
|
2349
|
+
await $({
|
|
2309
2350
|
cwd: appsDir,
|
|
2310
|
-
env: { CI: "true" }
|
|
2311
|
-
|
|
2312
|
-
});
|
|
2351
|
+
env: { CI: "true" }
|
|
2352
|
+
})`${args}`;
|
|
2313
2353
|
s.stop("OpenTUI setup complete!");
|
|
2314
2354
|
} catch (error) {
|
|
2315
2355
|
log.error(pc.red("Failed to set up OpenTUI"));
|
|
@@ -2410,14 +2450,13 @@ async function setupUltracite(config, hasHusky) {
|
|
|
2410
2450
|
if (agents.length > 0) ultraciteArgs.push("--agents", ...agents);
|
|
2411
2451
|
if (hooks.length > 0) ultraciteArgs.push("--hooks", ...hooks);
|
|
2412
2452
|
if (hasHusky) ultraciteArgs.push("--integrations", "husky", "lint-staged");
|
|
2413
|
-
const
|
|
2453
|
+
const args = getPackageExecutionArgs(packageManager, `ultracite@latest ${ultraciteArgs.join(" ")} --skip-install`);
|
|
2414
2454
|
const s = spinner();
|
|
2415
2455
|
s.start("Running Ultracite init command...");
|
|
2416
|
-
await
|
|
2456
|
+
await $({
|
|
2417
2457
|
cwd: projectDir,
|
|
2418
|
-
env: { CI: "true" }
|
|
2419
|
-
|
|
2420
|
-
});
|
|
2458
|
+
env: { CI: "true" }
|
|
2459
|
+
})`${args}`;
|
|
2421
2460
|
if (hasHusky) await addPackageDependency({
|
|
2422
2461
|
devDependencies: ["husky", "lint-staged"],
|
|
2423
2462
|
projectDir
|
|
@@ -2429,6 +2468,54 @@ async function setupUltracite(config, hasHusky) {
|
|
|
2429
2468
|
}
|
|
2430
2469
|
}
|
|
2431
2470
|
|
|
2471
|
+
//#endregion
|
|
2472
|
+
//#region src/utils/ts-morph.ts
|
|
2473
|
+
const tsProject = new Project({
|
|
2474
|
+
useInMemoryFileSystem: false,
|
|
2475
|
+
skipAddingFilesFromTsConfig: true,
|
|
2476
|
+
manipulationSettings: {
|
|
2477
|
+
quoteKind: QuoteKind.Single,
|
|
2478
|
+
indentationText: IndentationText.TwoSpaces
|
|
2479
|
+
}
|
|
2480
|
+
});
|
|
2481
|
+
function ensureArrayProperty(obj, name) {
|
|
2482
|
+
return obj.getProperty(name)?.getFirstDescendantByKind(SyntaxKind.ArrayLiteralExpression) ?? obj.addPropertyAssignment({
|
|
2483
|
+
name,
|
|
2484
|
+
initializer: "[]"
|
|
2485
|
+
}).getFirstDescendantByKindOrThrow(SyntaxKind.ArrayLiteralExpression);
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2488
|
+
//#endregion
|
|
2489
|
+
//#region src/helpers/addons/vite-pwa-setup.ts
|
|
2490
|
+
async function addPwaToViteConfig(viteConfigPath, projectName) {
|
|
2491
|
+
const sourceFile = tsProject.addSourceFileAtPathIfExists(viteConfigPath);
|
|
2492
|
+
if (!sourceFile) throw new Error("vite config not found");
|
|
2493
|
+
if (!sourceFile.getImportDeclarations().some((imp) => imp.getModuleSpecifierValue() === "vite-plugin-pwa")) sourceFile.insertImportDeclaration(0, {
|
|
2494
|
+
namedImports: ["VitePWA"],
|
|
2495
|
+
moduleSpecifier: "vite-plugin-pwa"
|
|
2496
|
+
});
|
|
2497
|
+
const defineCall = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression).find((expr) => {
|
|
2498
|
+
const expression = expr.getExpression();
|
|
2499
|
+
return Node.isIdentifier(expression) && expression.getText() === "defineConfig";
|
|
2500
|
+
});
|
|
2501
|
+
if (!defineCall) throw new Error("Could not find defineConfig call in vite config");
|
|
2502
|
+
const configObject = defineCall.getArguments()[0];
|
|
2503
|
+
if (!configObject) throw new Error("defineConfig argument is not an object literal");
|
|
2504
|
+
const pluginsArray = ensureArrayProperty(configObject, "plugins");
|
|
2505
|
+
if (!pluginsArray.getElements().some((el) => el.getText().startsWith("VitePWA("))) pluginsArray.addElement(`VitePWA({
|
|
2506
|
+
registerType: "autoUpdate",
|
|
2507
|
+
manifest: {
|
|
2508
|
+
name: "${projectName}",
|
|
2509
|
+
short_name: "${projectName}",
|
|
2510
|
+
description: "${projectName} - PWA Application",
|
|
2511
|
+
theme_color: "#0c0c0c",
|
|
2512
|
+
},
|
|
2513
|
+
pwaAssets: { disabled: false, config: true },
|
|
2514
|
+
devOptions: { enabled: true },
|
|
2515
|
+
})`);
|
|
2516
|
+
await tsProject.save();
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2432
2519
|
//#endregion
|
|
2433
2520
|
//#region src/helpers/addons/wxt-setup.ts
|
|
2434
2521
|
const TEMPLATES = {
|
|
@@ -2467,16 +2554,15 @@ async function setupWxt(config) {
|
|
|
2467
2554
|
initialValue: "react"
|
|
2468
2555
|
});
|
|
2469
2556
|
if (isCancel(template)) return exitCancelled("Operation cancelled");
|
|
2470
|
-
const
|
|
2557
|
+
const args = getPackageExecutionArgs(packageManager, `wxt@latest init extension --template ${template} --pm ${packageManager}`);
|
|
2471
2558
|
const appsDir = path.join(projectDir, "apps");
|
|
2472
2559
|
await fs.ensureDir(appsDir);
|
|
2473
2560
|
const s = spinner();
|
|
2474
2561
|
s.start("Running WXT init command...");
|
|
2475
|
-
await
|
|
2562
|
+
await $({
|
|
2476
2563
|
cwd: appsDir,
|
|
2477
|
-
env: { CI: "true" }
|
|
2478
|
-
|
|
2479
|
-
});
|
|
2564
|
+
env: { CI: "true" }
|
|
2565
|
+
})`${args}`;
|
|
2480
2566
|
const extensionDir = path.join(projectDir, "apps", "extension");
|
|
2481
2567
|
const packageJsonPath = path.join(extensionDir, "package.json");
|
|
2482
2568
|
if (await fs.pathExists(packageJsonPath)) {
|
|
@@ -2492,54 +2578,6 @@ async function setupWxt(config) {
|
|
|
2492
2578
|
}
|
|
2493
2579
|
}
|
|
2494
2580
|
|
|
2495
|
-
//#endregion
|
|
2496
|
-
//#region src/utils/ts-morph.ts
|
|
2497
|
-
const tsProject$1 = new Project({
|
|
2498
|
-
useInMemoryFileSystem: false,
|
|
2499
|
-
skipAddingFilesFromTsConfig: true,
|
|
2500
|
-
manipulationSettings: {
|
|
2501
|
-
quoteKind: QuoteKind.Single,
|
|
2502
|
-
indentationText: IndentationText.TwoSpaces
|
|
2503
|
-
}
|
|
2504
|
-
});
|
|
2505
|
-
function ensureArrayProperty(obj, name) {
|
|
2506
|
-
return obj.getProperty(name)?.getFirstDescendantByKind(SyntaxKind.ArrayLiteralExpression) ?? obj.addPropertyAssignment({
|
|
2507
|
-
name,
|
|
2508
|
-
initializer: "[]"
|
|
2509
|
-
}).getFirstDescendantByKindOrThrow(SyntaxKind.ArrayLiteralExpression);
|
|
2510
|
-
}
|
|
2511
|
-
|
|
2512
|
-
//#endregion
|
|
2513
|
-
//#region src/helpers/addons/vite-pwa-setup.ts
|
|
2514
|
-
async function addPwaToViteConfig(viteConfigPath, projectName) {
|
|
2515
|
-
const sourceFile = tsProject$1.addSourceFileAtPathIfExists(viteConfigPath);
|
|
2516
|
-
if (!sourceFile) throw new Error("vite config not found");
|
|
2517
|
-
if (!sourceFile.getImportDeclarations().some((imp) => imp.getModuleSpecifierValue() === "vite-plugin-pwa")) sourceFile.insertImportDeclaration(0, {
|
|
2518
|
-
namedImports: ["VitePWA"],
|
|
2519
|
-
moduleSpecifier: "vite-plugin-pwa"
|
|
2520
|
-
});
|
|
2521
|
-
const defineCall = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression).find((expr) => {
|
|
2522
|
-
const expression = expr.getExpression();
|
|
2523
|
-
return Node.isIdentifier(expression) && expression.getText() === "defineConfig";
|
|
2524
|
-
});
|
|
2525
|
-
if (!defineCall) throw new Error("Could not find defineConfig call in vite config");
|
|
2526
|
-
const configObject = defineCall.getArguments()[0];
|
|
2527
|
-
if (!configObject) throw new Error("defineConfig argument is not an object literal");
|
|
2528
|
-
const pluginsArray = ensureArrayProperty(configObject, "plugins");
|
|
2529
|
-
if (!pluginsArray.getElements().some((el) => el.getText().startsWith("VitePWA("))) pluginsArray.addElement(`VitePWA({
|
|
2530
|
-
registerType: "autoUpdate",
|
|
2531
|
-
manifest: {
|
|
2532
|
-
name: "${projectName}",
|
|
2533
|
-
short_name: "${projectName}",
|
|
2534
|
-
description: "${projectName} - PWA Application",
|
|
2535
|
-
theme_color: "#0c0c0c",
|
|
2536
|
-
},
|
|
2537
|
-
pwaAssets: { disabled: false, config: true },
|
|
2538
|
-
devOptions: { enabled: true },
|
|
2539
|
-
})`);
|
|
2540
|
-
await tsProject$1.save();
|
|
2541
|
-
}
|
|
2542
|
-
|
|
2543
2581
|
//#endregion
|
|
2544
2582
|
//#region src/helpers/addons/addons-setup.ts
|
|
2545
2583
|
async function setupAddons(config, isAddCommand = false) {
|
|
@@ -2768,38 +2806,6 @@ handlebars.registerHelper("or", (...args) => {
|
|
|
2768
2806
|
});
|
|
2769
2807
|
handlebars.registerHelper("includes", (array, value) => Array.isArray(array) && array.includes(value));
|
|
2770
2808
|
|
|
2771
|
-
//#endregion
|
|
2772
|
-
//#region src/helpers/deployment/alchemy/env-dts-setup.ts
|
|
2773
|
-
const tsProject = new Project({
|
|
2774
|
-
useInMemoryFileSystem: false,
|
|
2775
|
-
skipAddingFilesFromTsConfig: true
|
|
2776
|
-
});
|
|
2777
|
-
function determineImportPath(envDtsPath, projectDir, config) {
|
|
2778
|
-
const { webDeploy, serverDeploy, backend } = config;
|
|
2779
|
-
const isBackendSelf = backend === "self";
|
|
2780
|
-
let alchemyRunPath;
|
|
2781
|
-
if (webDeploy === "alchemy" && (serverDeploy === "alchemy" || isBackendSelf)) if (isBackendSelf) alchemyRunPath = path.join(projectDir, "apps/web/alchemy.run.ts");
|
|
2782
|
-
else alchemyRunPath = path.join(projectDir, "alchemy.run.ts");
|
|
2783
|
-
else if (webDeploy === "alchemy") alchemyRunPath = path.join(projectDir, "apps/web/alchemy.run.ts");
|
|
2784
|
-
else if (serverDeploy === "alchemy") alchemyRunPath = path.join(projectDir, "apps/server/alchemy.run.ts");
|
|
2785
|
-
else alchemyRunPath = path.join(projectDir, "alchemy.run.ts");
|
|
2786
|
-
const relativePath = path.relative(path.dirname(envDtsPath), alchemyRunPath.replace(/\.ts$/, ""));
|
|
2787
|
-
return (relativePath.startsWith(".") ? relativePath : `./${relativePath}`).replace(/\\/g, "/");
|
|
2788
|
-
}
|
|
2789
|
-
async function setupEnvDtsImport(envDtsPath, projectDir, config) {
|
|
2790
|
-
if (!await fs.pathExists(envDtsPath)) return;
|
|
2791
|
-
const importPath = determineImportPath(envDtsPath, projectDir, config);
|
|
2792
|
-
const sourceFile = tsProject.addSourceFileAtPath(envDtsPath);
|
|
2793
|
-
if (!sourceFile.getImportDeclarations().some((imp) => imp.getModuleSpecifierValue() === importPath && imp.getNamedImports().some((named) => named.getName() === "server"))) sourceFile.insertImportDeclaration(0, {
|
|
2794
|
-
moduleSpecifier: importPath,
|
|
2795
|
-
namedImports: [{
|
|
2796
|
-
name: "server",
|
|
2797
|
-
isTypeOnly: true
|
|
2798
|
-
}]
|
|
2799
|
-
});
|
|
2800
|
-
await sourceFile.save();
|
|
2801
|
-
}
|
|
2802
|
-
|
|
2803
2809
|
//#endregion
|
|
2804
2810
|
//#region src/helpers/core/template-manager.ts
|
|
2805
2811
|
async function processAndCopyFiles(sourcePattern, baseSourceDir, destDir, context, overwrite = true, ignorePatterns) {
|
|
@@ -2904,7 +2910,7 @@ async function setupFrontendTemplates(projectDir, context) {
|
|
|
2904
2910
|
}
|
|
2905
2911
|
}
|
|
2906
2912
|
}
|
|
2907
|
-
async function setupApiPackage(projectDir, context) {
|
|
2913
|
+
async function setupApiPackage$1(projectDir, context) {
|
|
2908
2914
|
if (context.api === "none") return;
|
|
2909
2915
|
const apiPackageDir = path.join(projectDir, "packages/api");
|
|
2910
2916
|
await fs.ensureDir(apiPackageDir);
|
|
@@ -2917,7 +2923,7 @@ async function setupConfigPackage(projectDir, context) {
|
|
|
2917
2923
|
const configBaseDir = path.join(PKG_ROOT, "templates/packages/config");
|
|
2918
2924
|
if (await fs.pathExists(configBaseDir)) await processAndCopyFiles("**/*", configBaseDir, configPackageDir, context);
|
|
2919
2925
|
}
|
|
2920
|
-
async function setupDbPackage(projectDir, context) {
|
|
2926
|
+
async function setupDbPackage$1(projectDir, context) {
|
|
2921
2927
|
if (context.database === "none" || context.orm === "none") return;
|
|
2922
2928
|
const dbPackageDir = path.join(projectDir, "packages/db");
|
|
2923
2929
|
await fs.ensureDir(dbPackageDir);
|
|
@@ -2944,21 +2950,78 @@ async function setupServerApp(projectDir, context) {
|
|
|
2944
2950
|
const frameworkSrcDir = path.join(PKG_ROOT, `templates/backend/server/${context.backend}`);
|
|
2945
2951
|
if (await fs.pathExists(frameworkSrcDir)) await processAndCopyFiles("**/*", frameworkSrcDir, serverAppDir, context, true);
|
|
2946
2952
|
}
|
|
2953
|
+
async function setupEnvPackage$1(projectDir, context) {
|
|
2954
|
+
const hasWebFrontend$1 = context.frontend.some((f) => [
|
|
2955
|
+
"tanstack-router",
|
|
2956
|
+
"react-router",
|
|
2957
|
+
"tanstack-start",
|
|
2958
|
+
"next",
|
|
2959
|
+
"nuxt",
|
|
2960
|
+
"svelte",
|
|
2961
|
+
"solid"
|
|
2962
|
+
].includes(f));
|
|
2963
|
+
const hasNative = context.frontend.some((f) => [
|
|
2964
|
+
"native-bare",
|
|
2965
|
+
"native-uniwind",
|
|
2966
|
+
"native-unistyles"
|
|
2967
|
+
].includes(f));
|
|
2968
|
+
if (!hasWebFrontend$1 && !hasNative && context.backend === "none") return;
|
|
2969
|
+
const envPackageDir = path.join(projectDir, "packages/env");
|
|
2970
|
+
await fs.ensureDir(envPackageDir);
|
|
2971
|
+
const envBaseDir = path.join(PKG_ROOT, "templates/packages/env");
|
|
2972
|
+
const packageJsonSrc = path.join(envBaseDir, "package.json.hbs");
|
|
2973
|
+
if (await fs.pathExists(packageJsonSrc)) await processAndCopyFiles("package.json.hbs", envBaseDir, envPackageDir, context);
|
|
2974
|
+
const tsconfigSrc = path.join(envBaseDir, "tsconfig.json.hbs");
|
|
2975
|
+
if (await fs.pathExists(tsconfigSrc)) await processAndCopyFiles("tsconfig.json.hbs", envBaseDir, envPackageDir, context);
|
|
2976
|
+
const needsServerEnv = context.backend !== "none" && context.backend !== "convex";
|
|
2977
|
+
if (needsServerEnv) {
|
|
2978
|
+
const serverSrc = path.join(envBaseDir, "src/server.ts.hbs");
|
|
2979
|
+
if (await fs.pathExists(serverSrc)) {
|
|
2980
|
+
await fs.ensureDir(path.join(envPackageDir, "src"));
|
|
2981
|
+
await processAndCopyFiles("src/server.ts.hbs", envBaseDir, envPackageDir, context);
|
|
2982
|
+
}
|
|
2983
|
+
}
|
|
2984
|
+
if (hasWebFrontend$1) {
|
|
2985
|
+
const webSrc = path.join(envBaseDir, "src/web.ts.hbs");
|
|
2986
|
+
if (await fs.pathExists(webSrc)) {
|
|
2987
|
+
await fs.ensureDir(path.join(envPackageDir, "src"));
|
|
2988
|
+
await processAndCopyFiles("src/web.ts.hbs", envBaseDir, envPackageDir, context);
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
if (hasNative) {
|
|
2992
|
+
const nativeSrc = path.join(envBaseDir, "src/native.ts.hbs");
|
|
2993
|
+
if (await fs.pathExists(nativeSrc)) {
|
|
2994
|
+
await fs.ensureDir(path.join(envPackageDir, "src"));
|
|
2995
|
+
await processAndCopyFiles("src/native.ts.hbs", envBaseDir, envPackageDir, context);
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
const packageJsonPath = path.join(envPackageDir, "package.json");
|
|
2999
|
+
if (await fs.pathExists(packageJsonPath)) {
|
|
3000
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
3001
|
+
const exports = {};
|
|
3002
|
+
if (needsServerEnv) exports["./server"] = "./src/server.ts";
|
|
3003
|
+
if (hasWebFrontend$1) exports["./web"] = "./src/web.ts";
|
|
3004
|
+
if (hasNative) exports["./native"] = "./src/native.ts";
|
|
3005
|
+
packageJson.exports = exports;
|
|
3006
|
+
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
3007
|
+
}
|
|
3008
|
+
}
|
|
2947
3009
|
async function setupBackendFramework(projectDir, context) {
|
|
2948
3010
|
await setupConfigPackage(projectDir, context);
|
|
3011
|
+
await setupEnvPackage$1(projectDir, context);
|
|
2949
3012
|
if (context.backend === "none") return;
|
|
2950
3013
|
if (context.backend === "convex") {
|
|
2951
3014
|
await setupConvexBackend(projectDir, context);
|
|
2952
3015
|
return;
|
|
2953
3016
|
}
|
|
2954
3017
|
if (context.backend === "self") {
|
|
2955
|
-
await setupApiPackage(projectDir, context);
|
|
2956
|
-
await setupDbPackage(projectDir, context);
|
|
3018
|
+
await setupApiPackage$1(projectDir, context);
|
|
3019
|
+
await setupDbPackage$1(projectDir, context);
|
|
2957
3020
|
return;
|
|
2958
3021
|
}
|
|
2959
3022
|
await setupServerApp(projectDir, context);
|
|
2960
|
-
await setupApiPackage(projectDir, context);
|
|
2961
|
-
await setupDbPackage(projectDir, context);
|
|
3023
|
+
await setupApiPackage$1(projectDir, context);
|
|
3024
|
+
await setupDbPackage$1(projectDir, context);
|
|
2962
3025
|
}
|
|
2963
3026
|
async function setupAuthTemplate(projectDir, context) {
|
|
2964
3027
|
if (!context.auth || context.auth === "none") return;
|
|
@@ -3287,35 +3350,22 @@ async function setupDockerComposeTemplates(projectDir, context) {
|
|
|
3287
3350
|
}
|
|
3288
3351
|
async function setupDeploymentTemplates(projectDir, context) {
|
|
3289
3352
|
const isBackendSelf = context.backend === "self";
|
|
3290
|
-
if (context.webDeploy === "
|
|
3291
|
-
const
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
if (!isBackendSelf) await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
|
|
3304
|
-
}
|
|
3305
|
-
}
|
|
3306
|
-
if (context.serverDeploy === "alchemy" && !isBackendSelf) {
|
|
3307
|
-
const serverAppDir = path.join(projectDir, "apps/server");
|
|
3308
|
-
if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(serverAppDir)) {
|
|
3309
|
-
await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
|
|
3310
|
-
const envDtsPath = path.join(serverAppDir, "env.d.ts");
|
|
3311
|
-
await processTemplate(path.join(alchemyTemplateSrc, "env.d.ts.hbs"), envDtsPath, context);
|
|
3312
|
-
await setupEnvDtsImport(envDtsPath, projectDir, context);
|
|
3313
|
-
await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
|
|
3314
|
-
}
|
|
3315
|
-
}
|
|
3353
|
+
if (context.webDeploy === "cloudflare" || context.serverDeploy === "cloudflare") {
|
|
3354
|
+
const infraTemplateSrc = path.join(PKG_ROOT, "templates/packages/infra");
|
|
3355
|
+
const infraDir = path.join(projectDir, "packages/infra");
|
|
3356
|
+
if (await fs.pathExists(infraTemplateSrc)) {
|
|
3357
|
+
await fs.ensureDir(infraDir);
|
|
3358
|
+
await processAndCopyFiles("package.json.hbs", infraTemplateSrc, infraDir, context);
|
|
3359
|
+
await processAndCopyFiles("alchemy.run.ts.hbs", infraTemplateSrc, infraDir, context);
|
|
3360
|
+
}
|
|
3361
|
+
if (!isBackendSelf) {
|
|
3362
|
+
const envTemplateSrc = path.join(PKG_ROOT, "templates/packages/env");
|
|
3363
|
+
const envDir = path.join(projectDir, "packages/env");
|
|
3364
|
+
const envDtsTemplatePath = path.join(envTemplateSrc, "env.d.ts.hbs");
|
|
3365
|
+
if (await fs.pathExists(envDtsTemplatePath)) await processTemplate(envDtsTemplatePath, path.join(envDir, "env.d.ts"), context);
|
|
3316
3366
|
}
|
|
3317
3367
|
}
|
|
3318
|
-
if (context.webDeploy !== "none" && context.webDeploy !== "
|
|
3368
|
+
if (context.webDeploy !== "none" && context.webDeploy !== "cloudflare") {
|
|
3319
3369
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3320
3370
|
if (await fs.pathExists(webAppDir)) {
|
|
3321
3371
|
const frontends = context.frontend;
|
|
@@ -3334,7 +3384,7 @@ async function setupDeploymentTemplates(projectDir, context) {
|
|
|
3334
3384
|
}
|
|
3335
3385
|
}
|
|
3336
3386
|
}
|
|
3337
|
-
if (context.serverDeploy !== "none" && context.serverDeploy !== "
|
|
3387
|
+
if (context.serverDeploy !== "none" && context.serverDeploy !== "cloudflare" && !isBackendSelf) {
|
|
3338
3388
|
const serverAppDir = path.join(projectDir, "apps/server");
|
|
3339
3389
|
if (await fs.pathExists(serverAppDir)) {
|
|
3340
3390
|
const deployTemplateSrc = path.join(PKG_ROOT, `templates/deploy/${context.serverDeploy}/server`);
|
|
@@ -3342,26 +3392,6 @@ async function setupDeploymentTemplates(projectDir, context) {
|
|
|
3342
3392
|
}
|
|
3343
3393
|
}
|
|
3344
3394
|
}
|
|
3345
|
-
async function addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc) {
|
|
3346
|
-
for (const packageName of [
|
|
3347
|
-
"packages/api",
|
|
3348
|
-
"packages/auth",
|
|
3349
|
-
"packages/db"
|
|
3350
|
-
]) {
|
|
3351
|
-
const packageDir = path.join(projectDir, packageName);
|
|
3352
|
-
if (await fs.pathExists(packageDir)) {
|
|
3353
|
-
const envDtsPath = path.join(packageDir, "env.d.ts");
|
|
3354
|
-
await processTemplate(path.join(alchemyTemplateSrc, "env.d.ts.hbs"), envDtsPath, context);
|
|
3355
|
-
await setupEnvDtsImport(envDtsPath, projectDir, context);
|
|
3356
|
-
}
|
|
3357
|
-
}
|
|
3358
|
-
const serverAppDir = path.join(projectDir, "apps/server");
|
|
3359
|
-
if (await fs.pathExists(serverAppDir)) {
|
|
3360
|
-
const envDtsPath = path.join(serverAppDir, "env.d.ts");
|
|
3361
|
-
await processTemplate(path.join(alchemyTemplateSrc, "env.d.ts.hbs"), envDtsPath, context);
|
|
3362
|
-
await setupEnvDtsImport(envDtsPath, projectDir, context);
|
|
3363
|
-
}
|
|
3364
|
-
}
|
|
3365
3395
|
|
|
3366
3396
|
//#endregion
|
|
3367
3397
|
//#region src/helpers/core/add-addons.ts
|
|
@@ -3410,50 +3440,9 @@ async function addAddonsToProject(input) {
|
|
|
3410
3440
|
}
|
|
3411
3441
|
}
|
|
3412
3442
|
|
|
3413
|
-
//#endregion
|
|
3414
|
-
//#region src/helpers/deployment/server-deploy-setup.ts
|
|
3415
|
-
async function setupServerDeploy(config) {
|
|
3416
|
-
const { serverDeploy, webDeploy, projectDir } = config;
|
|
3417
|
-
if (serverDeploy === "none") return;
|
|
3418
|
-
if (serverDeploy === "alchemy" && webDeploy === "alchemy") return;
|
|
3419
|
-
const serverDir = path.join(projectDir, "apps/server");
|
|
3420
|
-
if (!await fs.pathExists(serverDir)) return;
|
|
3421
|
-
if (serverDeploy === "alchemy") await setupAlchemyServerDeploy(serverDir, projectDir);
|
|
3422
|
-
}
|
|
3423
|
-
async function setupAlchemyServerDeploy(serverDir, projectDir) {
|
|
3424
|
-
if (!await fs.pathExists(serverDir)) return;
|
|
3425
|
-
await addPackageDependency({
|
|
3426
|
-
devDependencies: [
|
|
3427
|
-
"alchemy",
|
|
3428
|
-
"wrangler",
|
|
3429
|
-
"@types/node",
|
|
3430
|
-
"@cloudflare/workers-types"
|
|
3431
|
-
],
|
|
3432
|
-
projectDir: serverDir
|
|
3433
|
-
});
|
|
3434
|
-
if (projectDir) await addAlchemyPackagesDependencies$1(projectDir);
|
|
3435
|
-
const packageJsonPath = path.join(serverDir, "package.json");
|
|
3436
|
-
if (await fs.pathExists(packageJsonPath)) {
|
|
3437
|
-
const packageJson = await fs.readJson(packageJsonPath);
|
|
3438
|
-
packageJson.scripts = {
|
|
3439
|
-
...packageJson.scripts,
|
|
3440
|
-
dev: "alchemy dev",
|
|
3441
|
-
deploy: "alchemy deploy",
|
|
3442
|
-
destroy: "alchemy destroy"
|
|
3443
|
-
};
|
|
3444
|
-
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
3445
|
-
}
|
|
3446
|
-
}
|
|
3447
|
-
async function addAlchemyPackagesDependencies$1(projectDir) {
|
|
3448
|
-
await addPackageDependency({
|
|
3449
|
-
devDependencies: ["@cloudflare/workers-types"],
|
|
3450
|
-
projectDir
|
|
3451
|
-
});
|
|
3452
|
-
}
|
|
3453
|
-
|
|
3454
3443
|
//#endregion
|
|
3455
3444
|
//#region src/helpers/deployment/alchemy/alchemy-next-setup.ts
|
|
3456
|
-
async function setupNextAlchemyDeploy(projectDir, _packageManager,
|
|
3445
|
+
async function setupNextAlchemyDeploy(projectDir, _packageManager, _options) {
|
|
3457
3446
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3458
3447
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3459
3448
|
await addPackageDependency({
|
|
@@ -3465,17 +3454,6 @@ async function setupNextAlchemyDeploy(projectDir, _packageManager, options) {
|
|
|
3465
3454
|
],
|
|
3466
3455
|
projectDir: webAppDir
|
|
3467
3456
|
});
|
|
3468
|
-
const pkgPath = path.join(webAppDir, "package.json");
|
|
3469
|
-
if (await fs.pathExists(pkgPath)) {
|
|
3470
|
-
const pkg = await fs.readJson(pkgPath);
|
|
3471
|
-
if (!options?.skipAppScripts) pkg.scripts = {
|
|
3472
|
-
...pkg.scripts,
|
|
3473
|
-
dev: "alchemy dev",
|
|
3474
|
-
deploy: "alchemy deploy",
|
|
3475
|
-
destroy: "alchemy destroy"
|
|
3476
|
-
};
|
|
3477
|
-
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3478
|
-
}
|
|
3479
3457
|
const openNextConfigPath = path.join(webAppDir, "open-next.config.ts");
|
|
3480
3458
|
await fs.writeFile(openNextConfigPath, `import { defineCloudflareConfig } from "@opennextjs/cloudflare";
|
|
3481
3459
|
|
|
@@ -3489,7 +3467,7 @@ export default defineCloudflareConfig({});
|
|
|
3489
3467
|
|
|
3490
3468
|
//#endregion
|
|
3491
3469
|
//#region src/helpers/deployment/alchemy/alchemy-nuxt-setup.ts
|
|
3492
|
-
async function setupNuxtAlchemyDeploy(projectDir, _packageManager,
|
|
3470
|
+
async function setupNuxtAlchemyDeploy(projectDir, _packageManager, _options) {
|
|
3493
3471
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3494
3472
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3495
3473
|
await addPackageDependency({
|
|
@@ -3500,17 +3478,6 @@ async function setupNuxtAlchemyDeploy(projectDir, _packageManager, options) {
|
|
|
3500
3478
|
],
|
|
3501
3479
|
projectDir: webAppDir
|
|
3502
3480
|
});
|
|
3503
|
-
const pkgPath = path.join(webAppDir, "package.json");
|
|
3504
|
-
if (await fs.pathExists(pkgPath)) {
|
|
3505
|
-
const pkg = await fs.readJson(pkgPath);
|
|
3506
|
-
if (!options?.skipAppScripts) pkg.scripts = {
|
|
3507
|
-
...pkg.scripts,
|
|
3508
|
-
dev: "alchemy dev",
|
|
3509
|
-
deploy: "alchemy deploy",
|
|
3510
|
-
destroy: "alchemy destroy"
|
|
3511
|
-
};
|
|
3512
|
-
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3513
|
-
}
|
|
3514
3481
|
const nuxtConfigPath = path.join(webAppDir, "nuxt.config.ts");
|
|
3515
3482
|
if (!await fs.pathExists(nuxtConfigPath)) return;
|
|
3516
3483
|
try {
|
|
@@ -3555,68 +3522,35 @@ async function setupNuxtAlchemyDeploy(projectDir, _packageManager, options) {
|
|
|
3555
3522
|
|
|
3556
3523
|
//#endregion
|
|
3557
3524
|
//#region src/helpers/deployment/alchemy/alchemy-react-router-setup.ts
|
|
3558
|
-
async function setupReactRouterAlchemyDeploy(projectDir, _packageManager,
|
|
3525
|
+
async function setupReactRouterAlchemyDeploy(projectDir, _packageManager, _options) {
|
|
3559
3526
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3560
3527
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3561
3528
|
await addPackageDependency({
|
|
3562
3529
|
devDependencies: ["alchemy"],
|
|
3563
3530
|
projectDir: webAppDir
|
|
3564
3531
|
});
|
|
3565
|
-
const pkgPath = path.join(webAppDir, "package.json");
|
|
3566
|
-
if (await fs.pathExists(pkgPath)) {
|
|
3567
|
-
const pkg = await fs.readJson(pkgPath);
|
|
3568
|
-
if (!options?.skipAppScripts) pkg.scripts = {
|
|
3569
|
-
...pkg.scripts,
|
|
3570
|
-
dev: "alchemy dev",
|
|
3571
|
-
deploy: "alchemy deploy",
|
|
3572
|
-
destroy: "alchemy destroy"
|
|
3573
|
-
};
|
|
3574
|
-
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3575
|
-
}
|
|
3576
3532
|
}
|
|
3577
3533
|
|
|
3578
3534
|
//#endregion
|
|
3579
3535
|
//#region src/helpers/deployment/alchemy/alchemy-solid-setup.ts
|
|
3580
|
-
async function setupSolidAlchemyDeploy(projectDir, _packageManager,
|
|
3536
|
+
async function setupSolidAlchemyDeploy(projectDir, _packageManager, _options) {
|
|
3581
3537
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3582
3538
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3583
3539
|
await addPackageDependency({
|
|
3584
3540
|
devDependencies: ["alchemy"],
|
|
3585
3541
|
projectDir: webAppDir
|
|
3586
3542
|
});
|
|
3587
|
-
const pkgPath = path.join(webAppDir, "package.json");
|
|
3588
|
-
if (await fs.pathExists(pkgPath)) {
|
|
3589
|
-
const pkg = await fs.readJson(pkgPath);
|
|
3590
|
-
if (!options?.skipAppScripts) pkg.scripts = {
|
|
3591
|
-
...pkg.scripts,
|
|
3592
|
-
dev: "alchemy dev",
|
|
3593
|
-
deploy: "alchemy deploy",
|
|
3594
|
-
destroy: "alchemy destroy"
|
|
3595
|
-
};
|
|
3596
|
-
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3597
|
-
}
|
|
3598
3543
|
}
|
|
3599
3544
|
|
|
3600
3545
|
//#endregion
|
|
3601
3546
|
//#region src/helpers/deployment/alchemy/alchemy-svelte-setup.ts
|
|
3602
|
-
async function setupSvelteAlchemyDeploy(projectDir, _packageManager,
|
|
3547
|
+
async function setupSvelteAlchemyDeploy(projectDir, _packageManager, _options) {
|
|
3603
3548
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3604
3549
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3605
3550
|
await addPackageDependency({
|
|
3606
3551
|
devDependencies: ["alchemy", "@sveltejs/adapter-cloudflare"],
|
|
3607
3552
|
projectDir: webAppDir
|
|
3608
3553
|
});
|
|
3609
|
-
const pkgPath = path.join(webAppDir, "package.json");
|
|
3610
|
-
if (await fs.pathExists(pkgPath)) {
|
|
3611
|
-
const pkg = await fs.readJson(pkgPath);
|
|
3612
|
-
if (!options?.skipAppScripts) pkg.scripts = {
|
|
3613
|
-
...pkg.scripts,
|
|
3614
|
-
dev: "alchemy dev",
|
|
3615
|
-
deploy: "alchemy deploy",
|
|
3616
|
-
destroy: "alchemy destroy"
|
|
3617
|
-
};
|
|
3618
|
-
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3619
|
-
}
|
|
3620
3554
|
const svelteConfigPath = path.join(webAppDir, "svelte.config.js");
|
|
3621
3555
|
if (!await fs.pathExists(svelteConfigPath)) return;
|
|
3622
3556
|
try {
|
|
@@ -3665,46 +3599,24 @@ function updateAdapterInConfig(configObject) {
|
|
|
3665
3599
|
|
|
3666
3600
|
//#endregion
|
|
3667
3601
|
//#region src/helpers/deployment/alchemy/alchemy-tanstack-router-setup.ts
|
|
3668
|
-
async function setupTanStackRouterAlchemyDeploy(projectDir, _packageManager,
|
|
3602
|
+
async function setupTanStackRouterAlchemyDeploy(projectDir, _packageManager, _options) {
|
|
3669
3603
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3670
3604
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3671
3605
|
await addPackageDependency({
|
|
3672
3606
|
devDependencies: ["alchemy"],
|
|
3673
3607
|
projectDir: webAppDir
|
|
3674
3608
|
});
|
|
3675
|
-
const pkgPath = path.join(webAppDir, "package.json");
|
|
3676
|
-
if (await fs.pathExists(pkgPath)) {
|
|
3677
|
-
const pkg = await fs.readJson(pkgPath);
|
|
3678
|
-
if (!options?.skipAppScripts) pkg.scripts = {
|
|
3679
|
-
...pkg.scripts,
|
|
3680
|
-
dev: "alchemy dev",
|
|
3681
|
-
deploy: "alchemy deploy",
|
|
3682
|
-
destroy: "alchemy destroy"
|
|
3683
|
-
};
|
|
3684
|
-
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3685
|
-
}
|
|
3686
3609
|
}
|
|
3687
3610
|
|
|
3688
3611
|
//#endregion
|
|
3689
3612
|
//#region src/helpers/deployment/alchemy/alchemy-tanstack-start-setup.ts
|
|
3690
|
-
async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager,
|
|
3613
|
+
async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, _options) {
|
|
3691
3614
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3692
3615
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3693
3616
|
await addPackageDependency({
|
|
3694
3617
|
devDependencies: ["alchemy", "@cloudflare/vite-plugin"],
|
|
3695
3618
|
projectDir: webAppDir
|
|
3696
3619
|
});
|
|
3697
|
-
const pkgPath = path.join(webAppDir, "package.json");
|
|
3698
|
-
if (await fs.pathExists(pkgPath)) {
|
|
3699
|
-
const pkg = await fs.readJson(pkgPath);
|
|
3700
|
-
if (!options?.skipAppScripts) pkg.scripts = {
|
|
3701
|
-
...pkg.scripts,
|
|
3702
|
-
dev: "alchemy dev",
|
|
3703
|
-
deploy: "alchemy deploy",
|
|
3704
|
-
destroy: "alchemy destroy"
|
|
3705
|
-
};
|
|
3706
|
-
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3707
|
-
}
|
|
3708
3620
|
const viteConfigPath = path.join(webAppDir, "vite.config.ts");
|
|
3709
3621
|
if (await fs.pathExists(viteConfigPath)) try {
|
|
3710
3622
|
const project = new Project({ manipulationSettings: {
|
|
@@ -3745,22 +3657,16 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
|
|
|
3745
3657
|
|
|
3746
3658
|
//#endregion
|
|
3747
3659
|
//#region src/helpers/deployment/alchemy/alchemy-combined-setup.ts
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
if (await fs.pathExists(rootPkgPath)) {
|
|
3755
|
-
const pkg = await fs.readJson(rootPkgPath);
|
|
3756
|
-
pkg.scripts = {
|
|
3757
|
-
...pkg.scripts,
|
|
3758
|
-
deploy: "alchemy deploy",
|
|
3759
|
-
destroy: "alchemy destroy",
|
|
3760
|
-
dev: "alchemy dev"
|
|
3761
|
-
};
|
|
3762
|
-
await fs.writeJson(rootPkgPath, pkg, { spaces: 2 });
|
|
3660
|
+
function getInfraFilter(packageManager, hasTurborepo, infraWorkspace) {
|
|
3661
|
+
if (hasTurborepo) return (script) => `turbo -F ${infraWorkspace} ${script}`;
|
|
3662
|
+
switch (packageManager) {
|
|
3663
|
+
case "pnpm": return (script) => `pnpm --filter ${infraWorkspace} ${script}`;
|
|
3664
|
+
case "npm": return (script) => `npm run ${script} --workspace ${infraWorkspace}`;
|
|
3665
|
+
case "bun": return (script) => `bun run --filter ${infraWorkspace} ${script}`;
|
|
3763
3666
|
}
|
|
3667
|
+
}
|
|
3668
|
+
async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
|
|
3669
|
+
await setupInfraScripts(projectDir, packageManager, config);
|
|
3764
3670
|
const serverDir = path.join(projectDir, "apps/server");
|
|
3765
3671
|
if (await fs.pathExists(serverDir)) await setupAlchemyServerDeploy(serverDir, projectDir);
|
|
3766
3672
|
const frontend = config.frontend;
|
|
@@ -3779,6 +3685,77 @@ async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
|
|
|
3779
3685
|
else if (isReactRouter) await setupReactRouterAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
|
|
3780
3686
|
else if (isSolid) await setupSolidAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
|
|
3781
3687
|
}
|
|
3688
|
+
async function setupInfraScripts(projectDir, packageManager, config) {
|
|
3689
|
+
const projectName = config.projectName;
|
|
3690
|
+
const hasTurborepo = config.addons.includes("turborepo");
|
|
3691
|
+
const infraWorkspace = `@${projectName}/infra`;
|
|
3692
|
+
const rootPkgPath = path.join(projectDir, "package.json");
|
|
3693
|
+
if (await fs.pathExists(rootPkgPath)) {
|
|
3694
|
+
const pkg = await fs.readJson(rootPkgPath);
|
|
3695
|
+
const filter = getInfraFilter(packageManager, hasTurborepo, infraWorkspace);
|
|
3696
|
+
pkg.scripts = {
|
|
3697
|
+
...pkg.scripts,
|
|
3698
|
+
deploy: filter("deploy"),
|
|
3699
|
+
destroy: filter("destroy")
|
|
3700
|
+
};
|
|
3701
|
+
await fs.writeJson(rootPkgPath, pkg, { spaces: 2 });
|
|
3702
|
+
}
|
|
3703
|
+
if (config.serverDeploy === "cloudflare") {
|
|
3704
|
+
const serverPkgPath = path.join(projectDir, "apps/server/package.json");
|
|
3705
|
+
if (await fs.pathExists(serverPkgPath)) {
|
|
3706
|
+
const serverPkg = await fs.readJson(serverPkgPath);
|
|
3707
|
+
if (serverPkg.scripts?.dev) {
|
|
3708
|
+
serverPkg.scripts["dev:bare"] = serverPkg.scripts.dev;
|
|
3709
|
+
delete serverPkg.scripts.dev;
|
|
3710
|
+
await fs.writeJson(serverPkgPath, serverPkg, { spaces: 2 });
|
|
3711
|
+
}
|
|
3712
|
+
}
|
|
3713
|
+
}
|
|
3714
|
+
if (config.webDeploy === "cloudflare") {
|
|
3715
|
+
const webPkgPath = path.join(projectDir, "apps/web/package.json");
|
|
3716
|
+
if (await fs.pathExists(webPkgPath)) {
|
|
3717
|
+
const webPkg = await fs.readJson(webPkgPath);
|
|
3718
|
+
if (webPkg.scripts?.dev) {
|
|
3719
|
+
webPkg.scripts["dev:bare"] = webPkg.scripts.dev;
|
|
3720
|
+
delete webPkg.scripts.dev;
|
|
3721
|
+
await fs.writeJson(webPkgPath, webPkg, { spaces: 2 });
|
|
3722
|
+
}
|
|
3723
|
+
}
|
|
3724
|
+
}
|
|
3725
|
+
}
|
|
3726
|
+
|
|
3727
|
+
//#endregion
|
|
3728
|
+
//#region src/helpers/deployment/server-deploy-setup.ts
|
|
3729
|
+
async function setupServerDeploy(config) {
|
|
3730
|
+
const { serverDeploy, webDeploy, projectDir, packageManager } = config;
|
|
3731
|
+
if (serverDeploy === "none") return;
|
|
3732
|
+
if (serverDeploy === "cloudflare" && webDeploy === "cloudflare") return;
|
|
3733
|
+
const serverDir = path.join(projectDir, "apps/server");
|
|
3734
|
+
if (!await fs.pathExists(serverDir)) return;
|
|
3735
|
+
if (serverDeploy === "cloudflare") {
|
|
3736
|
+
await setupInfraScripts(projectDir, packageManager, config);
|
|
3737
|
+
await setupAlchemyServerDeploy(serverDir, projectDir);
|
|
3738
|
+
}
|
|
3739
|
+
}
|
|
3740
|
+
async function setupAlchemyServerDeploy(serverDir, projectDir) {
|
|
3741
|
+
if (!await fs.pathExists(serverDir)) return;
|
|
3742
|
+
await addPackageDependency({
|
|
3743
|
+
devDependencies: [
|
|
3744
|
+
"alchemy",
|
|
3745
|
+
"wrangler",
|
|
3746
|
+
"@types/node",
|
|
3747
|
+
"@cloudflare/workers-types"
|
|
3748
|
+
],
|
|
3749
|
+
projectDir: serverDir
|
|
3750
|
+
});
|
|
3751
|
+
if (projectDir) await addAlchemyPackagesDependencies$1(projectDir);
|
|
3752
|
+
}
|
|
3753
|
+
async function addAlchemyPackagesDependencies$1(projectDir) {
|
|
3754
|
+
await addPackageDependency({
|
|
3755
|
+
devDependencies: ["@cloudflare/workers-types"],
|
|
3756
|
+
projectDir
|
|
3757
|
+
});
|
|
3758
|
+
}
|
|
3782
3759
|
|
|
3783
3760
|
//#endregion
|
|
3784
3761
|
//#region src/helpers/deployment/web-deploy-setup.ts
|
|
@@ -3786,12 +3763,13 @@ async function setupWebDeploy(config) {
|
|
|
3786
3763
|
const { webDeploy, serverDeploy, frontend, projectDir } = config;
|
|
3787
3764
|
const { packageManager } = config;
|
|
3788
3765
|
if (webDeploy === "none") return;
|
|
3789
|
-
if (webDeploy !== "
|
|
3790
|
-
if (webDeploy === "
|
|
3766
|
+
if (webDeploy !== "cloudflare") return;
|
|
3767
|
+
if (webDeploy === "cloudflare" && serverDeploy === "cloudflare") {
|
|
3791
3768
|
await setupCombinedAlchemyDeploy(projectDir, packageManager, config);
|
|
3792
3769
|
await addAlchemyPackagesDependencies(projectDir);
|
|
3793
3770
|
return;
|
|
3794
3771
|
}
|
|
3772
|
+
await setupInfraScripts(projectDir, packageManager, config);
|
|
3795
3773
|
const isNext = frontend.includes("next");
|
|
3796
3774
|
const isNuxt = frontend.includes("nuxt");
|
|
3797
3775
|
const isSvelte = frontend.includes("svelte");
|
|
@@ -3880,7 +3858,9 @@ async function setupCatalogs(projectDir, options) {
|
|
|
3880
3858
|
"packages/db",
|
|
3881
3859
|
"packages/auth",
|
|
3882
3860
|
"packages/backend",
|
|
3883
|
-
"packages/config"
|
|
3861
|
+
"packages/config",
|
|
3862
|
+
"packages/env",
|
|
3863
|
+
"packages/infra"
|
|
3884
3864
|
];
|
|
3885
3865
|
const packagesInfo = [];
|
|
3886
3866
|
for (const pkgPath of packagePaths) {
|
|
@@ -4298,7 +4278,7 @@ async function setupBackendDependencies(config) {
|
|
|
4298
4278
|
//#region src/utils/better-auth-plugin-setup.ts
|
|
4299
4279
|
async function setupBetterAuthPlugins(projectDir, config) {
|
|
4300
4280
|
const authIndexPath = `${projectDir}/packages/auth/src/index.ts`;
|
|
4301
|
-
const authIndexFile = tsProject
|
|
4281
|
+
const authIndexFile = tsProject.addSourceFileAtPath(authIndexPath);
|
|
4302
4282
|
if (!authIndexFile) return;
|
|
4303
4283
|
const pluginsToAdd = [];
|
|
4304
4284
|
const importsToAdd = [];
|
|
@@ -4570,11 +4550,6 @@ async function setupEnvironmentVariables(config) {
|
|
|
4570
4550
|
value: backend === "convex" ? "https://<YOUR_CONVEX_URL>" : baseVar.value,
|
|
4571
4551
|
condition: backend === "convex" ? true : baseVar.write
|
|
4572
4552
|
}];
|
|
4573
|
-
if (hasNextJs) clientVars.push({
|
|
4574
|
-
key: "PORT",
|
|
4575
|
-
value: "3001",
|
|
4576
|
-
condition: true
|
|
4577
|
-
});
|
|
4578
4553
|
if (backend === "convex" && auth === "clerk") {
|
|
4579
4554
|
if (hasNextJs) clientVars.push({
|
|
4580
4555
|
key: "NEXT_PUBLIC_CLERK_FRONTEND_API_URL",
|
|
@@ -4710,7 +4685,7 @@ ${hasWebFrontend$1 ? "# npx convex env set SITE_URL http://localhost:3001\n" : "
|
|
|
4710
4685
|
databaseUrl = "mongodb://localhost:27017/mydatabase";
|
|
4711
4686
|
break;
|
|
4712
4687
|
case "sqlite":
|
|
4713
|
-
if (config.runtime === "workers" || webDeploy === "
|
|
4688
|
+
if (config.runtime === "workers" || webDeploy === "cloudflare" || serverDeploy === "cloudflare") databaseUrl = "http://127.0.0.1:8080";
|
|
4714
4689
|
else {
|
|
4715
4690
|
const dbAppDir = backend === "self" ? "apps/web" : "apps/server";
|
|
4716
4691
|
databaseUrl = `file:${path.join(config.projectDir, dbAppDir, "local.db")}`;
|
|
@@ -4758,33 +4733,13 @@ ${hasWebFrontend$1 ? "# npx convex env set SITE_URL http://localhost:3001\n" : "
|
|
|
4758
4733
|
const webDir = path.join(projectDir, "apps/web");
|
|
4759
4734
|
if (await fs.pathExists(webDir)) await addEnvVariablesToFile(path.join(webDir, ".env"), serverVars);
|
|
4760
4735
|
} else if (await fs.pathExists(serverDir)) await addEnvVariablesToFile(path.join(serverDir, ".env"), serverVars);
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
else if (isIndividualAlchemy) {
|
|
4769
|
-
if (webDeploy === "alchemy") {
|
|
4770
|
-
const webDir = path.join(projectDir, "apps/web");
|
|
4771
|
-
if (await fs.pathExists(webDir)) await addEnvVariablesToFile(path.join(webDir, ".env"), [{
|
|
4772
|
-
key: "ALCHEMY_PASSWORD",
|
|
4773
|
-
value: "please-change-this",
|
|
4774
|
-
condition: true
|
|
4775
|
-
}]);
|
|
4776
|
-
}
|
|
4777
|
-
if (serverDeploy === "alchemy") {
|
|
4778
|
-
const serverAlchemyVars = [{
|
|
4779
|
-
key: "ALCHEMY_PASSWORD",
|
|
4780
|
-
value: "please-change-this",
|
|
4781
|
-
condition: true
|
|
4782
|
-
}];
|
|
4783
|
-
if (backend === "self") {
|
|
4784
|
-
const webDir = path.join(projectDir, "apps/web");
|
|
4785
|
-
if (await fs.pathExists(webDir)) await addEnvVariablesToFile(path.join(webDir, ".env"), serverAlchemyVars);
|
|
4786
|
-
} else await addEnvVariablesToFile(path.join(serverDir, ".env"), serverAlchemyVars);
|
|
4787
|
-
}
|
|
4736
|
+
if (webDeploy === "cloudflare" && serverDeploy === "cloudflare" || webDeploy === "cloudflare" || serverDeploy === "cloudflare") {
|
|
4737
|
+
const infraDir = path.join(projectDir, "packages/infra");
|
|
4738
|
+
if (await fs.pathExists(infraDir)) await addEnvVariablesToFile(path.join(infraDir, ".env"), [{
|
|
4739
|
+
key: "ALCHEMY_PASSWORD",
|
|
4740
|
+
value: "please-change-this",
|
|
4741
|
+
condition: true
|
|
4742
|
+
}]);
|
|
4788
4743
|
}
|
|
4789
4744
|
}
|
|
4790
4745
|
|
|
@@ -4792,7 +4747,7 @@ ${hasWebFrontend$1 ? "# npx convex env set SITE_URL http://localhost:3001\n" : "
|
|
|
4792
4747
|
//#region src/helpers/database-providers/d1-setup.ts
|
|
4793
4748
|
async function setupCloudflareD1(config) {
|
|
4794
4749
|
const { projectDir, serverDeploy, orm, backend } = config;
|
|
4795
|
-
if (serverDeploy === "
|
|
4750
|
+
if (serverDeploy === "cloudflare" && orm === "prisma") {
|
|
4796
4751
|
const targetApp2 = backend === "self" ? "apps/web" : "apps/server";
|
|
4797
4752
|
await addEnvVariablesToFile(path.join(projectDir, targetApp2, ".env"), [{
|
|
4798
4753
|
key: "DATABASE_URL",
|
|
@@ -4838,8 +4793,8 @@ function getDatabaseUrl(database, projectName) {
|
|
|
4838
4793
|
//#region src/utils/command-exists.ts
|
|
4839
4794
|
async function commandExists(command) {
|
|
4840
4795
|
try {
|
|
4841
|
-
if (process.platform === "win32") return (await
|
|
4842
|
-
return (await
|
|
4796
|
+
if (process.platform === "win32") return (await $({ reject: false })`where ${command}`).exitCode === 0;
|
|
4797
|
+
return (await $({ reject: false })`which ${command}`).exitCode === 0;
|
|
4843
4798
|
} catch {
|
|
4844
4799
|
return false;
|
|
4845
4800
|
}
|
|
@@ -4866,11 +4821,10 @@ async function initMongoDBAtlas(serverDir) {
|
|
|
4866
4821
|
return null;
|
|
4867
4822
|
}
|
|
4868
4823
|
log.info("Running MongoDB Atlas setup...");
|
|
4869
|
-
await
|
|
4824
|
+
await $({
|
|
4870
4825
|
cwd: serverDir,
|
|
4871
|
-
shell: true,
|
|
4872
4826
|
stdio: "inherit"
|
|
4873
|
-
})
|
|
4827
|
+
})`atlas deployments setup`;
|
|
4874
4828
|
log.success("MongoDB Atlas deployment ready");
|
|
4875
4829
|
const connectionString = await text({
|
|
4876
4830
|
message: "Enter your MongoDB connection string:",
|
|
@@ -5008,9 +4962,9 @@ const NEON_REGIONS = [
|
|
|
5008
4962
|
async function executeNeonCommand(packageManager, commandArgsString, spinnerText) {
|
|
5009
4963
|
const s = spinner();
|
|
5010
4964
|
try {
|
|
5011
|
-
const
|
|
4965
|
+
const args = getPackageExecutionArgs(packageManager, commandArgsString);
|
|
5012
4966
|
if (spinnerText) s.start(spinnerText);
|
|
5013
|
-
const result = await
|
|
4967
|
+
const result = await $`${args}`;
|
|
5014
4968
|
if (spinnerText) s.stop(pc.green(spinnerText.replace("...", "").replace("ing ", "ed ").trim()));
|
|
5015
4969
|
return result;
|
|
5016
4970
|
} catch (error) {
|
|
@@ -5055,10 +5009,8 @@ async function setupWithNeonDb(projectDir, packageManager, backend) {
|
|
|
5055
5009
|
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
5056
5010
|
const targetDir = path.join(projectDir, targetApp);
|
|
5057
5011
|
await fs.ensureDir(targetDir);
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
cwd: targetDir
|
|
5061
|
-
});
|
|
5012
|
+
const packageArgs = getPackageExecutionArgs(packageManager, "get-db@latest --yes");
|
|
5013
|
+
await $({ cwd: targetDir })`${packageArgs}`;
|
|
5062
5014
|
s.stop(pc.green("Neon database created successfully!"));
|
|
5063
5015
|
return true;
|
|
5064
5016
|
} catch (error) {
|
|
@@ -5244,13 +5196,10 @@ async function setupWithCreateDb(serverDir, packageManager) {
|
|
|
5244
5196
|
initialValue: "ap-southeast-1"
|
|
5245
5197
|
});
|
|
5246
5198
|
if (isCancel(selectedRegion)) return null;
|
|
5247
|
-
const
|
|
5199
|
+
const createDbArgs = getPackageExecutionArgs(packageManager, `create-db@latest --json --region ${selectedRegion}`);
|
|
5248
5200
|
const s = spinner();
|
|
5249
5201
|
s.start("Creating Prisma Postgres database...");
|
|
5250
|
-
const { stdout } = await
|
|
5251
|
-
cwd: serverDir,
|
|
5252
|
-
shell: true
|
|
5253
|
-
});
|
|
5202
|
+
const { stdout } = await $({ cwd: serverDir })`${createDbArgs}`;
|
|
5254
5203
|
s.stop("Database created successfully!");
|
|
5255
5204
|
let createDbResponse;
|
|
5256
5205
|
try {
|
|
@@ -5377,11 +5326,11 @@ function extractDbUrl(output) {
|
|
|
5377
5326
|
async function initializeSupabase(serverDir, packageManager) {
|
|
5378
5327
|
log.info("Initializing Supabase project...");
|
|
5379
5328
|
try {
|
|
5380
|
-
|
|
5329
|
+
const supabaseInitArgs = getPackageExecutionArgs(packageManager, "supabase init");
|
|
5330
|
+
await $({
|
|
5381
5331
|
cwd: serverDir,
|
|
5382
|
-
stdio: "inherit"
|
|
5383
|
-
|
|
5384
|
-
});
|
|
5332
|
+
stdio: "inherit"
|
|
5333
|
+
})`${supabaseInitArgs}`;
|
|
5385
5334
|
log.success("Supabase project initialized");
|
|
5386
5335
|
return true;
|
|
5387
5336
|
} catch (error) {
|
|
@@ -5397,12 +5346,9 @@ async function initializeSupabase(serverDir, packageManager) {
|
|
|
5397
5346
|
}
|
|
5398
5347
|
async function startSupabase(serverDir, packageManager) {
|
|
5399
5348
|
log.info("Starting Supabase services (this may take a moment)...");
|
|
5400
|
-
const
|
|
5349
|
+
const supabaseStartArgs = getPackageExecutionArgs(packageManager, "supabase start");
|
|
5401
5350
|
try {
|
|
5402
|
-
const subprocess = execa(
|
|
5403
|
-
cwd: serverDir,
|
|
5404
|
-
shell: true
|
|
5405
|
-
});
|
|
5351
|
+
const subprocess = execa(supabaseStartArgs[0], supabaseStartArgs.slice(1), { cwd: serverDir });
|
|
5406
5352
|
let stdoutData = "";
|
|
5407
5353
|
if (subprocess.stdout) subprocess.stdout.on("data", (data) => {
|
|
5408
5354
|
const text$1 = data.toString();
|
|
@@ -6200,15 +6146,35 @@ BETTER_AUTH_URL={your-production-server-domain}
|
|
|
6200
6146
|
}
|
|
6201
6147
|
function generateDeploymentCommands(packageManagerRunCmd, webDeploy, serverDeploy) {
|
|
6202
6148
|
const lines = [];
|
|
6203
|
-
if (webDeploy === "
|
|
6204
|
-
lines.push("## Deployment (Alchemy)");
|
|
6205
|
-
if (webDeploy === "
|
|
6206
|
-
if (serverDeploy === "
|
|
6207
|
-
if (webDeploy === "
|
|
6149
|
+
if (webDeploy === "cloudflare" || serverDeploy === "cloudflare") {
|
|
6150
|
+
lines.push("## Deployment (Cloudflare via Alchemy)");
|
|
6151
|
+
if (webDeploy === "cloudflare" && serverDeploy !== "cloudflare") lines.push(`- Web dev: cd apps/web && ${packageManagerRunCmd} dev`, `- Web deploy: cd apps/web && ${packageManagerRunCmd} deploy`, `- Web destroy: cd apps/web && ${packageManagerRunCmd} destroy`);
|
|
6152
|
+
if (serverDeploy === "cloudflare" && webDeploy !== "cloudflare") lines.push(`- Server dev: cd apps/server && ${packageManagerRunCmd} dev`, `- Server deploy: cd apps/server && ${packageManagerRunCmd} deploy`, `- Server destroy: cd apps/server && ${packageManagerRunCmd} destroy`);
|
|
6153
|
+
if (webDeploy === "cloudflare" && serverDeploy === "cloudflare") lines.push(`- Dev: ${packageManagerRunCmd} dev`, `- Deploy: ${packageManagerRunCmd} deploy`, `- Destroy: ${packageManagerRunCmd} destroy`);
|
|
6208
6154
|
}
|
|
6209
6155
|
return lines.length ? `\n${lines.join("\n")}\n` : "";
|
|
6210
6156
|
}
|
|
6211
6157
|
|
|
6158
|
+
//#endregion
|
|
6159
|
+
//#region src/helpers/core/env-package-setup.ts
|
|
6160
|
+
async function setupEnvPackageDependencies(projectDir, options) {
|
|
6161
|
+
const envDir = path.join(projectDir, "packages/env");
|
|
6162
|
+
if (!await fs.pathExists(envDir)) return;
|
|
6163
|
+
await addPackageDependency({
|
|
6164
|
+
dependencies: getT3EnvDeps(options),
|
|
6165
|
+
projectDir: envDir
|
|
6166
|
+
});
|
|
6167
|
+
}
|
|
6168
|
+
function getT3EnvDeps(options) {
|
|
6169
|
+
const deps = ["zod"];
|
|
6170
|
+
const { frontend, backend, runtime } = options;
|
|
6171
|
+
if (frontend.includes("next")) deps.push("@t3-oss/env-nextjs");
|
|
6172
|
+
else if (frontend.includes("nuxt")) deps.push("@t3-oss/env-nuxt");
|
|
6173
|
+
else deps.push("@t3-oss/env-core");
|
|
6174
|
+
if (backend !== "convex" && backend !== "none" && runtime !== "workers" && !deps.includes("@t3-oss/env-core")) deps.push("@t3-oss/env-core");
|
|
6175
|
+
return deps;
|
|
6176
|
+
}
|
|
6177
|
+
|
|
6212
6178
|
//#endregion
|
|
6213
6179
|
//#region src/helpers/core/git.ts
|
|
6214
6180
|
async function initializeGit(projectDir, useGit) {
|
|
@@ -6231,6 +6197,17 @@ async function initializeGit(projectDir, useGit) {
|
|
|
6231
6197
|
await $({ cwd: projectDir })`git commit -m ${"initial commit"}`;
|
|
6232
6198
|
}
|
|
6233
6199
|
|
|
6200
|
+
//#endregion
|
|
6201
|
+
//#region src/helpers/core/infra-package-setup.ts
|
|
6202
|
+
async function setupInfraPackageDependencies(projectDir, _options) {
|
|
6203
|
+
const infraDir = path.join(projectDir, "packages/infra");
|
|
6204
|
+
if (!await fs.pathExists(infraDir)) return;
|
|
6205
|
+
await addPackageDependency({
|
|
6206
|
+
devDependencies: ["alchemy"],
|
|
6207
|
+
projectDir: infraDir
|
|
6208
|
+
});
|
|
6209
|
+
}
|
|
6210
|
+
|
|
6234
6211
|
//#endregion
|
|
6235
6212
|
//#region src/helpers/core/payments-setup.ts
|
|
6236
6213
|
async function setupPayments(config) {
|
|
@@ -6417,7 +6394,7 @@ async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup,
|
|
|
6417
6394
|
instructions.push("");
|
|
6418
6395
|
}
|
|
6419
6396
|
}
|
|
6420
|
-
if (dbSetup === "d1" && serverDeploy === "
|
|
6397
|
+
if (dbSetup === "d1" && serverDeploy === "cloudflare") {
|
|
6421
6398
|
if (orm === "drizzle") instructions.push(`${pc.cyan("•")} Generate migrations: ${`${runCmd} db:generate`}`);
|
|
6422
6399
|
else if (orm === "prisma") {
|
|
6423
6400
|
instructions.push(`${pc.cyan("•")} Generate Prisma client: ${`${runCmd} db:generate`}`);
|
|
@@ -6432,15 +6409,15 @@ async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup,
|
|
|
6432
6409
|
if (orm === "prisma") {
|
|
6433
6410
|
if (database === "mongodb" && dbSetup === "docker") instructions.push(`${pc.yellow("WARNING:")} Prisma + MongoDB + Docker combination\n may not work.`);
|
|
6434
6411
|
if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
|
|
6435
|
-
if (!(dbSetup === "d1" && serverDeploy === "
|
|
6412
|
+
if (!(dbSetup === "d1" && serverDeploy === "cloudflare")) {
|
|
6436
6413
|
instructions.push(`${pc.cyan("•")} Generate Prisma Client: ${`${runCmd} db:generate`}`);
|
|
6437
6414
|
instructions.push(`${pc.cyan("•")} Apply schema: ${`${runCmd} db:push`}`);
|
|
6438
6415
|
}
|
|
6439
|
-
if (!(dbSetup === "d1" && serverDeploy === "
|
|
6416
|
+
if (!(dbSetup === "d1" && serverDeploy === "cloudflare")) instructions.push(`${pc.cyan("•")} Database UI: ${`${runCmd} db:studio`}`);
|
|
6440
6417
|
} else if (orm === "drizzle") {
|
|
6441
6418
|
if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
|
|
6442
6419
|
if (dbSetup !== "d1") instructions.push(`${pc.cyan("•")} Apply schema: ${`${runCmd} db:push`}`);
|
|
6443
|
-
if (!(dbSetup === "d1" && serverDeploy === "
|
|
6420
|
+
if (!(dbSetup === "d1" && serverDeploy === "cloudflare")) instructions.push(`${pc.cyan("•")} Database UI: ${`${runCmd} db:studio`}`);
|
|
6444
6421
|
} else if (orm === "mongoose") {
|
|
6445
6422
|
if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
|
|
6446
6423
|
} else if (orm === "none") instructions.push(`${pc.yellow("NOTE:")} Manual database schema setup\n required.`);
|
|
@@ -6471,116 +6448,174 @@ function getPolarInstructions(backend) {
|
|
|
6471
6448
|
function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend) {
|
|
6472
6449
|
const instructions = [];
|
|
6473
6450
|
const isBackendSelf = backend === "self";
|
|
6474
|
-
if (webDeploy === "
|
|
6475
|
-
else if (serverDeploy === "
|
|
6476
|
-
else if (webDeploy === "
|
|
6451
|
+
if (webDeploy === "cloudflare" && serverDeploy !== "cloudflare") instructions.push(`${pc.bold("Deploy web with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/web && ${runCmd} alchemy dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/web && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/web && ${runCmd} destroy`}`);
|
|
6452
|
+
else if (serverDeploy === "cloudflare" && webDeploy !== "cloudflare" && !isBackendSelf) instructions.push(`${pc.bold("Deploy server with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/server && ${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/server && ${runCmd} destroy`}`);
|
|
6453
|
+
else if (webDeploy === "cloudflare" && (serverDeploy === "cloudflare" || isBackendSelf)) instructions.push(`${pc.bold("Deploy with Alchemy:")}\n${pc.cyan("•")} Dev: ${`${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`${runCmd} destroy`}`);
|
|
6477
6454
|
return instructions.length ? `\n${instructions.join("\n")}` : "";
|
|
6478
6455
|
}
|
|
6479
6456
|
|
|
6480
6457
|
//#endregion
|
|
6481
6458
|
//#region src/helpers/core/workspace-setup.ts
|
|
6482
6459
|
async function setupWorkspaceDependencies(projectDir, options) {
|
|
6483
|
-
const { projectName, packageManager,
|
|
6460
|
+
const { projectName, packageManager, runtime, backend } = options;
|
|
6484
6461
|
const workspaceVersion = packageManager === "npm" ? "*" : "workspace:*";
|
|
6485
|
-
const
|
|
6486
|
-
const
|
|
6487
|
-
const
|
|
6488
|
-
const
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6462
|
+
const packages = await detectPackages(projectDir);
|
|
6463
|
+
const configDep = packages.config.exists ? { [`@${projectName}/config`]: workspaceVersion } : {};
|
|
6464
|
+
const envDep = packages.env.exists ? { [`@${projectName}/env`]: workspaceVersion } : {};
|
|
6465
|
+
const ctx = {
|
|
6466
|
+
projectName,
|
|
6467
|
+
workspaceVersion,
|
|
6468
|
+
options,
|
|
6469
|
+
commonDeps: ["dotenv", "zod"],
|
|
6470
|
+
commonDevDeps: ["typescript"],
|
|
6471
|
+
configDep,
|
|
6472
|
+
envDep
|
|
6473
|
+
};
|
|
6474
|
+
await Promise.all([
|
|
6475
|
+
setupEnvPackage(packages.env, packages.infra, ctx),
|
|
6476
|
+
setupInfraPackage(packages.infra, ctx),
|
|
6477
|
+
setupDbPackage(packages.db, ctx),
|
|
6478
|
+
setupAuthPackage(packages.auth, packages.db, ctx),
|
|
6479
|
+
setupApiPackage(packages.api, packages.auth, packages.db, ctx),
|
|
6480
|
+
setupBackendPackage(packages.backend, ctx),
|
|
6481
|
+
setupServerPackage(packages.server, packages.api, packages.auth, packages.db, ctx),
|
|
6482
|
+
setupWebPackage(packages.web, packages.api, packages.auth, packages.backend, ctx),
|
|
6483
|
+
setupNativePackage(packages.native, packages.api, packages.backend, ctx)
|
|
6504
6484
|
]);
|
|
6505
|
-
const
|
|
6506
|
-
|
|
6507
|
-
dependencies: commonDeps,
|
|
6508
|
-
devDependencies: commonDevDeps,
|
|
6485
|
+
const runtimeDevDeps = getRuntimeDevDeps(runtime, backend);
|
|
6486
|
+
await addPackageDependency({
|
|
6487
|
+
dependencies: ctx.commonDeps,
|
|
6488
|
+
devDependencies: [...ctx.commonDevDeps, ...runtimeDevDeps],
|
|
6489
|
+
customDependencies: envDep,
|
|
6509
6490
|
customDevDependencies: configDep,
|
|
6510
|
-
projectDir
|
|
6491
|
+
projectDir
|
|
6511
6492
|
});
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
if (
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6493
|
+
}
|
|
6494
|
+
async function detectPackages(projectDir) {
|
|
6495
|
+
const entries = await Promise.all(Object.entries({
|
|
6496
|
+
config: "packages/config",
|
|
6497
|
+
env: "packages/env",
|
|
6498
|
+
infra: "packages/infra",
|
|
6499
|
+
db: "packages/db",
|
|
6500
|
+
auth: "packages/auth",
|
|
6501
|
+
api: "packages/api",
|
|
6502
|
+
backend: "packages/backend",
|
|
6503
|
+
server: "apps/server",
|
|
6504
|
+
web: "apps/web",
|
|
6505
|
+
native: "apps/native"
|
|
6506
|
+
}).map(async ([name, relativePath]) => {
|
|
6507
|
+
const dir = path.join(projectDir, relativePath);
|
|
6508
|
+
return [name, {
|
|
6509
|
+
dir,
|
|
6510
|
+
exists: await fs.pathExists(dir)
|
|
6511
|
+
}];
|
|
6512
|
+
}));
|
|
6513
|
+
return Object.fromEntries(entries);
|
|
6514
|
+
}
|
|
6515
|
+
async function setupEnvPackage(pkg, infraPkg, ctx) {
|
|
6516
|
+
if (!pkg.exists) return;
|
|
6517
|
+
const runtimeDevDeps = getRuntimeDevDeps(ctx.options.runtime, ctx.options.backend);
|
|
6518
|
+
const customDevDeps = { ...ctx.configDep };
|
|
6519
|
+
if ((ctx.options.serverDeploy === "cloudflare" || ctx.options.webDeploy === "cloudflare") && infraPkg.exists) customDevDeps[`@${ctx.projectName}/infra`] = ctx.workspaceVersion;
|
|
6520
|
+
await addPackageDependency({
|
|
6521
|
+
dependencies: ctx.commonDeps,
|
|
6522
|
+
devDependencies: [...ctx.commonDevDeps, ...runtimeDevDeps],
|
|
6523
|
+
customDevDependencies: customDevDeps,
|
|
6524
|
+
projectDir: pkg.dir
|
|
6540
6525
|
});
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
if (auth !== "none" && authExists) serverDeps[`@${projectName}/auth`] = workspaceVersion;
|
|
6545
|
-
if (database !== "none" && dbExists) serverDeps[`@${projectName}/db`] = workspaceVersion;
|
|
6546
|
-
await addPackageDependency({
|
|
6547
|
-
dependencies: commonDeps,
|
|
6548
|
-
devDependencies: [...commonDevDeps, "tsdown"],
|
|
6549
|
-
customDependencies: serverDeps,
|
|
6550
|
-
customDevDependencies: configDep,
|
|
6551
|
-
projectDir: serverDir
|
|
6552
|
-
});
|
|
6553
|
-
}
|
|
6554
|
-
if (webExists) {
|
|
6555
|
-
const webDeps = {};
|
|
6556
|
-
if (api !== "none" && apiExists) webDeps[`@${projectName}/api`] = workspaceVersion;
|
|
6557
|
-
if (auth !== "none" && authExists) webDeps[`@${projectName}/auth`] = workspaceVersion;
|
|
6558
|
-
if (backend === "convex" && backendExists) webDeps[`@${projectName}/backend`] = workspaceVersion;
|
|
6559
|
-
await addPackageDependency({
|
|
6560
|
-
dependencies: commonDeps,
|
|
6561
|
-
devDependencies: commonDevDeps,
|
|
6562
|
-
customDependencies: webDeps,
|
|
6563
|
-
customDevDependencies: configDep,
|
|
6564
|
-
projectDir: webDir
|
|
6565
|
-
});
|
|
6566
|
-
}
|
|
6567
|
-
if (nativeExists) {
|
|
6568
|
-
const nativeDeps = {};
|
|
6569
|
-
if (api !== "none" && apiExists) nativeDeps[`@${projectName}/api`] = workspaceVersion;
|
|
6570
|
-
if (backend === "convex" && backendExists) nativeDeps[`@${projectName}/backend`] = workspaceVersion;
|
|
6571
|
-
await addPackageDependency({
|
|
6572
|
-
dependencies: commonDeps,
|
|
6573
|
-
devDependencies: commonDevDeps,
|
|
6574
|
-
customDependencies: nativeDeps,
|
|
6575
|
-
customDevDependencies: configDep,
|
|
6576
|
-
projectDir: nativeDir
|
|
6577
|
-
});
|
|
6578
|
-
}
|
|
6579
|
-
const runtimeDevDeps = getRuntimeDevDeps(runtime, backend);
|
|
6526
|
+
}
|
|
6527
|
+
async function setupInfraPackage(pkg, ctx) {
|
|
6528
|
+
if (!pkg.exists) return;
|
|
6580
6529
|
await addPackageDependency({
|
|
6581
|
-
dependencies: commonDeps,
|
|
6582
|
-
devDependencies:
|
|
6583
|
-
|
|
6530
|
+
dependencies: ctx.commonDeps,
|
|
6531
|
+
devDependencies: ctx.commonDevDeps,
|
|
6532
|
+
customDevDependencies: ctx.configDep,
|
|
6533
|
+
projectDir: pkg.dir
|
|
6534
|
+
});
|
|
6535
|
+
}
|
|
6536
|
+
async function setupDbPackage(pkg, ctx) {
|
|
6537
|
+
if (!pkg.exists) return;
|
|
6538
|
+
await addPackageDependency({
|
|
6539
|
+
dependencies: ctx.commonDeps,
|
|
6540
|
+
devDependencies: ctx.commonDevDeps,
|
|
6541
|
+
customDependencies: ctx.envDep,
|
|
6542
|
+
customDevDependencies: ctx.configDep,
|
|
6543
|
+
projectDir: pkg.dir
|
|
6544
|
+
});
|
|
6545
|
+
}
|
|
6546
|
+
async function setupAuthPackage(pkg, dbPkg, ctx) {
|
|
6547
|
+
if (!pkg.exists) return;
|
|
6548
|
+
const deps = { ...ctx.envDep };
|
|
6549
|
+
if (ctx.options.database !== "none" && dbPkg.exists) deps[`@${ctx.projectName}/db`] = ctx.workspaceVersion;
|
|
6550
|
+
await addPackageDependency({
|
|
6551
|
+
dependencies: ctx.commonDeps,
|
|
6552
|
+
devDependencies: ctx.commonDevDeps,
|
|
6553
|
+
customDependencies: deps,
|
|
6554
|
+
customDevDependencies: ctx.configDep,
|
|
6555
|
+
projectDir: pkg.dir
|
|
6556
|
+
});
|
|
6557
|
+
}
|
|
6558
|
+
async function setupApiPackage(pkg, authPkg, dbPkg, ctx) {
|
|
6559
|
+
if (!pkg.exists) return;
|
|
6560
|
+
const deps = { ...ctx.envDep };
|
|
6561
|
+
if (ctx.options.auth !== "none" && authPkg.exists) deps[`@${ctx.projectName}/auth`] = ctx.workspaceVersion;
|
|
6562
|
+
if (ctx.options.database !== "none" && dbPkg.exists) deps[`@${ctx.projectName}/db`] = ctx.workspaceVersion;
|
|
6563
|
+
await addPackageDependency({
|
|
6564
|
+
dependencies: ctx.commonDeps,
|
|
6565
|
+
devDependencies: ctx.commonDevDeps,
|
|
6566
|
+
customDependencies: deps,
|
|
6567
|
+
customDevDependencies: ctx.configDep,
|
|
6568
|
+
projectDir: pkg.dir
|
|
6569
|
+
});
|
|
6570
|
+
}
|
|
6571
|
+
async function setupBackendPackage(pkg, ctx) {
|
|
6572
|
+
if (!pkg.exists) return;
|
|
6573
|
+
await addPackageDependency({
|
|
6574
|
+
dependencies: ctx.commonDeps,
|
|
6575
|
+
devDependencies: ctx.commonDevDeps,
|
|
6576
|
+
customDevDependencies: ctx.configDep,
|
|
6577
|
+
projectDir: pkg.dir
|
|
6578
|
+
});
|
|
6579
|
+
}
|
|
6580
|
+
async function setupServerPackage(pkg, apiPkg, authPkg, dbPkg, ctx) {
|
|
6581
|
+
if (!pkg.exists) return;
|
|
6582
|
+
const deps = { ...ctx.envDep };
|
|
6583
|
+
if (ctx.options.api !== "none" && apiPkg.exists) deps[`@${ctx.projectName}/api`] = ctx.workspaceVersion;
|
|
6584
|
+
if (ctx.options.auth !== "none" && authPkg.exists) deps[`@${ctx.projectName}/auth`] = ctx.workspaceVersion;
|
|
6585
|
+
if (ctx.options.database !== "none" && dbPkg.exists) deps[`@${ctx.projectName}/db`] = ctx.workspaceVersion;
|
|
6586
|
+
await addPackageDependency({
|
|
6587
|
+
dependencies: ctx.commonDeps,
|
|
6588
|
+
devDependencies: [...ctx.commonDevDeps, "tsdown"],
|
|
6589
|
+
customDependencies: deps,
|
|
6590
|
+
customDevDependencies: ctx.configDep,
|
|
6591
|
+
projectDir: pkg.dir
|
|
6592
|
+
});
|
|
6593
|
+
}
|
|
6594
|
+
async function setupWebPackage(pkg, apiPkg, authPkg, backendPkg, ctx) {
|
|
6595
|
+
if (!pkg.exists) return;
|
|
6596
|
+
const deps = { ...ctx.envDep };
|
|
6597
|
+
if (ctx.options.api !== "none" && apiPkg.exists) deps[`@${ctx.projectName}/api`] = ctx.workspaceVersion;
|
|
6598
|
+
if (ctx.options.auth !== "none" && authPkg.exists) deps[`@${ctx.projectName}/auth`] = ctx.workspaceVersion;
|
|
6599
|
+
if (ctx.options.backend === "convex" && backendPkg.exists) deps[`@${ctx.projectName}/backend`] = ctx.workspaceVersion;
|
|
6600
|
+
await addPackageDependency({
|
|
6601
|
+
dependencies: ctx.commonDeps,
|
|
6602
|
+
devDependencies: ctx.commonDevDeps,
|
|
6603
|
+
customDependencies: deps,
|
|
6604
|
+
customDevDependencies: ctx.configDep,
|
|
6605
|
+
projectDir: pkg.dir
|
|
6606
|
+
});
|
|
6607
|
+
}
|
|
6608
|
+
async function setupNativePackage(pkg, apiPkg, backendPkg, ctx) {
|
|
6609
|
+
if (!pkg.exists) return;
|
|
6610
|
+
const deps = { ...ctx.envDep };
|
|
6611
|
+
if (ctx.options.api !== "none" && apiPkg.exists) deps[`@${ctx.projectName}/api`] = ctx.workspaceVersion;
|
|
6612
|
+
if (ctx.options.backend === "convex" && backendPkg.exists) deps[`@${ctx.projectName}/backend`] = ctx.workspaceVersion;
|
|
6613
|
+
await addPackageDependency({
|
|
6614
|
+
dependencies: ctx.commonDeps,
|
|
6615
|
+
devDependencies: ctx.commonDevDeps,
|
|
6616
|
+
customDependencies: deps,
|
|
6617
|
+
customDevDependencies: ctx.configDep,
|
|
6618
|
+
projectDir: pkg.dir
|
|
6584
6619
|
});
|
|
6585
6620
|
}
|
|
6586
6621
|
function getRuntimeDevDeps(runtime, backend) {
|
|
@@ -6617,7 +6652,7 @@ async function updateRootPackageJson(projectDir, options) {
|
|
|
6617
6652
|
const dbPackageName = `@${projectName}/db`;
|
|
6618
6653
|
const hasTurborepo = addons.includes("turborepo");
|
|
6619
6654
|
const needsDbScripts = backend !== "convex" && database !== "none" && orm !== "none" && orm !== "mongoose";
|
|
6620
|
-
const isD1Alchemy = dbSetup === "d1" && serverDeploy === "
|
|
6655
|
+
const isD1Alchemy = dbSetup === "d1" && serverDeploy === "cloudflare";
|
|
6621
6656
|
const pmConfig = getPackageManagerConfig(packageManager, hasTurborepo);
|
|
6622
6657
|
scripts.dev = pmConfig.dev;
|
|
6623
6658
|
scripts.build = pmConfig.build;
|
|
@@ -6645,7 +6680,7 @@ async function updateRootPackageJson(projectDir, options) {
|
|
|
6645
6680
|
scripts["db:down"] = pmConfig.filter(dbPackageName, "db:down");
|
|
6646
6681
|
}
|
|
6647
6682
|
try {
|
|
6648
|
-
const { stdout } = await
|
|
6683
|
+
const { stdout } = await $`${packageManager} -v`;
|
|
6649
6684
|
packageJson.packageManager = `${packageManager}@${stdout.trim()}`;
|
|
6650
6685
|
} catch {
|
|
6651
6686
|
log.warn(`Could not determine ${packageManager} version.`);
|
|
@@ -6702,7 +6737,7 @@ async function updateDbPackageJson(projectDir, options) {
|
|
|
6702
6737
|
dbPackageJson.scripts = dbPackageJson.scripts || {};
|
|
6703
6738
|
const scripts = dbPackageJson.scripts;
|
|
6704
6739
|
const { database, orm, dbSetup, serverDeploy } = options;
|
|
6705
|
-
const isD1Alchemy = dbSetup === "d1" && serverDeploy === "
|
|
6740
|
+
const isD1Alchemy = dbSetup === "d1" && serverDeploy === "cloudflare";
|
|
6706
6741
|
if (database !== "none") {
|
|
6707
6742
|
if (database === "sqlite" && dbSetup !== "d1") scripts["db:local"] = "turso dev --db-file local.db";
|
|
6708
6743
|
if (orm === "prisma") {
|
|
@@ -6752,7 +6787,8 @@ async function updateConvexPackageJson(projectDir, options) {
|
|
|
6752
6787
|
|
|
6753
6788
|
//#endregion
|
|
6754
6789
|
//#region src/helpers/core/create-project.ts
|
|
6755
|
-
async function createProject(options, cliInput) {
|
|
6790
|
+
async function createProject(options, cliInput = {}) {
|
|
6791
|
+
const { silent = false } = cliInput;
|
|
6756
6792
|
const projectDir = options.projectDir;
|
|
6757
6793
|
const isConvex = options.backend === "convex";
|
|
6758
6794
|
const isSelfBackend = options.backend === "self";
|
|
@@ -6768,6 +6804,8 @@ async function createProject(options, cliInput) {
|
|
|
6768
6804
|
if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamplesTemplate(projectDir, options);
|
|
6769
6805
|
await setupAddonsTemplate(projectDir, options);
|
|
6770
6806
|
await setupDeploymentTemplates(projectDir, options);
|
|
6807
|
+
await setupEnvPackageDependencies(projectDir, options);
|
|
6808
|
+
if (options.serverDeploy === "cloudflare" || options.webDeploy === "cloudflare") await setupInfraPackageDependencies(projectDir, options);
|
|
6771
6809
|
await setupApi(options);
|
|
6772
6810
|
if (isConvex || needsServerSetup) await setupBackendDependencies(options);
|
|
6773
6811
|
if (!isConvex) {
|
|
@@ -6786,23 +6824,23 @@ async function createProject(options, cliInput) {
|
|
|
6786
6824
|
await setupCatalogs(projectDir, options);
|
|
6787
6825
|
await createReadme(projectDir, options);
|
|
6788
6826
|
await writeBtsConfig(options);
|
|
6789
|
-
log.success("Project template successfully scaffolded!");
|
|
6827
|
+
if (!silent) log.success("Project template successfully scaffolded!");
|
|
6790
6828
|
if (options.install) await installDependencies({
|
|
6791
6829
|
projectDir,
|
|
6792
6830
|
packageManager: options.packageManager
|
|
6793
6831
|
});
|
|
6794
6832
|
await initializeGit(projectDir, options.git);
|
|
6795
|
-
await displayPostInstallInstructions({
|
|
6833
|
+
if (!silent) await displayPostInstallInstructions({
|
|
6796
6834
|
...options,
|
|
6797
6835
|
depsInstalled: options.install
|
|
6798
6836
|
});
|
|
6799
6837
|
return projectDir;
|
|
6800
6838
|
} catch (error) {
|
|
6801
6839
|
if (error instanceof Error) {
|
|
6802
|
-
console.error(error.stack);
|
|
6840
|
+
if (!silent) console.error(error.stack);
|
|
6803
6841
|
exitWithError(`Error during project creation: ${error.message}`);
|
|
6804
6842
|
} else {
|
|
6805
|
-
console.error(error);
|
|
6843
|
+
if (!silent) console.error(error);
|
|
6806
6844
|
exitWithError(`An unexpected error occurred: ${String(error)}`);
|
|
6807
6845
|
}
|
|
6808
6846
|
}
|
|
@@ -6810,12 +6848,13 @@ async function createProject(options, cliInput) {
|
|
|
6810
6848
|
|
|
6811
6849
|
//#endregion
|
|
6812
6850
|
//#region src/helpers/core/command-handlers.ts
|
|
6813
|
-
async function createProjectHandler(input) {
|
|
6851
|
+
async function createProjectHandler(input, options = {}) {
|
|
6852
|
+
const { silent = false } = options;
|
|
6814
6853
|
const startTime = Date.now();
|
|
6815
6854
|
const timeScaffolded = (/* @__PURE__ */ new Date()).toISOString();
|
|
6816
|
-
if (input.renderTitle !== false) renderTitle();
|
|
6817
|
-
intro(pc.magenta("Creating a new Better-T-Stack project"));
|
|
6818
|
-
if (input.yolo) consola.fatal("YOLO mode enabled - skipping checks. Things may break!");
|
|
6855
|
+
if (!silent && input.renderTitle !== false) renderTitle();
|
|
6856
|
+
if (!silent) intro(pc.magenta("Creating a new Better-T-Stack project"));
|
|
6857
|
+
if (!silent && input.yolo) consola.fatal("YOLO mode enabled - skipping checks. Things may break!");
|
|
6819
6858
|
let currentPathInput;
|
|
6820
6859
|
if (input.yes && input.projectName) currentPathInput = input.projectName;
|
|
6821
6860
|
else if (input.yes) {
|
|
@@ -6884,8 +6923,10 @@ async function createProjectHandler(input) {
|
|
|
6884
6923
|
if (templateConfig) {
|
|
6885
6924
|
const templateName = input.template.toUpperCase();
|
|
6886
6925
|
const templateDescription = getTemplateDescription(input.template);
|
|
6887
|
-
|
|
6888
|
-
|
|
6926
|
+
if (!silent) {
|
|
6927
|
+
log.message(pc.bold(pc.cyan(`Using template: ${pc.white(templateName)}`)));
|
|
6928
|
+
log.message(pc.dim(` ${templateDescription}`));
|
|
6929
|
+
}
|
|
6889
6930
|
const userOverrides = {};
|
|
6890
6931
|
for (const [key, value] of Object.entries(originalInput)) if (value !== void 0) userOverrides[key] = value;
|
|
6891
6932
|
cliInput = {
|
|
@@ -6907,25 +6948,32 @@ async function createProjectHandler(input) {
|
|
|
6907
6948
|
relativePath: finalPathInput
|
|
6908
6949
|
};
|
|
6909
6950
|
validateConfigCompatibility(config, providedFlags, cliInput);
|
|
6910
|
-
|
|
6911
|
-
|
|
6951
|
+
if (!silent) {
|
|
6952
|
+
log.info(pc.yellow("Using default/flag options (config prompts skipped):"));
|
|
6953
|
+
log.message(displayConfig(config));
|
|
6954
|
+
}
|
|
6912
6955
|
} else {
|
|
6913
6956
|
const flagConfig = processAndValidateFlags(cliInput, providedFlags, finalBaseName);
|
|
6914
6957
|
const { projectName: _projectNameFromFlags, ...otherFlags } = flagConfig;
|
|
6915
|
-
if (Object.keys(otherFlags).length > 0) {
|
|
6958
|
+
if (!silent && Object.keys(otherFlags).length > 0) {
|
|
6916
6959
|
log.info(pc.yellow("Using these pre-selected options:"));
|
|
6917
6960
|
log.message(displayConfig(otherFlags));
|
|
6918
6961
|
log.message("");
|
|
6919
6962
|
}
|
|
6920
6963
|
config = await gatherConfig(flagConfig, finalBaseName, finalResolvedPath, finalPathInput);
|
|
6921
6964
|
}
|
|
6922
|
-
await createProject(config, {
|
|
6965
|
+
await createProject(config, {
|
|
6966
|
+
manualDb: cliInput.manualDb ?? input.manualDb,
|
|
6967
|
+
silent
|
|
6968
|
+
});
|
|
6923
6969
|
const reproducibleCommand = generateReproducibleCommand(config);
|
|
6924
|
-
log.success(pc.blue(`You can reproduce this setup with the following command:\n${reproducibleCommand}`));
|
|
6970
|
+
if (!silent) log.success(pc.blue(`You can reproduce this setup with the following command:\n${reproducibleCommand}`));
|
|
6925
6971
|
await trackProjectCreation(config, input.disableAnalytics);
|
|
6926
6972
|
const elapsedTimeMs = Date.now() - startTime;
|
|
6927
|
-
|
|
6928
|
-
|
|
6973
|
+
if (!silent) {
|
|
6974
|
+
const elapsedTimeInSeconds = (elapsedTimeMs / 1e3).toFixed(2);
|
|
6975
|
+
outro(pc.magenta(`Project created successfully in ${pc.bold(elapsedTimeInSeconds)} seconds!`));
|
|
6976
|
+
}
|
|
6929
6977
|
return {
|
|
6930
6978
|
success: true,
|
|
6931
6979
|
projectConfig: config,
|
|
@@ -7037,25 +7085,12 @@ async function addAddonsHandler(input) {
|
|
|
7037
7085
|
//#region src/utils/open-url.ts
|
|
7038
7086
|
async function openUrl(url) {
|
|
7039
7087
|
const platform = process.platform;
|
|
7040
|
-
let command;
|
|
7041
|
-
let args = [];
|
|
7042
|
-
if (platform === "darwin") {
|
|
7043
|
-
command = "open";
|
|
7044
|
-
args = [url];
|
|
7045
|
-
} else if (platform === "win32") {
|
|
7046
|
-
command = "cmd";
|
|
7047
|
-
args = [
|
|
7048
|
-
"/c",
|
|
7049
|
-
"start",
|
|
7050
|
-
"",
|
|
7051
|
-
url.replace(/&/g, "^&")
|
|
7052
|
-
];
|
|
7053
|
-
} else {
|
|
7054
|
-
command = "xdg-open";
|
|
7055
|
-
args = [url];
|
|
7056
|
-
}
|
|
7057
7088
|
try {
|
|
7058
|
-
|
|
7089
|
+
if (platform === "darwin") await $({ stdio: "ignore" })`open ${url}`;
|
|
7090
|
+
else if (platform === "win32") {
|
|
7091
|
+
const escapedUrl = url.replace(/&/g, "^&");
|
|
7092
|
+
await $({ stdio: "ignore" })`cmd /c start "" ${escapedUrl}`;
|
|
7093
|
+
} else await $({ stdio: "ignore" })`xdg-open ${url}`;
|
|
7059
7094
|
} catch {
|
|
7060
7095
|
log.message(`Please open ${url} in your browser.`);
|
|
7061
7096
|
}
|
|
@@ -7105,32 +7140,32 @@ function displaySponsorsBox(sponsors$1) {
|
|
|
7105
7140
|
//#endregion
|
|
7106
7141
|
//#region src/index.ts
|
|
7107
7142
|
const router = os.router({
|
|
7108
|
-
|
|
7143
|
+
create: os.meta({
|
|
7109
7144
|
description: "Create a new Better-T-Stack project",
|
|
7110
7145
|
default: true,
|
|
7111
7146
|
negateBooleans: true
|
|
7112
|
-
}).input(z.tuple([ProjectNameSchema.optional(), z.object({
|
|
7113
|
-
template: TemplateSchema.optional().describe("Use a predefined template"),
|
|
7147
|
+
}).input(z.tuple([types_exports.ProjectNameSchema.optional(), z.object({
|
|
7148
|
+
template: types_exports.TemplateSchema.optional().describe("Use a predefined template"),
|
|
7114
7149
|
yes: z.boolean().optional().default(false).describe("Use default configuration"),
|
|
7115
7150
|
yolo: z.boolean().optional().default(false).describe("(WARNING - NOT RECOMMENDED) Bypass validations and compatibility checks"),
|
|
7116
7151
|
verbose: z.boolean().optional().default(false).describe("Show detailed result information"),
|
|
7117
|
-
database: DatabaseSchema.optional(),
|
|
7118
|
-
orm: ORMSchema.optional(),
|
|
7119
|
-
auth: AuthSchema.optional(),
|
|
7120
|
-
payments: PaymentsSchema.optional(),
|
|
7121
|
-
frontend: z.array(FrontendSchema).optional(),
|
|
7122
|
-
addons: z.array(AddonsSchema).optional(),
|
|
7123
|
-
examples: z.array(ExamplesSchema).optional(),
|
|
7152
|
+
database: types_exports.DatabaseSchema.optional(),
|
|
7153
|
+
orm: types_exports.ORMSchema.optional(),
|
|
7154
|
+
auth: types_exports.AuthSchema.optional(),
|
|
7155
|
+
payments: types_exports.PaymentsSchema.optional(),
|
|
7156
|
+
frontend: z.array(types_exports.FrontendSchema).optional(),
|
|
7157
|
+
addons: z.array(types_exports.AddonsSchema).optional(),
|
|
7158
|
+
examples: z.array(types_exports.ExamplesSchema).optional(),
|
|
7124
7159
|
git: z.boolean().optional(),
|
|
7125
|
-
packageManager: PackageManagerSchema.optional(),
|
|
7160
|
+
packageManager: types_exports.PackageManagerSchema.optional(),
|
|
7126
7161
|
install: z.boolean().optional(),
|
|
7127
|
-
dbSetup: DatabaseSetupSchema.optional(),
|
|
7128
|
-
backend: BackendSchema.optional(),
|
|
7129
|
-
runtime: RuntimeSchema.optional(),
|
|
7130
|
-
api: APISchema.optional(),
|
|
7131
|
-
webDeploy: WebDeploySchema.optional(),
|
|
7132
|
-
serverDeploy: ServerDeploySchema.optional(),
|
|
7133
|
-
directoryConflict: DirectoryConflictSchema.optional(),
|
|
7162
|
+
dbSetup: types_exports.DatabaseSetupSchema.optional(),
|
|
7163
|
+
backend: types_exports.BackendSchema.optional(),
|
|
7164
|
+
runtime: types_exports.RuntimeSchema.optional(),
|
|
7165
|
+
api: types_exports.APISchema.optional(),
|
|
7166
|
+
webDeploy: types_exports.WebDeploySchema.optional(),
|
|
7167
|
+
serverDeploy: types_exports.ServerDeploySchema.optional(),
|
|
7168
|
+
directoryConflict: types_exports.DirectoryConflictSchema.optional(),
|
|
7134
7169
|
renderTitle: z.boolean().optional(),
|
|
7135
7170
|
disableAnalytics: z.boolean().optional().default(false).describe("Disable analytics"),
|
|
7136
7171
|
manualDb: z.boolean().optional().default(false).describe("Skip automatic/manual database setup prompt and use manual setup")
|
|
@@ -7143,12 +7178,12 @@ const router = os.router({
|
|
|
7143
7178
|
if (options.verbose) return result;
|
|
7144
7179
|
}),
|
|
7145
7180
|
add: os.meta({ description: "Add addons or deployment configurations to an existing Better-T-Stack project" }).input(z.tuple([z.object({
|
|
7146
|
-
addons: z.array(AddonsSchema).optional().default([]),
|
|
7147
|
-
webDeploy: WebDeploySchema.optional(),
|
|
7148
|
-
serverDeploy: ServerDeploySchema.optional(),
|
|
7181
|
+
addons: z.array(types_exports.AddonsSchema).optional().default([]),
|
|
7182
|
+
webDeploy: types_exports.WebDeploySchema.optional(),
|
|
7183
|
+
serverDeploy: types_exports.ServerDeploySchema.optional(),
|
|
7149
7184
|
projectDir: z.string().optional(),
|
|
7150
7185
|
install: z.boolean().optional().default(false).describe("Install dependencies after adding addons or deployment"),
|
|
7151
|
-
packageManager: PackageManagerSchema.optional()
|
|
7186
|
+
packageManager: types_exports.PackageManagerSchema.optional()
|
|
7152
7187
|
})])).handler(async ({ input }) => {
|
|
7153
7188
|
const [options] = input;
|
|
7154
7189
|
await addAddonsHandler(options);
|
|
@@ -7190,49 +7225,49 @@ function createBtsCli() {
|
|
|
7190
7225
|
});
|
|
7191
7226
|
}
|
|
7192
7227
|
/**
|
|
7193
|
-
*
|
|
7228
|
+
* Programmatic API to create a new Better-T-Stack project.
|
|
7229
|
+
* Returns pure JSON - no console output, no interactive prompts.
|
|
7194
7230
|
*
|
|
7195
|
-
* @example
|
|
7196
|
-
* ```bash
|
|
7197
|
-
* npx create-better-t-stack my-app --yes
|
|
7198
|
-
* ```
|
|
7199
|
-
*
|
|
7200
|
-
* @example Programmatic usage (always returns structured data):
|
|
7231
|
+
* @example
|
|
7201
7232
|
* ```typescript
|
|
7202
|
-
* import {
|
|
7233
|
+
* import { create } from "create-better-t-stack";
|
|
7203
7234
|
*
|
|
7204
|
-
* const result = await
|
|
7205
|
-
* yes: true,
|
|
7235
|
+
* const result = await create("my-app", {
|
|
7206
7236
|
* frontend: ["tanstack-router"],
|
|
7207
7237
|
* backend: "hono",
|
|
7238
|
+
* runtime: "bun",
|
|
7208
7239
|
* database: "sqlite",
|
|
7209
7240
|
* orm: "drizzle",
|
|
7210
|
-
* auth: "better-auth",
|
|
7211
|
-
* addons: ["biome", "turborepo"],
|
|
7212
|
-
* packageManager: "bun",
|
|
7213
|
-
* install: false,
|
|
7214
|
-
* directoryConflict: "increment", // auto-handle conflicts
|
|
7215
|
-
* disableAnalytics: true, // disable analytics
|
|
7216
7241
|
* });
|
|
7217
7242
|
*
|
|
7218
7243
|
* if (result.success) {
|
|
7219
7244
|
* console.log(`Project created at: ${result.projectDirectory}`);
|
|
7220
|
-
* console.log(`Reproducible command: ${result.reproducibleCommand}`);
|
|
7221
|
-
* console.log(`Time taken: ${result.elapsedTimeMs}ms`);
|
|
7222
7245
|
* }
|
|
7223
7246
|
* ```
|
|
7224
7247
|
*/
|
|
7225
|
-
async function
|
|
7226
|
-
const
|
|
7227
|
-
...options
|
|
7228
|
-
|
|
7248
|
+
async function create(projectName, options) {
|
|
7249
|
+
const input = {
|
|
7250
|
+
...options,
|
|
7251
|
+
projectName,
|
|
7252
|
+
renderTitle: false,
|
|
7253
|
+
verbose: true,
|
|
7254
|
+
disableAnalytics: options?.disableAnalytics ?? true,
|
|
7255
|
+
directoryConflict: options?.directoryConflict ?? "error"
|
|
7229
7256
|
};
|
|
7230
|
-
|
|
7231
|
-
|
|
7232
|
-
|
|
7233
|
-
|
|
7234
|
-
|
|
7235
|
-
|
|
7257
|
+
try {
|
|
7258
|
+
return await createProjectHandler(input, { silent: true });
|
|
7259
|
+
} catch (error) {
|
|
7260
|
+
return {
|
|
7261
|
+
success: false,
|
|
7262
|
+
error: error instanceof Error ? error.message : String(error),
|
|
7263
|
+
projectConfig: {},
|
|
7264
|
+
reproducibleCommand: "",
|
|
7265
|
+
timeScaffolded: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7266
|
+
elapsedTimeMs: 0,
|
|
7267
|
+
projectDirectory: "",
|
|
7268
|
+
relativePath: ""
|
|
7269
|
+
};
|
|
7270
|
+
}
|
|
7236
7271
|
}
|
|
7237
7272
|
async function sponsors() {
|
|
7238
7273
|
return caller.sponsors();
|
|
@@ -7245,4 +7280,4 @@ async function builder() {
|
|
|
7245
7280
|
}
|
|
7246
7281
|
|
|
7247
7282
|
//#endregion
|
|
7248
|
-
export { router as a,
|
|
7283
|
+
export { router as a, docs as i, create as n, sponsors as o, createBtsCli as r, builder as t };
|