t-comm
Version:
专业、稳定、纯粹的工具库
321 lines (318 loc) • 13.2 kB
JavaScript
import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
import _regeneratorRuntime from '@babel/runtime/regenerator';
import { _ as __awaiter } from '../tslib.es6-848120af.js';
import { getFs } from '../nodejs/fs.mjs';
import { getPath } from '../nodejs/path.mjs';
import { getGlob } from '../third-party/glob.mjs';
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
// 匹配版本标题行的正则表达式
// standard-version 格式: ### [1.0.29-beta.33](链接) (2025-12-29)
var VERSION_HEADER_REGEX = /^### \[(.+?)\]\([^)]+\) \(/;
// 格式: ## <small>1.0.457-beta.0 (2025-12-29)</small>
var VERSION_HEADER_REGEX_2 = /^## <small>(.+?) \(/;
// 匹配 alpha/beta 版本的正则表达式
var PRERELEASE_REGEX = /-(alpha|beta)\./i;
/**
* 解析 CHANGELOG 文件,将其分割为多个版本块
* @param {string} content
* @returns {{preamble: string[], sections: Array<{header: string, version: string, content: string[], isPrerelease: boolean}>}}
*/
function parseChangelog(content) {
var lines = content.split('\n');
var sections = [];
var preamble = [];
var currentSection = null;
var inPreamble = true;
var _iterator = _createForOfIteratorHelper(lines),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var line = _step.value;
var versionMatch = line.match(VERSION_HEADER_REGEX) || line.match(VERSION_HEADER_REGEX_2);
if (versionMatch) {
// 保存前一个 section
if (currentSection) {
sections.push(currentSection);
}
inPreamble = false;
var version = versionMatch[1];
var isPrerelease = PRERELEASE_REGEX.test(version);
currentSection = {
header: line,
version: version,
content: [line],
isPrerelease: isPrerelease
};
} else if (currentSection) {
// 如果遇到新的三级标题(但不是版本标题),则结束当前 section
// if (line.startsWith('### ') && !line.match(VERSION_HEADER_REGEX)) {
// sections.push(currentSection);
// currentSection = null;
// inPreamble = false;
// } else if (line.trim() === '' && currentSection.content.length > 1) {
// // 如果遇到空行且当前 section 有内容,可能表示 section 结束
// sections.push(currentSection);
// currentSection = null;
// } else {
currentSection.content.push(line);
// }
} else if (inPreamble) {
// 处理文件开头的非版本内容(如 # Change Log 标题等)
preamble.push(line);
}
}
// 保存最后一个 section
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
if (currentSection) {
sections.push(currentSection);
}
return {
preamble: preamble,
sections: sections
};
}
/**
* 过滤掉 alpha/beta 版本,只保留正式版本
* @param {Array} sections
* @returns {Array}
*/
function filterStableVersions(sections) {
return sections.filter(function (section) {
return !section.isPrerelease;
});
}
/**
* 将 sections 重新组合为 changelog 内容
* @param {string[]} preamble
* @param {Array} sections
* @returns {string}
*/
function rebuildChangelog(preamble, sections) {
console.log('sections', sections);
var result = [];
// 添加 preamble(如果有的话)
if (preamble.length > 0 && preamble.some(function (line) {
return line.trim();
})) {
result.push.apply(result, _toConsumableArray(preamble));
}
// 添加各个版本块
var _iterator2 = _createForOfIteratorHelper(sections),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var section = _step2.value;
result.push.apply(result, _toConsumableArray(section.content));
}
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
return result.join('\n');
}
/**
* 清理单个 CHANGELOG 文件
* @param {string} filePath
* @param {boolean} dryRun
* @returns {boolean} 是否成功
* @example
* ```ts
* // 实际写入
* cleanChangelogFile('CHANGELOG.md');
*
* // 预览模式,不修改文件
* cleanChangelogFile('CHANGELOG.md', true);
* ```
*/
function cleanChangelogFile(filePath) {
var dryRun = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var absolutePath = getPath().resolve(filePath);
if (!getFs().existsSync(absolutePath)) {
console.error("\u274C \u6587\u4EF6\u4E0D\u5B58\u5728: ".concat(absolutePath));
return false;
}
console.log("\uD83D\uDCD6 \u8BFB\u53D6\u6587\u4EF6: ".concat(absolutePath));
var content = getFs().readFileSync(absolutePath, 'utf-8');
var _parseChangelog = parseChangelog(content),
preamble = _parseChangelog.preamble,
sections = _parseChangelog.sections;
var totalSections = sections.length;
var prereleaseSections = sections.filter(function (s) {
return s.isPrerelease;
});
var stableSections = filterStableVersions(sections);
console.log('\n📊 统计信息:');
console.log(" - \u603B\u7248\u672C\u6570: ".concat(totalSections));
console.log(" - Alpha/Beta \u7248\u672C\u6570: ".concat(prereleaseSections.length));
console.log(" - \u6B63\u5F0F\u7248\u672C\u6570: ".concat(stableSections.length));
if (prereleaseSections.length > 0) {
console.log('\n🗑️ 将要删除的 Alpha/Beta 版本:');
prereleaseSections.forEach(function (s) {
console.log(" - ".concat(s.version));
});
}
if (dryRun) {
console.log('\n⚠️ Dry run 模式,不会实际修改文件');
return true;
}
if (prereleaseSections.length === 0) {
console.log('\n✅ 没有需要删除的 Alpha/Beta 版本');
return true;
}
var newContent = rebuildChangelog(preamble, stableSections);
// 备份原文件
// const backupPath = `${absolutePath}.backup`;
// getFs().writeFileSync(backupPath, content, 'utf-8');
// console.log(`\n💾 已备份原文件到: ${backupPath}`);
// 写入新内容
getFs().writeFileSync(absolutePath, newContent, 'utf-8');
console.log("\u2705 \u5DF2\u6E05\u7406\u5B8C\u6210\uFF0C\u5220\u9664\u4E86 ".concat(prereleaseSections.length, " \u4E2A Alpha/Beta \u7248\u672C\u8BB0\u5F55"));
return true;
}
/**
* 解析 glob 表达式,返回匹配的文件列表
* @param {string} pattern
* @returns {Promise<Array<string>>}
*/
function resolveGlobPatterns(pattern) {
return __awaiter(this, void 0, void 0, /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
var glob, files;
return _regeneratorRuntime.wrap(function (_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
if (!(!pattern.includes('*') && !pattern.includes('?') && !pattern.includes('['))) {
_context.next = 1;
break;
}
return _context.abrupt("return", [pattern]);
case 1:
glob = getGlob(); // 使用 glob 解析模式
_context.next = 2;
return glob(pattern, {
nodir: true,
absolute: false
});
case 2:
files = _context.sent;
return _context.abrupt("return", files.sort());
case 3:
case "end":
return _context.stop();
}
}, _callee);
}));
}
/**
* 主函数
* @example
* ```bash
* # 处理单个文件
* npx t-comm clean:changelog packages/network-v2/CHANGELOG.md
*
* # 使用 glob 表达式处理多个文件
* npx t-comm clean:changelog 'packages/*\/CHANGELOG.md'
*
* # 预览模式
* npx t-comm clean:changelog 'packages/*\/CHANGELOG.md' --dry-run
* ```
*/
function cleanChangelogScript(args) {
return __awaiter(this, void 0, void 0, /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
var patterns, dryRun, allFiles, _iterator3, _step3, pattern, files, uniqueFiles, successCount, failCount, i, file, success, _t;
return _regeneratorRuntime.wrap(function (_context2) {
while (1) switch (_context2.prev = _context2.next) {
case 0:
if (args.length === 0) {
console.log("\n\u4F7F\u7528\u65B9\u5F0F:\n npx t-comm clean:changelog <changelog\u6587\u4EF6\u8DEF\u5F84\u6216glob\u8868\u8FBE\u5F0F> [--dry-run]\n\n\u53C2\u6570:\n <changelog\u6587\u4EF6\u8DEF\u5F84> CHANGELOG.md \u6587\u4EF6\u7684\u8DEF\u5F84\uFF0C\u652F\u6301 glob \u8868\u8FBE\u5F0F\n --dry-run \u9884\u89C8\u6A21\u5F0F\uFF0C\u4E0D\u5B9E\u9645\u4FEE\u6539\u6587\u4EF6\n\n\u793A\u4F8B:\n # \u5904\u7406\u5355\u4E2A\u6587\u4EF6\n npx t-comm clean:changelog packages/network-v2/CHANGELOG.md\n\n # \u4F7F\u7528 glob \u8868\u8FBE\u5F0F\u5904\u7406\u591A\u4E2A\u6587\u4EF6\n npx t-comm clean:changelog 'packages/*/CHANGELOG.md'\n npx t-comm clean:changelog 'packages/**/CHANGELOG.md'\n\n # \u9884\u89C8\u6A21\u5F0F\n npx t-comm clean:changelog \"packages/*/CHANGELOG.md\" --dry-run\n");
process.exit(0);
}
// 过滤掉 --dry-run 参数,获取文件模式
patterns = args.filter(function (arg) {
return !arg.startsWith('--');
});
dryRun = args.includes('--dry-run');
if (patterns.length === 0) {
console.error('❌ 请提供至少一个文件路径或 glob 表达式');
process.exit(1);
}
// 解析所有 glob 表达式
allFiles = [];
_iterator3 = _createForOfIteratorHelper(patterns);
_context2.prev = 1;
_iterator3.s();
case 2:
if ((_step3 = _iterator3.n()).done) {
_context2.next = 5;
break;
}
pattern = _step3.value;
_context2.next = 3;
return resolveGlobPatterns(pattern);
case 3:
files = _context2.sent;
allFiles.push.apply(allFiles, _toConsumableArray(files));
case 4:
_context2.next = 2;
break;
case 5:
_context2.next = 7;
break;
case 6:
_context2.prev = 6;
_t = _context2["catch"](1);
_iterator3.e(_t);
case 7:
_context2.prev = 7;
_iterator3.f();
return _context2.finish(7);
case 8:
// 去重
uniqueFiles = Array.from(new Set(allFiles));
if (uniqueFiles.length === 0) {
console.log('⚠️ 没有匹配到任何文件');
process.exit(0);
}
console.log("\uD83D\uDD0D \u5339\u914D\u5230 ".concat(uniqueFiles.length, " \u4E2A\u6587\u4EF6:\n"));
uniqueFiles.forEach(function (f) {
return console.log(" - ".concat(f));
});
console.log("\n".concat('='.repeat(60), "\n"));
// 处理每个文件
successCount = 0;
failCount = 0;
for (i = 0; i < uniqueFiles.length; i++) {
file = uniqueFiles[i];
console.log("\n\uD83D\uDCC1 [".concat(i + 1, "/").concat(uniqueFiles.length, "] \u5904\u7406\u6587\u4EF6: ").concat(file));
console.log('-'.repeat(60));
success = cleanChangelogFile(file, dryRun);
if (success) {
successCount += 1;
} else {
failCount += 1;
}
if (i < uniqueFiles.length - 1) {
console.log('\n');
}
}
// 输出总结
console.log("\n".concat('='.repeat(60)));
console.log('\n📋 处理完成:');
console.log(" - \u6210\u529F: ".concat(successCount, " \u4E2A\u6587\u4EF6"));
if (failCount > 0) {
console.log(" - \u5931\u8D25: ".concat(failCount, " \u4E2A\u6587\u4EF6"));
}
case 9:
case "end":
return _context2.stop();
}
}, _callee2, null, [[1, 6, 7, 8]]);
}));
}
export { cleanChangelogFile, cleanChangelogScript };