11const path = require ( 'path' )
2+ const url = require ( 'url' )
23const markdownLinkExtractor = require ( 'markdown-link-extractor' )
34const unified = require ( 'unified' )
45const parser = require ( 'remark-parse' )
@@ -29,28 +30,73 @@ const cleanText = (string) => {
2930module . exports = ( { content, name } , _ , arr ) => {
3031 let newContent = content
3132 const b = markdownLinkExtractor ( content , true )
32- . filter ( ( { href : link , title } ) => {
33- if ( title && title . startsWith ( ':include' ) ) return false
34- const ext = path . parse ( link ) . ext
33+ . filter ( ( { title } ) => ! title || ! title . startsWith ( ':include' ) )
34+ . map ( ( { href } ) => ( { href, parsed : url . parse ( href ) } ) )
35+ . filter ( ( { parsed } ) => {
36+ const ext = path . parse ( parsed . pathname ) . ext
3537 return ext === '' || ext === '.md'
3638 } )
37- . map ( v => v . href )
38- . map ( link => ( { file : arr . find ( ( { name } ) => name . includes ( link ) ) , link } ) )
39+ . map ( ( { href, parsed } ) => {
40+ const linkPath = parsed . pathname
41+ let anchor = parsed . hash ? parsed . hash . substring ( 1 ) : null
42+ if ( ! anchor && parsed . query ) {
43+ const params = new URLSearchParams ( parsed . query )
44+ if ( params . has ( 'id' ) ) {
45+ anchor = params . get ( 'id' )
46+ }
47+ }
48+ const resolvedPath = path . resolve ( path . dirname ( name ) , linkPath )
49+ const docsPath = arr . find ( ( { name } ) => name . endsWith ( 'docs/README.md' ) )
50+ const docsDir = docsPath ? path . dirname ( docsPath . name ) : ''
51+
52+ return {
53+ file : arr . find ( ( { name : fileName } ) => {
54+ if ( fileName === resolvedPath ) return true
55+ if ( fileName === `${ resolvedPath } .md` ) return true
56+
57+ if ( docsDir ) {
58+ const resolvedFromDocs = path . resolve ( docsDir , linkPath )
59+ if ( fileName === resolvedFromDocs ) return true
60+ if ( fileName === `${ resolvedFromDocs } .md` ) return true
61+ }
62+ return false
63+ } ) ,
64+ link : href ,
65+ anchor
66+ }
67+ } )
3968 . filter ( ( { file } ) => file )
40- . map ( ( { file : { content } , link } ) => ( {
69+ . map ( ( { file : { content } , link, anchor } ) => ( {
4170 ast : unified ( ) . use ( parser )
4271 . parse ( content ) ,
43- link
72+ link,
73+ anchor
4474 } ) )
45- . map ( ( { ast, link } ) => {
46- const [ a ] = ast . children . filter ( ( { type } ) => type === 'heading' )
47- if ( ! a ) {
75+ . map ( ( { ast, link, anchor } ) => {
76+ let headingNode
77+ const headings = ast . children . filter ( ( { type } ) => type === 'heading' )
78+
79+ if ( anchor ) {
80+ for ( const heading of headings ) {
81+ const array = [ ]
82+ recursiveGetValueInChildren ( heading . children , array )
83+ const value = cleanText ( array . join ( ' ' ) ) . trim ( )
84+ if ( slug ( value ) === anchor ) {
85+ headingNode = heading
86+ break
87+ }
88+ }
89+ } else {
90+ [ headingNode ] = headings
91+ }
92+
93+ if ( ! headingNode ) {
4894 console . error ( 'no heading (non blocking error)' , link )
4995 return { link, unsafeTag : '' }
5096 }
5197
5298 const array = [ ]
53- recursiveGetValueInChildren ( a . children , array )
99+ recursiveGetValueInChildren ( headingNode . children , array )
54100 const value = cleanText ( array . join ( ' ' ) ) . trim ( )
55101
56102 return { link, unsafeTag : value }
@@ -65,7 +111,8 @@ module.exports = ({ content, name }, _, arr) => {
65111 } ) )
66112
67113 b . forEach ( ( { tag, link } ) => {
68- newContent = newContent . replace ( new RegExp ( '\\[(.*)\\]\\(' + link + '\\)' ) , `[$1](${ tag } )` )
114+ const escapedLink = link . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, '\\$&' )
115+ newContent = newContent . replace ( new RegExp ( '\\[(.*)\\]\\(' + escapedLink + '\\)' ) , `[$1](${ tag } )` )
69116 } )
70117
71118 return { content : newContent , name }
0 commit comments