@donotdev/cli 0.0.15 → 0.0.17

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 (83) hide show
  1. package/dependencies-matrix.json +67 -147
  2. package/dist/bin/commands/build.js +69 -52
  3. package/dist/bin/commands/bump.js +15 -14
  4. package/dist/bin/commands/create-app.js +258 -55
  5. package/dist/bin/commands/create-project.js +290 -161
  6. package/dist/bin/commands/deploy.js +146 -63
  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} +1370 -146
  11. package/dist/bin/commands/doctor.js.map +1 -0
  12. package/dist/bin/commands/emu.js +295 -107
  13. package/dist/bin/commands/make-admin.js +77519 -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 +12123 -0
  18. package/dist/bin/commands/setup.js.map +1 -0
  19. package/dist/bin/commands/type-check.d.ts.map +1 -1
  20. package/dist/bin/commands/type-check.js +2022 -283
  21. package/dist/bin/commands/type-check.js.map +1 -1
  22. package/dist/bin/dndev.js +54 -58
  23. package/dist/bin/donotdev.js +54 -58
  24. package/dist/index.js +860 -459
  25. package/package.json +2 -2
  26. package/templates/app-expo/.env.example +2 -22
  27. package/templates/app-expo/README.md.example +1 -1
  28. package/templates/app-expo/assets/adaptive-icon.png +0 -0
  29. package/templates/app-expo/assets/favicon.png +0 -0
  30. package/templates/app-expo/assets/icon.png +0 -0
  31. package/templates/app-expo/assets/splash.png +0 -0
  32. package/templates/app-expo/src/config/app.ts.example +46 -0
  33. package/templates/app-expo/src/config/providers.ts.example +7 -0
  34. package/templates/app-next/src/config/providers.ts.example +7 -0
  35. package/templates/app-vite/src/config/providers.ts.example +7 -0
  36. package/templates/app-vite/src/pages/HomePage.tsx.example +1 -1
  37. package/templates/functions-firebase/README.md.example +1 -1
  38. package/templates/functions-firebase/functions-firebase/.env.example.example +1 -1
  39. package/templates/functions-firebase/functions-firebase/README.md.example +1 -1
  40. package/templates/functions-firebase/functions-firebase/tsconfig.json.example +1 -1
  41. package/templates/functions-firebase/functions.config.js.example +1 -1
  42. package/templates/functions-supabase/supabase/config.toml.example +59 -0
  43. package/templates/functions-supabase/supabase/functions/.env.example +13 -0
  44. package/templates/functions-supabase/supabase/functions/deno.json.example +8 -0
  45. package/templates/overlay-firebase/env.fragment.example +1 -1
  46. package/templates/overlay-firebase/env.fragment.expo.example +1 -1
  47. package/templates/overlay-firebase/env.fragment.nextjs.example +1 -1
  48. package/templates/overlay-supabase/env.fragment.example +8 -3
  49. package/templates/overlay-supabase/env.fragment.expo.example +8 -3
  50. package/templates/overlay-supabase/env.fragment.nextjs.example +8 -3
  51. package/templates/overlay-vercel/env.fragment.example +1 -1
  52. package/templates/overlay-vercel/env.fragment.nextjs.example +1 -1
  53. package/templates/root-consumer/AI.md.example +15 -0
  54. package/templates/root-consumer/guides/dndev/AGENT_START_HERE.md.example +2 -2
  55. package/templates/root-consumer/guides/dndev/ENV_SETUP.md.example +12 -12
  56. package/templates/root-consumer/guides/dndev/INDEX.md.example +3 -3
  57. package/templates/root-consumer/guides/dndev/SETUP_APP_CONFIG.md.example +3 -3
  58. package/templates/root-consumer/guides/dndev/SETUP_AUTH.md.example +13 -6
  59. package/templates/root-consumer/guides/dndev/SETUP_CRUD.md.example +149 -988
  60. package/templates/root-consumer/guides/dndev/SETUP_FIREBASE.md.example +72 -20
  61. package/templates/root-consumer/guides/dndev/SETUP_FUNCTIONS.md.example +6 -111
  62. package/templates/root-consumer/guides/dndev/SETUP_OAUTH_PROVIDERS.md.example +60 -0
  63. package/templates/root-consumer/guides/dndev/SETUP_STRIPE.md.example +62 -0
  64. package/templates/root-consumer/guides/dndev/SETUP_SUPABASE.md.example +124 -33
  65. package/templates/root-consumer/guides/dndev/SETUP_VERCEL.md.example +108 -91
  66. package/templates/root-consumer/guides/dndev/advanced/EMULATORS.md.example +2 -2
  67. package/templates/root-consumer/guides/wai-way/WAI_WAY_CLI.md.example +7 -8
  68. package/templates/root-consumer/guides/wai-way/blueprints/1_scaffold.md.example +9 -5
  69. package/dist/bin/commands/firebase-setup.d.ts +0 -6
  70. package/dist/bin/commands/firebase-setup.d.ts.map +0 -1
  71. package/dist/bin/commands/firebase-setup.js +0 -7
  72. package/dist/bin/commands/firebase-setup.js.map +0 -1
  73. package/dist/bin/commands/lint.d.ts +0 -11
  74. package/dist/bin/commands/lint.d.ts.map +0 -1
  75. package/dist/bin/commands/lint.js.map +0 -1
  76. package/dist/bin/commands/staging.d.ts +0 -11
  77. package/dist/bin/commands/staging.d.ts.map +0 -1
  78. package/dist/bin/commands/staging.js +0 -12
  79. package/dist/bin/commands/staging.js.map +0 -1
  80. package/dist/bin/commands/supabase-setup.d.ts +0 -6
  81. package/dist/bin/commands/supabase-setup.d.ts.map +0 -1
  82. package/dist/bin/commands/supabase-setup.js +0 -7
  83. package/dist/bin/commands/supabase-setup.js.map +0 -1
package/dist/index.js CHANGED
@@ -8469,7 +8469,14 @@ function readServiceAccountKey(filePath) {
8469
8469
  throw new DoNotDevError(
8470
8470
  `Invalid service account key: missing required fields`,
8471
8471
  DO_NOT_DEV_ERROR_CODES.CONFIG_INVALID,
8472
- { context: { filePath, missingFields: ["project_id", "private_key", "client_email"].filter((f) => !key[f]) } }
8472
+ {
8473
+ context: {
8474
+ filePath,
8475
+ missingFields: ["project_id", "private_key", "client_email"].filter(
8476
+ (f) => !key[f]
8477
+ )
8478
+ }
8479
+ }
8473
8480
  );
8474
8481
  }
8475
8482
  return content;
@@ -8510,6 +8517,148 @@ var init_utils = __esm({
8510
8517
  }
8511
8518
  });
8512
8519
 
8520
+ // packages/tooling/src/utils/app-detection.ts
8521
+ function detectApps(projectRoot) {
8522
+ const apps = [];
8523
+ const appsDir = joinPath(projectRoot, "apps");
8524
+ const appsDirStat = statSync2(appsDir);
8525
+ if (pathExists(appsDir) && appsDirStat?.isDirectory()) {
8526
+ const appDirs = readdirSync2(appsDir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name).sort();
8527
+ for (const appDir of appDirs) {
8528
+ const appPath = joinPath(appsDir, appDir);
8529
+ const packageJsonPath = joinPath(appPath, "package.json");
8530
+ if (pathExists(packageJsonPath)) {
8531
+ const appInfo = analyzeApp(appPath, appDir);
8532
+ if (appInfo) {
8533
+ apps.push(appInfo);
8534
+ }
8535
+ }
8536
+ }
8537
+ } else {
8538
+ const packageJsonPath = joinPath(projectRoot, "package.json");
8539
+ if (pathExists(packageJsonPath)) {
8540
+ try {
8541
+ const packageJson = readPackageJson(packageJsonPath);
8542
+ if (packageJson && (packageJson.dependencies?.vite || packageJson.devDependencies?.vite || packageJson.dependencies?.next || packageJson.devDependencies?.next)) {
8543
+ const pathParts = projectRoot.split(/[/\\]/);
8544
+ const appName = pathParts[pathParts.length - 1] || "app";
8545
+ const appInfo = analyzeApp(projectRoot, appName);
8546
+ if (appInfo) {
8547
+ apps.push(appInfo);
8548
+ }
8549
+ }
8550
+ } catch {
8551
+ }
8552
+ }
8553
+ }
8554
+ return apps;
8555
+ }
8556
+ function analyzeApp(appPath, appName) {
8557
+ const packageJsonPath = joinPath(appPath, "package.json");
8558
+ if (!pathExists(packageJsonPath)) {
8559
+ return null;
8560
+ }
8561
+ let packageJson = null;
8562
+ try {
8563
+ packageJson = readPackageJson(packageJsonPath);
8564
+ if (!packageJson.name) {
8565
+ return null;
8566
+ }
8567
+ } catch {
8568
+ return null;
8569
+ }
8570
+ let framework = "unknown";
8571
+ 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) {
8572
+ framework = "nextjs";
8573
+ } 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) {
8574
+ framework = "vite";
8575
+ }
8576
+ const functionsPath = joinPath(appPath, "functions");
8577
+ const functionsStat = statSync2(functionsPath);
8578
+ const hasFunctions = pathExists(functionsPath) && (functionsStat?.isDirectory() ?? false);
8579
+ let platform6;
8580
+ const firebaseJsonInFunctions = hasFunctions && pathExists(joinPath(functionsPath, "firebase.json"));
8581
+ const firebaseJsonInApp = pathExists(joinPath(appPath, "firebase.json"));
8582
+ if (firebaseJsonInFunctions || firebaseJsonInApp) {
8583
+ platform6 = "firebase";
8584
+ }
8585
+ if (!platform6) {
8586
+ const supabaseConfigPath = joinPath(appPath, "supabase", "config.toml");
8587
+ const hasSupabaseDep = !!(packageJson.dependencies?.["@donotdev/supabase"] || packageJson.dependencies?.["@supabase/supabase-js"]);
8588
+ if (pathExists(supabaseConfigPath) || hasSupabaseDep) {
8589
+ platform6 = "supabase";
8590
+ }
8591
+ }
8592
+ if (!platform6) {
8593
+ const vercelJsonPath = joinPath(appPath, "vercel.json");
8594
+ if (pathExists(vercelJsonPath)) {
8595
+ platform6 = "vercel";
8596
+ }
8597
+ }
8598
+ return {
8599
+ name: appName,
8600
+ packageName: packageJson.name || appName,
8601
+ path: appPath,
8602
+ packageJsonPath,
8603
+ framework,
8604
+ hasFunctions,
8605
+ functionsPath: hasFunctions ? functionsPath : void 0,
8606
+ platform: platform6
8607
+ };
8608
+ }
8609
+ var init_app_detection = __esm({
8610
+ "packages/tooling/src/utils/app-detection.ts"() {
8611
+ "use strict";
8612
+ init_utils();
8613
+ init_pathResolver();
8614
+ init_typed_file_operations();
8615
+ }
8616
+ });
8617
+
8618
+ // packages/tooling/src/utils/app-selector.ts
8619
+ async function selectApp(projectRoot, appName) {
8620
+ const apps = detectApps(projectRoot);
8621
+ if (apps.length === 0) {
8622
+ log.error("No apps found in this project.");
8623
+ log.info('Run "dndev init" to create a new project.');
8624
+ return null;
8625
+ }
8626
+ if (appName) {
8627
+ const app = apps.find((a) => a.name === appName);
8628
+ if (!app) {
8629
+ log.warn(`App "${appName}" not found.`);
8630
+ log.info("Available apps:");
8631
+ apps.forEach((a) => log.info(` - ${a.name}`));
8632
+ log.info("Please select from the list:");
8633
+ } else {
8634
+ return app;
8635
+ }
8636
+ }
8637
+ if (apps.length === 1) {
8638
+ return apps[0] || null;
8639
+ }
8640
+ const selected = await ve({
8641
+ message: "Select app:",
8642
+ options: apps.map((app) => ({
8643
+ value: app.name,
8644
+ label: `${app.name} (${app.framework === "nextjs" ? "Next.js" : "Vite"}${app.hasFunctions ? ` + ${app.platform || "functions"}` : ""})`
8645
+ }))
8646
+ });
8647
+ if (typeof selected !== "string") {
8648
+ return null;
8649
+ }
8650
+ return apps.find((a) => a.name === selected) || null;
8651
+ }
8652
+ var init_app_selector = __esm({
8653
+ "packages/tooling/src/utils/app-selector.ts"() {
8654
+ "use strict";
8655
+ init_utils();
8656
+ init_dist2();
8657
+ init_app_detection();
8658
+ init_cli_output();
8659
+ }
8660
+ });
8661
+
8513
8662
  // packages/tooling/src/utils/cli-input.ts
8514
8663
  async function askForInput(message, defaultValue = "") {
8515
8664
  const result = await he({
@@ -8677,7 +8826,7 @@ function checkTsx() {
8677
8826
  return checkCLI("tsx");
8678
8827
  }
8679
8828
  function getCLIInstallInstructions(tool) {
8680
- const platform4 = process.platform;
8829
+ const platform6 = process.platform;
8681
8830
  const instructions = {
8682
8831
  [CLI_TOOLS.BUN]: {
8683
8832
  win32: [
@@ -8730,21 +8879,23 @@ function getCLIInstallInstructions(tool) {
8730
8879
  },
8731
8880
  [CLI_TOOLS.SUPABASE]: {
8732
8881
  win32: [
8733
- "npm install -g supabase",
8882
+ "scoop install supabase",
8883
+ "Or: winget install Supabase.CLI",
8734
8884
  "Or download from: https://github.com/supabase/cli/releases"
8735
8885
  ],
8736
- darwin: [
8737
- "brew install supabase/tap/supabase",
8738
- "Or: npm install -g supabase"
8739
- ],
8886
+ darwin: ["brew install supabase/tap/supabase"],
8740
8887
  linux: [
8741
- "npm install -g supabase",
8888
+ "brew install supabase/tap/supabase",
8742
8889
  "Or see: https://supabase.com/docs/guides/cli"
8743
8890
  ]
8744
8891
  },
8745
8892
  [CLI_TOOLS.VERCEL]: {
8746
8893
  win32: ["npm install -g vercel", "Or: npx vercel (no install)"],
8747
- darwin: ["npm install -g vercel", "Or: brew install vercel", "Or: npx vercel"],
8894
+ darwin: [
8895
+ "npm install -g vercel",
8896
+ "Or: brew install vercel",
8897
+ "Or: npx vercel"
8898
+ ],
8748
8899
  linux: ["npm install -g vercel", "Or: npx vercel"]
8749
8900
  },
8750
8901
  [CLI_TOOLS.SENTRY_CLI]: {
@@ -8791,7 +8942,7 @@ function getCLIInstallInstructions(tool) {
8791
8942
  linux: ["npm install -g tsx"]
8792
8943
  }
8793
8944
  };
8794
- const platformInstructions = instructions[tool]?.[platform4] || instructions[tool]?.linux || [];
8945
+ const platformInstructions = instructions[tool]?.[platform6] || instructions[tool]?.linux || [];
8795
8946
  return platformInstructions.join("\n \u2022 ");
8796
8947
  }
8797
8948
  function formatCLIMissingError(tool, additionalContext) {
@@ -8806,9 +8957,9 @@ function formatCLIMissingError(tool, additionalContext) {
8806
8957
  M2.info(`
8807
8958
  ${additionalContext}`);
8808
8959
  }
8809
- const platform4 = process.platform;
8960
+ const platform6 = process.platform;
8810
8961
  M2.info("\nIf already installed, ensure it's in your PATH:");
8811
- if (platform4 === "win32") {
8962
+ if (platform6 === "win32") {
8812
8963
  M2.message(" \u2022 Check with: where " + tool);
8813
8964
  M2.message(
8814
8965
  " \u2022 Common location: C:\\Users\\<username>\\AppData\\Roaming\\npm\\"
@@ -8865,6 +9016,40 @@ var init_cli_tools = __esm({
8865
9016
  }
8866
9017
  });
8867
9018
 
9019
+ // packages/tooling/src/cli/setup/vercel-token.ts
9020
+ function readEnvVar(filePath, varName) {
9021
+ if (!pathExists(filePath)) return null;
9022
+ const content = readSync(filePath, { format: "text" });
9023
+ if (typeof content !== "string") return null;
9024
+ for (const line of content.split(/\r?\n/)) {
9025
+ const trimmed = line.trim();
9026
+ if (!trimmed || trimmed.startsWith("#")) continue;
9027
+ if (trimmed.startsWith(`${varName}=`)) {
9028
+ const val = trimmed.substring(`${varName}=`.length).trim();
9029
+ if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
9030
+ return val.slice(1, -1);
9031
+ }
9032
+ return val || null;
9033
+ }
9034
+ }
9035
+ return null;
9036
+ }
9037
+ function resolveVercelToken(appDir) {
9038
+ if (process.env.VERCEL_TOKEN) return process.env.VERCEL_TOKEN;
9039
+ const fromEnv = readEnvVar(joinPath(appDir, ".env"), "VERCEL_TOKEN");
9040
+ if (fromEnv) return fromEnv;
9041
+ const fromLocal = readEnvVar(joinPath(appDir, ".env.local"), "VERCEL_TOKEN");
9042
+ if (fromLocal) return fromLocal;
9043
+ return null;
9044
+ }
9045
+ var init_vercel_token = __esm({
9046
+ "packages/tooling/src/cli/setup/vercel-token.ts"() {
9047
+ "use strict";
9048
+ init_utils();
9049
+ init_pathResolver();
9050
+ }
9051
+ });
9052
+
8868
9053
  // node_modules/.bun/yaml@2.8.2/node_modules/yaml/dist/nodes/identity.js
8869
9054
  var require_identity = __commonJS({
8870
9055
  "node_modules/.bun/yaml@2.8.2/node_modules/yaml/dist/nodes/identity.js"(exports) {
@@ -16226,13 +16411,36 @@ var require_dist = __commonJS({
16226
16411
  }
16227
16412
  });
16228
16413
 
16414
+ // packages/tooling/src/utils/error-handling.ts
16415
+ function isError(error2) {
16416
+ return error2 instanceof Error;
16417
+ }
16418
+ function getErrorMessage(error2) {
16419
+ if (isError(error2)) {
16420
+ return error2.message;
16421
+ }
16422
+ if (typeof error2 === "string") {
16423
+ return error2;
16424
+ }
16425
+ if (error2 && typeof error2 === "object" && "message" in error2) {
16426
+ return String(error2.message);
16427
+ }
16428
+ return String(error2);
16429
+ }
16430
+ var init_error_handling = __esm({
16431
+ "packages/tooling/src/utils/error-handling.ts"() {
16432
+ "use strict";
16433
+ init_utils();
16434
+ }
16435
+ });
16436
+
16229
16437
  // packages/tooling/src/apps/sync-secrets.ts
16230
16438
  var sync_secrets_exports = {};
16231
16439
  __export(sync_secrets_exports, {
16232
16440
  default: () => sync_secrets_default,
16233
16441
  main: () => main5
16234
16442
  });
16235
- import { spawnSync as spawnSync8 } from "node:child_process";
16443
+ import { spawnSync as spawnSync9 } from "node:child_process";
16236
16444
  function parseEnvFile(filePath) {
16237
16445
  if (!pathExists(filePath)) {
16238
16446
  throw new DoNotDevError(
@@ -16425,7 +16633,7 @@ async function setFirebaseSecret(key, value, projectId, dryRun = false, cwd) {
16425
16633
  NODE_OPTIONS: ""
16426
16634
  // Clear to avoid conflicts
16427
16635
  };
16428
- const result = spawnSync8(firebaseCmd, args, {
16636
+ const result = spawnSync9(firebaseCmd, args, {
16429
16637
  input: value,
16430
16638
  encoding: "utf8",
16431
16639
  stdio: ["pipe", "pipe", "pipe"],
@@ -16521,7 +16729,7 @@ function setVercelSecret(key, value, projectId, dryRun = false) {
16521
16729
  if (projectId) {
16522
16730
  args.push("--project", projectId);
16523
16731
  }
16524
- const result = spawnSync8("vercel", args, {
16732
+ const result = spawnSync9("vercel", args, {
16525
16733
  input: value,
16526
16734
  encoding: "utf8",
16527
16735
  stdio: ["pipe", "inherit", "inherit"]
@@ -16550,7 +16758,7 @@ function setVercelSecret(key, value, projectId, dryRun = false) {
16550
16758
  }
16551
16759
  function detectGitHubRepo() {
16552
16760
  try {
16553
- const result = spawnSync8("git", ["remote", "get-url", "origin"], {
16761
+ const result = spawnSync9("git", ["remote", "get-url", "origin"], {
16554
16762
  encoding: "utf8",
16555
16763
  stdio: ["pipe", "pipe", "pipe"]
16556
16764
  });
@@ -16578,7 +16786,7 @@ function setGitHubSecret(key, value, repo, dryRun = false) {
16578
16786
  if (repo) {
16579
16787
  args.push("--repo", repo);
16580
16788
  }
16581
- const result = spawnSync8("gh", args, {
16789
+ const result = spawnSync9("gh", args, {
16582
16790
  input: value,
16583
16791
  encoding: "utf8",
16584
16792
  stdio: ["pipe", "pipe", "pipe"]
@@ -16752,12 +16960,12 @@ Examples:
16752
16960
  }
16753
16961
  return 0;
16754
16962
  }
16755
- const platform4 = config.platform || detectPlatform();
16963
+ const platform6 = config.platform || detectPlatform();
16756
16964
  log.info(`Reading secrets from: ${envFilePath}`);
16757
16965
  if (config.verbose) {
16758
16966
  log.debug(`Working directory: ${currentDir}`);
16759
16967
  log.debug(`Environment file: ${envFilePath}`);
16760
- log.debug(`Platform: ${platform4}`);
16968
+ log.debug(`Platform: ${platform6}`);
16761
16969
  log.debug(`Dry run mode: ${config.dryRun}`);
16762
16970
  }
16763
16971
  const secrets = parseEnvFile(envFilePath);
@@ -16766,7 +16974,7 @@ Examples:
16766
16974
  log.info("No secrets found in .env file");
16767
16975
  return 0;
16768
16976
  }
16769
- log.info(`Found ${secretKeys.length} secrets to sync to ${platform4}:`);
16977
+ log.info(`Found ${secretKeys.length} secrets to sync to ${platform6}:`);
16770
16978
  secretKeys.forEach((key) => {
16771
16979
  if (config.verbose) {
16772
16980
  const value = secrets[key];
@@ -16777,12 +16985,12 @@ Examples:
16777
16985
  log.info(` ${key}`);
16778
16986
  }
16779
16987
  });
16780
- const finalFirebaseProjectDir = platform4 === "firebase" ? firebaseProjectDir || findFirebaseProjectDir(config.envFile) : void 0;
16988
+ const finalFirebaseProjectDir = platform6 === "firebase" ? firebaseProjectDir || findFirebaseProjectDir(config.envFile) : void 0;
16781
16989
  if (config.verbose && finalFirebaseProjectDir) {
16782
16990
  log.debug(`Firebase project directory: ${finalFirebaseProjectDir}`);
16783
16991
  }
16784
16992
  for (const [key, value] of Object.entries(secrets)) {
16785
- if (platform4 === "firebase") {
16993
+ if (platform6 === "firebase") {
16786
16994
  await setFirebaseSecret(
16787
16995
  key,
16788
16996
  value,
@@ -16790,18 +16998,18 @@ Examples:
16790
16998
  config.dryRun,
16791
16999
  finalFirebaseProjectDir
16792
17000
  );
16793
- } else if (platform4 === "vercel") {
17001
+ } else if (platform6 === "vercel") {
16794
17002
  setVercelSecret(key, value, config.vercelProjectId, config.dryRun);
16795
17003
  }
16796
17004
  }
16797
17005
  if (config.dryRun) {
16798
17006
  log.success(
16799
17007
  `
16800
- Dry run completed. No secrets were actually set to ${platform4}.`
17008
+ Dry run completed. No secrets were actually set to ${platform6}.`
16801
17009
  );
16802
17010
  } else {
16803
17011
  log.success(`
16804
- All secrets synced successfully to ${platform4}!`);
17012
+ All secrets synced successfully to ${platform6}!`);
16805
17013
  }
16806
17014
  return 0;
16807
17015
  } catch (err) {
@@ -16830,12 +17038,14 @@ var deploy_supabase_functions_exports = {};
16830
17038
  __export(deploy_supabase_functions_exports, {
16831
17039
  deploySupabaseFunctions: () => deploySupabaseFunctions
16832
17040
  });
16833
- import { execSync as execSync8 } from "node:child_process";
17041
+ import { execSync as execSync9 } from "node:child_process";
16834
17042
  async function deploySupabaseFunctions(appDir, config) {
16835
17043
  const supabaseDir = joinPath(appDir, "supabase");
16836
17044
  const functionsDir = joinPath(supabaseDir, "functions");
16837
17045
  if (!pathExists(functionsDir)) {
16838
- log.warn("No supabase/functions/ directory found. Skipping Supabase functions deployment.");
17046
+ log.warn(
17047
+ "No supabase/functions/ directory found. Skipping Supabase functions deployment."
17048
+ );
16839
17049
  return;
16840
17050
  }
16841
17051
  requireCLI(
@@ -16851,15 +17061,19 @@ async function deploySupabaseFunctions(appDir, config) {
16851
17061
  return pathExists(indexPath);
16852
17062
  });
16853
17063
  if (functionDirs.length === 0) {
16854
- log.warn("No Edge Functions found in supabase/functions/. Skipping deployment.");
17064
+ log.warn(
17065
+ "No Edge Functions found in supabase/functions/. Skipping deployment."
17066
+ );
16855
17067
  return;
16856
17068
  }
16857
- log.info(`Found ${functionDirs.length} Edge Function(s): ${functionDirs.join(", ")}`);
17069
+ log.info(
17070
+ `Found ${functionDirs.length} Edge Function(s): ${functionDirs.join(", ")}`
17071
+ );
16858
17072
  const s = Y2();
16859
17073
  for (const functionName of functionDirs) {
16860
17074
  s.start(`Deploying Edge Function: ${functionName}...`);
16861
17075
  try {
16862
- execSync8(`supabase functions deploy ${functionName}`, {
17076
+ execSync9(`supabase functions deploy ${functionName}`, {
16863
17077
  cwd: supabaseDir,
16864
17078
  stdio: config.verbose ? "inherit" : "pipe",
16865
17079
  env: {
@@ -16873,7 +17087,12 @@ async function deploySupabaseFunctions(appDir, config) {
16873
17087
  throw new DoNotDevError(
16874
17088
  `Failed to deploy Supabase Edge Function: ${functionName}`,
16875
17089
  "deployment-failed",
16876
- { context: { functionName, error: error2 instanceof Error ? error2.message : String(error2) } }
17090
+ {
17091
+ context: {
17092
+ functionName,
17093
+ error: error2 instanceof Error ? error2.message : String(error2)
17094
+ }
17095
+ }
16877
17096
  );
16878
17097
  }
16879
17098
  }
@@ -16898,161 +17117,36 @@ init_utils();
16898
17117
 
16899
17118
  // packages/tooling/src/apps/build.ts
16900
17119
  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();
17120
+ init_app_selector();
17121
+ init_cli_input();
17122
+ init_cli_output();
17123
+ init_errors();
16909
17124
  init_pathResolver();
16910
17125
  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;
17126
+ import { spawnSync, execSync } from "node:child_process";
17127
+ function validateFirebaseJson(appDir) {
17128
+ const firebaseJsonPath = joinPath(appDir, "firebase.json");
17129
+ if (!pathExists(firebaseJsonPath)) {
17130
+ return {
17131
+ valid: false,
17132
+ hasHosting: false,
17133
+ hasFunctions: false
17134
+ };
16950
17135
  }
16951
- let packageJson = null;
16952
17136
  try {
16953
- packageJson = readPackageJson(packageJsonPath);
16954
- if (!packageJson.name) {
16955
- return null;
17137
+ const config = readFirebaseJson(firebaseJsonPath);
17138
+ if (!config.projectId) {
17139
+ return {
17140
+ valid: false,
17141
+ hasHosting: false,
17142
+ hasFunctions: false
17143
+ };
16956
17144
  }
16957
- } 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
- };
17145
+ return {
17146
+ valid: true,
17147
+ hasHosting: !!config.hosting,
17148
+ hasFunctions: !!config.functions && Array.isArray(config.functions)
17149
+ };
17056
17150
  } catch {
17057
17151
  return {
17058
17152
  valid: false,
@@ -17220,11 +17314,12 @@ async function main() {
17220
17314
 
17221
17315
  // packages/tooling/src/apps/dev.ts
17222
17316
  init_utils();
17223
- import { spawn, execSync as execSync3 } from "node:child_process";
17224
- import { platform } from "node:os";
17317
+ init_app_selector();
17225
17318
  init_cli_output();
17226
17319
  init_errors();
17227
17320
  init_pathResolver();
17321
+ import { spawn, execSync as execSync3 } from "node:child_process";
17322
+ import { platform } from "node:os";
17228
17323
 
17229
17324
  // packages/tooling/src/utils/spawn-utils.ts
17230
17325
  init_utils();
@@ -17470,18 +17565,23 @@ async function main2() {
17470
17565
 
17471
17566
  // packages/tooling/src/apps/emu.ts
17472
17567
  init_utils();
17568
+ init_app_selector();
17569
+ init_cli_output();
17570
+ init_errors();
17571
+ init_pathResolver();
17572
+
17573
+ // packages/tooling/src/apps/emu-firebase.ts
17574
+ init_utils();
17575
+ init_cli_input();
17576
+ init_cli_output();
17577
+ init_pathResolver();
17578
+ init_typed_file_operations();
17473
17579
  import {
17474
17580
  spawn as spawn2,
17475
17581
  spawnSync as spawnSync3,
17476
17582
  execSync as execSync4
17477
17583
  } from "node:child_process";
17478
17584
  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
17585
  function discoverFirebaseProjectId(appPath) {
17486
17586
  const firebasercPath = joinPath(appPath, ".firebaserc");
17487
17587
  if (pathExists(firebasercPath)) {
@@ -17513,7 +17613,7 @@ function killPorts(ports) {
17513
17613
  pids.add(match[1]);
17514
17614
  }
17515
17615
  }
17516
- for (const pid of pids) {
17616
+ for (const pid of Array.from(pids)) {
17517
17617
  try {
17518
17618
  execSync4(`taskkill /F /PID ${pid}`, { stdio: "ignore" });
17519
17619
  log.debug(`Killed process ${pid} on port ${port}`);
@@ -17575,9 +17675,9 @@ function loadEnvFile(filePath) {
17575
17675
  } catch {
17576
17676
  }
17577
17677
  }
17578
- async function main3(options) {
17579
- const args = process.argv.slice(2);
17580
- const debug = options?.debug ?? false;
17678
+ async function startFirebase(app, projectRoot, options = {}) {
17679
+ const debug = options.debug ?? false;
17680
+ let selectedServices = options.selectedServices ?? [];
17581
17681
  const serviceChoices = [
17582
17682
  {
17583
17683
  title: "Auth",
@@ -17600,47 +17700,6 @@ async function main3(options) {
17600
17700
  hint: "Stripe webhook forwarding (for payment testing)"
17601
17701
  }
17602
17702
  ];
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
17703
  log.info(`Starting Firebase emulators + dev server for ${app.name}...
17645
17704
  `);
17646
17705
  if (selectedServices.length === 0) {
@@ -17772,7 +17831,7 @@ FIREBASE_AUTH_EMULATOR_HOST=${authEmulatorHost}
17772
17831
  const stripeWebhookUrl = `http://localhost:5001/${projectId}/${region}/stripeWebhook`;
17773
17832
  concurrentlyArgs.push(`stripe listen --forward-to ${stripeWebhookUrl}`);
17774
17833
  log.info("");
17775
- log.info("\u2705 Stripe webhook forwarding will start automatically");
17834
+ log.info("Stripe webhook forwarding will start automatically");
17776
17835
  log.info(` Webhook URL: ${stripeWebhookUrl}`);
17777
17836
  log.info(" To trigger test events, run in another terminal:");
17778
17837
  log.info(" stripe trigger checkout.session.completed");
@@ -17794,9 +17853,7 @@ FIREBASE_AUTH_EMULATOR_HOST=${authEmulatorHost}
17794
17853
  );
17795
17854
  const SIGNAL_EXIT_CODES3 = {
17796
17855
  SIGINT: 130,
17797
- // 128 + 2
17798
17856
  SIGTERM: 143
17799
- // 128 + 15
17800
17857
  };
17801
17858
  const cleanup = (signal) => {
17802
17859
  if (!childProcess.pid) return;
@@ -17846,23 +17903,232 @@ FIREBASE_AUTH_EMULATOR_HOST=${authEmulatorHost}
17846
17903
  });
17847
17904
  childProcess.on("error", (error2) => {
17848
17905
  handlers.forEach((h2) => h2());
17849
- log.error(`Failed to start emulators: ${error2.message}`);
17906
+ log.error(`Failed to start Firebase emulators: ${error2.message}`);
17850
17907
  resolve4(1);
17851
17908
  });
17852
17909
  });
17853
17910
  }
17854
17911
 
17855
- // packages/tooling/src/apps/preview.ts
17912
+ // packages/tooling/src/apps/emu-supabase.ts
17856
17913
  init_utils();
17857
- import {
17858
- spawn as spawn3,
17859
- spawnSync as spawnSync4,
17860
- execSync as execSync5
17861
- } from "node:child_process";
17914
+ init_cli_output();
17915
+ import { spawn as spawn3, execSync as execSync5 } from "node:child_process";
17862
17916
  import { platform as platform3 } from "node:os";
17917
+ function isDockerRunning() {
17918
+ try {
17919
+ execSync5("docker info", { stdio: "pipe", timeout: 5e3 });
17920
+ return true;
17921
+ } catch {
17922
+ return false;
17923
+ }
17924
+ }
17925
+ async function startSupabase(app, _projectRoot) {
17926
+ log.info(`Starting Supabase local stack + dev server for ${app.name}...
17927
+ `);
17928
+ if (!isDockerRunning()) {
17929
+ log.error("Docker is not running. Supabase local dev requires Docker.");
17930
+ log.error("Start Docker Desktop and try again.\n");
17931
+ return 1;
17932
+ }
17933
+ try {
17934
+ execSync5("supabase --version", { stdio: "pipe" });
17935
+ } catch {
17936
+ log.error("Supabase CLI not found. Install it:");
17937
+ log.error(" npm install -g supabase\n");
17938
+ return 1;
17939
+ }
17940
+ const isWindows = platform3() === "win32";
17941
+ const concurrentlyArgs = [
17942
+ "--kill-others-on-fail",
17943
+ "--prefix-colors",
17944
+ "cyan,green",
17945
+ "--prefix",
17946
+ "[{name}]",
17947
+ "--names",
17948
+ "supabase,dev",
17949
+ "supabase start",
17950
+ `bunx turbo dev --filter=${app.packageName}`
17951
+ ];
17952
+ const childProcess = spawn3(
17953
+ "bunx",
17954
+ ["concurrently", ...concurrentlyArgs],
17955
+ {
17956
+ stdio: "inherit",
17957
+ cwd: app.path,
17958
+ shell: isWindows
17959
+ }
17960
+ );
17961
+ const cleanup = () => {
17962
+ if (!childProcess.pid) return;
17963
+ if (isWindows) {
17964
+ try {
17965
+ execSync5(`taskkill /F /T /PID ${childProcess.pid}`, {
17966
+ stdio: "ignore",
17967
+ timeout: 2e3
17968
+ });
17969
+ } catch {
17970
+ }
17971
+ } else {
17972
+ try {
17973
+ process.kill(childProcess.pid, "SIGTERM");
17974
+ } catch {
17975
+ }
17976
+ }
17977
+ };
17978
+ process.on("SIGINT", () => {
17979
+ cleanup();
17980
+ process.exit(130);
17981
+ });
17982
+ process.on("SIGTERM", () => {
17983
+ cleanup();
17984
+ process.exit(143);
17985
+ });
17986
+ return new Promise((resolve4) => {
17987
+ childProcess.on("exit", (code) => resolve4(code ?? 0));
17988
+ childProcess.on("error", (error2) => {
17989
+ log.error(`Failed to start Supabase local dev: ${error2.message}`);
17990
+ resolve4(1);
17991
+ });
17992
+ });
17993
+ }
17994
+
17995
+ // packages/tooling/src/apps/emu-vercel.ts
17996
+ init_utils();
17997
+ init_cli_output();
17998
+ import { spawn as spawn4, execSync as execSync6 } from "node:child_process";
17999
+ import { platform as platform4 } from "node:os";
18000
+ async function startVercel(app, _projectRoot) {
18001
+ log.info(`Starting Vercel dev server for ${app.name}...
18002
+ `);
18003
+ try {
18004
+ execSync6("vercel --version", { stdio: "pipe" });
18005
+ } catch {
18006
+ log.error("Vercel CLI not found. Install it:");
18007
+ log.error(" npm install -g vercel\n");
18008
+ return 1;
18009
+ }
18010
+ const isWindows = platform4() === "win32";
18011
+ const childProcess = spawn4("vercel", ["dev"], {
18012
+ stdio: "inherit",
18013
+ cwd: app.path,
18014
+ shell: isWindows
18015
+ });
18016
+ const cleanup = () => {
18017
+ if (!childProcess.pid) return;
18018
+ if (isWindows) {
18019
+ try {
18020
+ execSync6(`taskkill /F /T /PID ${childProcess.pid}`, {
18021
+ stdio: "ignore",
18022
+ timeout: 2e3
18023
+ });
18024
+ } catch {
18025
+ }
18026
+ } else {
18027
+ try {
18028
+ process.kill(childProcess.pid, "SIGTERM");
18029
+ } catch {
18030
+ }
18031
+ }
18032
+ };
18033
+ process.on("SIGINT", () => {
18034
+ cleanup();
18035
+ process.exit(130);
18036
+ });
18037
+ process.on("SIGTERM", () => {
18038
+ cleanup();
18039
+ process.exit(143);
18040
+ });
18041
+ return new Promise((resolve4) => {
18042
+ childProcess.on("exit", (code) => resolve4(code ?? 0));
18043
+ childProcess.on("error", (error2) => {
18044
+ log.error(`Failed to start Vercel dev: ${error2.message}`);
18045
+ resolve4(1);
18046
+ });
18047
+ });
18048
+ }
18049
+
18050
+ // packages/tooling/src/apps/emu.ts
18051
+ async function main3(options) {
18052
+ const args = process.argv.slice(2);
18053
+ const projectRoot = getRepoRoot();
18054
+ if (!projectRoot) {
18055
+ throw new DoNotDevError(
18056
+ "Could not determine repository root",
18057
+ "path-resolution-error"
18058
+ );
18059
+ }
18060
+ const appName = args.find(
18061
+ (arg) => arg !== "emu" && arg !== "--debug" && !arg.startsWith("--")
18062
+ );
18063
+ const app = await selectApp(projectRoot, appName);
18064
+ if (!app) {
18065
+ return 1;
18066
+ }
18067
+ switch (app.platform) {
18068
+ case "firebase": {
18069
+ if (!app.hasFunctions) {
18070
+ log.error(
18071
+ `App "${app.name}" has Firebase config but no functions directory.`
18072
+ );
18073
+ log.error('Use "dndev dev" for apps without backend functions.\n');
18074
+ return 1;
18075
+ }
18076
+ const selectedServices = parseFirebaseServices(args, options);
18077
+ return startFirebase(app, projectRoot, {
18078
+ debug: options?.debug ?? false,
18079
+ selectedServices
18080
+ });
18081
+ }
18082
+ case "supabase":
18083
+ return startSupabase(app, projectRoot);
18084
+ case "vercel":
18085
+ return startVercel(app, projectRoot);
18086
+ default:
18087
+ log.error(`App "${app.name}" does not have a detected backend platform.`);
18088
+ log.error(
18089
+ "Expected: firebase.json (Firebase), supabase/config.toml or @donotdev/supabase (Supabase), or vercel.json (Vercel)."
18090
+ );
18091
+ log.error('Use "dndev dev" for apps without a backend.\n');
18092
+ return 1;
18093
+ }
18094
+ }
18095
+ function parseFirebaseServices(args, options) {
18096
+ const validServices = ["auth", "functions", "firestore", "stripe"];
18097
+ let selectedServices = [];
18098
+ if (options?.services) {
18099
+ selectedServices = options.services.split(",").map((s) => s.trim());
18100
+ } else {
18101
+ const servicesArg = args.find((arg) => arg.startsWith("--services="));
18102
+ if (servicesArg) {
18103
+ const servicesList = servicesArg.split("=")[1];
18104
+ if (servicesList) {
18105
+ selectedServices = servicesList.split(",").map((s) => s.trim());
18106
+ }
18107
+ } else {
18108
+ if (options?.auth || args.includes("--auth"))
18109
+ selectedServices.push("auth");
18110
+ if (args.includes("--functions")) selectedServices.push("functions");
18111
+ if (options?.firestore || args.includes("--firestore"))
18112
+ selectedServices.push("firestore");
18113
+ if (options?.stripe || args.includes("--stripe"))
18114
+ selectedServices.push("stripe");
18115
+ }
18116
+ }
18117
+ return selectedServices.filter((s) => validServices.includes(s));
18118
+ }
18119
+
18120
+ // packages/tooling/src/apps/preview.ts
18121
+ init_utils();
18122
+ init_app_selector();
17863
18123
  init_cli_output();
17864
18124
  init_errors();
17865
18125
  init_pathResolver();
18126
+ import {
18127
+ spawn as spawn5,
18128
+ spawnSync as spawnSync4,
18129
+ execSync as execSync7
18130
+ } from "node:child_process";
18131
+ import { platform as platform5 } from "node:os";
17866
18132
  var SIGNAL_EXIT_CODES2 = {
17867
18133
  SIGINT: 130,
17868
18134
  // 128 + 2
@@ -17905,8 +18171,8 @@ async function main4() {
17905
18171
  log.info(`Previewing ${app.name}...
17906
18172
  `);
17907
18173
  const turboArgs = ["preview", "--filter", app.packageName];
17908
- const isWindows = platform3() === "win32";
17909
- const childProcess = spawn3("bunx", ["turbo", ...turboArgs], {
18174
+ const isWindows = platform5() === "win32";
18175
+ const childProcess = spawn5("bunx", ["turbo", ...turboArgs], {
17910
18176
  stdio: "inherit",
17911
18177
  env: createAppEnv(app.path),
17912
18178
  cwd: projectRoot,
@@ -17917,7 +18183,7 @@ async function main4() {
17917
18183
  log.debug(`Received ${signal}, cleaning up processes...`);
17918
18184
  if (isWindows) {
17919
18185
  try {
17920
- execSync5(`taskkill /F /T /PID ${childProcess.pid}`, {
18186
+ execSync7(`taskkill /F /T /PID ${childProcess.pid}`, {
17921
18187
  stdio: "ignore",
17922
18188
  timeout: 2e3
17923
18189
  });
@@ -17929,7 +18195,7 @@ async function main4() {
17929
18195
  } catch {
17930
18196
  }
17931
18197
  try {
17932
- execSync5(`pkill -P ${childProcess.pid}`, {
18198
+ execSync7(`pkill -P ${childProcess.pid}`, {
17933
18199
  stdio: "ignore",
17934
18200
  timeout: 1e3
17935
18201
  });
@@ -17968,7 +18234,7 @@ async function main4() {
17968
18234
 
17969
18235
  // packages/tooling/src/apps/deploy.ts
17970
18236
  init_utils();
17971
- import { execSync as execSync9 } from "node:child_process";
18237
+ import { execSync as execSync10 } from "node:child_process";
17972
18238
 
17973
18239
  // packages/tooling/src/apps/deploy-frontend.ts
17974
18240
  init_utils();
@@ -18269,15 +18535,14 @@ function getMatrixPath(mode) {
18269
18535
  }
18270
18536
  const executionMode = mode || detectExecutionMode();
18271
18537
  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
- }
18538
+ const templatesRoot = getTemplatesRoot();
18539
+ const devPath = normalizePath(
18540
+ templatesRoot,
18541
+ "..",
18542
+ "dependencies-matrix.json"
18543
+ );
18544
+ if (pathExists(devPath)) {
18545
+ return devPath;
18281
18546
  }
18282
18547
  }
18283
18548
  return null;
@@ -18292,8 +18557,8 @@ function getCliVersion(mode) {
18292
18557
  }
18293
18558
  const executionMode = mode || detectExecutionMode();
18294
18559
  if (executionMode === "development") {
18295
- const repoRoot = getRepoRoot();
18296
- const cliPackageJson = joinPath(repoRoot, "packages/cli/package.json");
18560
+ const templatesRoot = getTemplatesRoot();
18561
+ const cliPackageJson = normalizePath(templatesRoot, "..", "package.json");
18297
18562
  if (pathExists(cliPackageJson)) {
18298
18563
  const pkg = readSync(cliPackageJson, { format: "json" });
18299
18564
  return String(pkg?.version || "0.0.0");
@@ -18446,7 +18711,12 @@ async function runQuestionnaire(questions, initialAnswers, showWIP, askFor) {
18446
18711
  async function deployFrontend(appDir, serviceAccountPath, projectId, config) {
18447
18712
  const s = Y2();
18448
18713
  s.start("Deploying frontend to Firebase Hosting...");
18449
- const args = buildFirebaseDeployArgs("hosting", projectId, config.debug, config.force ?? true);
18714
+ const args = buildFirebaseDeployArgs(
18715
+ "hosting",
18716
+ projectId,
18717
+ config.debug,
18718
+ config.force ?? true
18719
+ );
18450
18720
  const result = executeFirebaseCommand(args, {
18451
18721
  cwd: appDir,
18452
18722
  serviceAccountPath,
@@ -18470,15 +18740,33 @@ async function deployFrontend(appDir, serviceAccountPath, projectId, config) {
18470
18740
 
18471
18741
  // packages/tooling/src/apps/deploy-vercel-frontend.ts
18472
18742
  init_utils();
18473
- import { execSync as execSync6 } from "node:child_process";
18743
+ init_cli_output();
18744
+ init_vercel_token();
18745
+ import { spawnSync as spawnSync6 } from "node:child_process";
18474
18746
  async function deployVercelFrontend(appDir, _config) {
18475
18747
  const s = Y2();
18476
18748
  s.start("Deploying frontend to Vercel...");
18749
+ const token = resolveVercelToken(appDir);
18750
+ if (token) {
18751
+ log.debug("Using VERCEL_TOKEN from .env (token-based auth)");
18752
+ }
18753
+ const args = ["vercel", "--prod", "--yes"];
18754
+ if (token) {
18755
+ args.push("--token", token);
18756
+ }
18477
18757
  try {
18478
- execSync6("npx vercel --prod --yes", {
18758
+ const result = spawnSync6("bunx", args, {
18479
18759
  cwd: appDir,
18480
- stdio: "inherit"
18760
+ stdio: "pipe",
18761
+ encoding: "utf-8"
18481
18762
  });
18763
+ if (result.status !== 0) {
18764
+ s.stop("Vercel deployment failed");
18765
+ const errOutput = result.stderr?.trim();
18766
+ throw new Error(
18767
+ errOutput || `Vercel deploy exited with code ${result.status}`
18768
+ );
18769
+ }
18482
18770
  s.stop("Frontend deployed to Vercel");
18483
18771
  } catch (err) {
18484
18772
  s.stop("Vercel deployment failed");
@@ -18489,7 +18777,7 @@ async function deployVercelFrontend(appDir, _config) {
18489
18777
  // packages/tooling/src/apps/deploy-functions.ts
18490
18778
  init_utils();
18491
18779
  var import_yaml = __toESM(require_dist(), 1);
18492
- import { execSync as execSync7, spawnSync as spawnSync6 } from "node:child_process";
18780
+ import { execSync as execSync8, spawnSync as spawnSync7 } from "node:child_process";
18493
18781
  init_pathResolver();
18494
18782
  init_cli_tools();
18495
18783
  init_typed_file_operations();
@@ -18658,7 +18946,7 @@ function updateCloudRunIAM(functionNames, projectId, region, serviceAccountPath,
18658
18946
  let failCount = 0;
18659
18947
  for (const funcName of functionNames) {
18660
18948
  try {
18661
- const result = spawnSync6(
18949
+ const result = spawnSync7(
18662
18950
  "gcloud",
18663
18951
  [
18664
18952
  "run",
@@ -18776,7 +19064,7 @@ async function autoSyncSecrets(functionsDir, projectId, config) {
18776
19064
  let failCount = 0;
18777
19065
  for (const { key, value } of secrets) {
18778
19066
  try {
18779
- const result = spawnSync6(
19067
+ const result = spawnSync7(
18780
19068
  firebaseCmd,
18781
19069
  ["functions:secrets:set", key, "--project", projectId],
18782
19070
  {
@@ -18858,7 +19146,7 @@ To fix this, run:
18858
19146
  const s2 = Y2();
18859
19147
  s2.start("Building functions...");
18860
19148
  try {
18861
- execSync7("bun run build", {
19149
+ execSync8("bun run build", {
18862
19150
  cwd: functionsDir,
18863
19151
  stdio: config.verbose ? "inherit" : "pipe"
18864
19152
  });
@@ -18934,7 +19222,12 @@ async function deployRules(appDir, serviceAccountPath, projectId, config, option
18934
19222
  const targetNames = targets.join(", ");
18935
19223
  const s = Y2();
18936
19224
  s.start(`Deploying ${targetNames}...`);
18937
- const args = buildFirebaseDeployArgs(targets, projectId, config.debug, config.force ?? true);
19225
+ const args = buildFirebaseDeployArgs(
19226
+ targets,
19227
+ projectId,
19228
+ config.debug,
19229
+ config.force ?? true
19230
+ );
18938
19231
  const result = executeFirebaseCommand(args, {
18939
19232
  cwd: appDir,
18940
19233
  serviceAccountPath,
@@ -18958,29 +19251,10 @@ async function deployRules(appDir, serviceAccountPath, projectId, config, option
18958
19251
 
18959
19252
  // packages/tooling/src/apps/deploy-utils.ts
18960
19253
  init_utils();
18961
- import { spawnSync as spawnSync7 } from "node:child_process";
19254
+ import { spawnSync as spawnSync8 } from "node:child_process";
18962
19255
  init_pathResolver();
18963
19256
  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
19257
+ init_error_handling();
18984
19258
  function detectAvailableApps() {
18985
19259
  const currentDir = process.cwd();
18986
19260
  const appsDir = joinPath(currentDir, "apps");
@@ -19165,7 +19439,7 @@ How to fix:
19165
19439
  if (shouldOpen) {
19166
19440
  try {
19167
19441
  const openCommand = process.platform === "win32" ? "start" : process.platform === "darwin" ? "open" : "xdg-open";
19168
- spawnSync7(openCommand, [consoleUrl], { shell: true });
19442
+ spawnSync8(openCommand, [consoleUrl], { shell: true });
19169
19443
  log.success("Opening Firebase Console...");
19170
19444
  } catch {
19171
19445
  log.warn("Could not open browser. Please open the URL manually.");
@@ -19236,7 +19510,7 @@ function detectProvider(appDir) {
19236
19510
  if (hasFirebase) {
19237
19511
  try {
19238
19512
  const config = readSync(firebaseJsonPath, { format: "json" });
19239
- if (config && typeof config === "object" && "projectId" in config) {
19513
+ if (config && typeof config === "object") {
19240
19514
  const firebaseConfigObj = config;
19241
19515
  firebaseConfig = {
19242
19516
  projectId: firebaseConfigObj.projectId,
@@ -19245,8 +19519,6 @@ function detectProvider(appDir) {
19245
19519
  hasFirestoreRules: !!firebaseConfigObj.firestore?.rules,
19246
19520
  hasStorageRules: !!firebaseConfigObj.storage?.rules
19247
19521
  };
19248
- } else {
19249
- throw new Error("Invalid firebase.json structure");
19250
19522
  }
19251
19523
  } catch {
19252
19524
  firebaseConfig = {
@@ -19329,13 +19601,17 @@ async function main6(options = {}) {
19329
19601
  const availableApps = detectAvailableApps();
19330
19602
  if (availableApps.length === 0) {
19331
19603
  if (providerInfo.hasSupabase && !providerInfo.hasFirebase) {
19332
- log.info("Supabase project detected. Deploying Supabase resources...");
19604
+ log.info(
19605
+ "Supabase project detected. Deploying Supabase resources..."
19606
+ );
19333
19607
  } else {
19334
19608
  log.info("No apps with firebase.json or supabase/ directory found.");
19335
19609
  log.info(
19336
19610
  "To deploy, ensure your app has a firebase.json or supabase/ configuration."
19337
19611
  );
19338
- log.info("Run from a DoNotDev project, or create one with: dndev init");
19612
+ log.info(
19613
+ "Run from a DoNotDev project, or create one with: dndev init"
19614
+ );
19339
19615
  return;
19340
19616
  }
19341
19617
  }
@@ -19554,7 +19830,7 @@ async function main6(options = {}) {
19554
19830
  isStaging ? "Building (staging mode)..." : "Building application..."
19555
19831
  );
19556
19832
  try {
19557
- execSync9(buildCmd, {
19833
+ execSync10(buildCmd, {
19558
19834
  cwd: appDir,
19559
19835
  stdio: "inherit",
19560
19836
  env: {
@@ -19615,12 +19891,7 @@ async function main6(options = {}) {
19615
19891
  }
19616
19892
  }
19617
19893
  }
19618
- await deployFunctions(
19619
- appDir,
19620
- serviceAccountPath,
19621
- config.project,
19622
- config
19623
- );
19894
+ await deployFunctions(appDir, serviceAccountPath, config.project, config);
19624
19895
  }
19625
19896
  if (shouldDeployFirebaseRules && serviceAccountPath && config.project && appProviderInfo.firebaseConfig) {
19626
19897
  await deployRules(appDir, serviceAccountPath, config.project, config, {
@@ -19799,10 +20070,10 @@ function generatePackageJson(templateName, mode, options = {}) {
19799
20070
  }
19800
20071
  if (templateName.includes("functions")) {
19801
20072
  result.main = "lib/index.js";
19802
- result.engines = { node: "20" };
20073
+ result.engines = { node: "22" };
19803
20074
  if (options.appName) {
19804
- const platform4 = templateName.includes("vercel") ? "Vercel" : "Firebase";
19805
- result.description = `${options.appName} ${platform4} Functions`;
20075
+ const platform6 = templateName.includes("vercel") ? "Vercel" : "Firebase";
20076
+ result.description = `${options.appName} ${platform6} Functions`;
19806
20077
  }
19807
20078
  if (mode === "development") {
19808
20079
  const workspacePkgs = [
@@ -19844,18 +20115,102 @@ init_pathResolver();
19844
20115
  // packages/tooling/src/scaffolding/scaffold-matrix.ts
19845
20116
  init_utils();
19846
20117
  var MATRIX = [
19847
- { builder: "vite", backend: "none", baseTemplate: "app-vite", overlay: null, deployConfig: null, functionsTemplate: null },
19848
- { 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 },
19850
- { builder: "vite", backend: "vercel", baseTemplate: "app-vite", overlay: "overlay-vercel", deployConfig: "vercel-vercel", functionsTemplate: "functions-vercel" },
19851
- { builder: "nextjs", backend: "none", baseTemplate: "app-next", overlay: null, deployConfig: null, functionsTemplate: null },
19852
- { 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 },
19854
- { builder: "nextjs", backend: "vercel", baseTemplate: "app-next", overlay: "overlay-vercel", deployConfig: "vercel-vercel", functionsTemplate: "functions-vercel" },
19855
- { builder: "expo", backend: "none", baseTemplate: "app-expo", overlay: null, deployConfig: null, functionsTemplate: null },
19856
- { 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 },
19858
- { builder: "demo", backend: "none", baseTemplate: "app-demo", overlay: null, deployConfig: null, functionsTemplate: null }
20118
+ {
20119
+ builder: "vite",
20120
+ backend: "none",
20121
+ baseTemplate: "app-vite",
20122
+ overlay: null,
20123
+ deployConfig: null,
20124
+ functionsTemplate: null
20125
+ },
20126
+ {
20127
+ builder: "vite",
20128
+ backend: "firebase",
20129
+ baseTemplate: "app-vite",
20130
+ overlay: "overlay-firebase",
20131
+ deployConfig: "firebase",
20132
+ functionsTemplate: "functions-firebase"
20133
+ },
20134
+ {
20135
+ builder: "vite",
20136
+ backend: "supabase",
20137
+ baseTemplate: "app-vite",
20138
+ overlay: "overlay-supabase",
20139
+ deployConfig: "vercel-supabase",
20140
+ functionsTemplate: "functions-supabase"
20141
+ },
20142
+ {
20143
+ builder: "vite",
20144
+ backend: "vercel",
20145
+ baseTemplate: "app-vite",
20146
+ overlay: "overlay-vercel",
20147
+ deployConfig: "vercel-vercel",
20148
+ functionsTemplate: "functions-vercel"
20149
+ },
20150
+ {
20151
+ builder: "nextjs",
20152
+ backend: "none",
20153
+ baseTemplate: "app-next",
20154
+ overlay: null,
20155
+ deployConfig: null,
20156
+ functionsTemplate: null
20157
+ },
20158
+ {
20159
+ builder: "nextjs",
20160
+ backend: "firebase",
20161
+ baseTemplate: "app-next",
20162
+ overlay: "overlay-firebase",
20163
+ deployConfig: "firebase",
20164
+ functionsTemplate: "functions-firebase"
20165
+ },
20166
+ {
20167
+ builder: "nextjs",
20168
+ backend: "supabase",
20169
+ baseTemplate: "app-next",
20170
+ overlay: "overlay-supabase",
20171
+ deployConfig: "vercel-supabase",
20172
+ functionsTemplate: "functions-supabase"
20173
+ },
20174
+ {
20175
+ builder: "nextjs",
20176
+ backend: "vercel",
20177
+ baseTemplate: "app-next",
20178
+ overlay: "overlay-vercel",
20179
+ deployConfig: "vercel-vercel",
20180
+ functionsTemplate: "functions-vercel"
20181
+ },
20182
+ {
20183
+ builder: "expo",
20184
+ backend: "none",
20185
+ baseTemplate: "app-expo",
20186
+ overlay: null,
20187
+ deployConfig: null,
20188
+ functionsTemplate: null
20189
+ },
20190
+ {
20191
+ builder: "expo",
20192
+ backend: "firebase",
20193
+ baseTemplate: "app-expo",
20194
+ overlay: "overlay-firebase",
20195
+ deployConfig: null,
20196
+ functionsTemplate: "functions-firebase"
20197
+ },
20198
+ {
20199
+ builder: "expo",
20200
+ backend: "supabase",
20201
+ baseTemplate: "app-expo",
20202
+ overlay: "overlay-supabase",
20203
+ deployConfig: null,
20204
+ functionsTemplate: "functions-supabase"
20205
+ },
20206
+ {
20207
+ builder: "demo",
20208
+ backend: "none",
20209
+ baseTemplate: "app-demo",
20210
+ overlay: null,
20211
+ deployConfig: null,
20212
+ functionsTemplate: null
20213
+ }
19859
20214
  ];
19860
20215
  function comboKey(builder, backend) {
19861
20216
  return `${builder}-${backend}`;
@@ -19868,7 +20223,9 @@ function getScaffoldRow(builder, backend) {
19868
20223
  const key = comboKey(builder, backend);
19869
20224
  const row = ROWS_BY_KEY.get(key);
19870
20225
  if (!row) {
19871
- throw new Error(`Unsupported scaffold combo: ${key}. Supported: ${[...ROWS_BY_KEY.keys()].join(", ")}`);
20226
+ throw new Error(
20227
+ `Unsupported scaffold combo: ${key}. Supported: ${[...ROWS_BY_KEY.keys()].join(", ")}`
20228
+ );
19872
20229
  }
19873
20230
  return row;
19874
20231
  }
@@ -20024,13 +20381,16 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
20024
20381
  const variantFile = `src/config/providers.${appTemplate}.ts.example`;
20025
20382
  if (overlayFiles.includes(variantFile)) continue;
20026
20383
  }
20027
- const providersVariant = file.match(/^src\/config\/providers\.(\w+)\.ts\.example$/);
20384
+ const providersVariant = file.match(
20385
+ /^src\/config\/providers\.(\w+)\.ts\.example$/
20386
+ );
20028
20387
  if (providersVariant) {
20029
20388
  if (providersVariant[1] !== appTemplate) continue;
20030
20389
  const destPath2 = joinPath(appDir, "src/config/providers.ts");
20031
20390
  await ensureDir(getDirname(destPath2));
20032
- await copy(joinPath(overlayDir, file), destPath2);
20033
- if (await isTextFile(destPath2)) await replacePlaceholders(destPath2, replacements);
20391
+ await copy(joinPath(overlayDir, file), destPath2, { overwrite: true });
20392
+ if (await isTextFile(destPath2))
20393
+ await replacePlaceholders(destPath2, replacements);
20034
20394
  continue;
20035
20395
  }
20036
20396
  const sourcePath = joinPath(overlayDir, file);
@@ -20040,7 +20400,7 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
20040
20400
  }
20041
20401
  const destPath = joinPath(appDir, destFileName);
20042
20402
  await ensureDir(getDirname(destPath));
20043
- await copy(sourcePath, destPath);
20403
+ await copy(sourcePath, destPath, { overwrite: true });
20044
20404
  if (await isTextFile(destPath)) {
20045
20405
  await replacePlaceholders(destPath, replacements);
20046
20406
  }
@@ -20055,16 +20415,28 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
20055
20415
  }
20056
20416
  if (deployConfig === "vercel-supabase") {
20057
20417
  const vercelPath = joinPath(appDir, "vercel.json");
20058
- const headersFragmentPath = joinPath(overlayDir, "vercel.headers.example");
20418
+ const headersFragmentPath = joinPath(
20419
+ overlayDir,
20420
+ "vercel.headers.example"
20421
+ );
20059
20422
  const fullVercelPath = joinPath(overlayDir, "vercel.json.example");
20060
20423
  if (pathExists(vercelPath) && pathExists(headersFragmentPath)) {
20061
20424
  const vercelJson = readSync(vercelPath, { format: "json" });
20062
- const headersFragment = readSync(headersFragmentPath, { format: "json" });
20063
- vercelJson.headers = [...vercelJson.headers ?? [], ...headersFragment];
20064
- await write(vercelPath, vercelJson, { format: "json", overwrite: true });
20425
+ const headersFragment = readSync(headersFragmentPath, {
20426
+ format: "json"
20427
+ });
20428
+ vercelJson.headers = [
20429
+ ...vercelJson.headers ?? [],
20430
+ ...headersFragment
20431
+ ];
20432
+ await write(vercelPath, vercelJson, {
20433
+ format: "json",
20434
+ overwrite: true
20435
+ });
20065
20436
  } else if (pathExists(fullVercelPath)) {
20066
20437
  await copy(fullVercelPath, vercelPath);
20067
- if (await isTextFile(vercelPath)) await replacePlaceholders(vercelPath, replacements);
20438
+ if (await isTextFile(vercelPath))
20439
+ await replacePlaceholders(vercelPath, replacements);
20068
20440
  }
20069
20441
  }
20070
20442
  }
@@ -20083,23 +20455,33 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
20083
20455
  overwrite: true
20084
20456
  });
20085
20457
  if (row.functionsTemplate) {
20086
- const functionsRootDir = joinPath(appDir, "functions");
20087
20458
  const functionsTemplateName = row.functionsTemplate;
20088
- const functionsTemplateExists = pathExists(joinPath(templatesRoot, functionsTemplateName));
20459
+ const functionsTemplateExists = pathExists(
20460
+ joinPath(templatesRoot, functionsTemplateName)
20461
+ );
20089
20462
  if (!functionsTemplateExists) {
20090
- log.warn(`Functions template "${functionsTemplateName}" not found \u2014 skipping functions scaffolding.`);
20091
- } else {
20092
- const functionsPackageJson = generatePackageJson(
20093
- functionsTemplateName,
20094
- executionMode,
20095
- { appName, platform: row.functionsTemplate.replace("functions-", "") }
20463
+ log.warn(
20464
+ `Functions template "${functionsTemplateName}" not found \u2014 skipping functions scaffolding.`
20096
20465
  );
20097
- const packageJsonPath2 = joinPath(functionsRootDir, "package.json");
20098
- await ensureDir(functionsRootDir);
20099
- await write(packageJsonPath2, functionsPackageJson, {
20100
- format: "json",
20101
- overwrite: true
20102
- });
20466
+ } else {
20467
+ const isSupabaseFunctions = functionsTemplateName === "functions-supabase";
20468
+ const functionsRootDir = isSupabaseFunctions ? appDir : joinPath(appDir, "functions");
20469
+ if (!isSupabaseFunctions) {
20470
+ const functionsPackageJson = generatePackageJson(
20471
+ functionsTemplateName,
20472
+ executionMode,
20473
+ {
20474
+ appName,
20475
+ platform: row.functionsTemplate.replace("functions-", "")
20476
+ }
20477
+ );
20478
+ const packageJsonPath2 = joinPath(functionsRootDir, "package.json");
20479
+ await ensureDir(functionsRootDir);
20480
+ await write(packageJsonPath2, functionsPackageJson, {
20481
+ format: "json",
20482
+ overwrite: true
20483
+ });
20484
+ }
20103
20485
  const templateDir2 = joinPath(templatesRoot, functionsTemplateName);
20104
20486
  const templateFiles2 = await glob("**/*", {
20105
20487
  cwd: templateDir2,
@@ -20127,20 +20509,49 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
20127
20509
  }
20128
20510
  const deploymentTemplateDir = joinPath(templatesRoot, "root-consumer");
20129
20511
  if (deployConfig === "firebase") {
20130
- const firebaseJsonSource = joinPath(deploymentTemplateDir, "firebase.json.example");
20512
+ const firebaseJsonSource = joinPath(
20513
+ deploymentTemplateDir,
20514
+ "firebase.json.example"
20515
+ );
20131
20516
  if (pathExists(firebaseJsonSource)) {
20132
20517
  await copy(firebaseJsonSource, joinPath(appDir, "firebase.json"));
20133
20518
  const firebaseJsonDest = joinPath(appDir, "firebase.json");
20134
- if (await isTextFile(firebaseJsonDest)) await replacePlaceholders(firebaseJsonDest, replacements);
20519
+ if (await isTextFile(firebaseJsonDest))
20520
+ await replacePlaceholders(firebaseJsonDest, replacements);
20521
+ if (appTemplate === "nextjs") {
20522
+ const firebaseJson = readSync(firebaseJsonDest, {
20523
+ format: "json"
20524
+ });
20525
+ if (firebaseJson.hosting?.rewrites) {
20526
+ firebaseJson.hosting.rewrites = firebaseJson.hosting.rewrites.filter(
20527
+ (r2) => r2.destination !== "/index.html"
20528
+ );
20529
+ }
20530
+ if (firebaseJson.hosting) {
20531
+ firebaseJson.hosting.public = "out";
20532
+ }
20533
+ await write(firebaseJsonDest, firebaseJson, {
20534
+ format: "json",
20535
+ overwrite: true
20536
+ });
20537
+ }
20135
20538
  }
20136
- const firebasercSource = joinPath(deploymentTemplateDir, ".firebaserc.example");
20539
+ const firebasercSource = joinPath(
20540
+ deploymentTemplateDir,
20541
+ ".firebaserc.example"
20542
+ );
20137
20543
  if (pathExists(firebasercSource)) {
20138
20544
  await copy(firebasercSource, joinPath(appDir, ".firebaserc"));
20139
20545
  const firebasercDest = joinPath(appDir, ".firebaserc");
20140
- if (await isTextFile(firebasercDest)) await replacePlaceholders(firebasercDest, replacements);
20546
+ if (await isTextFile(firebasercDest))
20547
+ await replacePlaceholders(firebasercDest, replacements);
20141
20548
  }
20142
20549
  if (row.functionsTemplate === "functions-firebase") {
20143
- for (const example of ["firestore.rules.example", "firestore.indexes.json.example", "storage.rules.example"]) {
20550
+ for (const example of [
20551
+ "firestore.rules.example",
20552
+ "firestore.indexes.json.example",
20553
+ "storage.rules.example"
20554
+ ]) {
20144
20555
  const src = joinPath(deploymentTemplateDir, example);
20145
20556
  if (pathExists(src)) {
20146
20557
  await copy(src, joinPath(appDir, example.replace(".example", "")));
@@ -20148,12 +20559,76 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
20148
20559
  }
20149
20560
  }
20150
20561
  }
20562
+ if (!deployConfig && backend === "firebase" && row.functionsTemplate) {
20563
+ const firebaseJsonPath = joinPath(appDir, "firebase.json");
20564
+ if (!pathExists(firebaseJsonPath)) {
20565
+ const expoFirebaseJson = {
20566
+ functions: [
20567
+ {
20568
+ source: "functions",
20569
+ codebase: "default",
20570
+ ignore: [
20571
+ "node_modules",
20572
+ ".git",
20573
+ "firebase-debug.log",
20574
+ "firebase-debug.*.log",
20575
+ "**/.*",
20576
+ "**/*.test.ts",
20577
+ "**/__tests__/**"
20578
+ ],
20579
+ runtime: "nodejs22"
20580
+ }
20581
+ ],
20582
+ firestore: {
20583
+ rules: "firestore.rules",
20584
+ indexes: "firestore.indexes.json"
20585
+ },
20586
+ storage: { rules: "storage.rules" },
20587
+ emulators: {
20588
+ auth: { port: 9099 },
20589
+ functions: { port: 5001 },
20590
+ firestore: { port: 8080 },
20591
+ storage: { port: 9199 },
20592
+ ui: { enabled: true, port: 4e3 }
20593
+ }
20594
+ };
20595
+ await write(firebaseJsonPath, expoFirebaseJson, {
20596
+ format: "json",
20597
+ overwrite: true
20598
+ });
20599
+ }
20600
+ const firebasercSource = joinPath(
20601
+ deploymentTemplateDir,
20602
+ ".firebaserc.example"
20603
+ );
20604
+ const firebasercDest = joinPath(appDir, ".firebaserc");
20605
+ if (pathExists(firebasercSource) && !pathExists(firebasercDest)) {
20606
+ await copy(firebasercSource, firebasercDest);
20607
+ if (await isTextFile(firebasercDest))
20608
+ await replacePlaceholders(firebasercDest, replacements);
20609
+ }
20610
+ for (const example of [
20611
+ "firestore.rules.example",
20612
+ "firestore.indexes.json.example",
20613
+ "storage.rules.example"
20614
+ ]) {
20615
+ const src = joinPath(deploymentTemplateDir, example);
20616
+ const dest = joinPath(appDir, example.replace(".example", ""));
20617
+ if (pathExists(src) && !pathExists(dest)) {
20618
+ await copy(src, dest);
20619
+ }
20620
+ }
20621
+ }
20151
20622
  if (deployConfig === "vercel-vercel") {
20152
- const vercelJsonSource = joinPath(deploymentTemplateDir, "vercel.json.example");
20623
+ const vercelJsonSource = joinPath(
20624
+ deploymentTemplateDir,
20625
+ "vercel.json.example"
20626
+ );
20153
20627
  if (pathExists(vercelJsonSource)) {
20154
20628
  await copy(vercelJsonSource, joinPath(appDir, "vercel.json"));
20155
20629
  const vercelJsonDest = joinPath(appDir, "vercel.json");
20156
- if (await isTextFile(vercelJsonDest)) await replacePlaceholders(vercelJsonDest, replacements);
20630
+ if (await isTextFile(vercelJsonDest))
20631
+ await replacePlaceholders(vercelJsonDest, replacements);
20157
20632
  }
20158
20633
  }
20159
20634
  const backendInfo = row.functionsTemplate ? ` with ${row.functionsTemplate.replace("functions-", "")} functions` : "";
@@ -20228,23 +20703,25 @@ init_cli_input();
20228
20703
  init_cli_output();
20229
20704
  init_pathResolver();
20230
20705
  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;
20706
+ async function collectAppConfig(appName) {
20707
+ const answers = await runQuestionnaire(APP_QUESTIONNAIRE, {}, SHOW_WIP2, {
20708
+ selection: askForSelection,
20709
+ confirmation: askForConfirmation,
20710
+ input: askForInput
20711
+ });
20712
+ const framework = answers.framework || "vite";
20713
+ const needsBackend = answers.needsBackend || false;
20714
+ const defaultPlatform = framework === "nextjs" ? "vercel" : "firebase";
20715
+ return {
20716
+ template: framework,
20717
+ needsBackend,
20718
+ backendPlatform: needsBackend ? answers.backendPlatform || defaultPlatform : void 0,
20719
+ needsCRUD: false,
20720
+ selectedEntities: [],
20721
+ userAuth: "none",
20722
+ billing: false,
20723
+ features: []
20724
+ };
20248
20725
  }
20249
20726
  function calculateRelativePath(from, to) {
20250
20727
  try {
@@ -20286,8 +20763,8 @@ async function main9(options) {
20286
20763
  try {
20287
20764
  Ie("\u{1F680} DoNotDev Project Creator");
20288
20765
  try {
20289
- const { execSync: execSync10 } = await import("node:child_process");
20290
- execSync10("bun --version", { stdio: "ignore" });
20766
+ const { execSync: execSync11 } = await import("node:child_process");
20767
+ execSync11("bun --version", { stdio: "ignore" });
20291
20768
  } catch {
20292
20769
  log.error("bun is required but not installed.");
20293
20770
  Me(
@@ -20357,71 +20834,22 @@ async function main9(options) {
20357
20834
  let appNames = [];
20358
20835
  const appConfigs = {};
20359
20836
  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() === "") {
20837
+ let isFirstApp = true;
20838
+ while (true) {
20839
+ const prompt = isFirstApp ? "What's your first app name? (press Enter to skip)" : "App name (press Enter to finish)";
20840
+ const appNameInput = await askForInput(prompt, "");
20841
+ if (!appNameInput || appNameInput.trim() === "") {
20842
+ if (isFirstApp) break;
20414
20843
  break;
20415
20844
  }
20416
- const trimmedName = appName.trim();
20845
+ const trimmedName = appNameInput.trim();
20846
+ isFirstApp = false;
20417
20847
  if (appNames.includes(trimmedName)) {
20418
20848
  log.warn(`'${trimmedName}' already exists. Choose a different name.`);
20419
20849
  continue;
20420
20850
  }
20421
20851
  if (isReservedAppName(trimmedName)) {
20422
- log.warn(
20423
- `'${trimmedName}' is reserved for framework demos. Choose a different name.`
20424
- );
20852
+ log.warn(`'${trimmedName}' is reserved for framework demos.`);
20425
20853
  continue;
20426
20854
  }
20427
20855
  if (!isValidFileName(trimmedName)) {
@@ -20431,37 +20859,10 @@ async function main9(options) {
20431
20859
  continue;
20432
20860
  }
20433
20861
  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
- };
20862
+ Me(`Configuring "${trimmedName}"...`, "\u2699\uFE0F");
20863
+ const config = await collectAppConfig(trimmedName);
20864
+ appConfigs[trimmedName] = config;
20865
+ if (config.needsBackend) anyAppNeedsBackend = true;
20465
20866
  }
20466
20867
  let installDemoApp = await askForConfirmation(
20467
20868
  "Would you like to install the demo app? (component showcase)",
@@ -20793,7 +21194,7 @@ init_utils();
20793
21194
  init_cli_output();
20794
21195
  init_errors();
20795
21196
  init_pathResolver();
20796
- import { spawnSync as spawnSync9 } from "node:child_process";
21197
+ import { spawnSync as spawnSync10 } from "node:child_process";
20797
21198
  import { EOL as EOL2 } from "node:os";
20798
21199
  async function main10(options = {}) {
20799
21200
  const dryRun = options.dryRun ?? false;
@@ -21270,7 +21671,7 @@ async function runPrettier(rootDir, dryRun, verbose) {
21270
21671
  log.info("DRY RUN: Would run Prettier");
21271
21672
  return stats;
21272
21673
  }
21273
- const result = spawnSync9("bunx", prettierArgs, {
21674
+ const result = spawnSync10("bunx", prettierArgs, {
21274
21675
  cwd: rootDir,
21275
21676
  stdio: "pipe",
21276
21677
  shell: true,