@donotdev/cli 0.0.14 → 0.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/dependencies-matrix.json +372 -88
  2. package/dist/bin/commands/agent-setup.js +7 -1
  3. package/dist/bin/commands/build.js +141 -44
  4. package/dist/bin/commands/bump.js +81 -41
  5. package/dist/bin/commands/cacheout.js +37 -9
  6. package/dist/bin/commands/create-app.js +276 -121
  7. package/dist/bin/commands/create-project.js +506 -217
  8. package/dist/bin/commands/deploy.js +1785 -694
  9. package/dist/bin/commands/dev.js +177 -43
  10. package/dist/bin/commands/doctor.d.ts +6 -0
  11. package/dist/bin/commands/doctor.d.ts.map +1 -0
  12. package/dist/bin/commands/{lint.js → doctor.js} +1215 -156
  13. package/dist/bin/commands/doctor.js.map +1 -0
  14. package/dist/bin/commands/emu.js +451 -104
  15. package/dist/bin/commands/format.js +37 -9
  16. package/dist/bin/commands/make-admin.js +77499 -11
  17. package/dist/bin/commands/preview.js +181 -43
  18. package/dist/bin/commands/setup.d.ts +6 -0
  19. package/dist/bin/commands/setup.d.ts.map +1 -0
  20. package/dist/bin/commands/setup.js +11733 -0
  21. package/dist/bin/commands/setup.js.map +1 -0
  22. package/dist/bin/commands/supabase-setup.d.ts +6 -0
  23. package/dist/bin/commands/supabase-setup.d.ts.map +1 -0
  24. package/dist/bin/commands/supabase-setup.js +7 -0
  25. package/dist/bin/commands/supabase-setup.js.map +1 -0
  26. package/dist/bin/commands/sync-secrets.js +211 -34
  27. package/dist/bin/commands/type-check.d.ts +14 -0
  28. package/dist/bin/commands/type-check.d.ts.map +1 -0
  29. package/dist/bin/commands/type-check.js +2049 -0
  30. package/dist/bin/commands/type-check.js.map +1 -0
  31. package/dist/bin/commands/wai.js +3 -1
  32. package/dist/bin/dndev.js +73 -52
  33. package/dist/bin/donotdev.js +54 -45
  34. package/dist/index.js +4212 -3050
  35. package/package.json +3 -3
  36. package/templates/app-demo/src/App.tsx.example +1 -0
  37. package/templates/app-demo/src/pages/FullPage.tsx.example +2 -2
  38. package/templates/app-demo/src/pages/components/DemoLayout.tsx.example +2 -2
  39. package/templates/app-demo/src/themes.css.example +5 -12
  40. package/templates/app-expo/.env.example +44 -0
  41. package/templates/app-expo/.expo/README.md.example +5 -0
  42. package/templates/app-expo/.gitignore.example +36 -0
  43. package/templates/app-expo/README.md.example +58 -0
  44. package/templates/app-expo/app/.gitkeep +2 -0
  45. package/templates/app-expo/app/_layout.tsx.example +41 -0
  46. package/templates/app-expo/app/form.tsx.example +52 -0
  47. package/templates/app-expo/app/index.tsx.example +89 -0
  48. package/templates/app-expo/app/list.tsx.example +32 -0
  49. package/templates/app-expo/app/profile.tsx.example +76 -0
  50. package/templates/app-expo/app/signin.tsx.example +53 -0
  51. package/templates/app-expo/app.json.example +39 -0
  52. package/templates/app-expo/assets/adaptive-icon.png +0 -0
  53. package/templates/app-expo/assets/favicon.png +0 -0
  54. package/templates/app-expo/assets/icon.png +0 -0
  55. package/templates/app-expo/assets/splash.png +0 -0
  56. package/templates/app-expo/babel.config.js.example +10 -0
  57. package/templates/app-expo/eas.json.example +20 -0
  58. package/templates/app-expo/expo-env.d.ts.example +4 -0
  59. package/templates/app-expo/metro.config.js.example +20 -0
  60. package/templates/app-expo/service-account-key.json.example +12 -0
  61. package/templates/app-expo/src/config/app.ts.example +46 -0
  62. package/templates/app-expo/src/config/providers.ts.example +7 -0
  63. package/templates/app-expo/tsconfig.json.example +19 -0
  64. package/templates/app-next/.env.example +4 -33
  65. package/templates/app-next/src/app/ClientLayout.tsx.example +2 -0
  66. package/templates/app-next/src/app/layout.tsx.example +7 -6
  67. package/templates/app-next/src/config/providers.ts.example +7 -0
  68. package/templates/app-next/src/globals.css.example +2 -11
  69. package/templates/app-next/src/pages/HomePage.tsx.example +1 -1
  70. package/templates/app-next/src/themes.css.example +10 -13
  71. package/templates/app-vite/.env.example +3 -32
  72. package/templates/app-vite/index.html.example +2 -24
  73. package/templates/app-vite/src/App.tsx.example +2 -0
  74. package/templates/app-vite/src/config/providers.ts.example +7 -0
  75. package/templates/app-vite/src/globals.css.example +2 -12
  76. package/templates/app-vite/src/pages/FormPageExample.tsx.example +1 -2
  77. package/templates/app-vite/src/pages/HomePage.tsx.example +2 -2
  78. package/templates/app-vite/src/themes.css.example +109 -79
  79. package/templates/app-vite/vercel.json.example +11 -0
  80. package/templates/functions-firebase/README.md.example +1 -1
  81. package/templates/functions-firebase/build.mjs.example +2 -72
  82. package/templates/functions-firebase/functions-firebase/.env.example.example +24 -26
  83. package/templates/functions-firebase/functions-firebase/README.md.example +1 -1
  84. package/templates/functions-firebase/functions-firebase/build.mjs.example +2 -72
  85. package/templates/functions-firebase/functions-firebase/tsconfig.json.example +1 -1
  86. package/templates/functions-firebase/functions.config.js.example +1 -1
  87. package/templates/functions-supabase/supabase/config.toml.example +59 -0
  88. package/templates/functions-supabase/supabase/functions/.env.example +13 -0
  89. package/templates/functions-supabase/supabase/functions/cancel-subscription/index.ts.example +7 -0
  90. package/templates/functions-supabase/supabase/functions/change-plan/index.ts.example +11 -0
  91. package/templates/functions-supabase/supabase/functions/create-checkout-session/index.ts.example +11 -0
  92. package/templates/functions-supabase/supabase/functions/create-customer-portal/index.ts.example +7 -0
  93. package/templates/functions-supabase/supabase/functions/crud/index.ts.example +16 -0
  94. package/templates/functions-supabase/supabase/functions/delete-account/index.ts.example +7 -0
  95. package/templates/functions-supabase/supabase/functions/deno.json.example +8 -0
  96. package/templates/functions-supabase/supabase/functions/get-custom-claims/index.ts.example +7 -0
  97. package/templates/functions-supabase/supabase/functions/get-user-auth-status/index.ts.example +7 -0
  98. package/templates/functions-supabase/supabase/functions/refresh-subscription-status/index.ts.example +7 -0
  99. package/templates/functions-supabase/supabase/functions/remove-custom-claims/index.ts.example +7 -0
  100. package/templates/functions-supabase/supabase/functions/set-custom-claims/index.ts.example +7 -0
  101. package/templates/functions-supabase/supabase/migrations/20250101000000_idempotency.sql +24 -0
  102. package/templates/functions-supabase/supabase/migrations/20250101000001_rate_limits.sql +22 -0
  103. package/templates/functions-supabase/supabase/migrations/20250101000002_cleanup_jobs.sql +28 -0
  104. package/templates/functions-supabase/supabase/migrations/20250101000003_operation_metrics.sql +28 -0
  105. package/templates/functions-vercel/functions-vercel/tsconfig.json.example +1 -1
  106. package/templates/functions-vercel/functions-vercel/vercel.json.example +1 -1
  107. package/templates/functions-vercel/vercel.json.example +1 -1
  108. package/templates/github/github/workflows/firebase-deploy.yml.example +1 -1
  109. package/templates/github/workflows/firebase-deploy.yml.example +1 -1
  110. package/templates/overlay-firebase/env.fragment.example +34 -0
  111. package/templates/overlay-firebase/env.fragment.expo.example +34 -0
  112. package/templates/overlay-firebase/env.fragment.nextjs.example +34 -0
  113. package/templates/overlay-firebase/src/config/providers.expo.ts.example +49 -0
  114. package/templates/overlay-firebase/src/config/providers.ts.example +23 -0
  115. package/templates/overlay-supabase/env.fragment.example +12 -0
  116. package/templates/overlay-supabase/env.fragment.expo.example +12 -0
  117. package/templates/overlay-supabase/env.fragment.nextjs.example +12 -0
  118. package/templates/overlay-supabase/src/config/providers.expo.ts.example +35 -0
  119. package/templates/overlay-supabase/src/config/providers.ts.example +33 -0
  120. package/templates/overlay-supabase/vercel.headers.example +23 -0
  121. package/templates/overlay-supabase/vercel.json.example +22 -0
  122. package/templates/overlay-vercel/env.fragment.example +34 -0
  123. package/templates/overlay-vercel/env.fragment.nextjs.example +34 -0
  124. package/templates/overlay-vercel/src/config/providers.ts.example +24 -0
  125. package/templates/root-consumer/.claude/agents/architect.md.example +2 -310
  126. package/templates/root-consumer/.claude/agents/builder.md.example +2 -326
  127. package/templates/root-consumer/.claude/agents/coder.md.example +2 -83
  128. package/templates/root-consumer/.claude/agents/extractor.md.example +2 -231
  129. package/templates/root-consumer/.claude/agents/polisher.md.example +2 -132
  130. package/templates/root-consumer/.claude/agents/prompt-engineer.md.example +2 -81
  131. package/templates/root-consumer/.claude/commands/grill.md.example +30 -0
  132. package/templates/root-consumer/.claude/commands/techdebt.md.example +28 -0
  133. package/templates/root-consumer/.clinerules.example +1 -0
  134. package/templates/root-consumer/.cursor/rules/no-docs.mdc.example +15 -0
  135. package/templates/root-consumer/.cursorrules.example +1 -0
  136. package/templates/root-consumer/.github/copilot-instructions.md.example +1 -0
  137. package/templates/root-consumer/.windsurfrules.example +1 -0
  138. package/templates/root-consumer/AI.md.example +44 -123
  139. package/templates/root-consumer/CLAUDE.md.example +1 -134
  140. package/templates/root-consumer/CONVENTIONS.md.example +1 -0
  141. package/templates/root-consumer/GEMINI.md.example +1 -0
  142. package/templates/root-consumer/firebase.json.example +1 -1
  143. package/templates/root-consumer/guides/dndev/AGENT_START_HERE.md.example +22 -2
  144. package/templates/root-consumer/guides/dndev/COMPONENTS_ADV.md.example +0 -18
  145. package/templates/root-consumer/guides/dndev/COMPONENTS_UI.md.example +1 -1
  146. package/templates/root-consumer/guides/dndev/ENV_SETUP.md.example +101 -32
  147. package/templates/root-consumer/guides/dndev/INDEX.md.example +4 -2
  148. package/templates/root-consumer/guides/dndev/SETUP_APP_CONFIG.md.example +3 -3
  149. package/templates/root-consumer/guides/dndev/SETUP_CRUD.md.example +241 -12
  150. package/templates/root-consumer/guides/dndev/SETUP_FIREBASE.md.example +13 -7
  151. package/templates/root-consumer/guides/dndev/SETUP_OAUTH_PROVIDERS.md.example +60 -0
  152. package/templates/root-consumer/guides/dndev/SETUP_SOC2.md.example +234 -0
  153. package/templates/root-consumer/guides/dndev/SETUP_STRIPE.md.example +62 -0
  154. package/templates/root-consumer/guides/dndev/SETUP_SUPABASE.md.example +124 -0
  155. package/templates/root-consumer/guides/dndev/SETUP_THEMES.md.example +6 -2
  156. package/templates/root-consumer/guides/dndev/SETUP_VERCEL.md.example +176 -0
  157. package/templates/root-consumer/guides/dndev/USE_ROUTING.md.example +5 -9
  158. package/templates/root-consumer/guides/dndev/essences_reference.css.example +174 -0
  159. package/templates/root-consumer/guides/wai-way/WAI_WAY_CLI.md.example +7 -8
  160. package/templates/root-consumer/guides/wai-way/agents/builder.md.example +10 -0
  161. package/templates/root-consumer/guides/wai-way/agents/extractor.md.example +25 -5
  162. package/templates/root-consumer/guides/wai-way/agents/polisher.md.example +13 -2
  163. package/templates/root-consumer/guides/wai-way/blueprints/0_brainstorm.md.example +2 -2
  164. package/templates/root-consumer/guides/wai-way/blueprints/1_scaffold.md.example +55 -15
  165. package/templates/root-consumer/guides/wai-way/blueprints/3_compose.md.example +15 -4
  166. package/templates/root-consumer/guides/wai-way/spec_template.md.example +7 -6
  167. package/dist/bin/commands/lint.d.ts +0 -11
  168. package/dist/bin/commands/lint.d.ts.map +0 -1
  169. package/dist/bin/commands/lint.js.map +0 -1
  170. package/dist/bin/commands/staging.d.ts +0 -11
  171. package/dist/bin/commands/staging.d.ts.map +0 -1
  172. package/dist/bin/commands/staging.js +0 -12
  173. package/dist/bin/commands/staging.js.map +0 -1
  174. package/templates/app-payload/.env.example +0 -28
  175. package/templates/app-payload/README.md.example +0 -233
  176. package/templates/app-payload/collections/Company.ts.example +0 -125
  177. package/templates/app-payload/collections/Hero.ts.example +0 -62
  178. package/templates/app-payload/collections/Media.ts.example +0 -41
  179. package/templates/app-payload/collections/Products.ts.example +0 -115
  180. package/templates/app-payload/collections/Services.ts.example +0 -104
  181. package/templates/app-payload/collections/Testimonials.ts.example +0 -92
  182. package/templates/app-payload/collections/Users.ts.example +0 -35
  183. package/templates/app-payload/src/server.ts.example +0 -79
  184. package/templates/app-payload/tsconfig.json.example +0 -24
@@ -7884,16 +7884,19 @@ function getBinPath(binaryName) {
7884
7884
  return binaryName;
7885
7885
  }
7886
7886
  function detectExecutionMode() {
7887
- const fileUrlPath = new URL(import.meta.url).pathname;
7888
- if (!fileUrlPath.includes("node_modules")) {
7889
- return "development";
7887
+ const fileUrlPath = fileURLToPath2(import.meta.url);
7888
+ if (fileUrlPath.includes("node_modules")) {
7889
+ return "published";
7890
7890
  }
7891
- return "published";
7891
+ if (fileUrlPath.includes("/dist/")) {
7892
+ return "published";
7893
+ }
7894
+ return "development";
7892
7895
  }
7893
7896
  function getTemplatesRoot() {
7894
7897
  const mode = detectExecutionMode();
7895
7898
  if (mode === "development") {
7896
- const fileUrlPath = new URL(import.meta.url).pathname;
7899
+ const fileUrlPath = fileURLToPath2(import.meta.url);
7897
7900
  const frameworkRoot = normalizePath(dirname2(fileUrlPath), "../../../..");
7898
7901
  return normalizePath(frameworkRoot, PACKAGE_PATHS.CLI, "templates");
7899
7902
  } else {
@@ -8173,18 +8176,7 @@ var init_utils = __esm({
8173
8176
  }
8174
8177
  });
8175
8178
 
8176
- // packages/cli/src/bin/commands/create-app.ts
8177
- init_utils();
8178
-
8179
- // packages/tooling/src/index.ts
8180
- init_utils();
8181
-
8182
- // packages/tooling/src/cli/index.ts
8183
- init_utils();
8184
-
8185
8179
  // packages/tooling/src/utils/cli-input.ts
8186
- init_utils();
8187
- init_dist2();
8188
8180
  async function askForInput(message, defaultValue = "") {
8189
8181
  const result = await he({
8190
8182
  message,
@@ -8225,6 +8217,22 @@ async function askForSelection(message, choices, defaultValue = 0) {
8225
8217
  }
8226
8218
  return result;
8227
8219
  }
8220
+ var init_cli_input = __esm({
8221
+ "packages/tooling/src/utils/cli-input.ts"() {
8222
+ "use strict";
8223
+ init_utils();
8224
+ init_dist2();
8225
+ }
8226
+ });
8227
+
8228
+ // packages/cli/src/bin/commands/create-app.ts
8229
+ init_utils();
8230
+
8231
+ // packages/tooling/src/index.ts
8232
+ init_utils();
8233
+
8234
+ // packages/tooling/src/cli/index.ts
8235
+ init_utils();
8228
8236
 
8229
8237
  // packages/tooling/src/utils/create-utils.ts
8230
8238
  init_utils();
@@ -8385,15 +8393,10 @@ function getMatrixPath(mode) {
8385
8393
  }
8386
8394
  const executionMode = mode || detectExecutionMode();
8387
8395
  if (executionMode === "development") {
8388
- const repoRoot = getRepoRoot();
8389
- if (repoRoot) {
8390
- const devPath = normalizePath(
8391
- repoRoot,
8392
- "packages/cli/dependencies-matrix.json"
8393
- );
8394
- if (pathExists(devPath)) {
8395
- return devPath;
8396
- }
8396
+ const templatesRoot = getTemplatesRoot();
8397
+ const devPath = normalizePath(templatesRoot, "..", "dependencies-matrix.json");
8398
+ if (pathExists(devPath)) {
8399
+ return devPath;
8397
8400
  }
8398
8401
  }
8399
8402
  return null;
@@ -8408,8 +8411,8 @@ function getCliVersion(mode) {
8408
8411
  }
8409
8412
  const executionMode = mode || detectExecutionMode();
8410
8413
  if (executionMode === "development") {
8411
- const repoRoot = getRepoRoot();
8412
- const cliPackageJson = joinPath(repoRoot, "packages/cli/package.json");
8414
+ const templatesRoot = getTemplatesRoot();
8415
+ const cliPackageJson = normalizePath(templatesRoot, "..", "package.json");
8413
8416
  if (pathExists(cliPackageJson)) {
8414
8417
  const pkg = readSync(cliPackageJson, { format: "json" });
8415
8418
  return String(pkg?.version || "0.0.0");
@@ -8460,6 +8463,10 @@ var APP_QUESTIONNAIRE = [
8460
8463
  {
8461
8464
  title: "Next.js \u2014 Static content/SEO (SSR, SSG) \u2014 BETA",
8462
8465
  value: "nextjs"
8466
+ },
8467
+ {
8468
+ title: "Expo \u2014 Mobile app (iOS + Android)",
8469
+ value: "expo"
8463
8470
  }
8464
8471
  ]
8465
8472
  },
@@ -8486,6 +8493,10 @@ var APP_QUESTIONNAIRE = [
8486
8493
  {
8487
8494
  title: "Vercel \u2014 Serverless functions (edge + node.js)",
8488
8495
  value: "vercel"
8496
+ },
8497
+ {
8498
+ title: "Supabase \u2014 Postgres + Auth + Storage",
8499
+ value: "supabase"
8489
8500
  }
8490
8501
  ]
8491
8502
  }
@@ -8552,6 +8563,7 @@ async function runQuestionnaire(questions, initialAnswers, showWIP, askFor) {
8552
8563
 
8553
8564
  // packages/tooling/src/scaffolding/create-app.ts
8554
8565
  init_utils();
8566
+ init_cli_input();
8555
8567
  init_cli_output();
8556
8568
 
8557
8569
  // packages/tooling/src/utils/dependency-resolver.ts
@@ -8585,6 +8597,15 @@ function generateScripts(templateName, options) {
8585
8597
  scripts.preview = "next start";
8586
8598
  scripts.lint = "eslint src/";
8587
8599
  scripts["type-check"] = "tsc --noEmit";
8600
+ } else if (templateName.includes("expo")) {
8601
+ scripts.dev = "expo start";
8602
+ scripts.start = "expo start";
8603
+ scripts.android = "expo start --android";
8604
+ scripts.ios = "expo start --ios";
8605
+ scripts.web = "expo start --web";
8606
+ scripts.build = "expo export";
8607
+ scripts.lint = "eslint .";
8608
+ scripts["type-check"] = "tsc --noEmit";
8588
8609
  } else if (templateName === "consumer-root") {
8589
8610
  scripts.dev = "turbo run dev";
8590
8611
  scripts.build = "turbo run build";
@@ -8673,14 +8694,14 @@ function generatePackageJson(templateName, mode, options = {}) {
8673
8694
  result.main = "./index.ts";
8674
8695
  result.types = "./index.ts";
8675
8696
  }
8676
- if (templateName.includes("vite") || templateName.includes("nextjs") || templateName.includes("functions")) {
8697
+ if (templateName.includes("vite") || templateName.includes("nextjs") || templateName.includes("expo") || templateName.includes("functions")) {
8677
8698
  if (!dependencies.entities) {
8678
8699
  dependencies.entities = "workspace:*";
8679
8700
  }
8680
8701
  }
8681
8702
  if (templateName.includes("functions")) {
8682
8703
  result.main = "lib/index.js";
8683
- result.engines = { node: "20" };
8704
+ result.engines = { node: "22" };
8684
8705
  if (options.appName) {
8685
8706
  const platform = templateName.includes("vercel") ? "Vercel" : "Firebase";
8686
8707
  result.description = `${options.appName} ${platform} Functions`;
@@ -8721,6 +8742,40 @@ function generatePackageJson(templateName, mode, options = {}) {
8721
8742
  // packages/tooling/src/scaffolding/create-app.ts
8722
8743
  init_pathResolver();
8723
8744
  init_pathResolver();
8745
+
8746
+ // packages/tooling/src/scaffolding/scaffold-matrix.ts
8747
+ 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);
8768
+ }
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;
8776
+ }
8777
+
8778
+ // packages/tooling/src/scaffolding/create-app.ts
8724
8779
  var SHOW_WIP = process.env.SHOW_WIP === "true" || process.argv.includes("--wip");
8725
8780
  async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
8726
8781
  const isInteractive = !appName || !appConfig;
@@ -8806,14 +8861,19 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
8806
8861
  const s = Y2();
8807
8862
  s.start(`Creating app: ${appName}`);
8808
8863
  await ensureDir(appDir);
8809
- const templateDir = appTemplate === "demo" ? "app-demo" : appTemplate === "nextjs" ? "app-next" : "app-vite";
8864
+ const builder = appTemplate;
8865
+ const backend = appConfig.needsBackend && appConfig.backendPlatform ? appConfig.backendPlatform : "none";
8866
+ const row = getScaffoldRow(builder, backend);
8867
+ const templateDir = row.baseTemplate;
8810
8868
  const firebaseProjectId = (appConfig?.firebaseProjectId ?? "").trim() || appName.toLowerCase().replace(/\s+/g, "-");
8811
8869
  const firebaseRegion = appConfig?.firebaseRegion ?? "europe-west1";
8870
+ const backendPlatform = appConfig.backendPlatform ?? "firebase";
8871
+ const deployConfig = row.deployConfig;
8812
8872
  const replacements = {
8813
8873
  projectName: appName,
8814
8874
  appName,
8815
8875
  appShortName: appName.toUpperCase().replace(/-/g, " "),
8816
- includeFunctions: Boolean(appConfig.needsBackend),
8876
+ includeFunctions: Boolean(row.functionsTemplate),
8817
8877
  needsCRUD: Boolean(appConfig.needsCRUD),
8818
8878
  setupGithubActions: false,
8819
8879
  appNames: [appName],
@@ -8823,8 +8883,13 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
8823
8883
  monorepoRelativePath: "../../packages/tooling",
8824
8884
  appTemplate,
8825
8885
  isNextjs: appTemplate === "nextjs",
8886
+ isExpo: appTemplate === "expo",
8826
8887
  YOUR_FIREBASE_PROJECT_ID: firebaseProjectId,
8827
- YOUR_REGION: firebaseRegion
8888
+ YOUR_REGION: firebaseRegion,
8889
+ backendPlatform,
8890
+ isFirebase: backendPlatform === "firebase",
8891
+ isSupabase: backendPlatform === "supabase",
8892
+ isVercel: backendPlatform === "vercel"
8828
8893
  };
8829
8894
  const templateSourceDir = joinPath(templatesRoot, templateDir);
8830
8895
  const templateFiles = await glob("**/*", {
@@ -8845,118 +8910,208 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
8845
8910
  await replacePlaceholders(destPath, replacements);
8846
8911
  }
8847
8912
  }
8913
+ if (row.overlay) {
8914
+ const overlayDir = joinPath(templatesRoot, row.overlay);
8915
+ if (pathExists(overlayDir)) {
8916
+ const overlayFiles = await glob("**/*", {
8917
+ cwd: overlayDir,
8918
+ dot: true,
8919
+ onlyFiles: true
8920
+ });
8921
+ for (const file of overlayFiles) {
8922
+ if (file.startsWith("env.fragment")) continue;
8923
+ if (file === "vercel.headers.example") continue;
8924
+ if (file === "vercel.json.example") continue;
8925
+ if (file === "src/config/providers.ts.example") {
8926
+ const variantFile = `src/config/providers.${appTemplate}.ts.example`;
8927
+ if (overlayFiles.includes(variantFile)) continue;
8928
+ }
8929
+ const providersVariant = file.match(/^src\/config\/providers\.(\w+)\.ts\.example$/);
8930
+ if (providersVariant) {
8931
+ if (providersVariant[1] !== appTemplate) continue;
8932
+ const destPath2 = joinPath(appDir, "src/config/providers.ts");
8933
+ await ensureDir(getDirname(destPath2));
8934
+ await copy(joinPath(overlayDir, file), destPath2, { overwrite: true });
8935
+ if (await isTextFile(destPath2)) await replacePlaceholders(destPath2, replacements);
8936
+ continue;
8937
+ }
8938
+ const sourcePath = joinPath(overlayDir, file);
8939
+ let destFileName = file;
8940
+ if (destFileName.endsWith(".example")) {
8941
+ destFileName = destFileName.slice(0, -8);
8942
+ }
8943
+ const destPath = joinPath(appDir, destFileName);
8944
+ await ensureDir(getDirname(destPath));
8945
+ await copy(sourcePath, destPath, { overwrite: true });
8946
+ if (await isTextFile(destPath)) {
8947
+ await replacePlaceholders(destPath, replacements);
8948
+ }
8949
+ }
8950
+ const fragmentName = appTemplate === "nextjs" ? "env.fragment.nextjs.example" : appTemplate === "expo" ? "env.fragment.expo.example" : "env.fragment.example";
8951
+ const fragmentPath = joinPath(overlayDir, fragmentName);
8952
+ const envPath = joinPath(appDir, ".env");
8953
+ if (pathExists(fragmentPath) && pathExists(envPath)) {
8954
+ const baseEnv = readSync(envPath);
8955
+ const fragment = readSync(fragmentPath);
8956
+ await write(envPath, baseEnv + "\n" + fragment, { overwrite: true });
8957
+ }
8958
+ if (deployConfig === "vercel-supabase") {
8959
+ const vercelPath = joinPath(appDir, "vercel.json");
8960
+ const headersFragmentPath = joinPath(overlayDir, "vercel.headers.example");
8961
+ const fullVercelPath = joinPath(overlayDir, "vercel.json.example");
8962
+ if (pathExists(vercelPath) && pathExists(headersFragmentPath)) {
8963
+ 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 });
8967
+ } else if (pathExists(fullVercelPath)) {
8968
+ await copy(fullVercelPath, vercelPath);
8969
+ if (await isTextFile(vercelPath)) await replacePlaceholders(vercelPath, replacements);
8970
+ }
8971
+ }
8972
+ }
8973
+ }
8848
8974
  const executionMode = detectExecutionMode();
8849
8975
  const templateName = appTemplate === "demo" ? "demo" : executionMode === "development" ? `dev-${appTemplate}` : `consumer-${appTemplate}`;
8850
8976
  const packageJson = generatePackageJson(templateName, executionMode, {
8851
8977
  appName,
8852
8978
  template: appTemplate === "demo" ? "vite" : appTemplate,
8853
- includeFunctions: Boolean(appConfig.needsBackend)
8979
+ includeFunctions: Boolean(row.functionsTemplate),
8980
+ platform: appTemplate === "demo" ? void 0 : backendPlatform
8854
8981
  });
8855
8982
  const packageJsonPath = joinPath(appDir, "package.json");
8856
8983
  await write(packageJsonPath, packageJson, {
8857
8984
  format: "json",
8858
8985
  overwrite: true
8859
8986
  });
8860
- if (appConfig.needsBackend && appConfig.backendPlatform) {
8861
- const platform = appConfig.backendPlatform;
8862
- const functionsRootDir = joinPath(appDir, "functions");
8863
- const functionsTemplateName = `functions-${platform}`;
8864
- const functionsPackageJson = generatePackageJson(
8865
- functionsTemplateName,
8866
- executionMode,
8867
- {
8868
- appName,
8869
- platform
8870
- }
8871
- );
8872
- const packageJsonPath2 = joinPath(functionsRootDir, "package.json");
8873
- await ensureDir(functionsRootDir);
8874
- await write(packageJsonPath2, functionsPackageJson, {
8875
- format: "json",
8876
- overwrite: true
8877
- });
8878
- const templateSourceDir2 = joinPath(
8879
- templatesRoot,
8880
- functionsTemplateName,
8881
- functionsTemplateName
8882
- );
8883
- const fallbackTemplateDir = joinPath(templatesRoot, functionsTemplateName);
8884
- const templateDir2 = pathExists(templateSourceDir2) ? templateSourceDir2 : fallbackTemplateDir;
8885
- const templateFiles2 = await glob("**/*", {
8886
- cwd: templateDir2,
8887
- dot: true,
8888
- onlyFiles: true
8889
- });
8890
- for (const file of templateFiles2) {
8891
- if (file === "package.json.example") continue;
8892
- const sourcePath = joinPath(templateDir2, file);
8893
- let destFileName = file;
8894
- if (destFileName.endsWith(".example")) {
8895
- destFileName = destFileName.slice(0, -8);
8896
- }
8897
- const destPath = joinPath(functionsRootDir, destFileName);
8898
- await ensureDir(getDirname(destPath));
8899
- await copy(sourcePath, destPath);
8900
- if (await isTextFile(destPath)) {
8901
- await replacePlaceholders(destPath, {
8902
- ...replacements,
8903
- APP_NAME: appName
8987
+ if (row.functionsTemplate) {
8988
+ const functionsTemplateName = row.functionsTemplate;
8989
+ const functionsTemplateExists = pathExists(joinPath(templatesRoot, functionsTemplateName));
8990
+ if (!functionsTemplateExists) {
8991
+ log.warn(`Functions template "${functionsTemplateName}" not found \u2014 skipping functions scaffolding.`);
8992
+ } else {
8993
+ const isSupabaseFunctions = functionsTemplateName === "functions-supabase";
8994
+ const functionsRootDir = isSupabaseFunctions ? appDir : joinPath(appDir, "functions");
8995
+ if (!isSupabaseFunctions) {
8996
+ const functionsPackageJson = generatePackageJson(
8997
+ functionsTemplateName,
8998
+ executionMode,
8999
+ { appName, platform: row.functionsTemplate.replace("functions-", "") }
9000
+ );
9001
+ const packageJsonPath2 = joinPath(functionsRootDir, "package.json");
9002
+ await ensureDir(functionsRootDir);
9003
+ await write(packageJsonPath2, functionsPackageJson, {
9004
+ format: "json",
9005
+ overwrite: true
8904
9006
  });
8905
9007
  }
9008
+ const templateDir2 = joinPath(templatesRoot, functionsTemplateName);
9009
+ const templateFiles2 = await glob("**/*", {
9010
+ cwd: templateDir2,
9011
+ dot: true,
9012
+ onlyFiles: true
9013
+ });
9014
+ for (const file of templateFiles2) {
9015
+ if (file === "package.json.example") continue;
9016
+ const sourcePath = joinPath(templateDir2, file);
9017
+ let destFileName = file;
9018
+ if (destFileName.endsWith(".example")) {
9019
+ destFileName = destFileName.slice(0, -8);
9020
+ }
9021
+ const destPath = joinPath(functionsRootDir, destFileName);
9022
+ await ensureDir(getDirname(destPath));
9023
+ await copy(sourcePath, destPath);
9024
+ if (await isTextFile(destPath)) {
9025
+ await replacePlaceholders(destPath, {
9026
+ ...replacements,
9027
+ APP_NAME: appName
9028
+ });
9029
+ }
9030
+ }
8906
9031
  }
8907
9032
  }
8908
9033
  const deploymentTemplateDir = joinPath(templatesRoot, "root-consumer");
8909
- const firebaseJsonSource = joinPath(
8910
- deploymentTemplateDir,
8911
- "firebase.json.example"
8912
- );
8913
- if (pathExists(firebaseJsonSource)) {
8914
- const firebaseJsonDest = joinPath(appDir, "firebase.json");
8915
- await copy(firebaseJsonSource, firebaseJsonDest);
8916
- if (await isTextFile(firebaseJsonDest)) {
8917
- await replacePlaceholders(firebaseJsonDest, replacements);
9034
+ if (deployConfig === "firebase") {
9035
+ const firebaseJsonSource = joinPath(deploymentTemplateDir, "firebase.json.example");
9036
+ if (pathExists(firebaseJsonSource)) {
9037
+ await copy(firebaseJsonSource, joinPath(appDir, "firebase.json"));
9038
+ const firebaseJsonDest = joinPath(appDir, "firebase.json");
9039
+ if (await isTextFile(firebaseJsonDest)) await replacePlaceholders(firebaseJsonDest, replacements);
9040
+ if (appTemplate === "nextjs") {
9041
+ const firebaseJson = readSync(firebaseJsonDest, { format: "json" });
9042
+ if (firebaseJson.hosting?.rewrites) {
9043
+ firebaseJson.hosting.rewrites = firebaseJson.hosting.rewrites.filter(
9044
+ (r2) => r2.destination !== "/index.html"
9045
+ );
9046
+ }
9047
+ if (firebaseJson.hosting) {
9048
+ firebaseJson.hosting.public = "out";
9049
+ }
9050
+ await write(firebaseJsonDest, firebaseJson, { format: "json", overwrite: true });
9051
+ }
9052
+ }
9053
+ const firebasercSource = joinPath(deploymentTemplateDir, ".firebaserc.example");
9054
+ if (pathExists(firebasercSource)) {
9055
+ await copy(firebasercSource, joinPath(appDir, ".firebaserc"));
9056
+ const firebasercDest = joinPath(appDir, ".firebaserc");
9057
+ if (await isTextFile(firebasercDest)) await replacePlaceholders(firebasercDest, replacements);
9058
+ }
9059
+ if (row.functionsTemplate === "functions-firebase") {
9060
+ for (const example of ["firestore.rules.example", "firestore.indexes.json.example", "storage.rules.example"]) {
9061
+ const src = joinPath(deploymentTemplateDir, example);
9062
+ if (pathExists(src)) {
9063
+ await copy(src, joinPath(appDir, example.replace(".example", "")));
9064
+ }
9065
+ }
8918
9066
  }
8919
9067
  }
8920
- const firebasercSource = joinPath(
8921
- deploymentTemplateDir,
8922
- ".firebaserc.example"
8923
- );
8924
- if (pathExists(firebasercSource)) {
9068
+ if (!deployConfig && backend === "firebase" && row.functionsTemplate) {
9069
+ const firebaseJsonPath = joinPath(appDir, "firebase.json");
9070
+ if (!pathExists(firebaseJsonPath)) {
9071
+ const expoFirebaseJson = {
9072
+ functions: [
9073
+ {
9074
+ source: "functions",
9075
+ codebase: "default",
9076
+ ignore: ["node_modules", ".git", "firebase-debug.log", "firebase-debug.*.log", "**/.*", "**/*.test.ts", "**/__tests__/**"],
9077
+ runtime: "nodejs22"
9078
+ }
9079
+ ],
9080
+ firestore: { rules: "firestore.rules", indexes: "firestore.indexes.json" },
9081
+ storage: { rules: "storage.rules" },
9082
+ emulators: {
9083
+ auth: { port: 9099 },
9084
+ functions: { port: 5001 },
9085
+ firestore: { port: 8080 },
9086
+ storage: { port: 9199 },
9087
+ ui: { enabled: true, port: 4e3 }
9088
+ }
9089
+ };
9090
+ await write(firebaseJsonPath, expoFirebaseJson, { format: "json", overwrite: true });
9091
+ }
9092
+ const firebasercSource = joinPath(deploymentTemplateDir, ".firebaserc.example");
8925
9093
  const firebasercDest = joinPath(appDir, ".firebaserc");
8926
- await copy(firebasercSource, firebasercDest);
8927
- if (await isTextFile(firebasercDest)) {
8928
- await replacePlaceholders(firebasercDest, replacements);
8929
- }
8930
- }
8931
- if (appConfig.needsBackend && appConfig.backendPlatform === "firebase") {
8932
- const rulesFiles = [
8933
- "firestore.rules.example",
8934
- "firestore.indexes.json.example",
8935
- "storage.rules.example"
8936
- ];
8937
- for (const example of rulesFiles) {
9094
+ if (pathExists(firebasercSource) && !pathExists(firebasercDest)) {
9095
+ 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"]) {
8938
9099
  const src = joinPath(deploymentTemplateDir, example);
8939
- if (pathExists(src)) {
8940
- const destName = example.replace(".example", "");
8941
- const dest = joinPath(appDir, destName);
9100
+ const dest = joinPath(appDir, example.replace(".example", ""));
9101
+ if (pathExists(src) && !pathExists(dest)) {
8942
9102
  await copy(src, dest);
8943
9103
  }
8944
9104
  }
8945
9105
  }
8946
- if (appTemplate === "nextjs" || appConfig.needsBackend && appConfig.backendPlatform === "vercel") {
8947
- const vercelJsonSource = joinPath(
8948
- deploymentTemplateDir,
8949
- "vercel.json.example"
8950
- );
9106
+ if (deployConfig === "vercel-vercel") {
9107
+ const vercelJsonSource = joinPath(deploymentTemplateDir, "vercel.json.example");
8951
9108
  if (pathExists(vercelJsonSource)) {
9109
+ await copy(vercelJsonSource, joinPath(appDir, "vercel.json"));
8952
9110
  const vercelJsonDest = joinPath(appDir, "vercel.json");
8953
- await copy(vercelJsonSource, vercelJsonDest);
8954
- if (await isTextFile(vercelJsonDest)) {
8955
- await replacePlaceholders(vercelJsonDest, replacements);
8956
- }
9111
+ if (await isTextFile(vercelJsonDest)) await replacePlaceholders(vercelJsonDest, replacements);
8957
9112
  }
8958
9113
  }
8959
- const backendInfo = appConfig.needsBackend && appConfig.backendPlatform ? ` with ${appConfig.backendPlatform} functions` : "";
9114
+ const backendInfo = row.functionsTemplate ? ` with ${row.functionsTemplate.replace("functions-", "")} functions` : "";
8960
9115
  s.stop(`Created ${appName}${backendInfo}`);
8961
9116
  const rootPackageJsonPath = joinPath(workspaceRoot, "package.json");
8962
9117
  if (pathExists(rootPackageJsonPath)) {
@@ -8968,7 +9123,7 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
8968
9123
  }
8969
9124
  if (isInteractive) {
8970
9125
  Se("\u{1F389} App created successfully!");
8971
- const firebaseStep = appConfig.needsBackend && appConfig.backendPlatform === "firebase" ? `2. Set Firebase project: cd apps/${appName} && firebase use --add (or edit .firebase rc)
9126
+ const firebaseStep = row.functionsTemplate === "functions-firebase" ? `2. Set Firebase project: cd apps/${appName} && firebase use --add (or edit .firebaserc)
8972
9127
  3. bun install
8973
9128
  4. bun run dev` : `2. bun install
8974
9129
  3. bun run dev`;
@@ -8985,8 +9140,8 @@ Happy coding!`,
8985
9140
  }
8986
9141
  async function main(options) {
8987
9142
  try {
8988
- const hasCliOptions = options?.name || options?.builder !== void 0;
8989
- if (hasCliOptions && options?.name) {
9143
+ const hasExplicitFlags = options?.builder !== void 0 || options?.functions !== void 0;
9144
+ if (hasExplicitFlags && options?.name) {
8990
9145
  const appName = options.name;
8991
9146
  const builder = options.builder || "vite";
8992
9147
  const includeFunctions = options.functions ?? false;
@@ -9001,7 +9156,7 @@ async function main(options) {
9001
9156
  );
9002
9157
  }
9003
9158
  const appConfig = {
9004
- template: builder === "next" ? "nextjs" : "vite",
9159
+ template: builder === "next" ? "nextjs" : builder === "expo" ? "expo" : "vite",
9005
9160
  needsBackend: includeFunctions,
9006
9161
  backendPlatform: includeFunctions ? "firebase" : void 0,
9007
9162
  needsCRUD: true,
@@ -9014,7 +9169,7 @@ async function main(options) {
9014
9169
  };
9015
9170
  await createApp(appName, appConfig);
9016
9171
  } else {
9017
- await createApp();
9172
+ await createApp(options?.name);
9018
9173
  }
9019
9174
  } catch (error2) {
9020
9175
  log.error("\n\u274C Error creating app:", error2);