@contentful/experiences-core 1.36.0-dev-20250417T1301-b6204ec.0 → 1.36.0

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.
@@ -12,7 +12,7 @@ declare abstract class EntityStoreBase {
12
12
  entities: Array<Entry | Asset>;
13
13
  locale: string;
14
14
  });
15
- get entities(): (Asset<ChainModifiers, string> | Entry)[];
15
+ get entities(): (Entry | Asset<ChainModifiers, string>)[];
16
16
  updateEntity(entity: Entry | Asset): void;
17
17
  getEntryOrAsset(linkOrEntryOrAsset: UnresolvedLink<'Entry' | 'Asset'> | Asset | Entry, path: string): Entry | Asset | undefined;
18
18
  /**
@@ -24,7 +24,7 @@ declare abstract class EntityStoreBase {
24
24
  getValue(entityLink: UnresolvedLink<'Entry' | 'Asset'>, path: string[]): string | undefined;
25
25
  getEntityFromLink(link: UnresolvedLink<'Entry' | 'Asset'>): Asset | Entry | undefined;
26
26
  protected getEntitiesFromMap(type: 'Entry' | 'Asset', ids: string[]): {
27
- resolved: (Asset<ChainModifiers, string> | Entry)[];
27
+ resolved: (Entry | Asset<ChainModifiers, string>)[];
28
28
  missing: string[];
29
29
  };
30
30
  protected addEntity(entity: Entry | Asset): void;
package/dist/index.js CHANGED
@@ -1645,69 +1645,6 @@ const resetBreakpointsRegistry = () => {
1645
1645
  breakpointsRegistry = [];
1646
1646
  };
1647
1647
 
1648
- function getOptimizedImageUrl(url, width, quality, format) {
1649
- if (url.startsWith('//')) {
1650
- url = 'https:' + url;
1651
- }
1652
- const params = new URLSearchParams();
1653
- if (width) {
1654
- params.append('w', width.toString());
1655
- }
1656
- if (quality && quality > 0 && quality < 100) {
1657
- params.append('q', quality.toString());
1658
- }
1659
- if (format) {
1660
- params.append('fm', format);
1661
- }
1662
- const queryString = params.toString();
1663
- return `${url}${queryString ? '?' + queryString : ''}`;
1664
- }
1665
-
1666
- function validateParams(file, quality, format) {
1667
- if (!file.details.image) {
1668
- throw Error('No image in file asset to transform');
1669
- }
1670
- if (quality < 0 || quality > 100) {
1671
- throw Error('Quality must be between 0 and 100');
1672
- }
1673
- if (format && !SUPPORTED_IMAGE_FORMATS.includes(format)) {
1674
- throw Error(`Format must be one of ${SUPPORTED_IMAGE_FORMATS.join(', ')}`);
1675
- }
1676
- return true;
1677
- }
1678
-
1679
- const MAX_WIDTH_ALLOWED$1 = 2000;
1680
- const getOptimizedBackgroundImageAsset = (file, widthStyle, quality = '100%', format) => {
1681
- const qualityNumber = Number(quality.replace('%', ''));
1682
- if (!validateParams(file, qualityNumber, format)) ;
1683
- if (!validateParams(file, qualityNumber, format)) ;
1684
- const url = file.url;
1685
- const { width1x, width2x } = getWidths(widthStyle, file);
1686
- const imageUrl1x = getOptimizedImageUrl(url, width1x, qualityNumber, format);
1687
- const imageUrl2x = getOptimizedImageUrl(url, width2x, qualityNumber, format);
1688
- const srcSet = [`url(${imageUrl1x}) 1x`, `url(${imageUrl2x}) 2x`];
1689
- const returnedUrl = getOptimizedImageUrl(url, width2x, qualityNumber, format);
1690
- const optimizedBackgroundImageAsset = {
1691
- url: returnedUrl,
1692
- srcSet,
1693
- file,
1694
- };
1695
- return optimizedBackgroundImageAsset;
1696
- function getWidths(widthStyle, file) {
1697
- let width1x = 0;
1698
- let width2x = 0;
1699
- const intrinsicImageWidth = file.details.image.width;
1700
- if (widthStyle.endsWith('px')) {
1701
- width1x = Math.min(Number(widthStyle.replace('px', '')), intrinsicImageWidth);
1702
- }
1703
- else {
1704
- width1x = Math.min(MAX_WIDTH_ALLOWED$1, intrinsicImageWidth);
1705
- }
1706
- width2x = Math.min(width1x * 2, intrinsicImageWidth);
1707
- return { width1x, width2x };
1708
- }
1709
- };
1710
-
1711
1648
  const detachExperienceStyles = (experience) => {
1712
1649
  const experienceTreeRoot = experience.entityStore?.experienceEntryFields
1713
1650
  ?.componentTree;
@@ -1727,25 +1664,17 @@ const detachExperienceStyles = (experience) => {
1727
1664
  }), {});
1728
1665
  // getting the breakpoint ids
1729
1666
  const breakpointIds = Object.keys(mediaQueryDataByBreakpoint);
1730
- const iterateOverTreeAndExtractStyles = ({ componentTree, dataSource, unboundValues, componentSettings, componentVariablesOverwrites, patternWrapper, wrappingPatternIds, parentChainArr = [], }) => {
1667
+ const iterateOverTreeAndExtractStyles = ({ componentTree, dataSource, unboundValues, componentSettings, componentVariablesOverwrites, patternWrapper, wrappingPatternIds, patternNodeIdsChain = '', }) => {
1731
1668
  // traversing the tree
1732
1669
  const queue = [];
1733
- queue.push(...componentTree.children.map((child) => ({
1734
- node: child,
1735
- parentChain: [...parentChainArr],
1736
- })));
1670
+ queue.push(...componentTree.children);
1671
+ let currentNode = undefined;
1737
1672
  // for each tree node
1738
1673
  while (queue.length) {
1739
- const queueItem = queue.shift();
1740
- if (!queueItem) {
1741
- break;
1742
- }
1743
- const { node: currentNode, parentChain } = queueItem;
1674
+ currentNode = queue.shift();
1744
1675
  if (!currentNode) {
1745
1676
  break;
1746
1677
  }
1747
- const currentNodeParentChain = [...parentChain, currentNode.id || ''];
1748
- const currentPatternNodeIdsChain = currentNodeParentChain.join('');
1749
1678
  const usedComponents = experience.entityStore?.usedComponents ?? [];
1750
1679
  const isPatternNode = checkIsAssemblyNode({
1751
1680
  componentId: currentNode.definitionId,
@@ -1783,7 +1712,7 @@ const detachExperienceStyles = (experience) => {
1783
1712
  // pass top-level pattern node to store instance-specific child styles for rendering
1784
1713
  patternWrapper: currentNode,
1785
1714
  wrappingPatternIds: new Set([...wrappingPatternIds, currentNode.definitionId]),
1786
- parentChainArr: currentNodeParentChain,
1715
+ patternNodeIdsChain: `${patternNodeIdsChain}${currentNode.id}`,
1787
1716
  });
1788
1717
  continue;
1789
1718
  }
@@ -1869,12 +1798,13 @@ const detachExperienceStyles = (experience) => {
1869
1798
  // making sure that we respect the order of breakpoints from
1870
1799
  // we can achieve "desktop first" or "mobile first" approach to style over-writes
1871
1800
  if (patternWrapper) {
1801
+ currentNode.id = currentNode.id || generateRandomId(5);
1872
1802
  // @ts-expect-error -- valueByBreakpoint is not explicitly defined, but it's already defined in the patternWrapper styles
1873
1803
  patternWrapper.variables.cfSsrClassName = {
1874
1804
  ...(patternWrapper.variables.cfSsrClassName ?? {}),
1875
1805
  type: 'DesignValue',
1876
1806
  // Chain IDs to avoid overwriting styles across multiple instances of the same pattern
1877
- [currentPatternNodeIdsChain]: {
1807
+ [`${patternNodeIdsChain}${currentNode.id}`]: {
1878
1808
  valuesByBreakpoint: {
1879
1809
  [breakpointIds[0]]: currentNodeClassNames.join(' '),
1880
1810
  },
@@ -1889,10 +1819,7 @@ const detachExperienceStyles = (experience) => {
1889
1819
  },
1890
1820
  };
1891
1821
  }
1892
- queue.push(...currentNode.children.map((child) => ({
1893
- node: child,
1894
- parentChain: currentNodeParentChain,
1895
- })));
1822
+ queue.push(...currentNode.children);
1896
1823
  }
1897
1824
  };
1898
1825
  iterateOverTreeAndExtractStyles({
@@ -2014,28 +1941,7 @@ const maybePopulateDesignTokenValue = (variableName, variableValue, mapOfDesignV
2014
1941
  // Not trimming would end up with a trailing space that breaks the check in `calculateNodeDefaultHeight`
2015
1942
  return resolvedValue.trim();
2016
1943
  };
2017
- const transformMedia$1 = (boundAsset, width, options) => {
2018
- try {
2019
- const asset = boundAsset;
2020
- // Target width (px/rem/em) will be applied to the css url if it's lower than the original image width (in px)
2021
- const assetDetails = asset.fields.file?.details;
2022
- const assetWidth = assetDetails?.image?.width || 0; // This is always in px
2023
- if (!options) {
2024
- return asset.fields.file?.url;
2025
- }
2026
- const targetWidthObject = parseCSSValue(options.targetSize); // Contains value and unit (px/rem/em) so convert and then compare to assetWidth
2027
- const targetValue = targetWidthObject ? getTargetValueInPixels(targetWidthObject) : assetWidth;
2028
- if (targetValue < assetWidth)
2029
- width = `${targetValue}px`;
2030
- const value = getOptimizedBackgroundImageAsset(asset.fields.file, width, options.quality, options.format);
2031
- return value;
2032
- }
2033
- catch (error) {
2034
- console.error('Error transforming image asset', error);
2035
- }
2036
- return boundAsset.fields.file?.url;
2037
- };
2038
- const resolveBackgroundImageBinding = ({ variableData, getBoundEntityById, dataSource = {}, unboundValues = {}, componentVariablesOverwrites, componentSettings = { variableDefinitions: {} }, options, width, }) => {
1944
+ const resolveBackgroundImageBinding = ({ variableData, getBoundEntityById, dataSource = {}, unboundValues = {}, componentVariablesOverwrites, componentSettings = { variableDefinitions: {} }, }) => {
2039
1945
  if (variableData.type === 'UnboundValue') {
2040
1946
  const uuid = variableData.key;
2041
1947
  return unboundValues[uuid]?.value;
@@ -2060,8 +1966,6 @@ const resolveBackgroundImageBinding = ({ variableData, getBoundEntityById, dataS
2060
1966
  unboundValues,
2061
1967
  componentVariablesOverwrites,
2062
1968
  componentSettings,
2063
- options,
2064
- width,
2065
1969
  });
2066
1970
  return resolvedValue || defaultValue;
2067
1971
  }
@@ -2074,7 +1978,7 @@ const resolveBackgroundImageBinding = ({ variableData, getBoundEntityById, dataS
2074
1978
  return;
2075
1979
  }
2076
1980
  if (boundEntity.sys.type === 'Asset') {
2077
- return transformMedia$1(boundEntity, width, options);
1981
+ return boundEntity.fields.file?.url;
2078
1982
  }
2079
1983
  else {
2080
1984
  // '/lUERH7tX7nJTaPX6f0udB/fields/assetReference/~locale/fields/file/~locale'
@@ -2098,31 +2002,11 @@ const resolveBackgroundImageBinding = ({ variableData, getBoundEntityById, dataS
2098
2002
  if (!referencedAsset) {
2099
2003
  return;
2100
2004
  }
2101
- return transformMedia$1(referencedAsset, width, options);
2005
+ return referencedAsset.fields.file?.url;
2102
2006
  }
2103
2007
  }
2104
2008
  }
2105
2009
  };
2106
- const resolveVariable = ({ variableData, defaultBreakpoint, componentSettings = { variableDefinitions: {} }, componentVariablesOverwrites, }) => {
2107
- if (variableData?.type === 'DesignValue') {
2108
- return variableData.valuesByBreakpoint[defaultBreakpoint] || {};
2109
- }
2110
- else if (variableData?.type === 'ComponentValue') {
2111
- const variableDefinitionKey = variableData.key;
2112
- const variableDefinition = componentSettings.variableDefinitions[variableDefinitionKey];
2113
- const defaultValue = variableDefinition.defaultValue;
2114
- const userSetValue = componentVariablesOverwrites?.[variableDefinitionKey];
2115
- if (!userSetValue || userSetValue.type === 'ComponentValue') {
2116
- return defaultValue?.valuesByBreakpoint[defaultBreakpoint] || '';
2117
- }
2118
- return resolveVariable({
2119
- variableData: userSetValue,
2120
- defaultBreakpoint,
2121
- componentSettings,
2122
- componentVariablesOverwrites,
2123
- });
2124
- }
2125
- };
2126
2010
  /**
2127
2011
  * Takes the initial set of properties, filters only design properties that will be mapped to CSS and
2128
2012
  * re-organizes them to be indexed by breakpoint ID ("breakpoint > variable > value"). It will
@@ -2173,22 +2057,6 @@ const indexByBreakpoint = ({ variables, breakpointIds, getBoundEntityById, unbou
2173
2057
  if (variableName === 'cfBackgroundImageUrl' ||
2174
2058
  // TODO: Test this for nested patterns as the name might be just a random hash without the actual name (needs to be validated).
2175
2059
  variableName.startsWith('cfBackgroundImageUrl_')) {
2176
- const width = resolveVariable({
2177
- variableData: variables['cfWidth'],
2178
- defaultBreakpoint,
2179
- componentSettings,
2180
- componentVariablesOverwrites,
2181
- });
2182
- const options = resolveVariable({
2183
- variableData: variables['cfBackgroundImageOptions'],
2184
- defaultBreakpoint,
2185
- componentSettings,
2186
- componentVariablesOverwrites,
2187
- });
2188
- if (!options) {
2189
- console.error(`Error transforming image asset: Required variable [cfBackgroundImageOptions] missing from component definition`);
2190
- continue;
2191
- }
2192
2060
  const imageUrl = resolveBackgroundImageBinding({
2193
2061
  variableData,
2194
2062
  getBoundEntityById,
@@ -2196,8 +2064,6 @@ const indexByBreakpoint = ({ variables, breakpointIds, getBoundEntityById, unbou
2196
2064
  dataSource,
2197
2065
  componentSettings,
2198
2066
  componentVariablesOverwrites,
2199
- width,
2200
- options,
2201
2067
  });
2202
2068
  if (imageUrl) {
2203
2069
  variableValuesByBreakpoints[defaultBreakpoint][variableName] = imageUrl;
@@ -2337,6 +2203,69 @@ const resolveLinks = (node, entityStore) => {
2337
2203
  }
2338
2204
  };
2339
2205
 
2206
+ function getOptimizedImageUrl(url, width, quality, format) {
2207
+ if (url.startsWith('//')) {
2208
+ url = 'https:' + url;
2209
+ }
2210
+ const params = new URLSearchParams();
2211
+ if (width) {
2212
+ params.append('w', width.toString());
2213
+ }
2214
+ if (quality && quality > 0 && quality < 100) {
2215
+ params.append('q', quality.toString());
2216
+ }
2217
+ if (format) {
2218
+ params.append('fm', format);
2219
+ }
2220
+ const queryString = params.toString();
2221
+ return `${url}${queryString ? '?' + queryString : ''}`;
2222
+ }
2223
+
2224
+ function validateParams(file, quality, format) {
2225
+ if (!file.details.image) {
2226
+ throw Error('No image in file asset to transform');
2227
+ }
2228
+ if (quality < 0 || quality > 100) {
2229
+ throw Error('Quality must be between 0 and 100');
2230
+ }
2231
+ if (format && !SUPPORTED_IMAGE_FORMATS.includes(format)) {
2232
+ throw Error(`Format must be one of ${SUPPORTED_IMAGE_FORMATS.join(', ')}`);
2233
+ }
2234
+ return true;
2235
+ }
2236
+
2237
+ const MAX_WIDTH_ALLOWED$1 = 2000;
2238
+ const getOptimizedBackgroundImageAsset = (file, widthStyle, quality = '100%', format) => {
2239
+ const qualityNumber = Number(quality.replace('%', ''));
2240
+ if (!validateParams(file, qualityNumber, format)) ;
2241
+ if (!validateParams(file, qualityNumber, format)) ;
2242
+ const url = file.url;
2243
+ const { width1x, width2x } = getWidths(widthStyle, file);
2244
+ const imageUrl1x = getOptimizedImageUrl(url, width1x, qualityNumber, format);
2245
+ const imageUrl2x = getOptimizedImageUrl(url, width2x, qualityNumber, format);
2246
+ const srcSet = [`url(${imageUrl1x}) 1x`, `url(${imageUrl2x}) 2x`];
2247
+ const returnedUrl = getOptimizedImageUrl(url, width2x, qualityNumber, format);
2248
+ const optimizedBackgroundImageAsset = {
2249
+ url: returnedUrl,
2250
+ srcSet,
2251
+ file,
2252
+ };
2253
+ return optimizedBackgroundImageAsset;
2254
+ function getWidths(widthStyle, file) {
2255
+ let width1x = 0;
2256
+ let width2x = 0;
2257
+ const intrinsicImageWidth = file.details.image.width;
2258
+ if (widthStyle.endsWith('px')) {
2259
+ width1x = Math.min(Number(widthStyle.replace('px', '')), intrinsicImageWidth);
2260
+ }
2261
+ else {
2262
+ width1x = Math.min(MAX_WIDTH_ALLOWED$1, intrinsicImageWidth);
2263
+ }
2264
+ width2x = Math.min(width1x * 2, intrinsicImageWidth);
2265
+ return { width1x, width2x };
2266
+ }
2267
+ };
2268
+
2340
2269
  const MAX_WIDTH_ALLOWED = 4000;
2341
2270
  const getOptimizedImageAsset = ({ file, sizes, loading, quality = '100%', format, }) => {
2342
2271
  const qualityNumber = Number(quality.replace('%', ''));