11const path = require ( 'path' ) ;
22const { detectIndent, getFileExtension } = require ( '../Common/Helpers' ) ;
3+ const { HtmlParser } = require ( '../Common/HtmlParser' ) ;
34const { optionPreloadAsException } = require ( './Messages/Exception' ) ;
45
56/**
@@ -108,6 +109,7 @@ class Preload {
108109 const LF = this . pluginOption . getLF ( ) ;
109110 const indent = LF + detectIndent ( content , insertPos - 1 ) ;
110111 const groupBy = { } ;
112+ let hasImage = false ;
111113
112114 // normalize preload attributes and create sorted groups in the order of the specified 'preload' options
113115 for ( const conf of options ) {
@@ -127,6 +129,7 @@ class Preload {
127129
128130 // for the `font` type, the `crossorigin` attribute is mandatory, if it is not defined, set to default value
129131 if ( as === 'font' && ! ( 'crossorigin' in attrs ) ) attrs . crossorigin = true ;
132+ if ( as === 'image' ) hasImage = true ;
130133
131134 // whether the 'type' property exist regardless of a value;
132135 // if the property exists and have the undefined value, exclude this attribute in generating preload tag
@@ -141,6 +144,9 @@ class Preload {
141144 groupBy [ as ] = [ ] ;
142145 }
143146
147+ // pick up image attributes to preload
148+ const imageAttributes = hasImage ? this . #getImageAttributes( content ) : new Map ( ) ;
149+
144150 // prepare a flat array with preload assets
145151 for ( let item of data . assets ) {
146152 if ( item . inline ) continue ;
@@ -170,6 +176,12 @@ class Preload {
170176
171177 if ( this . pluginOption . applyAdvancedFiler ( { sourceFiles, outputFile } , conf . _opts . filter ) ) {
172178 let props = this . createPreloadAttributes ( conf , { integrity : item . integrity , crossorigin } ) ;
179+ let imgAttrs = imageAttributes . get ( item . assetFile ) ;
180+
181+ if ( imgAttrs ) {
182+ props . attrs = Object . assign ( props . attrs , imgAttrs ) ;
183+ }
184+
173185 preloadAssets . set ( item . assetFile , props ) ;
174186 }
175187 }
@@ -204,19 +216,20 @@ class Preload {
204216 : assetItem . assetFile ;
205217
206218 if ( this . pluginOption . applyAdvancedFiler ( { sourceFiles, outputFile } , conf . _opts . filter ) ) {
207- preloadAssets . set ( outputFile , conf . _opts ) ;
219+ let props = this . createPreloadAttributes ( conf ) ;
220+ preloadAssets . set ( outputFile , props ) ;
208221 }
209222 }
210223 }
211224 }
212225
213226 // save generated preload tags for verbose
214227 data . preloads = [ ] ;
215- for ( const [ filename , opts ] of preloadAssets . entries ( ) ) {
216- const attrs = { ... opts . attrs } ;
228+ for ( const [ filename , props ] of preloadAssets . entries ( ) ) {
229+ const { attrs } = props ;
217230
218231 attrs . href = filename ;
219- if ( ! opts . hasType ) {
232+ if ( ! props . hasType ) {
220233 const ext = getFileExtension ( filename ) ;
221234 attrs . type = mimeType [ attrs . as ] ?. [ ext ] || attrs . as + '/' + ext ;
222235 }
@@ -246,19 +259,59 @@ class Preload {
246259 }
247260
248261 /**
249- * @param {Object } conf
250- * @param {string|undefined } integrity
251- * @param {string } crossorigin
252- * @return {Object } Returns preload attributes. It my contains integrity if exists.
262+ * @param {Object } conf The source options from configuration.
263+ * @param {string|undefined } integrity The integrity, used by script and style only.
264+ * @param {string } crossorigin The attribute required for integrity, used by script and style only.
265+ * @return {Object } Returns new object of preload attributes. It may contains integrity if exists.
253266 */
254- createPreloadAttributes ( conf , { integrity, crossorigin } ) {
255- let opts = { ...conf . _opts } ;
267+ createPreloadAttributes ( conf , { integrity, crossorigin } = { } ) {
268+ let props = { ...conf . _opts } ;
269+
270+ // create new object for attributes, to keep original config unchanged
271+ props . attrs = { ...props . attrs } ;
256272
257273 if ( integrity ) {
258- opts . attrs = { ...opts . attrs , integrity, crossorigin } ;
274+ props . attrs . integrity = integrity ;
275+ props . attrs . crossorigin = crossorigin ;
276+ }
277+
278+ return props ;
279+ }
280+
281+ /**
282+ * Pick up image attributes to preload.
283+ *
284+ * @param {string } content
285+ * @return {Map<string, any> }
286+ */
287+ #getImageAttributes( content ) {
288+ const imageAttributes = new Map ( ) ;
289+ const parseOptions = {
290+ tag : 'img' ,
291+ attributes : [ 'srcset' , 'sizes' ] ,
292+ } ;
293+ const imageTags = HtmlParser . parseTag ( content , parseOptions ) ;
294+
295+ for ( const tag of imageTags ) {
296+ const attrs = { } ;
297+ let hasAttrs = false ;
298+
299+ if ( tag . attrs . srcset ) {
300+ attrs . imagesrcset = tag . attrs . srcset . replaceAll ( / \n | \b | \s { 2 , } / g, '' ) ;
301+ hasAttrs = true ;
302+ }
303+
304+ if ( tag . attrs . sizes ) {
305+ attrs . imagesizes = tag . attrs . sizes . replaceAll ( / \n | \b | \s { 2 , } / g, '' ) ;
306+ hasAttrs = true ;
307+ }
308+
309+ if ( hasAttrs ) {
310+ imageAttributes . set ( tag . attrs . src , attrs ) ;
311+ }
259312 }
260313
261- return opts ;
314+ return imageAttributes ;
262315 }
263316
264317 /**
0 commit comments