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 Contentful image loader #19117

Closed

Conversation

stefanjudis
Copy link
Contributor

@stefanjudis stefanjudis commented Nov 12, 2020

We took the freedom to add a contentful image loader on our stream.

There are no major changes and we only added another loader to the image config.

@ijjk Does that work for you? If so, we probably would have to adjust docs, too? Happy to continue to work on this PR. :)

@schoenwaldnils
Copy link
Contributor

Needs contentful in VALID_LOADERS

https://github.com/vercel/next.js/blob/canary/packages/next/next-server/server/image-config.ts#L5

@stefanjudis
Copy link
Contributor Author

Thanks @schoenwaldnils @AlexandraKlein . :) Let's see. looks like canary change in this very small time frame. :) I'll do some proper testing very soon. :)

@ijjk
Copy link
Member

ijjk commented Nov 13, 2020

Stats from current PR

Default Server Mode (Decrease detected ✓)
General Overall increase ⚠️
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
buildDuration 13.9s 14.2s ⚠️ +332ms
nodeModulesSize 84.9 MB 84.9 MB ⚠️ +854 B
Page Load Tests Overall decrease ⚠️
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
/ failed reqs 0 0
/ total time (seconds) 2.509 2.55 ⚠️ +0.04
/ avg req/sec 996.45 980.52 ⚠️ -15.93
/error-in-render failed reqs 0 0
/error-in-render total time (seconds) 1.316 1.361 ⚠️ +0.04
/error-in-render avg req/sec 1899.35 1836.97 ⚠️ -62.38
Client Bundles (main, webpack, commons)
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
677f882d2ed8..d9fb.js gzip 12.7 kB 12.7 kB
framework.HASH.js gzip 39 kB 39 kB
main-b0b971f..64a5.js gzip 6.5 kB 6.5 kB
webpack-e067..f178.js gzip 751 B 751 B
Overall change 58.9 kB 58.9 kB
Client Bundles (main, webpack, commons) Modern
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
677f882d2ed8..dule.js gzip 8.23 kB 8.23 kB
framework.HA..dule.js gzip 39 kB 39 kB
main-22cb0cf..dule.js gzip 5.61 kB 5.61 kB
webpack-07c5..dule.js gzip 751 B 751 B
Overall change 53.6 kB 53.6 kB
Legacy Client Bundles (polyfills)
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
polyfills-4b..e242.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_app-3b0cf13..85f8.js gzip 1.28 kB 1.28 kB
_error-6f635..c393.js gzip 3.44 kB 3.44 kB
hooks-d4ffc3..9e0f.js gzip 887 B 887 B
index-17468f..5d83.js gzip 227 B 227 B
link-d9a15a0..130a.js gzip 1.57 kB 1.57 kB
routerDirect..924c.js gzip 284 B 284 B
withRouter-7..c13d.js gzip 284 B 284 B
Overall change 7.98 kB 7.98 kB
Client Pages Modern
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_app-75d3a82..dule.js gzip 625 B 625 B
_error-f2fcc..dule.js gzip 2.29 kB 2.29 kB
hooks-cbf13f..dule.js gzip 387 B 387 B
index-b9a643..dule.js gzip 226 B 226 B
link-973cce3..dule.js gzip 1.51 kB 1.51 kB
routerDirect..dule.js gzip 284 B 284 B
withRouter-f..dule.js gzip 282 B 282 B
Overall change 5.6 kB 5.6 kB
Client Build Manifests
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_buildManifest.js gzip 321 B 321 B
_buildManife..dule.js gzip 330 B 330 B
Overall change 651 B 651 B
Rendered Page Sizes
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
index.html gzip 971 B 971 B
link.html gzip 978 B 978 B
withRouter.html gzip 965 B 965 B
Overall change 2.91 kB 2.91 kB

Serverless Mode
General Overall increase ⚠️
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
buildDuration 15.7s 15.1s -583ms
nodeModulesSize 84.9 MB 84.9 MB ⚠️ +854 B
Client Bundles (main, webpack, commons)
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
677f882d2ed8..d9fb.js gzip 12.7 kB 12.7 kB
framework.HASH.js gzip 39 kB 39 kB
main-b0b971f..64a5.js gzip 6.5 kB 6.5 kB
webpack-e067..f178.js gzip 751 B 751 B
Overall change 58.9 kB 58.9 kB
Client Bundles (main, webpack, commons) Modern
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
677f882d2ed8..dule.js gzip 8.23 kB 8.23 kB
framework.HA..dule.js gzip 39 kB 39 kB
main-22cb0cf..dule.js gzip 5.61 kB 5.61 kB
webpack-07c5..dule.js gzip 751 B 751 B
Overall change 53.6 kB 53.6 kB
Legacy Client Bundles (polyfills)
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
polyfills-4b..e242.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_app-3b0cf13..85f8.js gzip 1.28 kB 1.28 kB
_error-6f635..c393.js gzip 3.44 kB 3.44 kB
hooks-d4ffc3..9e0f.js gzip 887 B 887 B
index-17468f..5d83.js gzip 227 B 227 B
link-d9a15a0..130a.js gzip 1.57 kB 1.57 kB
routerDirect..924c.js gzip 284 B 284 B
withRouter-7..c13d.js gzip 284 B 284 B
Overall change 7.98 kB 7.98 kB
Client Pages Modern
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_app-75d3a82..dule.js gzip 625 B 625 B
_error-f2fcc..dule.js gzip 2.29 kB 2.29 kB
hooks-cbf13f..dule.js gzip 387 B 387 B
index-b9a643..dule.js gzip 226 B 226 B
link-973cce3..dule.js gzip 1.51 kB 1.51 kB
routerDirect..dule.js gzip 284 B 284 B
withRouter-f..dule.js gzip 282 B 282 B
Overall change 5.6 kB 5.6 kB
Client Build Manifests
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_buildManifest.js gzip 321 B 321 B
_buildManife..dule.js gzip 330 B 330 B
Overall change 651 B 651 B
Serverless bundles
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_error.js 916 kB 916 kB
404.html 4.61 kB 4.61 kB
hooks.html 3.86 kB 3.86 kB
index.js 916 kB 916 kB
link.js 974 kB 974 kB
routerDirect.js 967 kB 967 kB
withRouter.js 967 kB 967 kB
Overall change 4.75 MB 4.75 MB
Commit: 16281ad

@ijjk
Copy link
Member

ijjk commented Nov 13, 2020

Stats from current PR

Default Server Mode (Decrease detected ✓)
General Overall increase ⚠️
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
buildDuration 14.8s 15s ⚠️ +145ms
nodeModulesSize 84.9 MB 84.9 MB ⚠️ +854 B
Page Load Tests Overall decrease ⚠️
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
/ failed reqs 0 0
/ total time (seconds) 2.634 2.684 ⚠️ +0.05
/ avg req/sec 949.14 931.59 ⚠️ -17.55
/error-in-render failed reqs 0 0
/error-in-render total time (seconds) 1.463 1.536 ⚠️ +0.07
/error-in-render avg req/sec 1709.18 1627.27 ⚠️ -81.91
Client Bundles (main, webpack, commons)
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
677f882d2ed8..d9fb.js gzip 12.7 kB 12.7 kB
framework.HASH.js gzip 39 kB 39 kB
main-b0b971f..64a5.js gzip 6.5 kB 6.5 kB
webpack-e067..f178.js gzip 751 B 751 B
Overall change 58.9 kB 58.9 kB
Client Bundles (main, webpack, commons) Modern
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
677f882d2ed8..dule.js gzip 8.23 kB 8.23 kB
framework.HA..dule.js gzip 39 kB 39 kB
main-22cb0cf..dule.js gzip 5.61 kB 5.61 kB
webpack-07c5..dule.js gzip 751 B 751 B
Overall change 53.6 kB 53.6 kB
Legacy Client Bundles (polyfills)
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
polyfills-4b..e242.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_app-3b0cf13..85f8.js gzip 1.28 kB 1.28 kB
_error-6f635..c393.js gzip 3.44 kB 3.44 kB
hooks-d4ffc3..9e0f.js gzip 887 B 887 B
index-17468f..5d83.js gzip 227 B 227 B
link-d9a15a0..130a.js gzip 1.57 kB 1.57 kB
routerDirect..924c.js gzip 284 B 284 B
withRouter-7..c13d.js gzip 284 B 284 B
Overall change 7.98 kB 7.98 kB
Client Pages Modern
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_app-75d3a82..dule.js gzip 625 B 625 B
_error-f2fcc..dule.js gzip 2.29 kB 2.29 kB
hooks-cbf13f..dule.js gzip 387 B 387 B
index-b9a643..dule.js gzip 226 B 226 B
link-973cce3..dule.js gzip 1.51 kB 1.51 kB
routerDirect..dule.js gzip 284 B 284 B
withRouter-f..dule.js gzip 282 B 282 B
Overall change 5.6 kB 5.6 kB
Client Build Manifests
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_buildManifest.js gzip 321 B 321 B
_buildManife..dule.js gzip 330 B 330 B
Overall change 651 B 651 B
Rendered Page Sizes
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
index.html gzip 971 B 971 B
link.html gzip 978 B 978 B
withRouter.html gzip 965 B 965 B
Overall change 2.91 kB 2.91 kB

Serverless Mode
General Overall increase ⚠️
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
buildDuration 16.5s 16.6s ⚠️ +153ms
nodeModulesSize 84.9 MB 84.9 MB ⚠️ +854 B
Client Bundles (main, webpack, commons)
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
677f882d2ed8..d9fb.js gzip 12.7 kB 12.7 kB
framework.HASH.js gzip 39 kB 39 kB
main-b0b971f..64a5.js gzip 6.5 kB 6.5 kB
webpack-e067..f178.js gzip 751 B 751 B
Overall change 58.9 kB 58.9 kB
Client Bundles (main, webpack, commons) Modern
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
677f882d2ed8..dule.js gzip 8.23 kB 8.23 kB
framework.HA..dule.js gzip 39 kB 39 kB
main-22cb0cf..dule.js gzip 5.61 kB 5.61 kB
webpack-07c5..dule.js gzip 751 B 751 B
Overall change 53.6 kB 53.6 kB
Legacy Client Bundles (polyfills)
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
polyfills-4b..e242.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_app-3b0cf13..85f8.js gzip 1.28 kB 1.28 kB
_error-6f635..c393.js gzip 3.44 kB 3.44 kB
hooks-d4ffc3..9e0f.js gzip 887 B 887 B
index-17468f..5d83.js gzip 227 B 227 B
link-d9a15a0..130a.js gzip 1.57 kB 1.57 kB
routerDirect..924c.js gzip 284 B 284 B
withRouter-7..c13d.js gzip 284 B 284 B
Overall change 7.98 kB 7.98 kB
Client Pages Modern
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_app-75d3a82..dule.js gzip 625 B 625 B
_error-f2fcc..dule.js gzip 2.29 kB 2.29 kB
hooks-cbf13f..dule.js gzip 387 B 387 B
index-b9a643..dule.js gzip 226 B 226 B
link-973cce3..dule.js gzip 1.51 kB 1.51 kB
routerDirect..dule.js gzip 284 B 284 B
withRouter-f..dule.js gzip 282 B 282 B
Overall change 5.6 kB 5.6 kB
Client Build Manifests
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_buildManifest.js gzip 321 B 321 B
_buildManife..dule.js gzip 330 B 330 B
Overall change 651 B 651 B
Serverless bundles
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_error.js 916 kB 916 kB
404.html 4.61 kB 4.61 kB
hooks.html 3.86 kB 3.86 kB
index.js 916 kB 916 kB
link.js 974 kB 974 kB
routerDirect.js 967 kB 967 kB
withRouter.js 967 kB 967 kB
Overall change 4.75 MB 4.75 MB
Commit: 3857cbd

@ijjk
Copy link
Member

ijjk commented Nov 13, 2020

Failing test suites

Commit: 16281ad

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, akamai, contentful), 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/nextjs/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/nextjs/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/nextjs/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/nextjs/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:https://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:4616)
  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:4284)
  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:26:341
  "
  at Object.<anonymous> (integration/image-optimizer/test/index.test.js:570:22)

@AlexandraKlein
Copy link

@stefanjudis, are you still planning on moving forward with this PR? Really looking forward to having this available! 🥇

@AlexandraKlein AlexandraKlein mentioned this pull request Nov 16, 2020
24 tasks
@stefanjudis
Copy link
Contributor Author

@AlexandraKlein

Thanks for the headsup.

I do I do. 🙈 Weekend and workday came in the way. But I plan to polish and hopefully land it in the next few days.

@ijjk
Copy link
Member

ijjk commented Nov 16, 2020

Stats from current PR

Default Server Mode (Decrease detected ✓)
General Overall increase ⚠️
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
buildDuration 15.4s 15.3s -103ms
nodeModulesSize 85 MB 85 MB ⚠️ +854 B
Page Load Tests Overall decrease ⚠️
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
/ failed reqs 0 0
/ total time (seconds) 2.859 2.828 -0.03
/ avg req/sec 874.46 884.13 +9.67
/error-in-render failed reqs 0 0
/error-in-render total time (seconds) 1.725 1.755 ⚠️ +0.03
/error-in-render avg req/sec 1449.24 1424.84 ⚠️ -24.4
Client Bundles (main, webpack, commons)
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
677f882d2ed8..776c.js gzip 12.7 kB 12.7 kB
framework.HASH.js gzip 39 kB 39 kB
main-95c0df2..6c91.js gzip 6.53 kB 6.53 kB
webpack-e067..f178.js gzip 751 B 751 B
Overall change 58.9 kB 58.9 kB
Client Bundles (main, webpack, commons) Modern
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
677f882d2ed8..dule.js gzip 8.26 kB 8.26 kB
framework.HA..dule.js gzip 39 kB 39 kB
main-8ead8dd..dule.js gzip 5.64 kB 5.64 kB
webpack-07c5..dule.js gzip 751 B 751 B
Overall change 53.6 kB 53.6 kB
Legacy Client Bundles (polyfills)
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
polyfills-4b..e242.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_app-3b0cf13..85f8.js gzip 1.28 kB 1.28 kB
_error-6f635..c393.js gzip 3.44 kB 3.44 kB
hooks-d4ffc3..9e0f.js gzip 887 B 887 B
index-17468f..5d83.js gzip 227 B 227 B
link-d9a15a0..130a.js gzip 1.57 kB 1.57 kB
routerDirect..924c.js gzip 284 B 284 B
withRouter-7..c13d.js gzip 284 B 284 B
Overall change 7.98 kB 7.98 kB
Client Pages Modern
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_app-75d3a82..dule.js gzip 625 B 625 B
_error-f2fcc..dule.js gzip 2.29 kB 2.29 kB
hooks-cbf13f..dule.js gzip 387 B 387 B
index-b9a643..dule.js gzip 226 B 226 B
link-973cce3..dule.js gzip 1.51 kB 1.51 kB
routerDirect..dule.js gzip 284 B 284 B
withRouter-f..dule.js gzip 282 B 282 B
Overall change 5.6 kB 5.6 kB
Client Build Manifests
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_buildManifest.js gzip 321 B 321 B
_buildManife..dule.js gzip 330 B 330 B
Overall change 651 B 651 B
Rendered Page Sizes
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
index.html gzip 968 B 968 B
link.html gzip 974 B 974 B
withRouter.html gzip 961 B 961 B
Overall change 2.9 kB 2.9 kB

Serverless Mode
General Overall increase ⚠️
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
buildDuration 17.8s 18.2s ⚠️ +434ms
nodeModulesSize 85 MB 85 MB ⚠️ +854 B
Client Bundles (main, webpack, commons)
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
677f882d2ed8..776c.js gzip 12.7 kB 12.7 kB
framework.HASH.js gzip 39 kB 39 kB
main-95c0df2..6c91.js gzip 6.53 kB 6.53 kB
webpack-e067..f178.js gzip 751 B 751 B
Overall change 58.9 kB 58.9 kB
Client Bundles (main, webpack, commons) Modern
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
677f882d2ed8..dule.js gzip 8.26 kB 8.26 kB
framework.HA..dule.js gzip 39 kB 39 kB
main-8ead8dd..dule.js gzip 5.64 kB 5.64 kB
webpack-07c5..dule.js gzip 751 B 751 B
Overall change 53.6 kB 53.6 kB
Legacy Client Bundles (polyfills)
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
polyfills-4b..e242.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_app-3b0cf13..85f8.js gzip 1.28 kB 1.28 kB
_error-6f635..c393.js gzip 3.44 kB 3.44 kB
hooks-d4ffc3..9e0f.js gzip 887 B 887 B
index-17468f..5d83.js gzip 227 B 227 B
link-d9a15a0..130a.js gzip 1.57 kB 1.57 kB
routerDirect..924c.js gzip 284 B 284 B
withRouter-7..c13d.js gzip 284 B 284 B
Overall change 7.98 kB 7.98 kB
Client Pages Modern
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_app-75d3a82..dule.js gzip 625 B 625 B
_error-f2fcc..dule.js gzip 2.29 kB 2.29 kB
hooks-cbf13f..dule.js gzip 387 B 387 B
index-b9a643..dule.js gzip 226 B 226 B
link-973cce3..dule.js gzip 1.51 kB 1.51 kB
routerDirect..dule.js gzip 284 B 284 B
withRouter-f..dule.js gzip 282 B 282 B
Overall change 5.6 kB 5.6 kB
Client Build Manifests
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_buildManifest.js gzip 321 B 321 B
_buildManife..dule.js gzip 330 B 330 B
Overall change 651 B 651 B
Serverless bundles
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_error.js 918 kB 918 kB
404.html 4.61 kB 4.61 kB
hooks.html 3.86 kB 3.86 kB
index.js 918 kB 918 kB
link.js 976 kB 976 kB
routerDirect.js 969 kB 969 kB
withRouter.js 969 kB 969 kB
Overall change 4.76 MB 4.76 MB
Commit: da6a936

@ijjk
Copy link
Member

ijjk commented Nov 16, 2020

Failing test suites

Commit: da6a936

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, akamai, contentful), 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/nextjs/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/nextjs/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/nextjs/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/nextjs/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:https://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:4616)
  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:26:341
  "
  at Object.<anonymous> (integration/image-optimizer/test/index.test.js:570:22)

@ijjk
Copy link
Member

ijjk commented Nov 16, 2020

Stats from current PR

Default Server Mode (Decrease detected ✓)
General Overall increase ⚠️
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
buildDuration 13.9s 14.3s ⚠️ +316ms
nodeModulesSize 85 MB 85 MB ⚠️ +854 B
Page Load Tests Overall decrease ⚠️
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
/ failed reqs 0 0
/ total time (seconds) 2.607 2.646 ⚠️ +0.04
/ avg req/sec 959.13 944.78 ⚠️ -14.35
/error-in-render failed reqs 0 0
/error-in-render total time (seconds) 1.524 1.54 ⚠️ +0.02
/error-in-render avg req/sec 1640.87 1622.87 ⚠️ -18
Client Bundles (main, webpack, commons)
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
677f882d2ed8..776c.js gzip 12.7 kB 12.7 kB
framework.HASH.js gzip 39 kB 39 kB
main-95c0df2..6c91.js gzip 6.53 kB 6.53 kB
webpack-e067..f178.js gzip 751 B 751 B
Overall change 58.9 kB 58.9 kB
Client Bundles (main, webpack, commons) Modern
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
677f882d2ed8..dule.js gzip 8.26 kB 8.26 kB
framework.HA..dule.js gzip 39 kB 39 kB
main-8ead8dd..dule.js gzip 5.64 kB 5.64 kB
webpack-07c5..dule.js gzip 751 B 751 B
Overall change 53.6 kB 53.6 kB
Legacy Client Bundles (polyfills)
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
polyfills-4b..e242.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_app-3b0cf13..85f8.js gzip 1.28 kB 1.28 kB
_error-6f635..c393.js gzip 3.44 kB 3.44 kB
hooks-d4ffc3..9e0f.js gzip 887 B 887 B
index-17468f..5d83.js gzip 227 B 227 B
link-d9a15a0..130a.js gzip 1.57 kB 1.57 kB
routerDirect..924c.js gzip 284 B 284 B
withRouter-7..c13d.js gzip 284 B 284 B
Overall change 7.98 kB 7.98 kB
Client Pages Modern
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_app-75d3a82..dule.js gzip 625 B 625 B
_error-f2fcc..dule.js gzip 2.29 kB 2.29 kB
hooks-cbf13f..dule.js gzip 387 B 387 B
index-b9a643..dule.js gzip 226 B 226 B
link-973cce3..dule.js gzip 1.51 kB 1.51 kB
routerDirect..dule.js gzip 284 B 284 B
withRouter-f..dule.js gzip 282 B 282 B
Overall change 5.6 kB 5.6 kB
Client Build Manifests
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_buildManifest.js gzip 321 B 321 B
_buildManife..dule.js gzip 330 B 330 B
Overall change 651 B 651 B
Rendered Page Sizes
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
index.html gzip 968 B 968 B
link.html gzip 974 B 974 B
withRouter.html gzip 961 B 961 B
Overall change 2.9 kB 2.9 kB

Serverless Mode
General Overall increase ⚠️
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
buildDuration 15.6s 15.9s ⚠️ +312ms
nodeModulesSize 85 MB 85 MB ⚠️ +854 B
Client Bundles (main, webpack, commons)
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
677f882d2ed8..776c.js gzip 12.7 kB 12.7 kB
framework.HASH.js gzip 39 kB 39 kB
main-95c0df2..6c91.js gzip 6.53 kB 6.53 kB
webpack-e067..f178.js gzip 751 B 751 B
Overall change 58.9 kB 58.9 kB
Client Bundles (main, webpack, commons) Modern
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
677f882d2ed8..dule.js gzip 8.26 kB 8.26 kB
framework.HA..dule.js gzip 39 kB 39 kB
main-8ead8dd..dule.js gzip 5.64 kB 5.64 kB
webpack-07c5..dule.js gzip 751 B 751 B
Overall change 53.6 kB 53.6 kB
Legacy Client Bundles (polyfills)
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
polyfills-4b..e242.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_app-3b0cf13..85f8.js gzip 1.28 kB 1.28 kB
_error-6f635..c393.js gzip 3.44 kB 3.44 kB
hooks-d4ffc3..9e0f.js gzip 887 B 887 B
index-17468f..5d83.js gzip 227 B 227 B
link-d9a15a0..130a.js gzip 1.57 kB 1.57 kB
routerDirect..924c.js gzip 284 B 284 B
withRouter-7..c13d.js gzip 284 B 284 B
Overall change 7.98 kB 7.98 kB
Client Pages Modern
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_app-75d3a82..dule.js gzip 625 B 625 B
_error-f2fcc..dule.js gzip 2.29 kB 2.29 kB
hooks-cbf13f..dule.js gzip 387 B 387 B
index-b9a643..dule.js gzip 226 B 226 B
link-973cce3..dule.js gzip 1.51 kB 1.51 kB
routerDirect..dule.js gzip 284 B 284 B
withRouter-f..dule.js gzip 282 B 282 B
Overall change 5.6 kB 5.6 kB
Client Build Manifests
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_buildManifest.js gzip 321 B 321 B
_buildManife..dule.js gzip 330 B 330 B
Overall change 651 B 651 B
Serverless bundles
vercel/next.js canary stefanjudis/next.js add-contentful-image-component Change
_error.js 918 kB 918 kB
404.html 4.61 kB 4.61 kB
hooks.html 3.86 kB 3.86 kB
index.js 918 kB 918 kB
link.js 976 kB 976 kB
routerDirect.js 969 kB 969 kB
withRouter.js 969 kB 969 kB
Overall change 4.76 MB 4.76 MB
Commit: e623c4c

@stefanjudis
Copy link
Contributor Author

@ijjk I don't think that the failing test is related to my changes. :) Are you up for adding Contentful support for the Image component?

@stefanjudis stefanjudis marked this pull request as ready for review November 16, 2020 18:29
@@ -428,6 +429,16 @@ function akamaiLoader({ root, src, width }: LoaderProps): string {
return `${root}${normalizeSrc(src)}?imwidth=${width}`
}

function contentfulLoader({ src, quality, width }: LoaderProps): string {
const params = ['w=' + width]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about auto formatting (webp) and max width limiting? (see cloudinary/imgix below)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, these are params the Contentful Images API doesn't offer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could lead to upscaling source images and actually increase the size instead of the expected decrease.

What happens when you have a source image that's 500 pixels wide but you pass w=1000?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was a great question. 👍 The Contentful Images API is not upscaling and keeps the max uploaded image dimensions even when w is greater than the dimension.

Example:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@styfle be sure to pass a quality parameter q when you are delivering images via Contentful's Images API; the quality is set to 100% without it, resulting in much larger images. This is true of WebP and JPG. I recommend experimenting with q values from 30-70.

@abarnwell
Copy link

@stefanjudis thank you for putting in the work with this. I can't leverage the new Next.js image component without it as I would quickly go over my Vercel bandwidth allotment! What's the process to progress this into canary?

@LaserYGD
Copy link

LaserYGD commented Dec 2, 2020

Looking forward to this, thanks for the work!

@styfle
Copy link
Member

styfle commented Dec 2, 2020

We might decide to use #19325 in favor of this PR since it will handle arbitrary CDNs.

@stefanjudis
Copy link
Contributor Author

@styfle Makes sense. 🎉 Feel free to close if decided.

@abarnwell
Copy link

We might decide to use #19325 in favor of this PR since it will handle arbitrary CDNs.

Makes sense @styfle, thanks for linking and staying on top of it. Any word on when #19325 may be available?

Thanks for your work on this @stefanjudis!

@TheWirv
Copy link

TheWirv commented Dec 9, 2020

Hey guys, first of all thanks to @stefanjudis for the great work on this. In the meantime, since there's still some precision making to do (and obviously well under way) by the Next team, I have a recommendation for all Contentful/Next.js integrators who are eagerly awaiting the merge of either this or #19325:

So, for now you could just make Stefan's adjustments from this PR in your own next node_module and then use patch-package to patch it temporarily. As soon as one of these two PR's is an official addition to Next, you can just remove the patch and it's all good. I haven't tested it myself, but I use patch-package all the time and it should just work like with any other node_module.

Cheers :)

@styfle
Copy link
Member

styfle commented Jan 5, 2021

Closing in favor of #20216

@styfle styfle closed this Jan 5, 2021
@penx
Copy link

penx commented Nov 22, 2021

This can now be done by:

  1. adding { images: { loader: 'custom' } } to next.config.js
  2. where you are using Next Image, add the prop: loader={contentfulLoader}
  3. define contentfulLoader somewhere as follows:
function normalizeSrc(src: string) {
  return src[0] === '/' ? src.slice(1) : src;
}

function contentfulLoader({ src, quality, width }: ImageLoaderProps): string {
  const params = ['w=' + width];

  if (quality) {
    params.push('q=' + quality);
  }

  return `${normalizeSrc(src)}?${params.join('&')}`;
}

@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.

None yet

10 participants