Skip to content

Commit

Permalink
Merge branch 'canary' into update/codeowners
Browse files Browse the repository at this point in the history
  • Loading branch information
ijjk authored Jun 30, 2021
2 parents ddccc85 + 010a31c commit 75ffb3b
Show file tree
Hide file tree
Showing 10 changed files with 226 additions and 139 deletions.
67 changes: 34 additions & 33 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,36 +111,37 @@ stages:
- script: |
node run-tests.js -g 1/1 --timings --azure --type unit
displayName: 'Run tests'
- job: test_chrome_integration
pool:
vmImage: 'windows-2019'
strategy:
matrix:
nodejs-1:
group: 1/4
nodejs-2:
group: 2/4
nodejs-3:
group: 3/4
nodejs-4:
group: 4/4
steps:
- checkout: none
- script: |
wmic datafile where name="C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe" get Version /value
displayName: 'List Chrome version'
- task: NodeTool@0
inputs:
versionSpec: $(node_version)
displayName: 'Install Node.js'
- task: Cache@2
inputs:
# use deterministic cache key that is specific
# to this test run
key: $(Build.SourceVersion)
path: $(System.DefaultWorkingDirectory)
displayName: Cache Build
- script: |
node run-tests.js -g $(group) --timings --azure
displayName: 'Run tests'
# TODO: investigate re-enabling when stability matches running in
# tests in ubuntu environment
# - job: test_chrome_integration
# pool:
# vmImage: 'windows-2019'
# strategy:
# matrix:
# nodejs-1:
# group: 1/4
# nodejs-2:
# group: 2/4
# nodejs-3:
# group: 3/4
# nodejs-4:
# group: 4/4
# steps:
# - checkout: none
# - script: |
# wmic datafile where name="C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe" get Version /value
# displayName: 'List Chrome version'
# - task: NodeTool@0
# inputs:
# versionSpec: $(node_version)
# displayName: 'Install Node.js'
# - task: Cache@2
# inputs:
# # use deterministic cache key that is specific
# # to this test run
# key: $(Build.SourceVersion)
# path: $(System.DefaultWorkingDirectory)
# displayName: Cache Build
# - script: |
# node run-tests.js -g $(group) --timings --azure
# displayName: 'Run tests'
3 changes: 2 additions & 1 deletion packages/next/client/image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -445,8 +445,9 @@ export default function Image({
...(placeholder === 'blur'
? {
filter: 'blur(20px)',
backgroundSize: 'cover',
backgroundSize: objectFit || 'cover',
backgroundImage: `url("${blurDataURL}")`,
backgroundPosition: objectPosition || '0% 0%',
}
: undefined),
}
Expand Down
120 changes: 92 additions & 28 deletions packages/next/server/image-optimizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const PNG = 'image/png'
const JPEG = 'image/jpeg'
const GIF = 'image/gif'
const SVG = 'image/svg+xml'
const CACHE_VERSION = 2
const CACHE_VERSION = 3
const MODERN_TYPES = [/* AVIF, */ WEBP]
const ANIMATABLE_TYPES = [WEBP, PNG, GIF]
const VECTOR_TYPES = [SVG]
Expand All @@ -35,7 +35,8 @@ export async function imageOptimizer(
res: ServerResponse,
parsedUrl: UrlWithParsedQuery,
nextConfig: NextConfig,
distDir: string
distDir: string,
isDev = false
) {
const imageData: ImageConfig = nextConfig.images || imageConfigDefault
const { deviceSizes = [], imageSizes = [], domains = [], loader } = imageData
Expand Down Expand Up @@ -158,24 +159,24 @@ export async function imageOptimizer(
if (await fileExists(hashDir, 'directory')) {
const files = await promises.readdir(hashDir)
for (let file of files) {
const [prefix, etag, extension] = file.split('.')
const expireAt = Number(prefix)
const [maxAgeStr, expireAtSt, etag, extension] = file.split('.')
const maxAge = Number(maxAgeStr)
const expireAt = Number(expireAtSt)
const contentType = getContentType(extension)
const fsPath = join(hashDir, file)
if (now < expireAt) {
res.setHeader(
'Cache-Control',
isStatic
? 'public, max-age=315360000, immutable'
: 'public, max-age=0, must-revalidate'
const result = setResponseHeaders(
req,
res,
etag,
maxAge,
contentType,
isStatic,
isDev
)
if (sendEtagResponse(req, res, etag)) {
return { finished: true }
if (!result.finished) {
createReadStream(fsPath).pipe(res)
}
if (contentType) {
res.setHeader('Content-Type', contentType)
}
createReadStream(fsPath).pipe(res)
return { finished: true }
} else {
await promises.unlink(fsPath)
Expand Down Expand Up @@ -271,8 +272,22 @@ export async function imageOptimizer(
const animate =
ANIMATABLE_TYPES.includes(upstreamType) && isAnimated(upstreamBuffer)
if (vector || animate) {
await writeToCacheDir(hashDir, upstreamType, expireAt, upstreamBuffer)
sendResponse(req, res, upstreamType, upstreamBuffer, isStatic)
await writeToCacheDir(
hashDir,
upstreamType,
maxAge,
expireAt,
upstreamBuffer
)
sendResponse(
req,
res,
maxAge,
upstreamType,
upstreamBuffer,
isStatic,
isDev
)
return { finished: true }
}

Expand Down Expand Up @@ -342,13 +357,35 @@ export async function imageOptimizer(
}

if (optimizedBuffer) {
await writeToCacheDir(hashDir, contentType, expireAt, optimizedBuffer)
sendResponse(req, res, contentType, optimizedBuffer, isStatic)
await writeToCacheDir(
hashDir,
contentType,
maxAge,
expireAt,
optimizedBuffer
)
sendResponse(
req,
res,
maxAge,
contentType,
optimizedBuffer,
isStatic,
isDev
)
} else {
throw new Error('Unable to optimize buffer')
}
} catch (error) {
sendResponse(req, res, upstreamType, upstreamBuffer, isStatic)
sendResponse(
req,
res,
maxAge,
upstreamType,
upstreamBuffer,
isStatic,
isDev
)
}

return { finished: true }
Expand All @@ -362,37 +399,64 @@ export async function imageOptimizer(
async function writeToCacheDir(
dir: string,
contentType: string,
maxAge: number,
expireAt: number,
buffer: Buffer
) {
await promises.mkdir(dir, { recursive: true })
const extension = getExtension(contentType)
const etag = getHash([buffer])
const filename = join(dir, `${expireAt}.${etag}.${extension}`)
const filename = join(dir, `${maxAge}.${expireAt}.${etag}.${extension}`)
await promises.writeFile(filename, buffer)
}

function sendResponse(
function setResponseHeaders(
req: IncomingMessage,
res: ServerResponse,
etag: string,
maxAge: number,
contentType: string | null,
buffer: Buffer,
isStatic: boolean
isStatic: boolean,
isDev: boolean
) {
const etag = getHash([buffer])
res.setHeader(
'Cache-Control',
isStatic
? 'public, max-age=315360000, immutable'
: 'public, max-age=0, must-revalidate'
: `public, max-age=${isDev ? 0 : maxAge}, must-revalidate`
)
if (sendEtagResponse(req, res, etag)) {
return
// already called res.end() so we're finished
return { finished: true }
}
if (contentType) {
res.setHeader('Content-Type', contentType)
}
res.end(buffer)
return { finished: false }
}

function sendResponse(
req: IncomingMessage,
res: ServerResponse,
maxAge: number,
contentType: string | null,
buffer: Buffer,
isStatic: boolean,
isDev: boolean
) {
const etag = getHash([buffer])
const result = setResponseHeaders(
req,
res,
etag,
maxAge,
contentType,
isStatic,
isDev
)
if (!result.finished) {
res.end(buffer)
}
}

function getSupportedMimeType(options: string[], accept = ''): string {
Expand Down
3 changes: 2 additions & 1 deletion packages/next/server/next-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -789,7 +789,8 @@ export default class Server {
res,
parsedUrl,
server.nextConfig,
server.distDir
server.distDir,
this.renderOpts.dev
),
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@ export default function Page() {
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"
/>

<Image
priority
id="blurry-placeholder-tall-centered"
src="/test.ico"
width="400"
height="400"
placeholder="blur"
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"
objectPosition="center"
/>

<div id="spacer" style={{ height: '100vh' }} />

<Image
Expand Down
1 change: 1 addition & 0 deletions test/integration/image-component/default/pages/static.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import testBMP from '../public/test.bmp'
import testICO from '../public/test.ico'

import TallImage from '../components/TallImage'

const Page = () => {
return (
<div>
Expand Down
8 changes: 8 additions & 0 deletions test/integration/image-component/default/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,14 @@ describe('Image Component Tests', () => {
`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")`
)

expect($html('#blurry-placeholder')[0].attribs.style).toContain(
`background-position:0% 0%`
)

expect(
$html('#blurry-placeholder-tall-centered')[0].attribs.style
).toContain(`background-position:center`)

expect($html('#blurry-placeholder-with-lazy')[0].attribs.style).toContain(
`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")`
)
Expand Down
4 changes: 2 additions & 2 deletions test/integration/image-component/default/test/static.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ const runTests = () => {
})
it('Should add a blurry placeholder to statically imported jpg', async () => {
expect(html).toContain(
`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;)"`
`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%"`
)
})
it('Should add a blurry placeholder to statically imported png', async () => {
expect(html).toContain(
`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;)"`
`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%"`
)
})
}
Expand Down
Loading

0 comments on commit 75ffb3b

Please sign in to comment.