@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
@@ -8394,7 +8394,11 @@ function getMatrixPath(mode) {
8394
8394
  const executionMode = mode || detectExecutionMode();
8395
8395
  if (executionMode === "development") {
8396
8396
  const templatesRoot = getTemplatesRoot();
8397
- const devPath = normalizePath(templatesRoot, "..", "dependencies-matrix.json");
8397
+ const devPath = normalizePath(
8398
+ templatesRoot,
8399
+ "..",
8400
+ "dependencies-matrix.json"
8401
+ );
8398
8402
  if (pathExists(devPath)) {
8399
8403
  return devPath;
8400
8404
  }
@@ -8471,34 +8475,67 @@ var APP_QUESTIONNAIRE = [
8471
8475
  ]
8472
8476
  },
8473
8477
  {
8474
- id: "needsBackend",
8475
- question: "Include backend functions?",
8476
- type: "confirm",
8477
- defaultValue: false
8478
+ id: "host",
8479
+ question: "Where will you host?",
8480
+ type: "select",
8481
+ condition: (answers) => answers.framework !== "expo",
8482
+ defaultValue: (answers) => {
8483
+ return answers.framework === "nextjs" ? "vercel" : "none";
8484
+ },
8485
+ choices: [
8486
+ { title: "None \u2014 no hosting config", value: "none" },
8487
+ { title: "Vercel \u2014 edge CDN + serverless", value: "vercel" },
8488
+ { title: "Firebase Hosting \u2014 Google CDN", value: "firebase" }
8489
+ ]
8490
+ },
8491
+ {
8492
+ id: "functions",
8493
+ question: "Serverless functions?",
8494
+ type: "select",
8495
+ defaultValue: "none",
8496
+ // Choices filtered dynamically based on host
8497
+ choices: [
8498
+ { title: "None", value: "none" },
8499
+ { title: "Vercel Functions \u2014 edge + node.js", value: "vercel" },
8500
+ { title: "Firebase Functions \u2014 Google Cloud", value: "firebase" },
8501
+ { title: "Supabase Edge Functions \u2014 Deno", value: "supabase" }
8502
+ ],
8503
+ choicesFilter: (answers) => {
8504
+ const host = answers.host ?? "none";
8505
+ const framework = answers.framework;
8506
+ if (framework === "expo") return ["none", "firebase", "supabase"];
8507
+ if (host === "firebase") return ["none", "firebase"];
8508
+ if (host === "vercel") return ["none", "vercel", "supabase"];
8509
+ return ["none", "vercel", "firebase", "supabase"];
8510
+ }
8478
8511
  },
8479
8512
  {
8480
- id: "backendPlatform",
8481
- question: "Which backend platform?",
8513
+ id: "backend",
8514
+ question: "Backend / BaaS provider?",
8482
8515
  type: "select",
8483
- dependsOn: "needsBackend",
8484
- condition: (answers) => answers.needsBackend === true,
8485
8516
  defaultValue: (answers) => {
8486
- return answers.framework === "nextjs" ? "vercel" : "firebase";
8517
+ if (answers.functions === "firebase") return "firebase";
8518
+ if (answers.functions === "supabase") return "supabase";
8519
+ return "none";
8487
8520
  },
8488
8521
  choices: [
8522
+ { title: "None \u2014 frontend only", value: "none" },
8489
8523
  {
8490
- title: "Firebase \u2014 Full-stack platform (hosting + functions + auth + firestore)",
8524
+ title: "Firebase \u2014 Auth + Firestore + Storage",
8491
8525
  value: "firebase"
8492
8526
  },
8493
- {
8494
- title: "Vercel \u2014 Serverless functions (edge + node.js)",
8495
- value: "vercel"
8496
- },
8497
8527
  {
8498
8528
  title: "Supabase \u2014 Postgres + Auth + Storage",
8499
8529
  value: "supabase"
8500
8530
  }
8501
- ]
8531
+ ],
8532
+ choicesFilter: (answers) => {
8533
+ const functions = answers.functions ?? "none";
8534
+ if (functions === "firebase") return ["none", "firebase"];
8535
+ if (functions === "supabase") return ["none", "supabase"];
8536
+ if (functions === "vercel") return ["none"];
8537
+ return ["none", "firebase", "supabase"];
8538
+ }
8502
8539
  }
8503
8540
  ];
8504
8541
  var PROJECT_QUESTIONNAIRE = [
@@ -8516,12 +8553,13 @@ var PROJECT_QUESTIONNAIRE = [
8516
8553
  }
8517
8554
  ];
8518
8555
  function aggregateRequirements(answers) {
8556
+ const hasBackend = answers.backend && answers.backend !== "none" || answers.functions && answers.functions !== "none";
8519
8557
  const requirements = {
8520
8558
  entities: [],
8521
8559
  // Entities created in apps/<app>/entities/ (v1), root entities/ for v2
8522
8560
  billing: true,
8523
8561
  // Always on (useStripeBillingSafe handles missing package)
8524
- backend: answers.needsBackend || false,
8562
+ backend: hasBackend,
8525
8563
  features: [],
8526
8564
  template: "vite",
8527
8565
  // Only Vite for now
@@ -8545,6 +8583,10 @@ async function runQuestionnaire(questions, initialAnswers, showWIP, askFor) {
8545
8583
  if (!showWIP) {
8546
8584
  choices = choices.filter((choice) => !choice.wip);
8547
8585
  }
8586
+ if (question.choicesFilter) {
8587
+ const allowed = question.choicesFilter(answers);
8588
+ choices = choices.filter((c) => allowed.includes(c.value));
8589
+ }
8548
8590
  if (choices.length === 0) continue;
8549
8591
  answer = await askFor.selection(question.question, choices);
8550
8592
  break;
@@ -8745,34 +8787,22 @@ init_pathResolver();
8745
8787
 
8746
8788
  // packages/tooling/src/scaffolding/scaffold-matrix.ts
8747
8789
  init_utils();
8748
- var MATRIX = [
8749
- { builder: "vite", backend: "none", baseTemplate: "app-vite", overlay: null, deployConfig: null, functionsTemplate: null },
8750
- { builder: "vite", backend: "firebase", baseTemplate: "app-vite", overlay: "overlay-firebase", deployConfig: "firebase", functionsTemplate: "functions-firebase" },
8751
- { builder: "vite", backend: "supabase", baseTemplate: "app-vite", overlay: "overlay-supabase", deployConfig: "vercel-supabase", functionsTemplate: "functions-supabase" },
8752
- { builder: "vite", backend: "vercel", baseTemplate: "app-vite", overlay: "overlay-vercel", deployConfig: "vercel-vercel", functionsTemplate: "functions-vercel" },
8753
- { builder: "nextjs", backend: "none", baseTemplate: "app-next", overlay: null, deployConfig: null, functionsTemplate: null },
8754
- { builder: "nextjs", backend: "firebase", baseTemplate: "app-next", overlay: "overlay-firebase", deployConfig: "firebase", functionsTemplate: "functions-firebase" },
8755
- { builder: "nextjs", backend: "supabase", baseTemplate: "app-next", overlay: "overlay-supabase", deployConfig: "vercel-supabase", functionsTemplate: "functions-supabase" },
8756
- { builder: "nextjs", backend: "vercel", baseTemplate: "app-next", overlay: "overlay-vercel", deployConfig: "vercel-vercel", functionsTemplate: "functions-vercel" },
8757
- { builder: "expo", backend: "none", baseTemplate: "app-expo", overlay: null, deployConfig: null, functionsTemplate: null },
8758
- { builder: "expo", backend: "firebase", baseTemplate: "app-expo", overlay: "overlay-firebase", deployConfig: null, functionsTemplate: "functions-firebase" },
8759
- { builder: "expo", backend: "supabase", baseTemplate: "app-expo", overlay: "overlay-supabase", deployConfig: null, functionsTemplate: "functions-supabase" },
8760
- { builder: "demo", backend: "none", baseTemplate: "app-demo", overlay: null, deployConfig: null, functionsTemplate: null }
8761
- ];
8762
- function comboKey(builder, backend) {
8763
- return `${builder}-${backend}`;
8764
- }
8765
- var ROWS_BY_KEY = /* @__PURE__ */ new Map();
8766
- for (const row of MATRIX) {
8767
- ROWS_BY_KEY.set(comboKey(row.builder, row.backend), row);
8790
+ function resolveDeployConfig(host, backend) {
8791
+ if (host === "none") return null;
8792
+ if (host === "firebase") return "firebase";
8793
+ if (backend === "supabase") return "vercel-supabase";
8794
+ return "vercel-vercel";
8768
8795
  }
8769
- function getScaffoldRow(builder, backend) {
8770
- const key = comboKey(builder, backend);
8771
- const row = ROWS_BY_KEY.get(key);
8772
- if (!row) {
8773
- throw new Error(`Unsupported scaffold combo: ${key}. Supported: ${[...ROWS_BY_KEY.keys()].join(", ")}`);
8774
- }
8775
- return row;
8796
+ function getScaffoldParts(builder, host, functions, backend) {
8797
+ const baseTemplate = builder === "nextjs" ? "app-next" : builder === "demo" ? "app-demo" : `app-${builder}`;
8798
+ return {
8799
+ builder,
8800
+ backend,
8801
+ baseTemplate,
8802
+ overlay: backend !== "none" ? `overlay-${backend}` : null,
8803
+ deployConfig: resolveDeployConfig(host, backend),
8804
+ functionsTemplate: functions !== "none" ? `functions-${functions}` : null
8805
+ };
8776
8806
  }
8777
8807
 
8778
8808
  // packages/tooling/src/scaffolding/create-app.ts
@@ -8812,11 +8842,15 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
8812
8842
  input: askForInput
8813
8843
  });
8814
8844
  const requirements = aggregateRequirements(answers);
8815
- const defaultPlatform = answers.framework === "nextjs" ? "vercel" : "firebase";
8845
+ const framework = answers.framework || "vite";
8846
+ const host2 = answers.host || "none";
8847
+ const functions2 = answers.functions || "none";
8848
+ const backend2 = answers.backend || "none";
8816
8849
  appConfig = {
8817
- template: answers.framework || "vite",
8818
- needsBackend: answers.needsBackend || false,
8819
- backendPlatform: answers.needsBackend ? answers.backendPlatform || defaultPlatform : void 0,
8850
+ template: framework,
8851
+ host: host2,
8852
+ functions: functions2,
8853
+ backend: backend2,
8820
8854
  needsCRUD: true,
8821
8855
  // Always on
8822
8856
  selectedEntities: [],
@@ -8862,12 +8896,14 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
8862
8896
  s.start(`Creating app: ${appName}`);
8863
8897
  await ensureDir(appDir);
8864
8898
  const builder = appTemplate;
8865
- const backend = appConfig.needsBackend && appConfig.backendPlatform ? appConfig.backendPlatform : "none";
8866
- const row = getScaffoldRow(builder, backend);
8899
+ const host = appConfig.host ?? "none";
8900
+ const functions = appConfig.functions ?? "none";
8901
+ const backend = appConfig.backend ?? "none";
8902
+ const row = getScaffoldParts(builder, host, functions, backend);
8867
8903
  const templateDir = row.baseTemplate;
8868
8904
  const firebaseProjectId = (appConfig?.firebaseProjectId ?? "").trim() || appName.toLowerCase().replace(/\s+/g, "-");
8869
8905
  const firebaseRegion = appConfig?.firebaseRegion ?? "europe-west1";
8870
- const backendPlatform = appConfig.backendPlatform ?? "firebase";
8906
+ const backendPlatform = backend !== "none" ? backend : "firebase";
8871
8907
  const deployConfig = row.deployConfig;
8872
8908
  const replacements = {
8873
8909
  projectName: appName,
@@ -8926,13 +8962,16 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
8926
8962
  const variantFile = `src/config/providers.${appTemplate}.ts.example`;
8927
8963
  if (overlayFiles.includes(variantFile)) continue;
8928
8964
  }
8929
- const providersVariant = file.match(/^src\/config\/providers\.(\w+)\.ts\.example$/);
8965
+ const providersVariant = file.match(
8966
+ /^src\/config\/providers\.(\w+)\.ts\.example$/
8967
+ );
8930
8968
  if (providersVariant) {
8931
8969
  if (providersVariant[1] !== appTemplate) continue;
8932
8970
  const destPath2 = joinPath(appDir, "src/config/providers.ts");
8933
8971
  await ensureDir(getDirname(destPath2));
8934
8972
  await copy(joinPath(overlayDir, file), destPath2, { overwrite: true });
8935
- if (await isTextFile(destPath2)) await replacePlaceholders(destPath2, replacements);
8973
+ if (await isTextFile(destPath2))
8974
+ await replacePlaceholders(destPath2, replacements);
8936
8975
  continue;
8937
8976
  }
8938
8977
  const sourcePath = joinPath(overlayDir, file);
@@ -8957,16 +8996,28 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
8957
8996
  }
8958
8997
  if (deployConfig === "vercel-supabase") {
8959
8998
  const vercelPath = joinPath(appDir, "vercel.json");
8960
- const headersFragmentPath = joinPath(overlayDir, "vercel.headers.example");
8999
+ const headersFragmentPath = joinPath(
9000
+ overlayDir,
9001
+ "vercel.headers.example"
9002
+ );
8961
9003
  const fullVercelPath = joinPath(overlayDir, "vercel.json.example");
8962
9004
  if (pathExists(vercelPath) && pathExists(headersFragmentPath)) {
8963
9005
  const vercelJson = readSync(vercelPath, { format: "json" });
8964
- const headersFragment = readSync(headersFragmentPath, { format: "json" });
8965
- vercelJson.headers = [...vercelJson.headers ?? [], ...headersFragment];
8966
- await write(vercelPath, vercelJson, { format: "json", overwrite: true });
9006
+ const headersFragment = readSync(headersFragmentPath, {
9007
+ format: "json"
9008
+ });
9009
+ vercelJson.headers = [
9010
+ ...vercelJson.headers ?? [],
9011
+ ...headersFragment
9012
+ ];
9013
+ await write(vercelPath, vercelJson, {
9014
+ format: "json",
9015
+ overwrite: true
9016
+ });
8967
9017
  } else if (pathExists(fullVercelPath)) {
8968
9018
  await copy(fullVercelPath, vercelPath);
8969
- if (await isTextFile(vercelPath)) await replacePlaceholders(vercelPath, replacements);
9019
+ if (await isTextFile(vercelPath))
9020
+ await replacePlaceholders(vercelPath, replacements);
8970
9021
  }
8971
9022
  }
8972
9023
  }
@@ -8986,9 +9037,13 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
8986
9037
  });
8987
9038
  if (row.functionsTemplate) {
8988
9039
  const functionsTemplateName = row.functionsTemplate;
8989
- const functionsTemplateExists = pathExists(joinPath(templatesRoot, functionsTemplateName));
9040
+ const functionsTemplateExists = pathExists(
9041
+ joinPath(templatesRoot, functionsTemplateName)
9042
+ );
8990
9043
  if (!functionsTemplateExists) {
8991
- log.warn(`Functions template "${functionsTemplateName}" not found \u2014 skipping functions scaffolding.`);
9044
+ log.warn(
9045
+ `Functions template "${functionsTemplateName}" not found \u2014 skipping functions scaffolding.`
9046
+ );
8992
9047
  } else {
8993
9048
  const isSupabaseFunctions = functionsTemplateName === "functions-supabase";
8994
9049
  const functionsRootDir = isSupabaseFunctions ? appDir : joinPath(appDir, "functions");
@@ -8996,7 +9051,10 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
8996
9051
  const functionsPackageJson = generatePackageJson(
8997
9052
  functionsTemplateName,
8998
9053
  executionMode,
8999
- { appName, platform: row.functionsTemplate.replace("functions-", "") }
9054
+ {
9055
+ appName,
9056
+ platform: row.functionsTemplate.replace("functions-", "")
9057
+ }
9000
9058
  );
9001
9059
  const packageJsonPath2 = joinPath(functionsRootDir, "package.json");
9002
9060
  await ensureDir(functionsRootDir);
@@ -9032,13 +9090,19 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
9032
9090
  }
9033
9091
  const deploymentTemplateDir = joinPath(templatesRoot, "root-consumer");
9034
9092
  if (deployConfig === "firebase") {
9035
- const firebaseJsonSource = joinPath(deploymentTemplateDir, "firebase.json.example");
9093
+ const firebaseJsonSource = joinPath(
9094
+ deploymentTemplateDir,
9095
+ "firebase.json.example"
9096
+ );
9036
9097
  if (pathExists(firebaseJsonSource)) {
9037
9098
  await copy(firebaseJsonSource, joinPath(appDir, "firebase.json"));
9038
9099
  const firebaseJsonDest = joinPath(appDir, "firebase.json");
9039
- if (await isTextFile(firebaseJsonDest)) await replacePlaceholders(firebaseJsonDest, replacements);
9100
+ if (await isTextFile(firebaseJsonDest))
9101
+ await replacePlaceholders(firebaseJsonDest, replacements);
9040
9102
  if (appTemplate === "nextjs") {
9041
- const firebaseJson = readSync(firebaseJsonDest, { format: "json" });
9103
+ const firebaseJson = readSync(firebaseJsonDest, {
9104
+ format: "json"
9105
+ });
9042
9106
  if (firebaseJson.hosting?.rewrites) {
9043
9107
  firebaseJson.hosting.rewrites = firebaseJson.hosting.rewrites.filter(
9044
9108
  (r2) => r2.destination !== "/index.html"
@@ -9047,17 +9111,28 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
9047
9111
  if (firebaseJson.hosting) {
9048
9112
  firebaseJson.hosting.public = "out";
9049
9113
  }
9050
- await write(firebaseJsonDest, firebaseJson, { format: "json", overwrite: true });
9114
+ await write(firebaseJsonDest, firebaseJson, {
9115
+ format: "json",
9116
+ overwrite: true
9117
+ });
9051
9118
  }
9052
9119
  }
9053
- const firebasercSource = joinPath(deploymentTemplateDir, ".firebaserc.example");
9120
+ const firebasercSource = joinPath(
9121
+ deploymentTemplateDir,
9122
+ ".firebaserc.example"
9123
+ );
9054
9124
  if (pathExists(firebasercSource)) {
9055
9125
  await copy(firebasercSource, joinPath(appDir, ".firebaserc"));
9056
9126
  const firebasercDest = joinPath(appDir, ".firebaserc");
9057
- if (await isTextFile(firebasercDest)) await replacePlaceholders(firebasercDest, replacements);
9127
+ if (await isTextFile(firebasercDest))
9128
+ await replacePlaceholders(firebasercDest, replacements);
9058
9129
  }
9059
9130
  if (row.functionsTemplate === "functions-firebase") {
9060
- for (const example of ["firestore.rules.example", "firestore.indexes.json.example", "storage.rules.example"]) {
9131
+ for (const example of [
9132
+ "firestore.rules.example",
9133
+ "firestore.indexes.json.example",
9134
+ "storage.rules.example"
9135
+ ]) {
9061
9136
  const src = joinPath(deploymentTemplateDir, example);
9062
9137
  if (pathExists(src)) {
9063
9138
  await copy(src, joinPath(appDir, example.replace(".example", "")));
@@ -9073,11 +9148,22 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
9073
9148
  {
9074
9149
  source: "functions",
9075
9150
  codebase: "default",
9076
- ignore: ["node_modules", ".git", "firebase-debug.log", "firebase-debug.*.log", "**/.*", "**/*.test.ts", "**/__tests__/**"],
9151
+ ignore: [
9152
+ "node_modules",
9153
+ ".git",
9154
+ "firebase-debug.log",
9155
+ "firebase-debug.*.log",
9156
+ "**/.*",
9157
+ "**/*.test.ts",
9158
+ "**/__tests__/**"
9159
+ ],
9077
9160
  runtime: "nodejs22"
9078
9161
  }
9079
9162
  ],
9080
- firestore: { rules: "firestore.rules", indexes: "firestore.indexes.json" },
9163
+ firestore: {
9164
+ rules: "firestore.rules",
9165
+ indexes: "firestore.indexes.json"
9166
+ },
9081
9167
  storage: { rules: "storage.rules" },
9082
9168
  emulators: {
9083
9169
  auth: { port: 9099 },
@@ -9087,15 +9173,26 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
9087
9173
  ui: { enabled: true, port: 4e3 }
9088
9174
  }
9089
9175
  };
9090
- await write(firebaseJsonPath, expoFirebaseJson, { format: "json", overwrite: true });
9176
+ await write(firebaseJsonPath, expoFirebaseJson, {
9177
+ format: "json",
9178
+ overwrite: true
9179
+ });
9091
9180
  }
9092
- const firebasercSource = joinPath(deploymentTemplateDir, ".firebaserc.example");
9181
+ const firebasercSource = joinPath(
9182
+ deploymentTemplateDir,
9183
+ ".firebaserc.example"
9184
+ );
9093
9185
  const firebasercDest = joinPath(appDir, ".firebaserc");
9094
9186
  if (pathExists(firebasercSource) && !pathExists(firebasercDest)) {
9095
9187
  await copy(firebasercSource, firebasercDest);
9096
- if (await isTextFile(firebasercDest)) await replacePlaceholders(firebasercDest, replacements);
9097
- }
9098
- for (const example of ["firestore.rules.example", "firestore.indexes.json.example", "storage.rules.example"]) {
9188
+ if (await isTextFile(firebasercDest))
9189
+ await replacePlaceholders(firebasercDest, replacements);
9190
+ }
9191
+ for (const example of [
9192
+ "firestore.rules.example",
9193
+ "firestore.indexes.json.example",
9194
+ "storage.rules.example"
9195
+ ]) {
9099
9196
  const src = joinPath(deploymentTemplateDir, example);
9100
9197
  const dest = joinPath(appDir, example.replace(".example", ""));
9101
9198
  if (pathExists(src) && !pathExists(dest)) {
@@ -9104,11 +9201,15 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
9104
9201
  }
9105
9202
  }
9106
9203
  if (deployConfig === "vercel-vercel") {
9107
- const vercelJsonSource = joinPath(deploymentTemplateDir, "vercel.json.example");
9204
+ const vercelJsonSource = joinPath(
9205
+ deploymentTemplateDir,
9206
+ "vercel.json.example"
9207
+ );
9108
9208
  if (pathExists(vercelJsonSource)) {
9109
9209
  await copy(vercelJsonSource, joinPath(appDir, "vercel.json"));
9110
9210
  const vercelJsonDest = joinPath(appDir, "vercel.json");
9111
- if (await isTextFile(vercelJsonDest)) await replacePlaceholders(vercelJsonDest, replacements);
9211
+ if (await isTextFile(vercelJsonDest))
9212
+ await replacePlaceholders(vercelJsonDest, replacements);
9112
9213
  }
9113
9214
  }
9114
9215
  const backendInfo = row.functionsTemplate ? ` with ${row.functionsTemplate.replace("functions-", "")} functions` : "";
@@ -9140,11 +9241,13 @@ Happy coding!`,
9140
9241
  }
9141
9242
  async function main(options) {
9142
9243
  try {
9143
- const hasExplicitFlags = options?.builder !== void 0 || options?.functions !== void 0;
9244
+ const hasExplicitFlags = options?.builder !== void 0 || options?.host !== void 0 || options?.functions !== void 0 || options?.backend !== void 0;
9144
9245
  if (hasExplicitFlags && options?.name) {
9145
9246
  const appName = options.name;
9146
9247
  const builder = options.builder || "vite";
9147
- const includeFunctions = options.functions ?? false;
9248
+ const hostOpt = options.host ?? "none";
9249
+ const functionsOpt = options.functions ?? "none";
9250
+ const backendOpt = options.backend ?? "none";
9148
9251
  if (!isValidFileName(appName)) {
9149
9252
  throw new Error(
9150
9253
  `Invalid app name "${appName}". Use only letters, numbers, dashes (-), and underscores (_).`
@@ -9157,8 +9260,9 @@ async function main(options) {
9157
9260
  }
9158
9261
  const appConfig = {
9159
9262
  template: builder === "next" ? "nextjs" : builder === "expo" ? "expo" : "vite",
9160
- needsBackend: includeFunctions,
9161
- backendPlatform: includeFunctions ? "firebase" : void 0,
9263
+ host: hostOpt,
9264
+ functions: functionsOpt,
9265
+ backend: backendOpt,
9162
9266
  needsCRUD: true,
9163
9267
  selectedEntities: [],
9164
9268
  userAuth: "social",