@@ -62,13 +62,37 @@ class Vite implements Htmlable
62
62
*/
63
63
protected $ styleTagAttributesResolvers = [];
64
64
65
+ /**
66
+ * The preload tag attributes resolvers.
67
+ *
68
+ * @var array
69
+ */
70
+ protected $ preloadTagAttributesResolvers = [];
71
+
72
+ /**
73
+ * The preloaded assets.
74
+ *
75
+ * @var array
76
+ */
77
+ protected $ preloadedAssets = [];
78
+
65
79
/**
66
80
* The cached manifest files.
67
81
*
68
82
* @var array
69
83
*/
70
84
protected static $ manifests = [];
71
85
86
+ /**
87
+ * Get the preloaded assets.
88
+ *
89
+ * @var array
90
+ */
91
+ public function preloadedAssets ()
92
+ {
93
+ return $ this ->preloadedAssets ;
94
+ }
95
+
72
96
/**
73
97
* Get the Content Security Policy nonce applied to all generated tags.
74
98
*
@@ -186,6 +210,23 @@ public function useStyleTagAttributes($attributes)
186
210
return $ this ;
187
211
}
188
212
213
+ /**
214
+ * Use the given callback to resolve attributes for preload tags.
215
+ *
216
+ * @param (callable(string, string, ?array, ?array): array)|array $attributes
217
+ * @return $this
218
+ */
219
+ public function usePreloadTagAttributes ($ attributes )
220
+ {
221
+ if (! is_callable ($ attributes )) {
222
+ $ attributes = fn () => $ attributes ;
223
+ }
224
+
225
+ $ this ->preloadTagAttributesResolvers [] = $ attributes ;
226
+
227
+ return $ this ;
228
+ }
229
+
189
230
/**
190
231
* Generate Vite tags for an entrypoint.
191
232
*
@@ -212,14 +253,36 @@ public function __invoke($entrypoints, $buildDirectory = null)
212
253
$ manifest = $ this ->manifest ($ buildDirectory );
213
254
214
255
$ tags = collect ();
256
+ $ preloads = collect ();
215
257
216
258
foreach ($ entrypoints as $ entrypoint ) {
217
259
$ chunk = $ this ->chunk ($ manifest , $ entrypoint );
218
260
261
+ $ preloads ->push ([
262
+ $ chunk ['src ' ],
263
+ $ this ->assetPath ("{$ buildDirectory }/ {$ chunk ['file ' ]}" ),
264
+ $ chunk ,
265
+ $ manifest
266
+ ]);
267
+
219
268
foreach ($ chunk ['imports ' ] ?? [] as $ import ) {
269
+ $ preloads ->push ([
270
+ $ import ,
271
+ $ this ->assetPath ("{$ buildDirectory }/ {$ manifest [$ import ]['file ' ]}" ),
272
+ $ manifest [$ import ],
273
+ $ manifest
274
+ ]);
275
+
220
276
foreach ($ manifest [$ import ]['css ' ] ?? [] as $ css ) {
221
277
$ partialManifest = Collection::make ($ manifest )->where ('file ' , $ css );
222
278
279
+ $ preloads ->push ([
280
+ $ partialManifest ->keys ()->first (),
281
+ $ this ->assetPath ("{$ buildDirectory }/ {$ css }" ),
282
+ $ partialManifest ->first (),
283
+ $ manifest
284
+ ]);
285
+
223
286
$ tags ->push ($ this ->makeTagForChunk (
224
287
$ partialManifest ->keys ()->first (),
225
288
$ this ->assetPath ("{$ buildDirectory }/ {$ css }" ),
@@ -239,6 +302,13 @@ public function __invoke($entrypoints, $buildDirectory = null)
239
302
foreach ($ chunk ['css ' ] ?? [] as $ css ) {
240
303
$ partialManifest = Collection::make ($ manifest )->where ('file ' , $ css );
241
304
305
+ $ preloads ->push ([
306
+ $ partialManifest ->keys ()->first (),
307
+ $ this ->assetPath ("{$ buildDirectory }/ {$ css }" ),
308
+ $ partialManifest ->first (),
309
+ $ manifest
310
+ ]);
311
+
242
312
$ tags ->push ($ this ->makeTagForChunk (
243
313
$ partialManifest ->keys ()->first (),
244
314
$ this ->assetPath ("{$ buildDirectory }/ {$ css }" ),
@@ -250,7 +320,10 @@ public function __invoke($entrypoints, $buildDirectory = null)
250
320
251
321
[$ stylesheets , $ scripts ] = $ tags ->partition (fn ($ tag ) => str_starts_with ($ tag , '<link ' ));
252
322
253
- return new HtmlString ($ stylesheets ->join ('' ).$ scripts ->join ('' ));
323
+ $ preloads = $ preloads ->sortByDesc (fn ($ args ) => $ this ->isCssPath ($ args [1 ]))
324
+ ->map (fn ($ args ) => $ this ->makePreloadTagForChunk (...$ args ));
325
+
326
+ return new HtmlString ($ preloads ->join ('' ).$ stylesheets ->join ('' ).$ scripts ->join ('' ));
254
327
}
255
328
256
329
/**
@@ -286,6 +359,26 @@ protected function makeTagForChunk($src, $url, $chunk, $manifest)
286
359
);
287
360
}
288
361
362
+ /**
363
+ * Make a preload tag for the given chunk.
364
+ *
365
+ * @param string $src
366
+ * @param string $url
367
+ * @param array $chunk
368
+ * @param array $manifest
369
+ * @return string|null
370
+ */
371
+ protected function makePreloadTagForChunk ($ src , $ url , $ chunk , $ manifest )
372
+ {
373
+ $ attributes = $ this ->resolvePreloadTagAttributes ($ src , $ url , $ chunk , $ manifest );
374
+
375
+ $ this ->preloadedAssets [$ url ] = $ this ->parseAttributes (
376
+ Collection::make ($ attributes )->forget ('href ' )->all ()
377
+ );
378
+
379
+ return '<link ' .implode (' ' , $ this ->parseAttributes ($ attributes )).' /> ' ;
380
+ }
381
+
289
382
/**
290
383
* Resolve the attributes for the chunks generated script tag.
291
384
*
@@ -330,6 +423,37 @@ protected function resolveStylesheetTagAttributes($src, $url, $chunk, $manifest)
330
423
return $ attributes ;
331
424
}
332
425
426
+ /**
427
+ * Resolve the attributes for the chunks generated preload tag.
428
+ *
429
+ * @param string $src
430
+ * @param string $url
431
+ * @param array $chunk
432
+ * @param array $manifest
433
+ * @return array
434
+ */
435
+ protected function resolvePreloadTagAttributes ($ src , $ url , $ chunk , $ manifest )
436
+ {
437
+ $ attributes = $ this ->isCssPath ($ url ) ? [
438
+ 'rel ' => 'preload ' ,
439
+ 'as ' => 'style ' ,
440
+ 'href ' => $ url ,
441
+ ] : [
442
+ 'rel ' => 'modulepreload ' ,
443
+ 'href ' => $ url ,
444
+ ];
445
+
446
+ $ attributes = $ this ->integrityKey !== false
447
+ ? array_merge ($ attributes , ['integrity ' => $ chunk [$ this ->integrityKey ] ?? false ])
448
+ : $ attributes ;
449
+
450
+ foreach ($ this ->preloadTagAttributesResolvers as $ resolver ) {
451
+ $ attributes = array_merge ($ attributes , $ resolver ($ src , $ url , $ chunk , $ manifest ));
452
+ }
453
+
454
+ return $ attributes ;
455
+ }
456
+
333
457
/**
334
458
* Generate an appropriate tag for the given URL in HMR mode.
335
459
*
0 commit comments