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

Add pretty error when image import is invalid format #41267

Merged
merged 5 commits into from Oct 11, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 15 additions & 2 deletions packages/next/build/webpack/loaders/next-image-loader.js
Expand Up @@ -11,22 +11,35 @@ function nextImageLoader(content) {
const { isServer, isDev, assetPrefix, basePath } = this.getOptions()
const context = this.rootContext
const opts = { context, content }
const importName = loaderUtils.interpolateName(
this,
'[path][name].[ext]',
opts
)
const interpolatedName = loaderUtils.interpolateName(
this,
'/static/media/[name].[hash:8].[ext]',
opts
)
const outputPath = assetPrefix + '/_next' + interpolatedName

let extension = loaderUtils.interpolateName(this, '[ext]', opts)
if (extension === 'jpg') {
extension = 'jpeg'
}

const imageSizeSpan = imageLoaderSpan.traceChild('image-size-calculation')
const imageSize = await imageSizeSpan.traceAsyncFn(() =>
getImageSize(content, extension)
getImageSize(content, extension).catch((err) => err)
)

if (imageSize instanceof Error) {
const err = new Error(
`Image import "./${importName}" is not a valid image format`
styfle marked this conversation as resolved.
Show resolved Hide resolved
)
err.name = 'InvalidImageFormatError'
throw err
}

let blurDataURL
let blurWidth
let blurHeight
Expand Down
Expand Up @@ -118,3 +118,34 @@ export async function getNotFoundError(
return input
}
}

export async function getImageError(
compilation: any,
input: any,
fileName: string,
balazsorban44 marked this conversation as resolved.
Show resolved Hide resolved
err: Error
): Promise<SimpleWebpackError | false> {
if (err.name !== 'InvalidImageFormatError') {
return false
}

const moduleTrace = getModuleTrace(input, compilation)
const { origin, module } = moduleTrace[0] || {}
if (!origin || !module) {
return false
}
const page = origin.rawRequest.replace(/^private-next-pages/, './pages')
const importedFile = module.rawRequest
const source = origin.originalSource().buffer().toString('utf8') as string
let lineNumber = -1
source.split('\n').some((line) => {
lineNumber++
return line.includes(importedFile)
})
return new SimpleWebpackError(
`${chalk.cyan(page)}:${chalk.yellow(lineNumber.toString())}`,
chalk.red
.bold('Error')
.concat(`: Image import "${importedFile}" is not a valid image file`)
)
}
Expand Up @@ -5,7 +5,7 @@ import type { webpack } from 'next/dist/compiled/webpack/webpack'
import { getBabelError } from './parseBabel'
import { getCssError } from './parseCss'
import { getScssError } from './parseScss'
import { getNotFoundError } from './parseNotFoundError'
import { getNotFoundError, getImageError } from './parseNotFoundError'
import { SimpleWebpackError } from './simpleWebpackError'
import isError from '../../../../lib/is-error'
import { getRscError } from './parseRSC'
Expand Down Expand Up @@ -71,6 +71,16 @@ export async function getModuleBuildError(
return notFoundError
}

const imageError = await getImageError(
compilation,
input,
sourceFilename,
err
)
if (imageError !== false) {
return imageError
}

const babel = getBabelError(sourceFilename, err)
if (babel !== false) {
return babel
Expand Down
@@ -0,0 +1,14 @@
import React from 'react'
import Image from 'next/future/image'
import test from '../public/invalid.svg'

const Page = () => {
return (
<div>
<h1>Try to import and invalid image file</h1>
<Image id="invalid-img" src={test} width={400} height={400} />
</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,71 @@
/* eslint-env jest */

import { join } from 'path'
import {
findPort,
getRedboxHeader,
getRedboxSource,
hasRedbox,
killApp,
launchApp,
nextBuild,
nextStart,
balazsorban44 marked this conversation as resolved.
Show resolved Hide resolved
} from 'next-test-utils'
import webdriver from 'next-webdriver'

const appDir = join(__dirname, '../')
let appPort: number
let app
let stderr = ''
const msg =
'Error: Image import "../public/invalid.svg" is not a valid image file'

function runTests({ isDev }) {
it('should show error', async () => {
if (isDev) {
const browser = await webdriver(appPort, '/')
expect(await hasRedbox(browser)).toBe(true)
expect(await getRedboxHeader(browser)).toBe('Failed to compile')
expect(await getRedboxSource(browser)).toBe(`./pages/index.js:3\n${msg}`)
expect(stderr).toContain(msg)
} else {
expect(stderr).toContain(msg)
}
})
}

describe('Missing Import Image Tests', () => {
describe('dev mode', () => {
beforeAll(async () => {
stderr = ''
appPort = await findPort()
app = await launchApp(appDir, appPort, {
onStderr(msg) {
stderr += msg || ''
},
})
})
afterAll(async () => {
if (app) {
await killApp(app)
}
})

runTests({ isDev: true })
})

describe('server mode', () => {
beforeAll(async () => {
stderr = ''
const result = await nextBuild(appDir, [], { stderr: true })
stderr = result.stderr
})
afterAll(async () => {
if (app) {
await killApp(app)
}
})

runTests({ isDev: false })
})
})