1+ import hljs from "highlight.js/lib/core" ;
2+ window . hljs = hljs ;
3+ require ( "highlightjs-line-numbers.js/dist/highlightjs-line-numbers.min.js" ) ;
4+
15// CONSTANTS
26const kLoadingMessage = "Loading ..." ;
37
8+ function removeTags ( str ) {
9+ if ( ( str === null ) || ( str === "" ) ) {
10+ return false ;
11+ }
12+ // eslint-disable-next-line no-param-reassign
13+ str = str . toString ( ) ;
14+
15+ return str . replace ( / < \/ ? [ ^ > ] + ( > | $ ) / ig, "" ) ;
16+ }
17+
418export class CodeFetcher {
519 static setupDocumentClickHandler = true ;
620
721 static getLineFromFile ( code , location ) {
822 const [ [ startLine ] ] = location ;
923
10- return code . split ( "\n" ) [ startLine - 1 ] ;
24+ return code . split ( "\n" ) . slice ( startLine >= 10 ? startLine - 10 : 0 , startLine + 10 ) . join ( "\n" ) ;
1125 }
1226
1327 static hide ( event ) {
@@ -17,12 +31,12 @@ export class CodeFetcher {
1731 }
1832
1933 if (
20- ( packageCodeElement . innerHTML && packageCodeElement . innerHTML !== kLoadingMessage ) &&
34+ ( packageCodeElement . innerText && packageCodeElement . innerText !== kLoadingMessage ) &&
2135 ! packageCodeElement . contains ( event . target )
2236 && packageCodeElement . style . visibility === "visible"
2337 ) {
2438 packageCodeElement . style . visibility = "hidden" ;
25- packageCodeElement . innerHTML = "" ;
39+ packageCodeElement . innerText = "" ;
2640 }
2741 }
2842
@@ -37,13 +51,16 @@ export class CodeFetcher {
3751 }
3852
3953 async fetchCodeLine ( event , options = { } ) {
40- const { file, location, id } = options ;
54+ const { file, location, id, value } = options ;
4155
4256 this . container = document . querySelector ( ".package-code" ) ;
4357 this . container . style . visibility = "visible" ;
58+ const isJS = file . slice ( - 3 ) === ".js" || [ ".cjs" , ".mjs" ] . includes ( file . slice ( - 4 ) ) ;
59+ const isJSON = file . slice ( - 5 ) === ".json" ;
4460
4561 if ( this . cache . has ( id ) ) {
46- this . container . innerText = this . cache . get ( id ) ;
62+ this . container . innerHTML = this . cache . get ( id ) ;
63+ hljs . initLineNumbersOnLoad ( ) ;
4764 event . stopPropagation ( ) ;
4865
4966 return ;
@@ -52,10 +69,58 @@ export class CodeFetcher {
5269 this . container . innerText = kLoadingMessage ;
5370 const code = await fetch ( `${ this . unpkgRoot } ${ file } ` ) . then ( ( response ) => response . text ( ) ) ;
5471
55- this . container . innerText = code . length ?
56- CodeFetcher . getLineFromFile ( code , location ) :
57- "Line not found ..." ;
58- this . cache . set ( id , this . container . innerText ) ;
72+ if ( code . length ) {
73+ this . container . innerText = "" ;
74+
75+ const titleElement = document . createElement ( "div" ) ;
76+ titleElement . classList . add ( "file" ) ;
77+ titleElement . innerHTML = `<a href="${ this . unpkgRoot } ${ file } " target="_blank">${ file } </a>` ;
78+
79+ const preElement = document . createElement ( "pre" ) ;
80+ preElement . appendChild ( titleElement ) ;
81+
82+ const codeElement = document . createElement ( "code" ) ;
83+ codeElement . textContent = CodeFetcher . getLineFromFile ( code , location ) ;
84+ // eslint-disable-next-line no-nested-ternary
85+ codeElement . classList . add ( `language-${ isJS ? "js" : isJSON ? "json" : "text" } ` , "hljs" ) ;
86+ codeElement . setAttribute ( "data-ln-start-from" , location [ 0 ] [ 0 ] >= 10 ? location [ 0 ] [ 0 ] - 9 : 1 ) ;
87+
88+ preElement . appendChild ( codeElement ) ;
89+ this . container . appendChild ( preElement ) ;
90+ hljs . highlightElement ( codeElement ) ;
91+ hljs . initLineNumbersOnLoad ( ) ;
92+
93+ // Highlight the relevant lines / code
94+ codeElement . innerHTML = codeElement . innerHTML . split ( "\n" ) . map ( ( line , index ) => {
95+ const withoutTags = removeTags ( line ) ;
96+ if ( withoutTags === false ) {
97+ return line ;
98+ }
99+ const incriminedCodeSingleLine = code . split ( "\n" ) . slice ( location [ 0 ] [ 0 ] - 1 , location [ 0 ] [ 0 ] ) ;
100+ const isMultiLine = location [ 0 ] [ 0 ] < location [ 1 ] [ 0 ] ;
101+ const [ [ startLine ] ] = location ;
102+ const lineIndex = startLine >= 10 ? 9 : startLine - 1 ;
103+ const isRelevantLine = isMultiLine ?
104+ lineIndex <= index && index <= location [ 1 ] [ 0 ] - 1 :
105+ // eslint-disable-next-line max-len
106+ incriminedCodeSingleLine . includes ( value ) || ( ! isMultiLine && lineIndex === index && withoutTags . includes ( value ) ) || ( ! value && lineIndex === index ) ;
107+
108+ if ( isRelevantLine ) {
109+ if ( ! isMultiLine && value && line . includes ( value ) ) {
110+ return line . replace ( value , `<span class="relevant-line">${ value } </span>` ) ;
111+ }
112+
113+ return `<span class="relevant-line">${ line } </span>` ;
114+ }
115+
116+ return line ;
117+ } ) . join ( "\n" ) ;
118+ }
119+ else {
120+ this . container . innerText = "Line not found ..." ;
121+ }
122+
123+ this . cache . set ( id , this . container . innerHTML ) ;
59124
60125 event . stopPropagation ( ) ;
61126 }
0 commit comments