11const esbuild = require ( 'esbuild' ) ;
22const fs = require ( 'fs' ) ;
33const path = require ( 'path' ) ;
4- const crypto = require ( 'crypto' ) ;
5- const cheerio = require ( 'cheerio' ) ;
64
7- let html = fs . readFileSync ( 'src/index.html' , 'utf8' ) ;
5+ let htmlContent = fs . readFileSync ( 'src/index.html' , 'utf8' ) ;
86const itemsToCopy = [ 'favicon.ico' , 'img' ] ;
9- const cacheBuster = ( ) => crypto . randomBytes ( 3 ) . toString ( 'hex' ) . slice ( 0 , 5 ) ;
10- const uniqueHash = cacheBuster ( ) ;
7+ const cacheBuster = Math . random ( ) . toString ( 36 ) . slice ( 2 , 8 ) ;
118const addComment = ( content , fileType ) => {
129 const comment = "Hello and God bless! Christ is King! https://github.com/i1li/i" ;
1310 switch ( fileType ) {
@@ -29,82 +26,87 @@ const resolvePath = (filePath) => {
2926 }
3027 return path . join ( __dirname , 'src' , filePath ) ;
3128} ;
32- const jsFiles = [ ...html . matchAll ( scriptRegex ) ] . map ( match => resolvePath ( match [ 1 ] ) ) ;
33- const cssFiles = [ ...html . matchAll ( linkRegex ) ] . map ( match => resolvePath ( match [ 1 ] ) ) ;
34-
29+ const jsFiles = [ ...htmlContent . matchAll ( scriptRegex ) ] . map ( match => resolvePath ( match [ 1 ] ) ) ;
30+ const cssFiles = [ ...htmlContent . matchAll ( linkRegex ) ] . map ( match => resolvePath ( match [ 1 ] ) ) ;
3531if ( fs . existsSync ( 'dist' ) ) {
3632 fs . rmSync ( 'dist' , { recursive : true , force : true } ) ;
3733}
3834fs . mkdirSync ( 'dist' ) ;
3935
40- // NEW: Process links in HTML at build time
41- const $ = cheerio . load ( html ) ;
42- $ ( 'a' ) . each ( ( i , el ) => {
43- const href = $ ( el ) . attr ( 'href' ) ;
44- if ( ! href ) return ; // Skip anchors without href
45-
46- if ( href . startsWith ( 'http://' ) || href . startsWith ( 'https://' ) || href . startsWith ( '//' ) ) {
47- // External: Add target/rel
48- $ ( el ) . attr ( 'target' , '_blank' ) ;
49- $ ( el ) . attr ( 'rel' , 'noreferrer' ) ;
50- } else {
51- // Local: Add onclick with navigate call (assumes href starts with /)
52- const path = href . startsWith ( '/' ) ? href . substring ( 1 ) : href ;
53- const escapedPath = path . replace ( / ' / g, "\\'" ) ; // Escape for JS string
54- $ ( el ) . attr ( 'onclick' , `navigateSPA(event, '${ escapedPath } ')` ) ; // Pass event explicitly
55- }
56- } ) ;
57-
58- // Add onclick to overlay-eligible images
59- $ ( 'img:not(.img-footer,.img-header,.to-top,.no-overlay)' ) . each ( ( i , el ) => {
60- const src = $ ( el ) . attr ( 'src' ) ;
61- if ( src ) { // Skip images without src
62- const escapedSrc = src . replace ( / ' / g, "\\'" ) ;
63- $ ( el ) . attr ( 'onclick' , `openImageOverlay('${ escapedSrc } ');return false;` ) ;
64- }
65- } ) ;
66-
67- html = $ . html ( ) ; // Update html with modifications
36+ // Helper function to process <a> tags with regex
37+ function processLinks ( html ) {
38+ return html . replace ( / < a \s + ( [ ^ > ] * ?) > / gi, ( match , attrs ) => {
39+ const hrefMatch = attrs . match ( / h r e f \s * = \s * [ " ' ] ( [ ^ " ' ] + ) [ " ' ] / i) ;
40+ if ( ! hrefMatch ) return match ; // Skip if no href
41+ const href = hrefMatch [ 1 ] ;
42+ let newAttrs = attrs ;
43+ if ( href . startsWith ( 'http://' ) || href . startsWith ( 'https://' ) || href . startsWith ( '//' ) ) {
44+ // External: Add target and rel if not present
45+ if ( ! / t a r g e t \s * = \s * [ " ' ] _ b l a n k [ " ' ] / i. test ( newAttrs ) ) {
46+ newAttrs += ` target="_blank"` ;
47+ }
48+ if ( ! / r e l \s * = \s * [ " ' ] n o r e f e r r e r [ " ' ] / i. test ( newAttrs ) ) {
49+ newAttrs += ` rel="noreferrer"` ;
50+ }
51+ } else {
52+ // Local: Add onclick
53+ const path = href . startsWith ( '/' ) ? href . substring ( 1 ) : href ;
54+ const escapedPath = path . replace ( / ' / g, "\\'" ) ;
55+ if ( ! / o n c l i c k \s * = \s * [ " ' ] [ ^ " ' ] * [ " ' ] / i. test ( newAttrs ) ) {
56+ newAttrs += ` onclick="navigateSPA(event, '${ escapedPath } ')"` ;
57+ }
58+ }
59+ return `<a ${ newAttrs . trim ( ) } >` ;
60+ } ) ;
61+ }
62+ // Helper function to process <img> tags with regex
63+ function processImages ( html ) {
64+ return html . replace ( / < i m g \s + ( [ ^ > ] * ?) > / gi, ( match , attrs ) => {
65+ // Check for excluded classes
66+ if ( / \b ( i m g - f o o t e r | i m g - h e a d e r | t o - t o p | n o - o v e r l a y ) \b / i. test ( attrs ) ) {
67+ return match ; // Skip excluded images
68+ }
69+ const srcMatch = attrs . match ( / s r c \s * = \s * [ " ' ] ( [ ^ " ' ] + ) [ " ' ] / i) ;
70+ if ( ! srcMatch ) return match ; // Skip if no src
71+ const src = srcMatch [ 1 ] ;
72+ const escapedSrc = src . replace ( / ' / g, "\\'" ) ;
73+ let newAttrs = attrs ;
74+ if ( ! / o n c l i c k \s * = \s * [ " ' ] [ ^ " ' ] * [ " ' ] / i. test ( newAttrs ) ) {
75+ newAttrs += ` onclick="openImageOverlay('${ escapedSrc } ');return false;"` ;
76+ }
77+ return `<img ${ newAttrs . trim ( ) } >` ;
78+ } ) ;
79+ }
80+ htmlContent = processLinks ( htmlContent ) ;
81+ htmlContent = processImages ( htmlContent ) ;
6882
69- async function bundleJS ( ) {
70- let concatenatedJS = '' ;
71- for ( const file of jsFiles ) {
83+ async function bundleFiles ( type , files ) {
84+ let concatenated = '' ;
85+ for ( const file of files ) {
7286 const result = await esbuild . build ( {
7387 entryPoints : [ file ] ,
7488 bundle : false ,
7589 minify : true ,
7690 write : false ,
7791 } ) ;
78- concatenatedJS += result . outputFiles [ 0 ] . text . trim ( ) ;
92+ concatenated += result . outputFiles [ 0 ] . text . trim ( ) ;
7993 }
80- concatenatedJS = concatenatedJS . replace ( / \s + / g, ' ' ) ;
81- concatenatedJS = addComment ( concatenatedJS , 'js' ) ;
82- fs . writeFileSync ( `dist/bundle-${ uniqueHash } .js` , concatenatedJS ) ;
83- }
84- async function bundleCSS ( ) {
85- let concatenatedCSS = '' ;
86- for ( const file of cssFiles ) {
87- const result = await esbuild . build ( {
88- entryPoints : [ file ] ,
89- bundle : false ,
90- minify : true ,
91- write : false ,
92- } ) ;
93- concatenatedCSS += result . outputFiles [ 0 ] . text . trim ( ) ;
94+ if ( type === 'js' ) {
95+ concatenated = concatenated . replace ( / \s + / g, ' ' ) ;
9496 }
95- concatenatedCSS = addComment ( concatenatedCSS , 'css' ) ;
96- fs . writeFileSync ( `dist/bundle-${ uniqueHash } .css ` , concatenatedCSS ) ;
97+ concatenated = addComment ( concatenated , type ) ;
98+ fs . writeFileSync ( `dist/bundle-${ cacheBuster } . ${ type } ` , concatenated ) ;
9799}
98- Promise . all ( [ bundleJS ( ) , bundleCSS ( ) ] )
100+ Promise . all ( [ bundleFiles ( 'js' , jsFiles ) , bundleFiles ( 'css' , cssFiles ) ] )
99101 . then ( ( ) => {
100- html = html . replace ( / < s c r i p t .* ?< \/ s c r i p t > / g, '' ) ;
101- html = html . replace ( '</body>' , `<script src="/bundle-${ uniqueHash } .js"></script></body>` ) ;
102- html = html . replace ( / < l i n k .* ?s t y l e s h e e t .* ?> / g, '' ) ;
103- html = html . replace ( '</head>' , `<link rel="stylesheet" href="/bundle-${ uniqueHash } .css"></head>` ) ;
104- html = html . replace ( / \s + / g, ' ' ) ;
105- html = addComment ( html , 'html' ) ;
106- fs . writeFileSync ( 'dist/index.html' , html ) ;
107- fs . writeFileSync ( 'dist/404.html' , html ) ;
102+ htmlContent = htmlContent . replace ( / < s c r i p t .* ?< \/ s c r i p t > / g, '' ) ;
103+ htmlContent = htmlContent . replace ( '</body>' , `<script src="/bundle-${ cacheBuster } .js"></script></body>` ) ;
104+ htmlContent = htmlContent . replace ( / < l i n k .* ?s t y l e s h e e t .* ?> / g, '' ) ;
105+ htmlContent = htmlContent . replace ( '</head>' , `<link rel="stylesheet" href="/bundle-${ cacheBuster } .css"></head>` ) ;
106+ htmlContent = htmlContent . replace ( / \s + / g, ' ' ) ;
107+ htmlContent = addComment ( htmlContent , 'html' ) ;
108+ fs . writeFileSync ( 'dist/index.html' , htmlContent ) ;
109+ fs . writeFileSync ( 'dist/404.html' , htmlContent ) ;
108110 console . log ( 'Build complete' ) ;
109111 } )
110112 . catch ( ( error ) => {
0 commit comments