diff --git a/packages/next/client/image.tsx b/packages/next/client/image.tsx index 5f24b32a35fa6..5c49f177f63c5 100644 --- a/packages/next/client/image.tsx +++ b/packages/next/client/image.tsx @@ -1,13 +1,13 @@ -import React, { useRef, useEffect } from 'react' +import React, { useRef, useEffect, useContext } from 'react' import Head from '../shared/lib/head' import { ImageConfigComplete, imageConfigDefault, - imageConfigRuntime, LoaderValue, VALID_LOADERS, } from '../server/image-config' import { useIntersection } from './use-intersection' +import { RuntimeImageConfigContext } from '../shared/lib/runtime-image-config-context' const loadedImageURLs = new Set() const allImgs = new Map< @@ -112,16 +112,25 @@ export type ImageProps = Omit< onLoadingComplete?: OnLoadingComplete } -const { +let { deviceSizes: configDeviceSizes, imageSizes: configImageSizes, loader: configLoader, path: configPath, domains: configDomains, } = (process.env.__NEXT_IMAGE_OPTS as any as ImageConfigComplete) || -imageConfigRuntime || imageConfigDefault +function setRuntimeImageConfig(imagesConfig: ImageConfigComplete) { + if (!imagesConfig || process.env.__NEXT_IMAGE_OPTS) return + + configDeviceSizes = imagesConfig.deviceSizes + configImageSizes = imagesConfig.imageSizes + configLoader = imagesConfig.loader + configPath = imagesConfig.path + configDomains = imagesConfig.domains +} + // sort smallest to largest const allSizes = [...configDeviceSizes, ...configImageSizes] configDeviceSizes.sort((a, b) => a - b) @@ -392,6 +401,8 @@ export default function Image({ isLazy = false } + setRuntimeImageConfig(useContext(RuntimeImageConfigContext)) + if (process.env.NODE_ENV !== 'production') { if (!src) { throw new Error( diff --git a/packages/next/server/base-server.ts b/packages/next/server/base-server.ts index 0e475750d609a..0d28b29ae6be5 100644 --- a/packages/next/server/base-server.ts +++ b/packages/next/server/base-server.ts @@ -58,7 +58,6 @@ import { MIDDLEWARE_ROUTE } from '../lib/constants' import { addRequestMeta, getRequestMeta } from './request-meta' import { createHeaderRoute, createRedirectRoute } from './server-route-utils' import { PrerenderManifest } from '../build' -import { ImageConfigComplete, setImageConfigRuntime } from './image-config' export type FindComponentsResult = { components: LoadComponentsReturnType @@ -327,8 +326,6 @@ export default abstract class Server { publicRuntimeConfig, }) - this.saveImageConfigRuntime(this.nextConfig.images) - this.pagesManifest = this.getPagesManifest() this.middlewareManifest = this.getMiddlewareManifest() @@ -1816,12 +1813,6 @@ export default abstract class Server { protected get _isLikeServerless(): boolean { return isTargetLikeServerless(this.nextConfig.target) } - - public saveImageConfigRuntime( - imageConfig: Partial - ): void { - setImageConfigRuntime(imageConfig) - } } export function prepareServerlessUrl( diff --git a/packages/next/server/image-config.ts b/packages/next/server/image-config.ts index 82e565a2c85e3..df00ebaf766be 100644 --- a/packages/next/server/image-config.ts +++ b/packages/next/server/image-config.ts @@ -33,13 +33,3 @@ export const imageConfigDefault: ImageConfigComplete = { minimumCacheTTL: 60, formats: ['image/webp'], } - -export let imageConfigRuntime: Partial = imageConfigDefault - -export function setImageConfigRuntime( - imageConfig: Partial -) { - if (imageConfig) { - imageConfigRuntime = imageConfig - } -} diff --git a/packages/next/server/render.tsx b/packages/next/server/render.tsx index ac70a69964312..e4e59c844a7c6 100644 --- a/packages/next/server/render.tsx +++ b/packages/next/server/render.tsx @@ -63,6 +63,7 @@ import { DomainLocale } from './config' import RenderResult, { NodeWritablePiper } from './render-result' import isError from '../lib/is-error' import { readableStreamTee } from './web/utils' +import { RuntimeImageConfigContext } from '../shared/lib/runtime-image-config-context' let Writable: typeof import('stream').Writable let Buffer: typeof import('buffer').Buffer @@ -237,6 +238,7 @@ export type RenderOptsPartial = { serverComponents?: boolean customServer?: boolean crossOrigin?: string + images: string } export type RenderOpts = LoadComponentsReturnType & RenderOptsPartial @@ -447,6 +449,7 @@ export async function renderToHTML( devOnlyCacheBusterQueryString, supportsDynamicHTML, concurrentFeatures, + images, } = renderOpts const isServerComponent = !!serverComponentManifest @@ -718,7 +721,11 @@ export async function renderToHTML( value={(moduleName) => reactLoadableModules.push(moduleName)} > - {children} + + {children} + diff --git a/packages/next/shared/lib/runtime-image-config-context.ts b/packages/next/shared/lib/runtime-image-config-context.ts new file mode 100644 index 0000000000000..f8b578e791018 --- /dev/null +++ b/packages/next/shared/lib/runtime-image-config-context.ts @@ -0,0 +1,12 @@ +import React from 'react' +import { + ImageConfigComplete, + imageConfigDefault, +} from '../../server/image-config' + +export const RuntimeImageConfigContext = + React.createContext(imageConfigDefault) + +if (process.env.NODE_ENV !== 'production') { + RuntimeImageConfigContext.displayName = 'RuntimeImageConfigContext' +} diff --git a/test/integration/image-component/image-from-node-modules/test/index.test.js b/test/integration/image-component/image-from-node-modules/test/index.test.js index f22174590bfe2..ab6a0fa75fe0f 100644 --- a/test/integration/image-component/image-from-node-modules/test/index.test.js +++ b/test/integration/image-component/image-from-node-modules/test/index.test.js @@ -1,5 +1,11 @@ /* eslint-env jest */ -import { findPort, killApp, launchApp } from 'next-test-utils' +import { + killApp, + findPort, + nextStart, + nextBuild, + launchApp, +} from 'next-test-utils' import webdriver from 'next-webdriver' import { join } from 'path' @@ -8,7 +14,29 @@ let appPort let app let browser -// #31065 +function runTests() { + // #31065 + it('should apply image config for node_modules', async () => { + browser = await webdriver(appPort, '/image-from-node-modules') + expect( + await browser.elementById('image-from-node-modules').getAttribute('src') + ).toMatch('i.imgur.com') + }) +} + +describe('Image Component Tests In Prod Mode', () => { + beforeAll(async () => { + await nextBuild(appDir) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) + + runTests() +}) + describe('Image Component Tests In Dev Mode', () => { beforeAll(async () => { appPort = await findPort() @@ -18,10 +46,5 @@ describe('Image Component Tests In Dev Mode', () => { await killApp(app) }) - it('should apply image config for node_modules', async () => { - browser = await webdriver(appPort, '/image-from-node-modules') - expect( - await browser.elementById('image-from-node-modules').getAttribute('src') - ).toMatch('i.imgur.com') - }) + runTests() })