@docubook/cli 0.2.4 → 0.2.5
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/package.json +1 -1
- package/src/cli/program.js +117 -0
- package/src/index.js +2 -2
- package/src/tui/ascii.js +1 -19
- package/src/utils/packageManager.js +0 -26
- package/templates.json +17 -0
package/package.json
CHANGED
package/src/cli/program.js
CHANGED
|
@@ -9,6 +9,107 @@ import { detectPackageManager, getPackageManagerInfo, getPackageManagerVersion }
|
|
|
9
9
|
import { getAvailableTemplates, getTemplate, getDefaultTemplate } from "../utils/templateDetect.js";
|
|
10
10
|
import { execSync } from "child_process";
|
|
11
11
|
import ora from "ora";
|
|
12
|
+
import fs from "fs";
|
|
13
|
+
import os from "os";
|
|
14
|
+
import path from "path";
|
|
15
|
+
|
|
16
|
+
// Helpers to show changelog once per installed version. Stores shown versions under
|
|
17
|
+
// $HOME/.docubook_cli_seen_changelogs.json as a map: { "@docubook/cli": ["1.2.3"] }
|
|
18
|
+
const _CHANGELOG_STORE = path.join(os.homedir(), ".docubook_cli_seen_changelogs.json");
|
|
19
|
+
function _readChangelogStore() {
|
|
20
|
+
try {
|
|
21
|
+
const raw = fs.readFileSync(_CHANGELOG_STORE, "utf8");
|
|
22
|
+
return JSON.parse(raw || "{}");
|
|
23
|
+
} catch {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function _writeChangelogStore(obj) {
|
|
28
|
+
try {
|
|
29
|
+
fs.writeFileSync(_CHANGELOG_STORE, JSON.stringify(obj, null, 2), { mode: 0o600 });
|
|
30
|
+
} catch {
|
|
31
|
+
// non-fatal
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function _fetchChangelogFromGitHub(tag) {
|
|
36
|
+
// Try to fetch CHANGELOG.md from the repo tag. Support several tag-name variants
|
|
37
|
+
// (e.g. v1.2.3, 1.2.3, cli-v1.2.3, cli-1.2.3) and common filename variants.
|
|
38
|
+
const repo = "DocuBook/docubook";
|
|
39
|
+
|
|
40
|
+
const bare = tag.replace(/^v/, "");
|
|
41
|
+
const variants = [tag, bare, `cli-${tag}`, `cli-${bare}`].filter(Boolean);
|
|
42
|
+
|
|
43
|
+
const candidates = [];
|
|
44
|
+
for (const v of variants) {
|
|
45
|
+
candidates.push(`https://raw.githubusercontent.com/${repo}/${v}/CHANGELOG.md`);
|
|
46
|
+
candidates.push(`https://raw.githubusercontent.com/${repo}/${v}/CHANGELOG.MD`);
|
|
47
|
+
}
|
|
48
|
+
// final fallback to main branch
|
|
49
|
+
candidates.push(`https://raw.githubusercontent.com/${repo}/main/CHANGELOG.md`);
|
|
50
|
+
|
|
51
|
+
for (const url of candidates) {
|
|
52
|
+
try {
|
|
53
|
+
const res = await fetch(url);
|
|
54
|
+
if (res && res.ok) return await res.text();
|
|
55
|
+
} catch {
|
|
56
|
+
// ignore and try next
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function _extractVersionSection(changelogText, version) {
|
|
63
|
+
if (!changelogText) return null;
|
|
64
|
+
const lines = changelogText.split(/\r?\n/);
|
|
65
|
+
// Look for headings that include the version (e.g. "## v1.2.3" or "## 1.2.3")
|
|
66
|
+
const headerRe = new RegExp(`^#{1,3}\\s*(?:v)?${version.replace(/\./g, "\\.")}(?:\\b|\\D)`, "i");
|
|
67
|
+
let start = -1;
|
|
68
|
+
for (let i = 0; i < lines.length; i++) {
|
|
69
|
+
if (headerRe.test(lines[i])) {
|
|
70
|
+
start = i;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (start === -1) return changelogText.slice(0, 2000); // fallback: return beginning of changelog
|
|
75
|
+
|
|
76
|
+
let end = lines.length;
|
|
77
|
+
for (let j = start + 1; j < lines.length; j++) {
|
|
78
|
+
if (/^#{1,3}\s*/.test(lines[j])) {
|
|
79
|
+
end = j;
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return lines.slice(start, end).join("\n");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function showChangelogOnce(pkgName, version) {
|
|
87
|
+
try {
|
|
88
|
+
const store = _readChangelogStore();
|
|
89
|
+
const seen = Array.isArray(store[pkgName]) ? store[pkgName] : [];
|
|
90
|
+
if (seen.includes(version)) return;
|
|
91
|
+
|
|
92
|
+
const tag = version.startsWith("v") ? version : `v${version}`;
|
|
93
|
+
const changelog = await _fetchChangelogFromGitHub(tag);
|
|
94
|
+
if (!changelog) return;
|
|
95
|
+
|
|
96
|
+
const section = _extractVersionSection(changelog, version);
|
|
97
|
+
if (!section) return;
|
|
98
|
+
|
|
99
|
+
// Print a concise changelog section
|
|
100
|
+
console.log("\n=== DocuBook CLI changelog (new) ===\n");
|
|
101
|
+
console.log(section.trim());
|
|
102
|
+
console.log("\nFor full changelog, visit:");
|
|
103
|
+
console.log(` https://github.com/DocuBook/docubook/blob/main/CHANGELOG.md\n`);
|
|
104
|
+
|
|
105
|
+
// Mark as shown
|
|
106
|
+
store[pkgName] = Array.from(new Set([...seen, version]));
|
|
107
|
+
_writeChangelogStore(store);
|
|
108
|
+
} catch {
|
|
109
|
+
// silent on any error - changelog is a nicety
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
12
113
|
|
|
13
114
|
/**
|
|
14
115
|
* Initializes the CLI program
|
|
@@ -59,6 +160,12 @@ export function initializeProgram(version) {
|
|
|
59
160
|
try {
|
|
60
161
|
execSync(cmd, { stdio: "inherit" });
|
|
61
162
|
console.log(`Successfully updated to ${latest}`);
|
|
163
|
+
// Try to show changelog for the newly installed version once
|
|
164
|
+
try {
|
|
165
|
+
await showChangelogOnce(pkgName, latest);
|
|
166
|
+
} catch {
|
|
167
|
+
// non-fatal
|
|
168
|
+
}
|
|
62
169
|
} catch (installErr) {
|
|
63
170
|
// If install fails, provide a helpful message
|
|
64
171
|
console.error(`Update failed: ${installErr.message || installErr}`);
|
|
@@ -73,6 +180,16 @@ export function initializeProgram(version) {
|
|
|
73
180
|
}
|
|
74
181
|
});
|
|
75
182
|
|
|
183
|
+
// Expose a `version` subcommand: `docubook version`
|
|
184
|
+
program
|
|
185
|
+
.command('version')
|
|
186
|
+
.description('Print the DocuBook CLI version')
|
|
187
|
+
.action(() => {
|
|
188
|
+
console.log(`DocuBook CLI ${version}`);
|
|
189
|
+
console.log("Run 'docubook update' to check for updates.");
|
|
190
|
+
process.exit(0);
|
|
191
|
+
});
|
|
192
|
+
|
|
76
193
|
// Default behavior (create project)
|
|
77
194
|
program
|
|
78
195
|
.argument("[directory]", "The name of the project directory")
|
package/src/index.js
CHANGED
|
@@ -14,9 +14,9 @@ const packageJsonPath = path.join(__dirname, '..', 'package.json');
|
|
|
14
14
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
15
15
|
const VERSION = packageJson.version;
|
|
16
16
|
|
|
17
|
-
// Handle --version / -
|
|
17
|
+
// Handle --version / -v early to print custom output
|
|
18
18
|
const args = process.argv.slice(2);
|
|
19
|
-
if (args.includes('--version') || args.includes('-
|
|
19
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
20
20
|
console.log(`DocuBook CLI ${VERSION}`);
|
|
21
21
|
console.log("Run 'docubook update' to check for updates.");
|
|
22
22
|
process.exit(0);
|
package/src/tui/ascii.js
CHANGED
|
@@ -62,7 +62,7 @@ export function createWelcomeBanner(version) {
|
|
|
62
62
|
const logoMid = `${colors.cyan}${colors.bright}▌>_▐${colors.reset}`
|
|
63
63
|
const logoBot = `${colors.cyan}▙▄▄▟${colors.reset}`
|
|
64
64
|
|
|
65
|
-
const title = `${colors.cyan}${colors.bright}DocuBook${colors.reset} v${version}`
|
|
65
|
+
const title = `${colors.cyan}${colors.bright}DocuBook CLI${colors.reset} v${version}`
|
|
66
66
|
const subtitle = `${colors.gray}Initialize, build, and deploy docs from terminal.${colors.reset}`
|
|
67
67
|
|
|
68
68
|
const tip = `${colors.gray}Visit our documentation.${colors.reset}`
|
|
@@ -103,16 +103,8 @@ export function createBoxedMessage(title, content, color = colors.green) {
|
|
|
103
103
|
const reset = "\x1b[0m";
|
|
104
104
|
const termWidth = process.stdout.columns || 80;
|
|
105
105
|
|
|
106
|
-
// 1. Tentukan total lebar box (termasuk border)
|
|
107
106
|
const boxWidth = Math.min(80, termWidth - 4);
|
|
108
|
-
|
|
109
|
-
// 2. width adalah panjang garis horizontal (─) di atas dan bawah
|
|
110
|
-
// Total lebar box adalah width + 2 (untuk karakter pojok ┌ dan ┐)
|
|
111
107
|
const width = boxWidth - 2;
|
|
112
|
-
|
|
113
|
-
// 3. inner adalah ruang bersih di dalam box untuk teks (tanpa padding spasi)
|
|
114
|
-
// Kita beri padding 2 spasi di kiri dan 2 spasi di kanan (total 4)
|
|
115
|
-
// Jadi: 1(│) + 2(spasi) + inner + 2(spasi) + 1(│) = width + 2
|
|
116
108
|
const inner = (width + 2) - 6;
|
|
117
109
|
|
|
118
110
|
const centerTitle = () => {
|
|
@@ -126,16 +118,11 @@ export function createBoxedMessage(title, content, color = colors.green) {
|
|
|
126
118
|
|
|
127
119
|
const pad = (text = "") => {
|
|
128
120
|
const len = stringWidth(text);
|
|
129
|
-
// Tambahkan spasi hingga tepat mengisi 'inner'
|
|
130
121
|
return text + " ".repeat(Math.max(0, inner - len));
|
|
131
122
|
};
|
|
132
123
|
|
|
133
124
|
const lines = [];
|
|
134
|
-
|
|
135
|
-
// Header
|
|
136
125
|
lines.push(`${color}┌${centerTitle()}┐${reset}`);
|
|
137
|
-
|
|
138
|
-
// Padding atas (opsional)
|
|
139
126
|
lines.push(`${color}│${reset} ${pad("")} ${color}│${reset}`);
|
|
140
127
|
|
|
141
128
|
const items = typeof content === "string"
|
|
@@ -145,15 +132,10 @@ export function createBoxedMessage(title, content, color = colors.green) {
|
|
|
145
132
|
for (const line of items) {
|
|
146
133
|
const wrapped = wrapText(line, inner);
|
|
147
134
|
wrapped.forEach((w) => {
|
|
148
|
-
// Pastikan struktur: │ + spasi(2) + konten + spasi(2) + │
|
|
149
135
|
lines.push(`${color}│${reset} ${pad(w)} ${color}│${reset}`);
|
|
150
136
|
});
|
|
151
137
|
}
|
|
152
|
-
|
|
153
|
-
// Padding bawah (opsional)
|
|
154
138
|
lines.push(`${color}│${reset} ${pad("")} ${color}│${reset}`);
|
|
155
|
-
|
|
156
|
-
// Footer
|
|
157
139
|
lines.push(`${color}└${"─".repeat(width)}┘${reset}`);
|
|
158
140
|
|
|
159
141
|
return "\n" + lines.join("\n") + "\n";
|
|
@@ -1,32 +1,6 @@
|
|
|
1
|
-
import { execSync } from "child_process";
|
|
2
1
|
import fs from "fs";
|
|
3
2
|
import path from "path";
|
|
4
3
|
|
|
5
|
-
/**
|
|
6
|
-
* Gets the version of the specified package manager
|
|
7
|
-
* @param {string} pm - Package manager name
|
|
8
|
-
* @returns {string|null} Version string or null if not installed
|
|
9
|
-
*/
|
|
10
|
-
export function getPackageManagerVersion(pm) {
|
|
11
|
-
try {
|
|
12
|
-
return execSync(`${pm} --version`).toString().trim();
|
|
13
|
-
} catch {
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Detects the default package manager from user environment
|
|
20
|
-
* @returns {string} Default package manager name
|
|
21
|
-
*/
|
|
22
|
-
export function detectDefaultPackageManager() {
|
|
23
|
-
const userAgent = process.env.npm_config_user_agent || "";
|
|
24
|
-
if (userAgent.includes("pnpm")) return "pnpm";
|
|
25
|
-
if (userAgent.includes("yarn")) return "yarn";
|
|
26
|
-
if (userAgent.includes("bun")) return "bun";
|
|
27
|
-
return "npm";
|
|
28
|
-
}
|
|
29
|
-
|
|
30
4
|
/**
|
|
31
5
|
* Updates postcss config file extension for Bun compatibility
|
|
32
6
|
* @param {string} projectPath - Path to the project directory
|
package/templates.json
CHANGED
|
@@ -16,6 +16,23 @@
|
|
|
16
16
|
],
|
|
17
17
|
"url": "https://github.com/DocuBook/docubook/tree/main/packages/template/nextjs-vercel"
|
|
18
18
|
},
|
|
19
|
+
{
|
|
20
|
+
"id": "nextjs-docker",
|
|
21
|
+
"name": "nextjs-docker",
|
|
22
|
+
"description": "Modern documentation with Next.js standalone and Docker deployment",
|
|
23
|
+
"features": [
|
|
24
|
+
"Next.js 16",
|
|
25
|
+
"React 19",
|
|
26
|
+
"TypeScript",
|
|
27
|
+
"Tailwind CSS",
|
|
28
|
+
"MDX Support",
|
|
29
|
+
"Dark Mode",
|
|
30
|
+
"Search (Algolia)",
|
|
31
|
+
"Responsive Design",
|
|
32
|
+
"Docker Deployment"
|
|
33
|
+
],
|
|
34
|
+
"url": "https://github.com/DocuBook/docubook/tree/main/packages/template/nextjs-docker"
|
|
35
|
+
},
|
|
19
36
|
{
|
|
20
37
|
"id": "react-router",
|
|
21
38
|
"name": "react-router",
|