@navneet_25/tempjs 1.1.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.
- package/README.md +800 -35
- package/cli/brand-manager.js +277 -78
- package/cli/config.js +22 -1
- package/cli/copy.js +4 -18
- package/cli/db-setup.js +170 -49
- package/cli/doctor.js +393 -0
- package/cli/fetch-core-modules.js +130 -0
- package/cli/fetch.js +47 -29
- package/cli/file-tree.js +113 -0
- package/cli/fs-ignore.js +60 -0
- package/cli/index.js +274 -112
- package/cli/info.js +115 -0
- package/cli/init-options.js +209 -0
- package/cli/module-manager.js +168 -0
- package/cli/parse-args.js +177 -0
- package/cli/progress.js +33 -0
- package/cli/project-stamp.js +69 -0
- package/cli/prompt.js +19 -0
- package/cli/template-resolver.js +48 -0
- package/cli/theme-manager.js +28 -2
- package/cli/update.js +227 -0
- package/cli/version-manager.js +518 -0
- package/package.json +31 -4
- package/packages/core/modules/README.md +72 -0
- package/packages/core/modules/blog-compose/app/admin/components/blog-compose-form.ts +44 -0
- package/packages/core/modules/blog-compose/app/admin/hooks/useBlogPosts.ts +103 -0
- package/packages/core/modules/blog-compose/app/admin/panels/BlogComposePanel.tsx +388 -0
- package/packages/core/modules/blog-compose/app/api/blog-posts/[id]/route.ts +29 -0
- package/packages/core/modules/blog-compose/app/api/blog-posts/route.ts +21 -0
- package/packages/core/modules/blog-compose/app/blog/[slug]/page.tsx +79 -0
- package/packages/core/modules/blog-compose/app/blog/layout.tsx +22 -0
- package/packages/core/modules/blog-compose/app/blog/page.tsx +102 -0
- package/packages/core/modules/blog-compose/app/components/BlogSection.tsx +74 -0
- package/packages/core/modules/blog-compose/app/components/BlogShell.tsx +36 -0
- package/packages/core/modules/blog-compose/app/components/BlogSidebar.tsx +112 -0
- package/packages/core/modules/blog-compose/lib/blog/compose/annotate-text.tsx +103 -0
- package/packages/core/modules/blog-compose/lib/blog/compose/blocks/media-blocks.tsx +97 -0
- package/packages/core/modules/blog-compose/lib/blog/compose/blocks/text-blocks.tsx +115 -0
- package/packages/core/modules/blog-compose/lib/blog/compose/index.ts +21 -0
- package/packages/core/modules/blog-compose/lib/blog/compose/interpreter.tsx +55 -0
- package/packages/core/modules/blog-compose/lib/blog/compose/registry.ts +276 -0
- package/packages/core/modules/blog-compose/lib/blog/compose/types.ts +128 -0
- package/packages/core/modules/blog-compose/lib/blog/register-sitemap.ts +18 -0
- package/packages/core/modules/blog-compose/lib/controllers/BlogComposeController.ts +2 -0
- package/packages/core/modules/blog-compose/lib/features/blog-compose/blog-compose.controller.ts +78 -0
- package/packages/core/modules/blog-compose/lib/features/blog-compose/blog-compose.repository.ts +78 -0
- package/packages/core/modules/blog-compose/lib/features/blog-compose/blog-compose.service.ts +72 -0
- package/packages/core/modules/blog-compose/lib/features/blog-compose/blog-compose.types.ts +20 -0
- package/packages/core/modules/blog-compose/lib/features/blog-compose/index.ts +7 -0
- package/packages/core/modules/blog-compose/prisma/blog-compose.prisma +13 -0
- package/packages/core/modules/enquiry-modal/app/components/EnquiryModal.tsx +308 -0
- package/packages/core/modules/footer/app/components/Footer.tsx +127 -0
- package/packages/core/modules/gallery/app/admin/components/GalleryFormModal.tsx +132 -0
- package/packages/core/modules/gallery/app/admin/components/GalleryList.tsx +142 -0
- package/packages/core/modules/gallery/app/admin/hooks/useGallery.ts +72 -0
- package/packages/core/modules/gallery/app/api/gallery/[id]/route.ts +29 -0
- package/packages/core/modules/gallery/app/api/gallery/route.ts +22 -0
- package/packages/core/modules/gallery/app/components/GallerySection.tsx +92 -0
- package/packages/core/modules/gallery/app/gallery/layout.tsx +12 -0
- package/packages/core/modules/gallery/app/gallery/page.tsx +179 -0
- package/packages/core/modules/gallery/lib/controllers/GalleryController.ts +2 -0
- package/packages/core/modules/gallery/lib/features/gallery/gallery.controller.ts +78 -0
- package/packages/core/modules/gallery/lib/features/gallery/gallery.repository.ts +55 -0
- package/packages/core/modules/gallery/lib/features/gallery/gallery.service.ts +43 -0
- package/packages/core/modules/gallery/lib/features/gallery/gallery.types.ts +17 -0
- package/packages/core/modules/gallery/lib/features/gallery/index.ts +4 -0
- package/packages/core/modules/gallery/prisma/gallery.prisma +11 -0
- package/packages/core/modules/hero-simple/app/components/Hero.tsx +160 -0
- package/packages/core/modules/legal-pages/app/components/LegalDocumentPage.tsx +61 -0
- package/packages/core/modules/legal-pages/app/privacy-policy/page.tsx +19 -0
- package/packages/core/modules/legal-pages/app/terms-and-conditions/page.tsx +19 -0
- package/packages/core/modules/reviews/app/admin/components/ReviewFormModal.tsx +110 -0
- package/packages/core/modules/reviews/app/admin/components/ReviewsList.tsx +116 -0
- package/packages/core/modules/reviews/app/admin/hooks/useReviews.ts +70 -0
- package/packages/core/modules/reviews/app/api/reviews/[id]/route.ts +29 -0
- package/packages/core/modules/reviews/app/api/reviews/route.ts +17 -0
- package/packages/core/modules/reviews/app/components/ReviewsSection.tsx +100 -0
- package/packages/core/modules/reviews/lib/controllers/ReviewController.ts +2 -0
- package/packages/core/modules/reviews/lib/features/reviews/index.ts +3 -0
- package/packages/core/modules/reviews/lib/features/reviews/review.controller.ts +64 -0
- package/packages/core/modules/reviews/lib/features/reviews/review.repository.ts +50 -0
- package/packages/core/modules/reviews/lib/features/reviews/review.service.ts +38 -0
- package/packages/core/modules/reviews/prisma/reviews.prisma +9 -0
- package/packages/core/modules/seo/app/components/SiteJsonLd.tsx +16 -0
- package/packages/core/modules/seo/app/robots.ts +30 -0
- package/packages/core/modules/seo/app/sitemap.ts +9 -0
- package/packages/core/modules/seo/lib/seo/index.ts +5 -0
- package/packages/core/modules/seo/lib/seo/json-ld.ts +128 -0
- package/packages/core/modules/seo/lib/seo/metadata.ts +65 -0
- package/packages/core/modules/seo/lib/seo/sitemap.ts +82 -0
- package/packages/core/modules/seo/lib/seo/types.ts +30 -0
- package/packages/core/modules/seo/lib/seo/urls.ts +27 -0
- package/packages/core/modules/theme-modes/app/components/ThemeModeInit.tsx +24 -0
- package/packages/core/modules/theme-modes/app/components/ThemeModeToggle.tsx +58 -0
- package/packages/core/modules.json +140 -0
- package/packages/core/prisma/schema.prisma +33 -0
- package/scripts/module-installer-core.mjs +657 -0
- package/scripts/template-modules-admin.mjs +366 -0
- package/scripts/template-modules-home.mjs +120 -0
- package/templates.json +188 -3
package/cli/brand-manager.js
CHANGED
|
@@ -2,104 +2,303 @@ 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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {{
|
|
14
|
+
* yes?: boolean,
|
|
15
|
+
* name?: string,
|
|
16
|
+
* shortName?: string,
|
|
17
|
+
* baseUrl?: string,
|
|
18
|
+
* phone?: string,
|
|
19
|
+
* phoneDisplay?: string,
|
|
20
|
+
* countryCode?: string,
|
|
21
|
+
* email?: 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
|
|
30
|
+
* }} BrandOptions
|
|
31
|
+
*/
|
|
12
32
|
|
|
13
|
-
|
|
14
|
-
|
|
33
|
+
/**
|
|
34
|
+
* @param {string} siteContent
|
|
35
|
+
*/
|
|
36
|
+
function parseCurrentBrandValues(siteContent) {
|
|
37
|
+
const match = (pattern, fallback = "") => {
|
|
38
|
+
const result = siteContent.match(pattern);
|
|
39
|
+
return result?.[1] ?? fallback;
|
|
40
|
+
};
|
|
15
41
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
42
|
+
return {
|
|
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"),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
25
59
|
|
|
26
|
-
|
|
60
|
+
/**
|
|
61
|
+
* @param {string} baseUrl
|
|
62
|
+
*/
|
|
63
|
+
function deriveWwwHost(baseUrl) {
|
|
27
64
|
try {
|
|
28
|
-
|
|
65
|
+
const url = new URL(baseUrl);
|
|
66
|
+
return url.hostname.startsWith("www.") ? url.hostname : `www.${url.hostname}`;
|
|
67
|
+
} catch {
|
|
68
|
+
let wwwHost = baseUrl.replace(/^https?:\/\//, "");
|
|
69
|
+
if (!wwwHost.startsWith("www.")) wwwHost = `www.${wwwHost}`;
|
|
70
|
+
return wwwHost;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @param {string} value
|
|
76
|
+
*/
|
|
77
|
+
function escapeForTsString(value) {
|
|
78
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
79
|
+
}
|
|
29
80
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
81
|
+
/**
|
|
82
|
+
* @param {string} siteContent
|
|
83
|
+
* @param {ReturnType<typeof parseCurrentBrandValues>} current
|
|
84
|
+
* @param {BrandOptions} options
|
|
85
|
+
*/
|
|
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;
|
|
42
101
|
}
|
|
102
|
+
}
|
|
43
103
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
104
|
+
const addressParts = address.split(",");
|
|
105
|
+
if (addressParts.length >= 2) {
|
|
106
|
+
locality = addressParts[0].trim();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
brandName,
|
|
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,
|
|
117
|
+
address,
|
|
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
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* @param {string} siteContent
|
|
130
|
+
* @param {ReturnType<typeof buildBrandValues>} values
|
|
131
|
+
*/
|
|
132
|
+
function applyBrandToSiteContent(siteContent, values) {
|
|
133
|
+
const wwwHost = deriveWwwHost(values.baseUrl);
|
|
134
|
+
|
|
135
|
+
const newBrandBlock = `brand: {
|
|
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)}.",
|
|
59
143
|
}`;
|
|
60
144
|
|
|
61
|
-
|
|
62
|
-
baseUrl: "${baseUrl}",
|
|
63
|
-
wwwHost: "${wwwHost}",
|
|
145
|
+
const newDomainBlock = `domain: {
|
|
146
|
+
baseUrl: "${escapeForTsString(values.baseUrl)}",
|
|
147
|
+
wwwHost: "${escapeForTsString(wwwHost)}",
|
|
64
148
|
}`;
|
|
65
149
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const addressParts = address.split(",");
|
|
70
|
-
if (addressParts.length >= 2) {
|
|
71
|
-
locality = addressParts[0].trim();
|
|
72
|
-
region = addressParts[1].trim();
|
|
73
|
-
}
|
|
150
|
+
const templeLine = values.templeDistance
|
|
151
|
+
? `\n templeDistance: "${escapeForTsString(values.templeDistance)}",`
|
|
152
|
+
: "";
|
|
74
153
|
|
|
75
|
-
|
|
76
|
-
phone: "${phone}",
|
|
77
|
-
phoneDisplay: "${phoneDisplay}",
|
|
78
|
-
countryCode: "${countryCode}",
|
|
79
|
-
email: "${email}",
|
|
154
|
+
const newContactBlock = `contact: {
|
|
155
|
+
phone: "${escapeForTsString(values.phone)}",
|
|
156
|
+
phoneDisplay: "${escapeForTsString(values.phoneDisplay)}",
|
|
157
|
+
countryCode: "${escapeForTsString(values.countryCode)}",
|
|
158
|
+
email: "${escapeForTsString(values.email)}",
|
|
80
159
|
address: {
|
|
81
|
-
locality: "${locality}",
|
|
82
|
-
region: "${region}",
|
|
160
|
+
locality: "${escapeForTsString(values.locality)}",
|
|
161
|
+
region: "${escapeForTsString(values.region)}",
|
|
83
162
|
country: "IN",
|
|
84
|
-
full: "${address}",
|
|
85
|
-
}
|
|
163
|
+
full: "${escapeForTsString(values.address)}",
|
|
164
|
+
},${templeLine}
|
|
86
165
|
}`;
|
|
87
166
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
167
|
+
const brandRegex = /brand:\s*\{[\s\S]*?\n\s*\},/;
|
|
168
|
+
const domainRegex = /domain:\s*\{[\s\S]*?\n\s*\},/;
|
|
169
|
+
const contactRegex = /contact:\s*\{[\s\S]*?address:\s*\{[\s\S]*?\}[\s\S]*?\n\s*\},/;
|
|
170
|
+
const agentReraRegex = /agentRera:\s*"[^"]*"/;
|
|
92
171
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
172
|
+
if (brandRegex.test(siteContent)) {
|
|
173
|
+
siteContent = siteContent.replace(brandRegex, newBrandBlock + ",");
|
|
174
|
+
}
|
|
175
|
+
if (domainRegex.test(siteContent)) {
|
|
176
|
+
siteContent = siteContent.replace(domainRegex, newDomainBlock + ",");
|
|
177
|
+
}
|
|
178
|
+
if (contactRegex.test(siteContent)) {
|
|
179
|
+
siteContent = siteContent.replace(contactRegex, newContactBlock + ",");
|
|
180
|
+
}
|
|
181
|
+
if (agentReraRegex.test(siteContent)) {
|
|
182
|
+
siteContent = siteContent.replace(
|
|
183
|
+
agentReraRegex,
|
|
184
|
+
`agentRera: "${escapeForTsString(values.agentRera)}"`
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return siteContent;
|
|
189
|
+
}
|
|
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
|
+
|
|
234
|
+
/**
|
|
235
|
+
* @param {string} targetDir
|
|
236
|
+
* @param {BrandOptions} options
|
|
237
|
+
* @param {string} [templateId]
|
|
238
|
+
*/
|
|
239
|
+
export async function applyBrand(targetDir, options = {}, templateId) {
|
|
240
|
+
const siteTsPath = await findSiteTs(targetDir);
|
|
241
|
+
if (!siteTsPath) {
|
|
242
|
+
throw new Error(
|
|
243
|
+
"Could not locate constants/site.ts. Make sure you are inside an initialized tempjs template directory."
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
let siteContent = await readFile(siteTsPath, "utf8");
|
|
248
|
+
const current = parseCurrentBrandValues(siteContent);
|
|
249
|
+
const values = buildBrandValues(current, options);
|
|
250
|
+
siteContent = applyBrandToSiteContent(siteContent, values);
|
|
251
|
+
await writeFile(siteTsPath, siteContent, "utf8");
|
|
252
|
+
console.log("Successfully updated brand and contact configurations in constants/site.ts.");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* @param {string} targetDir
|
|
257
|
+
* @param {BrandOptions} options
|
|
258
|
+
* @param {string} [templateId]
|
|
259
|
+
*/
|
|
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
|
+
|
|
265
|
+
if (options.yes) {
|
|
266
|
+
const picked = entry
|
|
267
|
+
? pickBrandOptionsForTemplate(templateId, entry, options)
|
|
268
|
+
: options;
|
|
269
|
+
await applyBrand(targetDir, picked, templateId);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const siteTsPath = await findSiteTs(targetDir);
|
|
274
|
+
if (!siteTsPath) {
|
|
275
|
+
console.error(
|
|
276
|
+
"Error: Could not locate constants/site.ts. Make sure you are inside an initialized tempjs template directory."
|
|
277
|
+
);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
let siteContent = await readFile(siteTsPath, "utf8");
|
|
282
|
+
const current = parseCurrentBrandValues(siteContent);
|
|
283
|
+
const collected = { ...options };
|
|
284
|
+
|
|
285
|
+
const rl = createInterface({ input, output });
|
|
286
|
+
try {
|
|
287
|
+
console.log("\n--- Configure Brand & Contact Details ---");
|
|
288
|
+
|
|
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
|
+
}
|
|
101
298
|
}
|
|
102
299
|
|
|
300
|
+
const values = buildBrandValues(current, collected);
|
|
301
|
+
siteContent = applyBrandToSiteContent(siteContent, values);
|
|
103
302
|
await writeFile(siteTsPath, siteContent, "utf8");
|
|
104
303
|
console.log("Successfully updated brand and contact configurations in constants/site.ts.");
|
|
105
304
|
} finally {
|
package/cli/config.js
CHANGED
|
@@ -5,7 +5,28 @@ import { fileURLToPath } from "node:url";
|
|
|
5
5
|
const packageRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
6
6
|
|
|
7
7
|
/** @typedef {{ owner: string, repo: string, branch: string, templatesPath: string }} RepositoryConfig */
|
|
8
|
-
/** @typedef {{
|
|
8
|
+
/** @typedef {{
|
|
9
|
+
* directory: string,
|
|
10
|
+
* name: string,
|
|
11
|
+
* description: string,
|
|
12
|
+
* version?: string,
|
|
13
|
+
* stack?: string[],
|
|
14
|
+
* packageManager?: string,
|
|
15
|
+
* node?: string,
|
|
16
|
+
* setupTime?: string,
|
|
17
|
+
* docker?: boolean,
|
|
18
|
+
* tags?: string[],
|
|
19
|
+
* features?: 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
|
+
* }
|
|
29
|
+
* }} TemplateEntry */
|
|
9
30
|
/** @typedef {{ repository: RepositoryConfig, templates: Record<string, TemplateEntry> }} Manifest */
|
|
10
31
|
|
|
11
32
|
/**
|
package/cli/copy.js
CHANGED
|
@@ -9,23 +9,10 @@ import {
|
|
|
9
9
|
statSync,
|
|
10
10
|
} from "node:fs";
|
|
11
11
|
import { join } from "node:path";
|
|
12
|
+
import { NEVER_COPY_NAMES, shouldSkipFileName } from "./fs-ignore.js";
|
|
12
13
|
|
|
13
|
-
const NEVER_COPY = new Set([".git", ".gitignore.bak"]);
|
|
14
14
|
const NEVER_OVERWRITE = new Set([".git"]);
|
|
15
15
|
|
|
16
|
-
/**
|
|
17
|
-
* Files that must never be transferred into a generated project.
|
|
18
|
-
* @param {string} name
|
|
19
|
-
* @returns {boolean}
|
|
20
|
-
*/
|
|
21
|
-
function shouldSkipFile(name) {
|
|
22
|
-
if (NEVER_COPY.has(name)) return true;
|
|
23
|
-
if (name === ".env" || name.startsWith(".env.")) {
|
|
24
|
-
return name !== ".env.example";
|
|
25
|
-
}
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
16
|
/**
|
|
30
17
|
* @param {string} dir
|
|
31
18
|
* @returns {string[]}
|
|
@@ -75,7 +62,7 @@ function collectConflicts(sourceDir, targetDir, relative, conflicts) {
|
|
|
75
62
|
|
|
76
63
|
const sourceNames = readdirSync(currentSource);
|
|
77
64
|
for (const name of sourceNames) {
|
|
78
|
-
if (
|
|
65
|
+
if (shouldSkipFileName(name)) continue;
|
|
79
66
|
|
|
80
67
|
const relPath = relative ? join(relative, name) : name;
|
|
81
68
|
const sourcePath = join(sourceDir, relPath);
|
|
@@ -126,7 +113,7 @@ function copyRecursive(sourceRoot, targetRoot, relative) {
|
|
|
126
113
|
const names = readdirSync(sourcePath);
|
|
127
114
|
|
|
128
115
|
for (const name of names) {
|
|
129
|
-
if (
|
|
116
|
+
if (shouldSkipFileName(name) || NEVER_COPY_NAMES.has(name)) continue;
|
|
130
117
|
|
|
131
118
|
const relPath = relative ? join(relative, name) : name;
|
|
132
119
|
const from = join(sourceRoot, relPath);
|
|
@@ -138,7 +125,6 @@ function copyRecursive(sourceRoot, targetRoot, relative) {
|
|
|
138
125
|
|
|
139
126
|
const stat = lstatSync(from);
|
|
140
127
|
if (stat.isSymbolicLink()) {
|
|
141
|
-
const linkTarget = readlinkSync(from);
|
|
142
128
|
mkdirSync(join(to, ".."), { recursive: true });
|
|
143
129
|
cpSync(from, to, { recursive: true, force: true });
|
|
144
130
|
continue;
|
|
@@ -158,7 +144,7 @@ function copyRecursive(sourceRoot, targetRoot, relative) {
|
|
|
158
144
|
* @param {string} dir
|
|
159
145
|
*/
|
|
160
146
|
export function removeDirectory(dir) {
|
|
161
|
-
if (existsSync(dir)) {
|
|
147
|
+
if (dir && existsSync(dir)) {
|
|
162
148
|
rmSync(dir, { recursive: true, force: true });
|
|
163
149
|
}
|
|
164
150
|
}
|