create-better-fullstack 2.0.0 → 2.0.2

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,149 @@
1
+ #!/usr/bin/env node
2
+ import { g as getDefaultConfig, n as readBtsConfig, r as updateBtsConfig } from "./bts-config-Bg1Qea9Y.mjs";
3
+ import { W as renderTitle, c as applyDependencyVersionChannel, t as installDependencies, u as getAddonsToAdd } from "./install-dependencies-D6-GO3BZ.mjs";
4
+ import { a as CLIError, f as isSilent, o as UserCancelledError, p as runWithContextAsync, t as setupAddons } from "./addons-setup-HSghQS7c.mjs";
5
+ import { intro, log, outro } from "@clack/prompts";
6
+ import pc from "picocolors";
7
+ import fs from "fs-extra";
8
+ import path from "node:path";
9
+ import { EMBEDDED_TEMPLATES, VirtualFileSystem, processAddonTemplates, processAddonsDeps } from "@better-fullstack/template-generator";
10
+ import { writeTreeToFilesystem } from "@better-fullstack/template-generator/fs-writer";
11
+
12
+ //#region src/helpers/core/add-handler.ts
13
+ async function addHandler(input, options = {}) {
14
+ const { silent = false } = options;
15
+ return runWithContextAsync({ silent }, async () => {
16
+ try {
17
+ return await addHandlerInternal(input);
18
+ } catch (error) {
19
+ if (error instanceof UserCancelledError) {
20
+ if (isSilent()) return {
21
+ success: false,
22
+ addedAddons: [],
23
+ projectDir: "",
24
+ error: error.message
25
+ };
26
+ return;
27
+ }
28
+ if (error instanceof CLIError) {
29
+ if (isSilent()) return {
30
+ success: false,
31
+ addedAddons: [],
32
+ projectDir: "",
33
+ error: error.message
34
+ };
35
+ throw error;
36
+ }
37
+ if (isSilent()) return {
38
+ success: false,
39
+ addedAddons: [],
40
+ projectDir: "",
41
+ error: error instanceof Error ? error.message : String(error)
42
+ };
43
+ throw error;
44
+ }
45
+ });
46
+ }
47
+ async function addHandlerInternal(input) {
48
+ const projectDir = path.resolve(input.projectDir || process.cwd());
49
+ if (!isSilent()) {
50
+ renderTitle();
51
+ intro(pc.magenta("Add addons to your Better Fullstack project"));
52
+ }
53
+ const btsConfig = await readBtsConfig(projectDir);
54
+ if (!btsConfig) throw new CLIError(`No Better Fullstack project found in ${projectDir}. Make sure bts.jsonc exists.`);
55
+ const projectName = path.basename(projectDir);
56
+ if (!isSilent()) log.info(pc.dim(`Detected project: ${projectName}`));
57
+ const existingAddons = btsConfig.addons || [];
58
+ let addonsToAdd = [];
59
+ if (input.addons && input.addons.length > 0) addonsToAdd = input.addons.filter((addon) => addon !== "none" && !existingAddons.includes(addon));
60
+ else addonsToAdd = (await getAddonsToAdd(btsConfig.frontend || [], existingAddons, btsConfig.auth)).filter((addon) => addon !== "none");
61
+ if (addonsToAdd.length === 0) {
62
+ if (!isSilent()) {
63
+ log.info(pc.dim("No new addons selected."));
64
+ outro(pc.magenta("Nothing to add."));
65
+ }
66
+ return {
67
+ success: true,
68
+ addedAddons: [],
69
+ projectDir
70
+ };
71
+ }
72
+ if (!isSilent()) log.info(pc.cyan(`Adding addons: ${addonsToAdd.join(", ")}`));
73
+ const baseConfig = getDefaultConfig();
74
+ const config = {
75
+ ...baseConfig,
76
+ ...btsConfig,
77
+ projectName,
78
+ projectDir,
79
+ relativePath: ".",
80
+ packageManager: input.packageManager || btsConfig.packageManager || baseConfig.packageManager,
81
+ addons: addonsToAdd,
82
+ frontend: btsConfig.frontend || baseConfig.frontend,
83
+ examples: btsConfig.examples || [],
84
+ rustLibraries: btsConfig.rustLibraries || [],
85
+ pythonAi: btsConfig.pythonAi || [],
86
+ aiDocs: btsConfig.aiDocs || []
87
+ };
88
+ const vfs = new VirtualFileSystem();
89
+ const packageJsonPaths = await collectPackageJsonPaths(projectDir);
90
+ for (const pkgPath of packageJsonPaths) {
91
+ const fullPath = path.join(projectDir, pkgPath);
92
+ const content = await fs.readFile(fullPath, "utf-8");
93
+ vfs.writeFile(pkgPath, content);
94
+ }
95
+ await processAddonTemplates(vfs, EMBEDDED_TEMPLATES, config);
96
+ processAddonsDeps(vfs, config);
97
+ await writeTreeToFilesystem({
98
+ root: vfs.toTree(projectName),
99
+ fileCount: vfs.getFileCount(),
100
+ directoryCount: vfs.getDirectoryCount(),
101
+ config
102
+ }, projectDir);
103
+ const setupWarnings = await setupAddons(config);
104
+ await applyDependencyVersionChannel(projectDir, config.versionChannel);
105
+ const configUpdates = { addons: [...new Set([...existingAddons, ...addonsToAdd])] };
106
+ if (input.webDeploy !== void 0) configUpdates.webDeploy = input.webDeploy;
107
+ if (input.serverDeploy !== void 0) configUpdates.serverDeploy = input.serverDeploy;
108
+ await updateBtsConfig(projectDir, configUpdates);
109
+ if (input.install) await installDependencies({
110
+ projectDir,
111
+ packageManager: config.packageManager
112
+ });
113
+ if (!isSilent()) {
114
+ log.success(pc.green(`Successfully added: ${addonsToAdd.join(", ")}`));
115
+ for (const warning of setupWarnings) log.warn(pc.yellow(warning));
116
+ if (!input.install) {
117
+ const installCmd = config.packageManager === "npm" ? "npm install" : `${config.packageManager} install`;
118
+ log.info(pc.yellow(`Run '${installCmd}' to install new dependencies.`));
119
+ }
120
+ outro(pc.magenta("Addons added successfully!"));
121
+ }
122
+ return {
123
+ success: true,
124
+ addedAddons: addonsToAdd,
125
+ projectDir,
126
+ setupWarnings: setupWarnings.length > 0 ? setupWarnings : void 0
127
+ };
128
+ }
129
+ async function collectPackageJsonPaths(projectDir) {
130
+ const results = [];
131
+ async function walk(currentDir) {
132
+ const entries = await fs.readdir(currentDir, { withFileTypes: true });
133
+ for (const entry of entries) {
134
+ if (entry.name === "node_modules" || entry.name === ".git" || entry.name === ".turbo") continue;
135
+ const fullPath = path.join(currentDir, entry.name);
136
+ if (entry.isDirectory()) {
137
+ await walk(fullPath);
138
+ continue;
139
+ }
140
+ if (entry.isFile() && entry.name === "package.json") results.push(path.relative(projectDir, fullPath).replaceAll(path.sep, "/"));
141
+ }
142
+ }
143
+ await walk(projectDir);
144
+ if (!results.includes("package.json") && await fs.pathExists(path.join(projectDir, "package.json"))) results.push("package.json");
145
+ return results;
146
+ }
147
+
148
+ //#endregion
149
+ export { addHandler };
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import "./bts-config-Bg1Qea9Y.mjs";
3
+ import { t as setupAddons } from "./addons-setup-HSghQS7c.mjs";
4
+
5
+ export { setupAddons };
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { m as dependencyVersionMap, t as readBtsConfig } from "./bts-config-BMniWIbd.mjs";
2
+ import { n as readBtsConfig } from "./bts-config-Bg1Qea9Y.mjs";
3
3
  import { autocompleteMultiselect, cancel, group, isCancel, log, multiselect, select, spinner } from "@clack/prompts";
4
4
  import pc from "picocolors";
5
5
  import fs from "fs-extra";
@@ -16,8 +16,7 @@ function defaultContext() {
16
16
  isFirstPrompt: false,
17
17
  lastPromptShownUI: false
18
18
  },
19
- silent: false,
20
- verbose: false
19
+ silent: false
21
20
  };
22
21
  }
23
22
  function getContext() {
@@ -51,11 +50,7 @@ async function runWithContextAsync(options, fn) {
51
50
  isFirstPrompt: false,
52
51
  lastPromptShownUI: false
53
52
  },
54
- silent: options.silent ?? false,
55
- verbose: options.verbose ?? false,
56
- projectDir: options.projectDir,
57
- projectName: options.projectName,
58
- packageManager: options.packageManager
53
+ silent: options.silent ?? false
59
54
  };
60
55
  return cliStorage.run(ctx, fn);
61
56
  }
@@ -91,10 +86,33 @@ function handleError(error, fallbackMessage) {
91
86
  process.exit(1);
92
87
  }
93
88
 
89
+ //#endregion
90
+ //#region src/utils/prompt-environment.ts
91
+ function hasOwnProperty(value, property) {
92
+ return Object.prototype.hasOwnProperty.call(value, property);
93
+ }
94
+ function resolveCiValue(environment) {
95
+ if (environment && hasOwnProperty(environment, "ci")) return environment.ci;
96
+ return process.env.CI;
97
+ }
98
+ function isCiEnvironment(value) {
99
+ if (!value) return false;
100
+ const normalizedValue = value.trim().toLowerCase();
101
+ return normalizedValue !== "" && normalizedValue !== "0" && normalizedValue !== "false";
102
+ }
103
+ function canPromptInteractively(environment = {}) {
104
+ const silent = environment.silent ?? isSilent();
105
+ const stdinIsTTY = environment.stdinIsTTY ?? process.stdin.isTTY === true;
106
+ const stdoutIsTTY = environment.stdoutIsTTY ?? process.stdout.isTTY === true;
107
+ const ci = resolveCiValue(environment);
108
+ return !silent && stdinIsTTY && stdoutIsTTY && !isCiEnvironment(ci);
109
+ }
110
+
94
111
  //#endregion
95
112
  //#region src/utils/add-package-deps.ts
96
113
  const addPackageDependency = async (opts) => {
97
114
  const { dependencies = [], devDependencies = [], customDependencies = {}, customDevDependencies = {}, projectDir } = opts;
115
+ const { dependencyVersionMap } = await import("@better-fullstack/template-generator");
98
116
  const pkgJsonPath = path.join(projectDir, "package.json");
99
117
  const pkgJson = await fs.readJson(pkgJsonPath);
100
118
  if (!pkgJson.dependencies) pkgJson.dependencies = {};
@@ -178,28 +196,6 @@ function getPackageRunnerPrefix(packageManager) {
178
196
  }
179
197
  }
180
198
 
181
- //#endregion
182
- //#region src/utils/prompt-environment.ts
183
- function hasOwnProperty(value, property) {
184
- return Object.prototype.hasOwnProperty.call(value, property);
185
- }
186
- function resolveCiValue(environment) {
187
- if (environment && hasOwnProperty(environment, "ci")) return environment.ci;
188
- return process.env.CI;
189
- }
190
- function isCiEnvironment(value) {
191
- if (!value) return false;
192
- const normalizedValue = value.trim().toLowerCase();
193
- return normalizedValue !== "" && normalizedValue !== "0" && normalizedValue !== "false";
194
- }
195
- function canPromptInteractively(environment = {}) {
196
- const silent = environment.silent ?? isSilent();
197
- const stdinIsTTY = environment.stdinIsTTY ?? process.stdin.isTTY === true;
198
- const stdoutIsTTY = environment.stdoutIsTTY ?? process.stdout.isTTY === true;
199
- const ci = resolveCiValue(environment);
200
- return !silent && stdinIsTTY && stdoutIsTTY && !isCiEnvironment(ci);
201
- }
202
-
203
199
  //#endregion
204
200
  //#region src/helpers/addons/interactive-selection.ts
205
201
  function shouldPromptForAddonSelection(environment = {}) {
@@ -456,7 +452,7 @@ function getRecommendedMcpServers(config) {
456
452
  name: "supabase",
457
453
  target: "https://mcp.supabase.com/mcp"
458
454
  });
459
- if (config.auth === "better-auth") servers.push({
455
+ if (config.auth === "better-auth" || config.auth === "better-auth-organizations") servers.push({
460
456
  key: "better-auth",
461
457
  label: "Better Auth",
462
458
  name: "better-auth",
@@ -851,7 +847,7 @@ function getRecommendedSourceKeys(config) {
851
847
  if (frontend.includes("nuxt")) sources.push("nuxt/ui");
852
848
  if (frontend.includes("native-uniwind")) sources.push("heroui-inc/heroui");
853
849
  if (hasNativeFrontend(frontend)) sources.push("expo/skills");
854
- if (auth === "better-auth") sources.push("better-auth/skills");
850
+ if (auth === "better-auth" || auth === "better-auth-organizations") sources.push("better-auth/skills");
855
851
  if (dbSetup === "neon") sources.push("neondatabase/agent-skills");
856
852
  if (dbSetup === "supabase") sources.push("supabase/agent-skills");
857
853
  if (dbSetup === "planetscale") sources.push("planetscale/database-skills");
@@ -1416,4 +1412,4 @@ async function setupLefthook(projectDir) {
1416
1412
  }
1417
1413
 
1418
1414
  //#endregion
1419
- export { setIsFirstPrompt as _, canPromptInteractively as a, CLIError as c, exitWithError as d, handleError as f, runWithContextAsync as g, isSilent as h, setupLefthook as i, UserCancelledError as l, isFirstPrompt as m, setupBiome as n, getPackageExecutionArgs as o, didLastPromptShowUI as p, setupHusky as r, addPackageDependency as s, setupAddons as t, exitCancelled as u, setLastPromptShownUI as v };
1415
+ export { CLIError as a, exitWithError as c, isFirstPrompt as d, isSilent as f, setLastPromptShownUI as h, canPromptInteractively as i, handleError as l, setIsFirstPrompt as m, getPackageExecutionArgs as n, UserCancelledError as o, runWithContextAsync as p, addPackageDependency as r, exitCancelled as s, setupAddons as t, didLastPromptShowUI as u };