@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 +124 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +124 -10
- package/dist/index.esm.js.map +1 -1
- package/dist/registry/defaultRegistry.d.ts.map +1 -1
- package/dist/utils/blockExtractors.d.ts.map +1 -1
- package/dist/utils/contentExtractor.d.ts +3 -2
- package/dist/utils/contentExtractor.d.ts.map +1 -1
- package/package.json +1 -1
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 || {};
|