code996
Version:
通过分析 Git commit 的时间分布,计算出项目的'996指数'
264 lines (256 loc) • 13.1 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CLIManager = void 0;
const commander_1 = require("commander");
const chalk_1 = __importDefault(require("chalk"));
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const child_process_1 = require("child_process");
const version_1 = require("../utils/version");
const notices_1 = require("./common/notices");
class CLIManager {
/** 构造函数:初始化 Commander 实例并完成命令注册 */
constructor() {
this.program = new commander_1.Command();
this.setupProgram();
}
/** 配置 CLI 的基础信息与可用命令 */
setupProgram() {
this.program
.name('code996')
.description('通过分析 Git commit 的时间分布,计算出项目的"996指数"')
.version((0, version_1.getPackageVersion)(), '-v, --version', '显示版本号');
// 注册根命令默认行为,直接执行分析逻辑
this.setupDefaultAnalyzeAction();
this.addTrendCommand();
this.addHelpCommand();
// 错误处理
this.setupErrorHandling();
}
/** 注册根命令,使用户直接运行 code996 即触发分析,并可选传入仓库路径 */
setupDefaultAnalyzeAction() {
this.program
.argument('[repoPath]', 'Git 仓库根目录路径(默认当前目录)')
.option('-s, --since <date>', '开始日期 (YYYY-MM-DD)')
.option('-u, --until <date>', '结束日期 (YYYY-MM-DD)')
.option('-y, --year <year>', '指定年份或年份范围 (例如: 2025 或 2023-2025)')
.option('--all-time', '查询所有时间的数据(默认为最近一年)')
.option('--self', '仅统计当前 Git 用户的提交')
.action(async (repoPath, options, command) => {
const processedArgs = typeof repoPath === 'string' ? 1 : 0;
const extraArgs = (command.args ?? []).slice(processedArgs);
if (extraArgs.length > 0) {
const invalid = extraArgs[0];
console.error(chalk_1.default.red(`错误: 未知命令 '${invalid}'`));
console.log('运行 code996 -h 查看可用命令');
process.exit(1);
}
const mergedOptions = this.mergeGlobalOptions(options);
const targetPath = this.resolveTargetPath(repoPath, this.program.name());
await this.handleAnalyze(targetPath, mergedOptions);
});
}
/** 注册 trend 命令,分析月度趋势并支持自定义仓库路径 */
addTrendCommand() {
const trendCmd = new commander_1.Command('trend')
.description('分析月度996指数和工作时间的变化趋势')
.option('-s, --since <date>', '开始日期 (YYYY-MM-DD)')
.option('-u, --until <date>', '结束日期 (YYYY-MM-DD)')
.option('-y, --year <year>', '指定年份或年份范围 (例如: 2025 或 2023-2025)')
.option('--all-time', '查询所有时间的数据')
.option('--self', '仅统计当前 Git 用户的提交')
.argument('[repoPath]', 'Git 仓库根目录路径(默认当前目录)')
.action(async (repoPath, options, command) => {
const processedArgs = typeof repoPath === 'string' ? 1 : 0;
const extraArgs = (command.args ?? []).slice(processedArgs);
if (extraArgs.length > 0) {
const invalid = extraArgs[0];
console.error(chalk_1.default.red(`错误: 未知命令 '${invalid}'`));
console.log('运行 code996 help 查看可用命令');
process.exit(1);
}
const targetPath = this.resolveTargetPath(repoPath, `${this.program.name()} trend`);
await this.handleTrend(targetPath, options);
});
this.program.addCommand(trendCmd);
}
/** 注册 help 命令,提供统一的帮助入口 */
addHelpCommand() {
const helpCmd = new commander_1.Command('help').description('显示帮助信息').action(() => {
this.showHelp();
});
this.program.addCommand(helpCmd);
}
/** 统一注册错误处理逻辑,提升用户体验 */
setupErrorHandling() {
this.program.on('command:*', (operands) => {
console.error(chalk_1.default.red(`错误: 未知命令 '${operands[0]}'`));
console.log('运行 code996 -h 查看可用命令');
process.exit(1);
});
this.program.on('error', (err) => {
console.error(chalk_1.default.red('发生错误:'), err.message);
process.exit(1);
});
}
/** 处理分析流程的执行逻辑,targetPath 为已校验的 Git 根目录 */
async handleAnalyze(targetPath, options) {
// 默认以当前工作目录作为分析目标,保持使用体验简单
// 导入analyze命令并执行
const mergedOptions = this.mergeGlobalOptions(options);
const { AnalyzeExecutor } = await Promise.resolve().then(() => __importStar(require('./commands/analyze')));
await AnalyzeExecutor.execute(targetPath, mergedOptions);
(0, notices_1.printGlobalNotices)();
}
/** 处理趋势分析流程的执行逻辑,targetPath 为已校验的 Git 根目录 */
async handleTrend(targetPath, options) {
// 导入trend命令并执行
const mergedOptions = this.mergeGlobalOptions(options);
const { TrendExecutor } = await Promise.resolve().then(() => __importStar(require('./commands/trend')));
await TrendExecutor.execute(targetPath, mergedOptions);
(0, notices_1.printGlobalNotices)();
}
/** 合并全局选项(解决子命令无法直接读取根命令参数的问题) */
mergeGlobalOptions(options) {
const globalOpts = this.program.opts();
return {
...options,
self: options.self ?? globalOpts.self,
allTime: options.allTime ?? globalOpts.allTime,
since: options.since ?? globalOpts.since,
until: options.until ?? globalOpts.until,
year: options.year ?? globalOpts.year,
};
}
/** 解析并校验仓库路径,确保用户位于 Git 仓库根目录 */
resolveTargetPath(repoPathArg, commandLabel) {
const candidatePath = path_1.default.resolve(repoPathArg ?? process.cwd());
if (!fs_1.default.existsSync(candidatePath)) {
console.error(chalk_1.default.red('❌ 指定的目录不存在:'), candidatePath);
console.log(chalk_1.default.yellow('请确认路径是否正确,或在 Git 仓库根目录运行命令。'));
process.exit(1);
}
const stat = fs_1.default.statSync(candidatePath);
if (!stat.isDirectory()) {
console.error(chalk_1.default.red('❌ 指定路径不是目录:'), candidatePath);
console.log(chalk_1.default.yellow('请传入 Git 仓库根目录,而不是单个文件。'));
process.exit(1);
}
let gitRootRaw;
try {
gitRootRaw = (0, child_process_1.execSync)('git rev-parse --show-toplevel', {
cwd: candidatePath,
stdio: ['ignore', 'pipe', 'pipe'],
})
.toString()
.trim();
}
catch {
console.error(chalk_1.default.red('❌ 未检测到有效的 Git 仓库:'), candidatePath);
console.log(chalk_1.default.yellow('请在 Git 仓库根目录执行命令,或在命令末尾追加 Git 仓库路径,例如:'));
console.log(chalk_1.default.cyan(` ${commandLabel} /path/to/your/repo`));
process.exit(1);
}
const normalizedCandidate = fs_1.default.realpathSync(candidatePath);
const normalizedRoot = fs_1.default.realpathSync(gitRootRaw);
if (normalizedCandidate !== normalizedRoot) {
this.printGitRootWarning(normalizedCandidate, normalizedRoot, commandLabel);
}
return normalizedRoot;
}
/** 强提示当前路径非 Git 根目录,并指引用户的正确使用方式 */
printGitRootWarning(currentPath, rootPath, commandLabel) {
console.error(chalk_1.default.bgRed.white(' ⚠️ 当前目录不是 Git 仓库根目录 '));
console.error(chalk_1.default.red(`当前目录: ${currentPath}`));
console.error(chalk_1.default.green(`仓库根目录: ${rootPath}`));
console.log(chalk_1.default.yellow('请在仓库根目录执行命令,或直接在命令末尾追加根目录路径,例如:'));
console.log(chalk_1.default.cyan(` ${commandLabel} ${rootPath}`));
console.log(chalk_1.default.yellow('提示: 若你在子目录中,请先 cd 到上面的仓库根目录后再运行。'));
process.exit(1);
}
/** 自定义帮助信息展示,补充常用示例 */
showHelp() {
// 使用更紧凑的 CODE996 字符图,避免在窄终端中被截断
const banner = `
████ ████ █████ ██████ ████ ████ ████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ ██ █████ █████ █████ █████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
████ ████ █████ ██████ ████ ████ ████
`;
console.log(chalk_1.default.hex('#D72654')(banner));
console.log(`> 统计 Git 项目的 commit 时间分布,进而推导出项目的编码工作强度。
${chalk_1.default.bold('使用方法:')}
code996 [选项]
code996 trend [选项]
${chalk_1.default.bold('命令:')}
trend 查看月度996指数和工作时间的变化趋势
help 显示帮助信息
${chalk_1.default.bold('全局选项:')}
-v, --version 显示版本号
-h, --help 显示帮助信息
${chalk_1.default.bold('分析选项:')}
-s, --since <date> 开始日期 (YYYY-MM-DD)
-u, --until <date> 结束日期 (YYYY-MM-DD)
-y, --year <year> 指定年份或年份范围 (例如: 2025 或 2023-2025)
--all-time 查询所有时间的数据(覆盖整个仓库历史)
--self 仅统计当前 Git 用户的提交
${chalk_1.default.bold('默认策略:')}
自动以最后一次提交为基准,回溯365天进行分析
${chalk_1.default.bold('示例:')}
${chalk_1.default.gray('# 基础分析')}
code996 # 分析最近一年
code996 --since 2024-01-01 # 从指定日期开始
code996 -y 2025 # 分析2025年整年
code996 -y 2023-2025 # 分析2023-2025年
code996 --all-time # 分析所有时间
${chalk_1.default.gray('# 趋势分析')}
code996 trend # 分析最近一年的月度趋势
code996 trend -y 2024 # 分析2024年各月趋势
code996 trend --all-time # 分析所有时间的月度趋势
${chalk_1.default.bold('更多详情请访问:')} https://github.com/code996/code996
`);
}
/** 启动 CLI 参数解析入口 */
parse(argv) {
this.program.parse(argv);
}
}
exports.CLIManager = CLIManager;
//# sourceMappingURL=index.js.map