#!/usr/bin/env nu # Main build script for web page optimization # Optimizes CSS, SVG, and HTML for production # Import utilities use optimize-css.nu * use optimize-svg.nu * use optimize-html.nu * use utils.nu * # Main build function export def build_page [ --clean(-c) # Clean dist directory before build --dev(-d) # Development build (no minification) --verbose(-v) # Verbose output --analyze(-a) # Show file size analysis ] { let config = (load_config) if $verbose { print "๐Ÿš€ Starting page build..." print $"Source: ($config.src_dir)" print $"Destination: ($config.dist_dir)" } # Clean dist directory if requested if $clean { clean_dist $config $verbose } # Create directory structure setup_directories $config $verbose # Build assets in parallel print "๐Ÿ“ฆ Building assets..." # Process CSS (critical + non-critical) let css_result = (build_css $config $dev $verbose) # Process SVGs let svg_result = (build_svgs $config $dev $verbose) # Process HTML with inlined critical resources let html_result = (build_html $config $dev $verbose $css_result.critical) # Copy remaining assets copy_assets $config $verbose # Generate report if $analyze { generate_report $config $verbose } print "โœ… Build completed successfully!" if $verbose { print $"๐Ÿ“Š Build summary:" print $" HTML: ($html_result.size) bytes" print $" CSS: ($css_result.size) bytes" print $" SVG: ($svg_result.size) bytes" print $" Total: (($html_result.size + $css_result.size + $svg_result.size)) bytes" } } # Clean distribution directory def clean_dist [config: record, verbose: bool] { if $verbose { print "๐Ÿงน Cleaning dist directory..." } if ($config.dist_dir | path exists) { rm -rf $config.dist_dir } } # Setup directory structure def setup_directories [config: record, verbose: bool] { if $verbose { print "๐Ÿ“ Creating directory structure..." } mkdir $config.dist_dir mkdir $config.css_dist mkdir $config.svg_dist } # Build CSS (split into critical and non-critical) def build_css [config: record, dev: bool, verbose: bool] { if $verbose { print "๐ŸŽจ Processing CSS..." } let critical_modules = ($config.css_modules | first $config.critical_css_modules) let non_critical_modules = ($config.css_modules | skip $config.critical_css_modules) # Build critical CSS for inlining let critical_css = ($critical_modules | each { |module| open $"($config.css_src)/($module)" } | str join "\n" ) # Build non-critical CSS for external file let non_critical_css = ($non_critical_modules | each { |module| open $"($config.css_src)/($module)" } | str join "\n" ) # Add SVG data CSS and combine all CSS modules let svg_data_css = (open $"($config.css_src)/svg-data.css") let full_css = if $dev { # In dev mode, include ALL modules for easier debugging ([$svg_data_css, $critical_css, $non_critical_css] | str join "\n") } else { # In production mode, only external CSS (critical will be inlined) ([$svg_data_css, $non_critical_css] | str join "\n") } # Minify if not dev build let final_css = if $dev { $full_css } else { minify_css $full_css $config } # Save external CSS $final_css | save --force $"($config.css_dist)/($config.css_output)" # Return results { critical: (if $dev { $critical_css } else { minify_css $critical_css $config }), size: ($final_css | str length), critical_size: ($critical_css | str length) } } # Build SVGs def build_svgs [config: record, dev: bool, verbose: bool] { if $verbose { print "๐Ÿ–ผ๏ธ Processing SVGs..." } # Copy symbols (needed for inline use) cp $"($config.svg_src)/symbols.svg" $"($config.svg_dist)/symbols.svg" # Process decorative sprites let sprites_content = (open $"($config.svg_src)/decorative-sprites.svg") let optimized_sprites = if $dev { $sprites_content } else { optimize_svg_content $sprites_content $config } $optimized_sprites | save --force $"($config.svg_dist)/($config.svg_sprites)" { size: ($optimized_sprites | str length) } } # Build HTML with critical inlining def build_html [config: record, dev: bool, verbose: bool, critical_css: string] { if $verbose { print "๐Ÿ“„ Processing HTML..." } let html_content = (open $"($config.src_dir)/index.html") # Replace CSS link with critical inline + external link let css_replacement = if ($critical_css | str length) > $config.inline_css_max_size { # Critical CSS too large, use external only $'' } else { # Inline critical + external non-critical $' ' } # Replace the CSS import - handle different CSS file names let updated_html = ($html_content | str replace '' $css_replacement | str replace '' $css_replacement | str replace -r '' $css_replacement ) # Minify HTML if not dev build let final_html = if $dev { $updated_html } else { minify_html $updated_html $config } $final_html | save --force $"($config.dist_dir)/($config.html_output)" { size: ($final_html | str length) } } # Copy additional assets def copy_assets [config: record, verbose: bool] { if $verbose { print "๐Ÿ“‹ Copying additional assets..." } # Copy README if exists if ($"($config.src_dir)/README.md" | path exists) { cp $"($config.src_dir)/README.md" $"($config.dist_dir)/README.md" } } # Generate build report def generate_report [config: record, verbose: bool] { print "\n๐Ÿ“Š Build Analysis Report" print "========================" # Get file sizes let files = [ { name: "index.html", path: $"($config.dist_dir)/index.html" }, { name: "main.min.css", path: $"($config.css_dist)/($config.css_output)" }, { name: "sprites.svg", path: $"($config.svg_dist)/($config.svg_sprites)" }, { 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_kb = ($size / 1024 | math round -p 1) let size_text = $"($size) bytes" let kb_text = $"($size_kb) KB" print $" ($file.name): ($size_text) ($kb_text)" } } 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 ) let total_kb = ($total_size / 1024 | math round -p 1) let total_text = $"($total_size) bytes" let total_kb_text = $"($total_kb) KB" print $"\nTotal size: ($total_text) ($total_kb_text)" # Estimate gzipped size (rough approximation) let estimated_gzip = ($total_size * 0.3 | math round) let estimated_gzip_kb = ($estimated_gzip / 1024 | math round -p 1) let gzip_text = $"($estimated_gzip) bytes" let gzip_kb_text = $"($estimated_gzip_kb) KB" print $"Estimated gzipped: ($gzip_text) ($gzip_kb_text)" } # Main entry point for command line usage def main [ --clean(-c) # Clean dist directory before build --dev(-d) # Development build (no minification) --verbose(-v) # Verbose output --analyze(-a) # Show file size analysis ] { build_page --clean=$clean --dev=$dev --verbose=$verbose --analyze=$analyze } # Quick build command def "build quick" [] { build_page --clean --verbose } # Development build command def "build dev" [] { build_page --dev --verbose } # Production build with analysis def "build prod" [] { build_page --clean --verbose --analyze }