@donotdev/cli 0.0.16 → 0.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dependencies-matrix.json +38 -26
- package/dist/bin/commands/bump.js +9 -2
- package/dist/bin/commands/create-app.js +185 -81
- package/dist/bin/commands/create-project.js +186 -85
- package/dist/bin/commands/deploy.js +51 -20
- package/dist/bin/commands/doctor.js +249 -56
- package/dist/bin/commands/emu.js +18 -20
- package/dist/bin/commands/make-admin.js +30 -10
- package/dist/bin/commands/setup.js +512 -122
- package/dist/bin/commands/type-check.d.ts.map +1 -1
- package/dist/bin/commands/type-check.js +7 -3
- package/dist/bin/commands/type-check.js.map +1 -1
- package/dist/bin/dndev.js +9 -6
- package/dist/bin/donotdev.js +35 -20
- package/dist/index.js +262 -129
- package/package.json +1 -1
- package/templates/root-consumer/.claude/commands/brainstorm.md.example +15 -1
- package/templates/root-consumer/.claude/commands/build.md.example +24 -2
- package/templates/root-consumer/.claude/commands/design.md.example +17 -0
- package/templates/root-consumer/.claude/commands/polish.md.example +17 -0
- package/templates/root-consumer/AI.md.example +50 -18
- package/templates/root-consumer/guides/dndev/ENV_SETUP.md.example +6 -6
- package/templates/root-consumer/guides/dndev/INDEX.md.example +2 -2
- package/templates/root-consumer/guides/dndev/SETUP_AUTH.md.example +13 -6
- package/templates/root-consumer/guides/dndev/SETUP_CRUD.md.example +149 -1086
- package/templates/root-consumer/guides/dndev/SETUP_FIREBASE.md.example +68 -16
- package/templates/root-consumer/guides/dndev/SETUP_FUNCTIONS.md.example +6 -111
- package/templates/root-consumer/guides/dndev/SETUP_PAGES.md.example +64 -0
- package/templates/root-consumer/guides/dndev/SETUP_SUPABASE.md.example +123 -32
- package/templates/root-consumer/guides/dndev/SETUP_VERCEL.md.example +108 -91
- package/templates/root-consumer/guides/dndev/advanced/EMULATORS.md.example +2 -2
- package/templates/root-consumer/guides/wai-way/blueprints/0_brainstorm.md.example +1 -1
- package/dist/bin/commands/firebase-setup.d.ts +0 -6
- package/dist/bin/commands/firebase-setup.d.ts.map +0 -1
- package/dist/bin/commands/firebase-setup.js +0 -7
- package/dist/bin/commands/firebase-setup.js.map +0 -1
- package/dist/bin/commands/supabase-setup.d.ts +0 -6
- package/dist/bin/commands/supabase-setup.d.ts.map +0 -1
- package/dist/bin/commands/supabase-setup.js +0 -7
- package/dist/bin/commands/supabase-setup.js.map +0 -1
- package/templates/functions-firebase/functions-firebase/README.md.example +0 -123
- package/templates/functions-firebase/functions-firebase/build.mjs.example +0 -5
- package/templates/functions-firebase/functions-firebase/src/auth/getCustomClaims.ts.example +0 -19
- package/templates/functions-firebase/functions-firebase/src/auth/getUserAuthStatus.ts.example +0 -21
- package/templates/functions-firebase/functions-firebase/src/auth/index.ts.example +0 -11
- package/templates/functions-firebase/functions-firebase/src/auth/removeCustomClaims.ts.example +0 -21
- package/templates/functions-firebase/functions-firebase/src/auth/setCustomClaims.ts.example +0 -21
- package/templates/functions-firebase/functions-firebase/src/billing/handleStripeWebhook.ts.example +0 -24
- package/templates/functions-firebase/functions-firebase/src/billing/index.ts.example +0 -10
- package/templates/functions-firebase/functions-firebase/src/billing/processPaymentSuccess.ts.example +0 -14
- package/templates/functions-firebase/functions-firebase/src/billing/refreshSubscriptionStatus.ts.example +0 -14
- package/templates/functions-firebase/functions-firebase/src/index.ts.example +0 -39
- package/templates/functions-firebase/functions-firebase/src/oauth/checkGitHubAccess.ts.example +0 -14
- package/templates/functions-firebase/functions-firebase/src/oauth/disconnect.ts.example +0 -14
- package/templates/functions-firebase/functions-firebase/src/oauth/exchangeToken.ts.example +0 -14
- package/templates/functions-firebase/functions-firebase/src/oauth/getConnections.ts.example +0 -14
- package/templates/functions-firebase/functions-firebase/src/oauth/grantGitHubAccess.ts.example +0 -14
- package/templates/functions-firebase/functions-firebase/src/oauth/index.ts.example +0 -17
- package/templates/functions-firebase/functions-firebase/src/oauth/refreshToken.ts.example +0 -14
- package/templates/functions-firebase/functions-firebase/src/oauth/revokeGitHubAccess.ts.example +0 -14
- package/templates/functions-firebase/functions-firebase/tsconfig.json.example +0 -21
- package/templates/functions-vercel/functions-vercel/README.md.example +0 -116
- package/templates/functions-vercel/functions-vercel/build.mjs.example +0 -52
- package/templates/functions-vercel/functions-vercel/src/api/auth/getCustomClaims.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/auth/getUserAuthStatus.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/auth/removeCustomClaims.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/auth/setCustomClaims.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/billing/handleStripeWebhook.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/billing/processPaymentSuccess.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/billing/refreshSubscriptionStatus.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/crud/createEntity.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/crud/deleteEntity.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/crud/getEntity.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/crud/listEntities.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/crud/updateEntity.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/oauth/checkGitHubAccess.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/oauth/disconnect.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/oauth/exchangeToken.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/oauth/getConnections.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/oauth/grantGitHubAccess.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/oauth/refreshToken.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/src/api/oauth/revokeGitHubAccess.ts.example +0 -20
- package/templates/functions-vercel/functions-vercel/tsconfig.json.example +0 -21
- package/templates/functions-vercel/functions-vercel/vercel.json.example +0 -14
- package/templates/github/github/workflows/firebase-deploy.yml.example +0 -79
- /package/templates/functions-firebase/{functions-firebase/.env.example.example → .env.example} +0 -0
- /package/templates/functions-vercel/{functions-vercel/.env.example.example → .env.example} +0 -0
|
@@ -8714,7 +8714,11 @@ function getMatrixPath(mode) {
|
|
|
8714
8714
|
const executionMode = mode || detectExecutionMode();
|
|
8715
8715
|
if (executionMode === "development") {
|
|
8716
8716
|
const templatesRoot = getTemplatesRoot();
|
|
8717
|
-
const devPath = normalizePath(
|
|
8717
|
+
const devPath = normalizePath(
|
|
8718
|
+
templatesRoot,
|
|
8719
|
+
"..",
|
|
8720
|
+
"dependencies-matrix.json"
|
|
8721
|
+
);
|
|
8718
8722
|
if (pathExists(devPath)) {
|
|
8719
8723
|
return devPath;
|
|
8720
8724
|
}
|
|
@@ -8791,34 +8795,67 @@ var APP_QUESTIONNAIRE = [
|
|
|
8791
8795
|
]
|
|
8792
8796
|
},
|
|
8793
8797
|
{
|
|
8794
|
-
id: "
|
|
8795
|
-
question: "
|
|
8796
|
-
type: "
|
|
8797
|
-
|
|
8798
|
+
id: "host",
|
|
8799
|
+
question: "Where will you host?",
|
|
8800
|
+
type: "select",
|
|
8801
|
+
condition: (answers) => answers.framework !== "expo",
|
|
8802
|
+
defaultValue: (answers) => {
|
|
8803
|
+
return answers.framework === "nextjs" ? "vercel" : "none";
|
|
8804
|
+
},
|
|
8805
|
+
choices: [
|
|
8806
|
+
{ title: "None \u2014 no hosting config", value: "none" },
|
|
8807
|
+
{ title: "Vercel \u2014 edge CDN + serverless", value: "vercel" },
|
|
8808
|
+
{ title: "Firebase Hosting \u2014 Google CDN", value: "firebase" }
|
|
8809
|
+
]
|
|
8810
|
+
},
|
|
8811
|
+
{
|
|
8812
|
+
id: "functions",
|
|
8813
|
+
question: "Serverless functions?",
|
|
8814
|
+
type: "select",
|
|
8815
|
+
defaultValue: "none",
|
|
8816
|
+
// Choices filtered dynamically based on host
|
|
8817
|
+
choices: [
|
|
8818
|
+
{ title: "None", value: "none" },
|
|
8819
|
+
{ title: "Vercel Functions \u2014 edge + node.js", value: "vercel" },
|
|
8820
|
+
{ title: "Firebase Functions \u2014 Google Cloud", value: "firebase" },
|
|
8821
|
+
{ title: "Supabase Edge Functions \u2014 Deno", value: "supabase" }
|
|
8822
|
+
],
|
|
8823
|
+
choicesFilter: (answers) => {
|
|
8824
|
+
const host = answers.host ?? "none";
|
|
8825
|
+
const framework = answers.framework;
|
|
8826
|
+
if (framework === "expo") return ["none", "firebase", "supabase"];
|
|
8827
|
+
if (host === "firebase") return ["none", "firebase"];
|
|
8828
|
+
if (host === "vercel") return ["none", "vercel", "supabase"];
|
|
8829
|
+
return ["none", "vercel", "firebase", "supabase"];
|
|
8830
|
+
}
|
|
8798
8831
|
},
|
|
8799
8832
|
{
|
|
8800
|
-
id: "
|
|
8801
|
-
question: "
|
|
8833
|
+
id: "backend",
|
|
8834
|
+
question: "Backend / BaaS provider?",
|
|
8802
8835
|
type: "select",
|
|
8803
|
-
dependsOn: "needsBackend",
|
|
8804
|
-
condition: (answers) => answers.needsBackend === true,
|
|
8805
8836
|
defaultValue: (answers) => {
|
|
8806
|
-
|
|
8837
|
+
if (answers.functions === "firebase") return "firebase";
|
|
8838
|
+
if (answers.functions === "supabase") return "supabase";
|
|
8839
|
+
return "none";
|
|
8807
8840
|
},
|
|
8808
8841
|
choices: [
|
|
8842
|
+
{ title: "None \u2014 frontend only", value: "none" },
|
|
8809
8843
|
{
|
|
8810
|
-
title: "Firebase \u2014
|
|
8844
|
+
title: "Firebase \u2014 Auth + Firestore + Storage",
|
|
8811
8845
|
value: "firebase"
|
|
8812
8846
|
},
|
|
8813
|
-
{
|
|
8814
|
-
title: "Vercel \u2014 Serverless functions (edge + node.js)",
|
|
8815
|
-
value: "vercel"
|
|
8816
|
-
},
|
|
8817
8847
|
{
|
|
8818
8848
|
title: "Supabase \u2014 Postgres + Auth + Storage",
|
|
8819
8849
|
value: "supabase"
|
|
8820
8850
|
}
|
|
8821
|
-
]
|
|
8851
|
+
],
|
|
8852
|
+
choicesFilter: (answers) => {
|
|
8853
|
+
const functions = answers.functions ?? "none";
|
|
8854
|
+
if (functions === "firebase") return ["none", "firebase"];
|
|
8855
|
+
if (functions === "supabase") return ["none", "supabase"];
|
|
8856
|
+
if (functions === "vercel") return ["none"];
|
|
8857
|
+
return ["none", "firebase", "supabase"];
|
|
8858
|
+
}
|
|
8822
8859
|
}
|
|
8823
8860
|
];
|
|
8824
8861
|
var PROJECT_QUESTIONNAIRE = [
|
|
@@ -8836,12 +8873,13 @@ var PROJECT_QUESTIONNAIRE = [
|
|
|
8836
8873
|
}
|
|
8837
8874
|
];
|
|
8838
8875
|
function aggregateRequirements(answers) {
|
|
8876
|
+
const hasBackend = answers.backend && answers.backend !== "none" || answers.functions && answers.functions !== "none";
|
|
8839
8877
|
const requirements = {
|
|
8840
8878
|
entities: [],
|
|
8841
8879
|
// Entities created in apps/<app>/entities/ (v1), root entities/ for v2
|
|
8842
8880
|
billing: true,
|
|
8843
8881
|
// Always on (useStripeBillingSafe handles missing package)
|
|
8844
|
-
backend:
|
|
8882
|
+
backend: hasBackend,
|
|
8845
8883
|
features: [],
|
|
8846
8884
|
template: "vite",
|
|
8847
8885
|
// Only Vite for now
|
|
@@ -8865,6 +8903,10 @@ async function runQuestionnaire(questions, initialAnswers, showWIP, askFor) {
|
|
|
8865
8903
|
if (!showWIP) {
|
|
8866
8904
|
choices = choices.filter((choice) => !choice.wip);
|
|
8867
8905
|
}
|
|
8906
|
+
if (question.choicesFilter) {
|
|
8907
|
+
const allowed = question.choicesFilter(answers);
|
|
8908
|
+
choices = choices.filter((c) => allowed.includes(c.value));
|
|
8909
|
+
}
|
|
8868
8910
|
if (choices.length === 0) continue;
|
|
8869
8911
|
answer = await askFor.selection(question.question, choices);
|
|
8870
8912
|
break;
|
|
@@ -9065,34 +9107,22 @@ init_pathResolver();
|
|
|
9065
9107
|
|
|
9066
9108
|
// packages/tooling/src/scaffolding/scaffold-matrix.ts
|
|
9067
9109
|
init_utils();
|
|
9068
|
-
|
|
9069
|
-
|
|
9070
|
-
|
|
9071
|
-
|
|
9072
|
-
|
|
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);
|
|
9110
|
+
function resolveDeployConfig(host, backend) {
|
|
9111
|
+
if (host === "none") return null;
|
|
9112
|
+
if (host === "firebase") return "firebase";
|
|
9113
|
+
if (backend === "supabase") return "vercel-supabase";
|
|
9114
|
+
return "vercel-vercel";
|
|
9088
9115
|
}
|
|
9089
|
-
function
|
|
9090
|
-
const
|
|
9091
|
-
|
|
9092
|
-
|
|
9093
|
-
|
|
9094
|
-
|
|
9095
|
-
|
|
9116
|
+
function getScaffoldParts(builder, host, functions, backend) {
|
|
9117
|
+
const baseTemplate = builder === "nextjs" ? "app-next" : builder === "demo" ? "app-demo" : `app-${builder}`;
|
|
9118
|
+
return {
|
|
9119
|
+
builder,
|
|
9120
|
+
backend,
|
|
9121
|
+
baseTemplate,
|
|
9122
|
+
overlay: backend !== "none" ? `overlay-${backend}` : null,
|
|
9123
|
+
deployConfig: resolveDeployConfig(host, backend),
|
|
9124
|
+
functionsTemplate: functions !== "none" ? `functions-${functions}` : null
|
|
9125
|
+
};
|
|
9096
9126
|
}
|
|
9097
9127
|
|
|
9098
9128
|
// packages/tooling/src/scaffolding/create-app.ts
|
|
@@ -9132,11 +9162,15 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
|
|
|
9132
9162
|
input: askForInput
|
|
9133
9163
|
});
|
|
9134
9164
|
const requirements = aggregateRequirements(answers);
|
|
9135
|
-
const
|
|
9165
|
+
const framework = answers.framework || "vite";
|
|
9166
|
+
const host2 = answers.host || "none";
|
|
9167
|
+
const functions2 = answers.functions || "none";
|
|
9168
|
+
const backend2 = answers.backend || "none";
|
|
9136
9169
|
appConfig = {
|
|
9137
|
-
template:
|
|
9138
|
-
|
|
9139
|
-
|
|
9170
|
+
template: framework,
|
|
9171
|
+
host: host2,
|
|
9172
|
+
functions: functions2,
|
|
9173
|
+
backend: backend2,
|
|
9140
9174
|
needsCRUD: true,
|
|
9141
9175
|
// Always on
|
|
9142
9176
|
selectedEntities: [],
|
|
@@ -9182,12 +9216,14 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
|
|
|
9182
9216
|
s.start(`Creating app: ${appName}`);
|
|
9183
9217
|
await ensureDir(appDir);
|
|
9184
9218
|
const builder = appTemplate;
|
|
9185
|
-
const
|
|
9186
|
-
const
|
|
9219
|
+
const host = appConfig.host ?? "none";
|
|
9220
|
+
const functions = appConfig.functions ?? "none";
|
|
9221
|
+
const backend = appConfig.backend ?? "none";
|
|
9222
|
+
const row = getScaffoldParts(builder, host, functions, backend);
|
|
9187
9223
|
const templateDir = row.baseTemplate;
|
|
9188
9224
|
const firebaseProjectId = (appConfig?.firebaseProjectId ?? "").trim() || appName.toLowerCase().replace(/\s+/g, "-");
|
|
9189
9225
|
const firebaseRegion = appConfig?.firebaseRegion ?? "europe-west1";
|
|
9190
|
-
const backendPlatform =
|
|
9226
|
+
const backendPlatform = backend !== "none" ? backend : "firebase";
|
|
9191
9227
|
const deployConfig = row.deployConfig;
|
|
9192
9228
|
const replacements = {
|
|
9193
9229
|
projectName: appName,
|
|
@@ -9246,13 +9282,16 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
|
|
|
9246
9282
|
const variantFile = `src/config/providers.${appTemplate}.ts.example`;
|
|
9247
9283
|
if (overlayFiles.includes(variantFile)) continue;
|
|
9248
9284
|
}
|
|
9249
|
-
const providersVariant = file.match(
|
|
9285
|
+
const providersVariant = file.match(
|
|
9286
|
+
/^src\/config\/providers\.(\w+)\.ts\.example$/
|
|
9287
|
+
);
|
|
9250
9288
|
if (providersVariant) {
|
|
9251
9289
|
if (providersVariant[1] !== appTemplate) continue;
|
|
9252
9290
|
const destPath2 = joinPath(appDir, "src/config/providers.ts");
|
|
9253
9291
|
await ensureDir(getDirname(destPath2));
|
|
9254
9292
|
await copy(joinPath(overlayDir, file), destPath2, { overwrite: true });
|
|
9255
|
-
if (await isTextFile(destPath2))
|
|
9293
|
+
if (await isTextFile(destPath2))
|
|
9294
|
+
await replacePlaceholders(destPath2, replacements);
|
|
9256
9295
|
continue;
|
|
9257
9296
|
}
|
|
9258
9297
|
const sourcePath = joinPath(overlayDir, file);
|
|
@@ -9277,16 +9316,28 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
|
|
|
9277
9316
|
}
|
|
9278
9317
|
if (deployConfig === "vercel-supabase") {
|
|
9279
9318
|
const vercelPath = joinPath(appDir, "vercel.json");
|
|
9280
|
-
const headersFragmentPath = joinPath(
|
|
9319
|
+
const headersFragmentPath = joinPath(
|
|
9320
|
+
overlayDir,
|
|
9321
|
+
"vercel.headers.example"
|
|
9322
|
+
);
|
|
9281
9323
|
const fullVercelPath = joinPath(overlayDir, "vercel.json.example");
|
|
9282
9324
|
if (pathExists(vercelPath) && pathExists(headersFragmentPath)) {
|
|
9283
9325
|
const vercelJson = readSync(vercelPath, { format: "json" });
|
|
9284
|
-
const headersFragment = readSync(headersFragmentPath, {
|
|
9285
|
-
|
|
9286
|
-
|
|
9326
|
+
const headersFragment = readSync(headersFragmentPath, {
|
|
9327
|
+
format: "json"
|
|
9328
|
+
});
|
|
9329
|
+
vercelJson.headers = [
|
|
9330
|
+
...vercelJson.headers ?? [],
|
|
9331
|
+
...headersFragment
|
|
9332
|
+
];
|
|
9333
|
+
await write(vercelPath, vercelJson, {
|
|
9334
|
+
format: "json",
|
|
9335
|
+
overwrite: true
|
|
9336
|
+
});
|
|
9287
9337
|
} else if (pathExists(fullVercelPath)) {
|
|
9288
9338
|
await copy(fullVercelPath, vercelPath);
|
|
9289
|
-
if (await isTextFile(vercelPath))
|
|
9339
|
+
if (await isTextFile(vercelPath))
|
|
9340
|
+
await replacePlaceholders(vercelPath, replacements);
|
|
9290
9341
|
}
|
|
9291
9342
|
}
|
|
9292
9343
|
}
|
|
@@ -9306,9 +9357,13 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
|
|
|
9306
9357
|
});
|
|
9307
9358
|
if (row.functionsTemplate) {
|
|
9308
9359
|
const functionsTemplateName = row.functionsTemplate;
|
|
9309
|
-
const functionsTemplateExists = pathExists(
|
|
9360
|
+
const functionsTemplateExists = pathExists(
|
|
9361
|
+
joinPath(templatesRoot, functionsTemplateName)
|
|
9362
|
+
);
|
|
9310
9363
|
if (!functionsTemplateExists) {
|
|
9311
|
-
log.warn(
|
|
9364
|
+
log.warn(
|
|
9365
|
+
`Functions template "${functionsTemplateName}" not found \u2014 skipping functions scaffolding.`
|
|
9366
|
+
);
|
|
9312
9367
|
} else {
|
|
9313
9368
|
const isSupabaseFunctions = functionsTemplateName === "functions-supabase";
|
|
9314
9369
|
const functionsRootDir = isSupabaseFunctions ? appDir : joinPath(appDir, "functions");
|
|
@@ -9316,7 +9371,10 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
|
|
|
9316
9371
|
const functionsPackageJson = generatePackageJson(
|
|
9317
9372
|
functionsTemplateName,
|
|
9318
9373
|
executionMode,
|
|
9319
|
-
{
|
|
9374
|
+
{
|
|
9375
|
+
appName,
|
|
9376
|
+
platform: row.functionsTemplate.replace("functions-", "")
|
|
9377
|
+
}
|
|
9320
9378
|
);
|
|
9321
9379
|
const packageJsonPath2 = joinPath(functionsRootDir, "package.json");
|
|
9322
9380
|
await ensureDir(functionsRootDir);
|
|
@@ -9352,13 +9410,19 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
|
|
|
9352
9410
|
}
|
|
9353
9411
|
const deploymentTemplateDir = joinPath(templatesRoot, "root-consumer");
|
|
9354
9412
|
if (deployConfig === "firebase") {
|
|
9355
|
-
const firebaseJsonSource = joinPath(
|
|
9413
|
+
const firebaseJsonSource = joinPath(
|
|
9414
|
+
deploymentTemplateDir,
|
|
9415
|
+
"firebase.json.example"
|
|
9416
|
+
);
|
|
9356
9417
|
if (pathExists(firebaseJsonSource)) {
|
|
9357
9418
|
await copy(firebaseJsonSource, joinPath(appDir, "firebase.json"));
|
|
9358
9419
|
const firebaseJsonDest = joinPath(appDir, "firebase.json");
|
|
9359
|
-
if (await isTextFile(firebaseJsonDest))
|
|
9420
|
+
if (await isTextFile(firebaseJsonDest))
|
|
9421
|
+
await replacePlaceholders(firebaseJsonDest, replacements);
|
|
9360
9422
|
if (appTemplate === "nextjs") {
|
|
9361
|
-
const firebaseJson = readSync(firebaseJsonDest, {
|
|
9423
|
+
const firebaseJson = readSync(firebaseJsonDest, {
|
|
9424
|
+
format: "json"
|
|
9425
|
+
});
|
|
9362
9426
|
if (firebaseJson.hosting?.rewrites) {
|
|
9363
9427
|
firebaseJson.hosting.rewrites = firebaseJson.hosting.rewrites.filter(
|
|
9364
9428
|
(r2) => r2.destination !== "/index.html"
|
|
@@ -9367,17 +9431,28 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
|
|
|
9367
9431
|
if (firebaseJson.hosting) {
|
|
9368
9432
|
firebaseJson.hosting.public = "out";
|
|
9369
9433
|
}
|
|
9370
|
-
await write(firebaseJsonDest, firebaseJson, {
|
|
9434
|
+
await write(firebaseJsonDest, firebaseJson, {
|
|
9435
|
+
format: "json",
|
|
9436
|
+
overwrite: true
|
|
9437
|
+
});
|
|
9371
9438
|
}
|
|
9372
9439
|
}
|
|
9373
|
-
const firebasercSource = joinPath(
|
|
9440
|
+
const firebasercSource = joinPath(
|
|
9441
|
+
deploymentTemplateDir,
|
|
9442
|
+
".firebaserc.example"
|
|
9443
|
+
);
|
|
9374
9444
|
if (pathExists(firebasercSource)) {
|
|
9375
9445
|
await copy(firebasercSource, joinPath(appDir, ".firebaserc"));
|
|
9376
9446
|
const firebasercDest = joinPath(appDir, ".firebaserc");
|
|
9377
|
-
if (await isTextFile(firebasercDest))
|
|
9447
|
+
if (await isTextFile(firebasercDest))
|
|
9448
|
+
await replacePlaceholders(firebasercDest, replacements);
|
|
9378
9449
|
}
|
|
9379
9450
|
if (row.functionsTemplate === "functions-firebase") {
|
|
9380
|
-
for (const example of [
|
|
9451
|
+
for (const example of [
|
|
9452
|
+
"firestore.rules.example",
|
|
9453
|
+
"firestore.indexes.json.example",
|
|
9454
|
+
"storage.rules.example"
|
|
9455
|
+
]) {
|
|
9381
9456
|
const src = joinPath(deploymentTemplateDir, example);
|
|
9382
9457
|
if (pathExists(src)) {
|
|
9383
9458
|
await copy(src, joinPath(appDir, example.replace(".example", "")));
|
|
@@ -9393,11 +9468,22 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
|
|
|
9393
9468
|
{
|
|
9394
9469
|
source: "functions",
|
|
9395
9470
|
codebase: "default",
|
|
9396
|
-
ignore: [
|
|
9471
|
+
ignore: [
|
|
9472
|
+
"node_modules",
|
|
9473
|
+
".git",
|
|
9474
|
+
"firebase-debug.log",
|
|
9475
|
+
"firebase-debug.*.log",
|
|
9476
|
+
"**/.*",
|
|
9477
|
+
"**/*.test.ts",
|
|
9478
|
+
"**/__tests__/**"
|
|
9479
|
+
],
|
|
9397
9480
|
runtime: "nodejs22"
|
|
9398
9481
|
}
|
|
9399
9482
|
],
|
|
9400
|
-
firestore: {
|
|
9483
|
+
firestore: {
|
|
9484
|
+
rules: "firestore.rules",
|
|
9485
|
+
indexes: "firestore.indexes.json"
|
|
9486
|
+
},
|
|
9401
9487
|
storage: { rules: "storage.rules" },
|
|
9402
9488
|
emulators: {
|
|
9403
9489
|
auth: { port: 9099 },
|
|
@@ -9407,15 +9493,26 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
|
|
|
9407
9493
|
ui: { enabled: true, port: 4e3 }
|
|
9408
9494
|
}
|
|
9409
9495
|
};
|
|
9410
|
-
await write(firebaseJsonPath, expoFirebaseJson, {
|
|
9496
|
+
await write(firebaseJsonPath, expoFirebaseJson, {
|
|
9497
|
+
format: "json",
|
|
9498
|
+
overwrite: true
|
|
9499
|
+
});
|
|
9411
9500
|
}
|
|
9412
|
-
const firebasercSource = joinPath(
|
|
9501
|
+
const firebasercSource = joinPath(
|
|
9502
|
+
deploymentTemplateDir,
|
|
9503
|
+
".firebaserc.example"
|
|
9504
|
+
);
|
|
9413
9505
|
const firebasercDest = joinPath(appDir, ".firebaserc");
|
|
9414
9506
|
if (pathExists(firebasercSource) && !pathExists(firebasercDest)) {
|
|
9415
9507
|
await copy(firebasercSource, firebasercDest);
|
|
9416
|
-
if (await isTextFile(firebasercDest))
|
|
9417
|
-
|
|
9418
|
-
|
|
9508
|
+
if (await isTextFile(firebasercDest))
|
|
9509
|
+
await replacePlaceholders(firebasercDest, replacements);
|
|
9510
|
+
}
|
|
9511
|
+
for (const example of [
|
|
9512
|
+
"firestore.rules.example",
|
|
9513
|
+
"firestore.indexes.json.example",
|
|
9514
|
+
"storage.rules.example"
|
|
9515
|
+
]) {
|
|
9419
9516
|
const src = joinPath(deploymentTemplateDir, example);
|
|
9420
9517
|
const dest = joinPath(appDir, example.replace(".example", ""));
|
|
9421
9518
|
if (pathExists(src) && !pathExists(dest)) {
|
|
@@ -9424,11 +9521,15 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
|
|
|
9424
9521
|
}
|
|
9425
9522
|
}
|
|
9426
9523
|
if (deployConfig === "vercel-vercel") {
|
|
9427
|
-
const vercelJsonSource = joinPath(
|
|
9524
|
+
const vercelJsonSource = joinPath(
|
|
9525
|
+
deploymentTemplateDir,
|
|
9526
|
+
"vercel.json.example"
|
|
9527
|
+
);
|
|
9428
9528
|
if (pathExists(vercelJsonSource)) {
|
|
9429
9529
|
await copy(vercelJsonSource, joinPath(appDir, "vercel.json"));
|
|
9430
9530
|
const vercelJsonDest = joinPath(appDir, "vercel.json");
|
|
9431
|
-
if (await isTextFile(vercelJsonDest))
|
|
9531
|
+
if (await isTextFile(vercelJsonDest))
|
|
9532
|
+
await replacePlaceholders(vercelJsonDest, replacements);
|
|
9432
9533
|
}
|
|
9433
9534
|
}
|
|
9434
9535
|
const backendInfo = row.functionsTemplate ? ` with ${row.functionsTemplate.replace("functions-", "")} functions` : "";
|
|
@@ -9472,12 +9573,11 @@ async function collectAppConfig(appName) {
|
|
|
9472
9573
|
input: askForInput
|
|
9473
9574
|
});
|
|
9474
9575
|
const framework = answers.framework || "vite";
|
|
9475
|
-
const needsBackend = answers.needsBackend || false;
|
|
9476
|
-
const defaultPlatform = framework === "nextjs" ? "vercel" : "firebase";
|
|
9477
9576
|
return {
|
|
9478
9577
|
template: framework,
|
|
9479
|
-
|
|
9480
|
-
|
|
9578
|
+
host: answers.host || "none",
|
|
9579
|
+
functions: answers.functions || "none",
|
|
9580
|
+
backend: answers.backend || "none",
|
|
9481
9581
|
needsCRUD: false,
|
|
9482
9582
|
selectedEntities: [],
|
|
9483
9583
|
userAuth: "none",
|
|
@@ -9624,7 +9724,7 @@ async function main(options) {
|
|
|
9624
9724
|
Me(`Configuring "${trimmedName}"...`, "\u2699\uFE0F");
|
|
9625
9725
|
const config = await collectAppConfig(trimmedName);
|
|
9626
9726
|
appConfigs[trimmedName] = config;
|
|
9627
|
-
if (config.
|
|
9727
|
+
if (config.backend !== "none" || config.functions !== "none") anyAppNeedsBackend = true;
|
|
9628
9728
|
}
|
|
9629
9729
|
let installDemoApp = await askForConfirmation(
|
|
9630
9730
|
"Would you like to install the demo app? (component showcase)",
|
|
@@ -9700,8 +9800,9 @@ async function main(options) {
|
|
|
9700
9800
|
log.warn(`Missing config for app "${appName}", using defaults`);
|
|
9701
9801
|
appConfigs[appName] = {
|
|
9702
9802
|
template: "vite",
|
|
9703
|
-
|
|
9704
|
-
|
|
9803
|
+
host: "none",
|
|
9804
|
+
functions: "none",
|
|
9805
|
+
backend: "none",
|
|
9705
9806
|
needsCRUD: false,
|
|
9706
9807
|
selectedEntities: [],
|
|
9707
9808
|
userAuth: "none",
|
|
@@ -9749,7 +9850,7 @@ async function main(options) {
|
|
|
9749
9850
|
overwrite: true
|
|
9750
9851
|
});
|
|
9751
9852
|
const relativeMonorepoPath = executionMode === "development" ? calculateRelativePath(projectDirNormalized, monorepoRoot) : "";
|
|
9752
|
-
const primaryPlatform = Object.values(appConfigs).find((c) => c.
|
|
9853
|
+
const primaryPlatform = Object.values(appConfigs).find((c) => c.backend !== "none")?.backend ?? "firebase";
|
|
9753
9854
|
const rootPackageJson = generatePackageJson(
|
|
9754
9855
|
"consumer-root",
|
|
9755
9856
|
executionMode,
|
|
@@ -7951,7 +7951,14 @@ function readServiceAccountKey(filePath) {
|
|
|
7951
7951
|
throw new DoNotDevError(
|
|
7952
7952
|
`Invalid service account key: missing required fields`,
|
|
7953
7953
|
DO_NOT_DEV_ERROR_CODES.CONFIG_INVALID,
|
|
7954
|
-
{
|
|
7954
|
+
{
|
|
7955
|
+
context: {
|
|
7956
|
+
filePath,
|
|
7957
|
+
missingFields: ["project_id", "private_key", "client_email"].filter(
|
|
7958
|
+
(f) => !key[f]
|
|
7959
|
+
)
|
|
7960
|
+
}
|
|
7961
|
+
}
|
|
7955
7962
|
);
|
|
7956
7963
|
}
|
|
7957
7964
|
return content;
|
|
@@ -8198,9 +8205,7 @@ function getCLIInstallInstructions(tool) {
|
|
|
8198
8205
|
"Or: winget install Supabase.CLI",
|
|
8199
8206
|
"Or download from: https://github.com/supabase/cli/releases"
|
|
8200
8207
|
],
|
|
8201
|
-
darwin: [
|
|
8202
|
-
"brew install supabase/tap/supabase"
|
|
8203
|
-
],
|
|
8208
|
+
darwin: ["brew install supabase/tap/supabase"],
|
|
8204
8209
|
linux: [
|
|
8205
8210
|
"brew install supabase/tap/supabase",
|
|
8206
8211
|
"Or see: https://supabase.com/docs/guides/cli"
|
|
@@ -8208,7 +8213,11 @@ function getCLIInstallInstructions(tool) {
|
|
|
8208
8213
|
},
|
|
8209
8214
|
[CLI_TOOLS.VERCEL]: {
|
|
8210
8215
|
win32: ["npm install -g vercel", "Or: npx vercel (no install)"],
|
|
8211
|
-
darwin: [
|
|
8216
|
+
darwin: [
|
|
8217
|
+
"npm install -g vercel",
|
|
8218
|
+
"Or: brew install vercel",
|
|
8219
|
+
"Or: npx vercel"
|
|
8220
|
+
],
|
|
8212
8221
|
linux: ["npm install -g vercel", "Or: npx vercel"]
|
|
8213
8222
|
},
|
|
8214
8223
|
[CLI_TOOLS.SENTRY_CLI]: {
|
|
@@ -16356,7 +16365,9 @@ async function deploySupabaseFunctions(appDir, config) {
|
|
|
16356
16365
|
const supabaseDir = joinPath(appDir, "supabase");
|
|
16357
16366
|
const functionsDir = joinPath(supabaseDir, "functions");
|
|
16358
16367
|
if (!pathExists(functionsDir)) {
|
|
16359
|
-
log.warn(
|
|
16368
|
+
log.warn(
|
|
16369
|
+
"No supabase/functions/ directory found. Skipping Supabase functions deployment."
|
|
16370
|
+
);
|
|
16360
16371
|
return;
|
|
16361
16372
|
}
|
|
16362
16373
|
requireCLI(
|
|
@@ -16372,10 +16383,14 @@ async function deploySupabaseFunctions(appDir, config) {
|
|
|
16372
16383
|
return pathExists(indexPath);
|
|
16373
16384
|
});
|
|
16374
16385
|
if (functionDirs.length === 0) {
|
|
16375
|
-
log.warn(
|
|
16386
|
+
log.warn(
|
|
16387
|
+
"No Edge Functions found in supabase/functions/. Skipping deployment."
|
|
16388
|
+
);
|
|
16376
16389
|
return;
|
|
16377
16390
|
}
|
|
16378
|
-
log.info(
|
|
16391
|
+
log.info(
|
|
16392
|
+
`Found ${functionDirs.length} Edge Function(s): ${functionDirs.join(", ")}`
|
|
16393
|
+
);
|
|
16379
16394
|
const s = Y2();
|
|
16380
16395
|
for (const functionName of functionDirs) {
|
|
16381
16396
|
s.start(`Deploying Edge Function: ${functionName}...`);
|
|
@@ -16394,7 +16409,12 @@ async function deploySupabaseFunctions(appDir, config) {
|
|
|
16394
16409
|
throw new DoNotDevError(
|
|
16395
16410
|
`Failed to deploy Supabase Edge Function: ${functionName}`,
|
|
16396
16411
|
"deployment-failed",
|
|
16397
|
-
{
|
|
16412
|
+
{
|
|
16413
|
+
context: {
|
|
16414
|
+
functionName,
|
|
16415
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
16416
|
+
}
|
|
16417
|
+
}
|
|
16398
16418
|
);
|
|
16399
16419
|
}
|
|
16400
16420
|
}
|
|
@@ -16548,7 +16568,12 @@ init_utils();
|
|
|
16548
16568
|
async function deployFrontend(appDir, serviceAccountPath, projectId, config) {
|
|
16549
16569
|
const s = Y2();
|
|
16550
16570
|
s.start("Deploying frontend to Firebase Hosting...");
|
|
16551
|
-
const args = buildFirebaseDeployArgs(
|
|
16571
|
+
const args = buildFirebaseDeployArgs(
|
|
16572
|
+
"hosting",
|
|
16573
|
+
projectId,
|
|
16574
|
+
config.debug,
|
|
16575
|
+
config.force ?? true
|
|
16576
|
+
);
|
|
16552
16577
|
const result = executeFirebaseCommand(args, {
|
|
16553
16578
|
cwd: appDir,
|
|
16554
16579
|
serviceAccountPath,
|
|
@@ -16595,7 +16620,9 @@ async function deployVercelFrontend(appDir, _config) {
|
|
|
16595
16620
|
if (result.status !== 0) {
|
|
16596
16621
|
s.stop("Vercel deployment failed");
|
|
16597
16622
|
const errOutput = result.stderr?.trim();
|
|
16598
|
-
throw new Error(
|
|
16623
|
+
throw new Error(
|
|
16624
|
+
errOutput || `Vercel deploy exited with code ${result.status}`
|
|
16625
|
+
);
|
|
16599
16626
|
}
|
|
16600
16627
|
s.stop("Frontend deployed to Vercel");
|
|
16601
16628
|
} catch (err) {
|
|
@@ -17052,7 +17079,12 @@ async function deployRules(appDir, serviceAccountPath, projectId, config, option
|
|
|
17052
17079
|
const targetNames = targets.join(", ");
|
|
17053
17080
|
const s = Y2();
|
|
17054
17081
|
s.start(`Deploying ${targetNames}...`);
|
|
17055
|
-
const args = buildFirebaseDeployArgs(
|
|
17082
|
+
const args = buildFirebaseDeployArgs(
|
|
17083
|
+
targets,
|
|
17084
|
+
projectId,
|
|
17085
|
+
config.debug,
|
|
17086
|
+
config.force ?? true
|
|
17087
|
+
);
|
|
17056
17088
|
const result = executeFirebaseCommand(args, {
|
|
17057
17089
|
cwd: appDir,
|
|
17058
17090
|
serviceAccountPath,
|
|
@@ -17426,13 +17458,17 @@ async function main2(options = {}) {
|
|
|
17426
17458
|
const availableApps = detectAvailableApps();
|
|
17427
17459
|
if (availableApps.length === 0) {
|
|
17428
17460
|
if (providerInfo.hasSupabase && !providerInfo.hasFirebase) {
|
|
17429
|
-
log.info(
|
|
17461
|
+
log.info(
|
|
17462
|
+
"Supabase project detected. Deploying Supabase resources..."
|
|
17463
|
+
);
|
|
17430
17464
|
} else {
|
|
17431
17465
|
log.info("No apps with firebase.json or supabase/ directory found.");
|
|
17432
17466
|
log.info(
|
|
17433
17467
|
"To deploy, ensure your app has a firebase.json or supabase/ configuration."
|
|
17434
17468
|
);
|
|
17435
|
-
log.info(
|
|
17469
|
+
log.info(
|
|
17470
|
+
"Run from a DoNotDev project, or create one with: dndev init"
|
|
17471
|
+
);
|
|
17436
17472
|
return;
|
|
17437
17473
|
}
|
|
17438
17474
|
}
|
|
@@ -17712,12 +17748,7 @@ async function main2(options = {}) {
|
|
|
17712
17748
|
}
|
|
17713
17749
|
}
|
|
17714
17750
|
}
|
|
17715
|
-
await deployFunctions(
|
|
17716
|
-
appDir,
|
|
17717
|
-
serviceAccountPath,
|
|
17718
|
-
config.project,
|
|
17719
|
-
config
|
|
17720
|
-
);
|
|
17751
|
+
await deployFunctions(appDir, serviceAccountPath, config.project, config);
|
|
17721
17752
|
}
|
|
17722
17753
|
if (shouldDeployFirebaseRules && serviceAccountPath && config.project && appProviderInfo.firebaseConfig) {
|
|
17723
17754
|
await deployRules(appDir, serviceAccountPath, config.project, config, {
|