@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.
- package/dependencies-matrix.json +372 -88
- package/dist/bin/commands/agent-setup.js +7 -1
- package/dist/bin/commands/build.js +141 -44
- package/dist/bin/commands/bump.js +81 -41
- package/dist/bin/commands/cacheout.js +37 -9
- package/dist/bin/commands/create-app.js +276 -121
- package/dist/bin/commands/create-project.js +506 -217
- package/dist/bin/commands/deploy.js +1785 -694
- package/dist/bin/commands/dev.js +177 -43
- package/dist/bin/commands/doctor.d.ts +6 -0
- package/dist/bin/commands/doctor.d.ts.map +1 -0
- package/dist/bin/commands/{lint.js → doctor.js} +1215 -156
- package/dist/bin/commands/doctor.js.map +1 -0
- package/dist/bin/commands/emu.js +451 -104
- package/dist/bin/commands/format.js +37 -9
- package/dist/bin/commands/make-admin.js +77499 -11
- package/dist/bin/commands/preview.js +181 -43
- package/dist/bin/commands/setup.d.ts +6 -0
- package/dist/bin/commands/setup.d.ts.map +1 -0
- package/dist/bin/commands/setup.js +11733 -0
- package/dist/bin/commands/setup.js.map +1 -0
- package/dist/bin/commands/supabase-setup.d.ts +6 -0
- package/dist/bin/commands/supabase-setup.d.ts.map +1 -0
- package/dist/bin/commands/supabase-setup.js +7 -0
- package/dist/bin/commands/supabase-setup.js.map +1 -0
- package/dist/bin/commands/sync-secrets.js +211 -34
- package/dist/bin/commands/type-check.d.ts +14 -0
- package/dist/bin/commands/type-check.d.ts.map +1 -0
- package/dist/bin/commands/type-check.js +2049 -0
- package/dist/bin/commands/type-check.js.map +1 -0
- package/dist/bin/commands/wai.js +3 -1
- package/dist/bin/dndev.js +73 -52
- package/dist/bin/donotdev.js +54 -45
- package/dist/index.js +4212 -3050
- package/package.json +3 -3
- package/templates/app-demo/src/App.tsx.example +1 -0
- package/templates/app-demo/src/pages/FullPage.tsx.example +2 -2
- package/templates/app-demo/src/pages/components/DemoLayout.tsx.example +2 -2
- package/templates/app-demo/src/themes.css.example +5 -12
- package/templates/app-expo/.env.example +44 -0
- package/templates/app-expo/.expo/README.md.example +5 -0
- package/templates/app-expo/.gitignore.example +36 -0
- package/templates/app-expo/README.md.example +58 -0
- package/templates/app-expo/app/.gitkeep +2 -0
- package/templates/app-expo/app/_layout.tsx.example +41 -0
- package/templates/app-expo/app/form.tsx.example +52 -0
- package/templates/app-expo/app/index.tsx.example +89 -0
- package/templates/app-expo/app/list.tsx.example +32 -0
- package/templates/app-expo/app/profile.tsx.example +76 -0
- package/templates/app-expo/app/signin.tsx.example +53 -0
- package/templates/app-expo/app.json.example +39 -0
- package/templates/app-expo/assets/adaptive-icon.png +0 -0
- package/templates/app-expo/assets/favicon.png +0 -0
- package/templates/app-expo/assets/icon.png +0 -0
- package/templates/app-expo/assets/splash.png +0 -0
- package/templates/app-expo/babel.config.js.example +10 -0
- package/templates/app-expo/eas.json.example +20 -0
- package/templates/app-expo/expo-env.d.ts.example +4 -0
- package/templates/app-expo/metro.config.js.example +20 -0
- package/templates/app-expo/service-account-key.json.example +12 -0
- package/templates/app-expo/src/config/app.ts.example +46 -0
- package/templates/app-expo/src/config/providers.ts.example +7 -0
- package/templates/app-expo/tsconfig.json.example +19 -0
- package/templates/app-next/.env.example +4 -33
- package/templates/app-next/src/app/ClientLayout.tsx.example +2 -0
- package/templates/app-next/src/app/layout.tsx.example +7 -6
- package/templates/app-next/src/config/providers.ts.example +7 -0
- package/templates/app-next/src/globals.css.example +2 -11
- package/templates/app-next/src/pages/HomePage.tsx.example +1 -1
- package/templates/app-next/src/themes.css.example +10 -13
- package/templates/app-vite/.env.example +3 -32
- package/templates/app-vite/index.html.example +2 -24
- package/templates/app-vite/src/App.tsx.example +2 -0
- package/templates/app-vite/src/config/providers.ts.example +7 -0
- package/templates/app-vite/src/globals.css.example +2 -12
- package/templates/app-vite/src/pages/FormPageExample.tsx.example +1 -2
- package/templates/app-vite/src/pages/HomePage.tsx.example +2 -2
- package/templates/app-vite/src/themes.css.example +109 -79
- package/templates/app-vite/vercel.json.example +11 -0
- package/templates/functions-firebase/README.md.example +1 -1
- package/templates/functions-firebase/build.mjs.example +2 -72
- package/templates/functions-firebase/functions-firebase/.env.example.example +24 -26
- package/templates/functions-firebase/functions-firebase/README.md.example +1 -1
- package/templates/functions-firebase/functions-firebase/build.mjs.example +2 -72
- package/templates/functions-firebase/functions-firebase/tsconfig.json.example +1 -1
- package/templates/functions-firebase/functions.config.js.example +1 -1
- package/templates/functions-supabase/supabase/config.toml.example +59 -0
- package/templates/functions-supabase/supabase/functions/.env.example +13 -0
- package/templates/functions-supabase/supabase/functions/cancel-subscription/index.ts.example +7 -0
- package/templates/functions-supabase/supabase/functions/change-plan/index.ts.example +11 -0
- package/templates/functions-supabase/supabase/functions/create-checkout-session/index.ts.example +11 -0
- package/templates/functions-supabase/supabase/functions/create-customer-portal/index.ts.example +7 -0
- package/templates/functions-supabase/supabase/functions/crud/index.ts.example +16 -0
- package/templates/functions-supabase/supabase/functions/delete-account/index.ts.example +7 -0
- package/templates/functions-supabase/supabase/functions/deno.json.example +8 -0
- package/templates/functions-supabase/supabase/functions/get-custom-claims/index.ts.example +7 -0
- package/templates/functions-supabase/supabase/functions/get-user-auth-status/index.ts.example +7 -0
- package/templates/functions-supabase/supabase/functions/refresh-subscription-status/index.ts.example +7 -0
- package/templates/functions-supabase/supabase/functions/remove-custom-claims/index.ts.example +7 -0
- package/templates/functions-supabase/supabase/functions/set-custom-claims/index.ts.example +7 -0
- package/templates/functions-supabase/supabase/migrations/20250101000000_idempotency.sql +24 -0
- package/templates/functions-supabase/supabase/migrations/20250101000001_rate_limits.sql +22 -0
- package/templates/functions-supabase/supabase/migrations/20250101000002_cleanup_jobs.sql +28 -0
- package/templates/functions-supabase/supabase/migrations/20250101000003_operation_metrics.sql +28 -0
- package/templates/functions-vercel/functions-vercel/tsconfig.json.example +1 -1
- package/templates/functions-vercel/functions-vercel/vercel.json.example +1 -1
- package/templates/functions-vercel/vercel.json.example +1 -1
- package/templates/github/github/workflows/firebase-deploy.yml.example +1 -1
- package/templates/github/workflows/firebase-deploy.yml.example +1 -1
- package/templates/overlay-firebase/env.fragment.example +34 -0
- package/templates/overlay-firebase/env.fragment.expo.example +34 -0
- package/templates/overlay-firebase/env.fragment.nextjs.example +34 -0
- package/templates/overlay-firebase/src/config/providers.expo.ts.example +49 -0
- package/templates/overlay-firebase/src/config/providers.ts.example +23 -0
- package/templates/overlay-supabase/env.fragment.example +12 -0
- package/templates/overlay-supabase/env.fragment.expo.example +12 -0
- package/templates/overlay-supabase/env.fragment.nextjs.example +12 -0
- package/templates/overlay-supabase/src/config/providers.expo.ts.example +35 -0
- package/templates/overlay-supabase/src/config/providers.ts.example +33 -0
- package/templates/overlay-supabase/vercel.headers.example +23 -0
- package/templates/overlay-supabase/vercel.json.example +22 -0
- package/templates/overlay-vercel/env.fragment.example +34 -0
- package/templates/overlay-vercel/env.fragment.nextjs.example +34 -0
- package/templates/overlay-vercel/src/config/providers.ts.example +24 -0
- package/templates/root-consumer/.claude/agents/architect.md.example +2 -310
- package/templates/root-consumer/.claude/agents/builder.md.example +2 -326
- package/templates/root-consumer/.claude/agents/coder.md.example +2 -83
- package/templates/root-consumer/.claude/agents/extractor.md.example +2 -231
- package/templates/root-consumer/.claude/agents/polisher.md.example +2 -132
- package/templates/root-consumer/.claude/agents/prompt-engineer.md.example +2 -81
- package/templates/root-consumer/.claude/commands/grill.md.example +30 -0
- package/templates/root-consumer/.claude/commands/techdebt.md.example +28 -0
- package/templates/root-consumer/.clinerules.example +1 -0
- package/templates/root-consumer/.cursor/rules/no-docs.mdc.example +15 -0
- package/templates/root-consumer/.cursorrules.example +1 -0
- package/templates/root-consumer/.github/copilot-instructions.md.example +1 -0
- package/templates/root-consumer/.windsurfrules.example +1 -0
- package/templates/root-consumer/AI.md.example +44 -123
- package/templates/root-consumer/CLAUDE.md.example +1 -134
- package/templates/root-consumer/CONVENTIONS.md.example +1 -0
- package/templates/root-consumer/GEMINI.md.example +1 -0
- package/templates/root-consumer/firebase.json.example +1 -1
- package/templates/root-consumer/guides/dndev/AGENT_START_HERE.md.example +22 -2
- package/templates/root-consumer/guides/dndev/COMPONENTS_ADV.md.example +0 -18
- package/templates/root-consumer/guides/dndev/COMPONENTS_UI.md.example +1 -1
- package/templates/root-consumer/guides/dndev/ENV_SETUP.md.example +101 -32
- package/templates/root-consumer/guides/dndev/INDEX.md.example +4 -2
- package/templates/root-consumer/guides/dndev/SETUP_APP_CONFIG.md.example +3 -3
- package/templates/root-consumer/guides/dndev/SETUP_CRUD.md.example +241 -12
- package/templates/root-consumer/guides/dndev/SETUP_FIREBASE.md.example +13 -7
- package/templates/root-consumer/guides/dndev/SETUP_OAUTH_PROVIDERS.md.example +60 -0
- package/templates/root-consumer/guides/dndev/SETUP_SOC2.md.example +234 -0
- package/templates/root-consumer/guides/dndev/SETUP_STRIPE.md.example +62 -0
- package/templates/root-consumer/guides/dndev/SETUP_SUPABASE.md.example +124 -0
- package/templates/root-consumer/guides/dndev/SETUP_THEMES.md.example +6 -2
- package/templates/root-consumer/guides/dndev/SETUP_VERCEL.md.example +176 -0
- package/templates/root-consumer/guides/dndev/USE_ROUTING.md.example +5 -9
- package/templates/root-consumer/guides/dndev/essences_reference.css.example +174 -0
- package/templates/root-consumer/guides/wai-way/WAI_WAY_CLI.md.example +7 -8
- package/templates/root-consumer/guides/wai-way/agents/builder.md.example +10 -0
- package/templates/root-consumer/guides/wai-way/agents/extractor.md.example +25 -5
- package/templates/root-consumer/guides/wai-way/agents/polisher.md.example +13 -2
- package/templates/root-consumer/guides/wai-way/blueprints/0_brainstorm.md.example +2 -2
- package/templates/root-consumer/guides/wai-way/blueprints/1_scaffold.md.example +55 -15
- package/templates/root-consumer/guides/wai-way/blueprints/3_compose.md.example +15 -4
- package/templates/root-consumer/guides/wai-way/spec_template.md.example +7 -6
- package/dist/bin/commands/lint.d.ts +0 -11
- package/dist/bin/commands/lint.d.ts.map +0 -1
- package/dist/bin/commands/lint.js.map +0 -1
- package/dist/bin/commands/staging.d.ts +0 -11
- package/dist/bin/commands/staging.d.ts.map +0 -1
- package/dist/bin/commands/staging.js +0 -12
- package/dist/bin/commands/staging.js.map +0 -1
- package/templates/app-payload/.env.example +0 -28
- package/templates/app-payload/README.md.example +0 -233
- package/templates/app-payload/collections/Company.ts.example +0 -125
- package/templates/app-payload/collections/Hero.ts.example +0 -62
- package/templates/app-payload/collections/Media.ts.example +0 -41
- package/templates/app-payload/collections/Products.ts.example +0 -115
- package/templates/app-payload/collections/Services.ts.example +0 -104
- package/templates/app-payload/collections/Testimonials.ts.example +0 -92
- package/templates/app-payload/collections/Users.ts.example +0 -35
- package/templates/app-payload/src/server.ts.example +0 -79
- 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 =
|
|
7888
|
-
if (
|
|
7889
|
-
return "
|
|
8028
|
+
const fileUrlPath = fileURLToPath2(import.meta.url);
|
|
8029
|
+
if (fileUrlPath.includes("node_modules")) {
|
|
8030
|
+
return "published";
|
|
7890
8031
|
}
|
|
7891
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
8530
|
-
|
|
8531
|
-
|
|
8532
|
-
|
|
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
|
|
8553
|
-
const cliPackageJson =
|
|
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: "
|
|
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
|
|
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(
|
|
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(
|
|
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 (
|
|
9002
|
-
const
|
|
9003
|
-
const
|
|
9004
|
-
|
|
9005
|
-
|
|
9006
|
-
|
|
9007
|
-
|
|
9008
|
-
|
|
9009
|
-
|
|
9010
|
-
|
|
9011
|
-
|
|
9012
|
-
|
|
9013
|
-
|
|
9014
|
-
|
|
9015
|
-
|
|
9016
|
-
|
|
9017
|
-
|
|
9018
|
-
|
|
9019
|
-
|
|
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
|
-
|
|
9051
|
-
deploymentTemplateDir,
|
|
9052
|
-
|
|
9053
|
-
|
|
9054
|
-
|
|
9055
|
-
|
|
9056
|
-
|
|
9057
|
-
|
|
9058
|
-
|
|
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
|
-
|
|
9062
|
-
|
|
9063
|
-
|
|
9064
|
-
|
|
9065
|
-
|
|
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
|
-
|
|
9068
|
-
|
|
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
|
-
|
|
9081
|
-
|
|
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 (
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
9249
|
-
|
|
9250
|
-
""
|
|
9251
|
-
|
|
9252
|
-
|
|
9253
|
-
|
|
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 =
|
|
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
|
-
|
|
9319
|
-
|
|
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
|
|
9534
|
-
await copyTemplateFiles(
|
|
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 {
|