auto-version-tool
Version:
根据git commit历史自动修改版本号并生成changelog的CLI工具 (Automatically bump version & generate changelog based on git commits)
373 lines (368 loc) • 11.9 kB
JavaScript
"use strict";
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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConfigService = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
class ConfigService {
constructor() {
this.defaultConfig = {
git: {
defaultBranch: "main",
remoteOrigin: "origin",
tagPrefix: "v",
},
version: {
strategy: "semantic",
bumpRules: {
major: ["feat!", "fix!", "BREAKING CHANGE", "breaking"],
minor: ["feat"],
patch: ["fix", "perf", "refactor"],
},
prerelease: {
identifier: "alpha",
enable: false,
},
},
changelog: {
outputFile: "CHANGELOG.md",
template: "",
includeTypes: [
"feat",
"fix",
"perf",
"refactor",
"docs",
"style",
"test",
"build",
"ci",
"chore",
],
skipEmptyReleases: true,
groupBy: "type",
},
commitTypes: {
feat: {
title: "Features",
semver: "minor",
emoji: "✨",
},
fix: {
title: "Bug Fixes",
semver: "patch",
emoji: "🐛",
},
docs: {
title: "Documentation",
semver: "patch",
emoji: "📚",
},
style: {
title: "Styles",
semver: "patch",
emoji: "💎",
},
refactor: {
title: "Code Refactoring",
semver: "patch",
emoji: "📦",
},
perf: {
title: "Performance Improvements",
semver: "patch",
emoji: "🚀",
},
test: {
title: "Tests",
semver: "none",
emoji: "🚨",
},
build: {
title: "Builds",
semver: "patch",
emoji: "🛠",
},
ci: {
title: "Continuous Integrations",
semver: "none",
emoji: "⚙️",
},
chore: {
title: "Chores",
semver: "patch",
emoji: "♻️",
},
},
files: {
packageJson: "package.json",
changelogFile: "CHANGELOG.md",
},
hooks: {},
};
}
loadConfig(configPath) {
// 允许传入相对路径,这里统一转换为绝对路径,避免 require 相对 dist 目录解析失败
let configFile = configPath || this.findConfigFile();
if (configFile && !path.isAbsolute(configFile)) {
configFile = path.resolve(process.cwd(), configFile);
}
if (!configFile || !fs.existsSync(configFile)) {
console.log("🔧 使用默认配置");
return this.defaultConfig;
}
try {
console.log(`📄 加载配置文件: ${configFile}`);
// 根据文件扩展名加载配置
const config = this.loadConfigFromFile(configFile);
// 合并默认配置
return this.mergeConfig(this.defaultConfig, config);
}
catch (error) {
console.warn(`⚠️ 加载配置文件失败,使用默认配置: ${error}`);
return this.defaultConfig;
}
}
findConfigFile() {
const cwd = process.cwd();
const possibleFiles = [
"auto-version.config.js",
"auto-version.config.json",
".auto-versionrc",
".auto-versionrc.json",
".auto-versionrc.js",
];
for (const file of possibleFiles) {
const fullPath = path.join(cwd, file);
if (fs.existsSync(fullPath)) {
return fullPath;
}
}
// 检查package.json中的配置
const packageJsonPath = path.join(cwd, "package.json");
if (fs.existsSync(packageJsonPath)) {
try {
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
if (packageJson.autoVersion) {
return packageJsonPath;
}
}
catch {
// 忽略错误
}
}
return null;
}
loadConfigFromFile(configFile) {
const ext = path.extname(configFile);
if (ext === ".json" || configFile.endsWith("package.json")) {
const content = fs.readFileSync(configFile, "utf-8");
const json = JSON.parse(content);
// 如果是package.json,提取autoVersion字段
if (configFile.endsWith("package.json")) {
return json.autoVersion || {};
}
return json;
}
if (ext === ".js") {
// 动态导入JS配置文件
delete require.cache[require.resolve(configFile)];
const config = require(configFile);
return config.default || config;
}
throw new Error(`不支持的配置文件格式: ${ext}`);
}
mergeConfig(defaultConfig, userConfig) {
return {
git: { ...defaultConfig.git, ...userConfig.git },
version: {
...defaultConfig.version,
...userConfig.version,
bumpRules: {
...defaultConfig.version.bumpRules,
...userConfig.version?.bumpRules,
},
prerelease: {
...defaultConfig.version.prerelease,
...(userConfig.version?.prerelease || {}),
},
},
changelog: { ...defaultConfig.changelog, ...userConfig.changelog },
commitTypes: { ...defaultConfig.commitTypes, ...userConfig.commitTypes },
files: { ...defaultConfig.files, ...userConfig.files },
hooks: { ...defaultConfig.hooks, ...userConfig.hooks },
};
}
async createDefaultConfig(outputPath) {
const configContent = this.generateConfigFileContent(outputPath);
fs.writeFileSync(outputPath, configContent, "utf-8");
}
generateConfigFileContent(outputPath) {
const ext = path.extname(outputPath);
if (ext === ".json") {
return JSON.stringify(this.defaultConfig, null, 2);
}
// 生成JS配置文件
return `module.exports = {
// Git相关配置
git: {
defaultBranch: 'main',
remoteOrigin: 'origin',
tagPrefix: 'v'
},
// 版本控制配置
version: {
strategy: 'semantic', // 'semantic' | 'timestamp' | 'build'
bumpRules: {
major: ['feat!', 'fix!', 'BREAKING CHANGE', 'breaking'],
minor: ['feat'],
patch: ['fix', 'perf', 'refactor']
},
prerelease: {
identifier: 'alpha',
enable: false
}
},
// Changelog配置
changelog: {
outputFile: 'CHANGELOG.md',
template: '', // 可以指定自定义模板文件路径
includeTypes: ['feat', 'fix', 'perf', 'refactor', 'docs', 'style', 'test', 'build', 'ci', 'chore'],
skipEmptyReleases: true,
groupBy: 'type' // 'type' | 'scope' | 'none'
},
// 提交类型配置
commitTypes: {
feat: {
title: 'Features',
semver: 'minor',
emoji: '✨'
},
fix: {
title: 'Bug Fixes',
semver: 'patch',
emoji: '🐛'
},
docs: {
title: 'Documentation',
semver: 'patch',
emoji: '📚'
},
style: {
title: 'Styles',
semver: 'patch',
emoji: '💎'
},
refactor: {
title: 'Code Refactoring',
semver: 'patch',
emoji: '📦'
},
perf: {
title: 'Performance Improvements',
semver: 'patch',
emoji: '🚀'
},
test: {
title: 'Tests',
semver: 'none',
emoji: '🚨'
},
build: {
title: 'Builds',
semver: 'patch',
emoji: '🛠'
},
ci: {
title: 'Continuous Integrations',
semver: 'none',
emoji: '⚙️'
},
chore: {
title: 'Chores',
semver: 'patch',
emoji: '♻️'
}
},
// 文件配置
files: {
packageJson: 'package.json',
versionFile: '', // 可选,独立的版本文件
changelogFile: 'CHANGELOG.md'
},
// 钩子配置
hooks: {
// preVersion: 'npm run test',
// postVersion: 'npm run build',
// preCommit: 'npm run lint',
// postCommit: 'npm run deploy'
}
};
`;
}
validateConfig(config) {
const errors = [];
// 验证必要字段
if (!config.git?.defaultBranch) {
errors.push("git.defaultBranch 是必须的");
}
if (!config.files?.packageJson) {
errors.push("files.packageJson 是必须的");
}
if (!config.files?.changelogFile) {
errors.push("files.changelogFile 是必须的");
}
// 验证版本策略
const validStrategies = ["semantic", "timestamp", "build"];
if (!validStrategies.includes(config.version?.strategy)) {
errors.push(`version.strategy 必须是以下之一: ${validStrategies.join(", ")}`);
}
// 验证changelog分组方式
const validGroupBy = ["type", "scope", "none"];
if (config.changelog?.groupBy &&
!validGroupBy.includes(config.changelog.groupBy)) {
errors.push(`changelog.groupBy 必须是以下之一: ${validGroupBy.join(", ")}`);
}
return {
valid: errors.length === 0,
errors,
};
}
getDefaultConfig() {
return { ...this.defaultConfig };
}
}
exports.ConfigService = ConfigService;
//# sourceMappingURL=ConfigService.js.map