@marvalt/wparser 0.1.59 → 0.1.60

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
@@ -1406,115 +1406,6 @@ function renderBlock(block, registry, key, options, page) {
1406
1406
  return jsxRuntimeExports.jsx(React.Fragment, { children: node }, key);
1407
1407
  }
1408
1408
 
1409
- /**
1410
- * Parse shortcode attributes from a string
1411
- * Supports: [shortcode attr="value" attr2="value2"]
1412
- */
1413
- function parseShortcodeAttrs(attrString) {
1414
- if (!attrString || !attrString.trim()) {
1415
- return {};
1416
- }
1417
- const attrs = {};
1418
- // Match key="value" or key='value' patterns
1419
- const attrRegex = /(\w+)=["']([^"']+)["']/g;
1420
- let match;
1421
- while ((match = attrRegex.exec(attrString)) !== null) {
1422
- attrs[match[1]] = match[2];
1423
- }
1424
- return attrs;
1425
- }
1426
- /**
1427
- * Find all shortcodes in text
1428
- * Supports: [shortcode], [shortcode attr="value"], [shortcode]content[/shortcode]
1429
- */
1430
- function findShortcodes(text) {
1431
- const shortcodes = [];
1432
- // First, find closing tag pairs: [shortcode]content[/shortcode]
1433
- const closingTagRegex = /\[(\w+)(?:\s+([^\]]+))?\](.*?)\[\/\1\]/gs;
1434
- let match;
1435
- const processedRanges = [];
1436
- while ((match = closingTagRegex.exec(text)) !== null) {
1437
- const name = match[1];
1438
- const attrString = match[2] || '';
1439
- const content = match[3] || '';
1440
- const attrs = parseShortcodeAttrs(attrString);
1441
- shortcodes.push({
1442
- name,
1443
- attrs,
1444
- content,
1445
- fullMatch: match[0],
1446
- startIndex: match.index,
1447
- endIndex: match.index + match[0].length,
1448
- });
1449
- processedRanges.push({
1450
- start: match.index,
1451
- end: match.index + match[0].length,
1452
- });
1453
- }
1454
- // Then, find self-closing: [shortcode attr="value"]
1455
- // Skip ranges already processed by closing tags
1456
- const selfClosingRegex = /\[(\w+)(?:\s+([^\]]+))?\]/g;
1457
- let selfClosingMatch;
1458
- while ((selfClosingMatch = selfClosingRegex.exec(text)) !== null) {
1459
- // Check if this match is within a processed range
1460
- const isProcessed = processedRanges.some(range => selfClosingMatch.index >= range.start && selfClosingMatch.index < range.end);
1461
- if (isProcessed) {
1462
- continue;
1463
- }
1464
- const name = selfClosingMatch[1];
1465
- const attrString = selfClosingMatch[2] || '';
1466
- const attrs = parseShortcodeAttrs(attrString);
1467
- shortcodes.push({
1468
- name,
1469
- attrs,
1470
- fullMatch: selfClosingMatch[0],
1471
- startIndex: selfClosingMatch.index,
1472
- endIndex: selfClosingMatch.index + selfClosingMatch[0].length,
1473
- });
1474
- }
1475
- // Sort by start index
1476
- shortcodes.sort((a, b) => a.startIndex - b.startIndex);
1477
- return shortcodes;
1478
- }
1479
- /**
1480
- * Render text with shortcodes replaced by React components
1481
- */
1482
- function renderTextWithShortcodes(text, registry) {
1483
- const shortcodes = findShortcodes(text);
1484
- if (shortcodes.length === 0) {
1485
- return [text];
1486
- }
1487
- const parts = [];
1488
- let lastIndex = 0;
1489
- for (const shortcode of shortcodes) {
1490
- // Add text before shortcode
1491
- if (shortcode.startIndex > lastIndex) {
1492
- const textBefore = text.substring(lastIndex, shortcode.startIndex);
1493
- if (textBefore) {
1494
- parts.push(textBefore);
1495
- }
1496
- }
1497
- // Render shortcode
1498
- const renderer = registry.shortcodes[shortcode.name];
1499
- if (renderer) {
1500
- parts.push(jsxRuntimeExports.jsx(React.Fragment, { children: renderer(shortcode.attrs, shortcode.content) }, `shortcode-${shortcode.startIndex}`));
1501
- }
1502
- else {
1503
- // Keep original shortcode if no renderer found
1504
- parts.push(shortcode.fullMatch);
1505
- }
1506
- lastIndex = shortcode.endIndex;
1507
- }
1508
- // Add remaining text
1509
- if (lastIndex < text.length) {
1510
- const remainingText = text.substring(lastIndex);
1511
- if (remainingText) {
1512
- parts.push(remainingText);
1513
- }
1514
- }
1515
- return parts;
1516
- }
1517
-
1518
1409
  /**
1519
1410
  * Content extraction utilities for WordPress blocks
1520
1411
  * Extracts text content from various block formats
@@ -2374,109 +2265,24 @@ function buildClassName(...classes) {
2374
2265
  function getSpacing(spacingConfig, key, fallback) {
2375
2266
  return spacingConfig?.[key] || fallback;
2376
2267
  }
2377
- // Helper function to get heading spacing
2378
- function getHeadingSpacing(spacingConfig, level) {
2379
- const headingConfig = spacingConfig?.heading;
2380
- if (level === 1)
2381
- return headingConfig?.h1 || 'mt-10 mb-8';
2382
- if (level === 2)
2383
- return headingConfig?.h2 || 'mt-8 mb-6';
2384
- return headingConfig?.h3 || 'mt-6 mb-4';
2385
- }
2386
- const Paragraph = ({ block, context }) => {
2387
- const attrs = block.attributes || {};
2388
- const textAlign = getTextAlignClasses(attrs['align']);
2389
- const spacing = getSpacing(context.spacingConfig || context.registry.spacingConfig, 'paragraph', 'my-6');
2390
- // Extract text color if specified, otherwise inherit from parent (CSS variables handle default)
2391
- const textColor = extractTextColor(block, context);
2392
- const textColorClass = textColor || ''; // Don't hardcode - let CSS variables handle default
2393
- // Extract font size if specified
2394
- const fontSize = extractFontSize(block, context);
2395
- const fontSizeClass = fontSize ? getFontSizeClasses(fontSize) : '';
2396
- // Check if innerHTML contains HTML elements (like links, strong, em, etc.)
2397
- const hasHTML = block.innerHTML && /<[a-z][\s\S]*>/i.test(block.innerHTML);
2398
- // Check if content contains shortcodes (check both HTML and text content)
2399
- const htmlContent = block.innerHTML || '';
2400
- const textContent = getBlockTextContent(block);
2401
- const hasShortcodes = /\[(\w+)/.test(htmlContent) || /\[(\w+)/.test(textContent);
2402
- // If shortcodes are present, always process them (even if HTML is also present)
2403
- if (hasShortcodes && context.registry.shortcodes) {
2404
- // Process shortcodes from text content
2405
- const parts = renderTextWithShortcodes(textContent, context.registry);
2406
- // Check if any part is a block-level element (section, div, etc.)
2407
- const isBlockLevelElement = (element) => {
2408
- if (!React.isValidElement(element)) {
2409
- return false;
2410
- }
2411
- const type = element.type;
2412
- // Check for block-level HTML elements
2413
- if (typeof type === 'string' && ['section', 'div', 'article', 'header', 'footer', 'aside', 'nav'].includes(type)) {
2414
- return true;
2415
- }
2416
- // Check if it's React.Fragment - recursively check its children
2417
- if (type === React.Fragment) {
2418
- const fragmentProps = element.props;
2419
- const children = React.Children.toArray(fragmentProps.children);
2420
- return children.some((child) => isBlockLevelElement(child));
2421
- }
2422
- // Check if it's a React component (likely block-level)
2423
- if (typeof type === 'function' || (typeof type === 'object' && type !== null && type !== React.Fragment)) {
2424
- return true;
2425
- }
2426
- return false;
2427
- };
2428
- const hasBlockLevelContent = React.Children.toArray(parts).some((part) => isBlockLevelElement(part));
2429
- if (hasBlockLevelContent) {
2430
- // Render block-level content without <p> wrapper, but add spacing wrapper
2431
- const fontSize = extractFontSize(block, context);
2432
- const fontSizeClass = fontSize ? getFontSizeClasses(fontSize) : '';
2433
- return jsxRuntimeExports.jsx("div", { className: buildClassName(spacing, textColorClass, fontSizeClass), children: parts });
2434
- }
2435
- // Render shortcode parts inside paragraph (shortcodes are processed as React components)
2436
- return jsxRuntimeExports.jsx("p", { className: buildClassName(spacing, textAlign, textColorClass, fontSizeClass), children: parts });
2437
- }
2438
- // If innerHTML contains HTML elements but no shortcodes, render it directly (preserves links, formatting, etc.)
2439
- if (hasHTML && block.innerHTML) {
2440
- // Check if innerHTML already contains a paragraph tag - if so, extract content to avoid double nesting
2441
- const trimmedContent = htmlContent.trim();
2442
- if (trimmedContent.startsWith('<p') && trimmedContent.endsWith('</p>')) {
2443
- // Extract content from paragraph tag
2444
- const contentMatch = trimmedContent.match(/<p[^>]*>(.*?)<\/p>/s);
2445
- if (contentMatch) {
2446
- const innerContent = contentMatch[1];
2447
- // Check if inner content also has a paragraph (double nesting)
2448
- if (innerContent.trim().startsWith('<p')) {
2449
- // Render as div to avoid invalid HTML
2450
- return jsxRuntimeExports.jsx("div", { className: buildClassName(spacing, textAlign, textColorClass, fontSizeClass), dangerouslySetInnerHTML: { __html: innerContent } });
2451
- }
2452
- // Render the extracted content in a paragraph
2453
- return jsxRuntimeExports.jsx("p", { className: buildClassName(spacing, textAlign, textColorClass, fontSizeClass), dangerouslySetInnerHTML: { __html: innerContent } });
2454
- }
2455
- }
2456
- return jsxRuntimeExports.jsx("p", { className: buildClassName(spacing, textAlign, textColorClass, fontSizeClass), dangerouslySetInnerHTML: { __html: htmlContent } });
2268
+ const Paragraph = ({ block }) => {
2269
+ // If innerHTML exists, render it as-is to preserve WP classes/styles
2270
+ if (block.innerHTML) {
2271
+ return jsxRuntimeExports.jsx("div", { dangerouslySetInnerHTML: { __html: block.innerHTML } });
2457
2272
  }
2458
- // No HTML and no shortcodes, just render plain text content
2459
- return jsxRuntimeExports.jsx("p", { className: buildClassName(spacing, textAlign, textColorClass, fontSizeClass), children: textContent });
2273
+ const textContent = getBlockTextContent(block);
2274
+ return jsxRuntimeExports.jsx("p", { children: textContent });
2460
2275
  };
2461
- const Heading = ({ block, children, context }) => {
2276
+ const Heading = ({ block, children }) => {
2277
+ // If innerHTML exists, render it as-is to preserve WP classes/styles (font sizes, weights)
2278
+ if (block.innerHTML) {
2279
+ return jsxRuntimeExports.jsx("div", { dangerouslySetInnerHTML: { __html: block.innerHTML } });
2280
+ }
2462
2281
  const attrs = block.attributes || {};
2463
2282
  const { level = 2 } = attrs;
2464
2283
  const content = getBlockTextContent(block);
2465
- const textAlign = getTextAlignClasses(attrs['textAlign']);
2466
- // Extract font size - check both fontSize attribute and style.typography.fontSize
2467
- const extractedFontSize = extractFontSize(block, context);
2468
- const fontSize = extractedFontSize ? getFontSizeClasses(extractedFontSize) : '';
2469
2284
  const Tag = `h${Math.min(Math.max(Number(level) || 2, 1), 6)}`;
2470
- // Use CSS variables for font sizes (defined in index.css) instead of Tailwind classes
2471
- // Only use Tailwind class if explicitly set via fontSize attribute
2472
- const sizeClass = fontSize || ''; // Let CSS variables handle default sizes
2473
- // Get spacing from config with improved defaults
2474
- const spacingClass = getHeadingSpacing(context.spacingConfig || context.registry.spacingConfig, level);
2475
- // Extract text color if specified, otherwise inherit from parent (CSS variables handle default)
2476
- const textColor = extractTextColor(block, context);
2477
- const textColorClass = textColor || ''; // Don't hardcode - let CSS variables handle default
2478
- // Don't hardcode font-bold - let CSS variables handle font weight from WordPress
2479
- return (jsxRuntimeExports.jsx(Tag, { className: buildClassName(textColorClass, sizeClass, textAlign, spacingClass), children: children ?? content }));
2285
+ return jsxRuntimeExports.jsx(Tag, { children: children ?? content });
2480
2286
  };
2481
2287
  const Image = ({ block, context }) => {
2482
2288
  const imageAttrs = getImageAttributes(block);
@@ -3139,6 +2945,115 @@ class WPErrorBoundary extends React.Component {
3139
2945
  }
3140
2946
  }
3141
2947
 
2948
+ /**
2949
+ * Parse shortcode attributes from a string
2950
+ * Supports: [shortcode attr="value" attr2="value2"]
2951
+ */
2952
+ function parseShortcodeAttrs(attrString) {
2953
+ if (!attrString || !attrString.trim()) {
2954
+ return {};
2955
+ }
2956
+ const attrs = {};
2957
+ // Match key="value" or key='value' patterns
2958
+ const attrRegex = /(\w+)=["']([^"']+)["']/g;
2959
+ let match;
2960
+ while ((match = attrRegex.exec(attrString)) !== null) {
2961
+ attrs[match[1]] = match[2];
2962
+ }
2963
+ return attrs;
2964
+ }
2965
+ /**
2966
+ * Find all shortcodes in text
2967
+ * Supports: [shortcode], [shortcode attr="value"], [shortcode]content[/shortcode]
2968
+ */
2969
+ function findShortcodes(text) {
2970
+ const shortcodes = [];
2971
+ // First, find closing tag pairs: [shortcode]content[/shortcode]
2972
+ const closingTagRegex = /\[(\w+)(?:\s+([^\]]+))?\](.*?)\[\/\1\]/gs;
2973
+ let match;
2974
+ const processedRanges = [];
2975
+ while ((match = closingTagRegex.exec(text)) !== null) {
2976
+ const name = match[1];
2977
+ const attrString = match[2] || '';
2978
+ const content = match[3] || '';
2979
+ const attrs = parseShortcodeAttrs(attrString);
2980
+ shortcodes.push({
2981
+ name,
2982
+ attrs,
2983
+ content,
2984
+ fullMatch: match[0],
2985
+ startIndex: match.index,
2986
+ endIndex: match.index + match[0].length,
2987
+ });
2988
+ processedRanges.push({
2989
+ start: match.index,
2990
+ end: match.index + match[0].length,
2991
+ });
2992
+ }
2993
+ // Then, find self-closing: [shortcode attr="value"]
2994
+ // Skip ranges already processed by closing tags
2995
+ const selfClosingRegex = /\[(\w+)(?:\s+([^\]]+))?\]/g;
2996
+ let selfClosingMatch;
2997
+ while ((selfClosingMatch = selfClosingRegex.exec(text)) !== null) {
2998
+ // Check if this match is within a processed range
2999
+ const isProcessed = processedRanges.some(range => selfClosingMatch.index >= range.start && selfClosingMatch.index < range.end);
3000
+ if (isProcessed) {
3001
+ continue;
3002
+ }
3003
+ const name = selfClosingMatch[1];
3004
+ const attrString = selfClosingMatch[2] || '';
3005
+ const attrs = parseShortcodeAttrs(attrString);
3006
+ shortcodes.push({
3007
+ name,
3008
+ attrs,
3009
+ fullMatch: selfClosingMatch[0],
3010
+ startIndex: selfClosingMatch.index,
3011
+ endIndex: selfClosingMatch.index + selfClosingMatch[0].length,
3012
+ });
3013
+ }
3014
+ // Sort by start index
3015
+ shortcodes.sort((a, b) => a.startIndex - b.startIndex);
3016
+ return shortcodes;
3017
+ }
3018
+ /**
3019
+ * Render text with shortcodes replaced by React components
3020
+ */
3021
+ function renderTextWithShortcodes(text, registry) {
3022
+ const shortcodes = findShortcodes(text);
3023
+ if (shortcodes.length === 0) {
3024
+ return [text];
3025
+ }
3026
+ const parts = [];
3027
+ let lastIndex = 0;
3028
+ for (const shortcode of shortcodes) {
3029
+ // Add text before shortcode
3030
+ if (shortcode.startIndex > lastIndex) {
3031
+ const textBefore = text.substring(lastIndex, shortcode.startIndex);
3032
+ if (textBefore) {
3033
+ parts.push(textBefore);
3034
+ }
3035
+ }
3036
+ // Render shortcode
3037
+ const renderer = registry.shortcodes[shortcode.name];
3038
+ if (renderer) {
3039
+ parts.push(jsxRuntimeExports.jsx(React.Fragment, { children: renderer(shortcode.attrs, shortcode.content) }, `shortcode-${shortcode.startIndex}`));
3040
+ }
3041
+ else {
3042
+ // Keep original shortcode if no renderer found
3043
+ parts.push(shortcode.fullMatch);
3044
+ }
3045
+ lastIndex = shortcode.endIndex;
3046
+ }
3047
+ // Add remaining text
3048
+ if (lastIndex < text.length) {
3049
+ const remainingText = text.substring(lastIndex);
3050
+ if (remainingText) {
3051
+ parts.push(remainingText);
3052
+ }
3053
+ }
3054
+ return parts;
3055
+ }
3056
+
3142
3057
  /**
3143
3058
  * Convert image URL with optional Cloudflare variant transformation
3144
3059
  *