Preload fonts and images (#187)

Part of https://github.com/matrix-org/matrix-public-archive/issues/132
This commit is contained in:
Eric Eastwood 2023-04-26 16:35:00 -05:00 committed by GitHub
parent f0a11139cb
commit e20a67d2ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 57 additions and 8 deletions

View File

@ -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)),
};

View File

@ -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 `<link>` element apply to the `Link` header. "The `Link`
// header contains parameters [that] are equivalent to attributes of the `<link>`
// 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: <font_to_load.woff2>; 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;