88//-----------------------------------------------------------------------------
99
1010import GithubSlugger from "github-slugger" ;
11- import { htmlCommentPattern } from "../util.js" ;
11+ import { stripHtmlComments } from "../util.js" ;
1212
1313//-----------------------------------------------------------------------------
1414// Type Definitions
1515//-----------------------------------------------------------------------------
1616
1717/**
18- * @import { Link } from "mdast";
18+ * @import { Definition, Link } from "mdast";
1919 * @import { MarkdownRuleDefinition } from "../types.js";
2020 * @typedef {"invalidFragment" } NoMissingLinkFragmentsMessageIds
2121 * @typedef {[{ ignoreCase?: boolean; allowPattern?: string }] } NoMissingLinkFragmentsOptions
@@ -76,17 +76,16 @@ export default {
7676 } ,
7777
7878 create ( context ) {
79- const [ { allowPattern : allowPatternString , ignoreCase } ] =
80- context . options ;
81- const allowPattern = allowPatternString
82- ? new RegExp ( allowPatternString , "u" )
79+ const [ { allowPattern, ignoreCase } ] = context . options ;
80+ const allowPatternOrNull = allowPattern
81+ ? new RegExp ( allowPattern , "u" )
8382 : null ;
8483
8584 const fragmentIds = new Set ( [ "top" ] ) ;
8685 const slugger = new GithubSlugger ( ) ;
8786
88- /** @type {Array<{node: Link, fragment: string} > } */
89- const linkNodes = [ ] ;
87+ /** @type {Array<Definition | Link > } */
88+ const relevantNodes = [ ] ;
9089 /** @type {string } */
9190 let headingText ;
9291
@@ -101,58 +100,52 @@ export default {
101100
102101 "heading:exit" ( ) {
103102 const customIdMatch = headingText . match ( customHeadingIdPattern ) ;
104- const baseId = customIdMatch
103+ const id = customIdMatch
105104 ? customIdMatch . groups . id
106105 : headingText ;
107- const finalId = slugger . slug ( baseId ) ;
108- fragmentIds . add ( ignoreCase ? finalId . toLowerCase ( ) : finalId ) ;
106+
107+ fragmentIds . add ( slugger . slug ( id ) ) ;
109108 } ,
110109
111110 html ( node ) {
112111 // 1. Remove all comments
113- const htmlTextWithoutComments = node . value
114- . trim ( )
115- . replace ( htmlCommentPattern , "" ) ;
112+ const htmlTextWithoutComments = stripHtmlComments ( node . value ) ;
116113
117114 // 2. Then look for IDs in the remaining text
118115 for ( const match of htmlTextWithoutComments . matchAll (
119116 htmlIdNamePattern ,
120117 ) ) {
121- const extractedId = match . groups . id ;
122- const finalId = slugger . slug ( extractedId ) ;
123- fragmentIds . add (
124- ignoreCase ? finalId . toLowerCase ( ) : finalId ,
125- ) ;
118+ const { id } = match . groups ;
119+
120+ fragmentIds . add ( slugger . slug ( id ) ) ;
126121 }
127122 } ,
128123
129- link ( node ) {
130- const url = node . url ;
131- if ( ! url || ! url . startsWith ( "#" ) ) {
132- return ;
133- }
124+ "definition, link" ( /** @type {Definition | Link } */ node ) {
125+ const { url } = node ;
134126
135- const fragment = url . slice ( 1 ) ;
136- if ( ! fragment ) {
127+ // If ` url` is empty, `"#"`, or does not start with `"#"`, skip it.
128+ if ( url === "" || url === "#" || ! url . startsWith ( "#" ) ) {
137129 return ;
138130 }
139131
140- linkNodes . push ( { node, fragment } ) ;
132+ relevantNodes . push ( node ) ;
141133 } ,
142134
143135 "root:exit" ( ) {
144- for ( const { node, fragment } of linkNodes ) {
145- /** @type {string } */
146- let decodedFragment ;
136+ for ( const node of relevantNodes ) {
137+ const fragment = node . url . slice ( 1 ) ;
138+ let decodedFragment = fragment ;
139+
140+ // Decode URI component to handle encoded characters such as `%20`.
147141 try {
148142 decodedFragment = decodeURIComponent ( fragment ) ;
149143 } catch {
150- // fallback if not valid encoding
151- decodedFragment = fragment ;
144+ // If decoding fails due to an invalid URI sequence, use the original fragment.
152145 }
153146
154147 if (
155- allowPattern ?. test ( decodedFragment ) ||
148+ allowPatternOrNull ?. test ( decodedFragment ) ||
156149 githubLineReferencePattern . test ( decodedFragment )
157150 ) {
158151 continue ;
0 commit comments