From 1435de15bc40e2943730bdcb731638f58e11b1dd Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Wed, 3 Mar 2021 13:20:48 -0600 Subject: [PATCH] Ensure component load order (#22731) This ensures we load `_document` then `_app` and then the page's component in all cases which matches behavior between the serverless target and the default server target. Additional tests to ensure this order is followed has been added to prevent regression. Fixes: https://github.com/vercel/next.js/issues/22732 --- packages/next/build/index.ts | 3 ++- packages/next/build/utils.ts | 9 ++++++--- .../webpack/loaders/next-serverless-loader/index.ts | 4 +++- packages/next/next-server/server/config.ts | 5 +++++ packages/next/next-server/server/load-components.ts | 11 ++++++----- .../integration/required-server-files/lib/config.js | 13 +++++++++++++ .../required-server-files/next.config.js | 2 ++ .../integration/required-server-files/pages/_app.js | 7 +++++++ .../required-server-files/pages/index.js | 7 +++++++ .../required-server-files/test/index.test.js | 8 +++++++- 10 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 test/integration/required-server-files/lib/config.js create mode 100644 test/integration/required-server-files/pages/_app.js diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index 56b334bb19911..2ec1573b9c531 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -680,7 +680,8 @@ export default async function build( ) return staticCheckWorkers.isPageStatic( page, - serverBundle, + distDir, + isLikeServerless, runtimeEnvConfig, config.i18n?.locales, config.i18n?.defaultLocale, diff --git a/packages/next/build/utils.ts b/packages/next/build/utils.ts index 47635d4fa9568..62c42c3d014d4 100644 --- a/packages/next/build/utils.ts +++ b/packages/next/build/utils.ts @@ -31,6 +31,7 @@ import { normalizeLocalePath } from '../next-server/lib/i18n/normalize-locale-pa import * as Log from './output/log' import opentelemetryApi from '@opentelemetry/api' import { tracer, traceAsyncFn } from './tracer' +import { loadComponents } from '../next-server/server/load-components' const fileGzipStats: { [k: string]: Promise } = {} const fsStatGzip = (file: string) => { @@ -713,7 +714,8 @@ export async function buildStaticPaths( export async function isPageStatic( page: string, - serverBundle: string, + distDir: string, + serverless: boolean, runtimeEnvConfig: any, locales?: string[], defaultLocale?: string, @@ -742,8 +744,9 @@ export async function isPageStatic( require('../next-server/lib/runtime-config').setConfig( runtimeEnvConfig ) - const mod = await require(serverBundle) - const Comp = await (mod.default || mod) + const components = await loadComponents(distDir, page, serverless) + const mod = components.ComponentMod + const Comp = mod.default || mod if ( !Comp || diff --git a/packages/next/build/webpack/loaders/next-serverless-loader/index.ts b/packages/next/build/webpack/loaders/next-serverless-loader/index.ts index 7720a6b3df038..2dd6f884153a2 100644 --- a/packages/next/build/webpack/loaders/next-serverless-loader/index.ts +++ b/packages/next/build/webpack/loaders/next-serverless-loader/index.ts @@ -138,6 +138,8 @@ const nextServerlessLoader: webpack.loader.Loader = function () { } import { getPageHandler } from 'next/dist/build/webpack/loaders/next-serverless-loader/page-handler' + const documentModule = require("${absoluteDocumentPath}") + const appMod = require('${absoluteAppPath}') let App = appMod.default || appMod.then && appMod.then(mod => mod.default); @@ -163,7 +165,7 @@ const nextServerlessLoader: webpack.loader.Loader = function () { pageComponent: Component, pageConfig: config, appModule: App, - documentModule: require("${absoluteDocumentPath}"), + documentModule: documentModule, errorModule: require("${absoluteErrorPath}"), notFoundModule: ${ absolute404Path ? `require("${absolute404Path}")` : undefined diff --git a/packages/next/next-server/server/config.ts b/packages/next/next-server/server/config.ts index 9b3b66aaa6f25..edd6fdc34b072 100644 --- a/packages/next/next-server/server/config.ts +++ b/packages/next/next-server/server/config.ts @@ -2,6 +2,7 @@ import chalk from 'chalk' import findUp from 'next/dist/compiled/find-up' import { basename, extname } from 'path' import * as Log from '../../build/output/log' +import { hasNextSupport } from '../../telemetry/ci-info' import { CONFIG_FILE } from '../lib/constants' import { execOnce } from '../lib/utils' import { defaultConfig, normalizeConfig } from './config-shared' @@ -443,6 +444,10 @@ export default async function loadConfig( ) } + if (hasNextSupport) { + userConfig.target = process.env.NEXT_PRIVATE_TARGET || 'server' + } + return assignDefaults({ configOrigin: CONFIG_FILE, configFile: path, diff --git a/packages/next/next-server/server/load-components.ts b/packages/next/next-server/server/load-components.ts index 4eac4dce0633e..7d972bff0d2a1 100644 --- a/packages/next/next-server/server/load-components.ts +++ b/packages/next/next-server/server/load-components.ts @@ -32,6 +32,7 @@ export type LoadComponentsReturnType = { getStaticProps?: GetStaticProps getStaticPaths?: GetStaticPaths getServerSideProps?: GetServerSideProps + ComponentMod: any } export async function loadComponents( @@ -54,14 +55,13 @@ export async function loadComponents( getStaticProps, getStaticPaths, getServerSideProps, + ComponentMod: Component, } as LoadComponentsReturnType } - const [DocumentMod, AppMod, ComponentMod] = await Promise.all([ - requirePage('/_document', distDir, serverless), - requirePage('/_app', distDir, serverless), - requirePage(pathname, distDir, serverless), - ]) + const DocumentMod = await requirePage('/_document', distDir, serverless) + const AppMod = await requirePage('/_app', distDir, serverless) + const ComponentMod = await requirePage(pathname, distDir, serverless) const [ buildManifest, @@ -86,6 +86,7 @@ export async function loadComponents( buildManifest, reactLoadableManifest, pageConfig: ComponentMod.config || {}, + ComponentMod, getServerSideProps, getStaticProps, getStaticPaths, diff --git a/test/integration/required-server-files/lib/config.js b/test/integration/required-server-files/lib/config.js new file mode 100644 index 0000000000000..8df1465685f58 --- /dev/null +++ b/test/integration/required-server-files/lib/config.js @@ -0,0 +1,13 @@ +let curConfig + +const idk = Math.random() + +export default () => { + console.log('returning config', idk, curConfig) + return curConfig +} + +export function setConfig(configValue) { + curConfig = configValue + console.log('set config', idk, configValue) +} diff --git a/test/integration/required-server-files/next.config.js b/test/integration/required-server-files/next.config.js index 1214e33ad2f06..3636a73ef1f1b 100644 --- a/test/integration/required-server-files/next.config.js +++ b/test/integration/required-server-files/next.config.js @@ -1,4 +1,6 @@ module.exports = { + // ensure incorrect target is overridden by env + target: 'serverless', rewrites() { return [ { diff --git a/test/integration/required-server-files/pages/_app.js b/test/integration/required-server-files/pages/_app.js new file mode 100644 index 0000000000000..066e62144ad8c --- /dev/null +++ b/test/integration/required-server-files/pages/_app.js @@ -0,0 +1,7 @@ +import { setConfig } from '../lib/config' + +setConfig({ hello: 'world' }) + +export default function MyApp({ Component, pageProps }) { + return +} diff --git a/test/integration/required-server-files/pages/index.js b/test/integration/required-server-files/pages/index.js index 5d287f08b6681..336a12d7c95b0 100644 --- a/test/integration/required-server-files/pages/index.js +++ b/test/integration/required-server-files/pages/index.js @@ -1,4 +1,11 @@ import { useRouter } from 'next/router' +import getConfig from '../lib/config' + +const localConfig = getConfig() + +if (localConfig.hello !== 'world') { + throw new Error('oof import order is wrong, _app comes first') +} export const getServerSideProps = ({ req }) => { return { diff --git a/test/integration/required-server-files/test/index.test.js b/test/integration/required-server-files/test/index.test.js index 399fcfedbc748..57b4260e30af9 100644 --- a/test/integration/required-server-files/test/index.test.js +++ b/test/integration/required-server-files/test/index.test.js @@ -25,7 +25,11 @@ let errors = [] describe('Required Server Files', () => { beforeAll(async () => { await fs.remove(join(appDir, '.next')) - await nextBuild(appDir) + await nextBuild(appDir, undefined, { + env: { + NOW_BUILDER: '1', + }, + }) buildId = await fs.readFile(join(appDir, '.next/BUILD_ID'), 'utf8') requiredFilesManifest = await fs.readJSON( @@ -88,6 +92,8 @@ describe('Required Server Files', () => { console.log('checking', file) expect(await fs.exists(join(appDir, file))).toBe(true) } + + expect(await fs.exists(join(appDir, '.next/server'))).toBe(true) }) it('should render SSR page correctly', async () => {