@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/cli/update.js CHANGED
@@ -62,6 +62,13 @@ export function printUpdateReport(diff, meta) {
62
62
  console.log("");
63
63
  }
64
64
 
65
+ if (meta.projectVersion !== meta.latestVersion) {
66
+ console.log(
67
+ `Version ${meta.projectVersion} → ${meta.latestVersion}: see CHANGELOG.md for release notes.`
68
+ );
69
+ console.log("");
70
+ }
71
+
65
72
  if (diff.removedFromTemplate.length > 0) {
66
73
  console.log(`Removed from template (${diff.removedFromTemplate.length}) — not deleted locally:`);
67
74
  diff.removedFromTemplate.slice(0, 15).forEach((p) => console.log(` - ${p}`));
@@ -147,7 +154,15 @@ export async function runUpdate(projectDir, flags, mode) {
147
154
 
148
155
  if (mode.checkOnly) {
149
156
  if (stamp.templateVersion !== latestVersion) {
157
+ const changelogPath = join(projectDir, "CHANGELOG.md");
150
158
  console.log("Run `tempjs update --merge` to apply non-conflicting template updates.");
159
+ if (existsSync(changelogPath)) {
160
+ console.log(`See CHANGELOG.md for what changed in v${latestVersion}.`);
161
+ } else {
162
+ console.log(
163
+ `After updating, read CHANGELOG.md in the template for v${latestVersion} changes.`
164
+ );
165
+ }
151
166
  }
152
167
  return;
153
168
  }
@@ -0,0 +1,518 @@
1
+ import { execSync } from "node:child_process";
2
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { loadManifest, getPackageRoot } from "./config.js";
5
+
6
+ export const VERSION_STATE_FILE = ".tempjs-version.json";
7
+
8
+ const CLI_WATCH_PATHS = ["cli", "package.json", "templates.json"];
9
+
10
+ const CORE_PATH = "packages/core";
11
+ const SYNC_SCRIPT = "scripts/sync-templates.mjs";
12
+
13
+ /**
14
+ * @typedef {{
15
+ * version: string,
16
+ * gitCommit: string,
17
+ * recordedAt: string
18
+ * }} VersionRecord
19
+ */
20
+
21
+ /**
22
+ * @typedef {{
23
+ * cli?: VersionRecord,
24
+ * core?: VersionRecord,
25
+ * templates?: Record<string, VersionRecord>
26
+ * }} VersionState
27
+ */
28
+
29
+ /**
30
+ * @param {string} repoRoot
31
+ */
32
+ export function isGitRepository(repoRoot) {
33
+ try {
34
+ execSync("git rev-parse --git-dir", { cwd: repoRoot, stdio: "pipe" });
35
+ return true;
36
+ } catch {
37
+ return false;
38
+ }
39
+ }
40
+
41
+ /**
42
+ * @param {string} repoRoot
43
+ */
44
+ export function getHeadCommit(repoRoot) {
45
+ return execSync("git rev-parse HEAD", { cwd: repoRoot, encoding: "utf8" }).trim();
46
+ }
47
+
48
+ /**
49
+ * @param {string} repoRoot
50
+ * @returns {VersionState}
51
+ */
52
+ export function readVersionState(repoRoot) {
53
+ const path = join(repoRoot, VERSION_STATE_FILE);
54
+ if (!existsSync(path)) return {};
55
+ try {
56
+ return JSON.parse(readFileSync(path, "utf8"));
57
+ } catch {
58
+ return {};
59
+ }
60
+ }
61
+
62
+ /**
63
+ * @param {string} repoRoot
64
+ * @param {VersionState} state
65
+ */
66
+ export function writeVersionState(repoRoot, state) {
67
+ const path = join(repoRoot, VERSION_STATE_FILE);
68
+ writeFileSync(path, JSON.stringify(state, null, 2) + "\n", "utf8");
69
+ }
70
+
71
+ /**
72
+ * @param {string} version
73
+ * @param {"patch"|"minor"|"major"} level
74
+ */
75
+ export function bumpSemver(version, level) {
76
+ const parts = version.split(".").map((n) => Number.parseInt(n, 10) || 0);
77
+ const major = parts[0] ?? 0;
78
+ const minor = parts[1] ?? 0;
79
+ const patch = parts[2] ?? 0;
80
+
81
+ if (level === "major") return `${major + 1}.0.0`;
82
+ if (level === "minor") return `${major}.${minor + 1}.0`;
83
+ return `${major}.${minor}.${patch + 1}`;
84
+ }
85
+
86
+ /**
87
+ * @param {string} repoRoot
88
+ * @param {string[]} paths
89
+ */
90
+ function gitDiffFiles(repoRoot, sinceCommit, paths) {
91
+ if (!sinceCommit) return [];
92
+ const quoted = paths.map((p) => `"${p}"`).join(" ");
93
+ try {
94
+ const out = execSync(`git diff --name-only ${sinceCommit} HEAD -- ${quoted}`, {
95
+ cwd: repoRoot,
96
+ encoding: "utf8",
97
+ }).trim();
98
+ return out ? out.split("\n").filter(Boolean) : [];
99
+ } catch {
100
+ return [];
101
+ }
102
+ }
103
+
104
+ /**
105
+ * @param {string} repoRoot
106
+ * @param {string[]} paths
107
+ */
108
+ function gitDirtyFiles(repoRoot, paths) {
109
+ const quoted = paths.map((p) => `"${p}"`).join(" ");
110
+ try {
111
+ const out = execSync(`git status --porcelain -- ${quoted}`, {
112
+ cwd: repoRoot,
113
+ encoding: "utf8",
114
+ }).trim();
115
+ if (!out) return [];
116
+ return out
117
+ .split("\n")
118
+ .map((line) => line.slice(3).trim())
119
+ .filter(Boolean);
120
+ } catch {
121
+ return [];
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Paths that require sync before a template release (excludes merged output).
127
+ * @param {import('./config.js').TemplateEntry} entry
128
+ */
129
+ export function getTemplateSourcePaths(entry) {
130
+ return [CORE_PATH, SYNC_SCRIPT];
131
+ }
132
+
133
+ /**
134
+ * @param {string} templateId
135
+ * @param {import('./config.js').TemplateEntry} entry
136
+ */
137
+ export function getTemplateWatchPaths(templateId, entry) {
138
+ return [`templates/${entry.directory}`];
139
+ }
140
+
141
+ /**
142
+ * @param {string} repoRoot
143
+ * @returns {{ ok: boolean, output?: string }}
144
+ */
145
+ export function runSyncTemplatesCheck(repoRoot) {
146
+ const scriptPath = join(repoRoot, SYNC_SCRIPT);
147
+ if (!existsSync(scriptPath)) {
148
+ return { ok: false, output: `Sync script not found: ${SYNC_SCRIPT}` };
149
+ }
150
+
151
+ try {
152
+ const output = execSync(`node ${SYNC_SCRIPT} --check`, {
153
+ cwd: repoRoot,
154
+ encoding: "utf8",
155
+ stdio: ["pipe", "pipe", "pipe"],
156
+ });
157
+ return { ok: true, output };
158
+ } catch (error) {
159
+ const stdout = error.stdout?.toString?.() ?? "";
160
+ const stderr = error.stderr?.toString?.() ?? "";
161
+ return { ok: false, output: `${stdout}${stderr}`.trim() };
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Verify templates match packages/core when core or sync script changed.
167
+ * @param {string} repoRoot
168
+ * @param {import('./config.js').TemplateEntry} entry
169
+ * @param {string | undefined} sinceCommit
170
+ * @returns {boolean} false if check failed (sets process.exitCode)
171
+ */
172
+ export function ensureTemplateSyncIfNeeded(repoRoot, entry, sinceCommit) {
173
+ const sourcePaths = getTemplateSourcePaths(entry);
174
+ const { files } = collectPathChanges(repoRoot, sourcePaths, sinceCommit);
175
+
176
+ if (files.length === 0) {
177
+ return true;
178
+ }
179
+
180
+ console.log(
181
+ `Template source changed (${files.length} path(s)) — running sync-templates:check...`
182
+ );
183
+
184
+ const result = runSyncTemplatesCheck(repoRoot);
185
+
186
+ if (result.ok) {
187
+ console.log("✓ Templates are in sync with packages/core\n");
188
+ return true;
189
+ }
190
+
191
+ console.error("✗ Templates are out of sync with packages/core.");
192
+ if (result.output) {
193
+ console.error(result.output);
194
+ }
195
+ console.error("\nRun: pnpm sync-templates");
196
+ console.error("Then retry the version bump.");
197
+ process.exitCode = 1;
198
+ return false;
199
+ }
200
+
201
+ /**
202
+ * @param {string} repoRoot
203
+ * @param {string[]} paths
204
+ * @param {string | undefined} sinceCommit
205
+ */
206
+ export function collectPathChanges(repoRoot, paths, sinceCommit) {
207
+ const committed = gitDiffFiles(repoRoot, sinceCommit, paths);
208
+ const dirty = gitDirtyFiles(repoRoot, paths);
209
+ const unique = [...new Set([...committed, ...dirty])];
210
+ return { files: unique, committed, dirty };
211
+ }
212
+
213
+ /**
214
+ * @param {string} repoRoot
215
+ */
216
+ export function readCliPackageVersion(repoRoot) {
217
+ const pkg = JSON.parse(readFileSync(join(repoRoot, "package.json"), "utf8"));
218
+ return pkg.version ?? "0.0.0";
219
+ }
220
+
221
+ /**
222
+ * @param {string} repoRoot
223
+ * @param {string} version
224
+ */
225
+ export function writeCliPackageVersion(repoRoot, version) {
226
+ const pkgPath = join(repoRoot, "package.json");
227
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
228
+ pkg.version = version;
229
+ writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
230
+ }
231
+
232
+ /**
233
+ * @param {string} repoRoot
234
+ * @param {string} templateId
235
+ * @param {string} version
236
+ */
237
+ export function writeTemplateManifestVersion(repoRoot, templateId, version) {
238
+ const manifestPath = join(repoRoot, "templates.json");
239
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
240
+ if (!manifest.templates[templateId]) {
241
+ throw new Error(`Unknown template: ${templateId}`);
242
+ }
243
+ manifest.templates[templateId].version = version;
244
+ writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n", "utf8");
245
+ }
246
+
247
+ /**
248
+ * @param {string} repoRoot
249
+ * @param {string} directory
250
+ * @param {string} version
251
+ * @param {"patch"|"minor"|"major"} level
252
+ */
253
+ export function appendChangelogStub(repoRoot, directory, version, level) {
254
+ const changelogPath = join(repoRoot, "templates", directory, "CHANGELOG.md");
255
+ if (!existsSync(changelogPath)) return;
256
+
257
+ const date = new Date().toISOString().slice(0, 10);
258
+ const header = `## [${version}] — ${date}`;
259
+ if (readFileSync(changelogPath, "utf8").includes(header)) return;
260
+
261
+ const stub = `\n${header}\n\n### ${level === "patch" ? "Fixed" : "Changed"}\n- \n`;
262
+ writeFileSync(changelogPath, readFileSync(changelogPath, "utf8").trimEnd() + stub, "utf8");
263
+ }
264
+
265
+ /**
266
+ * @param {string} repoRoot
267
+ * @returns {VersionState}
268
+ */
269
+ export function initializeVersionState(repoRoot) {
270
+ const manifest = loadManifest();
271
+ const head = getHeadCommit(repoRoot);
272
+ const now = new Date().toISOString();
273
+
274
+ /** @type {VersionState} */
275
+ const state = {
276
+ cli: {
277
+ version: readCliPackageVersion(repoRoot),
278
+ gitCommit: head,
279
+ recordedAt: now,
280
+ },
281
+ core: {
282
+ version: "shared",
283
+ gitCommit: head,
284
+ recordedAt: now,
285
+ },
286
+ templates: {},
287
+ };
288
+
289
+ for (const [id, entry] of Object.entries(manifest.templates)) {
290
+ state.templates[id] = {
291
+ version: entry.version ?? "0.0.0",
292
+ gitCommit: head,
293
+ recordedAt: now,
294
+ };
295
+ }
296
+
297
+ writeVersionState(repoRoot, state);
298
+ return state;
299
+ }
300
+
301
+ /**
302
+ * @param {string[]} files
303
+ * @param {number} max
304
+ */
305
+ export function formatFileList(files, max = 8) {
306
+ if (files.length === 0) return "";
307
+ const preview = files.slice(0, max);
308
+ const lines = preview.map((f) => ` ${f}`);
309
+ if (files.length > max) {
310
+ lines.push(` ... and ${files.length - max} more`);
311
+ }
312
+ return lines.join("\n");
313
+ }
314
+
315
+ /**
316
+ * @typedef {{
317
+ * scope: string,
318
+ * version: string,
319
+ * baselineCommit?: string,
320
+ * upToDate: boolean,
321
+ * files: string[],
322
+ * dirty: boolean
323
+ * }} VersionCheckResult
324
+ */
325
+
326
+ /**
327
+ * @param {string} repoRoot
328
+ * @param {{ scope?: string }} options
329
+ */
330
+ export function runVersionCheck(repoRoot, options = {}) {
331
+ if (!isGitRepository(repoRoot)) {
332
+ console.error("Version tracking requires a git repository.");
333
+ console.error("Run from the templates monorepo root.");
334
+ process.exitCode = 1;
335
+ return;
336
+ }
337
+
338
+ let state = readVersionState(repoRoot);
339
+ if (!state.cli?.gitCommit) {
340
+ console.log("Initializing version tracking (.tempjs-version.json)...");
341
+ state = initializeVersionState(repoRoot);
342
+ console.log("Baseline recorded at current HEAD. All versions marked up to date.\n");
343
+ }
344
+
345
+ const manifest = loadManifest();
346
+ const scope = options.scope?.toLowerCase();
347
+ const checkCli = !scope || scope === "cli";
348
+ const checkTemplates = !scope || scope === "all" || (scope && scope !== "cli" && manifest.templates[scope]);
349
+
350
+ /** @type {VersionCheckResult[]} */
351
+ const results = [];
352
+
353
+ if (checkCli) {
354
+ const paths = CLI_WATCH_PATHS;
355
+ const record = state.cli;
356
+ const currentVersion = readCliPackageVersion(repoRoot);
357
+ const { files, dirty } = collectPathChanges(repoRoot, paths, record?.gitCommit);
358
+ const versionMismatch = record?.version !== currentVersion;
359
+ results.push({
360
+ scope: "cli",
361
+ version: currentVersion,
362
+ baselineCommit: record?.gitCommit,
363
+ upToDate: files.length === 0 && !versionMismatch,
364
+ files,
365
+ dirty: dirty.length > 0,
366
+ });
367
+ }
368
+
369
+ const templateIds =
370
+ scope && scope !== "cli" && scope !== "all"
371
+ ? [scope]
372
+ : checkTemplates
373
+ ? Object.keys(manifest.templates)
374
+ : [];
375
+
376
+ for (const templateId of templateIds) {
377
+ const entry = manifest.templates[templateId];
378
+ if (!entry) continue;
379
+ const paths = getTemplateWatchPaths(templateId, entry);
380
+ const record = state.templates?.[templateId];
381
+ const currentVersion = entry.version ?? "0.0.0";
382
+ const { files, dirty } = collectPathChanges(repoRoot, paths, record?.gitCommit);
383
+ const versionMismatch = record?.version !== currentVersion;
384
+ results.push({
385
+ scope: `template:${templateId}`,
386
+ version: currentVersion,
387
+ baselineCommit: record?.gitCommit,
388
+ upToDate: files.length === 0 && !versionMismatch,
389
+ files,
390
+ dirty: dirty.length > 0,
391
+ });
392
+ }
393
+
394
+ console.log("tempjs version check\n");
395
+
396
+ let anyStale = false;
397
+
398
+ for (const result of results) {
399
+ const label =
400
+ result.scope === "cli"
401
+ ? `CLI (@navneet_25/tempjs)`
402
+ : `Template ${result.scope.replace("template:", "")} (${manifest.templates[result.scope.replace("template:", "")]?.name ?? ""})`;
403
+
404
+ if (result.upToDate) {
405
+ console.log(`✓ ${label} v${result.version}`);
406
+ console.log(` Up to date since ${result.baselineCommit?.slice(0, 7) ?? "baseline"}`);
407
+ } else {
408
+ anyStale = true;
409
+ console.log(`✗ ${label} v${result.version}`);
410
+ if (result.files.length > 0) {
411
+ console.log(` ${result.files.length} path(s) changed since last version bump:`);
412
+ console.log(formatFileList(result.files));
413
+ }
414
+ if (result.scope === "cli") {
415
+ console.log(` → tempjs version inc patch cli`);
416
+ } else {
417
+ const id = result.scope.replace("template:", "");
418
+ console.log(` → tempjs version inc patch ${id}`);
419
+ }
420
+ console.log("");
421
+ }
422
+ }
423
+
424
+ if (!anyStale) {
425
+ console.log("\nEverything is up to date — no unreleased changes detected.");
426
+ } else {
427
+ console.log("\nDocument changes in CHANGELOG.md, then bump with tempjs version inc.");
428
+ process.exitCode = 1;
429
+ }
430
+ }
431
+
432
+ /**
433
+ * @param {string} repoRoot
434
+ * @param {"patch"|"minor"|"major"} level
435
+ * @param {string} target cli | template id | all
436
+ */
437
+ export function runVersionInc(repoRoot, level, target) {
438
+ if (!["patch", "minor", "major"].includes(level)) {
439
+ console.error(`Invalid level: ${level}. Use patch, minor, or major.`);
440
+ process.exitCode = 1;
441
+ return;
442
+ }
443
+
444
+ if (!isGitRepository(repoRoot)) {
445
+ console.error("Version bumps require a git repository.");
446
+ process.exitCode = 1;
447
+ return;
448
+ }
449
+
450
+ let state = readVersionState(repoRoot);
451
+ if (!state.cli?.gitCommit) {
452
+ state = initializeVersionState(repoRoot);
453
+ }
454
+
455
+ const manifest = loadManifest();
456
+ const head = getHeadCommit(repoRoot);
457
+ const now = new Date().toISOString();
458
+ const normalized = target?.toLowerCase() ?? "cli";
459
+
460
+ if (normalized === "all") {
461
+ runVersionInc(repoRoot, level, "cli");
462
+ for (const id of Object.keys(manifest.templates)) {
463
+ runVersionInc(repoRoot, level, id);
464
+ }
465
+ return;
466
+ }
467
+
468
+ if (normalized === "cli") {
469
+ const current = readCliPackageVersion(repoRoot);
470
+ const next = bumpSemver(current, level);
471
+ writeCliPackageVersion(repoRoot, next);
472
+ state.cli = { version: next, gitCommit: head, recordedAt: now };
473
+ writeVersionState(repoRoot, state);
474
+ console.log(`CLI: ${current} → ${next} (${level})`);
475
+ console.log(`Recorded baseline commit ${head.slice(0, 7)}`);
476
+ return;
477
+ }
478
+
479
+ const entry = manifest.templates[normalized];
480
+ if (!entry) {
481
+ console.error(`Unknown template: ${normalized}`);
482
+ console.error("Run `tempjs list` for template ids.");
483
+ process.exitCode = 1;
484
+ return;
485
+ }
486
+
487
+ const baselineCommit = state.templates?.[normalized]?.gitCommit;
488
+
489
+ const current = entry.version ?? "0.0.0";
490
+ const next = bumpSemver(current, level);
491
+ writeTemplateManifestVersion(repoRoot, normalized, next);
492
+ appendChangelogStub(repoRoot, entry.directory, next, level);
493
+
494
+ if (!state.templates) state.templates = {};
495
+ state.templates[normalized] = { version: next, gitCommit: head, recordedAt: now };
496
+ state.core = { version: "shared", gitCommit: head, recordedAt: now };
497
+ writeVersionState(repoRoot, state);
498
+
499
+ console.log(`Template ${normalized}: ${current} → ${next} (${level})`);
500
+ console.log(`Updated templates.json and CHANGELOG.md stub`);
501
+ console.log(`Recorded baseline commit ${head.slice(0, 7)}`);
502
+ console.log("\nNext: edit CHANGELOG, commit, publish/push.");
503
+ }
504
+
505
+ /**
506
+ * Ensure we're in the monorepo (has templates.json + cli).
507
+ * @param {string} cwd
508
+ */
509
+ export function resolveMonorepoRoot(cwd) {
510
+ const packageRoot = getPackageRoot();
511
+ if (existsSync(join(packageRoot, "templates.json")) && existsSync(join(packageRoot, "cli"))) {
512
+ return packageRoot;
513
+ }
514
+ if (existsSync(join(cwd, "templates.json")) && existsSync(join(cwd, "cli"))) {
515
+ return cwd;
516
+ }
517
+ return null;
518
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@navneet_25/tempjs",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "CLI to instantiate website project templates from a single GitHub repository",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,7 +8,13 @@
8
8
  },
9
9
  "files": [
10
10
  "cli",
11
- "templates.json"
11
+ "templates.json",
12
+ "packages/core/modules.json",
13
+ "packages/core/modules/**",
14
+ "packages/core/prisma/schema.prisma",
15
+ "scripts/module-installer-core.mjs",
16
+ "scripts/template-modules-home.mjs",
17
+ "scripts/template-modules-admin.mjs"
12
18
  ],
13
19
  "engines": {
14
20
  "node": ">=18"
@@ -23,6 +29,23 @@
23
29
  "license": "MIT",
24
30
  "scripts": {
25
31
  "sync-templates": "node scripts/sync-templates.mjs",
26
- "sync-templates:check": "node scripts/sync-templates.mjs --check"
32
+ "sync-templates:check": "node scripts/sync-templates.mjs --check",
33
+ "propagate-core": "node scripts/sync-templates.mjs",
34
+ "new-template": "node scripts/new-template.mjs",
35
+ "template:add-module": "node scripts/template-add-module.mjs",
36
+ "template:assemble": "node scripts/template-assemble.mjs",
37
+ "template:extract-modules": "node scripts/template-extract-modules.mjs",
38
+ "template:validate": "node scripts/template-validate.mjs",
39
+ "template:diff-core": "node scripts/template-diff-core.mjs",
40
+ "setup-hooks": "git config core.hooksPath .githooks && chmod +x .githooks/pre-commit",
41
+ "dev:hotel": "cd templates/hotel-website-template && pnpm dev",
42
+ "dev:real-estate": "cd templates/real-estate-website-template && pnpm dev",
43
+ "docs:dev": "cd docsite && pnpm dev",
44
+ "docs:build": "cd docsite && pnpm build",
45
+ "docsite:dev": "cd docsite && pnpm dev",
46
+ "docsite:build": "cd docsite && pnpm build",
47
+ "version:check": "node cli/index.js version check",
48
+ "version:inc": "node cli/index.js version inc",
49
+ "dev:dev-agency": "cd templates/dev-agency-website-template && pnpm dev"
27
50
  }
28
- }
51
+ }
@@ -0,0 +1,72 @@
1
+ # Optional core modules
2
+
3
+ Copy on demand via `pnpm new-template --modules`, `pnpm template:add-module` (monorepo), or `tempjs add-module` (client projects).
4
+
5
+ ## Available modules
6
+
7
+ | Id | Feature flag | Prisma | Admin UI today |
8
+ |----|--------------|--------|----------------|
9
+ | `enquiry-modal` | `enquiryModal` | — | — |
10
+ | `footer` | `footer` | — | — |
11
+ | `hero-simple` | `heroSimple` | — | — |
12
+ | `seo` | `seo` | — | sitemap, robots, JSON-LD |
13
+ | `gallery` | `gallery` | `GalleryImage` | GalleryList tab on unified dashboard |
14
+ | `reviews` | `reviews` | `Review` | ReviewsList tab on unified dashboard |
15
+ | `legal-pages` | `legalPages` | — | — |
16
+ | `blog-compose` | `blogCompose` | `BlogPost` | BlogComposePanel tab — compose editor + preview |
17
+ | Optional `+sidebar` on install | `blogSidebar` | — | BlogShell + BlogSidebar on /blog routes |
18
+
19
+ Registry: `packages/core/modules.json`
20
+
21
+ **Admin tab registry:** gallery/reviews/blog-compose update `app/admin/registry.ts` on install; unified `app/admin/page.tsx` uses `lib/admin/AdminShell` + `getAdminTabs()`.
22
+
23
+ ## Monorepo
24
+
25
+ ```bash
26
+ pnpm new-template bakery bakery-website-template --name "Bakery" \
27
+ --modules seo,hero-simple,footer,enquiry-modal,gallery,reviews,legal-pages
28
+
29
+ pnpm template:add-module bakery gallery,reviews
30
+ ```
31
+
32
+ ## Client projects
33
+
34
+ ```bash
35
+ tempjs add-module list
36
+ tempjs add-module seo,gallery,reviews
37
+ pnpm prisma db push
38
+ ```
39
+
40
+ Published CLI ships `packages/core/modules/` and fetches from GitHub when not in the monorepo. Records installed ids in `.tempjs-modules.json`.
41
+
42
+ ## SEO module
43
+
44
+ - `app/sitemap.ts` — `buildAppSitemap()` + `registerDynamicSitemapProvider()` for listing slugs
45
+ - `app/robots.ts` — host, sitemap URL, configurable disallow rules
46
+ - `lib/seo/json-ld.ts` — Organization/WebSite graph
47
+ - `lib/seo/metadata.ts` — canonical, Open Graph, Twitter cards
48
+ - `SiteJsonLd` wired into `app/layout.tsx` on install
49
+
50
+ Template vertical modules (hotel `lib/hotel/register-sitemap.ts`, real-estate `lib/real-estate/register-sitemap.ts`) register dynamic routes.
51
+
52
+ ## Gallery / reviews / blog-compose
53
+
54
+ - Appends Prisma models to `prisma/domain.prisma` and regenerates `schema.prisma`
55
+ - Public API + admin CRUD components
56
+ - Scaffold templates: wires `app/page.tsx` and updates `app/admin/registry.ts` (removes legacy `/admin/content` if present)
57
+ - Shipped hotel/real-estate: custom `registry.ts` with vertical tabs; optional module tabs when modules are enabled
58
+
59
+ ## Compose blog (`blog-compose`)
60
+
61
+ - Compose Blog Engine under `lib/blog/compose/` — declarative JSON blocks, interpreter, registry
62
+ - `BlogPost` model with `contentJson` Compose document
63
+ - `/blog`, `/blog/[slug]`, `BlogSection`, admin `BlogComposePanel` with live preview
64
+ - `lib/blog/register-sitemap.ts` — dynamic post slugs when `seo` module is installed (`add-module` patches `app/sitemap.ts`)
65
+
66
+ ## Legal pages
67
+
68
+ - `/privacy-policy` and `/terms-and-conditions` from `SITE.privacyPage` / `SITE.termsPage`
69
+
70
+ ## Template vertical modules (not in this folder)
71
+
72
+ Hotel and real-estate vertical slices live under `templates/<dir>/modules/` with `template-modules.json`. See `pnpm template:assemble` / `pnpm template:extract-modules`.