From 471c8de544b02bb3df72dad68061a056357b5b91 Mon Sep 17 00:00:00 2001 From: Gerald Monaco Date: Mon, 31 Jan 2022 21:06:50 +0000 Subject: [PATCH 1/7] Move top-level initialization into initNext --- packages/next/client/index.tsx | 273 ++++++++++++++++----------------- 1 file changed, 136 insertions(+), 137 deletions(-) diff --git a/packages/next/client/index.tsx b/packages/next/client/index.tsx index 2fce72ecb5a8..b287f899c893 100644 --- a/packages/next/client/index.tsx +++ b/packages/next/client/index.tsx @@ -59,125 +59,29 @@ type RenderRouteInfo = PrivateRouteInfo & { } type RenderErrorProps = Omit -const data: typeof window['__NEXT_DATA__'] = JSON.parse( - document.getElementById('__NEXT_DATA__')!.textContent! -) -window.__NEXT_DATA__ = data - export const version = process.env.__NEXT_VERSION +export let router: Router +export const emitter: MittEmitter = mitt() const looseToArray = (input: any): T[] => [].slice.call(input) -const { - props: hydrateProps, - err: hydrateErr, - page, - query, - buildId, - assetPrefix, - runtimeConfig, - dynamicIds, - isFallback, - locale, - locales, - domainLocales, - isPreview, - rsc, -} = data - -let { defaultLocale } = data - -const prefix: string = assetPrefix || '' - -// With dynamic assetPrefix it's no longer possible to set assetPrefix at the build time -// So, this is how we do it in the client side at runtime -__webpack_public_path__ = `${prefix}/_next/` //eslint-disable-line -// Initialize next/config with the environment configuration -setConfig({ - serverRuntimeConfig: {}, - publicRuntimeConfig: runtimeConfig || {}, -}) - -let asPath: string = getURL() - -// make sure not to attempt stripping basePath for 404s -if (hasBasePath(asPath)) { - asPath = delBasePath(asPath) -} - -if (process.env.__NEXT_I18N_SUPPORT) { - const { normalizeLocalePath } = - require('../shared/lib/i18n/normalize-locale-path') as typeof import('../shared/lib/i18n/normalize-locale-path') - - const { detectDomainLocale } = - require('../shared/lib/i18n/detect-domain-locale') as typeof import('../shared/lib/i18n/detect-domain-locale') - - const { parseRelativeUrl } = - require('../shared/lib/router/utils/parse-relative-url') as typeof import('../shared/lib/router/utils/parse-relative-url') - - const { formatUrl } = - require('../shared/lib/router/utils/format-url') as typeof import('../shared/lib/router/utils/format-url') - - if (locales) { - const parsedAs = parseRelativeUrl(asPath) - const localePathResult = normalizeLocalePath(parsedAs.pathname, locales) - - if (localePathResult.detectedLocale) { - parsedAs.pathname = localePathResult.pathname - asPath = formatUrl(parsedAs) - } else { - // derive the default locale if it wasn't detected in the asPath - // since we don't prerender static pages with all possible default - // locales - defaultLocale = locale - } - - // attempt detecting default locale based on hostname - const detectedDomain = detectDomainLocale( - process.env.__NEXT_I18N_DOMAINS as any, - window.location.hostname - ) - - // TODO: investigate if defaultLocale needs to be populated after - // hydration to prevent mismatched renders - if (detectedDomain) { - defaultLocale = detectedDomain.defaultLocale - } - } -} - -if (data.scriptLoader) { - const { initScriptLoader } = require('./script') - initScriptLoader(data.scriptLoader) -} - -type RegisterFn = (input: [string, () => void]) => void - -const pageLoader: PageLoader = new PageLoader(buildId, prefix) -const register: RegisterFn = ([r, f]) => - pageLoader.routeLoader.onEntrypoint(r, f) -if (window.__NEXT_P) { - // Defer page registration for another tick. This will increase the overall - // latency in hydrating the page, but reduce the total blocking time. - window.__NEXT_P.map((p) => setTimeout(() => register(p), 0)) -} -window.__NEXT_P = [] -;(window.__NEXT_P as any).push = register - -const headManager: { +let initialData: NEXT_DATA +let defaultLocale: string | undefined = undefined +let asPath: string +let pageLoader: PageLoader +let appElement: HTMLElement | null +let headManager: { mountedInstances: Set updateHead: (head: JSX.Element[]) => void getIsSsr?: () => boolean -} = initHeadManager() -const appElement: HTMLElement | null = document.getElementById('__next') +} let lastRenderReject: (() => void) | null let webpackHMR: any -export let router: Router let CachedApp: AppComponent, onPerfEntry: (metric: any) => void -headManager.getIsSsr = () => { - return router.isSsr -} +let CachedComponent: React.ComponentType + +type RegisterFn = (input: [string, () => void]) => void class Container extends React.Component<{ fn: (err: Error, info?: any) => void @@ -198,15 +102,15 @@ class Container extends React.Component<{ // We don't update for 404 requests as this can modify // the asPath unexpectedly e.g. adding basePath when // it wasn't originally present - page !== '/404' && - page !== '/_error' && - (isFallback || - (data.nextExport && + initialData.page !== '/404' && + initialData.page !== '/_error' && + (initialData.isFallback || + (initialData.nextExport && (isDynamicRoute(router.pathname) || location.search || process.env.__NEXT_HAS_REWRITES)) || - (hydrateProps && - hydrateProps.__N_SSG && + (initialData.props && + initialData.props.__N_SSG && (location.search || process.env.__NEXT_HAS_REWRITES))) ) { // update query on mount for exported pages @@ -230,7 +134,7 @@ class Container extends React.Component<{ // not shallow. // Other pages (strictly updating query) happens shallowly, as data // requirements would already be present. - shallow: !isFallback, + shallow: !initialData.isFallback, } ) } @@ -265,16 +169,104 @@ class Container extends React.Component<{ } } -export const emitter: MittEmitter = mitt() -let CachedComponent: React.ComponentType - export async function initNext(opts: { webpackHMR?: any } = {}) { // This makes sure this specific lines are removed in production if (process.env.NODE_ENV === 'development') { webpackHMR = opts.webpackHMR } - let initialErr = hydrateErr + initialData = JSON.parse( + document.getElementById('__NEXT_DATA__')!.textContent! + ) + window.__NEXT_DATA__ = initialData + + const prefix: string = initialData.assetPrefix || '' + // With dynamic assetPrefix it's no longer possible to set assetPrefix at the build time + // So, this is how we do it in the client side at runtime + __webpack_public_path__ = `${prefix}/_next/` //eslint-disable-line + + // Initialize next/config with the environment configuration + setConfig({ + serverRuntimeConfig: {}, + publicRuntimeConfig: initialData.runtimeConfig || {}, + }) + + asPath = getURL() + + // make sure not to attempt stripping basePath for 404s + if (hasBasePath(asPath)) { + asPath = delBasePath(asPath) + } + + if (process.env.__NEXT_I18N_SUPPORT) { + const { normalizeLocalePath } = + require('../shared/lib/i18n/normalize-locale-path') as typeof import('../shared/lib/i18n/normalize-locale-path') + + const { detectDomainLocale } = + require('../shared/lib/i18n/detect-domain-locale') as typeof import('../shared/lib/i18n/detect-domain-locale') + + const { parseRelativeUrl } = + require('../shared/lib/router/utils/parse-relative-url') as typeof import('../shared/lib/router/utils/parse-relative-url') + + const { formatUrl } = + require('../shared/lib/router/utils/format-url') as typeof import('../shared/lib/router/utils/format-url') + + if (initialData.locales) { + const parsedAs = parseRelativeUrl(asPath) + const localePathResult = normalizeLocalePath( + parsedAs.pathname, + initialData.locales + ) + + if (localePathResult.detectedLocale) { + parsedAs.pathname = localePathResult.pathname + asPath = formatUrl(parsedAs) + } else { + // derive the default locale if it wasn't detected in the asPath + // since we don't prerender static pages with all possible default + // locales + defaultLocale = initialData.locale + } + + // attempt detecting default locale based on hostname + const detectedDomain = detectDomainLocale( + process.env.__NEXT_I18N_DOMAINS as any, + window.location.hostname + ) + + // TODO: investigate if defaultLocale needs to be populated after + // hydration to prevent mismatched renders + if (detectedDomain) { + defaultLocale = detectedDomain.defaultLocale + } + } + } + + if (initialData.scriptLoader) { + const { initScriptLoader } = require('./script') + initScriptLoader(initialData.scriptLoader) + } + + pageLoader = new PageLoader(initialData.buildId, prefix) + + const register: RegisterFn = ([r, f]) => + pageLoader.routeLoader.onEntrypoint(r, f) + if (window.__NEXT_P) { + // Defer page registration for another tick. This will increase the overall + // latency in hydrating the page, but reduce the total blocking time. + window.__NEXT_P.map((p) => setTimeout(() => register(p), 0)) + } + window.__NEXT_P = [] + ;(window.__NEXT_P as any).push = register + + headManager = initHeadManager() + headManager.getIsSsr = () => { + return router.isSsr + } + + appElement = document.getElementById('__next') + + let initialErr = initialData.err try { const appEntrypoint = await pageLoader.routeLoader.whenEntrypoint('/_app') @@ -321,9 +313,9 @@ export async function initNext(opts: { webpackHMR?: any } = {}) { const pageEntrypoint = // The dev server fails to serve script assets when there's a hydration // error, so we need to skip waiting for the entrypoint. - process.env.NODE_ENV === 'development' && hydrateErr - ? { error: hydrateErr } - : await pageLoader.routeLoader.whenEntrypoint(page) + process.env.NODE_ENV === 'development' && initialData.err + ? { error: initialData.err } + : await pageLoader.routeLoader.whenEntrypoint(initialData.page) if ('error' in pageEntrypoint) { throw pageEntrypoint.error } @@ -333,7 +325,7 @@ export async function initNext(opts: { webpackHMR?: any } = {}) { const { isValidElementType } = require('next/dist/compiled/react-is') if (!isValidElementType(CachedComponent)) { throw new Error( - `The default export is not a React Component in page: "${page}"` + `The default export is not a React Component in page: "${initialData.page}"` ) } } @@ -349,7 +341,7 @@ export async function initNext(opts: { webpackHMR?: any } = {}) { // Server-side runtime errors need to be re-thrown on the client-side so // that the overlay is rendered. if (initialErr) { - if (initialErr === hydrateErr) { + if (initialErr === initialData.err) { setTimeout(() => { let error try { @@ -366,7 +358,7 @@ export async function initNext(opts: { webpackHMR?: any } = {}) { // Errors from the middleware are reported as client-side errors // since the middleware is compiled using the client compiler - if ('middleware' in hydrateErr) { + if (initialData.err && 'middleware' in initialData.err) { throw error } @@ -385,17 +377,17 @@ export async function initNext(opts: { webpackHMR?: any } = {}) { } if (window.__NEXT_PRELOADREADY) { - await window.__NEXT_PRELOADREADY(dynamicIds) + await window.__NEXT_PRELOADREADY(initialData.dynamicIds) } - router = createRouter(page, query, asPath, { - initialProps: hydrateProps, + router = createRouter(initialData.page, initialData.query, asPath, { + initialProps: initialData.props, pageLoader, App: CachedApp, Component: CachedComponent, wrapApp, err: initialErr, - isFallback: Boolean(isFallback), + isFallback: Boolean(initialData.isFallback), subscription: (info, App, scroll) => render( Object.assign< @@ -407,18 +399,18 @@ export async function initNext(opts: { webpackHMR?: any } = {}) { scroll, }) as RenderRouteInfo ), - locale, - locales, + locale: initialData.locale, + locales: initialData.locales, defaultLocale, - domainLocales, - isPreview, + domainLocales: initialData.domainLocales, + isPreview: initialData.isPreview, }) const renderCtx: RenderRouteInfo = { App: CachedApp, initial: true, Component: CachedComponent, - props: hydrateProps, + props: initialData.props, err: initialErr, } @@ -503,7 +495,13 @@ export function renderError(renderErrorProps: RenderErrorProps): Promise { Component: ErrorComponent, AppTree, router, - ctx: { err, pathname: page, query, asPath, AppTree }, + ctx: { + err, + pathname: initialData.page, + query: initialData.query, + asPath, + AppTree, + }, } return Promise.resolve( renderErrorProps.props @@ -644,7 +642,7 @@ const wrapApp = const appProps: AppProps = { ...wrappedAppProps, Component: CachedComponent, - err: hydrateErr, + err: initialData.err, router, } return {renderApp(App, appProps)} @@ -812,7 +810,8 @@ function doRender(input: RenderRouteInfo): Promise { Component = Component || lastAppProps.Component props = props || lastAppProps.props - const isRSC = process.env.__NEXT_RSC && 'initial' in input ? !!rsc : !!__N_RSC + const isRSC = + process.env.__NEXT_RSC && 'initial' in input ? !!initialData.rsc : !!__N_RSC const appProps: AppProps = { ...props, From af3a1c9c324d55942cd79790208221f0a1e5efc1 Mon Sep 17 00:00:00 2001 From: Gerald Monaco Date: Mon, 31 Jan 2022 21:36:19 +0000 Subject: [PATCH 2/7] Fix tests --- packages/next/client/index.tsx | 1 + yarn.lock | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/next/client/index.tsx b/packages/next/client/index.tsx index b287f899c893..a281b51e103a 100644 --- a/packages/next/client/index.tsx +++ b/packages/next/client/index.tsx @@ -180,6 +180,7 @@ export async function initNext(opts: { webpackHMR?: any } = {}) { ) window.__NEXT_DATA__ = initialData + defaultLocale = initialData.defaultLocale const prefix: string = initialData.assetPrefix || '' // With dynamic assetPrefix it's no longer possible to set assetPrefix at the build time // So, this is how we do it in the client side at runtime diff --git a/yarn.lock b/yarn.lock index 6b8527a1f1e5..4b4bf26470ea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20572,7 +20572,7 @@ webpack-bundle-analyzer@4.3.0: source-list-map "^2.0.0" source-map "~0.6.1" -"webpack-sources3@npm:webpack-sources@3.2.3", webpack-sources@^3.2.2, webpack-sources@^3.2.3: +"webpack-sources3@npm:webpack-sources@3.2.3", webpack-sources@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== From 244e721ae7f2da6270ea939c074df38dcf60434e Mon Sep 17 00:00:00 2001 From: Gerald Monaco Date: Tue, 1 Feb 2022 00:48:26 +0000 Subject: [PATCH 3/7] Fix HMR tests --- packages/next/client/next-dev.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/next/client/next-dev.js b/packages/next/client/next-dev.js index ebbbe11d60f7..ec36e348be30 100644 --- a/packages/next/client/next-dev.js +++ b/packages/next/client/next-dev.js @@ -9,15 +9,8 @@ import { urlQueryToSearchParams, } from '../shared/lib/router/utils/querystring' -const { - __NEXT_DATA__: { assetPrefix }, -} = window - -const prefix = assetPrefix || '' const webpackHMR = initWebpackHMR() -connectHMR({ assetPrefix: prefix, path: '/_next/webpack-hmr' }) - if (!window._nextSetupHydrationWarning) { const origConsoleError = window.console.error window.console.error = (...args) => { @@ -49,6 +42,12 @@ window.next = { } initNext({ webpackHMR }) .then(({ renderCtx }) => { + const { + __NEXT_DATA__: { assetPrefix }, + } = window + const prefix = assetPrefix || '' + + connectHMR({ assetPrefix: prefix, path: '/_next/webpack-hmr' }) initOnDemandEntries() let buildIndicatorHandler = () => {} From 521cef62b470e2b0e41fde69c6752cc978768c24 Mon Sep 17 00:00:00 2001 From: Gerald Monaco Date: Tue, 1 Feb 2022 01:42:16 +0000 Subject: [PATCH 4/7] Split into init and hydrate steps --- packages/next/client/index.tsx | 8 +- packages/next/client/next-dev.js | 128 +++++++++++++++++-------------- packages/next/client/next.js | 12 ++- 3 files changed, 86 insertions(+), 62 deletions(-) diff --git a/packages/next/client/index.tsx b/packages/next/client/index.tsx index a281b51e103a..e08aeb85c93e 100644 --- a/packages/next/client/index.tsx +++ b/packages/next/client/index.tsx @@ -169,7 +169,10 @@ class Container extends React.Component<{ } } -export async function initNext(opts: { webpackHMR?: any } = {}) { +export function initialize( + opts: { webpackHMR?: any }, + callback: (assetPrefix: string) => void +) { // This makes sure this specific lines are removed in production if (process.env.NODE_ENV === 'development') { webpackHMR = opts.webpackHMR @@ -266,7 +269,10 @@ export async function initNext(opts: { webpackHMR?: any } = {}) { } appElement = document.getElementById('__next') + callback(prefix) +} +export async function hydrate() { let initialErr = initialData.err try { diff --git a/packages/next/client/next-dev.js b/packages/next/client/next-dev.js index ec36e348be30..ead67100495a 100644 --- a/packages/next/client/next-dev.js +++ b/packages/next/client/next-dev.js @@ -1,4 +1,13 @@ -import { initNext, version, router, emitter, render, renderError } from './' +import { + initialize, + hydrate, + version, + router, + emitter, + render, + renderError, + initialize, +} from './' import initOnDemandEntries from './dev/on-demand-entries-client' import initWebpackHMR from './dev/webpack-hot-middleware-client' import initializeBuildWatcher from './dev/dev-build-watcher' @@ -40,75 +49,76 @@ window.next = { render, renderError, } -initNext({ webpackHMR }) - .then(({ renderCtx }) => { - const { - __NEXT_DATA__: { assetPrefix }, - } = window - const prefix = assetPrefix || '' - connectHMR({ assetPrefix: prefix, path: '/_next/webpack-hmr' }) - initOnDemandEntries() +initialize({ webpackHMR }, (assetPrefix) => { + connectHMR({ assetPrefix, path: '/_next/webpack-hmr' }) - let buildIndicatorHandler = () => {} + hydrate({ webpackHMR }) + .then(({ renderCtx }) => { + initOnDemandEntries() - function devPagesManifestListener(event) { - if (event.data.indexOf('devPagesManifest') !== -1) { - fetch(`${prefix}/_next/static/development/_devPagesManifest.json`) - .then((res) => res.json()) - .then((manifest) => { - window.__DEV_PAGES_MANIFEST = manifest - }) - .catch((err) => { - console.log(`Failed to fetch devPagesManifest`, err) - }) - } else if (event.data.indexOf('middlewareChanges') !== -1) { - return window.location.reload() - } else if (event.data.indexOf('serverOnlyChanges') !== -1) { - const { pages } = JSON.parse(event.data) + let buildIndicatorHandler = () => {} - // Make sure to reload when the dev-overlay is showing for an - // API route - if (pages.includes(router.query.__NEXT_PAGE)) { + function devPagesManifestListener(event) { + if (event.data.indexOf('devPagesManifest') !== -1) { + fetch( + `${assetPrefix}/_next/static/development/_devPagesManifest.json` + ) + .then((res) => res.json()) + .then((manifest) => { + window.__DEV_PAGES_MANIFEST = manifest + }) + .catch((err) => { + console.log(`Failed to fetch devPagesManifest`, err) + }) + } else if (event.data.indexOf('middlewareChanges') !== -1) { return window.location.reload() - } + } else if (event.data.indexOf('serverOnlyChanges') !== -1) { + const { pages } = JSON.parse(event.data) - if (!router.clc && pages.includes(router.pathname)) { - console.log('Refreshing page data due to server-side change') + // Make sure to reload when the dev-overlay is showing for an + // API route + if (pages.includes(router.query.__NEXT_PAGE)) { + return window.location.reload() + } - buildIndicatorHandler('building') + if (!router.clc && pages.includes(router.pathname)) { + console.log('Refreshing page data due to server-side change') - const clearIndicator = () => buildIndicatorHandler('built') + buildIndicatorHandler('building') - router - .replace( - router.pathname + - '?' + - String( - assign( - urlQueryToSearchParams(router.query), - new URLSearchParams(location.search) - ) - ), - router.asPath - ) - .finally(clearIndicator) + const clearIndicator = () => buildIndicatorHandler('built') + + router + .replace( + router.pathname + + '?' + + String( + assign( + urlQueryToSearchParams(router.query), + new URLSearchParams(location.search) + ) + ), + router.asPath + ) + .finally(clearIndicator) + } } } - } - addMessageListener(devPagesManifestListener) + addMessageListener(devPagesManifestListener) - if (process.env.__NEXT_BUILD_INDICATOR) { - initializeBuildWatcher((handler) => { - buildIndicatorHandler = handler - }, process.env.__NEXT_BUILD_INDICATOR_POSITION) - } + if (process.env.__NEXT_BUILD_INDICATOR) { + initializeBuildWatcher((handler) => { + buildIndicatorHandler = handler + }, process.env.__NEXT_BUILD_INDICATOR_POSITION) + } - // delay rendering until after styles have been applied in development - displayContent(() => { - render(renderCtx) + // delay rendering until after styles have been applied in development + displayContent(() => { + render(renderCtx) + }) + }) + .catch((err) => { + console.error('Error was not caught', err) }) - }) - .catch((err) => { - console.error('Error was not caught', err) - }) +}) diff --git a/packages/next/client/next.js b/packages/next/client/next.js index 8a0f8bad9e4a..e09753f0a020 100644 --- a/packages/next/client/next.js +++ b/packages/next/client/next.js @@ -1,4 +1,12 @@ -import { initNext, version, router, emitter, render, renderError } from './' +import { + initialize, + hydrate, + version, + router, + emitter, + render, + renderError, +} from './' window.next = { version, @@ -11,4 +19,4 @@ window.next = { renderError, } -initNext().catch(console.error) +initialize({}, () => hydrate().catch(console.error)) From d1225166ca0c0fe04863647cf884b061ac8ee15b Mon Sep 17 00:00:00 2001 From: Gerald Monaco Date: Tue, 1 Feb 2022 01:43:22 +0000 Subject: [PATCH 5/7] Remove unnecessary arg --- packages/next/client/next-dev.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/client/next-dev.js b/packages/next/client/next-dev.js index ead67100495a..739b813735e8 100644 --- a/packages/next/client/next-dev.js +++ b/packages/next/client/next-dev.js @@ -53,7 +53,7 @@ window.next = { initialize({ webpackHMR }, (assetPrefix) => { connectHMR({ assetPrefix, path: '/_next/webpack-hmr' }) - hydrate({ webpackHMR }) + hydrate() .then(({ renderCtx }) => { initOnDemandEntries() From 18d72e8360eb3303e0f51ade483473e251674f86 Mon Sep 17 00:00:00 2001 From: Gerald Monaco Date: Tue, 1 Feb 2022 01:50:14 +0000 Subject: [PATCH 6/7] Fix lint --- packages/next/client/next-dev.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/next/client/next-dev.js b/packages/next/client/next-dev.js index 739b813735e8..34cc32711657 100644 --- a/packages/next/client/next-dev.js +++ b/packages/next/client/next-dev.js @@ -6,7 +6,6 @@ import { emitter, render, renderError, - initialize, } from './' import initOnDemandEntries from './dev/on-demand-entries-client' import initWebpackHMR from './dev/webpack-hot-middleware-client' From 5711bb256fdf0e352aae62d43a1cf347730dae8e Mon Sep 17 00:00:00 2001 From: Gerald Monaco Date: Mon, 7 Mar 2022 16:50:21 +0000 Subject: [PATCH 7/7] Fix lint --- packages/next/client/next-dev.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/next/client/next-dev.js b/packages/next/client/next-dev.js index 22705ae8956a..d3e4ad7c5754 100644 --- a/packages/next/client/next-dev.js +++ b/packages/next/client/next-dev.js @@ -49,7 +49,9 @@ initialize({ webpackHMR }) function devPagesManifestListener(event) { if (event.data.indexOf('devPagesManifest') !== -1) { - fetch(`${prefix}/_next/static/development/_devPagesManifest.json`) + fetch( + `${assetPrefix}/_next/static/development/_devPagesManifest.json` + ) .then((res) => res.json()) .then((manifest) => { window.__DEV_PAGES_MANIFEST = manifest