provisioning-outreach/presentations/rust-laspalmas-250926/auroraframe/scripts/build.nu

276 lines
8.4 KiB
Text
Raw Normal View History

#!/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
$'<link rel="stylesheet" href="assets/css/($config.css_output)">'
} else {
# Inline critical + external non-critical
$'<style>($critical_css)</style>
<link rel="preload" href="assets/css/($config.css_output)" as="style" onload="this.onload=null;this.rel=\'stylesheet\'">
<noscript><link rel="stylesheet" href="assets/css/($config.css_output)"></noscript>'
}
# Replace the CSS import - handle different CSS file names
let updated_html = ($html_content
| str replace '<link rel="stylesheet" href="assets/css/poster.css">' $css_replacement
| str replace '<link rel="stylesheet" href="assets/css/main.css">' $css_replacement
| str replace -r '<link rel="stylesheet" href="assets/css/[^"]*\.css">' $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
}