@donotdev/cli 0.0.16 → 0.0.18

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 (87) hide show
  1. package/dependencies-matrix.json +38 -26
  2. package/dist/bin/commands/bump.js +9 -2
  3. package/dist/bin/commands/create-app.js +185 -81
  4. package/dist/bin/commands/create-project.js +186 -85
  5. package/dist/bin/commands/deploy.js +51 -20
  6. package/dist/bin/commands/doctor.js +249 -56
  7. package/dist/bin/commands/emu.js +18 -20
  8. package/dist/bin/commands/make-admin.js +30 -10
  9. package/dist/bin/commands/setup.js +512 -122
  10. package/dist/bin/commands/type-check.d.ts.map +1 -1
  11. package/dist/bin/commands/type-check.js +7 -3
  12. package/dist/bin/commands/type-check.js.map +1 -1
  13. package/dist/bin/dndev.js +9 -6
  14. package/dist/bin/donotdev.js +35 -20
  15. package/dist/index.js +262 -129
  16. package/package.json +1 -1
  17. package/templates/root-consumer/.claude/commands/brainstorm.md.example +15 -1
  18. package/templates/root-consumer/.claude/commands/build.md.example +24 -2
  19. package/templates/root-consumer/.claude/commands/design.md.example +17 -0
  20. package/templates/root-consumer/.claude/commands/polish.md.example +17 -0
  21. package/templates/root-consumer/AI.md.example +50 -18
  22. package/templates/root-consumer/guides/dndev/ENV_SETUP.md.example +6 -6
  23. package/templates/root-consumer/guides/dndev/INDEX.md.example +2 -2
  24. package/templates/root-consumer/guides/dndev/SETUP_AUTH.md.example +13 -6
  25. package/templates/root-consumer/guides/dndev/SETUP_CRUD.md.example +149 -1086
  26. package/templates/root-consumer/guides/dndev/SETUP_FIREBASE.md.example +68 -16
  27. package/templates/root-consumer/guides/dndev/SETUP_FUNCTIONS.md.example +6 -111
  28. package/templates/root-consumer/guides/dndev/SETUP_PAGES.md.example +64 -0
  29. package/templates/root-consumer/guides/dndev/SETUP_SUPABASE.md.example +123 -32
  30. package/templates/root-consumer/guides/dndev/SETUP_VERCEL.md.example +108 -91
  31. package/templates/root-consumer/guides/dndev/advanced/EMULATORS.md.example +2 -2
  32. package/templates/root-consumer/guides/wai-way/blueprints/0_brainstorm.md.example +1 -1
  33. package/dist/bin/commands/firebase-setup.d.ts +0 -6
  34. package/dist/bin/commands/firebase-setup.d.ts.map +0 -1
  35. package/dist/bin/commands/firebase-setup.js +0 -7
  36. package/dist/bin/commands/firebase-setup.js.map +0 -1
  37. package/dist/bin/commands/supabase-setup.d.ts +0 -6
  38. package/dist/bin/commands/supabase-setup.d.ts.map +0 -1
  39. package/dist/bin/commands/supabase-setup.js +0 -7
  40. package/dist/bin/commands/supabase-setup.js.map +0 -1
  41. package/templates/functions-firebase/functions-firebase/README.md.example +0 -123
  42. package/templates/functions-firebase/functions-firebase/build.mjs.example +0 -5
  43. package/templates/functions-firebase/functions-firebase/src/auth/getCustomClaims.ts.example +0 -19
  44. package/templates/functions-firebase/functions-firebase/src/auth/getUserAuthStatus.ts.example +0 -21
  45. package/templates/functions-firebase/functions-firebase/src/auth/index.ts.example +0 -11
  46. package/templates/functions-firebase/functions-firebase/src/auth/removeCustomClaims.ts.example +0 -21
  47. package/templates/functions-firebase/functions-firebase/src/auth/setCustomClaims.ts.example +0 -21
  48. package/templates/functions-firebase/functions-firebase/src/billing/handleStripeWebhook.ts.example +0 -24
  49. package/templates/functions-firebase/functions-firebase/src/billing/index.ts.example +0 -10
  50. package/templates/functions-firebase/functions-firebase/src/billing/processPaymentSuccess.ts.example +0 -14
  51. package/templates/functions-firebase/functions-firebase/src/billing/refreshSubscriptionStatus.ts.example +0 -14
  52. package/templates/functions-firebase/functions-firebase/src/index.ts.example +0 -39
  53. package/templates/functions-firebase/functions-firebase/src/oauth/checkGitHubAccess.ts.example +0 -14
  54. package/templates/functions-firebase/functions-firebase/src/oauth/disconnect.ts.example +0 -14
  55. package/templates/functions-firebase/functions-firebase/src/oauth/exchangeToken.ts.example +0 -14
  56. package/templates/functions-firebase/functions-firebase/src/oauth/getConnections.ts.example +0 -14
  57. package/templates/functions-firebase/functions-firebase/src/oauth/grantGitHubAccess.ts.example +0 -14
  58. package/templates/functions-firebase/functions-firebase/src/oauth/index.ts.example +0 -17
  59. package/templates/functions-firebase/functions-firebase/src/oauth/refreshToken.ts.example +0 -14
  60. package/templates/functions-firebase/functions-firebase/src/oauth/revokeGitHubAccess.ts.example +0 -14
  61. package/templates/functions-firebase/functions-firebase/tsconfig.json.example +0 -21
  62. package/templates/functions-vercel/functions-vercel/README.md.example +0 -116
  63. package/templates/functions-vercel/functions-vercel/build.mjs.example +0 -52
  64. package/templates/functions-vercel/functions-vercel/src/api/auth/getCustomClaims.ts.example +0 -20
  65. package/templates/functions-vercel/functions-vercel/src/api/auth/getUserAuthStatus.ts.example +0 -20
  66. package/templates/functions-vercel/functions-vercel/src/api/auth/removeCustomClaims.ts.example +0 -20
  67. package/templates/functions-vercel/functions-vercel/src/api/auth/setCustomClaims.ts.example +0 -20
  68. package/templates/functions-vercel/functions-vercel/src/api/billing/handleStripeWebhook.ts.example +0 -20
  69. package/templates/functions-vercel/functions-vercel/src/api/billing/processPaymentSuccess.ts.example +0 -20
  70. package/templates/functions-vercel/functions-vercel/src/api/billing/refreshSubscriptionStatus.ts.example +0 -20
  71. package/templates/functions-vercel/functions-vercel/src/api/crud/createEntity.ts.example +0 -20
  72. package/templates/functions-vercel/functions-vercel/src/api/crud/deleteEntity.ts.example +0 -20
  73. package/templates/functions-vercel/functions-vercel/src/api/crud/getEntity.ts.example +0 -20
  74. package/templates/functions-vercel/functions-vercel/src/api/crud/listEntities.ts.example +0 -20
  75. package/templates/functions-vercel/functions-vercel/src/api/crud/updateEntity.ts.example +0 -20
  76. package/templates/functions-vercel/functions-vercel/src/api/oauth/checkGitHubAccess.ts.example +0 -20
  77. package/templates/functions-vercel/functions-vercel/src/api/oauth/disconnect.ts.example +0 -20
  78. package/templates/functions-vercel/functions-vercel/src/api/oauth/exchangeToken.ts.example +0 -20
  79. package/templates/functions-vercel/functions-vercel/src/api/oauth/getConnections.ts.example +0 -20
  80. package/templates/functions-vercel/functions-vercel/src/api/oauth/grantGitHubAccess.ts.example +0 -20
  81. package/templates/functions-vercel/functions-vercel/src/api/oauth/refreshToken.ts.example +0 -20
  82. package/templates/functions-vercel/functions-vercel/src/api/oauth/revokeGitHubAccess.ts.example +0 -20
  83. package/templates/functions-vercel/functions-vercel/tsconfig.json.example +0 -21
  84. package/templates/functions-vercel/functions-vercel/vercel.json.example +0 -14
  85. package/templates/github/github/workflows/firebase-deploy.yml.example +0 -79
  86. /package/templates/functions-firebase/{functions-firebase/.env.example.example → .env.example} +0 -0
  87. /package/templates/functions-vercel/{functions-vercel/.env.example.example → .env.example} +0 -0
@@ -8714,7 +8714,11 @@ function getMatrixPath(mode) {
8714
8714
  const executionMode = mode || detectExecutionMode();
8715
8715
  if (executionMode === "development") {
8716
8716
  const templatesRoot = getTemplatesRoot();
8717
- const devPath = normalizePath(templatesRoot, "..", "dependencies-matrix.json");
8717
+ const devPath = normalizePath(
8718
+ templatesRoot,
8719
+ "..",
8720
+ "dependencies-matrix.json"
8721
+ );
8718
8722
  if (pathExists(devPath)) {
8719
8723
  return devPath;
8720
8724
  }
@@ -8791,34 +8795,67 @@ var APP_QUESTIONNAIRE = [
8791
8795
  ]
8792
8796
  },
8793
8797
  {
8794
- id: "needsBackend",
8795
- question: "Include backend functions?",
8796
- type: "confirm",
8797
- defaultValue: false
8798
+ id: "host",
8799
+ question: "Where will you host?",
8800
+ type: "select",
8801
+ condition: (answers) => answers.framework !== "expo",
8802
+ defaultValue: (answers) => {
8803
+ return answers.framework === "nextjs" ? "vercel" : "none";
8804
+ },
8805
+ choices: [
8806
+ { title: "None \u2014 no hosting config", value: "none" },
8807
+ { title: "Vercel \u2014 edge CDN + serverless", value: "vercel" },
8808
+ { title: "Firebase Hosting \u2014 Google CDN", value: "firebase" }
8809
+ ]
8810
+ },
8811
+ {
8812
+ id: "functions",
8813
+ question: "Serverless functions?",
8814
+ type: "select",
8815
+ defaultValue: "none",
8816
+ // Choices filtered dynamically based on host
8817
+ choices: [
8818
+ { title: "None", value: "none" },
8819
+ { title: "Vercel Functions \u2014 edge + node.js", value: "vercel" },
8820
+ { title: "Firebase Functions \u2014 Google Cloud", value: "firebase" },
8821
+ { title: "Supabase Edge Functions \u2014 Deno", value: "supabase" }
8822
+ ],
8823
+ choicesFilter: (answers) => {
8824
+ const host = answers.host ?? "none";
8825
+ const framework = answers.framework;
8826
+ if (framework === "expo") return ["none", "firebase", "supabase"];
8827
+ if (host === "firebase") return ["none", "firebase"];
8828
+ if (host === "vercel") return ["none", "vercel", "supabase"];
8829
+ return ["none", "vercel", "firebase", "supabase"];
8830
+ }
8798
8831
  },
8799
8832
  {
8800
- id: "backendPlatform",
8801
- question: "Which backend platform?",
8833
+ id: "backend",
8834
+ question: "Backend / BaaS provider?",
8802
8835
  type: "select",
8803
- dependsOn: "needsBackend",
8804
- condition: (answers) => answers.needsBackend === true,
8805
8836
  defaultValue: (answers) => {
8806
- return answers.framework === "nextjs" ? "vercel" : "firebase";
8837
+ if (answers.functions === "firebase") return "firebase";
8838
+ if (answers.functions === "supabase") return "supabase";
8839
+ return "none";
8807
8840
  },
8808
8841
  choices: [
8842
+ { title: "None \u2014 frontend only", value: "none" },
8809
8843
  {
8810
- title: "Firebase \u2014 Full-stack platform (hosting + functions + auth + firestore)",
8844
+ title: "Firebase \u2014 Auth + Firestore + Storage",
8811
8845
  value: "firebase"
8812
8846
  },
8813
- {
8814
- title: "Vercel \u2014 Serverless functions (edge + node.js)",
8815
- value: "vercel"
8816
- },
8817
8847
  {
8818
8848
  title: "Supabase \u2014 Postgres + Auth + Storage",
8819
8849
  value: "supabase"
8820
8850
  }
8821
- ]
8851
+ ],
8852
+ choicesFilter: (answers) => {
8853
+ const functions = answers.functions ?? "none";
8854
+ if (functions === "firebase") return ["none", "firebase"];
8855
+ if (functions === "supabase") return ["none", "supabase"];
8856
+ if (functions === "vercel") return ["none"];
8857
+ return ["none", "firebase", "supabase"];
8858
+ }
8822
8859
  }
8823
8860
  ];
8824
8861
  var PROJECT_QUESTIONNAIRE = [
@@ -8836,12 +8873,13 @@ var PROJECT_QUESTIONNAIRE = [
8836
8873
  }
8837
8874
  ];
8838
8875
  function aggregateRequirements(answers) {
8876
+ const hasBackend = answers.backend && answers.backend !== "none" || answers.functions && answers.functions !== "none";
8839
8877
  const requirements = {
8840
8878
  entities: [],
8841
8879
  // Entities created in apps/<app>/entities/ (v1), root entities/ for v2
8842
8880
  billing: true,
8843
8881
  // Always on (useStripeBillingSafe handles missing package)
8844
- backend: answers.needsBackend || false,
8882
+ backend: hasBackend,
8845
8883
  features: [],
8846
8884
  template: "vite",
8847
8885
  // Only Vite for now
@@ -8865,6 +8903,10 @@ async function runQuestionnaire(questions, initialAnswers, showWIP, askFor) {
8865
8903
  if (!showWIP) {
8866
8904
  choices = choices.filter((choice) => !choice.wip);
8867
8905
  }
8906
+ if (question.choicesFilter) {
8907
+ const allowed = question.choicesFilter(answers);
8908
+ choices = choices.filter((c) => allowed.includes(c.value));
8909
+ }
8868
8910
  if (choices.length === 0) continue;
8869
8911
  answer = await askFor.selection(question.question, choices);
8870
8912
  break;
@@ -9065,34 +9107,22 @@ init_pathResolver();
9065
9107
 
9066
9108
  // packages/tooling/src/scaffolding/scaffold-matrix.ts
9067
9109
  init_utils();
9068
- var MATRIX = [
9069
- { builder: "vite", backend: "none", baseTemplate: "app-vite", overlay: null, deployConfig: null, functionsTemplate: null },
9070
- { builder: "vite", backend: "firebase", baseTemplate: "app-vite", overlay: "overlay-firebase", deployConfig: "firebase", functionsTemplate: "functions-firebase" },
9071
- { builder: "vite", backend: "supabase", baseTemplate: "app-vite", overlay: "overlay-supabase", deployConfig: "vercel-supabase", functionsTemplate: "functions-supabase" },
9072
- { builder: "vite", backend: "vercel", baseTemplate: "app-vite", overlay: "overlay-vercel", deployConfig: "vercel-vercel", functionsTemplate: "functions-vercel" },
9073
- { builder: "nextjs", backend: "none", baseTemplate: "app-next", overlay: null, deployConfig: null, functionsTemplate: null },
9074
- { builder: "nextjs", backend: "firebase", baseTemplate: "app-next", overlay: "overlay-firebase", deployConfig: "firebase", functionsTemplate: "functions-firebase" },
9075
- { builder: "nextjs", backend: "supabase", baseTemplate: "app-next", overlay: "overlay-supabase", deployConfig: "vercel-supabase", functionsTemplate: "functions-supabase" },
9076
- { builder: "nextjs", backend: "vercel", baseTemplate: "app-next", overlay: "overlay-vercel", deployConfig: "vercel-vercel", functionsTemplate: "functions-vercel" },
9077
- { builder: "expo", backend: "none", baseTemplate: "app-expo", overlay: null, deployConfig: null, functionsTemplate: null },
9078
- { builder: "expo", backend: "firebase", baseTemplate: "app-expo", overlay: "overlay-firebase", deployConfig: null, functionsTemplate: "functions-firebase" },
9079
- { builder: "expo", backend: "supabase", baseTemplate: "app-expo", overlay: "overlay-supabase", deployConfig: null, functionsTemplate: "functions-supabase" },
9080
- { builder: "demo", backend: "none", baseTemplate: "app-demo", overlay: null, deployConfig: null, functionsTemplate: null }
9081
- ];
9082
- function comboKey(builder, backend) {
9083
- return `${builder}-${backend}`;
9084
- }
9085
- var ROWS_BY_KEY = /* @__PURE__ */ new Map();
9086
- for (const row of MATRIX) {
9087
- ROWS_BY_KEY.set(comboKey(row.builder, row.backend), row);
9110
+ function resolveDeployConfig(host, backend) {
9111
+ if (host === "none") return null;
9112
+ if (host === "firebase") return "firebase";
9113
+ if (backend === "supabase") return "vercel-supabase";
9114
+ return "vercel-vercel";
9088
9115
  }
9089
- function getScaffoldRow(builder, backend) {
9090
- const key = comboKey(builder, backend);
9091
- const row = ROWS_BY_KEY.get(key);
9092
- if (!row) {
9093
- throw new Error(`Unsupported scaffold combo: ${key}. Supported: ${[...ROWS_BY_KEY.keys()].join(", ")}`);
9094
- }
9095
- return row;
9116
+ function getScaffoldParts(builder, host, functions, backend) {
9117
+ const baseTemplate = builder === "nextjs" ? "app-next" : builder === "demo" ? "app-demo" : `app-${builder}`;
9118
+ return {
9119
+ builder,
9120
+ backend,
9121
+ baseTemplate,
9122
+ overlay: backend !== "none" ? `overlay-${backend}` : null,
9123
+ deployConfig: resolveDeployConfig(host, backend),
9124
+ functionsTemplate: functions !== "none" ? `functions-${functions}` : null
9125
+ };
9096
9126
  }
9097
9127
 
9098
9128
  // packages/tooling/src/scaffolding/create-app.ts
@@ -9132,11 +9162,15 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
9132
9162
  input: askForInput
9133
9163
  });
9134
9164
  const requirements = aggregateRequirements(answers);
9135
- const defaultPlatform = answers.framework === "nextjs" ? "vercel" : "firebase";
9165
+ const framework = answers.framework || "vite";
9166
+ const host2 = answers.host || "none";
9167
+ const functions2 = answers.functions || "none";
9168
+ const backend2 = answers.backend || "none";
9136
9169
  appConfig = {
9137
- template: answers.framework || "vite",
9138
- needsBackend: answers.needsBackend || false,
9139
- backendPlatform: answers.needsBackend ? answers.backendPlatform || defaultPlatform : void 0,
9170
+ template: framework,
9171
+ host: host2,
9172
+ functions: functions2,
9173
+ backend: backend2,
9140
9174
  needsCRUD: true,
9141
9175
  // Always on
9142
9176
  selectedEntities: [],
@@ -9182,12 +9216,14 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
9182
9216
  s.start(`Creating app: ${appName}`);
9183
9217
  await ensureDir(appDir);
9184
9218
  const builder = appTemplate;
9185
- const backend = appConfig.needsBackend && appConfig.backendPlatform ? appConfig.backendPlatform : "none";
9186
- const row = getScaffoldRow(builder, backend);
9219
+ const host = appConfig.host ?? "none";
9220
+ const functions = appConfig.functions ?? "none";
9221
+ const backend = appConfig.backend ?? "none";
9222
+ const row = getScaffoldParts(builder, host, functions, backend);
9187
9223
  const templateDir = row.baseTemplate;
9188
9224
  const firebaseProjectId = (appConfig?.firebaseProjectId ?? "").trim() || appName.toLowerCase().replace(/\s+/g, "-");
9189
9225
  const firebaseRegion = appConfig?.firebaseRegion ?? "europe-west1";
9190
- const backendPlatform = appConfig.backendPlatform ?? "firebase";
9226
+ const backendPlatform = backend !== "none" ? backend : "firebase";
9191
9227
  const deployConfig = row.deployConfig;
9192
9228
  const replacements = {
9193
9229
  projectName: appName,
@@ -9246,13 +9282,16 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
9246
9282
  const variantFile = `src/config/providers.${appTemplate}.ts.example`;
9247
9283
  if (overlayFiles.includes(variantFile)) continue;
9248
9284
  }
9249
- const providersVariant = file.match(/^src\/config\/providers\.(\w+)\.ts\.example$/);
9285
+ const providersVariant = file.match(
9286
+ /^src\/config\/providers\.(\w+)\.ts\.example$/
9287
+ );
9250
9288
  if (providersVariant) {
9251
9289
  if (providersVariant[1] !== appTemplate) continue;
9252
9290
  const destPath2 = joinPath(appDir, "src/config/providers.ts");
9253
9291
  await ensureDir(getDirname(destPath2));
9254
9292
  await copy(joinPath(overlayDir, file), destPath2, { overwrite: true });
9255
- if (await isTextFile(destPath2)) await replacePlaceholders(destPath2, replacements);
9293
+ if (await isTextFile(destPath2))
9294
+ await replacePlaceholders(destPath2, replacements);
9256
9295
  continue;
9257
9296
  }
9258
9297
  const sourcePath = joinPath(overlayDir, file);
@@ -9277,16 +9316,28 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
9277
9316
  }
9278
9317
  if (deployConfig === "vercel-supabase") {
9279
9318
  const vercelPath = joinPath(appDir, "vercel.json");
9280
- const headersFragmentPath = joinPath(overlayDir, "vercel.headers.example");
9319
+ const headersFragmentPath = joinPath(
9320
+ overlayDir,
9321
+ "vercel.headers.example"
9322
+ );
9281
9323
  const fullVercelPath = joinPath(overlayDir, "vercel.json.example");
9282
9324
  if (pathExists(vercelPath) && pathExists(headersFragmentPath)) {
9283
9325
  const vercelJson = readSync(vercelPath, { format: "json" });
9284
- const headersFragment = readSync(headersFragmentPath, { format: "json" });
9285
- vercelJson.headers = [...vercelJson.headers ?? [], ...headersFragment];
9286
- await write(vercelPath, vercelJson, { format: "json", overwrite: true });
9326
+ const headersFragment = readSync(headersFragmentPath, {
9327
+ format: "json"
9328
+ });
9329
+ vercelJson.headers = [
9330
+ ...vercelJson.headers ?? [],
9331
+ ...headersFragment
9332
+ ];
9333
+ await write(vercelPath, vercelJson, {
9334
+ format: "json",
9335
+ overwrite: true
9336
+ });
9287
9337
  } else if (pathExists(fullVercelPath)) {
9288
9338
  await copy(fullVercelPath, vercelPath);
9289
- if (await isTextFile(vercelPath)) await replacePlaceholders(vercelPath, replacements);
9339
+ if (await isTextFile(vercelPath))
9340
+ await replacePlaceholders(vercelPath, replacements);
9290
9341
  }
9291
9342
  }
9292
9343
  }
@@ -9306,9 +9357,13 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
9306
9357
  });
9307
9358
  if (row.functionsTemplate) {
9308
9359
  const functionsTemplateName = row.functionsTemplate;
9309
- const functionsTemplateExists = pathExists(joinPath(templatesRoot, functionsTemplateName));
9360
+ const functionsTemplateExists = pathExists(
9361
+ joinPath(templatesRoot, functionsTemplateName)
9362
+ );
9310
9363
  if (!functionsTemplateExists) {
9311
- log.warn(`Functions template "${functionsTemplateName}" not found \u2014 skipping functions scaffolding.`);
9364
+ log.warn(
9365
+ `Functions template "${functionsTemplateName}" not found \u2014 skipping functions scaffolding.`
9366
+ );
9312
9367
  } else {
9313
9368
  const isSupabaseFunctions = functionsTemplateName === "functions-supabase";
9314
9369
  const functionsRootDir = isSupabaseFunctions ? appDir : joinPath(appDir, "functions");
@@ -9316,7 +9371,10 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
9316
9371
  const functionsPackageJson = generatePackageJson(
9317
9372
  functionsTemplateName,
9318
9373
  executionMode,
9319
- { appName, platform: row.functionsTemplate.replace("functions-", "") }
9374
+ {
9375
+ appName,
9376
+ platform: row.functionsTemplate.replace("functions-", "")
9377
+ }
9320
9378
  );
9321
9379
  const packageJsonPath2 = joinPath(functionsRootDir, "package.json");
9322
9380
  await ensureDir(functionsRootDir);
@@ -9352,13 +9410,19 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
9352
9410
  }
9353
9411
  const deploymentTemplateDir = joinPath(templatesRoot, "root-consumer");
9354
9412
  if (deployConfig === "firebase") {
9355
- const firebaseJsonSource = joinPath(deploymentTemplateDir, "firebase.json.example");
9413
+ const firebaseJsonSource = joinPath(
9414
+ deploymentTemplateDir,
9415
+ "firebase.json.example"
9416
+ );
9356
9417
  if (pathExists(firebaseJsonSource)) {
9357
9418
  await copy(firebaseJsonSource, joinPath(appDir, "firebase.json"));
9358
9419
  const firebaseJsonDest = joinPath(appDir, "firebase.json");
9359
- if (await isTextFile(firebaseJsonDest)) await replacePlaceholders(firebaseJsonDest, replacements);
9420
+ if (await isTextFile(firebaseJsonDest))
9421
+ await replacePlaceholders(firebaseJsonDest, replacements);
9360
9422
  if (appTemplate === "nextjs") {
9361
- const firebaseJson = readSync(firebaseJsonDest, { format: "json" });
9423
+ const firebaseJson = readSync(firebaseJsonDest, {
9424
+ format: "json"
9425
+ });
9362
9426
  if (firebaseJson.hosting?.rewrites) {
9363
9427
  firebaseJson.hosting.rewrites = firebaseJson.hosting.rewrites.filter(
9364
9428
  (r2) => r2.destination !== "/index.html"
@@ -9367,17 +9431,28 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
9367
9431
  if (firebaseJson.hosting) {
9368
9432
  firebaseJson.hosting.public = "out";
9369
9433
  }
9370
- await write(firebaseJsonDest, firebaseJson, { format: "json", overwrite: true });
9434
+ await write(firebaseJsonDest, firebaseJson, {
9435
+ format: "json",
9436
+ overwrite: true
9437
+ });
9371
9438
  }
9372
9439
  }
9373
- const firebasercSource = joinPath(deploymentTemplateDir, ".firebaserc.example");
9440
+ const firebasercSource = joinPath(
9441
+ deploymentTemplateDir,
9442
+ ".firebaserc.example"
9443
+ );
9374
9444
  if (pathExists(firebasercSource)) {
9375
9445
  await copy(firebasercSource, joinPath(appDir, ".firebaserc"));
9376
9446
  const firebasercDest = joinPath(appDir, ".firebaserc");
9377
- if (await isTextFile(firebasercDest)) await replacePlaceholders(firebasercDest, replacements);
9447
+ if (await isTextFile(firebasercDest))
9448
+ await replacePlaceholders(firebasercDest, replacements);
9378
9449
  }
9379
9450
  if (row.functionsTemplate === "functions-firebase") {
9380
- for (const example of ["firestore.rules.example", "firestore.indexes.json.example", "storage.rules.example"]) {
9451
+ for (const example of [
9452
+ "firestore.rules.example",
9453
+ "firestore.indexes.json.example",
9454
+ "storage.rules.example"
9455
+ ]) {
9381
9456
  const src = joinPath(deploymentTemplateDir, example);
9382
9457
  if (pathExists(src)) {
9383
9458
  await copy(src, joinPath(appDir, example.replace(".example", "")));
@@ -9393,11 +9468,22 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
9393
9468
  {
9394
9469
  source: "functions",
9395
9470
  codebase: "default",
9396
- ignore: ["node_modules", ".git", "firebase-debug.log", "firebase-debug.*.log", "**/.*", "**/*.test.ts", "**/__tests__/**"],
9471
+ ignore: [
9472
+ "node_modules",
9473
+ ".git",
9474
+ "firebase-debug.log",
9475
+ "firebase-debug.*.log",
9476
+ "**/.*",
9477
+ "**/*.test.ts",
9478
+ "**/__tests__/**"
9479
+ ],
9397
9480
  runtime: "nodejs22"
9398
9481
  }
9399
9482
  ],
9400
- firestore: { rules: "firestore.rules", indexes: "firestore.indexes.json" },
9483
+ firestore: {
9484
+ rules: "firestore.rules",
9485
+ indexes: "firestore.indexes.json"
9486
+ },
9401
9487
  storage: { rules: "storage.rules" },
9402
9488
  emulators: {
9403
9489
  auth: { port: 9099 },
@@ -9407,15 +9493,26 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
9407
9493
  ui: { enabled: true, port: 4e3 }
9408
9494
  }
9409
9495
  };
9410
- await write(firebaseJsonPath, expoFirebaseJson, { format: "json", overwrite: true });
9496
+ await write(firebaseJsonPath, expoFirebaseJson, {
9497
+ format: "json",
9498
+ overwrite: true
9499
+ });
9411
9500
  }
9412
- const firebasercSource = joinPath(deploymentTemplateDir, ".firebaserc.example");
9501
+ const firebasercSource = joinPath(
9502
+ deploymentTemplateDir,
9503
+ ".firebaserc.example"
9504
+ );
9413
9505
  const firebasercDest = joinPath(appDir, ".firebaserc");
9414
9506
  if (pathExists(firebasercSource) && !pathExists(firebasercDest)) {
9415
9507
  await copy(firebasercSource, firebasercDest);
9416
- if (await isTextFile(firebasercDest)) await replacePlaceholders(firebasercDest, replacements);
9417
- }
9418
- for (const example of ["firestore.rules.example", "firestore.indexes.json.example", "storage.rules.example"]) {
9508
+ if (await isTextFile(firebasercDest))
9509
+ await replacePlaceholders(firebasercDest, replacements);
9510
+ }
9511
+ for (const example of [
9512
+ "firestore.rules.example",
9513
+ "firestore.indexes.json.example",
9514
+ "storage.rules.example"
9515
+ ]) {
9419
9516
  const src = joinPath(deploymentTemplateDir, example);
9420
9517
  const dest = joinPath(appDir, example.replace(".example", ""));
9421
9518
  if (pathExists(src) && !pathExists(dest)) {
@@ -9424,11 +9521,15 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
9424
9521
  }
9425
9522
  }
9426
9523
  if (deployConfig === "vercel-vercel") {
9427
- const vercelJsonSource = joinPath(deploymentTemplateDir, "vercel.json.example");
9524
+ const vercelJsonSource = joinPath(
9525
+ deploymentTemplateDir,
9526
+ "vercel.json.example"
9527
+ );
9428
9528
  if (pathExists(vercelJsonSource)) {
9429
9529
  await copy(vercelJsonSource, joinPath(appDir, "vercel.json"));
9430
9530
  const vercelJsonDest = joinPath(appDir, "vercel.json");
9431
- if (await isTextFile(vercelJsonDest)) await replacePlaceholders(vercelJsonDest, replacements);
9531
+ if (await isTextFile(vercelJsonDest))
9532
+ await replacePlaceholders(vercelJsonDest, replacements);
9432
9533
  }
9433
9534
  }
9434
9535
  const backendInfo = row.functionsTemplate ? ` with ${row.functionsTemplate.replace("functions-", "")} functions` : "";
@@ -9472,12 +9573,11 @@ async function collectAppConfig(appName) {
9472
9573
  input: askForInput
9473
9574
  });
9474
9575
  const framework = answers.framework || "vite";
9475
- const needsBackend = answers.needsBackend || false;
9476
- const defaultPlatform = framework === "nextjs" ? "vercel" : "firebase";
9477
9576
  return {
9478
9577
  template: framework,
9479
- needsBackend,
9480
- backendPlatform: needsBackend ? answers.backendPlatform || defaultPlatform : void 0,
9578
+ host: answers.host || "none",
9579
+ functions: answers.functions || "none",
9580
+ backend: answers.backend || "none",
9481
9581
  needsCRUD: false,
9482
9582
  selectedEntities: [],
9483
9583
  userAuth: "none",
@@ -9624,7 +9724,7 @@ async function main(options) {
9624
9724
  Me(`Configuring "${trimmedName}"...`, "\u2699\uFE0F");
9625
9725
  const config = await collectAppConfig(trimmedName);
9626
9726
  appConfigs[trimmedName] = config;
9627
- if (config.needsBackend) anyAppNeedsBackend = true;
9727
+ if (config.backend !== "none" || config.functions !== "none") anyAppNeedsBackend = true;
9628
9728
  }
9629
9729
  let installDemoApp = await askForConfirmation(
9630
9730
  "Would you like to install the demo app? (component showcase)",
@@ -9700,8 +9800,9 @@ async function main(options) {
9700
9800
  log.warn(`Missing config for app "${appName}", using defaults`);
9701
9801
  appConfigs[appName] = {
9702
9802
  template: "vite",
9703
- needsBackend: false,
9704
- backendPlatform: void 0,
9803
+ host: "none",
9804
+ functions: "none",
9805
+ backend: "none",
9705
9806
  needsCRUD: false,
9706
9807
  selectedEntities: [],
9707
9808
  userAuth: "none",
@@ -9749,7 +9850,7 @@ async function main(options) {
9749
9850
  overwrite: true
9750
9851
  });
9751
9852
  const relativeMonorepoPath = executionMode === "development" ? calculateRelativePath(projectDirNormalized, monorepoRoot) : "";
9752
- const primaryPlatform = Object.values(appConfigs).find((c) => c.backendPlatform)?.backendPlatform ?? "firebase";
9853
+ const primaryPlatform = Object.values(appConfigs).find((c) => c.backend !== "none")?.backend ?? "firebase";
9753
9854
  const rootPackageJson = generatePackageJson(
9754
9855
  "consumer-root",
9755
9856
  executionMode,
@@ -7951,7 +7951,14 @@ function readServiceAccountKey(filePath) {
7951
7951
  throw new DoNotDevError(
7952
7952
  `Invalid service account key: missing required fields`,
7953
7953
  DO_NOT_DEV_ERROR_CODES.CONFIG_INVALID,
7954
- { context: { filePath, missingFields: ["project_id", "private_key", "client_email"].filter((f) => !key[f]) } }
7954
+ {
7955
+ context: {
7956
+ filePath,
7957
+ missingFields: ["project_id", "private_key", "client_email"].filter(
7958
+ (f) => !key[f]
7959
+ )
7960
+ }
7961
+ }
7955
7962
  );
7956
7963
  }
7957
7964
  return content;
@@ -8198,9 +8205,7 @@ function getCLIInstallInstructions(tool) {
8198
8205
  "Or: winget install Supabase.CLI",
8199
8206
  "Or download from: https://github.com/supabase/cli/releases"
8200
8207
  ],
8201
- darwin: [
8202
- "brew install supabase/tap/supabase"
8203
- ],
8208
+ darwin: ["brew install supabase/tap/supabase"],
8204
8209
  linux: [
8205
8210
  "brew install supabase/tap/supabase",
8206
8211
  "Or see: https://supabase.com/docs/guides/cli"
@@ -8208,7 +8213,11 @@ function getCLIInstallInstructions(tool) {
8208
8213
  },
8209
8214
  [CLI_TOOLS.VERCEL]: {
8210
8215
  win32: ["npm install -g vercel", "Or: npx vercel (no install)"],
8211
- darwin: ["npm install -g vercel", "Or: brew install vercel", "Or: npx vercel"],
8216
+ darwin: [
8217
+ "npm install -g vercel",
8218
+ "Or: brew install vercel",
8219
+ "Or: npx vercel"
8220
+ ],
8212
8221
  linux: ["npm install -g vercel", "Or: npx vercel"]
8213
8222
  },
8214
8223
  [CLI_TOOLS.SENTRY_CLI]: {
@@ -16356,7 +16365,9 @@ async function deploySupabaseFunctions(appDir, config) {
16356
16365
  const supabaseDir = joinPath(appDir, "supabase");
16357
16366
  const functionsDir = joinPath(supabaseDir, "functions");
16358
16367
  if (!pathExists(functionsDir)) {
16359
- log.warn("No supabase/functions/ directory found. Skipping Supabase functions deployment.");
16368
+ log.warn(
16369
+ "No supabase/functions/ directory found. Skipping Supabase functions deployment."
16370
+ );
16360
16371
  return;
16361
16372
  }
16362
16373
  requireCLI(
@@ -16372,10 +16383,14 @@ async function deploySupabaseFunctions(appDir, config) {
16372
16383
  return pathExists(indexPath);
16373
16384
  });
16374
16385
  if (functionDirs.length === 0) {
16375
- log.warn("No Edge Functions found in supabase/functions/. Skipping deployment.");
16386
+ log.warn(
16387
+ "No Edge Functions found in supabase/functions/. Skipping deployment."
16388
+ );
16376
16389
  return;
16377
16390
  }
16378
- log.info(`Found ${functionDirs.length} Edge Function(s): ${functionDirs.join(", ")}`);
16391
+ log.info(
16392
+ `Found ${functionDirs.length} Edge Function(s): ${functionDirs.join(", ")}`
16393
+ );
16379
16394
  const s = Y2();
16380
16395
  for (const functionName of functionDirs) {
16381
16396
  s.start(`Deploying Edge Function: ${functionName}...`);
@@ -16394,7 +16409,12 @@ async function deploySupabaseFunctions(appDir, config) {
16394
16409
  throw new DoNotDevError(
16395
16410
  `Failed to deploy Supabase Edge Function: ${functionName}`,
16396
16411
  "deployment-failed",
16397
- { context: { functionName, error: error2 instanceof Error ? error2.message : String(error2) } }
16412
+ {
16413
+ context: {
16414
+ functionName,
16415
+ error: error2 instanceof Error ? error2.message : String(error2)
16416
+ }
16417
+ }
16398
16418
  );
16399
16419
  }
16400
16420
  }
@@ -16548,7 +16568,12 @@ init_utils();
16548
16568
  async function deployFrontend(appDir, serviceAccountPath, projectId, config) {
16549
16569
  const s = Y2();
16550
16570
  s.start("Deploying frontend to Firebase Hosting...");
16551
- const args = buildFirebaseDeployArgs("hosting", projectId, config.debug, config.force ?? true);
16571
+ const args = buildFirebaseDeployArgs(
16572
+ "hosting",
16573
+ projectId,
16574
+ config.debug,
16575
+ config.force ?? true
16576
+ );
16552
16577
  const result = executeFirebaseCommand(args, {
16553
16578
  cwd: appDir,
16554
16579
  serviceAccountPath,
@@ -16595,7 +16620,9 @@ async function deployVercelFrontend(appDir, _config) {
16595
16620
  if (result.status !== 0) {
16596
16621
  s.stop("Vercel deployment failed");
16597
16622
  const errOutput = result.stderr?.trim();
16598
- throw new Error(errOutput || `Vercel deploy exited with code ${result.status}`);
16623
+ throw new Error(
16624
+ errOutput || `Vercel deploy exited with code ${result.status}`
16625
+ );
16599
16626
  }
16600
16627
  s.stop("Frontend deployed to Vercel");
16601
16628
  } catch (err) {
@@ -17052,7 +17079,12 @@ async function deployRules(appDir, serviceAccountPath, projectId, config, option
17052
17079
  const targetNames = targets.join(", ");
17053
17080
  const s = Y2();
17054
17081
  s.start(`Deploying ${targetNames}...`);
17055
- const args = buildFirebaseDeployArgs(targets, projectId, config.debug, config.force ?? true);
17082
+ const args = buildFirebaseDeployArgs(
17083
+ targets,
17084
+ projectId,
17085
+ config.debug,
17086
+ config.force ?? true
17087
+ );
17056
17088
  const result = executeFirebaseCommand(args, {
17057
17089
  cwd: appDir,
17058
17090
  serviceAccountPath,
@@ -17426,13 +17458,17 @@ async function main2(options = {}) {
17426
17458
  const availableApps = detectAvailableApps();
17427
17459
  if (availableApps.length === 0) {
17428
17460
  if (providerInfo.hasSupabase && !providerInfo.hasFirebase) {
17429
- log.info("Supabase project detected. Deploying Supabase resources...");
17461
+ log.info(
17462
+ "Supabase project detected. Deploying Supabase resources..."
17463
+ );
17430
17464
  } else {
17431
17465
  log.info("No apps with firebase.json or supabase/ directory found.");
17432
17466
  log.info(
17433
17467
  "To deploy, ensure your app has a firebase.json or supabase/ configuration."
17434
17468
  );
17435
- log.info("Run from a DoNotDev project, or create one with: dndev init");
17469
+ log.info(
17470
+ "Run from a DoNotDev project, or create one with: dndev init"
17471
+ );
17436
17472
  return;
17437
17473
  }
17438
17474
  }
@@ -17712,12 +17748,7 @@ async function main2(options = {}) {
17712
17748
  }
17713
17749
  }
17714
17750
  }
17715
- await deployFunctions(
17716
- appDir,
17717
- serviceAccountPath,
17718
- config.project,
17719
- config
17720
- );
17751
+ await deployFunctions(appDir, serviceAccountPath, config.project, config);
17721
17752
  }
17722
17753
  if (shouldDeployFirebaseRules && serviceAccountPath && config.project && appProviderInfo.firebaseConfig) {
17723
17754
  await deployRules(appDir, serviceAccountPath, config.project, config, {