#!/usr/bin/env nu # Production workflow script use build.nu build_page use utils.nu * use optimize-html.nu * use optimize-css.nu * use optimize-svg.nu * # Production build with full optimization def main [ --analyze(-a) # Show detailed analysis --compress(-c) # Generate compressed versions --verbose(-v) # Verbose output --output(-o): string # Custom output directory ] { let config = if ($output | is-not-empty) { (load_config | update dist_dir $output) } else { (load_config) } print "šŸš€ Starting production build..." print $"šŸ“ Output directory: ($config.dist_dir)" # Validate configuration if not (validate_config $config) { print "āŒ Configuration validation failed" return } # Full production build let build_result = (measure_time "Production build" { build_page --clean --verbose --analyze }) # Generate compressed versions if requested if $compress { generate_compressed_versions $config $verbose } # Performance audit if $analyze { performance_audit $config $verbose } # Generate deployment files generate_deployment_files $config $verbose print "āœ… Production build completed successfully!" # Show final summary show_build_summary $config } # Generate compressed versions (gzip, brotli) def generate_compressed_versions [config: record, verbose: bool] { print "šŸ“¦ Generating compressed versions..." let files_to_compress = [ $"($config.dist_dir)/($config.html_output)", $"($config.css_dist)/($config.css_output)", $"($config.svg_dist)/sprites.svg" ] let compressed_files = (compress_files $files_to_compress) if $verbose { print $"šŸ“¦ Generated ($compressed_files | length) compressed files" } } # Comprehensive performance audit def performance_audit [config: record, verbose: bool] { print "\nšŸ” Performance Audit" print "====================" # HTML analysis let html_file = $"($config.dist_dir)/($config.html_output)" if ($html_file | path exists) { let html_content = (open $html_file) let html_analysis = (analyze_html $html_content) let performance_hints = (generate_performance_hints $html_content) print $"šŸ“„ HTML Analysis:" print $" Size: (format_file_size $html_analysis.size_bytes)" print $" Lines: ($html_analysis.lines)" print $" External resources: CSS($html_analysis.external_css), JS($html_analysis.external_js), Images($html_analysis.images)" if ($performance_hints | length) > 0 { print $" šŸ’” Performance hints:" $performance_hints | each { |hint| print $" - ($hint)" } } } # CSS analysis let css_file = $"($config.css_dist)/($config.css_output)" if ($css_file | path exists) { let css_content = (open $css_file) let css_analysis = (analyze_css $css_content) let css_metrics = (css_performance_metrics $css_content) print $"\nšŸŽØ CSS Analysis:" print $" Size: (format_file_size $css_analysis.size_bytes)" print $" Rules: ($css_analysis.rule_lines)" print $" Selectors: ($css_metrics.total_selectors)" print $" Properties: ($css_metrics.total_properties)" print $" Avg properties per rule: ($css_metrics.avg_properties_per_rule)" print $" Complex selectors: ($css_metrics.complex_selectors)" } # SVG analysis let svg_files = (glob $"($config.svg_dist)/*.svg") if ($svg_files | length) > 0 { print $"\nšŸ–¼ļø SVG Analysis:" let svg_report = (svg_performance_report $svg_files) $svg_report | each { |svg| print $" ($svg.file | path basename): (format_file_size $svg.size)" print $" Elements: ~($svg.elements), Paths: ~($svg.paths)" print $" Animations: ($svg.animations), Valid: ($svg.valid)" print $" Optimization potential: (format_file_size $svg.optimization_potential)" } } # Overall performance score calculate_performance_score $config } # Calculate overall performance score def calculate_performance_score [config: record] { let html_file = $"($config.dist_dir)/($config.html_output)" let css_file = $"($config.css_dist)/($config.css_output)" let total_size = ([$html_file, $css_file] | where ($it | path exists) | each { |file| let size_raw = (ls $file | get size | first) ($size_raw | into int) } | math sum ) let score = if $total_size < 50000 { "A+ (Excellent)" } else if $total_size < 100000 { "A (Good)" } else if $total_size < 200000 { "B (Average)" } else { "C (Needs optimization)" } print $"\n⭐ Performance Score: ($score)" print $" Total size: (format_file_size $total_size)" # Recommendations based on size if $total_size > 100000 { print "šŸ’” Recommendations:" print " - Consider splitting CSS into critical/non-critical" print " - Optimize SVG files further" print " - Enable server compression (gzip/brotli)" } } # Generate deployment files def generate_deployment_files [config: record, verbose: bool] { print "šŸ“‹ Generating deployment files..." # Ensure the output directory exists if not ($config.dist_dir | path exists) { mkdir $config.dist_dir } # Generate .htaccess for Apache generate_htaccess $config # Generate nginx.conf snippet generate_nginx_config $config # Generate deploy script generate_deploy_script $config # Generate robots.txt generate_robots_txt $config if $verbose { print "šŸ“‹ Generated deployment configuration files" } } # Generate Apache .htaccess def generate_htaccess [config: record] { let htaccess_content = '# Page Build System - Apache Configuration # Enable compression AddOutputFilterByType DEFLATE text/plain AddOutputFilterByType DEFLATE text/html AddOutputFilterByType DEFLATE text/xml AddOutputFilterByType DEFLATE text/css AddOutputFilterByType DEFLATE application/xml AddOutputFilterByType DEFLATE application/xhtml+xml AddOutputFilterByType DEFLATE application/rss+xml AddOutputFilterByType DEFLATE application/javascript AddOutputFilterByType DEFLATE application/x-javascript AddOutputFilterByType DEFLATE image/svg+xml # Cache static assets ExpiresActive on ExpiresByType text/css "access plus 1 year" ExpiresByType application/javascript "access plus 1 year" ExpiresByType image/svg+xml "access plus 1 year" ExpiresByType text/html "access plus 1 hour" # Security headers Header always set X-Content-Type-Options nosniff Header always set X-Frame-Options DENY Header always set X-XSS-Protection "1; mode=block" Header always set Content-Security-Policy "default-src '\''self'\''; style-src '\''self'\'' '\''unsafe-inline'\'' fonts.googleapis.com; font-src fonts.gstatic.com; img-src '\''self'\'' data:; script-src '\''none'\'';" # MIME types AddType image/svg+xml .svg AddType text/css .css' $htaccess_content | save --force $"($config.dist_dir)/.htaccess" } # Generate Nginx configuration snippet def generate_nginx_config [config: record] { let nginx_content = '# Page Build System - Nginx Configuration # Add this to your nginx server block location / { try_files $uri $uri/ =404; # Cache static assets location ~* \.(css|js|svg)$ { expires 1y; add_header Cache-Control "public, immutable"; } # HTML files - shorter cache location ~* \.html$ { expires 1h; add_header Cache-Control "public"; } } # Compression gzip on; gzip_vary on; gzip_min_length 1024; gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json image/svg+xml; # Security headers add_header X-Content-Type-Options nosniff; add_header X-Frame-Options DENY; add_header X-XSS-Protection "1; mode=block"; add_header Content-Security-Policy "default-src '\''self'\''; style-src '\''self'\'' '\''unsafe-inline'\'' fonts.googleapis.com; font-src fonts.gstatic.com; img-src '\''self'\'' data:; script-src '\''none'\'';";' $nginx_content | save --force $"($config.dist_dir)/nginx.conf" } # Generate deployment script def generate_deploy_script [config: record] { let deploy_script = '#!/bin/bash # Deploy script for optimized web page set -e echo "šŸš€ Deploying optimized web page..." # Configuration DIST_DIR="dist" REMOTE_HOST="${DEPLOY_HOST:-your-server.com}" REMOTE_PATH="${DEPLOY_PATH:-/var/www/optimized-page}" REMOTE_USER="${DEPLOY_USER:-deploy}" # Check if dist directory exists if [ ! -d "$DIST_DIR" ]; then echo "āŒ Distribution directory not found. Run build first." exit 1 fi # Deploy via rsync echo "šŸ“¦ Uploading files..." rsync -avz --delete \ --exclude="*.map" \ --exclude="build-manifest.json" \ "$DIST_DIR/" "$REMOTE_USER@$REMOTE_HOST:$REMOTE_PATH/" echo "āœ… Deployment completed!" echo "🌐 Site should be available at: https://$REMOTE_HOST"' $deploy_script | save --force $"($config.dist_dir)/deploy.sh" # Make executable chmod +x $"($config.dist_dir)/deploy.sh" } # Generate robots.txt def generate_robots_txt [config: record] { let robots_content = 'User-agent: * Allow: / # Sitemap Sitemap: https://your-domain.com/sitemap.xml' $robots_content | save --force $"($config.dist_dir)/robots.txt" } # Show build summary def show_build_summary [config: record] { print "\nšŸ“Š Build Summary" print "================" let files = [ { name: "index.html", path: $"($config.dist_dir)/($config.html_output)" }, { name: "main.min.css", path: $"($config.css_dist)/($config.css_output)" }, { name: "sprites.svg", path: $"($config.svg_dist)/sprites.svg" }, { name: "symbols.svg", path: $"($config.svg_dist)/symbols.svg" } ] $files | each { |file| if ($file.path | path exists) { let size_raw = (ls $file.path | get size | first) let size = ($size_raw | into int) let size_formatted = (format_file_size $size) # Check for compressed version let compressed_path = $"($file.path).gz" let compressed_info = if ($compressed_path | path exists) { let compressed_size_raw = (ls $compressed_path | get size | first) let compressed_size = ($compressed_size_raw | into int) let savings = (($size - $compressed_size) / $size * 100 | math round -p 1) $" \(gzipped: (format_file_size $compressed_size), -($savings)%\)" } else { "" } print $" šŸ“„ ($file.name): ($size_formatted)($compressed_info)" } } # Total uncompressed size let total_size = ($files | each { |file| if ($file.path | path exists) { let size_raw = (ls $file.path | get size | first) ($size_raw | into int) } else { 0 } } | math sum ) print $"\nšŸ“¦ Total size: (format_file_size $total_size)" # Build info print $"\nšŸ“‹ Build completed at: (date now | format date '%Y-%m-%d %H:%M:%S')" print $"šŸ“ Output directory: ($config.dist_dir)" print $"🌐 Ready for deployment!" } # Quick production build def "main quick" [ --analyze(-a) # Show detailed analysis --compress(-c) # Generate compressed versions --verbose(-v) # Verbose output --output(-o): string # Custom output directory ] { # Quick build defaults to verbose, user can override other flags main --analyze=$analyze --compress=$compress --verbose=true --output=$output } # Production build with full analysis def "main full" [ --analyze(-a) # Show detailed analysis --compress(-c) # Generate compressed versions --verbose(-v) # Verbose output --output(-o): string # Custom output directory ] { # Full build defaults to analyze+compress+verbose, user can override let final_analyze = if $analyze != null { $analyze } else { true } let final_compress = if $compress != null { $compress } else { true } let final_verbose = if $verbose != null { $verbose } else { true } main --analyze=$final_analyze --compress=$final_compress --verbose=$final_verbose --output=$output } # Deploy to staging def "main staging" [ --analyze(-a) # Show detailed analysis --compress(-c) # Generate compressed versions --verbose(-v) # Verbose output --output(-o): string # Custom output directory ] { print "šŸš€ Building for staging..." # Staging defaults to output="staging" and verbose=true, user can override let final_output = if ($output | is-not-empty) { $output } else { "staging" } let final_verbose = if $verbose != null { $verbose } else { true } main --analyze=$analyze --compress=$compress --verbose=$final_verbose --output=$final_output print $"šŸ“¦ Staging build ready in ./($final_output)/" } # Production help def "main help" [] { print "šŸ­ Production Commands:" print "=======================" print " nu prod.nu - Full production build (all flags)" print " nu prod.nu quick - Quick production build" print " nu prod.nu full - Full build with analysis and compression" print " nu prod.nu staging - Build for staging environment" print " nu prod.nu help - Show this help" print "" print "Main Command Flags:" print " --analyze, -a - Show detailed performance analysis" print " --compress, -c - Generate compressed versions (gzip)" print " --verbose, -v - Verbose output" print " --output, -o - Custom output directory" print "" print "All subcommands support all flags:" print " quick: defaults to --verbose" print " full: defaults to --analyze --compress --verbose" print " staging: defaults to --verbose --output staging" print "" print "Examples:" print " nu prod.nu --analyze --compress # Main with all flags" print " nu prod.nu --output staging # Main with custom output" print " nu prod.nu quick -v -o test # Quick build to test dir" print " nu prod.nu staging -a # Staging with analysis" print " nu prod.nu full -o production # Full build to production dir" }