From 71cc4ca8652a8b9c58dca9d80b480ef29a4f7d22 Mon Sep 17 00:00:00 2001 From: Steven Date: Tue, 20 Dec 2022 19:07:07 -0500 Subject: [PATCH] [next] Disable Image Optimization API when next.config.js has `unoptimized: true` (#9110) - Related to https://github.com/vercel/next.js/pull/44205 --- packages/next/src/index.ts | 26 +-------- packages/next/src/server-build.ts | 14 +---- packages/next/src/utils.ts | 20 +++++++ packages/next/test/unit/utils.test.js | 81 +++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 35 deletions(-) diff --git a/packages/next/src/index.ts b/packages/next/src/index.ts index caabe6737ad..5a8c76c70d1 100644 --- a/packages/next/src/index.ts +++ b/packages/next/src/index.ts @@ -65,6 +65,7 @@ import { getExportIntent, getExportStatus, getFilesMapFromReasons, + getImagesConfig, getImagesManifest, getMiddlewareManifest, getNextConfig, @@ -802,18 +803,7 @@ export const build: BuildV2 = async ({ return { output, - images: - imagesManifest?.images?.loader === 'default' - ? { - domains: imagesManifest.images.domains, - sizes: imagesManifest.images.sizes, - remotePatterns: imagesManifest.images.remotePatterns, - minimumCacheTTL: imagesManifest.images.minimumCacheTTL, - dangerouslyAllowSVG: imagesManifest.images.dangerouslyAllowSVG, - contentSecurityPolicy: - imagesManifest.images.contentSecurityPolicy, - } - : undefined, + images: getImagesConfig(imagesManifest), routes: [ ...privateOutputs.routes, @@ -2170,17 +2160,7 @@ export const build: BuildV2 = async ({ ...privateOutputs.files, }, wildcard: wildcardConfig, - images: - imagesManifest?.images?.loader === 'default' - ? { - domains: imagesManifest.images.domains, - sizes: imagesManifest.images.sizes, - remotePatterns: imagesManifest.images.remotePatterns, - minimumCacheTTL: imagesManifest.images.minimumCacheTTL, - dangerouslyAllowSVG: imagesManifest.images.dangerouslyAllowSVG, - contentSecurityPolicy: imagesManifest.images.contentSecurityPolicy, - } - : undefined, + images: getImagesConfig(imagesManifest), /* Desired routes order - Runtime headers diff --git a/packages/next/src/server-build.ts b/packages/next/src/server-build.ts index 86c702016e0..06b54911cee 100644 --- a/packages/next/src/server-build.ts +++ b/packages/next/src/server-build.ts @@ -38,6 +38,7 @@ import { outputFunctionFileSizeInfo, MAX_UNCOMPRESSED_LAMBDA_SIZE, normalizeIndexOutput, + getImagesConfig, getNextServerPath, getMiddlewareBundle, getFilesMapFromReasons, @@ -1158,18 +1159,7 @@ export async function serverBuild({ return { wildcard: wildcardConfig, - images: - imagesManifest?.images?.loader === 'default' - ? { - domains: imagesManifest.images.domains, - sizes: imagesManifest.images.sizes, - remotePatterns: imagesManifest.images.remotePatterns, - minimumCacheTTL: imagesManifest.images.minimumCacheTTL, - formats: imagesManifest.images.formats, - dangerouslyAllowSVG: imagesManifest.images.dangerouslyAllowSVG, - contentSecurityPolicy: imagesManifest.images.contentSecurityPolicy, - } - : undefined, + images: getImagesConfig(imagesManifest), output: { ...publicDirectoryFiles, ...lambdas, diff --git a/packages/next/src/utils.ts b/packages/next/src/utils.ts index b154cc6e366..692ba5b6f17 100644 --- a/packages/next/src/utils.ts +++ b/packages/next/src/utils.ts @@ -14,6 +14,7 @@ import { isSymbolicLink, NodejsLambda, EdgeFunction, + Images, } from '@vercel/build-utils'; import { NodeFileTraceReasons } from '@vercel/nft'; import type { @@ -155,6 +156,23 @@ async function getNextConfig(workPath: string, entryPath: string) { return null; } +function getImagesConfig( + imagesManifest: NextImagesManifest | undefined +): Images | undefined { + return imagesManifest?.images?.loader === 'default' && + imagesManifest.images?.unoptimized !== true + ? { + domains: imagesManifest.images.domains, + sizes: imagesManifest.images.sizes, + remotePatterns: imagesManifest.images.remotePatterns, + minimumCacheTTL: imagesManifest.images.minimumCacheTTL, + formats: imagesManifest.images.formats, + dangerouslyAllowSVG: imagesManifest.images.dangerouslyAllowSVG, + contentSecurityPolicy: imagesManifest.images.contentSecurityPolicy, + } + : undefined; +} + function normalizePage(page: string): string { // Resolve on anything that doesn't start with `/` if (!page.startsWith('/')) { @@ -499,6 +517,7 @@ export type NextImagesManifest = { remotePatterns: RemotePattern[]; minimumCacheTTL?: number; formats?: ImageFormat[]; + unoptimized?: boolean; dangerouslyAllowSVG?: boolean; contentSecurityPolicy?: string; }; @@ -2244,6 +2263,7 @@ export { validateEntrypoint, normalizePackageJson, getNextConfig, + getImagesConfig, stringMap, normalizePage, isDynamicRoute, diff --git a/packages/next/test/unit/utils.test.js b/packages/next/test/unit/utils.test.js index 21afc7341d1..7897a744394 100644 --- a/packages/next/test/unit/utils.test.js +++ b/packages/next/test/unit/utils.test.js @@ -3,6 +3,7 @@ const { excludeFiles, validateEntrypoint, normalizePackageJson, + getImagesConfig, getNextConfig, } = require('../../dist/utils'); const { FileRef } = require('@vercel/build-utils'); @@ -27,6 +28,86 @@ describe('getNextConfig', () => { }); }); +describe('getImagesConfig', () => { + it('should return undefined when undefined config', async () => { + const result = await getImagesConfig(undefined); + expect(result).toBeUndefined(); + }); + + it('should return undefined when null config', async () => { + const result = await getImagesConfig(null); + expect(result).toBeUndefined(); + }); + + it('should return undefined when empty object config', async () => { + const result = await getImagesConfig({ images: {} }); + expect(result).toBeUndefined(); + }); + + it('should return pass-through props when loader is default and unoptimized undefined', async () => { + const images = { + loader: 'default', + domains: ['example.com'], + sizes: [512, 1024], + remotePatterns: undefined, + formats: ['image/webp'], + minimumCacheTTL: 60, + dangerouslyAllowSVG: false, + contentSecurityPolicy: undefined, + }; + const result = await getImagesConfig({ images }); + expect(result).toEqual({ + domains: ['example.com'], + sizes: [512, 1024], + remotePatterns: undefined, + formats: ['image/webp'], + minimumCacheTTL: 60, + dangerouslyAllowSVG: false, + contentSecurityPolicy: undefined, + }); + }); + + it('should return pass-through props when loader is default and unoptimized false', async () => { + const images = { + unoptimized: false, + loader: 'default', + domains: ['example.com'], + sizes: [512, 1024], + remotePatterns: undefined, + formats: ['image/webp'], + minimumCacheTTL: 60, + dangerouslyAllowSVG: false, + contentSecurityPolicy: undefined, + }; + const result = await getImagesConfig({ images }); + expect(result).toEqual({ + domains: ['example.com'], + sizes: [512, 1024], + remotePatterns: undefined, + formats: ['image/webp'], + minimumCacheTTL: 60, + dangerouslyAllowSVG: false, + contentSecurityPolicy: undefined, + }); + }); + + it('return return undefined when loader is default and unoptimized true', async () => { + const images = { + unoptimized: true, + loader: 'default', + domains: ['example.com'], + sizes: [512, 1024], + remotePatterns: undefined, + formats: ['image/webp'], + minimumCacheTTL: 60, + dangerouslyAllowSVG: false, + contentSecurityPolicy: undefined, + }; + const result = await getImagesConfig({ images }); + expect(result).toBeUndefined(); + }); +}); + describe('excludeFiles', () => { it('should exclude files', () => { const files = {