wukong-gitlog-cli 1.0.4 → 1.0.6

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/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [1.0.6](https://github.com/tomatobybike/wukong-gitlog-cli/compare/v1.0.5...v1.0.6) (2025-11-29)
6
+
7
+
8
+ ### Features
9
+
10
+ * 🎸 auto open ([380e013](https://github.com/tomatobybike/wukong-gitlog-cli/commit/380e01386300342b5b4009f4ddf5463a19cf1be8))
11
+
12
+ ### [1.0.5](https://github.com/tomatobybike/wukong-gitlog-cli/compare/v1.0.4...v1.0.5) (2025-11-29)
13
+
5
14
  ### [1.0.4](https://github.com/tomatobybike/wukong-gitlog-cli/compare/v1.0.3...v1.0.4) (2025-11-29)
6
15
 
7
16
  ### [1.0.3](https://github.com/tomatobybike/wukong-gitlog-cli/compare/v1.0.2...v1.0.3) (2025-11-29)
package/README.zh-CN.md CHANGED
@@ -62,7 +62,7 @@ wukong-gitlog-cli [options]
62
62
  ### 常用参数
63
63
 
64
64
  | 参数 | 描述 |
65
- | -------------------- | ------------------------------------------------------------------ | --- |
65
+ | -------------------- | ------------------------------------------------------------------ |
66
66
  | `--author <name>` | 按作者过滤 |
67
67
  | `--email <email>` | 按邮箱过滤 |
68
68
  | `--since <date>` | 起始日期(如 2025-01-01) |
@@ -70,8 +70,8 @@ wukong-gitlog-cli [options]
70
70
  | `--limit <n>` | 限制提交数量 |
71
71
  | `--no-merges` | 排除 merge 提交 |
72
72
  | `--json` | 输出 JSON |
73
- | `--format <type>` | 输出格式:text / excel / json(默认 text) |
74
- | `--group-by <type>` | 分组:day / month |
73
+ | `--format <type>` | 输出格式: text / excel / json(默认 text) |
74
+ | `--group-by <type>` | 分组: day / month |
75
75
  | `--overtime` | 启用加班文化分析 |
76
76
  | `--country <code>` | 假期:CN 或 US(默认 CN) |
77
77
  | `--stats` | Excel 中包含统计 sheet |
@@ -79,10 +79,10 @@ wukong-gitlog-cli [options]
79
79
  | `--gerrit-api <url>` | Gerrit API 地址(用于 changeNumber) |
80
80
  | `--out <file>` | 输出文件名 |
81
81
  | `--out-dir <dir>` | 输出目录 |
82
- | `--out-parent` | 输出到父目录的 output/ |
83
- | `--serve` | 启动本地 web 服务,查看提交统计(将在 output/data 下生成数据文件) |
84
- | `--port <n>` | 本地 web 服务端口(默认 3000) (default: 3000) | |
85
- | `--serve-only` | 仅启动 web 服务,不导出或分析数据(使用 output/data 中已有的数据) |
82
+ | `--out-parent` | 输出到父目录的 `output/` |
83
+ | `--serve` | 启动本地 Web 服务查看提交统计(会生成 output/data 下的数据) |
84
+ | `--port <n>` | Web 服务端口(默认 3000) |
85
+ | `--serve-only` | 仅启动 Web 服务,不导出或分析数据(使用现有 output/data|
86
86
  | `--version` | 显示版本号 |
87
87
 
88
88
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wukong-gitlog-cli",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Advanced Git commit log exporter with Excel/JSON/TXT output, grouping, stats and CLI.",
5
5
  "keywords": [
6
6
  "git",
package/src/server.mjs CHANGED
@@ -1,8 +1,9 @@
1
- import http from 'http';
2
- import fs from 'fs';
3
- import path from 'path';
4
- import chalk from 'chalk';
5
- import { fileURLToPath } from 'url';
1
+ import chalk from 'chalk'
2
+ import { exec } from 'child_process'
3
+ import fs from 'fs'
4
+ import http from 'http'
5
+ import path from 'path'
6
+ import { fileURLToPath } from 'url'
6
7
 
7
8
  const mime = new Map([
8
9
  ['.html', 'text/html; charset=utf-8'],
@@ -14,74 +15,116 @@ const mime = new Map([
14
15
  ['.png', 'image/png'],
15
16
  ['.jpg', 'image/jpeg'],
16
17
  ['.jpeg', 'image/jpeg'],
17
- ['.map', 'application/json; charset=utf-8'],
18
- ]);
18
+ ['.map', 'application/json; charset=utf-8']
19
+ ])
20
+
21
+ // --------------------------------
22
+ // 打开浏览器(跨平台)
23
+ // --------------------------------
24
+ function openBrowser(url) {
25
+ const { platform } = process
26
+
27
+ let cmd
28
+ if (platform === 'win32') {
29
+ cmd = `start "" "${url}"`
30
+ } else if (platform === 'darwin') {
31
+ cmd = `open "${url}"`
32
+ } else {
33
+ cmd = `xdg-open "${url}"`
34
+ }
35
+
36
+ exec(cmd, (err) => {
37
+ if (err) {
38
+ console.log(chalk.yellow(`Failed to auto-open browser: ${err.message}`))
39
+ }
40
+ })
41
+ }
19
42
 
20
43
  // eslint-disable-next-line default-param-last
21
44
  export function startServer(port = 3000, outputDir) {
22
45
  // 解析包根目录,确保 web 资源在全局安装后也能找到
23
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
24
- const pkgRoot = path.resolve(__dirname, '..');
25
- const webRoot = path.resolve(pkgRoot, 'web');
26
- const dataRoot = outputDir ? path.resolve(outputDir) : path.resolve(process.cwd(), 'output');
46
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
47
+ const pkgRoot = path.resolve(__dirname, '..')
48
+ const webRoot = path.resolve(pkgRoot, 'web')
49
+ const dataRoot = outputDir
50
+ ? path.resolve(outputDir)
51
+ : path.resolve(process.cwd(), 'output')
27
52
 
28
53
  // warn if web directory or data directory doesn't exist
29
54
  if (!fs.existsSync(webRoot)) {
30
- console.warn(chalk.yellow(`Warning: web/ directory not found at ${webRoot}. Server will still run but no UI will be available.`));
55
+ console.warn(
56
+ chalk.yellow(
57
+ `Warning: web/ directory not found at ${webRoot}. Server will still run but no UI will be available.`
58
+ )
59
+ )
31
60
  }
32
61
  if (!fs.existsSync(dataRoot)) {
33
- console.warn(chalk.yellow(`Warning: output data directory not found at ${dataRoot}. Server will still run but data endpoints (/data/) may 404.`));
62
+ console.warn(
63
+ chalk.yellow(
64
+ `Warning: output data directory not found at ${dataRoot}. Server will still run but data endpoints (/data/) may 404.`
65
+ )
66
+ )
34
67
  }
35
68
 
36
69
  const server = http.createServer((req, res) => {
37
70
  try {
38
71
  // Normalize URL path
39
- const u = new URL(req.url, `http://localhost`);
40
- let pathname = decodeURIComponent(u.pathname);
72
+ const u = new URL(req.url, `http://localhost`)
73
+ let pathname = decodeURIComponent(u.pathname)
41
74
 
42
75
  // Serve data files under /data/* mapped to dataRoot/data/*
43
76
  if (pathname.startsWith('/data/')) {
44
- const relative = pathname.replace(/^\/data\//, '');
45
- const fileLocal = path.join(dataRoot, 'data', relative);
77
+ const relative = pathname.replace(/^\/data\//, '')
78
+ const fileLocal = path.join(dataRoot, 'data', relative)
46
79
  if (fs.existsSync(fileLocal) && fs.statSync(fileLocal).isFile()) {
47
- const ext = path.extname(fileLocal).toLowerCase();
48
- res.setHeader('Content-Type', mime.get(ext) || 'application/octet-stream');
49
- res.setHeader('Access-Control-Allow-Origin', '*');
50
- const stream = fs.createReadStream(fileLocal);
51
- stream.pipe(res);
52
- return;
80
+ const ext = path.extname(fileLocal).toLowerCase()
81
+ res.setHeader(
82
+ 'Content-Type',
83
+ mime.get(ext) || 'application/octet-stream'
84
+ )
85
+ res.setHeader('Access-Control-Allow-Origin', '*')
86
+ const stream = fs.createReadStream(fileLocal)
87
+ stream.pipe(res)
88
+ return
53
89
  }
54
90
  }
55
91
 
56
92
  // Resolve web assets
57
- if (pathname === '/') pathname = '/index.html';
58
- const fileLocal = path.join(webRoot, pathname);
93
+ if (pathname === '/') pathname = '/index.html'
94
+ const fileLocal = path.join(webRoot, pathname)
59
95
  if (fs.existsSync(fileLocal) && fs.statSync(fileLocal).isFile()) {
60
- const ext = path.extname(fileLocal).toLowerCase();
61
- res.setHeader('Content-Type', mime.get(ext) || 'application/octet-stream');
62
- const stream = fs.createReadStream(fileLocal);
63
- stream.pipe(res);
64
- return;
96
+ const ext = path.extname(fileLocal).toLowerCase()
97
+ res.setHeader(
98
+ 'Content-Type',
99
+ mime.get(ext) || 'application/octet-stream'
100
+ )
101
+ const stream = fs.createReadStream(fileLocal)
102
+ stream.pipe(res)
103
+ return
65
104
  }
66
105
 
67
106
  // file not found
68
- res.statusCode = 404;
69
- res.setHeader('Content-Type', 'text/plain; charset=utf-8');
70
- res.end('Not Found');
107
+ res.statusCode = 404
108
+ res.setHeader('Content-Type', 'text/plain; charset=utf-8')
109
+ res.end('Not Found')
71
110
  } catch (err) {
72
- res.statusCode = 500;
73
- res.end('Server error');
111
+ res.statusCode = 500
112
+ res.end('Server error')
74
113
  }
75
- });
114
+ })
76
115
 
77
116
  return new Promise((resolve, reject) => {
78
- server.on('error', (err) => reject(err));
117
+ server.on('error', (err) => reject(err))
79
118
  server.listen(port, () => {
80
- console.log(chalk.green(`Server started at http://localhost:${port}`));
81
- console.log(chalk.green(`Serving web/ and output/data/`));
82
- resolve(server);
83
- });
84
- });
119
+ const url = `http://localhost:${port}`
120
+ console.log(chalk.green(`Server started at ${url}`))
121
+ console.log(chalk.green(`Serving web/ and output/data/`))
122
+
123
+ // ====== 自动打开浏览器 ======
124
+ openBrowser(url)
125
+ resolve(server)
126
+ })
127
+ })
85
128
  }
86
129
 
87
- export default startServer;
130
+ export default startServer