diff --git a/.dumi/rehypeChangelog.ts b/.dumi/rehypeChangelog.ts index b9e434c8d9..81bf748f9f 100644 --- a/.dumi/rehypeChangelog.ts +++ b/.dumi/rehypeChangelog.ts @@ -1,5 +1,6 @@ import type { UnifiedTransformer } from 'dumi'; import { unistUtilVisit } from 'dumi'; +import semver from 'semver'; import set from 'lodash/set'; let hastToString: typeof import('hast-util-to-string').toString; @@ -9,6 +10,24 @@ let hastToString: typeof import('hast-util-to-string').toString; ({ toString: hastToString } = await import('hast-util-to-string')); })(); +function isValidStrictVer(ver: string): boolean { + if (!semver.valid(ver)) { + return false; + } + + const parts = ver.split('.'); + if (parts.length !== 3) { + return false; + } + + return parts.every((part) => /^\d+$/.test(part)); +} + +function isValidDate(dateStr: string): boolean { + // (YYYY-MM-DD) + return /^\d{4}-\d{2}-\d{2}$/.test(dateStr); +} + const COMPONENT_NAME = 'RefinedChangelog'; function rehypeChangelog(): UnifiedTransformer { @@ -23,32 +42,69 @@ function rehypeChangelog(): UnifiedTransformer { const nodesToWrap: { parent: any; startIdx: number }[] = []; const WRAPPER_FLAG = 'data-changelog-wrapped'; // 包裹容器唯一标识 + function checkLogSegment(node: any, strict = true) { + if (node && node.type === 'element' && node.tagName === 'h2') { + if (strict) { + const ver = hastToString(node); + return isValidStrictVer(ver) && semver.major(ver) >= 5; + } + return true; + } + return false; + } + unistUtilVisit.visit(tree, 'element', (node, idx, parent) => { if (node.properties?.[WRAPPER_FLAG]) { return unistUtilVisit.SKIP; } - if ( - idx !== undefined && - parent && - idx! + 2 < parent.children.length && - node.tagName === 'h2' && - parent.children[idx! + 1].tagName === 'p' && - parent.children[idx! + 2].tagName === 'ul' - ) { + + if (idx !== undefined && parent && checkLogSegment(node)) { nodesToWrap.push({ parent, startIdx: idx! }); } }); - nodesToWrap.reverse().forEach(({ parent, startIdx }) => { - const [heading, date, list] = parent.children.splice(startIdx, 3); + const totalNodesToWrap = nodesToWrap.length; + for (let i = totalNodesToWrap - 1; i >= 0; i--) { + const { parent, startIdx } = nodesToWrap[i]; + let endIdx = -1; + const isEndOfWrap = i === totalNodesToWrap - 1; + for (let j = startIdx + 1; j < parent.children.length; j++) { + const nextNode = parent.children[j]; + if ( + (isEndOfWrap && checkLogSegment(nextNode, false)) || // 日志页通常还存在历史 major 版本 + nextNode.properties?.[WRAPPER_FLAG] || // 已经被处理 + checkLogSegment(nextNode) // 下一段日志 + ) { + endIdx = j; + break; + } + } + if (endIdx === -1) continue; + + // Version + const heading = parent.children[startIdx]; + + // Find Date + let dateIdx = -1; + for (let j = startIdx + 1; j < endIdx; j++) { + const node = parent.children[j]; + if (node.type === 'element' && isValidDate(hastToString(node))) { + dateIdx = j; + break; + } + } + if (dateIdx === -1) continue; + + // Collect list nodes between dateIdx and endIdx const version = hastToString(heading); + const date = parent.children[dateIdx]; const dateStr = hastToString(date); + const details = parent.children.slice(dateIdx + 1, endIdx); const headingWrap = { type: 'element', tagName: `${COMPONENT_NAME}.Version`, - // 为标签添加语义化 className (下面同理) children: [set(heading, 'properties.className', 'changelog-version')], }; @@ -58,10 +114,13 @@ function rehypeChangelog(): UnifiedTransformer { children: [set(date, 'properties.className', 'changelog-date')], }; - const listWrap = { + const detailWrap = { type: 'element', tagName: `${COMPONENT_NAME}.Details`, - children: [set(list, 'properties.className', 'changelog-details')], + properties: { + className: 'changelog-details', + }, + children: details, }; const wrapper = { @@ -82,11 +141,11 @@ function rehypeChangelog(): UnifiedTransformer { value: JSON.stringify(dateStr), }, ], - children: [headingWrap, dateWrap, listWrap], + children: [headingWrap, dateWrap, detailWrap], }; - parent.children.splice(startIdx, 0, wrapper); - }); + parent.children.splice(startIdx, endIdx - startIdx, wrapper); + } }; } diff --git a/.dumi/theme/builtins/RefinedChangelog/index.tsx b/.dumi/theme/builtins/RefinedChangelog/index.tsx index b67cad00d6..e3669b61d5 100644 --- a/.dumi/theme/builtins/RefinedChangelog/index.tsx +++ b/.dumi/theme/builtins/RefinedChangelog/index.tsx @@ -119,7 +119,10 @@ const Version: React.FC = ({ children }) => { const DateComp: React.FC = (props) => props.children; -const DetailsComp: React.FC = (props) => props.children; +const DetailsComp: React.FC> = (props) => { + const { children, className } = props; + return
{children}
; +}; export default Object.assign(RefinedChangelog, { Version,