Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Cloudflare image loader #19613

Closed
wants to merge 2 commits into from

Conversation

danielyefet
Copy link
Contributor

This makes Cloudflare's Image Resizing a supported cloud provider for Image Optimization.

@ijjk
Copy link
Member

ijjk commented Nov 28, 2020

Stats from current PR

Default Server Mode (Increase detected ⚠️)
General Overall increase ⚠️
vercel/next.js canary danielyefet/next.js cloudflare-images Change
buildDuration 10.2s 10.3s ⚠️ +132ms
nodeModulesSize 85 MB 85 MB ⚠️ +748 B
Page Load Tests Overall increase ✓
vercel/next.js canary danielyefet/next.js cloudflare-images Change
/ failed reqs 0 0
/ total time (seconds) 2.19 2.247 ⚠️ +0.06
/ avg req/sec 1141.78 1112.59 ⚠️ -29.19
/error-in-render failed reqs 0 0
/error-in-render total time (seconds) 1.358 1.29 -0.07
/error-in-render avg req/sec 1840.46 1938.69 +98.23
Client Bundles (main, webpack, commons)
vercel/next.js canary danielyefet/next.js cloudflare-images Change
677f882d2ed8..8b6e.js gzip 12.8 kB 12.8 kB
framework.HASH.js gzip 39 kB 39 kB
main-90b3d5a..55ad.js gzip 6.54 kB 6.54 kB
webpack-e067..f178.js gzip 751 B 751 B
Overall change 59 kB 59 kB
Legacy Client Bundles (polyfills)
vercel/next.js canary danielyefet/next.js cloudflare-images Change
polyfills-4b..e242.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages
vercel/next.js canary danielyefet/next.js cloudflare-images Change
_app-7231d4b..5856.js gzip 1.28 kB 1.28 kB
_error-fca3d..2eb1.js gzip 3.44 kB 3.44 kB
hooks-d4591d..e7c2.js gzip 887 B 887 B
index-17468f..5d83.js gzip 227 B 227 B
link-db223d9..dbd7.js gzip 1.61 kB 1.61 kB
routerDirect..924c.js gzip 284 B 284 B
withRouter-7..c13d.js gzip 284 B 284 B
Overall change 8.01 kB 8.01 kB
Client Build Manifests
vercel/next.js canary danielyefet/next.js cloudflare-images Change
_buildManifest.js gzip 321 B 321 B
Overall change 321 B 321 B
Rendered Page Sizes
vercel/next.js canary danielyefet/next.js cloudflare-images Change
index.html gzip 614 B 614 B
link.html gzip 621 B 621 B
withRouter.html gzip 608 B 608 B
Overall change 1.84 kB 1.84 kB

Serverless Mode
General Overall increase ⚠️
vercel/next.js canary danielyefet/next.js cloudflare-images Change
buildDuration 12.3s 12.3s ⚠️ +23ms
nodeModulesSize 85 MB 85 MB ⚠️ +748 B
Client Bundles (main, webpack, commons)
vercel/next.js canary danielyefet/next.js cloudflare-images Change
677f882d2ed8..8b6e.js gzip 12.8 kB 12.8 kB
framework.HASH.js gzip 39 kB 39 kB
main-90b3d5a..55ad.js gzip 6.54 kB 6.54 kB
webpack-e067..f178.js gzip 751 B 751 B
Overall change 59 kB 59 kB
Legacy Client Bundles (polyfills)
vercel/next.js canary danielyefet/next.js cloudflare-images Change
polyfills-4b..e242.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages
vercel/next.js canary danielyefet/next.js cloudflare-images Change
_app-7231d4b..5856.js gzip 1.28 kB 1.28 kB
_error-fca3d..2eb1.js gzip 3.44 kB 3.44 kB
hooks-d4591d..e7c2.js gzip 887 B 887 B
index-17468f..5d83.js gzip 227 B 227 B
link-db223d9..dbd7.js gzip 1.61 kB 1.61 kB
routerDirect..924c.js gzip 284 B 284 B
withRouter-7..c13d.js gzip 284 B 284 B
Overall change 8.01 kB 8.01 kB
Client Build Manifests
vercel/next.js canary danielyefet/next.js cloudflare-images Change
_buildManifest.js gzip 321 B 321 B
Overall change 321 B 321 B
Serverless bundles
vercel/next.js canary danielyefet/next.js cloudflare-images Change
_error.js 996 kB 996 kB
404.html 2.67 kB 2.67 kB
hooks.html 1.92 kB 1.92 kB
index.js 996 kB 996 kB
link.js 1.05 MB 1.05 MB
routerDirect.js 1.04 MB 1.04 MB
withRouter.js 1.04 MB 1.04 MB
Overall change 5.13 MB 5.13 MB
Commit: 629ee6f

@ijjk
Copy link
Member

ijjk commented Nov 28, 2020

Failing test suites

Commit: 629ee6f

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

  • Image Optimizer > config checks > should error when loader contains invalid value
Expand output

● Image Optimizer › config checks › should error when loader contains invalid value

expect(received).toContain(expected) // indexOf

Expected substring: "Specified images.loader should be one of (default, imgix, cloudinary, akamai), received invalid value (notreal)"
Received string:    "Error: Specified images.loader should be one of (default, imgix, cloudinary, cloudflare, akamai), received invalid value (notreal).
See more info here: https://err.sh/next.js/invalid-images-config

  3 | if(userDistDir==='public'){throw new Error(`The 'public' directory is reserved in Next.js and can not be set as the 'distDir'. https://err.sh/vercel/next.js/can-not-output-to-public`);}// make sure distDir isn't an empty string as it can result in the provided
  4 | // directory being deleted in development mode
> 5 | if(userDistDir.length===0){throw new Error(`Invalid distDir provided, distDir can not be an empty string. Please remove this config or set it to undefined`);}}if(key==='pageExtensions'){if(!Array.isArray(value)){throw new Error(`Specified pageExtensions is not an array of strings, found "${value}". Please update this config or remove it.`);}if(!value.length){throw new Error(`Specified pageExtensions is an empty array. Please update it with the relevant extensions or remove it.`);}value.forEach(ext=>{if(typeof ext!=='string'){throw new Error(`Specified pageExtensions is not an array of strings, found "${ext}" of type "${typeof ext}". Please update this config or remove it.`);}});}if(!!value&&value.constructor===Object){currentConfig[key]={...defaultConfig[key],...Object.keys(value).reduce((c,k)=>{const v=value[k];if(v!==undefined&&v!==null){c[k]=v;}return c;},{})};}else{currentConfig[key]=value;}return currentConfig;},{});const result={...defaultConfig,...config};if(typeof result.assetPrefix!=='string'){throw new Error(`Specified assetPrefix is not a string, found type "${typeof result.assetPrefix}" https://err.sh/vercel/next.js/invalid-assetprefix`);}if(typeof result.basePath!=='string'){throw new Error(`Specified basePath is not a string, found type "${typeof result.basePath}"`);}if(result.basePath!==''){if(result.basePath==='/'){throw new Error(`Specified basePath /. basePath has to be either an empty string or a path prefix"`);}if(!result.basePath.startsWith('/')){throw new Error(`Specified basePath has to start with a /, found "${result.basePath}"`);}if(result.basePath!=='/'){if(result.basePath.endsWith('/')){throw new Error(`Specified basePath should not end with /, found "${result.basePath}"`);}if(result.assetPrefix===''){result.assetPrefix=result.basePath;}if(result.amp.canonicalBase===''){result.amp.canonicalBase=result.basePath;}}}if(result==null?void 0:result.images){const images=result.images;if(typeof images!=='object'){throw new Error(`Specified images should be an object received ${typeof images}.\nSee more info here: https://err.sh/next.js/invalid-images-config`);}if(images.domains){if(!Array.isArray(images.domains)){throw new Error(`Specified images.domains should be an Array received ${typeof images.domains}.\nSee more info here: https://err.sh/next.js/invalid-images-config`);}if(images.domains.length>50){throw new Error(`Specified images.domains exceeds length of 50, received length (${images.domains.length}), please reduce the length of the array to continue.\nSee more info here: https://err.sh/next.js/invalid-images-config`);}const invalid=images.domains.filter(d=>typeof d!=='string');if(invalid.length>0){throw new Error(`Specified images.domains should be an Array of strings received invalid values (${invalid.join(', ')}).\nSee more info here: https://err.sh/next.js/invalid-images-config`);}}if(images.deviceSizes){const{deviceSizes}=images;if(!Array.isArray(deviceSizes)){throw new Error(`Specified images.deviceSizes should be an Array received ${typeof deviceSizes}.\nSee more info here: https://err.sh/next.js/invalid-images-config`);}if(deviceSizes.length>25){throw new Error(`Specified images.deviceSizes exceeds length of 25, received length (${deviceSizes.length}), please reduce the length of the array to continue.\nSee more info here: https://err.sh/next.js/invalid-images-config`);}const invalid=deviceSizes.filter(d=>{return typeof d!=='number'||d<1||d>10000;});if(invalid.length>0){throw new Error(`Specified images.deviceSizes should be an Array of numbers that are between 1 and 10000, received invalid values (${invalid.join(', ')}).\nSee more info here: https://err.sh/next.js/invalid-images-config`);}}if(images.imageSizes){const{imageSizes}=images;if(!Array.isArray(imageSizes)){throw new Error(`Specified images.imageSizes should be an Array received ${typeof imageSizes}.\nSee more info here: https://err.sh/next.js/invalid-images-config`);}if(imageSizes.length>25){throw new Error(`Specified images.imageSizes exceeds length of 25, received length (${imageSizes.length}), please reduce the length of the array to continue.\nSee more info here: https://err.sh/next.js/invalid-images-config`);}const invalid=imageSizes.filter(d=>{return typeof d!=='number'||d<1||d>10000;});if(invalid.length>0){throw new Error(`Specified images.imageSizes should be an Array of numbers that are between 1 and 10000, received invalid values (${invalid.join(', ')}).\nSee more info here: https://err.sh/next.js/invalid-images-config`);}}if(!images.loader){images.loader='default';}if(!_imageConfig.VALID_LOADERS.includes(images.loader)){throw new Error(`Specified images.loader should be one of (${_imageConfig.VALID_LOADERS.join(', ')}), received invalid value (${images.loader}).\nSee more info here: https://err.sh/next.js/invalid-images-config`);}// Append trailing slash for non-default loaders
    |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           ^
  6 | if(images.path){if(images.loader!=='default'&&images.path[images.path.length-1]!=='/'){images.path+='/';}}}if(result.i18n){const{i18n}=result;const i18nType=typeof i18n;if(i18nType!=='object'){throw new Error(`Specified i18n should be an object received ${i18nType}.\nSee more info here: https://err.sh/next.js/invalid-i18n-config`);}if(!Array.isArray(i18n.locales)){throw new Error(`Specified i18n.locales should be an Array received ${typeof i18n.locales}.\nSee more info here: https://err.sh/next.js/invalid-i18n-config`);}const defaultLocaleType=typeof i18n.defaultLocale;if(!i18n.defaultLocale||defaultLocaleType!=='string'){throw new Error(`Specified i18n.defaultLocale should be a string.\nSee more info here: https://err.sh/next.js/invalid-i18n-config`);}if(typeof i18n.domains!=='undefined'&&!Array.isArray(i18n.domains)){throw new Error(`Specified i18n.domains must be an array of domain objects e.g. [ { domain: 'example.fr', defaultLocale: 'fr', locales: ['fr'] } ] received ${typeof i18n.domains}.\nSee more info here: https://err.sh/next.js/invalid-i18n-config`);}if(i18n.domains){const invalidDomainItems=i18n.domains.filter(item=>{if(!item||typeof item!=='object')return true;if(!item.defaultLocale)return true;if(!item.domain||typeof item.domain!=='string')return true;let hasInvalidLocale=false;if(Array.isArray(item.locales)){for(const locale of item.locales){if(typeof locale!=='string')hasInvalidLocale=true;for(const domainItem of i18n.domains){if(domainItem===item)continue;if(domainItem.locales&&domainItem.locales.includes(locale)){console.warn(`Both ${item.domain} and ${domainItem.domain} configured the locale (${locale}) but only one can. Remove it from one i18n.domains config to continue`);hasInvalidLocale=true;break;}}}}return hasInvalidLocale;});if(invalidDomainItems.length>0){throw new Error(`Invalid i18n.domains values:\n${invalidDomainItems.map(item=>JSON.stringify(item)).join('\n')}\n\ndomains value must follow format { domain: 'example.fr', defaultLocale: 'fr', locales: ['fr'] }.\nSee more info here: https://err.sh/next.js/invalid-i18n-config`);}}if(!Array.isArray(i18n.locales)){throw new Error(`Specified i18n.locales must be an array of locale strings e.g. ["en-US", "nl-NL"] received ${typeof i18n.locales}.\nSee more info here: https://err.sh/next.js/invalid-i18n-config`);}const invalidLocales=i18n.locales.filter(locale=>typeof locale!=='string');if(invalidLocales.length>0){throw new Error(`Specified i18n.locales contains invalid values (${invalidLocales.map(String).join(', ')}), locales must be valid locale tags provided as strings e.g. "en-US".\n`+`See here for list of valid language sub-tags: http://www.iana.org/assignments/language-subtag-registry/language-subtag-registry`);}if(!i18n.locales.includes(i18n.defaultLocale)){throw new Error(`Specified i18n.defaultLocale should be included in i18n.locales.\nSee more info here: https://err.sh/next.js/invalid-i18n-config`);}// make sure default Locale is at the front
  7 | i18n.locales=[i18n.defaultLocale,...i18n.locales.filter(locale=>locale!==i18n.defaultLocale)];const localeDetectionType=typeof i18n.localeDetection;if(localeDetectionType!=='boolean'&&localeDetectionType!=='undefined'){throw new Error(`Specified i18n.localeDetection should be undefined or a boolean received ${localeDetectionType}.\nSee more info here: https://err.sh/next.js/invalid-i18n-config`);}}return result;}function normalizeConfig(phase,config){if(typeof config==='function'){config=config(phase,{defaultConfig});if(typeof config.then==='function'){throw new Error('> Promise returned in next config. https://err.sh/vercel/next.js/promise-in-next-config');}}return config;}function loadConfig(phase,dir,customConfig){if(customConfig){return assignDefaults({configOrigin:'server',...customConfig});}const path=_findUp.default.sync(_constants.CONFIG_FILE,{cwd:dir});// If config file was found
  8 | if(path==null?void 0:path.length){var _userConfig$amp,_userConfig$experimen;const userConfigModule=require(path);const userConfig=normalizeConfig(phase,userConfigModule.default||userConfigModule);if(Object.keys(userConfig).length===0){Log.warn('Detected next.config.js, no exported configuration found. https://err.sh/vercel/next.js/empty-configuration');}if(userConfig.target&&!targets.includes(userConfig.target)){throw new Error(`Specified target is invalid. Provided: "${userConfig.target}" should be one of ${targets.join(', ')}`);}if((_userConfig$amp=userConfig.amp)==null?void 0:_userConfig$amp.canonicalBase){const{canonicalBase}=userConfig.amp||{};userConfig.amp=userConfig.amp||{};userConfig.amp.canonicalBase=(canonicalBase.endsWith('/')?canonicalBase.slice(0,-1):canonicalBase)||'';}if(((_userConfig$experimen=userConfig.experimental)==null?void 0:_userConfig$experimen.reactMode)&&!reactModes.includes(userConfig.experimental.reactMode)){throw new Error(`Specified React Mode is invalid. Provided: ${userConfig.experimental.reactMode} should be one of ${reactModes.join(', ')}`);}return assignDefaults({configOrigin:_constants.CONFIG_FILE,configFile:path,...userConfig});}else{const configBaseName=(0,_path.basename)(_constants.CONFIG_FILE,(0,_path.extname)(_constants.CONFIG_FILE));const nonJsPath=_findUp.default.sync([`${configBaseName}.jsx`,`${configBaseName}.ts`,`${configBaseName}.tsx`,`${configBaseName}.json`],{cwd:dir});if(nonJsPath==null?void 0:nonJsPath.length){throw new Error(`Configuring Next.js via '${(0,_path.basename)(nonJsPath)}' is not supported. Please replace the file with 'next.config.js'.`);}}return defaultConfig;}function isTargetLikeServerless(target){const isServerless=target==='serverless';const isServerlessTrace=target==='experimental-serverless-trace';return isServerless||isServerlessTrace;}

  at assignDefaults (../packages/next/dist/next-server/server/config.js:5:4619)
  at loadConfig (../packages/next/dist/next-server/server/config.js:8:1101)
  at new Server (../packages/next/dist/next-server/server/next-server.js:1:4383)
  at new DevServer (../packages/next/dist/server/next-dev-server.js:1:2964)
  at createServer (../packages/next/dist/server/next.js:2:607)
  at start (../packages/next/dist/server/lib/start-server.js:1:323)
  at nextDev (../packages/next/dist/cli/next-dev.js:20:1776)
  at ../packages/next/dist/bin/next:27:115
  "
  at Object.<anonymous> (integration/image-optimizer/test/index.test.js:570:22)

@vercel vercel bot temporarily deployed to Preview November 28, 2020 08:28 Inactive
@ijjk
Copy link
Member

ijjk commented Nov 28, 2020

Stats from current PR

Default Server Mode (Decrease detected ✓)
General Overall increase ⚠️
vercel/next.js canary danielyefet/next.js cloudflare-images Change
buildDuration 11s 11.2s ⚠️ +263ms
nodeModulesSize 85 MB 85 MB ⚠️ +748 B
Page Load Tests Overall decrease ⚠️
vercel/next.js canary danielyefet/next.js cloudflare-images Change
/ failed reqs 0 0
/ total time (seconds) 2.417 2.464 ⚠️ +0.05
/ avg req/sec 1034.37 1014.81 ⚠️ -19.56
/error-in-render failed reqs 0 0
/error-in-render total time (seconds) 1.565 1.588 ⚠️ +0.02
/error-in-render avg req/sec 1597.74 1574.54 ⚠️ -23.2
Client Bundles (main, webpack, commons)
vercel/next.js canary danielyefet/next.js cloudflare-images Change
677f882d2ed8..8b6e.js gzip 12.8 kB 12.8 kB
framework.HASH.js gzip 39 kB 39 kB
main-90b3d5a..55ad.js gzip 6.54 kB 6.54 kB
webpack-e067..f178.js gzip 751 B 751 B
Overall change 59 kB 59 kB
Legacy Client Bundles (polyfills)
vercel/next.js canary danielyefet/next.js cloudflare-images Change
polyfills-4b..e242.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages
vercel/next.js canary danielyefet/next.js cloudflare-images Change
_app-7231d4b..5856.js gzip 1.28 kB 1.28 kB
_error-fca3d..2eb1.js gzip 3.44 kB 3.44 kB
hooks-d4591d..e7c2.js gzip 887 B 887 B
index-17468f..5d83.js gzip 227 B 227 B
link-db223d9..dbd7.js gzip 1.61 kB 1.61 kB
routerDirect..924c.js gzip 284 B 284 B
withRouter-7..c13d.js gzip 284 B 284 B
Overall change 8.01 kB 8.01 kB
Client Build Manifests
vercel/next.js canary danielyefet/next.js cloudflare-images Change
_buildManifest.js gzip 321 B 321 B
Overall change 321 B 321 B
Rendered Page Sizes
vercel/next.js canary danielyefet/next.js cloudflare-images Change
index.html gzip 614 B 614 B
link.html gzip 621 B 621 B
withRouter.html gzip 608 B 608 B
Overall change 1.84 kB 1.84 kB

Serverless Mode
General Overall increase ⚠️
vercel/next.js canary danielyefet/next.js cloudflare-images Change
buildDuration 12.7s 13s ⚠️ +302ms
nodeModulesSize 85 MB 85 MB ⚠️ +748 B
Client Bundles (main, webpack, commons)
vercel/next.js canary danielyefet/next.js cloudflare-images Change
677f882d2ed8..8b6e.js gzip 12.8 kB 12.8 kB
framework.HASH.js gzip 39 kB 39 kB
main-90b3d5a..55ad.js gzip 6.54 kB 6.54 kB
webpack-e067..f178.js gzip 751 B 751 B
Overall change 59 kB 59 kB
Legacy Client Bundles (polyfills)
vercel/next.js canary danielyefet/next.js cloudflare-images Change
polyfills-4b..e242.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages
vercel/next.js canary danielyefet/next.js cloudflare-images Change
_app-7231d4b..5856.js gzip 1.28 kB 1.28 kB
_error-fca3d..2eb1.js gzip 3.44 kB 3.44 kB
hooks-d4591d..e7c2.js gzip 887 B 887 B
index-17468f..5d83.js gzip 227 B 227 B
link-db223d9..dbd7.js gzip 1.61 kB 1.61 kB
routerDirect..924c.js gzip 284 B 284 B
withRouter-7..c13d.js gzip 284 B 284 B
Overall change 8.01 kB 8.01 kB
Client Build Manifests
vercel/next.js canary danielyefet/next.js cloudflare-images Change
_buildManifest.js gzip 321 B 321 B
Overall change 321 B 321 B
Serverless bundles
vercel/next.js canary danielyefet/next.js cloudflare-images Change
_error.js 996 kB 996 kB
404.html 2.67 kB 2.67 kB
hooks.html 1.92 kB 1.92 kB
index.js 996 kB 996 kB
link.js 1.05 MB 1.05 MB
routerDirect.js 1.04 MB 1.04 MB
withRouter.js 1.04 MB 1.04 MB
Overall change 5.13 MB 5.13 MB
Commit: d1b70bd

@danielyefet danielyefet changed the title Add cloudflare image loader Add Cloudflare image loader Nov 30, 2020
@jakubriedl
Copy link

This is great, can't wait for it to be merged. This is what will allow us to use Image component. Thanks.

@danielyefet
Copy link
Contributor Author

Me too! Although, I think it's likely that this one will get merged instead - #19325

@ijjk what do you think? Can't wait to be able to use the Image component properly!

@Timer
Copy link
Member

Timer commented Jan 5, 2021

Hi! We just added the ability to pass your own custom loader to the Image component, so we're no longer accepting new built-in providers.

See this PR to learn how to use it: #20788

Thanks!

@Timer Timer closed this Jan 5, 2021
@danielyefet
Copy link
Contributor Author

Ooh this is good news! Looking forward to using it :)

@danielyefet danielyefet deleted the cloudflare-images branch February 15, 2021 11:43
@ivanvanderbyl
Copy link

@Timer since this didn't get merged, would you be open to a PR that adds support for custom loaders being specified in the config? We have a large project with many Images that we'd rather configure the loader globally for all images. In this case it's Cloudflare Images.

@vercel vercel locked as resolved and limited conversation to collaborators Jan 27, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants