|
1 | 1 | import type { Package } from "@changesets/types"; |
2 | 2 | import { getPackages } from "@manypkg/get-packages"; |
3 | | -import { fromMarkdown as stringToMdast } from "mdast-util-from-markdown"; |
4 | | -import { toMarkdown as mdastToString } from "mdast-util-to-markdown"; |
5 | | -import { toString as mdastNodeToString } from "mdast-util-to-string"; |
6 | 3 |
|
7 | 4 | export const BumpLevels = { |
8 | 5 | dep: 0, |
@@ -34,53 +31,54 @@ export async function getChangedPackages( |
34 | 31 | } |
35 | 32 |
|
36 | 33 | export function getChangelogEntry(changelog: string, version: string) { |
37 | | - const ast = stringToMdast(changelog); |
38 | | - |
39 | 34 | let highestLevel: number = BumpLevels.dep; |
40 | | - |
41 | | - const nodes = ast.children; |
42 | | - let headingStartInfo: |
43 | | - | { |
44 | | - index: number; |
45 | | - depth: number; |
46 | | - } |
47 | | - | undefined; |
| 35 | + let headingStartInfo: { index: number; depth: number } | undefined; |
48 | 36 | let endIndex: number | undefined; |
49 | 37 |
|
50 | | - for (let i = 0; i < nodes.length; i++) { |
51 | | - const node = nodes[i]; |
52 | | - if (node.type === "heading") { |
53 | | - const stringified: string = mdastNodeToString(node); |
54 | | - const match = stringified.toLowerCase().match(/(major|minor|patch)/); |
55 | | - if (match != null) { |
56 | | - const level = BumpLevels[match[0] as "major" | "minor" | "patch"]; |
57 | | - highestLevel = Math.max(level, highestLevel); |
58 | | - } |
59 | | - if (headingStartInfo == null && stringified === version) { |
60 | | - headingStartInfo = { |
61 | | - index: i, |
62 | | - depth: node.depth, |
63 | | - }; |
| 38 | + // Iterate through each headings and code blocks (for skipping its contents) |
| 39 | + const regex = /^(#{1,6})\s(.*)$|^(`{3,})/gm; |
| 40 | + let match: RegExpExecArray | null; |
| 41 | + while ((match = regex.exec(changelog)) != null) { |
| 42 | + // Skip over code blocks so we don't match any headings inside of them |
| 43 | + if (match[3]) { |
| 44 | + const endOfCodeBlockRegex = new RegExp(`^${match[3]}`, "gm"); |
| 45 | + endOfCodeBlockRegex.lastIndex = regex.lastIndex; |
| 46 | + const endMatch = endOfCodeBlockRegex.exec(changelog); |
| 47 | + if (endMatch) { |
| 48 | + // Start next search for headings after the end of the code block |
| 49 | + regex.lastIndex = endOfCodeBlockRegex.lastIndex; |
64 | 50 | continue; |
65 | | - } |
66 | | - if ( |
67 | | - endIndex == null && |
68 | | - headingStartInfo != null && |
69 | | - headingStartInfo.depth === node.depth |
70 | | - ) { |
71 | | - endIndex = i; |
| 51 | + } else { |
| 52 | + // Can't find end of code block, probably malformed |
72 | 53 | break; |
73 | 54 | } |
74 | 55 | } |
| 56 | + |
| 57 | + const headingDepth = match[1].length; |
| 58 | + const headingText = match[2].trim(); |
| 59 | + |
| 60 | + // Search for the highest bump level in the entire changelog |
| 61 | + const levelMatch = /(major|minor|patch)/.exec(headingText.toLowerCase()); |
| 62 | + if (levelMatch != null) { |
| 63 | + const level = BumpLevels[levelMatch[0] as "major" | "minor" | "patch"]; |
| 64 | + highestLevel = Math.max(level, highestLevel); |
| 65 | + } |
| 66 | + |
| 67 | + // Search for heading of the entry |
| 68 | + if (headingText === version) { |
| 69 | + headingStartInfo = { index: regex.lastIndex, depth: headingDepth }; |
| 70 | + continue; |
| 71 | + } |
| 72 | + |
| 73 | + // If we've found the entry heading, search for the closing heading with the same depth |
| 74 | + if (headingStartInfo && headingDepth === headingStartInfo.depth) { |
| 75 | + endIndex = match.index; |
| 76 | + break; |
| 77 | + } |
75 | 78 | } |
76 | | - if (headingStartInfo) { |
77 | | - ast.children = (ast.children as any).slice( |
78 | | - headingStartInfo.index + 1, |
79 | | - endIndex, |
80 | | - ); |
81 | | - } |
| 79 | + |
82 | 80 | return { |
83 | | - content: mdastToString(ast), |
| 81 | + content: changelog.slice(headingStartInfo?.index, endIndex).trim(), |
84 | 82 | highestLevel, |
85 | 83 | }; |
86 | 84 | } |
|
0 commit comments