@marvalt/wparser 0.1.34 → 0.1.38

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.cjs CHANGED
@@ -1592,16 +1592,41 @@ function getImageUrl(block) {
1592
1592
  return null;
1593
1593
  }
1594
1594
  /**
1595
- * Extract image attributes (alt, width, height) from block
1595
+ * Extract image attributes (alt, width, height, alignment) from block
1596
1596
  */
1597
1597
  function getImageAttributes(block) {
1598
1598
  const attrs = block.attributes || {};
1599
1599
  const url = getImageUrl(block);
1600
+ // Extract width - can be number or string like "640px"
1601
+ let width;
1602
+ const widthAttr = attrs['width'];
1603
+ if (widthAttr) {
1604
+ if (typeof widthAttr === 'number') {
1605
+ width = widthAttr;
1606
+ }
1607
+ else if (typeof widthAttr === 'string') {
1608
+ // Try to parse number from string like "640px" or "640"
1609
+ const numMatch = widthAttr.match(/^(\d+)/);
1610
+ if (numMatch) {
1611
+ width = parseInt(numMatch[1], 10);
1612
+ }
1613
+ else {
1614
+ // Keep as string if it's not a simple number
1615
+ width = widthAttr;
1616
+ }
1617
+ }
1618
+ }
1619
+ // Extract alignment
1620
+ const align = attrs['align'];
1621
+ const alignValue = align === 'left' || align === 'center' || align === 'right'
1622
+ ? align
1623
+ : undefined;
1600
1624
  return {
1601
1625
  url,
1602
1626
  alt: attrs['alt'] || '',
1603
- width: attrs['width'] ? Number(attrs['width']) : undefined,
1627
+ width,
1604
1628
  height: attrs['height'] ? Number(attrs['height']) : undefined,
1629
+ align: alignValue,
1605
1630
  };
1606
1631
  }
1607
1632
  /**
@@ -1733,7 +1758,17 @@ function extractImageUrl(block) {
1733
1758
  * Extract image attributes (url, alt, width, height)
1734
1759
  */
1735
1760
  function extractImageAttributes(block) {
1736
- return getImageAttributes(block);
1761
+ const attrs = getImageAttributes(block);
1762
+ // Convert width to number if it's a string
1763
+ const width = typeof attrs.width === 'string'
1764
+ ? (parseInt(attrs.width, 10) || undefined)
1765
+ : attrs.width;
1766
+ return {
1767
+ url: attrs.url,
1768
+ alt: attrs.alt,
1769
+ width,
1770
+ height: attrs.height,
1771
+ };
1737
1772
  }
1738
1773
  /**
1739
1774
  * Extract title/heading text from a block
@@ -2237,16 +2272,63 @@ function getHeadingSpacing(spacingConfig, level) {
2237
2272
  return headingConfig?.h3 || 'mt-6 mb-4';
2238
2273
  }
2239
2274
  const Paragraph = ({ block, context }) => {
2240
- const content = getBlockTextContent(block);
2241
2275
  const attrs = block.attributes || {};
2242
2276
  const textAlign = getTextAlignClasses(attrs['align']);
2243
2277
  const spacing = getSpacing(context.spacingConfig || context.registry.spacingConfig, 'paragraph', 'my-6');
2244
- // Check if content contains shortcodes
2278
+ // Check if innerHTML contains HTML elements (like links, strong, em, etc.)
2279
+ const hasHTML = block.innerHTML && /<[a-z][\s\S]*>/i.test(block.innerHTML);
2280
+ // If innerHTML contains HTML elements, render it directly (preserves links, formatting, etc.)
2281
+ if (hasHTML && block.innerHTML) {
2282
+ const htmlContent = block.innerHTML;
2283
+ // Check if HTML contains shortcodes
2284
+ const hasShortcodes = /\[(\w+)/.test(htmlContent);
2285
+ if (hasShortcodes && context.registry.shortcodes) {
2286
+ // For HTML with shortcodes, we need to process shortcodes first
2287
+ // Extract text content to process shortcodes, then reconstruct
2288
+ const textContent = getBlockTextContent(block);
2289
+ const parts = renderTextWithShortcodes(textContent, context.registry);
2290
+ // Check if any part is a block-level element (section, div, etc.)
2291
+ const isBlockLevelElement = (element) => {
2292
+ if (!React.isValidElement(element)) {
2293
+ return false;
2294
+ }
2295
+ const type = element.type;
2296
+ // Check for block-level HTML elements
2297
+ if (typeof type === 'string' && ['section', 'div', 'article', 'header', 'footer', 'aside', 'nav'].includes(type)) {
2298
+ return true;
2299
+ }
2300
+ // Check if it's React.Fragment - recursively check its children
2301
+ if (type === React.Fragment) {
2302
+ const fragmentProps = element.props;
2303
+ const children = React.Children.toArray(fragmentProps.children);
2304
+ return children.some((child) => isBlockLevelElement(child));
2305
+ }
2306
+ // Check if it's a React component (likely block-level)
2307
+ if (typeof type === 'function' || (typeof type === 'object' && type !== null && type !== React.Fragment)) {
2308
+ return true;
2309
+ }
2310
+ return false;
2311
+ };
2312
+ const hasBlockLevelContent = React.Children.toArray(parts).some((part) => isBlockLevelElement(part));
2313
+ if (hasBlockLevelContent) {
2314
+ // Render block-level content without <p> wrapper, but add spacing wrapper
2315
+ return jsxRuntimeExports.jsx("div", { className: spacing, children: parts });
2316
+ }
2317
+ // For mixed HTML and shortcodes, render shortcode parts but this is complex
2318
+ // For now, fall back to rendering HTML with shortcodes processed in text
2319
+ // This is a limitation - shortcodes in HTML paragraphs with links won't work perfectly
2320
+ // But links will be preserved
2321
+ return jsxRuntimeExports.jsx("p", { className: buildClassName(spacing, textAlign), dangerouslySetInnerHTML: { __html: htmlContent } });
2322
+ }
2323
+ // No shortcodes, just render HTML directly (preserves links, formatting, etc.)
2324
+ return jsxRuntimeExports.jsx("p", { className: buildClassName(spacing, textAlign), dangerouslySetInnerHTML: { __html: htmlContent } });
2325
+ }
2326
+ // No HTML, use text content (existing logic for shortcodes)
2327
+ const content = getBlockTextContent(block);
2245
2328
  const hasShortcodes = /\[(\w+)/.test(content);
2246
2329
  if (hasShortcodes && context.registry.shortcodes) {
2247
2330
  const parts = renderTextWithShortcodes(content, context.registry);
2248
2331
  // Check if any part is a block-level element (section, div, etc.)
2249
- // If so, render without wrapping in <p> to avoid DOM nesting violations
2250
2332
  const isBlockLevelElement = (element) => {
2251
2333
  if (!React.isValidElement(element)) {
2252
2334
  return false;
@@ -2263,7 +2345,6 @@ const Paragraph = ({ block, context }) => {
2263
2345
  return children.some((child) => isBlockLevelElement(child));
2264
2346
  }
2265
2347
  // Check if it's a React component (likely block-level)
2266
- // Most custom components render block-level content
2267
2348
  if (typeof type === 'function' || (typeof type === 'object' && type !== null && type !== React.Fragment)) {
2268
2349
  return true;
2269
2350
  }
@@ -2300,15 +2381,48 @@ const Image = ({ block, context }) => {
2300
2381
  const imageAttrs = getImageAttributes(block);
2301
2382
  if (!imageAttrs.url)
2302
2383
  return null;
2384
+ // Extract width for Cloudflare variant (use numeric value)
2385
+ const numericWidth = typeof imageAttrs.width === 'number'
2386
+ ? imageAttrs.width
2387
+ : (typeof imageAttrs.width === 'string' ? parseInt(imageAttrs.width, 10) || 1024 : 1024);
2303
2388
  // Use Cloudflare variant URL if it's a Cloudflare image
2304
2389
  let imageUrl = imageAttrs.url;
2305
2390
  if (isCloudflareImageUrl(imageUrl)) {
2306
- const width = imageAttrs.width || 1024;
2307
2391
  const height = imageAttrs.height;
2308
- imageUrl = getCloudflareVariantUrl(imageUrl, { width, height });
2392
+ imageUrl = getCloudflareVariantUrl(imageUrl, { width: numericWidth, height });
2309
2393
  }
2310
2394
  const spacing = getSpacing(context.spacingConfig || context.registry.spacingConfig, 'image', 'my-6');
2311
- return (jsxRuntimeExports.jsx("img", { src: imageUrl, alt: imageAttrs.alt, width: imageAttrs.width, height: imageAttrs.height, className: buildClassName('w-full h-auto rounded-lg object-cover', spacing), style: { maxWidth: '100%', height: 'auto' }, loading: "lazy" }));
2395
+ // Handle alignment (left, center, right)
2396
+ let alignClass = '';
2397
+ if (imageAttrs.align === 'left') {
2398
+ alignClass = 'mr-auto';
2399
+ }
2400
+ else if (imageAttrs.align === 'center') {
2401
+ alignClass = 'mx-auto';
2402
+ }
2403
+ else if (imageAttrs.align === 'right') {
2404
+ alignClass = 'ml-auto';
2405
+ }
2406
+ // Handle width - if specified, use it; otherwise use w-full
2407
+ let widthClass = 'w-full';
2408
+ let widthStyle = {};
2409
+ if (imageAttrs.width) {
2410
+ if (typeof imageAttrs.width === 'string' && imageAttrs.width.includes('px')) {
2411
+ // Use inline style for pixel values
2412
+ widthStyle.width = imageAttrs.width;
2413
+ widthClass = ''; // Don't use w-full if we have explicit width
2414
+ }
2415
+ else if (typeof imageAttrs.width === 'number') {
2416
+ // Use inline style for numeric pixel values
2417
+ widthStyle.width = `${imageAttrs.width}px`;
2418
+ widthClass = ''; // Don't use w-full if we have explicit width
2419
+ }
2420
+ }
2421
+ // Build image classes - include shadow (app can override via registry)
2422
+ // Default shadow is shadow-md, but app can customize
2423
+ const imageClasses = buildClassName('h-auto rounded-lg object-cover shadow-md', // shadow-md is default, app can override
2424
+ widthClass, alignClass, spacing);
2425
+ return (jsxRuntimeExports.jsx("img", { src: imageUrl, alt: imageAttrs.alt, width: typeof imageAttrs.width === 'number' ? imageAttrs.width : undefined, height: imageAttrs.height, className: imageClasses, style: { maxWidth: '100%', height: 'auto', ...widthStyle }, loading: "lazy" }));
2312
2426
  };
2313
2427
  const List = ({ block, children, context }) => {
2314
2428
  const attrs = block.attributes || {};