@se-studio/contentful-rest-api 1.0.22 → 1.0.23

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.
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { revalidateTag } from 'next/cache';
2
+ import { isSvgImage } from '@se-studio/core-data-types';
2
3
  import { NextResponse } from 'next/server';
3
4
 
4
5
  var __defProp = Object.defineProperty;
@@ -501,6 +502,20 @@ function lookupAsset(context, asset) {
501
502
  if (!asset) return void 0;
502
503
  return context.assets.get(asset.sys.id);
503
504
  }
505
+ function lookupIconAsset(context, asset) {
506
+ const visual = lookupAsset(context, asset);
507
+ if (!visual) return void 0;
508
+ if (visual.image?.type === "Svg image") {
509
+ return {
510
+ ...visual,
511
+ image: {
512
+ ...visual.image,
513
+ isIcon: true
514
+ }
515
+ };
516
+ }
517
+ return visual;
518
+ }
504
519
  var DEFAULT_POSITION_FIELDS = {
505
520
  index: 0,
506
521
  isFirst: false,
@@ -788,6 +803,170 @@ function lookupMediaEntry(context, link) {
788
803
  return void 0;
789
804
  }
790
805
 
806
+ // src/converters/svgProcessor.ts
807
+ async function fetchSvgContent(url) {
808
+ const response = await fetch(url);
809
+ if (!response.ok) {
810
+ throw new Error(`Failed to fetch SVG from ${url}: ${response.statusText}`);
811
+ }
812
+ return await response.text();
813
+ }
814
+ function processSvgForSprite(svgContent, iconId) {
815
+ const viewBoxMatch = svgContent.match(/viewBox\s*=\s*["']([^"']+)["']/i);
816
+ const viewBox = viewBoxMatch ? viewBoxMatch[1] : "0 0 24 24";
817
+ const innerContentMatch = svgContent.match(/<svg[^>]*>([\s\S]*)<\/svg>/i);
818
+ if (!innerContentMatch) {
819
+ throw new Error(`Invalid SVG content: could not parse SVG structure`);
820
+ }
821
+ let innerContent = innerContentMatch[1] ?? "";
822
+ innerContent = innerContent.replace(/\s(width|height)=["'][^"']*["']/gi, "");
823
+ const symbolId = `icon-${iconId}`;
824
+ return `<symbol id="${symbolId}" viewBox="${viewBox}">${innerContent}</symbol>`;
825
+ }
826
+
827
+ // src/converters/iconCollector.ts
828
+ function extractIconFromVisual(visual) {
829
+ if (!visual?.image) {
830
+ return void 0;
831
+ }
832
+ if (isSvgImage(visual.image) && visual.image.isIcon) {
833
+ return visual.image;
834
+ }
835
+ return void 0;
836
+ }
837
+ function extractIconsFromResponsiveVisual(visual) {
838
+ const icons = [];
839
+ if (visual?.visual) {
840
+ const svg = extractIconFromVisual(visual.visual);
841
+ if (svg) {
842
+ icons.push(svg);
843
+ }
844
+ }
845
+ if (visual?.mobileVisual) {
846
+ const svg = extractIconFromVisual(visual.mobileVisual);
847
+ if (svg) {
848
+ icons.push(svg);
849
+ }
850
+ }
851
+ return icons;
852
+ }
853
+ function collectIconsFromComponent(component) {
854
+ if (!component) {
855
+ return [];
856
+ }
857
+ const icon = extractIconFromVisual(component.icon);
858
+ return icon ? [icon] : [];
859
+ }
860
+ function collectIconsFromCollection(collection) {
861
+ if (!collection) {
862
+ return [];
863
+ }
864
+ const icon = extractIconFromVisual(collection.icon);
865
+ return icon ? [icon] : [];
866
+ }
867
+ function collectIconsFromNavigation(navigation) {
868
+ if (!navigation) {
869
+ return [];
870
+ }
871
+ const icons = [];
872
+ const seenIds = /* @__PURE__ */ new Set();
873
+ function addIcon(icon) {
874
+ if (!seenIds.has(icon.id)) {
875
+ seenIds.add(icon.id);
876
+ icons.push(icon);
877
+ }
878
+ }
879
+ function processNavItem(item) {
880
+ if (item.link?.icon) {
881
+ const linkIcons = extractIconsFromResponsiveVisual(item.link.icon);
882
+ for (const icon of linkIcons) {
883
+ addIcon(icon);
884
+ }
885
+ }
886
+ if (item.entries) {
887
+ for (const nestedItem of item.entries) {
888
+ processNavItem(nestedItem);
889
+ }
890
+ }
891
+ }
892
+ for (const item of navigation.entries) {
893
+ processNavItem(item);
894
+ }
895
+ return icons;
896
+ }
897
+ function collectIconsFromContent(contents) {
898
+ if (!contents) {
899
+ return [];
900
+ }
901
+ const icons = [];
902
+ const seenIds = /* @__PURE__ */ new Set();
903
+ function addIcon(icon) {
904
+ if (!seenIds.has(icon.id)) {
905
+ seenIds.add(icon.id);
906
+ icons.push(icon);
907
+ }
908
+ }
909
+ for (const content of contents) {
910
+ if (content.type === "Component") {
911
+ const componentIcons = collectIconsFromComponent(content);
912
+ for (const icon of componentIcons) {
913
+ addIcon(icon);
914
+ }
915
+ const component = content;
916
+ if (component.contents) {
917
+ const nestedIcons = collectIconsFromContent(component.contents);
918
+ for (const icon of nestedIcons) {
919
+ addIcon(icon);
920
+ }
921
+ }
922
+ }
923
+ if (content.type === "Collection") {
924
+ const collectionIcons = collectIconsFromCollection(content);
925
+ for (const icon of collectionIcons) {
926
+ addIcon(icon);
927
+ }
928
+ const collection = content;
929
+ if (collection.contents) {
930
+ const nestedIcons = collectIconsFromContent(collection.contents);
931
+ for (const icon of nestedIcons) {
932
+ addIcon(icon);
933
+ }
934
+ }
935
+ }
936
+ if (content.type === "Internal link" || content.type === "External link" || content.type === "Download link" || content.type === "Blank link") {
937
+ const link = content;
938
+ if (link.icon) {
939
+ const linkIcons = extractIconsFromResponsiveVisual(link.icon);
940
+ for (const icon of linkIcons) {
941
+ addIcon(icon);
942
+ }
943
+ }
944
+ }
945
+ }
946
+ return icons;
947
+ }
948
+ async function processIconsForSprite(icons) {
949
+ if (icons.length === 0) {
950
+ return [];
951
+ }
952
+ const processedIcons = await Promise.all(
953
+ icons.map(async (icon) => {
954
+ try {
955
+ const svgContent = await fetchSvgContent(icon.svgSrc);
956
+ const symbolContent = processSvgForSprite(svgContent, icon.id);
957
+ return {
958
+ ...icon,
959
+ symbolContent
960
+ };
961
+ } catch (error) {
962
+ console.error(`Failed to process icon ${icon.id}:`, error);
963
+ return icon;
964
+ }
965
+ })
966
+ );
967
+ return processedIcons;
968
+ }
969
+
791
970
  // src/api/helpers.ts
792
971
  init_utils();
793
972
 
@@ -898,6 +1077,13 @@ async function fetchSingleEntity(context, config, fetchConfig, options) {
898
1077
  errors: []
899
1078
  };
900
1079
  const converted = fetchConfig.resolver(fullContext, entry);
1080
+ if (converted && typeof converted === "object" && "icons" in converted) {
1081
+ const icons = converted.icons;
1082
+ if (icons && Array.isArray(icons) && icons.length > 0) {
1083
+ const processedIcons = await processIconsForSprite(icons);
1084
+ converted.icons = processedIcons.length > 0 ? processedIcons : void 0;
1085
+ }
1086
+ }
901
1087
  if (fullContext.errors.length > 0 && typeof process !== "undefined" && process.env?.NODE_ENV === "production") {
902
1088
  console.error(`CMS conversion errors for ${fetchConfig.contentType}:`, {
903
1089
  entryId: entry.sys.id,
@@ -1319,8 +1505,8 @@ function createLink(context, entry) {
1319
1505
  ...otherFields
1320
1506
  } = fields;
1321
1507
  const icon = createResponsiveVisual(
1322
- lookupAsset(context, navIcon),
1323
- lookupAsset(context, mobileNavIcon)
1508
+ lookupIconAsset(context, navIcon),
1509
+ lookupIconAsset(context, mobileNavIcon)
1324
1510
  );
1325
1511
  const realText = useTitle ? resolveBuildYear(title) : text ? resolveBuildYear(text) : void 0;
1326
1512
  if (link) {
@@ -1460,6 +1646,20 @@ function baseArticleConverter(context, entry) {
1460
1646
  ...postContent,
1461
1647
  ...bottomContent
1462
1648
  ]);
1649
+ const finalMenu = template?.menu;
1650
+ const finalFooter = template?.footer;
1651
+ const contentIcons = collectIconsFromContent(contents);
1652
+ const menuIcons = collectIconsFromNavigation(finalMenu);
1653
+ const footerIcons = collectIconsFromNavigation(finalFooter);
1654
+ const allIcons = [...contentIcons, ...menuIcons, ...footerIcons];
1655
+ const seenIconIds = /* @__PURE__ */ new Set();
1656
+ const icons = allIcons.filter((icon) => {
1657
+ if (seenIconIds.has(icon.id)) {
1658
+ return false;
1659
+ }
1660
+ seenIconIds.add(icon.id);
1661
+ return true;
1662
+ });
1463
1663
  const article = {
1464
1664
  type: "Article",
1465
1665
  id,
@@ -1470,11 +1670,12 @@ function baseArticleConverter(context, entry) {
1470
1670
  articleType: articleTypeLink,
1471
1671
  tags: tags?.map((tag) => resolveLink(context, id, tag)),
1472
1672
  contents,
1673
+ icons: icons.length > 0 ? icons : void 0,
1473
1674
  ...simpleFields,
1474
1675
  // Note: summary field exists in Contentful but is not part of IArticle interface
1475
1676
  // Keeping it in simpleFields for potential future use
1476
- menu: template?.menu,
1477
- footer: template?.footer
1677
+ menu: finalMenu,
1678
+ footer: finalFooter
1478
1679
  };
1479
1680
  return article;
1480
1681
  }
@@ -1565,6 +1766,20 @@ function baseArticleTypeConverter(context, entry) {
1565
1766
  const preContent = template?.preContent ?? [];
1566
1767
  const postContent = template?.postContent ?? [];
1567
1768
  const contents = addPositionMetadata([...topContent, ...preContent, ...postContent]);
1769
+ const finalMenu = menu;
1770
+ const finalFooter = footer;
1771
+ const contentIcons = collectIconsFromContent(contents);
1772
+ const menuIcons = collectIconsFromNavigation(finalMenu);
1773
+ const footerIcons = collectIconsFromNavigation(finalFooter);
1774
+ const allIcons = [...contentIcons, ...menuIcons, ...footerIcons];
1775
+ const seenIconIds = /* @__PURE__ */ new Set();
1776
+ const icons = allIcons.filter((icon) => {
1777
+ if (seenIconIds.has(icon.id)) {
1778
+ return false;
1779
+ }
1780
+ seenIconIds.add(icon.id);
1781
+ return true;
1782
+ });
1568
1783
  const articleType = {
1569
1784
  type: "Article type",
1570
1785
  id,
@@ -1573,9 +1788,10 @@ function baseArticleTypeConverter(context, entry) {
1573
1788
  description: makeContentfulDescription(indexPageDescription, sys.id),
1574
1789
  featuredImage: lookupAsset(context, featuredImage),
1575
1790
  contents,
1791
+ icons: icons.length > 0 ? icons : void 0,
1576
1792
  structuredData,
1577
- menu,
1578
- footer,
1793
+ menu: finalMenu,
1794
+ footer: finalFooter,
1579
1795
  ...other
1580
1796
  };
1581
1797
  return articleType;
@@ -1623,7 +1839,7 @@ function baseCollectionConverter(context, entry) {
1623
1839
  heading: showHeading ? heading : void 0,
1624
1840
  body: resolveRichTextDocument(context, id, bodyField),
1625
1841
  additionalCopy: resolveRichTextDocument(context, id, additionalCopyField),
1626
- icon: lookupAsset(context, iconField),
1842
+ icon: lookupIconAsset(context, iconField),
1627
1843
  backgroundVisual,
1628
1844
  visual,
1629
1845
  links: linksField?.map((link) => resolveLink(context, id, link)),
@@ -1676,7 +1892,7 @@ function baseComponentConverter(context, entry) {
1676
1892
  heading: showHeading ? heading : void 0,
1677
1893
  body: resolveRichTextDocument(context, id, bodyField),
1678
1894
  additionalCopy: resolveRichTextDocument(context, id, additionalCopyField),
1679
- icon: lookupAsset(context, iconField),
1895
+ icon: lookupIconAsset(context, iconField),
1680
1896
  backgroundVisual,
1681
1897
  visual,
1682
1898
  links: resolveLinks(context, id, linksField),
@@ -1769,8 +1985,8 @@ function baseLinkConverter(context, entry) {
1769
1985
  useName ? name : fields.linkText ?? makeContentfulTitle(fields.linkText, id, "Link text for ")
1770
1986
  );
1771
1987
  const icon = createResponsiveVisual(
1772
- lookupAsset(context, fields.icon),
1773
- lookupAsset(context, fields.mobileIcon)
1988
+ lookupIconAsset(context, fields.icon),
1989
+ lookupIconAsset(context, fields.mobileIcon)
1774
1990
  );
1775
1991
  const backgroundColour = fields.backgroundColour ?? null;
1776
1992
  const textColour = fields.textColour ?? null;
@@ -1874,6 +2090,20 @@ function basePageConverter(context, entry) {
1874
2090
  ...postContent,
1875
2091
  ...bottomContent
1876
2092
  ]);
2093
+ const finalMenu = pageMenuNav ?? template?.menu;
2094
+ const finalFooter = pageFooterNav ?? template?.footer;
2095
+ const contentIcons = collectIconsFromContent(contents);
2096
+ const menuIcons = collectIconsFromNavigation(finalMenu);
2097
+ const footerIcons = collectIconsFromNavigation(finalFooter);
2098
+ const allIcons = [...contentIcons, ...menuIcons, ...footerIcons];
2099
+ const seenIconIds = /* @__PURE__ */ new Set();
2100
+ const icons = allIcons.filter((icon) => {
2101
+ if (seenIconIds.has(icon.id)) {
2102
+ return false;
2103
+ }
2104
+ seenIconIds.add(icon.id);
2105
+ return true;
2106
+ });
1877
2107
  const page = {
1878
2108
  type: "Page",
1879
2109
  id,
@@ -1884,9 +2114,10 @@ function basePageConverter(context, entry) {
1884
2114
  featuredImage: lookupAsset(context, featuredImage),
1885
2115
  tags: tags?.map((tag) => resolveLink(context, id, tag)),
1886
2116
  contents,
2117
+ icons: icons.length > 0 ? icons : void 0,
1887
2118
  ...simpleFields,
1888
- menu: pageMenuNav ?? template?.menu,
1889
- footer: pageFooterNav ?? template?.footer
2119
+ menu: finalMenu,
2120
+ footer: finalFooter
1890
2121
  };
1891
2122
  return page;
1892
2123
  }