provisioning-outreach/presentations/rust-laspalmas-250926/auroraframe/framework/optimizers/optimize-js.nu

207 lines
No EOL
7.4 KiB
Text

#!/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<string>] {
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 <input> -o <output> --minify
(run-external $cmd_info.command $temp_input "-o" $temp_output ...$cmd_info.args)
} else {
# Other tools use: tool <args> <input> --output <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<string>, 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
}