From edabb95f3aa9d9d6b666b30d6046de51d20458cf Mon Sep 17 00:00:00 2001 From: Janicklas Ralph James Date: Sat, 4 Apr 2020 14:36:29 -0700 Subject: [PATCH 01/15] Updating native-url version --- packages/next/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/package.json b/packages/next/package.json index a72c2e1e6af4bc7..3f0e8fd3f508ca1 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -83,7 +83,7 @@ "jest-worker": "24.9.0", "loader-utils": "2.0.0", "mini-css-extract-plugin": "0.8.0", - "native-url": "0.2.6", + "native-url": "0.3.0", "pnp-webpack-plugin": "1.5.0", "postcss": "7.0.27", "prop-types": "15.7.2", From c1470991c1efb7b995961e0d743d6029768ade6a Mon Sep 17 00:00:00 2001 From: Janicklas Ralph James Date: Sat, 4 Apr 2020 17:32:37 -0700 Subject: [PATCH 02/15] Bump version --- packages/next/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/package.json b/packages/next/package.json index 3f0e8fd3f508ca1..8cb0f1dd11d3dd2 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -83,7 +83,7 @@ "jest-worker": "24.9.0", "loader-utils": "2.0.0", "mini-css-extract-plugin": "0.8.0", - "native-url": "0.3.0", + "native-url": "0.3.1", "pnp-webpack-plugin": "1.5.0", "postcss": "7.0.27", "prop-types": "15.7.2", From 0aa63a4c1b79e206fcb5a72fb0fb9c8a97855854 Mon Sep 17 00:00:00 2001 From: Janicklas Ralph James Date: Mon, 24 Aug 2020 18:47:41 -0700 Subject: [PATCH 03/15] Setup CSS optimization. Experimental inlining critical css --- packages/next/build/webpack-config.ts | 3 ++ packages/next/export/index.ts | 1 + packages/next/export/worker.ts | 9 +++++ packages/next/next-server/server/config.ts | 1 + .../next/next-server/server/next-server.ts | 5 +++ packages/next/next-server/server/render.tsx | 39 ++++++++++++++----- packages/next/pages/_document.tsx | 39 ++++++++++++------- 7 files changed, 72 insertions(+), 25 deletions(-) diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index acfece465c8b5c1..3f0deda84bd90b1 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -947,6 +947,9 @@ export default async function getBaseWebpackConfig( 'process.env.__NEXT_OPTIMIZE_IMAGES': JSON.stringify( config.experimental.optimizeImages ), + 'process.env.__NEXT_OPTIMIZE_CSS': JSON.stringify( + config.experimental.optimizeCss && !dev + ), 'process.env.__NEXT_SCROLL_RESTORATION': JSON.stringify( config.experimental.scrollRestoration ), diff --git a/packages/next/export/index.ts b/packages/next/export/index.ts index fcd2edfc1edaecf..ec789ce230a93a4 100644 --- a/packages/next/export/index.ts +++ b/packages/next/export/index.ts @@ -408,6 +408,7 @@ export default async function exportApp( serverless: isTargetLikeServerless(nextConfig.target), optimizeFonts: nextConfig.experimental.optimizeFonts, optimizeImages: nextConfig.experimental.optimizeImages, + optimizeCss: nextConfig.experimental.optimizeCss, }) for (const validation of result.ampValidations || []) { diff --git a/packages/next/export/worker.ts b/packages/next/export/worker.ts index eb8734f07119ffe..0c2e5b402db0745 100644 --- a/packages/next/export/worker.ts +++ b/packages/next/export/worker.ts @@ -48,6 +48,7 @@ interface ExportPageInput { serverless: boolean optimizeFonts: boolean optimizeImages: boolean + optimizeCss: boolean } interface ExportPageResults { @@ -66,6 +67,7 @@ interface RenderOpts { inAmpMode?: boolean optimizeFonts?: boolean optimizeImages?: boolean + optimizeCss?: boolean fontManifest?: FontManifest } @@ -87,6 +89,7 @@ export default async function exportPage({ serverless, optimizeFonts, optimizeImages, + optimizeCss, }: ExportPageInput): Promise { let results: ExportPageResults = { ampValidations: [], @@ -226,6 +229,8 @@ export default async function exportPage({ optimizeFonts, /// @ts-ignore optimizeImages, + /// @ts-ignore + optimizeCss, fontManifest: optimizeFonts ? requireFontManifest(distDir, serverless) : null, @@ -276,6 +281,9 @@ export default async function exportPage({ if (optimizeImages) { process.env.__NEXT_OPTIMIZE_IMAGES = JSON.stringify(true) } + if (optimizeCss) { + process.env.__NEXT_OPTIMIZE_CSS = JSON.stringify(true) + } curRenderOpts = { ...components, ...renderOpts, @@ -283,6 +291,7 @@ export default async function exportPage({ params, optimizeFonts, optimizeImages, + optimizeCss, fontManifest: optimizeFonts ? requireFontManifest(distDir, serverless) : null, diff --git a/packages/next/next-server/server/config.ts b/packages/next/next-server/server/config.ts index c9eefdca419a663..c723aa2dab9c057 100644 --- a/packages/next/next-server/server/config.ts +++ b/packages/next/next-server/server/config.ts @@ -53,6 +53,7 @@ const defaultConfig: { [key: string]: any } = { productionBrowserSourceMaps: false, optimizeFonts: false, optimizeImages: false, + optimizeCss: false, scrollRestoration: false, unstable_webpack5cache: false, }, diff --git a/packages/next/next-server/server/next-server.ts b/packages/next/next-server/server/next-server.ts index 195931dfd871bf3..57b5276d28227ad 100644 --- a/packages/next/next-server/server/next-server.ts +++ b/packages/next/next-server/server/next-server.ts @@ -123,6 +123,7 @@ export default class Server { optimizeFonts: boolean fontManifest: FontManifest optimizeImages: boolean + optimizeCss: boolean } private compression?: Middleware private onErrorMiddleware?: ({ err }: { err: Error }) => Promise @@ -175,6 +176,7 @@ export default class Server { ? requireFontManifest(this.distDir, this._isLikeServerless) : null, optimizeImages: this.nextConfig.experimental.optimizeImages, + optimizeCss: this.nextConfig.experimental.optimizeCss, } // Only the `publicRuntimeConfig` key is exposed to the client side @@ -242,6 +244,9 @@ export default class Server { if (this.renderOpts.optimizeImages) { process.env.__NEXT_OPTIMIZE_IMAGES = JSON.stringify(true) } + if (this.renderOpts.optimizeCss) { + process.env.__NEXT_OPTIMIZE_CSS = JSON.stringify(true) + } } protected currentPhase(): string { diff --git a/packages/next/next-server/server/render.tsx b/packages/next/next-server/server/render.tsx index 661d5a9d03acb49..09f9f3e0629b12d 100644 --- a/packages/next/next-server/server/render.tsx +++ b/packages/next/next-server/server/render.tsx @@ -2,6 +2,7 @@ import { IncomingMessage, ServerResponse } from 'http' import { ParsedUrlQuery } from 'querystring' import React from 'react' import { renderToStaticMarkup, renderToString } from 'react-dom/server' +import Critters from 'critters' import { UnwrapPromise } from '../../lib/coalesced-function' import { GSP_NO_RETURNED_VALUE, @@ -149,6 +150,7 @@ export type RenderOptsPartial = { optimizeFonts: boolean fontManifest?: FontManifest optimizeImages: boolean + optimizeCss: boolean devOnlyCacheBusterQueryString?: string } @@ -186,6 +188,7 @@ function renderDocument( appGip, unstable_runtimeJS, devOnlyCacheBusterQueryString, + optimizeCss, }: RenderOpts & { props: any docProps: DocumentInitialProps @@ -237,6 +240,7 @@ function renderDocument( canonicalBase, ampPath, inAmpMode, + optimizeCss, isDevelopment: !!dev, hybridAmp, dynamicImports, @@ -802,16 +806,31 @@ export async function renderToHTML( } } - html = await postProcess( - html, - { - getFontDefinition, - }, - { - optimizeFonts: renderOpts.optimizeFonts, - optimizeImages: renderOpts.optimizeImages, - } - ) + if (renderOpts.optimizeFonts || renderOpts.optimizeImages) { + html = await postProcess( + html, + { + getFontDefinition, + }, + { + optimizeFonts: renderOpts.optimizeFonts, + optimizeImages: renderOpts.optimizeImages, + } + ) + } + + if (renderOpts.optimizeCss) { + const cssOptimizer = new Critters({ + ssrMode: true, + reduceInlineStyles: false, + path: renderOpts.distDir, + publicPath: '/_next/', + preload: 'media', + fonts: false, + }) + + html = await cssOptimizer.process(html) + } if (inAmpMode || hybridAmp) { // fix & being escaped for amphtml rel link diff --git a/packages/next/pages/_document.tsx b/packages/next/pages/_document.tsx index 96b16f7385b1230..d6519cbc55a0e2c 100644 --- a/packages/next/pages/_document.tsx +++ b/packages/next/pages/_document.tsx @@ -151,7 +151,10 @@ export class Head extends Component< context!: React.ContextType - getCssLinks(files: DocumentFiles): JSX.Element[] | null { + getCssLinks( + files: DocumentFiles, + optimizeCss: Boolean + ): JSX.Element[] | null { const { assetPrefix, devOnlyCacheBusterQueryString } = this.context const cssFiles = files.allFiles.filter((f) => f.endsWith('.css')) const sharedFiles = new Set(files.sharedFiles) @@ -160,19 +163,24 @@ export class Head extends Component< cssFiles.forEach((file) => { const isSharedFile = sharedFiles.has(file) + if (!optimizeCss) { + cssLinkElements.push( + + ) + } + cssLinkElements.push( - , )} {process.env.__NEXT_OPTIMIZE_FONTS - ? this.makeStylesheetInert(this.getCssLinks(files)) - : this.getCssLinks(files)} + ? this.makeStylesheetInert(this.getCssLinks(files, optimizeCss)) + : this.getCssLinks(files, optimizeCss)} {!disableRuntimeJS && this.getPreloadDynamicChunks()} {!disableRuntimeJS && this.getPreloadMainLinks(files)} {this.context.isDevelopment && ( From 90f475dd249adbf937650bf5afc6af6d178effa6 Mon Sep 17 00:00:00 2001 From: Janicklas Ralph James Date: Tue, 29 Sep 2020 15:46:39 -0700 Subject: [PATCH 04/15] Critical CSS setup --- packages/next/next-server/server/render.tsx | 3 +- packages/next/package.json | 1 + packages/next/pages/_document.tsx | 12 ++-- yarn.lock | 71 ++++++++++++++++++++- 4 files changed, 76 insertions(+), 11 deletions(-) diff --git a/packages/next/next-server/server/render.tsx b/packages/next/next-server/server/render.tsx index 5d42314904af950..13b7b4c395a07ca 100644 --- a/packages/next/next-server/server/render.tsx +++ b/packages/next/next-server/server/render.tsx @@ -158,6 +158,7 @@ export type RenderOptsPartial = { devOnlyCacheBusterQueryString?: string resolvedUrl?: string resolvedAsPath?: string + distDir?: string } export type RenderOpts = LoadComponentsReturnType & RenderOptsPartial @@ -195,7 +196,6 @@ function renderDocument( appGip, unstable_runtimeJS, devOnlyCacheBusterQueryString, - optimizeCss, }: RenderOpts & { props: any docComponentsRendered: DocumentProps['docComponentsRendered'] @@ -265,7 +265,6 @@ function renderDocument( canonicalBase, ampPath, inAmpMode, - optimizeCss, isDevelopment: !!dev, hybridAmp, dynamicImports, diff --git a/packages/next/package.json b/packages/next/package.json index 0d1b9352b2ac8b1..cfde8e3a4b80d5d 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -88,6 +88,7 @@ "cacache": "15.0.5", "caniuse-lite": "^1.0.30001113", "chokidar": "2.1.8", + "critters": "0.0.4", "crypto-browserify": "3.12.0", "css-loader": "4.3.0", "cssnano-simple": "1.2.0", diff --git a/packages/next/pages/_document.tsx b/packages/next/pages/_document.tsx index 2aeb684f8d32b1c..4103a3bf0fc711d 100644 --- a/packages/next/pages/_document.tsx +++ b/packages/next/pages/_document.tsx @@ -156,10 +156,7 @@ export class Head extends Component< context!: React.ContextType - getCssLinks( - files: DocumentFiles, - optimizeCss: Boolean - ): JSX.Element[] | null { + getCssLinks(files: DocumentFiles): JSX.Element[] | null { const { assetPrefix, devOnlyCacheBusterQueryString, @@ -183,7 +180,7 @@ export class Head extends Component< cssFiles.forEach((file) => { const isSharedFile = sharedFiles.has(file) - if (!optimizeCss) { + if (!process.env.__NEXT_OPTIMIZE_CSS) { cssLinkElements.push( )} {process.env.__NEXT_OPTIMIZE_FONTS - ? this.makeStylesheetInert(this.getCssLinks(files, optimizeCss)) - : this.getCssLinks(files, optimizeCss)} + ? this.makeStylesheetInert(this.getCssLinks(files)) + : this.getCssLinks(files)}