Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: next/image usage from node_modules #33559

Merged
merged 15 commits into from Feb 9, 2022
18 changes: 16 additions & 2 deletions packages/next/client/image.tsx
@@ -1,4 +1,4 @@
import React, { useRef, useEffect } from 'react'
import React, { useRef, useEffect, useContext } from 'react'
import Head from '../shared/lib/head'
import {
ImageConfigComplete,
Expand All @@ -7,6 +7,7 @@ import {
VALID_LOADERS,
} from '../server/image-config'
import { useIntersection } from './use-intersection'
import { ImageConfigContext } from '../shared/lib/image-config-context'

const loadedImageURLs = new Set<string>()
const allImgs = new Map<
Expand Down Expand Up @@ -111,14 +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) ||
imageConfigDefault

function setImageConfig(imagesConfig: ImageConfigComplete) {
if (!imagesConfig) return

configDeviceSizes = imagesConfig.deviceSizes
configImageSizes = imagesConfig.imageSizes
configLoader = imagesConfig.loader
configPath = imagesConfig.path
configDomains = imagesConfig.domains
}

// sort smallest to largest
const allSizes = [...configDeviceSizes, ...configImageSizes]
styfle marked this conversation as resolved.
Show resolved Hide resolved
configDeviceSizes.sort((a, b) => a - b)
Expand Down Expand Up @@ -389,6 +401,8 @@ export default function Image({
isLazy = false
}

setImageConfig(useContext(ImageConfigContext))

if (process.env.NODE_ENV !== 'production') {
if (!src) {
throw new Error(
Expand Down
8 changes: 7 additions & 1 deletion packages/next/client/index.tsx
Expand Up @@ -38,6 +38,8 @@ import {
trackWebVitalMetric,
} from './streaming/vitals'
import { RefreshContext } from './streaming/refresh'
import { ImageConfigContext } from '../shared/lib/image-config-context'
import { ImageConfigComplete } from '../server/image-config'

/// <reference types="react-dom/experimental" />

Expand Down Expand Up @@ -626,7 +628,11 @@ function AppContainer({
>
<RouterContext.Provider value={makePublicRouterInstance(router)}>
<HeadManagerContext.Provider value={headManager}>
{children}
<ImageConfigContext.Provider
value={process.env.__NEXT_IMAGE_OPTS as any as ImageConfigComplete}
>
{children}
</ImageConfigContext.Provider>
</HeadManagerContext.Provider>
</RouterContext.Provider>
</Container>
Expand Down
5 changes: 3 additions & 2 deletions packages/next/server/base-server.ts
Expand Up @@ -58,6 +58,7 @@ 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 } from './image-config'

export type FindComponentsResult = {
components: LoadComponentsReturnType
Expand Down Expand Up @@ -145,7 +146,7 @@ export default abstract class Server {
ampOptimizerConfig?: { [key: string]: any }
basePath: string
optimizeFonts: boolean
images: string
images: ImageConfigComplete
fontManifest?: FontManifest
optimizeImages: boolean
disableOptimizedLoading?: boolean
Expand Down Expand Up @@ -295,7 +296,7 @@ export default abstract class Server {
customServer: customServer === true ? true : undefined,
ampOptimizerConfig: this.nextConfig.experimental.amp?.optimizer,
basePath: this.nextConfig.basePath,
images: JSON.stringify(this.nextConfig.images),
images: this.nextConfig.images,
optimizeFonts: !!this.nextConfig.optimizeFonts && !dev,
fontManifest:
this.nextConfig.optimizeFonts && !dev
Expand Down
8 changes: 7 additions & 1 deletion packages/next/server/render.tsx
Expand Up @@ -61,6 +61,8 @@ import { DomainLocale } from './config'
import RenderResult from './render-result'
import isError from '../lib/is-error'
import { readableStreamTee } from './web/utils'
import { ImageConfigContext } from '../shared/lib/image-config-context'
import { ImageConfigComplete } from './image-config'

let optimizeAmp: typeof import('./optimize-amp').default
let getFontDefinitionFromManifest: typeof import('./font-utils').getFontDefinitionFromManifest
Expand Down Expand Up @@ -232,6 +234,7 @@ export type RenderOptsPartial = {
serverComponents?: boolean
customServer?: boolean
crossOrigin?: string
images: ImageConfigComplete
}

export type RenderOpts = LoadComponentsReturnType & RenderOptsPartial
Expand Down Expand Up @@ -442,6 +445,7 @@ export async function renderToHTML(
devOnlyCacheBusterQueryString,
supportsDynamicHTML,
concurrentFeatures,
images,
} = renderOpts

const isServerComponent = !!serverComponentManifest
Expand Down Expand Up @@ -713,7 +717,9 @@ export async function renderToHTML(
value={(moduleName) => reactLoadableModules.push(moduleName)}
>
<StyleRegistry registry={jsxStyleRegistry}>
{children}
<ImageConfigContext.Provider value={images}>
{children}
</ImageConfigContext.Provider>
</StyleRegistry>
</LoadableContext.Provider>
</HeadManagerContext.Provider>
Expand Down
12 changes: 12 additions & 0 deletions packages/next/shared/lib/image-config-context.ts
@@ -0,0 +1,12 @@
import React from 'react'
import {
ImageConfigComplete,
imageConfigDefault,
} from '../../server/image-config'

export const ImageConfigContext =
React.createContext<ImageConfigComplete>(imageConfigDefault)

if (process.env.NODE_ENV !== 'production') {
ImageConfigContext.displayName = 'ImageConfigContext'
}
@@ -0,0 +1,5 @@
module.exports = {
images: {
domains: ['i.imgur.com'],
},
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

@@ -0,0 +1,18 @@
import MyCoolImage from 'my-cool-image'

const Page = () => {
return (
<div>
<h1>next/image from node_modules</h1>

<MyCoolImage
id="image-from-node-modules"
width={1404}
height={936}
src="https://i.imgur.com/CgezKMb.jpg"
/>
</div>
)
}

export default Page
@@ -0,0 +1,50 @@
/* eslint-env jest */
import {
killApp,
findPort,
nextStart,
nextBuild,
launchApp,
} from 'next-test-utils'
import webdriver from 'next-webdriver'
import { join } from 'path'

const appDir = join(__dirname, '../')
let appPort
let app
let browser

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', () => {
JuniorTour marked this conversation as resolved.
Show resolved Hide resolved
beforeAll(async () => {
appPort = await findPort()
app = await launchApp(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})

runTests()
})