diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index 6621ed8ef2cf5b1..cdb0f9d14df6123 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -930,6 +930,22 @@ export default async function build( return returnValue }) + if (isNextImageImported) { + try { + if (process.env.NEXT_SHARP_PATH) { + require(process.env.NEXT_SHARP_PATH) + } else { + require.resolve('sharp', { + paths: [path.join(dir, 'node_modules')], + }) + } + } catch (e) { + Log.warn( + 'Detected `next/image` usage without `sharp`. https://nextjs.org/docs/messages/sharp-missing-in-production' + ) + } + } + if (customAppGetInitialProps) { console.warn( chalk.bold.yellow(`Warning: `) + diff --git a/test/integration/image-optimizer/test/index.test.js b/test/integration/image-optimizer/test/index.test.js index 9faab437aa3d7fd..739501224c7b1e7 100644 --- a/test/integration/image-optimizer/test/index.test.js +++ b/test/integration/image-optimizer/test/index.test.js @@ -23,10 +23,11 @@ const imagesDir = join(appDir, '.next', 'cache', 'images') const nextConfig = new File(join(appDir, 'next.config.js')) const largeSize = 1080 // defaults defined in server/config.ts let nextOutput +let buildOutput let appPort let app - -const sharpMissingText = `For production Image Optimization with Next.js, the optional 'sharp' package is strongly recommended` +const sharpRuntimeWarning = `For production Image Optimization with Next.js, the optional 'sharp' package is strongly recommended` +const sharpBuildWarning = 'Detected `next/image` usage without `sharp`.' async function fsToJson(dir, output = {}) { const files = await fs.readdir(dir) @@ -726,13 +727,29 @@ function runTests({ w, isDev, domains = [], ttl, isSharp }) { expect(Object.keys(json1).length).toBe(1) }) - if (isDev || isSharp) { - it('should not have sharp missing warning', () => { - expect(nextOutput).not.toContain(sharpMissingText) + if (isDev) { + it('should not have runtime warning in dev', () => { + expect(nextOutput).not.toContain(sharpRuntimeWarning) + }) + + it('should not have build warning in dev', () => { + expect(buildOutput).not.toContain(sharpBuildWarning) + }) + } else if (isSharp) { + it('should not have runtime warning when sharp is installed', () => { + expect(nextOutput).not.toContain(sharpRuntimeWarning) + }) + + it('should not have build warning when sharp is installed', () => { + expect(buildOutput).not.toContain(sharpBuildWarning) }) } else { - it('should have sharp missing warning', () => { - expect(nextOutput).toContain(sharpMissingText) + it('should have runtime warning when sharp is not installed', () => { + expect(nextOutput).toContain(sharpRuntimeWarning) + }) + + it('should have build warning when sharp is not installed', () => { + expect(buildOutput).toContain(sharpBuildWarning) }) } } @@ -910,8 +927,10 @@ describe('Image Optimizer', () => { }, }) nextOutput = '' + buildOutput = '' nextConfig.replace('{ /* replaceme */ }', json) - await nextBuild(appDir) + const out = await nextBuild(appDir, [], { stderr: true }) + buildOutput = out.stderr appPort = await findPort() app = await nextStart(appDir, appPort, { onStderr(msg) { @@ -949,7 +968,8 @@ describe('Image Optimizer', () => { }, }` ) - await nextBuild(appDir) + const out = await nextBuild(appDir, [], { stderr: true }) + buildOutput = out.stderr appPort = await findPort() app = await nextStart(appDir, appPort) }) @@ -1025,7 +1045,8 @@ describe('Image Optimizer', () => { }, }` nextConfig.replace('{ /* replaceme */ }', newConfig) - await nextBuild(appDir) + const out = await nextBuild(appDir, [], { stderr: true }) + buildOutput = out.stderr appPort = await findPort() app = await nextStart(appDir, appPort) }) @@ -1084,6 +1105,7 @@ describe('Image Optimizer', () => { const size = 384 // defaults defined in server/config.ts beforeAll(async () => { nextOutput = '' + buildOutput = '' appPort = await findPort() app = await launchApp(appDir, appPort, { onStderr(msg) { @@ -1111,6 +1133,7 @@ describe('Image Optimizer', () => { }, }) nextOutput = '' + buildOutput = '' nextConfig.replace('{ /* replaceme */ }', json) appPort = await findPort() app = await launchApp(appDir, appPort, { @@ -1133,7 +1156,9 @@ describe('Image Optimizer', () => { const size = 384 // defaults defined in server/config.ts beforeAll(async () => { nextOutput = '' - await nextBuild(appDir) + buildOutput = '' + const out = await nextBuild(appDir, [], { stderr: true }) + buildOutput = out.stderr appPort = await findPort() app = await nextStart(appDir, appPort, { onStderr(msg) { @@ -1167,8 +1192,10 @@ describe('Image Optimizer', () => { }, }) nextOutput = '' + buildOutput = '' nextConfig.replace('{ /* replaceme */ }', json) - await nextBuild(appDir) + const out = await nextBuild(appDir, [], { stderr: true }) + buildOutput = out.stderr appPort = await findPort() app = await nextStart(appDir, appPort, { onStderr(msg) {