create-better-fullstack 2.0.3 → 2.1.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.
@@ -1,46 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import { f as types_exports, m as DEFAULT_CONFIG } from "./bts-config-Bg1Qea9Y.mjs";
3
- import { a as CLIError, c as exitWithError, d as isFirstPrompt, f as isSilent, h as setLastPromptShownUI, m as setIsFirstPrompt$1, s as exitCancelled } from "./addons-setup-HSghQS7c.mjs";
2
+ import { c as DEFAULT_CONFIG, y as types_exports } from "./bts-config-InNcw1aJ.mjs";
3
+ import { c as isSilent, d as setLastPromptShownUI, i as exitWithError, r as exitCancelled, s as isFirstPrompt, t as CLIError, u as setIsFirstPrompt$1 } from "./errors-ns_o2OKg.mjs";
4
4
  import { log, spinner } from "@clack/prompts";
5
5
  import pc from "picocolors";
6
6
  import fs from "fs-extra";
7
7
  import path from "node:path";
8
8
  import { allowedApisForFrontends, getAIFrontendCompatibilityIssue, getApiFrontendCompatibilityIssue, getCompatibleAddons, getCompatibleCSSFrameworks, getCompatibleUILibraries, getUnsupportedWebDeployFrontend, hasDockerComposeCompatibleFrontend, hasWebStyling, isBackendUtilsCompatibleBackend, isExampleAIAllowed, isExampleChatSdkAllowed, isFrontendAllowedWithBackend, isWebFrontend, requiresChatSdkVercelAIForSelection, splitFrontends, validateAddonCompatibility } from "@better-fullstack/types";
9
- import gradient from "gradient-string";
10
9
  import consola from "consola";
11
10
  import { ConfirmPrompt, GroupMultiSelectPrompt, MultiSelectPrompt, SelectPrompt, isCancel as isCancel$1 } from "@clack/core";
12
11
  import { $ } from "execa";
13
12
 
14
- //#region src/utils/render-title.ts
15
- const TITLE_TEXT = `
16
- ██████╗ ███████╗████████╗████████╗███████╗██████╗
17
- ██╔══██╗██╔════╝╚══██╔══╝╚══██╔══╝██╔════╝██╔══██╗
18
- ██████╔╝█████╗ ██║ ██║ █████╗ ██████╔╝
19
- ██╔══██╗██╔══╝ ██║ ██║ ██╔══╝ ██╔══██╗
20
- ██████╔╝███████╗ ██║ ██║ ███████╗██║ ██║
21
- ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝
22
-
23
- ███████╗██╗ ██╗██╗ ██╗ ███████╗████████╗ █████╗ ██████╗██╗ ██╗
24
- ██╔════╝██║ ██║██║ ██║ ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝
25
- █████╗ ██║ ██║██║ ██║ ███████╗ ██║ ███████║██║ █████╔╝
26
- ██╔══╝ ██║ ██║██║ ██║ ╚════██║ ██║ ██╔══██║██║ ██╔═██╗
27
- ██║ ╚██████╔╝███████╗███████╗███████║ ██║ ██║ ██║╚██████╗██║ ██╗
28
- ╚═╝ ╚═════╝ ╚══════╝╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
29
- `;
30
- const monochromeTheme = {
31
- white: "#FFFFFF",
32
- lightGray: "#E5E5E5",
33
- gray: "#A3A3A3",
34
- darkGray: "#737373"
35
- };
36
- const renderTitle = () => {
37
- const terminalWidth = process.stdout.columns || 80;
38
- const titleLines = TITLE_TEXT.split("\n");
39
- if (terminalWidth < Math.max(...titleLines.map((line) => line.length))) console.log(gradient(Object.values(monochromeTheme)).multiline(`Better Fullstack`));
40
- else console.log(gradient(Object.values(monochromeTheme)).multiline(TITLE_TEXT));
41
- };
42
-
43
- //#endregion
44
13
  //#region src/utils/error-formatter.ts
45
14
  function getCategoryTitle(category) {
46
15
  return {
@@ -241,34 +210,46 @@ function validateAddonCompatibility$1(addon, frontend, _auth, backend, runtime,
241
210
  reason: "Backend Utils requires a Hono, Express, Fastify, Elysia, feTS, or NestJS backend"
242
211
  };
243
212
  }
244
- if (addon === "docker-compose") {
213
+ if (addon === "docker-compose" || addon === "devcontainer") {
214
+ const label = addon === "devcontainer" ? "DevContainer" : "docker-compose";
215
+ const title = addon === "devcontainer" ? "DevContainer" : "Docker Compose";
245
216
  if (backend === "convex") return {
246
217
  isCompatible: false,
247
- reason: "docker-compose is not compatible with Convex backend (managed service)"
218
+ reason: `${label} is not compatible with Convex backend (managed service)`
248
219
  };
249
220
  if (runtime === "workers") return {
250
221
  isCompatible: false,
251
- reason: "docker-compose is not compatible with Cloudflare Workers runtime"
222
+ reason: `${label} is not compatible with Cloudflare Workers runtime`
223
+ };
224
+ if (ecosystem !== void 0 && ![
225
+ "typescript",
226
+ "python",
227
+ "go",
228
+ "rust",
229
+ "java"
230
+ ].includes(ecosystem)) return {
231
+ isCompatible: false,
232
+ reason: `${title} currently supports TypeScript, Python, Go, Rust, or Java projects`
252
233
  };
253
234
  if (ecosystem === "typescript" && !hasDockerComposeCompatibleFrontend(frontend)) return {
254
235
  isCompatible: false,
255
- reason: "Docker Compose currently supports Next.js, Vinext, TanStack Router, React Router, React Vite, Solid, or Astro"
236
+ reason: `${title} currently supports Next.js, Vinext, TanStack Router, React Router, React Vite, Solid, or Astro`
256
237
  };
257
238
  if (ecosystem === "typescript" && backend === "self" && !frontend.includes("next") && !frontend.includes("vinext")) return {
258
239
  isCompatible: false,
259
- reason: "Docker Compose self-backend support currently requires Next.js or Vinext"
240
+ reason: `${title} self-backend support currently requires Next.js or Vinext`
260
241
  };
261
242
  if (ecosystem === "rust" && rustFrontend && rustFrontend !== "none") return {
262
243
  isCompatible: false,
263
- reason: "Docker Compose for Rust currently supports server-only projects"
244
+ reason: `${title} for Rust currently supports server-only projects`
264
245
  };
265
246
  if (ecosystem === "java" && javaWebFramework && javaWebFramework !== "spring-boot") return {
266
247
  isCompatible: false,
267
- reason: "Docker Compose for Java currently requires Spring Boot"
248
+ reason: `${title} for Java currently requires Spring Boot`
268
249
  };
269
250
  if (ecosystem === "python" && database && database !== "none" && database !== "sqlite" && database !== "postgres") return {
270
251
  isCompatible: false,
271
- reason: "Docker Compose for Python ORM projects currently supports SQLite defaults or Postgres"
252
+ reason: `${title} for Python ORM projects currently supports SQLite defaults or Postgres`
272
253
  };
273
254
  }
274
255
  return { isCompatible: true };
@@ -732,10 +713,18 @@ function getAddonDisplay(addon) {
732
713
  label = "Backend Utils";
733
714
  hint = "asyncHandler, ApiResponse & global error handler for your server";
734
715
  break;
716
+ case "devcontainer":
717
+ label = "DevContainer";
718
+ hint = "VS Code container config with stack-aware ports and extensions";
719
+ break;
735
720
  case "docker-compose":
736
721
  label = "Docker Compose";
737
722
  hint = "Containerize your app for deployment";
738
723
  break;
724
+ case "github-actions":
725
+ label = "GitHub Actions";
726
+ hint = "Ship a CI workflow (install, lint, type-check, build)";
727
+ break;
739
728
  default:
740
729
  label = addon;
741
730
  hint = `Add ${addon}`;
@@ -749,6 +738,7 @@ const ADDON_GROUPS = {
749
738
  Tooling: [
750
739
  "turborepo",
751
740
  "nx",
741
+ "github-actions",
752
742
  "biome",
753
743
  "oxlint",
754
744
  "ultracite",
@@ -762,6 +752,7 @@ const ADDON_GROUPS = {
762
752
  "opentui",
763
753
  "wxt",
764
754
  "ruler",
755
+ "devcontainer",
765
756
  "docker-compose"
766
757
  ],
767
758
  Integrations: [
@@ -1021,6 +1012,9 @@ async function applyDependencyVersionChannel(projectDir, channel) {
1021
1012
 
1022
1013
  //#endregion
1023
1014
  //#region src/helpers/core/install-dependencies.ts
1015
+ function toErrorMessage(error) {
1016
+ return error instanceof Error ? error.message : String(error);
1017
+ }
1024
1018
  function getInstallEnvironment(packageManager) {
1025
1019
  if (packageManager === "yarn") return {
1026
1020
  YARN_ENABLE_HARDENED_MODE: "0",
@@ -1033,6 +1027,7 @@ function getInstallArgs(packageManager) {
1033
1027
  }
1034
1028
  async function installDependencies({ projectDir, packageManager }) {
1035
1029
  const s = spinner();
1030
+ const step = "Install dependencies";
1036
1031
  try {
1037
1032
  s.start(`Running ${packageManager} install...`);
1038
1033
  const installArgs = getInstallArgs(packageManager);
@@ -1045,13 +1040,24 @@ async function installDependencies({ projectDir, packageManager }) {
1045
1040
  stderr: "inherit"
1046
1041
  })`${packageManager} ${installArgs}`;
1047
1042
  s.stop("Dependencies installed successfully");
1043
+ return {
1044
+ step,
1045
+ success: true
1046
+ };
1048
1047
  } catch (error) {
1049
1048
  s.stop(pc.red("Failed to install dependencies"));
1050
- if (error instanceof Error) consola.error(pc.red(`Installation error: ${error.message}`));
1049
+ const errorMessage = toErrorMessage(error);
1050
+ consola.error(pc.red(`Installation error: ${errorMessage}`));
1051
+ return {
1052
+ step,
1053
+ success: false,
1054
+ errorMessage
1055
+ };
1051
1056
  }
1052
1057
  }
1053
1058
  async function runCargoBuild({ projectDir }) {
1054
1059
  const s = spinner();
1060
+ const step = "Cargo build";
1055
1061
  try {
1056
1062
  s.start("Running cargo build...");
1057
1063
  await $({
@@ -1059,13 +1065,24 @@ async function runCargoBuild({ projectDir }) {
1059
1065
  stderr: "inherit"
1060
1066
  })`cargo build`;
1061
1067
  s.stop("Cargo build completed");
1068
+ return {
1069
+ step,
1070
+ success: true
1071
+ };
1062
1072
  } catch (error) {
1063
1073
  s.stop(pc.red("Cargo build failed"));
1064
- if (error instanceof Error) consola.error(pc.red(`Cargo build error: ${error.message}`));
1074
+ const errorMessage = toErrorMessage(error);
1075
+ consola.error(pc.red(`Cargo build error: ${errorMessage}`));
1076
+ return {
1077
+ step,
1078
+ success: false,
1079
+ errorMessage
1080
+ };
1065
1081
  }
1066
1082
  }
1067
1083
  async function runUvSync({ projectDir }) {
1068
1084
  const s = spinner();
1085
+ const step = "uv sync (Python dependencies)";
1069
1086
  try {
1070
1087
  s.start("Running uv sync...");
1071
1088
  await $({
@@ -1073,13 +1090,24 @@ async function runUvSync({ projectDir }) {
1073
1090
  stderr: "inherit"
1074
1091
  })`uv sync`;
1075
1092
  s.stop("Python dependencies installed successfully");
1093
+ return {
1094
+ step,
1095
+ success: true
1096
+ };
1076
1097
  } catch (error) {
1077
1098
  s.stop(pc.red("uv sync failed"));
1078
- if (error instanceof Error) consola.error(pc.red(`uv sync error: ${error.message}`));
1099
+ const errorMessage = toErrorMessage(error);
1100
+ consola.error(pc.red(`uv sync error: ${errorMessage}`));
1101
+ return {
1102
+ step,
1103
+ success: false,
1104
+ errorMessage
1105
+ };
1079
1106
  }
1080
1107
  }
1081
1108
  async function runGoModTidy({ projectDir }) {
1082
1109
  const s = spinner();
1110
+ const step = "go mod tidy";
1083
1111
  try {
1084
1112
  s.start("Running go mod tidy...");
1085
1113
  await $({
@@ -1087,14 +1115,25 @@ async function runGoModTidy({ projectDir }) {
1087
1115
  stderr: "inherit"
1088
1116
  })`go mod tidy`;
1089
1117
  s.stop("Go dependencies installed successfully");
1118
+ return {
1119
+ step,
1120
+ success: true
1121
+ };
1090
1122
  } catch (error) {
1091
1123
  s.stop(pc.red("go mod tidy failed"));
1092
- if (error instanceof Error) consola.error(pc.red(`go mod tidy error: ${error.message}`));
1124
+ const errorMessage = toErrorMessage(error);
1125
+ consola.error(pc.red(`go mod tidy error: ${errorMessage}`));
1126
+ return {
1127
+ step,
1128
+ success: false,
1129
+ errorMessage
1130
+ };
1093
1131
  }
1094
1132
  }
1095
1133
  async function runMavenTests({ projectDir }) {
1096
1134
  const s = spinner();
1097
1135
  const mvnw = process.platform === "win32" ? "mvnw.cmd" : "./mvnw";
1136
+ const step = "Maven tests";
1098
1137
  try {
1099
1138
  s.start("Running Maven tests...");
1100
1139
  await $({
@@ -1102,14 +1141,25 @@ async function runMavenTests({ projectDir }) {
1102
1141
  stderr: "inherit"
1103
1142
  })`${mvnw} test`;
1104
1143
  s.stop("Maven tests completed");
1144
+ return {
1145
+ step,
1146
+ success: true
1147
+ };
1105
1148
  } catch (error) {
1106
1149
  s.stop(pc.red("Maven tests failed"));
1107
- if (error instanceof Error) consola.error(pc.red(`Maven test error: ${error.message}`));
1150
+ const errorMessage = toErrorMessage(error);
1151
+ consola.error(pc.red(`Maven test error: ${errorMessage}`));
1152
+ return {
1153
+ step,
1154
+ success: false,
1155
+ errorMessage
1156
+ };
1108
1157
  }
1109
1158
  }
1110
1159
  async function runGradleTests({ projectDir }) {
1111
1160
  const s = spinner();
1112
1161
  const gradlew = process.platform === "win32" ? "gradlew.bat" : "./gradlew";
1162
+ const step = "Gradle tests";
1113
1163
  try {
1114
1164
  s.start("Running Gradle tests...");
1115
1165
  await $({
@@ -1117,13 +1167,24 @@ async function runGradleTests({ projectDir }) {
1117
1167
  stderr: "inherit"
1118
1168
  })`${gradlew} test`;
1119
1169
  s.stop("Gradle tests completed");
1170
+ return {
1171
+ step,
1172
+ success: true
1173
+ };
1120
1174
  } catch (error) {
1121
1175
  s.stop(pc.red("Gradle tests failed"));
1122
- if (error instanceof Error) consola.error(pc.red(`Gradle test error: ${error.message}`));
1176
+ const errorMessage = toErrorMessage(error);
1177
+ consola.error(pc.red(`Gradle test error: ${errorMessage}`));
1178
+ return {
1179
+ step,
1180
+ success: false,
1181
+ errorMessage
1182
+ };
1123
1183
  }
1124
1184
  }
1125
1185
  async function runMixCompile({ projectDir }) {
1126
1186
  const s = spinner();
1187
+ const step = "mix deps.get / compile";
1127
1188
  try {
1128
1189
  s.start("Running mix deps.get and mix compile...");
1129
1190
  await $({
@@ -1135,11 +1196,21 @@ async function runMixCompile({ projectDir }) {
1135
1196
  stderr: "inherit"
1136
1197
  })`mix compile`;
1137
1198
  s.stop("Elixir dependencies installed and project compiled");
1199
+ return {
1200
+ step,
1201
+ success: true
1202
+ };
1138
1203
  } catch (error) {
1139
1204
  s.stop(pc.red("mix compile failed"));
1140
- if (error instanceof Error) consola.error(pc.red(`Mix error: ${error.message}`));
1205
+ const errorMessage = toErrorMessage(error);
1206
+ consola.error(pc.red(`Mix error: ${errorMessage}`));
1207
+ return {
1208
+ step,
1209
+ success: false,
1210
+ errorMessage
1211
+ };
1141
1212
  }
1142
1213
  }
1143
1214
 
1144
1215
  //#endregion
1145
- export { validateAddonsAgainstFrontends as A, validateWorkersCompatibility as B, isExampleAIAllowed$1 as C, requiresChatSdkVercelAI as D, isWebFrontend$1 as E, validateServerDeployRequiresBackend as F, incompatibilityError as H, validateUILibraryCSSFrameworkCompatibility as I, validateUILibraryFrontendCompatibility as L, validateExamplesCompatibility as M, validatePaymentsCompatibility as N, splitFrontends$1 as O, validateSelfBackendCompatibility as P, validateWebDeployFrontendTemplates as R, hasWebStyling$1 as S, isFrontendAllowedWithBackend$1 as T, missingRequirementError as U, constraintError as V, renderTitle as W, isGoBack as _, runMavenTests as a, getCompatibleCSSFrameworks$1 as b, applyDependencyVersionChannel as c, isCancel$1 as d, navigableConfirm as f, GO_BACK_SYMBOL as g, setIsFirstPrompt as h, runGradleTests as i, validateApiFrontendCompatibility as j, validateAIFrontendCompatibility as k, getAddonsChoice as l, navigableSelect as m, runCargoBuild as n, runMixCompile as o, navigableMultiselect as p, runGoModTidy as r, runUvSync as s, installDependencies as t, getAddonsToAdd as u, allowedApisForFrontends$1 as v, isExampleChatSdkAllowed$1 as w, getCompatibleUILibraries$1 as x, ensureSingleWebAndNative as y, validateWebDeployRequiresWebFrontend as z };
1216
+ export { validateAddonsAgainstFrontends as A, validateWorkersCompatibility as B, isExampleAIAllowed$1 as C, requiresChatSdkVercelAI as D, isWebFrontend$1 as E, validateServerDeployRequiresBackend as F, incompatibilityError as H, validateUILibraryCSSFrameworkCompatibility as I, validateUILibraryFrontendCompatibility as L, validateExamplesCompatibility as M, validatePaymentsCompatibility as N, splitFrontends$1 as O, validateSelfBackendCompatibility as P, validateWebDeployFrontendTemplates as R, hasWebStyling$1 as S, isFrontendAllowedWithBackend$1 as T, missingRequirementError as U, constraintError as V, isGoBack as _, runMavenTests as a, getCompatibleCSSFrameworks$1 as b, applyDependencyVersionChannel as c, isCancel$1 as d, navigableConfirm as f, GO_BACK_SYMBOL as g, setIsFirstPrompt as h, runGradleTests as i, validateApiFrontendCompatibility as j, validateAIFrontendCompatibility as k, getAddonsChoice as l, navigableSelect as m, runCargoBuild as n, runMixCompile as o, navigableMultiselect as p, runGoModTidy as r, runUvSync as s, installDependencies as t, getAddonsToAdd as u, allowedApisForFrontends$1 as v, isExampleChatSdkAllowed$1 as w, getCompatibleUILibraries$1 as x, ensureSingleWebAndNative as y, validateWebDeployRequiresWebFrontend as z };
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import "./bts-config-Bg1Qea9Y.mjs";
2
+ import "./bts-config-InNcw1aJ.mjs";
3
3
  import { n as startMcpServer, t as getMcpGraphPreview } from "./mcp-entry.mjs";
4
4
 
5
5
  export { startMcpServer };