@@ -15,6 +15,7 @@ type TemplateRendererOptions = {
15
15
inject ?: boolean ;
16
16
clientManifest ?: ClientManifest ;
17
17
shouldPreload ?: ( file : string , type : string ) => boolean ;
18
+ shouldPrefetch ?: ( file : string , type : string ) => boolean ;
18
19
} ;
19
20
20
21
export type ClientManifest = {
@@ -30,7 +31,7 @@ export type ClientManifest = {
30
31
}
31
32
} ;
32
33
33
- type PreloadFile = {
34
+ type Resource = {
34
35
file : string ;
35
36
extension : string ;
36
37
fileWithoutQuery: string ;
@@ -43,8 +44,8 @@ export default class TemplateRenderer {
43
44
parsedTemplate : ParsedTemplate | null ;
44
45
publicPath : string ;
45
46
clientManifest : ClientManifest ;
46
- preloadFiles : Array < string > ;
47
- prefetchFiles : Array < string > ;
47
+ preloadFiles : Array < Resource > ;
48
+ prefetchFiles : Array < Resource > ;
48
49
mapFiles : AsyncFileMapper ;
49
50
50
51
constructor ( options : TemplateRendererOptions ) {
@@ -61,8 +62,8 @@ export default class TemplateRenderer {
61
62
const clientManifest = this . clientManifest = options . clientManifest
62
63
this . publicPath = clientManifest . publicPath . replace ( / \/ $ / , '' )
63
64
// preload/prefetch directives
64
- this . preloadFiles = clientManifest . initial
65
- this . prefetchFiles = clientManifest . async
65
+ this . preloadFiles = ( clientManifest . initial || [ ] ) . map ( normalizeFile )
66
+ this . prefetchFiles = ( clientManifest . async || [ ] ) . map ( normalizeFile )
66
67
// initial async chunk mapping
67
68
this . mapFiles = createMapper ( clientManifest )
68
69
}
@@ -125,30 +126,21 @@ export default class TemplateRenderer {
125
126
return this . renderPreloadLinks ( context ) + this . renderPrefetchLinks ( context )
126
127
}
127
128
128
- getPreloadFiles ( context : Object ) : Array < PreloadFile > {
129
+ getPreloadFiles ( context : Object ) : Array < Resource > {
129
130
const usedAsyncFiles = this . getUsedAsyncFiles ( context )
130
131
if ( this . preloadFiles || usedAsyncFiles ) {
131
- return ( this . preloadFiles || [ ] ) . concat ( usedAsyncFiles || [ ] ) . map ( file => {
132
- const withoutQuery = file . replace ( / \? .* / , '' )
133
- const extension = path . extname ( withoutQuery ) . slice ( 1 )
134
- return {
135
- file,
136
- extension,
137
- fileWithoutQuery : withoutQuery ,
138
- asType : getPreloadType ( extension )
139
- }
140
- } )
132
+ return ( this . preloadFiles || [ ] ) . concat ( usedAsyncFiles || [ ] )
141
133
} else {
142
134
return [ ]
143
135
}
144
136
}
145
137
146
138
renderPreloadLinks ( context : Object ) : string {
147
139
const files = this . getPreloadFiles ( context )
140
+ const shouldPreload = this . options . shouldPreload
148
141
if ( files . length ) {
149
142
return files . map ( ( { file, extension, fileWithoutQuery, asType } ) => {
150
143
let extra = ''
151
- const shouldPreload = this . options . shouldPreload
152
144
// by default, we only preload scripts or css
153
145
if ( ! shouldPreload && asType !== 'script' && asType !== 'style' ) {
154
146
return ''
@@ -174,17 +166,20 @@ export default class TemplateRenderer {
174
166
}
175
167
176
168
renderPrefetchLinks ( context : Object ) : string {
169
+ const shouldPrefetch = this . options . shouldPrefetch
177
170
if ( this . prefetchFiles ) {
178
171
const usedAsyncFiles = this . getUsedAsyncFiles ( context )
179
172
const alreadyRendered = file => {
180
- return usedAsyncFiles && usedAsyncFiles . some ( f => f === file )
173
+ return usedAsyncFiles && usedAsyncFiles . some ( f => f . file === file )
181
174
}
182
- return this . prefetchFiles . map ( file => {
183
- if ( ! alreadyRendered ( file ) ) {
184
- return `<link rel="prefetch" href="${ this . publicPath } /${ file } ">`
185
- } else {
175
+ return this . prefetchFiles . map ( ( { file, fileWithoutQuery, asType } ) => {
176
+ if ( shouldPrefetch && ! shouldPrefetch ( fileWithoutQuery , asType ) ) {
177
+ return ''
178
+ }
179
+ if ( alreadyRendered ( file ) ) {
186
180
return ''
187
181
}
182
+ return `<link rel="prefetch" href="${ this . publicPath } /${ file } ">`
188
183
} ) . join ( '' )
189
184
} else {
190
185
return ''
@@ -205,20 +200,21 @@ export default class TemplateRenderer {
205
200
206
201
renderScripts ( context : Object ) : string {
207
202
if ( this . clientManifest ) {
208
- const initial = this . clientManifest . initial
203
+ const initial = this . preloadFiles
209
204
const async = this . getUsedAsyncFiles ( context )
210
205
const needed = [ initial [ 0 ] ] . concat ( async || [ ] , initial . slice ( 1 ) )
211
- return needed . filter ( isJS ) . map ( file => {
206
+ return needed . filter ( ( { file } ) => isJS ( file ) ) . map ( ( { file } ) => {
212
207
return `<script src="${ this . publicPath } /${ file } " defer></script>`
213
208
} ) . join ( '' )
214
209
} else {
215
210
return ''
216
211
}
217
212
}
218
213
219
- getUsedAsyncFiles ( context : Object ) : ?Array < string > {
214
+ getUsedAsyncFiles ( context : Object ) : ?Array < Resource > {
220
215
if ( ! context . _mappedFiles && context . _registeredComponents && this . mapFiles ) {
221
- context . _mappedFiles = this . mapFiles ( Array . from ( context . _registeredComponents ) )
216
+ const registered = Array . from ( context . _registeredComponents )
217
+ context . _mappedFiles = this . mapFiles ( registered ) . map ( normalizeFile )
222
218
}
223
219
return context . _mappedFiles
224
220
}
@@ -232,6 +228,17 @@ export default class TemplateRenderer {
232
228
}
233
229
}
234
230
231
+ function normalizeFile ( file : string ) : Resource {
232
+ const withoutQuery = file . replace ( / \? .* / , '' )
233
+ const extension = path . extname ( withoutQuery ) . slice ( 1 )
234
+ return {
235
+ file ,
236
+ extension ,
237
+ fileWithoutQuery : withoutQuery ,
238
+ asType : getPreloadType ( extension )
239
+ }
240
+ }
241
+
235
242
function getPreloadType ( ext : string ) : string {
236
243
if ( ext === 'js' ) {
237
244
return 'script '
0 commit comments