@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/index.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { execSync } from "node:child_process";
|
|
4
|
-
import { createInterface } from "node:readline/promises";
|
|
5
|
-
import { stdin as input, stdout as output } from "node:process";
|
|
6
4
|
import { loadManifest, getPackageRoot, resolveRepositoryConfig } from "./config.js";
|
|
7
5
|
import {
|
|
8
6
|
promptTheme,
|
|
@@ -12,56 +10,97 @@ import {
|
|
|
12
10
|
} from "./theme-manager.js";
|
|
13
11
|
import { promptAndApplyBrand } from "./brand-manager.js";
|
|
14
12
|
import { promptAndSetupDb } from "./db-setup.js";
|
|
13
|
+
import { printTemplateInfo } from "./info.js";
|
|
14
|
+
import { parseArgs, toCliOptions } from "./parse-args.js";
|
|
15
|
+
import {
|
|
16
|
+
pickBrandOptionsForTemplate,
|
|
17
|
+
resolveTemplateInitConfig,
|
|
18
|
+
} from "./init-options.js";
|
|
19
|
+
import { readProjectStamp } from "./project-stamp.js";
|
|
15
20
|
import {
|
|
16
21
|
copyTemplate,
|
|
17
22
|
findConflictingPaths,
|
|
18
23
|
isDirectoryEmpty,
|
|
19
24
|
listDirectoryEntries,
|
|
20
|
-
removeDirectory,
|
|
21
25
|
targetHasGitRepo,
|
|
22
26
|
} from "./copy.js";
|
|
23
|
-
import {
|
|
27
|
+
import { writeProjectStamp } from "./project-stamp.js";
|
|
28
|
+
import { printFetchComplete } from "./progress.js";
|
|
29
|
+
import { confirmYesNo } from "./prompt.js";
|
|
30
|
+
import { resolveTemplateSource } from "./template-resolver.js";
|
|
31
|
+
import { runUpdate } from "./update.js";
|
|
32
|
+
import { runDoctor } from "./doctor.js";
|
|
33
|
+
import {
|
|
34
|
+
resolveMonorepoRoot,
|
|
35
|
+
runVersionCheck,
|
|
36
|
+
runVersionInc,
|
|
37
|
+
} from "./version-manager.js";
|
|
38
|
+
import { runAddModule, printAvailableModules } from "./module-manager.js";
|
|
24
39
|
|
|
25
40
|
const HELP_TEXT = `
|
|
26
41
|
tempjs — instantiate project templates from GitHub
|
|
27
42
|
|
|
28
43
|
USAGE
|
|
29
44
|
tempjs list
|
|
45
|
+
tempjs info <template-id>
|
|
30
46
|
tempjs <template-id> [options]
|
|
31
|
-
tempjs <template-id> config
|
|
32
|
-
tempjs
|
|
33
|
-
tempjs
|
|
34
|
-
tempjs
|
|
35
|
-
tempjs
|
|
47
|
+
tempjs <template-id> config Interactive theme, typography, brand & database setup
|
|
48
|
+
tempjs update [--check | --merge]
|
|
49
|
+
tempjs theme Change theme in an initialized project
|
|
50
|
+
tempjs font Change font styling in an initialized project
|
|
51
|
+
tempjs brand Configure brand & contact info
|
|
52
|
+
tempjs init-db Configure .env and sync database schema
|
|
53
|
+
tempjs doctor Check if this project can run (env, DB, deps)
|
|
54
|
+
tempjs add-module <ids> Add optional core modules (seo, gallery, reviews, …)
|
|
55
|
+
tempjs add-module list List available core modules
|
|
56
|
+
tempjs version check [cli|all|<template-id>]
|
|
57
|
+
tempjs version inc <patch|minor|major> [cli|all|<template-id>]
|
|
36
58
|
tempjs --help
|
|
37
59
|
|
|
60
|
+
VERSION (maintainers — run from monorepo root)
|
|
61
|
+
tempjs version check Compare git changes since last version bump
|
|
62
|
+
tempjs version check hotel Check one template only
|
|
63
|
+
tempjs version inc patch cli Bump @navneet_25/tempjs in package.json
|
|
64
|
+
tempjs version inc minor hotel Bump template in templates.json + CHANGELOG stub
|
|
65
|
+
|
|
66
|
+
UPDATE
|
|
67
|
+
tempjs update --check Show diff vs latest template (read-only)
|
|
68
|
+
tempjs update --merge Apply non-conflicting template updates only
|
|
69
|
+
tempjs update --merge --yes Apply without confirmation prompt
|
|
70
|
+
|
|
38
71
|
OPTIONS
|
|
39
|
-
--config
|
|
40
|
-
--
|
|
41
|
-
--
|
|
42
|
-
--
|
|
43
|
-
--
|
|
72
|
+
--config Run full setup (theme, font, brand, database) after copying
|
|
73
|
+
--yes, -y Skip all prompts
|
|
74
|
+
--no-prompt Same as --yes
|
|
75
|
+
--force, -f Overwrite existing files without prompting
|
|
76
|
+
--remote Fetch from GitHub even if a local template copy exists
|
|
77
|
+
--init-git Run git init after copying the template
|
|
78
|
+
|
|
79
|
+
(See tempjs --help for theme, brand, and database flags.)
|
|
44
80
|
|
|
45
81
|
EXAMPLES
|
|
46
82
|
mkdir hotel-client && cd hotel-client
|
|
47
83
|
tempjs hotel config
|
|
48
|
-
|
|
49
|
-
tempjs
|
|
84
|
+
|
|
85
|
+
tempjs update --check
|
|
86
|
+
tempjs update --merge --yes
|
|
87
|
+
|
|
88
|
+
tempjs doctor
|
|
89
|
+
|
|
90
|
+
tempjs add-module seo,gallery,reviews
|
|
91
|
+
tempjs add-module list
|
|
50
92
|
|
|
51
93
|
ENVIRONMENT
|
|
52
|
-
TEMPLATES_REPO_URL GitHub repo URL or owner/repo
|
|
94
|
+
TEMPLATES_REPO_URL GitHub repo URL or owner/repo
|
|
53
95
|
TEMPLATES_REPO_OWNER GitHub owner/username
|
|
54
96
|
TEMPLATES_REPO_REPO GitHub repository name
|
|
55
97
|
TEMPLATES_REPO_BRANCH Branch name (default: main)
|
|
56
98
|
TEMPLATE_USE_REMOTE=1 Always fetch from GitHub
|
|
57
|
-
GITHUB_TOKEN / GH_TOKEN
|
|
58
|
-
|
|
59
|
-
CONFIGURATION
|
|
60
|
-
Repository and template mappings live in templates.json at the package root.
|
|
99
|
+
GITHUB_TOKEN / GH_TOKEN Optional — private repositories only
|
|
61
100
|
`;
|
|
62
101
|
|
|
63
102
|
/**
|
|
64
|
-
* @param {Record<string,
|
|
103
|
+
* @param {Record<string, import('./config.js').TemplateEntry>} templates
|
|
65
104
|
*/
|
|
66
105
|
function printTemplateList(templates) {
|
|
67
106
|
console.log("Available templates:\n");
|
|
@@ -70,41 +109,20 @@ function printTemplateList(templates) {
|
|
|
70
109
|
|
|
71
110
|
for (const id of ids) {
|
|
72
111
|
const entry = templates[id];
|
|
73
|
-
|
|
112
|
+
const version = entry.version ? ` v${entry.version}` : "";
|
|
113
|
+
console.log(`${id.padEnd(idWidth + 2)}${entry.name}${version}`);
|
|
74
114
|
if (entry.description) {
|
|
75
115
|
console.log(`${"".padEnd(idWidth + 2)}${entry.description}`);
|
|
76
116
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
* @param {string[]} argv
|
|
83
|
-
*/
|
|
84
|
-
function parseArgs(argv) {
|
|
85
|
-
const flags = {
|
|
86
|
-
force: false,
|
|
87
|
-
remote: false,
|
|
88
|
-
initGit: false,
|
|
89
|
-
help: false,
|
|
90
|
-
config: false,
|
|
91
|
-
};
|
|
92
|
-
const positionals = [];
|
|
93
|
-
|
|
94
|
-
for (const arg of argv) {
|
|
95
|
-
if (arg === "--force") flags.force = true;
|
|
96
|
-
else if (arg === "--remote") flags.remote = true;
|
|
97
|
-
else if (arg === "--init-git") flags.initGit = true;
|
|
98
|
-
else if (arg === "--help" || arg === "-h") flags.help = true;
|
|
99
|
-
else if (arg === "--config") flags.config = true;
|
|
100
|
-
else if (arg.startsWith("-")) {
|
|
101
|
-
throw new Error(`Unknown option: ${arg}`);
|
|
102
|
-
} else {
|
|
103
|
-
positionals.push(arg);
|
|
117
|
+
if (entry.tags?.length) {
|
|
118
|
+
console.log(`${"".padEnd(idWidth + 2)}[${entry.tags.join(", ")}]`);
|
|
119
|
+
}
|
|
120
|
+
if (entry.stack?.length) {
|
|
121
|
+
console.log(`${"".padEnd(idWidth + 2)}${entry.stack.slice(0, 4).join(" · ")}`);
|
|
104
122
|
}
|
|
105
123
|
}
|
|
106
|
-
|
|
107
|
-
|
|
124
|
+
console.log("");
|
|
125
|
+
console.log("Run `tempjs info <template-id>` for full details.\n");
|
|
108
126
|
}
|
|
109
127
|
|
|
110
128
|
/**
|
|
@@ -123,24 +141,10 @@ function printConflictWarning(conflicts) {
|
|
|
123
141
|
console.log("");
|
|
124
142
|
}
|
|
125
143
|
|
|
126
|
-
/**
|
|
127
|
-
* @param {boolean} force
|
|
128
|
-
*/
|
|
129
|
-
async function confirmOverwrite(force) {
|
|
130
|
-
if (force) return true;
|
|
131
|
-
const rl = createInterface({ input, output });
|
|
132
|
-
try {
|
|
133
|
-
const answer = await rl.question("Continue? [y/N] ");
|
|
134
|
-
return answer.trim().toLowerCase() === "y" || answer.trim().toLowerCase() === "yes";
|
|
135
|
-
} finally {
|
|
136
|
-
rl.close();
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
144
|
/**
|
|
141
145
|
* @param {string} targetDir
|
|
142
146
|
* @param {string} templateId
|
|
143
|
-
* @param {
|
|
147
|
+
* @param {import('./parse-args.js').CliFlags} flags
|
|
144
148
|
* @param {boolean} runWithConfig
|
|
145
149
|
*/
|
|
146
150
|
async function runTemplate(targetDir, templateId, flags, runWithConfig = false) {
|
|
@@ -156,30 +160,22 @@ async function runTemplate(targetDir, templateId, flags, runWithConfig = false)
|
|
|
156
160
|
|
|
157
161
|
const repo = resolveRepositoryConfig(manifest.repository);
|
|
158
162
|
const packageRoot = getPackageRoot();
|
|
163
|
+
const cliOptions = toCliOptions(flags);
|
|
159
164
|
const useRemote =
|
|
160
165
|
flags.remote ||
|
|
161
166
|
process.env.TEMPLATE_USE_REMOTE === "1" ||
|
|
162
167
|
process.env.TEMPLATE_USE_REMOTE === "true";
|
|
163
168
|
|
|
164
|
-
let
|
|
165
|
-
let cleanupDir = null;
|
|
169
|
+
let resolved = null;
|
|
166
170
|
|
|
167
171
|
try {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
if (!sourceDir) {
|
|
177
|
-
console.log(`Fetching template "${templateId}" from ${repo.owner}/${repo.repo}...`);
|
|
178
|
-
sourceDir = await fetchTemplateFromGitHub(repo, entry.directory);
|
|
179
|
-
cleanupDir = sourceDir.replace(/[/\\]template$/, "");
|
|
180
|
-
} else {
|
|
181
|
-
console.log(`Using local template "${templateId}"...`);
|
|
182
|
-
}
|
|
172
|
+
resolved = await resolveTemplateSource({
|
|
173
|
+
repo,
|
|
174
|
+
packageRoot,
|
|
175
|
+
templateDirectory: entry.directory,
|
|
176
|
+
useRemote,
|
|
177
|
+
});
|
|
178
|
+
printFetchComplete({ templateId, stats: resolved.stats });
|
|
183
179
|
|
|
184
180
|
if (targetHasGitRepo(targetDir)) {
|
|
185
181
|
console.warn(
|
|
@@ -189,19 +185,20 @@ async function runTemplate(targetDir, templateId, flags, runWithConfig = false)
|
|
|
189
185
|
|
|
190
186
|
const entries = listDirectoryEntries(targetDir);
|
|
191
187
|
const hasContent = entries.length > 0;
|
|
188
|
+
const autoConfirm = flags.force || flags.yes;
|
|
192
189
|
|
|
193
190
|
if (hasContent && !flags.force) {
|
|
194
|
-
const conflicts = findConflictingPaths(
|
|
191
|
+
const conflicts = findConflictingPaths(resolved.templateRoot, targetDir);
|
|
195
192
|
if (conflicts.length > 0) {
|
|
196
193
|
printConflictWarning(conflicts);
|
|
197
|
-
const confirmed = await
|
|
194
|
+
const confirmed = await confirmYesNo("Continue? [y/N] ", autoConfirm);
|
|
198
195
|
if (!confirmed) {
|
|
199
196
|
console.log("Aborted.");
|
|
200
197
|
return;
|
|
201
198
|
}
|
|
202
199
|
} else if (!isDirectoryEmpty(targetDir)) {
|
|
203
200
|
console.log("Current directory is not empty, but no files would be overwritten.");
|
|
204
|
-
const confirmed = await
|
|
201
|
+
const confirmed = await confirmYesNo("Continue? [y/N] ", autoConfirm);
|
|
205
202
|
if (!confirmed) {
|
|
206
203
|
console.log("Aborted.");
|
|
207
204
|
return;
|
|
@@ -210,32 +207,50 @@ async function runTemplate(targetDir, templateId, flags, runWithConfig = false)
|
|
|
210
207
|
}
|
|
211
208
|
|
|
212
209
|
const allowOverwrite = flags.force || hasContent;
|
|
213
|
-
copyTemplate(
|
|
210
|
+
copyTemplate(resolved.templateRoot, targetDir, { force: allowOverwrite });
|
|
211
|
+
|
|
212
|
+
await writeProjectStamp(targetDir, {
|
|
213
|
+
templateId,
|
|
214
|
+
templateVersion: entry.version ?? "0.0.0",
|
|
215
|
+
templateDirectory: entry.directory,
|
|
216
|
+
repository: `${repo.owner}/${repo.repo}`,
|
|
217
|
+
branch: repo.branch,
|
|
218
|
+
sourceDir: resolved.templateRoot,
|
|
219
|
+
isUpdate: false,
|
|
220
|
+
});
|
|
214
221
|
|
|
215
222
|
if (flags.initGit && !targetHasGitRepo(targetDir)) {
|
|
216
223
|
execSync("git init", { cwd: targetDir, stdio: "inherit" });
|
|
217
224
|
}
|
|
218
225
|
|
|
219
226
|
if (runWithConfig) {
|
|
227
|
+
const initConfig = resolveTemplateInitConfig(entry);
|
|
220
228
|
console.log("\nConfiguring project theme and typography...");
|
|
221
|
-
const selectedTheme = await promptTheme("theme1");
|
|
222
|
-
const selectedFont = await promptFont("default");
|
|
229
|
+
const selectedTheme = await promptTheme("theme1", cliOptions);
|
|
230
|
+
const selectedFont = await promptFont("default", cliOptions);
|
|
223
231
|
await applyThemeAndFont(targetDir, selectedTheme, selectedFont);
|
|
224
232
|
|
|
225
|
-
|
|
226
|
-
|
|
233
|
+
if (!initConfig.skipBrand) {
|
|
234
|
+
const brandOpts = pickBrandOptionsForTemplate(templateId, entry, cliOptions);
|
|
235
|
+
await promptAndApplyBrand(targetDir, brandOpts, templateId);
|
|
236
|
+
}
|
|
227
237
|
|
|
228
|
-
|
|
229
|
-
|
|
238
|
+
if (!initConfig.skipDatabase) {
|
|
239
|
+
await promptAndSetupDb(targetDir, cliOptions);
|
|
240
|
+
}
|
|
230
241
|
}
|
|
231
242
|
|
|
232
243
|
console.log(`\nTemplate "${entry.name}" created successfully in ${targetDir}`);
|
|
233
|
-
console.log(
|
|
234
|
-
console.log("
|
|
235
|
-
console.log(" pnpm
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
244
|
+
console.log(`Stamped .tempjs.json (template v${entry.version ?? "0.0.0"})`);
|
|
245
|
+
console.log("\nNext steps — see GETTING_STARTED.md:");
|
|
246
|
+
console.log(" 1. pnpm install");
|
|
247
|
+
console.log(" 2. cp .env.example .env");
|
|
248
|
+
console.log(" 3. docker compose up -d # or: tempjs init-db");
|
|
249
|
+
console.log(" 4. pnpm prisma db push && pnpm prisma db seed");
|
|
250
|
+
console.log(" 5. pnpm dev");
|
|
251
|
+
console.log(" 6. Open /admin — login with ADMIN_USER / ADMIN_PASSWORD");
|
|
252
|
+
console.log("\n curl http://localhost:3000/api/health # verify DB + env");
|
|
253
|
+
console.log(" tempjs doctor # full readiness check");
|
|
239
254
|
} catch (error) {
|
|
240
255
|
if (error instanceof Error && error.message === "TARGET_NOT_EMPTY") {
|
|
241
256
|
printConflictWarning(error.conflicts ?? []);
|
|
@@ -247,8 +262,8 @@ async function runTemplate(targetDir, templateId, flags, runWithConfig = false)
|
|
|
247
262
|
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
248
263
|
process.exitCode = 1;
|
|
249
264
|
} finally {
|
|
250
|
-
if (
|
|
251
|
-
|
|
265
|
+
if (resolved) {
|
|
266
|
+
resolved.release();
|
|
252
267
|
}
|
|
253
268
|
}
|
|
254
269
|
}
|
|
@@ -258,33 +273,180 @@ async function runTemplate(targetDir, templateId, flags, runWithConfig = false)
|
|
|
258
273
|
*/
|
|
259
274
|
async function main(argv) {
|
|
260
275
|
const { flags, positionals } = parseArgs(argv);
|
|
261
|
-
|
|
276
|
+
const command = positionals[0];
|
|
262
277
|
|
|
263
278
|
if (flags.help || command === "help" || (!command && argv.length === 0)) {
|
|
264
279
|
console.log(HELP_TEXT.trim());
|
|
265
280
|
return;
|
|
266
281
|
}
|
|
267
282
|
|
|
268
|
-
|
|
283
|
+
const manifest = loadManifest();
|
|
284
|
+
const repo = resolveRepositoryConfig(manifest.repository);
|
|
285
|
+
const cliOptions = toCliOptions(flags);
|
|
286
|
+
|
|
287
|
+
if (command === "list") {
|
|
288
|
+
printTemplateList(manifest.templates);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (command === "info") {
|
|
293
|
+
const templateId = positionals[1];
|
|
294
|
+
if (!templateId) {
|
|
295
|
+
console.error("Usage: tempjs info <template-id>");
|
|
296
|
+
console.error("Run `tempjs list` to see available templates.");
|
|
297
|
+
process.exitCode = 1;
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
const entry = manifest.templates[templateId];
|
|
301
|
+
if (!entry) {
|
|
302
|
+
console.error(`Unknown template: ${templateId}`);
|
|
303
|
+
console.error("Run `tempjs list` to see available templates.");
|
|
304
|
+
process.exitCode = 1;
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
printTemplateInfo(templateId, entry, repo);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (command === "add-module") {
|
|
269
312
|
const targetDir = process.cwd();
|
|
313
|
+
const sub = positionals[1];
|
|
314
|
+
|
|
315
|
+
if (sub === "list" || flags.list) {
|
|
316
|
+
console.log("Available core modules:\n");
|
|
317
|
+
await printAvailableModules(repo);
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const moduleArg = sub ?? positionals.slice(1).join(",");
|
|
322
|
+
const moduleIds = moduleArg
|
|
323
|
+
? moduleArg.split(",").map((s) => s.trim()).filter(Boolean)
|
|
324
|
+
: [];
|
|
325
|
+
|
|
326
|
+
if (moduleIds.length === 0) {
|
|
327
|
+
console.error("Usage: tempjs add-module <module-id>[,<module-id>...]");
|
|
328
|
+
console.error(" tempjs add-module list");
|
|
329
|
+
process.exitCode = 1;
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
await runAddModule(targetDir, moduleIds, repo, {
|
|
334
|
+
useRemote: flags.remote,
|
|
335
|
+
});
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (command === "update") {
|
|
340
|
+
const hasCheck = flags.updateCheck || argv.includes("--check");
|
|
341
|
+
const hasMerge = flags.updateMerge || argv.includes("--merge");
|
|
342
|
+
|
|
343
|
+
if (!hasCheck && !hasMerge) {
|
|
344
|
+
console.error("Usage: tempjs update --check | tempjs update --merge [--yes]");
|
|
345
|
+
process.exitCode = 1;
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const targetDir = process.cwd();
|
|
350
|
+
await runUpdate(targetDir, flags, { checkOnly: hasCheck && !hasMerge });
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (command === "version") {
|
|
355
|
+
const repoRoot = resolveMonorepoRoot(process.cwd());
|
|
356
|
+
if (!repoRoot) {
|
|
357
|
+
console.error("tempjs version commands must run from the templates monorepo root.");
|
|
358
|
+
console.error("(Directory containing templates.json and cli/)");
|
|
359
|
+
process.exitCode = 1;
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const sub = positionals[1];
|
|
364
|
+
|
|
365
|
+
if (sub === "check") {
|
|
366
|
+
runVersionCheck(repoRoot, { scope: positionals[2] });
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (sub === "inc") {
|
|
371
|
+
const level = positionals[2];
|
|
372
|
+
const target = positionals[3] ?? "cli";
|
|
373
|
+
|
|
374
|
+
if (!level || !["patch", "minor", "major"].includes(level)) {
|
|
375
|
+
console.error("Usage: tempjs version inc <patch|minor|major> [cli|all|<template-id>]");
|
|
376
|
+
process.exitCode = 1;
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
runVersionInc(repoRoot, level, target);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
console.log(`
|
|
385
|
+
tempjs version — release tracking for CLI and templates (maintainers)
|
|
386
|
+
|
|
387
|
+
USAGE
|
|
388
|
+
tempjs version check [cli|all|<template-id>]
|
|
389
|
+
tempjs version inc <patch|minor|major> [cli|all|<template-id>]
|
|
390
|
+
|
|
391
|
+
HOW IT WORKS
|
|
392
|
+
.tempjs-version.json stores the git commit recorded at each version bump.
|
|
393
|
+
"check" diffs tracked paths since that commit (+ uncommitted changes).
|
|
394
|
+
|
|
395
|
+
CLI tracks: cli/, package.json, templates.json
|
|
396
|
+
Templates track: packages/core/, template directory, sync script
|
|
397
|
+
|
|
398
|
+
EXAMPLES
|
|
399
|
+
tempjs version check
|
|
400
|
+
tempjs version check hotel
|
|
401
|
+
tempjs version inc patch cli
|
|
402
|
+
tempjs version inc minor hotel
|
|
403
|
+
tempjs version inc patch all
|
|
404
|
+
|
|
405
|
+
See VERSIONING.md in the repo for the full maintainer guide.
|
|
406
|
+
`.trim());
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (
|
|
411
|
+
command === "theme" ||
|
|
412
|
+
command === "font" ||
|
|
413
|
+
command === "brand" ||
|
|
414
|
+
command === "init-db" ||
|
|
415
|
+
command === "doctor"
|
|
416
|
+
) {
|
|
417
|
+
const targetDir = process.cwd();
|
|
418
|
+
|
|
419
|
+
if (command === "doctor") {
|
|
420
|
+
const code = await runDoctor(targetDir);
|
|
421
|
+
if (code !== 0) process.exitCode = 1;
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
|
|
270
425
|
const currentConfig = getSavedConfig(targetDir);
|
|
271
426
|
|
|
272
427
|
if (command === "theme") {
|
|
273
|
-
const selectedTheme = await promptTheme(currentConfig.theme || "theme1");
|
|
274
|
-
await
|
|
428
|
+
const selectedTheme = await promptTheme(currentConfig.theme || "theme1", cliOptions);
|
|
429
|
+
const selectedFont = await promptFont(currentConfig.font || "default", {
|
|
430
|
+
...cliOptions,
|
|
431
|
+
yes: cliOptions.yes || Boolean(cliOptions.font),
|
|
432
|
+
});
|
|
433
|
+
await applyThemeAndFont(targetDir, selectedTheme, selectedFont);
|
|
275
434
|
} else if (command === "font") {
|
|
276
|
-
const selectedFont = await promptFont(currentConfig.font || "default");
|
|
277
|
-
await applyThemeAndFont(
|
|
435
|
+
const selectedFont = await promptFont(currentConfig.font || "default", cliOptions);
|
|
436
|
+
await applyThemeAndFont(
|
|
437
|
+
targetDir,
|
|
438
|
+
currentConfig.theme || "theme1",
|
|
439
|
+
selectedFont
|
|
440
|
+
);
|
|
278
441
|
} else if (command === "brand") {
|
|
279
|
-
|
|
442
|
+
const stamp = readProjectStamp(targetDir);
|
|
443
|
+
await promptAndApplyBrand(targetDir, cliOptions, stamp?.template);
|
|
280
444
|
} else if (command === "init-db") {
|
|
281
|
-
await promptAndSetupDb(targetDir);
|
|
445
|
+
await promptAndSetupDb(targetDir, cliOptions);
|
|
282
446
|
}
|
|
283
447
|
return;
|
|
284
448
|
}
|
|
285
449
|
|
|
286
|
-
const manifest = loadManifest();
|
|
287
|
-
|
|
288
450
|
let runWithConfig = flags.config;
|
|
289
451
|
if (positionals.length >= 2 && positionals[1] === "config") {
|
|
290
452
|
runWithConfig = true;
|
package/cli/info.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { loadManifest, resolveRepositoryConfig } from "./config.js";
|
|
2
|
+
import { getTemplateBrandFields } from "./init-options.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param {string} templateId
|
|
6
|
+
* @param {import('./config.js').TemplateEntry} entry
|
|
7
|
+
* @param {import('./config.js').RepositoryConfig} repo
|
|
8
|
+
*/
|
|
9
|
+
export function printTemplateInfo(templateId, entry, repo) {
|
|
10
|
+
const lines = [];
|
|
11
|
+
|
|
12
|
+
lines.push(`${entry.name} (${templateId})`);
|
|
13
|
+
lines.push("=".repeat(Math.min(60, entry.name.length + templateId.length + 4)));
|
|
14
|
+
lines.push("");
|
|
15
|
+
|
|
16
|
+
if (entry.description) {
|
|
17
|
+
lines.push(entry.description);
|
|
18
|
+
lines.push("");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (entry.version) {
|
|
22
|
+
lines.push(`Version: ${entry.version}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (entry.stack?.length) {
|
|
26
|
+
lines.push(`Stack: ${entry.stack.join(", ")}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (entry.node) {
|
|
30
|
+
lines.push(`Node.js: ${entry.node}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (entry.packageManager) {
|
|
34
|
+
lines.push(`Package manager: ${entry.packageManager}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (entry.setupTime) {
|
|
38
|
+
lines.push(`Typical setup: ${entry.setupTime}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (entry.docker !== undefined) {
|
|
42
|
+
lines.push(`Docker support: ${entry.docker ? "Yes (docker-compose.yml)" : "No"}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (entry.tags?.length) {
|
|
46
|
+
lines.push(`Tags: ${entry.tags.join(", ")}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
lines.push(`Source directory: ${repo.templatesPath}/${entry.directory}`);
|
|
50
|
+
lines.push(`Repository: github.com/${repo.owner}/${repo.repo} (${repo.branch})`);
|
|
51
|
+
|
|
52
|
+
if (entry.features?.length) {
|
|
53
|
+
lines.push("");
|
|
54
|
+
lines.push("Features:");
|
|
55
|
+
for (const feature of entry.features) {
|
|
56
|
+
lines.push(` • ${feature}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (entry.coreModules?.length) {
|
|
61
|
+
lines.push("");
|
|
62
|
+
lines.push(`Core modules: ${entry.coreModules.join(", ")}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (entry.optionalModuleExamples?.length) {
|
|
66
|
+
lines.push(`Optional examples: ${entry.optionalModuleExamples.join(", ")}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (entry.templateModules?.length) {
|
|
70
|
+
lines.push(`Template modules: ${entry.templateModules.join(", ")}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
lines.push("");
|
|
74
|
+
lines.push("Quick start:");
|
|
75
|
+
lines.push(` mkdir my-project && cd my-project`);
|
|
76
|
+
lines.push(` tempjs ${templateId} config`);
|
|
77
|
+
lines.push(` pnpm install && pnpm dev`);
|
|
78
|
+
lines.push("");
|
|
79
|
+
lines.push("Non-interactive:");
|
|
80
|
+
const brandFields = getTemplateBrandFields(entry);
|
|
81
|
+
const flagPreview = brandFields
|
|
82
|
+
.slice(0, 4)
|
|
83
|
+
.map((f) => f.flag)
|
|
84
|
+
.join(" ");
|
|
85
|
+
lines.push(
|
|
86
|
+
` tempjs ${templateId} --config --yes --theme theme1 ${flagPreview} --db-host localhost`
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
if (entry.init?.brandFields?.length) {
|
|
90
|
+
lines.push("");
|
|
91
|
+
lines.push("Init options for this template:");
|
|
92
|
+
for (const field of brandFields) {
|
|
93
|
+
lines.push(` ${field.flag.padEnd(22)} ${field.description}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (entry.docs) {
|
|
98
|
+
lines.push("");
|
|
99
|
+
lines.push(`Docs in generated project: ${entry.docs}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
console.log(lines.join("\n"));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @param {Record<string, import('./config.js').TemplateEntry>} templates
|
|
107
|
+
* @param {import('./config.js').RepositoryConfig} repo
|
|
108
|
+
*/
|
|
109
|
+
export function printAllTemplatesInfo(templates, repo) {
|
|
110
|
+
const ids = Object.keys(templates).sort();
|
|
111
|
+
for (let i = 0; i < ids.length; i++) {
|
|
112
|
+
if (i > 0) console.log("\n");
|
|
113
|
+
printTemplateInfo(ids[i], templates[ids[i]], repo);
|
|
114
|
+
}
|
|
115
|
+
}
|