@@ -24,23 +24,30 @@ const REGEX = new RegExp(`(\\b|\\W)(${PATTERN})(\\b|\\W)`);
24
24
const CUSTOM_LINK_START = new RegExp ( "^<([a-zA-Z]+Link|a) " ) ;
25
25
const CUSTOM_LINK_END = new RegExp ( "^</([a-zA-Z]+Link|a)>" ) ;
26
26
27
- function replace ( node ) {
27
+ function replace ( state , node ) {
28
+ if ( node . type == "root" ) return ;
29
+
28
30
// If this is an empty node there's nothing to consider.
29
- if ( ! node . children ) return ;
31
+ if ( ! node . children ) return "skip" ;
30
32
31
33
// Do not replace abbreviations in headings because that appears to break the heading anchors.
32
- if ( node . type == "heading" ) return ;
34
+ if ( node . type == "heading" ) {
35
+ state . alreadyExplained = { } ;
36
+ return "skip" ;
37
+ }
33
38
34
39
// Do not replace abbreviations in links because that's two interactive
35
40
// nested elements nested in each other, and we decided we don't want to
36
41
// style that either.
37
42
//
38
43
// This currently doesn't handle nesting of e.g.
39
44
// <a><strong><abbr>... but we don't have that in docs.
40
- if ( node . type == "link" ) return ;
45
+ if ( node . type == "link" ) return "skip" ;
41
46
42
47
let insideCustomLink = false ;
43
48
49
+ const newChildren = [ ] ;
50
+
44
51
// If a text node is present in child nodes, check if an abbreviation is present
45
52
for ( let c = 0 ; c < node . children . length ; c ++ ) {
46
53
const child = node . children [ c ] ;
@@ -85,36 +92,42 @@ function replace(node) {
85
92
}
86
93
}
87
94
88
- if ( insideCustomLink ) continue ;
89
- if ( child . type !== "text" ) continue ;
90
- if ( ! REGEX . test ( child . value ) ) continue ;
95
+ if ( insideCustomLink || child . type !== "text" || ! REGEX . test ( child . value ) ) {
96
+ newChildren . push ( child ) ;
97
+ continue ;
98
+ }
91
99
92
100
// Transform node
93
101
const newTexts = child . value . split ( REGEX ) ;
94
102
95
- // Remove old text node
96
- node . children . splice ( c , 1 ) ;
97
-
98
103
// Replace abbreviations
99
104
for ( let i = 0 ; i < newTexts . length ; i ++ ) {
100
105
const content = newTexts [ i ] ;
101
- node . children . splice (
102
- c + i ,
103
- 0 ,
104
- TERMS [ content ]
105
- ? {
106
- type : "html" ,
107
- value : `<div class="term-wrapper"><span class="term">${ content } </span><span class="description" role="tooltip" aria-label="${ content } definition">${ TERMS [ content ] } </span></div>` ,
108
- }
109
- : {
110
- type : "text" ,
111
- value : content ,
112
- }
113
- ) ;
106
+ let newNode ;
107
+ if ( TERMS [ content ] && ! state . alreadyExplained [ content ] ) {
108
+ state . alreadyExplained [ content ] = true ;
109
+ newNode = {
110
+ type : "html" ,
111
+ value : `<div class="term-wrapper"><span class="term">${ content } </span><span class="description" role="tooltip" aria-label="${ content } definition">${ TERMS [ content ] } </span></div>` ,
112
+ } ;
113
+ } else {
114
+ newNode = {
115
+ type : "text" ,
116
+ value : content ,
117
+ } ;
118
+ }
119
+
120
+ newChildren . push ( newNode ) ;
114
121
}
115
122
}
123
+
124
+ node . children = newChildren ;
116
125
}
117
126
118
127
module . exports = async ( { markdownAST } ) => {
119
- visit ( markdownAST , ( ) => true , replace ) ;
128
+ const state = {
129
+ alreadyExplained : { } ,
130
+ } ;
131
+ const replaceWithState = replace . bind ( null , state ) ;
132
+ visit ( markdownAST , ( ) => true , replaceWithState ) ;
120
133
} ;
0 commit comments