Skip to content

Commit

Permalink
Imageloader: collect images serverside to include images from staticp… (
Browse files Browse the repository at this point in the history
#41554)

In #41548, I show that I would like to provide an object with images in
getStaticProps. The StaticImageData is parsed correctly and provided as
a prop to the page. Nonetheless, the image is not available in the
static directory. Therefore the image is not shown. This is also
addressed in issue #29571. The underlying cause is that the import of
the image is removed from the client bundle and only present in the
server bundle.

Evaluating the next-image-loader shows that the file is only placed in
the static directory if emitted from the client bundle by firing
this.emitFile. By changing this to only emitting the file from the
serverside bundle in the webpackloader, static images loaded in the
getStaticProps are made available properly as well as images directly
used in componts (so present in server and client bundle).

This would PR would prevent the circumventing solution which enforces
that the StaticImageData should be present in the client side bundle
while it will also be present in the staticprops.

<!--
Thanks for opening a PR! Your contribution is much appreciated.
To make sure your PR is handled as smoothly as possible we request that
you follow the checklist sections below.
Choose the right checklist for the change that you're making:
-->

## Bug

- [x] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Errors have a helpful link attached, see `contributing.md`


Closes #42783, Fixes #42443

Co-authored-by: Diederik <diederik@digitalpatrol.nl>
  • Loading branch information
Laityned and Diederik committed Nov 23, 2022
1 parent 33d4694 commit 369d6b7
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 5 deletions.
8 changes: 6 additions & 2 deletions packages/next/build/webpack/loaders/next-image-loader.js
Expand Up @@ -91,8 +91,12 @@ function nextImageLoader(content) {
})
)

if (!isServer) {
this.emitFile(interpolatedName, content, null)
if (isServer) {
this.emitFile(
`../${isDev ? '' : '../'}${interpolatedName}`,
content,
null
)
}

return `export default ${stringifiedData};`
Expand Down
Expand Up @@ -24,6 +24,19 @@ const TRACE_IGNORES = [
'**/*/next/dist/bin/next',
]

const NOT_TRACEABLE = [
'.wasm',
'.png',
'.jpg',
'.jpeg',
'.gif',
'.webp',
'.avif',
'.ico',
'.bmp',
'.svg',
]

const TURBO_TRACE_DEFAULT_MAX_FILES = 128

function getModuleFromDependency(
Expand Down Expand Up @@ -137,7 +150,11 @@ export class TraceEntryPointsPlugin implements webpack.WebpackPluginInstance {
await span.traceChild('create-trace-assets').traceAsyncFn(async () => {
const entryFilesMap = new Map<any, Set<string>>()
const chunksToTrace = new Set<string>()
const isTraceable = (file: string) => !file.endsWith('.wasm')

const isTraceable = (file: string) =>
!NOT_TRACEABLE.some((suffix) => {
return file.endsWith(suffix)
})

for (const entrypoint of compilation.entrypoints.values()) {
const entryFiles = new Set<string>()
Expand Down
13 changes: 12 additions & 1 deletion test/integration/next-image-legacy/default/pages/static-img.js
@@ -1,5 +1,6 @@
import React from 'react'
import testImg from '../public/foo/test-rect.jpg'
import testImgProp from '../public/exif-rotation.jpg'
import Image from 'next/legacy/image'

import testJPG from '../public/test.jpg'
Expand All @@ -13,7 +14,11 @@ import testICO from '../public/test.ico'

import TallImage from '../components/TallImage'

const Page = () => {
export const getStaticProps = () => ({
props: { testImgProp },
})

const Page = ({ testImgProp }) => {
return (
<div>
<h1 id="page-header">Static Image</h1>
Expand All @@ -23,6 +28,12 @@ const Page = () => {
layout="fixed"
placeholder="blur"
/>
<Image
id="basic-staticprop"
src={testImgProp}
layout="fixed"
placeholder="blur"
/>
<TallImage />
<Image
id="defined-size-static"
Expand Down
22 changes: 22 additions & 0 deletions test/integration/next-image-legacy/default/test/static.test.ts
Expand Up @@ -74,6 +74,28 @@ const runTests = () => {
`style="position:absolute;top:0;left:0;bottom:0;right:0;box-sizing:border-box;padding:0;border:none;margin:auto;display:block;width:0;height:0;min-width:100%;max-width:100%;min-height:100%;max-height:100%;background-size:cover;background-position:0% 0%;filter:blur(20px);background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAAAAADhZOFXAAAAOklEQVR42iWGsQkAIBDE0iuIdiLOJjiGIzjiL/Meb4okiNYIlLjK3hJMzCQG1/0qmXXOUkjAV+m9wAMe3QiV6Ne8VgAAAABJRU5ErkJggg==&quot;)`
)
})

it('should load direct imported image', async () => {
const src = await browser.elementById('basic-static').getAttribute('src')
expect(src).toMatch(
/_next\/image\?url=%2F_next%2Fstatic%2Fmedia%2Ftest-rect(.+)\.jpg&w=828&q=75/
)
const fullSrc = new URL(src, `http://localhost:${appPort}`)
const res = await fetch(fullSrc)
expect(res.status).toBe(200)
})

it('should load staticprops imported image', async () => {
const src = await browser
.elementById('basic-staticprop')
.getAttribute('src')
expect(src).toMatch(
/_next\/image\?url=%2F_next%2Fstatic%2Fmedia%2Fexif-rotation(.+)\.jpg&w=256&q=75/
)
const fullSrc = new URL(src, `http://localhost:${appPort}`)
const res = await fetch(fullSrc)
expect(res.status).toBe(200)
})
}

describe('Build Error Tests', () => {
Expand Down
8 changes: 7 additions & 1 deletion test/integration/next-image-new/default/pages/static-img.js
@@ -1,5 +1,6 @@
import React from 'react'
import testImg from '../public/foo/test-rect.jpg'
import testImgProp from '../public/exif-rotation.jpg'
import Image from 'next/image'

import testJPG from '../public/test.jpg'
Expand All @@ -19,11 +20,16 @@ import TallImage from '../components/TallImage'
const blurDataURL =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNM/s/wBwAFjwJgf8HDLgAAAABJRU5ErkJggg=='

const Page = () => {
export const getStaticProps = () => ({
props: { testImgProp },
})

const Page = ({ testImgProp }) => {
return (
<div>
<h1 id="page-header">Static Image</h1>
<Image id="basic-static" src={testImg} placeholder="blur" />
<Image id="basic-staticprop" src={testImgProp} placeholder="blur" />
<TallImage />
<Image
id="defined-width-and-height"
Expand Down
22 changes: 22 additions & 0 deletions test/integration/next-image-new/default/test/static.test.ts
Expand Up @@ -142,6 +142,28 @@ const runTests = (isDev) => {
`color:transparent;background-size:cover;background-position:50% 50%;background-repeat:no-repeat;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http%3A//www.w3.org/2000/svg' viewBox='0 0 100 200'%3E%3Cfilter id='b' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='20'/%3E%3C/filter%3E%3Cimage preserveAspectRatio='none' filter='url(%23b)' x='0' y='0' height='100%25' width='100%25' href='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNM/s/wBwAFjwJgf8HDLgAAAABJRU5ErkJggg=='/%3E%3C/svg%3E")`
)
})

it('should load direct imported image', async () => {
const src = await browser.elementById('basic-static').getAttribute('src')
expect(src).toMatch(
/_next\/image\?url=%2F_next%2Fstatic%2Fmedia%2Ftest-rect(.+)\.jpg&w=828&q=75/
)
const fullSrc = new URL(src, `http://localhost:${appPort}`)
const res = await fetch(fullSrc)
expect(res.status).toBe(200)
})

it('should load staticprops imported image', async () => {
const src = await browser
.elementById('basic-staticprop')
.getAttribute('src')
expect(src).toMatch(
/_next\/image\?url=%2F_next%2Fstatic%2Fmedia%2Fexif-rotation(.+)\.jpg&w=256&q=75/
)
const fullSrc = new URL(src, `http://localhost:${appPort}`)
const res = await fetch(fullSrc)
expect(res.status).toBe(200)
})
}

describe('Build Error Tests', () => {
Expand Down

0 comments on commit 369d6b7

Please sign in to comment.