diff --git a/bin/qsgen3 b/bin/qsgen3 index 69ad584..135c804 100755 --- a/bin/qsgen3 +++ b/bin/qsgen3 @@ -257,10 +257,16 @@ _load_config() { # Default build options if not set : ${QSG_CONFIG[build_options_process_drafts]:=false} : ${QSG_CONFIG[build_options_generate_rss]:=true} + : ${QSG_CONFIG[build_options_minify_html]:=false} + : ${QSG_CONFIG[build_options_minify_css]:=false} + : ${QSG_CONFIG[build_options_minify_xml]:=false} # : ${QSG_CONFIG[build_options_generate_sitemap]:=true} # Example if sitemap is added _log DEBUG "build_options_process_drafts set to: ${QSG_CONFIG[build_options_process_drafts]}" _log DEBUG "build_options_generate_rss set to: ${QSG_CONFIG[build_options_generate_rss]}" + _log DEBUG "build_options_minify_html set to: ${QSG_CONFIG[build_options_minify_html]}" + _log DEBUG "build_options_minify_css set to: ${QSG_CONFIG[build_options_minify_css]}" + _log DEBUG "build_options_minify_xml set to: ${QSG_CONFIG[build_options_minify_xml]}" # _log DEBUG "build_options_generate_sitemap set to: ${QSG_CONFIG[build_options_generate_sitemap]}" if [[ "$missing_configs" == true ]]; then @@ -341,6 +347,255 @@ _generate_sitemap() { return 0 } +# --- Minification Functions --- +_minify_html() { + local input_file="$1" + local output_file="$2" + + if [[ ! -f "$input_file" ]]; then + _log ERROR "Input file for HTML minification not found: $input_file" + return 1 + fi + + _log DEBUG "Minifying HTML: $input_file -> $output_file" + + # HTML minification using sed and tr + # Remove comments, extra whitespace, and empty lines while preserving content + sed -e ' + # Remove HTML comments (but preserve conditional comments) + //d + //!{ + N + b comment + } + d + } + } + # Remove leading and trailing whitespace from lines + s/^[[:space:]]*// + s/[[:space:]]*$// + # Remove empty lines + /^$/d + # Compress multiple spaces between tags to single space + s/>[[:space:]]\+|\n|g + s||\n|g + s||\n|g + ' > "$output_file" + + if [[ $? -eq 0 ]]; then + _log DEBUG "HTML minification completed successfully" + return 0 + else + _log ERROR "HTML minification failed" + return 1 + fi +} + +_minify_css() { + local input_file="$1" + local output_file="$2" + + if [[ ! -f "$input_file" ]]; then + _log ERROR "Input file for CSS minification not found: $input_file" + return 1 + fi + + _log DEBUG "Minifying CSS: $input_file -> $output_file" + + # CSS minification using sed + sed -e ' + # Remove CSS comments + /\/\*/{ + :comment + /\*\//!{ + N + b comment + } + s|/\*.*\*/||g + } + # Remove leading and trailing whitespace + s/^[[:space:]]*// + s/[[:space:]]*$// + # Remove empty lines + /^$/d + # Remove spaces around { } : ; , + s/[[:space:]]*{[[:space:]]*/{/g + s/[[:space:]]*}[[:space:]]*/}/g + s/[[:space:]]*:[[:space:]]*/:/g + s/[[:space:]]*;[[:space:]]*/;/g + s/[[:space:]]*,[[:space:]]*/,/g + # Remove trailing semicolon before } + s/;}/}/g + # Compress multiple spaces to single space + s/[[:space:]]\+/ /g + ' "$input_file" | tr -d '\n' > "$output_file" + + if [[ $? -eq 0 ]]; then + _log DEBUG "CSS minification completed successfully" + return 0 + else + _log ERROR "CSS minification failed" + return 1 + fi +} + +_minify_xml() { + local input_file="$1" + local output_file="$2" + + if [[ ! -f "$input_file" ]]; then + _log ERROR "Input file for XML minification not found: $input_file" + return 1 + fi + + _log DEBUG "Minifying XML: $input_file -> $output_file" + + # XML minification using sed + sed -e ' + # Remove XML comments + //!{ + N + b comment + } + s|||g + } + # Remove leading and trailing whitespace + s/^[[:space:]]*// + s/[[:space:]]*$// + # Remove empty lines + /^$/d + # Compress whitespace between tags + s/>[[:space:]]\+|?>\n|g + s||\n|g + s||\n|g + ' > "$output_file" + + if [[ $? -eq 0 ]]; then + _log DEBUG "XML minification completed successfully" + return 0 + else + _log ERROR "XML minification failed" + return 1 + fi +} + +_minify_file() { + local file_path="$1" + + if [[ ! -f "$file_path" ]]; then + _log ERROR "File for minification not found: $file_path" + return 1 + fi + + local file_extension="${file_path##*.}" + local temp_file="${file_path}.tmp" + local should_minify=false + + case "$file_extension" in + html|htm) + if [[ "${QSG_CONFIG[build_options_minify_html]}" == "true" ]]; then + should_minify=true + _minify_html "$file_path" "$temp_file" + fi + ;; + css) + if [[ "${QSG_CONFIG[build_options_minify_css]}" == "true" ]]; then + should_minify=true + _minify_css "$file_path" "$temp_file" + fi + ;; + xml) + if [[ "${QSG_CONFIG[build_options_minify_xml]}" == "true" ]]; then + should_minify=true + _minify_xml "$file_path" "$temp_file" + fi + ;; + *) + _log DEBUG "No minification available for file type: $file_extension ($file_path)" + return 0 + ;; + esac + + if [[ "$should_minify" == "true" ]]; then + if [[ $? -eq 0 && -f "$temp_file" ]]; then + # Calculate size reduction + local original_size=$(wc -c < "$file_path") + local minified_size=$(wc -c < "$temp_file") + local reduction=$((original_size - minified_size)) + local percentage=0 + if [[ $original_size -gt 0 ]]; then + percentage=$(( (reduction * 100) / original_size )) + fi + + mv "$temp_file" "$file_path" + _log INFO "Minified $file_path: ${original_size} -> ${minified_size} bytes (${percentage}% reduction)" + else + _log ERROR "Minification failed for $file_path" + [[ -f "$temp_file" ]] && rm -f "$temp_file" + return 1 + fi + fi + + return 0 +} + +_minify_output_directory() { + local output_dir="${QSG_CONFIG[paths_output_dir]}" + + if [[ ! -d "$output_dir" ]]; then + _log WARNING "Output directory not found for minification: $output_dir" + return 0 + fi + + _log INFO "Starting minification of generated files..." + + # Find and minify all relevant files + local files_processed=0 + local files_minified=0 + + while IFS= read -r -d '' file; do + files_processed=$((files_processed + 1)) + local file_extension="${file##*.}" + local should_process=false + + case "$file_extension" in + html|htm) + [[ "${QSG_CONFIG[build_options_minify_html]}" == "true" ]] && should_process=true + ;; + css) + [[ "${QSG_CONFIG[build_options_minify_css]}" == "true" ]] && should_process=true + ;; + xml) + [[ "${QSG_CONFIG[build_options_minify_xml]}" == "true" ]] && should_process=true + ;; + esac + + if [[ "$should_process" == "true" ]]; then + if _minify_file "$file"; then + files_minified=$((files_minified + 1)) + fi + fi + done < <(find "$output_dir" -type f \( -name "*.html" -o -name "*.htm" -o -name "*.css" -o -name "*.xml" \) -print0) + + _log INFO "Minification complete: ${files_minified}/${files_processed} files minified" + return 0 +} + # --- Core Functions --- _clean_output_dir() { _log INFO "Cleaning output directory: ${QSG_CONFIG[paths_output_dir]}" @@ -1232,6 +1487,9 @@ main() { _generate_rss_feed if [[ $? -ne 0 ]]; then _log ERROR "RSS feed generation failed."; exit 1; fi + _minify_output_directory + if [[ $? -ne 0 ]]; then _log WARNING "Minification encountered issues."; fi # Non-fatal, just a warning + _generate_sitemap if [[ $? -ne 0 ]]; then _log WARNING "Sitemap generation encountered issues."; fi # Non-fatal, just a warning diff --git a/site.conf b/site.conf index eff513c..523d0f2 100644 --- a/site.conf +++ b/site.conf @@ -17,3 +17,6 @@ paths_static_dir="static" build_options_generate_rss=true build_options_generate_sitemap=true build_options_process_drafts=false +build_options_minify_html=true +build_options_minify_css=true +build_options_minify_xml=true diff --git a/site.conf.example b/site.conf.example index 62ecc84..e06d39a 100644 --- a/site.conf.example +++ b/site.conf.example @@ -28,3 +28,17 @@ build_options_generate_sitemap=true # Generate a sitemap.xml. Set to 'tr build_options_process_drafts=false # Process draft posts/pages. Set to 'true' to include content marked as 'draft: true' in its frontmatter, # 'false' to exclude them from the build. + +# --- Minification Options --- +# Control file minification to reduce file sizes and improve site loading performance. +build_options_minify_html=false # Minify HTML files by removing comments, extra whitespace, and empty lines. + # Set to 'true' to enable HTML minification, 'false' to disable. + # Typical size reduction: 25-35%. + +build_options_minify_css=false # Minify CSS files by removing comments, whitespace, and optimizing syntax. + # Set to 'true' to enable CSS minification, 'false' to disable. + # Typical size reduction: 5-15%. + +build_options_minify_xml=false # Minify XML files (RSS feeds, sitemaps) by removing comments and extra whitespace. + # Set to 'true' to enable XML minification, 'false' to disable. + # Typical size reduction: 5-10%.