UNPKG

next

Version:

The React Framework

49 lines • 14.6 kB
"use strict";var _interopRequireDefault=require("@babel/runtime/helpers/interopRequireDefault");exports.__esModule=true;exports.default=Image;var _objectWithoutPropertiesLoose2=_interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));var _extends2=_interopRequireDefault(require("@babel/runtime/helpers/extends"));var _react=_interopRequireDefault(require("react"));var _head=_interopRequireDefault(require("../next-server/lib/head"));var _toBase=require("../next-server/lib/to-base-64");var _imageConfig=require("../next-server/server/image-config");var _useIntersection=require("./use-intersection");if(typeof window==='undefined'){;global.__NEXT_IMAGE_IMPORTED=true;}const VALID_LOADING_VALUES=['lazy','eager',undefined];const loaders=new Map([['imgix',imgixLoader],['cloudinary',cloudinaryLoader],['akamai',akamaiLoader],['default',defaultLoader]]);const VALID_LAYOUT_VALUES=['fill','fixed','intrinsic','responsive',undefined];function isStaticRequire(src){return src.default!==undefined;}function isStaticImageData(src){return src.src!==undefined;}function isStaticImport(src){return typeof src==='object'&&(isStaticRequire(src)||isStaticImageData(src));}const{deviceSizes:configDeviceSizes,imageSizes:configImageSizes,loader:configLoader,path:configPath,domains:configDomains}=process.env.__NEXT_IMAGE_OPTS||_imageConfig.imageConfigDefault;// sort smallest to largest const allSizes=[...configDeviceSizes,...configImageSizes];configDeviceSizes.sort((a,b)=>a-b);allSizes.sort((a,b)=>a-b);function getWidths(width,layout,sizes){if(sizes&&(layout==='fill'||layout==='responsive')){// Find all the "vw" percent sizes used in the sizes prop const viewportWidthRe=/(^|\s)(1?\d?\d)vw/g;const percentSizes=[];for(let match;match=viewportWidthRe.exec(sizes);match){percentSizes.push(parseInt(match[2]));}if(percentSizes.length){const smallestRatio=Math.min(...percentSizes)*0.01;return{widths:allSizes.filter(s=>s>=configDeviceSizes[0]*smallestRatio),kind:'w'};}return{widths:allSizes,kind:'w'};}if(typeof width!=='number'||layout==='fill'||layout==='responsive'){return{widths:configDeviceSizes,kind:'w'};}const widths=[...new Set(// > This means that most OLED screens that say they are 3x resolution, // > are actually 3x in the green color, but only 1.5x in the red and // > blue colors. Showing a 3x resolution image in the app vs a 2x // > resolution image will be visually the same, though the 3x image // > takes significantly more data. Even true 3x resolution screens are // > wasteful as the human eye cannot see that level of detail without // > something like a magnifying glass. // https://blog.twitter.com/engineering/en_us/topics/infrastructure/2019/capping-image-fidelity-on-ultra-high-resolution-devices.html [width,width*2/*, width * 3*/].map(w=>allSizes.find(p=>p>=w)||allSizes[allSizes.length-1]))];return{widths,kind:'x'};}function generateImgAttrs({src,unoptimized,layout,width,quality,sizes,loader}){if(unoptimized){return{src,srcSet:undefined,sizes:undefined};}const{widths,kind}=getWidths(width,layout,sizes);const last=widths.length-1;return{sizes:!sizes&&kind==='w'?'100vw':sizes,srcSet:widths.map((w,i)=>`${loader({src,quality,width:w})} ${kind==='w'?w:i+1}${kind}`).join(', '),// It's intended to keep `src` the last attribute because React updates // attributes in order. If we keep `src` the first one, Safari will // immediately start to fetch `src`, before `sizes` and `srcSet` are even // updated by React. That causes multiple unnecessary requests if `srcSet` // and `sizes` are defined. // This bug cannot be reproduced in Chrome or Firefox. src:loader({src,quality,width:widths[last]})};}function getInt(x){if(typeof x==='number'){return x;}if(typeof x==='string'){return parseInt(x,10);}return undefined;}function defaultImageLoader(loaderProps){const load=loaders.get(configLoader);if(load){return load((0,_extends2.default)({root:configPath},loaderProps));}throw new Error(`Unknown "loader" found in "next.config.js". Expected: ${_imageConfig.VALID_LOADERS.join(', ')}. Received: ${configLoader}`);}// See https://stackoverflow.com/q/39777833/266535 for why we use this ref // handler instead of the img's onLoad attribute. function removePlaceholder(img,placeholder){if(placeholder==='blur'&&img){const handleLoad=()=>{if(!img.src.startsWith('data:')){const p='decode'in img?img.decode():Promise.resolve();p.catch(()=>{}).then(()=>{img.style.filter='none';img.style.backgroundSize='none';img.style.backgroundImage='none';});}};if(img.complete){// If the real image fails to load, this will still remove the placeholder. // This is the desired behavior for now, and will be revisited when error // handling is worked on for the image component itself. handleLoad();}else{img.onload=handleLoad;}}}function Image(_ref){let{src,sizes,unoptimized=false,priority=false,loading,className,quality,width,height,objectFit,objectPosition,loader=defaultImageLoader,placeholder='empty',blurDataURL}=_ref,all=(0,_objectWithoutPropertiesLoose2.default)(_ref,["src","sizes","unoptimized","priority","loading","className","quality","width","height","objectFit","objectPosition","loader","placeholder","blurDataURL"]);let rest=all;let layout=sizes?'responsive':'intrinsic';if('layout'in rest){// Override default layout if the user specified one: if(rest.layout)layout=rest.layout;// Remove property so it's not spread into image: delete rest['layout'];}let staticSrc='';if(isStaticImport(src)){const staticImageData=isStaticRequire(src)?src.default:src;if(!staticImageData.src){throw new Error(`An object should only be passed to the image component src parameter if it comes from a static image import. It must include src. Received ${JSON.stringify(staticImageData)}`);}blurDataURL=blurDataURL||staticImageData.blurDataURL;staticSrc=staticImageData.src;if(!layout||layout!=='fill'){height=height||staticImageData.height;width=width||staticImageData.width;if(!staticImageData.height||!staticImageData.width){throw new Error(`An object should only be passed to the image component src parameter if it comes from a static image import. It must include height and width. Received ${JSON.stringify(staticImageData)}`);}}}src=typeof src==='string'?src:staticSrc;const widthInt=getInt(width);const heightInt=getInt(height);const qualityInt=getInt(quality);if(process.env.NODE_ENV!=='production'){if(!src){throw new Error(`Image is missing required "src" property. Make sure you pass "src" in props to the \`next/image\` component. Received: ${JSON.stringify({width,height,quality})}`);}if(!VALID_LAYOUT_VALUES.includes(layout)){throw new Error(`Image with src "${src}" has invalid "layout" property. Provided "${layout}" should be one of ${VALID_LAYOUT_VALUES.map(String).join(',')}.`);}if(typeof widthInt!=='undefined'&&isNaN(widthInt)||typeof heightInt!=='undefined'&&isNaN(heightInt)){throw new Error(`Image with src "${src}" has invalid "width" or "height" property. These should be numeric values.`);}if(!VALID_LOADING_VALUES.includes(loading)){throw new Error(`Image with src "${src}" has invalid "loading" property. Provided "${loading}" should be one of ${VALID_LOADING_VALUES.map(String).join(',')}.`);}if(priority&&loading==='lazy'){throw new Error(`Image with src "${src}" has both "priority" and "loading='lazy'" properties. Only one should be used.`);}if(placeholder==='blur'){if(layout!=='fill'&&(widthInt||0)*(heightInt||0)<1600){console.warn(`Image with src "${src}" is smaller than 40x40. Consider removing the "placeholder='blur'" property to improve performance.`);}if(!blurDataURL){const VALID_BLUR_EXT=['jpeg','png','webp'];// should match next-image-loader throw new Error(`Image with src "${src}" has "placeholder='blur'" property but is missing the "blurDataURL" property. Possible solutions: - Add a "blurDataURL" property, the contents should be a small Data URL to represent the image - Change the "src" property to a static import with one of the supported file types: ${VALID_BLUR_EXT.join(',')} - Remove the "placeholder" property, effectively no blur effect Read more: https://nextjs.org/docs/messages/placeholder-blur-data-url`);}}}let isLazy=!priority&&(loading==='lazy'||typeof loading==='undefined');if(src&&src.startsWith('data:')){// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs unoptimized=true;isLazy=false;}const[setRef,isIntersected]=(0,_useIntersection.useIntersection)({rootMargin:'200px',disabled:!isLazy});const isVisible=!isLazy||isIntersected;let wrapperStyle;let sizerStyle;let sizerSvg;let imgStyle=(0,_extends2.default)({position:'absolute',top:0,left:0,bottom:0,right:0,boxSizing:'border-box',padding:0,border:'none',margin:'auto',display:'block',width:0,height:0,minWidth:'100%',maxWidth:'100%',minHeight:'100%',maxHeight:'100%',objectFit,objectPosition},placeholder==='blur'?{filter:'blur(20px)',backgroundSize:'cover',backgroundImage:`url("${blurDataURL}")`}:undefined);if(typeof widthInt!=='undefined'&&typeof heightInt!=='undefined'&&layout!=='fill'){// <Image src="i.png" width="100" height="100" /> const quotient=heightInt/widthInt;const paddingTop=isNaN(quotient)?'100%':`${quotient*100}%`;if(layout==='responsive'){// <Image src="i.png" width="100" height="100" layout="responsive" /> wrapperStyle={display:'block',overflow:'hidden',position:'relative',boxSizing:'border-box',margin:0};sizerStyle={display:'block',boxSizing:'border-box',paddingTop};}else if(layout==='intrinsic'){// <Image src="i.png" width="100" height="100" layout="intrinsic" /> wrapperStyle={display:'inline-block',maxWidth:'100%',overflow:'hidden',position:'relative',boxSizing:'border-box',margin:0};sizerStyle={boxSizing:'border-box',display:'block',maxWidth:'100%'};sizerSvg=`<svg width="${widthInt}" height="${heightInt}" xmlns="http://www.w3.org/2000/svg" version="1.1"/>`;}else if(layout==='fixed'){// <Image src="i.png" width="100" height="100" layout="fixed" /> wrapperStyle={overflow:'hidden',boxSizing:'border-box',display:'inline-block',position:'relative',width:widthInt,height:heightInt};}}else if(typeof widthInt==='undefined'&&typeof heightInt==='undefined'&&layout==='fill'){// <Image src="i.png" layout="fill" /> wrapperStyle={display:'block',overflow:'hidden',position:'absolute',top:0,left:0,bottom:0,right:0,boxSizing:'border-box',margin:0};}else{// <Image src="i.png" /> if(process.env.NODE_ENV!=='production'){throw new Error(`Image with src "${src}" must use "width" and "height" properties or "layout='fill'" property.`);}}let imgAttributes={src:'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7',srcSet:undefined,sizes:undefined};if(isVisible){imgAttributes=generateImgAttrs({src,unoptimized,layout,width:widthInt,quality:qualityInt,sizes,loader});}return/*#__PURE__*/_react.default.createElement("div",{style:wrapperStyle},sizerStyle?/*#__PURE__*/_react.default.createElement("div",{style:sizerStyle},sizerSvg?/*#__PURE__*/_react.default.createElement("img",{style:{maxWidth:'100%',display:'block',margin:0,border:'none',padding:0},alt:"","aria-hidden":true,role:"presentation",src:`data:image/svg+xml;base64,${(0,_toBase.toBase64)(sizerSvg)}`}):null):null,!isVisible&&/*#__PURE__*/_react.default.createElement("noscript",null,/*#__PURE__*/_react.default.createElement("img",Object.assign({},rest,generateImgAttrs({src,unoptimized,layout,width:widthInt,quality:qualityInt,sizes,loader}),{decoding:"async",style:imgStyle,className:className}))),/*#__PURE__*/_react.default.createElement("img",Object.assign({},rest,imgAttributes,{decoding:"async",className:className,ref:element=>{setRef(element);removePlaceholder(element,placeholder);},style:imgStyle})),priority?/*#__PURE__*/ // Note how we omit the `href` attribute, as it would only be relevant // for browsers that do not support `imagesrcset`, and in those cases // it would likely cause the incorrect image to be preloaded. // // https://html.spec.whatwg.org/multipage/semantics.html#attr-link-imagesrcset _react.default.createElement(_head.default,null,/*#__PURE__*/_react.default.createElement("link",{key:'__nimg-'+imgAttributes.src+imgAttributes.srcSet+imgAttributes.sizes,rel:"preload",as:"image",href:imgAttributes.srcSet?undefined:imgAttributes.src// @ts-ignore: imagesrcset is not yet in the link element type ,imagesrcset:imgAttributes.srcSet// @ts-ignore: imagesizes is not yet in the link element type ,imagesizes:imgAttributes.sizes})):null);}//BUILT IN LOADERS function normalizeSrc(src){return src[0]==='/'?src.slice(1):src;}function imgixLoader({root,src,width,quality}){// Demo: https://static.imgix.net/daisy.png?format=auto&fit=max&w=300 const params=['auto=format','fit=max','w='+width];let paramsString='';if(quality){params.push('q='+quality);}if(params.length){paramsString='?'+params.join('&');}return`${root}${normalizeSrc(src)}${paramsString}`;}function akamaiLoader({root,src,width}){return`${root}${normalizeSrc(src)}?imwidth=${width}`;}function cloudinaryLoader({root,src,width,quality}){// Demo: https://res.cloudinary.com/demo/image/upload/w_300,c_limit,q_auto/turtles.jpg const params=['f_auto','c_limit','w_'+width,'q_'+(quality||'auto')];let paramsString=params.join(',')+'/';return`${root}${paramsString}${normalizeSrc(src)}`;}function defaultLoader({root,src,width,quality}){if(process.env.NODE_ENV!=='production'){const missingValues=[];// these should always be provided but make sure they are if(!src)missingValues.push('src');if(!width)missingValues.push('width');if(missingValues.length>0){throw new Error(`Next Image Optimization requires ${missingValues.join(', ')} to be provided. Make sure you pass them as props to the \`next/image\` component. Received: ${JSON.stringify({src,width,quality})}`);}if(src.startsWith('//')){throw new Error(`Failed to parse src "${src}" on \`next/image\`, protocol-relative URL (//) must be changed to an absolute URL (http:// or https://)`);}if(!src.startsWith('/')&&configDomains){let parsedSrc;try{parsedSrc=new URL(src);}catch(err){console.error(err);throw new Error(`Failed to parse src "${src}" on \`next/image\`, if using relative image it must start with a leading slash "/" or be an absolute URL (http:// or https://)`);}if(!configDomains.includes(parsedSrc.hostname)){throw new Error(`Invalid src prop (${src}) on \`next/image\`, hostname "${parsedSrc.hostname}" is not configured under images in your \`next.config.js\`\n`+`See more info: https://nextjs.org/docs/messages/next-image-unconfigured-host`);}}}return`${root}?url=${encodeURIComponent(src)}&w=${width}&q=${quality||75}`;} //# sourceMappingURL=image.js.map