@marvalt/wparser 0.1.61 → 0.1.64
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 +177 -121
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.esm.js +177 -121
- package/dist/index.esm.js.map +1 -1
- package/dist/registry/defaultRegistry.d.ts.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1406,6 +1406,115 @@ 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
|
+
|
|
1409
1518
|
/**
|
|
1410
1519
|
* Content extraction utilities for WordPress blocks
|
|
1411
1520
|
* Extracts text content from various block formats
|
|
@@ -2277,24 +2386,80 @@ function buildClassName(...classes) {
|
|
|
2277
2386
|
function getSpacing(spacingConfig, key, fallback) {
|
|
2278
2387
|
return spacingConfig?.[key] || fallback;
|
|
2279
2388
|
}
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2389
|
+
// Helper function to get heading spacing
|
|
2390
|
+
function getHeadingSpacing(spacingConfig, level) {
|
|
2391
|
+
const headingConfig = spacingConfig?.heading;
|
|
2392
|
+
if (level === 1)
|
|
2393
|
+
return headingConfig?.h1 || 'mt-10 mb-8';
|
|
2394
|
+
if (level === 2)
|
|
2395
|
+
return headingConfig?.h2 || 'mt-8 mb-6';
|
|
2396
|
+
return headingConfig?.h3 || 'mt-6 mb-4';
|
|
2397
|
+
}
|
|
2398
|
+
const Paragraph = ({ block, context }) => {
|
|
2399
|
+
const attrs = block.attributes || {};
|
|
2400
|
+
const textAlign = getTextAlignClasses(attrs['align']);
|
|
2401
|
+
const spacing = getSpacing(context.spacingConfig || context.registry.spacingConfig, 'paragraph', 'my-6');
|
|
2402
|
+
// Extract text color if specified, otherwise inherit from parent (CSS variables handle default)
|
|
2403
|
+
const textColor = extractTextColor(block, context);
|
|
2404
|
+
const textColorClass = textColor || '';
|
|
2405
|
+
// Extract font size if specified
|
|
2406
|
+
const fontSize = extractFontSize(block, context);
|
|
2407
|
+
const fontSizeClass = fontSize ? getFontSizeClasses(fontSize) : '';
|
|
2408
|
+
const hasHTML = block.innerHTML && /<[a-z][\s\S]*>/i.test(block.innerHTML);
|
|
2409
|
+
const htmlContent = block.innerHTML || '';
|
|
2285
2410
|
const textContent = getBlockTextContent(block);
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
const
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2411
|
+
const hasShortcodes = /\[(\w+)/.test(htmlContent) || /\[(\w+)/.test(textContent);
|
|
2412
|
+
if (hasShortcodes && context.registry.shortcodes) {
|
|
2413
|
+
const parts = renderTextWithShortcodes(textContent, context.registry);
|
|
2414
|
+
const isBlockLevelElement = (element) => {
|
|
2415
|
+
if (!React.isValidElement(element))
|
|
2416
|
+
return false;
|
|
2417
|
+
const type = element.type;
|
|
2418
|
+
if (typeof type === 'string' && ['section', 'div', 'article', 'header', 'footer', 'aside', 'nav'].includes(type))
|
|
2419
|
+
return true;
|
|
2420
|
+
if (type === React.Fragment) {
|
|
2421
|
+
const children = React.Children.toArray(element.props.children);
|
|
2422
|
+
return children.some((child) => isBlockLevelElement(child));
|
|
2423
|
+
}
|
|
2424
|
+
if (typeof type === 'function' || (typeof type === 'object' && type !== null && type !== React.Fragment))
|
|
2425
|
+
return true;
|
|
2426
|
+
return false;
|
|
2427
|
+
};
|
|
2428
|
+
const hasBlockLevelContent = React.Children.toArray(parts).some((part) => isBlockLevelElement(part));
|
|
2429
|
+
if (hasBlockLevelContent) {
|
|
2430
|
+
return jsxRuntimeExports.jsx("div", { className: buildClassName(spacing, textColorClass, fontSizeClass), children: parts });
|
|
2431
|
+
}
|
|
2432
|
+
return jsxRuntimeExports.jsx("p", { className: buildClassName(spacing, textAlign, textColorClass, fontSizeClass), children: parts });
|
|
2433
|
+
}
|
|
2434
|
+
if (hasHTML && block.innerHTML) {
|
|
2435
|
+
const trimmedContent = htmlContent.trim();
|
|
2436
|
+
if (trimmedContent.startsWith('<p') && trimmedContent.endsWith('</p>')) {
|
|
2437
|
+
const contentMatch = trimmedContent.match(/<p[^>]*>(.*?)<\/p>/s);
|
|
2438
|
+
if (contentMatch) {
|
|
2439
|
+
const innerContent = contentMatch[1];
|
|
2440
|
+
if (innerContent.trim().startsWith('<p')) {
|
|
2441
|
+
return jsxRuntimeExports.jsx("div", { className: buildClassName(spacing, textAlign, textColorClass, fontSizeClass), dangerouslySetInnerHTML: { __html: innerContent } });
|
|
2442
|
+
}
|
|
2443
|
+
return jsxRuntimeExports.jsx("p", { className: buildClassName(spacing, textAlign, textColorClass, fontSizeClass), dangerouslySetInnerHTML: { __html: innerContent } });
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
return jsxRuntimeExports.jsx("p", { className: buildClassName(spacing, textAlign, textColorClass, fontSizeClass), dangerouslySetInnerHTML: { __html: htmlContent } });
|
|
2292
2447
|
}
|
|
2448
|
+
return jsxRuntimeExports.jsx("p", { className: buildClassName(spacing, textAlign, textColorClass, fontSizeClass), children: textContent });
|
|
2449
|
+
};
|
|
2450
|
+
const Heading = ({ block, children, context }) => {
|
|
2293
2451
|
const attrs = block.attributes || {};
|
|
2294
2452
|
const { level = 2 } = attrs;
|
|
2295
2453
|
const content = getBlockTextContent(block);
|
|
2454
|
+
const textAlign = getTextAlignClasses(attrs['textAlign']);
|
|
2455
|
+
const extractedFontSize = extractFontSize(block, context);
|
|
2456
|
+
const fontSize = extractedFontSize ? getFontSizeClasses(extractedFontSize) : '';
|
|
2296
2457
|
const Tag = `h${Math.min(Math.max(Number(level) || 2, 1), 6)}`;
|
|
2297
|
-
|
|
2458
|
+
const sizeClass = fontSize || '';
|
|
2459
|
+
const spacingClass = getHeadingSpacing(context.spacingConfig || context.registry.spacingConfig, level);
|
|
2460
|
+
const textColor = extractTextColor(block, context);
|
|
2461
|
+
const textColorClass = textColor || '';
|
|
2462
|
+
return (jsxRuntimeExports.jsx(Tag, { className: buildClassName(textColorClass, sizeClass, textAlign, spacingClass), children: children ?? content }));
|
|
2298
2463
|
};
|
|
2299
2464
|
const Image = ({ block, context }) => {
|
|
2300
2465
|
const imageAttrs = getImageAttributes(block);
|
|
@@ -2960,115 +3125,6 @@ class WPErrorBoundary extends React.Component {
|
|
|
2960
3125
|
}
|
|
2961
3126
|
}
|
|
2962
3127
|
|
|
2963
|
-
/**
|
|
2964
|
-
* Parse shortcode attributes from a string
|
|
2965
|
-
* Supports: [shortcode attr="value" attr2="value2"]
|
|
2966
|
-
*/
|
|
2967
|
-
function parseShortcodeAttrs(attrString) {
|
|
2968
|
-
if (!attrString || !attrString.trim()) {
|
|
2969
|
-
return {};
|
|
2970
|
-
}
|
|
2971
|
-
const attrs = {};
|
|
2972
|
-
// Match key="value" or key='value' patterns
|
|
2973
|
-
const attrRegex = /(\w+)=["']([^"']+)["']/g;
|
|
2974
|
-
let match;
|
|
2975
|
-
while ((match = attrRegex.exec(attrString)) !== null) {
|
|
2976
|
-
attrs[match[1]] = match[2];
|
|
2977
|
-
}
|
|
2978
|
-
return attrs;
|
|
2979
|
-
}
|
|
2980
|
-
/**
|
|
2981
|
-
* Find all shortcodes in text
|
|
2982
|
-
* Supports: [shortcode], [shortcode attr="value"], [shortcode]content[/shortcode]
|
|
2983
|
-
*/
|
|
2984
|
-
function findShortcodes(text) {
|
|
2985
|
-
const shortcodes = [];
|
|
2986
|
-
// First, find closing tag pairs: [shortcode]content[/shortcode]
|
|
2987
|
-
const closingTagRegex = /\[(\w+)(?:\s+([^\]]+))?\](.*?)\[\/\1\]/gs;
|
|
2988
|
-
let match;
|
|
2989
|
-
const processedRanges = [];
|
|
2990
|
-
while ((match = closingTagRegex.exec(text)) !== null) {
|
|
2991
|
-
const name = match[1];
|
|
2992
|
-
const attrString = match[2] || '';
|
|
2993
|
-
const content = match[3] || '';
|
|
2994
|
-
const attrs = parseShortcodeAttrs(attrString);
|
|
2995
|
-
shortcodes.push({
|
|
2996
|
-
name,
|
|
2997
|
-
attrs,
|
|
2998
|
-
content,
|
|
2999
|
-
fullMatch: match[0],
|
|
3000
|
-
startIndex: match.index,
|
|
3001
|
-
endIndex: match.index + match[0].length,
|
|
3002
|
-
});
|
|
3003
|
-
processedRanges.push({
|
|
3004
|
-
start: match.index,
|
|
3005
|
-
end: match.index + match[0].length,
|
|
3006
|
-
});
|
|
3007
|
-
}
|
|
3008
|
-
// Then, find self-closing: [shortcode attr="value"]
|
|
3009
|
-
// Skip ranges already processed by closing tags
|
|
3010
|
-
const selfClosingRegex = /\[(\w+)(?:\s+([^\]]+))?\]/g;
|
|
3011
|
-
let selfClosingMatch;
|
|
3012
|
-
while ((selfClosingMatch = selfClosingRegex.exec(text)) !== null) {
|
|
3013
|
-
// Check if this match is within a processed range
|
|
3014
|
-
const isProcessed = processedRanges.some(range => selfClosingMatch.index >= range.start && selfClosingMatch.index < range.end);
|
|
3015
|
-
if (isProcessed) {
|
|
3016
|
-
continue;
|
|
3017
|
-
}
|
|
3018
|
-
const name = selfClosingMatch[1];
|
|
3019
|
-
const attrString = selfClosingMatch[2] || '';
|
|
3020
|
-
const attrs = parseShortcodeAttrs(attrString);
|
|
3021
|
-
shortcodes.push({
|
|
3022
|
-
name,
|
|
3023
|
-
attrs,
|
|
3024
|
-
fullMatch: selfClosingMatch[0],
|
|
3025
|
-
startIndex: selfClosingMatch.index,
|
|
3026
|
-
endIndex: selfClosingMatch.index + selfClosingMatch[0].length,
|
|
3027
|
-
});
|
|
3028
|
-
}
|
|
3029
|
-
// Sort by start index
|
|
3030
|
-
shortcodes.sort((a, b) => a.startIndex - b.startIndex);
|
|
3031
|
-
return shortcodes;
|
|
3032
|
-
}
|
|
3033
|
-
/**
|
|
3034
|
-
* Render text with shortcodes replaced by React components
|
|
3035
|
-
*/
|
|
3036
|
-
function renderTextWithShortcodes(text, registry) {
|
|
3037
|
-
const shortcodes = findShortcodes(text);
|
|
3038
|
-
if (shortcodes.length === 0) {
|
|
3039
|
-
return [text];
|
|
3040
|
-
}
|
|
3041
|
-
const parts = [];
|
|
3042
|
-
let lastIndex = 0;
|
|
3043
|
-
for (const shortcode of shortcodes) {
|
|
3044
|
-
// Add text before shortcode
|
|
3045
|
-
if (shortcode.startIndex > lastIndex) {
|
|
3046
|
-
const textBefore = text.substring(lastIndex, shortcode.startIndex);
|
|
3047
|
-
if (textBefore) {
|
|
3048
|
-
parts.push(textBefore);
|
|
3049
|
-
}
|
|
3050
|
-
}
|
|
3051
|
-
// Render shortcode
|
|
3052
|
-
const renderer = registry.shortcodes[shortcode.name];
|
|
3053
|
-
if (renderer) {
|
|
3054
|
-
parts.push(jsxRuntimeExports.jsx(React.Fragment, { children: renderer(shortcode.attrs, shortcode.content) }, `shortcode-${shortcode.startIndex}`));
|
|
3055
|
-
}
|
|
3056
|
-
else {
|
|
3057
|
-
// Keep original shortcode if no renderer found
|
|
3058
|
-
parts.push(shortcode.fullMatch);
|
|
3059
|
-
}
|
|
3060
|
-
lastIndex = shortcode.endIndex;
|
|
3061
|
-
}
|
|
3062
|
-
// Add remaining text
|
|
3063
|
-
if (lastIndex < text.length) {
|
|
3064
|
-
const remainingText = text.substring(lastIndex);
|
|
3065
|
-
if (remainingText) {
|
|
3066
|
-
parts.push(remainingText);
|
|
3067
|
-
}
|
|
3068
|
-
}
|
|
3069
|
-
return parts;
|
|
3070
|
-
}
|
|
3071
|
-
|
|
3072
3128
|
/**
|
|
3073
3129
|
* Convert image URL with optional Cloudflare variant transformation
|
|
3074
3130
|
*
|