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/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 (srcDir, options) => {
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: path.resolve(srcDir),
53
- outDir: path.resolve(options.out),
58
+ srcDir: currentDir,
59
+ outDir: outDir,
54
60
  template: options.template ? path.resolve(options.template) : undefined,
55
61
  watch: options.watch,
56
- verbose: options.verbose
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
- .argument('<dir>', 'Directory to clean')
91
- .action(async (dir) => {
106
+ .description('Clean .zen/dist output directory')
107
+ .action(async () => {
92
108
  try {
93
109
  const builder = new ZenBuilder();
94
- await builder.clean(path.resolve(dir));
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 docs --out dist\`
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, 'docs', 'index.md'),
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 docs --out dist';
176
- packageJson.scripts['build:watch'] = 'zengen build docs --out dist --watch';
177
- packageJson.scripts.clean = 'zengen clean dist';
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 docs --out dist',
187
- 'build:watch': 'zengen build docs --out dist --watch',
188
- clean: 'zengen clean dist'
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 'docs' directory
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
- ├── docs/ # Your documentation files
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 ./docs --out ./dist
251
- $ zengen build ./docs --out ./dist --watch
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 ./dist
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 itemPath = isMarkdownFile
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 itemPath = `/${file.relativePath.replace(/\.md$/, '.html')}`;
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: string = 'https://example.com'): string {
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>${baseUrl}${path}</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