@donotdev/cli 0.0.7 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +3 -18
  2. package/dependencies-matrix.json +54 -45
  3. package/dist/bin/commands/build.js +19 -5
  4. package/dist/bin/commands/bump.js +30 -5
  5. package/dist/bin/commands/cacheout.js +36 -19
  6. package/dist/bin/commands/create-app.js +73 -7
  7. package/dist/bin/commands/create-project.js +90 -8
  8. package/dist/bin/commands/deploy.js +130 -27
  9. package/dist/bin/commands/dev.js +23 -7
  10. package/dist/bin/commands/emu.js +28 -12
  11. package/dist/bin/commands/format.js +39 -22
  12. package/dist/bin/commands/lint.js +36 -19
  13. package/dist/bin/commands/preview.js +24 -8
  14. package/dist/bin/commands/sync-secrets.js +19 -5
  15. package/dist/bin/dndev.js +7 -20
  16. package/dist/bin/donotdev.js +7 -20
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +209 -100
  20. package/dist/index.js.map +1 -1
  21. package/package.json +4 -3
  22. package/templates/app-next/src/config/app.ts.example +1 -1
  23. package/templates/app-vite/index.html.example +24 -2
  24. package/templates/app-vite/src/config/app.ts.example +1 -1
  25. package/templates/app-vite/src/pages/FormPageExample.tsx.example +8 -5
  26. package/templates/app-vite/src/pages/ListPageExample.tsx.example +4 -7
  27. package/templates/root-consumer/.firebaserc.example +5 -0
  28. package/templates/root-consumer/entities/ExampleEntity.ts.example +2 -1
  29. package/templates/root-consumer/entities/demo.ts.example +15 -1
  30. package/templates/root-consumer/eslint.config.js.example +2 -80
  31. package/templates/root-consumer/firestore.indexes.json.example +4 -0
  32. package/templates/root-consumer/firestore.rules.example +11 -0
  33. package/templates/root-consumer/guides/dndev/COMPONENTS_CRUD.md.example +9 -6
  34. package/templates/root-consumer/guides/dndev/SETUP_CRUD.md.example +376 -38
  35. package/templates/root-consumer/guides/dndev/SETUP_I18N.md.example +46 -0
  36. package/templates/root-consumer/guides/wai-way/entity_patterns.md.example +1 -1
  37. package/templates/root-consumer/storage.rules.example +8 -0
@@ -46,13 +46,12 @@ 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 to existing project (--builder vite|next, --functions)
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
53
53
  emu [app] Start dev with Firebase emulators
54
54
  format Format code with Prettier
55
- lint Lint code with ESLint
56
55
  deploy [app] Deploy to Firebase
57
56
  sync-secrets Sync env vars to Firebase/Vercel
58
57
  cacheout [app] Clear build caches
@@ -67,7 +66,8 @@ Examples:
67
66
  dndev init my-project Create a new project
68
67
  dndev create-app Interactive app creation
69
68
  dndev create-app my-app Create 'my-app' with defaults (vite, no functions)
70
- dndev create-app my-app --builder next --functions Create Next.js app with 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)
71
71
  dndev dev Start dev server
72
72
  dndev dev:web Start dev server for 'web' app
73
73
  dndev wai Output WAI-WAY activation prompt
@@ -91,14 +91,16 @@ program.command("init [name]").alias("create-project").description("Create a new
91
91
  const { main } = await import("./commands/create-project.js");
92
92
  await main({ projectName: name });
93
93
  });
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").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) => {
95
95
  const { main } = await import("./commands/create-app.js");
96
96
  const appName = name || options.name;
97
97
  if (appName) {
98
98
  await main({
99
99
  name: appName,
100
100
  builder: options.builder,
101
- functions: options.functions
101
+ functions: options.functions,
102
+ firebaseProjectId: options.project,
103
+ firebaseRegion: options.region
102
104
  });
103
105
  } else {
104
106
  await main();
@@ -119,21 +121,6 @@ formatCmd.action(async (commanderOptions) => {
119
121
  throw error;
120
122
  }
121
123
  });
122
- var lintCmd = program.command("lint").description("Lint code with ESLint").option("-f, --fix", "automatically fix issues");
123
- addCommonOptions(lintCmd);
124
- lintCmd.action(async (commanderOptions) => {
125
- const commonOptions = extractCommonOptions(commanderOptions);
126
- const options = { ...commonOptions, ...commanderOptions };
127
- try {
128
- const { main } = await import("./commands/lint.js");
129
- await main(options);
130
- } catch (error) {
131
- if (error.code === "invalid-argument" || error.name === "DoNotDevError") {
132
- process.exit(error.context?.exitCode || 1);
133
- }
134
- throw error;
135
- }
136
- });
137
124
  var cacheoutCmd = program.command("cacheout [app]").alias("co").description("Clear build caches").option("--app <app>", "App name to clear cache for");
138
125
  addCommonOptions(cacheoutCmd);
139
126
  cacheoutCmd.action(async (app, commanderOptions) => {
package/dist/index.d.ts CHANGED
@@ -4,5 +4,5 @@
4
4
  * Thin wrapper around @donotdev/tooling for public distribution.
5
5
  * Tooling is the source of truth for all CLI code.
6
6
  */
7
- export { createApp, createProject, format, lint, dev, build, preview, emu, cacheout, syncSecrets, deploy, } from '@donotdev/tooling';
7
+ export { createApp, createProject, format, dev, build, preview, emu, cacheout, syncSecrets, deploy, } from '@donotdev/tooling';
8
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAGH,OAAO,EACL,SAAS,EACT,aAAa,EACb,MAAM,EACN,IAAI,EACJ,GAAG,EACH,KAAK,EACL,OAAO,EACP,GAAG,EACH,QAAQ,EACR,WAAW,EACX,MAAM,GACP,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAGH,OAAO,EACL,SAAS,EACT,aAAa,EACb,MAAM,EACN,GAAG,EACH,KAAK,EACL,OAAO,EACP,GAAG,EACH,QAAQ,EACR,WAAW,EACX,MAAM,GACP,MAAM,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -897,6 +897,7 @@ __export(cli_output_exports, {
897
897
  success: () => success,
898
898
  warn: () => warn
899
899
  });
900
+ import "node:os";
900
901
  function supportsColor() {
901
902
  if (process.env.NO_COLOR) return false;
902
903
  if (process.platform === "win32") {
@@ -6730,7 +6731,16 @@ var init_constants = __esm({
6730
6731
  },
6731
6732
  i18n: {
6732
6733
  eager: ["src/locales/*_*.json"],
6733
- lazy: ["src/**/locales/*_*.json", "!src/locales/*_*.json"],
6734
+ lazy: [
6735
+ "src/**/locales/*_*.json",
6736
+ "!src/locales/*_*.json",
6737
+ // Auto-detect shared entity translations in monorepos (if exists, use it; if not, no problem)
6738
+ "../../entities/locales/*_*.json"
6739
+ ],
6740
+ // Additional paths from workspace packages (e.g., shared entities)
6741
+ // Consumers can still configure via i18n.additionalPaths in dndev/vite config for custom paths
6742
+ // Example: ['../../packages/shared/locales/*_*.json']
6743
+ additional: [],
6734
6744
  framework: {
6735
6745
  eager: [`${I18N_PATHS.SOURCE_EAGER}/*_*.json`],
6736
6746
  lazy: [`${I18N_PATHS.SOURCE_LAZY}/*_*.json`]
@@ -6823,6 +6833,7 @@ var init_constants = __esm({
6823
6833
 
6824
6834
  // packages/core/config/utils/PathResolver.ts
6825
6835
  import * as fs from "node:fs";
6836
+ import "node:fs";
6826
6837
  import { createRequire } from "node:module";
6827
6838
  import {
6828
6839
  resolve,
@@ -8290,6 +8301,7 @@ var init_pathResolver = __esm({
8290
8301
 
8291
8302
  // packages/tooling/src/bundler/utils.ts
8292
8303
  import { Buffer as Buffer2 } from "node:buffer";
8304
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
8293
8305
  import { createRequire as createRequire3 } from "node:module";
8294
8306
  import { dirname as dirname3, resolve as resolve3 } from "node:path";
8295
8307
  import process from "node:process";
@@ -8791,8 +8803,10 @@ function executeFirebaseCommand(args, options) {
8791
8803
  errorOutput
8792
8804
  };
8793
8805
  }
8794
- function buildFirebaseDeployArgs(deployType, projectId, debug, force) {
8795
- const args = ["deploy", "--only", deployType, "--non-interactive"];
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"];
8796
8810
  if (projectId) {
8797
8811
  args.push("--project", projectId);
8798
8812
  }
@@ -10200,6 +10214,47 @@ To fix this, run:
10200
10214
  }
10201
10215
  }
10202
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
+
10203
10258
  // packages/tooling/src/apps/deploy-utils.ts
10204
10259
  init_utils();
10205
10260
  import { spawnSync as spawnSync7 } from "node:child_process";
@@ -10320,6 +10375,9 @@ function validateFirebaseJson2(appDir) {
10320
10375
  valid: false,
10321
10376
  hasHosting: false,
10322
10377
  hasFunctions: false,
10378
+ hasFirestoreRules: false,
10379
+ hasFirestoreIndexes: false,
10380
+ hasStorageRules: false,
10323
10381
  errors: [`firebase.json not found at: ${firebaseJsonPath}`]
10324
10382
  };
10325
10383
  }
@@ -10332,11 +10390,17 @@ function validateFirebaseJson2(appDir) {
10332
10390
  }
10333
10391
  const hasHosting = !!config.hosting;
10334
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));
10335
10396
  return {
10336
10397
  valid: true,
10337
10398
  projectId: config.projectId,
10338
10399
  hasHosting,
10339
10400
  hasFunctions,
10401
+ hasFirestoreRules,
10402
+ hasFirestoreIndexes,
10403
+ hasStorageRules,
10340
10404
  errors: []
10341
10405
  };
10342
10406
  } catch (error2) {
@@ -10344,6 +10408,9 @@ function validateFirebaseJson2(appDir) {
10344
10408
  valid: false,
10345
10409
  hasHosting: false,
10346
10410
  hasFunctions: false,
10411
+ hasFirestoreRules: false,
10412
+ hasFirestoreIndexes: false,
10413
+ hasStorageRules: false,
10347
10414
  errors: [
10348
10415
  `Invalid JSON format: ${error2 instanceof DoNotDevError ? error2.message : error2 instanceof Error ? error2.message : String(error2)}`
10349
10416
  ]
@@ -10508,16 +10575,36 @@ async function main5(options = {}) {
10508
10575
  hint: "Deploy cloud functions"
10509
10576
  });
10510
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
+ }
10511
10586
  if (firebaseConfig2.hasHosting && firebaseConfig2.hasFunctions) {
10512
10587
  choices.push({
10513
- title: "Both frontend and functions",
10588
+ title: "Frontend + Functions",
10514
10589
  value: "both",
10515
- hint: "Deploy everything"
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)"
10516
10603
  });
10517
10604
  }
10518
10605
  if (choices.length === 0) {
10519
10606
  log.error(
10520
- "No deployment targets found. firebase.json must have hosting or functions configuration."
10607
+ "No deployment targets found. firebase.json must have hosting, functions, or rules configuration."
10521
10608
  );
10522
10609
  process.exit(1);
10523
10610
  }
@@ -10562,13 +10649,28 @@ async function main5(options = {}) {
10562
10649
  showFirebaseJsonError(firebaseConfig.errors, appDir, deploymentType);
10563
10650
  process.exit(1);
10564
10651
  }
10565
- if (deploymentType === "frontend" || deploymentType === "both") {
10566
- if (!firebaseConfig.hasHosting) {
10567
- log.error(
10568
- "firebase.json does not contain hosting configuration, but frontend deployment was requested."
10569
- );
10570
- process.exit(1);
10571
- }
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) {
10572
10674
  const buildStatus = validateBuild(appDir);
10573
10675
  let shouldBuild = false;
10574
10676
  if (!buildStatus.exists || buildStatus.isEmpty) {
@@ -10611,14 +10713,6 @@ async function main5(options = {}) {
10611
10713
  }
10612
10714
  }
10613
10715
  }
10614
- if (deploymentType === "functions" || deploymentType === "both") {
10615
- if (!firebaseConfig.hasFunctions) {
10616
- log.error(
10617
- "firebase.json does not contain functions configuration, but functions deployment was requested."
10618
- );
10619
- process.exit(1);
10620
- }
10621
- }
10622
10716
  clearFirebaseCache(appDir);
10623
10717
  Me(
10624
10718
  `App: ${appName}
@@ -10627,10 +10721,10 @@ Deployment: ${deploymentType}
10627
10721
  Service Account: ${serviceAccountResult.info.clientEmail}`,
10628
10722
  "Deployment Configuration"
10629
10723
  );
10630
- if (deploymentType === "frontend" || deploymentType === "both") {
10724
+ if (shouldDeployFrontend) {
10631
10725
  await deployFrontend(appDir, serviceAccountPath, config.project, config);
10632
10726
  }
10633
- if (deploymentType === "functions" || deploymentType === "both") {
10727
+ if (shouldDeployFunctions) {
10634
10728
  await deployFunctions(
10635
10729
  appDir,
10636
10730
  serviceAccountPath,
@@ -10638,6 +10732,13 @@ Service Account: ${serviceAccountResult.info.clientEmail}`,
10638
10732
  config
10639
10733
  );
10640
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
+ }
10641
10742
  Se("Deployment completed successfully!");
10642
10743
  } catch (error2) {
10643
10744
  if (error2 instanceof DoNotDevError) {
@@ -11168,7 +11269,7 @@ function generatePackageJson(templateName, mode, options = {}) {
11168
11269
  "dependencies-matrix.json not found. This command requires the matrix file."
11169
11270
  );
11170
11271
  }
11171
- const { matrix, cliVersion } = matrixResult;
11272
+ const { matrix } = matrixResult;
11172
11273
  const template = matrix.templateMapping?.[templateName];
11173
11274
  if (!template) {
11174
11275
  throw new Error(`Template "${templateName}" not found in matrix`);
@@ -11232,6 +11333,7 @@ function generatePackageJson(templateName, mode, options = {}) {
11232
11333
  }
11233
11334
  }
11234
11335
  if (templateName.includes("functions")) {
11336
+ result.main = "lib/index.js";
11235
11337
  result.engines = { node: "20" };
11236
11338
  if (options.appName) {
11237
11339
  const platform = templateName.includes("vercel") ? "Vercel" : "Firebase";
@@ -11250,6 +11352,23 @@ function generatePackageJson(templateName, mode, options = {}) {
11250
11352
  }
11251
11353
  }
11252
11354
  }
11355
+ if (mode === "published") {
11356
+ const overrides = {};
11357
+ for (const [groupName, group] of Object.entries(matrix.groups || {})) {
11358
+ if (groupName.startsWith("@donotdev/")) {
11359
+ const version = group.packages?.[groupName];
11360
+ if (version) {
11361
+ overrides[groupName] = version.replace(/^\^/, "");
11362
+ }
11363
+ }
11364
+ }
11365
+ if (matrix.overrides) {
11366
+ Object.assign(overrides, matrix.overrides);
11367
+ }
11368
+ if (Object.keys(overrides).length > 0) {
11369
+ result.overrides = overrides;
11370
+ }
11371
+ }
11253
11372
  return result;
11254
11373
  }
11255
11374
 
@@ -11342,6 +11461,8 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
11342
11461
  s.start(`Creating app: ${appName}`);
11343
11462
  await ensureDir(appDir);
11344
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";
11345
11466
  const replacements = {
11346
11467
  projectName: appName,
11347
11468
  appName,
@@ -11350,11 +11471,14 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
11350
11471
  needsCRUD: Boolean(appConfig.needsCRUD),
11351
11472
  setupGithubActions: false,
11352
11473
  appNames: [appName],
11353
- firebaseProjectId: appName.toLowerCase(),
11474
+ firebaseProjectId,
11475
+ firebaseRegion,
11354
11476
  firebaseSecretName: appName.toUpperCase().replace(/-/g, "_"),
11355
11477
  monorepoRelativePath: "../../packages/tooling",
11356
11478
  appTemplate,
11357
- isNextjs: appTemplate === "nextjs"
11479
+ isNextjs: appTemplate === "nextjs",
11480
+ YOUR_FIREBASE_PROJECT_ID: firebaseProjectId,
11481
+ YOUR_REGION: firebaseRegion
11358
11482
  };
11359
11483
  const templateSourceDir = joinPath(templatesRoot, templateDir);
11360
11484
  const templateFiles = await glob("**/*", {
@@ -11447,6 +11571,32 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
11447
11571
  await replacePlaceholders(firebaseJsonDest, replacements);
11448
11572
  }
11449
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
+ }
11450
11600
  if (appTemplate === "nextjs" || appConfig.needsBackend && appConfig.backendPlatform === "vercel") {
11451
11601
  const vercelJsonSource = joinPath(
11452
11602
  deploymentTemplateDir,
@@ -11472,12 +11622,15 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
11472
11622
  }
11473
11623
  if (isInteractive) {
11474
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`;
11475
11629
  Me(
11476
11630
  `Next steps:
11477
11631
 
11478
11632
  1. cd apps/${appName}
11479
- 2. bun install
11480
- 3. bun run dev
11633
+ ${firebaseStep}
11481
11634
 
11482
11635
  Happy coding!`,
11483
11636
  "\u{1F4CB} Next Steps"
@@ -11509,7 +11662,9 @@ async function main7(options) {
11509
11662
  selectedEntities: [],
11510
11663
  userAuth: "social",
11511
11664
  billing: true,
11512
- features: []
11665
+ features: [],
11666
+ firebaseProjectId: options.firebaseProjectId,
11667
+ firebaseRegion: options.firebaseRegion
11513
11668
  };
11514
11669
  await createApp(appName, appConfig);
11515
11670
  } else {
@@ -11849,6 +12004,8 @@ async function main8(options) {
11849
12004
  overwrite: true
11850
12005
  });
11851
12006
  const rootTemplateDir = joinPath(templatesRoot, "root-consumer");
12007
+ const firebaseProjectId = projectName.toLowerCase().replace(/\s+/g, "-");
12008
+ const firebaseRegion = "europe-west1";
11852
12009
  const rootReplacements = {
11853
12010
  projectName,
11854
12011
  appNames: isMergeMode ? allAppNames : appNames,
@@ -11857,12 +12014,22 @@ async function main8(options) {
11857
12014
  monorepoRelativePath: relativeMonorepoPath,
11858
12015
  appTemplate: "vite",
11859
12016
  isNextjs: false,
11860
- firebaseProjectId: projectName.toLowerCase(),
12017
+ firebaseProjectId,
12018
+ firebaseRegion,
11861
12019
  firebaseSecretName: projectName.toUpperCase().replace(/-/g, "_"),
12020
+ YOUR_FIREBASE_PROJECT_ID: firebaseProjectId,
12021
+ YOUR_REGION: firebaseRegion,
11862
12022
  needsAuth,
11863
12023
  needsOAuth,
11864
12024
  needsBilling
11865
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
+ ]);
11866
12033
  const files = await glob("**/*", {
11867
12034
  cwd: rootTemplateDir,
11868
12035
  dot: true,
@@ -11870,6 +12037,7 @@ async function main8(options) {
11870
12037
  });
11871
12038
  for (const file of files) {
11872
12039
  if (file === "package.json.example") continue;
12040
+ if (firebaseRootFiles.has(file)) continue;
11873
12041
  const sourcePath = joinPath(rootTemplateDir, file);
11874
12042
  let destFileName = file;
11875
12043
  if (destFileName.endsWith(".example")) {
@@ -11944,6 +12112,8 @@ async function main8(options) {
11944
12112
  }
11945
12113
  }
11946
12114
  if (installDemoApp) {
12115
+ const demoFirebaseProjectId = projectName.toLowerCase().replace(/\s+/g, "-");
12116
+ const demoFirebaseRegion = "europe-west1";
11947
12117
  await copyTemplateFiles(demoTemplateDir, demoAppDir, {
11948
12118
  projectName,
11949
12119
  appName: "demo",
@@ -11951,8 +12121,11 @@ async function main8(options) {
11951
12121
  needsCRUD: false,
11952
12122
  setupGithubActions: false,
11953
12123
  appNames,
11954
- firebaseProjectId: projectName.toLowerCase(),
12124
+ firebaseProjectId: demoFirebaseProjectId,
12125
+ firebaseRegion: demoFirebaseRegion,
11955
12126
  firebaseSecretName: projectName.toUpperCase().replace(/-/g, "_"),
12127
+ YOUR_FIREBASE_PROJECT_ID: demoFirebaseProjectId,
12128
+ YOUR_REGION: demoFirebaseRegion,
11956
12129
  monorepoRelativePath: executionMode === "development" ? calculateRelativePath(projectDirNormalized, monorepoRoot) : "",
11957
12130
  appTemplate: "demo"
11958
12131
  });
@@ -12006,7 +12179,7 @@ init_cli_output();
12006
12179
  init_errors();
12007
12180
  init_pathResolver();
12008
12181
  import { spawnSync as spawnSync9 } from "node:child_process";
12009
- import { EOL } from "node:os";
12182
+ import { EOL as EOL2 } from "node:os";
12010
12183
  async function main9(options = {}) {
12011
12184
  const dryRun = options.dryRun ?? false;
12012
12185
  const verbose = options.verbose ?? false;
@@ -12124,7 +12297,7 @@ async function addPathComments(files, rootDir, dryRun, verbose) {
12124
12297
  let modified = false;
12125
12298
  if (contentLines.length === 0) {
12126
12299
  if (!dryRun) {
12127
- writeSync(file, `${expectedPathComment}${EOL}`, {
12300
+ writeSync(file, `${expectedPathComment}${EOL2}`, {
12128
12301
  overwrite: true
12129
12302
  });
12130
12303
  }
@@ -12249,7 +12422,7 @@ async function addPathComments(files, rootDir, dryRun, verbose) {
12249
12422
  newLines.push("");
12250
12423
  }
12251
12424
  newLines.push(...contentLines);
12252
- const newContent = newLines.join(EOL);
12425
+ const newContent = newLines.join(EOL2);
12253
12426
  const contentChanged = fileContent !== newContent;
12254
12427
  if (contentChanged) {
12255
12428
  try {
@@ -12576,69 +12749,6 @@ ${stderr}` : "");
12576
12749
  }
12577
12750
  }
12578
12751
 
12579
- // packages/tooling/src/quality/lint.ts
12580
- init_utils();
12581
- init_cli_output();
12582
- init_errors();
12583
- init_pathResolver();
12584
- import { spawnSync as spawnSync10 } from "node:child_process";
12585
- async function main10(options = {}) {
12586
- const fix = options.fix ?? false;
12587
- const cwd = process.cwd();
12588
- const eslintConfigs = [
12589
- "eslint.config.js",
12590
- "eslint.config.mjs",
12591
- "eslint.config.cjs"
12592
- ];
12593
- const hasEslintConfig = eslintConfigs.some(
12594
- (config) => pathExists(joinPath(cwd, config))
12595
- );
12596
- if (!hasEslintConfig) {
12597
- log.info("No eslint.config.js found in current directory. Skipping lint.");
12598
- log.info("To enable linting, create an eslint.config.js file.");
12599
- return 0;
12600
- }
12601
- const eslintArgs = fix ? ["--fix"] : [];
12602
- if (options.debug) {
12603
- eslintArgs.push("--debug");
12604
- }
12605
- if (options.quiet) {
12606
- eslintArgs.push("--quiet");
12607
- }
12608
- if (options.debug || options.verbose) {
12609
- log.debug(`Linting code${fix ? " and fixing issues" : ""}...`);
12610
- log.debug(` Working directory: ${cwd}`);
12611
- log.debug(` ESLint args: ${eslintArgs.join(" ")}`);
12612
- } else {
12613
- log.info(fix ? "Linting and fixing code..." : "Linting code...");
12614
- }
12615
- try {
12616
- const result = spawnSync10("bunx", ["eslint", ".", ...eslintArgs], {
12617
- cwd,
12618
- stdio: "inherit",
12619
- env: process.env,
12620
- shell: true
12621
- });
12622
- const exitCode = result.status ?? 1;
12623
- if (exitCode === 0) {
12624
- if (!options.debug && !options.verbose) {
12625
- log.success("Linting completed successfully");
12626
- }
12627
- } else {
12628
- if (!options.debug && !options.verbose) {
12629
- log.error("Linting found issues (see above for details)");
12630
- }
12631
- }
12632
- return exitCode;
12633
- } catch (err) {
12634
- throw DoNotDevError.from(
12635
- err,
12636
- "Failed to run ESLint",
12637
- "cli-execution-error"
12638
- );
12639
- }
12640
- }
12641
-
12642
12752
  // packages/tooling/src/maintenance/cacheout.ts
12643
12753
  init_utils();
12644
12754
  init_cli_output();
@@ -12765,7 +12875,7 @@ function removeItem(path, rootDir, dryRun, verbose) {
12765
12875
  return false;
12766
12876
  }
12767
12877
  }
12768
- async function main11(options) {
12878
+ async function main10(options) {
12769
12879
  const opts = options;
12770
12880
  const targetDir = opts.app ? joinPath(process.cwd(), "apps", opts.app) : process.cwd();
12771
12881
  if (opts.app && !pathExists(targetDir)) {
@@ -12814,14 +12924,13 @@ async function main11(options) {
12814
12924
  }
12815
12925
  export {
12816
12926
  main as build,
12817
- main11 as cacheout,
12927
+ main10 as cacheout,
12818
12928
  main7 as createApp,
12819
12929
  main8 as createProject,
12820
12930
  main5 as deploy,
12821
12931
  main2 as dev,
12822
12932
  main3 as emu,
12823
12933
  main9 as format,
12824
- main10 as lint,
12825
12934
  main4 as preview,
12826
12935
  main6 as syncSecrets
12827
12936
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAE5B;;;;;GAKG;AAEH,qFAAqF;AACrF,OAAO,EACL,SAAS,EACT,aAAa,EACb,MAAM,EACN,IAAI,EACJ,GAAG,EACH,KAAK,EACL,OAAO,EACP,GAAG,EACH,QAAQ,EACR,WAAW,EACX,MAAM,GACP,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAE5B;;;;;GAKG;AAEH,qFAAqF;AACrF,OAAO,EACL,SAAS,EACT,aAAa,EACb,MAAM,EACN,GAAG,EACH,KAAK,EACL,OAAO,EACP,GAAG,EACH,QAAQ,EACR,WAAW,EACX,MAAM,GACP,MAAM,mBAAmB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@donotdev/cli",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "Command-line interface for DoNotDev Framework",
5
5
  "type": "module",
6
6
  "private": false,
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@clack/prompts": "^0.11.0",
37
- "commander": "^14.0.2",
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
- // url: 'https://yourapp.com', // Uncomment for production
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: {