Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Font optimization bug fix #24162

Merged
merged 37 commits into from
Apr 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
edabb95
Updating native-url version
janicklas-ralph Apr 4, 2020
c147099
Bump version
janicklas-ralph Apr 5, 2020
63b04ca
Merge branch 'canary' of github.com:zeit/next.js into canary
janicklas-ralph Apr 16, 2020
df52e5e
Merge branch 'canary' of github.com:zeit/next.js into canary
janicklas-ralph May 4, 2020
1baaf87
Merge branch 'canary' of github.com:zeit/next.js into canary
janicklas-ralph Aug 14, 2020
68bff2a
Merge branch 'canary' of github.com:zeit/next.js into canary
janicklas-ralph Aug 24, 2020
5f3c00a
Merge branch 'canary' of github.com:zeit/next.js into canary
janicklas-ralph Sep 24, 2020
bceebd3
Merge branch 'canary' of github.com:janicklas-ralph/next.js; branch '…
janicklas-ralph Dec 4, 2020
b3921d3
Merge branch 'canary' of github.com:zeit/next.js into canary
janicklas-ralph Jan 7, 2021
9c6456f
Merge branch 'canary' of github.com:zeit/next.js into canary
janicklas-ralph Jan 29, 2021
7f37a0f
Enable font optimization by dafault
janicklas-ralph Jan 29, 2021
16b5cf2
Refactor test case
janicklas-ralph Jan 30, 2021
df6120d
Merge branch 'canary' of github.com:zeit/next.js into font
janicklas-ralph Feb 1, 2021
6c217d0
Merge branch 'canary' of github.com:zeit/next.js into font
janicklas-ralph Feb 23, 2021
e2b637c
Merge remote-tracking branch 'upstream/canary' into font
ijjk Mar 8, 2021
646edce
Update build-output test
ijjk Mar 8, 2021
064a9ab
Merge branch 'canary' of github.com:zeit/next.js into font
janicklas-ralph Mar 9, 2021
e1b801b
Moving optimizeFonts flag outside experimental and default true
janicklas-ralph Mar 9, 2021
1f724a3
Merge branch 'font' of github.com:janicklas-ralph/next.js into font
janicklas-ralph Mar 9, 2021
2a10bca
Remove flag in tests
janicklas-ralph Mar 9, 2021
a4f5193
Merge branch 'canary' of github.com:zeit/next.js into font
janicklas-ralph Mar 9, 2021
75cfad7
Merge remote-tracking branch 'upstream/canary' into font
ijjk Mar 16, 2021
0ad4bbe
Merge branch 'canary' of github.com:zeit/next.js into font
janicklas-ralph Mar 31, 2021
03e6eb6
Font bugfix. Handle failure to download fonts during build
janicklas-ralph Apr 1, 2021
9fe777d
Merge branch 'font' of github.com:janicklas-ralph/next.js into font
janicklas-ralph Apr 1, 2021
4457df9
Update error message
janicklas-ralph Apr 1, 2021
08c0f1d
Refactor minify css function
janicklas-ralph Apr 1, 2021
ff04f68
Fix lint
janicklas-ralph Apr 1, 2021
0afba62
Merge branch 'canary' of github.com:zeit/next.js into font
janicklas-ralph Apr 1, 2021
7dfea0c
Using log module to wrap message
janicklas-ralph Apr 1, 2021
cf771cd
Merge branch 'canary' of github.com:zeit/next.js into font
janicklas-ralph Apr 1, 2021
964a8bc
Merge branch 'canary' into font
ijjk Apr 5, 2021
0acfd84
Merge branch 'canary' of github.com:zeit/next.js into font
janicklas-ralph Apr 16, 2021
2c320c4
Font optimization bugfix #23896
janicklas-ralph Apr 16, 2021
594958a
Merge branch 'font' of github.com:janicklas-ralph/next.js into font
janicklas-ralph Apr 16, 2021
47fce16
Merge branch 'canary' of github.com:zeit/next.js into font
janicklas-ralph Apr 26, 2021
dbcc8ee
Adding tests
janicklas-ralph Apr 26, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 26 additions & 43 deletions packages/next/next-server/lib/post-process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,9 @@ type postProcessOptions = {
type renderOptions = {
getFontDefinition?: (url: string) => string
}

type PostProcessData = {
preloads: {
images: Array<string>
}
}

interface PostProcessMiddleware {
inspect: (
originalDom: HTMLElement,
data: PostProcessData,
options: renderOptions
) => void
mutate: (
markup: string,
data: PostProcessData,
options: renderOptions
) => Promise<string>
inspect: (originalDom: HTMLElement, options: renderOptions) => any
mutate: (markup: string, data: any, options: renderOptions) => Promise<string>
}

type middlewareSignature = {
Expand Down Expand Up @@ -58,18 +43,13 @@ async function processHTML(
if (!middlewareRegistry[0]) {
return html
}
const postProcessData: PostProcessData = {
preloads: {
images: [],
},
}
const root: HTMLElement = parse(html)
let document = html
// Calls the middleware, with some instrumentation and logging
async function callMiddleWare(middleware: PostProcessMiddleware) {
// let timer = Date.now()
middleware.inspect(root, postProcessData, data)
document = await middleware.mutate(document, postProcessData, data)
const inspectData = middleware.inspect(root, data)
document = await middleware.mutate(document, inspectData, data)
// timer = Date.now() - timer
// if (timer > MIDDLEWARE_TIME_BUDGET) {
// TODO: Identify a correct upper limit for the postprocess step
Expand All @@ -89,15 +69,11 @@ async function processHTML(
}

class FontOptimizerMiddleware implements PostProcessMiddleware {
fontDefinitions: (string | undefined)[][] = []
inspect(
originalDom: HTMLElement,
_data: PostProcessData,
options: renderOptions
) {
inspect(originalDom: HTMLElement, options: renderOptions) {
if (!options.getFontDefinition) {
return
}
const fontDefinitions: (string | undefined)[][] = []
// collecting all the requested font definitions
originalDom
.querySelectorAll('link')
Expand All @@ -115,30 +91,35 @@ class FontOptimizerMiddleware implements PostProcessMiddleware {
const nonce = element.getAttribute('nonce')

if (url) {
this.fontDefinitions.push([url, nonce])
fontDefinitions.push([url, nonce])
}
})

return fontDefinitions
}
mutate = async (
markup: string,
_data: PostProcessData,
fontDefinitions: string[][],
options: renderOptions
) => {
let result = markup
if (!options.getFontDefinition) {
return markup
}
for (const key in this.fontDefinitions) {
const [url, nonce] = this.fontDefinitions[key]

fontDefinitions.forEach((fontDef) => {
const [url, nonce] = fontDef
const fallBackLinkTag = `<link rel="stylesheet" href="${url}"/>`
if (
result.indexOf(`<style data-href="${url}">`) > -1 ||
result.indexOf(fallBackLinkTag) > -1
) {
// The font is already optimized and probably the response is cached
continue
return
}
const fontContent = options.getFontDefinition(url as string)
const fontContent = options.getFontDefinition
? options.getFontDefinition(url as string)
: null
if (!fontContent) {
/**
* In case of unreachable font definitions, fallback to default link tag.
Expand All @@ -151,13 +132,15 @@ class FontOptimizerMiddleware implements PostProcessMiddleware {
`<style data-href="${url}"${nonceStr}>${fontContent}</style></head>`
)
}
}
})

return result
}
}

class ImageOptimizerMiddleware implements PostProcessMiddleware {
inspect(originalDom: HTMLElement, _data: PostProcessData) {
inspect(originalDom: HTMLElement) {
const imgPreloads = []
const imgElements = originalDom.querySelectorAll('img')
let eligibleImages: Array<HTMLElement> = []
for (let i = 0; i < imgElements.length; i++) {
Expand All @@ -169,18 +152,18 @@ class ImageOptimizerMiddleware implements PostProcessMiddleware {
}
}

_data.preloads.images = []

for (const imgEl of eligibleImages) {
const src = imgEl.getAttribute('src')
if (src) {
_data.preloads.images.push(src)
imgPreloads.push(src)
}
}

return imgPreloads
}
mutate = async (markup: string, _data: PostProcessData) => {
mutate = async (markup: string, imgPreloads: string[]) => {
let result = markup
let imagePreloadTags = _data.preloads.images
let imagePreloadTags = imgPreloads
.filter((imgHref) => !preloadTagAlreadyExists(markup, imgHref))
.reduce(
(acc, imgHref) =>
Expand Down
30 changes: 30 additions & 0 deletions test/integration/font-optimization/pages/with-font.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Head from 'next/head'
import Link from 'next/link'

const WithFont = () => {
return (
<>
<Head>
<link
href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700;900&display=swap"
rel="stylesheet"
/>
</Head>

<div>
Page with custom fonts
<br />
<br />
<Link href="/without-font">Without font</Link>
</div>
</>
)
}

export const getServerSideProps = async () => {
return {
props: {},
}
}

export default WithFont
26 changes: 26 additions & 0 deletions test/integration/font-optimization/pages/without-font.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import Head from 'next/head'
import Link from 'next/link'

const WithoutFont = () => {
return (
<>
<Head>
<meta name="robots" content="noindex, nofollow" />
</Head>
<div>
Page without custom fonts
<br />
<br />
<Link href="/with-font">With font</Link>
</div>
</>
)
}

export const getServerSideProps = async () => {
return {
props: {},
}
}

export default WithoutFont
19 changes: 19 additions & 0 deletions test/integration/font-optimization/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,25 @@ function runTests() {
)
})

it('should only inline included fonts per page', async () => {
const html = await renderViaHTTP(appPort, '/with-font')
expect(await fsExists(builtPage('font-manifest.json'))).toBe(true)
expect(html).toContain(
'<link rel="stylesheet" data-href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700;900&amp;display=swap"/>'
)
expect(html).toMatch(
/<style data-href="https:\/\/fonts\.googleapis\.com\/css2\?family=Roboto:wght@400;700;900&display=swap">.*<\/style>/
)

const htmlWithoutFont = await renderViaHTTP(appPort, '/without-font')
expect(htmlWithoutFont).not.toContain(
'<link rel="stylesheet" data-href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700;900&amp;display=swap"/>'
)
expect(htmlWithoutFont).not.toMatch(
/<style data-href="https:\/\/fonts\.googleapis\.com\/css2\?family=Roboto:wght@400;700;900&display=swap">.*<\/style>/
)
})

it.skip('should minify the css', async () => {
const snapshotJson = JSON.parse(
await fs.readFile(join(__dirname, 'manifest-snapshot.json'), {
Expand Down