@marvalt/wparser 0.1.14 → 0.1.16

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.esm.js CHANGED
@@ -1593,6 +1593,57 @@ function getImageAttributes(block) {
1593
1593
  height: attrs['height'] ? Number(attrs['height']) : undefined,
1594
1594
  };
1595
1595
  }
1596
+ /**
1597
+ * Validate if a Cloudflare URL is complete (has image ID)
1598
+ * A complete Cloudflare URL should have format: https://imagedelivery.net/{account}/{image-id}/
1599
+ */
1600
+ function isValidCloudflareUrl(url) {
1601
+ if (!url)
1602
+ return false;
1603
+ // Check if it's a Cloudflare URL
1604
+ if (!url.includes('imagedelivery.net'))
1605
+ return false;
1606
+ // A complete URL should have at least 3 path segments: /account/image-id/
1607
+ // Incomplete URLs might end with just /account/ or /account
1608
+ const urlObj = new URL(url);
1609
+ const pathSegments = urlObj.pathname.split('/').filter(Boolean);
1610
+ // Should have at least account hash and image ID (2 segments minimum)
1611
+ // But we also accept URLs that end with / (which means they might be complete)
1612
+ // The issue is URLs like: https://imagedelivery.net/ZFArYcvsK9lQ3btUK-x2rA/
1613
+ // This is incomplete - it's missing the image ID
1614
+ return pathSegments.length >= 2;
1615
+ }
1616
+ /**
1617
+ * Extract image URL from block with priority:
1618
+ * 1. Valid cloudflareUrl from attributes
1619
+ * 2. Extract from innerHTML (which should be converted by plugin)
1620
+ * 3. Regular URL attributes
1621
+ *
1622
+ * This handles cases where cloudflareUrl might be incomplete
1623
+ */
1624
+ function extractImageUrlWithFallback(block) {
1625
+ const attrs = block.attributes || {};
1626
+ // Check for cloudflareUrl first (from WordPress plugin)
1627
+ const cloudflareUrl = attrs['cloudflareUrl'];
1628
+ if (cloudflareUrl && isValidCloudflareUrl(cloudflareUrl)) {
1629
+ return cloudflareUrl;
1630
+ }
1631
+ // Try to extract from innerHTML (should be converted by plugin)
1632
+ if (block.innerHTML) {
1633
+ // Extract img src from innerHTML
1634
+ const imgMatch = block.innerHTML.match(/<img[^>]+src=["']([^"']+)["']/i);
1635
+ if (imgMatch && imgMatch[1]) {
1636
+ return imgMatch[1];
1637
+ }
1638
+ // Try background-image in style attribute
1639
+ const bgMatch = block.innerHTML.match(/background-image:\s*url\(["']?([^"')]+)["']?\)/i);
1640
+ if (bgMatch && bgMatch[1]) {
1641
+ return bgMatch[1];
1642
+ }
1643
+ }
1644
+ // Fall back to regular URL attributes
1645
+ return getImageUrl(block);
1646
+ }
1596
1647
 
1597
1648
  /**
1598
1649
  * Style mapping utilities
@@ -1835,28 +1886,28 @@ const ButtonBlock = ({ block }) => {
1835
1886
  };
1836
1887
  const Cover = ({ block, children }) => {
1837
1888
  const attrs = block.attributes || {};
1838
- const { url, id, backgroundImage, overlayColor, dimRatio = 0, align = 'full', minHeight, minHeightUnit = 'vh', hasParallax, } = attrs;
1889
+ const { url, id, backgroundImage, cloudflareUrl, overlayColor, dimRatio = 0, align = 'full', minHeight, minHeightUnit = 'vh', hasParallax, } = attrs;
1839
1890
  // Get background image URL from various possible sources
1840
- let bgImageUrl = url || backgroundImage || (typeof backgroundImage === 'object' && backgroundImage?.url);
1841
- // If not found in attributes, try to extract from innerHTML
1842
- if (!bgImageUrl && block.innerHTML) {
1843
- // Try to extract img src from innerHTML
1844
- const imgMatch = block.innerHTML.match(/<img[^>]+src=["']([^"']+)["']/i);
1845
- if (imgMatch && imgMatch[1]) {
1846
- bgImageUrl = imgMatch[1];
1847
- }
1848
- // Try background-image in style attribute
1849
- if (!bgImageUrl) {
1850
- const bgMatch = block.innerHTML.match(/background-image:\s*url\(["']?([^"')]+)["']?\)/i);
1851
- if (bgMatch && bgMatch[1]) {
1852
- bgImageUrl = bgMatch[1];
1853
- }
1854
- }
1891
+ // Use the improved extraction function that handles incomplete cloudflareUrl
1892
+ let bgImageUrl = null;
1893
+ // First, try cloudflareUrl if it's valid
1894
+ if (cloudflareUrl && isValidCloudflareUrl(cloudflareUrl)) {
1895
+ bgImageUrl = cloudflareUrl;
1896
+ }
1897
+ // If not valid or not found, try regular attributes
1898
+ if (!bgImageUrl) {
1899
+ bgImageUrl = url || backgroundImage || (typeof backgroundImage === 'object' && backgroundImage?.url) || null;
1855
1900
  }
1856
- // Convert to Cloudflare URL if it's a Cloudflare image, otherwise use as-is
1857
- if (bgImageUrl && isCloudflareImageUrl(bgImageUrl)) {
1858
- // Use full width for cover images
1859
- bgImageUrl = getCloudflareVariantUrl(bgImageUrl, { width: 1920 });
1901
+ // If still not found, use the fallback extraction (from innerHTML)
1902
+ if (!bgImageUrl) {
1903
+ bgImageUrl = extractImageUrlWithFallback(block);
1904
+ }
1905
+ // Convert to Cloudflare URL variant if it's a Cloudflare image
1906
+ if (bgImageUrl) {
1907
+ if (isCloudflareImageUrl(bgImageUrl)) {
1908
+ // Use full width for cover images
1909
+ bgImageUrl = getCloudflareVariantUrl(bgImageUrl, { width: 1920 });
1910
+ }
1860
1911
  }
1861
1912
  // Build alignment classes
1862
1913
  const alignClass = getAlignmentClasses(align);
@@ -1883,20 +1934,42 @@ const Cover = ({ block, children }) => {
1883
1934
  };
1884
1935
  const MediaText = ({ block, children, context }) => {
1885
1936
  const attrs = block.attributes || {};
1886
- const { mediaPosition = 'left', verticalAlignment = 'center', imageFill = false, align = 'wide', } = attrs;
1937
+ const { mediaPosition = 'left', verticalAlignment = 'center', imageFill = false, align, } = attrs;
1887
1938
  // Access innerBlocks to identify media vs content
1888
1939
  const innerBlocks = block.innerBlocks || [];
1889
1940
  // Find media block (image or video)
1890
- const mediaBlockIndex = innerBlocks.findIndex((b) => b.name === 'core/image' || b.name === 'core/video');
1941
+ let mediaBlockIndex = innerBlocks.findIndex((b) => b.name === 'core/image' || b.name === 'core/video');
1891
1942
  // Render children - media-text typically has media as first child, then content
1892
1943
  const childrenArray = React.Children.toArray(children);
1893
- const mediaElement = mediaBlockIndex >= 0 && childrenArray[mediaBlockIndex]
1944
+ let mediaElement = mediaBlockIndex >= 0 && childrenArray[mediaBlockIndex]
1894
1945
  ? childrenArray[mediaBlockIndex]
1895
1946
  : null;
1947
+ // If no media element from innerBlocks, try to extract image URL
1948
+ if (!mediaElement) {
1949
+ const imageUrl = extractImageUrlWithFallback(block);
1950
+ if (imageUrl) {
1951
+ // Convert to Cloudflare variant if it's a Cloudflare URL
1952
+ const finalImageUrl = isCloudflareImageUrl(imageUrl)
1953
+ ? getCloudflareVariantUrl(imageUrl, { width: 1024 })
1954
+ : imageUrl;
1955
+ mediaElement = (jsxRuntimeExports.jsx("img", { src: finalImageUrl, alt: "", className: "w-full h-auto rounded-lg object-cover", loading: "lazy" }));
1956
+ }
1957
+ }
1896
1958
  // Content is all other children
1897
1959
  const contentElements = childrenArray.filter((_, index) => index !== mediaBlockIndex);
1898
- // Build alignment classes
1899
- const alignClass = getAlignmentClasses(align) || 'max-w-7xl mx-auto';
1960
+ // Build alignment classes - ensure proper container width
1961
+ // For 'wide', use max-w-7xl; for 'full', use w-full; default to contained
1962
+ let alignClass;
1963
+ if (align === 'full') {
1964
+ alignClass = 'w-full';
1965
+ }
1966
+ else if (align === 'wide') {
1967
+ alignClass = 'max-w-7xl mx-auto';
1968
+ }
1969
+ else {
1970
+ // Default to contained width (not full width)
1971
+ alignClass = 'container mx-auto';
1972
+ }
1900
1973
  // Vertical alignment classes
1901
1974
  const verticalAlignClass = verticalAlignment === 'top' ? 'items-start' :
1902
1975
  verticalAlignment === 'bottom' ? 'items-end' :
@@ -1905,7 +1978,9 @@ const MediaText = ({ block, children, context }) => {
1905
1978
  const stackClass = 'flex-col md:flex-row';
1906
1979
  // Media position determines order
1907
1980
  const isMediaRight = mediaPosition === 'right';
1908
- return (jsxRuntimeExports.jsx("div", { className: buildClassName(alignClass, 'px-4'), children: jsxRuntimeExports.jsxs("div", { className: buildClassName('flex', stackClass, verticalAlignClass, 'gap-6 lg:gap-12'), children: [jsxRuntimeExports.jsx("div", { className: buildClassName(isMediaRight ? 'order-2' : 'order-1', imageFill ? 'w-full md:w-1/2' : 'flex-shrink-0'), children: mediaElement || jsxRuntimeExports.jsx("div", { className: "bg-gray-200 h-64 rounded-lg" }) }), jsxRuntimeExports.jsx("div", { className: buildClassName(isMediaRight ? 'order-1' : 'order-2', imageFill ? 'w-full md:w-1/2' : 'flex-1', 'space-y-4'), children: contentElements.length > 0 ? contentElements : children })] }) }));
1981
+ // Add section spacing for consistent vertical rhythm
1982
+ const spacingClass = getSectionSpacingClasses();
1983
+ return (jsxRuntimeExports.jsx("div", { className: buildClassName(alignClass, spacingClass, 'px-4'), children: jsxRuntimeExports.jsxs("div", { className: buildClassName('flex', stackClass, verticalAlignClass, 'gap-6 lg:gap-12'), children: [jsxRuntimeExports.jsx("div", { className: buildClassName(isMediaRight ? 'order-2' : 'order-1', imageFill ? 'w-full md:w-1/2' : 'flex-shrink-0 md:w-1/2'), children: mediaElement || jsxRuntimeExports.jsx("div", { className: "bg-gray-200 h-64 rounded-lg" }) }), jsxRuntimeExports.jsx("div", { className: buildClassName(isMediaRight ? 'order-1' : 'order-2', 'md:w-1/2', getContentSpacingClasses()), children: contentElements.length > 0 ? contentElements : children })] }) }));
1909
1984
  };
1910
1985
  const Fallback = ({ block, children }) => {
1911
1986
  // Minimal fallback; do not render innerHTML directly in v1 for safety
@@ -2177,7 +2252,7 @@ class WPErrorBoundary extends React.Component {
2177
2252
 
2178
2253
  /**
2179
2254
  * Extract background image URL from a block
2180
- * Checks various possible sources: url, backgroundImage, innerHTML, featured image
2255
+ * Checks various possible sources: cloudflareUrl, url, backgroundImage, innerHTML, featured image
2181
2256
  */
2182
2257
  function extractBackgroundImage(block, page) {
2183
2258
  const attrs = block.attributes || {};
@@ -2185,34 +2260,17 @@ function extractBackgroundImage(block, page) {
2185
2260
  if (attrs['useFeaturedImage'] === true && page?._embedded?.['wp:featuredmedia']?.[0]?.source_url) {
2186
2261
  return page._embedded['wp:featuredmedia'][0].source_url;
2187
2262
  }
2188
- // Try various attribute keys
2189
- let url = attrs['url'] ||
2190
- attrs['backgroundImage'] ||
2191
- (typeof attrs['backgroundImage'] === 'object' && attrs['backgroundImage']?.url);
2192
- if (typeof url === 'string' && url.trim()) {
2193
- return url.trim();
2194
- }
2195
- // Try to extract from innerHTML if not found in attributes
2196
- if (block.innerHTML) {
2197
- // Try img src from innerHTML
2198
- const imgMatch = block.innerHTML.match(/<img[^>]+src=["']([^"']+)["']/i);
2199
- if (imgMatch && imgMatch[1]) {
2200
- return imgMatch[1];
2201
- }
2202
- // Try background-image in style attribute
2203
- const bgMatch = block.innerHTML.match(/background-image:\s*url\(["']?([^"')]+)["']?\)/i);
2204
- if (bgMatch && bgMatch[1]) {
2205
- return bgMatch[1];
2206
- }
2207
- }
2208
- return null;
2263
+ // Use the improved extraction function that handles incomplete cloudflareUrl
2264
+ // This will check cloudflareUrl first, then innerHTML, then regular attributes
2265
+ return extractImageUrlWithFallback(block);
2209
2266
  }
2210
2267
  /**
2211
2268
  * Extract image URL from a block
2212
2269
  * Returns Cloudflare URL if available, otherwise WordPress URL
2213
2270
  */
2214
2271
  function extractImageUrl(block) {
2215
- return getImageUrl(block);
2272
+ // Use the improved extraction function that handles incomplete cloudflareUrl
2273
+ return extractImageUrlWithFallback(block);
2216
2274
  }
2217
2275
  /**
2218
2276
  * Extract image attributes (url, alt, width, height)
@@ -2596,5 +2654,5 @@ const SectionWrapper = ({ children, background = 'light', spacing = 'medium', co
2596
2654
  return (jsxRuntimeExports.jsx("section", { className: buildClassName(backgroundClasses[finalBackground] || backgroundClasses.light, spacingClasses[finalSpacing] || spacingClasses.medium, containerClasses[finalContainer] || containerClasses.contained, className), children: children }));
2597
2655
  };
2598
2656
 
2599
- export { SectionWrapper, WPContent, WPErrorBoundary, WPPage, buildClassName, convertImageToCloudflareVariant, convertImageUrl, convertImageUrls, createDefaultRegistry, createEnhancedRegistry, extractAlignment, extractBackgroundImage, extractButtonsFromInnerBlocks, extractContent, extractDimRatio, extractFontSize, extractHeadingLevel, extractImageAttributes, extractImageUrl, extractMediaPosition, extractMinHeight, extractOverlayColor, extractSubtitleFromInnerBlocks, extractTextAlign, extractTextAlignFromInnerBlocks, extractTextFromHTML, extractTitle, extractTitleFromInnerBlocks, extractVerticalAlignment, extractVideoIframeFromInnerBlocks, findMatchingMapping, findShortcodes, getAlignmentClasses, getBlockTextContent, getCloudflareVariantUrl, getContainerClasses, getContentSpacingClasses, getFontSizeClasses, getImageAttributes, getImageUrl, getSectionSpacingClasses, getTextAlignClasses, isCloudflareImageUrl, matchesPattern, parseContentPosition, parseGutenbergBlocks, parseShortcodeAttrs, renderNodes, renderTextWithShortcodes };
2657
+ export { SectionWrapper, WPContent, WPErrorBoundary, WPPage, buildClassName, convertImageToCloudflareVariant, convertImageUrl, convertImageUrls, createDefaultRegistry, createEnhancedRegistry, extractAlignment, extractBackgroundImage, extractButtonsFromInnerBlocks, extractContent, extractDimRatio, extractFontSize, extractHeadingLevel, extractImageAttributes, extractImageUrl, extractImageUrlWithFallback, extractMediaPosition, extractMinHeight, extractOverlayColor, extractSubtitleFromInnerBlocks, extractTextAlign, extractTextAlignFromInnerBlocks, extractTextFromHTML, extractTitle, extractTitleFromInnerBlocks, extractVerticalAlignment, extractVideoIframeFromInnerBlocks, findMatchingMapping, findShortcodes, getAlignmentClasses, getBlockTextContent, getCloudflareVariantUrl, getContainerClasses, getContentSpacingClasses, getFontSizeClasses, getImageAttributes, getImageUrl, getSectionSpacingClasses, getTextAlignClasses, isCloudflareImageUrl, isValidCloudflareUrl, matchesPattern, parseContentPosition, parseGutenbergBlocks, parseShortcodeAttrs, renderNodes, renderTextWithShortcodes };
2600
2658
  //# sourceMappingURL=index.esm.js.map