@@ -23,9 +23,7 @@ export default function rehypeExternalLinks(options: ExternalLinkOptions = {}) {
2323 customIcons = { }
2424 } = options
2525
26- return async function transformer ( tree : Root ) : Promise < void > {
27- const nodesToProcess : { node : Element ; hostname : string } [ ] = [ ]
28-
26+ return function transformer ( tree : Root ) : void {
2927 visit ( tree , 'element' , ( node : Element ) => {
3028 if ( node . tagName === 'a' && typeof node . properties ?. href === 'string' ) {
3129 const href = node . properties . href
@@ -43,92 +41,57 @@ export default function rehypeExternalLinks(options: ExternalLinkOptions = {}) {
4341 const url = protocolRelative ? `http:${ href } ` : href
4442 try {
4543 const hostname = new URL ( url ) . hostname
46- nodesToProcess . push ( { node, hostname } )
47- } catch ( e ) {
48- // Ignore invalid URLs
49- }
50- }
51- }
52- } )
53-
54- await Promise . all (
55- nodesToProcess . map ( async ( { node, hostname } ) => {
56- let iconNode : Element | undefined
44+ let iconNode : Element | undefined
5745
58- const customIconKey = customIcons ?. [ hostname ]
59- if ( customIconKey ) {
60- let svgString = Icons [ customIconKey as keyof typeof Icons ]
61- if ( svgString ) {
62- if ( ! svgString . trim ( ) . startsWith ( '<svg' ) ) {
63- svgString = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">${ svgString } </svg>`
64- }
65- const dataUri = `data:image/svg+xml;base64,${ Buffer . from ( svgString ) . toString ( 'base64' ) } `
66- iconNode = {
67- type : 'element' ,
68- tagName : 'img' ,
69- properties : {
70- src : dataUri ,
71- className : [ 'external-link-icon' ] ,
72- alt : '' , // Decorative
73- width : 16 ,
74- height : 16
75- } ,
76- children : [ ]
46+ const customIconKey = customIcons ?. [ hostname ]
47+ if ( customIconKey ) {
48+ let svgString = Icons [ customIconKey as keyof typeof Icons ]
49+ if ( svgString ) {
50+ if ( ! svgString . trim ( ) . startsWith ( '<svg' ) ) {
51+ svgString = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">${ svgString } </svg>`
52+ }
53+ const dataUri = `data:image/svg+xml;base64,${ Buffer . from ( svgString ) . toString (
54+ 'base64'
55+ ) } `
56+ iconNode = {
57+ type : 'element' ,
58+ tagName : 'img' ,
59+ properties : {
60+ src : dataUri ,
61+ className : [ 'external-link-icon' ] ,
62+ alt : '' , // Decorative
63+ width : 16 ,
64+ height : 16
65+ } ,
66+ children : [ ]
67+ }
68+ }
7769 }
78- }
79- }
8070
81- if ( ! iconNode ) {
82- const controller = new AbortController ( )
83- const timeout = setTimeout ( ( ) => {
84- controller . abort ( )
85- } , 2000 )
86-
87- try {
88- const response = await fetch ( `https://www.google.com/s2/favicons?domain=${ hostname } &size=16` , {
89- signal : controller . signal
90- } )
91-
92- if ( ! response . ok ) {
93- throw new Error ( `Failed to fetch favicon for ${ hostname } , status: ${ response . status } ` )
71+ if ( ! iconNode ) {
72+ iconNode = {
73+ type : 'element' ,
74+ tagName : 'img' ,
75+ properties : {
76+ src : `https://www.google.com/s2/favicons?domain=${ hostname } &size=16` ,
77+ className : [ 'external-link-icon' ] ,
78+ alt : '' , // Decorative
79+ width : 16 ,
80+ height : 16 ,
81+ onerror : "this.parentNode.replaceChild(document.createTextNode('🌐'), this)"
82+ } ,
83+ children : [ ]
84+ }
9485 }
9586
96- const buffer = await response . arrayBuffer ( )
97- const base64 = Buffer . from ( buffer ) . toString ( 'base64' )
98- const type = response . headers . get ( 'content-type' ) || 'image/x-icon'
99- const dataUri = `data:${ type } ;base64,${ base64 } `
100-
101- iconNode = {
102- type : 'element' ,
103- tagName : 'img' ,
104- properties : {
105- src : dataUri ,
106- className : [ 'external-link-icon' ] ,
107- alt : '' , // Decorative
108- width : 16 ,
109- height : 16
110- } ,
111- children : [ ]
87+ if ( iconNode ) {
88+ node . children . unshift ( iconNode )
11289 }
11390 } catch ( e ) {
114- // When timeout or other fetch error occurs
115- iconNode = {
116- type : 'element' ,
117- tagName : 'span' ,
118- properties : {
119- className : [ 'external-link-icon' ]
120- } ,
121- children : [ { type : 'text' , value : '🌐' } ]
122- }
123- } finally {
124- clearTimeout ( timeout )
91+ // Ignore invalid URLs
12592 }
12693 }
127-
128- if ( iconNode ) {
129- node . children . unshift ( iconNode )
130- }
131- } )
132- )
94+ }
95+ } )
13396 }
13497}
0 commit comments