zengen 0.1.2 → 0.1.32
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/.github/workflows/README-PAGES.md +105 -0
- package/.github/workflows/pages.yml +72 -0
- package/.github/workflows/publish.yml +29 -4
- package/README.md +6 -71
- package/dist/builder.d.ts +4 -0
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +63 -7
- package/dist/builder.js.map +1 -1
- package/dist/cli.js +47 -64
- package/dist/cli.js.map +1 -1
- package/dist/markdown.d.ts.map +1 -1
- package/dist/markdown.js +4 -0
- package/dist/markdown.js.map +1 -1
- package/dist/navigation.d.ts +10 -0
- package/dist/navigation.d.ts.map +1 -1
- package/dist/navigation.js +28 -4
- package/dist/navigation.js.map +1 -1
- package/dist/types.d.ts +5 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -2
- package/src/builder.ts +65 -7
- package/src/cli.ts +50 -65
- package/src/markdown.ts +5 -0
- package/src/navigation.ts +35 -4
- package/src/types.ts +5 -0
- package/test-build/test.md +1 -0
package/src/cli.ts
CHANGED
|
@@ -24,15 +24,17 @@ program
|
|
|
24
24
|
// 构建命令
|
|
25
25
|
program
|
|
26
26
|
.command('build')
|
|
27
|
-
.description('Build documentation site from Markdown files')
|
|
28
|
-
.argument('<src-dir>', 'Source directory containing Markdown files')
|
|
29
|
-
.option('-o, --out <dir>', 'Output directory for generated HTML', 'dist')
|
|
27
|
+
.description('Build documentation site from Markdown files in current directory')
|
|
30
28
|
.option('-t, --template <file>', 'Custom template file')
|
|
31
29
|
.option('-w, --watch', 'Watch for changes and rebuild automatically')
|
|
30
|
+
.option('-s, --serve', 'Start HTTP server for preview (requires --watch)')
|
|
31
|
+
.option('-p, --port <number>', 'HTTP server port', '3000')
|
|
32
|
+
.option('--host <host>', 'HTTP server host', 'localhost')
|
|
32
33
|
.option('-v, --verbose', 'Show detailed output')
|
|
33
34
|
.option('-c, --config <file>', 'Configuration file')
|
|
35
|
+
.option('--base-url <url>', 'Base URL for generated links (e.g., https://example.com/docs)')
|
|
34
36
|
.option('--clean', 'Clean output directory before building')
|
|
35
|
-
.action(async (
|
|
37
|
+
.action(async (options) => {
|
|
36
38
|
try {
|
|
37
39
|
// 加载配置文件
|
|
38
40
|
let config: ZenConfig = {};
|
|
@@ -47,15 +49,24 @@ program
|
|
|
47
49
|
}
|
|
48
50
|
}
|
|
49
51
|
|
|
52
|
+
// 强制使用当前目录作为 src 目录,输出到 .zen/dist 目录
|
|
53
|
+
const currentDir = process.cwd();
|
|
54
|
+
const outDir = path.join(currentDir, '.zen', 'dist');
|
|
55
|
+
|
|
50
56
|
// 合并命令行参数和配置
|
|
51
57
|
const buildOptions = {
|
|
52
|
-
srcDir:
|
|
53
|
-
outDir:
|
|
58
|
+
srcDir: currentDir,
|
|
59
|
+
outDir: outDir,
|
|
54
60
|
template: options.template ? path.resolve(options.template) : undefined,
|
|
55
61
|
watch: options.watch,
|
|
56
|
-
|
|
62
|
+
serve: options.serve,
|
|
63
|
+
port: parseInt(options.port, 10),
|
|
64
|
+
host: options.host,
|
|
65
|
+
verbose: options.verbose,
|
|
66
|
+
baseUrl: options.baseUrl || config.baseUrl
|
|
57
67
|
};
|
|
58
68
|
|
|
69
|
+
|
|
59
70
|
const builder = new ZenBuilder(config);
|
|
60
71
|
|
|
61
72
|
// 验证配置
|
|
@@ -66,6 +77,12 @@ program
|
|
|
66
77
|
process.exit(1);
|
|
67
78
|
}
|
|
68
79
|
|
|
80
|
+
// 警告 --serve 选项需要 --watch 选项
|
|
81
|
+
if (options.serve && !options.watch) {
|
|
82
|
+
console.warn('⚠️ Warning: --serve option requires --watch option, ignoring --serve');
|
|
83
|
+
buildOptions.serve = false;
|
|
84
|
+
}
|
|
85
|
+
|
|
69
86
|
// 清理输出目录
|
|
70
87
|
if (options.clean) {
|
|
71
88
|
await builder.clean(buildOptions.outDir);
|
|
@@ -86,12 +103,13 @@ program
|
|
|
86
103
|
// 清理命令
|
|
87
104
|
program
|
|
88
105
|
.command('clean')
|
|
89
|
-
.description('Clean output directory')
|
|
90
|
-
.
|
|
91
|
-
.action(async (dir) => {
|
|
106
|
+
.description('Clean .zen/dist output directory')
|
|
107
|
+
.action(async () => {
|
|
92
108
|
try {
|
|
93
109
|
const builder = new ZenBuilder();
|
|
94
|
-
|
|
110
|
+
const currentDir = process.cwd();
|
|
111
|
+
const outDir = path.join(currentDir, '.zen', 'dist');
|
|
112
|
+
await builder.clean(outDir);
|
|
95
113
|
} catch (error) {
|
|
96
114
|
console.error('❌ Clean failed:', error);
|
|
97
115
|
process.exit(1);
|
|
@@ -108,7 +126,6 @@ program
|
|
|
108
126
|
const targetDir = path.resolve(options.dir);
|
|
109
127
|
|
|
110
128
|
// 创建目录结构
|
|
111
|
-
await fs.mkdir(path.join(targetDir, 'docs'), { recursive: true });
|
|
112
129
|
await fs.mkdir(path.join(targetDir, 'static'), { recursive: true });
|
|
113
130
|
|
|
114
131
|
// 创建示例文档
|
|
@@ -118,9 +135,9 @@ This is an example documentation page generated by ZEN.
|
|
|
118
135
|
|
|
119
136
|
## Getting Started
|
|
120
137
|
|
|
121
|
-
1. Write your documentation in Markdown format
|
|
122
|
-
2. Run \`zengen build
|
|
123
|
-
3. Open the generated HTML files in your browser
|
|
138
|
+
1. Write your documentation in Markdown format in current directory
|
|
139
|
+
2. Run \`zengen build\`
|
|
140
|
+
3. Open the generated HTML files in your browser at .zen/dist
|
|
124
141
|
|
|
125
142
|
## Features
|
|
126
143
|
|
|
@@ -141,15 +158,13 @@ console.log('Hello ZEN!');
|
|
|
141
158
|
*Happy documenting!*`;
|
|
142
159
|
|
|
143
160
|
await fs.writeFile(
|
|
144
|
-
path.join(targetDir, '
|
|
161
|
+
path.join(targetDir, 'index.md'),
|
|
145
162
|
exampleDoc,
|
|
146
163
|
'utf-8'
|
|
147
164
|
);
|
|
148
165
|
|
|
149
166
|
// 创建配置文件
|
|
150
167
|
const config = {
|
|
151
|
-
srcDir: './docs',
|
|
152
|
-
outDir: './dist',
|
|
153
168
|
template: undefined,
|
|
154
169
|
i18n: {
|
|
155
170
|
sourceLang: 'en-US',
|
|
@@ -172,9 +187,10 @@ console.log('Hello ZEN!');
|
|
|
172
187
|
packageJson.scripts = {};
|
|
173
188
|
}
|
|
174
189
|
|
|
175
|
-
packageJson.scripts.build = 'zengen build
|
|
176
|
-
packageJson.scripts['build:watch'] = 'zengen build
|
|
177
|
-
packageJson.scripts
|
|
190
|
+
packageJson.scripts.build = 'zengen build';
|
|
191
|
+
packageJson.scripts['build:watch'] = 'zengen build --watch';
|
|
192
|
+
packageJson.scripts['build:serve'] = 'zengen build --watch --serve';
|
|
193
|
+
packageJson.scripts.clean = 'zengen clean';
|
|
178
194
|
|
|
179
195
|
await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf-8');
|
|
180
196
|
} catch (error) {
|
|
@@ -183,9 +199,10 @@ console.log('Hello ZEN!');
|
|
|
183
199
|
name: 'zen-docs',
|
|
184
200
|
version: '1.0.0',
|
|
185
201
|
scripts: {
|
|
186
|
-
build: 'zengen build
|
|
187
|
-
'build:watch': 'zengen build
|
|
188
|
-
|
|
202
|
+
build: 'zengen build',
|
|
203
|
+
'build:watch': 'zengen build --watch',
|
|
204
|
+
'build:serve': 'zengen build --watch --serve',
|
|
205
|
+
clean: 'zengen clean'
|
|
189
206
|
}
|
|
190
207
|
};
|
|
191
208
|
|
|
@@ -200,14 +217,14 @@ console.log('Hello ZEN!');
|
|
|
200
217
|
🎉 ZEN project initialized successfully!
|
|
201
218
|
|
|
202
219
|
Next steps:
|
|
203
|
-
1. Add your Markdown files to the
|
|
204
|
-
2. Run 'npm run build' to generate the site
|
|
220
|
+
1. Add your Markdown files to the current directory
|
|
221
|
+
2. Run 'npm run build' to generate the site (output will be in .zen/dist)
|
|
205
222
|
3. Run 'npm run build:watch' for development with auto-reload
|
|
223
|
+
4. Run 'npm run build:serve' for development with auto-reload and HTTP server
|
|
206
224
|
|
|
207
225
|
Project structure:
|
|
208
226
|
${targetDir}/
|
|
209
|
-
├──
|
|
210
|
-
│ └── index.md # Example document
|
|
227
|
+
├── index.md # Example document (in current directory)
|
|
211
228
|
├── static/ # Static assets (images, CSS, JS)
|
|
212
229
|
├── zen.config.json # Configuration file
|
|
213
230
|
└── package.json # npm scripts
|
|
@@ -247,49 +264,17 @@ Commands:
|
|
|
247
264
|
info Show this information
|
|
248
265
|
|
|
249
266
|
Examples:
|
|
250
|
-
$ zengen build
|
|
251
|
-
$ zengen build
|
|
267
|
+
$ zengen build
|
|
268
|
+
$ zengen build --watch
|
|
269
|
+
$ zengen build --watch --serve
|
|
270
|
+
$ zengen build --watch --serve --port 8080
|
|
252
271
|
$ zengen init --dir ./my-docs
|
|
253
|
-
$ zengen clean
|
|
272
|
+
$ zengen clean
|
|
254
273
|
|
|
255
274
|
For more help, run: zengen --help
|
|
256
275
|
`);
|
|
257
276
|
});
|
|
258
277
|
|
|
259
|
-
// 默认命令(兼容旧格式)
|
|
260
|
-
program
|
|
261
|
-
.argument('[src-dir]', 'Source directory')
|
|
262
|
-
.option('-o, --out <dir>', 'Output directory')
|
|
263
|
-
.option('-t, --template <file>', 'Custom template file')
|
|
264
|
-
.option('-w, --watch', 'Watch for changes')
|
|
265
|
-
.option('-v, --verbose', 'Show detailed output')
|
|
266
|
-
.action(async (srcDir, options) => {
|
|
267
|
-
if (!srcDir) {
|
|
268
|
-
program.help();
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
try {
|
|
273
|
-
const builder = new ZenBuilder();
|
|
274
|
-
|
|
275
|
-
const buildOptions = {
|
|
276
|
-
srcDir: path.resolve(srcDir),
|
|
277
|
-
outDir: path.resolve(options.out || 'dist'),
|
|
278
|
-
template: options.template ? path.resolve(options.template) : undefined,
|
|
279
|
-
watch: options.watch,
|
|
280
|
-
verbose: options.verbose
|
|
281
|
-
};
|
|
282
|
-
|
|
283
|
-
if (options.watch) {
|
|
284
|
-
await builder.watch(buildOptions);
|
|
285
|
-
} else {
|
|
286
|
-
await builder.build(buildOptions);
|
|
287
|
-
}
|
|
288
|
-
} catch (error) {
|
|
289
|
-
console.error('❌ Build failed:', error);
|
|
290
|
-
process.exit(1);
|
|
291
|
-
}
|
|
292
|
-
});
|
|
293
278
|
|
|
294
279
|
// 错误处理
|
|
295
280
|
program.showHelpAfterError();
|
package/src/markdown.ts
CHANGED
|
@@ -171,6 +171,11 @@ export class MarkdownConverter {
|
|
|
171
171
|
for (const entry of entries) {
|
|
172
172
|
const fullPath = path.join(currentPath, entry.name);
|
|
173
173
|
|
|
174
|
+
// 忽略 .zen 目录
|
|
175
|
+
if (entry.name === '.zen') {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
|
|
174
179
|
if (entry.isDirectory()) {
|
|
175
180
|
await scanDirectory(fullPath);
|
|
176
181
|
} else if (entry.isFile() && entry.name.endsWith('.md')) {
|
package/src/navigation.ts
CHANGED
|
@@ -2,6 +2,19 @@ import { NavigationItem, FileInfo } from './types';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
|
|
4
4
|
export class NavigationGenerator {
|
|
5
|
+
private baseUrl: string;
|
|
6
|
+
|
|
7
|
+
constructor(baseUrl: string = '') {
|
|
8
|
+
this.baseUrl = baseUrl;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 更新 baseUrl
|
|
13
|
+
*/
|
|
14
|
+
setBaseUrl(baseUrl: string): void {
|
|
15
|
+
this.baseUrl = baseUrl;
|
|
16
|
+
}
|
|
17
|
+
|
|
5
18
|
/**
|
|
6
19
|
* 从文件信息生成导航结构
|
|
7
20
|
*/
|
|
@@ -40,9 +53,10 @@ export class NavigationGenerator {
|
|
|
40
53
|
const title = file.metadata?.title || this.formatTitle(displayName);
|
|
41
54
|
|
|
42
55
|
// 生成路径
|
|
43
|
-
const
|
|
56
|
+
const rawPath = isMarkdownFile
|
|
44
57
|
? `/${file.relativePath.replace(/\.md$/, '.html')}`
|
|
45
58
|
: `/${parts.slice(0, i + 1).join('/')}`;
|
|
59
|
+
const itemPath = this.generatePath(rawPath);
|
|
46
60
|
|
|
47
61
|
if (isLastPart) {
|
|
48
62
|
// 添加文件节点
|
|
@@ -76,6 +90,21 @@ export class NavigationGenerator {
|
|
|
76
90
|
}
|
|
77
91
|
}
|
|
78
92
|
|
|
93
|
+
/**
|
|
94
|
+
* 生成带 baseUrl 的路径
|
|
95
|
+
*/
|
|
96
|
+
private generatePath(path: string): string {
|
|
97
|
+
if (!this.baseUrl) {
|
|
98
|
+
return path;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 确保 baseUrl 不以斜杠结尾,路径以斜杠开头
|
|
102
|
+
const cleanBaseUrl = this.baseUrl.replace(/\/$/, '');
|
|
103
|
+
const cleanPath = path.startsWith('/') ? path : `/${path}`;
|
|
104
|
+
|
|
105
|
+
return `${cleanBaseUrl}${cleanPath}`;
|
|
106
|
+
}
|
|
107
|
+
|
|
79
108
|
/**
|
|
80
109
|
* 格式化标题(将连字符/下划线转换为空格并首字母大写)
|
|
81
110
|
*/
|
|
@@ -99,7 +128,8 @@ export class NavigationGenerator {
|
|
|
99
128
|
generateFlat(files: FileInfo[]): NavigationItem[] {
|
|
100
129
|
return files.map(file => {
|
|
101
130
|
const title = file.metadata?.title || this.formatTitle(file.name);
|
|
102
|
-
const
|
|
131
|
+
const rawPath = `/${file.relativePath.replace(/\.md$/, '.html')}`;
|
|
132
|
+
const itemPath = this.generatePath(rawPath);
|
|
103
133
|
|
|
104
134
|
return {
|
|
105
135
|
title,
|
|
@@ -162,13 +192,14 @@ export class NavigationGenerator {
|
|
|
162
192
|
/**
|
|
163
193
|
* 生成站点地图 XML
|
|
164
194
|
*/
|
|
165
|
-
generateSitemap(files: FileInfo[], baseUrl
|
|
195
|
+
generateSitemap(files: FileInfo[], baseUrl?: string): string {
|
|
196
|
+
const effectiveBaseUrl = baseUrl || this.baseUrl || 'https://example.com';
|
|
166
197
|
const urls = files.map(file => {
|
|
167
198
|
const path = `/${file.relativePath.replace(/\.md$/, '.html')}`;
|
|
168
199
|
const lastmod = file.metadata?.last_modified || file.metadata?.date || new Date().toISOString().split('T')[0];
|
|
169
200
|
|
|
170
201
|
return ` <url>
|
|
171
|
-
<loc>${
|
|
202
|
+
<loc>${effectiveBaseUrl}${path}</loc>
|
|
172
203
|
<lastmod>${lastmod}</lastmod>
|
|
173
204
|
<changefreq>weekly</changefreq>
|
|
174
205
|
<priority>0.8</priority>
|
package/src/types.ts
CHANGED
|
@@ -4,6 +4,10 @@ export interface BuildOptions {
|
|
|
4
4
|
template?: string;
|
|
5
5
|
watch?: boolean;
|
|
6
6
|
verbose?: boolean;
|
|
7
|
+
serve?: boolean;
|
|
8
|
+
port?: number;
|
|
9
|
+
host?: string;
|
|
10
|
+
baseUrl?: string;
|
|
7
11
|
}
|
|
8
12
|
|
|
9
13
|
export interface FileInfo {
|
|
@@ -39,6 +43,7 @@ export interface ZenConfig {
|
|
|
39
43
|
srcDir?: string;
|
|
40
44
|
outDir?: string;
|
|
41
45
|
template?: string;
|
|
46
|
+
baseUrl?: string;
|
|
42
47
|
i18n?: {
|
|
43
48
|
sourceLang: string;
|
|
44
49
|
targetLangs: string[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Test Document
|