@donotdev/cli 0.0.15 → 0.0.16

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.
Files changed (70) hide show
  1. package/dependencies-matrix.json +36 -20
  2. package/dist/bin/commands/build.js +69 -52
  3. package/dist/bin/commands/bump.js +7 -13
  4. package/dist/bin/commands/create-app.js +77 -29
  5. package/dist/bin/commands/create-project.js +109 -135
  6. package/dist/bin/commands/deploy.js +97 -45
  7. package/dist/bin/commands/dev.js +43 -24
  8. package/dist/bin/commands/doctor.d.ts +6 -0
  9. package/dist/bin/commands/doctor.d.ts.map +1 -0
  10. package/dist/bin/commands/{lint.js → doctor.js} +1178 -147
  11. package/dist/bin/commands/doctor.js.map +1 -0
  12. package/dist/bin/commands/emu.js +297 -107
  13. package/dist/bin/commands/make-admin.js +77499 -11
  14. package/dist/bin/commands/preview.js +44 -25
  15. package/dist/bin/commands/setup.d.ts +6 -0
  16. package/dist/bin/commands/setup.d.ts.map +1 -0
  17. package/dist/bin/commands/setup.js +11733 -0
  18. package/dist/bin/commands/setup.js.map +1 -0
  19. package/dist/bin/commands/type-check.js +2018 -283
  20. package/dist/bin/dndev.js +54 -58
  21. package/dist/bin/donotdev.js +28 -44
  22. package/dist/index.js +633 -416
  23. package/package.json +2 -2
  24. package/templates/app-expo/.env.example +2 -22
  25. package/templates/app-expo/README.md.example +1 -1
  26. package/templates/app-expo/assets/adaptive-icon.png +0 -0
  27. package/templates/app-expo/assets/favicon.png +0 -0
  28. package/templates/app-expo/assets/icon.png +0 -0
  29. package/templates/app-expo/assets/splash.png +0 -0
  30. package/templates/app-expo/src/config/app.ts.example +46 -0
  31. package/templates/app-expo/src/config/providers.ts.example +7 -0
  32. package/templates/app-next/src/config/providers.ts.example +7 -0
  33. package/templates/app-vite/src/config/providers.ts.example +7 -0
  34. package/templates/app-vite/src/pages/HomePage.tsx.example +1 -1
  35. package/templates/functions-firebase/README.md.example +1 -1
  36. package/templates/functions-firebase/functions-firebase/.env.example.example +1 -1
  37. package/templates/functions-firebase/functions-firebase/README.md.example +1 -1
  38. package/templates/functions-firebase/functions-firebase/tsconfig.json.example +1 -1
  39. package/templates/functions-firebase/functions.config.js.example +1 -1
  40. package/templates/functions-supabase/supabase/config.toml.example +59 -0
  41. package/templates/functions-supabase/supabase/functions/.env.example +13 -0
  42. package/templates/functions-supabase/supabase/functions/deno.json.example +8 -0
  43. package/templates/overlay-firebase/env.fragment.example +1 -1
  44. package/templates/overlay-firebase/env.fragment.expo.example +1 -1
  45. package/templates/overlay-firebase/env.fragment.nextjs.example +1 -1
  46. package/templates/overlay-supabase/env.fragment.example +8 -3
  47. package/templates/overlay-supabase/env.fragment.expo.example +8 -3
  48. package/templates/overlay-supabase/env.fragment.nextjs.example +8 -3
  49. package/templates/overlay-vercel/env.fragment.example +1 -1
  50. package/templates/overlay-vercel/env.fragment.nextjs.example +1 -1
  51. package/templates/root-consumer/AI.md.example +15 -0
  52. package/templates/root-consumer/guides/dndev/AGENT_START_HERE.md.example +2 -2
  53. package/templates/root-consumer/guides/dndev/ENV_SETUP.md.example +6 -6
  54. package/templates/root-consumer/guides/dndev/INDEX.md.example +2 -2
  55. package/templates/root-consumer/guides/dndev/SETUP_APP_CONFIG.md.example +3 -3
  56. package/templates/root-consumer/guides/dndev/SETUP_CRUD.md.example +98 -0
  57. package/templates/root-consumer/guides/dndev/SETUP_FIREBASE.md.example +4 -4
  58. package/templates/root-consumer/guides/dndev/SETUP_OAUTH_PROVIDERS.md.example +60 -0
  59. package/templates/root-consumer/guides/dndev/SETUP_STRIPE.md.example +62 -0
  60. package/templates/root-consumer/guides/dndev/SETUP_SUPABASE.md.example +3 -3
  61. package/templates/root-consumer/guides/dndev/SETUP_VERCEL.md.example +2 -2
  62. package/templates/root-consumer/guides/wai-way/WAI_WAY_CLI.md.example +7 -8
  63. package/templates/root-consumer/guides/wai-way/blueprints/1_scaffold.md.example +9 -5
  64. package/dist/bin/commands/lint.d.ts +0 -11
  65. package/dist/bin/commands/lint.d.ts.map +0 -1
  66. package/dist/bin/commands/lint.js.map +0 -1
  67. package/dist/bin/commands/staging.d.ts +0 -11
  68. package/dist/bin/commands/staging.d.ts.map +0 -1
  69. package/dist/bin/commands/staging.js +0 -12
  70. package/dist/bin/commands/staging.js.map +0 -1
package/dist/index.js CHANGED
@@ -8510,6 +8510,148 @@ var init_utils = __esm({
8510
8510
  }
8511
8511
  });
8512
8512
 
8513
+ // packages/tooling/src/utils/app-detection.ts
8514
+ function detectApps(projectRoot) {
8515
+ const apps = [];
8516
+ const appsDir = joinPath(projectRoot, "apps");
8517
+ const appsDirStat = statSync2(appsDir);
8518
+ if (pathExists(appsDir) && appsDirStat?.isDirectory()) {
8519
+ const appDirs = readdirSync2(appsDir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name).sort();
8520
+ for (const appDir of appDirs) {
8521
+ const appPath = joinPath(appsDir, appDir);
8522
+ const packageJsonPath = joinPath(appPath, "package.json");
8523
+ if (pathExists(packageJsonPath)) {
8524
+ const appInfo = analyzeApp(appPath, appDir);
8525
+ if (appInfo) {
8526
+ apps.push(appInfo);
8527
+ }
8528
+ }
8529
+ }
8530
+ } else {
8531
+ const packageJsonPath = joinPath(projectRoot, "package.json");
8532
+ if (pathExists(packageJsonPath)) {
8533
+ try {
8534
+ const packageJson = readPackageJson(packageJsonPath);
8535
+ if (packageJson && (packageJson.dependencies?.vite || packageJson.devDependencies?.vite || packageJson.dependencies?.next || packageJson.devDependencies?.next)) {
8536
+ const pathParts = projectRoot.split(/[/\\]/);
8537
+ const appName = pathParts[pathParts.length - 1] || "app";
8538
+ const appInfo = analyzeApp(projectRoot, appName);
8539
+ if (appInfo) {
8540
+ apps.push(appInfo);
8541
+ }
8542
+ }
8543
+ } catch {
8544
+ }
8545
+ }
8546
+ }
8547
+ return apps;
8548
+ }
8549
+ function analyzeApp(appPath, appName) {
8550
+ const packageJsonPath = joinPath(appPath, "package.json");
8551
+ if (!pathExists(packageJsonPath)) {
8552
+ return null;
8553
+ }
8554
+ let packageJson = null;
8555
+ try {
8556
+ packageJson = readPackageJson(packageJsonPath);
8557
+ if (!packageJson.name) {
8558
+ return null;
8559
+ }
8560
+ } catch {
8561
+ return null;
8562
+ }
8563
+ let framework = "unknown";
8564
+ if (pathExists(joinPath(appPath, "next.config.js")) || pathExists(joinPath(appPath, "next.config.ts")) || pathExists(joinPath(appPath, "next.config.mjs")) || packageJson.dependencies?.next || packageJson.devDependencies?.next) {
8565
+ framework = "nextjs";
8566
+ } else if (pathExists(joinPath(appPath, "vite.config.ts")) || pathExists(joinPath(appPath, "vite.config.js")) || pathExists(joinPath(appPath, "vite.config.mjs")) || packageJson.dependencies?.vite || packageJson.devDependencies?.vite) {
8567
+ framework = "vite";
8568
+ }
8569
+ const functionsPath = joinPath(appPath, "functions");
8570
+ const functionsStat = statSync2(functionsPath);
8571
+ const hasFunctions = pathExists(functionsPath) && (functionsStat?.isDirectory() ?? false);
8572
+ let platform6;
8573
+ const firebaseJsonInFunctions = hasFunctions && pathExists(joinPath(functionsPath, "firebase.json"));
8574
+ const firebaseJsonInApp = pathExists(joinPath(appPath, "firebase.json"));
8575
+ if (firebaseJsonInFunctions || firebaseJsonInApp) {
8576
+ platform6 = "firebase";
8577
+ }
8578
+ if (!platform6) {
8579
+ const supabaseConfigPath = joinPath(appPath, "supabase", "config.toml");
8580
+ const hasSupabaseDep = !!(packageJson.dependencies?.["@donotdev/supabase"] || packageJson.dependencies?.["@supabase/supabase-js"]);
8581
+ if (pathExists(supabaseConfigPath) || hasSupabaseDep) {
8582
+ platform6 = "supabase";
8583
+ }
8584
+ }
8585
+ if (!platform6) {
8586
+ const vercelJsonPath = joinPath(appPath, "vercel.json");
8587
+ if (pathExists(vercelJsonPath)) {
8588
+ platform6 = "vercel";
8589
+ }
8590
+ }
8591
+ return {
8592
+ name: appName,
8593
+ packageName: packageJson.name || appName,
8594
+ path: appPath,
8595
+ packageJsonPath,
8596
+ framework,
8597
+ hasFunctions,
8598
+ functionsPath: hasFunctions ? functionsPath : void 0,
8599
+ platform: platform6
8600
+ };
8601
+ }
8602
+ var init_app_detection = __esm({
8603
+ "packages/tooling/src/utils/app-detection.ts"() {
8604
+ "use strict";
8605
+ init_utils();
8606
+ init_pathResolver();
8607
+ init_typed_file_operations();
8608
+ }
8609
+ });
8610
+
8611
+ // packages/tooling/src/utils/app-selector.ts
8612
+ async function selectApp(projectRoot, appName) {
8613
+ const apps = detectApps(projectRoot);
8614
+ if (apps.length === 0) {
8615
+ log.error("No apps found in this project.");
8616
+ log.info('Run "dndev init" to create a new project.');
8617
+ return null;
8618
+ }
8619
+ if (appName) {
8620
+ const app = apps.find((a) => a.name === appName);
8621
+ if (!app) {
8622
+ log.warn(`App "${appName}" not found.`);
8623
+ log.info("Available apps:");
8624
+ apps.forEach((a) => log.info(` - ${a.name}`));
8625
+ log.info("Please select from the list:");
8626
+ } else {
8627
+ return app;
8628
+ }
8629
+ }
8630
+ if (apps.length === 1) {
8631
+ return apps[0] || null;
8632
+ }
8633
+ const selected = await ve({
8634
+ message: "Select app:",
8635
+ options: apps.map((app) => ({
8636
+ value: app.name,
8637
+ label: `${app.name} (${app.framework === "nextjs" ? "Next.js" : "Vite"}${app.hasFunctions ? ` + ${app.platform || "functions"}` : ""})`
8638
+ }))
8639
+ });
8640
+ if (typeof selected !== "string") {
8641
+ return null;
8642
+ }
8643
+ return apps.find((a) => a.name === selected) || null;
8644
+ }
8645
+ var init_app_selector = __esm({
8646
+ "packages/tooling/src/utils/app-selector.ts"() {
8647
+ "use strict";
8648
+ init_utils();
8649
+ init_dist2();
8650
+ init_app_detection();
8651
+ init_cli_output();
8652
+ }
8653
+ });
8654
+
8513
8655
  // packages/tooling/src/utils/cli-input.ts
8514
8656
  async function askForInput(message, defaultValue = "") {
8515
8657
  const result = await he({
@@ -8677,7 +8819,7 @@ function checkTsx() {
8677
8819
  return checkCLI("tsx");
8678
8820
  }
8679
8821
  function getCLIInstallInstructions(tool) {
8680
- const platform4 = process.platform;
8822
+ const platform6 = process.platform;
8681
8823
  const instructions = {
8682
8824
  [CLI_TOOLS.BUN]: {
8683
8825
  win32: [
@@ -8730,15 +8872,15 @@ function getCLIInstallInstructions(tool) {
8730
8872
  },
8731
8873
  [CLI_TOOLS.SUPABASE]: {
8732
8874
  win32: [
8733
- "npm install -g supabase",
8875
+ "scoop install supabase",
8876
+ "Or: winget install Supabase.CLI",
8734
8877
  "Or download from: https://github.com/supabase/cli/releases"
8735
8878
  ],
8736
8879
  darwin: [
8737
- "brew install supabase/tap/supabase",
8738
- "Or: npm install -g supabase"
8880
+ "brew install supabase/tap/supabase"
8739
8881
  ],
8740
8882
  linux: [
8741
- "npm install -g supabase",
8883
+ "brew install supabase/tap/supabase",
8742
8884
  "Or see: https://supabase.com/docs/guides/cli"
8743
8885
  ]
8744
8886
  },
@@ -8791,7 +8933,7 @@ function getCLIInstallInstructions(tool) {
8791
8933
  linux: ["npm install -g tsx"]
8792
8934
  }
8793
8935
  };
8794
- const platformInstructions = instructions[tool]?.[platform4] || instructions[tool]?.linux || [];
8936
+ const platformInstructions = instructions[tool]?.[platform6] || instructions[tool]?.linux || [];
8795
8937
  return platformInstructions.join("\n \u2022 ");
8796
8938
  }
8797
8939
  function formatCLIMissingError(tool, additionalContext) {
@@ -8806,9 +8948,9 @@ function formatCLIMissingError(tool, additionalContext) {
8806
8948
  M2.info(`
8807
8949
  ${additionalContext}`);
8808
8950
  }
8809
- const platform4 = process.platform;
8951
+ const platform6 = process.platform;
8810
8952
  M2.info("\nIf already installed, ensure it's in your PATH:");
8811
- if (platform4 === "win32") {
8953
+ if (platform6 === "win32") {
8812
8954
  M2.message(" \u2022 Check with: where " + tool);
8813
8955
  M2.message(
8814
8956
  " \u2022 Common location: C:\\Users\\<username>\\AppData\\Roaming\\npm\\"
@@ -8865,6 +9007,40 @@ var init_cli_tools = __esm({
8865
9007
  }
8866
9008
  });
8867
9009
 
9010
+ // packages/tooling/src/cli/setup/vercel-token.ts
9011
+ function readEnvVar(filePath, varName) {
9012
+ if (!pathExists(filePath)) return null;
9013
+ const content = readSync(filePath, { format: "text" });
9014
+ if (typeof content !== "string") return null;
9015
+ for (const line of content.split(/\r?\n/)) {
9016
+ const trimmed = line.trim();
9017
+ if (!trimmed || trimmed.startsWith("#")) continue;
9018
+ if (trimmed.startsWith(`${varName}=`)) {
9019
+ const val = trimmed.substring(`${varName}=`.length).trim();
9020
+ if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
9021
+ return val.slice(1, -1);
9022
+ }
9023
+ return val || null;
9024
+ }
9025
+ }
9026
+ return null;
9027
+ }
9028
+ function resolveVercelToken(appDir) {
9029
+ if (process.env.VERCEL_TOKEN) return process.env.VERCEL_TOKEN;
9030
+ const fromEnv = readEnvVar(joinPath(appDir, ".env"), "VERCEL_TOKEN");
9031
+ if (fromEnv) return fromEnv;
9032
+ const fromLocal = readEnvVar(joinPath(appDir, ".env.local"), "VERCEL_TOKEN");
9033
+ if (fromLocal) return fromLocal;
9034
+ return null;
9035
+ }
9036
+ var init_vercel_token = __esm({
9037
+ "packages/tooling/src/cli/setup/vercel-token.ts"() {
9038
+ "use strict";
9039
+ init_utils();
9040
+ init_pathResolver();
9041
+ }
9042
+ });
9043
+
8868
9044
  // node_modules/.bun/yaml@2.8.2/node_modules/yaml/dist/nodes/identity.js
8869
9045
  var require_identity = __commonJS({
8870
9046
  "node_modules/.bun/yaml@2.8.2/node_modules/yaml/dist/nodes/identity.js"(exports) {
@@ -16226,13 +16402,36 @@ var require_dist = __commonJS({
16226
16402
  }
16227
16403
  });
16228
16404
 
16405
+ // packages/tooling/src/utils/error-handling.ts
16406
+ function isError(error2) {
16407
+ return error2 instanceof Error;
16408
+ }
16409
+ function getErrorMessage(error2) {
16410
+ if (isError(error2)) {
16411
+ return error2.message;
16412
+ }
16413
+ if (typeof error2 === "string") {
16414
+ return error2;
16415
+ }
16416
+ if (error2 && typeof error2 === "object" && "message" in error2) {
16417
+ return String(error2.message);
16418
+ }
16419
+ return String(error2);
16420
+ }
16421
+ var init_error_handling = __esm({
16422
+ "packages/tooling/src/utils/error-handling.ts"() {
16423
+ "use strict";
16424
+ init_utils();
16425
+ }
16426
+ });
16427
+
16229
16428
  // packages/tooling/src/apps/sync-secrets.ts
16230
16429
  var sync_secrets_exports = {};
16231
16430
  __export(sync_secrets_exports, {
16232
16431
  default: () => sync_secrets_default,
16233
16432
  main: () => main5
16234
16433
  });
16235
- import { spawnSync as spawnSync8 } from "node:child_process";
16434
+ import { spawnSync as spawnSync9 } from "node:child_process";
16236
16435
  function parseEnvFile(filePath) {
16237
16436
  if (!pathExists(filePath)) {
16238
16437
  throw new DoNotDevError(
@@ -16425,7 +16624,7 @@ async function setFirebaseSecret(key, value, projectId, dryRun = false, cwd) {
16425
16624
  NODE_OPTIONS: ""
16426
16625
  // Clear to avoid conflicts
16427
16626
  };
16428
- const result = spawnSync8(firebaseCmd, args, {
16627
+ const result = spawnSync9(firebaseCmd, args, {
16429
16628
  input: value,
16430
16629
  encoding: "utf8",
16431
16630
  stdio: ["pipe", "pipe", "pipe"],
@@ -16521,7 +16720,7 @@ function setVercelSecret(key, value, projectId, dryRun = false) {
16521
16720
  if (projectId) {
16522
16721
  args.push("--project", projectId);
16523
16722
  }
16524
- const result = spawnSync8("vercel", args, {
16723
+ const result = spawnSync9("vercel", args, {
16525
16724
  input: value,
16526
16725
  encoding: "utf8",
16527
16726
  stdio: ["pipe", "inherit", "inherit"]
@@ -16550,7 +16749,7 @@ function setVercelSecret(key, value, projectId, dryRun = false) {
16550
16749
  }
16551
16750
  function detectGitHubRepo() {
16552
16751
  try {
16553
- const result = spawnSync8("git", ["remote", "get-url", "origin"], {
16752
+ const result = spawnSync9("git", ["remote", "get-url", "origin"], {
16554
16753
  encoding: "utf8",
16555
16754
  stdio: ["pipe", "pipe", "pipe"]
16556
16755
  });
@@ -16578,7 +16777,7 @@ function setGitHubSecret(key, value, repo, dryRun = false) {
16578
16777
  if (repo) {
16579
16778
  args.push("--repo", repo);
16580
16779
  }
16581
- const result = spawnSync8("gh", args, {
16780
+ const result = spawnSync9("gh", args, {
16582
16781
  input: value,
16583
16782
  encoding: "utf8",
16584
16783
  stdio: ["pipe", "pipe", "pipe"]
@@ -16752,12 +16951,12 @@ Examples:
16752
16951
  }
16753
16952
  return 0;
16754
16953
  }
16755
- const platform4 = config.platform || detectPlatform();
16954
+ const platform6 = config.platform || detectPlatform();
16756
16955
  log.info(`Reading secrets from: ${envFilePath}`);
16757
16956
  if (config.verbose) {
16758
16957
  log.debug(`Working directory: ${currentDir}`);
16759
16958
  log.debug(`Environment file: ${envFilePath}`);
16760
- log.debug(`Platform: ${platform4}`);
16959
+ log.debug(`Platform: ${platform6}`);
16761
16960
  log.debug(`Dry run mode: ${config.dryRun}`);
16762
16961
  }
16763
16962
  const secrets = parseEnvFile(envFilePath);
@@ -16766,7 +16965,7 @@ Examples:
16766
16965
  log.info("No secrets found in .env file");
16767
16966
  return 0;
16768
16967
  }
16769
- log.info(`Found ${secretKeys.length} secrets to sync to ${platform4}:`);
16968
+ log.info(`Found ${secretKeys.length} secrets to sync to ${platform6}:`);
16770
16969
  secretKeys.forEach((key) => {
16771
16970
  if (config.verbose) {
16772
16971
  const value = secrets[key];
@@ -16777,12 +16976,12 @@ Examples:
16777
16976
  log.info(` ${key}`);
16778
16977
  }
16779
16978
  });
16780
- const finalFirebaseProjectDir = platform4 === "firebase" ? firebaseProjectDir || findFirebaseProjectDir(config.envFile) : void 0;
16979
+ const finalFirebaseProjectDir = platform6 === "firebase" ? firebaseProjectDir || findFirebaseProjectDir(config.envFile) : void 0;
16781
16980
  if (config.verbose && finalFirebaseProjectDir) {
16782
16981
  log.debug(`Firebase project directory: ${finalFirebaseProjectDir}`);
16783
16982
  }
16784
16983
  for (const [key, value] of Object.entries(secrets)) {
16785
- if (platform4 === "firebase") {
16984
+ if (platform6 === "firebase") {
16786
16985
  await setFirebaseSecret(
16787
16986
  key,
16788
16987
  value,
@@ -16790,18 +16989,18 @@ Examples:
16790
16989
  config.dryRun,
16791
16990
  finalFirebaseProjectDir
16792
16991
  );
16793
- } else if (platform4 === "vercel") {
16992
+ } else if (platform6 === "vercel") {
16794
16993
  setVercelSecret(key, value, config.vercelProjectId, config.dryRun);
16795
16994
  }
16796
16995
  }
16797
16996
  if (config.dryRun) {
16798
16997
  log.success(
16799
16998
  `
16800
- Dry run completed. No secrets were actually set to ${platform4}.`
16999
+ Dry run completed. No secrets were actually set to ${platform6}.`
16801
17000
  );
16802
17001
  } else {
16803
17002
  log.success(`
16804
- All secrets synced successfully to ${platform4}!`);
17003
+ All secrets synced successfully to ${platform6}!`);
16805
17004
  }
16806
17005
  return 0;
16807
17006
  } catch (err) {
@@ -16830,7 +17029,7 @@ var deploy_supabase_functions_exports = {};
16830
17029
  __export(deploy_supabase_functions_exports, {
16831
17030
  deploySupabaseFunctions: () => deploySupabaseFunctions
16832
17031
  });
16833
- import { execSync as execSync8 } from "node:child_process";
17032
+ import { execSync as execSync9 } from "node:child_process";
16834
17033
  async function deploySupabaseFunctions(appDir, config) {
16835
17034
  const supabaseDir = joinPath(appDir, "supabase");
16836
17035
  const functionsDir = joinPath(supabaseDir, "functions");
@@ -16859,7 +17058,7 @@ async function deploySupabaseFunctions(appDir, config) {
16859
17058
  for (const functionName of functionDirs) {
16860
17059
  s.start(`Deploying Edge Function: ${functionName}...`);
16861
17060
  try {
16862
- execSync8(`supabase functions deploy ${functionName}`, {
17061
+ execSync9(`supabase functions deploy ${functionName}`, {
16863
17062
  cwd: supabaseDir,
16864
17063
  stdio: config.verbose ? "inherit" : "pipe",
16865
17064
  env: {
@@ -16898,167 +17097,42 @@ init_utils();
16898
17097
 
16899
17098
  // packages/tooling/src/apps/build.ts
16900
17099
  init_utils();
16901
- import { spawnSync, execSync } from "node:child_process";
16902
-
16903
- // packages/tooling/src/utils/app-selector.ts
16904
- init_utils();
16905
- init_dist2();
16906
-
16907
- // packages/tooling/src/utils/app-detection.ts
16908
- init_utils();
17100
+ init_app_selector();
17101
+ init_cli_input();
17102
+ init_cli_output();
17103
+ init_errors();
16909
17104
  init_pathResolver();
16910
17105
  init_typed_file_operations();
16911
- function detectApps(projectRoot) {
16912
- const apps = [];
16913
- const appsDir = joinPath(projectRoot, "apps");
16914
- const appsDirStat = statSync2(appsDir);
16915
- if (pathExists(appsDir) && appsDirStat?.isDirectory()) {
16916
- const appDirs = readdirSync2(appsDir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name).sort();
16917
- for (const appDir of appDirs) {
16918
- const appPath = joinPath(appsDir, appDir);
16919
- const packageJsonPath = joinPath(appPath, "package.json");
16920
- if (pathExists(packageJsonPath)) {
16921
- const appInfo = analyzeApp(appPath, appDir);
16922
- if (appInfo) {
16923
- apps.push(appInfo);
16924
- }
16925
- }
16926
- }
16927
- } else {
16928
- const packageJsonPath = joinPath(projectRoot, "package.json");
16929
- if (pathExists(packageJsonPath)) {
16930
- try {
16931
- const packageJson = readPackageJson(packageJsonPath);
16932
- if (packageJson && (packageJson.dependencies?.vite || packageJson.devDependencies?.vite || packageJson.dependencies?.next || packageJson.devDependencies?.next)) {
16933
- const pathParts = projectRoot.split(/[/\\]/);
16934
- const appName = pathParts[pathParts.length - 1] || "app";
16935
- const appInfo = analyzeApp(projectRoot, appName);
16936
- if (appInfo) {
16937
- apps.push(appInfo);
16938
- }
16939
- }
16940
- } catch {
16941
- }
16942
- }
16943
- }
16944
- return apps;
16945
- }
16946
- function analyzeApp(appPath, appName) {
16947
- const packageJsonPath = joinPath(appPath, "package.json");
16948
- if (!pathExists(packageJsonPath)) {
16949
- return null;
17106
+ import { spawnSync, execSync } from "node:child_process";
17107
+ function validateFirebaseJson(appDir) {
17108
+ const firebaseJsonPath = joinPath(appDir, "firebase.json");
17109
+ if (!pathExists(firebaseJsonPath)) {
17110
+ return {
17111
+ valid: false,
17112
+ hasHosting: false,
17113
+ hasFunctions: false
17114
+ };
16950
17115
  }
16951
- let packageJson = null;
16952
17116
  try {
16953
- packageJson = readPackageJson(packageJsonPath);
16954
- if (!packageJson.name) {
16955
- return null;
17117
+ const config = readFirebaseJson(firebaseJsonPath);
17118
+ if (!config.projectId) {
17119
+ return {
17120
+ valid: false,
17121
+ hasHosting: false,
17122
+ hasFunctions: false
17123
+ };
16956
17124
  }
17125
+ return {
17126
+ valid: true,
17127
+ hasHosting: !!config.hosting,
17128
+ hasFunctions: !!config.functions && Array.isArray(config.functions)
17129
+ };
16957
17130
  } catch {
16958
- return null;
16959
- }
16960
- let framework = "unknown";
16961
- if (pathExists(joinPath(appPath, "next.config.js")) || pathExists(joinPath(appPath, "next.config.ts")) || pathExists(joinPath(appPath, "next.config.mjs")) || packageJson.dependencies?.next || packageJson.devDependencies?.next) {
16962
- framework = "nextjs";
16963
- } else if (pathExists(joinPath(appPath, "vite.config.ts")) || pathExists(joinPath(appPath, "vite.config.js")) || pathExists(joinPath(appPath, "vite.config.mjs")) || packageJson.dependencies?.vite || packageJson.devDependencies?.vite) {
16964
- framework = "vite";
16965
- }
16966
- const functionsPath = joinPath(appPath, "functions");
16967
- const functionsStat = statSync2(functionsPath);
16968
- const hasFunctions = pathExists(functionsPath) && (functionsStat?.isDirectory() ?? false);
16969
- let platform4;
16970
- if (hasFunctions) {
16971
- const firebaseJsonPath = joinPath(functionsPath, "firebase.json");
16972
- const vercelJsonPath = joinPath(appPath, "vercel.json");
16973
- if (pathExists(firebaseJsonPath)) {
16974
- platform4 = "firebase";
16975
- } else if (pathExists(vercelJsonPath)) {
16976
- platform4 = "vercel";
16977
- }
16978
- }
16979
- return {
16980
- name: appName,
16981
- packageName: packageJson.name || appName,
16982
- path: appPath,
16983
- packageJsonPath,
16984
- framework,
16985
- hasFunctions,
16986
- functionsPath: hasFunctions ? functionsPath : void 0,
16987
- platform: platform4
16988
- };
16989
- }
16990
-
16991
- // packages/tooling/src/utils/app-selector.ts
16992
- init_cli_output();
16993
- async function selectApp(projectRoot, appName) {
16994
- const apps = detectApps(projectRoot);
16995
- if (apps.length === 0) {
16996
- log.error("No apps found in this project.");
16997
- log.info('Run "dndev init" to create a new project.');
16998
- return null;
16999
- }
17000
- if (appName) {
17001
- const app = apps.find((a) => a.name === appName);
17002
- if (!app) {
17003
- log.warn(`App "${appName}" not found.`);
17004
- log.info("Available apps:");
17005
- apps.forEach((a) => log.info(` - ${a.name}`));
17006
- log.info("Please select from the list:");
17007
- } else {
17008
- return app;
17009
- }
17010
- }
17011
- if (apps.length === 1) {
17012
- return apps[0] || null;
17013
- }
17014
- const selected = await ve({
17015
- message: "Select app:",
17016
- options: apps.map((app) => ({
17017
- value: app.name,
17018
- label: `${app.name} (${app.framework === "nextjs" ? "Next.js" : "Vite"}${app.hasFunctions ? ` + ${app.platform || "functions"}` : ""})`
17019
- }))
17020
- });
17021
- if (typeof selected !== "string") {
17022
- return null;
17023
- }
17024
- return apps.find((a) => a.name === selected) || null;
17025
- }
17026
-
17027
- // packages/tooling/src/apps/build.ts
17028
- init_cli_input();
17029
- init_cli_output();
17030
- init_errors();
17031
- init_pathResolver();
17032
- init_typed_file_operations();
17033
- function validateFirebaseJson(appDir) {
17034
- const firebaseJsonPath = joinPath(appDir, "firebase.json");
17035
- if (!pathExists(firebaseJsonPath)) {
17036
- return {
17037
- valid: false,
17038
- hasHosting: false,
17039
- hasFunctions: false
17040
- };
17041
- }
17042
- try {
17043
- const config = readFirebaseJson(firebaseJsonPath);
17044
- if (!config.projectId) {
17045
- return {
17046
- valid: false,
17047
- hasHosting: false,
17048
- hasFunctions: false
17049
- };
17050
- }
17051
- return {
17052
- valid: true,
17053
- hasHosting: !!config.hosting,
17054
- hasFunctions: !!config.functions && Array.isArray(config.functions)
17055
- };
17056
- } catch {
17057
- return {
17058
- valid: false,
17059
- hasHosting: false,
17060
- hasFunctions: false
17061
- };
17131
+ return {
17132
+ valid: false,
17133
+ hasHosting: false,
17134
+ hasFunctions: false
17135
+ };
17062
17136
  }
17063
17137
  }
17064
17138
  function parseBuildArgs(args) {
@@ -17220,11 +17294,12 @@ async function main() {
17220
17294
 
17221
17295
  // packages/tooling/src/apps/dev.ts
17222
17296
  init_utils();
17223
- import { spawn, execSync as execSync3 } from "node:child_process";
17224
- import { platform } from "node:os";
17297
+ init_app_selector();
17225
17298
  init_cli_output();
17226
17299
  init_errors();
17227
17300
  init_pathResolver();
17301
+ import { spawn, execSync as execSync3 } from "node:child_process";
17302
+ import { platform } from "node:os";
17228
17303
 
17229
17304
  // packages/tooling/src/utils/spawn-utils.ts
17230
17305
  init_utils();
@@ -17470,18 +17545,23 @@ async function main2() {
17470
17545
 
17471
17546
  // packages/tooling/src/apps/emu.ts
17472
17547
  init_utils();
17548
+ init_app_selector();
17549
+ init_cli_output();
17550
+ init_errors();
17551
+ init_pathResolver();
17552
+
17553
+ // packages/tooling/src/apps/emu-firebase.ts
17554
+ init_utils();
17555
+ init_cli_input();
17556
+ init_cli_output();
17557
+ init_pathResolver();
17558
+ init_typed_file_operations();
17473
17559
  import {
17474
17560
  spawn as spawn2,
17475
17561
  spawnSync as spawnSync3,
17476
17562
  execSync as execSync4
17477
17563
  } from "node:child_process";
17478
17564
  import { platform as platform2 } from "node:os";
17479
- init_cli_input();
17480
- init_cli_output();
17481
- init_errors();
17482
- init_pathResolver();
17483
- init_pathResolver();
17484
- init_typed_file_operations();
17485
17565
  function discoverFirebaseProjectId(appPath) {
17486
17566
  const firebasercPath = joinPath(appPath, ".firebaserc");
17487
17567
  if (pathExists(firebasercPath)) {
@@ -17513,7 +17593,7 @@ function killPorts(ports) {
17513
17593
  pids.add(match[1]);
17514
17594
  }
17515
17595
  }
17516
- for (const pid of pids) {
17596
+ for (const pid of Array.from(pids)) {
17517
17597
  try {
17518
17598
  execSync4(`taskkill /F /PID ${pid}`, { stdio: "ignore" });
17519
17599
  log.debug(`Killed process ${pid} on port ${port}`);
@@ -17575,9 +17655,9 @@ function loadEnvFile(filePath) {
17575
17655
  } catch {
17576
17656
  }
17577
17657
  }
17578
- async function main3(options) {
17579
- const args = process.argv.slice(2);
17580
- const debug = options?.debug ?? false;
17658
+ async function startFirebase(app, projectRoot, options = {}) {
17659
+ const debug = options.debug ?? false;
17660
+ let selectedServices = options.selectedServices ?? [];
17581
17661
  const serviceChoices = [
17582
17662
  {
17583
17663
  title: "Auth",
@@ -17600,47 +17680,6 @@ async function main3(options) {
17600
17680
  hint: "Stripe webhook forwarding (for payment testing)"
17601
17681
  }
17602
17682
  ];
17603
- let selectedServices = [];
17604
- const appName = args.find(
17605
- (arg) => arg !== "emu" && arg !== "--debug" && !arg.startsWith("--")
17606
- );
17607
- if (options?.services) {
17608
- selectedServices = options.services.split(",").map((s) => s.trim());
17609
- } else {
17610
- const servicesArg = args.find((arg) => arg.startsWith("--services="));
17611
- if (servicesArg) {
17612
- const servicesList = servicesArg.split("=")[1];
17613
- if (servicesList) {
17614
- selectedServices = servicesList.split(",").map((s) => s.trim());
17615
- }
17616
- } else {
17617
- if (options?.auth || args.includes("--auth"))
17618
- selectedServices.push("auth");
17619
- if (args.includes("--functions")) selectedServices.push("functions");
17620
- if (options?.firestore || args.includes("--firestore"))
17621
- selectedServices.push("firestore");
17622
- if (options?.stripe || args.includes("--stripe"))
17623
- selectedServices.push("stripe");
17624
- }
17625
- }
17626
- const validServices = serviceChoices.map((c) => c.value);
17627
- selectedServices = selectedServices.filter((s) => validServices.includes(s));
17628
- const projectRoot = getRepoRoot();
17629
- if (!projectRoot) {
17630
- throw new DoNotDevError(
17631
- "Could not determine repository root",
17632
- "path-resolution-error"
17633
- );
17634
- }
17635
- const app = await selectApp(projectRoot, appName);
17636
- if (!app) {
17637
- return 1;
17638
- }
17639
- if (!app.hasFunctions || app.platform !== "firebase") {
17640
- log.error(`App "${app.name}" does not have Firebase functions.`);
17641
- log.error('Use "dndev dev" for apps without functions.\n');
17642
- return 1;
17643
- }
17644
17683
  log.info(`Starting Firebase emulators + dev server for ${app.name}...
17645
17684
  `);
17646
17685
  if (selectedServices.length === 0) {
@@ -17772,7 +17811,7 @@ FIREBASE_AUTH_EMULATOR_HOST=${authEmulatorHost}
17772
17811
  const stripeWebhookUrl = `http://localhost:5001/${projectId}/${region}/stripeWebhook`;
17773
17812
  concurrentlyArgs.push(`stripe listen --forward-to ${stripeWebhookUrl}`);
17774
17813
  log.info("");
17775
- log.info("\u2705 Stripe webhook forwarding will start automatically");
17814
+ log.info("Stripe webhook forwarding will start automatically");
17776
17815
  log.info(` Webhook URL: ${stripeWebhookUrl}`);
17777
17816
  log.info(" To trigger test events, run in another terminal:");
17778
17817
  log.info(" stripe trigger checkout.session.completed");
@@ -17794,9 +17833,7 @@ FIREBASE_AUTH_EMULATOR_HOST=${authEmulatorHost}
17794
17833
  );
17795
17834
  const SIGNAL_EXIT_CODES3 = {
17796
17835
  SIGINT: 130,
17797
- // 128 + 2
17798
17836
  SIGTERM: 143
17799
- // 128 + 15
17800
17837
  };
17801
17838
  const cleanup = (signal) => {
17802
17839
  if (!childProcess.pid) return;
@@ -17846,23 +17883,234 @@ FIREBASE_AUTH_EMULATOR_HOST=${authEmulatorHost}
17846
17883
  });
17847
17884
  childProcess.on("error", (error2) => {
17848
17885
  handlers.forEach((h2) => h2());
17849
- log.error(`Failed to start emulators: ${error2.message}`);
17886
+ log.error(`Failed to start Firebase emulators: ${error2.message}`);
17850
17887
  resolve4(1);
17851
17888
  });
17852
17889
  });
17853
17890
  }
17854
17891
 
17855
- // packages/tooling/src/apps/preview.ts
17892
+ // packages/tooling/src/apps/emu-supabase.ts
17856
17893
  init_utils();
17894
+ init_cli_output();
17857
17895
  import {
17858
17896
  spawn as spawn3,
17859
- spawnSync as spawnSync4,
17860
17897
  execSync as execSync5
17861
17898
  } from "node:child_process";
17862
17899
  import { platform as platform3 } from "node:os";
17900
+ function isDockerRunning() {
17901
+ try {
17902
+ execSync5("docker info", { stdio: "pipe", timeout: 5e3 });
17903
+ return true;
17904
+ } catch {
17905
+ return false;
17906
+ }
17907
+ }
17908
+ async function startSupabase(app, _projectRoot) {
17909
+ log.info(`Starting Supabase local stack + dev server for ${app.name}...
17910
+ `);
17911
+ if (!isDockerRunning()) {
17912
+ log.error("Docker is not running. Supabase local dev requires Docker.");
17913
+ log.error("Start Docker Desktop and try again.\n");
17914
+ return 1;
17915
+ }
17916
+ try {
17917
+ execSync5("supabase --version", { stdio: "pipe" });
17918
+ } catch {
17919
+ log.error("Supabase CLI not found. Install it:");
17920
+ log.error(" npm install -g supabase\n");
17921
+ return 1;
17922
+ }
17923
+ const isWindows = platform3() === "win32";
17924
+ const concurrentlyArgs = [
17925
+ "--kill-others-on-fail",
17926
+ "--prefix-colors",
17927
+ "cyan,green",
17928
+ "--prefix",
17929
+ "[{name}]",
17930
+ "--names",
17931
+ "supabase,dev",
17932
+ "supabase start",
17933
+ `bunx turbo dev --filter=${app.packageName}`
17934
+ ];
17935
+ const childProcess = spawn3(
17936
+ "bunx",
17937
+ ["concurrently", ...concurrentlyArgs],
17938
+ {
17939
+ stdio: "inherit",
17940
+ cwd: app.path,
17941
+ shell: isWindows
17942
+ }
17943
+ );
17944
+ const cleanup = () => {
17945
+ if (!childProcess.pid) return;
17946
+ if (isWindows) {
17947
+ try {
17948
+ execSync5(`taskkill /F /T /PID ${childProcess.pid}`, { stdio: "ignore", timeout: 2e3 });
17949
+ } catch {
17950
+ }
17951
+ } else {
17952
+ try {
17953
+ process.kill(childProcess.pid, "SIGTERM");
17954
+ } catch {
17955
+ }
17956
+ }
17957
+ };
17958
+ process.on("SIGINT", () => {
17959
+ cleanup();
17960
+ process.exit(130);
17961
+ });
17962
+ process.on("SIGTERM", () => {
17963
+ cleanup();
17964
+ process.exit(143);
17965
+ });
17966
+ return new Promise((resolve4) => {
17967
+ childProcess.on("exit", (code) => resolve4(code ?? 0));
17968
+ childProcess.on("error", (error2) => {
17969
+ log.error(`Failed to start Supabase local dev: ${error2.message}`);
17970
+ resolve4(1);
17971
+ });
17972
+ });
17973
+ }
17974
+
17975
+ // packages/tooling/src/apps/emu-vercel.ts
17976
+ init_utils();
17977
+ init_cli_output();
17978
+ import {
17979
+ spawn as spawn4,
17980
+ execSync as execSync6
17981
+ } from "node:child_process";
17982
+ import { platform as platform4 } from "node:os";
17983
+ async function startVercel(app, _projectRoot) {
17984
+ log.info(`Starting Vercel dev server for ${app.name}...
17985
+ `);
17986
+ try {
17987
+ execSync6("vercel --version", { stdio: "pipe" });
17988
+ } catch {
17989
+ log.error("Vercel CLI not found. Install it:");
17990
+ log.error(" npm install -g vercel\n");
17991
+ return 1;
17992
+ }
17993
+ const isWindows = platform4() === "win32";
17994
+ const childProcess = spawn4(
17995
+ "vercel",
17996
+ ["dev"],
17997
+ {
17998
+ stdio: "inherit",
17999
+ cwd: app.path,
18000
+ shell: isWindows
18001
+ }
18002
+ );
18003
+ const cleanup = () => {
18004
+ if (!childProcess.pid) return;
18005
+ if (isWindows) {
18006
+ try {
18007
+ execSync6(`taskkill /F /T /PID ${childProcess.pid}`, { stdio: "ignore", timeout: 2e3 });
18008
+ } catch {
18009
+ }
18010
+ } else {
18011
+ try {
18012
+ process.kill(childProcess.pid, "SIGTERM");
18013
+ } catch {
18014
+ }
18015
+ }
18016
+ };
18017
+ process.on("SIGINT", () => {
18018
+ cleanup();
18019
+ process.exit(130);
18020
+ });
18021
+ process.on("SIGTERM", () => {
18022
+ cleanup();
18023
+ process.exit(143);
18024
+ });
18025
+ return new Promise((resolve4) => {
18026
+ childProcess.on("exit", (code) => resolve4(code ?? 0));
18027
+ childProcess.on("error", (error2) => {
18028
+ log.error(`Failed to start Vercel dev: ${error2.message}`);
18029
+ resolve4(1);
18030
+ });
18031
+ });
18032
+ }
18033
+
18034
+ // packages/tooling/src/apps/emu.ts
18035
+ async function main3(options) {
18036
+ const args = process.argv.slice(2);
18037
+ const projectRoot = getRepoRoot();
18038
+ if (!projectRoot) {
18039
+ throw new DoNotDevError(
18040
+ "Could not determine repository root",
18041
+ "path-resolution-error"
18042
+ );
18043
+ }
18044
+ const appName = args.find(
18045
+ (arg) => arg !== "emu" && arg !== "--debug" && !arg.startsWith("--")
18046
+ );
18047
+ const app = await selectApp(projectRoot, appName);
18048
+ if (!app) {
18049
+ return 1;
18050
+ }
18051
+ switch (app.platform) {
18052
+ case "firebase": {
18053
+ if (!app.hasFunctions) {
18054
+ log.error(`App "${app.name}" has Firebase config but no functions directory.`);
18055
+ log.error('Use "dndev dev" for apps without backend functions.\n');
18056
+ return 1;
18057
+ }
18058
+ const selectedServices = parseFirebaseServices(args, options);
18059
+ return startFirebase(app, projectRoot, {
18060
+ debug: options?.debug ?? false,
18061
+ selectedServices
18062
+ });
18063
+ }
18064
+ case "supabase":
18065
+ return startSupabase(app, projectRoot);
18066
+ case "vercel":
18067
+ return startVercel(app, projectRoot);
18068
+ default:
18069
+ log.error(`App "${app.name}" does not have a detected backend platform.`);
18070
+ log.error(
18071
+ "Expected: firebase.json (Firebase), supabase/config.toml or @donotdev/supabase (Supabase), or vercel.json (Vercel)."
18072
+ );
18073
+ log.error('Use "dndev dev" for apps without a backend.\n');
18074
+ return 1;
18075
+ }
18076
+ }
18077
+ function parseFirebaseServices(args, options) {
18078
+ const validServices = ["auth", "functions", "firestore", "stripe"];
18079
+ let selectedServices = [];
18080
+ if (options?.services) {
18081
+ selectedServices = options.services.split(",").map((s) => s.trim());
18082
+ } else {
18083
+ const servicesArg = args.find((arg) => arg.startsWith("--services="));
18084
+ if (servicesArg) {
18085
+ const servicesList = servicesArg.split("=")[1];
18086
+ if (servicesList) {
18087
+ selectedServices = servicesList.split(",").map((s) => s.trim());
18088
+ }
18089
+ } else {
18090
+ if (options?.auth || args.includes("--auth"))
18091
+ selectedServices.push("auth");
18092
+ if (args.includes("--functions")) selectedServices.push("functions");
18093
+ if (options?.firestore || args.includes("--firestore"))
18094
+ selectedServices.push("firestore");
18095
+ if (options?.stripe || args.includes("--stripe"))
18096
+ selectedServices.push("stripe");
18097
+ }
18098
+ }
18099
+ return selectedServices.filter((s) => validServices.includes(s));
18100
+ }
18101
+
18102
+ // packages/tooling/src/apps/preview.ts
18103
+ init_utils();
18104
+ init_app_selector();
17863
18105
  init_cli_output();
17864
18106
  init_errors();
17865
18107
  init_pathResolver();
18108
+ import {
18109
+ spawn as spawn5,
18110
+ spawnSync as spawnSync4,
18111
+ execSync as execSync7
18112
+ } from "node:child_process";
18113
+ import { platform as platform5 } from "node:os";
17866
18114
  var SIGNAL_EXIT_CODES2 = {
17867
18115
  SIGINT: 130,
17868
18116
  // 128 + 2
@@ -17905,8 +18153,8 @@ async function main4() {
17905
18153
  log.info(`Previewing ${app.name}...
17906
18154
  `);
17907
18155
  const turboArgs = ["preview", "--filter", app.packageName];
17908
- const isWindows = platform3() === "win32";
17909
- const childProcess = spawn3("bunx", ["turbo", ...turboArgs], {
18156
+ const isWindows = platform5() === "win32";
18157
+ const childProcess = spawn5("bunx", ["turbo", ...turboArgs], {
17910
18158
  stdio: "inherit",
17911
18159
  env: createAppEnv(app.path),
17912
18160
  cwd: projectRoot,
@@ -17917,7 +18165,7 @@ async function main4() {
17917
18165
  log.debug(`Received ${signal}, cleaning up processes...`);
17918
18166
  if (isWindows) {
17919
18167
  try {
17920
- execSync5(`taskkill /F /T /PID ${childProcess.pid}`, {
18168
+ execSync7(`taskkill /F /T /PID ${childProcess.pid}`, {
17921
18169
  stdio: "ignore",
17922
18170
  timeout: 2e3
17923
18171
  });
@@ -17929,7 +18177,7 @@ async function main4() {
17929
18177
  } catch {
17930
18178
  }
17931
18179
  try {
17932
- execSync5(`pkill -P ${childProcess.pid}`, {
18180
+ execSync7(`pkill -P ${childProcess.pid}`, {
17933
18181
  stdio: "ignore",
17934
18182
  timeout: 1e3
17935
18183
  });
@@ -17968,7 +18216,7 @@ async function main4() {
17968
18216
 
17969
18217
  // packages/tooling/src/apps/deploy.ts
17970
18218
  init_utils();
17971
- import { execSync as execSync9 } from "node:child_process";
18219
+ import { execSync as execSync10 } from "node:child_process";
17972
18220
 
17973
18221
  // packages/tooling/src/apps/deploy-frontend.ts
17974
18222
  init_utils();
@@ -18269,15 +18517,10 @@ function getMatrixPath(mode) {
18269
18517
  }
18270
18518
  const executionMode = mode || detectExecutionMode();
18271
18519
  if (executionMode === "development") {
18272
- const repoRoot = getRepoRoot();
18273
- if (repoRoot) {
18274
- const devPath = normalizePath(
18275
- repoRoot,
18276
- "packages/cli/dependencies-matrix.json"
18277
- );
18278
- if (pathExists(devPath)) {
18279
- return devPath;
18280
- }
18520
+ const templatesRoot = getTemplatesRoot();
18521
+ const devPath = normalizePath(templatesRoot, "..", "dependencies-matrix.json");
18522
+ if (pathExists(devPath)) {
18523
+ return devPath;
18281
18524
  }
18282
18525
  }
18283
18526
  return null;
@@ -18292,8 +18535,8 @@ function getCliVersion(mode) {
18292
18535
  }
18293
18536
  const executionMode = mode || detectExecutionMode();
18294
18537
  if (executionMode === "development") {
18295
- const repoRoot = getRepoRoot();
18296
- const cliPackageJson = joinPath(repoRoot, "packages/cli/package.json");
18538
+ const templatesRoot = getTemplatesRoot();
18539
+ const cliPackageJson = normalizePath(templatesRoot, "..", "package.json");
18297
18540
  if (pathExists(cliPackageJson)) {
18298
18541
  const pkg = readSync(cliPackageJson, { format: "json" });
18299
18542
  return String(pkg?.version || "0.0.0");
@@ -18470,15 +18713,31 @@ async function deployFrontend(appDir, serviceAccountPath, projectId, config) {
18470
18713
 
18471
18714
  // packages/tooling/src/apps/deploy-vercel-frontend.ts
18472
18715
  init_utils();
18473
- import { execSync as execSync6 } from "node:child_process";
18716
+ init_cli_output();
18717
+ init_vercel_token();
18718
+ import { spawnSync as spawnSync6 } from "node:child_process";
18474
18719
  async function deployVercelFrontend(appDir, _config) {
18475
18720
  const s = Y2();
18476
18721
  s.start("Deploying frontend to Vercel...");
18722
+ const token = resolveVercelToken(appDir);
18723
+ if (token) {
18724
+ log.debug("Using VERCEL_TOKEN from .env (token-based auth)");
18725
+ }
18726
+ const args = ["vercel", "--prod", "--yes"];
18727
+ if (token) {
18728
+ args.push("--token", token);
18729
+ }
18477
18730
  try {
18478
- execSync6("npx vercel --prod --yes", {
18731
+ const result = spawnSync6("bunx", args, {
18479
18732
  cwd: appDir,
18480
- stdio: "inherit"
18733
+ stdio: "pipe",
18734
+ encoding: "utf-8"
18481
18735
  });
18736
+ if (result.status !== 0) {
18737
+ s.stop("Vercel deployment failed");
18738
+ const errOutput = result.stderr?.trim();
18739
+ throw new Error(errOutput || `Vercel deploy exited with code ${result.status}`);
18740
+ }
18482
18741
  s.stop("Frontend deployed to Vercel");
18483
18742
  } catch (err) {
18484
18743
  s.stop("Vercel deployment failed");
@@ -18489,7 +18748,7 @@ async function deployVercelFrontend(appDir, _config) {
18489
18748
  // packages/tooling/src/apps/deploy-functions.ts
18490
18749
  init_utils();
18491
18750
  var import_yaml = __toESM(require_dist(), 1);
18492
- import { execSync as execSync7, spawnSync as spawnSync6 } from "node:child_process";
18751
+ import { execSync as execSync8, spawnSync as spawnSync7 } from "node:child_process";
18493
18752
  init_pathResolver();
18494
18753
  init_cli_tools();
18495
18754
  init_typed_file_operations();
@@ -18658,7 +18917,7 @@ function updateCloudRunIAM(functionNames, projectId, region, serviceAccountPath,
18658
18917
  let failCount = 0;
18659
18918
  for (const funcName of functionNames) {
18660
18919
  try {
18661
- const result = spawnSync6(
18920
+ const result = spawnSync7(
18662
18921
  "gcloud",
18663
18922
  [
18664
18923
  "run",
@@ -18776,7 +19035,7 @@ async function autoSyncSecrets(functionsDir, projectId, config) {
18776
19035
  let failCount = 0;
18777
19036
  for (const { key, value } of secrets) {
18778
19037
  try {
18779
- const result = spawnSync6(
19038
+ const result = spawnSync7(
18780
19039
  firebaseCmd,
18781
19040
  ["functions:secrets:set", key, "--project", projectId],
18782
19041
  {
@@ -18858,7 +19117,7 @@ To fix this, run:
18858
19117
  const s2 = Y2();
18859
19118
  s2.start("Building functions...");
18860
19119
  try {
18861
- execSync7("bun run build", {
19120
+ execSync8("bun run build", {
18862
19121
  cwd: functionsDir,
18863
19122
  stdio: config.verbose ? "inherit" : "pipe"
18864
19123
  });
@@ -18958,29 +19217,10 @@ async function deployRules(appDir, serviceAccountPath, projectId, config, option
18958
19217
 
18959
19218
  // packages/tooling/src/apps/deploy-utils.ts
18960
19219
  init_utils();
18961
- import { spawnSync as spawnSync7 } from "node:child_process";
19220
+ import { spawnSync as spawnSync8 } from "node:child_process";
18962
19221
  init_pathResolver();
18963
19222
  init_typed_file_operations();
18964
-
18965
- // packages/tooling/src/utils/error-handling.ts
18966
- init_utils();
18967
- function isError(error2) {
18968
- return error2 instanceof Error;
18969
- }
18970
- function getErrorMessage(error2) {
18971
- if (isError(error2)) {
18972
- return error2.message;
18973
- }
18974
- if (typeof error2 === "string") {
18975
- return error2;
18976
- }
18977
- if (error2 && typeof error2 === "object" && "message" in error2) {
18978
- return String(error2.message);
18979
- }
18980
- return String(error2);
18981
- }
18982
-
18983
- // packages/tooling/src/apps/deploy-utils.ts
19223
+ init_error_handling();
18984
19224
  function detectAvailableApps() {
18985
19225
  const currentDir = process.cwd();
18986
19226
  const appsDir = joinPath(currentDir, "apps");
@@ -19165,7 +19405,7 @@ How to fix:
19165
19405
  if (shouldOpen) {
19166
19406
  try {
19167
19407
  const openCommand = process.platform === "win32" ? "start" : process.platform === "darwin" ? "open" : "xdg-open";
19168
- spawnSync7(openCommand, [consoleUrl], { shell: true });
19408
+ spawnSync8(openCommand, [consoleUrl], { shell: true });
19169
19409
  log.success("Opening Firebase Console...");
19170
19410
  } catch {
19171
19411
  log.warn("Could not open browser. Please open the URL manually.");
@@ -19236,7 +19476,7 @@ function detectProvider(appDir) {
19236
19476
  if (hasFirebase) {
19237
19477
  try {
19238
19478
  const config = readSync(firebaseJsonPath, { format: "json" });
19239
- if (config && typeof config === "object" && "projectId" in config) {
19479
+ if (config && typeof config === "object") {
19240
19480
  const firebaseConfigObj = config;
19241
19481
  firebaseConfig = {
19242
19482
  projectId: firebaseConfigObj.projectId,
@@ -19245,8 +19485,6 @@ function detectProvider(appDir) {
19245
19485
  hasFirestoreRules: !!firebaseConfigObj.firestore?.rules,
19246
19486
  hasStorageRules: !!firebaseConfigObj.storage?.rules
19247
19487
  };
19248
- } else {
19249
- throw new Error("Invalid firebase.json structure");
19250
19488
  }
19251
19489
  } catch {
19252
19490
  firebaseConfig = {
@@ -19554,7 +19792,7 @@ async function main6(options = {}) {
19554
19792
  isStaging ? "Building (staging mode)..." : "Building application..."
19555
19793
  );
19556
19794
  try {
19557
- execSync9(buildCmd, {
19795
+ execSync10(buildCmd, {
19558
19796
  cwd: appDir,
19559
19797
  stdio: "inherit",
19560
19798
  env: {
@@ -19799,10 +20037,10 @@ function generatePackageJson(templateName, mode, options = {}) {
19799
20037
  }
19800
20038
  if (templateName.includes("functions")) {
19801
20039
  result.main = "lib/index.js";
19802
- result.engines = { node: "20" };
20040
+ result.engines = { node: "22" };
19803
20041
  if (options.appName) {
19804
- const platform4 = templateName.includes("vercel") ? "Vercel" : "Firebase";
19805
- result.description = `${options.appName} ${platform4} Functions`;
20042
+ const platform6 = templateName.includes("vercel") ? "Vercel" : "Firebase";
20043
+ result.description = `${options.appName} ${platform6} Functions`;
19806
20044
  }
19807
20045
  if (mode === "development") {
19808
20046
  const workspacePkgs = [
@@ -19846,15 +20084,15 @@ init_utils();
19846
20084
  var MATRIX = [
19847
20085
  { builder: "vite", backend: "none", baseTemplate: "app-vite", overlay: null, deployConfig: null, functionsTemplate: null },
19848
20086
  { builder: "vite", backend: "firebase", baseTemplate: "app-vite", overlay: "overlay-firebase", deployConfig: "firebase", functionsTemplate: "functions-firebase" },
19849
- { builder: "vite", backend: "supabase", baseTemplate: "app-vite", overlay: "overlay-supabase", deployConfig: "vercel-supabase", functionsTemplate: null },
20087
+ { builder: "vite", backend: "supabase", baseTemplate: "app-vite", overlay: "overlay-supabase", deployConfig: "vercel-supabase", functionsTemplate: "functions-supabase" },
19850
20088
  { builder: "vite", backend: "vercel", baseTemplate: "app-vite", overlay: "overlay-vercel", deployConfig: "vercel-vercel", functionsTemplate: "functions-vercel" },
19851
20089
  { builder: "nextjs", backend: "none", baseTemplate: "app-next", overlay: null, deployConfig: null, functionsTemplate: null },
19852
20090
  { builder: "nextjs", backend: "firebase", baseTemplate: "app-next", overlay: "overlay-firebase", deployConfig: "firebase", functionsTemplate: "functions-firebase" },
19853
- { builder: "nextjs", backend: "supabase", baseTemplate: "app-next", overlay: "overlay-supabase", deployConfig: "vercel-supabase", functionsTemplate: null },
20091
+ { builder: "nextjs", backend: "supabase", baseTemplate: "app-next", overlay: "overlay-supabase", deployConfig: "vercel-supabase", functionsTemplate: "functions-supabase" },
19854
20092
  { builder: "nextjs", backend: "vercel", baseTemplate: "app-next", overlay: "overlay-vercel", deployConfig: "vercel-vercel", functionsTemplate: "functions-vercel" },
19855
20093
  { builder: "expo", backend: "none", baseTemplate: "app-expo", overlay: null, deployConfig: null, functionsTemplate: null },
19856
20094
  { builder: "expo", backend: "firebase", baseTemplate: "app-expo", overlay: "overlay-firebase", deployConfig: null, functionsTemplate: "functions-firebase" },
19857
- { builder: "expo", backend: "supabase", baseTemplate: "app-expo", overlay: "overlay-supabase", deployConfig: null, functionsTemplate: null },
20095
+ { builder: "expo", backend: "supabase", baseTemplate: "app-expo", overlay: "overlay-supabase", deployConfig: null, functionsTemplate: "functions-supabase" },
19858
20096
  { builder: "demo", backend: "none", baseTemplate: "app-demo", overlay: null, deployConfig: null, functionsTemplate: null }
19859
20097
  ];
19860
20098
  function comboKey(builder, backend) {
@@ -20029,7 +20267,7 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
20029
20267
  if (providersVariant[1] !== appTemplate) continue;
20030
20268
  const destPath2 = joinPath(appDir, "src/config/providers.ts");
20031
20269
  await ensureDir(getDirname(destPath2));
20032
- await copy(joinPath(overlayDir, file), destPath2);
20270
+ await copy(joinPath(overlayDir, file), destPath2, { overwrite: true });
20033
20271
  if (await isTextFile(destPath2)) await replacePlaceholders(destPath2, replacements);
20034
20272
  continue;
20035
20273
  }
@@ -20040,7 +20278,7 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
20040
20278
  }
20041
20279
  const destPath = joinPath(appDir, destFileName);
20042
20280
  await ensureDir(getDirname(destPath));
20043
- await copy(sourcePath, destPath);
20281
+ await copy(sourcePath, destPath, { overwrite: true });
20044
20282
  if (await isTextFile(destPath)) {
20045
20283
  await replacePlaceholders(destPath, replacements);
20046
20284
  }
@@ -20083,23 +20321,26 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
20083
20321
  overwrite: true
20084
20322
  });
20085
20323
  if (row.functionsTemplate) {
20086
- const functionsRootDir = joinPath(appDir, "functions");
20087
20324
  const functionsTemplateName = row.functionsTemplate;
20088
20325
  const functionsTemplateExists = pathExists(joinPath(templatesRoot, functionsTemplateName));
20089
20326
  if (!functionsTemplateExists) {
20090
20327
  log.warn(`Functions template "${functionsTemplateName}" not found \u2014 skipping functions scaffolding.`);
20091
20328
  } else {
20092
- const functionsPackageJson = generatePackageJson(
20093
- functionsTemplateName,
20094
- executionMode,
20095
- { appName, platform: row.functionsTemplate.replace("functions-", "") }
20096
- );
20097
- const packageJsonPath2 = joinPath(functionsRootDir, "package.json");
20098
- await ensureDir(functionsRootDir);
20099
- await write(packageJsonPath2, functionsPackageJson, {
20100
- format: "json",
20101
- overwrite: true
20102
- });
20329
+ const isSupabaseFunctions = functionsTemplateName === "functions-supabase";
20330
+ const functionsRootDir = isSupabaseFunctions ? appDir : joinPath(appDir, "functions");
20331
+ if (!isSupabaseFunctions) {
20332
+ const functionsPackageJson = generatePackageJson(
20333
+ functionsTemplateName,
20334
+ executionMode,
20335
+ { appName, platform: row.functionsTemplate.replace("functions-", "") }
20336
+ );
20337
+ const packageJsonPath2 = joinPath(functionsRootDir, "package.json");
20338
+ await ensureDir(functionsRootDir);
20339
+ await write(packageJsonPath2, functionsPackageJson, {
20340
+ format: "json",
20341
+ overwrite: true
20342
+ });
20343
+ }
20103
20344
  const templateDir2 = joinPath(templatesRoot, functionsTemplateName);
20104
20345
  const templateFiles2 = await glob("**/*", {
20105
20346
  cwd: templateDir2,
@@ -20132,6 +20373,18 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
20132
20373
  await copy(firebaseJsonSource, joinPath(appDir, "firebase.json"));
20133
20374
  const firebaseJsonDest = joinPath(appDir, "firebase.json");
20134
20375
  if (await isTextFile(firebaseJsonDest)) await replacePlaceholders(firebaseJsonDest, replacements);
20376
+ if (appTemplate === "nextjs") {
20377
+ const firebaseJson = readSync(firebaseJsonDest, { format: "json" });
20378
+ if (firebaseJson.hosting?.rewrites) {
20379
+ firebaseJson.hosting.rewrites = firebaseJson.hosting.rewrites.filter(
20380
+ (r2) => r2.destination !== "/index.html"
20381
+ );
20382
+ }
20383
+ if (firebaseJson.hosting) {
20384
+ firebaseJson.hosting.public = "out";
20385
+ }
20386
+ await write(firebaseJsonDest, firebaseJson, { format: "json", overwrite: true });
20387
+ }
20135
20388
  }
20136
20389
  const firebasercSource = joinPath(deploymentTemplateDir, ".firebaserc.example");
20137
20390
  if (pathExists(firebasercSource)) {
@@ -20148,6 +20401,44 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
20148
20401
  }
20149
20402
  }
20150
20403
  }
20404
+ if (!deployConfig && backend === "firebase" && row.functionsTemplate) {
20405
+ const firebaseJsonPath = joinPath(appDir, "firebase.json");
20406
+ if (!pathExists(firebaseJsonPath)) {
20407
+ const expoFirebaseJson = {
20408
+ functions: [
20409
+ {
20410
+ source: "functions",
20411
+ codebase: "default",
20412
+ ignore: ["node_modules", ".git", "firebase-debug.log", "firebase-debug.*.log", "**/.*", "**/*.test.ts", "**/__tests__/**"],
20413
+ runtime: "nodejs22"
20414
+ }
20415
+ ],
20416
+ firestore: { rules: "firestore.rules", indexes: "firestore.indexes.json" },
20417
+ storage: { rules: "storage.rules" },
20418
+ emulators: {
20419
+ auth: { port: 9099 },
20420
+ functions: { port: 5001 },
20421
+ firestore: { port: 8080 },
20422
+ storage: { port: 9199 },
20423
+ ui: { enabled: true, port: 4e3 }
20424
+ }
20425
+ };
20426
+ await write(firebaseJsonPath, expoFirebaseJson, { format: "json", overwrite: true });
20427
+ }
20428
+ const firebasercSource = joinPath(deploymentTemplateDir, ".firebaserc.example");
20429
+ const firebasercDest = joinPath(appDir, ".firebaserc");
20430
+ if (pathExists(firebasercSource) && !pathExists(firebasercDest)) {
20431
+ await copy(firebasercSource, firebasercDest);
20432
+ if (await isTextFile(firebasercDest)) await replacePlaceholders(firebasercDest, replacements);
20433
+ }
20434
+ for (const example of ["firestore.rules.example", "firestore.indexes.json.example", "storage.rules.example"]) {
20435
+ const src = joinPath(deploymentTemplateDir, example);
20436
+ const dest = joinPath(appDir, example.replace(".example", ""));
20437
+ if (pathExists(src) && !pathExists(dest)) {
20438
+ await copy(src, dest);
20439
+ }
20440
+ }
20441
+ }
20151
20442
  if (deployConfig === "vercel-vercel") {
20152
20443
  const vercelJsonSource = joinPath(deploymentTemplateDir, "vercel.json.example");
20153
20444
  if (pathExists(vercelJsonSource)) {
@@ -20228,23 +20519,25 @@ init_cli_input();
20228
20519
  init_cli_output();
20229
20520
  init_pathResolver();
20230
20521
  var SHOW_WIP2 = process.env.SHOW_WIP === "true" || process.argv.includes("--wip");
20231
- var BACKEND_CHOICES = [
20232
- { title: "None", value: "none" },
20233
- {
20234
- title: "Firebase \u2014 Full platform (Auth, Firestore, Storage, Functions, Hosting)",
20235
- value: "firebase"
20236
- },
20237
- {
20238
- title: "Supabase \u2014 Auth, Postgres, Storage, Functions",
20239
- value: "supabase"
20240
- },
20241
- {
20242
- title: "Vercel \u2014 Serverless functions + Hosting (e.g. Next.js API routes)",
20243
- value: "vercel"
20244
- }
20245
- ];
20246
- function getDefaultBackendIndex(framework) {
20247
- return framework === "nextjs" ? 3 : 1;
20522
+ async function collectAppConfig(appName) {
20523
+ const answers = await runQuestionnaire(APP_QUESTIONNAIRE, {}, SHOW_WIP2, {
20524
+ selection: askForSelection,
20525
+ confirmation: askForConfirmation,
20526
+ input: askForInput
20527
+ });
20528
+ const framework = answers.framework || "vite";
20529
+ const needsBackend = answers.needsBackend || false;
20530
+ const defaultPlatform = framework === "nextjs" ? "vercel" : "firebase";
20531
+ return {
20532
+ template: framework,
20533
+ needsBackend,
20534
+ backendPlatform: needsBackend ? answers.backendPlatform || defaultPlatform : void 0,
20535
+ needsCRUD: false,
20536
+ selectedEntities: [],
20537
+ userAuth: "none",
20538
+ billing: false,
20539
+ features: []
20540
+ };
20248
20541
  }
20249
20542
  function calculateRelativePath(from, to) {
20250
20543
  try {
@@ -20286,8 +20579,8 @@ async function main9(options) {
20286
20579
  try {
20287
20580
  Ie("\u{1F680} DoNotDev Project Creator");
20288
20581
  try {
20289
- const { execSync: execSync10 } = await import("node:child_process");
20290
- execSync10("bun --version", { stdio: "ignore" });
20582
+ const { execSync: execSync11 } = await import("node:child_process");
20583
+ execSync11("bun --version", { stdio: "ignore" });
20291
20584
  } catch {
20292
20585
  log.error("bun is required but not installed.");
20293
20586
  Me(
@@ -20357,71 +20650,22 @@ async function main9(options) {
20357
20650
  let appNames = [];
20358
20651
  const appConfigs = {};
20359
20652
  let anyAppNeedsBackend = false;
20360
- const firstAppName = await askForInput(
20361
- "What's your first app name? (press Enter to skip)",
20362
- ""
20363
- );
20364
- if (firstAppName && firstAppName.trim() !== "") {
20365
- const trimmedName = firstAppName.trim();
20366
- if (isReservedAppName(trimmedName)) {
20367
- log.warn(`'${trimmedName}' is reserved for framework demos.`);
20368
- } else if (!isValidFileName(trimmedName)) {
20369
- log.warn(
20370
- `Invalid app name. Use only letters, numbers, dashes (-), and underscores (_).`
20371
- );
20372
- } else {
20373
- appNames.push(trimmedName);
20374
- const framework = await askForSelection(
20375
- `Builder for "${trimmedName}"?`,
20376
- [
20377
- {
20378
- title: "Vite \u2014 SPA/SaaS (client-side rendering, fast dev)",
20379
- value: "vite"
20380
- },
20381
- {
20382
- title: "Next.js \u2014 Static content/SEO (SSR, SSG) \u2014 DoNotDev support: BETA",
20383
- value: "nextjs"
20384
- },
20385
- {
20386
- title: "Expo \u2014 Mobile app (iOS + Android, single codebase)",
20387
- value: "expo"
20388
- }
20389
- ],
20390
- 0
20391
- );
20392
- const backendValue = await askForSelection(
20393
- `Backend for "${trimmedName}"?`,
20394
- [...BACKEND_CHOICES],
20395
- getDefaultBackendIndex(framework)
20396
- );
20397
- const needsBackend = backendValue !== "none";
20398
- if (needsBackend) anyAppNeedsBackend = true;
20399
- appConfigs[trimmedName] = {
20400
- template: framework,
20401
- needsBackend,
20402
- backendPlatform: needsBackend ? backendValue : void 0,
20403
- needsCRUD: false,
20404
- selectedEntities: [],
20405
- userAuth: "none",
20406
- billing: false,
20407
- features: []
20408
- };
20409
- }
20410
- }
20411
- while (appNames.length > 0) {
20412
- const appName = await askForInput("App name (press Enter to finish)", "");
20413
- if (!appName || appName.trim() === "") {
20653
+ let isFirstApp = true;
20654
+ while (true) {
20655
+ const prompt = isFirstApp ? "What's your first app name? (press Enter to skip)" : "App name (press Enter to finish)";
20656
+ const appNameInput = await askForInput(prompt, "");
20657
+ if (!appNameInput || appNameInput.trim() === "") {
20658
+ if (isFirstApp) break;
20414
20659
  break;
20415
20660
  }
20416
- const trimmedName = appName.trim();
20661
+ const trimmedName = appNameInput.trim();
20662
+ isFirstApp = false;
20417
20663
  if (appNames.includes(trimmedName)) {
20418
20664
  log.warn(`'${trimmedName}' already exists. Choose a different name.`);
20419
20665
  continue;
20420
20666
  }
20421
20667
  if (isReservedAppName(trimmedName)) {
20422
- log.warn(
20423
- `'${trimmedName}' is reserved for framework demos. Choose a different name.`
20424
- );
20668
+ log.warn(`'${trimmedName}' is reserved for framework demos.`);
20425
20669
  continue;
20426
20670
  }
20427
20671
  if (!isValidFileName(trimmedName)) {
@@ -20431,37 +20675,10 @@ async function main9(options) {
20431
20675
  continue;
20432
20676
  }
20433
20677
  appNames.push(trimmedName);
20434
- const framework = await askForSelection(
20435
- `Builder for "${trimmedName}"?`,
20436
- [
20437
- {
20438
- title: "Vite \u2014 SPA/SaaS (client-side rendering, fast dev)",
20439
- value: "vite"
20440
- },
20441
- {
20442
- title: "Next.js \u2014 Static content/SEO (SSR, SSG) \u2014 DoNotDev support: BETA",
20443
- value: "nextjs"
20444
- }
20445
- ],
20446
- 0
20447
- );
20448
- const backendValue = await askForSelection(
20449
- `Backend for "${trimmedName}"?`,
20450
- [...BACKEND_CHOICES],
20451
- getDefaultBackendIndex(framework)
20452
- );
20453
- const needsBackend = backendValue !== "none";
20454
- if (needsBackend) anyAppNeedsBackend = true;
20455
- appConfigs[trimmedName] = {
20456
- template: framework,
20457
- needsBackend,
20458
- backendPlatform: needsBackend ? backendValue : void 0,
20459
- needsCRUD: false,
20460
- selectedEntities: [],
20461
- userAuth: "none",
20462
- billing: false,
20463
- features: []
20464
- };
20678
+ Me(`Configuring "${trimmedName}"...`, "\u2699\uFE0F");
20679
+ const config = await collectAppConfig(trimmedName);
20680
+ appConfigs[trimmedName] = config;
20681
+ if (config.needsBackend) anyAppNeedsBackend = true;
20465
20682
  }
20466
20683
  let installDemoApp = await askForConfirmation(
20467
20684
  "Would you like to install the demo app? (component showcase)",
@@ -20793,7 +21010,7 @@ init_utils();
20793
21010
  init_cli_output();
20794
21011
  init_errors();
20795
21012
  init_pathResolver();
20796
- import { spawnSync as spawnSync9 } from "node:child_process";
21013
+ import { spawnSync as spawnSync10 } from "node:child_process";
20797
21014
  import { EOL as EOL2 } from "node:os";
20798
21015
  async function main10(options = {}) {
20799
21016
  const dryRun = options.dryRun ?? false;
@@ -21270,7 +21487,7 @@ async function runPrettier(rootDir, dryRun, verbose) {
21270
21487
  log.info("DRY RUN: Would run Prettier");
21271
21488
  return stats;
21272
21489
  }
21273
- const result = spawnSync9("bunx", prettierArgs, {
21490
+ const result = spawnSync10("bunx", prettierArgs, {
21274
21491
  cwd: rootDir,
21275
21492
  stdio: "pipe",
21276
21493
  shell: true,