@navneet_25/tempjs 2.0.0 → 3.0.0

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 (90) hide show
  1. package/README.md +66 -18
  2. package/cli/brand-manager.js +183 -96
  3. package/cli/config.js +9 -1
  4. package/cli/doctor.js +393 -0
  5. package/cli/fetch-core-modules.js +130 -0
  6. package/cli/index.js +141 -10
  7. package/cli/info.js +28 -1
  8. package/cli/init-options.js +209 -0
  9. package/cli/module-manager.js +168 -0
  10. package/cli/parse-args.js +21 -0
  11. package/cli/update.js +15 -0
  12. package/cli/version-manager.js +518 -0
  13. package/package.json +27 -4
  14. package/packages/core/modules/README.md +72 -0
  15. package/packages/core/modules/blog-compose/app/admin/components/blog-compose-form.ts +44 -0
  16. package/packages/core/modules/blog-compose/app/admin/hooks/useBlogPosts.ts +103 -0
  17. package/packages/core/modules/blog-compose/app/admin/panels/BlogComposePanel.tsx +388 -0
  18. package/packages/core/modules/blog-compose/app/api/blog-posts/[id]/route.ts +29 -0
  19. package/packages/core/modules/blog-compose/app/api/blog-posts/route.ts +21 -0
  20. package/packages/core/modules/blog-compose/app/blog/[slug]/page.tsx +79 -0
  21. package/packages/core/modules/blog-compose/app/blog/layout.tsx +22 -0
  22. package/packages/core/modules/blog-compose/app/blog/page.tsx +102 -0
  23. package/packages/core/modules/blog-compose/app/components/BlogSection.tsx +74 -0
  24. package/packages/core/modules/blog-compose/app/components/BlogShell.tsx +36 -0
  25. package/packages/core/modules/blog-compose/app/components/BlogSidebar.tsx +112 -0
  26. package/packages/core/modules/blog-compose/lib/blog/compose/annotate-text.tsx +103 -0
  27. package/packages/core/modules/blog-compose/lib/blog/compose/blocks/media-blocks.tsx +97 -0
  28. package/packages/core/modules/blog-compose/lib/blog/compose/blocks/text-blocks.tsx +115 -0
  29. package/packages/core/modules/blog-compose/lib/blog/compose/index.ts +21 -0
  30. package/packages/core/modules/blog-compose/lib/blog/compose/interpreter.tsx +55 -0
  31. package/packages/core/modules/blog-compose/lib/blog/compose/registry.ts +276 -0
  32. package/packages/core/modules/blog-compose/lib/blog/compose/types.ts +128 -0
  33. package/packages/core/modules/blog-compose/lib/blog/register-sitemap.ts +18 -0
  34. package/packages/core/modules/blog-compose/lib/controllers/BlogComposeController.ts +2 -0
  35. package/packages/core/modules/blog-compose/lib/features/blog-compose/blog-compose.controller.ts +78 -0
  36. package/packages/core/modules/blog-compose/lib/features/blog-compose/blog-compose.repository.ts +78 -0
  37. package/packages/core/modules/blog-compose/lib/features/blog-compose/blog-compose.service.ts +72 -0
  38. package/packages/core/modules/blog-compose/lib/features/blog-compose/blog-compose.types.ts +20 -0
  39. package/packages/core/modules/blog-compose/lib/features/blog-compose/index.ts +7 -0
  40. package/packages/core/modules/blog-compose/prisma/blog-compose.prisma +13 -0
  41. package/packages/core/modules/enquiry-modal/app/components/EnquiryModal.tsx +308 -0
  42. package/packages/core/modules/footer/app/components/Footer.tsx +127 -0
  43. package/packages/core/modules/gallery/app/admin/components/GalleryFormModal.tsx +132 -0
  44. package/packages/core/modules/gallery/app/admin/components/GalleryList.tsx +142 -0
  45. package/packages/core/modules/gallery/app/admin/hooks/useGallery.ts +72 -0
  46. package/packages/core/modules/gallery/app/api/gallery/[id]/route.ts +29 -0
  47. package/packages/core/modules/gallery/app/api/gallery/route.ts +22 -0
  48. package/packages/core/modules/gallery/app/components/GallerySection.tsx +92 -0
  49. package/packages/core/modules/gallery/app/gallery/layout.tsx +12 -0
  50. package/packages/core/modules/gallery/app/gallery/page.tsx +179 -0
  51. package/packages/core/modules/gallery/lib/controllers/GalleryController.ts +2 -0
  52. package/packages/core/modules/gallery/lib/features/gallery/gallery.controller.ts +78 -0
  53. package/packages/core/modules/gallery/lib/features/gallery/gallery.repository.ts +55 -0
  54. package/packages/core/modules/gallery/lib/features/gallery/gallery.service.ts +43 -0
  55. package/packages/core/modules/gallery/lib/features/gallery/gallery.types.ts +17 -0
  56. package/packages/core/modules/gallery/lib/features/gallery/index.ts +4 -0
  57. package/packages/core/modules/gallery/prisma/gallery.prisma +11 -0
  58. package/packages/core/modules/hero-simple/app/components/Hero.tsx +160 -0
  59. package/packages/core/modules/legal-pages/app/components/LegalDocumentPage.tsx +61 -0
  60. package/packages/core/modules/legal-pages/app/privacy-policy/page.tsx +19 -0
  61. package/packages/core/modules/legal-pages/app/terms-and-conditions/page.tsx +19 -0
  62. package/packages/core/modules/reviews/app/admin/components/ReviewFormModal.tsx +110 -0
  63. package/packages/core/modules/reviews/app/admin/components/ReviewsList.tsx +116 -0
  64. package/packages/core/modules/reviews/app/admin/hooks/useReviews.ts +70 -0
  65. package/packages/core/modules/reviews/app/api/reviews/[id]/route.ts +29 -0
  66. package/packages/core/modules/reviews/app/api/reviews/route.ts +17 -0
  67. package/packages/core/modules/reviews/app/components/ReviewsSection.tsx +100 -0
  68. package/packages/core/modules/reviews/lib/controllers/ReviewController.ts +2 -0
  69. package/packages/core/modules/reviews/lib/features/reviews/index.ts +3 -0
  70. package/packages/core/modules/reviews/lib/features/reviews/review.controller.ts +64 -0
  71. package/packages/core/modules/reviews/lib/features/reviews/review.repository.ts +50 -0
  72. package/packages/core/modules/reviews/lib/features/reviews/review.service.ts +38 -0
  73. package/packages/core/modules/reviews/prisma/reviews.prisma +9 -0
  74. package/packages/core/modules/seo/app/components/SiteJsonLd.tsx +16 -0
  75. package/packages/core/modules/seo/app/robots.ts +30 -0
  76. package/packages/core/modules/seo/app/sitemap.ts +9 -0
  77. package/packages/core/modules/seo/lib/seo/index.ts +5 -0
  78. package/packages/core/modules/seo/lib/seo/json-ld.ts +128 -0
  79. package/packages/core/modules/seo/lib/seo/metadata.ts +65 -0
  80. package/packages/core/modules/seo/lib/seo/sitemap.ts +82 -0
  81. package/packages/core/modules/seo/lib/seo/types.ts +30 -0
  82. package/packages/core/modules/seo/lib/seo/urls.ts +27 -0
  83. package/packages/core/modules/theme-modes/app/components/ThemeModeInit.tsx +24 -0
  84. package/packages/core/modules/theme-modes/app/components/ThemeModeToggle.tsx +58 -0
  85. package/packages/core/modules.json +140 -0
  86. package/packages/core/prisma/schema.prisma +33 -0
  87. package/scripts/module-installer-core.mjs +657 -0
  88. package/scripts/template-modules-admin.mjs +366 -0
  89. package/scripts/template-modules-home.mjs +120 -0
  90. package/templates.json +163 -8
package/README.md CHANGED
@@ -17,6 +17,8 @@ git add .
17
17
  git commit -m "Initial project"
18
18
  ```
19
19
 
20
+ **Documentation site:** [`docsite/`](docsite/) — JSON-driven docs for developers and maintainers. Run `pnpm docs:dev` from repo root.
21
+
20
22
  ## Available templates
21
23
 
22
24
  | ID | Name | Description |
@@ -202,6 +204,15 @@ tempjs info hotel # shows full version in details
202
204
 
203
205
  When maintainers bump `"version"` in `templates.json` and push, clients can pull fixes safely.
204
206
 
207
+ **Two version numbers (don’t confuse them):**
208
+
209
+ | Version | Where | Meaning |
210
+ |---------|--------|---------|
211
+ | **CLI** (`@navneet_25/tempjs`) | npm / `package.json` in templates repo | The `tempjs` tool itself |
212
+ | **Template** (`hotel`, `real-estate`) | `templates.json` → your `.tempjs.json` | The website template snapshot |
213
+
214
+ Maintainers: see **[VERSIONING.md](./VERSIONING.md)** for `tempjs version check` / `tempjs version inc`.
215
+
205
216
  ---
206
217
 
207
218
  ### Update an existing project (`tempjs update`)
@@ -462,9 +473,9 @@ npm install -g @navneet_25/tempjs
462
473
  npm install -g @navneet_25/tempjs@latest
463
474
  ```
464
475
 
465
- Current version: **1.4.0** (update, versioning, fetch progress, info, non-interactive flags).
476
+ Current version: **2.1.0** (doctor, version tracking, copy-once core starter kit, optional sync propagate, update merge).
466
477
 
467
- See also: [guide.md](./guide.md) for CLI architecture and update algorithm details.
478
+ See also: [guide.md](./guide.md) for CLI architecture, [VERSIONING.md](./VERSIONING.md) for release workflow.
468
479
 
469
480
  Verify:
470
481
 
@@ -485,9 +496,42 @@ tempjs theme --theme theme3 --yes
485
496
  tempjs brand --yes --name "Mi Plaza" --base-url "https://miplaza.com"
486
497
  tempjs init-db --yes --db-host localhost --db-name my_db --skip-db-push
487
498
  tempjs real-estate --force # overwrite existing files
499
+ tempjs doctor # readiness check (generated projects)
500
+ tempjs add-module list # optional core modules available
501
+ tempjs add-module seo,gallery,reviews # add modules to existing project
488
502
  tempjs --help # show help
489
503
  ```
490
504
 
505
+ ### Optional core modules (client projects)
506
+
507
+ After `tempjs hotel`, add cross-vertical features without re-copying the template:
508
+
509
+ ```bash
510
+ tempjs add-module list
511
+ tempjs add-module seo,gallery,reviews
512
+ pnpm prisma db push && pnpm dev
513
+ ```
514
+
515
+ Modules: `enquiry-modal`, `footer`, `hero-simple`, `seo`, `gallery`, `reviews`, `legal-pages`. See `packages/core/modules.json` and docsite → Developers → Optional modules.
516
+
517
+ Shipped **hotel** and **real-estate** templates already include vertical features and adopted core modules where they overlapped bespoke code.
518
+
519
+ ### Maintainer commands (monorepo root)
520
+
521
+ ```bash
522
+ pnpm new-template bakery --name "Bakery" --modules enquiry-modal,footer,hero-simple,seo
523
+ pnpm template:add-module hotel gallery,reviews
524
+ pnpm template:assemble hotel # vertical modules → template root
525
+ pnpm template:extract-modules hotel # template root → modules/
526
+ pnpm sync-templates
527
+ pnpm dev:hotel
528
+ tempjs version check # unreleased CLI/template changes?
529
+ tempjs version inc patch cli
530
+ tempjs version inc minor hotel
531
+ ```
532
+
533
+ See [VERSIONING.md](./VERSIONING.md).
534
+
491
535
  ### Options (summary)
492
536
 
493
537
  | Option | Description |
@@ -613,36 +657,40 @@ The archive contains the whole templates repo on the wire, but only the requeste
613
657
 
614
658
  ```
615
659
  templates/ # this repository root
616
- ├── ARCHITECTURE.md # core + overlay design
660
+ ├── ARCHITECTURE.md # copy-once core + standalone templates
661
+ ├── MAINTAINERS.md # maintainer handbook
662
+ ├── ROADMAP.md # planned improvements
617
663
  ├── packages/
618
- │ └── core/ # shared source (synced into templates)
664
+ │ └── core/ # starter kit (copied once via new-template)
619
665
  ├── templates/
620
- │ ├── overlays/ # template-specific source only
621
- │ │ ├── hotel-website-template/
622
- │ │ └── real-estate-website-template/
623
- │ ├── hotel-website-template/ # merged output (what tempjs copies)
666
+ │ ├── hotel-website-template/ # full hotel project (what tempjs copies)
624
667
  │ └── real-estate-website-template/
625
668
  ├── scripts/
626
- └── sync-templates.mjs
627
- ├── package.json # CLI package + sync scripts
669
+ ├── new-template.mjs
670
+ │ └── sync-templates.mjs # optional: propagate core fixes
671
+ ├── package.json # CLI package + maintainer scripts
628
672
  ├── templates.json
629
673
  ├── cli/
630
674
  │ └── ...
631
675
  ```
632
676
 
633
- ## Shared core package (Option 1 — implemented)
677
+ ## Shared core starter kit (copy-once)
634
678
 
635
- This repo uses **pre-merge sync**: shared code lives in `packages/core/`, template-specific code in `templates/overlays/`, and `pnpm sync-templates` produces the full template folders that `tempjs` copies to users.
679
+ `packages/core/` is **generic boilerplate** copied once when you run `pnpm new-template`. After that, each template under `templates/<name>/` is a **standalone** Next.js project — edit it directly. Use `pnpm sync-templates` only when you intentionally propagate a fix from core into existing templates.
636
680
 
637
- **Full design doc:** [ARCHITECTURE.md](./ARCHITECTURE.md)
681
+ **Full design doc:** [ARCHITECTURE.md](./ARCHITECTURE.md) · **Maintainer guide:** [MAINTAINERS.md](./MAINTAINERS.md)
638
682
 
639
683
  ### Quick maintainer commands
640
684
 
641
685
  ```bash
642
- # After editing packages/core or templates/overlays/*
643
- pnpm sync-templates
686
+ # Create a new template (copies core + scaffold once)
687
+ pnpm new-template bakery bakery-website-template --name "Bakery Website"
644
688
 
645
- # CI / pre-PR check
689
+ # Daily dev (no sync)
690
+ pnpm dev:hotel
691
+
692
+ # Optional: after fixing packages/core, propagate to existing templates
693
+ pnpm sync-templates
646
694
  pnpm sync-templates:check
647
695
  ```
648
696
 
@@ -653,7 +701,7 @@ A normal Next.js project with organized `lib/`:
653
701
  - `lib/database/` — Prisma client
654
702
  - `lib/features/leads/` — shared lead module
655
703
  - `lib/storage/` — FTP uploads
656
- - `lib/features/<domain>/` — template-specific modules (rooms, projects, …) in overlay
704
+ - `lib/features/<domain>/` — template-specific modules (rooms, projects, …)
657
705
  - `app/` — routes and UI
658
706
 
659
707
  All source is on disk; no submodule, no `@tempjs/core` npm dependency in client projects.
@@ -662,7 +710,7 @@ All source is on disk; no submodule, no `@tempjs/core` npm dependency in client
662
710
 
663
711
  | Approach | User gets full source? | Maintained in this repo |
664
712
  |----------|------------------------|-------------------------|
665
- | **Option 1 — pre-merge sync** (current) | Yes | `packages/core` + overlays |
713
+ | **Copy-once core** (current) | Yes | `packages/core` starter + `templates/<name>/` |
666
714
  | CLI merge at copy time | Yes | `packages/core` only in git |
667
715
  | npm `@tempjs/core` package | Partial (core in node_modules) | Published package |
668
716
 
@@ -2,6 +2,12 @@ import { createInterface } from "node:readline/promises";
2
2
  import { stdin as input, stdout as output } from "node:process";
3
3
  import { readFile, writeFile } from "node:fs/promises";
4
4
  import { findSiteTs } from "./theme-manager.js";
5
+ import {
6
+ INIT_FIELD_REGISTRY,
7
+ getTemplateBrandFields,
8
+ pickBrandOptionsForTemplate,
9
+ } from "./init-options.js";
10
+ import { loadManifest } from "./config.js";
5
11
 
6
12
  /**
7
13
  * @typedef {{
@@ -13,7 +19,14 @@ import { findSiteTs } from "./theme-manager.js";
13
19
  * phoneDisplay?: string,
14
20
  * countryCode?: string,
15
21
  * email?: string,
16
- * address?: string
22
+ * address?: string,
23
+ * tagline?: string,
24
+ * developerName?: string,
25
+ * channelPartner?: string,
26
+ * agentRera?: string,
27
+ * templeDistance?: string,
28
+ * company?: string,
29
+ * location?: string
17
30
  * }} BrandOptions
18
31
  */
19
32
 
@@ -21,15 +34,26 @@ import { findSiteTs } from "./theme-manager.js";
21
34
  * @param {string} siteContent
22
35
  */
23
36
  function parseCurrentBrandValues(siteContent) {
37
+ const match = (pattern, fallback = "") => {
38
+ const result = siteContent.match(pattern);
39
+ return result?.[1] ?? fallback;
40
+ };
41
+
24
42
  return {
25
- brandName: (siteContent.match(/name:\s*"([^"]+)"/) || [])[1] || "Chanakya Resort",
26
- shortName: (siteContent.match(/shortName:\s*"([^"]+)"/) || [])[1] || "Chanakya",
27
- baseUrl: (siteContent.match(/baseUrl:\s*"([^"]+)"/) || [])[1] || "https://chanakyaresort.com",
28
- phone: (siteContent.match(/phone:\s*"([^"]+)"/) || [])[1] || "9876543210",
29
- phoneDisplay: (siteContent.match(/phoneDisplay:\s*"([^"]+)"/) || [])[1] || "+91 98765 43210",
30
- countryCode: (siteContent.match(/countryCode:\s*"([^"]+)"/) || [])[1] || "91",
31
- email: (siteContent.match(/email:\s*"([^"]+)"/) || [])[1] || "reservations@chanakyaresort.com",
32
- address: (siteContent.match(/full:\s*"([^"]+)"/) || [])[1] || "Lonavala, Maharashtra, India",
43
+ brandName: match(/name:\s*"([^"]+)"/, "Demo Client Site"),
44
+ shortName: match(/shortName:\s*"([^"]+)"/, "Demo"),
45
+ baseUrl: match(/baseUrl:\s*"([^"]+)"/, "https://example.com"),
46
+ phone: match(/phone:\s*"([^"]+)"/, "9876543210"),
47
+ phoneDisplay: match(/phoneDisplay:\s*"([^"]+)"/, "+91 98765 43210"),
48
+ countryCode: match(/countryCode:\s*"([^"]+)"/, "91"),
49
+ email: match(/email:\s*"([^"]+)"/, "hello@example.com"),
50
+ address: match(/full:\s*"([^"]+)"/, "Lonavala, Maharashtra, India"),
51
+ tagline: match(/tagline:\s*"([^"]+)"/, ""),
52
+ developerName: match(/developerName:\s*"([^"]+)"/, ""),
53
+ channelPartner: match(/channelPartner:\s*"([^"]+)"/, ""),
54
+ agentRera: match(/agentRera:\s*"([^"]*)"/, ""),
55
+ templeDistance: match(/templeDistance:\s*"([^"]*)"/, ""),
56
+ locality: match(/locality:\s*"([^"]+)"/, "Lonavala"),
33
57
  };
34
58
  }
35
59
 
@@ -47,72 +71,103 @@ function deriveWwwHost(baseUrl) {
47
71
  }
48
72
  }
49
73
 
74
+ /**
75
+ * @param {string} value
76
+ */
77
+ function escapeForTsString(value) {
78
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
79
+ }
80
+
50
81
  /**
51
82
  * @param {string} siteContent
52
- * @param {{
53
- * brandName: string,
54
- * shortName: string,
55
- * baseUrl: string,
56
- * phone: string,
57
- * phoneDisplay: string,
58
- * countryCode: string,
59
- * email: string,
60
- * address: string
61
- * }} values
83
+ * @param {ReturnType<typeof parseCurrentBrandValues>} current
84
+ * @param {BrandOptions} options
62
85
  */
63
- function applyBrandToSiteContent(siteContent, values) {
64
- const {
86
+ function buildBrandValues(current, options) {
87
+ const brandName = options.name?.trim() || current.brandName;
88
+ const developerName =
89
+ options.developerName?.trim() || options.company?.trim() || current.developerName || brandName;
90
+
91
+ let address = options.address?.trim() || current.address;
92
+ let locality = current.locality;
93
+
94
+ if (options.location?.trim() && !options.address?.trim()) {
95
+ locality = options.location.trim();
96
+ const parts = address.split(",");
97
+ if (parts.length >= 2) {
98
+ address = `${locality}, ${parts.slice(1).join(",").trim()}`;
99
+ } else {
100
+ address = locality;
101
+ }
102
+ }
103
+
104
+ const addressParts = address.split(",");
105
+ if (addressParts.length >= 2) {
106
+ locality = addressParts[0].trim();
107
+ }
108
+
109
+ return {
65
110
  brandName,
66
- shortName,
67
- baseUrl,
68
- phone,
69
- phoneDisplay,
70
- countryCode,
71
- email,
111
+ shortName: options.shortName?.trim() || current.shortName,
112
+ baseUrl: options.baseUrl?.trim() || current.baseUrl,
113
+ phone: options.phone?.trim() || current.phone,
114
+ phoneDisplay: options.phoneDisplay?.trim() || current.phoneDisplay,
115
+ countryCode: options.countryCode?.trim() || current.countryCode,
116
+ email: options.email?.trim() || current.email,
72
117
  address,
73
- } = values;
118
+ locality,
119
+ region: addressParts.length >= 2 ? addressParts[1].trim() : "MH",
120
+ tagline: options.tagline?.trim() || current.tagline,
121
+ developerName,
122
+ channelPartner: options.channelPartner?.trim() || current.channelPartner || `${brandName} Partner`,
123
+ agentRera: options.agentRera?.trim() ?? current.agentRera,
124
+ templeDistance: options.templeDistance?.trim() ?? current.templeDistance,
125
+ };
126
+ }
74
127
 
75
- const wwwHost = deriveWwwHost(baseUrl);
128
+ /**
129
+ * @param {string} siteContent
130
+ * @param {ReturnType<typeof buildBrandValues>} values
131
+ */
132
+ function applyBrandToSiteContent(siteContent, values) {
133
+ const wwwHost = deriveWwwHost(values.baseUrl);
76
134
 
77
135
  const newBrandBlock = `brand: {
78
- name: "${brandName}",
79
- shortName: "${shortName}",
80
- tagline: "Where Nature Meets Refined Comfort",
81
- developerName: "${brandName}",
82
- channelPartner: "${brandName} Partner",
83
- copyright: "${brandName}. All Rights Reserved.",
84
- managedBy: "Managed by ${brandName}.",
136
+ name: "${escapeForTsString(values.brandName)}",
137
+ shortName: "${escapeForTsString(values.shortName)}",
138
+ tagline: "${escapeForTsString(values.tagline)}",
139
+ developerName: "${escapeForTsString(values.developerName)}",
140
+ channelPartner: "${escapeForTsString(values.channelPartner)}",
141
+ copyright: "${escapeForTsString(values.brandName)}. All Rights Reserved.",
142
+ managedBy: "Managed by ${escapeForTsString(values.developerName)}.",
85
143
  }`;
86
144
 
87
145
  const newDomainBlock = `domain: {
88
- baseUrl: "${baseUrl}",
89
- wwwHost: "${wwwHost}",
146
+ baseUrl: "${escapeForTsString(values.baseUrl)}",
147
+ wwwHost: "${escapeForTsString(wwwHost)}",
90
148
  }`;
91
149
 
92
- let locality = "Lonavala";
93
- let region = "MH";
94
- const addressParts = address.split(",");
95
- if (addressParts.length >= 2) {
96
- locality = addressParts[0].trim();
97
- region = addressParts[1].trim();
98
- }
150
+ const templeLine = values.templeDistance
151
+ ? `\n templeDistance: "${escapeForTsString(values.templeDistance)}",`
152
+ : "";
99
153
 
100
154
  const newContactBlock = `contact: {
101
- phone: "${phone}",
102
- phoneDisplay: "${phoneDisplay}",
103
- countryCode: "${countryCode}",
104
- email: "${email}",
155
+ phone: "${escapeForTsString(values.phone)}",
156
+ phoneDisplay: "${escapeForTsString(values.phoneDisplay)}",
157
+ countryCode: "${escapeForTsString(values.countryCode)}",
158
+ email: "${escapeForTsString(values.email)}",
105
159
  address: {
106
- locality: "${locality}",
107
- region: "${region}",
160
+ locality: "${escapeForTsString(values.locality)}",
161
+ region: "${escapeForTsString(values.region)}",
108
162
  country: "IN",
109
- full: "${address}",
110
- },
163
+ full: "${escapeForTsString(values.address)}",
164
+ },${templeLine}
111
165
  }`;
112
166
 
113
167
  const brandRegex = /brand:\s*\{[\s\S]*?\n\s*\},/;
114
168
  const domainRegex = /domain:\s*\{[\s\S]*?\n\s*\},/;
115
169
  const contactRegex = /contact:\s*\{[\s\S]*?address:\s*\{[\s\S]*?\}[\s\S]*?\n\s*\},/;
170
+ const agentReraRegex = /agentRera:\s*"[^"]*"/;
116
171
 
117
172
  if (brandRegex.test(siteContent)) {
118
173
  siteContent = siteContent.replace(brandRegex, newBrandBlock + ",");
@@ -123,15 +178,65 @@ function applyBrandToSiteContent(siteContent, values) {
123
178
  if (contactRegex.test(siteContent)) {
124
179
  siteContent = siteContent.replace(contactRegex, newContactBlock + ",");
125
180
  }
181
+ if (agentReraRegex.test(siteContent)) {
182
+ siteContent = siteContent.replace(
183
+ agentReraRegex,
184
+ `agentRera: "${escapeForTsString(values.agentRera)}"`
185
+ );
186
+ }
126
187
 
127
188
  return siteContent;
128
189
  }
129
190
 
191
+ /**
192
+ * @param {ReturnType<typeof parseCurrentBrandValues>} current
193
+ * @param {string} fieldKey
194
+ */
195
+ function currentValueForField(current, fieldKey) {
196
+ const def = INIT_FIELD_REGISTRY[fieldKey];
197
+ if (!def) return "";
198
+
199
+ switch (fieldKey) {
200
+ case "name":
201
+ return current.brandName;
202
+ case "shortName":
203
+ return current.shortName;
204
+ case "baseUrl":
205
+ return current.baseUrl;
206
+ case "phone":
207
+ return current.phone;
208
+ case "phoneDisplay":
209
+ return current.phoneDisplay;
210
+ case "countryCode":
211
+ return current.countryCode;
212
+ case "email":
213
+ return current.email;
214
+ case "address":
215
+ return current.address;
216
+ case "tagline":
217
+ return current.tagline;
218
+ case "developerName":
219
+ case "company":
220
+ return current.developerName;
221
+ case "channelPartner":
222
+ return current.channelPartner;
223
+ case "agentRera":
224
+ return current.agentRera;
225
+ case "templeDistance":
226
+ return current.templeDistance;
227
+ case "location":
228
+ return current.locality;
229
+ default:
230
+ return "";
231
+ }
232
+ }
233
+
130
234
  /**
131
235
  * @param {string} targetDir
132
236
  * @param {BrandOptions} options
237
+ * @param {string} [templateId]
133
238
  */
134
- export async function applyBrand(targetDir, options = {}) {
239
+ export async function applyBrand(targetDir, options = {}, templateId) {
135
240
  const siteTsPath = await findSiteTs(targetDir);
136
241
  if (!siteTsPath) {
137
242
  throw new Error(
@@ -141,18 +246,7 @@ export async function applyBrand(targetDir, options = {}) {
141
246
 
142
247
  let siteContent = await readFile(siteTsPath, "utf8");
143
248
  const current = parseCurrentBrandValues(siteContent);
144
-
145
- const values = {
146
- brandName: options.name?.trim() || current.brandName,
147
- shortName: options.shortName?.trim() || current.shortName,
148
- baseUrl: options.baseUrl?.trim() || current.baseUrl,
149
- phone: options.phone?.trim() || current.phone,
150
- phoneDisplay: options.phoneDisplay?.trim() || current.phoneDisplay,
151
- countryCode: options.countryCode?.trim() || current.countryCode,
152
- email: options.email?.trim() || current.email,
153
- address: options.address?.trim() || current.address,
154
- };
155
-
249
+ const values = buildBrandValues(current, options);
156
250
  siteContent = applyBrandToSiteContent(siteContent, values);
157
251
  await writeFile(siteTsPath, siteContent, "utf8");
158
252
  console.log("Successfully updated brand and contact configurations in constants/site.ts.");
@@ -161,10 +255,18 @@ export async function applyBrand(targetDir, options = {}) {
161
255
  /**
162
256
  * @param {string} targetDir
163
257
  * @param {BrandOptions} options
258
+ * @param {string} [templateId]
164
259
  */
165
- export async function promptAndApplyBrand(targetDir, options = {}) {
260
+ export async function promptAndApplyBrand(targetDir, options = {}, templateId) {
261
+ const manifest = loadManifest();
262
+ const entry = templateId ? manifest.templates[templateId] : undefined;
263
+ const fieldKeys = entry?.init?.brandFields ?? Object.keys(INIT_FIELD_REGISTRY);
264
+
166
265
  if (options.yes) {
167
- await applyBrand(targetDir, options);
266
+ const picked = entry
267
+ ? pickBrandOptionsForTemplate(templateId, entry, options)
268
+ : options;
269
+ await applyBrand(targetDir, picked, templateId);
168
270
  return;
169
271
  }
170
272
 
@@ -178,40 +280,25 @@ export async function promptAndApplyBrand(targetDir, options = {}) {
178
280
 
179
281
  let siteContent = await readFile(siteTsPath, "utf8");
180
282
  const current = parseCurrentBrandValues(siteContent);
283
+ const collected = { ...options };
181
284
 
182
285
  const rl = createInterface({ input, output });
183
286
  try {
184
287
  console.log("\n--- Configure Brand & Contact Details ---");
185
288
 
186
- const brandName =
187
- (await rl.question(`Brand Name [${current.brandName}]: `)).trim() || current.brandName;
188
- const shortName =
189
- (await rl.question(`Short/Display Name [${current.shortName}]: `)).trim() || current.shortName;
190
- const baseUrl =
191
- (await rl.question(`Base URL [${current.baseUrl}]: `)).trim() || current.baseUrl;
192
- const phone =
193
- (await rl.question(`Contact Phone Number [${current.phone}]: `)).trim() || current.phone;
194
- const phoneDisplay =
195
- (await rl.question(`Display Phone Number [${current.phoneDisplay}]: `)).trim() ||
196
- current.phoneDisplay;
197
- const countryCode =
198
- (await rl.question(`Country Code [${current.countryCode}]: `)).trim() || current.countryCode;
199
- const email =
200
- (await rl.question(`Contact Email [${current.email}]: `)).trim() || current.email;
201
- const address =
202
- (await rl.question(`Full Address [${current.address}]: `)).trim() || current.address;
203
-
204
- siteContent = applyBrandToSiteContent(siteContent, {
205
- brandName,
206
- shortName,
207
- baseUrl,
208
- phone,
209
- phoneDisplay,
210
- countryCode,
211
- email,
212
- address,
213
- });
289
+ for (const fieldKey of fieldKeys) {
290
+ const field = INIT_FIELD_REGISTRY[fieldKey];
291
+ if (!field) continue;
292
+
293
+ const currentVal = currentValueForField(current, fieldKey);
294
+ const answer = (await rl.question(`${field.prompt} [${currentVal}]: `)).trim();
295
+ if (answer) {
296
+ collected[field.cliKey] = answer;
297
+ }
298
+ }
214
299
 
300
+ const values = buildBrandValues(current, collected);
301
+ siteContent = applyBrandToSiteContent(siteContent, values);
215
302
  await writeFile(siteTsPath, siteContent, "utf8");
216
303
  console.log("Successfully updated brand and contact configurations in constants/site.ts.");
217
304
  } finally {
package/cli/config.js CHANGED
@@ -17,7 +17,15 @@ const packageRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
17
17
  * docker?: boolean,
18
18
  * tags?: string[],
19
19
  * features?: string[],
20
- * docs?: string
20
+ * docs?: string,
21
+ * coreModules?: string[],
22
+ * templateModules?: string[],
23
+ * init?: {
24
+ * flagGroups?: string[],
25
+ * brandFields?: string[],
26
+ * skipBrand?: boolean,
27
+ * skipDatabase?: boolean
28
+ * }
21
29
  * }} TemplateEntry */
22
30
  /** @typedef {{ repository: RepositoryConfig, templates: Record<string, TemplateEntry> }} Manifest */
23
31