Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invalid minified output when minifying Handlebars #183

Open
Ragudos opened this issue Mar 25, 2024 · 0 comments
Open

Invalid minified output when minifying Handlebars #183

Ragudos opened this issue Mar 25, 2024 · 0 comments

Comments

@Ragudos
Copy link

Ragudos commented Mar 25, 2024

I made a simple script to minify all templates on my project. There are some invalid outputs. Here's an example:

Input:

<ul class="list-of-links" id="list-of-community-links" {{#if oobswap}}hx-swap-oob="true"{{/if}}>
    <li>
        <a class="active-indicator {{#if (eq current_page "community")}}active{{/if}}" id="community-link" {{#if (eq current_page "community")}}aria-current="page"{{/if}} hx-target="main" hx-target-error="main" href="/community/{{community_id}}">Home</a>
    </li>
    <li>
        <a class="active-indicator {{#if (eq current_page "members")}}active{{/if}}" id="members-link" {{#if (eq current_page "members")}}aria-current="page"{{/if}} hx-target="main" hx-target-error="main" href="/community/{{community_id}}/members">Members</a>
    </li>
    <li>
        <a class="active-indicator {{#if (eq current_page "about")}}active{{/if}}" id="about-link" {{#if (eq current_page "about")}}aria-current="page"{{/if}} hx-target="main" hx-target-error="main" href="/community/{{community_id}}/about">About</a>
    </li>
    <li>
        <a class="active-indicator {{#if (eq current_page "settings")}}active{{/if}}" id="settings-link" {{#if (eq current_page "settings")}}aria-current="page"{{/if}} hx-target="main" hx-target-error="main" href="/community/{{community_id}}/settings">Settings</a>
    </li>
</ul>

Output:

<ul class=list-of-links id=list-of-community-links if}} oobswap}}hx-swap-oob=true {{ {{#if><li><a class="active-indicator {{#if (eq current_page""community")}}aria-current=page (eq community")}}active{{ current_page href=/community/{{community_id}} hx-target=main hx-target-error=main id=community-link if}} if}}" {{ {{#if>Home</a></li><li><a class="active-indicator {{#if (eq current_page""members")}}aria-current=page (eq current_page href=/community/{{community_id}}/members hx-target=main hx-target-error=main id=members-link if}} if}}" members")}}active{{ {{ {{#if>Members</a></li><li><a class="active-indicator {{#if (eq current_page""about")}}aria-current=page (eq about")}}active{{ current_page href=/community/{{community_id}}/about hx-target=main hx-target-error=main id=about-link if}} if}}" {{ {{#if>About</a></li><li><a class="active-indicator {{#if (eq current_page""settings")}}aria-current=page (eq current_page href=/community/{{community_id}}/settings hx-target=main hx-target-error=main id=settings-link if}} if}}" settings")}}active{{ {{ {{#if>Settings</a></li></ul>

My Rust script:

// main.rs
use std::process;

use template_minifier::{run, Config};

fn main() {
    println!("");
    let args = std::env::args().collect::<Vec<String>>();
    let config = Config::new(&args).unwrap_or_else(|error| {
        eprintln!("{:#?}", error);
        process::exit(1);
    });
    if let Err(error) = run(&config) {
        eprintln!("{:#?}", error);
        process::exit(1);
    }
    println!("");

    process::exit(0);
}

// lib.rs
use colored::Colorize;
use std::io::Write;

pub struct Config {
    pub input_path: String,
}

impl Config {
    pub fn new(args: &[String]) -> Result<Self, &'static str> {
        if args.len() != 2 {
            return Err("Please provide the path to minify.");
        }

        let input_path = args[1].clone();

        Ok(Config { input_path })
    }
}

pub fn run(config: &Config) -> Result<(), Box<dyn std::error::Error>> {
    let input_path = &config.input_path;
    let input_metadata = std::fs::metadata(input_path)?;
    let mut cfg = minify_html::Cfg::new();

    cfg.minify_css = true;
    cfg.minify_js = true;
    cfg.preserve_brace_template_syntax = true;
    cfg.keep_closing_tags = true;

    if input_metadata.is_file() {
        minify_file(input_path, &cfg)?;
    } else if input_metadata.is_dir() {
        minify_dir(input_path, &cfg)?;
    }

    Ok(())
}

/// Minify a file
fn minify_file(file_path: &str, cfg: &minify_html::Cfg) -> Result<(), Box<dyn std::error::Error>> {
    let file = std::fs::read(file_path)?;
    let minified_html = minify_html::minify(&file, &cfg);
    let path = std::path::Path::new(file_path);
    let file_name_of_path = path
        .to_str()
        .expect("Expected file name to exist");
    let root_dir_name = std::path::Path::new(file_path)
        .parent()
        .and_then(|parent| {
            parent.components().nth(0)
        })
        .map(|c| c.as_os_str().to_str().unwrap())
        .unwrap_or_default();
    let file_path_without_root = path.parent().unwrap().strip_prefix(root_dir_name)?;
    let output_dir = std::path::PathBuf::from("templates").join(file_path_without_root);

    if !output_dir.exists() {
        std::fs::create_dir_all(&output_dir)?;
    }

    let outpule_file_path = output_dir.join(path.file_name().unwrap().to_str().unwrap());
    let mut file = std::fs::File::create(outpule_file_path.clone())?;

    file.write_all(&minified_html)?;

    println!(
        "{}{}",
        "Minified: ".bold(),
        file_name_of_path,
    );

    Ok(())
}

/// Minify a directory
fn minify_dir(dir_path: &str, cfg: &minify_html::Cfg) -> Result<(), Box<dyn std::error::Error>> {
    let mut file_paths: Vec<std::path::PathBuf> = Vec::new();

    collect_filenames_recursively(dir_path, &mut file_paths)?;

    for path in &file_paths {
        if let Err(err) = minify_file(path.to_str().unwrap(), cfg) {
            eprintln!("{}: {}", "Error".red(), err);
        }
    }

    Ok(())
}

fn collect_filenames_recursively(path: &str, files: &mut Vec<std::path::PathBuf>) -> Result<(), Box<dyn std::error::Error>> {
    for entry in std::fs::read_dir(path)? {
        let entry = entry?;
        let path = entry.path();
        let path_name = path.to_str().expect("Expected path to have a name");

        if path.is_dir() {
            collect_filenames_recursively(path_name, files)?;
        } else {
            files.push(path.clone());
        }
    }

    Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant