@marvalt/wparser 0.1.6 → 0.1.7

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.d.ts CHANGED
@@ -47,6 +47,46 @@ interface ComponentRegistry {
47
47
  /** Fallback renderer for unknown blocks */
48
48
  fallback: BlockRenderer;
49
49
  }
50
+ /**
51
+ * Pattern matching for blocks - allows matching blocks by name and attributes
52
+ */
53
+ interface BlockPattern {
54
+ /** Block name (e.g., 'core/cover', 'core/media-text') */
55
+ name: string;
56
+ /** Optional attribute matching - all specified attributes must match */
57
+ attributes?: Record<string, any>;
58
+ /** Optional nested block patterns for innerBlocks */
59
+ innerBlocks?: BlockPattern[];
60
+ }
61
+ /**
62
+ * Component mapping configuration for enhanced registry
63
+ * Maps block patterns to React components with prop extraction
64
+ */
65
+ interface ComponentMapping<TProps = any> {
66
+ /** Pattern to match against blocks */
67
+ pattern: BlockPattern;
68
+ /** React component to render when pattern matches */
69
+ component: React.ComponentType<TProps>;
70
+ /** Function to extract props from block data */
71
+ extractProps: (block: WordPressBlock, context: RenderContext) => TProps;
72
+ /** Optional wrapper component (e.g., for section spacing) */
73
+ wrapper?: React.ComponentType<{
74
+ children: React.ReactNode;
75
+ block?: WordPressBlock;
76
+ }>;
77
+ /** Priority for matching (higher = more specific, checked first) */
78
+ priority?: number;
79
+ }
80
+ /**
81
+ * Enhanced registry that supports pattern-based component mapping
82
+ * Extends the base ComponentRegistry with pattern matching capabilities
83
+ */
84
+ interface EnhancedRegistry extends ComponentRegistry {
85
+ /** Array of component mappings with patterns */
86
+ mappings: ComponentMapping[];
87
+ /** Find matching component mapping for a block */
88
+ matchBlock: (block: WordPressBlock) => ComponentMapping | null;
89
+ }
50
90
 
51
91
  type WPNode = WordPressBlock;
52
92
  interface ParseOptions {
@@ -60,6 +100,36 @@ declare function renderNodes(blocks: WordPressBlock[], registry: ComponentRegist
60
100
 
61
101
  declare function createDefaultRegistry(): ComponentRegistry;
62
102
 
103
+ /**
104
+ * Create an enhanced registry that supports pattern-based component mapping
105
+ *
106
+ * This combines the default registry (for fallback) with app-specific component mappings.
107
+ * When a block matches a pattern, it uses the mapped component. Otherwise, it falls back
108
+ * to the default renderer.
109
+ *
110
+ * @param mappings - Array of component mappings with patterns
111
+ * @param baseRegistry - Optional base registry (defaults to createDefaultRegistry())
112
+ * @returns Enhanced registry with pattern matching capabilities
113
+ *
114
+ * @example
115
+ * ```ts
116
+ * const mappings: ComponentMapping[] = [
117
+ * {
118
+ * pattern: { name: 'core/cover' },
119
+ * component: HomeHeroSection,
120
+ * extractProps: (block) => ({
121
+ * backgroundImage: extractBackgroundImage(block),
122
+ * title: extractTitle(block),
123
+ * }),
124
+ * wrapper: SectionWrapper,
125
+ * },
126
+ * ];
127
+ *
128
+ * const registry = createEnhancedRegistry(mappings);
129
+ * ```
130
+ */
131
+ declare function createEnhancedRegistry(mappings?: ComponentMapping[], baseRegistry?: ComponentRegistry): EnhancedRegistry;
132
+
63
133
  interface WPContentProps {
64
134
  blocks: WordPressBlock[];
65
135
  registry: ComponentRegistry;
@@ -202,5 +272,141 @@ declare const isCloudflareImageUrl: (url: string | undefined | null) => boolean;
202
272
  */
203
273
  declare const getCloudflareVariantUrl: (url: string, options: CloudflareVariantOptions) => string;
204
274
 
205
- export { WPContent, WPErrorBoundary, WPPage, buildClassName, createDefaultRegistry, extractTextFromHTML, findShortcodes, getAlignmentClasses, getBlockTextContent, getCloudflareVariantUrl, getContainerClasses, getContentSpacingClasses, getFontSizeClasses, getImageAttributes, getImageUrl, getSectionSpacingClasses, getTextAlignClasses, isCloudflareImageUrl, parseGutenbergBlocks, parseShortcodeAttrs, renderNodes, renderTextWithShortcodes };
206
- export type { AuthMode, BlockRenderer, BlockRendererProps, CloudflareVariantOptions, ComponentRegistry, ParseOptions, ParsedShortcode, RenderContext, RenderOptions, ShortcodeAttributes, ShortcodeRenderer, WPContentProps, WPNode, WPPageProps, WordPressBlock, WordPressEmbedded, WordPressFeaturedMedia, WordPressPageMinimal, WordPressTitleField };
275
+ /**
276
+ * Check if a block matches a pattern
277
+ */
278
+ declare function matchesPattern(block: WordPressBlock, pattern: BlockPattern): boolean;
279
+ /**
280
+ * Find the best matching component mapping for a block
281
+ * Returns the mapping with highest priority that matches, or null
282
+ */
283
+ declare function findMatchingMapping(block: WordPressBlock, mappings: ComponentMapping[]): ComponentMapping | null;
284
+
285
+ /**
286
+ * Extract background image URL from a block
287
+ * Checks various possible sources: url, backgroundImage, innerHTML
288
+ */
289
+ declare function extractBackgroundImage(block: WordPressBlock): string | null;
290
+ /**
291
+ * Extract image URL from a block
292
+ * Returns Cloudflare URL if available, otherwise WordPress URL
293
+ */
294
+ declare function extractImageUrl(block: WordPressBlock): string | null;
295
+ /**
296
+ * Extract image attributes (url, alt, width, height)
297
+ */
298
+ declare function extractImageAttributes(block: WordPressBlock): {
299
+ url: string | null;
300
+ alt: string;
301
+ width?: number;
302
+ height?: number;
303
+ };
304
+ /**
305
+ * Extract title/heading text from a block
306
+ */
307
+ declare function extractTitle(block: WordPressBlock): string | null;
308
+ /**
309
+ * Extract content/text from a block
310
+ * Returns React node for rendering
311
+ */
312
+ declare function extractContent(block: WordPressBlock, context: RenderContext): React$1.ReactNode;
313
+ /**
314
+ * Extract media position from media-text block
315
+ */
316
+ declare function extractMediaPosition(block: WordPressBlock): 'left' | 'right';
317
+ /**
318
+ * Extract vertical alignment from block
319
+ */
320
+ declare function extractVerticalAlignment(block: WordPressBlock): 'top' | 'center' | 'bottom';
321
+ /**
322
+ * Extract alignment (full, wide, contained) from block
323
+ */
324
+ declare function extractAlignment(block: WordPressBlock): 'full' | 'wide' | 'contained';
325
+ /**
326
+ * Extract overlay color from cover block
327
+ */
328
+ declare function extractOverlayColor(block: WordPressBlock): string | null;
329
+ /**
330
+ * Extract dim ratio (overlay opacity) from cover block
331
+ */
332
+ declare function extractDimRatio(block: WordPressBlock): number;
333
+ /**
334
+ * Extract min height from block
335
+ */
336
+ declare function extractMinHeight(block: WordPressBlock): {
337
+ value: number;
338
+ unit: string;
339
+ } | null;
340
+ /**
341
+ * Extract heading level from heading block
342
+ */
343
+ declare function extractHeadingLevel(block: WordPressBlock): number;
344
+ /**
345
+ * Extract text alignment from block
346
+ */
347
+ declare function extractTextAlign(block: WordPressBlock): 'left' | 'center' | 'right' | null;
348
+ /**
349
+ * Extract font size from block
350
+ */
351
+ declare function extractFontSize(block: WordPressBlock): string | null;
352
+ /**
353
+ * Convert image URL to Cloudflare variant if it's a Cloudflare URL
354
+ */
355
+ declare function convertImageToCloudflareVariant(url: string | null, options?: {
356
+ width?: number;
357
+ height?: number;
358
+ }): string | null;
359
+
360
+ interface ImageConversionOptions {
361
+ /** Convert to Cloudflare variant if URL is already Cloudflare */
362
+ convertToCloudflare?: boolean;
363
+ /** Default width for Cloudflare variant */
364
+ defaultWidth?: number;
365
+ /** Default height for Cloudflare variant */
366
+ defaultHeight?: number;
367
+ /** Force conversion even if URL is not Cloudflare (not recommended) */
368
+ forceCloudflare?: boolean;
369
+ }
370
+ /**
371
+ * Convert image URL with optional Cloudflare variant transformation
372
+ *
373
+ * @param url - Image URL (WordPress or Cloudflare)
374
+ * @param options - Conversion options
375
+ * @returns Converted URL or original if conversion not applicable
376
+ */
377
+ declare function convertImageUrl(url: string | null | undefined, options?: ImageConversionOptions): string | null;
378
+ /**
379
+ * Batch convert multiple image URLs
380
+ */
381
+ declare function convertImageUrls(urls: (string | null | undefined)[], options?: ImageConversionOptions): (string | null)[];
382
+
383
+ interface SectionWrapperProps {
384
+ children: React$1.ReactNode;
385
+ /** Background color variant */
386
+ background?: 'light' | 'dark' | 'transparent';
387
+ /** Vertical spacing between sections */
388
+ spacing?: 'none' | 'small' | 'medium' | 'large';
389
+ /** Container width */
390
+ container?: 'full' | 'wide' | 'contained';
391
+ /** Additional CSS classes */
392
+ className?: string;
393
+ /** Optional block reference for extracting additional props */
394
+ block?: WordPressBlock;
395
+ }
396
+ /**
397
+ * Generic section wrapper component for consistent spacing and layout
398
+ *
399
+ * Usage in component mappings:
400
+ * ```ts
401
+ * {
402
+ * pattern: { name: 'core/cover' },
403
+ * component: HeroSection,
404
+ * wrapper: SectionWrapper,
405
+ * extractProps: (block) => ({ ... })
406
+ * }
407
+ * ```
408
+ */
409
+ declare const SectionWrapper: React$1.FC<SectionWrapperProps>;
410
+
411
+ export { SectionWrapper, WPContent, WPErrorBoundary, WPPage, buildClassName, convertImageToCloudflareVariant, convertImageUrl, convertImageUrls, createDefaultRegistry, createEnhancedRegistry, extractAlignment, extractBackgroundImage, extractContent, extractDimRatio, extractFontSize, extractHeadingLevel, extractImageAttributes, extractImageUrl, extractMediaPosition, extractMinHeight, extractOverlayColor, extractTextAlign, extractTextFromHTML, extractTitle, extractVerticalAlignment, findMatchingMapping, findShortcodes, getAlignmentClasses, getBlockTextContent, getCloudflareVariantUrl, getContainerClasses, getContentSpacingClasses, getFontSizeClasses, getImageAttributes, getImageUrl, getSectionSpacingClasses, getTextAlignClasses, isCloudflareImageUrl, matchesPattern, parseGutenbergBlocks, parseShortcodeAttrs, renderNodes, renderTextWithShortcodes };
412
+ export type { AuthMode, BlockPattern, BlockRenderer, BlockRendererProps, CloudflareVariantOptions, ComponentMapping, ComponentRegistry, EnhancedRegistry, ImageConversionOptions, ParseOptions, ParsedShortcode, RenderContext, RenderOptions, SectionWrapperProps, ShortcodeAttributes, ShortcodeRenderer, WPContentProps, WPNode, WPPageProps, WordPressBlock, WordPressEmbedded, WordPressFeaturedMedia, WordPressPageMinimal, WordPressTitleField };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,6BAA6B,CAAC"}
package/dist/index.esm.js CHANGED
@@ -1931,6 +1931,138 @@ function getString(block) {
1931
1931
  return getBlockTextContent(block);
1932
1932
  }
1933
1933
 
1934
+ /**
1935
+ * Check if a block matches a pattern
1936
+ */
1937
+ function matchesPattern(block, pattern) {
1938
+ // Check block name
1939
+ if (block.name !== pattern.name) {
1940
+ return false;
1941
+ }
1942
+ // Check attributes if specified
1943
+ if (pattern.attributes) {
1944
+ const blockAttrs = block.attributes || {};
1945
+ for (const [key, value] of Object.entries(pattern.attributes)) {
1946
+ if (blockAttrs[key] !== value) {
1947
+ return false;
1948
+ }
1949
+ }
1950
+ }
1951
+ // Check innerBlocks patterns if specified
1952
+ if (pattern.innerBlocks && pattern.innerBlocks.length > 0) {
1953
+ const blockInnerBlocks = block.innerBlocks || [];
1954
+ // If pattern specifies innerBlocks, check if block has matching innerBlocks
1955
+ for (const innerPattern of pattern.innerBlocks) {
1956
+ // Find at least one matching innerBlock
1957
+ const hasMatch = blockInnerBlocks.some(innerBlock => matchesPattern(innerBlock, innerPattern));
1958
+ if (!hasMatch) {
1959
+ return false;
1960
+ }
1961
+ }
1962
+ }
1963
+ return true;
1964
+ }
1965
+ /**
1966
+ * Find the best matching component mapping for a block
1967
+ * Returns the mapping with highest priority that matches, or null
1968
+ */
1969
+ function findMatchingMapping(block, mappings) {
1970
+ // Sort by priority (higher first), then by order in array
1971
+ const sortedMappings = [...mappings].sort((a, b) => {
1972
+ const priorityA = a.priority ?? 0;
1973
+ const priorityB = b.priority ?? 0;
1974
+ if (priorityA !== priorityB) {
1975
+ return priorityB - priorityA; // Higher priority first
1976
+ }
1977
+ return 0; // Keep original order for same priority
1978
+ });
1979
+ // Find first matching mapping
1980
+ for (const mapping of sortedMappings) {
1981
+ if (matchesPattern(block, mapping.pattern)) {
1982
+ return mapping;
1983
+ }
1984
+ }
1985
+ return null;
1986
+ }
1987
+
1988
+ /**
1989
+ * Create an enhanced registry that supports pattern-based component mapping
1990
+ *
1991
+ * This combines the default registry (for fallback) with app-specific component mappings.
1992
+ * When a block matches a pattern, it uses the mapped component. Otherwise, it falls back
1993
+ * to the default renderer.
1994
+ *
1995
+ * @param mappings - Array of component mappings with patterns
1996
+ * @param baseRegistry - Optional base registry (defaults to createDefaultRegistry())
1997
+ * @returns Enhanced registry with pattern matching capabilities
1998
+ *
1999
+ * @example
2000
+ * ```ts
2001
+ * const mappings: ComponentMapping[] = [
2002
+ * {
2003
+ * pattern: { name: 'core/cover' },
2004
+ * component: HomeHeroSection,
2005
+ * extractProps: (block) => ({
2006
+ * backgroundImage: extractBackgroundImage(block),
2007
+ * title: extractTitle(block),
2008
+ * }),
2009
+ * wrapper: SectionWrapper,
2010
+ * },
2011
+ * ];
2012
+ *
2013
+ * const registry = createEnhancedRegistry(mappings);
2014
+ * ```
2015
+ */
2016
+ function createEnhancedRegistry(mappings = [], baseRegistry) {
2017
+ const base = baseRegistry || createDefaultRegistry();
2018
+ // Create enhanced renderers that check patterns first
2019
+ const enhancedRenderers = {
2020
+ ...base.renderers,
2021
+ };
2022
+ // Override renderers for blocks that have mappings
2023
+ // We need to check patterns at render time, so we create a wrapper renderer
2024
+ const createPatternRenderer = (blockName) => {
2025
+ return (props) => {
2026
+ const { block, context } = props;
2027
+ // Find matching mapping
2028
+ const mapping = findMatchingMapping(block, mappings);
2029
+ if (mapping) {
2030
+ // Extract props from block
2031
+ const componentProps = mapping.extractProps(block, context);
2032
+ // Render component
2033
+ const Component = mapping.component;
2034
+ const content = jsxRuntimeExports.jsx(Component, { ...componentProps });
2035
+ // Wrap with wrapper if provided
2036
+ if (mapping.wrapper) {
2037
+ const Wrapper = mapping.wrapper;
2038
+ return jsxRuntimeExports.jsx(Wrapper, { block: block, children: content });
2039
+ }
2040
+ return content;
2041
+ }
2042
+ // Fall back to default renderer
2043
+ const defaultRenderer = base.renderers[blockName] || base.fallback;
2044
+ return defaultRenderer(props);
2045
+ };
2046
+ };
2047
+ // For each mapping, override the renderer for that block name
2048
+ for (const mapping of mappings) {
2049
+ const blockName = mapping.pattern.name;
2050
+ if (blockName) {
2051
+ enhancedRenderers[blockName] = createPatternRenderer(blockName);
2052
+ }
2053
+ }
2054
+ // Create matchBlock function
2055
+ const matchBlock = (block) => {
2056
+ return findMatchingMapping(block, mappings);
2057
+ };
2058
+ return {
2059
+ ...base,
2060
+ renderers: enhancedRenderers,
2061
+ mappings,
2062
+ matchBlock,
2063
+ };
2064
+ }
2065
+
1934
2066
  const WPContent = ({ blocks, registry, className }) => {
1935
2067
  if (!Array.isArray(blocks)) {
1936
2068
  if (process.env.NODE_ENV !== 'production') {
@@ -2011,5 +2143,252 @@ class WPErrorBoundary extends React.Component {
2011
2143
  }
2012
2144
  }
2013
2145
 
2014
- export { WPContent, WPErrorBoundary, WPPage, buildClassName, createDefaultRegistry, extractTextFromHTML, findShortcodes, getAlignmentClasses, getBlockTextContent, getCloudflareVariantUrl, getContainerClasses, getContentSpacingClasses, getFontSizeClasses, getImageAttributes, getImageUrl, getSectionSpacingClasses, getTextAlignClasses, isCloudflareImageUrl, parseGutenbergBlocks, parseShortcodeAttrs, renderNodes, renderTextWithShortcodes };
2146
+ /**
2147
+ * Extract background image URL from a block
2148
+ * Checks various possible sources: url, backgroundImage, innerHTML
2149
+ */
2150
+ function extractBackgroundImage(block) {
2151
+ const attrs = block.attributes || {};
2152
+ // Try various attribute keys
2153
+ let url = attrs['url'] ||
2154
+ attrs['backgroundImage'] ||
2155
+ (typeof attrs['backgroundImage'] === 'object' && attrs['backgroundImage']?.url);
2156
+ if (typeof url === 'string' && url.trim()) {
2157
+ return url.trim();
2158
+ }
2159
+ // Try to extract from innerHTML if not found in attributes
2160
+ if (block.innerHTML) {
2161
+ // Try img src from innerHTML
2162
+ const imgMatch = block.innerHTML.match(/<img[^>]+src=["']([^"']+)["']/i);
2163
+ if (imgMatch && imgMatch[1]) {
2164
+ return imgMatch[1];
2165
+ }
2166
+ // Try background-image in style attribute
2167
+ const bgMatch = block.innerHTML.match(/background-image:\s*url\(["']?([^"')]+)["']?\)/i);
2168
+ if (bgMatch && bgMatch[1]) {
2169
+ return bgMatch[1];
2170
+ }
2171
+ }
2172
+ return null;
2173
+ }
2174
+ /**
2175
+ * Extract image URL from a block
2176
+ * Returns Cloudflare URL if available, otherwise WordPress URL
2177
+ */
2178
+ function extractImageUrl(block) {
2179
+ return getImageUrl(block);
2180
+ }
2181
+ /**
2182
+ * Extract image attributes (url, alt, width, height)
2183
+ */
2184
+ function extractImageAttributes(block) {
2185
+ return getImageAttributes(block);
2186
+ }
2187
+ /**
2188
+ * Extract title/heading text from a block
2189
+ */
2190
+ function extractTitle(block) {
2191
+ const attrs = block.attributes || {};
2192
+ const title = attrs['title'] || attrs['content'] || getBlockTextContent(block);
2193
+ return typeof title === 'string' ? title.trim() : null;
2194
+ }
2195
+ /**
2196
+ * Extract content/text from a block
2197
+ * Returns React node for rendering
2198
+ */
2199
+ function extractContent(block, context) {
2200
+ const text = getBlockTextContent(block);
2201
+ return text || null;
2202
+ }
2203
+ /**
2204
+ * Extract media position from media-text block
2205
+ */
2206
+ function extractMediaPosition(block) {
2207
+ const attrs = block.attributes || {};
2208
+ const position = attrs['mediaPosition'] || 'left';
2209
+ return position === 'right' ? 'right' : 'left';
2210
+ }
2211
+ /**
2212
+ * Extract vertical alignment from block
2213
+ */
2214
+ function extractVerticalAlignment(block) {
2215
+ const attrs = block.attributes || {};
2216
+ const alignment = attrs['verticalAlignment'] || 'center';
2217
+ if (alignment === 'top' || alignment === 'bottom') {
2218
+ return alignment;
2219
+ }
2220
+ return 'center';
2221
+ }
2222
+ /**
2223
+ * Extract alignment (full, wide, contained) from block
2224
+ */
2225
+ function extractAlignment(block) {
2226
+ const attrs = block.attributes || {};
2227
+ const align = attrs['align'];
2228
+ if (align === 'full' || align === 'wide') {
2229
+ return align;
2230
+ }
2231
+ return 'contained';
2232
+ }
2233
+ /**
2234
+ * Extract overlay color from cover block
2235
+ */
2236
+ function extractOverlayColor(block) {
2237
+ const attrs = block.attributes || {};
2238
+ const overlayColor = attrs['overlayColor'];
2239
+ if (typeof overlayColor === 'string') {
2240
+ return overlayColor;
2241
+ }
2242
+ return null;
2243
+ }
2244
+ /**
2245
+ * Extract dim ratio (overlay opacity) from cover block
2246
+ */
2247
+ function extractDimRatio(block) {
2248
+ const attrs = block.attributes || {};
2249
+ const dimRatio = attrs['dimRatio'];
2250
+ if (typeof dimRatio === 'number') {
2251
+ return dimRatio;
2252
+ }
2253
+ return 0;
2254
+ }
2255
+ /**
2256
+ * Extract min height from block
2257
+ */
2258
+ function extractMinHeight(block) {
2259
+ const attrs = block.attributes || {};
2260
+ const minHeight = attrs['minHeight'];
2261
+ const minHeightUnit = attrs['minHeightUnit'] || 'vh';
2262
+ if (typeof minHeight === 'number') {
2263
+ return { value: minHeight, unit: minHeightUnit };
2264
+ }
2265
+ return null;
2266
+ }
2267
+ /**
2268
+ * Extract heading level from heading block
2269
+ */
2270
+ function extractHeadingLevel(block) {
2271
+ const attrs = block.attributes || {};
2272
+ const level = attrs['level'];
2273
+ if (typeof level === 'number' && level >= 1 && level <= 6) {
2274
+ return level;
2275
+ }
2276
+ return 2; // Default to h2
2277
+ }
2278
+ /**
2279
+ * Extract text alignment from block
2280
+ */
2281
+ function extractTextAlign(block) {
2282
+ const attrs = block.attributes || {};
2283
+ const align = attrs['align'] || attrs['textAlign'];
2284
+ if (align === 'left' || align === 'center' || align === 'right') {
2285
+ return align;
2286
+ }
2287
+ return null;
2288
+ }
2289
+ /**
2290
+ * Extract font size from block
2291
+ */
2292
+ function extractFontSize(block) {
2293
+ const attrs = block.attributes || {};
2294
+ const fontSize = attrs['fontSize'];
2295
+ return typeof fontSize === 'string' ? fontSize : null;
2296
+ }
2297
+ /**
2298
+ * Convert image URL to Cloudflare variant if it's a Cloudflare URL
2299
+ */
2300
+ function convertImageToCloudflareVariant(url, options = {}) {
2301
+ if (!url)
2302
+ return null;
2303
+ if (isCloudflareImageUrl(url)) {
2304
+ const width = options.width || 1024;
2305
+ const height = options.height;
2306
+ return getCloudflareVariantUrl(url, { width, height });
2307
+ }
2308
+ return url;
2309
+ }
2310
+
2311
+ /**
2312
+ * Convert image URL with optional Cloudflare variant transformation
2313
+ *
2314
+ * @param url - Image URL (WordPress or Cloudflare)
2315
+ * @param options - Conversion options
2316
+ * @returns Converted URL or original if conversion not applicable
2317
+ */
2318
+ function convertImageUrl(url, options = {}) {
2319
+ if (!url)
2320
+ return null;
2321
+ const { convertToCloudflare = true, defaultWidth = 1024, defaultHeight, forceCloudflare = false, } = options;
2322
+ // If already Cloudflare URL and conversion is enabled
2323
+ if (isCloudflareImageUrl(url)) {
2324
+ if (convertToCloudflare) {
2325
+ return getCloudflareVariantUrl(url, {
2326
+ width: defaultWidth,
2327
+ height: defaultHeight,
2328
+ });
2329
+ }
2330
+ return url;
2331
+ }
2332
+ // If force conversion is enabled (not recommended - requires WordPress plugin to provide Cloudflare URLs)
2333
+ if (forceCloudflare) {
2334
+ // This would require additional logic to map WordPress URLs to Cloudflare URLs
2335
+ // which should be handled by the WordPress plugin providing Cloudflare URLs in block data
2336
+ console.warn('forceCloudflare is enabled but URL is not Cloudflare. WordPress plugin should provide Cloudflare URLs in block metadata.');
2337
+ }
2338
+ // Return original URL if not Cloudflare
2339
+ return url;
2340
+ }
2341
+ /**
2342
+ * Batch convert multiple image URLs
2343
+ */
2344
+ function convertImageUrls(urls, options = {}) {
2345
+ return urls.map(url => convertImageUrl(url, options));
2346
+ }
2347
+
2348
+ /**
2349
+ * Generic section wrapper component for consistent spacing and layout
2350
+ *
2351
+ * Usage in component mappings:
2352
+ * ```ts
2353
+ * {
2354
+ * pattern: { name: 'core/cover' },
2355
+ * component: HeroSection,
2356
+ * wrapper: SectionWrapper,
2357
+ * extractProps: (block) => ({ ... })
2358
+ * }
2359
+ * ```
2360
+ */
2361
+ const SectionWrapper = ({ children, background = 'light', spacing = 'medium', container = 'contained', className, block, }) => {
2362
+ // Background classes
2363
+ const backgroundClasses = {
2364
+ light: 'bg-white',
2365
+ dark: 'bg-gray-900 text-white',
2366
+ transparent: 'bg-transparent',
2367
+ };
2368
+ // Spacing classes (vertical padding)
2369
+ const spacingClasses = {
2370
+ none: '',
2371
+ small: 'py-8 md:py-12',
2372
+ medium: 'py-16 md:py-24',
2373
+ large: 'py-24 md:py-32',
2374
+ };
2375
+ // Container classes
2376
+ const containerClasses = {
2377
+ full: 'w-full',
2378
+ wide: 'max-w-7xl mx-auto px-4',
2379
+ contained: 'container mx-auto px-4',
2380
+ };
2381
+ // Extract additional props from block if provided
2382
+ const blockAttrs = block?.attributes || {};
2383
+ const blockBackground = blockAttrs['backgroundColor'] || blockAttrs['background'];
2384
+ const blockSpacing = blockAttrs['spacing'];
2385
+ const blockContainer = blockAttrs['container'] || blockAttrs['align'];
2386
+ // Override with block attributes if present
2387
+ const finalBackground = blockBackground || background;
2388
+ const finalSpacing = blockSpacing || spacing;
2389
+ const finalContainer = blockContainer || container;
2390
+ return (jsxRuntimeExports.jsx("section", { className: buildClassName(backgroundClasses[finalBackground] || backgroundClasses.light, spacingClasses[finalSpacing] || spacingClasses.medium, containerClasses[finalContainer] || containerClasses.contained, className), children: children }));
2391
+ };
2392
+
2393
+ export { SectionWrapper, WPContent, WPErrorBoundary, WPPage, buildClassName, convertImageToCloudflareVariant, convertImageUrl, convertImageUrls, createDefaultRegistry, createEnhancedRegistry, extractAlignment, extractBackgroundImage, extractContent, extractDimRatio, extractFontSize, extractHeadingLevel, extractImageAttributes, extractImageUrl, extractMediaPosition, extractMinHeight, extractOverlayColor, extractTextAlign, extractTextFromHTML, extractTitle, extractVerticalAlignment, findMatchingMapping, findShortcodes, getAlignmentClasses, getBlockTextContent, getCloudflareVariantUrl, getContainerClasses, getContentSpacingClasses, getFontSizeClasses, getImageAttributes, getImageUrl, getSectionSpacingClasses, getTextAlignClasses, isCloudflareImageUrl, matchesPattern, parseGutenbergBlocks, parseShortcodeAttrs, renderNodes, renderTextWithShortcodes };
2015
2394
  //# sourceMappingURL=index.esm.js.map