migration wip

This commit is contained in:
2025-12-30 00:06:54 +01:00
parent 3efbac78cb
commit 89dbf8af87
94 changed files with 5674 additions and 308 deletions

View File

@@ -139,31 +139,132 @@ function sanitizeHTML(html: string): string {
/**
* Process WordPress shortcodes by converting them to HTML with proper styling
* Also handles mixed scenarios where some content is already HTML with WordPress classes
*/
function processShortcodes(html: string): string {
export function processShortcodes(html: string): string {
let processed = html;
try {
// Step 1: Convert any existing HTML with WordPress classes back to shortcode format
// This ensures we have a consistent format to work with
// Handle vc_row and vc_row_inner
processed = processed.replace(/<div[^>]*class=["'][^"']*(?:vc-row|vc_row|vc_row_inner)[^"']*["'][^>]*>/gi, (match) => {
const attrs = extractAttributesFromHTML(match);
const isInner = match.includes('vc_row_inner') || match.includes('vc-row-inner');
return `[${isInner ? 'vc_row_inner' : 'vc_row'} ${attrs}]`;
});
// Handle vc_column and vc_column_inner
processed = processed.replace(/<div[^>]*class=["'][^"']*(?:vc-column|vc_column|vc_column_inner)[^"']*["'][^>]*>/gi, (match) => {
const attrs = extractAttributesFromHTML(match);
const isInner = match.includes('vc_column_inner') || match.includes('vc-column-inner');
return `[${isInner ? 'vc_column_inner' : 'vc_column'} ${attrs}]`;
});
// Handle vc_column_text
processed = processed.replace(/<div[^>]*class=["'][^"']*(?:vc-column-text|vc_column_text)[^"']*["'][^>]*>/gi, (match) => {
const attrs = extractAttributesFromHTML(match);
return `[vc_column_text ${attrs}]`;
});
// Handle vc_single_image
processed = processed.replace(/<img[^>]*class=["'][^"']*(?:vc-single-image|vc_single_image)[^"']*["'][^>]*>/gi, (match) => {
const attrs = extractAttributesFromHTML(match);
const imageId = extractAttribute(attrs, 'data-wp-image-id') || extractAttribute(attrs, 'src');
const width = extractAttribute(attrs, 'data-width') || '';
return `[vc_single_image src="${imageId}" width="${width}"]`;
});
// Handle vc_btn
processed = processed.replace(/<a[^>]*class=["'][^"']*(?:vc-btn|vc_btn)[^"']*["'][^>]*>(.*?)<\/a>/gi, (match, content) => {
const attrs = extractAttributesFromHTML(match);
const href = extractAttribute(attrs, 'href');
const title = content;
return `[vc_btn href="${href}" title="${title}"]`;
});
// Handle vc_separator
processed = processed.replace(/<hr[^>]*class=["'][^"']*(?:vc-separator|vc_separator)[^"']*["'][^>]*>/gi, (match) => {
const attrs = extractAttributesFromHTML(match);
return `[vc_separator ${attrs}]`;
});
// Handle closing div tags by looking for matching opening shortcode tags
// This is more complex, so we'll handle it carefully
processed = processed.replace(/<\/div>/gi, (match, offset) => {
const beforeContent = processed.substring(0, offset);
const lastOpenTag = beforeContent.match(/\[(vc_row(?:_inner)?|vc_column(?:_inner)?|vc_column_text)\s*[^\]]*\]$/i);
if (lastOpenTag) {
return `[/${lastOpenTag[1]}]`;
}
// If no matching shortcode, keep the div closing tag
return match;
});
// Step 2: Process shortcode blocks into HTML
processed = processVcRowShortcodes(processed);
processed = processVcColumnShortcodes(processed);
processed = processVcColumnTextShortcodes(processed);
processed = processVcImageShortcodes(processed);
processed = processVcButtonShortcodes(processed);
processed = processVcSeparatorShortcodes(processed);
processed = processVcVideoShortcodes(processed);
processed = processBackgroundShortcodes(processed);
// Step 3: Check for unprocessed shortcodes and log them
const unprocessedShortcodes = processed.match(/\[[^\]]*\]/g);
if (unprocessedShortcodes && unprocessedShortcodes.length > 0) {
console.warn('Unprocessed shortcodes found and will be removed:', unprocessedShortcodes);
}
// Clean up any remaining shortcode artifacts
// Only remove shortcodes that weren't processed
processed = processed.replace(/\[[^\]]*\]/g, '');
// Step 4: Clean up any remaining empty div tags
processed = processed.replace(/<div[^>]*>\s*<\/div>/g, '');
return processed;
} catch (error) {
console.error('Error processing shortcodes:', error);
return html;
}
}
/**
* Extract attributes from HTML tag
*/
function extractAttributesFromHTML(html: string): string {
// Extract all key="value" pairs from HTML tag
const attrMatches = html.matchAll(/([a-zA-Z-]+)=["']([^"']*)["']/g);
const attrs: string[] = [];
// Process shortcode blocks first (most complex)
processed = processVcRowShortcodes(processed);
processed = processVcColumnShortcodes(processed);
processed = processVcColumnTextShortcodes(processed);
processed = processVcImageShortcodes(processed);
processed = processVcButtonShortcodes(processed);
processed = processVcSeparatorShortcodes(processed);
processed = processVcVideoShortcodes(processed);
processed = processBackgroundShortcodes(processed);
for (const match of attrMatches) {
const key = match[1];
const value = match[2];
// Map HTML data attributes back to shortcode attributes
if (key.startsWith('data-')) {
const shortcodeKey = key.replace('data-', '').replace(/-([a-z])/g, (g) => g[1].toUpperCase());
attrs.push(`${shortcodeKey}="${value}"`);
} else if (key === 'class') {
// Skip class attribute for shortcode conversion
continue;
} else {
attrs.push(`${key}="${value}"`);
}
}
// Remove any remaining shortcodes
processed = processed.replace(/\[[^\]]*\]/g, '');
return processed;
return attrs.join(' ');
}
/**
* Process [vc_row] shortcodes and convert to flex containers
* Also handles underscored versions: vc_row, vc_row_inner
*/
function processVcRowShortcodes(html: string): string {
return html.replace(/\[vc_row([^\]]*)\]([\s\S]*?)\[\/vc_row\]/g, (match, attrs, content) => {
return html.replace(/\[vc_row(?:_inner)?([^\]]*)\]([\s\S]*?)\[\/vc_row(?:_inner)?\]/g, (match, attrs, content) => {
const classes = ['vc-row', 'flex', 'flex-wrap', '-mx-4'];
// Parse attributes for background colors, images, etc.