Skip to content

Commit

Permalink
feat(image): add support for custom loaderFile when `loader: defaul…
Browse files Browse the repository at this point in the history
…t` (#53417)

Image Optimization API currently does not work if a custom loaderFile is specified in next.config.js, even if loader is explicitly set to 'default', because it is currently being overridden to 'custom' simply because a loaderFile is specified. This is unnecessary and causing the Image Optimization API routes not to be initialized since the change to the config happens before the routes are initialized.

[Sandbox Reproduction](https://codesandbox.io/p/sandbox/purple-pine-t7hhgl?file=%2Fimage-loader.js%3A8%2C1)

- Fixes #53415
  • Loading branch information
goguda committed Aug 18, 2023
1 parent 725ddc7 commit a0749a5
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 1 deletion.
1 change: 0 additions & 1 deletion packages/next/src/server/config.ts
Expand Up @@ -335,7 +335,6 @@ function assignDefaults(
`Specified images.loaderFile does not exist at "${absolutePath}".`
)
}
images.loader = 'custom'
images.loaderFile = absolutePath
}
}
Expand Down
@@ -0,0 +1,3 @@
export default function dummyLoader({ src, width, quality }) {
return `/_next/image/?url=${src}&w=${width}&q=${quality || 50}`
}
@@ -0,0 +1,6 @@
module.exports = {
images: {
loader: 'default',
loaderFile: './dummy-loader.js',
},
}
@@ -0,0 +1,34 @@
import { unstable_getImgProps as getImgProps } from 'next/image'

function loader({ src, width, quality }) {
return `${src}?wid=${width}&qual=${quality || 35}`
}

export default function Page() {
const { props: img1 } = getImgProps({
id: 'img1',
alt: 'img1',
src: '/logo.png',
width: '400',
height: '400',
priority: true,
})
const { props: img2 } = getImgProps({
id: 'img2',
alt: 'img2',
src: '/logo.png',
width: '200',
height: '200',
loader,
})
return (
<div>
<h1>Loader Config</h1>
<img {...img1} />
<p>Scroll down...</p>
<div style={{ height: '100vh' }} />
<h2>Loader Prop</h2>
<img {...img2} />
</div>
)
}
@@ -0,0 +1,35 @@
import React from 'react'
import Image from 'next/image'

function loader({ src, width, quality }) {
return `${src}?wid=${width}&qual=${quality || 35}`
}

const Page = () => {
return (
<div>
<h1>Loader Config</h1>
<Image
id="img1"
alt="img1"
src="/logo.png"
width="400"
height="400"
priority
/>
<p>Scroll down...</p>
<div style={{ height: '100vh' }} />
<h2>Loader Prop</h2>
<Image
id="img2"
alt="img2"
src="/logo.png"
width="200"
height="200"
loader={loader}
/>
</div>
)
}

export default Page
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -0,0 +1,92 @@
/* eslint-env jest */

import {
findPort,
killApp,
launchApp,
nextBuild,
nextStart,
} from 'next-test-utils'
import webdriver from 'next-webdriver'
import { join } from 'path'

const appDir = join(__dirname, '../')

let appPort
let app

function runTests(url: string) {
it('should work with loaderFile config, leaving default image optimization enabled', async () => {
const browser = await webdriver(appPort, url)
expect(await browser.elementById('img1').getAttribute('src')).toBe(
'/_next/image/?url=/logo.png&w=828&q=50'
)
expect(await browser.elementById('img1').getAttribute('srcset')).toBe(
'/_next/image/?url=/logo.png&w=640&q=50 1x, /_next/image/?url=/logo.png&w=828&q=50 2x'
)

// make sure the image was indeed successfully loaded from /_next/image
expect(
await browser.eval(
`document.getElementById('img1').complete && document.getElementById('img1').naturalWidth !== 0`
)
).toBe(true)
})

it('should work with loader prop', async () => {
const browser = await webdriver(appPort, url)
expect(await browser.elementById('img2').getAttribute('src')).toBe(
'/logo.png?wid=640&qual=35'
)
expect(await browser.elementById('img2').getAttribute('srcset')).toBe(
'/logo.png?wid=256&qual=35 1x, /logo.png?wid=640&qual=35 2x'
)
})
}

describe('Image Loader Config', () => {
describe('dev mode - component', () => {
beforeAll(async () => {
appPort = await findPort()
app = await launchApp(appDir, appPort)
})
afterAll(() => {
killApp(app)
})
runTests('/')
})

describe('server mode - component', () => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => {
killApp(app)
})
runTests('/')
})
describe('dev mode - getImgProps', () => {
beforeAll(async () => {
appPort = await findPort()
app = await launchApp(appDir, appPort)
})
afterAll(() => {
killApp(app)
})
runTests('/get-img-props')
})

describe('server mode - getImgProps', () => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => {
killApp(app)
})
runTests('/get-img-props')
})
})

0 comments on commit a0749a5

Please sign in to comment.