zhangdocs 1.1.29 → 1.1.34

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/.bin/zhangdocs.js CHANGED
@@ -18,9 +18,11 @@ commander
18
18
  .option('-b, build', 'Markdown produces static pages document.')
19
19
  .option('-w, watch', 'Listener "md" file is automatically generated pages.')
20
20
  .option('-s, server', 'Open local static html server.')
21
+ .option('-d, --daemon', 'Run server in background (use with -s). Close terminal safely.')
22
+ .option('--stop', 'Stop background server started with -s -d.')
21
23
  .option('-c, clean', 'Clear the generate static files.')
22
24
  .option('-t, theme', 'Choose a theme.')
23
- .option('-d, deploy', 'Publish to a gh-pages branch on GitHub.')
25
+ .option('--deploy', 'Publish to a gh-pages branch on GitHub.')
24
26
  .option('-p, pdf', 'PDF generation.')
25
27
 
26
28
  commander
@@ -31,9 +33,11 @@ commander
31
33
  log(' $ zhangdocs init [path] ');
32
34
  log(' $ zhangdocs init [path] -C ~/zhangdocs/');
33
35
  log(' $ zhangdocs watch');
34
- log(' $ zhangdocs server');
35
- log(' $ zhangdocs clean');
36
- log(' $ zhangdocs deploy');
36
+ log(' $ doc -s');
37
+ log(' $ doc -s -d # server in background');
38
+ log(' $ doc -s --stop # stop background server');
39
+ log(' $ doc clean');
40
+ log(' $ doc --deploy');
37
41
  log(' $ zhangdocs theme');
38
42
  log(' $ zhangdocs -t ~/git/zhangdocs-theme-slate/');
39
43
 
@@ -0,0 +1 @@
1
+ [2026-05-16T11:05:28.652Z] doc server started (PID 34124)
package/README.md CHANGED
@@ -32,6 +32,8 @@ npm install -g zhangdocs
32
32
  npm install zhangdocs
33
33
  ```
34
34
 
35
+ 安装完成后,可以使用 `doc` 命令来操作项目。
36
+
35
37
  ## 🚀 快速开始
36
38
 
37
39
  ### 1. 初始化项目
@@ -39,13 +41,13 @@ npm install zhangdocs
39
41
  在项目目录下运行:
40
42
 
41
43
  ```bash
42
- zhangdocs init
44
+ doc init
43
45
  ```
44
46
 
45
47
  或者指定项目名称:
46
48
 
47
49
  ```bash
48
- zhangdocs init --Create my-docs
50
+ doc init --Create my-docs
49
51
  ```
50
52
 
51
53
  初始化过程会引导你配置项目信息,包括:
@@ -75,7 +77,7 @@ zhangdocs init --Create my-docs
75
77
  ### 3. 构建文档
76
78
 
77
79
  ```bash
78
- zhangdocs build
80
+ doc build
79
81
  ```
80
82
 
81
83
  构建完成后,HTML 文件将生成在 `html/` 目录下,首页 `index.html` 会生成在项目根目录。
@@ -83,15 +85,29 @@ zhangdocs build
83
85
  ### 4. 启动开发服务器
84
86
 
85
87
  ```bash
86
- zhangdocs server
88
+ doc -s
87
89
  ```
88
90
 
89
91
  这将启动一个本地服务器,并监听文件变化自动重新构建。
90
92
 
93
+ 后台运行(关闭命令行窗口后服务仍继续):
94
+
95
+ ```bash
96
+ doc -s -d
97
+ ```
98
+
99
+ 停止后台服务:
100
+
101
+ ```bash
102
+ doc -s --stop
103
+ ```
104
+
105
+ 日志文件位于项目目录下的 `.zhangdocs/server.log`。
106
+
91
107
  ### 5. 监听文件变化
92
108
 
93
109
  ```bash
94
- zhangdocs watch
110
+ doc watch
95
111
  ```
96
112
 
97
113
  监听 `md/` 目录下的文件变化,自动重新构建。
@@ -100,14 +116,16 @@ zhangdocs watch
100
116
 
101
117
  | 命令 | 说明 |
102
118
  |------|------|
103
- | `zhangdocs init` | 初始化项目 |
104
- | `zhangdocs build` | 构建静态页面 |
105
- | `zhangdocs watch` | 监听文件变化并自动构建 |
106
- | `zhangdocs server` | 启动开发服务器(包含 watch 功能) |
107
- | `zhangdocs clean` | 清理构建文件 |
108
- | `zhangdocs deploy` | 部署文档 |
109
- | `zhangdocs theme` | 主题管理 |
110
- | `zhangdocs -V` | 查看版本号 |
119
+ | `doc init` | 初始化项目 |
120
+ | `doc build` | 构建静态页面 |
121
+ | `doc watch` | 监听文件变化并自动构建 |
122
+ | `doc -s` | 启动开发服务器(包含 watch 功能) |
123
+ | `doc -s -d` | 后台启动开发服务器,可关闭终端 |
124
+ | `doc -s --stop` | 停止后台开发服务器 |
125
+ | `doc clean` | 清理构建文件 |
126
+ | `doc --deploy` | 部署文档 |
127
+ | `doc theme` | 主题管理 |
128
+ | `doc -V` | 查看版本号 |
111
129
 
112
130
  ## ⚙️ 配置说明
113
131
 
@@ -210,7 +228,7 @@ $$\int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi}$$
210
228
 
211
229
  1. 修改现有主题的样式和模板
212
230
  2. 创建新主题
213
- 3. 使用 `zhangdocs theme` 命令管理主题
231
+ 3. 使用 `doc theme` 命令管理主题
214
232
 
215
233
  ## 📝 注意事项
216
234
 
@@ -218,19 +236,3 @@ $$\int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi}$$
218
236
  2. **图片路径**:图片应放在 `static/img/` 目录下,在 Markdown 中使用相对路径引用
219
237
  3. **数学公式**:使用 `$...$` 表示行内公式,`$$...$$` 表示块级公式
220
238
  4. **Mermaid 图表**:在代码块中指定语言为 `mermaid`,工具会自动处理特殊字符转义
221
-
222
- ## 📄 许可证
223
-
224
- MIT License
225
-
226
- ## 👤 作者
227
-
228
- kenny wang <wowohoo@qq.com>
229
-
230
- ## 🔗 相关链接
231
-
232
- - [GitHub 仓库](https://github.com/zhangrenyang/zhangdocs.git)
233
-
234
- ---
235
-
236
- **Happy Documenting! 📚**
package/index.js CHANGED
@@ -8,7 +8,7 @@ var pdf = require('./lib/pdf');
8
8
  var theme = require('./lib/theme');
9
9
  var color = require('colors-cli');
10
10
  var path = require('path');
11
- var server = require('ssr');
11
+ var runServer = require('./lib/server');
12
12
 
13
13
  module.exports = function (commander) {
14
14
  var pkgurl = path.resolve('package.json');
@@ -36,9 +36,15 @@ module.exports = function (commander) {
36
36
  if (commander.watch) {
37
37
  return watch(commander, build);
38
38
  }
39
+ if (commander.stop) {
40
+ return runServer.stop();
41
+ }
39
42
  if (commander.server) {
40
- server();
41
- return watch(commander, build);
43
+ return runServer(commander, {
44
+ build: build,
45
+ server: require('ssr'),
46
+ watch: watch
47
+ });
42
48
  }
43
49
  if (commander.clean) {
44
50
  return clean(commander);
package/lib/build.js CHANGED
@@ -144,15 +144,12 @@ function build(commander, changeFile) {
144
144
  var _path;
145
145
  var tocHTML = '';//单页面导航静态页面
146
146
  var navHTML = '';
147
- var pathArr = file.readMDSync(todir + 'md/');
148
-
149
-
150
- if (changeFile) pathArr = [changeFile];
147
+ var allPathArr = file.readMDSync(todir + 'md/');
151
148
 
152
149
  // 按文件名前面的数字排序
153
150
  // 如果文件名以数字开头(格式:数字.标题.md),按数字排序
154
151
  // 如果文件名不以数字开头,排在有序号的后面
155
- pathArr.sort(function(a, b) {
152
+ allPathArr.sort(function(a, b) {
156
153
  var aBasename = path.basename(a, '.md');
157
154
  var bBasename = path.basename(b, '.md');
158
155
 
@@ -179,8 +176,22 @@ function build(commander, changeFile) {
179
176
  return aBasename.localeCompare(bBasename);
180
177
  });
181
178
 
179
+ // 增量编译时只写变更文件,但上一节/下一节、导航使用全量文件列表计算
180
+ var buildPathArr = allPathArr;
181
+ if (changeFile) {
182
+ var normalizedChangeFile = path.normalize(changeFile);
183
+ var matchedFile = null;
184
+ for (var c = 0; c < allPathArr.length; c++) {
185
+ if (path.normalize(allPathArr[c]) === normalizedChangeFile) {
186
+ matchedFile = allPathArr[c];
187
+ break;
188
+ }
189
+ }
190
+ buildPathArr = [matchedFile || normalizedChangeFile];
191
+ }
192
+
182
193
  // 先处理所有md文件,包括第一个md文件
183
- pathArr.forEach(function (item, idx) {
194
+ buildPathArr.forEach(function (item) {
184
195
  //菜单层级
185
196
  var isIndex = false
186
197
  var len = item.replace(process.cwd() + '/md/', '').split('/').length;
@@ -195,7 +206,7 @@ function build(commander, changeFile) {
195
206
  var _path = path.normalize(process.cwd() + '/html/' + itemRel).replace('.md', ".html");
196
207
 
197
208
  //导航菜单生成 - 传入HTML路径作为当前页面
198
- var navHTML = nav(zhangdocsmd, _path, pathArr, len);
209
+ var navHTML = nav(zhangdocsmd, _path, allPathArr, len);
199
210
 
200
211
  let mdContent = file.read(item);
201
212
  // 自动把 \[ ... \] 替换为 $$ ... $$
@@ -225,9 +236,17 @@ function build(commander, changeFile) {
225
236
  var prevTitle = null;
226
237
  var nextTitle = null;
227
238
 
228
- if (idx > 0) {
239
+ var currentIndex = -1;
240
+ for (var i = 0; i < allPathArr.length; i++) {
241
+ if (path.normalize(allPathArr[i]) === path.normalize(item)) {
242
+ currentIndex = i;
243
+ break;
244
+ }
245
+ }
246
+
247
+ if (currentIndex > 0) {
229
248
  // 上一节
230
- var prevItem = pathArr[idx - 1];
249
+ var prevItem = allPathArr[currentIndex - 1];
231
250
  var prevItemRel = path.normalize(prevItem);
232
251
  prevItemRel = prevItemRel.replace(path.normalize(process.cwd() + '/md/'), '');
233
252
  var prevPath = path.normalize(process.cwd() + '/html/' + prevItemRel).replace('.md', ".html");
@@ -247,9 +266,9 @@ function build(commander, changeFile) {
247
266
  prevTitle = path.basename(prevItem, '.md');
248
267
  }
249
268
 
250
- if (idx < pathArr.length - 1) {
269
+ if (currentIndex > -1 && currentIndex < allPathArr.length - 1) {
251
270
  // 下一节
252
- var nextItem = pathArr[idx + 1];
271
+ var nextItem = allPathArr[currentIndex + 1];
253
272
  var nextItemRel = path.normalize(nextItem);
254
273
  nextItemRel = nextItemRel.replace(path.normalize(process.cwd() + '/md/'), '');
255
274
  var nextPath = path.normalize(process.cwd() + '/html/' + nextItemRel).replace('.md', ".html");
@@ -291,8 +310,8 @@ function build(commander, changeFile) {
291
310
 
292
311
  // 单独生成index.html作为目录页面
293
312
  var indexPath = path.normalize(process.cwd() + '/index.html');
294
- var indexNavHTML = nav(zhangdocsmd, indexPath, pathArr, 0);
295
- var indexMarkedstr = generateIndexPage(zhangdocsmd, pathArr, indexPath, file.relativePath(indexPath, process.cwd()));
313
+ var indexNavHTML = nav(zhangdocsmd, indexPath, allPathArr, 0);
314
+ var indexMarkedstr = generateIndexPage(zhangdocsmd, allPathArr, indexPath, file.relativePath(indexPath, process.cwd()));
296
315
 
297
316
  html = file.ejs(template + '/layout.ejs', {
298
317
  title: pkg.name,//项目工程名字
@@ -0,0 +1,63 @@
1
+ /**
2
+ * 后台服务模式子进程:启动静态服务 + 监听 md 变更
3
+ * 由 lib/server.js 以 detached 方式拉起,勿直接调用
4
+ */
5
+ var fs = require('fs');
6
+ var path = require('path');
7
+ var file = require('./file');
8
+ var build = require('./build');
9
+ var watch = require('./watch');
10
+ var server = require('ssr');
11
+
12
+ var pidDir = path.join(process.cwd(), '.zhangdocs');
13
+ var pidFile = path.join(pidDir, 'server.pid');
14
+ var logFile = path.join(pidDir, 'server.log');
15
+
16
+ function appendLog(line) {
17
+ try {
18
+ if (!fs.existsSync(pidDir)) {
19
+ file.mkdirsSync(pidDir, 0o777);
20
+ }
21
+ fs.appendFileSync(logFile, line + '\n', 'utf8');
22
+ } catch (e) {
23
+ // ignore
24
+ }
25
+ }
26
+
27
+ function cleanup() {
28
+ try {
29
+ if (fs.existsSync(pidFile)) {
30
+ fs.unlinkSync(pidFile);
31
+ }
32
+ } catch (e) {
33
+ // ignore
34
+ }
35
+ }
36
+
37
+ process.on('SIGINT', function () {
38
+ cleanup();
39
+ process.exit(0);
40
+ });
41
+ process.on('SIGTERM', function () {
42
+ cleanup();
43
+ process.exit(0);
44
+ });
45
+ process.on('exit', cleanup);
46
+
47
+ try {
48
+ if (!fs.existsSync(pidDir)) {
49
+ file.mkdirsSync(pidDir, 0o777);
50
+ }
51
+ fs.writeFileSync(pidFile, String(process.pid), 'utf8');
52
+
53
+ var commanderStub = {};
54
+ build(commanderStub);
55
+ server();
56
+ watch(commanderStub, build);
57
+
58
+ appendLog('[' + new Date().toISOString() + '] doc server started (PID ' + process.pid + ')');
59
+ } catch (err) {
60
+ appendLog('[' + new Date().toISOString() + '] doc server failed: ' + (err && err.stack ? err.stack : err));
61
+ cleanup();
62
+ process.exit(1);
63
+ }
package/lib/server.js ADDED
@@ -0,0 +1,135 @@
1
+ var fs = require('fs');
2
+ var path = require('path');
3
+ var spawn = require('child_process').spawn;
4
+ var color = require('colors-cli');
5
+ var file = require('./file');
6
+
7
+ var PID_DIR = '.zhangdocs';
8
+ var PID_NAME = 'server.pid';
9
+ var LOG_NAME = 'server.log';
10
+
11
+ function pidPath() {
12
+ return path.join(process.cwd(), PID_DIR, PID_NAME);
13
+ }
14
+
15
+ function logPath() {
16
+ return path.join(process.cwd(), PID_DIR, LOG_NAME);
17
+ }
18
+
19
+ function ensurePidDir() {
20
+ var dir = path.join(process.cwd(), PID_DIR);
21
+ if (!fs.existsSync(dir)) {
22
+ file.mkdirsSync(dir, 0o777);
23
+ }
24
+ return dir;
25
+ }
26
+
27
+ function isProcessAlive(pid) {
28
+ if (!pid || pid <= 0) {
29
+ return false;
30
+ }
31
+ try {
32
+ process.kill(pid, 0);
33
+ return true;
34
+ } catch (e) {
35
+ return false;
36
+ }
37
+ }
38
+
39
+ function readPid() {
40
+ var file = pidPath();
41
+ if (!fs.existsSync(file)) {
42
+ return null;
43
+ }
44
+ var pid = parseInt(String(fs.readFileSync(file, 'utf8')).trim(), 10);
45
+ return isNaN(pid) ? null : pid;
46
+ }
47
+
48
+ function clearPidFile() {
49
+ try {
50
+ if (fs.existsSync(pidPath())) {
51
+ fs.unlinkSync(pidPath());
52
+ }
53
+ } catch (e) {
54
+ // ignore
55
+ }
56
+ }
57
+
58
+ function killPid(pid) {
59
+ if (process.platform === 'win32') {
60
+ spawn('taskkill', ['/PID', String(pid), '/T', '/F'], {
61
+ stdio: 'ignore',
62
+ windowsHide: true
63
+ });
64
+ } else {
65
+ process.kill(pid, 'SIGTERM');
66
+ }
67
+ }
68
+
69
+ function stopBackground() {
70
+ var pid = readPid();
71
+ if (!pid || !isProcessAlive(pid)) {
72
+ clearPidFile();
73
+ console.log(color.yellow('\n 未发现运行中的后台 doc server。\n'));
74
+ return;
75
+ }
76
+ killPid(pid);
77
+ clearPidFile();
78
+ console.log(color.green('\n 已停止后台 doc server (PID ' + pid + ')。\n'));
79
+ }
80
+
81
+ function startForeground(commander, build, server, watch) {
82
+ build(commander);
83
+ server();
84
+ watch(commander, build);
85
+ }
86
+
87
+ function startDaemon(commander, build) {
88
+ var pid = readPid();
89
+ if (pid && isProcessAlive(pid)) {
90
+ console.log(color.yellow('\n doc server 已在后台运行 (PID ' + pid + ')。'));
91
+ console.log(' 日志: ' + logPath());
92
+ console.log(' 停止: doc -s --stop\n');
93
+ return;
94
+ }
95
+ if (pid) {
96
+ clearPidFile();
97
+ }
98
+
99
+ ensurePidDir();
100
+ build(commander);
101
+
102
+ var worker = path.join(__dirname, 'server-worker.js');
103
+ var child = spawn(process.execPath, [worker], {
104
+ cwd: process.cwd(),
105
+ detached: true,
106
+ stdio: 'ignore',
107
+ windowsHide: true,
108
+ env: process.env
109
+ });
110
+
111
+ child.unref();
112
+
113
+ console.log(color.green('\n doc server 已在后台启动。'));
114
+ console.log(' 可关闭当前命令行窗口,服务会继续运行。');
115
+ console.log(' 日志: ' + logPath());
116
+ console.log(' 停止: doc -s --stop\n');
117
+ }
118
+
119
+ /**
120
+ * @param {object} commander
121
+ * @param {{ build: Function, server: Function, watch: Function }} deps
122
+ */
123
+ function runServer(commander, deps) {
124
+ if (commander.stop) {
125
+ return stopBackground();
126
+ }
127
+ if (commander.daemon) {
128
+ return startDaemon(commander, deps.build);
129
+ }
130
+ return startForeground(commander, deps.build, deps.server, deps.watch);
131
+ }
132
+
133
+ runServer.stop = stopBackground;
134
+
135
+ module.exports = runServer;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zhangdocs",
3
- "version": "1.1.29",
3
+ "version": "1.1.34",
4
4
  "description": "Simple document generation tool. Dependence Node.js run.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -20,3 +20,4 @@ Thumbs.db
20
20
  node_modules
21
21
  .cache
22
22
  zhangdocs-theme-*/
23
+ .zhangdocs/
@@ -34,13 +34,23 @@
34
34
  mermaid.initialize({
35
35
  startOnLoad: true,
36
36
  theme: 'default',
37
+ flowchart: {
38
+ useMaxWidth: true,
39
+ htmlLabels: false,
40
+ wrappingWidth: 180,
41
+ nodeSpacing: 45,
42
+ rankSpacing: 50,
43
+ padding: 16
44
+ },
45
+ themeCSS: '.mermaid .label text, .mermaid .nodeLabel { dominant-baseline: central; }',
37
46
  themeVariables: {
38
47
  primaryColor: '#ff6b6b',
39
48
  primaryTextColor: '#333',
40
49
  primaryBorderColor: '#ff6b6b',
41
50
  lineColor: '#333',
42
51
  secondaryColor: '#f8f9fa',
43
- tertiaryColor: '#fff'
52
+ tertiaryColor: '#fff',
53
+ fontSize: '10px'
44
54
  }
45
55
  });
46
56
  });