create-better-fullstack 2.1.3 → 2.1.5

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.
@@ -0,0 +1,497 @@
1
+ #!/usr/bin/env node
2
+ import { A as types_exports } from "./errors-Cyol8zbN.mjs";
3
+
4
+ //#region src/utils/generate-reproducible-command.ts
5
+ function getBaseCommand(packageManager) {
6
+ switch (packageManager) {
7
+ case "bun": return "bun create better-fullstack@latest";
8
+ case "pnpm": return "pnpm create better-fullstack@latest";
9
+ case "yarn": return "yarn create better-fullstack@latest";
10
+ case "npm":
11
+ default: return "npx create-better-fullstack@latest";
12
+ }
13
+ }
14
+ function formatArrayFlag(flag, values) {
15
+ const normalizedValues = values.filter((value) => value !== "none");
16
+ if (normalizedValues.length === 0) return `--${flag} none`;
17
+ return `--${flag} ${normalizedValues.join(" ")}`;
18
+ }
19
+ function appendCommonFlags(flags, config) {
20
+ if (config.aiDocs && config.aiDocs.length > 0) flags.push(formatArrayFlag("ai-docs", config.aiDocs));
21
+ else flags.push("--ai-docs none");
22
+ flags.push(config.git ? "--git" : "--no-git");
23
+ flags.push(`--package-manager ${config.packageManager}`);
24
+ if (config.versionChannel !== "stable") flags.push(`--version-channel ${config.versionChannel}`);
25
+ flags.push(config.install ? "--install" : "--no-install");
26
+ }
27
+ function hasGraphPrimaryPart(config, role, ecosystem) {
28
+ return config.stackParts?.some((part) => part.source !== "provided" && part.role === role && !part.ownerPartId && (!ecosystem || part.ecosystem === ecosystem));
29
+ }
30
+ function getGraphPartIncludingDisabled(config, role, ecosystem) {
31
+ return config.stackParts?.find((part) => part.source !== "provided" && part.role === role && (!ecosystem || part.ecosystem === ecosystem));
32
+ }
33
+ function getOwnedGraphPartIncludingDisabled(config, ownerRole, role, ecosystem) {
34
+ const owner = config.stackParts?.find((part) => part.source !== "provided" && part.role === ownerRole && !part.ownerPartId && (!ecosystem || part.ecosystem === ecosystem));
35
+ if (!owner) return void 0;
36
+ return config.stackParts?.find((part) => part.source !== "provided" && part.role === role && part.ownerPartId === owner.id && (!ecosystem || part.ecosystem === ecosystem));
37
+ }
38
+ function hasAnyGraphAddonPart(config) {
39
+ return Boolean(config.stackParts?.some((part) => {
40
+ if (part.source === "provided" || part.toolId === "none") return false;
41
+ const binding = (0, types_exports.getAddonStackPartBinding)(part.toolId);
42
+ if (!binding || binding.role !== part.role || binding.ecosystem !== part.ecosystem) return false;
43
+ if (!binding.ownerRole) return !part.ownerPartId;
44
+ const owner = config.stackParts?.find((candidate) => candidate.source !== "provided" && candidate.role === binding.ownerRole && candidate.ecosystem === binding.ecosystem && candidate.id === part.ownerPartId);
45
+ return Boolean(owner);
46
+ }));
47
+ }
48
+ function hasAnyGraphExamplePart(config) {
49
+ return Boolean(config.stackParts?.some((part) => part.source !== "provided" && part.toolId !== "none" && part.role === "examples" && part.ecosystem === "universal" && !part.ownerPartId));
50
+ }
51
+ function appendChangedStringFlag(flags, flag, value, defaultValue) {
52
+ if (value !== defaultValue) flags.push(`--${flag} ${value}`);
53
+ }
54
+ function appendChangedGraphStringFlag(flags, config, role, ecosystem, flag, value, defaultValue) {
55
+ const graphPart = getGraphPartIncludingDisabled(config, role, ecosystem);
56
+ if (graphPart) {
57
+ appendChangedStringFlag(flags, flag, graphPart.toolId === "none" ? "none" : defaultValue, defaultValue);
58
+ return;
59
+ }
60
+ appendChangedStringFlag(flags, flag, value, defaultValue);
61
+ }
62
+ function appendChangedOwnedGraphStringFlag(flags, config, ownerRole, role, ecosystem, flag, value, defaultValue) {
63
+ const graphPart = getOwnedGraphPartIncludingDisabled(config, ownerRole, role, ecosystem);
64
+ if (graphPart) {
65
+ appendChangedStringFlag(flags, flag, graphPart.toolId === "none" ? "none" : defaultValue, defaultValue);
66
+ return;
67
+ }
68
+ appendChangedStringFlag(flags, flag, value, defaultValue);
69
+ }
70
+ function hasAnyOwnedGraphArrayPart(config, ownerRole, role, ecosystem) {
71
+ return Boolean(getOwnedGraphPartIncludingDisabled(config, ownerRole, role, ecosystem));
72
+ }
73
+ function appendChangedOwnedGraphArrayFlag(flags, config, ownerRole, role, ecosystem, flag, values, defaultValues) {
74
+ if (hasAnyOwnedGraphArrayPart(config, ownerRole, role, ecosystem)) return;
75
+ appendChangedArrayFlag(flags, flag, values, defaultValues);
76
+ }
77
+ function appendChangedArrayFlag(flags, flag, values, defaultValues) {
78
+ if (values.length !== defaultValues.length || values.some((value, index) => value !== defaultValues[index])) flags.push(formatArrayFlag(flag, values));
79
+ }
80
+ function appendAstroIntegrationFlag(flags, config) {
81
+ if (config.frontend.includes("astro") && config.astroIntegration !== "none") flags.push(`--astro-integration ${config.astroIntegration}`);
82
+ }
83
+ function appendGraphExtraFlags(flags, config) {
84
+ if (!hasAnyGraphAddonPart(config)) appendChangedArrayFlag(flags, "addons", config.addons, ["turborepo"]);
85
+ if (!hasAnyGraphExamplePart(config)) appendChangedArrayFlag(flags, "examples", config.examples, []);
86
+ if (hasGraphPrimaryPart(config, "database")) appendChangedOwnedGraphStringFlag(flags, config, "database", "dbSetup", "universal", "db-setup", config.dbSetup, "none");
87
+ else appendChangedStringFlag(flags, "db-setup", config.dbSetup, "none");
88
+ if (hasGraphPrimaryPart(config, "frontend", "typescript")) {
89
+ const effectiveUiLibrary = getGraphPartIncludingDisabled(config, "ui", "typescript")?.toolId ?? config.uiLibrary;
90
+ appendAstroIntegrationFlag(flags, config);
91
+ appendChangedOwnedGraphStringFlag(flags, config, "frontend", "deploy", "typescript", "web-deploy", config.webDeploy, "none");
92
+ appendChangedGraphStringFlag(flags, config, "css", "typescript", "css-framework", config.cssFramework, "tailwind");
93
+ appendChangedGraphStringFlag(flags, config, "ui", "typescript", "ui-library", config.uiLibrary, "shadcn-ui");
94
+ if (effectiveUiLibrary === "shadcn-ui") {
95
+ appendChangedStringFlag(flags, "shadcn-base", config.shadcnBase ?? "radix", "radix");
96
+ appendChangedStringFlag(flags, "shadcn-style", config.shadcnStyle ?? "nova", "nova");
97
+ appendChangedStringFlag(flags, "shadcn-icon-library", config.shadcnIconLibrary ?? "lucide", "lucide");
98
+ appendChangedStringFlag(flags, "shadcn-color-theme", config.shadcnColorTheme ?? "neutral", "neutral");
99
+ appendChangedStringFlag(flags, "shadcn-base-color", config.shadcnBaseColor ?? "neutral", "neutral");
100
+ appendChangedStringFlag(flags, "shadcn-font", config.shadcnFont ?? "inter", "inter");
101
+ appendChangedStringFlag(flags, "shadcn-radius", config.shadcnRadius ?? "default", "default");
102
+ }
103
+ appendChangedGraphStringFlag(flags, config, "stateManagement", "typescript", "state-management", config.stateManagement, "none");
104
+ appendChangedGraphStringFlag(flags, config, "forms", "typescript", "forms", config.forms, "react-hook-form");
105
+ appendChangedStringFlag(flags, "validation", config.validation, "zod");
106
+ appendChangedStringFlag(flags, "testing", config.testing, "vitest");
107
+ appendChangedGraphStringFlag(flags, config, "animation", "typescript", "animation", config.animation, "none");
108
+ appendChangedGraphStringFlag(flags, config, "fileUpload", "typescript", "file-upload", config.fileUpload, "none");
109
+ appendChangedGraphStringFlag(flags, config, "i18n", "typescript", "i18n", config.i18n, "none");
110
+ appendChangedGraphStringFlag(flags, config, "analytics", "typescript", "analytics", config.analytics, "none");
111
+ }
112
+ if (hasGraphPrimaryPart(config, "frontend", "typescript") || hasGraphPrimaryPart(config, "backend", "typescript")) {
113
+ if (hasGraphPrimaryPart(config, "backend", "typescript")) {
114
+ appendChangedOwnedGraphStringFlag(flags, config, "backend", "runtime", "typescript", "runtime", config.runtime, "bun");
115
+ appendChangedOwnedGraphStringFlag(flags, config, "backend", "deploy", "typescript", "server-deploy", config.serverDeploy, "none");
116
+ } else appendChangedStringFlag(flags, "server-deploy", config.serverDeploy, "none");
117
+ appendChangedGraphStringFlag(flags, config, "payments", "typescript", "payments", config.payments, "none");
118
+ appendChangedGraphStringFlag(flags, config, "email", "typescript", "email", config.email, "none");
119
+ appendChangedStringFlag(flags, "effect", config.effect, "none");
120
+ appendChangedGraphStringFlag(flags, config, "ai", "typescript", "ai", config.ai, "none");
121
+ appendChangedGraphStringFlag(flags, config, "realtime", "typescript", "realtime", config.realtime, "none");
122
+ appendChangedGraphStringFlag(flags, config, "jobQueue", "typescript", "job-queue", config.jobQueue, "none");
123
+ appendChangedGraphStringFlag(flags, config, "logging", "typescript", "logging", config.logging, "none");
124
+ appendChangedGraphStringFlag(flags, config, "observability", "typescript", "observability", config.observability, "none");
125
+ appendChangedGraphStringFlag(flags, config, "featureFlags", "typescript", "feature-flags", config.featureFlags, "none");
126
+ appendChangedGraphStringFlag(flags, config, "caching", "typescript", "caching", config.caching, "none");
127
+ appendChangedGraphStringFlag(flags, config, "rateLimit", "typescript", "rate-limit", config.rateLimit, "none");
128
+ appendChangedGraphStringFlag(flags, config, "cms", "typescript", "cms", config.cms, "none");
129
+ appendChangedGraphStringFlag(flags, config, "search", "typescript", "search", config.search, "none");
130
+ appendChangedGraphStringFlag(flags, config, "vectorDb", "typescript", "vectorDb", config.vectorDb, "none");
131
+ appendChangedGraphStringFlag(flags, config, "fileStorage", "typescript", "file-storage", config.fileStorage, "none");
132
+ }
133
+ if (hasGraphPrimaryPart(config, "mobile")) {
134
+ appendChangedOwnedGraphStringFlag(flags, config, "mobile", "navigation", "react-native", "mobile-navigation", config.mobileNavigation, "expo-router");
135
+ appendChangedOwnedGraphStringFlag(flags, config, "mobile", "ui", "react-native", "mobile-ui", config.mobileUI, "none");
136
+ appendChangedOwnedGraphStringFlag(flags, config, "mobile", "storage", "react-native", "mobile-storage", config.mobileStorage, "none");
137
+ appendChangedOwnedGraphStringFlag(flags, config, "mobile", "testing", "react-native", "mobile-testing", config.mobileTesting, "none");
138
+ appendChangedOwnedGraphStringFlag(flags, config, "mobile", "push", "react-native", "mobile-push", config.mobilePush, "none");
139
+ appendChangedOwnedGraphStringFlag(flags, config, "mobile", "ota", "react-native", "mobile-ota", config.mobileOTA, "none");
140
+ appendChangedOwnedGraphStringFlag(flags, config, "mobile", "deepLinking", "react-native", "mobile-deep-linking", config.mobileDeepLinking, "none");
141
+ }
142
+ if (hasGraphPrimaryPart(config, "frontend", "rust")) appendChangedStringFlag(flags, "rust-frontend", config.rustFrontend, "none");
143
+ if (hasGraphPrimaryPart(config, "backend", "rust")) {
144
+ appendChangedOwnedGraphStringFlag(flags, config, "backend", "cli", "rust", "rust-cli", config.rustCli, "none");
145
+ appendChangedOwnedGraphArrayFlag(flags, config, "backend", "libraries", "rust", "rust-libraries", config.rustLibraries, []);
146
+ appendChangedOwnedGraphStringFlag(flags, config, "backend", "logging", "rust", "rust-logging", config.rustLogging, "tracing");
147
+ appendChangedOwnedGraphStringFlag(flags, config, "backend", "errorHandling", "rust", "rust-error-handling", config.rustErrorHandling, "anyhow-thiserror");
148
+ appendChangedOwnedGraphStringFlag(flags, config, "backend", "caching", "rust", "rust-caching", config.rustCaching, "none");
149
+ }
150
+ if (hasGraphPrimaryPart(config, "backend", "python")) {
151
+ appendChangedOwnedGraphStringFlag(flags, config, "backend", "validation", "python", "python-validation", config.pythonValidation, "none");
152
+ appendChangedOwnedGraphArrayFlag(flags, config, "backend", "ai", "python", "python-ai", config.pythonAi, []);
153
+ appendChangedOwnedGraphStringFlag(flags, config, "backend", "jobQueue", "python", "python-task-queue", config.pythonTaskQueue, "none");
154
+ appendChangedOwnedGraphStringFlag(flags, config, "backend", "api", "python", "python-graphql", config.pythonGraphql, "none");
155
+ appendChangedOwnedGraphStringFlag(flags, config, "backend", "codeQuality", "python", "python-quality", config.pythonQuality, "none");
156
+ }
157
+ if (hasGraphPrimaryPart(config, "backend", "go")) {
158
+ appendChangedOwnedGraphStringFlag(flags, config, "backend", "cli", "go", "go-cli", config.goCli, "none");
159
+ appendChangedOwnedGraphStringFlag(flags, config, "backend", "logging", "go", "go-logging", config.goLogging, "none");
160
+ }
161
+ if (hasGraphPrimaryPart(config, "backend", "java")) {
162
+ appendChangedOwnedGraphStringFlag(flags, config, "backend", "buildTool", "java", "java-build-tool", config.javaBuildTool, "maven");
163
+ appendChangedOwnedGraphArrayFlag(flags, config, "backend", "libraries", "java", "java-libraries", config.javaLibraries, []);
164
+ appendChangedOwnedGraphArrayFlag(flags, config, "backend", "testing", "java", "java-testing-libraries", config.javaTestingLibraries, ["junit5"]);
165
+ }
166
+ if (hasGraphPrimaryPart(config, "backend", "elixir")) {
167
+ appendChangedGraphStringFlag(flags, config, "realtime", "elixir", "elixir-realtime", config.elixirRealtime, "channels");
168
+ appendChangedGraphStringFlag(flags, config, "jobQueue", "elixir", "elixir-jobs", config.elixirJobs, "none");
169
+ appendChangedGraphStringFlag(flags, config, "validation", "elixir", "elixir-validation", config.elixirValidation, "ecto-changesets");
170
+ appendChangedOwnedGraphStringFlag(flags, config, "backend", "httpClient", "elixir", "elixir-http", config.elixirHttp, "req");
171
+ appendChangedStringFlag(flags, "elixir-json", config.elixirJson, "jason");
172
+ appendChangedGraphStringFlag(flags, config, "email", "elixir", "elixir-email", config.elixirEmail, "none");
173
+ appendChangedGraphStringFlag(flags, config, "caching", "elixir", "elixir-caching", config.elixirCaching, "none");
174
+ appendChangedGraphStringFlag(flags, config, "observability", "elixir", "elixir-observability", config.elixirObservability, "telemetry");
175
+ appendChangedGraphStringFlag(flags, config, "testing", "elixir", "elixir-testing", config.elixirTesting, "ex_unit");
176
+ appendChangedOwnedGraphStringFlag(flags, config, "backend", "codeQuality", "elixir", "elixir-quality", config.elixirQuality, "credo");
177
+ appendChangedGraphStringFlag(flags, config, "deploy", "elixir", "elixir-deploy", config.elixirDeploy, "none");
178
+ }
179
+ }
180
+ function appendSharedNonTypeScriptFlags(flags, config) {
181
+ flags.push(`--email ${config.email}`);
182
+ flags.push(`--observability ${config.observability}`);
183
+ flags.push(`--caching ${config.caching}`);
184
+ flags.push(`--search ${config.search}`);
185
+ flags.push(formatArrayFlag("addons", config.addons));
186
+ flags.push(formatArrayFlag("examples", config.examples));
187
+ flags.push(`--db-setup ${config.dbSetup}`);
188
+ flags.push(`--web-deploy ${config.webDeploy}`);
189
+ flags.push(`--server-deploy ${config.serverDeploy}`);
190
+ }
191
+ function getTypeScriptFlags(config) {
192
+ const flags = [];
193
+ if (config.frontend && config.frontend.length > 0) flags.push(`--frontend ${config.frontend.join(" ")}`);
194
+ else flags.push("--frontend none");
195
+ appendAstroIntegrationFlag(flags, config);
196
+ flags.push(`--backend ${config.backend}`);
197
+ flags.push(`--runtime ${config.runtime}`);
198
+ flags.push(`--database ${config.database}`);
199
+ flags.push(`--orm ${config.orm}`);
200
+ flags.push(`--api ${config.api}`);
201
+ flags.push(`--auth ${config.auth}`);
202
+ flags.push(`--payments ${config.payments}`);
203
+ flags.push(`--email ${config.email}`);
204
+ flags.push(`--file-upload ${config.fileUpload}`);
205
+ flags.push(`--effect ${config.effect}`);
206
+ flags.push(`--css-framework ${config.cssFramework}`);
207
+ flags.push(`--ui-library ${config.uiLibrary}`);
208
+ if (config.uiLibrary === "shadcn-ui") {
209
+ flags.push(`--shadcn-base ${config.shadcnBase}`);
210
+ flags.push(`--shadcn-style ${config.shadcnStyle}`);
211
+ flags.push(`--shadcn-icon-library ${config.shadcnIconLibrary}`);
212
+ flags.push(`--shadcn-color-theme ${config.shadcnColorTheme}`);
213
+ flags.push(`--shadcn-base-color ${config.shadcnBaseColor}`);
214
+ flags.push(`--shadcn-font ${config.shadcnFont}`);
215
+ flags.push(`--shadcn-radius ${config.shadcnRadius}`);
216
+ }
217
+ flags.push(`--ai ${config.ai}`);
218
+ flags.push(`--state-management ${config.stateManagement}`);
219
+ flags.push(`--forms ${config.forms}`);
220
+ flags.push(`--validation ${config.validation}`);
221
+ flags.push(`--testing ${config.testing}`);
222
+ flags.push(`--animation ${config.animation}`);
223
+ flags.push(`--realtime ${config.realtime}`);
224
+ flags.push(`--job-queue ${config.jobQueue}`);
225
+ flags.push(`--logging ${config.logging}`);
226
+ flags.push(`--observability ${config.observability}`);
227
+ flags.push(`--feature-flags ${config.featureFlags}`);
228
+ flags.push(`--caching ${config.caching}`);
229
+ flags.push(`--rate-limit ${config.rateLimit}`);
230
+ flags.push(`--i18n ${config.i18n}`);
231
+ flags.push(`--cms ${config.cms}`);
232
+ flags.push(`--search ${config.search}`);
233
+ flags.push(`--vector-db ${config.vectorDb}`);
234
+ flags.push(`--file-storage ${config.fileStorage}`);
235
+ flags.push(`--mobile-navigation ${config.mobileNavigation}`);
236
+ flags.push(`--mobile-ui ${config.mobileUI}`);
237
+ flags.push(`--mobile-storage ${config.mobileStorage}`);
238
+ flags.push(`--mobile-testing ${config.mobileTesting}`);
239
+ flags.push(`--mobile-push ${config.mobilePush}`);
240
+ flags.push(`--mobile-ota ${config.mobileOTA}`);
241
+ flags.push(`--mobile-deep-linking ${config.mobileDeepLinking}`);
242
+ if (config.addons && config.addons.length > 0) flags.push(`--addons ${config.addons.join(" ")}`);
243
+ else flags.push("--addons none");
244
+ if (config.examples && config.examples.length > 0) flags.push(`--examples ${config.examples.join(" ")}`);
245
+ else flags.push("--examples none");
246
+ flags.push(`--db-setup ${config.dbSetup}`);
247
+ flags.push(`--web-deploy ${config.webDeploy}`);
248
+ flags.push(`--server-deploy ${config.serverDeploy}`);
249
+ appendCommonFlags(flags, config);
250
+ return flags;
251
+ }
252
+ function getReactNativeFlags(config) {
253
+ const flags = ["--ecosystem react-native"];
254
+ if (config.frontend && config.frontend.length > 0) flags.push(`--frontend ${config.frontend.join(" ")}`);
255
+ else flags.push("--frontend native-bare");
256
+ flags.push(`--auth ${config.auth}`);
257
+ flags.push(`--mobile-navigation ${config.mobileNavigation}`);
258
+ flags.push(`--mobile-ui ${config.mobileUI}`);
259
+ flags.push(`--mobile-storage ${config.mobileStorage}`);
260
+ flags.push(`--mobile-testing ${config.mobileTesting}`);
261
+ flags.push(`--mobile-push ${config.mobilePush}`);
262
+ flags.push(`--mobile-ota ${config.mobileOTA}`);
263
+ flags.push(`--mobile-deep-linking ${config.mobileDeepLinking}`);
264
+ appendCommonFlags(flags, config);
265
+ return flags;
266
+ }
267
+ function getRustFlags(config) {
268
+ const flags = ["--ecosystem rust"];
269
+ flags.push(`--rust-web-framework ${config.rustWebFramework}`);
270
+ flags.push(`--rust-frontend ${config.rustFrontend}`);
271
+ flags.push(`--rust-orm ${config.rustOrm}`);
272
+ flags.push(`--rust-api ${config.rustApi}`);
273
+ flags.push(`--rust-cli ${config.rustCli}`);
274
+ flags.push(formatArrayFlag("rust-libraries", config.rustLibraries));
275
+ flags.push(`--rust-logging ${config.rustLogging}`);
276
+ flags.push(`--rust-error-handling ${config.rustErrorHandling}`);
277
+ flags.push(`--rust-caching ${config.rustCaching}`);
278
+ flags.push(`--rust-auth ${config.rustAuth}`);
279
+ flags.push(`--rust-realtime ${config.rustRealtime}`);
280
+ flags.push(`--rust-message-queue ${config.rustMessageQueue}`);
281
+ flags.push(`--rust-observability ${config.rustObservability}`);
282
+ flags.push(`--rust-templating ${config.rustTemplating}`);
283
+ appendSharedNonTypeScriptFlags(flags, config);
284
+ appendCommonFlags(flags, config);
285
+ return flags;
286
+ }
287
+ function getPythonFlags(config) {
288
+ const flags = ["--ecosystem python"];
289
+ flags.push(`--python-web-framework ${config.pythonWebFramework}`);
290
+ flags.push(`--python-orm ${config.pythonOrm}`);
291
+ flags.push(`--python-validation ${config.pythonValidation}`);
292
+ flags.push(formatArrayFlag("python-ai", config.pythonAi));
293
+ flags.push(`--python-auth ${config.pythonAuth}`);
294
+ flags.push(`--python-api ${config.pythonApi}`);
295
+ flags.push(`--python-task-queue ${config.pythonTaskQueue}`);
296
+ flags.push(`--python-graphql ${config.pythonGraphql}`);
297
+ flags.push(`--python-quality ${config.pythonQuality}`);
298
+ flags.push(formatArrayFlag("python-testing", config.pythonTesting));
299
+ flags.push(`--python-caching ${config.pythonCaching}`);
300
+ flags.push(`--python-realtime ${config.pythonRealtime}`);
301
+ flags.push(`--python-observability ${config.pythonObservability}`);
302
+ flags.push(formatArrayFlag("python-cli", config.pythonCli));
303
+ appendSharedNonTypeScriptFlags(flags, config);
304
+ appendCommonFlags(flags, config);
305
+ return flags;
306
+ }
307
+ function getGoFlags(config) {
308
+ const flags = ["--ecosystem go"];
309
+ flags.push(`--go-web-framework ${config.goWebFramework}`);
310
+ flags.push(`--go-orm ${config.goOrm}`);
311
+ flags.push(`--go-api ${config.goApi}`);
312
+ flags.push(`--go-cli ${config.goCli}`);
313
+ flags.push(`--go-logging ${config.goLogging}`);
314
+ flags.push(`--go-auth ${config.goAuth}`);
315
+ flags.push(formatArrayFlag("go-testing", config.goTesting));
316
+ flags.push(`--go-realtime ${config.goRealtime}`);
317
+ flags.push(`--go-message-queue ${config.goMessageQueue}`);
318
+ flags.push(`--go-caching ${config.goCaching}`);
319
+ flags.push(`--go-config ${config.goConfig}`);
320
+ flags.push(`--go-observability ${config.goObservability}`);
321
+ flags.push(`--auth ${config.auth}`);
322
+ appendSharedNonTypeScriptFlags(flags, config);
323
+ appendCommonFlags(flags, config);
324
+ return flags;
325
+ }
326
+ function getJavaFlags(config) {
327
+ const flags = ["--ecosystem java"];
328
+ flags.push(`--java-web-framework ${config.javaWebFramework}`);
329
+ flags.push(`--java-build-tool ${config.javaBuildTool}`);
330
+ flags.push(`--java-orm ${config.javaOrm}`);
331
+ flags.push(`--java-auth ${config.javaAuth}`);
332
+ flags.push(`--java-api ${config.javaApi}`);
333
+ flags.push(`--java-logging ${config.javaLogging}`);
334
+ flags.push(formatArrayFlag("java-libraries", config.javaLibraries));
335
+ flags.push(formatArrayFlag("java-testing-libraries", config.javaTestingLibraries));
336
+ appendSharedNonTypeScriptFlags(flags, config);
337
+ appendCommonFlags(flags, config);
338
+ return flags;
339
+ }
340
+ function getDotnetFlags(config) {
341
+ const flags = ["--ecosystem dotnet"];
342
+ flags.push(`--dotnet-web-framework ${config.dotnetWebFramework}`);
343
+ flags.push(`--dotnet-orm ${config.dotnetOrm}`);
344
+ flags.push(`--dotnet-auth ${config.dotnetAuth}`);
345
+ flags.push(`--dotnet-api ${config.dotnetApi}`);
346
+ flags.push(formatArrayFlag("dotnet-testing", config.dotnetTesting));
347
+ flags.push(`--dotnet-job-queue ${config.dotnetJobQueue}`);
348
+ flags.push(`--dotnet-realtime ${config.dotnetRealtime}`);
349
+ flags.push(formatArrayFlag("dotnet-observability", config.dotnetObservability));
350
+ flags.push(`--dotnet-validation ${config.dotnetValidation}`);
351
+ flags.push(`--dotnet-caching ${config.dotnetCaching}`);
352
+ flags.push(`--dotnet-deploy ${config.dotnetDeploy}`);
353
+ appendCommonFlags(flags, config);
354
+ return flags;
355
+ }
356
+ function getElixirFlags(config) {
357
+ const flags = ["--ecosystem elixir"];
358
+ flags.push(`--elixir-web-framework ${config.elixirWebFramework}`);
359
+ flags.push(`--elixir-orm ${config.elixirOrm}`);
360
+ flags.push(`--elixir-auth ${config.elixirAuth}`);
361
+ flags.push(`--elixir-api ${config.elixirApi}`);
362
+ flags.push(`--elixir-realtime ${config.elixirRealtime}`);
363
+ flags.push(`--elixir-jobs ${config.elixirJobs}`);
364
+ flags.push(`--elixir-validation ${config.elixirValidation}`);
365
+ flags.push(`--elixir-http ${config.elixirHttp}`);
366
+ flags.push(`--elixir-json ${config.elixirJson}`);
367
+ flags.push(`--elixir-email ${config.elixirEmail}`);
368
+ flags.push(`--elixir-caching ${config.elixirCaching}`);
369
+ flags.push(`--elixir-observability ${config.elixirObservability}`);
370
+ flags.push(`--elixir-testing ${config.elixirTesting}`);
371
+ flags.push(`--elixir-quality ${config.elixirQuality}`);
372
+ flags.push(`--elixir-deploy ${config.elixirDeploy}`);
373
+ flags.push(formatArrayFlag("elixir-libraries", config.elixirLibraries));
374
+ appendCommonFlags(flags, config);
375
+ return flags;
376
+ }
377
+ function generateReproducibleCommand(config) {
378
+ let flags;
379
+ if (config.stackParts && config.stackParts.length > 0) {
380
+ flags = config.stackParts.filter((part) => part.source !== "provided" && part.toolId !== "none").map((part) => `--part ${(0, types_exports.formatStackPartSpec)(part, config.stackParts ?? [])}`);
381
+ appendGraphExtraFlags(flags, config);
382
+ appendCommonFlags(flags, config);
383
+ return `${getBaseCommand(config.packageManager)}${config.relativePath ? ` ${config.relativePath}` : ""} ${flags.join(" ")}`;
384
+ }
385
+ switch (config.ecosystem) {
386
+ case "react-native":
387
+ flags = getReactNativeFlags(config);
388
+ break;
389
+ case "rust":
390
+ flags = getRustFlags(config);
391
+ break;
392
+ case "python":
393
+ flags = getPythonFlags(config);
394
+ break;
395
+ case "go":
396
+ flags = getGoFlags(config);
397
+ break;
398
+ case "java":
399
+ flags = getJavaFlags(config);
400
+ break;
401
+ case "dotnet":
402
+ flags = getDotnetFlags(config);
403
+ break;
404
+ case "elixir":
405
+ flags = getElixirFlags(config);
406
+ break;
407
+ case "typescript":
408
+ default:
409
+ flags = getTypeScriptFlags(config);
410
+ break;
411
+ }
412
+ return `${getBaseCommand(config.packageManager)}${config.relativePath ? ` ${config.relativePath}` : ""} ${flags.join(" ")}`;
413
+ }
414
+
415
+ //#endregion
416
+ //#region src/utils/templates.ts
417
+ const TEMPLATE_PRESETS = {
418
+ mern: {
419
+ database: "mongodb",
420
+ orm: "mongoose",
421
+ backend: "express",
422
+ runtime: "node",
423
+ frontend: ["react-router"],
424
+ api: "orpc",
425
+ auth: "better-auth",
426
+ payments: "none",
427
+ addons: ["turborepo"],
428
+ examples: ["none"],
429
+ dbSetup: "mongodb-atlas",
430
+ webDeploy: "none",
431
+ serverDeploy: "none"
432
+ },
433
+ pern: {
434
+ database: "postgres",
435
+ orm: "drizzle",
436
+ backend: "express",
437
+ runtime: "node",
438
+ frontend: ["tanstack-router"],
439
+ api: "trpc",
440
+ auth: "better-auth",
441
+ payments: "none",
442
+ addons: ["turborepo"],
443
+ examples: ["none"],
444
+ dbSetup: "none",
445
+ webDeploy: "none",
446
+ serverDeploy: "none"
447
+ },
448
+ t3: {
449
+ database: "postgres",
450
+ orm: "prisma",
451
+ backend: "self",
452
+ runtime: "none",
453
+ frontend: ["next"],
454
+ api: "trpc",
455
+ auth: "better-auth",
456
+ payments: "none",
457
+ addons: ["biome", "turborepo"],
458
+ examples: ["none"],
459
+ dbSetup: "none",
460
+ webDeploy: "none",
461
+ serverDeploy: "none"
462
+ },
463
+ uniwind: {
464
+ database: "none",
465
+ orm: "none",
466
+ backend: "none",
467
+ runtime: "none",
468
+ frontend: ["native-uniwind"],
469
+ api: "none",
470
+ auth: "none",
471
+ payments: "none",
472
+ addons: ["none"],
473
+ examples: ["none"],
474
+ dbSetup: "none",
475
+ webDeploy: "none",
476
+ serverDeploy: "none"
477
+ },
478
+ none: null
479
+ };
480
+ function getTemplateConfig(template) {
481
+ if (template === "none" || !template) return null;
482
+ const config = TEMPLATE_PRESETS[template];
483
+ if (!config) throw new Error(`Unknown template: ${template}`);
484
+ return config;
485
+ }
486
+ function getTemplateDescription(template) {
487
+ return {
488
+ mern: "MongoDB + Express + React + Node.js - Classic MERN stack",
489
+ pern: "PostgreSQL + Express + React + Node.js - Popular PERN stack",
490
+ t3: "T3 Stack - Next.js + tRPC + Prisma + PostgreSQL + Better Auth",
491
+ uniwind: "Expo + Uniwind native app with no backend services",
492
+ none: "No template - Full customization"
493
+ }[template] || "";
494
+ }
495
+
496
+ //#endregion
497
+ export { getTemplateDescription as n, generateReproducibleCommand as r, getTemplateConfig as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-better-fullstack",
3
- "version": "2.1.3",
3
+ "version": "2.1.5",
4
4
  "description": "Scaffold production-ready fullstack apps in seconds. Pick your stack from 425 options — the CLI wires everything together.",
5
5
  "keywords": [
6
6
  "algolia",
@@ -128,8 +128,8 @@
128
128
  "prepublishOnly": "npm run build"
129
129
  },
130
130
  "dependencies": {
131
- "@better-fullstack/template-generator": "^2.1.3",
132
- "@better-fullstack/types": "^2.1.3",
131
+ "@better-fullstack/template-generator": "^2.1.5",
132
+ "@better-fullstack/types": "^2.1.5",
133
133
  "@clack/core": "^0.5.0",
134
134
  "@clack/prompts": "^1.6.0",
135
135
  "@orpc/server": "^1.14.6",
@@ -1,151 +0,0 @@
1
- #!/usr/bin/env node
2
- import { c as isSilent, g as updateBtsConfig, l as runWithContextAsync, m as readBtsConfig, n as UserCancelledError, t as CLIError, x as getDefaultConfig } from "./errors-D9yiiGVq.mjs";
3
- import { t as renderTitle } from "./render-title-zvyKC1ej.mjs";
4
- import { t as setupAddons } from "./addons-setup-C_xrNtkL.mjs";
5
- import "./compatibility-rules-D7zYNVjC.mjs";
6
- import { c as applyDependencyVersionChannel, t as installDependencies, u as getAddonsToAdd } from "./install-dependencies-DDGF-zDG.mjs";
7
- import { intro, log, outro } from "@clack/prompts";
8
- import pc from "picocolors";
9
- import fs from "fs-extra";
10
- import path from "node:path";
11
- import { EMBEDDED_TEMPLATES, VirtualFileSystem, processAddonTemplates, processAddonsDeps } from "@better-fullstack/template-generator";
12
- import { writeTreeToFilesystem } from "@better-fullstack/template-generator/fs-writer";
13
-
14
- //#region src/helpers/core/add-handler.ts
15
- async function addHandler(input, options = {}) {
16
- const { silent = false } = options;
17
- return runWithContextAsync({ silent }, async () => {
18
- try {
19
- return await addHandlerInternal(input);
20
- } catch (error) {
21
- if (error instanceof UserCancelledError) {
22
- if (isSilent()) return {
23
- success: false,
24
- addedAddons: [],
25
- projectDir: "",
26
- error: error.message
27
- };
28
- return;
29
- }
30
- if (error instanceof CLIError) {
31
- if (isSilent()) return {
32
- success: false,
33
- addedAddons: [],
34
- projectDir: "",
35
- error: error.message
36
- };
37
- throw error;
38
- }
39
- if (isSilent()) return {
40
- success: false,
41
- addedAddons: [],
42
- projectDir: "",
43
- error: error instanceof Error ? error.message : String(error)
44
- };
45
- throw error;
46
- }
47
- });
48
- }
49
- async function addHandlerInternal(input) {
50
- const projectDir = path.resolve(input.projectDir || process.cwd());
51
- if (!isSilent()) {
52
- renderTitle();
53
- intro(pc.magenta("Add addons to your Better Fullstack project"));
54
- }
55
- const btsConfig = await readBtsConfig(projectDir);
56
- if (!btsConfig) throw new CLIError(`No Better Fullstack project found in ${projectDir}. Make sure bts.jsonc exists.`);
57
- const projectName = path.basename(projectDir);
58
- if (!isSilent()) log.info(pc.dim(`Detected project: ${projectName}`));
59
- const existingAddons = btsConfig.addons || [];
60
- let addonsToAdd = [];
61
- if (input.addons && input.addons.length > 0) addonsToAdd = input.addons.filter((addon) => addon !== "none" && !existingAddons.includes(addon));
62
- else addonsToAdd = (await getAddonsToAdd(btsConfig.frontend || [], existingAddons, btsConfig.auth)).filter((addon) => addon !== "none");
63
- if (addonsToAdd.length === 0) {
64
- if (!isSilent()) {
65
- log.info(pc.dim("No new addons selected."));
66
- outro(pc.magenta("Nothing to add."));
67
- }
68
- return {
69
- success: true,
70
- addedAddons: [],
71
- projectDir
72
- };
73
- }
74
- if (!isSilent()) log.info(pc.cyan(`Adding addons: ${addonsToAdd.join(", ")}`));
75
- const baseConfig = getDefaultConfig();
76
- const config = {
77
- ...baseConfig,
78
- ...btsConfig,
79
- projectName,
80
- projectDir,
81
- relativePath: ".",
82
- packageManager: input.packageManager || btsConfig.packageManager || baseConfig.packageManager,
83
- addons: addonsToAdd,
84
- frontend: btsConfig.frontend || baseConfig.frontend,
85
- examples: btsConfig.examples || [],
86
- rustLibraries: btsConfig.rustLibraries || [],
87
- pythonAi: btsConfig.pythonAi || [],
88
- aiDocs: btsConfig.aiDocs || []
89
- };
90
- const vfs = new VirtualFileSystem();
91
- const packageJsonPaths = await collectPackageJsonPaths(projectDir);
92
- for (const pkgPath of packageJsonPaths) {
93
- const fullPath = path.join(projectDir, pkgPath);
94
- const content = await fs.readFile(fullPath, "utf-8");
95
- vfs.writeFile(pkgPath, content);
96
- }
97
- await processAddonTemplates(vfs, EMBEDDED_TEMPLATES, config);
98
- processAddonsDeps(vfs, config);
99
- await writeTreeToFilesystem({
100
- root: vfs.toTree(projectName),
101
- fileCount: vfs.getFileCount(),
102
- directoryCount: vfs.getDirectoryCount(),
103
- config
104
- }, projectDir);
105
- const setupWarnings = await setupAddons(config);
106
- await applyDependencyVersionChannel(projectDir, config.versionChannel);
107
- const configUpdates = { addons: [...new Set([...existingAddons, ...addonsToAdd])] };
108
- if (input.webDeploy !== void 0) configUpdates.webDeploy = input.webDeploy;
109
- if (input.serverDeploy !== void 0) configUpdates.serverDeploy = input.serverDeploy;
110
- await updateBtsConfig(projectDir, configUpdates);
111
- let addonInstallFailed = false;
112
- if (input.install) addonInstallFailed = !(await installDependencies({
113
- projectDir,
114
- packageManager: config.packageManager
115
- })).success;
116
- if (!isSilent()) {
117
- log.success(pc.green(`Successfully added: ${addonsToAdd.join(", ")}`));
118
- for (const warning of setupWarnings) log.warn(pc.yellow(warning));
119
- const installCmd = config.packageManager === "npm" ? "npm install" : `${config.packageManager} install`;
120
- if (!input.install) log.info(pc.yellow(`Run '${installCmd}' to install new dependencies.`));
121
- else if (addonInstallFailed) log.warn(pc.yellow(`Dependency installation failed. Run '${installCmd}' after resolving the error above.`));
122
- outro(pc.magenta("Addons added successfully!"));
123
- }
124
- return {
125
- success: true,
126
- addedAddons: addonsToAdd,
127
- projectDir,
128
- setupWarnings: setupWarnings.length > 0 ? setupWarnings : void 0
129
- };
130
- }
131
- async function collectPackageJsonPaths(projectDir) {
132
- const results = [];
133
- async function walk(currentDir) {
134
- const entries = await fs.readdir(currentDir, { withFileTypes: true });
135
- for (const entry of entries) {
136
- if (entry.name === "node_modules" || entry.name === ".git" || entry.name === ".turbo") continue;
137
- const fullPath = path.join(currentDir, entry.name);
138
- if (entry.isDirectory()) {
139
- await walk(fullPath);
140
- continue;
141
- }
142
- if (entry.isFile() && entry.name === "package.json") results.push(path.relative(projectDir, fullPath).replaceAll(path.sep, "/"));
143
- }
144
- }
145
- await walk(projectDir);
146
- if (!results.includes("package.json") && await fs.pathExists(path.join(projectDir, "package.json"))) results.push("package.json");
147
- return results;
148
- }
149
-
150
- //#endregion
151
- export { addHandler };