@marvalt/wparser 0.1.0 → 0.1.4
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/README.md +2 -0
- package/dist/index.cjs +183 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +35 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +181 -2
- package/dist/index.esm.js.map +1 -1
- package/dist/registry/defaultRegistry.d.ts.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/shortcodeParser.d.ts +29 -0
- package/dist/utils/shortcodeParser.d.ts.map +1 -0
- package/package.json +2 -3
package/dist/index.esm.js
CHANGED
|
@@ -1395,8 +1395,123 @@ function renderBlock(block, registry, key, options) {
|
|
|
1395
1395
|
return jsxRuntimeExports.jsx(React.Fragment, { children: node }, key);
|
|
1396
1396
|
}
|
|
1397
1397
|
|
|
1398
|
-
|
|
1398
|
+
/**
|
|
1399
|
+
* Parse shortcode attributes from a string
|
|
1400
|
+
* Supports: [shortcode attr="value" attr2="value2"]
|
|
1401
|
+
*/
|
|
1402
|
+
function parseShortcodeAttrs(attrString) {
|
|
1403
|
+
if (!attrString || !attrString.trim()) {
|
|
1404
|
+
return {};
|
|
1405
|
+
}
|
|
1406
|
+
const attrs = {};
|
|
1407
|
+
// Match key="value" or key='value' patterns
|
|
1408
|
+
const attrRegex = /(\w+)=["']([^"']+)["']/g;
|
|
1409
|
+
let match;
|
|
1410
|
+
while ((match = attrRegex.exec(attrString)) !== null) {
|
|
1411
|
+
attrs[match[1]] = match[2];
|
|
1412
|
+
}
|
|
1413
|
+
return attrs;
|
|
1414
|
+
}
|
|
1415
|
+
/**
|
|
1416
|
+
* Find all shortcodes in text
|
|
1417
|
+
* Supports: [shortcode], [shortcode attr="value"], [shortcode]content[/shortcode]
|
|
1418
|
+
*/
|
|
1419
|
+
function findShortcodes(text) {
|
|
1420
|
+
const shortcodes = [];
|
|
1421
|
+
// First, find closing tag pairs: [shortcode]content[/shortcode]
|
|
1422
|
+
const closingTagRegex = /\[(\w+)(?:\s+([^\]]+))?\](.*?)\[\/\1\]/gs;
|
|
1423
|
+
let match;
|
|
1424
|
+
const processedRanges = [];
|
|
1425
|
+
while ((match = closingTagRegex.exec(text)) !== null) {
|
|
1426
|
+
const name = match[1];
|
|
1427
|
+
const attrString = match[2] || '';
|
|
1428
|
+
const content = match[3] || '';
|
|
1429
|
+
const attrs = parseShortcodeAttrs(attrString);
|
|
1430
|
+
shortcodes.push({
|
|
1431
|
+
name,
|
|
1432
|
+
attrs,
|
|
1433
|
+
content,
|
|
1434
|
+
fullMatch: match[0],
|
|
1435
|
+
startIndex: match.index,
|
|
1436
|
+
endIndex: match.index + match[0].length,
|
|
1437
|
+
});
|
|
1438
|
+
processedRanges.push({
|
|
1439
|
+
start: match.index,
|
|
1440
|
+
end: match.index + match[0].length,
|
|
1441
|
+
});
|
|
1442
|
+
}
|
|
1443
|
+
// Then, find self-closing: [shortcode attr="value"]
|
|
1444
|
+
// Skip ranges already processed by closing tags
|
|
1445
|
+
const selfClosingRegex = /\[(\w+)(?:\s+([^\]]+))?\]/g;
|
|
1446
|
+
let selfClosingMatch;
|
|
1447
|
+
while ((selfClosingMatch = selfClosingRegex.exec(text)) !== null) {
|
|
1448
|
+
// Check if this match is within a processed range
|
|
1449
|
+
const isProcessed = processedRanges.some(range => selfClosingMatch.index >= range.start && selfClosingMatch.index < range.end);
|
|
1450
|
+
if (isProcessed) {
|
|
1451
|
+
continue;
|
|
1452
|
+
}
|
|
1453
|
+
const name = selfClosingMatch[1];
|
|
1454
|
+
const attrString = selfClosingMatch[2] || '';
|
|
1455
|
+
const attrs = parseShortcodeAttrs(attrString);
|
|
1456
|
+
shortcodes.push({
|
|
1457
|
+
name,
|
|
1458
|
+
attrs,
|
|
1459
|
+
fullMatch: selfClosingMatch[0],
|
|
1460
|
+
startIndex: selfClosingMatch.index,
|
|
1461
|
+
endIndex: selfClosingMatch.index + selfClosingMatch[0].length,
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
// Sort by start index
|
|
1465
|
+
shortcodes.sort((a, b) => a.startIndex - b.startIndex);
|
|
1466
|
+
return shortcodes;
|
|
1467
|
+
}
|
|
1468
|
+
/**
|
|
1469
|
+
* Render text with shortcodes replaced by React components
|
|
1470
|
+
*/
|
|
1471
|
+
function renderTextWithShortcodes(text, registry) {
|
|
1472
|
+
const shortcodes = findShortcodes(text);
|
|
1473
|
+
if (shortcodes.length === 0) {
|
|
1474
|
+
return [text];
|
|
1475
|
+
}
|
|
1476
|
+
const parts = [];
|
|
1477
|
+
let lastIndex = 0;
|
|
1478
|
+
for (const shortcode of shortcodes) {
|
|
1479
|
+
// Add text before shortcode
|
|
1480
|
+
if (shortcode.startIndex > lastIndex) {
|
|
1481
|
+
const textBefore = text.substring(lastIndex, shortcode.startIndex);
|
|
1482
|
+
if (textBefore) {
|
|
1483
|
+
parts.push(textBefore);
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
// Render shortcode
|
|
1487
|
+
const renderer = registry.shortcodes[shortcode.name];
|
|
1488
|
+
if (renderer) {
|
|
1489
|
+
parts.push(jsxRuntimeExports.jsx(React.Fragment, { children: renderer(shortcode.attrs, shortcode.content) }, `shortcode-${shortcode.startIndex}`));
|
|
1490
|
+
}
|
|
1491
|
+
else {
|
|
1492
|
+
// Keep original shortcode if no renderer found
|
|
1493
|
+
parts.push(shortcode.fullMatch);
|
|
1494
|
+
}
|
|
1495
|
+
lastIndex = shortcode.endIndex;
|
|
1496
|
+
}
|
|
1497
|
+
// Add remaining text
|
|
1498
|
+
if (lastIndex < text.length) {
|
|
1499
|
+
const remainingText = text.substring(lastIndex);
|
|
1500
|
+
if (remainingText) {
|
|
1501
|
+
parts.push(remainingText);
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
return parts;
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
const Paragraph = ({ block, context }) => {
|
|
1399
1508
|
const content = getString(block);
|
|
1509
|
+
// Check if content contains shortcodes
|
|
1510
|
+
const hasShortcodes = /\[(\w+)/.test(content);
|
|
1511
|
+
if (hasShortcodes && context.registry.shortcodes) {
|
|
1512
|
+
const parts = renderTextWithShortcodes(content, context.registry);
|
|
1513
|
+
return jsxRuntimeExports.jsx("p", { className: "prose-p", children: parts });
|
|
1514
|
+
}
|
|
1400
1515
|
return jsxRuntimeExports.jsx("p", { className: "prose-p", children: content });
|
|
1401
1516
|
};
|
|
1402
1517
|
const Heading = ({ block, children }) => {
|
|
@@ -1435,6 +1550,59 @@ const ButtonBlock = ({ block }) => {
|
|
|
1435
1550
|
return null;
|
|
1436
1551
|
return (jsxRuntimeExports.jsx("a", { href: url, className: "inline-flex items-center rounded-md bg-primary px-4 py-2 text-white", children: text || 'Learn more' }));
|
|
1437
1552
|
};
|
|
1553
|
+
const Cover = ({ block, children }) => {
|
|
1554
|
+
const attrs = block.attributes || {};
|
|
1555
|
+
const { url, backgroundImage, overlayColor, dimRatio = 0, align = 'full', minHeight, hasParallax, } = attrs;
|
|
1556
|
+
// Get background image URL from various possible sources
|
|
1557
|
+
const bgImageUrl = url || backgroundImage || (typeof backgroundImage === 'object' && backgroundImage?.url);
|
|
1558
|
+
// Build alignment classes
|
|
1559
|
+
const alignClass = align === 'full' ? 'w-full' : align === 'wide' ? 'max-w-7xl mx-auto' : '';
|
|
1560
|
+
// Build style object
|
|
1561
|
+
const style = {};
|
|
1562
|
+
if (minHeight) {
|
|
1563
|
+
style.minHeight = typeof minHeight === 'number' ? `${minHeight}px` : minHeight;
|
|
1564
|
+
}
|
|
1565
|
+
if (bgImageUrl) {
|
|
1566
|
+
style.backgroundImage = `url(${bgImageUrl})`;
|
|
1567
|
+
style.backgroundSize = 'cover';
|
|
1568
|
+
style.backgroundPosition = 'center';
|
|
1569
|
+
if (hasParallax) {
|
|
1570
|
+
style.backgroundAttachment = 'fixed';
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
// Calculate overlay opacity
|
|
1574
|
+
const overlayOpacity = dimRatio / 100;
|
|
1575
|
+
return (jsxRuntimeExports.jsxs("div", { className: `relative ${alignClass}`, style: style, children: [overlayOpacity > 0 && (jsxRuntimeExports.jsx("span", { className: "absolute inset-0", style: {
|
|
1576
|
+
backgroundColor: overlayColor || '#000000',
|
|
1577
|
+
opacity: overlayOpacity,
|
|
1578
|
+
}, "aria-hidden": "true" })), jsxRuntimeExports.jsx("div", { className: "relative z-10 container mx-auto px-4 py-12", children: children })] }));
|
|
1579
|
+
};
|
|
1580
|
+
const MediaText = ({ block, children, context }) => {
|
|
1581
|
+
const attrs = block.attributes || {};
|
|
1582
|
+
const { mediaPosition = 'left', verticalAlignment = 'center', imageFill = false, align = 'wide', } = attrs;
|
|
1583
|
+
// Access innerBlocks to identify media vs content
|
|
1584
|
+
const innerBlocks = block.innerBlocks || [];
|
|
1585
|
+
// Find media block (image or video)
|
|
1586
|
+
const mediaBlockIndex = innerBlocks.findIndex((b) => b.name === 'core/image' || b.name === 'core/video');
|
|
1587
|
+
// Render children - media-text typically has media as first child, then content
|
|
1588
|
+
const childrenArray = React.Children.toArray(children);
|
|
1589
|
+
const mediaElement = mediaBlockIndex >= 0 && childrenArray[mediaBlockIndex]
|
|
1590
|
+
? childrenArray[mediaBlockIndex]
|
|
1591
|
+
: null;
|
|
1592
|
+
// Content is all other children
|
|
1593
|
+
const contentElements = childrenArray.filter((_, index) => index !== mediaBlockIndex);
|
|
1594
|
+
// Build alignment classes
|
|
1595
|
+
const alignClass = align === 'full' ? 'w-full' : align === 'wide' ? 'max-w-7xl mx-auto' : 'max-w-6xl mx-auto';
|
|
1596
|
+
// Vertical alignment classes
|
|
1597
|
+
const verticalAlignClass = verticalAlignment === 'top' ? 'items-start' :
|
|
1598
|
+
verticalAlignment === 'bottom' ? 'items-end' :
|
|
1599
|
+
'items-center';
|
|
1600
|
+
// Stack on mobile
|
|
1601
|
+
const stackClass = 'flex-col md:flex-row';
|
|
1602
|
+
// Media position determines order
|
|
1603
|
+
const isMediaRight = mediaPosition === 'right';
|
|
1604
|
+
return (jsxRuntimeExports.jsx("div", { className: `${alignClass} px-4`, children: jsxRuntimeExports.jsxs("div", { className: `flex ${stackClass} ${verticalAlignClass} gap-6`, children: [jsxRuntimeExports.jsx("div", { className: `${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" }) }), jsxRuntimeExports.jsx("div", { className: `${isMediaRight ? 'order-1' : 'order-2'} ${imageFill ? 'w-full md:w-1/2' : 'flex-1'} space-y-4`, children: contentElements.length > 0 ? contentElements : children })] }) }));
|
|
1605
|
+
};
|
|
1438
1606
|
const Fallback = ({ block, children }) => {
|
|
1439
1607
|
// Minimal fallback; do not render innerHTML directly in v1 for safety
|
|
1440
1608
|
return jsxRuntimeExports.jsx("div", { "data-unknown-block": block.name, children: children });
|
|
@@ -1458,9 +1626,20 @@ function createDefaultRegistry() {
|
|
|
1458
1626
|
'core/table': ({ children }) => jsxRuntimeExports.jsx("div", { className: "overflow-x-auto", children: jsxRuntimeExports.jsx("table", { className: "table-auto w-full", children: children }) }),
|
|
1459
1627
|
'core/table-row': ({ children }) => jsxRuntimeExports.jsx("tr", { children: children }),
|
|
1460
1628
|
'core/table-cell': ({ children }) => jsxRuntimeExports.jsx("td", { className: "border px-3 py-2", children: children }),
|
|
1629
|
+
// Cover block - hero sections with background images
|
|
1630
|
+
'core/cover': Cover,
|
|
1631
|
+
// Media & Text block - side-by-side media and content
|
|
1632
|
+
'core/media-text': MediaText,
|
|
1633
|
+
// HTML block - render innerHTML as-is
|
|
1634
|
+
// Note: Shortcodes in HTML blocks are not parsed (they would need to be in text content)
|
|
1635
|
+
'core/html': ({ block }) => {
|
|
1636
|
+
const html = block.innerHTML || '';
|
|
1637
|
+
return jsxRuntimeExports.jsx("div", { dangerouslySetInnerHTML: { __html: html } });
|
|
1638
|
+
},
|
|
1461
1639
|
};
|
|
1462
1640
|
return {
|
|
1463
1641
|
renderers,
|
|
1642
|
+
shortcodes: {}, // Empty by default - apps extend this
|
|
1464
1643
|
fallback: Fallback,
|
|
1465
1644
|
};
|
|
1466
1645
|
}
|
|
@@ -1552,5 +1731,5 @@ class WPErrorBoundary extends React.Component {
|
|
|
1552
1731
|
}
|
|
1553
1732
|
}
|
|
1554
1733
|
|
|
1555
|
-
export { WPContent, WPErrorBoundary, WPPage, createDefaultRegistry, parseGutenbergBlocks, renderNodes };
|
|
1734
|
+
export { WPContent, WPErrorBoundary, WPPage, createDefaultRegistry, findShortcodes, parseGutenbergBlocks, parseShortcodeAttrs, renderNodes, renderTextWithShortcodes };
|
|
1556
1735
|
//# sourceMappingURL=index.esm.js.map
|