Skip to content

Commit 05d4363

Browse files
committed
Merge remote-tracking branch 'upstream/canary' into fix/analytics-test
2 parents cba353b + 010a31c commit 05d4363

File tree

10 files changed

+226
-139
lines changed

10 files changed

+226
-139
lines changed

azure-pipelines.yml

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -111,36 +111,37 @@ stages:
111111
- script: |
112112
node run-tests.js -g 1/1 --timings --azure --type unit
113113
displayName: 'Run tests'
114-
115-
- job: test_chrome_integration
116-
pool:
117-
vmImage: 'windows-2019'
118-
strategy:
119-
matrix:
120-
nodejs-1:
121-
group: 1/4
122-
nodejs-2:
123-
group: 2/4
124-
nodejs-3:
125-
group: 3/4
126-
nodejs-4:
127-
group: 4/4
128-
steps:
129-
- checkout: none
130-
- script: |
131-
wmic datafile where name="C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe" get Version /value
132-
displayName: 'List Chrome version'
133-
- task: NodeTool@0
134-
inputs:
135-
versionSpec: $(node_version)
136-
displayName: 'Install Node.js'
137-
- task: Cache@2
138-
inputs:
139-
# use deterministic cache key that is specific
140-
# to this test run
141-
key: $(Build.SourceVersion)
142-
path: $(System.DefaultWorkingDirectory)
143-
displayName: Cache Build
144-
- script: |
145-
node run-tests.js -g $(group) --timings --azure
146-
displayName: 'Run tests'
114+
# TODO: investigate re-enabling when stability matches running in
115+
# tests in ubuntu environment
116+
# - job: test_chrome_integration
117+
# pool:
118+
# vmImage: 'windows-2019'
119+
# strategy:
120+
# matrix:
121+
# nodejs-1:
122+
# group: 1/4
123+
# nodejs-2:
124+
# group: 2/4
125+
# nodejs-3:
126+
# group: 3/4
127+
# nodejs-4:
128+
# group: 4/4
129+
# steps:
130+
# - checkout: none
131+
# - script: |
132+
# wmic datafile where name="C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe" get Version /value
133+
# displayName: 'List Chrome version'
134+
# - task: NodeTool@0
135+
# inputs:
136+
# versionSpec: $(node_version)
137+
# displayName: 'Install Node.js'
138+
# - task: Cache@2
139+
# inputs:
140+
# # use deterministic cache key that is specific
141+
# # to this test run
142+
# key: $(Build.SourceVersion)
143+
# path: $(System.DefaultWorkingDirectory)
144+
# displayName: Cache Build
145+
# - script: |
146+
# node run-tests.js -g $(group) --timings --azure
147+
# displayName: 'Run tests'

packages/next/client/image.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,8 +445,9 @@ export default function Image({
445445
...(placeholder === 'blur'
446446
? {
447447
filter: 'blur(20px)',
448-
backgroundSize: 'cover',
448+
backgroundSize: objectFit || 'cover',
449449
backgroundImage: `url("${blurDataURL}")`,
450+
backgroundPosition: objectPosition || '0% 0%',
450451
}
451452
: undefined),
452453
}

packages/next/server/image-optimizer.ts

Lines changed: 92 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const PNG = 'image/png'
2222
const JPEG = 'image/jpeg'
2323
const GIF = 'image/gif'
2424
const SVG = 'image/svg+xml'
25-
const CACHE_VERSION = 2
25+
const CACHE_VERSION = 3
2626
const MODERN_TYPES = [/* AVIF, */ WEBP]
2727
const ANIMATABLE_TYPES = [WEBP, PNG, GIF]
2828
const VECTOR_TYPES = [SVG]
@@ -35,7 +35,8 @@ export async function imageOptimizer(
3535
res: ServerResponse,
3636
parsedUrl: UrlWithParsedQuery,
3737
nextConfig: NextConfig,
38-
distDir: string
38+
distDir: string,
39+
isDev = false
3940
) {
4041
const imageData: ImageConfig = nextConfig.images || imageConfigDefault
4142
const { deviceSizes = [], imageSizes = [], domains = [], loader } = imageData
@@ -158,24 +159,24 @@ export async function imageOptimizer(
158159
if (await fileExists(hashDir, 'directory')) {
159160
const files = await promises.readdir(hashDir)
160161
for (let file of files) {
161-
const [prefix, etag, extension] = file.split('.')
162-
const expireAt = Number(prefix)
162+
const [maxAgeStr, expireAtSt, etag, extension] = file.split('.')
163+
const maxAge = Number(maxAgeStr)
164+
const expireAt = Number(expireAtSt)
163165
const contentType = getContentType(extension)
164166
const fsPath = join(hashDir, file)
165167
if (now < expireAt) {
166-
res.setHeader(
167-
'Cache-Control',
168-
isStatic
169-
? 'public, max-age=315360000, immutable'
170-
: 'public, max-age=0, must-revalidate'
168+
const result = setResponseHeaders(
169+
req,
170+
res,
171+
etag,
172+
maxAge,
173+
contentType,
174+
isStatic,
175+
isDev
171176
)
172-
if (sendEtagResponse(req, res, etag)) {
173-
return { finished: true }
177+
if (!result.finished) {
178+
createReadStream(fsPath).pipe(res)
174179
}
175-
if (contentType) {
176-
res.setHeader('Content-Type', contentType)
177-
}
178-
createReadStream(fsPath).pipe(res)
179180
return { finished: true }
180181
} else {
181182
await promises.unlink(fsPath)
@@ -271,8 +272,22 @@ export async function imageOptimizer(
271272
const animate =
272273
ANIMATABLE_TYPES.includes(upstreamType) && isAnimated(upstreamBuffer)
273274
if (vector || animate) {
274-
await writeToCacheDir(hashDir, upstreamType, expireAt, upstreamBuffer)
275-
sendResponse(req, res, upstreamType, upstreamBuffer, isStatic)
275+
await writeToCacheDir(
276+
hashDir,
277+
upstreamType,
278+
maxAge,
279+
expireAt,
280+
upstreamBuffer
281+
)
282+
sendResponse(
283+
req,
284+
res,
285+
maxAge,
286+
upstreamType,
287+
upstreamBuffer,
288+
isStatic,
289+
isDev
290+
)
276291
return { finished: true }
277292
}
278293

@@ -342,13 +357,35 @@ export async function imageOptimizer(
342357
}
343358

344359
if (optimizedBuffer) {
345-
await writeToCacheDir(hashDir, contentType, expireAt, optimizedBuffer)
346-
sendResponse(req, res, contentType, optimizedBuffer, isStatic)
360+
await writeToCacheDir(
361+
hashDir,
362+
contentType,
363+
maxAge,
364+
expireAt,
365+
optimizedBuffer
366+
)
367+
sendResponse(
368+
req,
369+
res,
370+
maxAge,
371+
contentType,
372+
optimizedBuffer,
373+
isStatic,
374+
isDev
375+
)
347376
} else {
348377
throw new Error('Unable to optimize buffer')
349378
}
350379
} catch (error) {
351-
sendResponse(req, res, upstreamType, upstreamBuffer, isStatic)
380+
sendResponse(
381+
req,
382+
res,
383+
maxAge,
384+
upstreamType,
385+
upstreamBuffer,
386+
isStatic,
387+
isDev
388+
)
352389
}
353390

354391
return { finished: true }
@@ -362,37 +399,64 @@ export async function imageOptimizer(
362399
async function writeToCacheDir(
363400
dir: string,
364401
contentType: string,
402+
maxAge: number,
365403
expireAt: number,
366404
buffer: Buffer
367405
) {
368406
await promises.mkdir(dir, { recursive: true })
369407
const extension = getExtension(contentType)
370408
const etag = getHash([buffer])
371-
const filename = join(dir, `${expireAt}.${etag}.${extension}`)
409+
const filename = join(dir, `${maxAge}.${expireAt}.${etag}.${extension}`)
372410
await promises.writeFile(filename, buffer)
373411
}
374412

375-
function sendResponse(
413+
function setResponseHeaders(
376414
req: IncomingMessage,
377415
res: ServerResponse,
416+
etag: string,
417+
maxAge: number,
378418
contentType: string | null,
379-
buffer: Buffer,
380-
isStatic: boolean
419+
isStatic: boolean,
420+
isDev: boolean
381421
) {
382-
const etag = getHash([buffer])
383422
res.setHeader(
384423
'Cache-Control',
385424
isStatic
386425
? 'public, max-age=315360000, immutable'
387-
: 'public, max-age=0, must-revalidate'
426+
: `public, max-age=${isDev ? 0 : maxAge}, must-revalidate`
388427
)
389428
if (sendEtagResponse(req, res, etag)) {
390-
return
429+
// already called res.end() so we're finished
430+
return { finished: true }
391431
}
392432
if (contentType) {
393433
res.setHeader('Content-Type', contentType)
394434
}
395-
res.end(buffer)
435+
return { finished: false }
436+
}
437+
438+
function sendResponse(
439+
req: IncomingMessage,
440+
res: ServerResponse,
441+
maxAge: number,
442+
contentType: string | null,
443+
buffer: Buffer,
444+
isStatic: boolean,
445+
isDev: boolean
446+
) {
447+
const etag = getHash([buffer])
448+
const result = setResponseHeaders(
449+
req,
450+
res,
451+
etag,
452+
maxAge,
453+
contentType,
454+
isStatic,
455+
isDev
456+
)
457+
if (!result.finished) {
458+
res.end(buffer)
459+
}
396460
}
397461

398462
function getSupportedMimeType(options: string[], accept = ''): string {

packages/next/server/next-server.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -789,7 +789,8 @@ export default class Server {
789789
res,
790790
parsedUrl,
791791
server.nextConfig,
792-
server.distDir
792+
server.distDir,
793+
this.renderOpts.dev
793794
),
794795
},
795796
{

test/integration/image-component/default/pages/blurry-placeholder.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,17 @@ export default function Page() {
1515
blurDataURL="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400' viewBox='0 0 400 400'%3E%3Cfilter id='blur' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='20' edgeMode='duplicate' /%3E%3CfeComponentTransfer%3E%3CfeFuncA type='discrete' tableValues='1 1' /%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Cimage filter='url(%23blur)' href='data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMDAwMDAwQEBAQFBQUFBQcHBgYHBwsICQgJCAsRCwwLCwwLEQ8SDw4PEg8bFRMTFRsfGhkaHyYiIiYwLTA+PlT/wAALCAAKAAoBAREA/8QAMwABAQEAAAAAAAAAAAAAAAAAAAcJEAABAwUAAwAAAAAAAAAAAAAFAAYRAQMEEyEVMlH/2gAIAQEAAD8Az1bLPaxhiuk0QdeCOLDtHixN2dmd2bsc5FPX7VTREX//2Q==' x='0' y='0' height='100%25' width='100%25'/%3E%3C/svg%3E"
1616
/>
1717

18+
<Image
19+
priority
20+
id="blurry-placeholder-tall-centered"
21+
src="/test.ico"
22+
width="400"
23+
height="400"
24+
placeholder="blur"
25+
blurDataURL="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='800' viewBox='0 0 400 800'%3E%3Cfilter id='blur' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='20' edgeMode='duplicate' /%3E%3CfeComponentTransfer%3E%3CfeFuncA type='discrete' tableValues='1 1' /%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Cimage filter='url(%23blur)' href='data:image/jpg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBMRXhpZgAATU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAACqADAAQAAAABAAAAFAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgAFAAKAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAQEBAQEBAgEBAgMCAgIDBAMDAwMEBQQEBAQEBQYFBQUFBQUGBgYGBgYGBgcHBwcHBwgICAgICQkJCQkJCQkJCf/bAEMBAQEBAgICBAICBAkGBQYJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCf/dAAQAAf/aAAwDAQACEQMRAD8A/hN8LfBf4reNvhv4p+MHhLQLzUPC/gk2Q17U4Yy1vp/9pStBZ+e/RPPlUonqRivMa/sP/wCCZ3/BaL/gjR+xp/wTB1f9gP40fC7x94qvviNbXh8e39pY6R5V9dXqGILbSS6mkqx2cQRbZiissiGYKjuQP5CvEkfh2LxFfxeD5rm40lbmUWUt5GkNy9sHPlNNHG8qJIUwXVZHVWyAzAZIB//Q/wA/+iiigD//2Q==' x='0' y='0' height='100%25' width='100%25'/%3E%3C/svg%3E"
26+
objectPosition="center"
27+
/>
28+
1829
<div id="spacer" style={{ height: '100vh' }} />
1930

2031
<Image

test/integration/image-component/default/pages/static.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import testBMP from '../public/test.bmp'
1111
import testICO from '../public/test.ico'
1212

1313
import TallImage from '../components/TallImage'
14+
1415
const Page = () => {
1516
return (
1617
<div>

test/integration/image-component/default/test/index.test.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,14 @@ describe('Image Component Tests', () => {
640640
`background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400' viewBox='0 0 400 400'%3E%3Cfilter id='blur' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='20' edgeMode='duplicate' /%3E%3CfeComponentTransfer%3E%3CfeFuncA type='discrete' tableValues='1 1' /%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Cimage filter='url(%23blur)' href='data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMDAwMDAwQEBAQFBQUFBQcHBgYHBwsICQgJCAsRCwwLCwwLEQ8SDw4PEg8bFRMTFRsfGhkaHyYiIiYwLTA+PlT/wAALCAAKAAoBAREA/8QAMwABAQEAAAAAAAAAAAAAAAAAAAcJEAABAwUAAwAAAAAAAAAAAAAFAAYRAQMEEyEVMlH/2gAIAQEAAD8Az1bLPaxhiuk0QdeCOLDtHixN2dmd2bsc5FPX7VTREX//2Q==' x='0' y='0' height='100%25' width='100%25'/%3E%3C/svg%3E")`
641641
)
642642

643+
expect($html('#blurry-placeholder')[0].attribs.style).toContain(
644+
`background-position:0% 0%`
645+
)
646+
647+
expect(
648+
$html('#blurry-placeholder-tall-centered')[0].attribs.style
649+
).toContain(`background-position:center`)
650+
643651
expect($html('#blurry-placeholder-with-lazy')[0].attribs.style).toContain(
644652
`background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400' viewBox='0 0 400 400'%3E%3Cfilter id='blur' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='20' edgeMode='duplicate' /%3E%3CfeComponentTransfer%3E%3CfeFuncA type='discrete' tableValues='1 1' /%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Cimage filter='url(%23blur)' href='data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMDAwMDAwQEBAQFBQUFBQcHBgYHBwsICQgJCAsRCwwLCwwLEQ8SDw4PEg8bFRMTFRsfGhkaHyYiIiYwLTA+PlT/wAALCAAKAAoBAREA/8QAMwABAQEAAAAAAAAAAAAAAAAAAAcJEAABAwUAAwAAAAAAAAAAAAAFAAYRAQMEEyEVMlH/2gAIAQEAAD8Az1bLPaxhiuk0QdeCOLDtHixN2dmd2bsc5FPX7VTREX//2Q==' x='0' y='0' height='100%25' width='100%25'/%3E%3C/svg%3E")`
645653
)

test/integration/image-component/default/test/static.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,12 @@ const runTests = () => {
3838
})
3939
it('Should add a blurry placeholder to statically imported jpg', async () => {
4040
expect(html).toContain(
41-
`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%;filter:blur(20px);background-size:cover;background-image:url(&quot;data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAgACAMBIgACEQEDEQH/xAAUAAEAAAAAAAAAAAAAAAAAAAAH/9oACAEBAAAAADX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oACAECEAAAAH//xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oACAEDEAAAAH//xAAdEAABAgcAAAAAAAAAAAAAAAATEhUAAwUUIzLS/9oACAEBAAE/AB0ZlUac43GqMYuo/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwB//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPwB//9k=&quot;)"`
41+
`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%;filter:blur(20px);background-size:cover;background-image:url(&quot;data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAgACAMBIgACEQEDEQH/xAAUAAEAAAAAAAAAAAAAAAAAAAAH/9oACAEBAAAAADX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oACAECEAAAAH//xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oACAEDEAAAAH//xAAdEAABAgcAAAAAAAAAAAAAAAATEhUAAwUUIzLS/9oACAEBAAE/AB0ZlUac43GqMYuo/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwB//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPwB//9k=&quot;);background-position:0% 0%"`
4242
)
4343
})
4444
it('Should add a blurry placeholder to statically imported png', async () => {
4545
expect(html).toContain(
46-
`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%;filter:blur(20px);background-size:cover;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAQAAABuBnYAAAAATklEQVR42i2I0QmAMBQD869Q9K+IsxU6RkfoiA6T55VXDpJLJC9uUJIzcx+XFd2dXMbx8n+QpoeYDpgY66RaDA83jCUfVpK2pER1dcEUP+KfSBtXK+BpAAAAAElFTkSuQmCC&quot;)"`
46+
`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%;filter:blur(20px);background-size:cover;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAQAAABuBnYAAAAATklEQVR42i2I0QmAMBQD869Q9K+IsxU6RkfoiA6T55VXDpJLJC9uUJIzcx+XFd2dXMbx8n+QpoeYDpgY66RaDA83jCUfVpK2pER1dcEUP+KfSBtXK+BpAAAAAElFTkSuQmCC&quot;);background-position:0% 0%"`
4747
)
4848
})
4949
}

0 commit comments

Comments
 (0)