153 lines
5.1 KiB
Text
153 lines
5.1 KiB
Text
|
|
#!/usr/bin/env nu
|
||
|
|
# CSS optimization utilities
|
||
|
|
|
||
|
|
# Minify CSS content
|
||
|
|
export def minify_css [css_content: string, config: record] {
|
||
|
|
let minified = ($css_content
|
||
|
|
# Remove CSS 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*' '~'
|
||
|
|
# Remove trailing semicolons before }
|
||
|
|
| str replace -ar ';\s*}' '}'
|
||
|
|
# Remove empty rules
|
||
|
|
| str replace -ar '[^}]+\{\s*\}' ''
|
||
|
|
# Trim
|
||
|
|
| str trim
|
||
|
|
)
|
||
|
|
|
||
|
|
$minified
|
||
|
|
}
|
||
|
|
|
||
|
|
# Extract critical CSS from modules
|
||
|
|
export def extract_critical_css [modules: list<string>, css_dir: string] {
|
||
|
|
$modules | each { |module|
|
||
|
|
let module_path = $"($css_dir)/($module)"
|
||
|
|
if ($module_path | path exists) {
|
||
|
|
open $module_path
|
||
|
|
} else {
|
||
|
|
""
|
||
|
|
}
|
||
|
|
} | str join "\n"
|
||
|
|
}
|
||
|
|
|
||
|
|
# Combine CSS modules
|
||
|
|
export def combine_css_modules [modules: list<string>, css_dir: string] {
|
||
|
|
$modules | each { |module|
|
||
|
|
let module_path = $"($css_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"
|
||
|
|
}
|
||
|
|
|
||
|
|
# Optimize CSS custom properties
|
||
|
|
export def optimize_css_variables [css_content: string] {
|
||
|
|
# This could be extended to:
|
||
|
|
# - Remove unused CSS custom properties
|
||
|
|
# - Optimize color values (hex shorthand)
|
||
|
|
# - Combine similar rules
|
||
|
|
$css_content
|
||
|
|
}
|
||
|
|
|
||
|
|
# Generate CSS source map (for debugging)
|
||
|
|
export def generate_css_sourcemap [original_modules: list<string>, minified_css: string] {
|
||
|
|
let sourcemap = {
|
||
|
|
version: 3,
|
||
|
|
sources: $original_modules,
|
||
|
|
names: [],
|
||
|
|
mappings: "",
|
||
|
|
file: "main.min.css"
|
||
|
|
}
|
||
|
|
|
||
|
|
$sourcemap | to json
|
||
|
|
}
|
||
|
|
|
||
|
|
# Analyze CSS for optimization opportunities
|
||
|
|
export def analyze_css [css_content: string] {
|
||
|
|
let lines = ($css_content | lines)
|
||
|
|
let total_lines = ($lines | length)
|
||
|
|
let comment_lines = ($lines | where ($it | str starts-with "/*") | length)
|
||
|
|
let empty_lines = ($lines | where ($it | str trim) == "" | length)
|
||
|
|
let rule_lines = ($lines | where ($it | str contains "{") | length)
|
||
|
|
|
||
|
|
{
|
||
|
|
total_lines: $total_lines,
|
||
|
|
comment_lines: $comment_lines,
|
||
|
|
empty_lines: $empty_lines,
|
||
|
|
rule_lines: $rule_lines,
|
||
|
|
content_lines: ($total_lines - $comment_lines - $empty_lines),
|
||
|
|
size_bytes: ($css_content | str length),
|
||
|
|
estimated_minified_size: (($css_content | str length) * 0.6 | math round)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Split CSS into critical and non-critical based on selectors
|
||
|
|
export def split_critical_css [css_content: string] {
|
||
|
|
let lines = ($css_content | lines)
|
||
|
|
|
||
|
|
# Define critical selectors (above-the-fold)
|
||
|
|
let critical_selectors = [
|
||
|
|
"body", "html", ".poster", ".title", ".header",
|
||
|
|
".ocean-tech-bg", ".top-logo", ".main-title",
|
||
|
|
"@keyframes gradient-shift", "@keyframes progressBar"
|
||
|
|
]
|
||
|
|
|
||
|
|
mut critical_rules = []
|
||
|
|
mut non_critical_rules = []
|
||
|
|
mut current_rule = ""
|
||
|
|
mut in_critical_rule = false
|
||
|
|
|
||
|
|
for line in $lines {
|
||
|
|
if ($line | str contains "{") {
|
||
|
|
# Start of a new rule
|
||
|
|
let is_critical = ($critical_selectors | any { |sel| $line | str contains $sel })
|
||
|
|
$in_critical_rule = $is_critical
|
||
|
|
$current_rule = $line
|
||
|
|
} else if ($line | str contains "}") {
|
||
|
|
# End of rule
|
||
|
|
$current_rule = ($current_rule + "\n" + $line)
|
||
|
|
if $in_critical_rule {
|
||
|
|
$critical_rules = ($critical_rules | append $current_rule)
|
||
|
|
} else {
|
||
|
|
$non_critical_rules = ($non_critical_rules | append $current_rule)
|
||
|
|
}
|
||
|
|
$current_rule = ""
|
||
|
|
} else {
|
||
|
|
# Inside rule
|
||
|
|
$current_rule = ($current_rule + "\n" + $line)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
{
|
||
|
|
critical: ($critical_rules | str join "\n"),
|
||
|
|
non_critical: ($non_critical_rules | str join "\n")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# CSS performance metrics
|
||
|
|
export def css_performance_metrics [css_content: string] {
|
||
|
|
let selectors = ($css_content | str replace -ar '\{[^}]*\}' '' | lines | where ($it | str trim) != "")
|
||
|
|
let properties = ($css_content | str replace -ar '[^{]*\{|\}[^{]*' '' | split row ";" | where ($it | str trim) != "")
|
||
|
|
|
||
|
|
{
|
||
|
|
total_selectors: ($selectors | length),
|
||
|
|
total_properties: ($properties | length),
|
||
|
|
avg_properties_per_rule: (($properties | length) / ($selectors | length) | math round -p 1),
|
||
|
|
complex_selectors: ($selectors | where ($it | str contains " ") | length),
|
||
|
|
id_selectors: ($selectors | where ($it | str contains "#") | length),
|
||
|
|
class_selectors: ($selectors | where ($it | str contains ".") | length)
|
||
|
|
}
|
||
|
|
}
|