#!/usr/bin/env nu # JavaScript optimization utilities # Check if external JS minifier tool is available export def check_js_tool_available [tool_name: string, tool_path: string] { if ($tool_path | str length) > 0 { # Use explicit path ($tool_path | path exists) } else { # Check if tool is in PATH (which $tool_name | length) > 0 } } # Get the command to run for JS minification export def get_js_minifier_command [tool_name: string, tool_path: string, args: list] { let tool_command = if ($tool_path | str length) > 0 { $tool_path } else { $tool_name } # Default arguments for common tools if none provided let default_args = match $tool_name { "swc" => ["--minify", "--target", "es2018"], "terser" => ["--compress", "--mangle"], "uglifyjs" => ["--compress", "--mangle"], "esbuild" => ["--minify", "--target=es2018"], _ => [] } let final_args = if ($args | length) > 0 { $args } else { $default_args } { command: $tool_command, args: $final_args } } # Minify JavaScript using external tool export def minify_js_with_external_tool [js_content: string, config: record] { let tool_name = $config.js_minifier let tool_path = $config.js_minifier_path let tool_args = $config.js_minifier_args if not (check_js_tool_available $tool_name $tool_path) { error make {msg: $"JavaScript minifier '($tool_name)' not found"} } let cmd_info = (get_js_minifier_command $tool_name $tool_path $tool_args) # Create temporary files let temp_input = $"/tmp/js_input_(random chars -l 8).js" let temp_output = $"/tmp/js_output_(random chars -l 8).js" try { # Write input to temp file $js_content | save $temp_input # Run minification tool with tool-specific syntax let result = if $tool_name == "swc" { # SWC uses different syntax: swc -o --minify (run-external $cmd_info.command $temp_input "-o" $temp_output ...$cmd_info.args) } else { # Other tools use: tool --output (run-external $cmd_info.command ...$cmd_info.args $temp_input "--output" $temp_output) } # Read result if ($temp_output | path exists) { let minified = (open $temp_output) # Cleanup temp files rm $temp_input rm $temp_output $minified } else { error make {msg: $"Minification failed: output file not created"} } } catch { |err| # Cleanup temp files in case of error if ($temp_input | path exists) { rm $temp_input } if ($temp_output | path exists) { rm $temp_output } error make {msg: $"External minification failed: ($err.msg)"} } } # Basic JavaScript minification (fallback) export def minify_js_builtin [js_content: string, config: record] { let minified = ($js_content # Remove single-line comments (// ...) | str replace -ar '//.*?$' '' # Remove multi-line comments (/* ... */) | str replace -ar '/\*[\s\S]*?\*/' '' # Remove unnecessary whitespace | str replace -ar '\s+' ' ' # Remove spaces around specific characters | str replace -ar '\s*{\s*' '{' | str replace -ar '\s*}\s*' '}' | str replace -ar '\s*;\s*' ';' | str replace -ar '\s*,\s*' ',' | str replace -ar '\s*=\s*' '=' | str replace -ar '\s*\(\s*' '(' | str replace -ar '\s*\)\s*' ')' | str replace -ar '\s*\[\s*' '[' | str replace -ar '\s*\]\s*' ']' # Trim | str trim ) $minified } # Main JavaScript minification function with tool selection export def minify_js [js_content: string, config: record] { let tool_name = $config.js_minifier if ($tool_name | str length) > 0 { # Try external tool first try { minify_js_with_external_tool $js_content $config } catch { |err| print $"⚠️ External tool failed: ($err.msg), falling back to built-in minifier" minify_js_builtin $js_content $config } } else { # Use built-in minifier minify_js_builtin $js_content $config } } # Combine JavaScript modules export def combine_js_modules [modules: list, js_dir: string] { $modules | each { |module| let module_path = $"($js_dir)/($module)" if ($module_path | path exists) { let content = (open $module_path) $"/* === ($module) === */\n($content)\n" } else { $"/* WARNING: ($module) not found */\n" } } | str join "\n" } # Analyze JavaScript for optimization opportunities export def analyze_js [js_content: string] { let lines = ($js_content | lines) let total_lines = ($lines | length) let comment_lines = ($lines | where ($it | str starts-with "//") or ($it | str contains "/*") | length) let empty_lines = ($lines | where ($it | str trim) == "" | length) let function_lines = ($lines | where ($it | str contains "function") or ($it | str contains "=>") | length) { total_lines: $total_lines, comment_lines: $comment_lines, empty_lines: $empty_lines, function_lines: $function_lines, content_lines: ($total_lines - $comment_lines - $empty_lines), size_bytes: ($js_content | str length), estimated_minified_size: (($js_content | str length) * 0.7 | math round) } } # JavaScript performance metrics export def js_performance_metrics [js_content: string] { let functions = ($js_content | str replace -ar 'function\s+\w+\s*\([^)]*\)\s*\{[^}]*\}' '' | split row "function" | length) let variables = ($js_content | str replace -ar '(var|let|const)\s+\w+' '' | split row " " | length) let event_listeners = ($js_content | str replace -ar 'addEventListener\s*\(' '' | split row "addEventListener" | length) { estimated_functions: ($functions - 1), estimated_variables: (if $variables > 10 { $variables - 10 } else { 0 }), event_listeners: (if $event_listeners > 1 { $event_listeners - 1 } else { 0 }), has_jquery: ($js_content | str contains "$" or ($js_content | str contains "jQuery")), has_async: ($js_content | str contains "async" or ($js_content | str contains "await")), has_modules: ($js_content | str contains "import" or ($js_content | str contains "export")) } } # Generate performance hints for JavaScript export def generate_js_performance_hints [js_content: string] { mut hints = [] if ($js_content | str contains "document.getElementById") { $hints = ($hints | append "Consider caching DOM element selections") } if ($js_content | str contains "setInterval") or ($js_content | str contains "setTimeout") { $hints = ($hints | append "Ensure timers are properly cleared to prevent memory leaks") } if ($js_content | str contains "addEventListener") and not ($js_content | str contains "removeEventListener") { $hints = ($hints | append "Consider removing event listeners when no longer needed") } if ($js_content | str length) > 50000 { $hints = ($hints | append "Consider splitting large JavaScript files") } if ($js_content | str contains "console.log") { $hints = ($hints | append "Remove console.log statements in production") } $hints }