#!/usr/bin/env nu # Email optimization and compatibility processor # Transforms HTML for email client compatibility # Main email processing function export def process_email [html_content: string, config: record, dev: bool, verbose: bool] { let email_config = if "email" in ($config | columns) { $config.email } else { { enabled: false } } if not $email_config.enabled { if $verbose { print "📧 Email optimization disabled, skipping..." } return { html: $html_content, text_version: "", size: 0 } } if $verbose { print "📧 Processing email optimizations..." } # Apply email-specific transformations let optimized_html = ($html_content | inline_all_css $email_config $verbose | optimize_for_email_clients $email_config $verbose | convert_relative_urls $email_config $verbose | strip_unsupported_elements $email_config $verbose | add_email_fallbacks $email_config $verbose ) # Generate plain text version if requested let text_version = if "text_output" in ($email_config.output | columns) { generate_text_version $optimized_html $verbose } else { "" } { html: $optimized_html, text_version: $text_version, size: ($optimized_html | str length) } } # Inline all CSS for email compatibility def inline_all_css [html_content: string, email_config: record, verbose: bool] { if not $email_config.inline_css { return $html_content } if $verbose { print " 🎨 Inlining all CSS for email compatibility..." } # Extract CSS from ' '::STYLE::$1::STYLE::') let style_parts = ($style_matches | split row '::STYLE::' | where { |part| not ($part | str starts-with '::') and ($part | str length) > 0 }) for part in $style_parts { if not ($part | str starts-with '::') { $css_content = ($css_content + "\n" + $part) } } # Find all tags and read their content let link_matches = ($html_content | str find-replace -ar ']*rel="stylesheet"[^>]*href="([^"]*)"[^>]*>' '::LINK::$1::LINK::') let link_parts = ($link_matches | split row '::LINK::' | where { |part| not ($part | str starts-with '::') and ($part | str length) > 0 }) for link_url in $link_parts { if not ($link_url | str starts-with '::') { # Try to read the CSS file let css_path = if ($link_url | str starts-with 'http') { # External URL - would need http get continue } else { # Local file $link_url } if ($css_path | path exists) { let css_file_content = (open $css_path) $css_content = ($css_content + "\n" + $css_file_content) } } } # Remove all ' '' | str replace -ar ']*rel="stylesheet"[^>]*>' '' ) # Apply CSS inlining (simplified version - in production would use a proper CSS parser) let inlined_html = (apply_css_inline $html_without_css $css_content $verbose) $inlined_html } # Apply CSS rules inline to elements (simplified implementation) def apply_css_inline [html_content: string, css_content: string, verbose: bool] { # This is a simplified CSS inlining - in production, you'd use a proper CSS parser # For now, just add the CSS as an inline style block for email clients that support it let style_block = if ($css_content | str length) > 0 { $"" } else { "" } # Insert the style block in the head let result = ($html_content | str replace '' $"($style_block)\n") if $verbose { print " ✅ CSS inlined (simplified implementation)" } $result } # Optimize HTML for email client compatibility def optimize_for_email_clients [html_content: string, email_config: record, verbose: bool] { if $verbose { print " 🔧 Optimizing for email client compatibility..." } mut optimized_html = $html_content # Convert divs to tables for better Outlook compatibility if $email_config.compatibility.outlook { # This is a simplified conversion - in production would be more sophisticated $optimized_html = ($optimized_html | str replace -ar ']*class="[^"]*container[^"]*"[^>]*)>' '' | str replace -ar '(\s*)?' '' ) } # Add Outlook-specific MSO conditionals if $email_config.compatibility.outlook { $optimized_html = ($optimized_html | str replace '
' '' ) } # Ensure proper width constraints for email let max_width = $email_config.max_width $optimized_html = ($optimized_html | str replace -ar 'max-width:\s*\d+px' $"max-width: ($max_width)px" | str replace -ar 'width:\s*100%' $"width: ($max_width)px; max-width: 100%" ) if $verbose { print " ✅ Applied email client optimizations" } $optimized_html } # Convert relative URLs to absolute URLs def convert_relative_urls [html_content: string, email_config: record, verbose: bool] { if not $email_config.absolute_urls { return $html_content } if $verbose { print " 🔗 Converting relative URLs to absolute..." } # This would need a base URL from config let base_url = if "base_url" in ($email_config | columns) { $email_config.base_url } else { "https://example.com" } let result = ($html_content | str replace -ar 'href="(?!http|mailto|#)([^"]*)"' $'href="($base_url)/$1"' | str replace -ar 'src="(?!http|data:)([^"]*)"' $'src="($base_url)/$1"' ) if $verbose { print " ✅ Converted relative URLs" } $result } # Remove unsupported HTML elements and CSS properties def strip_unsupported_elements [html_content: string, email_config: record, verbose: bool] { if not $email_config.remove_unsupported { return $html_content } if $verbose { print " 🚫 Removing unsupported elements..." } mut stripped_html = $html_content # Remove JavaScript if $email_config.strip_js { $stripped_html = ($stripped_html | str replace -ar ']*>.*?' '' | str replace -ar 'on\w+="[^"]*"' '' | str replace -ar "on\w+='[^']*'" '' ) } # Remove unsupported CSS properties $stripped_html = ($stripped_html | str replace -ar 'position:\s*(fixed|absolute|sticky);?' '' | str replace -ar 'z-index:\s*\d+;?' '' | str replace -ar 'transform:[^;]+;?' '' | str replace -ar 'transition:[^;]+;?' '' | str replace -ar 'animation:[^;]+;?' '' | str replace -ar '@keyframes[^}]+}' '' | str replace -ar '@media[^{]+{[^}]+}' '' ) # Remove form elements (not well supported in email) $stripped_html = ($stripped_html | str replace -ar ']*>.*?' '' | str replace -ar ']*>' '' | str replace -ar ']*>.*?' '' | str replace -ar ']*>.*?' '' | str replace -ar ']*>.*?' '' ) if $verbose { print " ✅ Removed unsupported elements" } $stripped_html } # Add fallbacks for email clients def add_email_fallbacks [html_content: string, email_config: record, verbose: bool] { if $verbose { print " 🔄 Adding email client fallbacks..." } mut fallback_html = $html_content # Add fallback fonts let fallback_fonts = ($email_config.fallback_fonts | str join ", ") $fallback_html = ($fallback_html | str replace -ar 'font-family:\s*[^;]+' $"font-family: ($fallback_fonts)" ) # Add alt text to images that don't have it $fallback_html = ($fallback_html | str replace -ar ']+)src="([^"]*)"([^>]*?)>' '' | str replace -ar 'alt="Image"([^>]*?)alt="([^"]*)"' 'alt="$2"$1' ) # Add table-based layout fallbacks $fallback_html = ($fallback_html | str replace -ar '
' '
' | str replace -ar '(\s*)?' '
' ) if $verbose { print " ✅ Added email client fallbacks" } $fallback_html } # Generate plain text version of the email def generate_text_version [html_content: string, verbose: bool] { if $verbose { print " 📝 Generating plain text version..." } # Remove HTML tags and convert to plain text let text_content = ($html_content | str replace -ar ']*>.*?' '' | str replace -ar ']*>.*?' '' | str replace -ar '<[^>]+>' '' | str replace -ar ' ' ' ' | str replace -ar '&' '&' | str replace -ar '<' '<' | str replace -ar '>' '>' | str replace -ar '"' '"' | str replace -ar ''' "'" | str replace -ar '\s+' ' ' | str trim ) if $verbose { print " ✅ Generated plain text version" } $text_content } # Save email outputs export def save_email_outputs [html_content: string, text_content: string, config: record, verbose: bool] { let email_config = $config.email let output_config = $email_config.output # Save HTML version let html_output_path = $"($config.dist_dir)/($output_config.html_output)" $html_content | save --force $html_output_path if $verbose { print $" ✅ Saved email HTML: ($html_output_path)" } # Save text version if configured if "text_output" in ($output_config | columns) and ($text_content | str length) > 0 { let text_output_path = $"($config.dist_dir)/($output_config.text_output)" $text_content | save --force $text_output_path if $verbose { print $" ✅ Saved email text: ($text_output_path)" } } # Generate preview version if configured if "preview_output" in ($output_config | columns) { let preview_html = (generate_email_preview $html_content $config) let preview_output_path = $"($config.dist_dir)/($output_config.preview_output)" $preview_html | save --force $preview_output_path if $verbose { print $" ✅ Saved email preview: ($preview_output_path)" } } } # Generate email preview with client simulation def generate_email_preview [email_html: string, config: record] { let preview_html = $" Email Preview
📧 Email Preview - Web Builder Framework

Note: This is a preview of how your email might appear in email clients.

($email_html)
" $preview_html }