Skip to content

Commit 51a2a02

Browse files
authored
Add warning during next build when sharp is missing (#27933)
Follow up to #27346 ![image](https://user-images.githubusercontent.com/229881/128935917-ca6da384-91f4-43d3-8059-4e06220fbc19.png)
1 parent 459b391 commit 51a2a02

File tree

2 files changed

+55
-12
lines changed

2 files changed

+55
-12
lines changed

packages/next/build/index.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -930,6 +930,22 @@ export default async function build(
930930
return returnValue
931931
})
932932

933+
if (isNextImageImported) {
934+
try {
935+
if (process.env.NEXT_SHARP_PATH) {
936+
require(process.env.NEXT_SHARP_PATH)
937+
} else {
938+
require.resolve('sharp', {
939+
paths: [path.join(dir, 'node_modules')],
940+
})
941+
}
942+
} catch (e) {
943+
Log.warn(
944+
'Detected `next/image` usage without `sharp`. https://nextjs.org/docs/messages/sharp-missing-in-production'
945+
)
946+
}
947+
}
948+
933949
if (customAppGetInitialProps) {
934950
console.warn(
935951
chalk.bold.yellow(`Warning: `) +

test/integration/image-optimizer/test/index.test.js

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@ const imagesDir = join(appDir, '.next', 'cache', 'images')
2323
const nextConfig = new File(join(appDir, 'next.config.js'))
2424
const largeSize = 1080 // defaults defined in server/config.ts
2525
let nextOutput
26+
let buildOutput
2627
let appPort
2728
let app
28-
29-
const sharpMissingText = `For production Image Optimization with Next.js, the optional 'sharp' package is strongly recommended`
29+
const sharpRuntimeWarning = `For production Image Optimization with Next.js, the optional 'sharp' package is strongly recommended`
30+
const sharpBuildWarning = 'Detected `next/image` usage without `sharp`.'
3031

3132
async function fsToJson(dir, output = {}) {
3233
const files = await fs.readdir(dir)
@@ -726,13 +727,29 @@ function runTests({ w, isDev, domains = [], ttl, isSharp }) {
726727
expect(Object.keys(json1).length).toBe(1)
727728
})
728729

729-
if (isDev || isSharp) {
730-
it('should not have sharp missing warning', () => {
731-
expect(nextOutput).not.toContain(sharpMissingText)
730+
if (isDev) {
731+
it('should not have runtime warning in dev', () => {
732+
expect(nextOutput).not.toContain(sharpRuntimeWarning)
733+
})
734+
735+
it('should not have build warning in dev', () => {
736+
expect(buildOutput).not.toContain(sharpBuildWarning)
737+
})
738+
} else if (isSharp) {
739+
it('should not have runtime warning when sharp is installed', () => {
740+
expect(nextOutput).not.toContain(sharpRuntimeWarning)
741+
})
742+
743+
it('should not have build warning when sharp is installed', () => {
744+
expect(buildOutput).not.toContain(sharpBuildWarning)
732745
})
733746
} else {
734-
it('should have sharp missing warning', () => {
735-
expect(nextOutput).toContain(sharpMissingText)
747+
it('should have runtime warning when sharp is not installed', () => {
748+
expect(nextOutput).toContain(sharpRuntimeWarning)
749+
})
750+
751+
it('should have build warning when sharp is not installed', () => {
752+
expect(buildOutput).toContain(sharpBuildWarning)
736753
})
737754
}
738755
}
@@ -910,8 +927,10 @@ describe('Image Optimizer', () => {
910927
},
911928
})
912929
nextOutput = ''
930+
buildOutput = ''
913931
nextConfig.replace('{ /* replaceme */ }', json)
914-
await nextBuild(appDir)
932+
const out = await nextBuild(appDir, [], { stderr: true })
933+
buildOutput = out.stderr
915934
appPort = await findPort()
916935
app = await nextStart(appDir, appPort, {
917936
onStderr(msg) {
@@ -949,7 +968,8 @@ describe('Image Optimizer', () => {
949968
},
950969
}`
951970
)
952-
await nextBuild(appDir)
971+
const out = await nextBuild(appDir, [], { stderr: true })
972+
buildOutput = out.stderr
953973
appPort = await findPort()
954974
app = await nextStart(appDir, appPort)
955975
})
@@ -1025,7 +1045,8 @@ describe('Image Optimizer', () => {
10251045
},
10261046
}`
10271047
nextConfig.replace('{ /* replaceme */ }', newConfig)
1028-
await nextBuild(appDir)
1048+
const out = await nextBuild(appDir, [], { stderr: true })
1049+
buildOutput = out.stderr
10291050
appPort = await findPort()
10301051
app = await nextStart(appDir, appPort)
10311052
})
@@ -1084,6 +1105,7 @@ describe('Image Optimizer', () => {
10841105
const size = 384 // defaults defined in server/config.ts
10851106
beforeAll(async () => {
10861107
nextOutput = ''
1108+
buildOutput = ''
10871109
appPort = await findPort()
10881110
app = await launchApp(appDir, appPort, {
10891111
onStderr(msg) {
@@ -1111,6 +1133,7 @@ describe('Image Optimizer', () => {
11111133
},
11121134
})
11131135
nextOutput = ''
1136+
buildOutput = ''
11141137
nextConfig.replace('{ /* replaceme */ }', json)
11151138
appPort = await findPort()
11161139
app = await launchApp(appDir, appPort, {
@@ -1133,7 +1156,9 @@ describe('Image Optimizer', () => {
11331156
const size = 384 // defaults defined in server/config.ts
11341157
beforeAll(async () => {
11351158
nextOutput = ''
1136-
await nextBuild(appDir)
1159+
buildOutput = ''
1160+
const out = await nextBuild(appDir, [], { stderr: true })
1161+
buildOutput = out.stderr
11371162
appPort = await findPort()
11381163
app = await nextStart(appDir, appPort, {
11391164
onStderr(msg) {
@@ -1167,8 +1192,10 @@ describe('Image Optimizer', () => {
11671192
},
11681193
})
11691194
nextOutput = ''
1195+
buildOutput = ''
11701196
nextConfig.replace('{ /* replaceme */ }', json)
1171-
await nextBuild(appDir)
1197+
const out = await nextBuild(appDir, [], { stderr: true })
1198+
buildOutput = out.stderr
11721199
appPort = await findPort()
11731200
app = await nextStart(appDir, appPort, {
11741201
onStderr(msg) {

0 commit comments

Comments
 (0)