betterstart-cli 0.0.40 → 0.0.41

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.js CHANGED
@@ -22241,7 +22241,7 @@ async function promptPlugins(cwd, options = {}) {
22241
22241
  } else {
22242
22242
  if (existingToken) {
22243
22243
  p12.log.info(
22244
- `Using the existing Vercel Blob token from .env.local ${styleText(
22244
+ `Using the existing BLOB_READ_WRITE_TOKEN from .env.local ${styleText(
22245
22245
  "dim",
22246
22246
  `(${maskBlobToken(existingToken)})`
22247
22247
  )}`
@@ -22270,30 +22270,13 @@ async function promptPlugins(cwd, options = {}) {
22270
22270
  process.exit(0);
22271
22271
  }
22272
22272
  const integrations = [];
22273
- const setupResend = await p12.confirm({
22274
- message: `Do you want to setup resend.com emails?
22275
- ${styleText(
22276
- "dim",
22277
- "This will be used for form submission confirmations, password resets etc."
22278
- )}`,
22279
- initialValue: true
22273
+ sections.push({
22274
+ header: "Email (Resend)",
22275
+ vars: [
22276
+ { key: "BETTERSTART_RESEND_API_KEY", value: "" },
22277
+ { key: "BETTERSTART_EMAIL_FROM", value: "" }
22278
+ ]
22280
22279
  });
22281
- if (p12.isCancel(setupResend)) {
22282
- p12.cancel("Setup cancelled.");
22283
- process.exit(0);
22284
- }
22285
- if (setupResend) {
22286
- integrations.push("resend");
22287
- mergeIntegrationConfig(await collectIntegrationConfig(cwd, ["resend"]));
22288
- } else {
22289
- sections.push({
22290
- header: "Email (Resend)",
22291
- vars: [
22292
- { key: "BETTERSTART_RESEND_API_KEY", value: "" },
22293
- { key: "BETTERSTART_EMAIL_FROM", value: "" }
22294
- ]
22295
- });
22296
- }
22297
22280
  if (storage === "r2") {
22298
22281
  integrations.push("r2");
22299
22282
  } else if (storage === "vercel-blob") {
@@ -25505,18 +25488,6 @@ function readNamespaceFromConfigSource(source) {
25505
25488
  const match = source.match(/\bnamespace\s*:\s*(['"`])([^'"`]+)\1/);
25506
25489
  return normalizeNamespaceForRemoval(match?.[2]);
25507
25490
  }
25508
- function formatStorageProviderLabel(provider) {
25509
- switch (provider) {
25510
- case "vercel-blob":
25511
- return "Vercel Blob";
25512
- case "r2":
25513
- return "Cloudflare R2";
25514
- case "local":
25515
- return "local filesystem";
25516
- default:
25517
- return provider;
25518
- }
25519
- }
25520
25491
  function resolveNonInteractiveProject(name) {
25521
25492
  const projectName = name?.trim() || "my-app";
25522
25493
  if (projectName !== "." && !/^[a-z0-9_-]+$/i.test(projectName)) {
@@ -25808,7 +25779,7 @@ async function runInitCommand(name, options) {
25808
25779
  }
25809
25780
  } else if (existingDbUrl) {
25810
25781
  const masked = maskDbUrl(existingDbUrl);
25811
- p18.log.info(`Using existing database URL from .env.local ${pc7.dim(`(${masked})`)}`);
25782
+ p18.log.info(`Using the existing DATABASE_URL from .env.local ${pc7.dim(`(${masked})`)}`);
25812
25783
  databaseUrl = existingDbUrl;
25813
25784
  } else {
25814
25785
  const databaseStep = deferNextPromptCheck();
@@ -25913,35 +25884,21 @@ async function runInitCommand(name, options) {
25913
25884
  config.frameworkConfig.next.paths = deriveNamespacePaths(namespace, srcDir);
25914
25885
  config.paths = config.frameworkConfig.next.paths;
25915
25886
  config.database.migrationsDir = deriveMigrationsDir(namespace);
25916
- const results = [];
25917
25887
  const s = spinner2();
25918
25888
  s.start("Directory structure");
25919
- const baseFiles = scaffoldBase({
25889
+ scaffoldBase({
25920
25890
  cwd,
25921
25891
  config,
25922
25892
  includeEmailTemplatesDir: pluginSelection.integrations.some(isEmailProviderIntegration)
25923
25893
  });
25924
- results.push({ label: "Directory structure", result: `${baseFiles.length} files` });
25925
25894
  s.message("TypeScript aliases");
25926
- const tsResult = scaffoldTsconfig(cwd, config);
25927
- results.push({
25928
- label: "TypeScript aliases",
25929
- result: tsResult.added.length > 0 ? `${tsResult.added.length} paths` : "already set"
25930
- });
25895
+ scaffoldTsconfig(cwd, config);
25931
25896
  s.message("Tailwind CSS");
25932
- const twResult = scaffoldTailwind(cwd, srcDir, namespace);
25933
- results.push({
25934
- label: "Tailwind CSS",
25935
- result: twResult.appended ? "updated" : twResult.file ? "already set" : "no CSS file"
25936
- });
25897
+ scaffoldTailwind(cwd, srcDir, namespace);
25937
25898
  s.message("Dev server");
25938
- const devServerResult = ensureDefaultDevScriptPort(cwd);
25939
- results.push({
25940
- label: "Dev server",
25941
- result: formatDevServerResult(devServerResult)
25942
- });
25899
+ ensureDefaultDevScriptPort(cwd);
25943
25900
  s.message("Environment variables");
25944
- const envResult = scaffoldEnv(cwd, { databaseUrl, namespace });
25901
+ scaffoldEnv(cwd, { databaseUrl, namespace });
25945
25902
  if (collectedIntegrationConfig.sections.length > 0) {
25946
25903
  appendEnvVars(
25947
25904
  cwd,
@@ -25949,49 +25906,26 @@ async function runInitCommand(name, options) {
25949
25906
  collectedIntegrationConfig.overwriteKeys
25950
25907
  );
25951
25908
  }
25952
- const envCount = envResult.added.length + envResult.updated.length;
25953
- results.push({
25954
- label: "Environment variables",
25955
- result: envCount > 0 ? `${envCount} vars` : "already set"
25956
- });
25957
25909
  s.message("Database");
25958
25910
  const dbFiles = scaffoldDatabase({ cwd, config });
25959
- results.push({ label: "Database", result: `${dbFiles.length} files` });
25960
25911
  s.message("Authentication");
25961
- const authFiles = scaffoldAuth({ cwd, config });
25962
- results.push({ label: "Authentication", result: `${authFiles.length} files` });
25912
+ scaffoldAuth({ cwd, config });
25963
25913
  s.message("Components");
25964
- const compFiles = scaffoldComponents({ cwd, config });
25965
- results.push({ label: "Components", result: `${compFiles.length} files` });
25914
+ scaffoldComponents({ cwd, config });
25966
25915
  s.message("Pages & layouts");
25967
- const layoutFiles = scaffoldLayout({ cwd, config });
25968
- results.push({ label: "Pages & layouts", result: `${layoutFiles.length} files` });
25916
+ scaffoldLayout({ cwd, config });
25969
25917
  s.message("API routes");
25970
- const apiFiles = scaffoldApiRoutes({ cwd, config });
25971
- results.push({ label: "API routes", result: `${apiFiles.length} routes` });
25918
+ scaffoldApiRoutes({ cwd, config });
25972
25919
  s.message("Linter");
25973
- let linterResult;
25974
25920
  if (project2.linter.type === "none") {
25975
- const biomeResult = scaffoldBiome(cwd, project2.linter);
25976
- linterResult = biomeResult.installed ? "biome (new)" : "none";
25977
- } else {
25978
- linterResult = project2.linter.type;
25921
+ scaffoldBiome(cwd, project2.linter);
25979
25922
  }
25980
- results.push({ label: "Linter", result: linterResult });
25981
25923
  s.message("Next.js config");
25982
25924
  const nextConfigResult = scaffoldNextConfig({ cwd, nextMajorVersion });
25983
- results.push({
25984
- label: "Next.js config",
25985
- result: nextConfigResult.status === "created" ? "created" : nextConfigResult.status === "updated" ? "updated" : nextConfigResult.status === "already-set" ? "already set" : nextConfigResult.status === "not-needed" ? "not needed" : "manual setup needed"
25986
- });
25987
- const maxLabel = Math.max(...results.map((r) => r.label.length));
25988
- const noteLines = results.map((r) => {
25989
- const padded = r.label.padEnd(maxLabel + 3);
25990
- return `${pc7.green("\u2713")} ${padded}${pc7.dim(r.result)}`;
25991
- });
25992
- s.stop("");
25993
- process.stdout.write("\x1B[2A\x1B[J");
25994
- p18.note(noteLines.join("\n"), "Scaffolded admin");
25925
+ s.clear();
25926
+ if (nextConfigResult.status === "unsupported") {
25927
+ p18.log.warn("The Next.js config could not be updated automatically \u2014 review it manually.");
25928
+ }
25995
25929
  const drizzleConfigPath = path50.join(cwd, "drizzle.config.ts");
25996
25930
  if (!dbFiles.includes("drizzle.config.ts") && fs39.existsSync(drizzleConfigPath)) {
25997
25931
  if (forceMode) {
@@ -26096,42 +26030,20 @@ async function runInitCommand(name, options) {
26096
26030
  }
26097
26031
  writeConfigFile(cwd, resolvedIntegrationInstallResult.config);
26098
26032
  const usesLocalStorage = resolvedIntegrationInstallResult.config.storage.provider === "local";
26099
- const installLines = [];
26100
- if (depsInstalled) {
26101
- installLines.push(
26102
- `${pc7.green("\u2713")} Dependencies ${pc7.dim(`${depsResult.dependencies.length} deps + ${depsResult.devDeps.length} dev deps`)}`
26103
- );
26104
- }
26105
- if (coreSchemasResult.errors.length > 0) {
26106
- installLines.push(
26107
- `${pc7.yellow("\u25B2")} Core schemas ${pc7.dim(`${coreSchemasResult.errors.length} warning(s)`)}`
26108
- );
26109
- for (const err of coreSchemasResult.errors) {
26110
- installLines.push(` ${pc7.dim(err)}`);
26111
- }
26112
- } else {
26113
- installLines.push(
26114
- `${pc7.green("\u2713")} Core schemas ${pc7.dim(`${coreSchemasResult.schemas.length} schemas, ${coreSchemasResult.generatedFiles.length} files`)}`
26115
- );
26033
+ for (const err of coreSchemasResult.errors) {
26034
+ p18.log.warn(`Core schemas: ${err}`);
26116
26035
  }
26117
- installLines.push(
26118
- `${pc7.green("\u2713")} Plugins ${pc7.dim(resolvedPluginInstallResult.installed.length > 0 ? resolvedPluginInstallResult.installed.join(", ") : "core only")}`
26119
- );
26120
26036
  for (const warning of resolvedPluginInstallResult.warnings) {
26121
- installLines.push(` ${pc7.yellow("\u25B2")} ${warning}`);
26037
+ p18.log.warn(warning);
26122
26038
  }
26123
- installLines.push(
26124
- `${pc7.green("\u2713")} Integrations ${pc7.dim(resolvedIntegrationInstallResult.installed.length > 0 ? resolvedIntegrationInstallResult.installed.join(", ") : "none")}`
26125
- );
26126
26039
  for (const warning of resolvedIntegrationInstallResult.warnings) {
26127
- installLines.push(` ${pc7.yellow("\u25B2")} ${warning}`);
26040
+ p18.log.warn(warning);
26128
26041
  }
26129
26042
  if (usesLocalStorage) {
26130
- installLines.push(
26131
- ` ${pc7.yellow("\u25B2")} Local filesystem storage is only for development or self-hosted persistent-disk deployments. Use Vercel Blob or Cloudflare R2 for hosted/serverless production.`
26043
+ p18.log.warn(
26044
+ "Local filesystem storage is only for development or self-hosted persistent-disk deployments. Use Vercel Blob or Cloudflare R2 for hosted/serverless production."
26132
26045
  );
26133
26046
  }
26134
- p18.note(installLines.join("\n"), "Installed");
26135
26047
  let dbPushed = false;
26136
26048
  if (depsResult.success && options.skipMigration && hasDbUrl(cwd)) {
26137
26049
  p18.log.info(`Skipping database schema push ${pc7.dim("(--skip-migration)")}`);
@@ -26160,9 +26072,7 @@ async function runInitCommand(name, options) {
26160
26072
  }
26161
26073
  if (driverReady) {
26162
26074
  const useTerminalForDbPush = shouldUseTerminalForDbPush(options);
26163
- if (useTerminalForDbPush) {
26164
- p18.log.info(`Running ${pc7.cyan("drizzle-kit push --force")}`);
26165
- } else {
26075
+ if (!useTerminalForDbPush) {
26166
26076
  s.start("Pushing database schema (drizzle-kit push)");
26167
26077
  }
26168
26078
  const pushResult = await runDrizzlePush(cwd, { interactive: useTerminalForDbPush });
@@ -26174,11 +26084,8 @@ async function runInitCommand(name, options) {
26174
26084
  p18.log.error("Database was not reachable. Aborting setup.");
26175
26085
  process.exit(1);
26176
26086
  }
26177
- }
26178
- if (useTerminalForDbPush) {
26179
- p18.log.success("Database schema pushed");
26180
26087
  } else {
26181
- s.stop(`${pc7.green("\u2713")} Database schema pushed`);
26088
+ s.clear();
26182
26089
  }
26183
26090
  dbPushed = true;
26184
26091
  } else {
@@ -26243,12 +26150,6 @@ async function runInitCommand(name, options) {
26243
26150
  }
26244
26151
  }
26245
26152
  if (!adminAccountReady) {
26246
- p18.note(
26247
- pc7.dim(
26248
- replaceExistingAdmin ? "Replace the existing admin account to access the admin." : "Create your first admin user to access the dashboard."
26249
- ),
26250
- "Admin account"
26251
- );
26252
26153
  const credentials = await p18.group(
26253
26154
  {
26254
26155
  email: () => p18.text({
@@ -26338,19 +26239,8 @@ Run manually: ${pc7.cyan(betterstartExecCommand(pm, "seed"))}`,
26338
26239
  s.stop("Git commit skipped");
26339
26240
  }
26340
26241
  }
26341
- const totalFiles = baseFiles.length + dbFiles.length + authFiles.length + compFiles.length + layoutFiles.length + apiFiles.length;
26342
26242
  const adminNamespace = resolveAdminNamespace(namespace);
26343
26243
  const adminLoginUrl = `http://localhost:${detectDevPort(cwd)}${adminNamespace.routePath}/login`;
26344
- const installedPluginList = resolvedPluginInstallResult.installed.length > 0 ? resolvedPluginInstallResult.installed.join(", ") : "core only";
26345
- const installedIntegrationList = resolvedIntegrationInstallResult.installed.length > 0 ? resolvedIntegrationInstallResult.installed.join(", ") : "none";
26346
- const summaryLines = [
26347
- `Plugins: ${pc7.cyan(installedPluginList)}`,
26348
- `Integrations: ${pc7.cyan(installedIntegrationList)}`,
26349
- `Storage: ${pc7.cyan(formatStorageProviderLabel(resolvedIntegrationInstallResult.config.storage.provider))}`,
26350
- `Files created: ${pc7.cyan(String(totalFiles))}`,
26351
- `Env vars: ${envResult.added.length} added, ${envResult.skipped.length} skipped`
26352
- ];
26353
- p18.note(summaryLines.join("\n"), "Admin scaffolded successfully");
26354
26244
  if (options.vercel !== false && !options.skipDeploy) {
26355
26245
  const linkedVercelProject = Boolean(readLinkedProjectId(cwd));
26356
26246
  if (!options.yes) {
@@ -26463,20 +26353,6 @@ function detectInstalledNextVersion(cwd) {
26463
26353
  return void 0;
26464
26354
  }
26465
26355
  }
26466
- function formatDevServerResult(result) {
26467
- switch (result.status) {
26468
- case "updated":
26469
- return `set to ${result.port}`;
26470
- case "preserved":
26471
- return `using ${result.port}`;
26472
- case "skipped":
26473
- return `custom script preserved; BetterStart uses ${result.port}`;
26474
- case "missing-script":
26475
- return `no dev script; BetterStart uses ${result.port}`;
26476
- case "missing-package":
26477
- return `package.json missing; BetterStart uses ${result.port}`;
26478
- }
26479
- }
26480
26356
  function shouldUseTerminalForDbPush(options) {
26481
26357
  return !options.yes && process.env.CI !== "true" && Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY);
26482
26358
  }