image.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777
  1. "client";
  2. "use strict";
  3. Object.defineProperty(exports, "__esModule", {
  4. value: true
  5. });
  6. exports.default = Image;
  7. var _extends = require("@swc/helpers/lib/_extends.js").default;
  8. var _interop_require_default = require("@swc/helpers/lib/_interop_require_default.js").default;
  9. var _interop_require_wildcard = require("@swc/helpers/lib/_interop_require_wildcard.js").default;
  10. var _object_without_properties_loose = require("@swc/helpers/lib/_object_without_properties_loose.js").default;
  11. var _react = _interop_require_wildcard(require("react"));
  12. var _head = _interop_require_default(require("../shared/lib/head"));
  13. var _imageConfig = require("../shared/lib/image-config");
  14. var _useIntersection = require("./use-intersection");
  15. var _imageConfigContext = require("../shared/lib/image-config-context");
  16. var _utils = require("../shared/lib/utils");
  17. var _normalizeTrailingSlash = require("./normalize-trailing-slash");
  18. function Image(_param) {
  19. var { src , sizes , unoptimized =false , priority =false , loading , lazyRoot =null , lazyBoundary , className , quality , width , height , style , objectFit , objectPosition , onLoadingComplete , placeholder ='empty' , blurDataURL } = _param, all = _object_without_properties_loose(_param, [
  20. "src",
  21. "sizes",
  22. "unoptimized",
  23. "priority",
  24. "loading",
  25. "lazyRoot",
  26. "lazyBoundary",
  27. "className",
  28. "quality",
  29. "width",
  30. "height",
  31. "style",
  32. "objectFit",
  33. "objectPosition",
  34. "onLoadingComplete",
  35. "placeholder",
  36. "blurDataURL"
  37. ]);
  38. const configContext = (0, _react).useContext(_imageConfigContext.ImageConfigContext);
  39. const config = (0, _react).useMemo(()=>{
  40. const c = configEnv || configContext || _imageConfig.imageConfigDefault;
  41. const allSizes = [
  42. ...c.deviceSizes,
  43. ...c.imageSizes
  44. ].sort((a, b)=>a - b);
  45. const deviceSizes = c.deviceSizes.sort((a, b)=>a - b);
  46. return _extends({}, c, {
  47. allSizes,
  48. deviceSizes
  49. });
  50. }, [
  51. configContext
  52. ]);
  53. let rest = all;
  54. let layout = sizes ? 'responsive' : 'intrinsic';
  55. if ('layout' in rest) {
  56. // Override default layout if the user specified one:
  57. if (rest.layout) layout = rest.layout;
  58. // Remove property so it's not spread on <img>:
  59. delete rest.layout;
  60. }
  61. let loader = defaultImageLoader;
  62. if ('loader' in rest) {
  63. if (rest.loader) {
  64. const customImageLoader = rest.loader;
  65. var _tmp;
  66. _tmp = (obj)=>{
  67. const { config: _ } = obj, opts = _object_without_properties_loose(obj, [
  68. "config"
  69. ]);
  70. // The config object is internal only so we must
  71. // not pass it to the user-defined loader()
  72. return customImageLoader(opts);
  73. }, loader = _tmp, _tmp;
  74. }
  75. // Remove property so it's not spread on <img>
  76. delete rest.loader;
  77. }
  78. let staticSrc = '';
  79. if (isStaticImport(src)) {
  80. const staticImageData = isStaticRequire(src) ? src.default : src;
  81. if (!staticImageData.src) {
  82. 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)}`);
  83. }
  84. blurDataURL = blurDataURL || staticImageData.blurDataURL;
  85. staticSrc = staticImageData.src;
  86. if (!layout || layout !== 'fill') {
  87. height = height || staticImageData.height;
  88. width = width || staticImageData.width;
  89. if (!staticImageData.height || !staticImageData.width) {
  90. 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)}`);
  91. }
  92. }
  93. }
  94. src = typeof src === 'string' ? src : staticSrc;
  95. let isLazy = !priority && (loading === 'lazy' || typeof loading === 'undefined');
  96. if (src.startsWith('data:') || src.startsWith('blob:')) {
  97. // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs
  98. unoptimized = true;
  99. isLazy = false;
  100. }
  101. if (typeof window !== 'undefined' && loadedImageURLs.has(src)) {
  102. isLazy = false;
  103. }
  104. if (config.unoptimized) {
  105. unoptimized = true;
  106. }
  107. const [blurComplete, setBlurComplete] = (0, _react).useState(false);
  108. const [setIntersection, isIntersected, resetIntersected] = (0, _useIntersection).useIntersection({
  109. rootRef: lazyRoot,
  110. rootMargin: lazyBoundary || '200px',
  111. disabled: !isLazy
  112. });
  113. const isVisible = !isLazy || isIntersected;
  114. const wrapperStyle = {
  115. boxSizing: 'border-box',
  116. display: 'block',
  117. overflow: 'hidden',
  118. width: 'initial',
  119. height: 'initial',
  120. background: 'none',
  121. opacity: 1,
  122. border: 0,
  123. margin: 0,
  124. padding: 0
  125. };
  126. const sizerStyle = {
  127. boxSizing: 'border-box',
  128. display: 'block',
  129. width: 'initial',
  130. height: 'initial',
  131. background: 'none',
  132. opacity: 1,
  133. border: 0,
  134. margin: 0,
  135. padding: 0
  136. };
  137. let hasSizer = false;
  138. let sizerSvgUrl;
  139. const layoutStyle = {
  140. position: 'absolute',
  141. top: 0,
  142. left: 0,
  143. bottom: 0,
  144. right: 0,
  145. boxSizing: 'border-box',
  146. padding: 0,
  147. border: 'none',
  148. margin: 'auto',
  149. display: 'block',
  150. width: 0,
  151. height: 0,
  152. minWidth: '100%',
  153. maxWidth: '100%',
  154. minHeight: '100%',
  155. maxHeight: '100%',
  156. objectFit,
  157. objectPosition
  158. };
  159. let widthInt = getInt(width);
  160. let heightInt = getInt(height);
  161. const qualityInt = getInt(quality);
  162. if (process.env.NODE_ENV !== 'production') {
  163. if (!src) {
  164. // React doesn't show the stack trace and there's
  165. // no `src` to help identify which image, so we
  166. // instead console.error(ref) during mount.
  167. widthInt = widthInt || 1;
  168. heightInt = heightInt || 1;
  169. unoptimized = true;
  170. } else {
  171. if (!VALID_LAYOUT_VALUES.includes(layout)) {
  172. if (layout === 'raw') {
  173. throw new Error(`The layout="raw" experiment has been moved to a new module. Please import \`next/future/image\` instead.`);
  174. }
  175. throw new Error(`Image with src "${src}" has invalid "layout" property. Provided "${layout}" should be one of ${VALID_LAYOUT_VALUES.map(String).join(',')}.`);
  176. }
  177. if (typeof widthInt !== 'undefined' && isNaN(widthInt) || typeof heightInt !== 'undefined' && isNaN(heightInt)) {
  178. throw new Error(`Image with src "${src}" has invalid "width" or "height" property. These should be numeric values.`);
  179. }
  180. if (layout === 'fill' && (width || height)) {
  181. (0, _utils).warnOnce(`Image with src "${src}" and "layout='fill'" has unused properties assigned. Please remove "width" and "height".`);
  182. }
  183. if (!VALID_LOADING_VALUES.includes(loading)) {
  184. throw new Error(`Image with src "${src}" has invalid "loading" property. Provided "${loading}" should be one of ${VALID_LOADING_VALUES.map(String).join(',')}.`);
  185. }
  186. if (priority && loading === 'lazy') {
  187. throw new Error(`Image with src "${src}" has both "priority" and "loading='lazy'" properties. Only one should be used.`);
  188. }
  189. if (sizes && layout !== 'fill' && layout !== 'responsive') {
  190. (0, _utils).warnOnce(`Image with src "${src}" has "sizes" property but it will be ignored. Only use "sizes" with "layout='fill'" or "layout='responsive'"`);
  191. }
  192. if (placeholder === 'blur') {
  193. if (layout !== 'fill' && (widthInt || 0) * (heightInt || 0) < 1600) {
  194. (0, _utils).warnOnce(`Image with src "${src}" is smaller than 40x40. Consider removing the "placeholder='blur'" property to improve performance.`);
  195. }
  196. if (!blurDataURL) {
  197. const VALID_BLUR_EXT = [
  198. 'jpeg',
  199. 'png',
  200. 'webp',
  201. 'avif'
  202. ] // should match next-image-loader
  203. ;
  204. throw new Error(`Image with src "${src}" has "placeholder='blur'" property but is missing the "blurDataURL" property.
  205. Possible solutions:
  206. - Add a "blurDataURL" property, the contents should be a small Data URL to represent the image
  207. - Change the "src" property to a static import with one of the supported file types: ${VALID_BLUR_EXT.join(',')}
  208. - Remove the "placeholder" property, effectively no blur effect
  209. Read more: https://nextjs.org/docs/messages/placeholder-blur-data-url`);
  210. }
  211. }
  212. if ('ref' in rest) {
  213. (0, _utils).warnOnce(`Image with src "${src}" is using unsupported "ref" property. Consider using the "onLoadingComplete" property instead.`);
  214. }
  215. if (!unoptimized && loader !== defaultImageLoader) {
  216. const urlStr = loader({
  217. config,
  218. src,
  219. width: widthInt || 400,
  220. quality: qualityInt || 75
  221. });
  222. let url;
  223. try {
  224. url = new URL(urlStr);
  225. } catch (err) {}
  226. if (urlStr === src || url && url.pathname === src && !url.search) {
  227. (0, _utils).warnOnce(`Image with src "${src}" has a "loader" property that does not implement width. Please implement it or use the "unoptimized" property instead.` + `\nRead more: https://nextjs.org/docs/messages/next-image-missing-loader-width`);
  228. }
  229. }
  230. if (style) {
  231. let overwrittenStyles = Object.keys(style).filter((key)=>key in layoutStyle);
  232. if (overwrittenStyles.length) {
  233. (0, _utils).warnOnce(`Image with src ${src} is assigned the following styles, which are overwritten by automatically-generated styles: ${overwrittenStyles.join(', ')}`);
  234. }
  235. }
  236. if (typeof window !== 'undefined' && !perfObserver && window.PerformanceObserver) {
  237. perfObserver = new PerformanceObserver((entryList)=>{
  238. for (const entry of entryList.getEntries()){
  239. var ref;
  240. // @ts-ignore - missing "LargestContentfulPaint" class with "element" prop
  241. const imgSrc = (entry == null ? void 0 : (ref = entry.element) == null ? void 0 : ref.src) || '';
  242. const lcpImage = allImgs.get(imgSrc);
  243. if (lcpImage && !lcpImage.priority && lcpImage.placeholder !== 'blur' && !lcpImage.src.startsWith('data:') && !lcpImage.src.startsWith('blob:')) {
  244. // https://web.dev/lcp/#measure-lcp-in-javascript
  245. (0, _utils).warnOnce(`Image with src "${lcpImage.src}" was detected as the Largest Contentful Paint (LCP). Please add the "priority" property if this image is above the fold.` + `\nRead more: https://nextjs.org/docs/api-reference/next/image#priority`);
  246. }
  247. }
  248. });
  249. try {
  250. perfObserver.observe({
  251. type: 'largest-contentful-paint',
  252. buffered: true
  253. });
  254. } catch (err) {
  255. // Log error but don't crash the app
  256. console.error(err);
  257. }
  258. }
  259. }
  260. }
  261. const imgStyle = Object.assign({}, style, layoutStyle);
  262. const blurStyle = placeholder === 'blur' && !blurComplete ? {
  263. backgroundSize: objectFit || 'cover',
  264. backgroundPosition: objectPosition || '0% 0%',
  265. filter: 'blur(20px)',
  266. backgroundImage: `url("${blurDataURL}")`
  267. } : {};
  268. if (layout === 'fill') {
  269. // <Image src="i.png" layout="fill" />
  270. wrapperStyle.display = 'block';
  271. wrapperStyle.position = 'absolute';
  272. wrapperStyle.top = 0;
  273. wrapperStyle.left = 0;
  274. wrapperStyle.bottom = 0;
  275. wrapperStyle.right = 0;
  276. } else if (typeof widthInt !== 'undefined' && typeof heightInt !== 'undefined') {
  277. // <Image src="i.png" width="100" height="100" />
  278. const quotient = heightInt / widthInt;
  279. const paddingTop = isNaN(quotient) ? '100%' : `${quotient * 100}%`;
  280. if (layout === 'responsive') {
  281. // <Image src="i.png" width="100" height="100" layout="responsive" />
  282. wrapperStyle.display = 'block';
  283. wrapperStyle.position = 'relative';
  284. hasSizer = true;
  285. sizerStyle.paddingTop = paddingTop;
  286. } else if (layout === 'intrinsic') {
  287. // <Image src="i.png" width="100" height="100" layout="intrinsic" />
  288. wrapperStyle.display = 'inline-block';
  289. wrapperStyle.position = 'relative';
  290. wrapperStyle.maxWidth = '100%';
  291. hasSizer = true;
  292. sizerStyle.maxWidth = '100%';
  293. sizerSvgUrl = `data:image/svg+xml,%3csvg%20xmlns=%27http://www.w3.org/2000/svg%27%20version=%271.1%27%20width=%27${widthInt}%27%20height=%27${heightInt}%27/%3e`;
  294. } else if (layout === 'fixed') {
  295. // <Image src="i.png" width="100" height="100" layout="fixed" />
  296. wrapperStyle.display = 'inline-block';
  297. wrapperStyle.position = 'relative';
  298. wrapperStyle.width = widthInt;
  299. wrapperStyle.height = heightInt;
  300. }
  301. } else {
  302. // <Image src="i.png" />
  303. if (process.env.NODE_ENV !== 'production') {
  304. throw new Error(`Image with src "${src}" must use "width" and "height" properties or "layout='fill'" property.`);
  305. }
  306. }
  307. let imgAttributes = {
  308. src: emptyDataURL,
  309. srcSet: undefined,
  310. sizes: undefined
  311. };
  312. if (isVisible) {
  313. imgAttributes = generateImgAttrs({
  314. config,
  315. src,
  316. unoptimized,
  317. layout,
  318. width: widthInt,
  319. quality: qualityInt,
  320. sizes,
  321. loader
  322. });
  323. }
  324. let srcString = src;
  325. if (process.env.NODE_ENV !== 'production') {
  326. if (typeof window !== 'undefined') {
  327. let fullUrl;
  328. try {
  329. fullUrl = new URL(imgAttributes.src);
  330. } catch (e) {
  331. fullUrl = new URL(imgAttributes.src, window.location.href);
  332. }
  333. allImgs.set(fullUrl.href, {
  334. src,
  335. priority,
  336. placeholder
  337. });
  338. }
  339. }
  340. let imageSrcSetPropName = 'imagesrcset';
  341. let imageSizesPropName = 'imagesizes';
  342. if (process.env.__NEXT_REACT_ROOT) {
  343. imageSrcSetPropName = 'imageSrcSet';
  344. imageSizesPropName = 'imageSizes';
  345. }
  346. const linkProps = {
  347. // Note: imagesrcset and imagesizes are not in the link element type with react 17.
  348. [imageSrcSetPropName]: imgAttributes.srcSet,
  349. [imageSizesPropName]: imgAttributes.sizes,
  350. crossOrigin: rest.crossOrigin
  351. };
  352. const useLayoutEffect = typeof window === 'undefined' ? _react.default.useEffect : _react.default.useLayoutEffect;
  353. const onLoadingCompleteRef = (0, _react).useRef(onLoadingComplete);
  354. const previousImageSrc = (0, _react).useRef(src);
  355. (0, _react).useEffect(()=>{
  356. onLoadingCompleteRef.current = onLoadingComplete;
  357. }, [
  358. onLoadingComplete
  359. ]);
  360. useLayoutEffect(()=>{
  361. if (previousImageSrc.current !== src) {
  362. resetIntersected();
  363. previousImageSrc.current = src;
  364. }
  365. }, [
  366. resetIntersected,
  367. src
  368. ]);
  369. const imgElementArgs = _extends({
  370. isLazy,
  371. imgAttributes,
  372. heightInt,
  373. widthInt,
  374. qualityInt,
  375. layout,
  376. className,
  377. imgStyle,
  378. blurStyle,
  379. loading,
  380. config,
  381. unoptimized,
  382. placeholder,
  383. loader,
  384. srcString,
  385. onLoadingCompleteRef,
  386. setBlurComplete,
  387. setIntersection,
  388. isVisible,
  389. noscriptSizes: sizes
  390. }, rest);
  391. return /*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/ _react.default.createElement("span", {
  392. style: wrapperStyle
  393. }, hasSizer ? /*#__PURE__*/ _react.default.createElement("span", {
  394. style: sizerStyle
  395. }, sizerSvgUrl ? /*#__PURE__*/ _react.default.createElement("img", {
  396. style: {
  397. display: 'block',
  398. maxWidth: '100%',
  399. width: 'initial',
  400. height: 'initial',
  401. background: 'none',
  402. opacity: 1,
  403. border: 0,
  404. margin: 0,
  405. padding: 0
  406. },
  407. alt: "",
  408. "aria-hidden": true,
  409. src: sizerSvgUrl
  410. }) : null) : null, /*#__PURE__*/ _react.default.createElement(ImageElement, Object.assign({}, imgElementArgs))), priority ? // Note how we omit the `href` attribute, as it would only be relevant
  411. // for browsers that do not support `imagesrcset`, and in those cases
  412. // it would likely cause the incorrect image to be preloaded.
  413. //
  414. // https://html.spec.whatwg.org/multipage/semantics.html#attr-link-imagesrcset
  415. /*#__PURE__*/ _react.default.createElement(_head.default, null, /*#__PURE__*/ _react.default.createElement("link", Object.assign({
  416. key: '__nimg-' + imgAttributes.src + imgAttributes.srcSet + imgAttributes.sizes,
  417. rel: "preload",
  418. as: "image",
  419. href: imgAttributes.srcSet ? undefined : imgAttributes.src
  420. }, linkProps))) : null);
  421. }
  422. 'client';
  423. function normalizeSrc(src) {
  424. return src[0] === '/' ? src.slice(1) : src;
  425. }
  426. const configEnv = process.env.__NEXT_IMAGE_OPTS;
  427. const loadedImageURLs = new Set();
  428. const allImgs = new Map();
  429. let perfObserver;
  430. const emptyDataURL = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
  431. if (typeof window === 'undefined') {
  432. global.__NEXT_IMAGE_IMPORTED = true;
  433. }
  434. const VALID_LOADING_VALUES = [
  435. 'lazy',
  436. 'eager',
  437. undefined
  438. ];
  439. function imgixLoader({ config , src , width , quality }) {
  440. // Demo: https://static.imgix.net/daisy.png?auto=format&fit=max&w=300
  441. const url = new URL(`${config.path}${normalizeSrc(src)}`);
  442. const params = url.searchParams;
  443. // auto params can be combined with comma separation, or reiteration
  444. params.set('auto', params.getAll('auto').join(',') || 'format');
  445. params.set('fit', params.get('fit') || 'max');
  446. params.set('w', params.get('w') || width.toString());
  447. if (quality) {
  448. params.set('q', quality.toString());
  449. }
  450. return url.href;
  451. }
  452. function akamaiLoader({ config , src , width }) {
  453. return `${config.path}${normalizeSrc(src)}?imwidth=${width}`;
  454. }
  455. function cloudinaryLoader({ config , src , width , quality }) {
  456. // Demo: https://res.cloudinary.com/demo/image/upload/w_300,c_limit,q_auto/turtles.jpg
  457. const params = [
  458. 'f_auto',
  459. 'c_limit',
  460. 'w_' + width,
  461. 'q_' + (quality || 'auto')
  462. ];
  463. const paramsString = params.join(',') + '/';
  464. return `${config.path}${paramsString}${normalizeSrc(src)}`;
  465. }
  466. function customLoader({ src }) {
  467. throw new Error(`Image with src "${src}" is missing "loader" prop.` + `\nRead more: https://nextjs.org/docs/messages/next-image-missing-loader`);
  468. }
  469. function defaultLoader({ config , src , width , quality }) {
  470. if (process.env.NODE_ENV !== 'production') {
  471. const missingValues = [];
  472. // these should always be provided but make sure they are
  473. if (!src) missingValues.push('src');
  474. if (!width) missingValues.push('width');
  475. if (missingValues.length > 0) {
  476. 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({
  477. src,
  478. width,
  479. quality
  480. })}`);
  481. }
  482. if (src.startsWith('//')) {
  483. throw new Error(`Failed to parse src "${src}" on \`next/image\`, protocol-relative URL (//) must be changed to an absolute URL (http:// or https://)`);
  484. }
  485. if (!src.startsWith('/') && (config.domains || config.remotePatterns)) {
  486. let parsedSrc;
  487. try {
  488. parsedSrc = new URL(src);
  489. } catch (err) {
  490. console.error(err);
  491. 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://)`);
  492. }
  493. if (process.env.NODE_ENV !== 'test') {
  494. // We use dynamic require because this should only error in development
  495. const { hasMatch } = require('../shared/lib/match-remote-pattern');
  496. if (!hasMatch(config.domains, config.remotePatterns, parsedSrc)) {
  497. 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`);
  498. }
  499. }
  500. }
  501. }
  502. if (src.endsWith('.svg') && !config.dangerouslyAllowSVG) {
  503. // Special case to make svg serve as-is to avoid proxying
  504. // through the built-in Image Optimization API.
  505. return src;
  506. }
  507. return `${(0, _normalizeTrailingSlash).normalizePathTrailingSlash(config.path)}?url=${encodeURIComponent(src)}&w=${width}&q=${quality || 75}`;
  508. }
  509. const loaders = new Map([
  510. [
  511. 'default',
  512. defaultLoader
  513. ],
  514. [
  515. 'imgix',
  516. imgixLoader
  517. ],
  518. [
  519. 'cloudinary',
  520. cloudinaryLoader
  521. ],
  522. [
  523. 'akamai',
  524. akamaiLoader
  525. ],
  526. [
  527. 'custom',
  528. customLoader
  529. ],
  530. ]);
  531. const VALID_LAYOUT_VALUES = [
  532. 'fill',
  533. 'fixed',
  534. 'intrinsic',
  535. 'responsive',
  536. undefined,
  537. ];
  538. function isStaticRequire(src) {
  539. return src.default !== undefined;
  540. }
  541. function isStaticImageData(src) {
  542. return src.src !== undefined;
  543. }
  544. function isStaticImport(src) {
  545. return typeof src === 'object' && (isStaticRequire(src) || isStaticImageData(src));
  546. }
  547. function getWidths({ deviceSizes , allSizes }, width, layout, sizes) {
  548. if (sizes && (layout === 'fill' || layout === 'responsive')) {
  549. // Find all the "vw" percent sizes used in the sizes prop
  550. const viewportWidthRe = /(^|\s)(1?\d?\d)vw/g;
  551. const percentSizes = [];
  552. for(let match; match = viewportWidthRe.exec(sizes); match){
  553. percentSizes.push(parseInt(match[2]));
  554. }
  555. if (percentSizes.length) {
  556. const smallestRatio = Math.min(...percentSizes) * 0.01;
  557. return {
  558. widths: allSizes.filter((s)=>s >= deviceSizes[0] * smallestRatio),
  559. kind: 'w'
  560. };
  561. }
  562. return {
  563. widths: allSizes,
  564. kind: 'w'
  565. };
  566. }
  567. if (typeof width !== 'number' || layout === 'fill' || layout === 'responsive') {
  568. return {
  569. widths: deviceSizes,
  570. kind: 'w'
  571. };
  572. }
  573. const widths = [
  574. ...new Set(// > This means that most OLED screens that say they are 3x resolution,
  575. // > are actually 3x in the green color, but only 1.5x in the red and
  576. // > blue colors. Showing a 3x resolution image in the app vs a 2x
  577. // > resolution image will be visually the same, though the 3x image
  578. // > takes significantly more data. Even true 3x resolution screens are
  579. // > wasteful as the human eye cannot see that level of detail without
  580. // > something like a magnifying glass.
  581. // https://blog.twitter.com/engineering/en_us/topics/infrastructure/2019/capping-image-fidelity-on-ultra-high-resolution-devices.html
  582. [
  583. width,
  584. width * 2 /*, width * 3*/
  585. ].map((w)=>allSizes.find((p)=>p >= w) || allSizes[allSizes.length - 1])),
  586. ];
  587. return {
  588. widths,
  589. kind: 'x'
  590. };
  591. }
  592. function generateImgAttrs({ config , src , unoptimized , layout , width , quality , sizes , loader }) {
  593. if (unoptimized) {
  594. return {
  595. src,
  596. srcSet: undefined,
  597. sizes: undefined
  598. };
  599. }
  600. const { widths , kind } = getWidths(config, width, layout, sizes);
  601. const last = widths.length - 1;
  602. return {
  603. sizes: !sizes && kind === 'w' ? '100vw' : sizes,
  604. srcSet: widths.map((w, i)=>`${loader({
  605. config,
  606. src,
  607. quality,
  608. width: w
  609. })} ${kind === 'w' ? w : i + 1}${kind}`).join(', '),
  610. // It's intended to keep `src` the last attribute because React updates
  611. // attributes in order. If we keep `src` the first one, Safari will
  612. // immediately start to fetch `src`, before `sizes` and `srcSet` are even
  613. // updated by React. That causes multiple unnecessary requests if `srcSet`
  614. // and `sizes` are defined.
  615. // This bug cannot be reproduced in Chrome or Firefox.
  616. src: loader({
  617. config,
  618. src,
  619. quality,
  620. width: widths[last]
  621. })
  622. };
  623. }
  624. function getInt(x) {
  625. if (typeof x === 'number') {
  626. return x;
  627. }
  628. if (typeof x === 'string') {
  629. return parseInt(x, 10);
  630. }
  631. return undefined;
  632. }
  633. function defaultImageLoader(loaderProps) {
  634. var ref;
  635. const loaderKey = ((ref = loaderProps.config) == null ? void 0 : ref.loader) || 'default';
  636. const load = loaders.get(loaderKey);
  637. if (load) {
  638. return load(loaderProps);
  639. }
  640. throw new Error(`Unknown "loader" found in "next.config.js". Expected: ${_imageConfig.VALID_LOADERS.join(', ')}. Received: ${loaderKey}`);
  641. }
  642. // See https://stackoverflow.com/q/39777833/266535 for why we use this ref
  643. // handler instead of the img's onLoad attribute.
  644. function handleLoading(img, src, layout, placeholder, onLoadingCompleteRef, setBlurComplete) {
  645. if (!img || img.src === emptyDataURL || img['data-loaded-src'] === src) {
  646. return;
  647. }
  648. img['data-loaded-src'] = src;
  649. const p = 'decode' in img ? img.decode() : Promise.resolve();
  650. p.catch(()=>{}).then(()=>{
  651. if (!img.parentNode) {
  652. // Exit early in case of race condition:
  653. // - onload() is called
  654. // - decode() is called but incomplete
  655. // - unmount is called
  656. // - decode() completes
  657. return;
  658. }
  659. loadedImageURLs.add(src);
  660. if (placeholder === 'blur') {
  661. setBlurComplete(true);
  662. }
  663. if (onLoadingCompleteRef == null ? void 0 : onLoadingCompleteRef.current) {
  664. const { naturalWidth , naturalHeight } = img;
  665. // Pass back read-only primitive values but not the
  666. // underlying DOM element because it could be misused.
  667. onLoadingCompleteRef.current({
  668. naturalWidth,
  669. naturalHeight
  670. });
  671. }
  672. if (process.env.NODE_ENV !== 'production') {
  673. var ref;
  674. if ((ref = img.parentElement) == null ? void 0 : ref.parentElement) {
  675. const parent = getComputedStyle(img.parentElement.parentElement);
  676. if (!parent.position) {
  677. // The parent has not been rendered to the dom yet and therefore it has no position. Skip the warnings for such cases.
  678. } else if (layout === 'responsive' && parent.display === 'flex') {
  679. (0, _utils).warnOnce(`Image with src "${src}" may not render properly as a child of a flex container. Consider wrapping the image with a div to configure the width.`);
  680. } else if (layout === 'fill' && parent.position !== 'relative' && parent.position !== 'fixed' && parent.position !== 'absolute') {
  681. (0, _utils).warnOnce(`Image with src "${src}" may not render properly with a parent using position:"${parent.position}". Consider changing the parent style to position:"relative" with a width and height.`);
  682. }
  683. }
  684. }
  685. });
  686. }
  687. const ImageElement = (_param)=>{
  688. var { imgAttributes , heightInt , widthInt , qualityInt , layout , className , imgStyle , blurStyle , isLazy , placeholder , loading , srcString , config , unoptimized , loader , onLoadingCompleteRef , setBlurComplete , setIntersection , onLoad , onError , isVisible , noscriptSizes } = _param, rest = _object_without_properties_loose(_param, [
  689. "imgAttributes",
  690. "heightInt",
  691. "widthInt",
  692. "qualityInt",
  693. "layout",
  694. "className",
  695. "imgStyle",
  696. "blurStyle",
  697. "isLazy",
  698. "placeholder",
  699. "loading",
  700. "srcString",
  701. "config",
  702. "unoptimized",
  703. "loader",
  704. "onLoadingCompleteRef",
  705. "setBlurComplete",
  706. "setIntersection",
  707. "onLoad",
  708. "onError",
  709. "isVisible",
  710. "noscriptSizes"
  711. ]);
  712. loading = isLazy ? 'lazy' : loading;
  713. return /*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/ _react.default.createElement("img", Object.assign({}, rest, imgAttributes, {
  714. decoding: "async",
  715. "data-nimg": layout,
  716. className: className,
  717. style: _extends({}, imgStyle, blurStyle),
  718. ref: (0, _react).useCallback((img)=>{
  719. if (process.env.NODE_ENV !== 'production') {
  720. if (img && !srcString) {
  721. console.error(`Image is missing required "src" property:`, img);
  722. }
  723. }
  724. setIntersection(img);
  725. if (img == null ? void 0 : img.complete) {
  726. handleLoading(img, srcString, layout, placeholder, onLoadingCompleteRef, setBlurComplete);
  727. }
  728. }, [
  729. setIntersection,
  730. srcString,
  731. layout,
  732. placeholder,
  733. onLoadingCompleteRef,
  734. setBlurComplete,
  735. ]),
  736. onLoad: (event)=>{
  737. const img = event.currentTarget;
  738. handleLoading(img, srcString, layout, placeholder, onLoadingCompleteRef, setBlurComplete);
  739. if (onLoad) {
  740. onLoad(event);
  741. }
  742. },
  743. onError: (event)=>{
  744. if (placeholder === 'blur') {
  745. // If the real image fails to load, this will still remove the placeholder.
  746. setBlurComplete(true);
  747. }
  748. if (onError) {
  749. onError(event);
  750. }
  751. }
  752. })), (isLazy || placeholder === 'blur') && /*#__PURE__*/ _react.default.createElement("noscript", null, /*#__PURE__*/ _react.default.createElement("img", Object.assign({}, rest, generateImgAttrs({
  753. config,
  754. src: srcString,
  755. unoptimized,
  756. layout,
  757. width: widthInt,
  758. quality: qualityInt,
  759. sizes: noscriptSizes,
  760. loader
  761. }), {
  762. decoding: "async",
  763. "data-nimg": layout,
  764. style: imgStyle,
  765. className: className,
  766. // @ts-ignore - TODO: upgrade to `@types/react@17`
  767. loading: loading
  768. }))));
  769. };
  770. if ((typeof exports.default === 'function' || (typeof exports.default === 'object' && exports.default !== null)) && typeof exports.default.__esModule === 'undefined') {
  771. Object.defineProperty(exports.default, '__esModule', { value: true });
  772. Object.assign(exports.default, exports);
  773. module.exports = exports.default;
  774. }
  775. //# sourceMappingURL=image.js.map