diff --git a/server/lib/get-dependencies-for-entry-point-name.js b/server/lib/get-dependencies-for-entry-point-name.js index 155da31..dd8cac1 100644 --- a/server/lib/get-dependencies-for-entry-point-name.js +++ b/server/lib/get-dependencies-for-entry-point-name.js @@ -30,6 +30,7 @@ function getEntryPoints() { return _entryPoints; } +// eslint-disable-next-line max-statements function recurseManifestEntryName(entryName) { const manifest = getManifest(); const entry = manifest[entryName]; @@ -38,6 +39,9 @@ function recurseManifestEntryName(entryName) { // css const styles = []; + // assets + const fonts = []; + const images = []; // imports const scripts = [entryFilePath]; // imports, dynamicImports @@ -50,11 +54,15 @@ function recurseManifestEntryName(entryName) { const { styles: moreStyles, + fonts: moreFonts, + images: moreImages, scripts: moreScripts, preloadScripts: morePreloadScripts, } = recurseManifestEntryName(importName); styles.push(...moreStyles); + fonts.push(...moreFonts); + images.push(...moreImages); scripts.push(...moreScripts); preloadScripts.push(...morePreloadScripts); } @@ -65,11 +73,15 @@ function recurseManifestEntryName(entryName) { const { styles: moreStyles, + fonts: moreFonts, + images: moreImages, scripts: moreScripts, preloadScripts: morePreloadScripts, } = recurseManifestEntryName(dynamicImportName); styles.push(...moreStyles); + fonts.push(...moreFonts); + images.push(...moreImages); scripts.push(...moreScripts); preloadScripts.push(...morePreloadScripts); } @@ -78,9 +90,36 @@ function recurseManifestEntryName(entryName) { styles.push(path.join('/', cssName)); } + for (const assetName of entry.assets || []) { + const assetFileExtension = path.extname(assetName); + const assetFilePath = path.join('/', assetName); + + if ( + // We only care about preloading `.woff2` fonts since that is all of the major + // browsers support them. + ['.woff2'].includes(assetFileExtension) && + // We only care about a few variants that we will actually likely use on the page + // (this may need to change over time). + ['-Regular-', '-Bold-', '-SemiBold-'].some((variant) => assetName.includes(variant)) + ) { + fonts.push(path.join('/', assetFilePath)); + } else if ( + // Preload a specific file we use on the room directory homepage + assetName.includes('matrix-lines-hero') + // We don't care about preloading *all* images at the moment because there are a + // lot that we just don't use even though they are bundled because they are + // referened in the CSS. + //['.jpg', '.png', '.svg'].includes(assetFileExtension) + ) { + images.push(path.join('/', assetFilePath)); + } + } + return { // css styles, + fonts, + images, // imports scripts, // dynamicImports @@ -103,11 +142,14 @@ function getDependenciesForEntryPointName(entryPointName) { )}` ); - const { styles, scripts, preloadScripts } = recurseManifestEntryName(entryPointName); + const { styles, fonts, images, scripts, preloadScripts } = + recurseManifestEntryName(entryPointName); return { // De-duplicate assets styles: Array.from(new Set(styles)), + fonts: Array.from(new Set(fonts)), + images: Array.from(new Set(images)), scripts: Array.from(new Set(scripts)), preloadScripts: Array.from(new Set(preloadScripts)), }; diff --git a/server/lib/set-headers-to-preload-assets.js b/server/lib/set-headers-to-preload-assets.js index c501a69..9a173f7 100644 --- a/server/lib/set-headers-to-preload-assets.js +++ b/server/lib/set-headers-to-preload-assets.js @@ -16,13 +16,17 @@ function setHeadersToPreloadAssets(res, pageOptions) { assert(pageOptions); assert(pageOptions.entryPoint); - const { styles, preloadScripts } = getDependenciesForEntryPointName(pageOptions.entryPoint); + const { styles, fonts, images, preloadScripts } = getDependenciesForEntryPointName( + pageOptions.entryPoint + ); + // Work on assembling the `Link` headers + // // Note: Any docs for the `` element apply to the `Link` header. "The `Link` // header contains parameters [that] are equivalent to attributes of the `` // element." // (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link#parameters) - + // // XXX: Should we add `nopush` to the `Link` headers here? Many servers initiate an // HTTP/2 Server Push when they encounter a preload link in HTTP header form // otherwise. Do we want/care about that (or maybe we don't)? (mentioned in @@ -32,16 +36,19 @@ function setHeadersToPreloadAssets(res, pageOptions) { return `<${styleUrl}>; rel=preload; as=style`; }); - // TODO: We should preload fonts as well. - // // We use `crossorigin` because fonts are fetched with anonymous mode "cors" and // "same-origin" credentials mode (see // https://drafts.csswg.org/css-fonts/#font-fetching-requirements). `crossorigin` is // just short-hand for `crossorigin=anonymous` (see // https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin and // https://html.spec.whatwg.org/multipage/infrastructure.html#cors-settings-attribute). - // - // `Link: ; rel=preload; as=font; crossorigin` + const fontLinks = fonts.map((fontUrl) => { + return `<${fontUrl}>; rel=preload; as=font; crossorigin`; + }); + + const imageLinks = images.map((imageUrl) => { + return `<${imageUrl}>; rel=preload; as=image`; + }); // We use `rel=modulepreload` instead of `rel=preload` for the JavaScript modules // because it's a nice dedicated thing to handle ESM modules that not only downloads @@ -63,7 +70,7 @@ function setHeadersToPreloadAssets(res, pageOptions) { return `<${scriptUrl}>; rel=modulepreload`; }); - res.append('Link', [].concat(styleLinks, scriptLinks).join(', ')); + res.append('Link', [].concat(styleLinks, fontLinks, imageLinks, scriptLinks).join(', ')); } module.exports = setHeadersToPreloadAssets;