@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 +243 -12
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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
|
-
|
|
1323
|
-
|
|
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:
|
|
1477
|
-
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:
|
|
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:
|
|
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
|
-
|
|
1773
|
-
|
|
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:
|
|
1889
|
-
footer:
|
|
2119
|
+
menu: finalMenu,
|
|
2120
|
+
footer: finalFooter
|
|
1890
2121
|
};
|
|
1891
2122
|
return page;
|
|
1892
2123
|
}
|