UNPKG

t-comm

Version:

专业、稳定、纯粹的工具库

321 lines (318 loc) 13.2 kB
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 };