1+ import * as path from 'path' ;
2+ import { promises as fs } from "fs" ;
3+
4+ function getMimeType ( ext : string ) : string {
5+ switch ( ext ) {
6+ case 'jpg' :
7+ case 'jpeg' :
8+ return 'image/jpeg' ;
9+ case 'svg' :
10+ return 'image/svg+xml' ;
11+ case 'gif' :
12+ case 'png' :
13+ case 'webp' :
14+ return `image/${ ext } ` ;
15+ default :
16+ return 'application/octet-stream' ;
17+ }
18+ } ;
19+
20+ export async function inlineImages ( html : string , htmlDir : string ) : Promise < string > {
21+ const imgTagRegex = / < i m g ( .* ) ? s r c = " ( [ \w . \- \/ ] + ) " ( .* ) > / ;
22+ let matches = html . match ( new RegExp ( imgTagRegex , 'g' ) ) ;
23+ if ( ! matches )
24+ return html ;
25+ let imgPromises = matches
26+ . map ( imgTag => imgTag . match ( imgTagRegex ) [ 2 ] )
27+ . map ( relImgPath => path . resolve ( htmlDir , relImgPath ) )
28+ . map ( imgPath => fs . readFile ( imgPath ) ) ;
29+ let i = 0 ;
30+ return Promise . all ( imgPromises ) . then ( images =>
31+ html . replace ( new RegExp ( imgTagRegex , 'g' ) , ( _match , p1 , p2 , p3 ) =>
32+ `<img ${ p1 || '' } src="data:${ getMimeType ( p2 . split ( '.' ) . pop ( ) ) } ;base64, ${ images [ i ++ ] . toString ( 'base64' ) } "${ p3 } >`
33+ ) ) ;
34+ }
35+
36+ export async function inlineHtmlScripts ( html : string , htmlDir : string ) : Promise < string > {
37+ const scriptTagRegex = / < s c r i p t (?: .* ) ? s r c = " ( [ \w . \- \/ ] + ) " .* > < \/ s c r i p t > / ;
38+ let matches = html . match ( new RegExp ( scriptTagRegex , 'g' ) ) ;
39+ if ( ! matches )
40+ return html ;
41+ let scriptPromises = matches
42+ . map ( scriptTag => scriptTag . match ( scriptTagRegex ) [ 1 ] )
43+ . map ( relScriptPath => path . resolve ( htmlDir , relScriptPath ) )
44+ . map ( scriptPath => fs . readFile ( scriptPath , 'utf8' ) ) ;
45+ let i = 0 ;
46+ return Promise . all ( scriptPromises ) . then ( scripts =>
47+ html . replace ( new RegExp ( scriptTagRegex , 'g' ) , ( ) =>
48+ `<script>${ scripts [ i ++ ] . replace ( / < \/ s c r i p t > / g, '<\\/script>' ) } </script>` ) ) ;
49+ }
50+
51+ export async function inlineHtmlStyles ( html : string , htmlDir : string ) : Promise < string > {
52+ const linkTagRegex = / < l i n k (?: .* ) ? r e l = " s t y l e s h e e t " (?: .* ) ? h r e f = " ( [ \w . \- \/ ] + ) " .* > | < l i n k (?: .* ) ? h r e f = " ( [ \w . \- \/ ] + ) " (?: .* ) ? r e l = " s t y l e s h e e t " .* > / ;
53+ let matches = html . match ( new RegExp ( linkTagRegex , 'g' ) ) ;
54+ if ( ! matches )
55+ return html ;
56+ let stylesheetPromises = matches
57+ . map ( linkTag => {
58+ let m = linkTag . match ( linkTagRegex ) ;
59+ return m [ 1 ] || m [ 2 ] ;
60+ } )
61+ . map ( relPath => path . resolve ( htmlDir , relPath ) )
62+ . map ( stylesheetPath => fs . readFile ( stylesheetPath , 'utf8' ) ) ;
63+ let i = 0 ;
64+ return Promise . all ( stylesheetPromises ) . then ( stylesheets =>
65+ html . replace ( new RegExp ( linkTagRegex , 'g' ) , ( ) =>
66+ `<style>${ stylesheets [ i ++ ] } </style>` ) ) ;
67+ }
68+
69+ export async function inlineAll ( html : string , htmlDir : string ) : Promise < string > {
70+ return await inlineHtmlStyles (
71+ await inlineImages (
72+ await inlineHtmlScripts ( html , htmlDir ) ,
73+ htmlDir ) ,
74+ htmlDir )
75+ }
0 commit comments