@donotdev/cli 0.0.8 → 0.0.11
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 +177 -76
- package/dist/bin/commands/build.js +2 -2
- package/dist/bin/commands/bump.js +578 -94
- package/dist/bin/commands/cacheout.js +2 -2
- package/dist/bin/commands/create-app.js +46 -9
- package/dist/bin/commands/create-project.js +63 -10
- package/dist/bin/commands/deploy.js +114 -25
- package/dist/bin/commands/dev.js +2 -2
- package/dist/bin/commands/emu.js +2 -2
- package/dist/bin/commands/format.js +2 -2
- package/dist/bin/commands/lint.js +2 -2
- package/dist/bin/commands/preview.js +2 -2
- package/dist/bin/commands/sync-secrets.js +2 -2
- package/dist/bin/dndev.js +7 -4
- package/dist/bin/donotdev.js +7 -4
- package/dist/index.js +177 -33
- package/package.json +5 -4
- package/templates/app-next/src/config/app.ts.example +1 -1
- package/templates/app-vite/index.html.example +24 -2
- package/templates/app-vite/src/config/app.ts.example +1 -1
- package/templates/app-vite/src/pages/FormPageExample.tsx.example +8 -5
- package/templates/app-vite/src/pages/ListPageExample.tsx.example +4 -7
- package/templates/root-consumer/.claude/agents/architect.md.example +313 -0
- package/templates/root-consumer/.claude/agents/builder.md.example +329 -0
- package/templates/root-consumer/.claude/agents/coder.md.example +87 -0
- package/templates/root-consumer/.claude/agents/extractor.md.example +235 -0
- package/templates/root-consumer/.claude/agents/polisher.md.example +359 -0
- package/templates/root-consumer/.claude/agents/prompt-engineer.md.example +85 -0
- package/templates/root-consumer/.claude/commands/brainstorm.md.example +133 -0
- package/templates/root-consumer/.claude/commands/build.md.example +109 -0
- package/templates/root-consumer/.claude/commands/design.md.example +136 -0
- package/templates/root-consumer/.claude/commands/polish.md.example +145 -0
- package/templates/root-consumer/.cursor/mcp.json.example +8 -0
- package/templates/root-consumer/.firebaserc.example +5 -0
- package/templates/root-consumer/.mcp.json.example +8 -0
- package/templates/root-consumer/CLAUDE.md.example +146 -0
- package/templates/root-consumer/entities/ExampleEntity.ts.example +2 -1
- package/templates/root-consumer/entities/demo.ts.example +1 -1
- package/templates/root-consumer/firestore.indexes.json.example +4 -0
- package/templates/root-consumer/firestore.rules.example +11 -0
- package/templates/root-consumer/guides/dndev/AGENT_START_HERE.md.example +15 -12
- package/templates/root-consumer/guides/dndev/COMPONENTS_CRUD.md.example +9 -6
- package/templates/root-consumer/guides/dndev/COMPONENT_API.md.example +195 -0
- package/templates/root-consumer/guides/dndev/INDEX.md.example +3 -1
- package/templates/root-consumer/guides/dndev/SETUP_CRUD.md.example +485 -57
- package/templates/root-consumer/guides/wai-way/entity_patterns.md.example +1 -1
- package/templates/root-consumer/storage.rules.example +8 -0
|
@@ -163,7 +163,7 @@ var require_picocolors = __commonJS({
|
|
|
163
163
|
}
|
|
164
164
|
});
|
|
165
165
|
|
|
166
|
-
// node_modules/.bun/@clack+
|
|
166
|
+
// node_modules/.bun/@clack+prompts@0.11.0/node_modules/@clack/prompts/node_modules/@clack/core/dist/index.mjs
|
|
167
167
|
import { stdin as j, stdout as M } from "node:process";
|
|
168
168
|
import O from "node:readline";
|
|
169
169
|
import { Writable as X } from "node:stream";
|
|
@@ -257,7 +257,7 @@ function m(e2, u2) {
|
|
|
257
257
|
}
|
|
258
258
|
var import_sisteransi, uD, W, tD, eD, FD, sD, w, N, I, R, r, iD, CD, ED, d, oD, y, V, nD, G, _, z, K, aD, k, hD, lD, xD, B, AD, S, gD, vD, h, x, A, OD, PD, J, LD;
|
|
259
259
|
var init_dist = __esm({
|
|
260
|
-
"node_modules/.bun/@clack+
|
|
260
|
+
"node_modules/.bun/@clack+prompts@0.11.0/node_modules/@clack/prompts/node_modules/@clack/core/dist/index.mjs"() {
|
|
261
261
|
init_utils();
|
|
262
262
|
import_sisteransi = __toESM(require_src(), 1);
|
|
263
263
|
uD = DD();
|
package/dist/bin/dndev.js
CHANGED
|
@@ -46,7 +46,7 @@ Usage: dndev <command>[:<app>] [options]
|
|
|
46
46
|
|
|
47
47
|
Commands:
|
|
48
48
|
init, create-project Create a new DoNotDev project
|
|
49
|
-
create-app [name] Add app
|
|
49
|
+
create-app [name] Add app (--builder vite|next, --functions, --project <id>)
|
|
50
50
|
dev [app] Start development server
|
|
51
51
|
build [app] Build for production
|
|
52
52
|
preview [app] Preview production build
|
|
@@ -66,7 +66,8 @@ Examples:
|
|
|
66
66
|
dndev init my-project Create a new project
|
|
67
67
|
dndev create-app Interactive app creation
|
|
68
68
|
dndev create-app my-app Create 'my-app' with defaults (vite, no functions)
|
|
69
|
-
dndev create-app my-app --builder next --functions
|
|
69
|
+
dndev create-app my-app --builder next --functions Next.js + functions
|
|
70
|
+
dndev create-app my-app --functions --project my-fb-id Set Firebase project (2-min setup)
|
|
70
71
|
dndev dev Start dev server
|
|
71
72
|
dndev dev:web Start dev server for 'web' app
|
|
72
73
|
dndev wai Output WAI-WAY activation prompt
|
|
@@ -90,14 +91,16 @@ program.command("init [name]").alias("create-project").description("Create a new
|
|
|
90
91
|
const { main } = await import("./commands/create-project.js");
|
|
91
92
|
await main({ projectName: name });
|
|
92
93
|
});
|
|
93
|
-
program.command("create-app [name]").description("Add a new app to existing project").option("--name <name>", "App name (non-interactive)").option("--builder <builder>", "Framework: vite or next (default: vite)").option("--functions", "Include Firebase functions").action(async (name, options) => {
|
|
94
|
+
program.command("create-app [name]").description("Add a new app to existing project").option("--name <name>", "App name (non-interactive)").option("--builder <builder>", "Framework: vite or next (default: vite)").option("--functions", "Include Firebase functions").option("--project <id>", "Firebase project ID (default: app name)").option("--region <region>", "Firebase region (default: europe-west1)").action(async (name, options) => {
|
|
94
95
|
const { main } = await import("./commands/create-app.js");
|
|
95
96
|
const appName = name || options.name;
|
|
96
97
|
if (appName) {
|
|
97
98
|
await main({
|
|
98
99
|
name: appName,
|
|
99
100
|
builder: options.builder,
|
|
100
|
-
functions: options.functions
|
|
101
|
+
functions: options.functions,
|
|
102
|
+
firebaseProjectId: options.project,
|
|
103
|
+
firebaseRegion: options.region
|
|
101
104
|
});
|
|
102
105
|
} else {
|
|
103
106
|
await main();
|
package/dist/bin/donotdev.js
CHANGED
|
@@ -46,7 +46,7 @@ Usage: dndev <command>[:<app>] [options]
|
|
|
46
46
|
|
|
47
47
|
Commands:
|
|
48
48
|
init, create-project Create a new DoNotDev project
|
|
49
|
-
create-app [name] Add app
|
|
49
|
+
create-app [name] Add app (--builder vite|next, --functions, --project <id>)
|
|
50
50
|
dev [app] Start development server
|
|
51
51
|
build [app] Build for production
|
|
52
52
|
preview [app] Preview production build
|
|
@@ -66,7 +66,8 @@ Examples:
|
|
|
66
66
|
dndev init my-project Create a new project
|
|
67
67
|
dndev create-app Interactive app creation
|
|
68
68
|
dndev create-app my-app Create 'my-app' with defaults (vite, no functions)
|
|
69
|
-
dndev create-app my-app --builder next --functions
|
|
69
|
+
dndev create-app my-app --builder next --functions Next.js + functions
|
|
70
|
+
dndev create-app my-app --functions --project my-fb-id Set Firebase project (2-min setup)
|
|
70
71
|
dndev dev Start dev server
|
|
71
72
|
dndev dev:web Start dev server for 'web' app
|
|
72
73
|
dndev wai Output WAI-WAY activation prompt
|
|
@@ -90,14 +91,16 @@ program.command("init [name]").alias("create-project").description("Create a new
|
|
|
90
91
|
const { main } = await import("./commands/create-project.js");
|
|
91
92
|
await main({ projectName: name });
|
|
92
93
|
});
|
|
93
|
-
program.command("create-app [name]").description("Add a new app to existing project").option("--name <name>", "App name (non-interactive)").option("--builder <builder>", "Framework: vite or next (default: vite)").option("--functions", "Include Firebase functions").action(async (name, options) => {
|
|
94
|
+
program.command("create-app [name]").description("Add a new app to existing project").option("--name <name>", "App name (non-interactive)").option("--builder <builder>", "Framework: vite or next (default: vite)").option("--functions", "Include Firebase functions").option("--project <id>", "Firebase project ID (default: app name)").option("--region <region>", "Firebase region (default: europe-west1)").action(async (name, options) => {
|
|
94
95
|
const { main } = await import("./commands/create-app.js");
|
|
95
96
|
const appName = name || options.name;
|
|
96
97
|
if (appName) {
|
|
97
98
|
await main({
|
|
98
99
|
name: appName,
|
|
99
100
|
builder: options.builder,
|
|
100
|
-
functions: options.functions
|
|
101
|
+
functions: options.functions,
|
|
102
|
+
firebaseProjectId: options.project,
|
|
103
|
+
firebaseRegion: options.region
|
|
101
104
|
});
|
|
102
105
|
} else {
|
|
103
106
|
await main();
|
package/dist/index.js
CHANGED
|
@@ -167,7 +167,7 @@ var require_picocolors = __commonJS({
|
|
|
167
167
|
}
|
|
168
168
|
});
|
|
169
169
|
|
|
170
|
-
// node_modules/.bun/@clack+
|
|
170
|
+
// node_modules/.bun/@clack+prompts@0.11.0/node_modules/@clack/prompts/node_modules/@clack/core/dist/index.mjs
|
|
171
171
|
import { stdin as j, stdout as M } from "node:process";
|
|
172
172
|
import * as g from "node:readline";
|
|
173
173
|
import O from "node:readline";
|
|
@@ -283,7 +283,7 @@ function fD({ input: e2 = j, output: u2 = M, overwrite: t = true, hideCursor: F2
|
|
|
283
283
|
}
|
|
284
284
|
var import_sisteransi, import_picocolors, uD, W, tD, eD, FD, sD, w, N, I, R, r, iD, CD, ED, d, oD, y, V, nD, G, _, z, K, aD, k, hD, lD, xD, B, AD, S, gD, vD, h, x, dD, A, kD, $D, H, SD, OD, PD, J, LD, RD;
|
|
285
285
|
var init_dist = __esm({
|
|
286
|
-
"node_modules/.bun/@clack+
|
|
286
|
+
"node_modules/.bun/@clack+prompts@0.11.0/node_modules/@clack/prompts/node_modules/@clack/core/dist/index.mjs"() {
|
|
287
287
|
init_utils();
|
|
288
288
|
import_sisteransi = __toESM(require_src(), 1);
|
|
289
289
|
import_picocolors = __toESM(require_picocolors(), 1);
|
|
@@ -8468,7 +8468,7 @@ async function askForInput(message, defaultValue = "") {
|
|
|
8468
8468
|
const result = await he({
|
|
8469
8469
|
message,
|
|
8470
8470
|
placeholder: defaultValue || void 0,
|
|
8471
|
-
|
|
8471
|
+
initialValue: defaultValue || void 0
|
|
8472
8472
|
});
|
|
8473
8473
|
if (pD(result)) {
|
|
8474
8474
|
xe("Operation cancelled.");
|
|
@@ -8803,8 +8803,10 @@ function executeFirebaseCommand(args, options) {
|
|
|
8803
8803
|
errorOutput
|
|
8804
8804
|
};
|
|
8805
8805
|
}
|
|
8806
|
-
function buildFirebaseDeployArgs(
|
|
8807
|
-
const
|
|
8806
|
+
function buildFirebaseDeployArgs(deployTargets, projectId, debug, force) {
|
|
8807
|
+
const targets = Array.isArray(deployTargets) ? deployTargets : [deployTargets];
|
|
8808
|
+
const targetString = targets.join(",");
|
|
8809
|
+
const args = ["deploy", "--only", targetString, "--non-interactive"];
|
|
8808
8810
|
if (projectId) {
|
|
8809
8811
|
args.push("--project", projectId);
|
|
8810
8812
|
}
|
|
@@ -10212,6 +10214,47 @@ To fix this, run:
|
|
|
10212
10214
|
}
|
|
10213
10215
|
}
|
|
10214
10216
|
|
|
10217
|
+
// packages/tooling/src/apps/deploy-rules.ts
|
|
10218
|
+
init_utils();
|
|
10219
|
+
async function deployRules(appDir, serviceAccountPath, projectId, config, options) {
|
|
10220
|
+
const targets = [];
|
|
10221
|
+
if (options.firestore) {
|
|
10222
|
+
targets.push("firestore:rules");
|
|
10223
|
+
}
|
|
10224
|
+
if (options.firestoreIndexes) {
|
|
10225
|
+
targets.push("firestore:indexes");
|
|
10226
|
+
}
|
|
10227
|
+
if (options.storage) {
|
|
10228
|
+
targets.push("storage");
|
|
10229
|
+
}
|
|
10230
|
+
if (targets.length === 0) {
|
|
10231
|
+
return;
|
|
10232
|
+
}
|
|
10233
|
+
const targetNames = targets.join(", ");
|
|
10234
|
+
const s = Y2();
|
|
10235
|
+
s.start(`Deploying ${targetNames}...`);
|
|
10236
|
+
const args = buildFirebaseDeployArgs(targets, projectId, config.debug);
|
|
10237
|
+
const result = executeFirebaseCommand(args, {
|
|
10238
|
+
cwd: appDir,
|
|
10239
|
+
serviceAccountPath,
|
|
10240
|
+
projectId,
|
|
10241
|
+
debug: config.debug
|
|
10242
|
+
});
|
|
10243
|
+
if (result.error) {
|
|
10244
|
+
s.stop("Rules deployment failed");
|
|
10245
|
+
throw result.error;
|
|
10246
|
+
}
|
|
10247
|
+
if (!result.success) {
|
|
10248
|
+
s.stop("Rules deployment failed");
|
|
10249
|
+
handleDeploymentFailure(
|
|
10250
|
+
result,
|
|
10251
|
+
`firebase ${args.join(" ")}`,
|
|
10252
|
+
serviceAccountPath
|
|
10253
|
+
);
|
|
10254
|
+
}
|
|
10255
|
+
s.stop(`${targetNames} deployed successfully`);
|
|
10256
|
+
}
|
|
10257
|
+
|
|
10215
10258
|
// packages/tooling/src/apps/deploy-utils.ts
|
|
10216
10259
|
init_utils();
|
|
10217
10260
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
@@ -10332,6 +10375,9 @@ function validateFirebaseJson2(appDir) {
|
|
|
10332
10375
|
valid: false,
|
|
10333
10376
|
hasHosting: false,
|
|
10334
10377
|
hasFunctions: false,
|
|
10378
|
+
hasFirestoreRules: false,
|
|
10379
|
+
hasFirestoreIndexes: false,
|
|
10380
|
+
hasStorageRules: false,
|
|
10335
10381
|
errors: [`firebase.json not found at: ${firebaseJsonPath}`]
|
|
10336
10382
|
};
|
|
10337
10383
|
}
|
|
@@ -10344,11 +10390,17 @@ function validateFirebaseJson2(appDir) {
|
|
|
10344
10390
|
}
|
|
10345
10391
|
const hasHosting = !!config.hosting;
|
|
10346
10392
|
const hasFunctions = !!config.functions && Array.isArray(config.functions);
|
|
10393
|
+
const hasFirestoreRules = !!config.firestore?.rules && pathExists(joinPath(appDir, config.firestore.rules));
|
|
10394
|
+
const hasFirestoreIndexes = !!config.firestore?.indexes && pathExists(joinPath(appDir, config.firestore.indexes));
|
|
10395
|
+
const hasStorageRules = !!config.storage?.rules && pathExists(joinPath(appDir, config.storage.rules));
|
|
10347
10396
|
return {
|
|
10348
10397
|
valid: true,
|
|
10349
10398
|
projectId: config.projectId,
|
|
10350
10399
|
hasHosting,
|
|
10351
10400
|
hasFunctions,
|
|
10401
|
+
hasFirestoreRules,
|
|
10402
|
+
hasFirestoreIndexes,
|
|
10403
|
+
hasStorageRules,
|
|
10352
10404
|
errors: []
|
|
10353
10405
|
};
|
|
10354
10406
|
} catch (error2) {
|
|
@@ -10356,6 +10408,9 @@ function validateFirebaseJson2(appDir) {
|
|
|
10356
10408
|
valid: false,
|
|
10357
10409
|
hasHosting: false,
|
|
10358
10410
|
hasFunctions: false,
|
|
10411
|
+
hasFirestoreRules: false,
|
|
10412
|
+
hasFirestoreIndexes: false,
|
|
10413
|
+
hasStorageRules: false,
|
|
10359
10414
|
errors: [
|
|
10360
10415
|
`Invalid JSON format: ${error2 instanceof DoNotDevError ? error2.message : error2 instanceof Error ? error2.message : String(error2)}`
|
|
10361
10416
|
]
|
|
@@ -10520,16 +10575,36 @@ async function main5(options = {}) {
|
|
|
10520
10575
|
hint: "Deploy cloud functions"
|
|
10521
10576
|
});
|
|
10522
10577
|
}
|
|
10578
|
+
const hasRules = firebaseConfig2.hasFirestoreRules || firebaseConfig2.hasFirestoreIndexes || firebaseConfig2.hasStorageRules;
|
|
10579
|
+
if (hasRules) {
|
|
10580
|
+
choices.push({
|
|
10581
|
+
title: "Rules only",
|
|
10582
|
+
value: "rules",
|
|
10583
|
+
hint: "Deploy Firestore/Storage rules"
|
|
10584
|
+
});
|
|
10585
|
+
}
|
|
10523
10586
|
if (firebaseConfig2.hasHosting && firebaseConfig2.hasFunctions) {
|
|
10524
10587
|
choices.push({
|
|
10525
|
-
title: "
|
|
10588
|
+
title: "Frontend + Functions",
|
|
10526
10589
|
value: "both",
|
|
10527
|
-
hint: "Deploy
|
|
10590
|
+
hint: "Deploy hosting and functions"
|
|
10591
|
+
});
|
|
10592
|
+
}
|
|
10593
|
+
const deployableKindsCount = [
|
|
10594
|
+
firebaseConfig2.hasHosting,
|
|
10595
|
+
firebaseConfig2.hasFunctions,
|
|
10596
|
+
hasRules
|
|
10597
|
+
].filter(Boolean).length;
|
|
10598
|
+
if (deployableKindsCount > 1) {
|
|
10599
|
+
choices.push({
|
|
10600
|
+
title: "All",
|
|
10601
|
+
value: "all",
|
|
10602
|
+
hint: "Deploy everything (hosting, functions, rules)"
|
|
10528
10603
|
});
|
|
10529
10604
|
}
|
|
10530
10605
|
if (choices.length === 0) {
|
|
10531
10606
|
log.error(
|
|
10532
|
-
"No deployment targets found. firebase.json must have hosting or
|
|
10607
|
+
"No deployment targets found. firebase.json must have hosting, functions, or rules configuration."
|
|
10533
10608
|
);
|
|
10534
10609
|
process.exit(1);
|
|
10535
10610
|
}
|
|
@@ -10574,13 +10649,28 @@ async function main5(options = {}) {
|
|
|
10574
10649
|
showFirebaseJsonError(firebaseConfig.errors, appDir, deploymentType);
|
|
10575
10650
|
process.exit(1);
|
|
10576
10651
|
}
|
|
10577
|
-
|
|
10578
|
-
|
|
10579
|
-
|
|
10580
|
-
|
|
10581
|
-
|
|
10582
|
-
|
|
10583
|
-
|
|
10652
|
+
const shouldDeployFrontend = (deploymentType === "frontend" || deploymentType === "both" || deploymentType === "all") && firebaseConfig.hasHosting;
|
|
10653
|
+
const shouldDeployFunctions = (deploymentType === "functions" || deploymentType === "both" || deploymentType === "all") && firebaseConfig.hasFunctions;
|
|
10654
|
+
const shouldDeployRules = (deploymentType === "rules" || deploymentType === "all") && (firebaseConfig.hasFirestoreRules || firebaseConfig.hasFirestoreIndexes || firebaseConfig.hasStorageRules);
|
|
10655
|
+
if (deploymentType === "frontend" && !firebaseConfig.hasHosting) {
|
|
10656
|
+
log.error(
|
|
10657
|
+
"firebase.json does not contain hosting configuration, but frontend deployment was requested."
|
|
10658
|
+
);
|
|
10659
|
+
process.exit(1);
|
|
10660
|
+
}
|
|
10661
|
+
if (deploymentType === "functions" && !firebaseConfig.hasFunctions) {
|
|
10662
|
+
log.error(
|
|
10663
|
+
"firebase.json does not contain functions configuration, but functions deployment was requested."
|
|
10664
|
+
);
|
|
10665
|
+
process.exit(1);
|
|
10666
|
+
}
|
|
10667
|
+
if (deploymentType === "rules" && !shouldDeployRules) {
|
|
10668
|
+
log.error(
|
|
10669
|
+
"firebase.json does not contain rules configuration, but rules deployment was requested."
|
|
10670
|
+
);
|
|
10671
|
+
process.exit(1);
|
|
10672
|
+
}
|
|
10673
|
+
if (shouldDeployFrontend) {
|
|
10584
10674
|
const buildStatus = validateBuild(appDir);
|
|
10585
10675
|
let shouldBuild = false;
|
|
10586
10676
|
if (!buildStatus.exists || buildStatus.isEmpty) {
|
|
@@ -10623,14 +10713,6 @@ async function main5(options = {}) {
|
|
|
10623
10713
|
}
|
|
10624
10714
|
}
|
|
10625
10715
|
}
|
|
10626
|
-
if (deploymentType === "functions" || deploymentType === "both") {
|
|
10627
|
-
if (!firebaseConfig.hasFunctions) {
|
|
10628
|
-
log.error(
|
|
10629
|
-
"firebase.json does not contain functions configuration, but functions deployment was requested."
|
|
10630
|
-
);
|
|
10631
|
-
process.exit(1);
|
|
10632
|
-
}
|
|
10633
|
-
}
|
|
10634
10716
|
clearFirebaseCache(appDir);
|
|
10635
10717
|
Me(
|
|
10636
10718
|
`App: ${appName}
|
|
@@ -10639,10 +10721,10 @@ Deployment: ${deploymentType}
|
|
|
10639
10721
|
Service Account: ${serviceAccountResult.info.clientEmail}`,
|
|
10640
10722
|
"Deployment Configuration"
|
|
10641
10723
|
);
|
|
10642
|
-
if (
|
|
10724
|
+
if (shouldDeployFrontend) {
|
|
10643
10725
|
await deployFrontend(appDir, serviceAccountPath, config.project, config);
|
|
10644
10726
|
}
|
|
10645
|
-
if (
|
|
10727
|
+
if (shouldDeployFunctions) {
|
|
10646
10728
|
await deployFunctions(
|
|
10647
10729
|
appDir,
|
|
10648
10730
|
serviceAccountPath,
|
|
@@ -10650,6 +10732,13 @@ Service Account: ${serviceAccountResult.info.clientEmail}`,
|
|
|
10650
10732
|
config
|
|
10651
10733
|
);
|
|
10652
10734
|
}
|
|
10735
|
+
if (shouldDeployRules) {
|
|
10736
|
+
await deployRules(appDir, serviceAccountPath, config.project, config, {
|
|
10737
|
+
firestore: firebaseConfig.hasFirestoreRules,
|
|
10738
|
+
firestoreIndexes: firebaseConfig.hasFirestoreIndexes,
|
|
10739
|
+
storage: firebaseConfig.hasStorageRules
|
|
10740
|
+
});
|
|
10741
|
+
}
|
|
10653
10742
|
Se("Deployment completed successfully!");
|
|
10654
10743
|
} catch (error2) {
|
|
10655
10744
|
if (error2 instanceof DoNotDevError) {
|
|
@@ -11180,7 +11269,7 @@ function generatePackageJson(templateName, mode, options = {}) {
|
|
|
11180
11269
|
"dependencies-matrix.json not found. This command requires the matrix file."
|
|
11181
11270
|
);
|
|
11182
11271
|
}
|
|
11183
|
-
const { matrix
|
|
11272
|
+
const { matrix } = matrixResult;
|
|
11184
11273
|
const template = matrix.templateMapping?.[templateName];
|
|
11185
11274
|
if (!template) {
|
|
11186
11275
|
throw new Error(`Template "${templateName}" not found in matrix`);
|
|
@@ -11244,6 +11333,7 @@ function generatePackageJson(templateName, mode, options = {}) {
|
|
|
11244
11333
|
}
|
|
11245
11334
|
}
|
|
11246
11335
|
if (templateName.includes("functions")) {
|
|
11336
|
+
result.main = "lib/index.js";
|
|
11247
11337
|
result.engines = { node: "20" };
|
|
11248
11338
|
if (options.appName) {
|
|
11249
11339
|
const platform = templateName.includes("vercel") ? "Vercel" : "Firebase";
|
|
@@ -11371,6 +11461,8 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
|
|
|
11371
11461
|
s.start(`Creating app: ${appName}`);
|
|
11372
11462
|
await ensureDir(appDir);
|
|
11373
11463
|
const templateDir = appTemplate === "demo" ? "app-demo" : appTemplate === "nextjs" ? "app-next" : "app-vite";
|
|
11464
|
+
const firebaseProjectId = (appConfig?.firebaseProjectId ?? "").trim() || appName.toLowerCase().replace(/\s+/g, "-");
|
|
11465
|
+
const firebaseRegion = appConfig?.firebaseRegion ?? "europe-west1";
|
|
11374
11466
|
const replacements = {
|
|
11375
11467
|
projectName: appName,
|
|
11376
11468
|
appName,
|
|
@@ -11379,11 +11471,14 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
|
|
|
11379
11471
|
needsCRUD: Boolean(appConfig.needsCRUD),
|
|
11380
11472
|
setupGithubActions: false,
|
|
11381
11473
|
appNames: [appName],
|
|
11382
|
-
firebaseProjectId
|
|
11474
|
+
firebaseProjectId,
|
|
11475
|
+
firebaseRegion,
|
|
11383
11476
|
firebaseSecretName: appName.toUpperCase().replace(/-/g, "_"),
|
|
11384
11477
|
monorepoRelativePath: "../../packages/tooling",
|
|
11385
11478
|
appTemplate,
|
|
11386
|
-
isNextjs: appTemplate === "nextjs"
|
|
11479
|
+
isNextjs: appTemplate === "nextjs",
|
|
11480
|
+
YOUR_FIREBASE_PROJECT_ID: firebaseProjectId,
|
|
11481
|
+
YOUR_REGION: firebaseRegion
|
|
11387
11482
|
};
|
|
11388
11483
|
const templateSourceDir = joinPath(templatesRoot, templateDir);
|
|
11389
11484
|
const templateFiles = await glob("**/*", {
|
|
@@ -11476,6 +11571,32 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
|
|
|
11476
11571
|
await replacePlaceholders(firebaseJsonDest, replacements);
|
|
11477
11572
|
}
|
|
11478
11573
|
}
|
|
11574
|
+
const firebasercSource = joinPath(
|
|
11575
|
+
deploymentTemplateDir,
|
|
11576
|
+
".firebaserc.example"
|
|
11577
|
+
);
|
|
11578
|
+
if (pathExists(firebasercSource)) {
|
|
11579
|
+
const firebasercDest = joinPath(appDir, ".firebaserc");
|
|
11580
|
+
await copy(firebasercSource, firebasercDest);
|
|
11581
|
+
if (await isTextFile(firebasercDest)) {
|
|
11582
|
+
await replacePlaceholders(firebasercDest, replacements);
|
|
11583
|
+
}
|
|
11584
|
+
}
|
|
11585
|
+
if (appConfig.needsBackend && appConfig.backendPlatform === "firebase") {
|
|
11586
|
+
const rulesFiles = [
|
|
11587
|
+
"firestore.rules.example",
|
|
11588
|
+
"firestore.indexes.json.example",
|
|
11589
|
+
"storage.rules.example"
|
|
11590
|
+
];
|
|
11591
|
+
for (const example of rulesFiles) {
|
|
11592
|
+
const src = joinPath(deploymentTemplateDir, example);
|
|
11593
|
+
if (pathExists(src)) {
|
|
11594
|
+
const destName = example.replace(".example", "");
|
|
11595
|
+
const dest = joinPath(appDir, destName);
|
|
11596
|
+
await copy(src, dest);
|
|
11597
|
+
}
|
|
11598
|
+
}
|
|
11599
|
+
}
|
|
11479
11600
|
if (appTemplate === "nextjs" || appConfig.needsBackend && appConfig.backendPlatform === "vercel") {
|
|
11480
11601
|
const vercelJsonSource = joinPath(
|
|
11481
11602
|
deploymentTemplateDir,
|
|
@@ -11501,12 +11622,15 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
|
|
|
11501
11622
|
}
|
|
11502
11623
|
if (isInteractive) {
|
|
11503
11624
|
Se("\u{1F389} App created successfully!");
|
|
11625
|
+
const firebaseStep = appConfig.needsBackend && appConfig.backendPlatform === "firebase" ? `2. Set Firebase project: cd apps/${appName} && firebase use --add (or edit .firebase rc)
|
|
11626
|
+
3. bun install
|
|
11627
|
+
4. bun run dev` : `2. bun install
|
|
11628
|
+
3. bun run dev`;
|
|
11504
11629
|
Me(
|
|
11505
11630
|
`Next steps:
|
|
11506
11631
|
|
|
11507
11632
|
1. cd apps/${appName}
|
|
11508
|
-
|
|
11509
|
-
3. bun run dev
|
|
11633
|
+
${firebaseStep}
|
|
11510
11634
|
|
|
11511
11635
|
Happy coding!`,
|
|
11512
11636
|
"\u{1F4CB} Next Steps"
|
|
@@ -11538,7 +11662,9 @@ async function main7(options) {
|
|
|
11538
11662
|
selectedEntities: [],
|
|
11539
11663
|
userAuth: "social",
|
|
11540
11664
|
billing: true,
|
|
11541
|
-
features: []
|
|
11665
|
+
features: [],
|
|
11666
|
+
firebaseProjectId: options.firebaseProjectId,
|
|
11667
|
+
firebaseRegion: options.firebaseRegion
|
|
11542
11668
|
};
|
|
11543
11669
|
await createApp(appName, appConfig);
|
|
11544
11670
|
} else {
|
|
@@ -11878,6 +12004,8 @@ async function main8(options) {
|
|
|
11878
12004
|
overwrite: true
|
|
11879
12005
|
});
|
|
11880
12006
|
const rootTemplateDir = joinPath(templatesRoot, "root-consumer");
|
|
12007
|
+
const firebaseProjectId = projectName.toLowerCase().replace(/\s+/g, "-");
|
|
12008
|
+
const firebaseRegion = "europe-west1";
|
|
11881
12009
|
const rootReplacements = {
|
|
11882
12010
|
projectName,
|
|
11883
12011
|
appNames: isMergeMode ? allAppNames : appNames,
|
|
@@ -11886,12 +12014,22 @@ async function main8(options) {
|
|
|
11886
12014
|
monorepoRelativePath: relativeMonorepoPath,
|
|
11887
12015
|
appTemplate: "vite",
|
|
11888
12016
|
isNextjs: false,
|
|
11889
|
-
firebaseProjectId
|
|
12017
|
+
firebaseProjectId,
|
|
12018
|
+
firebaseRegion,
|
|
11890
12019
|
firebaseSecretName: projectName.toUpperCase().replace(/-/g, "_"),
|
|
12020
|
+
YOUR_FIREBASE_PROJECT_ID: firebaseProjectId,
|
|
12021
|
+
YOUR_REGION: firebaseRegion,
|
|
11891
12022
|
needsAuth,
|
|
11892
12023
|
needsOAuth,
|
|
11893
12024
|
needsBilling
|
|
11894
12025
|
};
|
|
12026
|
+
const firebaseRootFiles = /* @__PURE__ */ new Set([
|
|
12027
|
+
"firebase.json.example",
|
|
12028
|
+
".firebaserc.example",
|
|
12029
|
+
"firestore.rules.example",
|
|
12030
|
+
"firestore.indexes.json.example",
|
|
12031
|
+
"storage.rules.example"
|
|
12032
|
+
]);
|
|
11895
12033
|
const files = await glob("**/*", {
|
|
11896
12034
|
cwd: rootTemplateDir,
|
|
11897
12035
|
dot: true,
|
|
@@ -11899,6 +12037,7 @@ async function main8(options) {
|
|
|
11899
12037
|
});
|
|
11900
12038
|
for (const file of files) {
|
|
11901
12039
|
if (file === "package.json.example") continue;
|
|
12040
|
+
if (firebaseRootFiles.has(file)) continue;
|
|
11902
12041
|
const sourcePath = joinPath(rootTemplateDir, file);
|
|
11903
12042
|
let destFileName = file;
|
|
11904
12043
|
if (destFileName.endsWith(".example")) {
|
|
@@ -11973,6 +12112,8 @@ async function main8(options) {
|
|
|
11973
12112
|
}
|
|
11974
12113
|
}
|
|
11975
12114
|
if (installDemoApp) {
|
|
12115
|
+
const demoFirebaseProjectId = projectName.toLowerCase().replace(/\s+/g, "-");
|
|
12116
|
+
const demoFirebaseRegion = "europe-west1";
|
|
11976
12117
|
await copyTemplateFiles(demoTemplateDir, demoAppDir, {
|
|
11977
12118
|
projectName,
|
|
11978
12119
|
appName: "demo",
|
|
@@ -11980,8 +12121,11 @@ async function main8(options) {
|
|
|
11980
12121
|
needsCRUD: false,
|
|
11981
12122
|
setupGithubActions: false,
|
|
11982
12123
|
appNames,
|
|
11983
|
-
firebaseProjectId:
|
|
12124
|
+
firebaseProjectId: demoFirebaseProjectId,
|
|
12125
|
+
firebaseRegion: demoFirebaseRegion,
|
|
11984
12126
|
firebaseSecretName: projectName.toUpperCase().replace(/-/g, "_"),
|
|
12127
|
+
YOUR_FIREBASE_PROJECT_ID: demoFirebaseProjectId,
|
|
12128
|
+
YOUR_REGION: demoFirebaseRegion,
|
|
11985
12129
|
monorepoRelativePath: executionMode === "development" ? calculateRelativePath(projectDirNormalized, monorepoRoot) : "",
|
|
11986
12130
|
appTemplate: "demo"
|
|
11987
12131
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@donotdev/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"description": "Command-line interface for DoNotDev Framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"type-check": "tsc --noEmit"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@clack/prompts": "^0.
|
|
37
|
-
"commander": "^14.0.
|
|
36
|
+
"@clack/prompts": "^1.0.0",
|
|
37
|
+
"commander": "^14.0.3",
|
|
38
38
|
"fast-glob": "^3.3.3"
|
|
39
39
|
},
|
|
40
40
|
"repository": {
|
|
@@ -59,5 +59,6 @@
|
|
|
59
59
|
"publishConfig": {
|
|
60
60
|
"registry": "https://registry.npmjs.org",
|
|
61
61
|
"access": "public"
|
|
62
|
-
}
|
|
62
|
+
},
|
|
63
|
+
"peerDependencies": {}
|
|
63
64
|
}
|
|
@@ -29,7 +29,7 @@ export const appConfig: AppConfig = {
|
|
|
29
29
|
name: APP_NAME,
|
|
30
30
|
shortName: APP_SHORT_NAME,
|
|
31
31
|
description: APP_DESCRIPTION,
|
|
32
|
-
//
|
|
32
|
+
// Note: URL comes from NEXT_PUBLIC_APP_URL in .env, not here
|
|
33
33
|
|
|
34
34
|
// Footer legal links - remove any you don't need
|
|
35
35
|
footer: {
|
|
@@ -16,8 +16,13 @@
|
|
|
16
16
|
<!-- ✅ PWA: Manifest link (if exists) -->
|
|
17
17
|
<link rel="manifest" href="/manifest.json" />
|
|
18
18
|
|
|
19
|
-
<!-- ✅ PERFORMANCE:
|
|
20
|
-
<link rel="
|
|
19
|
+
<!-- ✅ PERFORMANCE: Preload critical fonts (non-blocking) -->
|
|
20
|
+
<link rel="preload" href="/fonts/Inter-latin.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
|
21
|
+
<link rel="preload" href="/fonts/Roboto-400-latin.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
|
22
|
+
|
|
23
|
+
<!-- ✅ PERFORMANCE: Load extended font subsets async (non-blocking) -->
|
|
24
|
+
<link rel="stylesheet" href="/fonts/fonts.css" media="print" onload="this.media='all'">
|
|
25
|
+
<noscript><link rel="stylesheet" href="/fonts/fonts.css"></noscript>
|
|
21
26
|
|
|
22
27
|
<!-- ✅ PERFORMANCE: Preconnect to external domains (OAuth providers) -->
|
|
23
28
|
<!-- GitHub OAuth -->
|
|
@@ -36,6 +41,23 @@
|
|
|
36
41
|
|
|
37
42
|
<!-- ✅ PERFORMANCE: Critical CSS inlined here by build -->
|
|
38
43
|
<style>
|
|
44
|
+
/* Critical @font-face declarations - must be inline for preloaded fonts to work */
|
|
45
|
+
@font-face {
|
|
46
|
+
font-family: Inter;
|
|
47
|
+
font-style: normal;
|
|
48
|
+
font-weight: 400 700;
|
|
49
|
+
font-display: swap;
|
|
50
|
+
src: url('/fonts/Inter-latin.woff2') format('woff2');
|
|
51
|
+
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
52
|
+
}
|
|
53
|
+
@font-face {
|
|
54
|
+
font-family: Roboto;
|
|
55
|
+
font-style: normal;
|
|
56
|
+
font-weight: 400;
|
|
57
|
+
font-display: swap;
|
|
58
|
+
src: url('/fonts/Roboto-400-latin.woff2') format('woff2');
|
|
59
|
+
unicode-range: U+0000-00FF;
|
|
60
|
+
}
|
|
39
61
|
/* Critical above-the-fold styles */
|
|
40
62
|
html, body {
|
|
41
63
|
margin: 0;
|
|
@@ -29,7 +29,7 @@ export const appConfig: AppConfig = {
|
|
|
29
29
|
name: APP_NAME,
|
|
30
30
|
shortName: APP_SHORT_NAME,
|
|
31
31
|
description: APP_DESCRIPTION,
|
|
32
|
-
//
|
|
32
|
+
// Note: URL comes from VITE_APP_URL in .env, not here
|
|
33
33
|
|
|
34
34
|
// Footer legal links - remove any you don't need
|
|
35
35
|
footer: {
|
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
import { useEffect, useState } from 'react';
|
|
14
14
|
|
|
15
15
|
import { Section, Button, Alert } from '@donotdev/components';
|
|
16
|
-
import {
|
|
16
|
+
import { useCrud } from '@donotdev/crud';
|
|
17
17
|
import { useTranslation } from '@donotdev/core';
|
|
18
18
|
import type { PageMeta } from '@donotdev/core';
|
|
19
|
-
import { PageContainer, Link,
|
|
19
|
+
import { PageContainer, Link, EntityFormRenderer } from '@donotdev/ui';
|
|
20
20
|
|
|
21
21
|
// Import your entity from root-level entities folder
|
|
22
22
|
// import { productEntity } from 'entities/Product';
|
|
@@ -51,7 +51,6 @@ export const meta: PageMeta = {
|
|
|
51
51
|
export default function ProductPage() {
|
|
52
52
|
const { t } = useTranslation(NAMESPACE);
|
|
53
53
|
const id = useParam('id');
|
|
54
|
-
const navigate = useNavigate();
|
|
55
54
|
const isNew = id === 'new';
|
|
56
55
|
|
|
57
56
|
// useCrud provides CRUD operations with optimistic updates
|
|
@@ -84,8 +83,8 @@ export default function ProductPage() {
|
|
|
84
83
|
update(id, data); // No await - fires in background
|
|
85
84
|
}
|
|
86
85
|
|
|
87
|
-
//
|
|
88
|
-
navigate
|
|
86
|
+
// Navigation happens automatically via cancelPath (defaults to /products)
|
|
87
|
+
// Or you can navigate manually if needed
|
|
89
88
|
};
|
|
90
89
|
|
|
91
90
|
// ==========================================================================
|
|
@@ -121,6 +120,8 @@ export default function ProductPage() {
|
|
|
121
120
|
onSubmit={handleSubmit}
|
|
122
121
|
defaultValues={{ status: 'draft' }} // Initial values for new items
|
|
123
122
|
submitText={t('create')}
|
|
123
|
+
// Cancel automatically navigates to /products (or cancelPath if provided)
|
|
124
|
+
cancelPath="/products"
|
|
124
125
|
/>
|
|
125
126
|
) : (
|
|
126
127
|
// EDIT MODE
|
|
@@ -130,6 +131,8 @@ export default function ProductPage() {
|
|
|
130
131
|
onSubmit={handleSubmit}
|
|
131
132
|
defaultValues={formData} // Loaded data
|
|
132
133
|
submitText={t('update')}
|
|
134
|
+
// Cancel automatically navigates to /products (or cancelPath if provided)
|
|
135
|
+
cancelPath="/products"
|
|
133
136
|
/>
|
|
134
137
|
)}
|
|
135
138
|
</Section>
|