@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
@@ -7757,6 +7757,147 @@ var init_PathResolver = __esm({
7757
7757
  }
7758
7758
  });
7759
7759
 
7760
+ // packages/tooling/src/utils/errors.ts
7761
+ var DO_NOT_DEV_ERROR_CODES, DoNotDevError;
7762
+ var init_errors = __esm({
7763
+ "packages/tooling/src/utils/errors.ts"() {
7764
+ "use strict";
7765
+ init_utils();
7766
+ DO_NOT_DEV_ERROR_CODES = {
7767
+ CONFIGURATION_ERROR: "configuration-error",
7768
+ CONFIG_NOT_FOUND: "config-not-found",
7769
+ CONFIG_INVALID: "config-invalid",
7770
+ PATH_RESOLUTION_ERROR: "path-resolution-error",
7771
+ FILE_OPERATION_ERROR: "file-operation-error",
7772
+ FILE_NOT_FOUND: "file-not-found",
7773
+ PERMISSION_DENIED: "permission-denied",
7774
+ GENERATION_ERROR: "generation-error",
7775
+ TEMPLATE_ERROR: "template-error",
7776
+ TEMPLATE_NOT_FOUND: "template-not-found",
7777
+ CLI_EXECUTION_ERROR: "cli-execution-error",
7778
+ COMMAND_NOT_FOUND: "command-not-found",
7779
+ COMMAND_FAILED: "command-failed",
7780
+ VALIDATION_ERROR: "validation-error",
7781
+ SCHEMA_ERROR: "schema-error",
7782
+ DEPENDENCY_ERROR: "dependency-error",
7783
+ DEPENDENCY_NOT_FOUND: "dependency-not-found",
7784
+ DEPENDENCY_VERSION_ERROR: "dependency-version-error",
7785
+ INVALID_ARGUMENT: "invalid-argument",
7786
+ MISSING_ARGUMENT: "missing-argument",
7787
+ MISSING_PROJECT_ID: "missing-project-id",
7788
+ FIREBASE_CLI_ERROR: "firebase-cli-error",
7789
+ DEPLOYMENT_FAILED: "deployment-failed",
7790
+ OPERATION_CANCELLED: "operation-cancelled",
7791
+ TIMEOUT_ERROR: "timeout-error",
7792
+ UNKNOWN_ERROR: "unknown-error"
7793
+ };
7794
+ DoNotDevError = class _DoNotDevError extends Error {
7795
+ /** The error code categorizing this error */
7796
+ code;
7797
+ /** Original error if this is wrapping another error */
7798
+ originalError;
7799
+ /** Additional context for the error */
7800
+ context;
7801
+ /** Whether this error should be displayed to the user */
7802
+ displayable;
7803
+ /**
7804
+ * Creates a new DoNotDev error
7805
+ *
7806
+ * @param {string} message - Error message
7807
+ * @param {DoNotDevErrorCode} code - Error code
7808
+ * @param {object} [options] - Additional error options
7809
+ * @param {Error} [options.originalError] - Original error if wrapping
7810
+ * @param {Record<string, any>} [options.context] - Additional context data
7811
+ * @param {boolean} [options.displayable=true] - Whether this error should be displayed to the user
7812
+ */
7813
+ constructor(message, code = DO_NOT_DEV_ERROR_CODES.UNKNOWN_ERROR, options) {
7814
+ super(message);
7815
+ this.name = "DoNotDevError";
7816
+ this.code = code;
7817
+ this.originalError = options?.originalError;
7818
+ this.context = options?.context;
7819
+ this.displayable = options?.displayable !== false;
7820
+ Object.setPrototypeOf(this, _DoNotDevError.prototype);
7821
+ if (Error.captureStackTrace) {
7822
+ Error.captureStackTrace(this, _DoNotDevError);
7823
+ }
7824
+ }
7825
+ /**
7826
+ * Formats the error for logging or display
7827
+ *
7828
+ * @returns {string} Formatted error message with code
7829
+ */
7830
+ format() {
7831
+ return `[${this.code}] ${this.message}`;
7832
+ }
7833
+ /**
7834
+ * Creates a wrapped error from another error
7835
+ *
7836
+ * @param {Error} error - Original error to wrap
7837
+ * @param {string} [context] - Additional context for the error
7838
+ * @param {DoNotDevErrorCode} [code='unknown-error'] - Error code
7839
+ * @param {object} [options] - Additional error options
7840
+ * @param {Record<string, any>} [options.context] - Additional context data
7841
+ * @param {boolean} [options.displayable=true] - Whether this error should be displayed to the user
7842
+ * @returns {DoNotDevError} New DoNotDev error wrapping the original
7843
+ */
7844
+ static from(error2, context, code = DO_NOT_DEV_ERROR_CODES.UNKNOWN_ERROR, options) {
7845
+ if (!(error2 instanceof Error)) {
7846
+ return new _DoNotDevError(
7847
+ `Unknown error: ${String(error2)}`,
7848
+ code,
7849
+ options
7850
+ );
7851
+ }
7852
+ if (error2 instanceof _DoNotDevError) {
7853
+ if (!context) {
7854
+ return error2;
7855
+ }
7856
+ return new _DoNotDevError(`${context}: ${error2.message}`, error2.code, {
7857
+ originalError: error2.originalError || error2,
7858
+ context: { ...error2.context, ...options?.context || {} },
7859
+ displayable: options?.displayable ?? error2.displayable
7860
+ });
7861
+ }
7862
+ const message = context ? `${context}: ${error2.message}` : error2.message;
7863
+ return new _DoNotDevError(message, code, {
7864
+ originalError: error2,
7865
+ context: options?.context,
7866
+ displayable: options?.displayable
7867
+ });
7868
+ }
7869
+ /**
7870
+ * Maps common error types to DoNotDevErrorCode
7871
+ *
7872
+ * @param {Error} error - Error to analyze
7873
+ * @returns {DoNotDevErrorCode} Mapped error code
7874
+ */
7875
+ static getErrorCodeFromError(error2) {
7876
+ if (error2 instanceof _DoNotDevError) {
7877
+ return error2.code;
7878
+ }
7879
+ const message = error2.message.toLowerCase();
7880
+ if (error2.name === "ValidationError" || message.includes("validation")) {
7881
+ return DO_NOT_DEV_ERROR_CODES.VALIDATION_ERROR;
7882
+ }
7883
+ if (message.includes("not found") || message.includes("no such file")) {
7884
+ return DO_NOT_DEV_ERROR_CODES.FILE_NOT_FOUND;
7885
+ }
7886
+ if (message.includes("permission") || message.includes("access denied")) {
7887
+ return DO_NOT_DEV_ERROR_CODES.PERMISSION_DENIED;
7888
+ }
7889
+ if (message.includes("timeout") || message.includes("timed out")) {
7890
+ return DO_NOT_DEV_ERROR_CODES.TIMEOUT_ERROR;
7891
+ }
7892
+ if (message.includes("dependency") || message.includes("module not found")) {
7893
+ return DO_NOT_DEV_ERROR_CODES.DEPENDENCY_ERROR;
7894
+ }
7895
+ return DO_NOT_DEV_ERROR_CODES.UNKNOWN_ERROR;
7896
+ }
7897
+ };
7898
+ }
7899
+ });
7900
+
7760
7901
  // packages/tooling/src/utils/pathResolver.ts
7761
7902
  var pathResolver_exports = {};
7762
7903
  __export(pathResolver_exports, {
@@ -7884,16 +8025,19 @@ function getBinPath(binaryName) {
7884
8025
  return binaryName;
7885
8026
  }
7886
8027
  function detectExecutionMode() {
7887
- const fileUrlPath = new URL(import.meta.url).pathname;
7888
- if (!fileUrlPath.includes("node_modules")) {
7889
- return "development";
8028
+ const fileUrlPath = fileURLToPath2(import.meta.url);
8029
+ if (fileUrlPath.includes("node_modules")) {
8030
+ return "published";
7890
8031
  }
7891
- return "published";
8032
+ if (fileUrlPath.includes("/dist/")) {
8033
+ return "published";
8034
+ }
8035
+ return "development";
7892
8036
  }
7893
8037
  function getTemplatesRoot() {
7894
8038
  const mode = detectExecutionMode();
7895
8039
  if (mode === "development") {
7896
- const fileUrlPath = new URL(import.meta.url).pathname;
8040
+ const fileUrlPath = fileURLToPath2(import.meta.url);
7897
8041
  const frameworkRoot = normalizePath(dirname2(fileUrlPath), "../../../..");
7898
8042
  return normalizePath(frameworkRoot, PACKAGE_PATHS.CLI, "templates");
7899
8043
  } else {
@@ -8146,6 +8290,48 @@ var init_pathResolver = __esm({
8146
8290
  }
8147
8291
  });
8148
8292
 
8293
+ // packages/tooling/src/utils/typed-file-operations.ts
8294
+ function readTurboJson(filePath) {
8295
+ if (!pathExists(filePath)) {
8296
+ return null;
8297
+ }
8298
+ const content = readSync(filePath, { format: "json" });
8299
+ if (!content || typeof content !== "object") {
8300
+ throw new DoNotDevError(
8301
+ `Invalid turbo.json: ${filePath}`,
8302
+ DO_NOT_DEV_ERROR_CODES.CONFIG_INVALID,
8303
+ { context: { filePath } }
8304
+ );
8305
+ }
8306
+ return content;
8307
+ }
8308
+ function readTsConfigJson(filePath) {
8309
+ if (!pathExists(filePath)) {
8310
+ throw new DoNotDevError(
8311
+ `tsconfig.json not found: ${filePath}`,
8312
+ DO_NOT_DEV_ERROR_CODES.FILE_NOT_FOUND,
8313
+ { context: { filePath } }
8314
+ );
8315
+ }
8316
+ const content = readSync(filePath, { format: "json" });
8317
+ if (!content || typeof content !== "object") {
8318
+ throw new DoNotDevError(
8319
+ `Invalid tsconfig.json: ${filePath}`,
8320
+ DO_NOT_DEV_ERROR_CODES.CONFIG_INVALID,
8321
+ { context: { filePath } }
8322
+ );
8323
+ }
8324
+ return content;
8325
+ }
8326
+ var init_typed_file_operations = __esm({
8327
+ "packages/tooling/src/utils/typed-file-operations.ts"() {
8328
+ "use strict";
8329
+ init_utils();
8330
+ init_pathResolver();
8331
+ init_errors();
8332
+ }
8333
+ });
8334
+
8149
8335
  // packages/tooling/src/bundler/utils.ts
8150
8336
  import { Buffer as Buffer2 } from "node:buffer";
8151
8337
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
@@ -8173,18 +8359,7 @@ var init_utils = __esm({
8173
8359
  }
8174
8360
  });
8175
8361
 
8176
- // packages/cli/src/bin/commands/create-project.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
8362
  // packages/tooling/src/utils/cli-input.ts
8186
- init_utils();
8187
- init_dist2();
8188
8363
  async function askForInput(message, defaultValue = "") {
8189
8364
  const result = await he({
8190
8365
  message,
@@ -8225,11 +8400,28 @@ async function askForSelection(message, choices, defaultValue = 0) {
8225
8400
  }
8226
8401
  return result;
8227
8402
  }
8403
+ var init_cli_input = __esm({
8404
+ "packages/tooling/src/utils/cli-input.ts"() {
8405
+ "use strict";
8406
+ init_utils();
8407
+ init_dist2();
8408
+ }
8409
+ });
8410
+
8411
+ // packages/cli/src/bin/commands/create-project.ts
8412
+ init_utils();
8413
+
8414
+ // packages/tooling/src/index.ts
8415
+ init_utils();
8416
+
8417
+ // packages/tooling/src/cli/index.ts
8418
+ init_utils();
8228
8419
 
8229
8420
  // packages/tooling/src/utils/create-utils.ts
8230
8421
  init_utils();
8231
8422
  init_cli_output();
8232
8423
  init_pathResolver();
8424
+ init_typed_file_operations();
8233
8425
  async function copyTemplateFiles(sourceDir, destDir, replacements) {
8234
8426
  if (!pathExists(sourceDir)) {
8235
8427
  log.warn(`Warning: Template directory does not exist: ${sourceDir}`);
@@ -8414,12 +8606,7 @@ async function mergeRootTsConfig(rootDir, allAppNames, includeFunctions = false)
8414
8606
  return;
8415
8607
  }
8416
8608
  try {
8417
- const tsconfig = readSync(tsconfigPath, { format: "json" });
8418
- if (!tsconfig) {
8419
- throw new Error(
8420
- `tsconfig.json is empty or invalid JSON at ${tsconfigPath}`
8421
- );
8422
- }
8609
+ const tsconfig = readTsConfigJson(tsconfigPath);
8423
8610
  tsconfig.references = tsconfig.references || [];
8424
8611
  const existingPaths = new Set(
8425
8612
  tsconfig.references.map((ref) => ref.path)
@@ -8453,7 +8640,7 @@ async function mergeRootTurboJson(rootDir) {
8453
8640
  return;
8454
8641
  }
8455
8642
  try {
8456
- const turboJson = readSync(turboJsonPath, { format: "json" });
8643
+ const turboJson = readTurboJson(turboJsonPath);
8457
8644
  if (!turboJson) {
8458
8645
  throw new Error(
8459
8646
  `turbo.json is empty or invalid JSON at ${turboJsonPath}`
@@ -8505,7 +8692,7 @@ async function mergeRootTurboJson(rootDir) {
8505
8692
  }
8506
8693
  } catch (error2) {
8507
8694
  log.warn(
8508
- `Warning: Could not merge root turbo.json: ${error2.message || "Unknown error"}`
8695
+ `Warning: Could not merge root turbo.json: ${error2 instanceof Error ? error2.message : String(error2)}`
8509
8696
  );
8510
8697
  }
8511
8698
  }
@@ -8526,15 +8713,10 @@ function getMatrixPath(mode) {
8526
8713
  }
8527
8714
  const executionMode = mode || detectExecutionMode();
8528
8715
  if (executionMode === "development") {
8529
- const repoRoot = getRepoRoot();
8530
- if (repoRoot) {
8531
- const devPath = normalizePath(
8532
- repoRoot,
8533
- "packages/cli/dependencies-matrix.json"
8534
- );
8535
- if (pathExists(devPath)) {
8536
- return devPath;
8537
- }
8716
+ const templatesRoot = getTemplatesRoot();
8717
+ const devPath = normalizePath(templatesRoot, "..", "dependencies-matrix.json");
8718
+ if (pathExists(devPath)) {
8719
+ return devPath;
8538
8720
  }
8539
8721
  }
8540
8722
  return null;
@@ -8549,8 +8731,8 @@ function getCliVersion(mode) {
8549
8731
  }
8550
8732
  const executionMode = mode || detectExecutionMode();
8551
8733
  if (executionMode === "development") {
8552
- const repoRoot = getRepoRoot();
8553
- const cliPackageJson = joinPath(repoRoot, "packages/cli/package.json");
8734
+ const templatesRoot = getTemplatesRoot();
8735
+ const cliPackageJson = normalizePath(templatesRoot, "..", "package.json");
8554
8736
  if (pathExists(cliPackageJson)) {
8555
8737
  const pkg = readSync(cliPackageJson, { format: "json" });
8556
8738
  return String(pkg?.version || "0.0.0");
@@ -8601,6 +8783,10 @@ var APP_QUESTIONNAIRE = [
8601
8783
  {
8602
8784
  title: "Next.js \u2014 Static content/SEO (SSR, SSG) \u2014 BETA",
8603
8785
  value: "nextjs"
8786
+ },
8787
+ {
8788
+ title: "Expo \u2014 Mobile app (iOS + Android)",
8789
+ value: "expo"
8604
8790
  }
8605
8791
  ]
8606
8792
  },
@@ -8627,6 +8813,10 @@ var APP_QUESTIONNAIRE = [
8627
8813
  {
8628
8814
  title: "Vercel \u2014 Serverless functions (edge + node.js)",
8629
8815
  value: "vercel"
8816
+ },
8817
+ {
8818
+ title: "Supabase \u2014 Postgres + Auth + Storage",
8819
+ value: "supabase"
8630
8820
  }
8631
8821
  ]
8632
8822
  }
@@ -8693,6 +8883,7 @@ async function runQuestionnaire(questions, initialAnswers, showWIP, askFor) {
8693
8883
 
8694
8884
  // packages/tooling/src/scaffolding/create-app.ts
8695
8885
  init_utils();
8886
+ init_cli_input();
8696
8887
  init_cli_output();
8697
8888
 
8698
8889
  // packages/tooling/src/utils/dependency-resolver.ts
@@ -8726,6 +8917,15 @@ function generateScripts(templateName, options) {
8726
8917
  scripts.preview = "next start";
8727
8918
  scripts.lint = "eslint src/";
8728
8919
  scripts["type-check"] = "tsc --noEmit";
8920
+ } else if (templateName.includes("expo")) {
8921
+ scripts.dev = "expo start";
8922
+ scripts.start = "expo start";
8923
+ scripts.android = "expo start --android";
8924
+ scripts.ios = "expo start --ios";
8925
+ scripts.web = "expo start --web";
8926
+ scripts.build = "expo export";
8927
+ scripts.lint = "eslint .";
8928
+ scripts["type-check"] = "tsc --noEmit";
8729
8929
  } else if (templateName === "consumer-root") {
8730
8930
  scripts.dev = "turbo run dev";
8731
8931
  scripts.build = "turbo run build";
@@ -8814,14 +9014,14 @@ function generatePackageJson(templateName, mode, options = {}) {
8814
9014
  result.main = "./index.ts";
8815
9015
  result.types = "./index.ts";
8816
9016
  }
8817
- if (templateName.includes("vite") || templateName.includes("nextjs") || templateName.includes("functions")) {
9017
+ if (templateName.includes("vite") || templateName.includes("nextjs") || templateName.includes("expo") || templateName.includes("functions")) {
8818
9018
  if (!dependencies.entities) {
8819
9019
  dependencies.entities = "workspace:*";
8820
9020
  }
8821
9021
  }
8822
9022
  if (templateName.includes("functions")) {
8823
9023
  result.main = "lib/index.js";
8824
- result.engines = { node: "20" };
9024
+ result.engines = { node: "22" };
8825
9025
  if (options.appName) {
8826
9026
  const platform = templateName.includes("vercel") ? "Vercel" : "Firebase";
8827
9027
  result.description = `${options.appName} ${platform} Functions`;
@@ -8862,6 +9062,40 @@ function generatePackageJson(templateName, mode, options = {}) {
8862
9062
  // packages/tooling/src/scaffolding/create-app.ts
8863
9063
  init_pathResolver();
8864
9064
  init_pathResolver();
9065
+
9066
+ // packages/tooling/src/scaffolding/scaffold-matrix.ts
9067
+ 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);
9088
+ }
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;
9096
+ }
9097
+
9098
+ // packages/tooling/src/scaffolding/create-app.ts
8865
9099
  var SHOW_WIP = process.env.SHOW_WIP === "true" || process.argv.includes("--wip");
8866
9100
  async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
8867
9101
  const isInteractive = !appName || !appConfig;
@@ -8947,14 +9181,19 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
8947
9181
  const s = Y2();
8948
9182
  s.start(`Creating app: ${appName}`);
8949
9183
  await ensureDir(appDir);
8950
- const templateDir = appTemplate === "demo" ? "app-demo" : appTemplate === "nextjs" ? "app-next" : "app-vite";
9184
+ const builder = appTemplate;
9185
+ const backend = appConfig.needsBackend && appConfig.backendPlatform ? appConfig.backendPlatform : "none";
9186
+ const row = getScaffoldRow(builder, backend);
9187
+ const templateDir = row.baseTemplate;
8951
9188
  const firebaseProjectId = (appConfig?.firebaseProjectId ?? "").trim() || appName.toLowerCase().replace(/\s+/g, "-");
8952
9189
  const firebaseRegion = appConfig?.firebaseRegion ?? "europe-west1";
9190
+ const backendPlatform = appConfig.backendPlatform ?? "firebase";
9191
+ const deployConfig = row.deployConfig;
8953
9192
  const replacements = {
8954
9193
  projectName: appName,
8955
9194
  appName,
8956
9195
  appShortName: appName.toUpperCase().replace(/-/g, " "),
8957
- includeFunctions: Boolean(appConfig.needsBackend),
9196
+ includeFunctions: Boolean(row.functionsTemplate),
8958
9197
  needsCRUD: Boolean(appConfig.needsCRUD),
8959
9198
  setupGithubActions: false,
8960
9199
  appNames: [appName],
@@ -8964,8 +9203,13 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
8964
9203
  monorepoRelativePath: "../../packages/tooling",
8965
9204
  appTemplate,
8966
9205
  isNextjs: appTemplate === "nextjs",
9206
+ isExpo: appTemplate === "expo",
8967
9207
  YOUR_FIREBASE_PROJECT_ID: firebaseProjectId,
8968
- YOUR_REGION: firebaseRegion
9208
+ YOUR_REGION: firebaseRegion,
9209
+ backendPlatform,
9210
+ isFirebase: backendPlatform === "firebase",
9211
+ isSupabase: backendPlatform === "supabase",
9212
+ isVercel: backendPlatform === "vercel"
8969
9213
  };
8970
9214
  const templateSourceDir = joinPath(templatesRoot, templateDir);
8971
9215
  const templateFiles = await glob("**/*", {
@@ -8986,118 +9230,208 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
8986
9230
  await replacePlaceholders(destPath, replacements);
8987
9231
  }
8988
9232
  }
9233
+ if (row.overlay) {
9234
+ const overlayDir = joinPath(templatesRoot, row.overlay);
9235
+ if (pathExists(overlayDir)) {
9236
+ const overlayFiles = await glob("**/*", {
9237
+ cwd: overlayDir,
9238
+ dot: true,
9239
+ onlyFiles: true
9240
+ });
9241
+ for (const file of overlayFiles) {
9242
+ if (file.startsWith("env.fragment")) continue;
9243
+ if (file === "vercel.headers.example") continue;
9244
+ if (file === "vercel.json.example") continue;
9245
+ if (file === "src/config/providers.ts.example") {
9246
+ const variantFile = `src/config/providers.${appTemplate}.ts.example`;
9247
+ if (overlayFiles.includes(variantFile)) continue;
9248
+ }
9249
+ const providersVariant = file.match(/^src\/config\/providers\.(\w+)\.ts\.example$/);
9250
+ if (providersVariant) {
9251
+ if (providersVariant[1] !== appTemplate) continue;
9252
+ const destPath2 = joinPath(appDir, "src/config/providers.ts");
9253
+ await ensureDir(getDirname(destPath2));
9254
+ await copy(joinPath(overlayDir, file), destPath2, { overwrite: true });
9255
+ if (await isTextFile(destPath2)) await replacePlaceholders(destPath2, replacements);
9256
+ continue;
9257
+ }
9258
+ const sourcePath = joinPath(overlayDir, file);
9259
+ let destFileName = file;
9260
+ if (destFileName.endsWith(".example")) {
9261
+ destFileName = destFileName.slice(0, -8);
9262
+ }
9263
+ const destPath = joinPath(appDir, destFileName);
9264
+ await ensureDir(getDirname(destPath));
9265
+ await copy(sourcePath, destPath, { overwrite: true });
9266
+ if (await isTextFile(destPath)) {
9267
+ await replacePlaceholders(destPath, replacements);
9268
+ }
9269
+ }
9270
+ const fragmentName = appTemplate === "nextjs" ? "env.fragment.nextjs.example" : appTemplate === "expo" ? "env.fragment.expo.example" : "env.fragment.example";
9271
+ const fragmentPath = joinPath(overlayDir, fragmentName);
9272
+ const envPath = joinPath(appDir, ".env");
9273
+ if (pathExists(fragmentPath) && pathExists(envPath)) {
9274
+ const baseEnv = readSync(envPath);
9275
+ const fragment = readSync(fragmentPath);
9276
+ await write(envPath, baseEnv + "\n" + fragment, { overwrite: true });
9277
+ }
9278
+ if (deployConfig === "vercel-supabase") {
9279
+ const vercelPath = joinPath(appDir, "vercel.json");
9280
+ const headersFragmentPath = joinPath(overlayDir, "vercel.headers.example");
9281
+ const fullVercelPath = joinPath(overlayDir, "vercel.json.example");
9282
+ if (pathExists(vercelPath) && pathExists(headersFragmentPath)) {
9283
+ 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 });
9287
+ } else if (pathExists(fullVercelPath)) {
9288
+ await copy(fullVercelPath, vercelPath);
9289
+ if (await isTextFile(vercelPath)) await replacePlaceholders(vercelPath, replacements);
9290
+ }
9291
+ }
9292
+ }
9293
+ }
8989
9294
  const executionMode = detectExecutionMode();
8990
9295
  const templateName = appTemplate === "demo" ? "demo" : executionMode === "development" ? `dev-${appTemplate}` : `consumer-${appTemplate}`;
8991
9296
  const packageJson = generatePackageJson(templateName, executionMode, {
8992
9297
  appName,
8993
9298
  template: appTemplate === "demo" ? "vite" : appTemplate,
8994
- includeFunctions: Boolean(appConfig.needsBackend)
9299
+ includeFunctions: Boolean(row.functionsTemplate),
9300
+ platform: appTemplate === "demo" ? void 0 : backendPlatform
8995
9301
  });
8996
9302
  const packageJsonPath = joinPath(appDir, "package.json");
8997
9303
  await write(packageJsonPath, packageJson, {
8998
9304
  format: "json",
8999
9305
  overwrite: true
9000
9306
  });
9001
- if (appConfig.needsBackend && appConfig.backendPlatform) {
9002
- const platform = appConfig.backendPlatform;
9003
- const functionsRootDir = joinPath(appDir, "functions");
9004
- const functionsTemplateName = `functions-${platform}`;
9005
- const functionsPackageJson = generatePackageJson(
9006
- functionsTemplateName,
9007
- executionMode,
9008
- {
9009
- appName,
9010
- platform
9011
- }
9012
- );
9013
- const packageJsonPath2 = joinPath(functionsRootDir, "package.json");
9014
- await ensureDir(functionsRootDir);
9015
- await write(packageJsonPath2, functionsPackageJson, {
9016
- format: "json",
9017
- overwrite: true
9018
- });
9019
- const templateSourceDir2 = joinPath(
9020
- templatesRoot,
9021
- functionsTemplateName,
9022
- functionsTemplateName
9023
- );
9024
- const fallbackTemplateDir = joinPath(templatesRoot, functionsTemplateName);
9025
- const templateDir2 = pathExists(templateSourceDir2) ? templateSourceDir2 : fallbackTemplateDir;
9026
- const templateFiles2 = await glob("**/*", {
9027
- cwd: templateDir2,
9028
- dot: true,
9029
- onlyFiles: true
9030
- });
9031
- for (const file of templateFiles2) {
9032
- if (file === "package.json.example") continue;
9033
- const sourcePath = joinPath(templateDir2, file);
9034
- let destFileName = file;
9035
- if (destFileName.endsWith(".example")) {
9036
- destFileName = destFileName.slice(0, -8);
9037
- }
9038
- const destPath = joinPath(functionsRootDir, destFileName);
9039
- await ensureDir(getDirname(destPath));
9040
- await copy(sourcePath, destPath);
9041
- if (await isTextFile(destPath)) {
9042
- await replacePlaceholders(destPath, {
9043
- ...replacements,
9044
- APP_NAME: appName
9307
+ if (row.functionsTemplate) {
9308
+ const functionsTemplateName = row.functionsTemplate;
9309
+ const functionsTemplateExists = pathExists(joinPath(templatesRoot, functionsTemplateName));
9310
+ if (!functionsTemplateExists) {
9311
+ log.warn(`Functions template "${functionsTemplateName}" not found \u2014 skipping functions scaffolding.`);
9312
+ } else {
9313
+ const isSupabaseFunctions = functionsTemplateName === "functions-supabase";
9314
+ const functionsRootDir = isSupabaseFunctions ? appDir : joinPath(appDir, "functions");
9315
+ if (!isSupabaseFunctions) {
9316
+ const functionsPackageJson = generatePackageJson(
9317
+ functionsTemplateName,
9318
+ executionMode,
9319
+ { appName, platform: row.functionsTemplate.replace("functions-", "") }
9320
+ );
9321
+ const packageJsonPath2 = joinPath(functionsRootDir, "package.json");
9322
+ await ensureDir(functionsRootDir);
9323
+ await write(packageJsonPath2, functionsPackageJson, {
9324
+ format: "json",
9325
+ overwrite: true
9045
9326
  });
9046
9327
  }
9328
+ const templateDir2 = joinPath(templatesRoot, functionsTemplateName);
9329
+ const templateFiles2 = await glob("**/*", {
9330
+ cwd: templateDir2,
9331
+ dot: true,
9332
+ onlyFiles: true
9333
+ });
9334
+ for (const file of templateFiles2) {
9335
+ if (file === "package.json.example") continue;
9336
+ const sourcePath = joinPath(templateDir2, file);
9337
+ let destFileName = file;
9338
+ if (destFileName.endsWith(".example")) {
9339
+ destFileName = destFileName.slice(0, -8);
9340
+ }
9341
+ const destPath = joinPath(functionsRootDir, destFileName);
9342
+ await ensureDir(getDirname(destPath));
9343
+ await copy(sourcePath, destPath);
9344
+ if (await isTextFile(destPath)) {
9345
+ await replacePlaceholders(destPath, {
9346
+ ...replacements,
9347
+ APP_NAME: appName
9348
+ });
9349
+ }
9350
+ }
9047
9351
  }
9048
9352
  }
9049
9353
  const deploymentTemplateDir = joinPath(templatesRoot, "root-consumer");
9050
- const firebaseJsonSource = joinPath(
9051
- deploymentTemplateDir,
9052
- "firebase.json.example"
9053
- );
9054
- if (pathExists(firebaseJsonSource)) {
9055
- const firebaseJsonDest = joinPath(appDir, "firebase.json");
9056
- await copy(firebaseJsonSource, firebaseJsonDest);
9057
- if (await isTextFile(firebaseJsonDest)) {
9058
- await replacePlaceholders(firebaseJsonDest, replacements);
9354
+ if (deployConfig === "firebase") {
9355
+ const firebaseJsonSource = joinPath(deploymentTemplateDir, "firebase.json.example");
9356
+ if (pathExists(firebaseJsonSource)) {
9357
+ await copy(firebaseJsonSource, joinPath(appDir, "firebase.json"));
9358
+ const firebaseJsonDest = joinPath(appDir, "firebase.json");
9359
+ if (await isTextFile(firebaseJsonDest)) await replacePlaceholders(firebaseJsonDest, replacements);
9360
+ if (appTemplate === "nextjs") {
9361
+ const firebaseJson = readSync(firebaseJsonDest, { format: "json" });
9362
+ if (firebaseJson.hosting?.rewrites) {
9363
+ firebaseJson.hosting.rewrites = firebaseJson.hosting.rewrites.filter(
9364
+ (r2) => r2.destination !== "/index.html"
9365
+ );
9366
+ }
9367
+ if (firebaseJson.hosting) {
9368
+ firebaseJson.hosting.public = "out";
9369
+ }
9370
+ await write(firebaseJsonDest, firebaseJson, { format: "json", overwrite: true });
9371
+ }
9372
+ }
9373
+ const firebasercSource = joinPath(deploymentTemplateDir, ".firebaserc.example");
9374
+ if (pathExists(firebasercSource)) {
9375
+ await copy(firebasercSource, joinPath(appDir, ".firebaserc"));
9376
+ const firebasercDest = joinPath(appDir, ".firebaserc");
9377
+ if (await isTextFile(firebasercDest)) await replacePlaceholders(firebasercDest, replacements);
9378
+ }
9379
+ if (row.functionsTemplate === "functions-firebase") {
9380
+ for (const example of ["firestore.rules.example", "firestore.indexes.json.example", "storage.rules.example"]) {
9381
+ const src = joinPath(deploymentTemplateDir, example);
9382
+ if (pathExists(src)) {
9383
+ await copy(src, joinPath(appDir, example.replace(".example", "")));
9384
+ }
9385
+ }
9059
9386
  }
9060
9387
  }
9061
- const firebasercSource = joinPath(
9062
- deploymentTemplateDir,
9063
- ".firebaserc.example"
9064
- );
9065
- if (pathExists(firebasercSource)) {
9388
+ if (!deployConfig && backend === "firebase" && row.functionsTemplate) {
9389
+ const firebaseJsonPath = joinPath(appDir, "firebase.json");
9390
+ if (!pathExists(firebaseJsonPath)) {
9391
+ const expoFirebaseJson = {
9392
+ functions: [
9393
+ {
9394
+ source: "functions",
9395
+ codebase: "default",
9396
+ ignore: ["node_modules", ".git", "firebase-debug.log", "firebase-debug.*.log", "**/.*", "**/*.test.ts", "**/__tests__/**"],
9397
+ runtime: "nodejs22"
9398
+ }
9399
+ ],
9400
+ firestore: { rules: "firestore.rules", indexes: "firestore.indexes.json" },
9401
+ storage: { rules: "storage.rules" },
9402
+ emulators: {
9403
+ auth: { port: 9099 },
9404
+ functions: { port: 5001 },
9405
+ firestore: { port: 8080 },
9406
+ storage: { port: 9199 },
9407
+ ui: { enabled: true, port: 4e3 }
9408
+ }
9409
+ };
9410
+ await write(firebaseJsonPath, expoFirebaseJson, { format: "json", overwrite: true });
9411
+ }
9412
+ const firebasercSource = joinPath(deploymentTemplateDir, ".firebaserc.example");
9066
9413
  const firebasercDest = joinPath(appDir, ".firebaserc");
9067
- await copy(firebasercSource, firebasercDest);
9068
- if (await isTextFile(firebasercDest)) {
9069
- await replacePlaceholders(firebasercDest, replacements);
9070
- }
9071
- }
9072
- if (appConfig.needsBackend && appConfig.backendPlatform === "firebase") {
9073
- const rulesFiles = [
9074
- "firestore.rules.example",
9075
- "firestore.indexes.json.example",
9076
- "storage.rules.example"
9077
- ];
9078
- for (const example of rulesFiles) {
9414
+ if (pathExists(firebasercSource) && !pathExists(firebasercDest)) {
9415
+ 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"]) {
9079
9419
  const src = joinPath(deploymentTemplateDir, example);
9080
- if (pathExists(src)) {
9081
- const destName = example.replace(".example", "");
9082
- const dest = joinPath(appDir, destName);
9420
+ const dest = joinPath(appDir, example.replace(".example", ""));
9421
+ if (pathExists(src) && !pathExists(dest)) {
9083
9422
  await copy(src, dest);
9084
9423
  }
9085
9424
  }
9086
9425
  }
9087
- if (appTemplate === "nextjs" || appConfig.needsBackend && appConfig.backendPlatform === "vercel") {
9088
- const vercelJsonSource = joinPath(
9089
- deploymentTemplateDir,
9090
- "vercel.json.example"
9091
- );
9426
+ if (deployConfig === "vercel-vercel") {
9427
+ const vercelJsonSource = joinPath(deploymentTemplateDir, "vercel.json.example");
9092
9428
  if (pathExists(vercelJsonSource)) {
9429
+ await copy(vercelJsonSource, joinPath(appDir, "vercel.json"));
9093
9430
  const vercelJsonDest = joinPath(appDir, "vercel.json");
9094
- await copy(vercelJsonSource, vercelJsonDest);
9095
- if (await isTextFile(vercelJsonDest)) {
9096
- await replacePlaceholders(vercelJsonDest, replacements);
9097
- }
9431
+ if (await isTextFile(vercelJsonDest)) await replacePlaceholders(vercelJsonDest, replacements);
9098
9432
  }
9099
9433
  }
9100
- const backendInfo = appConfig.needsBackend && appConfig.backendPlatform ? ` with ${appConfig.backendPlatform} functions` : "";
9434
+ const backendInfo = row.functionsTemplate ? ` with ${row.functionsTemplate.replace("functions-", "")} functions` : "";
9101
9435
  s.stop(`Created ${appName}${backendInfo}`);
9102
9436
  const rootPackageJsonPath = joinPath(workspaceRoot, "package.json");
9103
9437
  if (pathExists(rootPackageJsonPath)) {
@@ -9109,7 +9443,7 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
9109
9443
  }
9110
9444
  if (isInteractive) {
9111
9445
  Se("\u{1F389} App created successfully!");
9112
- const firebaseStep = appConfig.needsBackend && appConfig.backendPlatform === "firebase" ? `2. Set Firebase project: cd apps/${appName} && firebase use --add (or edit .firebase rc)
9446
+ const firebaseStep = row.functionsTemplate === "functions-firebase" ? `2. Set Firebase project: cd apps/${appName} && firebase use --add (or edit .firebaserc)
9113
9447
  3. bun install
9114
9448
  4. bun run dev` : `2. bun install
9115
9449
  3. bun run dev`;
@@ -9127,9 +9461,30 @@ Happy coding!`,
9127
9461
 
9128
9462
  // packages/tooling/src/scaffolding/create-project.ts
9129
9463
  init_utils();
9464
+ init_cli_input();
9130
9465
  init_cli_output();
9131
9466
  init_pathResolver();
9132
9467
  var SHOW_WIP2 = process.env.SHOW_WIP === "true" || process.argv.includes("--wip");
9468
+ async function collectAppConfig(appName) {
9469
+ const answers = await runQuestionnaire(APP_QUESTIONNAIRE, {}, SHOW_WIP2, {
9470
+ selection: askForSelection,
9471
+ confirmation: askForConfirmation,
9472
+ input: askForInput
9473
+ });
9474
+ const framework = answers.framework || "vite";
9475
+ const needsBackend = answers.needsBackend || false;
9476
+ const defaultPlatform = framework === "nextjs" ? "vercel" : "firebase";
9477
+ return {
9478
+ template: framework,
9479
+ needsBackend,
9480
+ backendPlatform: needsBackend ? answers.backendPlatform || defaultPlatform : void 0,
9481
+ needsCRUD: false,
9482
+ selectedEntities: [],
9483
+ userAuth: "none",
9484
+ billing: false,
9485
+ features: []
9486
+ };
9487
+ }
9133
9488
  function calculateRelativePath(from, to) {
9134
9489
  try {
9135
9490
  return getRelativePathBetween(from, to).replace(/\\/g, "/");
@@ -9238,74 +9593,25 @@ async function main(options) {
9238
9593
  Me("Merge mode: Merging root files, creating new apps", "\u{1F527} Mode");
9239
9594
  }
9240
9595
  }
9241
- let installDemoApp = await askForConfirmation(
9242
- "Would you like to install the demo app? (component showcase)",
9243
- false
9244
- );
9245
9596
  let appNames = [];
9246
9597
  const appConfigs = {};
9247
9598
  let anyAppNeedsBackend = false;
9248
- const firstAppName = await askForInput(
9249
- "What's your first app name? (press Enter to skip)",
9250
- ""
9251
- );
9252
- if (firstAppName && firstAppName.trim() !== "") {
9253
- const trimmedName = firstAppName.trim();
9254
- if (isReservedAppName(trimmedName)) {
9255
- log.warn(`'${trimmedName}' is reserved for framework demos.`);
9256
- } else if (!isValidFileName(trimmedName)) {
9257
- log.warn(
9258
- `Invalid app name. Use only letters, numbers, dashes (-), and underscores (_).`
9259
- );
9260
- } else {
9261
- appNames.push(trimmedName);
9262
- const framework = await askForSelection(
9263
- `Builder for "${trimmedName}"?`,
9264
- [
9265
- {
9266
- title: "Vite \u2014 SPA/SaaS (client-side rendering, fast dev)",
9267
- value: "vite"
9268
- },
9269
- {
9270
- title: "Next.js \u2014 Static content/SEO (SSR, SSG) \u2014 DoNotDev support: BETA",
9271
- value: "nextjs"
9272
- }
9273
- ],
9274
- 0
9275
- );
9276
- const needsBackend = await askForConfirmation(
9277
- `Do you need a backend for "${trimmedName}"?`,
9278
- false
9279
- );
9280
- if (needsBackend) {
9281
- anyAppNeedsBackend = true;
9282
- }
9283
- appConfigs[trimmedName] = {
9284
- template: framework,
9285
- needsBackend,
9286
- backendPlatform: needsBackend ? framework === "nextjs" ? "vercel" : "firebase" : void 0,
9287
- needsCRUD: false,
9288
- selectedEntities: [],
9289
- userAuth: "none",
9290
- billing: false,
9291
- features: []
9292
- };
9293
- }
9294
- }
9295
- while (appNames.length > 0) {
9296
- const appName = await askForInput("App name (press Enter to finish)", "");
9297
- if (!appName || appName.trim() === "") {
9599
+ let isFirstApp = true;
9600
+ while (true) {
9601
+ const prompt = isFirstApp ? "What's your first app name? (press Enter to skip)" : "App name (press Enter to finish)";
9602
+ const appNameInput = await askForInput(prompt, "");
9603
+ if (!appNameInput || appNameInput.trim() === "") {
9604
+ if (isFirstApp) break;
9298
9605
  break;
9299
9606
  }
9300
- const trimmedName = appName.trim();
9607
+ const trimmedName = appNameInput.trim();
9608
+ isFirstApp = false;
9301
9609
  if (appNames.includes(trimmedName)) {
9302
9610
  log.warn(`'${trimmedName}' already exists. Choose a different name.`);
9303
9611
  continue;
9304
9612
  }
9305
9613
  if (isReservedAppName(trimmedName)) {
9306
- log.warn(
9307
- `'${trimmedName}' is reserved for framework demos. Choose a different name.`
9308
- );
9614
+ log.warn(`'${trimmedName}' is reserved for framework demos.`);
9309
9615
  continue;
9310
9616
  }
9311
9617
  if (!isValidFileName(trimmedName)) {
@@ -9315,38 +9621,15 @@ async function main(options) {
9315
9621
  continue;
9316
9622
  }
9317
9623
  appNames.push(trimmedName);
9318
- const framework = await askForSelection(
9319
- `Builder for "${trimmedName}"?`,
9320
- [
9321
- {
9322
- title: "Vite \u2014 SPA/SaaS (client-side rendering, fast dev)",
9323
- value: "vite"
9324
- },
9325
- {
9326
- title: "Next.js \u2014 Static content/SEO (SSR, SSG) \u2014 DoNotDev support: BETA",
9327
- value: "nextjs"
9328
- }
9329
- ],
9330
- 0
9331
- );
9332
- const needsBackend = await askForConfirmation(
9333
- `Do you need a backend for "${trimmedName}"?`,
9334
- false
9335
- );
9336
- if (needsBackend) {
9337
- anyAppNeedsBackend = true;
9338
- }
9339
- appConfigs[trimmedName] = {
9340
- template: framework,
9341
- needsBackend,
9342
- backendPlatform: needsBackend ? framework === "nextjs" ? "vercel" : "firebase" : void 0,
9343
- needsCRUD: false,
9344
- selectedEntities: [],
9345
- userAuth: "none",
9346
- billing: false,
9347
- features: []
9348
- };
9624
+ Me(`Configuring "${trimmedName}"...`, "\u2699\uFE0F");
9625
+ const config = await collectAppConfig(trimmedName);
9626
+ appConfigs[trimmedName] = config;
9627
+ if (config.needsBackend) anyAppNeedsBackend = true;
9349
9628
  }
9629
+ let installDemoApp = await askForConfirmation(
9630
+ "Would you like to install the demo app? (component showcase)",
9631
+ false
9632
+ );
9350
9633
  let allAppNames = [...appNames];
9351
9634
  let appsToCreate = [];
9352
9635
  let appsToSkip = [];
@@ -9466,12 +9749,14 @@ async function main(options) {
9466
9749
  overwrite: true
9467
9750
  });
9468
9751
  const relativeMonorepoPath = executionMode === "development" ? calculateRelativePath(projectDirNormalized, monorepoRoot) : "";
9752
+ const primaryPlatform = Object.values(appConfigs).find((c) => c.backendPlatform)?.backendPlatform ?? "firebase";
9469
9753
  const rootPackageJson = generatePackageJson(
9470
9754
  "consumer-root",
9471
9755
  executionMode,
9472
9756
  {
9473
9757
  projectName,
9474
- includeFunctions: anyAppNeedsBackend
9758
+ includeFunctions: anyAppNeedsBackend,
9759
+ platform: primaryPlatform
9475
9760
  }
9476
9761
  );
9477
9762
  const packageJsonPath = joinPath(projectDirNormalized, "package.json");
@@ -9530,8 +9815,12 @@ async function main(options) {
9530
9815
  }
9531
9816
  }
9532
9817
  if (setupGithubActions) {
9533
- const ciTemplateDir = joinPath(templatesRoot, "github-consumer");
9534
- await copyTemplateFiles(ciTemplateDir, projectDirNormalized, rootReplacements);
9818
+ const ciTemplateDir = joinPath(templatesRoot, "github");
9819
+ await copyTemplateFiles(
9820
+ ciTemplateDir,
9821
+ projectDirNormalized,
9822
+ rootReplacements
9823
+ );
9535
9824
  }
9536
9825
  s.stop("Project structure created");
9537
9826
  } else {