From 74909ecfd40412132e6dda7bdebd76c2dcc05274 Mon Sep 17 00:00:00 2001 From: Alex Castle Date: Wed, 30 Dec 2020 14:12:46 -0800 Subject: [PATCH] Move CSS Preloads to top of head at document render (#18864) Co-authored-by: Joe Haddad --- packages/next/pages/_document.tsx | 17 ++++++++++++++++ .../image-component/basic/pages/index.js | 5 +++++ .../image-component/basic/public/styles.css | 3 +++ .../image-component/basic/test/index.test.js | 20 +++++++++++++++++++ 4 files changed, 45 insertions(+) create mode 100644 test/integration/image-component/basic/public/styles.css diff --git a/packages/next/pages/_document.tsx b/packages/next/pages/_document.tsx index cb8384f15a4f7..923651e172910 100644 --- a/packages/next/pages/_document.tsx +++ b/packages/next/pages/_document.tsx @@ -348,6 +348,23 @@ export class Head extends Component< this.context.docComponentsRendered.Head = true let { head } = this.context + let cssPreloads: Array = [] + let otherHeadElements: Array = [] + if (head) { + head.forEach((c) => { + if ( + c && + c.type === 'link' && + c.props['rel'] === 'preload' && + c.props['as'] === 'style' + ) { + cssPreloads.push(c) + } else { + c && otherHeadElements.push(c) + } + }) + head = cssPreloads.concat(otherHeadElements) + } let children = this.props.children // show a warning if Head contains (only in development) if (process.env.NODE_ENV !== 'production') { diff --git a/test/integration/image-component/basic/pages/index.js b/test/integration/image-component/basic/pages/index.js index c2f939b1acbf1..db07560c03ec0 100644 --- a/test/integration/image-component/basic/pages/index.js +++ b/test/integration/image-component/basic/pages/index.js @@ -1,6 +1,7 @@ import React from 'react' import Image from 'next/image' import Link from 'next/link' +import Head from 'next/head' const Page = () => { return ( @@ -90,6 +91,10 @@ const Page = () => { <Link href="/lazy"> <a id="lazylink">lazy</a> </Link> + <Head> + <link rel="stylesheet" href="styles.css" /> + <link rel="preload" href="styles.css" as="style" /> + </Head> <p id="stubtext">This is the index page</p> </div> ) diff --git a/test/integration/image-component/basic/public/styles.css b/test/integration/image-component/basic/public/styles.css new file mode 100644 index 0000000000000..3d9a2b2a48459 --- /dev/null +++ b/test/integration/image-component/basic/public/styles.css @@ -0,0 +1,3 @@ +p { + color: red; +} diff --git a/test/integration/image-component/basic/test/index.test.js b/test/integration/image-component/basic/test/index.test.js index 915e37d403618..ca860b342f4b6 100644 --- a/test/integration/image-component/basic/test/index.test.js +++ b/test/integration/image-component/basic/test/index.test.js @@ -202,6 +202,23 @@ async function hasPreloadLinkMatchingUrl(url) { return false } +async function hasImagePreloadBeforeCSSPreload() { + const links = await browser.elementsByCss('link') + let foundImage = false + for (const link of links) { + const rel = await link.getAttribute('rel') + if (rel === 'preload') { + const linkAs = await link.getAttribute('as') + if (linkAs === 'image') { + foundImage = true + } else if (linkAs === 'style' && foundImage) { + return true + } + } + } + return false +} + describe('Image Component Tests', () => { beforeAll(async () => { await nextBuild(appDir) @@ -245,6 +262,9 @@ describe('Image Component Tests', () => { ) ).toBe(true) }) + it('should not create any preload tags higher up the page than CSS preload tags', async () => { + expect(await hasImagePreloadBeforeCSSPreload()).toBe(false) + }) }) describe('Client-side Image Component Tests', () => { beforeAll(async () => {