diff --git a/src/server/template-renderer/index.js b/src/server/template-renderer/index.js index c8927b2de2c..5cfdef53d49 100644 --- a/src/server/template-renderer/index.js +++ b/src/server/template-renderer/index.js @@ -14,6 +14,9 @@ type TemplateRendererOptions = { template?: string | (content: string, context: any) => string; inject?: boolean; clientManifest?: ClientManifest; + getPrefetchLinkAttrs?: (ref: Resource) => Resource; + getPreloadLinkAttrs?: (ref: Resource) => Resource; + getScriptAttrs?: (ref: Resource) => Resource; shouldPreload?: (file: string, type: string) => boolean; shouldPrefetch?: (file: string, type: string) => boolean; serializer?: Function; @@ -37,6 +40,7 @@ type Resource = { extension: string; fileWithoutQuery: string; asType: string; + attrs?: string; }; export default class TemplateRenderer { @@ -55,7 +59,7 @@ export default class TemplateRenderer { this.inject = options.inject !== false // if no template option is provided, the renderer is created // as a utility object for rendering assets like preload links and scripts. - + const { template } = options this.parsedTemplate = template ? typeof template === 'string' @@ -156,10 +160,13 @@ export default class TemplateRenderer { renderPreloadLinks (context: Object): string { const files = this.getPreloadFiles(context) - const shouldPreload = this.options.shouldPreload if (files.length) { - return files.map(({ file, extension, fileWithoutQuery, asType }) => { - let extra = '' + const { getPreloadLinkAttrs, shouldPreload } = this.options + return files.map(ref => { + if(typeof getPreloadLinkAttrs === 'function') { + ref = getPreloadLinkAttrs(ref) + } + const { file, extension, fileWithoutQuery, asType, attrs } = ref // by default, we only preload scripts or css if (!shouldPreload && asType !== 'script' && asType !== 'style') { return '' @@ -168,6 +175,9 @@ export default class TemplateRenderer { if (shouldPreload && !shouldPreload(fileWithoutQuery, asType)) { return '' } + + let extra = attrs || '' + if (asType === 'font') { extra = ` type="font/${extension}" crossorigin` } @@ -185,20 +195,25 @@ export default class TemplateRenderer { } renderPrefetchLinks (context: Object): string { - const shouldPrefetch = this.options.shouldPrefetch + const { getPrefetchLinkAttrs, shouldPrefetch } = this.options if (this.prefetchFiles) { const usedAsyncFiles = this.getUsedAsyncFiles(context) const alreadyRendered = file => { return usedAsyncFiles && usedAsyncFiles.some(f => f.file === file) } - return this.prefetchFiles.map(({ file, fileWithoutQuery, asType }) => { + return this.prefetchFiles.map(ref => { + if(typeof getPrefetchLinkAttrs === 'function') { + ref = getPrefetchLinkAttrs(ref) + } + const { file, fileWithoutQuery, asType } = ref if (shouldPrefetch && !shouldPrefetch(fileWithoutQuery, asType)) { return '' } if (alreadyRendered(file)) { return '' } - return `` + const { attrs='rel="prefetch"' } = ref + return `` }).join('') } else { return '' @@ -225,8 +240,13 @@ export default class TemplateRenderer { const initial = this.preloadFiles.filter(({ file }) => isJS(file)) const async = (this.getUsedAsyncFiles(context) || []).filter(({ file }) => isJS(file)) const needed = [initial[0]].concat(async, initial.slice(1)) - return needed.map(({ file }) => { - return `` + const { getScriptAttrs } = this.options + return needed.map((ref) => { + if(typeof getScriptAttrs === 'function') { + ref = getScriptAttrs(ref) + } + const { file, attrs = 'defer' } = ref + return `` }).join('') } else { return ''