@donotdev/cli 0.0.14 → 0.0.15

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 (151) hide show
  1. package/dependencies-matrix.json +356 -88
  2. package/dist/bin/commands/agent-setup.js +7 -1
  3. package/dist/bin/commands/build.js +118 -38
  4. package/dist/bin/commands/bump.js +74 -28
  5. package/dist/bin/commands/cacheout.js +37 -9
  6. package/dist/bin/commands/create-app.js +222 -115
  7. package/dist/bin/commands/create-project.js +455 -140
  8. package/dist/bin/commands/deploy.js +1736 -697
  9. package/dist/bin/commands/dev.js +138 -23
  10. package/dist/bin/commands/emu.js +215 -58
  11. package/dist/bin/commands/format.js +37 -9
  12. package/dist/bin/commands/lint.js +37 -9
  13. package/dist/bin/commands/preview.js +142 -23
  14. package/dist/bin/commands/supabase-setup.d.ts +6 -0
  15. package/dist/bin/commands/supabase-setup.d.ts.map +1 -0
  16. package/dist/bin/commands/supabase-setup.js +7 -0
  17. package/dist/bin/commands/supabase-setup.js.map +1 -0
  18. package/dist/bin/commands/sync-secrets.js +211 -34
  19. package/dist/bin/commands/type-check.d.ts +14 -0
  20. package/dist/bin/commands/type-check.d.ts.map +1 -0
  21. package/dist/bin/commands/type-check.js +314 -0
  22. package/dist/bin/commands/type-check.js.map +1 -0
  23. package/dist/bin/commands/wai.js +3 -1
  24. package/dist/bin/dndev.js +27 -2
  25. package/dist/bin/donotdev.js +27 -2
  26. package/dist/index.js +3960 -3015
  27. package/package.json +2 -2
  28. package/templates/app-demo/src/App.tsx.example +1 -0
  29. package/templates/app-demo/src/pages/FullPage.tsx.example +2 -2
  30. package/templates/app-demo/src/pages/components/DemoLayout.tsx.example +2 -2
  31. package/templates/app-demo/src/themes.css.example +5 -12
  32. package/templates/app-expo/.env.example +64 -0
  33. package/templates/app-expo/.expo/README.md.example +5 -0
  34. package/templates/app-expo/.gitignore.example +36 -0
  35. package/templates/app-expo/README.md.example +58 -0
  36. package/templates/app-expo/app/.gitkeep +2 -0
  37. package/templates/app-expo/app/_layout.tsx.example +41 -0
  38. package/templates/app-expo/app/form.tsx.example +52 -0
  39. package/templates/app-expo/app/index.tsx.example +89 -0
  40. package/templates/app-expo/app/list.tsx.example +32 -0
  41. package/templates/app-expo/app/profile.tsx.example +76 -0
  42. package/templates/app-expo/app/signin.tsx.example +53 -0
  43. package/templates/app-expo/app.json.example +39 -0
  44. package/templates/app-expo/babel.config.js.example +10 -0
  45. package/templates/app-expo/eas.json.example +20 -0
  46. package/templates/app-expo/expo-env.d.ts.example +4 -0
  47. package/templates/app-expo/metro.config.js.example +20 -0
  48. package/templates/app-expo/service-account-key.json.example +12 -0
  49. package/templates/app-expo/tsconfig.json.example +19 -0
  50. package/templates/app-next/.env.example +4 -33
  51. package/templates/app-next/src/app/ClientLayout.tsx.example +2 -0
  52. package/templates/app-next/src/app/layout.tsx.example +7 -6
  53. package/templates/app-next/src/globals.css.example +2 -11
  54. package/templates/app-next/src/pages/HomePage.tsx.example +1 -1
  55. package/templates/app-next/src/themes.css.example +10 -13
  56. package/templates/app-vite/.env.example +3 -32
  57. package/templates/app-vite/index.html.example +2 -24
  58. package/templates/app-vite/src/App.tsx.example +2 -0
  59. package/templates/app-vite/src/globals.css.example +2 -12
  60. package/templates/app-vite/src/pages/FormPageExample.tsx.example +1 -2
  61. package/templates/app-vite/src/pages/HomePage.tsx.example +1 -1
  62. package/templates/app-vite/src/themes.css.example +109 -79
  63. package/templates/app-vite/vercel.json.example +11 -0
  64. package/templates/functions-firebase/build.mjs.example +2 -72
  65. package/templates/functions-firebase/functions-firebase/.env.example.example +23 -25
  66. package/templates/functions-firebase/functions-firebase/build.mjs.example +2 -72
  67. package/templates/functions-firebase/functions-firebase/tsconfig.json.example +1 -1
  68. package/templates/functions-supabase/supabase/functions/cancel-subscription/index.ts.example +7 -0
  69. package/templates/functions-supabase/supabase/functions/change-plan/index.ts.example +11 -0
  70. package/templates/functions-supabase/supabase/functions/create-checkout-session/index.ts.example +11 -0
  71. package/templates/functions-supabase/supabase/functions/create-customer-portal/index.ts.example +7 -0
  72. package/templates/functions-supabase/supabase/functions/crud/index.ts.example +16 -0
  73. package/templates/functions-supabase/supabase/functions/delete-account/index.ts.example +7 -0
  74. package/templates/functions-supabase/supabase/functions/get-custom-claims/index.ts.example +7 -0
  75. package/templates/functions-supabase/supabase/functions/get-user-auth-status/index.ts.example +7 -0
  76. package/templates/functions-supabase/supabase/functions/refresh-subscription-status/index.ts.example +7 -0
  77. package/templates/functions-supabase/supabase/functions/remove-custom-claims/index.ts.example +7 -0
  78. package/templates/functions-supabase/supabase/functions/set-custom-claims/index.ts.example +7 -0
  79. package/templates/functions-supabase/supabase/migrations/20250101000000_idempotency.sql +24 -0
  80. package/templates/functions-supabase/supabase/migrations/20250101000001_rate_limits.sql +22 -0
  81. package/templates/functions-supabase/supabase/migrations/20250101000002_cleanup_jobs.sql +28 -0
  82. package/templates/functions-supabase/supabase/migrations/20250101000003_operation_metrics.sql +28 -0
  83. package/templates/functions-vercel/functions-vercel/tsconfig.json.example +1 -1
  84. package/templates/functions-vercel/functions-vercel/vercel.json.example +1 -1
  85. package/templates/functions-vercel/vercel.json.example +1 -1
  86. package/templates/github/github/workflows/firebase-deploy.yml.example +1 -1
  87. package/templates/github/workflows/firebase-deploy.yml.example +1 -1
  88. package/templates/overlay-firebase/env.fragment.example +34 -0
  89. package/templates/overlay-firebase/env.fragment.expo.example +34 -0
  90. package/templates/overlay-firebase/env.fragment.nextjs.example +34 -0
  91. package/templates/overlay-firebase/src/config/providers.expo.ts.example +49 -0
  92. package/templates/overlay-firebase/src/config/providers.ts.example +23 -0
  93. package/templates/overlay-supabase/env.fragment.example +7 -0
  94. package/templates/overlay-supabase/env.fragment.expo.example +7 -0
  95. package/templates/overlay-supabase/env.fragment.nextjs.example +7 -0
  96. package/templates/overlay-supabase/src/config/providers.expo.ts.example +35 -0
  97. package/templates/overlay-supabase/src/config/providers.ts.example +33 -0
  98. package/templates/overlay-supabase/vercel.headers.example +23 -0
  99. package/templates/overlay-supabase/vercel.json.example +22 -0
  100. package/templates/overlay-vercel/env.fragment.example +34 -0
  101. package/templates/overlay-vercel/env.fragment.nextjs.example +34 -0
  102. package/templates/overlay-vercel/src/config/providers.ts.example +24 -0
  103. package/templates/root-consumer/.claude/agents/architect.md.example +2 -310
  104. package/templates/root-consumer/.claude/agents/builder.md.example +2 -326
  105. package/templates/root-consumer/.claude/agents/coder.md.example +2 -83
  106. package/templates/root-consumer/.claude/agents/extractor.md.example +2 -231
  107. package/templates/root-consumer/.claude/agents/polisher.md.example +2 -132
  108. package/templates/root-consumer/.claude/agents/prompt-engineer.md.example +2 -81
  109. package/templates/root-consumer/.claude/commands/grill.md.example +30 -0
  110. package/templates/root-consumer/.claude/commands/techdebt.md.example +28 -0
  111. package/templates/root-consumer/.clinerules.example +1 -0
  112. package/templates/root-consumer/.cursor/rules/no-docs.mdc.example +15 -0
  113. package/templates/root-consumer/.cursorrules.example +1 -0
  114. package/templates/root-consumer/.github/copilot-instructions.md.example +1 -0
  115. package/templates/root-consumer/.windsurfrules.example +1 -0
  116. package/templates/root-consumer/AI.md.example +29 -123
  117. package/templates/root-consumer/CLAUDE.md.example +1 -134
  118. package/templates/root-consumer/CONVENTIONS.md.example +1 -0
  119. package/templates/root-consumer/GEMINI.md.example +1 -0
  120. package/templates/root-consumer/firebase.json.example +1 -1
  121. package/templates/root-consumer/guides/dndev/AGENT_START_HERE.md.example +20 -0
  122. package/templates/root-consumer/guides/dndev/COMPONENTS_ADV.md.example +0 -18
  123. package/templates/root-consumer/guides/dndev/COMPONENTS_UI.md.example +1 -1
  124. package/templates/root-consumer/guides/dndev/ENV_SETUP.md.example +99 -30
  125. package/templates/root-consumer/guides/dndev/INDEX.md.example +3 -1
  126. package/templates/root-consumer/guides/dndev/SETUP_CRUD.md.example +143 -12
  127. package/templates/root-consumer/guides/dndev/SETUP_FIREBASE.md.example +9 -3
  128. package/templates/root-consumer/guides/dndev/SETUP_SOC2.md.example +234 -0
  129. package/templates/root-consumer/guides/dndev/SETUP_SUPABASE.md.example +124 -0
  130. package/templates/root-consumer/guides/dndev/SETUP_THEMES.md.example +6 -2
  131. package/templates/root-consumer/guides/dndev/SETUP_VERCEL.md.example +176 -0
  132. package/templates/root-consumer/guides/dndev/USE_ROUTING.md.example +5 -9
  133. package/templates/root-consumer/guides/dndev/essences_reference.css.example +174 -0
  134. package/templates/root-consumer/guides/wai-way/agents/builder.md.example +10 -0
  135. package/templates/root-consumer/guides/wai-way/agents/extractor.md.example +25 -5
  136. package/templates/root-consumer/guides/wai-way/agents/polisher.md.example +13 -2
  137. package/templates/root-consumer/guides/wai-way/blueprints/0_brainstorm.md.example +2 -2
  138. package/templates/root-consumer/guides/wai-way/blueprints/1_scaffold.md.example +47 -11
  139. package/templates/root-consumer/guides/wai-way/blueprints/3_compose.md.example +15 -4
  140. package/templates/root-consumer/guides/wai-way/spec_template.md.example +7 -6
  141. package/templates/app-payload/.env.example +0 -28
  142. package/templates/app-payload/README.md.example +0 -233
  143. package/templates/app-payload/collections/Company.ts.example +0 -125
  144. package/templates/app-payload/collections/Hero.ts.example +0 -62
  145. package/templates/app-payload/collections/Media.ts.example +0 -41
  146. package/templates/app-payload/collections/Products.ts.example +0 -115
  147. package/templates/app-payload/collections/Services.ts.example +0 -104
  148. package/templates/app-payload/collections/Testimonials.ts.example +0 -92
  149. package/templates/app-payload/collections/Users.ts.example +0 -35
  150. package/templates/app-payload/src/server.ts.example +0 -79
  151. 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
  }
@@ -8601,6 +8788,10 @@ var APP_QUESTIONNAIRE = [
8601
8788
  {
8602
8789
  title: "Next.js \u2014 Static content/SEO (SSR, SSG) \u2014 BETA",
8603
8790
  value: "nextjs"
8791
+ },
8792
+ {
8793
+ title: "Expo \u2014 Mobile app (iOS + Android)",
8794
+ value: "expo"
8604
8795
  }
8605
8796
  ]
8606
8797
  },
@@ -8627,6 +8818,10 @@ var APP_QUESTIONNAIRE = [
8627
8818
  {
8628
8819
  title: "Vercel \u2014 Serverless functions (edge + node.js)",
8629
8820
  value: "vercel"
8821
+ },
8822
+ {
8823
+ title: "Supabase \u2014 Postgres + Auth + Storage",
8824
+ value: "supabase"
8630
8825
  }
8631
8826
  ]
8632
8827
  }
@@ -8693,6 +8888,7 @@ async function runQuestionnaire(questions, initialAnswers, showWIP, askFor) {
8693
8888
 
8694
8889
  // packages/tooling/src/scaffolding/create-app.ts
8695
8890
  init_utils();
8891
+ init_cli_input();
8696
8892
  init_cli_output();
8697
8893
 
8698
8894
  // packages/tooling/src/utils/dependency-resolver.ts
@@ -8726,6 +8922,15 @@ function generateScripts(templateName, options) {
8726
8922
  scripts.preview = "next start";
8727
8923
  scripts.lint = "eslint src/";
8728
8924
  scripts["type-check"] = "tsc --noEmit";
8925
+ } else if (templateName.includes("expo")) {
8926
+ scripts.dev = "expo start";
8927
+ scripts.start = "expo start";
8928
+ scripts.android = "expo start --android";
8929
+ scripts.ios = "expo start --ios";
8930
+ scripts.web = "expo start --web";
8931
+ scripts.build = "expo export";
8932
+ scripts.lint = "eslint .";
8933
+ scripts["type-check"] = "tsc --noEmit";
8729
8934
  } else if (templateName === "consumer-root") {
8730
8935
  scripts.dev = "turbo run dev";
8731
8936
  scripts.build = "turbo run build";
@@ -8814,7 +9019,7 @@ function generatePackageJson(templateName, mode, options = {}) {
8814
9019
  result.main = "./index.ts";
8815
9020
  result.types = "./index.ts";
8816
9021
  }
8817
- if (templateName.includes("vite") || templateName.includes("nextjs") || templateName.includes("functions")) {
9022
+ if (templateName.includes("vite") || templateName.includes("nextjs") || templateName.includes("expo") || templateName.includes("functions")) {
8818
9023
  if (!dependencies.entities) {
8819
9024
  dependencies.entities = "workspace:*";
8820
9025
  }
@@ -8862,6 +9067,40 @@ function generatePackageJson(templateName, mode, options = {}) {
8862
9067
  // packages/tooling/src/scaffolding/create-app.ts
8863
9068
  init_pathResolver();
8864
9069
  init_pathResolver();
9070
+
9071
+ // packages/tooling/src/scaffolding/scaffold-matrix.ts
9072
+ init_utils();
9073
+ var MATRIX = [
9074
+ { builder: "vite", backend: "none", baseTemplate: "app-vite", overlay: null, deployConfig: null, functionsTemplate: null },
9075
+ { builder: "vite", backend: "firebase", baseTemplate: "app-vite", overlay: "overlay-firebase", deployConfig: "firebase", functionsTemplate: "functions-firebase" },
9076
+ { builder: "vite", backend: "supabase", baseTemplate: "app-vite", overlay: "overlay-supabase", deployConfig: "vercel-supabase", functionsTemplate: null },
9077
+ { builder: "vite", backend: "vercel", baseTemplate: "app-vite", overlay: "overlay-vercel", deployConfig: "vercel-vercel", functionsTemplate: "functions-vercel" },
9078
+ { builder: "nextjs", backend: "none", baseTemplate: "app-next", overlay: null, deployConfig: null, functionsTemplate: null },
9079
+ { builder: "nextjs", backend: "firebase", baseTemplate: "app-next", overlay: "overlay-firebase", deployConfig: "firebase", functionsTemplate: "functions-firebase" },
9080
+ { builder: "nextjs", backend: "supabase", baseTemplate: "app-next", overlay: "overlay-supabase", deployConfig: "vercel-supabase", functionsTemplate: null },
9081
+ { builder: "nextjs", backend: "vercel", baseTemplate: "app-next", overlay: "overlay-vercel", deployConfig: "vercel-vercel", functionsTemplate: "functions-vercel" },
9082
+ { builder: "expo", backend: "none", baseTemplate: "app-expo", overlay: null, deployConfig: null, functionsTemplate: null },
9083
+ { builder: "expo", backend: "firebase", baseTemplate: "app-expo", overlay: "overlay-firebase", deployConfig: null, functionsTemplate: "functions-firebase" },
9084
+ { builder: "expo", backend: "supabase", baseTemplate: "app-expo", overlay: "overlay-supabase", deployConfig: null, functionsTemplate: null },
9085
+ { builder: "demo", backend: "none", baseTemplate: "app-demo", overlay: null, deployConfig: null, functionsTemplate: null }
9086
+ ];
9087
+ function comboKey(builder, backend) {
9088
+ return `${builder}-${backend}`;
9089
+ }
9090
+ var ROWS_BY_KEY = /* @__PURE__ */ new Map();
9091
+ for (const row of MATRIX) {
9092
+ ROWS_BY_KEY.set(comboKey(row.builder, row.backend), row);
9093
+ }
9094
+ function getScaffoldRow(builder, backend) {
9095
+ const key = comboKey(builder, backend);
9096
+ const row = ROWS_BY_KEY.get(key);
9097
+ if (!row) {
9098
+ throw new Error(`Unsupported scaffold combo: ${key}. Supported: ${[...ROWS_BY_KEY.keys()].join(", ")}`);
9099
+ }
9100
+ return row;
9101
+ }
9102
+
9103
+ // packages/tooling/src/scaffolding/create-app.ts
8865
9104
  var SHOW_WIP = process.env.SHOW_WIP === "true" || process.argv.includes("--wip");
8866
9105
  async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
8867
9106
  const isInteractive = !appName || !appConfig;
@@ -8947,14 +9186,19 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
8947
9186
  const s = Y2();
8948
9187
  s.start(`Creating app: ${appName}`);
8949
9188
  await ensureDir(appDir);
8950
- const templateDir = appTemplate === "demo" ? "app-demo" : appTemplate === "nextjs" ? "app-next" : "app-vite";
9189
+ const builder = appTemplate;
9190
+ const backend = appConfig.needsBackend && appConfig.backendPlatform ? appConfig.backendPlatform : "none";
9191
+ const row = getScaffoldRow(builder, backend);
9192
+ const templateDir = row.baseTemplate;
8951
9193
  const firebaseProjectId = (appConfig?.firebaseProjectId ?? "").trim() || appName.toLowerCase().replace(/\s+/g, "-");
8952
9194
  const firebaseRegion = appConfig?.firebaseRegion ?? "europe-west1";
9195
+ const backendPlatform = appConfig.backendPlatform ?? "firebase";
9196
+ const deployConfig = row.deployConfig;
8953
9197
  const replacements = {
8954
9198
  projectName: appName,
8955
9199
  appName,
8956
9200
  appShortName: appName.toUpperCase().replace(/-/g, " "),
8957
- includeFunctions: Boolean(appConfig.needsBackend),
9201
+ includeFunctions: Boolean(row.functionsTemplate),
8958
9202
  needsCRUD: Boolean(appConfig.needsCRUD),
8959
9203
  setupGithubActions: false,
8960
9204
  appNames: [appName],
@@ -8964,8 +9208,13 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
8964
9208
  monorepoRelativePath: "../../packages/tooling",
8965
9209
  appTemplate,
8966
9210
  isNextjs: appTemplate === "nextjs",
9211
+ isExpo: appTemplate === "expo",
8967
9212
  YOUR_FIREBASE_PROJECT_ID: firebaseProjectId,
8968
- YOUR_REGION: firebaseRegion
9213
+ YOUR_REGION: firebaseRegion,
9214
+ backendPlatform,
9215
+ isFirebase: backendPlatform === "firebase",
9216
+ isSupabase: backendPlatform === "supabase",
9217
+ isVercel: backendPlatform === "vercel"
8969
9218
  };
8970
9219
  const templateSourceDir = joinPath(templatesRoot, templateDir);
8971
9220
  const templateFiles = await glob("**/*", {
@@ -8986,118 +9235,155 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
8986
9235
  await replacePlaceholders(destPath, replacements);
8987
9236
  }
8988
9237
  }
9238
+ if (row.overlay) {
9239
+ const overlayDir = joinPath(templatesRoot, row.overlay);
9240
+ if (pathExists(overlayDir)) {
9241
+ const overlayFiles = await glob("**/*", {
9242
+ cwd: overlayDir,
9243
+ dot: true,
9244
+ onlyFiles: true
9245
+ });
9246
+ for (const file of overlayFiles) {
9247
+ if (file.startsWith("env.fragment")) continue;
9248
+ if (file === "vercel.headers.example") continue;
9249
+ if (file === "vercel.json.example") continue;
9250
+ if (file === "src/config/providers.ts.example") {
9251
+ const variantFile = `src/config/providers.${appTemplate}.ts.example`;
9252
+ if (overlayFiles.includes(variantFile)) continue;
9253
+ }
9254
+ const providersVariant = file.match(/^src\/config\/providers\.(\w+)\.ts\.example$/);
9255
+ if (providersVariant) {
9256
+ if (providersVariant[1] !== appTemplate) continue;
9257
+ const destPath2 = joinPath(appDir, "src/config/providers.ts");
9258
+ await ensureDir(getDirname(destPath2));
9259
+ await copy(joinPath(overlayDir, file), destPath2);
9260
+ if (await isTextFile(destPath2)) await replacePlaceholders(destPath2, replacements);
9261
+ continue;
9262
+ }
9263
+ const sourcePath = joinPath(overlayDir, file);
9264
+ let destFileName = file;
9265
+ if (destFileName.endsWith(".example")) {
9266
+ destFileName = destFileName.slice(0, -8);
9267
+ }
9268
+ const destPath = joinPath(appDir, destFileName);
9269
+ await ensureDir(getDirname(destPath));
9270
+ await copy(sourcePath, destPath);
9271
+ if (await isTextFile(destPath)) {
9272
+ await replacePlaceholders(destPath, replacements);
9273
+ }
9274
+ }
9275
+ const fragmentName = appTemplate === "nextjs" ? "env.fragment.nextjs.example" : appTemplate === "expo" ? "env.fragment.expo.example" : "env.fragment.example";
9276
+ const fragmentPath = joinPath(overlayDir, fragmentName);
9277
+ const envPath = joinPath(appDir, ".env");
9278
+ if (pathExists(fragmentPath) && pathExists(envPath)) {
9279
+ const baseEnv = readSync(envPath);
9280
+ const fragment = readSync(fragmentPath);
9281
+ await write(envPath, baseEnv + "\n" + fragment, { overwrite: true });
9282
+ }
9283
+ if (deployConfig === "vercel-supabase") {
9284
+ const vercelPath = joinPath(appDir, "vercel.json");
9285
+ const headersFragmentPath = joinPath(overlayDir, "vercel.headers.example");
9286
+ const fullVercelPath = joinPath(overlayDir, "vercel.json.example");
9287
+ if (pathExists(vercelPath) && pathExists(headersFragmentPath)) {
9288
+ const vercelJson = readSync(vercelPath, { format: "json" });
9289
+ const headersFragment = readSync(headersFragmentPath, { format: "json" });
9290
+ vercelJson.headers = [...vercelJson.headers ?? [], ...headersFragment];
9291
+ await write(vercelPath, vercelJson, { format: "json", overwrite: true });
9292
+ } else if (pathExists(fullVercelPath)) {
9293
+ await copy(fullVercelPath, vercelPath);
9294
+ if (await isTextFile(vercelPath)) await replacePlaceholders(vercelPath, replacements);
9295
+ }
9296
+ }
9297
+ }
9298
+ }
8989
9299
  const executionMode = detectExecutionMode();
8990
9300
  const templateName = appTemplate === "demo" ? "demo" : executionMode === "development" ? `dev-${appTemplate}` : `consumer-${appTemplate}`;
8991
9301
  const packageJson = generatePackageJson(templateName, executionMode, {
8992
9302
  appName,
8993
9303
  template: appTemplate === "demo" ? "vite" : appTemplate,
8994
- includeFunctions: Boolean(appConfig.needsBackend)
9304
+ includeFunctions: Boolean(row.functionsTemplate),
9305
+ platform: appTemplate === "demo" ? void 0 : backendPlatform
8995
9306
  });
8996
9307
  const packageJsonPath = joinPath(appDir, "package.json");
8997
9308
  await write(packageJsonPath, packageJson, {
8998
9309
  format: "json",
8999
9310
  overwrite: true
9000
9311
  });
9001
- if (appConfig.needsBackend && appConfig.backendPlatform) {
9002
- const platform = appConfig.backendPlatform;
9312
+ if (row.functionsTemplate) {
9003
9313
  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
9045
- });
9314
+ const functionsTemplateName = row.functionsTemplate;
9315
+ const functionsTemplateExists = pathExists(joinPath(templatesRoot, functionsTemplateName));
9316
+ if (!functionsTemplateExists) {
9317
+ log.warn(`Functions template "${functionsTemplateName}" not found \u2014 skipping functions scaffolding.`);
9318
+ } else {
9319
+ const functionsPackageJson = generatePackageJson(
9320
+ functionsTemplateName,
9321
+ executionMode,
9322
+ { appName, platform: row.functionsTemplate.replace("functions-", "") }
9323
+ );
9324
+ const packageJsonPath2 = joinPath(functionsRootDir, "package.json");
9325
+ await ensureDir(functionsRootDir);
9326
+ await write(packageJsonPath2, functionsPackageJson, {
9327
+ format: "json",
9328
+ overwrite: true
9329
+ });
9330
+ const templateDir2 = joinPath(templatesRoot, functionsTemplateName);
9331
+ const templateFiles2 = await glob("**/*", {
9332
+ cwd: templateDir2,
9333
+ dot: true,
9334
+ onlyFiles: true
9335
+ });
9336
+ for (const file of templateFiles2) {
9337
+ if (file === "package.json.example") continue;
9338
+ const sourcePath = joinPath(templateDir2, file);
9339
+ let destFileName = file;
9340
+ if (destFileName.endsWith(".example")) {
9341
+ destFileName = destFileName.slice(0, -8);
9342
+ }
9343
+ const destPath = joinPath(functionsRootDir, destFileName);
9344
+ await ensureDir(getDirname(destPath));
9345
+ await copy(sourcePath, destPath);
9346
+ if (await isTextFile(destPath)) {
9347
+ await replacePlaceholders(destPath, {
9348
+ ...replacements,
9349
+ APP_NAME: appName
9350
+ });
9351
+ }
9046
9352
  }
9047
9353
  }
9048
9354
  }
9049
9355
  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);
9356
+ if (deployConfig === "firebase") {
9357
+ const firebaseJsonSource = joinPath(deploymentTemplateDir, "firebase.json.example");
9358
+ if (pathExists(firebaseJsonSource)) {
9359
+ await copy(firebaseJsonSource, joinPath(appDir, "firebase.json"));
9360
+ const firebaseJsonDest = joinPath(appDir, "firebase.json");
9361
+ if (await isTextFile(firebaseJsonDest)) await replacePlaceholders(firebaseJsonDest, replacements);
9362
+ }
9363
+ const firebasercSource = joinPath(deploymentTemplateDir, ".firebaserc.example");
9364
+ if (pathExists(firebasercSource)) {
9365
+ await copy(firebasercSource, joinPath(appDir, ".firebaserc"));
9366
+ const firebasercDest = joinPath(appDir, ".firebaserc");
9367
+ if (await isTextFile(firebasercDest)) await replacePlaceholders(firebasercDest, replacements);
9368
+ }
9369
+ if (row.functionsTemplate === "functions-firebase") {
9370
+ for (const example of ["firestore.rules.example", "firestore.indexes.json.example", "storage.rules.example"]) {
9371
+ const src = joinPath(deploymentTemplateDir, example);
9372
+ if (pathExists(src)) {
9373
+ await copy(src, joinPath(appDir, example.replace(".example", "")));
9374
+ }
9375
+ }
9059
9376
  }
9060
9377
  }
9061
- const firebasercSource = joinPath(
9062
- deploymentTemplateDir,
9063
- ".firebaserc.example"
9064
- );
9065
- if (pathExists(firebasercSource)) {
9066
- 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) {
9079
- const src = joinPath(deploymentTemplateDir, example);
9080
- if (pathExists(src)) {
9081
- const destName = example.replace(".example", "");
9082
- const dest = joinPath(appDir, destName);
9083
- await copy(src, dest);
9084
- }
9085
- }
9086
- }
9087
- if (appTemplate === "nextjs" || appConfig.needsBackend && appConfig.backendPlatform === "vercel") {
9088
- const vercelJsonSource = joinPath(
9089
- deploymentTemplateDir,
9090
- "vercel.json.example"
9091
- );
9378
+ if (deployConfig === "vercel-vercel") {
9379
+ const vercelJsonSource = joinPath(deploymentTemplateDir, "vercel.json.example");
9092
9380
  if (pathExists(vercelJsonSource)) {
9381
+ await copy(vercelJsonSource, joinPath(appDir, "vercel.json"));
9093
9382
  const vercelJsonDest = joinPath(appDir, "vercel.json");
9094
- await copy(vercelJsonSource, vercelJsonDest);
9095
- if (await isTextFile(vercelJsonDest)) {
9096
- await replacePlaceholders(vercelJsonDest, replacements);
9097
- }
9383
+ if (await isTextFile(vercelJsonDest)) await replacePlaceholders(vercelJsonDest, replacements);
9098
9384
  }
9099
9385
  }
9100
- const backendInfo = appConfig.needsBackend && appConfig.backendPlatform ? ` with ${appConfig.backendPlatform} functions` : "";
9386
+ const backendInfo = row.functionsTemplate ? ` with ${row.functionsTemplate.replace("functions-", "")} functions` : "";
9101
9387
  s.stop(`Created ${appName}${backendInfo}`);
9102
9388
  const rootPackageJsonPath = joinPath(workspaceRoot, "package.json");
9103
9389
  if (pathExists(rootPackageJsonPath)) {
@@ -9109,7 +9395,7 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
9109
9395
  }
9110
9396
  if (isInteractive) {
9111
9397
  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)
9398
+ const firebaseStep = row.functionsTemplate === "functions-firebase" ? `2. Set Firebase project: cd apps/${appName} && firebase use --add (or edit .firebaserc)
9113
9399
  3. bun install
9114
9400
  4. bun run dev` : `2. bun install
9115
9401
  3. bun run dev`;
@@ -9127,9 +9413,28 @@ Happy coding!`,
9127
9413
 
9128
9414
  // packages/tooling/src/scaffolding/create-project.ts
9129
9415
  init_utils();
9416
+ init_cli_input();
9130
9417
  init_cli_output();
9131
9418
  init_pathResolver();
9132
9419
  var SHOW_WIP2 = process.env.SHOW_WIP === "true" || process.argv.includes("--wip");
9420
+ var BACKEND_CHOICES = [
9421
+ { title: "None", value: "none" },
9422
+ {
9423
+ title: "Firebase \u2014 Full platform (Auth, Firestore, Storage, Functions, Hosting)",
9424
+ value: "firebase"
9425
+ },
9426
+ {
9427
+ title: "Supabase \u2014 Auth, Postgres, Storage, Functions",
9428
+ value: "supabase"
9429
+ },
9430
+ {
9431
+ title: "Vercel \u2014 Serverless functions + Hosting (e.g. Next.js API routes)",
9432
+ value: "vercel"
9433
+ }
9434
+ ];
9435
+ function getDefaultBackendIndex(framework) {
9436
+ return framework === "nextjs" ? 3 : 1;
9437
+ }
9133
9438
  function calculateRelativePath(from, to) {
9134
9439
  try {
9135
9440
  return getRelativePathBetween(from, to).replace(/\\/g, "/");
@@ -9238,10 +9543,6 @@ async function main(options) {
9238
9543
  Me("Merge mode: Merging root files, creating new apps", "\u{1F527} Mode");
9239
9544
  }
9240
9545
  }
9241
- let installDemoApp = await askForConfirmation(
9242
- "Would you like to install the demo app? (component showcase)",
9243
- false
9244
- );
9245
9546
  let appNames = [];
9246
9547
  const appConfigs = {};
9247
9548
  let anyAppNeedsBackend = false;
@@ -9269,21 +9570,25 @@ async function main(options) {
9269
9570
  {
9270
9571
  title: "Next.js \u2014 Static content/SEO (SSR, SSG) \u2014 DoNotDev support: BETA",
9271
9572
  value: "nextjs"
9573
+ },
9574
+ {
9575
+ title: "Expo \u2014 Mobile app (iOS + Android, single codebase)",
9576
+ value: "expo"
9272
9577
  }
9273
9578
  ],
9274
9579
  0
9275
9580
  );
9276
- const needsBackend = await askForConfirmation(
9277
- `Do you need a backend for "${trimmedName}"?`,
9278
- false
9581
+ const backendValue = await askForSelection(
9582
+ `Backend for "${trimmedName}"?`,
9583
+ [...BACKEND_CHOICES],
9584
+ getDefaultBackendIndex(framework)
9279
9585
  );
9280
- if (needsBackend) {
9281
- anyAppNeedsBackend = true;
9282
- }
9586
+ const needsBackend = backendValue !== "none";
9587
+ if (needsBackend) anyAppNeedsBackend = true;
9283
9588
  appConfigs[trimmedName] = {
9284
9589
  template: framework,
9285
9590
  needsBackend,
9286
- backendPlatform: needsBackend ? framework === "nextjs" ? "vercel" : "firebase" : void 0,
9591
+ backendPlatform: needsBackend ? backendValue : void 0,
9287
9592
  needsCRUD: false,
9288
9593
  selectedEntities: [],
9289
9594
  userAuth: "none",
@@ -9329,17 +9634,17 @@ async function main(options) {
9329
9634
  ],
9330
9635
  0
9331
9636
  );
9332
- const needsBackend = await askForConfirmation(
9333
- `Do you need a backend for "${trimmedName}"?`,
9334
- false
9637
+ const backendValue = await askForSelection(
9638
+ `Backend for "${trimmedName}"?`,
9639
+ [...BACKEND_CHOICES],
9640
+ getDefaultBackendIndex(framework)
9335
9641
  );
9336
- if (needsBackend) {
9337
- anyAppNeedsBackend = true;
9338
- }
9642
+ const needsBackend = backendValue !== "none";
9643
+ if (needsBackend) anyAppNeedsBackend = true;
9339
9644
  appConfigs[trimmedName] = {
9340
9645
  template: framework,
9341
9646
  needsBackend,
9342
- backendPlatform: needsBackend ? framework === "nextjs" ? "vercel" : "firebase" : void 0,
9647
+ backendPlatform: needsBackend ? backendValue : void 0,
9343
9648
  needsCRUD: false,
9344
9649
  selectedEntities: [],
9345
9650
  userAuth: "none",
@@ -9347,6 +9652,10 @@ async function main(options) {
9347
9652
  features: []
9348
9653
  };
9349
9654
  }
9655
+ let installDemoApp = await askForConfirmation(
9656
+ "Would you like to install the demo app? (component showcase)",
9657
+ false
9658
+ );
9350
9659
  let allAppNames = [...appNames];
9351
9660
  let appsToCreate = [];
9352
9661
  let appsToSkip = [];
@@ -9466,12 +9775,14 @@ async function main(options) {
9466
9775
  overwrite: true
9467
9776
  });
9468
9777
  const relativeMonorepoPath = executionMode === "development" ? calculateRelativePath(projectDirNormalized, monorepoRoot) : "";
9778
+ const primaryPlatform = Object.values(appConfigs).find((c) => c.backendPlatform)?.backendPlatform ?? "firebase";
9469
9779
  const rootPackageJson = generatePackageJson(
9470
9780
  "consumer-root",
9471
9781
  executionMode,
9472
9782
  {
9473
9783
  projectName,
9474
- includeFunctions: anyAppNeedsBackend
9784
+ includeFunctions: anyAppNeedsBackend,
9785
+ platform: primaryPlatform
9475
9786
  }
9476
9787
  );
9477
9788
  const packageJsonPath = joinPath(projectDirNormalized, "package.json");
@@ -9530,8 +9841,12 @@ async function main(options) {
9530
9841
  }
9531
9842
  }
9532
9843
  if (setupGithubActions) {
9533
- const ciTemplateDir = joinPath(templatesRoot, "github-consumer");
9534
- await copyTemplateFiles(ciTemplateDir, projectDirNormalized, rootReplacements);
9844
+ const ciTemplateDir = joinPath(templatesRoot, "github");
9845
+ await copyTemplateFiles(
9846
+ ciTemplateDir,
9847
+ projectDirNormalized,
9848
+ rootReplacements
9849
+ );
9535
9850
  }
9536
9851
  s.stop("Project structure created");
9537
9852
  } else {