@se-studio/contentful-rest-api 1.0.21 → 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 +272 -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),
|
|
@@ -1727,6 +1943,34 @@ function baseCustomTypeConverter(context, entry) {
|
|
|
1727
1943
|
};
|
|
1728
1944
|
return customType;
|
|
1729
1945
|
}
|
|
1946
|
+
function baseCustomTypeLinkConverter(context, entry) {
|
|
1947
|
+
const {
|
|
1948
|
+
sys: { id, contentType },
|
|
1949
|
+
fields
|
|
1950
|
+
} = entry;
|
|
1951
|
+
if (contentType.sys.id !== "customType") {
|
|
1952
|
+
throw new Error(`Invalid content type: expected "customType", got "${contentType.sys.id}"`);
|
|
1953
|
+
}
|
|
1954
|
+
return createInternalLink(
|
|
1955
|
+
id,
|
|
1956
|
+
{
|
|
1957
|
+
cmsLabel: fields.name,
|
|
1958
|
+
title: fields.name,
|
|
1959
|
+
featuredImage: fields.featuredImage,
|
|
1960
|
+
backgroundColour: fields.backgroundColour,
|
|
1961
|
+
textColour: fields.textColour,
|
|
1962
|
+
indexed: fields.indexed,
|
|
1963
|
+
hidden: fields.hidden,
|
|
1964
|
+
slug: fields.indexPageSlug
|
|
1965
|
+
},
|
|
1966
|
+
context,
|
|
1967
|
+
calculateCustomTypeHref(fields.indexPageSlug),
|
|
1968
|
+
"CustomType"
|
|
1969
|
+
);
|
|
1970
|
+
}
|
|
1971
|
+
function calculateCustomTypeHref(slug) {
|
|
1972
|
+
return `/${slug}/`;
|
|
1973
|
+
}
|
|
1730
1974
|
|
|
1731
1975
|
// src/converters/link.ts
|
|
1732
1976
|
function baseLinkConverter(context, entry) {
|
|
@@ -1741,8 +1985,8 @@ function baseLinkConverter(context, entry) {
|
|
|
1741
1985
|
useName ? name : fields.linkText ?? makeContentfulTitle(fields.linkText, id, "Link text for ")
|
|
1742
1986
|
);
|
|
1743
1987
|
const icon = createResponsiveVisual(
|
|
1744
|
-
|
|
1745
|
-
|
|
1988
|
+
lookupIconAsset(context, fields.icon),
|
|
1989
|
+
lookupIconAsset(context, fields.mobileIcon)
|
|
1746
1990
|
);
|
|
1747
1991
|
const backgroundColour = fields.backgroundColour ?? null;
|
|
1748
1992
|
const textColour = fields.textColour ?? null;
|
|
@@ -1846,6 +2090,20 @@ function basePageConverter(context, entry) {
|
|
|
1846
2090
|
...postContent,
|
|
1847
2091
|
...bottomContent
|
|
1848
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
|
+
});
|
|
1849
2107
|
const page = {
|
|
1850
2108
|
type: "Page",
|
|
1851
2109
|
id,
|
|
@@ -1856,9 +2114,10 @@ function basePageConverter(context, entry) {
|
|
|
1856
2114
|
featuredImage: lookupAsset(context, featuredImage),
|
|
1857
2115
|
tags: tags?.map((tag) => resolveLink(context, id, tag)),
|
|
1858
2116
|
contents,
|
|
2117
|
+
icons: icons.length > 0 ? icons : void 0,
|
|
1859
2118
|
...simpleFields,
|
|
1860
|
-
menu:
|
|
1861
|
-
footer:
|
|
2119
|
+
menu: finalMenu,
|
|
2120
|
+
footer: finalFooter
|
|
1862
2121
|
};
|
|
1863
2122
|
return page;
|
|
1864
2123
|
}
|
|
@@ -2016,6 +2275,7 @@ function createBaseConverterContext() {
|
|
|
2016
2275
|
linkResolver.set("person", basePersonLinkConverter);
|
|
2017
2276
|
linkResolver.set("pageVariant", basePageVariantLinkConverter);
|
|
2018
2277
|
linkResolver.set("link", baseLinkConverter);
|
|
2278
|
+
linkResolver.set("customType", baseCustomTypeLinkConverter);
|
|
2019
2279
|
const contentResolver = /* @__PURE__ */ new Map();
|
|
2020
2280
|
contentResolver.set("collection", baseCollectionConverter);
|
|
2021
2281
|
contentResolver.set("component", baseComponentConverter);
|