not-tailwind 0.2.0

A tool for shortening css classes
Documentation
use std::{fs::read_to_string, path::Path};

use crate::{
    args::NtwArgs, visit_class::check_html, visit_map::check_js,
    visit_selectors::ClassVisitor,
};
use ignore::Walk;
use lightningcss::{
    stylesheet::{ParserOptions, PrinterOptions, StyleSheet},
    visitor::Visit,
};

pub fn start_all2(arg: NtwArgs) {
    let mut css_walker = CSSWalker::new(arg.ignored);
    let output = arg.output.unwrap_or(String::from("not-tailwind"));
    css_walker.walk(&output);
    templates_walk(&output, arg.run, &css_walker.class_visitor);
    js_walk(&output, &css_walker.class_visitor);
}

fn js_walk(output: &str, class_visitor: &ClassVisitor) {
    for result in Walk::new("./") {
        match result {
            Ok(entry) => {
                let path = entry.path();
                if !path.ends_with("not-tailwind.ts") {
                    continue;
                }

                let content = read_to_string(&entry.path()).unwrap_or_default();

                if let Some(new_content) = check_js(&content, class_visitor) {
                    if let Ok(js) = String::from_utf8(new_content) {
                        write_to_file(path, js, output);
                    }
                }
                break;
            }
            Err(e) => {
                dbg!(e);
            }
        }
    }
}

fn templates_walk(
    output: &str,
    file_types: Vec<String>,
    visitor: &ClassVisitor,
) {
    for result in Walk::new("./") {
        match result {
            Ok(entry) => {
                let ext = entry.path().extension().is_some_and(|v| {
                    file_types
                        .contains(&v.to_str().unwrap_or_default().to_string())
                });
                if ext == false {
                    continue;
                }
                let content = read_to_string(&entry.path()).unwrap_or_default();
                let new_content =
                    check_html(&content, visitor).unwrap_or_default();
                write_to_file(entry.path(), new_content, output);
            }
            Err(_) => {}
        }
    }
}

fn handle_path(path: &Path) -> &Path {
    if path.starts_with("./") {
        if let Some(path2) = path.to_str() {
            let mut path2 = path2.chars();
            path2.next();
            path2.next();
            return Path::new(path2.as_str());
        }
    }
    path
}

struct CSSWalker {
    pub class_visitor: ClassVisitor,
    pub ignored: Vec<String>,
}

impl CSSWalker {
    pub fn new(ignored: Option<Vec<String>>) -> Self {
        Self {
            class_visitor: ClassVisitor::default(),
            ignored: ignored.unwrap_or(vec![]),
        }
    }

    pub fn is_ignored(&self, path: &Path) -> bool {
        if let Some(path) = path.to_str() {
            if !path.ends_with(".css") {
                return true;
            }
            return self.ignored.contains(&path.to_string());
        }
        false
    }

    pub fn walk(&mut self, output: &String) {
        for result in Walk::new("./") {
            match result {
                Ok(entry) => {
                    if self.is_ignored(entry.path()) {
                        continue;
                    }
                    let content =
                        read_to_string(&entry.path()).unwrap_or_default();
                    let a =
                        StyleSheet::parse(&content, ParserOptions::default());

                    if let Ok(mut stylesheet) = a {
                        let _ = stylesheet.visit(&mut self.class_visitor);
                        let opt = PrinterOptions {
                            minify: true,
                            ..Default::default()
                        };

                        if let Ok(f) = stylesheet.to_css(opt) {
                            write_to_file(&entry.path(), f.code, output);
                        }
                    }
                }
                Err(_) => {}
            }
        }
    }
}

fn write_to_file(path: &Path, code: String, output: &str) {
    let output_path = Path::new(output);
    let new_path = output_path.join(handle_path(path));
    if let Some(parent) = new_path.parent() {
        let _ = std::fs::create_dir_all(parent);
        let _ = std::fs::write(new_path, code);
    }
}