create-better-fullstack 1.1.6 → 1.1.7

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/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { o as createBtsCli } from "./src--S4NPHRv.mjs";
2
+ import { o as createBtsCli } from "./src-bp7xdTNG.mjs";
3
3
 
4
4
  //#region src/cli.ts
5
5
  createBtsCli().run();
package/dist/index.d.mts CHANGED
@@ -467,6 +467,13 @@ declare const router: {
467
467
  sponsors: _orpc_server0.Procedure<_orpc_server0.MergedInitialContext<Record<never, never>, Record<never, never>, Record<never, never>>, Record<never, never>, _orpc_server0.Schema<unknown, unknown>, _orpc_server0.Schema<void, void>, _orpc_server0.MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
468
468
  docs: _orpc_server0.Procedure<_orpc_server0.MergedInitialContext<Record<never, never>, Record<never, never>, Record<never, never>>, Record<never, never>, _orpc_server0.Schema<unknown, unknown>, _orpc_server0.Schema<void, void>, _orpc_server0.MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
469
469
  builder: _orpc_server0.Procedure<_orpc_server0.MergedInitialContext<Record<never, never>, Record<never, never>, Record<never, never>>, Record<never, never>, _orpc_server0.Schema<unknown, unknown>, _orpc_server0.Schema<void, void>, _orpc_server0.MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
470
+ "update-deps": _orpc_server0.Procedure<_orpc_server0.MergedInitialContext<Record<never, never>, Record<never, never>, Record<never, never>>, Record<never, never>, z.ZodObject<{
471
+ check: z.ZodDefault<z.ZodBoolean>;
472
+ patch: z.ZodDefault<z.ZodBoolean>;
473
+ all: z.ZodDefault<z.ZodBoolean>;
474
+ ecosystem: z.ZodOptional<z.ZodString>;
475
+ "list-ecosystems": z.ZodDefault<z.ZodBoolean>;
476
+ }, z.core.$strip>, _orpc_server0.Schema<void, void>, _orpc_server0.MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
470
477
  };
471
478
  declare function createBtsCli(): trpc_cli0.TrpcCli;
472
479
  /**
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { a as create, c as docs, d as sponsors, i as builder, l as generateVirtualProject, n as TEMPLATE_COUNT, o as createBtsCli, r as VirtualFileSystem, s as createVirtual, t as EMBEDDED_TEMPLATES, u as router } from "./src--S4NPHRv.mjs";
2
+ import { a as create, c as docs, d as sponsors, i as builder, l as generateVirtualProject, n as TEMPLATE_COUNT, o as createBtsCli, r as VirtualFileSystem, s as createVirtual, t as EMBEDDED_TEMPLATES, u as router } from "./src-bp7xdTNG.mjs";
3
3
 
4
4
  export { EMBEDDED_TEMPLATES, TEMPLATE_COUNT, VirtualFileSystem, builder, create, createBtsCli, createVirtual, docs, generateVirtualProject, router, sponsors };
@@ -5,11 +5,12 @@ import { createRouterClient, os } from "@orpc/server";
5
5
  import pc from "picocolors";
6
6
  import { createCli } from "trpc-cli";
7
7
  import z from "zod";
8
- import consola, { consola as consola$1 } from "consola";
8
+ import { ECOSYSTEM_GROUPS, EMBEDDED_TEMPLATES, EMBEDDED_TEMPLATES as EMBEDDED_TEMPLATES$1, TEMPLATE_COUNT, VirtualFileSystem, checkAllVersions, dependencyVersionMap, generateCliReport, generateVirtualProject, generateVirtualProject as generateVirtualProject$1, listEcosystems } from "@better-fullstack/template-generator";
9
9
  import fs from "fs-extra";
10
- import path from "node:path";
10
+ import path from "path";
11
+ import consola, { consola as consola$1 } from "consola";
12
+ import path$1 from "node:path";
11
13
  import { fileURLToPath } from "node:url";
12
- import { EMBEDDED_TEMPLATES, EMBEDDED_TEMPLATES as EMBEDDED_TEMPLATES$1, TEMPLATE_COUNT, VirtualFileSystem, dependencyVersionMap, generateVirtualProject, generateVirtualProject as generateVirtualProject$1 } from "@better-fullstack/template-generator";
13
14
  import { AsyncLocalStorage } from "node:async_hooks";
14
15
  import { ConfirmPrompt, GroupMultiSelectPrompt, MultiSelectPrompt, SelectPrompt, isCancel as isCancel$1 } from "@clack/core";
15
16
  import gradient from "gradient-string";
@@ -19,6 +20,183 @@ import * as JSONC from "jsonc-parser";
19
20
  import { format } from "oxfmt";
20
21
  import os$1 from "node:os";
21
22
 
23
+ //#region src/commands/update-deps.ts
24
+ /**
25
+ * CLI command to check and update dependency versions
26
+ */
27
+ /**
28
+ * Format a version update for display
29
+ */
30
+ function formatUpdate(info) {
31
+ const colorFn = {
32
+ major: pc.red,
33
+ minor: pc.yellow,
34
+ patch: pc.green,
35
+ none: pc.gray
36
+ }[info.updateType];
37
+ return `${colorFn(`[${info.updateType.toUpperCase()}]`.padEnd(8))} ${pc.cyan(info.name.padEnd(45))} ${pc.dim(info.current)} ${pc.dim("->")} ${pc.green(info.latest)}`;
38
+ }
39
+ /**
40
+ * Get the path to add-deps.ts
41
+ */
42
+ function getAddDepsPath() {
43
+ const possiblePaths = [
44
+ path.join(process.cwd(), "packages/template-generator/src/utils/add-deps.ts"),
45
+ path.join(process.cwd(), "../../packages/template-generator/src/utils/add-deps.ts"),
46
+ path.join(process.cwd(), "node_modules/@better-fullstack/template-generator/src/utils/add-deps.ts")
47
+ ];
48
+ for (const p of possiblePaths) if (fs.existsSync(p)) return p;
49
+ return possiblePaths[0];
50
+ }
51
+ /**
52
+ * Update the add-deps.ts file with new versions
53
+ */
54
+ async function updateAddDepsFile(updates) {
55
+ const filePath = getAddDepsPath();
56
+ if (!fs.existsSync(filePath)) {
57
+ log.error(`Could not find add-deps.ts at ${filePath}`);
58
+ return false;
59
+ }
60
+ let content = await fs.readFile(filePath, "utf-8");
61
+ let updated = false;
62
+ for (const update of updates) {
63
+ const escapedName = update.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
64
+ const pattern = new RegExp(`(["']${escapedName}["']:\\s*["'])([^"']+)(["'])`, "g");
65
+ const newContent = content.replace(pattern, `$1${update.latest}$3`);
66
+ if (newContent !== content) {
67
+ content = newContent;
68
+ updated = true;
69
+ }
70
+ }
71
+ if (updated) {
72
+ await fs.writeFile(filePath, content, "utf-8");
73
+ return true;
74
+ }
75
+ return false;
76
+ }
77
+ /**
78
+ * Interactive mode: prompt for each update
79
+ */
80
+ async function interactiveUpdate(updates) {
81
+ const toApply = [];
82
+ for (const update of updates) {
83
+ console.log("\n" + pc.bold("-----------------------------------"));
84
+ console.log(formatUpdate(update));
85
+ if (update.ecosystem) console.log(pc.dim(` Ecosystem: ${update.ecosystem}`));
86
+ if (update.updateType === "major") console.log(pc.yellow(" Warning: Breaking changes possible. Check changelog."));
87
+ const action = await select({
88
+ message: "What would you like to do?",
89
+ options: [
90
+ {
91
+ value: "update",
92
+ label: "Update to latest"
93
+ },
94
+ {
95
+ value: "skip",
96
+ label: "Skip this package"
97
+ },
98
+ {
99
+ value: "quit",
100
+ label: "Quit (apply selected updates)"
101
+ }
102
+ ]
103
+ });
104
+ if (isCancel(action) || action === "quit") break;
105
+ if (action === "update") toApply.push(update);
106
+ }
107
+ return toApply;
108
+ }
109
+ /**
110
+ * Main handler for the update-deps command
111
+ */
112
+ async function updateDepsHandler(options) {
113
+ const { check = false, patch = false, all = false, ecosystem } = options;
114
+ if (ecosystem) {
115
+ const validEcosystems = listEcosystems();
116
+ if (!validEcosystems.includes(ecosystem)) {
117
+ log.error(`Invalid ecosystem: ${ecosystem}. Valid options: ${validEcosystems.join(", ")}`);
118
+ return;
119
+ }
120
+ }
121
+ const s = spinner();
122
+ s.start(ecosystem ? `Checking ${ecosystem} packages for updates...` : "Checking all packages for updates...");
123
+ let result;
124
+ try {
125
+ result = await checkAllVersions({
126
+ ecosystem,
127
+ concurrency: 5,
128
+ delayMs: 100,
129
+ onProgress: (checked, total, current) => {
130
+ s.message(`Checking packages (${checked}/${total})...`);
131
+ }
132
+ });
133
+ } catch (error) {
134
+ s.stop("Failed to check versions");
135
+ log.error(String(error));
136
+ return;
137
+ }
138
+ s.stop("Version check complete");
139
+ console.log(generateCliReport(result));
140
+ if (check || result.outdated.length === 0) return;
141
+ let toApply = [];
142
+ if (patch) {
143
+ toApply = result.outdated.filter((u) => u.updateType === "patch" || u.updateType === "minor");
144
+ if (toApply.length === 0) {
145
+ log.info("No patch/minor updates available.");
146
+ return;
147
+ }
148
+ log.info(`Found ${toApply.length} patch/minor updates to apply automatically.`);
149
+ const shouldProceed = await confirm({ message: `Apply ${toApply.length} safe updates?` });
150
+ if (isCancel(shouldProceed) || !shouldProceed) {
151
+ log.info("Cancelled.");
152
+ return;
153
+ }
154
+ } else if (all) {
155
+ log.info("\nEntering interactive mode...");
156
+ toApply = await interactiveUpdate(result.outdated);
157
+ if (toApply.length === 0) {
158
+ log.info("No updates selected.");
159
+ return;
160
+ }
161
+ } else {
162
+ const shouldProceed = await confirm({ message: `Apply all ${result.outdated.length} updates?` });
163
+ if (isCancel(shouldProceed) || !shouldProceed) {
164
+ log.info("Cancelled. Use --check to only view updates without prompting.");
165
+ return;
166
+ }
167
+ toApply = result.outdated;
168
+ }
169
+ const updateSpinner = spinner();
170
+ updateSpinner.start(`Applying ${toApply.length} updates...`);
171
+ try {
172
+ if (await updateAddDepsFile(toApply)) {
173
+ updateSpinner.stop("Updates applied successfully!");
174
+ console.log("\n" + pc.green("Updated packages:"));
175
+ for (const update of toApply) console.log(` ${pc.cyan(update.name)}: ${update.current} -> ${update.latest}`);
176
+ console.log("\n" + pc.yellow("Next steps:"));
177
+ console.log(" 1. Review the changes in add-deps.ts");
178
+ console.log(" 2. Run: bun run build (in packages/template-generator)");
179
+ console.log(" 3. Run tests to verify compatibility");
180
+ console.log(" 4. Update any hardcoded versions in template files");
181
+ } else {
182
+ updateSpinner.stop("No changes were made");
183
+ log.warn("Could not apply updates. The file may have changed.");
184
+ }
185
+ } catch (error) {
186
+ updateSpinner.stop("Failed to apply updates");
187
+ log.error(String(error));
188
+ }
189
+ }
190
+ /**
191
+ * Show available ecosystems
192
+ */
193
+ function showEcosystems() {
194
+ console.log("\nAvailable ecosystems:\n");
195
+ for (const [name, packages] of Object.entries(ECOSYSTEM_GROUPS)) console.log(` ${pc.cyan(name.padEnd(15))} (${packages.length} packages)`);
196
+ console.log(`\nUsage: update-deps --ecosystem <name>`);
197
+ }
198
+
199
+ //#endregion
22
200
  //#region src/utils/get-package-manager.ts
23
201
  const getUserPkgManager = () => {
24
202
  const userAgent = process.env.npm_config_user_agent;
@@ -30,8 +208,8 @@ const getUserPkgManager = () => {
30
208
  //#endregion
31
209
  //#region src/constants.ts
32
210
  const __filename = fileURLToPath(import.meta.url);
33
- const distPath = path.dirname(__filename);
34
- const PKG_ROOT = path.join(distPath, "../");
211
+ const distPath = path$1.dirname(__filename);
212
+ const PKG_ROOT = path$1.join(distPath, "../");
35
213
  const DEFAULT_CONFIG_BASE = {
36
214
  projectName: "my-better-t-app",
37
215
  relativePath: "my-better-t-app",
@@ -78,7 +256,7 @@ const DEFAULT_CONFIG_BASE = {
78
256
  function getDefaultConfig() {
79
257
  return {
80
258
  ...DEFAULT_CONFIG_BASE,
81
- projectDir: path.resolve(process.cwd(), DEFAULT_CONFIG_BASE.projectName),
259
+ projectDir: path$1.resolve(process.cwd(), DEFAULT_CONFIG_BASE.projectName),
82
260
  packageManager: getUserPkgManager(),
83
261
  frontend: [...DEFAULT_CONFIG_BASE.frontend],
84
262
  addons: [...DEFAULT_CONFIG_BASE.addons],
@@ -3036,9 +3214,9 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
3036
3214
  //#endregion
3037
3215
  //#region src/prompts/project-name.ts
3038
3216
  function isPathWithinCwd(targetPath) {
3039
- const resolved = path.resolve(targetPath);
3040
- const rel = path.relative(process.cwd(), resolved);
3041
- return !rel.startsWith("..") && !path.isAbsolute(rel);
3217
+ const resolved = path$1.resolve(targetPath);
3218
+ const rel = path$1.relative(process.cwd(), resolved);
3219
+ return !rel.startsWith("..") && !path$1.isAbsolute(rel);
3042
3220
  }
3043
3221
  function validateDirectoryName(name) {
3044
3222
  if (name === ".") return void 0;
@@ -3048,8 +3226,8 @@ function validateDirectoryName(name) {
3048
3226
  async function getProjectName(initialName) {
3049
3227
  if (initialName) {
3050
3228
  if (initialName === ".") return initialName;
3051
- if (!validateDirectoryName(path.basename(initialName))) {
3052
- if (isPathWithinCwd(path.resolve(process.cwd(), initialName))) return initialName;
3229
+ if (!validateDirectoryName(path$1.basename(initialName))) {
3230
+ if (isPathWithinCwd(path$1.resolve(process.cwd(), initialName))) return initialName;
3053
3231
  consola.error(pc.red("Project path must be within current directory"));
3054
3232
  }
3055
3233
  }
@@ -3057,7 +3235,7 @@ async function getProjectName(initialName) {
3057
3235
  let projectPath = "";
3058
3236
  let defaultName = DEFAULT_CONFIG.projectName;
3059
3237
  let counter = 1;
3060
- while (await fs.pathExists(path.resolve(process.cwd(), defaultName)) && (await fs.readdir(path.resolve(process.cwd(), defaultName))).length > 0) {
3238
+ while (await fs.pathExists(path$1.resolve(process.cwd(), defaultName)) && (await fs.readdir(path$1.resolve(process.cwd(), defaultName))).length > 0) {
3061
3239
  defaultName = `${DEFAULT_CONFIG.projectName}-${counter}`;
3062
3240
  counter++;
3063
3241
  }
@@ -3069,10 +3247,10 @@ async function getProjectName(initialName) {
3069
3247
  defaultValue: defaultName,
3070
3248
  validate: (value) => {
3071
3249
  const nameToUse = String(value ?? "").trim() || defaultName;
3072
- const validationError = validateDirectoryName(path.basename(nameToUse));
3250
+ const validationError = validateDirectoryName(path$1.basename(nameToUse));
3073
3251
  if (validationError) return validationError;
3074
3252
  if (nameToUse !== ".") {
3075
- if (!isPathWithinCwd(path.resolve(process.cwd(), nameToUse))) return "Project path must be within current directory";
3253
+ if (!isPathWithinCwd(path$1.resolve(process.cwd(), nameToUse))) return "Project path must be within current directory";
3076
3254
  }
3077
3255
  }
3078
3256
  });
@@ -3086,7 +3264,7 @@ async function getProjectName(initialName) {
3086
3264
  //#endregion
3087
3265
  //#region src/utils/get-latest-cli-version.ts
3088
3266
  const getLatestCLIVersion = () => {
3089
- const packageJsonPath = path.join(PKG_ROOT, "package.json");
3267
+ const packageJsonPath = path$1.join(PKG_ROOT, "package.json");
3090
3268
  return fs.readJSONSync(packageJsonPath).version ?? "1.0.0";
3091
3269
  };
3092
3270
 
@@ -3108,7 +3286,7 @@ function isTelemetryEnabled() {
3108
3286
 
3109
3287
  //#endregion
3110
3288
  //#region src/utils/analytics.ts
3111
- const CONVEX_INGEST_URL = "https://third-antelope-510.convex.site/api/analytics/ingest";
3289
+ const CONVEX_INGEST_URL = "https://curious-elephant-653.convex.site/api/analytics/ingest";
3112
3290
  async function sendConvexEvent(payload) {
3113
3291
  try {
3114
3292
  await fetch(CONVEX_INGEST_URL, {
@@ -3216,7 +3394,7 @@ function generateReproducibleCommand(config) {
3216
3394
  //#region src/utils/project-directory.ts
3217
3395
  async function handleDirectoryConflict(currentPathInput) {
3218
3396
  while (true) {
3219
- const resolvedPath = path.resolve(process.cwd(), currentPathInput);
3397
+ const resolvedPath = path$1.resolve(process.cwd(), currentPathInput);
3220
3398
  if (!(await fs.pathExists(resolvedPath) && (await fs.readdir(resolvedPath)).length > 0)) return {
3221
3399
  finalPathInput: currentPathInput,
3222
3400
  shouldClearDirectory: false
@@ -3273,10 +3451,10 @@ async function setupProjectDirectory(finalPathInput, shouldClearDirectory) {
3273
3451
  let finalBaseName;
3274
3452
  if (finalPathInput === ".") {
3275
3453
  finalResolvedPath = process.cwd();
3276
- finalBaseName = path.basename(finalResolvedPath);
3454
+ finalBaseName = path$1.basename(finalResolvedPath);
3277
3455
  } else {
3278
- finalResolvedPath = path.resolve(process.cwd(), finalPathInput);
3279
- finalBaseName = path.basename(finalResolvedPath);
3456
+ finalResolvedPath = path$1.resolve(process.cwd(), finalPathInput);
3457
+ finalBaseName = path$1.basename(finalResolvedPath);
3280
3458
  }
3281
3459
  if (shouldClearDirectory) {
3282
3460
  const s = spinner();
@@ -3415,7 +3593,7 @@ function processArrayOption(options) {
3415
3593
  }
3416
3594
  function deriveProjectName(projectName, projectDirectory) {
3417
3595
  if (projectName) return projectName;
3418
- if (projectDirectory) return path.basename(path.resolve(process.cwd(), projectDirectory));
3596
+ if (projectDirectory) return path$1.basename(path$1.resolve(process.cwd(), projectDirectory));
3419
3597
  return "";
3420
3598
  }
3421
3599
  function processFlags(options, projectName) {
@@ -3678,9 +3856,9 @@ function validateProjectNameThrow(name) {
3678
3856
  if (!result.success) throw new Error(`Invalid project name: ${result.error.issues[0]?.message}`);
3679
3857
  }
3680
3858
  function extractAndValidateProjectName(projectName, projectDirectory, throwOnError = false) {
3681
- const derivedName = projectName || (projectDirectory ? path.basename(path.resolve(process.cwd(), projectDirectory)) : "");
3859
+ const derivedName = projectName || (projectDirectory ? path$1.basename(path$1.resolve(process.cwd(), projectDirectory)) : "");
3682
3860
  if (!derivedName) return "";
3683
- const nameToValidate = projectName ? path.basename(projectName) : derivedName;
3861
+ const nameToValidate = projectName ? path$1.basename(projectName) : derivedName;
3684
3862
  if (throwOnError) validateProjectNameThrow(nameToValidate);
3685
3863
  else validateProjectName(nameToValidate);
3686
3864
  return projectName || derivedName;
@@ -3844,7 +4022,7 @@ async function writeBtsConfig(projectConfig) {
3844
4022
  // safe to delete
3845
4023
 
3846
4024
  ${configContent}`;
3847
- const configPath = path.join(projectConfig.projectDir, BTS_CONFIG_FILE);
4025
+ const configPath = path$1.join(projectConfig.projectDir, BTS_CONFIG_FILE);
3848
4026
  await fs.writeFile(configPath, finalContent, "utf-8");
3849
4027
  }
3850
4028
 
@@ -3856,7 +4034,7 @@ const formatOptions = {
3856
4034
  };
3857
4035
  async function formatCode(filePath, content) {
3858
4036
  try {
3859
- const result = await format(path.basename(filePath), content, formatOptions);
4037
+ const result = await format(path$1.basename(filePath), content, formatOptions);
3860
4038
  if (result.errors && result.errors.length > 0) return null;
3861
4039
  return result.code;
3862
4040
  } catch {
@@ -3867,7 +4045,7 @@ async function formatProject(projectDir) {
3867
4045
  async function formatDirectory(dir) {
3868
4046
  const entries = await fs.readdir(dir, { withFileTypes: true });
3869
4047
  await Promise.all(entries.map(async (entry) => {
3870
- const fullPath = path.join(dir, entry.name);
4048
+ const fullPath = path$1.join(dir, entry.name);
3871
4049
  if (entry.isDirectory()) await formatDirectory(fullPath);
3872
4050
  else if (entry.isFile()) try {
3873
4051
  const content = await fs.readFile(fullPath, "utf-8");
@@ -3883,7 +4061,7 @@ async function formatProject(projectDir) {
3883
4061
  //#region src/utils/add-package-deps.ts
3884
4062
  const addPackageDependency = async (opts) => {
3885
4063
  const { dependencies = [], devDependencies = [], customDependencies = {}, customDevDependencies = {}, projectDir } = opts;
3886
- const pkgJsonPath = path.join(projectDir, "package.json");
4064
+ const pkgJsonPath = path$1.join(projectDir, "package.json");
3887
4065
  const pkgJson = await fs.readJson(pkgJsonPath);
3888
4066
  if (!pkgJson.dependencies) pkgJson.dependencies = {};
3889
4067
  if (!pkgJson.devDependencies) pkgJson.devDependencies = {};
@@ -4003,7 +4181,7 @@ async function setupFumadocs(config) {
4003
4181
  });
4004
4182
  if (isCancel(template)) return exitCancelled("Operation cancelled");
4005
4183
  const args = getPackageExecutionArgs(packageManager, `create-fumadocs-app@latest fumadocs --template ${TEMPLATES$2[template].value} --src --pm ${packageManager} --no-git`);
4006
- const appsDir = path.join(projectDir, "apps");
4184
+ const appsDir = path$1.join(projectDir, "apps");
4007
4185
  await fs.ensureDir(appsDir);
4008
4186
  const s = spinner();
4009
4187
  s.start("Running Fumadocs create command...");
@@ -4011,8 +4189,8 @@ async function setupFumadocs(config) {
4011
4189
  cwd: appsDir,
4012
4190
  env: { CI: "true" }
4013
4191
  })`${args}`;
4014
- const fumadocsDir = path.join(projectDir, "apps", "fumadocs");
4015
- const packageJsonPath = path.join(fumadocsDir, "package.json");
4192
+ const fumadocsDir = path$1.join(projectDir, "apps", "fumadocs");
4193
+ const packageJsonPath = path$1.join(fumadocsDir, "package.json");
4016
4194
  if (await fs.pathExists(packageJsonPath)) {
4017
4195
  const packageJson = await fs.readJson(packageJsonPath);
4018
4196
  packageJson.name = "fumadocs";
@@ -4033,7 +4211,7 @@ async function setupOxlint(projectDir, packageManager) {
4033
4211
  devDependencies: ["oxlint", "oxfmt"],
4034
4212
  projectDir
4035
4213
  });
4036
- const packageJsonPath = path.join(projectDir, "package.json");
4214
+ const packageJsonPath = path$1.join(projectDir, "package.json");
4037
4215
  if (await fs.pathExists(packageJsonPath)) {
4038
4216
  const packageJson = await fs.readJson(packageJsonPath);
4039
4217
  packageJson.scripts = {
@@ -4063,7 +4241,7 @@ async function setupRuler(config) {
4063
4241
  const { packageManager, projectDir } = config;
4064
4242
  try {
4065
4243
  log.info("Setting up Ruler...");
4066
- const rulerDir = path.join(projectDir, ".ruler");
4244
+ const rulerDir = path$1.join(projectDir, ".ruler");
4067
4245
  if (!await fs.pathExists(rulerDir)) {
4068
4246
  log.error(pc.red("Ruler template directory not found. Please ensure ruler addon is properly installed."));
4069
4247
  return;
@@ -4112,7 +4290,7 @@ async function setupRuler(config) {
4112
4290
  log.info(pc.cyan(`${getPackageExecutionCommand(packageManager, "@intellectronica/ruler@latest apply --local-only")}`));
4113
4291
  return;
4114
4292
  }
4115
- const configFile = path.join(rulerDir, "ruler.toml");
4293
+ const configFile = path$1.join(rulerDir, "ruler.toml");
4116
4294
  let updatedConfig = await fs.readFile(configFile, "utf-8");
4117
4295
  const defaultAgentsLine = `default_agents = [${selectedEditors.map((editor) => `"${editor}"`).join(", ")}]`;
4118
4296
  updatedConfig = updatedConfig.replace(/default_agents = \[\]/, defaultAgentsLine);
@@ -4136,7 +4314,7 @@ async function setupRuler(config) {
4136
4314
  }
4137
4315
  }
4138
4316
  async function addRulerScriptToPackageJson(projectDir, packageManager) {
4139
- const rootPackageJsonPath = path.join(projectDir, "package.json");
4317
+ const rootPackageJsonPath = path$1.join(projectDir, "package.json");
4140
4318
  if (!await fs.pathExists(rootPackageJsonPath)) {
4141
4319
  log.warn("Root package.json not found, skipping ruler:apply script addition");
4142
4320
  return;
@@ -4165,7 +4343,7 @@ async function setupStarlight(config) {
4165
4343
  "--no-git",
4166
4344
  "--skip-houston"
4167
4345
  ].join(" ")}`);
4168
- const appsDir = path.join(projectDir, "apps");
4346
+ const appsDir = path$1.join(projectDir, "apps");
4169
4347
  await fs.ensureDir(appsDir);
4170
4348
  await $({
4171
4349
  cwd: appsDir,
@@ -4183,7 +4361,7 @@ async function setupStarlight(config) {
4183
4361
  async function setupTauri(config) {
4184
4362
  const { packageManager, frontend, projectDir } = config;
4185
4363
  const s = spinner();
4186
- const clientPackageDir = path.join(projectDir, "apps/web");
4364
+ const clientPackageDir = path$1.join(projectDir, "apps/web");
4187
4365
  if (!await fs.pathExists(clientPackageDir)) return;
4188
4366
  try {
4189
4367
  s.start("Setting up Tauri desktop app support...");
@@ -4196,8 +4374,8 @@ async function setupTauri(config) {
4196
4374
  const tauriArgs = [
4197
4375
  "@tauri-apps/cli@latest",
4198
4376
  "init",
4199
- `--app-name=${path.basename(projectDir)}`,
4200
- `--window-title=${path.basename(projectDir)}`,
4377
+ `--app-name=${path$1.basename(projectDir)}`,
4378
+ `--window-title=${path$1.basename(projectDir)}`,
4201
4379
  `--frontend-dist=${frontendDist}`,
4202
4380
  `--dev-url=${devUrl}`,
4203
4381
  `--before-dev-command=${packageManager} run dev`,
@@ -4246,7 +4424,7 @@ async function setupTui(config) {
4246
4424
  });
4247
4425
  if (isCancel(template)) return exitCancelled("Operation cancelled");
4248
4426
  const args = getPackageExecutionArgs(packageManager, `create-tui@latest --template ${template} --no-git --no-install tui`);
4249
- const appsDir = path.join(projectDir, "apps");
4427
+ const appsDir = path$1.join(projectDir, "apps");
4250
4428
  await fs.ensureDir(appsDir);
4251
4429
  const s = spinner();
4252
4430
  s.start("Running OpenTUI create command...");
@@ -4450,7 +4628,7 @@ async function setupWxt(config) {
4450
4628
  });
4451
4629
  if (isCancel(template)) return exitCancelled("Operation cancelled");
4452
4630
  const args = getPackageExecutionArgs(packageManager, `wxt@latest init extension --template ${template} --pm ${packageManager}`);
4453
- const appsDir = path.join(projectDir, "apps");
4631
+ const appsDir = path$1.join(projectDir, "apps");
4454
4632
  await fs.ensureDir(appsDir);
4455
4633
  const s = spinner();
4456
4634
  s.start("Running WXT init command...");
@@ -4458,8 +4636,8 @@ async function setupWxt(config) {
4458
4636
  cwd: appsDir,
4459
4637
  env: { CI: "true" }
4460
4638
  })`${args}`;
4461
- const extensionDir = path.join(projectDir, "apps", "extension");
4462
- const packageJsonPath = path.join(extensionDir, "package.json");
4639
+ const extensionDir = path$1.join(projectDir, "apps", "extension");
4640
+ const packageJsonPath = path$1.join(extensionDir, "package.json");
4463
4641
  if (await fs.pathExists(packageJsonPath)) {
4464
4642
  const packageJson = await fs.readJson(packageJsonPath);
4465
4643
  packageJson.name = "extension";
@@ -4515,7 +4693,7 @@ async function setupBiome(projectDir) {
4515
4693
  devDependencies: ["@biomejs/biome"],
4516
4694
  projectDir
4517
4695
  });
4518
- const packageJsonPath = path.join(projectDir, "package.json");
4696
+ const packageJsonPath = path$1.join(projectDir, "package.json");
4519
4697
  if (await fs.pathExists(packageJsonPath)) {
4520
4698
  const packageJson = await fs.readJson(packageJsonPath);
4521
4699
  packageJson.scripts = {
@@ -4530,7 +4708,7 @@ async function setupHusky(projectDir, linter) {
4530
4708
  devDependencies: ["husky", "lint-staged"],
4531
4709
  projectDir
4532
4710
  });
4533
- const packageJsonPath = path.join(projectDir, "package.json");
4711
+ const packageJsonPath = path$1.join(projectDir, "package.json");
4534
4712
  if (await fs.pathExists(packageJsonPath)) {
4535
4713
  const packageJson = await fs.readJson(packageJsonPath);
4536
4714
  packageJson.scripts = {
@@ -4586,14 +4764,14 @@ async function setupCloudflareD1(config) {
4586
4764
  const { projectDir, serverDeploy, orm, backend } = config;
4587
4765
  if (serverDeploy === "cloudflare" && orm === "prisma") {
4588
4766
  const targetApp2 = backend === "self" ? "apps/web" : "apps/server";
4589
- await addEnvVariablesToFile(path.join(projectDir, targetApp2, ".env"), [{
4767
+ await addEnvVariablesToFile(path$1.join(projectDir, targetApp2, ".env"), [{
4590
4768
  key: "DATABASE_URL",
4591
- value: `file:${path.join(projectDir, "apps/server", "local.db")}`,
4769
+ value: `file:${path$1.join(projectDir, "apps/server", "local.db")}`,
4592
4770
  condition: true
4593
4771
  }]);
4594
4772
  await addPackageDependency({
4595
4773
  dependencies: ["@prisma/adapter-d1"],
4596
- projectDir: path.join(projectDir, backend === "self" ? "apps/web" : "apps/server")
4774
+ projectDir: path$1.join(projectDir, backend === "self" ? "apps/web" : "apps/server")
4597
4775
  });
4598
4776
  }
4599
4777
  }
@@ -4611,7 +4789,7 @@ async function setupDockerCompose(config) {
4611
4789
  }
4612
4790
  async function writeEnvFile$4(projectDir, database, projectName, backend) {
4613
4791
  const targetApp = backend === "self" ? "apps/web" : "apps/server";
4614
- await addEnvVariablesToFile(path.join(projectDir, targetApp, ".env"), [{
4792
+ await addEnvVariablesToFile(path$1.join(projectDir, targetApp, ".env"), [{
4615
4793
  key: "DATABASE_URL",
4616
4794
  value: getDatabaseUrl(database, projectName),
4617
4795
  condition: true
@@ -4684,7 +4862,7 @@ async function initMongoDBAtlas(serverDir) {
4684
4862
  async function writeEnvFile$3(projectDir, backend, config) {
4685
4863
  try {
4686
4864
  const targetApp = backend === "self" ? "apps/web" : "apps/server";
4687
- await addEnvVariablesToFile(path.join(projectDir, targetApp, ".env"), [{
4865
+ await addEnvVariablesToFile(path$1.join(projectDir, targetApp, ".env"), [{
4688
4866
  key: "DATABASE_URL",
4689
4867
  value: config?.connectionString ?? "mongodb://localhost:27017/mydb",
4690
4868
  condition: true
@@ -4713,7 +4891,7 @@ ${pc.green("MongoDB Atlas Manual Setup Instructions:")}
4713
4891
  async function setupMongoDBAtlas(config, cliInput) {
4714
4892
  const { projectDir, backend } = config;
4715
4893
  const manualDb = cliInput?.manualDb ?? false;
4716
- const serverDir = path.join(projectDir, "packages/db");
4894
+ const serverDir = path$1.join(projectDir, "packages/db");
4717
4895
  try {
4718
4896
  await fs.ensureDir(serverDir);
4719
4897
  if (manualDb) {
@@ -4832,7 +5010,7 @@ async function createNeonProject(projectName, regionId, packageManager) {
4832
5010
  }
4833
5011
  async function writeEnvFile$2(projectDir, backend, config) {
4834
5012
  const targetApp = backend === "self" ? "apps/web" : "apps/server";
4835
- await addEnvVariablesToFile(path.join(projectDir, targetApp, ".env"), [{
5013
+ await addEnvVariablesToFile(path$1.join(projectDir, targetApp, ".env"), [{
4836
5014
  key: "DATABASE_URL",
4837
5015
  value: config?.connectionString ?? "postgresql://postgres:postgres@localhost:5432/mydb?schema=public",
4838
5016
  condition: true
@@ -4844,7 +5022,7 @@ async function setupWithNeonDb(projectDir, packageManager, backend) {
4844
5022
  const s = spinner();
4845
5023
  s.start("Creating Neon database using get-db...");
4846
5024
  const targetApp = backend === "self" ? "apps/web" : "apps/server";
4847
- const targetDir = path.join(projectDir, targetApp);
5025
+ const targetDir = path$1.join(projectDir, targetApp);
4848
5026
  await fs.ensureDir(targetDir);
4849
5027
  const packageArgs = getPackageExecutionArgs(packageManager, "get-db@latest --yes");
4850
5028
  await $({ cwd: targetDir })`${packageArgs}`;
@@ -4909,7 +5087,7 @@ async function setupNeonPostgres(config, cliInput) {
4909
5087
  if (isCancel(setupMethod)) return exitCancelled("Operation cancelled");
4910
5088
  if (setupMethod === "neondb") await setupWithNeonDb(projectDir, packageManager, backend);
4911
5089
  else {
4912
- const suggestedProjectName = path.basename(projectDir);
5090
+ const suggestedProjectName = path$1.basename(projectDir);
4913
5091
  const projectName = await text({
4914
5092
  message: "Enter a name for your Neon project:",
4915
5093
  defaultValue: suggestedProjectName,
@@ -4940,7 +5118,7 @@ async function setupNeonPostgres(config, cliInput) {
4940
5118
  async function setupPlanetScale(config) {
4941
5119
  const { projectDir, database, orm, backend } = config;
4942
5120
  const targetApp = backend === "self" ? "apps/web" : "apps/server";
4943
- const envPath = path.join(projectDir, targetApp, ".env");
5121
+ const envPath = path$1.join(projectDir, targetApp, ".env");
4944
5122
  if (database === "mysql" && orm === "drizzle") {
4945
5123
  const variables = [
4946
5124
  {
@@ -4964,7 +5142,7 @@ async function setupPlanetScale(config) {
4964
5142
  condition: true
4965
5143
  }
4966
5144
  ];
4967
- await fs.ensureDir(path.join(projectDir, targetApp));
5145
+ await fs.ensureDir(path$1.join(projectDir, targetApp));
4968
5146
  await addEnvVariablesToFile(envPath, variables);
4969
5147
  }
4970
5148
  if (database === "postgres" && orm === "prisma") {
@@ -4973,7 +5151,7 @@ async function setupPlanetScale(config) {
4973
5151
  value: "postgresql://username:password@host/database?sslaccept=strict",
4974
5152
  condition: true
4975
5153
  }];
4976
- await fs.ensureDir(path.join(projectDir, targetApp));
5154
+ await fs.ensureDir(path$1.join(projectDir, targetApp));
4977
5155
  await addEnvVariablesToFile(envPath, variables);
4978
5156
  }
4979
5157
  if (database === "postgres" && orm === "drizzle") {
@@ -4982,7 +5160,7 @@ async function setupPlanetScale(config) {
4982
5160
  value: "postgresql://username:password@host/database?sslmode=verify-full",
4983
5161
  condition: true
4984
5162
  }];
4985
- await fs.ensureDir(path.join(projectDir, targetApp));
5163
+ await fs.ensureDir(path$1.join(projectDir, targetApp));
4986
5164
  await addEnvVariablesToFile(envPath, variables);
4987
5165
  }
4988
5166
  if (database === "mysql" && orm === "prisma") {
@@ -4991,7 +5169,7 @@ async function setupPlanetScale(config) {
4991
5169
  value: "mysql://username:password@host/database?sslaccept=strict",
4992
5170
  condition: true
4993
5171
  }];
4994
- await fs.ensureDir(path.join(projectDir, targetApp));
5172
+ await fs.ensureDir(path$1.join(projectDir, targetApp));
4995
5173
  await addEnvVariablesToFile(envPath, variables);
4996
5174
  }
4997
5175
  }
@@ -5057,7 +5235,7 @@ async function setupWithCreateDb(serverDir, packageManager) {
5057
5235
  async function writeEnvFile$1(projectDir, backend, config) {
5058
5236
  try {
5059
5237
  const targetApp = backend === "self" ? "apps/web" : "apps/server";
5060
- const envPath = path.join(projectDir, targetApp, ".env");
5238
+ const envPath = path$1.join(projectDir, targetApp, ".env");
5061
5239
  const variables = [{
5062
5240
  key: "DATABASE_URL",
5063
5241
  value: config?.databaseUrl ?? "postgresql://postgres:postgres@localhost:5432/mydb?schema=public",
@@ -5086,7 +5264,7 @@ DATABASE_URL="your_database_url"`);
5086
5264
  async function setupPrismaPostgres(config, cliInput) {
5087
5265
  const { packageManager, projectDir, backend } = config;
5088
5266
  const manualDb = cliInput?.manualDb ?? false;
5089
- const dbDir = path.join(projectDir, "packages/db");
5267
+ const dbDir = path$1.join(projectDir, "packages/db");
5090
5268
  try {
5091
5269
  await fs.ensureDir(dbDir);
5092
5270
  if (manualDb) {
@@ -5137,7 +5315,7 @@ async function setupPrismaPostgres(config, cliInput) {
5137
5315
  async function writeSupabaseEnvFile(projectDir, backend, databaseUrl) {
5138
5316
  try {
5139
5317
  const targetApp = backend === "self" ? "apps/web" : "apps/server";
5140
- const envPath = path.join(projectDir, targetApp, ".env");
5318
+ const envPath = path$1.join(projectDir, targetApp, ".env");
5141
5319
  const dbUrlToUse = databaseUrl || "postgresql://postgres:postgres@127.0.0.1:54322/postgres";
5142
5320
  await addEnvVariablesToFile(envPath, [{
5143
5321
  key: "DATABASE_URL",
@@ -5221,7 +5399,7 @@ ${pc.dim(output)}` : ""}
5221
5399
  async function setupSupabase(config, cliInput) {
5222
5400
  const { projectDir, packageManager, backend } = config;
5223
5401
  const manualDb = cliInput?.manualDb ?? false;
5224
- const serverDir = path.join(projectDir, "packages", "db");
5402
+ const serverDir = path$1.join(projectDir, "packages", "db");
5225
5403
  try {
5226
5404
  await fs.ensureDir(serverDir);
5227
5405
  if (manualDb) {
@@ -5387,7 +5565,7 @@ async function createTursoDatabase(dbName, groupName) {
5387
5565
  }
5388
5566
  async function writeEnvFile(projectDir, backend, config) {
5389
5567
  const targetApp = backend === "self" ? "apps/web" : "apps/server";
5390
- await addEnvVariablesToFile(path.join(projectDir, targetApp, ".env"), [{
5568
+ await addEnvVariablesToFile(path$1.join(projectDir, targetApp, ".env"), [{
5391
5569
  key: "DATABASE_URL",
5392
5570
  value: config?.dbUrl ?? "",
5393
5571
  condition: true
@@ -5465,7 +5643,7 @@ async function setupTurso(config, cliInput) {
5465
5643
  const selectedGroup = await selectTursoGroup();
5466
5644
  let success = false;
5467
5645
  let dbName = "";
5468
- let suggestedName = path.basename(projectDir);
5646
+ let suggestedName = path$1.basename(projectDir);
5469
5647
  while (!success) {
5470
5648
  const dbNameResponse = await text({
5471
5649
  message: "Enter a name for your database:",
@@ -5506,12 +5684,12 @@ async function setupDatabase(config, cliInput) {
5506
5684
  const { database, dbSetup, backend, projectDir } = config;
5507
5685
  if (backend === "convex" || database === "none") {
5508
5686
  if (backend !== "convex") {
5509
- const serverDbDir = path.join(projectDir, "apps/server/src/db");
5687
+ const serverDbDir = path$1.join(projectDir, "apps/server/src/db");
5510
5688
  if (await fs.pathExists(serverDbDir)) await fs.remove(serverDbDir);
5511
5689
  }
5512
5690
  return;
5513
5691
  }
5514
- const dbPackageDir = path.join(projectDir, "packages/db");
5692
+ const dbPackageDir = path$1.join(projectDir, "packages/db");
5515
5693
  if (!await fs.pathExists(dbPackageDir)) return;
5516
5694
  try {
5517
5695
  if (dbSetup === "docker") await setupDockerCompose(config);
@@ -5830,7 +6008,7 @@ async function createProject(options, cliInput = {}) {
5830
6008
  }
5831
6009
  }
5832
6010
  async function setPackageManagerVersion(projectDir, packageManager) {
5833
- const pkgJsonPath = path.join(projectDir, "package.json");
6011
+ const pkgJsonPath = path$1.join(projectDir, "package.json");
5834
6012
  if (!await fs.pathExists(pkgJsonPath)) return;
5835
6013
  try {
5836
6014
  const { stdout } = await $`${packageManager} -v`;
@@ -5862,7 +6040,7 @@ async function createProjectHandler(input, options = {}) {
5862
6040
  const defaultConfig = getDefaultConfig();
5863
6041
  let defaultName = defaultConfig.relativePath;
5864
6042
  let counter = 1;
5865
- while (await fs.pathExists(path.resolve(process.cwd(), defaultName)) && (await fs.readdir(path.resolve(process.cwd(), defaultName))).length > 0) {
6043
+ while (await fs.pathExists(path$1.resolve(process.cwd(), defaultName)) && (await fs.readdir(path$1.resolve(process.cwd(), defaultName))).length > 0) {
5866
6044
  defaultName = `${defaultConfig.projectName}-${counter}`;
5867
6045
  counter++;
5868
6046
  }
@@ -6038,7 +6216,7 @@ async function createProjectHandler(input, options = {}) {
6038
6216
  });
6039
6217
  }
6040
6218
  async function handleDirectoryConflictProgrammatically(currentPathInput, strategy) {
6041
- const currentPath = path.resolve(process.cwd(), currentPathInput);
6219
+ const currentPath = path$1.resolve(process.cwd(), currentPathInput);
6042
6220
  if (!await fs.pathExists(currentPath)) return {
6043
6221
  finalPathInput: currentPathInput,
6044
6222
  shouldClearDirectory: false
@@ -6060,7 +6238,7 @@ async function handleDirectoryConflictProgrammatically(currentPathInput, strateg
6060
6238
  let counter = 1;
6061
6239
  const baseName = currentPathInput;
6062
6240
  let finalPathInput = `${baseName}-${counter}`;
6063
- while (await fs.pathExists(path.resolve(process.cwd(), finalPathInput)) && (await fs.readdir(path.resolve(process.cwd(), finalPathInput))).length > 0) {
6241
+ while (await fs.pathExists(path$1.resolve(process.cwd(), finalPathInput)) && (await fs.readdir(path$1.resolve(process.cwd(), finalPathInput))).length > 0) {
6064
6242
  counter++;
6065
6243
  finalPathInput = `${baseName}-${counter}`;
6066
6244
  }
@@ -6221,6 +6399,24 @@ const router = os.router({
6221
6399
  } catch {
6222
6400
  log.message(`Please visit ${BUILDER_URL}`);
6223
6401
  }
6402
+ }),
6403
+ "update-deps": os.meta({ description: "Check and update dependency versions in add-deps.ts" }).input(z.object({
6404
+ check: z.boolean().default(false).describe("Report only, no changes"),
6405
+ patch: z.boolean().default(false).describe("Apply patch/minor updates only"),
6406
+ all: z.boolean().default(false).describe("Interactive mode for all updates"),
6407
+ ecosystem: z.string().optional().describe("Filter by ecosystem (effect, tanstack, prisma, etc.)"),
6408
+ "list-ecosystems": z.boolean().default(false).describe("List available ecosystems")
6409
+ })).handler(async ({ input }) => {
6410
+ if (input["list-ecosystems"]) {
6411
+ showEcosystems();
6412
+ return;
6413
+ }
6414
+ await updateDepsHandler({
6415
+ check: input.check,
6416
+ patch: input.patch,
6417
+ all: input.all,
6418
+ ecosystem: input.ecosystem
6419
+ });
6224
6420
  })
6225
6421
  });
6226
6422
  const caller = createRouterClient(router, { context: {} });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-better-fullstack",
3
- "version": "1.1.6",
3
+ "version": "1.1.7",
4
4
  "description": "A CLI-first toolkit for building Full Stack applications. Skip the configuration. Ship the code.",
5
5
  "keywords": [
6
6
  "better-auth",
@@ -72,8 +72,8 @@
72
72
  "prepublishOnly": "npm run build"
73
73
  },
74
74
  "dependencies": {
75
- "@better-fullstack/template-generator": "^1.1.6",
76
- "@better-fullstack/types": "^1.1.6",
75
+ "@better-fullstack/template-generator": "^1.1.7",
76
+ "@better-fullstack/types": "^1.1.7",
77
77
  "@clack/core": "^0.5.0",
78
78
  "@clack/prompts": "^1.0.0-alpha.8",
79
79
  "@orpc/server": "^1.13.0",