diff --git a/packages/next/client/index.tsx b/packages/next/client/index.tsx index 3c8626f10e4a0..128e49063e9b5 100644 --- a/packages/next/client/index.tsx +++ b/packages/next/client/index.tsx @@ -17,7 +17,7 @@ import * as envConfig from '../next-server/lib/runtime-config' import { getURL, loadGetInitialProps, ST } from '../next-server/lib/utils' import type { NEXT_DATA } from '../next-server/lib/utils' import initHeadManager from './head-manager' -import PageLoader, { StyleSheetTuple } from './page-loader' +import PageLoader, { looseToArray, StyleSheetTuple } from './page-loader' import measureWebVitals from './performance-relayer' import { createRouter, makePublicRouterInstance } from './router' @@ -84,26 +84,7 @@ if (hasBasePath(asPath)) { type RegisterFn = (input: [string, () => void]) => void -const looseToArray = (input: any): T[] => [].slice.call(input) - -const pageLoader = new PageLoader( - buildId, - prefix, - page, - looseToArray(document.styleSheets) - .filter( - (el: CSSStyleSheet) => - el.ownerNode && - (el.ownerNode as Element).tagName === 'LINK' && - (el.ownerNode as Element).hasAttribute('data-n-p') - ) - .map((sheet) => ({ - href: (sheet.ownerNode as Element).getAttribute('href')!, - text: looseToArray(sheet.cssRules) - .map((r) => r.cssText) - .join(''), - })) -) +const pageLoader = new PageLoader(buildId, prefix, page) const register: RegisterFn = ([r, f]) => pageLoader.registerPage(r, f) if (window.__NEXT_P) { // Defer page registration for another tick. This will increase the overall diff --git a/packages/next/client/page-loader.ts b/packages/next/client/page-loader.ts index 21bf4e5a44fa3..fc4f7b073448c 100644 --- a/packages/next/client/page-loader.ts +++ b/packages/next/client/page-loader.ts @@ -12,6 +12,25 @@ import { searchParamsToUrlQuery } from '../next-server/lib/router/utils/querystr import { getRouteMatcher } from '../next-server/lib/router/utils/route-matcher' import { getRouteRegex } from '../next-server/lib/router/utils/route-regex' +export const looseToArray = (input: any): T[] => + [].slice.call(input) + +function getInitialStylesheets(): StyleSheetTuple[] { + return looseToArray(document.styleSheets) + .filter( + (el: CSSStyleSheet) => + el.ownerNode && + (el.ownerNode as Element).tagName === 'LINK' && + (el.ownerNode as Element).hasAttribute('data-n-p') + ) + .map((sheet) => ({ + href: (sheet.ownerNode as Element).getAttribute('href')!, + text: looseToArray(sheet.cssRules) + .map((r) => r.cssText) + .join(''), + })) +} + function hasRel(rel: string, link?: HTMLLinkElement) { try { link = document.createElement('link') @@ -99,7 +118,6 @@ export type PageCacheEntry = { error: any } | GoodPageCache export default class PageLoader { private initialPage: string - private initialStyleSheets: StyleSheetTuple[] private buildId: string private assetPrefix: string private pageCache: Record @@ -109,14 +127,8 @@ export default class PageLoader { private promisedSsgManifest?: Promise private promisedDevPagesManifest?: Promise - constructor( - buildId: string, - assetPrefix: string, - initialPage: string, - initialStyleSheets: StyleSheetTuple[] - ) { + constructor(buildId: string, assetPrefix: string, initialPage: string) { this.initialPage = initialPage - this.initialStyleSheets = initialStyleSheets this.buildId = buildId this.assetPrefix = assetPrefix @@ -422,23 +434,34 @@ export default class PageLoader { }) } + const isInitialLoad = route === this.initialPage const promisedDeps: Promise = // Shared styles will already be on the page: route === '/_app' || // We use `style-loader` in development: process.env.NODE_ENV !== 'production' ? Promise.resolve([]) - : route === this.initialPage - ? Promise.resolve(this.initialStyleSheets) : // Tests that this does not block hydration: // test/integration/css-fixtures/hydrate-without-deps/ - this.getDependencies(route) - .then((deps) => deps.filter((d) => d.endsWith('.css'))) - .then((cssFiles) => - // These files should've already been fetched by now, so this - // should resolve pretty much instantly. - Promise.all(cssFiles.map((d) => fetchStyleSheet(d))) + (isInitialLoad + ? Promise.resolve( + looseToArray( + document.querySelectorAll('link[data-n-p]') + ).map((e) => e.getAttribute('href')!) + ) + : this.getDependencies(route).then((deps) => + deps.filter((d) => d.endsWith('.css')) + ) + ).then((cssFiles) => + // These files should've already been fetched by now, so this + // should resolve instantly. + Promise.all(cssFiles.map((d) => fetchStyleSheet(d))).catch( + (err) => { + if (isInitialLoad) return getInitialStylesheets() + throw err + } ) + ) promisedDeps.then( (deps) => register(deps), (error) => {