@shopify/hydrogen-react 2024.7.2 → 2024.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/browser-dev/Image.mjs +29 -11
  2. package/dist/browser-dev/Image.mjs.map +1 -1
  3. package/dist/browser-dev/ProductPrice.mjs +30 -10
  4. package/dist/browser-dev/ProductPrice.mjs.map +1 -1
  5. package/dist/browser-dev/analytics-schema-custom-storefront-customer-tracking.mjs +18 -7
  6. package/dist/browser-dev/analytics-schema-custom-storefront-customer-tracking.mjs.map +1 -1
  7. package/dist/browser-dev/analytics-schema-trekkie-storefront-page-view.mjs +1 -1
  8. package/dist/browser-dev/analytics-schema-trekkie-storefront-page-view.mjs.map +1 -1
  9. package/dist/browser-dev/packages/hydrogen-react/package.json.mjs +5 -0
  10. package/dist/browser-dev/packages/hydrogen-react/package.json.mjs.map +1 -0
  11. package/dist/browser-prod/Image.mjs +29 -11
  12. package/dist/browser-prod/Image.mjs.map +1 -1
  13. package/dist/browser-prod/ProductPrice.mjs +30 -10
  14. package/dist/browser-prod/ProductPrice.mjs.map +1 -1
  15. package/dist/browser-prod/analytics-schema-custom-storefront-customer-tracking.mjs +18 -7
  16. package/dist/browser-prod/analytics-schema-custom-storefront-customer-tracking.mjs.map +1 -1
  17. package/dist/browser-prod/analytics-schema-trekkie-storefront-page-view.mjs +1 -1
  18. package/dist/browser-prod/analytics-schema-trekkie-storefront-page-view.mjs.map +1 -1
  19. package/dist/browser-prod/packages/hydrogen-react/package.json.mjs +5 -0
  20. package/dist/browser-prod/packages/hydrogen-react/package.json.mjs.map +1 -0
  21. package/dist/node-dev/Image.js +29 -11
  22. package/dist/node-dev/Image.js.map +1 -1
  23. package/dist/node-dev/Image.mjs +29 -11
  24. package/dist/node-dev/Image.mjs.map +1 -1
  25. package/dist/node-dev/ProductPrice.js +30 -10
  26. package/dist/node-dev/ProductPrice.js.map +1 -1
  27. package/dist/node-dev/ProductPrice.mjs +30 -10
  28. package/dist/node-dev/ProductPrice.mjs.map +1 -1
  29. package/dist/node-dev/analytics-schema-custom-storefront-customer-tracking.js +18 -7
  30. package/dist/node-dev/analytics-schema-custom-storefront-customer-tracking.js.map +1 -1
  31. package/dist/node-dev/analytics-schema-custom-storefront-customer-tracking.mjs +18 -7
  32. package/dist/node-dev/analytics-schema-custom-storefront-customer-tracking.mjs.map +1 -1
  33. package/dist/node-dev/analytics-schema-trekkie-storefront-page-view.js +1 -1
  34. package/dist/node-dev/analytics-schema-trekkie-storefront-page-view.js.map +1 -1
  35. package/dist/node-dev/analytics-schema-trekkie-storefront-page-view.mjs +1 -1
  36. package/dist/node-dev/analytics-schema-trekkie-storefront-page-view.mjs.map +1 -1
  37. package/dist/node-dev/packages/hydrogen-react/package.json.js +5 -0
  38. package/dist/node-dev/packages/hydrogen-react/package.json.js.map +1 -0
  39. package/dist/node-dev/packages/hydrogen-react/package.json.mjs +5 -0
  40. package/dist/node-dev/packages/hydrogen-react/package.json.mjs.map +1 -0
  41. package/dist/node-prod/Image.js +29 -11
  42. package/dist/node-prod/Image.js.map +1 -1
  43. package/dist/node-prod/Image.mjs +29 -11
  44. package/dist/node-prod/Image.mjs.map +1 -1
  45. package/dist/node-prod/ProductPrice.js +30 -10
  46. package/dist/node-prod/ProductPrice.js.map +1 -1
  47. package/dist/node-prod/ProductPrice.mjs +30 -10
  48. package/dist/node-prod/ProductPrice.mjs.map +1 -1
  49. package/dist/node-prod/analytics-schema-custom-storefront-customer-tracking.js +18 -7
  50. package/dist/node-prod/analytics-schema-custom-storefront-customer-tracking.js.map +1 -1
  51. package/dist/node-prod/analytics-schema-custom-storefront-customer-tracking.mjs +18 -7
  52. package/dist/node-prod/analytics-schema-custom-storefront-customer-tracking.mjs.map +1 -1
  53. package/dist/node-prod/analytics-schema-trekkie-storefront-page-view.js +1 -1
  54. package/dist/node-prod/analytics-schema-trekkie-storefront-page-view.js.map +1 -1
  55. package/dist/node-prod/analytics-schema-trekkie-storefront-page-view.mjs +1 -1
  56. package/dist/node-prod/analytics-schema-trekkie-storefront-page-view.mjs.map +1 -1
  57. package/dist/node-prod/packages/hydrogen-react/package.json.js +5 -0
  58. package/dist/node-prod/packages/hydrogen-react/package.json.js.map +1 -0
  59. package/dist/node-prod/packages/hydrogen-react/package.json.mjs +5 -0
  60. package/dist/node-prod/packages/hydrogen-react/package.json.mjs.map +1 -0
  61. package/dist/types/Image.d.ts +4 -1
  62. package/dist/types/analytics-types.d.ts +15 -3
  63. package/dist/umd/hydrogen-react.dev.js +78 -29
  64. package/dist/umd/hydrogen-react.dev.js.map +1 -1
  65. package/dist/umd/hydrogen-react.prod.js +6 -6
  66. package/dist/umd/hydrogen-react.prod.js.map +1 -1
  67. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"Image.mjs","sources":["../../src/Image.tsx"],"sourcesContent":["/* eslint-disable eslint-comments/disable-enable-pair */\n/* eslint-disable @typescript-eslint/explicit-function-return-type */\n/* eslint-disable hydrogen/prefer-image-component */\nimport * as React from 'react';\nimport type {PartialDeep} from 'type-fest';\nimport type {Image as ImageType} from './storefront-api-types.js';\n\n/*\n * An optional prop you can use to change the\n * default srcSet generation behaviour\n */\ntype SrcSetOptions = {\n /** The number of sizes to generate */\n intervals: number;\n /** The smallest image size */\n startingWidth: number;\n /** The increment by which to increase for each size, in pixels */\n incrementSize: number;\n /** The size used for placeholder fallback images */\n placeholderWidth: number;\n};\n\ntype NormalizedProps = {\n alt: string;\n aspectRatio: string | undefined;\n height: string;\n src: string | undefined;\n width: string;\n};\n\nexport type LoaderParams = {\n /** The base URL of the image */\n src?: ImageType['url'];\n /** The URL param that controls width */\n width?: number;\n /** The URL param that controls height */\n height?: number;\n /** The URL param that controls the cropping region */\n crop?: Crop;\n};\n\nexport type Loader = (params: LoaderParams) => string;\n\n/*\n * @TODO: Expand to include focal point support; and/or switch this to be an SF API type\n */\ntype Crop = 'center' | 'top' | 'bottom' | 'left' | 'right';\n\nexport type HydrogenImageProps = React.ComponentPropsWithRef<'img'> &\n HydrogenImageBaseProps;\n\ntype HydrogenImageBaseProps = {\n /** The aspect ratio of the image, in the format of `width/height`.\n *\n * @example\n * ```\n * <Image data={productImage} aspectRatio=\"4/5\" />\n * ```\n */\n aspectRatio?: string;\n /** The crop position of the image.\n *\n * @remarks\n * In the event that AspectRatio is set, without specifying a crop,\n * the Shopify CDN won't return the expected image.\n *\n * @defaultValue `center`\n */\n crop?: Crop;\n /** Data mapping to the [Storefront API `Image`](https://shopify.dev/docs/api/storefront/2024-07/objects/Image) object. Must be an Image object.\n *\n * @example\n * ```\n * import {IMAGE_FRAGMENT, Image} from '@shopify/hydrogen';\n *\n * export const IMAGE_QUERY = `#graphql\n * ${IMAGE_FRAGMENT}\n * query {\n * product {\n * featuredImage {\n * ...Image\n * }\n * }\n * }`\n *\n * <Image\n * data={productImage}\n * sizes=\"(min-width: 45em) 50vw, 100vw\"\n * aspectRatio=\"4/5\"\n * />\n * ```\n *\n * Image: {@link https://shopify.dev/api/storefront/reference/common-objects/image}\n */\n data?: PartialDeep<ImageType, {recurseIntoArrays: true}>;\n /** A function that returns a URL string for an image.\n *\n * @remarks\n * By default, this uses Shopify’s CDN {@link https://cdn.shopify.com/} but you can provide\n * your own function to use a another provider, as long as they support URL based image transformations.\n */\n loader?: Loader;\n /** An optional prop you can use to change the default srcSet generation behaviour */\n srcSetOptions?: SrcSetOptions;\n};\n\n/**\n * A Storefront API GraphQL fragment that can be used to query for an image.\n */\nexport const IMAGE_FRAGMENT = `#graphql\n fragment Image on Image {\n altText\n url\n width\n height\n }\n`;\n\n/**\n * Hydrogen’s Image component is a wrapper around the HTML image element.\n * It supports the same props as the HTML `img` element, but automatically\n * generates the srcSet and sizes attributes for you. For most use cases,\n * you’ll want to set the `aspectRatio` prop to ensure the image is sized\n * correctly.\n *\n * @remarks\n * - `decoding` is set to `async` by default.\n * - `loading` is set to `lazy` by default.\n * - `alt` will automatically be set to the `altText` from the Storefront API if passed in the `data` prop\n * - `src` will automatically be set to the `url` from the Storefront API if passed in the `data` prop\n *\n * @example\n * A responsive image with a 4:5 aspect ratio:\n * ```\n * <Image\n * data={product.featuredImage}\n * aspectRatio=\"4/5\"\n * sizes=\"(min-width: 45em) 40vw, 100vw\"\n * />\n * ```\n * @example\n * A fixed size image:\n * ```\n * <Image\n * data={product.featuredImage}\n * width={100}\n * height={100}\n * />\n * ```\n *\n * {@link https://shopify.dev/docs/api/hydrogen-react/components/image}\n */\nexport const Image = React.forwardRef<HTMLImageElement, HydrogenImageProps>(\n (\n {\n alt,\n aspectRatio,\n crop = 'center',\n data,\n decoding = 'async',\n height = 'auto',\n loader = shopifyLoader,\n loading = 'lazy',\n sizes,\n src,\n srcSetOptions = {\n intervals: 15,\n startingWidth: 200,\n incrementSize: 200,\n placeholderWidth: 100,\n },\n width = '100%',\n ...passthroughProps\n },\n ref,\n ) => {\n /*\n * Gets normalized values for width, height from data prop\n */\n const normalizedData = React.useMemo(() => {\n /* Only use data width if height is also set */\n const dataWidth: number | undefined =\n data?.width && data?.height ? data?.width : undefined;\n\n const dataHeight: number | undefined =\n data?.width && data?.height ? data?.height : undefined;\n\n return {\n width: dataWidth,\n height: dataHeight,\n unitsMatch: Boolean(unitsMatch(dataWidth, dataHeight)),\n };\n }, [data]);\n\n /*\n * Gets normalized values for width, height, src, alt, and aspectRatio props\n * supporting the presence of `data` in addition to flat props.\n */\n const normalizedProps = React.useMemo(() => {\n const nWidthProp: string | number = width || '100%';\n const widthParts = getUnitValueParts(nWidthProp.toString());\n const nWidth = `${widthParts.number}${widthParts.unit}`;\n\n const autoHeight = height === undefined || height === null;\n const heightParts = autoHeight\n ? null\n : getUnitValueParts(height.toString());\n\n const fixedHeight = heightParts\n ? `${heightParts.number}${heightParts.unit}`\n : '';\n\n const nHeight = autoHeight ? 'auto' : fixedHeight;\n\n const nSrc: string | undefined = src || data?.url;\n\n if (__HYDROGEN_DEV__ && !nSrc) {\n console.warn(\n `No src or data.url provided to Image component.`,\n passthroughProps?.key || '',\n );\n }\n\n const nAlt: string = data?.altText && !alt ? data?.altText : alt || '';\n\n const nAspectRatio: string | undefined = aspectRatio\n ? aspectRatio\n : normalizedData.unitsMatch\n ? [\n getNormalizedFixedUnit(normalizedData.width),\n getNormalizedFixedUnit(normalizedData.height),\n ].join('/')\n : undefined;\n\n return {\n width: nWidth,\n height: nHeight,\n src: nSrc,\n alt: nAlt,\n aspectRatio: nAspectRatio,\n };\n }, [\n width,\n height,\n src,\n data,\n alt,\n aspectRatio,\n normalizedData,\n passthroughProps?.key,\n ]);\n\n const {intervals, startingWidth, incrementSize, placeholderWidth} =\n srcSetOptions;\n\n /*\n * This function creates an array of widths to be used in srcSet\n */\n const imageWidths = React.useMemo(() => {\n return generateImageWidths(\n width,\n intervals,\n startingWidth,\n incrementSize,\n );\n }, [width, intervals, startingWidth, incrementSize]);\n\n const fixedWidth = isFixedWidth(normalizedProps.width);\n\n if (__HYDROGEN_DEV__ && !sizes && !fixedWidth) {\n console.warn(\n [\n 'No sizes prop provided to Image component,',\n 'you may be loading unnecessarily large images.',\n `Image used is ${\n src || data?.url || passthroughProps?.key || 'unknown'\n }`,\n ].join(' '),\n );\n }\n\n /*\n * We check to see whether the image is fixed width or not,\n * if fixed, we still provide a srcSet, but only to account for\n * different pixel densities.\n */\n if (fixedWidth) {\n return (\n <FixedWidthImage\n aspectRatio={aspectRatio}\n crop={crop}\n decoding={decoding}\n height={height}\n imageWidths={imageWidths}\n loader={loader}\n loading={loading}\n normalizedProps={normalizedProps}\n passthroughProps={passthroughProps}\n ref={ref}\n width={width}\n />\n );\n } else {\n return (\n <FluidImage\n aspectRatio={aspectRatio}\n crop={crop}\n decoding={decoding}\n imageWidths={imageWidths}\n loader={loader}\n loading={loading}\n normalizedProps={normalizedProps}\n passthroughProps={passthroughProps}\n placeholderWidth={placeholderWidth}\n ref={ref}\n sizes={sizes}\n />\n );\n }\n },\n);\n\ntype FixedImageExludedProps =\n | 'data'\n | 'loader'\n | 'loaderOptions'\n | 'sizes'\n | 'srcSetOptions'\n | 'widths';\n\ntype FixedWidthImageProps = Omit<HydrogenImageProps, FixedImageExludedProps> & {\n loader: Loader;\n passthroughProps: React.ImgHTMLAttributes<HTMLImageElement>;\n normalizedProps: NormalizedProps;\n imageWidths: number[];\n ref: React.Ref<HTMLImageElement>;\n};\n\nconst FixedWidthImage = React.forwardRef<\n HTMLImageElement,\n FixedWidthImageProps\n>(\n (\n {\n aspectRatio,\n crop,\n decoding,\n height,\n imageWidths,\n loader = shopifyLoader,\n loading,\n normalizedProps,\n passthroughProps,\n width,\n },\n ref,\n ) => {\n const fixed = React.useMemo(() => {\n const intWidth: number | undefined = getNormalizedFixedUnit(width);\n const intHeight: number | undefined = getNormalizedFixedUnit(height);\n\n /*\n * The aspect ratio for fixed width images is taken from the explicitly\n * set prop, but if that's not present, and both width and height are\n * set, we calculate the aspect ratio from the width and height—as\n * long as they share the same unit type (e.g. both are 'px').\n */\n const fixedAspectRatio = aspectRatio\n ? aspectRatio\n : unitsMatch(normalizedProps.width, normalizedProps.height)\n ? [intWidth, intHeight].join('/')\n : normalizedProps.aspectRatio\n ? normalizedProps.aspectRatio\n : undefined;\n\n /*\n * The Sizes Array generates an array of all of the parts\n * that make up the srcSet, including the width, height, and crop\n */\n const sizesArray =\n imageWidths === undefined\n ? undefined\n : generateSizes(imageWidths, fixedAspectRatio, crop);\n\n const fixedHeight = intHeight\n ? intHeight\n : fixedAspectRatio && intWidth\n ? intWidth * (parseAspectRatio(fixedAspectRatio) ?? 1)\n : undefined;\n\n const srcSet = generateSrcSet(normalizedProps.src, sizesArray, loader);\n const src = loader({\n src: normalizedProps.src,\n width: intWidth,\n height: fixedHeight,\n crop: normalizedProps.height === 'auto' ? undefined : crop,\n });\n\n return {\n width: intWidth,\n aspectRatio: fixedAspectRatio,\n height: fixedHeight,\n srcSet,\n src,\n };\n }, [\n aspectRatio,\n crop,\n height,\n imageWidths,\n loader,\n normalizedProps,\n width,\n ]);\n\n return (\n <img\n ref={ref}\n alt={normalizedProps.alt}\n decoding={decoding}\n height={fixed.height}\n loading={loading}\n src={fixed.src}\n srcSet={fixed.srcSet}\n width={fixed.width}\n style={{\n aspectRatio: fixed.aspectRatio,\n ...passthroughProps.style,\n }}\n {...passthroughProps}\n />\n );\n },\n);\n\ntype FluidImageExcludedProps =\n | 'data'\n | 'width'\n | 'height'\n | 'loader'\n | 'loaderOptions'\n | 'srcSetOptions';\n\ntype FluidImageProps = Omit<HydrogenImageProps, FluidImageExcludedProps> & {\n imageWidths: number[];\n loader: Loader;\n normalizedProps: NormalizedProps;\n passthroughProps: React.ImgHTMLAttributes<HTMLImageElement>;\n placeholderWidth: number;\n ref: React.Ref<HTMLImageElement>;\n};\n\nconst FluidImage = React.forwardRef<HTMLImageElement, FluidImageProps>(\n (\n {\n crop,\n decoding,\n imageWidths,\n loader = shopifyLoader,\n loading,\n normalizedProps,\n passthroughProps,\n placeholderWidth,\n sizes,\n },\n ref,\n ) => {\n const fluid = React.useMemo(() => {\n const sizesArray =\n imageWidths === undefined\n ? undefined\n : generateSizes(imageWidths, normalizedProps.aspectRatio, crop);\n\n const placeholderHeight =\n normalizedProps.aspectRatio && placeholderWidth\n ? placeholderWidth *\n (parseAspectRatio(normalizedProps.aspectRatio) ?? 1)\n : undefined;\n\n const srcSet = generateSrcSet(normalizedProps.src, sizesArray, loader);\n\n const src = loader({\n src: normalizedProps.src,\n width: placeholderWidth,\n height: placeholderHeight,\n crop,\n });\n\n return {\n placeholderHeight,\n srcSet,\n src,\n };\n }, [crop, imageWidths, loader, normalizedProps, placeholderWidth]);\n\n return (\n <img\n ref={ref}\n alt={normalizedProps.alt}\n decoding={decoding}\n height={fluid.placeholderHeight}\n loading={loading}\n sizes={sizes}\n src={fluid.src}\n srcSet={fluid.srcSet}\n width={placeholderWidth}\n {...passthroughProps}\n style={{\n width: normalizedProps.width,\n aspectRatio: normalizedProps.aspectRatio,\n ...passthroughProps.style,\n }}\n />\n );\n },\n);\n\n/**\n * The shopifyLoader function is a simple utility function that takes a src, width,\n * height, and crop and returns a string that can be used as the src for an image.\n * It can be used with the Hydrogen Image component or with the next/image component.\n * (or any others that accept equivalent configuration)\n * @param src - The source URL of the image, e.g. `https://cdn.shopify.com/static/sample-images/garnished.jpeg`\n * @param width - The width of the image, e.g. `100`\n * @param height - The height of the image, e.g. `100`\n * @param crop - The crop of the image, e.g. `center`\n * @returns A Shopify image URL with the correct query parameters, e.g. `https://cdn.shopify.com/static/sample-images/garnished.jpeg?width=100&height=100&crop=center`\n *\n * @example\n * ```\n * shopifyLoader({\n * src: 'https://cdn.shopify.com/static/sample-images/garnished.jpeg',\n * width: 100,\n * height: 100,\n * crop: 'center',\n * })\n * ```\n */\nexport function shopifyLoader({src, width, height, crop}: LoaderParams) {\n if (!src) {\n return '';\n }\n\n const url = new URL(src);\n\n if (width) {\n url.searchParams.append('width', Math.round(width).toString());\n }\n\n if (height) {\n url.searchParams.append('height', Math.round(height).toString());\n }\n\n if (crop) {\n url.searchParams.append('crop', crop);\n }\n return url.href;\n}\n\n/**\n * Checks whether the width and height share the same unit type\n * @param width - The width of the image, e.g. 100% | 10px\n * @param height - The height of the image, e.g. auto | 100px\n * @returns Whether the width and height share the same unit type (boolean)\n */\nfunction unitsMatch(\n width: string | number = '100%',\n height: string | number = 'auto',\n): boolean {\n return (\n getUnitValueParts(width.toString()).unit ===\n getUnitValueParts(height.toString()).unit\n );\n}\n\n/**\n * Given a CSS size, returns the unit and number parts of the value\n * @param value - The CSS size, e.g. 100px\n * @returns The unit and number parts of the value, e.g. \\{unit: 'px', number: 100\\}\n */\nfunction getUnitValueParts(value: string): {unit: string; number: number} {\n const unit = value.replace(/[0-9.]/g, '');\n const number = parseFloat(value.replace(unit, ''));\n\n return {\n unit: unit === '' ? (number === undefined ? 'auto' : 'px') : unit,\n number,\n };\n}\n\n/**\n * Given a value, returns the width of the image as an integer in pixels\n * @param value - The width of the image, e.g. 16px | 1rem | 1em | 16\n * @returns The width of the image in pixels, e.g. 16, or undefined if the value is not a fixed unit\n */\nfunction getNormalizedFixedUnit(value?: string | number): number | undefined {\n if (value === undefined) {\n return;\n }\n\n const {unit, number} = getUnitValueParts(value.toString());\n\n switch (unit) {\n case 'em':\n return number * 16;\n case 'rem':\n return number * 16;\n case 'px':\n return number;\n case '':\n return number;\n default:\n return;\n }\n}\n\n/**\n * This function checks whether a width is fixed or not.\n * @param width - The width of the image, e.g. 100 | '100px' | '100em' | '100rem'\n * @returns Whether the width is fixed or not\n */\nfunction isFixedWidth(width: string | number): boolean {\n const fixedEndings = /\\d(px|em|rem)$/;\n return (\n typeof width === 'number' ||\n (typeof width === 'string' && fixedEndings.test(width))\n );\n}\n\n/**\n * This function generates a srcSet for Shopify images.\n * @param src - The source URL of the image, e.g. https://cdn.shopify.com/static/sample-images/garnished.jpeg\n * @param sizesArray - An array of objects containing the `width`, `height`, and `crop` of the image, e.g. [\\{width: 200, height: 200, crop: 'center'\\}, \\{width: 400, height: 400, crop: 'center'\\}]\n * @param loader - A function that takes a Shopify image URL and returns a Shopify image URL with the correct query parameters\n * @returns A srcSet for Shopify images, e.g. 'https://cdn.shopify.com/static/sample-images/garnished.jpeg?width=200&height=200&crop=center 200w, https://cdn.shopify.com/static/sample-images/garnished.jpeg?width=400&height=400&crop=center 400w'\n */\nexport function generateSrcSet(\n src?: string,\n sizesArray?: Array<{width?: number; height?: number; crop?: Crop}>,\n loader: Loader = shopifyLoader,\n): string {\n if (!src) {\n return '';\n }\n\n if (sizesArray?.length === 0 || !sizesArray) {\n return src;\n }\n\n return sizesArray\n .map(\n (size, i) =>\n `${loader({\n src,\n width: size.width,\n height: size.height,\n crop: size.crop,\n })} ${sizesArray.length === 3 ? `${i + 1}x` : `${size.width ?? 0}w`}`,\n )\n .join(`, `);\n}\n\n/**\n * This function generates an array of sizes for Shopify images, for both fixed and responsive images.\n * @param width - The CSS width of the image\n * @param intervals - The number of intervals to generate\n * @param startingWidth - The starting width of the image\n * @param incrementSize - The size of each interval\n * @returns An array of widths\n */\nexport function generateImageWidths(\n width: string | number = '100%',\n intervals: number,\n startingWidth: number,\n incrementSize: number,\n): number[] {\n const responsive = Array.from(\n {length: intervals},\n (_, i) => i * incrementSize + startingWidth,\n );\n\n const fixed = Array.from(\n {length: 3},\n (_, i) => (i + 1) * (getNormalizedFixedUnit(width) ?? 0),\n );\n\n return isFixedWidth(width) ? fixed : responsive;\n}\n\n/**\n * Simple utility function to convert an aspect ratio CSS string to a decimal, currently only supports values like `1/1`, not `0.5`, or `auto`\n * @param aspectRatio - The aspect ratio of the image, e.g. `1/1`\n * @returns The aspect ratio as a number, e.g. `0.5`\n *\n * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio}\n */\nexport function parseAspectRatio(aspectRatio?: string): number | undefined {\n if (!aspectRatio) return;\n const [width, height] = aspectRatio.split('/');\n return 1 / (Number(width) / Number(height));\n}\n\n// Generate data needed for Imagery loader\nexport function generateSizes(\n imageWidths?: number[],\n aspectRatio?: string,\n crop: Crop = 'center',\n):\n | {\n width: number;\n height: number | undefined;\n crop: Crop;\n }[]\n | undefined {\n if (!imageWidths) return;\n const sizes = imageWidths.map((width: number) => {\n return {\n width,\n height: aspectRatio\n ? width * (parseAspectRatio(aspectRatio) ?? 1)\n : undefined,\n crop,\n };\n });\n return sizes;\n /*\n Given:\n ([100, 200], 1/1, 'center')\n Returns:\n [{width: 100, height: 100, crop: 'center'},\n {width: 200, height: 200, crop: 'center'}]\n */\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Image.mjs","sources":["../../src/Image.tsx"],"sourcesContent":["/* eslint-disable eslint-comments/disable-enable-pair */\n/* eslint-disable @typescript-eslint/explicit-function-return-type */\n/* eslint-disable hydrogen/prefer-image-component */\nimport * as React from 'react';\nimport type {PartialDeep} from 'type-fest';\nimport type {Image as ImageType} from './storefront-api-types.js';\n\n/*\n * An optional prop you can use to change the\n * default srcSet generation behaviour\n */\ntype SrcSetOptions = {\n /** The number of sizes to generate */\n intervals: number;\n /** The smallest image size */\n startingWidth: number;\n /** The increment by which to increase for each size, in pixels */\n incrementSize: number;\n /** The size used for placeholder fallback images */\n placeholderWidth: number;\n};\n\ntype NormalizedProps = {\n alt: string;\n aspectRatio: string | undefined;\n height: string;\n src: string | undefined;\n width: string;\n};\n\nexport type LoaderParams = {\n /** The base URL of the image */\n src?: ImageType['url'];\n /** The URL param that controls width */\n width?: number;\n /** The URL param that controls height */\n height?: number;\n /** The URL param that controls the cropping region */\n crop?: Crop;\n};\n\nexport type Loader = (params: LoaderParams) => string;\n\n/*\n * @TODO: Expand to include focal point support; and/or switch this to be an SF API type\n */\ntype Crop = 'center' | 'top' | 'bottom' | 'left' | 'right';\n\nexport type HydrogenImageProps = React.ComponentPropsWithRef<'img'> &\n HydrogenImageBaseProps;\n\ntype HydrogenImageBaseProps = {\n /** The aspect ratio of the image, in the format of `width/height`.\n *\n * @example\n * ```\n * <Image data={productImage} aspectRatio=\"4/5\" />\n * ```\n */\n aspectRatio?: string;\n /** The crop position of the image.\n *\n * @remarks\n * In the event that AspectRatio is set, without specifying a crop,\n * the Shopify CDN won't return the expected image.\n *\n * @defaultValue `center`\n */\n crop?: Crop;\n /** Data mapping to the [Storefront API `Image`](https://shopify.dev/docs/api/storefront/2024-07/objects/Image) object. Must be an Image object.\n *\n * @example\n * ```\n * import {IMAGE_FRAGMENT, Image} from '@shopify/hydrogen';\n *\n * export const IMAGE_QUERY = `#graphql\n * ${IMAGE_FRAGMENT}\n * query {\n * product {\n * featuredImage {\n * ...Image\n * }\n * }\n * }`\n *\n * <Image\n * data={productImage}\n * sizes=\"(min-width: 45em) 50vw, 100vw\"\n * aspectRatio=\"4/5\"\n * />\n * ```\n *\n * Image: {@link https://shopify.dev/api/storefront/reference/common-objects/image}\n */\n data?: PartialDeep<ImageType, {recurseIntoArrays: true}>;\n /** A function that returns a URL string for an image.\n *\n * @remarks\n * By default, this uses Shopify’s CDN {@link https://cdn.shopify.com/} but you can provide\n * your own function to use a another provider, as long as they support URL based image transformations.\n */\n loader?: Loader;\n /** An optional prop you can use to change the default srcSet generation behaviour */\n srcSetOptions?: SrcSetOptions;\n};\n\n/**\n * A Storefront API GraphQL fragment that can be used to query for an image.\n */\nexport const IMAGE_FRAGMENT = `#graphql\n fragment Image on Image {\n altText\n url\n width\n height\n }\n`;\n\n/**\n * Hydrogen’s Image component is a wrapper around the HTML image element.\n * It supports the same props as the HTML `img` element, but automatically\n * generates the srcSet and sizes attributes for you. For most use cases,\n * you’ll want to set the `aspectRatio` prop to ensure the image is sized\n * correctly.\n *\n * @remarks\n * - `decoding` is set to `async` by default.\n * - `loading` is set to `lazy` by default.\n * - `alt` will automatically be set to the `altText` from the Storefront API if passed in the `data` prop\n * - `src` will automatically be set to the `url` from the Storefront API if passed in the `data` prop\n *\n * @example\n * A responsive image with a 4:5 aspect ratio:\n * ```\n * <Image\n * data={product.featuredImage}\n * aspectRatio=\"4/5\"\n * sizes=\"(min-width: 45em) 40vw, 100vw\"\n * />\n * ```\n * @example\n * A fixed size image:\n * ```\n * <Image\n * data={product.featuredImage}\n * width={100}\n * height={100}\n * />\n * ```\n *\n * {@link https://shopify.dev/docs/api/hydrogen-react/components/image}\n */\nexport const Image = React.forwardRef<HTMLImageElement, HydrogenImageProps>(\n (\n {\n alt,\n aspectRatio,\n crop = 'center',\n data,\n decoding = 'async',\n height = 'auto',\n loader = shopifyLoader,\n loading = 'lazy',\n sizes,\n src,\n srcSetOptions = {\n intervals: 15,\n startingWidth: 200,\n incrementSize: 200,\n placeholderWidth: 100,\n },\n width = '100%',\n ...passthroughProps\n },\n ref,\n ) => {\n /*\n * Gets normalized values for width, height from data prop\n */\n const normalizedData = React.useMemo(() => {\n /* Only use data width if height is also set */\n const dataWidth: number | undefined =\n data?.width && data?.height ? data?.width : undefined;\n\n const dataHeight: number | undefined =\n data?.width && data?.height ? data?.height : undefined;\n\n return {\n width: dataWidth,\n height: dataHeight,\n unitsMatch: Boolean(unitsMatch(dataWidth, dataHeight)),\n };\n }, [data]);\n\n /*\n * Gets normalized values for width, height, src, alt, and aspectRatio props\n * supporting the presence of `data` in addition to flat props.\n */\n const normalizedProps = React.useMemo(() => {\n const nWidthProp: string | number = width || '100%';\n const widthParts = getUnitValueParts(nWidthProp.toString());\n const nWidth = `${widthParts.number}${widthParts.unit}`;\n\n const autoHeight = height === undefined || height === null;\n const heightParts = autoHeight\n ? null\n : getUnitValueParts(height.toString());\n\n const fixedHeight = heightParts\n ? `${heightParts.number}${heightParts.unit}`\n : '';\n\n const nHeight = autoHeight ? 'auto' : fixedHeight;\n\n const nSrc: string | undefined = src || data?.url;\n\n if (__HYDROGEN_DEV__ && !nSrc) {\n console.warn(\n `No src or data.url provided to Image component.`,\n passthroughProps?.key || '',\n );\n }\n\n const nAlt: string = data?.altText && !alt ? data?.altText : alt || '';\n\n const nAspectRatio: string | undefined = aspectRatio\n ? aspectRatio\n : normalizedData.unitsMatch\n ? [\n getNormalizedFixedUnit(normalizedData.width),\n getNormalizedFixedUnit(normalizedData.height),\n ].join('/')\n : undefined;\n\n return {\n width: nWidth,\n height: nHeight,\n src: nSrc,\n alt: nAlt,\n aspectRatio: nAspectRatio,\n };\n }, [\n width,\n height,\n src,\n data,\n alt,\n aspectRatio,\n normalizedData,\n passthroughProps?.key,\n ]);\n\n const {intervals, startingWidth, incrementSize, placeholderWidth} =\n srcSetOptions;\n\n /*\n * This function creates an array of widths to be used in srcSet\n */\n const imageWidths = React.useMemo(() => {\n return generateImageWidths(\n width,\n intervals,\n startingWidth,\n incrementSize,\n );\n }, [width, intervals, startingWidth, incrementSize]);\n\n const fixedWidth = isFixedWidth(normalizedProps.width);\n\n if (__HYDROGEN_DEV__ && !sizes && !fixedWidth) {\n console.warn(\n [\n 'No sizes prop provided to Image component,',\n 'you may be loading unnecessarily large images.',\n `Image used is ${\n src || data?.url || passthroughProps?.key || 'unknown'\n }`,\n ].join(' '),\n );\n }\n\n /*\n * We check to see whether the image is fixed width or not,\n * if fixed, we still provide a srcSet, but only to account for\n * different pixel densities.\n */\n if (fixedWidth) {\n return (\n <FixedWidthImage\n aspectRatio={aspectRatio}\n crop={crop}\n decoding={decoding}\n height={height}\n imageWidths={imageWidths}\n loader={loader}\n loading={loading}\n normalizedProps={normalizedProps}\n passthroughProps={passthroughProps}\n ref={ref}\n width={width}\n data={data}\n />\n );\n } else {\n return (\n <FluidImage\n aspectRatio={aspectRatio}\n crop={crop}\n decoding={decoding}\n imageWidths={imageWidths}\n loader={loader}\n loading={loading}\n normalizedProps={normalizedProps}\n passthroughProps={passthroughProps}\n placeholderWidth={placeholderWidth}\n ref={ref}\n sizes={sizes}\n data={data}\n />\n );\n }\n },\n);\n\ntype FixedImageExludedProps =\n | 'data'\n | 'loader'\n | 'loaderOptions'\n | 'sizes'\n | 'srcSetOptions'\n | 'widths';\n\ntype FixedWidthImageProps = Omit<HydrogenImageProps, FixedImageExludedProps> &\n Pick<HydrogenImageBaseProps, 'data'> & {\n loader: Loader;\n passthroughProps: React.ImgHTMLAttributes<HTMLImageElement>;\n normalizedProps: NormalizedProps;\n imageWidths: number[];\n ref: React.Ref<HTMLImageElement>;\n };\n\nconst FixedWidthImage = React.forwardRef<\n HTMLImageElement,\n FixedWidthImageProps\n>(\n (\n {\n aspectRatio,\n crop,\n decoding,\n height,\n imageWidths,\n loader = shopifyLoader,\n loading,\n normalizedProps,\n passthroughProps,\n width,\n data,\n },\n ref,\n ) => {\n const fixed = React.useMemo(() => {\n const intWidth: number | undefined = getNormalizedFixedUnit(width);\n const intHeight: number | undefined = getNormalizedFixedUnit(height);\n\n /*\n * The aspect ratio for fixed width images is taken from the explicitly\n * set prop, but if that's not present, and both width and height are\n * set, we calculate the aspect ratio from the width and height—as\n * long as they share the same unit type (e.g. both are 'px').\n */\n const fixedAspectRatio = aspectRatio\n ? aspectRatio\n : unitsMatch(normalizedProps.width, normalizedProps.height)\n ? [intWidth, intHeight].join('/')\n : normalizedProps.aspectRatio\n ? normalizedProps.aspectRatio\n : undefined;\n\n /*\n * The Sizes Array generates an array of all the parts\n * that make up the srcSet, including the width, height, and crop\n */\n const sizesArray =\n imageWidths === undefined\n ? undefined\n : generateSizes(imageWidths, fixedAspectRatio, crop, {\n width: data?.width ?? undefined,\n height: data?.height ?? undefined,\n });\n\n const fixedHeight = intHeight\n ? intHeight\n : fixedAspectRatio && intWidth\n ? intWidth * (parseAspectRatio(fixedAspectRatio) ?? 1)\n : undefined;\n\n const srcSet = generateSrcSet(normalizedProps.src, sizesArray, loader);\n const src = loader({\n src: normalizedProps.src,\n width: intWidth,\n height: fixedHeight,\n crop: normalizedProps.height === 'auto' ? undefined : crop,\n });\n\n return {\n width: intWidth,\n aspectRatio: fixedAspectRatio,\n height: fixedHeight,\n srcSet,\n src,\n };\n }, [\n aspectRatio,\n crop,\n data,\n height,\n imageWidths,\n loader,\n normalizedProps,\n width,\n ]);\n\n return (\n <img\n ref={ref}\n alt={normalizedProps.alt}\n decoding={decoding}\n height={fixed.height}\n loading={loading}\n src={fixed.src}\n srcSet={fixed.srcSet}\n width={fixed.width}\n style={{\n aspectRatio: fixed.aspectRatio,\n ...passthroughProps.style,\n }}\n {...passthroughProps}\n />\n );\n },\n);\n\ntype FluidImageExcludedProps =\n | 'data'\n | 'width'\n | 'height'\n | 'loader'\n | 'loaderOptions'\n | 'srcSetOptions';\n\ntype FluidImageProps = Omit<HydrogenImageProps, FluidImageExcludedProps> &\n Pick<HydrogenImageBaseProps, 'data'> & {\n imageWidths: number[];\n loader: Loader;\n normalizedProps: NormalizedProps;\n passthroughProps: React.ImgHTMLAttributes<HTMLImageElement>;\n placeholderWidth: number;\n ref: React.Ref<HTMLImageElement>;\n };\n\nconst FluidImage = React.forwardRef<HTMLImageElement, FluidImageProps>(\n (\n {\n crop,\n decoding,\n imageWidths,\n loader = shopifyLoader,\n loading,\n normalizedProps,\n passthroughProps,\n placeholderWidth,\n sizes,\n data,\n },\n ref,\n ) => {\n const fluid = React.useMemo(() => {\n const sizesArray =\n imageWidths === undefined\n ? undefined\n : generateSizes(imageWidths, normalizedProps.aspectRatio, crop, {\n width: data?.width ?? undefined,\n height: data?.height ?? undefined,\n });\n\n const placeholderHeight =\n normalizedProps.aspectRatio && placeholderWidth\n ? placeholderWidth *\n (parseAspectRatio(normalizedProps.aspectRatio) ?? 1)\n : undefined;\n\n const srcSet = generateSrcSet(normalizedProps.src, sizesArray, loader);\n\n const src = loader({\n src: normalizedProps.src,\n width: placeholderWidth,\n height: placeholderHeight,\n crop,\n });\n\n return {\n placeholderHeight,\n srcSet,\n src,\n };\n }, [crop, data, imageWidths, loader, normalizedProps, placeholderWidth]);\n\n return (\n <img\n ref={ref}\n alt={normalizedProps.alt}\n decoding={decoding}\n height={fluid.placeholderHeight}\n loading={loading}\n sizes={sizes}\n src={fluid.src}\n srcSet={fluid.srcSet}\n width={placeholderWidth}\n {...passthroughProps}\n style={{\n width: normalizedProps.width,\n aspectRatio: normalizedProps.aspectRatio,\n ...passthroughProps.style,\n }}\n />\n );\n },\n);\n\n/**\n * The shopifyLoader function is a simple utility function that takes a src, width,\n * height, and crop and returns a string that can be used as the src for an image.\n * It can be used with the Hydrogen Image component or with the next/image component.\n * (or any others that accept equivalent configuration)\n * @param src - The source URL of the image, e.g. `https://cdn.shopify.com/static/sample-images/garnished.jpeg`\n * @param width - The width of the image, e.g. `100`\n * @param height - The height of the image, e.g. `100`\n * @param crop - The crop of the image, e.g. `center`\n * @returns A Shopify image URL with the correct query parameters, e.g. `https://cdn.shopify.com/static/sample-images/garnished.jpeg?width=100&height=100&crop=center`\n *\n * @example\n * ```\n * shopifyLoader({\n * src: 'https://cdn.shopify.com/static/sample-images/garnished.jpeg',\n * width: 100,\n * height: 100,\n * crop: 'center',\n * })\n * ```\n */\nexport function shopifyLoader({src, width, height, crop}: LoaderParams) {\n if (!src) {\n return '';\n }\n\n const url = new URL(src);\n\n if (width) {\n url.searchParams.append('width', Math.round(width).toString());\n }\n\n if (height) {\n url.searchParams.append('height', Math.round(height).toString());\n }\n\n if (crop) {\n url.searchParams.append('crop', crop);\n }\n return url.href;\n}\n\n/**\n * Checks whether the width and height share the same unit type\n * @param width - The width of the image, e.g. 100% | 10px\n * @param height - The height of the image, e.g. auto | 100px\n * @returns Whether the width and height share the same unit type (boolean)\n */\nfunction unitsMatch(\n width: string | number = '100%',\n height: string | number = 'auto',\n): boolean {\n return (\n getUnitValueParts(width.toString()).unit ===\n getUnitValueParts(height.toString()).unit\n );\n}\n\n/**\n * Given a CSS size, returns the unit and number parts of the value\n * @param value - The CSS size, e.g. 100px\n * @returns The unit and number parts of the value, e.g. \\{unit: 'px', number: 100\\}\n */\nfunction getUnitValueParts(value: string): {unit: string; number: number} {\n const unit = value.replace(/[0-9.]/g, '');\n const number = parseFloat(value.replace(unit, ''));\n\n return {\n unit: unit === '' ? (number === undefined ? 'auto' : 'px') : unit,\n number,\n };\n}\n\n/**\n * Given a value, returns the width of the image as an integer in pixels\n * @param value - The width of the image, e.g. 16px | 1rem | 1em | 16\n * @returns The width of the image in pixels, e.g. 16, or undefined if the value is not a fixed unit\n */\nfunction getNormalizedFixedUnit(value?: string | number): number | undefined {\n if (value === undefined) {\n return;\n }\n\n const {unit, number} = getUnitValueParts(value.toString());\n\n switch (unit) {\n case 'em':\n return number * 16;\n case 'rem':\n return number * 16;\n case 'px':\n return number;\n case '':\n return number;\n default:\n return;\n }\n}\n\n/**\n * This function checks whether a width is fixed or not.\n * @param width - The width of the image, e.g. 100 | '100px' | '100em' | '100rem'\n * @returns Whether the width is fixed or not\n */\nfunction isFixedWidth(width: string | number): boolean {\n const fixedEndings = /\\d(px|em|rem)$/;\n return typeof width === 'number' || fixedEndings.test(width);\n}\n\n/**\n * This function generates a srcSet for Shopify images.\n * @param src - The source URL of the image, e.g. https://cdn.shopify.com/static/sample-images/garnished.jpeg\n * @param sizesArray - An array of objects containing the `width`, `height`, and `crop` of the image, e.g. [\\{width: 200, height: 200, crop: 'center'\\}, \\{width: 400, height: 400, crop: 'center'\\}]\n * @param loader - A function that takes a Shopify image URL and returns a Shopify image URL with the correct query parameters\n * @returns A srcSet for Shopify images, e.g. 'https://cdn.shopify.com/static/sample-images/garnished.jpeg?width=200&height=200&crop=center 200w, https://cdn.shopify.com/static/sample-images/garnished.jpeg?width=400&height=400&crop=center 400w'\n */\nexport function generateSrcSet(\n src?: string,\n sizesArray?: Array<{width?: number; height?: number; crop?: Crop}>,\n loader: Loader = shopifyLoader,\n): string {\n if (!src) {\n return '';\n }\n\n if (sizesArray?.length === 0 || !sizesArray) {\n return src;\n }\n\n return sizesArray\n .map(\n (size, i) =>\n `${loader({\n src,\n width: size.width,\n height: size.height,\n crop: size.crop,\n })} ${sizesArray.length === 3 ? `${i + 1}x` : `${size.width ?? 0}w`}`,\n )\n .join(`, `);\n}\n\n/**\n * This function generates an array of sizes for Shopify images, for both fixed and responsive images.\n * @param width - The CSS width of the image\n * @param intervals - The number of intervals to generate\n * @param startingWidth - The starting width of the image\n * @param incrementSize - The size of each interval\n * @returns An array of widths\n */\nexport function generateImageWidths(\n width: string | number = '100%',\n intervals: number,\n startingWidth: number,\n incrementSize: number,\n): number[] {\n const responsive = Array.from(\n {length: intervals},\n (_, i) => i * incrementSize + startingWidth,\n );\n\n const fixed = Array.from(\n {length: 3},\n (_, i) => (i + 1) * (getNormalizedFixedUnit(width) ?? 0),\n );\n\n return isFixedWidth(width) ? fixed : responsive;\n}\n\n/**\n * Simple utility function to convert an aspect ratio CSS string to a decimal, currently only supports values like `1/1`, not `0.5`, or `auto`\n * @param aspectRatio - The aspect ratio of the image, e.g. `1/1`\n * @returns The aspect ratio as a number, e.g. `0.5`\n *\n * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio}\n */\nexport function parseAspectRatio(aspectRatio?: string): number | undefined {\n if (!aspectRatio) return;\n const [width, height] = aspectRatio.split('/');\n return 1 / (Number(width) / Number(height));\n}\n\n// Generate data needed for Imagery loader\nexport function generateSizes(\n imageWidths?: number[],\n aspectRatio?: string,\n crop: Crop = 'center',\n sourceDimensions?: {width?: number; height?: number},\n):\n | {\n width: number;\n height: number | undefined;\n crop: Crop;\n }[]\n | undefined {\n if (!imageWidths) return;\n return imageWidths\n .map((width: number) => {\n return {\n width,\n height: aspectRatio\n ? width * (parseAspectRatio(aspectRatio) ?? 1)\n : undefined,\n crop,\n };\n })\n .filter(({width, height}) => {\n if (sourceDimensions?.width && width > sourceDimensions.width) {\n return false;\n }\n\n if (\n sourceDimensions?.height &&\n height &&\n height > sourceDimensions.height\n ) {\n return false;\n }\n\n return true;\n });\n /*\n Given:\n ([100, 200], 1/1, 'center')\n Returns:\n [{width: 100, height: 100, crop: 'center'},\n {width: 200, height: 200, crop: 'center'}]\n */\n}\n"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@ import { jsx } from "react/jsx-runtime";
2
2
  import { Money } from "./Money.mjs";
3
3
  import { flattenConnection } from "./flatten-connection.mjs";
4
4
  function ProductPrice(props) {
5
- var _a, _b, _c, _d, _e, _f;
5
+ var _a, _b, _c, _d, _e, _f, _g;
6
6
  const {
7
7
  priceType = "regular",
8
8
  variantId,
@@ -18,28 +18,48 @@ function ProductPrice(props) {
18
18
  const variant = variantId ? flattenConnection((product == null ? void 0 : product.variants) ?? {}).find(
19
19
  (variant2) => (variant2 == null ? void 0 : variant2.id) === variantId
20
20
  ) ?? null : null;
21
+ const variantPriceProperty = valueType === "max" ? "maxVariantPrice" : "minVariantPrice";
21
22
  if (priceType === "compareAt") {
22
23
  if (variantId && variant) {
23
- if (((_a = variant.compareAtPriceV2) == null ? void 0 : _a.amount) === ((_b = variant.priceV2) == null ? void 0 : _b.amount)) {
24
- return null;
24
+ if (variant.compareAtPriceV2) {
25
+ console.error(
26
+ "<ProductPrice> `compareAtPriceV2` is deprecated. Use `compareAtPrice` instead."
27
+ );
25
28
  }
26
- price = variant.compareAtPriceV2;
27
- } else if (valueType === "max") {
28
- price = (_c = product == null ? void 0 : product.compareAtPriceRange) == null ? void 0 : _c.maxVariantPrice;
29
+ price = variant.compareAtPrice ?? variant.compareAtPriceV2;
30
+ } else {
31
+ price = (_a = product == null ? void 0 : product.compareAtPriceRange) == null ? void 0 : _a[variantPriceProperty];
32
+ }
33
+ let priceAsNumber;
34
+ if (variantId && variant) {
35
+ priceAsNumber = parseFloat(
36
+ ((_b = variant.price) == null ? void 0 : _b.amount) ?? ((_c = variant.priceV2) == null ? void 0 : _c.amount) ?? "0"
37
+ );
29
38
  } else {
30
- price = (_d = product == null ? void 0 : product.compareAtPriceRange) == null ? void 0 : _d.minVariantPrice;
39
+ priceAsNumber = parseFloat(
40
+ ((_e = (_d = product == null ? void 0 : product.priceRange) == null ? void 0 : _d[variantPriceProperty]) == null ? void 0 : _e.amount) ?? "0"
41
+ );
42
+ }
43
+ const compareAtPriceAsNumber = parseFloat((price == null ? void 0 : price.amount) ?? "0");
44
+ if (priceAsNumber >= compareAtPriceAsNumber) {
45
+ return null;
31
46
  }
32
47
  } else {
33
48
  if (variantId && variant) {
34
- price = variant.priceV2;
49
+ if (variant.priceV2) {
50
+ console.error(
51
+ "<ProductPrice> `priceV2` is deprecated. Use `price` instead."
52
+ );
53
+ }
54
+ price = variant.price ?? variant.priceV2;
35
55
  if (valueType === "unit") {
36
56
  price = variant.unitPrice;
37
57
  measurement = variant.unitPriceMeasurement;
38
58
  }
39
59
  } else if (valueType === "max") {
40
- price = (_e = product.priceRange) == null ? void 0 : _e.maxVariantPrice;
60
+ price = (_f = product.priceRange) == null ? void 0 : _f.maxVariantPrice;
41
61
  } else {
42
- price = (_f = product.priceRange) == null ? void 0 : _f.minVariantPrice;
62
+ price = (_g = product.priceRange) == null ? void 0 : _g.minVariantPrice;
43
63
  }
44
64
  }
45
65
  if (!price) {
@@ -1 +1 @@
1
- {"version":3,"file":"ProductPrice.mjs","sources":["../../src/ProductPrice.tsx"],"sourcesContent":["import type {\n MoneyV2,\n UnitPriceMeasurement,\n Product,\n} from './storefront-api-types.js';\nimport {Money, type MoneyProps, type MoneyPropsBase} from './Money.js';\nimport type {PartialDeep} from 'type-fest';\nimport {flattenConnection} from './flatten-connection.js';\n\nexport interface ProductPriceProps {\n /** A Storefront API [Product object](https://shopify.dev/api/storefront/reference/products/product). */\n data: PartialDeep<Product, {recurseIntoArrays: true}>;\n /** The type of price. Valid values: `regular` (default) or `compareAt`. */\n priceType?: 'regular' | 'compareAt';\n /** The type of value. Valid values: `min` (default), `max` or `unit`. */\n valueType?: 'max' | 'min' | 'unit';\n /** The ID of the variant. */\n variantId?: string;\n}\n\n/**\n * The `ProductPrice` component renders a `Money` component with the product\n * [`priceRange`](https://shopify.dev/api/storefront/reference/products/productpricerange)'s `maxVariantPrice` or `minVariantPrice`, for either the regular price or compare at price range.\n */\nexport function ProductPrice<\n ComponentGeneric extends React.ElementType = 'div',\n>(\n props: ProductPriceProps &\n Omit<MoneyProps<ComponentGeneric>, 'data' | 'measurement'>,\n): JSX.Element | null {\n const {\n priceType = 'regular',\n variantId,\n valueType = 'min',\n data: product,\n ...passthroughProps\n } = props;\n\n if (product == null) {\n throw new Error(`<ProductPrice/> requires a product as the 'data' prop`);\n }\n\n let price: Partial<MoneyV2> | undefined | null;\n let measurement: Partial<UnitPriceMeasurement> | undefined | null;\n\n const variant = variantId\n ? flattenConnection(product?.variants ?? {}).find(\n (variant) => variant?.id === variantId,\n ) ?? null\n : null;\n\n if (priceType === 'compareAt') {\n if (variantId && variant) {\n if (variant.compareAtPriceV2?.amount === variant.priceV2?.amount) {\n return null;\n }\n price = variant.compareAtPriceV2;\n } else if (valueType === 'max') {\n price = product?.compareAtPriceRange?.maxVariantPrice;\n } else {\n price = product?.compareAtPriceRange?.minVariantPrice;\n }\n } else {\n if (variantId && variant) {\n price = variant.priceV2;\n if (valueType === 'unit') {\n price = variant.unitPrice;\n measurement = variant.unitPriceMeasurement;\n }\n } else if (valueType === 'max') {\n price = product.priceRange?.maxVariantPrice;\n } else {\n price = product.priceRange?.minVariantPrice;\n }\n }\n\n if (!price) {\n return null;\n }\n\n if (measurement) {\n return (\n <Money {...passthroughProps} data={price} measurement={measurement} />\n );\n }\n\n return <Money {...passthroughProps} data={price} />;\n}\n\n// This is only for documenation purposes, and it is not used in the code.\nexport interface ProductPricePropsForDocs<\n AsType extends React.ElementType = 'div',\n> extends Omit<MoneyPropsBase<AsType>, 'data' | 'measurement'>,\n ProductPriceProps {}\n"],"names":["variant"],"mappings":";;;AAwBO,SAAS,aAGd,OAEoB;;AACd,QAAA;AAAA,IACJ,YAAY;AAAA,IACZ;AAAA,IACA,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,GAAG;AAAA,EACD,IAAA;AAEJ,MAAI,WAAW,MAAM;AACb,UAAA,IAAI,MAAM,uDAAuD;AAAA,EACzE;AAEI,MAAA;AACA,MAAA;AAEJ,QAAM,UAAU,YACZ,mBAAkB,mCAAS,aAAY,CAAE,CAAA,EAAE;AAAA,IACzC,CAACA,cAAYA,qCAAS,QAAO;AAAA,EAAA,KAC1B,OACL;AAEJ,MAAI,cAAc,aAAa;AAC7B,QAAI,aAAa,SAAS;AACxB,YAAI,aAAQ,qBAAR,mBAA0B,cAAW,aAAQ,YAAR,mBAAiB,SAAQ;AACzD,eAAA;AAAA,MACT;AACA,cAAQ,QAAQ;AAAA,IAAA,WACP,cAAc,OAAO;AAC9B,eAAQ,wCAAS,wBAAT,mBAA8B;AAAA,IAAA,OACjC;AACL,eAAQ,wCAAS,wBAAT,mBAA8B;AAAA,IACxC;AAAA,EAAA,OACK;AACL,QAAI,aAAa,SAAS;AACxB,cAAQ,QAAQ;AAChB,UAAI,cAAc,QAAQ;AACxB,gBAAQ,QAAQ;AAChB,sBAAc,QAAQ;AAAA,MACxB;AAAA,IAAA,WACS,cAAc,OAAO;AAC9B,eAAQ,aAAQ,eAAR,mBAAoB;AAAA,IAAA,OACvB;AACL,eAAQ,aAAQ,eAAR,mBAAoB;AAAA,IAC9B;AAAA,EACF;AAEA,MAAI,CAAC,OAAO;AACH,WAAA;AAAA,EACT;AAEA,MAAI,aAAa;AACf,+BACG,OAAO,EAAA,GAAG,kBAAkB,MAAM,OAAO,YAA0B,CAAA;AAAA,EAExE;AAEA,SAAQ,oBAAA,OAAA,EAAO,GAAG,kBAAkB,MAAM,MAAO,CAAA;AACnD;"}
1
+ {"version":3,"file":"ProductPrice.mjs","sources":["../../src/ProductPrice.tsx"],"sourcesContent":["import type {\n MoneyV2,\n UnitPriceMeasurement,\n Product,\n} from './storefront-api-types.js';\nimport {Money, type MoneyProps, type MoneyPropsBase} from './Money.js';\nimport type {PartialDeep} from 'type-fest';\nimport {flattenConnection} from './flatten-connection.js';\n\nexport interface ProductPriceProps {\n /** A Storefront API [Product object](https://shopify.dev/api/storefront/reference/products/product). */\n data: PartialDeep<Product, {recurseIntoArrays: true}>;\n /** The type of price. Valid values: `regular` (default) or `compareAt`. */\n priceType?: 'regular' | 'compareAt';\n /** The type of value. Valid values: `min` (default), `max` or `unit`. */\n valueType?: 'max' | 'min' | 'unit';\n /** The ID of the variant. */\n variantId?: string;\n}\n\n/**\n * The `ProductPrice` component renders a `Money` component with the product\n * [`priceRange`](https://shopify.dev/api/storefront/reference/products/productpricerange)'s `maxVariantPrice` or `minVariantPrice`, for either the regular price or compare at price range.\n */\nexport function ProductPrice<\n ComponentGeneric extends React.ElementType = 'div',\n>(\n props: ProductPriceProps &\n Omit<MoneyProps<ComponentGeneric>, 'data' | 'measurement'>,\n): JSX.Element | null {\n const {\n priceType = 'regular',\n variantId,\n valueType = 'min',\n data: product,\n ...passthroughProps\n } = props;\n\n if (product == null) {\n throw new Error(`<ProductPrice/> requires a product as the 'data' prop`);\n }\n\n let price: Partial<MoneyV2> | undefined | null;\n let measurement: Partial<UnitPriceMeasurement> | undefined | null;\n\n const variant = variantId\n ? flattenConnection(product?.variants ?? {}).find(\n (variant) => variant?.id === variantId,\n ) ?? null\n : null;\n\n /**\n * @deprecated (Next major release) Stop using compareAtPriceV2 and priceV2\n */\n const variantPriceProperty =\n valueType === 'max' ? 'maxVariantPrice' : 'minVariantPrice';\n\n if (priceType === 'compareAt') {\n if (variantId && variant) {\n if (variant.compareAtPriceV2) {\n console.error(\n '<ProductPrice> `compareAtPriceV2` is deprecated. Use `compareAtPrice` instead.',\n );\n }\n\n price = variant.compareAtPrice ?? variant.compareAtPriceV2;\n } else {\n price = product?.compareAtPriceRange?.[variantPriceProperty];\n }\n\n let priceAsNumber: number;\n if (variantId && variant) {\n priceAsNumber = parseFloat(\n variant.price?.amount ?? variant.priceV2?.amount ?? '0',\n );\n } else {\n priceAsNumber = parseFloat(\n product?.priceRange?.[variantPriceProperty]?.amount ?? '0',\n );\n }\n\n const compareAtPriceAsNumber = parseFloat(price?.amount ?? '0');\n\n if (priceAsNumber >= compareAtPriceAsNumber) {\n return null;\n }\n } else {\n if (variantId && variant) {\n if (variant.priceV2) {\n console.error(\n '<ProductPrice> `priceV2` is deprecated. Use `price` instead.',\n );\n }\n\n price = variant.price ?? variant.priceV2;\n if (valueType === 'unit') {\n price = variant.unitPrice;\n measurement = variant.unitPriceMeasurement;\n }\n } else if (valueType === 'max') {\n price = product.priceRange?.maxVariantPrice;\n } else {\n price = product.priceRange?.minVariantPrice;\n }\n }\n\n if (!price) {\n return null;\n }\n\n if (measurement) {\n return (\n <Money {...passthroughProps} data={price} measurement={measurement} />\n );\n }\n\n return <Money {...passthroughProps} data={price} />;\n}\n\n// This is only for documenation purposes, and it is not used in the code.\nexport interface ProductPricePropsForDocs<\n AsType extends React.ElementType = 'div',\n> extends Omit<MoneyPropsBase<AsType>, 'data' | 'measurement'>,\n ProductPriceProps {}\n"],"names":["variant"],"mappings":";;;AAwBO,SAAS,aAGd,OAEoB;;AACd,QAAA;AAAA,IACJ,YAAY;AAAA,IACZ;AAAA,IACA,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,GAAG;AAAA,EACD,IAAA;AAEJ,MAAI,WAAW,MAAM;AACb,UAAA,IAAI,MAAM,uDAAuD;AAAA,EACzE;AAEI,MAAA;AACA,MAAA;AAEJ,QAAM,UAAU,YACZ,mBAAkB,mCAAS,aAAY,CAAE,CAAA,EAAE;AAAA,IACzC,CAACA,cAAYA,qCAAS,QAAO;AAAA,EAAA,KAC1B,OACL;AAKE,QAAA,uBACJ,cAAc,QAAQ,oBAAoB;AAE5C,MAAI,cAAc,aAAa;AAC7B,QAAI,aAAa,SAAS;AACxB,UAAI,QAAQ,kBAAkB;AACpB,gBAAA;AAAA,UACN;AAAA,QAAA;AAAA,MAEJ;AAEQ,cAAA,QAAQ,kBAAkB,QAAQ;AAAA,IAAA,OACrC;AACG,eAAA,wCAAS,wBAAT,mBAA+B;AAAA,IACzC;AAEI,QAAA;AACJ,QAAI,aAAa,SAAS;AACR,sBAAA;AAAA,UACd,aAAQ,UAAR,mBAAe,aAAU,aAAQ,YAAR,mBAAiB,WAAU;AAAA,MAAA;AAAA,IACtD,OACK;AACW,sBAAA;AAAA,UACd,8CAAS,eAAT,mBAAsB,0BAAtB,mBAA6C,WAAU;AAAA,MAAA;AAAA,IAE3D;AAEA,UAAM,yBAAyB,YAAW,+BAAO,WAAU,GAAG;AAE9D,QAAI,iBAAiB,wBAAwB;AACpC,aAAA;AAAA,IACT;AAAA,EAAA,OACK;AACL,QAAI,aAAa,SAAS;AACxB,UAAI,QAAQ,SAAS;AACX,gBAAA;AAAA,UACN;AAAA,QAAA;AAAA,MAEJ;AAEQ,cAAA,QAAQ,SAAS,QAAQ;AACjC,UAAI,cAAc,QAAQ;AACxB,gBAAQ,QAAQ;AAChB,sBAAc,QAAQ;AAAA,MACxB;AAAA,IAAA,WACS,cAAc,OAAO;AAC9B,eAAQ,aAAQ,eAAR,mBAAoB;AAAA,IAAA,OACvB;AACL,eAAQ,aAAQ,eAAR,mBAAoB;AAAA,IAC9B;AAAA,EACF;AAEA,MAAI,CAAC,OAAO;AACH,WAAA;AAAA,EACT;AAEA,MAAI,aAAa;AACf,+BACG,OAAO,EAAA,GAAG,kBAAkB,MAAM,OAAO,YAA0B,CAAA;AAAA,EAExE;AAEA,SAAQ,oBAAA,OAAA,EAAO,GAAG,kBAAkB,MAAM,MAAO,CAAA;AACnD;"}
@@ -1,7 +1,8 @@
1
1
  import { AnalyticsPageType, ShopifySalesChannel } from "./analytics-constants.mjs";
2
2
  import { schemaWrapper, addDataIf, parseGid } from "./analytics-utils.mjs";
3
3
  import { buildUUID } from "./cookies-utils.mjs";
4
- const SCHEMA_ID = "custom_storefront_customer_tracking/1.0";
4
+ import { version } from "./packages/hydrogen-react/package.json.mjs";
5
+ const SCHEMA_ID = "custom_storefront_customer_tracking/1.2";
5
6
  const PAGE_RENDERED_EVENT_NAME = "page_rendered";
6
7
  const COLLECTION_PAGE_RENDERED_EVENT_NAME = "collection_page_rendered";
7
8
  const PRODUCT_PAGE_RENDERED_EVENT_NAME = "product_page_rendered";
@@ -39,7 +40,10 @@ function pageView(payload) {
39
40
  {
40
41
  event_name: COLLECTION_PAGE_RENDERED_EVENT_NAME,
41
42
  ...additionalPayload,
42
- collection_name: pageViewPayload.collectionHandle
43
+ collection_name: pageViewPayload.collectionHandle,
44
+ collection_id: parseInt(
45
+ parseGid(pageViewPayload.collectionId).id
46
+ )
43
47
  },
44
48
  formatPayload(pageViewPayload)
45
49
  )
@@ -106,7 +110,8 @@ function collectionView(payload) {
106
110
  {
107
111
  event_name: COLLECTION_PAGE_RENDERED_EVENT_NAME,
108
112
  ...additionalPayload,
109
- collection_name: pageViewPayload.collectionHandle
113
+ collection_name: pageViewPayload.collectionHandle,
114
+ collection_id: parseInt(parseGid(pageViewPayload.collectionId).id)
110
115
  },
111
116
  formatPayload(pageViewPayload)
112
117
  )
@@ -174,10 +179,10 @@ function addToCart(payload) {
174
179
  function formatPayload(payload) {
175
180
  return {
176
181
  source: payload.shopifySalesChannel || ShopifySalesChannel.headless,
177
- hydrogenSubchannelId: payload.storefrontId || "0",
182
+ asset_version_id: payload.assetVersionId || version,
183
+ hydrogenSubchannelId: payload.storefrontId || payload.hydrogenSubchannelId || "0",
178
184
  is_persistent_cookie: payload.hasUserConsent,
179
- ccpa_enforced: false,
180
- gdpr_enforced: false,
185
+ deprecated_visit_token: payload.visitToken,
181
186
  unique_token: payload.uniqueToken,
182
187
  event_time: Date.now(),
183
188
  event_id: buildUUID(),
@@ -187,7 +192,13 @@ function formatPayload(payload) {
187
192
  navigation_type: payload.navigationType,
188
193
  navigation_api: payload.navigationApi,
189
194
  shop_id: parseInt(parseGid(payload.shopId).id),
190
- currency: payload.currency
195
+ currency: payload.currency,
196
+ ccpa_enforced: payload.ccpaEnforced || false,
197
+ gdpr_enforced: payload.gdprEnforced || false,
198
+ gdpr_enforced_as_string: payload.gdprEnforced ? "true" : "false",
199
+ analytics_allowed: payload.analyticsAllowed || false,
200
+ marketing_allowed: payload.marketingAllowed || false,
201
+ sale_of_data_allowed: payload.saleOfDataAllowed || false
191
202
  };
192
203
  }
193
204
  function formatProductPayload(products) {
@@ -1 +1 @@
1
- {"version":3,"file":"analytics-schema-custom-storefront-customer-tracking.mjs","sources":["../../src/analytics-schema-custom-storefront-customer-tracking.ts"],"sourcesContent":["import {\n ShopifyAnalyticsPayload,\n ShopifyPageViewPayload,\n ShopifyAddToCartPayload,\n ShopifyMonorailPayload,\n ShopifyAnalyticsProduct,\n ShopifyMonorailEvent,\n} from './analytics-types.js';\nimport {AnalyticsPageType, ShopifySalesChannel} from './analytics-constants.js';\nimport {addDataIf, schemaWrapper, parseGid} from './analytics-utils.js';\nimport {buildUUID} from './cookies-utils.js';\n\nconst SCHEMA_ID = 'custom_storefront_customer_tracking/1.0';\nconst PAGE_RENDERED_EVENT_NAME = 'page_rendered';\nconst COLLECTION_PAGE_RENDERED_EVENT_NAME = 'collection_page_rendered';\nconst PRODUCT_PAGE_RENDERED_EVENT_NAME = 'product_page_rendered';\nconst PRODUCT_ADDED_TO_CART_EVENT_NAME = 'product_added_to_cart';\nconst SEARCH_SUBMITTED_EVENT_NAME = 'search_submitted';\n\nfunction prepareAdditionalPayload(\n payload: ShopifyPageViewPayload,\n): Pick<ShopifyMonorailPayload, 'canonical_url' | 'customer_id'> {\n return {\n canonical_url: payload.canonicalUrl || payload.url,\n customer_id: parseInt(parseGid(payload.customerId).id || '0'),\n };\n}\n\n// Send the page view event to the Monorail server.\n// It also sends additional page view events based on the page type.\nexport function pageView(\n payload: ShopifyPageViewPayload,\n): ShopifyMonorailEvent[] {\n const pageViewPayload = payload;\n const additionalPayload = prepareAdditionalPayload(pageViewPayload);\n\n const pageType = pageViewPayload.pageType;\n const pageViewEvents = [];\n\n pageViewEvents.push(\n schemaWrapper(\n SCHEMA_ID,\n addDataIf(\n {\n event_name: PAGE_RENDERED_EVENT_NAME,\n ...additionalPayload,\n },\n formatPayload(pageViewPayload),\n ),\n ),\n );\n\n switch (pageType) {\n case AnalyticsPageType.collection:\n pageViewEvents.push(\n schemaWrapper(\n SCHEMA_ID,\n addDataIf(\n {\n event_name: COLLECTION_PAGE_RENDERED_EVENT_NAME,\n ...additionalPayload,\n collection_name: pageViewPayload.collectionHandle,\n },\n formatPayload(pageViewPayload),\n ),\n ),\n );\n break;\n case AnalyticsPageType.product:\n pageViewEvents.push(\n schemaWrapper(\n SCHEMA_ID,\n addDataIf(\n {\n event_name: PRODUCT_PAGE_RENDERED_EVENT_NAME,\n ...additionalPayload,\n products: formatProductPayload(pageViewPayload.products),\n total_value: pageViewPayload.totalValue,\n },\n formatPayload(pageViewPayload),\n ),\n ),\n );\n break;\n case AnalyticsPageType.search:\n pageViewEvents.push(\n schemaWrapper(\n SCHEMA_ID,\n addDataIf(\n {\n event_name: SEARCH_SUBMITTED_EVENT_NAME,\n ...additionalPayload,\n search_string: pageViewPayload.searchString,\n },\n formatPayload(pageViewPayload),\n ),\n ),\n );\n break;\n }\n\n return pageViewEvents;\n}\n\n// Sends page view event to the Monorail server.\nexport function pageView2(\n payload: ShopifyPageViewPayload,\n): ShopifyMonorailEvent[] {\n const pageViewPayload = payload;\n const additionalPayload = prepareAdditionalPayload(pageViewPayload);\n\n return [\n schemaWrapper(\n SCHEMA_ID,\n addDataIf(\n {\n event_name: PAGE_RENDERED_EVENT_NAME,\n ...additionalPayload,\n },\n formatPayload(pageViewPayload),\n ),\n ),\n ];\n}\n\n// Sends collection view event to the Monorail server.\nexport function collectionView(\n payload: ShopifyPageViewPayload,\n): ShopifyMonorailEvent[] {\n const pageViewPayload = payload;\n const additionalPayload = prepareAdditionalPayload(pageViewPayload);\n\n return [\n schemaWrapper(\n SCHEMA_ID,\n addDataIf(\n {\n event_name: COLLECTION_PAGE_RENDERED_EVENT_NAME,\n ...additionalPayload,\n collection_name: pageViewPayload.collectionHandle,\n },\n formatPayload(pageViewPayload),\n ),\n ),\n ];\n}\n\n// Sends product view event to the Monorail server.\nexport function productView(\n payload: ShopifyPageViewPayload,\n): ShopifyMonorailEvent[] {\n const pageViewPayload = payload;\n const additionalPayload = prepareAdditionalPayload(pageViewPayload);\n\n return [\n schemaWrapper(\n SCHEMA_ID,\n addDataIf(\n {\n event_name: PRODUCT_PAGE_RENDERED_EVENT_NAME,\n ...additionalPayload,\n products: formatProductPayload(pageViewPayload.products),\n total_value: pageViewPayload.totalValue,\n },\n formatPayload(pageViewPayload),\n ),\n ),\n ];\n}\n\n// Sends search view event to the Monorail server.\nexport function searchView(\n payload: ShopifyPageViewPayload,\n): ShopifyMonorailEvent[] {\n const pageViewPayload = payload;\n const additionalPayload = prepareAdditionalPayload(pageViewPayload);\n\n return [\n schemaWrapper(\n SCHEMA_ID,\n addDataIf(\n {\n event_name: SEARCH_SUBMITTED_EVENT_NAME,\n ...additionalPayload,\n search_string: pageViewPayload.searchString,\n },\n formatPayload(pageViewPayload),\n ),\n ),\n ];\n}\n\nexport function addToCart(\n payload: ShopifyAddToCartPayload,\n): ShopifyMonorailEvent[] {\n const addToCartPayload = payload;\n const cartToken = parseGid(addToCartPayload.cartId);\n const cart_token = cartToken?.id ? `${cartToken.id}` : null;\n return [\n schemaWrapper(\n SCHEMA_ID,\n addDataIf(\n {\n event_name: PRODUCT_ADDED_TO_CART_EVENT_NAME,\n customerId: addToCartPayload.customerId,\n cart_token,\n total_value: addToCartPayload.totalValue,\n products: formatProductPayload(addToCartPayload.products),\n customer_id: parseInt(\n parseGid(addToCartPayload.customerId).id || '0',\n ),\n },\n formatPayload(addToCartPayload),\n ),\n ),\n ];\n}\n\nfunction formatPayload(\n payload: ShopifyAnalyticsPayload,\n): ShopifyMonorailPayload {\n return {\n source: payload.shopifySalesChannel || ShopifySalesChannel.headless,\n hydrogenSubchannelId: payload.storefrontId || '0',\n\n is_persistent_cookie: payload.hasUserConsent,\n ccpa_enforced: false,\n gdpr_enforced: false,\n unique_token: payload.uniqueToken,\n event_time: Date.now(),\n event_id: buildUUID(),\n\n event_source_url: payload.url,\n referrer: payload.referrer,\n user_agent: payload.userAgent,\n navigation_type: payload.navigationType,\n navigation_api: payload.navigationApi,\n\n shop_id: parseInt(parseGid(payload.shopId).id),\n currency: payload.currency,\n };\n}\n\nfunction formatProductPayload(products?: ShopifyAnalyticsProduct[]): string[] {\n return products\n ? products.map((p: ShopifyAnalyticsProduct) => {\n const product = addDataIf(\n {\n variant_gid: p.variantGid,\n category: p.category,\n sku: p.sku,\n product_id: parseInt(parseGid(p.productGid).id),\n variant_id: parseInt(parseGid(p.variantGid).id),\n },\n {\n product_gid: p.productGid,\n name: p.name,\n variant: p.variantName || '',\n brand: p.brand,\n price: parseFloat(p.price),\n quantity: Number(p.quantity || 0),\n },\n );\n return JSON.stringify(product);\n })\n : [];\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"analytics-schema-custom-storefront-customer-tracking.mjs","sources":["../../src/analytics-schema-custom-storefront-customer-tracking.ts"],"sourcesContent":["import {\n ShopifyAnalyticsPayload,\n ShopifyPageViewPayload,\n ShopifyAddToCartPayload,\n ShopifyMonorailPayload,\n ShopifyAnalyticsProduct,\n ShopifyMonorailEvent,\n} from './analytics-types.js';\nimport {AnalyticsPageType, ShopifySalesChannel} from './analytics-constants.js';\nimport {addDataIf, schemaWrapper, parseGid} from './analytics-utils.js';\nimport {buildUUID} from './cookies-utils.js';\nimport {version} from '../package.json';\n\nconst SCHEMA_ID = 'custom_storefront_customer_tracking/1.2';\nconst PAGE_RENDERED_EVENT_NAME = 'page_rendered';\nconst COLLECTION_PAGE_RENDERED_EVENT_NAME = 'collection_page_rendered';\nconst PRODUCT_PAGE_RENDERED_EVENT_NAME = 'product_page_rendered';\nconst PRODUCT_ADDED_TO_CART_EVENT_NAME = 'product_added_to_cart';\nconst SEARCH_SUBMITTED_EVENT_NAME = 'search_submitted';\n\nfunction prepareAdditionalPayload(\n payload: ShopifyPageViewPayload,\n): Pick<ShopifyMonorailPayload, 'canonical_url' | 'customer_id'> {\n return {\n canonical_url: payload.canonicalUrl || payload.url,\n customer_id: parseInt(parseGid(payload.customerId).id || '0'),\n };\n}\n\n// Send the page view event to the Monorail server.\n// It also sends additional page view events based on the page type.\nexport function pageView(\n payload: ShopifyPageViewPayload,\n): ShopifyMonorailEvent[] {\n const pageViewPayload = payload;\n const additionalPayload = prepareAdditionalPayload(pageViewPayload);\n\n const pageType = pageViewPayload.pageType;\n const pageViewEvents = [];\n\n pageViewEvents.push(\n schemaWrapper(\n SCHEMA_ID,\n addDataIf(\n {\n event_name: PAGE_RENDERED_EVENT_NAME,\n ...additionalPayload,\n },\n formatPayload(pageViewPayload),\n ),\n ),\n );\n\n switch (pageType) {\n case AnalyticsPageType.collection:\n pageViewEvents.push(\n schemaWrapper(\n SCHEMA_ID,\n addDataIf(\n {\n event_name: COLLECTION_PAGE_RENDERED_EVENT_NAME,\n ...additionalPayload,\n collection_name: pageViewPayload.collectionHandle,\n collection_id: parseInt(\n parseGid(pageViewPayload.collectionId).id,\n ),\n },\n formatPayload(pageViewPayload),\n ),\n ),\n );\n break;\n case AnalyticsPageType.product:\n pageViewEvents.push(\n schemaWrapper(\n SCHEMA_ID,\n addDataIf(\n {\n event_name: PRODUCT_PAGE_RENDERED_EVENT_NAME,\n ...additionalPayload,\n products: formatProductPayload(pageViewPayload.products),\n total_value: pageViewPayload.totalValue,\n },\n formatPayload(pageViewPayload),\n ),\n ),\n );\n break;\n case AnalyticsPageType.search:\n pageViewEvents.push(\n schemaWrapper(\n SCHEMA_ID,\n addDataIf(\n {\n event_name: SEARCH_SUBMITTED_EVENT_NAME,\n ...additionalPayload,\n search_string: pageViewPayload.searchString,\n },\n formatPayload(pageViewPayload),\n ),\n ),\n );\n break;\n }\n\n return pageViewEvents;\n}\n\n// Sends page view event to the Monorail server.\nexport function pageView2(\n payload: ShopifyPageViewPayload,\n): ShopifyMonorailEvent[] {\n const pageViewPayload = payload;\n const additionalPayload = prepareAdditionalPayload(pageViewPayload);\n\n return [\n schemaWrapper(\n SCHEMA_ID,\n addDataIf(\n {\n event_name: PAGE_RENDERED_EVENT_NAME,\n ...additionalPayload,\n },\n formatPayload(pageViewPayload),\n ),\n ),\n ];\n}\n\n// Sends collection view event to the Monorail server.\nexport function collectionView(\n payload: ShopifyPageViewPayload,\n): ShopifyMonorailEvent[] {\n const pageViewPayload = payload;\n const additionalPayload = prepareAdditionalPayload(pageViewPayload);\n\n return [\n schemaWrapper(\n SCHEMA_ID,\n addDataIf(\n {\n event_name: COLLECTION_PAGE_RENDERED_EVENT_NAME,\n ...additionalPayload,\n collection_name: pageViewPayload.collectionHandle,\n collection_id: parseInt(parseGid(pageViewPayload.collectionId).id),\n },\n formatPayload(pageViewPayload),\n ),\n ),\n ];\n}\n\n// Sends product view event to the Monorail server.\nexport function productView(\n payload: ShopifyPageViewPayload,\n): ShopifyMonorailEvent[] {\n const pageViewPayload = payload;\n const additionalPayload = prepareAdditionalPayload(pageViewPayload);\n\n return [\n schemaWrapper(\n SCHEMA_ID,\n addDataIf(\n {\n event_name: PRODUCT_PAGE_RENDERED_EVENT_NAME,\n ...additionalPayload,\n products: formatProductPayload(pageViewPayload.products),\n total_value: pageViewPayload.totalValue,\n },\n formatPayload(pageViewPayload),\n ),\n ),\n ];\n}\n\n// Sends search view event to the Monorail server.\nexport function searchView(\n payload: ShopifyPageViewPayload,\n): ShopifyMonorailEvent[] {\n const pageViewPayload = payload;\n const additionalPayload = prepareAdditionalPayload(pageViewPayload);\n\n return [\n schemaWrapper(\n SCHEMA_ID,\n addDataIf(\n {\n event_name: SEARCH_SUBMITTED_EVENT_NAME,\n ...additionalPayload,\n search_string: pageViewPayload.searchString,\n },\n formatPayload(pageViewPayload),\n ),\n ),\n ];\n}\n\nexport function addToCart(\n payload: ShopifyAddToCartPayload,\n): ShopifyMonorailEvent[] {\n const addToCartPayload = payload;\n const cartToken = parseGid(addToCartPayload.cartId);\n const cart_token = cartToken?.id ? `${cartToken.id}` : null;\n return [\n schemaWrapper(\n SCHEMA_ID,\n addDataIf(\n {\n event_name: PRODUCT_ADDED_TO_CART_EVENT_NAME,\n customerId: addToCartPayload.customerId,\n cart_token,\n total_value: addToCartPayload.totalValue,\n products: formatProductPayload(addToCartPayload.products),\n customer_id: parseInt(\n parseGid(addToCartPayload.customerId).id || '0',\n ),\n },\n formatPayload(addToCartPayload),\n ),\n ),\n ];\n}\n\nfunction formatPayload(\n payload: ShopifyAnalyticsPayload,\n): ShopifyMonorailPayload {\n return {\n source: payload.shopifySalesChannel || ShopifySalesChannel.headless,\n asset_version_id: payload.assetVersionId || version,\n hydrogenSubchannelId:\n payload.storefrontId || payload.hydrogenSubchannelId || '0',\n\n is_persistent_cookie: payload.hasUserConsent,\n deprecated_visit_token: payload.visitToken,\n unique_token: payload.uniqueToken,\n event_time: Date.now(),\n event_id: buildUUID(),\n\n event_source_url: payload.url,\n referrer: payload.referrer,\n user_agent: payload.userAgent,\n navigation_type: payload.navigationType,\n navigation_api: payload.navigationApi,\n\n shop_id: parseInt(parseGid(payload.shopId).id),\n currency: payload.currency,\n\n ccpa_enforced: payload.ccpaEnforced || false,\n gdpr_enforced: payload.gdprEnforced || false,\n gdpr_enforced_as_string: payload.gdprEnforced ? 'true' : 'false',\n analytics_allowed: payload.analyticsAllowed || false,\n marketing_allowed: payload.marketingAllowed || false,\n sale_of_data_allowed: payload.saleOfDataAllowed || false,\n };\n}\n\nfunction formatProductPayload(products?: ShopifyAnalyticsProduct[]): string[] {\n return products\n ? products.map((p: ShopifyAnalyticsProduct) => {\n const product = addDataIf(\n {\n variant_gid: p.variantGid,\n category: p.category,\n sku: p.sku,\n product_id: parseInt(parseGid(p.productGid).id),\n variant_id: parseInt(parseGid(p.variantGid).id),\n },\n {\n product_gid: p.productGid,\n name: p.name,\n variant: p.variantName || '',\n brand: p.brand,\n price: parseFloat(p.price),\n quantity: Number(p.quantity || 0),\n },\n );\n return JSON.stringify(product);\n })\n : [];\n}\n"],"names":[],"mappings":"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"}
@@ -26,7 +26,7 @@ function formatPayload(payload) {
26
26
  return {
27
27
  appClientId: payload.shopifySalesChannel ? ShopifyAppId[payload.shopifySalesChannel] : ShopifyAppId.headless,
28
28
  isMerchantRequest: isMerchantRequest(payload.url),
29
- hydrogenSubchannelId: payload.storefrontId || "0",
29
+ hydrogenSubchannelId: payload.storefrontId || payload.hydrogenSubchannelId || "0",
30
30
  isPersistentCookie: payload.hasUserConsent,
31
31
  uniqToken: payload.uniqueToken,
32
32
  visitToken: payload.visitToken,
@@ -1 +1 @@
1
- {"version":3,"file":"analytics-schema-trekkie-storefront-page-view.mjs","sources":["../../src/analytics-schema-trekkie-storefront-page-view.ts"],"sourcesContent":["import {\n ShopifyPageViewPayload,\n ShopifyMonorailPayload,\n ShopifyMonorailEvent,\n} from './analytics-types.js';\nimport {ShopifyAppId} from './analytics-constants.js';\nimport {addDataIf, schemaWrapper, parseGid} from './analytics-utils.js';\nimport {buildUUID} from './cookies-utils.js';\n\nconst SCHEMA_ID = 'trekkie_storefront_page_view/1.4';\nconst OXYGEN_DOMAIN = 'myshopify.dev';\n\nexport function pageView(\n payload: ShopifyPageViewPayload,\n): ShopifyMonorailEvent[] {\n const pageViewPayload = payload;\n const {id, resource} = parseGid(pageViewPayload.resourceId);\n const resourceType = resource ? resource.toLowerCase() : undefined;\n return [\n schemaWrapper(\n SCHEMA_ID,\n addDataIf(\n {\n pageType: pageViewPayload.pageType,\n customerId: parseInt(parseGid(pageViewPayload.customerId).id || '0'),\n resourceType,\n resourceId: parseInt(id),\n },\n formatPayload(pageViewPayload),\n ),\n ),\n ];\n}\n\nfunction formatPayload(\n payload: ShopifyPageViewPayload,\n): ShopifyMonorailPayload {\n return {\n appClientId: payload.shopifySalesChannel\n ? ShopifyAppId[payload.shopifySalesChannel]\n : ShopifyAppId.headless,\n isMerchantRequest: isMerchantRequest(payload.url),\n hydrogenSubchannelId: payload.storefrontId || '0',\n\n isPersistentCookie: payload.hasUserConsent,\n uniqToken: payload.uniqueToken,\n visitToken: payload.visitToken,\n microSessionId: buildUUID(),\n microSessionCount: 1,\n\n url: payload.url,\n path: payload.path,\n search: payload.search,\n referrer: payload.referrer,\n title: payload.title,\n\n shopId: parseInt(parseGid(payload.shopId).id),\n currency: payload.currency,\n contentLanguage: payload.acceptedLanguage || 'en',\n };\n}\n\nfunction isMerchantRequest(url: string): boolean {\n if (typeof url !== 'string') {\n return false;\n }\n const hostname = new URL(url).hostname;\n if (hostname.indexOf(OXYGEN_DOMAIN) !== -1 || hostname === 'localhost') {\n return true;\n }\n return false;\n}\n"],"names":[],"mappings":";;;AASA,MAAM,YAAY;AAClB,MAAM,gBAAgB;AAEf,SAAS,SACd,SACwB;AACxB,QAAM,kBAAkB;AACxB,QAAM,EAAC,IAAI,SAAA,IAAY,SAAS,gBAAgB,UAAU;AAC1D,QAAM,eAAe,WAAW,SAAS,YAAgB,IAAA;AAClD,SAAA;AAAA,IACL;AAAA,MACE;AAAA,MACA;AAAA,QACE;AAAA,UACE,UAAU,gBAAgB;AAAA,UAC1B,YAAY,SAAS,SAAS,gBAAgB,UAAU,EAAE,MAAM,GAAG;AAAA,UACnE;AAAA,UACA,YAAY,SAAS,EAAE;AAAA,QACzB;AAAA,QACA,cAAc,eAAe;AAAA,MAC/B;AAAA,IACF;AAAA,EAAA;AAEJ;AAEA,SAAS,cACP,SACwB;AACjB,SAAA;AAAA,IACL,aAAa,QAAQ,sBACjB,aAAa,QAAQ,mBAAmB,IACxC,aAAa;AAAA,IACjB,mBAAmB,kBAAkB,QAAQ,GAAG;AAAA,IAChD,sBAAsB,QAAQ,gBAAgB;AAAA,IAE9C,oBAAoB,QAAQ;AAAA,IAC5B,WAAW,QAAQ;AAAA,IACnB,YAAY,QAAQ;AAAA,IACpB,gBAAgB,UAAU;AAAA,IAC1B,mBAAmB;AAAA,IAEnB,KAAK,QAAQ;AAAA,IACb,MAAM,QAAQ;AAAA,IACd,QAAQ,QAAQ;AAAA,IAChB,UAAU,QAAQ;AAAA,IAClB,OAAO,QAAQ;AAAA,IAEf,QAAQ,SAAS,SAAS,QAAQ,MAAM,EAAE,EAAE;AAAA,IAC5C,UAAU,QAAQ;AAAA,IAClB,iBAAiB,QAAQ,oBAAoB;AAAA,EAAA;AAEjD;AAEA,SAAS,kBAAkB,KAAsB;AAC3C,MAAA,OAAO,QAAQ,UAAU;AACpB,WAAA;AAAA,EACT;AACA,QAAM,WAAW,IAAI,IAAI,GAAG,EAAE;AAC9B,MAAI,SAAS,QAAQ,aAAa,MAAM,MAAM,aAAa,aAAa;AAC/D,WAAA;AAAA,EACT;AACO,SAAA;AACT;"}
1
+ {"version":3,"file":"analytics-schema-trekkie-storefront-page-view.mjs","sources":["../../src/analytics-schema-trekkie-storefront-page-view.ts"],"sourcesContent":["import {\n ShopifyPageViewPayload,\n ShopifyMonorailPayload,\n ShopifyMonorailEvent,\n} from './analytics-types.js';\nimport {ShopifyAppId} from './analytics-constants.js';\nimport {addDataIf, schemaWrapper, parseGid} from './analytics-utils.js';\nimport {buildUUID} from './cookies-utils.js';\n\nconst SCHEMA_ID = 'trekkie_storefront_page_view/1.4';\nconst OXYGEN_DOMAIN = 'myshopify.dev';\n\nexport function pageView(\n payload: ShopifyPageViewPayload,\n): ShopifyMonorailEvent[] {\n const pageViewPayload = payload;\n const {id, resource} = parseGid(pageViewPayload.resourceId);\n const resourceType = resource ? resource.toLowerCase() : undefined;\n return [\n schemaWrapper(\n SCHEMA_ID,\n addDataIf(\n {\n pageType: pageViewPayload.pageType,\n customerId: parseInt(parseGid(pageViewPayload.customerId).id || '0'),\n resourceType,\n resourceId: parseInt(id),\n },\n formatPayload(pageViewPayload),\n ),\n ),\n ];\n}\n\nfunction formatPayload(\n payload: ShopifyPageViewPayload,\n): ShopifyMonorailPayload {\n return {\n appClientId: payload.shopifySalesChannel\n ? ShopifyAppId[payload.shopifySalesChannel]\n : ShopifyAppId.headless,\n isMerchantRequest: isMerchantRequest(payload.url),\n hydrogenSubchannelId:\n payload.storefrontId || payload.hydrogenSubchannelId || '0',\n\n isPersistentCookie: payload.hasUserConsent,\n uniqToken: payload.uniqueToken,\n visitToken: payload.visitToken,\n microSessionId: buildUUID(),\n microSessionCount: 1,\n\n url: payload.url,\n path: payload.path,\n search: payload.search,\n referrer: payload.referrer,\n title: payload.title,\n\n shopId: parseInt(parseGid(payload.shopId).id),\n currency: payload.currency,\n contentLanguage: payload.acceptedLanguage || 'en',\n };\n}\n\nfunction isMerchantRequest(url: string): boolean {\n if (typeof url !== 'string') {\n return false;\n }\n const hostname = new URL(url).hostname;\n if (hostname.indexOf(OXYGEN_DOMAIN) !== -1 || hostname === 'localhost') {\n return true;\n }\n return false;\n}\n"],"names":[],"mappings":";;;AASA,MAAM,YAAY;AAClB,MAAM,gBAAgB;AAEf,SAAS,SACd,SACwB;AACxB,QAAM,kBAAkB;AACxB,QAAM,EAAC,IAAI,SAAA,IAAY,SAAS,gBAAgB,UAAU;AAC1D,QAAM,eAAe,WAAW,SAAS,YAAgB,IAAA;AAClD,SAAA;AAAA,IACL;AAAA,MACE;AAAA,MACA;AAAA,QACE;AAAA,UACE,UAAU,gBAAgB;AAAA,UAC1B,YAAY,SAAS,SAAS,gBAAgB,UAAU,EAAE,MAAM,GAAG;AAAA,UACnE;AAAA,UACA,YAAY,SAAS,EAAE;AAAA,QACzB;AAAA,QACA,cAAc,eAAe;AAAA,MAC/B;AAAA,IACF;AAAA,EAAA;AAEJ;AAEA,SAAS,cACP,SACwB;AACjB,SAAA;AAAA,IACL,aAAa,QAAQ,sBACjB,aAAa,QAAQ,mBAAmB,IACxC,aAAa;AAAA,IACjB,mBAAmB,kBAAkB,QAAQ,GAAG;AAAA,IAChD,sBACE,QAAQ,gBAAgB,QAAQ,wBAAwB;AAAA,IAE1D,oBAAoB,QAAQ;AAAA,IAC5B,WAAW,QAAQ;AAAA,IACnB,YAAY,QAAQ;AAAA,IACpB,gBAAgB,UAAU;AAAA,IAC1B,mBAAmB;AAAA,IAEnB,KAAK,QAAQ;AAAA,IACb,MAAM,QAAQ;AAAA,IACd,QAAQ,QAAQ;AAAA,IAChB,UAAU,QAAQ;AAAA,IAClB,OAAO,QAAQ;AAAA,IAEf,QAAQ,SAAS,SAAS,QAAQ,MAAM,EAAE,EAAE;AAAA,IAC5C,UAAU,QAAQ;AAAA,IAClB,iBAAiB,QAAQ,oBAAoB;AAAA,EAAA;AAEjD;AAEA,SAAS,kBAAkB,KAAsB;AAC3C,MAAA,OAAO,QAAQ,UAAU;AACpB,WAAA;AAAA,EACT;AACA,QAAM,WAAW,IAAI,IAAI,GAAG,EAAE;AAC9B,MAAI,SAAS,QAAQ,aAAa,MAAM,MAAM,aAAa,aAAa;AAC/D,WAAA;AAAA,EACT;AACO,SAAA;AACT;"}
@@ -0,0 +1,5 @@
1
+ const version = "2024.7.4";
2
+ export {
3
+ version
4
+ };
5
+ //# sourceMappingURL=package.json.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package.json.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -127,7 +127,8 @@ const Image = React__namespace.forwardRef(
127
127
  normalizedProps,
128
128
  passthroughProps,
129
129
  ref,
130
- width
130
+ width,
131
+ data
131
132
  }
132
133
  );
133
134
  } else {
@@ -144,7 +145,8 @@ const Image = React__namespace.forwardRef(
144
145
  passthroughProps,
145
146
  placeholderWidth,
146
147
  ref,
147
- sizes
148
+ sizes,
149
+ data
148
150
  }
149
151
  );
150
152
  }
@@ -161,13 +163,17 @@ const FixedWidthImage = React__namespace.forwardRef(
161
163
  loading,
162
164
  normalizedProps,
163
165
  passthroughProps,
164
- width
166
+ width,
167
+ data
165
168
  }, ref) => {
166
169
  const fixed = React__namespace.useMemo(() => {
167
170
  const intWidth = getNormalizedFixedUnit(width);
168
171
  const intHeight = getNormalizedFixedUnit(height);
169
172
  const fixedAspectRatio = aspectRatio ? aspectRatio : unitsMatch(normalizedProps.width, normalizedProps.height) ? [intWidth, intHeight].join("/") : normalizedProps.aspectRatio ? normalizedProps.aspectRatio : void 0;
170
- const sizesArray = imageWidths === void 0 ? void 0 : generateSizes(imageWidths, fixedAspectRatio, crop);
173
+ const sizesArray = imageWidths === void 0 ? void 0 : generateSizes(imageWidths, fixedAspectRatio, crop, {
174
+ width: (data == null ? void 0 : data.width) ?? void 0,
175
+ height: (data == null ? void 0 : data.height) ?? void 0
176
+ });
171
177
  const fixedHeight = intHeight ? intHeight : fixedAspectRatio && intWidth ? intWidth * (parseAspectRatio(fixedAspectRatio) ?? 1) : void 0;
172
178
  const srcSet = generateSrcSet(normalizedProps.src, sizesArray, loader);
173
179
  const src = loader({
@@ -186,6 +192,7 @@ const FixedWidthImage = React__namespace.forwardRef(
186
192
  }, [
187
193
  aspectRatio,
188
194
  crop,
195
+ data,
189
196
  height,
190
197
  imageWidths,
191
198
  loader,
@@ -222,10 +229,14 @@ const FluidImage = React__namespace.forwardRef(
222
229
  normalizedProps,
223
230
  passthroughProps,
224
231
  placeholderWidth,
225
- sizes
232
+ sizes,
233
+ data
226
234
  }, ref) => {
227
235
  const fluid = React__namespace.useMemo(() => {
228
- const sizesArray = imageWidths === void 0 ? void 0 : generateSizes(imageWidths, normalizedProps.aspectRatio, crop);
236
+ const sizesArray = imageWidths === void 0 ? void 0 : generateSizes(imageWidths, normalizedProps.aspectRatio, crop, {
237
+ width: (data == null ? void 0 : data.width) ?? void 0,
238
+ height: (data == null ? void 0 : data.height) ?? void 0
239
+ });
229
240
  const placeholderHeight = normalizedProps.aspectRatio && placeholderWidth ? placeholderWidth * (parseAspectRatio(normalizedProps.aspectRatio) ?? 1) : void 0;
230
241
  const srcSet = generateSrcSet(normalizedProps.src, sizesArray, loader);
231
242
  const src = loader({
@@ -239,7 +250,7 @@ const FluidImage = React__namespace.forwardRef(
239
250
  srcSet,
240
251
  src
241
252
  };
242
- }, [crop, imageWidths, loader, normalizedProps, placeholderWidth]);
253
+ }, [crop, data, imageWidths, loader, normalizedProps, placeholderWidth]);
243
254
  return /* @__PURE__ */ jsxRuntime.jsx(
244
255
  "img",
245
256
  {
@@ -309,7 +320,7 @@ function getNormalizedFixedUnit(value) {
309
320
  }
310
321
  function isFixedWidth(width) {
311
322
  const fixedEndings = /\d(px|em|rem)$/;
312
- return typeof width === "number" || typeof width === "string" && fixedEndings.test(width);
323
+ return typeof width === "number" || fixedEndings.test(width);
313
324
  }
314
325
  function generateSrcSet(src, sizesArray, loader = shopifyLoader) {
315
326
  if (!src) {
@@ -344,17 +355,24 @@ function parseAspectRatio(aspectRatio) {
344
355
  const [width, height] = aspectRatio.split("/");
345
356
  return 1 / (Number(width) / Number(height));
346
357
  }
347
- function generateSizes(imageWidths, aspectRatio, crop = "center") {
358
+ function generateSizes(imageWidths, aspectRatio, crop = "center", sourceDimensions) {
348
359
  if (!imageWidths)
349
360
  return;
350
- const sizes = imageWidths.map((width) => {
361
+ return imageWidths.map((width) => {
351
362
  return {
352
363
  width,
353
364
  height: aspectRatio ? width * (parseAspectRatio(aspectRatio) ?? 1) : void 0,
354
365
  crop
355
366
  };
367
+ }).filter(({ width, height }) => {
368
+ if ((sourceDimensions == null ? void 0 : sourceDimensions.width) && width > sourceDimensions.width) {
369
+ return false;
370
+ }
371
+ if ((sourceDimensions == null ? void 0 : sourceDimensions.height) && height && height > sourceDimensions.height) {
372
+ return false;
373
+ }
374
+ return true;
356
375
  });
357
- return sizes;
358
376
  }
359
377
  exports.IMAGE_FRAGMENT = IMAGE_FRAGMENT;
360
378
  exports.Image = Image;