1- const { remark } = require ( 'remark' ) ;
1+ const remarkParse = require ( 'remark-parse' ) ;
2+ const remarkStringify = require ( 'remark-stringify' ) ;
3+ const { unified } = require ( 'unified' ) ;
4+ const visitParents = require ( 'unist-util-visit-parents' ) ;
25
36/**
47 * Updates the markdown content for better UX and compatibility with Docusaurus v3.
58 * @param {string } changelog The markdown content.
69 * @returns {string } The updated markdown content.
710 */
811function updateChangelog ( changelog ) {
9- const tree = remark . parse ( changelog ) ;
10-
11- bumpHeadingsLevels ( tree ) ;
12- linkifyUserTags ( tree ) ;
13- prettifyPRLinks ( tree ) ;
14-
15- changelog = remark . stringify ( tree ) ;
12+ const pipeline = unified ( )
13+ . use ( remarkParse )
14+ . use ( incrementHeadingLevels )
15+ . use ( prettifyPRLinks )
16+ . use ( linkifyUserTags )
17+ . use ( remarkStringify ) ;
18+
19+ changelog = pipeline . processSync ( changelog ) . toString ( ) ;
1620 changelog = addFrontmatter ( changelog ) ;
1721 changelog = escapeMDXCharacters ( changelog ) ;
1822 return changelog ;
@@ -25,94 +29,82 @@ function updateChangelog(changelog) {
2529 * @param {* } tree Remark AST tree.
2630 * @returns {void } Nothing. This function modifies the tree in place.
2731 */
28- function bumpHeadingsLevels ( tree ) {
29- tree . children ?. forEach ( ( child ) => {
30- if ( child . type === 'heading' ) {
31- child . depth += 1 ;
32- }
33-
34- bumpHeadingsLevels ( child ) ;
32+ const incrementHeadingLevels = ( ) => ( tree ) => {
33+ visitParents ( tree , 'heading' , ( node ) => {
34+ node . depth += 1 ;
3535 } ) ;
36- }
36+ } ;
3737
3838/**
3939 * Links user tags in the markdown content. This function replaces the user tags
4040 * (e.g. `@username`) with a link to the user's GitHub profile (just like GitHub's UI).
4141 * @param {* } tree Remark AST tree.
4242 * @returns {void } Nothing. This function modifies the tree in place.
4343 */
44- function linkifyUserTags ( tree ) {
45- for ( let i = 0 ; i < tree . children ?. length ; i ++ ) {
46- const child = tree . children [ i ] ;
47- if ( child . type === 'text' ) {
48- const userTagRegex = / @ ( [ a - z A - Z 0 - 9 - ] + ) ( \s | $ ) / g;
49- const match = userTagRegex . exec ( child . value ) ;
50-
51- if ( match ) {
52- const username = match [ 1 ] ;
53- const ending = match [ 2 ] === ' ' ? ' ' : '' ;
54- const before = child . value . slice ( 0 , match . index ) ;
55- const after = child . value . slice ( userTagRegex . lastIndex ) ;
56-
57- const link = {
58- type : 'link' ,
59- url : `https://github.com/${ username } ` ,
60- children : [ { type : 'text' , value : `@${ username } ` } ] ,
61- } ;
62- child . value = before ;
63-
64- tree . children . splice ( i + 1 , 0 , link ) ;
65-
66- if ( after ) {
67- tree . children . splice ( i + 2 , 0 , { type : 'text' , value : `${ ending } ${ after } ` } ) ;
68- }
69-
70- i += 2 ;
71- }
72- }
73-
74- linkifyUserTags ( child ) ;
75- }
76- }
44+ const linkifyUserTags = ( ) => ( tree ) => {
45+ visitParents ( tree , 'text' , ( node , parents ) => {
46+ const userTagRegex = / @ ( [ a - z A - Z 0 - 9 - ] + ) ( \s | $ ) / g;
47+ const match = userTagRegex . exec ( node . value ) ;
48+
49+ if ( ! match ) return ;
50+
51+ const directParent = parents [ parents . length - 1 ] ;
52+ const nodeIndexInParent = directParent . children . findIndex ( ( x ) => x === node ) ;
53+
54+ const username = match [ 1 ] ;
55+ const ending = match [ 2 ] === ' ' ? ' ' : '' ;
56+ const before = node . value . slice ( 0 , match . index ) ;
57+ const after = node . value . slice ( userTagRegex . lastIndex ) ;
58+
59+ const link = {
60+ type : 'link' ,
61+ url : `https://github.com/${ username } ` ,
62+ children : [ { type : 'text' , value : `@${ username } ` } ] ,
63+ } ;
64+ node . value = before ;
65+ directParent . children . splice ( nodeIndexInParent + 1 , 0 , link ) ;
66+
67+ if ( ! after ) return nodeIndexInParent + 2 ;
68+
69+ directParent . children . splice ( nodeIndexInParent + 2 , 0 , { type : 'text' , value : `${ ending } ${ after } ` } ) ;
70+ return nodeIndexInParent + 3 ;
71+ } ) ;
72+ } ;
7773
7874/**
7975 * Prettifies PR links in the markdown content. Just like GitHub's UI, this function
8076 * replaces the full PR URL with a link represented by the PR number (prefixed by a hashtag).
8177 * @param {* } tree Remark AST tree.
8278 * @returns {void } Nothing. This function modifies the tree in place.
8379 */
84- function prettifyPRLinks ( tree ) {
85- for ( let i = 0 ; i < tree . children ?. length ; i ++ ) {
86- const child = tree . children [ i ] ;
87- if ( child . type === 'text' ) {
88- const prLinkRegex = / h t t p s : \/ \/ g i t h u b .c o m \/ .* \/ p u l l \/ ( \d + ) / g;
89- const match = prLinkRegex . exec ( child . value ) ;
90-
91- if ( match ) {
92- const prNumber = match [ 1 ] ;
93- const before = child . value . slice ( 0 , match . index ) ;
94- const after = child . value . slice ( prLinkRegex . lastIndex ) ;
95-
96- const link = {
97- type : 'link' ,
98- url : match [ 0 ] ,
99- children : [ { type : 'text' , value : `#${ prNumber } ` } ] ,
100- } ;
101- child . value = before ;
102-
103- tree . children . splice ( i + 1 , 0 , link ) ;
104-
105- if ( after ) {
106- tree . children . splice ( i + 2 , 0 , { type : 'text' , value : after } ) ;
107- }
108-
109- i += 2 ;
110- }
111- }
112-
113- prettifyPRLinks ( child ) ;
114- }
115- }
80+ const prettifyPRLinks = ( ) => ( tree ) => {
81+ visitParents ( tree , 'text' , ( node , parents ) => {
82+ const prLinkRegex = / h t t p s : \/ \/ g i t h u b .c o m \/ [ ^ \s ] + \/ p u l l \/ ( \d + ) / g;
83+ const match = prLinkRegex . exec ( node . value ) ;
84+
85+ if ( ! match ) return ;
86+
87+ const directParent = parents [ parents . length - 1 ] ;
88+ const nodeIndexInParent = directParent . children . findIndex ( ( x ) => x === node ) ;
89+
90+ const prNumber = match [ 1 ] ;
91+ const before = node . value . slice ( 0 , match . index ) ;
92+ const after = node . value . slice ( prLinkRegex . lastIndex ) ;
93+
94+ const link = {
95+ type : 'link' ,
96+ url : match [ 0 ] ,
97+ children : [ { type : 'text' , value : `#${ prNumber } ` } ] ,
98+ } ;
99+ node . value = before ;
100+
101+ directParent . children . splice ( nodeIndexInParent + 1 , 0 , link ) ;
102+ if ( ! after ) return nodeIndexInParent + 1 ;
103+
104+ directParent . children . splice ( nodeIndexInParent + 2 , 0 , { type : 'text' , value : after } ) ;
105+ return nodeIndexInParent + 2 ;
106+ } ) ;
107+ } ;
116108
117109/**
118110 * Adds frontmatter to the markdown content.
0 commit comments