use crate::extend::string::StringExtend;
use crate::less::fileinfo::{FileInfo, FileRef};
use crate::less::node::{NodeRef, StyleNode};
use crate::less::parse::Parse;
use crate::less::select_node::SelectorNode;
use crate::sourcemap::loc::{Loc, LocMap};
use crate::style_core::context::ParseContext;
use crate::style_core::extension::StyleExtension;
use crate::style_core::filenode::StyleFileNode;
use crate::style_core::option::OptionExtend;
use crate::util::hash::StyleHash;
use crate::util::str_enum::StringToEnum;
use serde::Serialize;
use std::collections::{HashMap, HashSet};
use std::path::Path;
#[derive(Clone, Debug, Serialize)]
pub struct FileNode {
pub info: FileRef,
}
impl FileNode {
pub fn getrules(&self) -> Vec<NodeRef> {
let mut list = vec![];
self.info.borrow().block_node.iter().for_each(|x| {
if let StyleNode::Rule(rule) = x {
list.push(rule.clone())
}
});
list
}
pub fn code_gen(&self) -> Result<String, String> {
let mut res = "".to_string();
let mut set = HashSet::new();
let info = self.info.borrow();
let option = info.get_options();
let mut need_add_cache = false;
if !info.import_files.is_empty() {
for item in info.import_files.iter() {
match item {
StyleFileNode::Less(less_node) => {
let has_codegen_record = {
let context = info.context.lock().unwrap();
context.has_codegen_record(&less_node.info.borrow().disk_location)
};
if !has_codegen_record {
let import_res = less_node.code_gen()?;
res += &import_res;
if !option.minify {
res += "\n";
} else {
res += " ";
}
}
}
StyleFileNode::Css(css_node) => {
let has_codegen_record = {
let context = info.context.lock().unwrap();
context.has_codegen_record(&css_node.info.borrow().disk_location)
};
if !has_codegen_record {
let import_res = css_node.code_gen()?;
res += &import_res;
if !option.minify {
res += "\n";
} else {
res += " ";
}
}
}
}
}
}
let mut self_code_gen_res = "".to_string();
let source = {
info
.context
.lock()
.unwrap()
.get_render_cache(info.disk_location.as_str())
};
if let Some(code) = source {
self_code_gen_res = code;
} else {
for item in self.getrules() {
item.borrow().code_gen(&mut self_code_gen_res, &mut set)?;
}
need_add_cache = true;
}
res += self_code_gen_res.as_str();
let mut context = info.context.lock().unwrap();
context.add_codegen_record(info.disk_location.as_str());
if need_add_cache {
context.add_render_cache(info.disk_location.as_str(), self_code_gen_res.as_str());
}
drop(context);
drop(info);
if !set.is_empty() {
self.info.borrow_mut().class_selector_collect = set.clone();
let info = self.info.borrow();
let key = info.disk_location.clone();
let context = info.context.lock().unwrap();
let mut parse_cache_map = context.filecache.lock().unwrap();
parse_cache_map.remove(&key);
let file_info_json = serde_json::to_string_pretty(self).unwrap();
parse_cache_map.insert(key, file_info_json);
}
Ok(res)
}
pub fn code_gen_into_map(&self, map: &mut HashMap<String, String>) -> Result<(), String> {
let info = self.info.borrow();
let mut set = HashSet::new();
let mut need_add_cache = false;
if !info.import_files.is_empty() {
for item in info.import_files.iter() {
match item {
StyleFileNode::Less(less_node) => {
let has_codegen_record = {
let context = info.context.lock().unwrap();
context.has_codegen_record(&less_node.info.borrow().disk_location)
};
if !has_codegen_record {
less_node.code_gen_into_map(map)?;
}
}
StyleFileNode::Css(css_node) => {
let has_codegen_record = {
let context = info.context.lock().unwrap();
context.has_codegen_record(&css_node.info.borrow().disk_location)
};
if !has_codegen_record {
css_node.code_gen_into_map(map)?;
}
}
}
}
}
let mut res = "".to_string();
let source = {
info
.context
.lock()
.unwrap()
.get_render_cache(info.disk_location.as_str())
};
if let Some(code) = source {
res = code;
} else {
for item in self.getrules() {
item.borrow().code_gen(&mut res, &mut set)?;
}
need_add_cache = true;
}
let mut context = info.context.lock().unwrap();
if need_add_cache {
context.add_render_cache(info.disk_location.as_str(), res.as_str());
}
map.insert(self.info.borrow().disk_location.clone(), res);
context.add_codegen_record(info.disk_location.as_str());
drop(context);
drop(info);
if !set.is_empty() {
self.info.borrow_mut().class_selector_collect = set.clone();
let info = self.info.borrow();
let key = info.disk_location.clone();
let context = info.context.lock().unwrap();
let mut parse_cache_map = context.filecache.lock().unwrap();
parse_cache_map.remove(&key);
let file_info_json = serde_json::to_string_pretty(self).unwrap();
parse_cache_map.insert(key, file_info_json);
}
Ok(())
}
pub fn output_js_with_cssmodule(
class_selector_collect: &Vec<String>,
perfix_hash: &str,
) -> String {
let mut res = "".to_string();
for item in class_selector_collect {
let key = if item.contains('-') {
format!(r#"["{}"]"#, item)
} else {
item.to_string()
};
let value = format!("{}_{}", item, perfix_hash);
res += format!(r#"{}: "{}","#, key, value).as_str();
res += " \n";
}
res
}
pub fn get_hash_perfix(content: &str, filepath: &str) -> String {
StyleHash::generate_css_module_hash(filepath, content)
}
pub fn parse_select_all_node(&self) -> Result<(), String> {
for node in self.info.borrow().block_node.iter() {
if let StyleNode::Rule(heapnode) = node {
let mut mut_node = heapnode.borrow_mut();
if let Some(SelectorNode::Select(s_node)) = mut_node.selector.as_mut() {
s_node.parse(None)?;
}
drop(mut_node);
heapnode.borrow().parse_select_all_node()?;
}
}
Ok(())
}
pub fn is_need_css_modules(filepath: &str, modules: Option<bool>) -> bool {
if let Some(module) = modules {
module
} else {
let path = Path::new(filepath);
let filename = path.file_name().unwrap().to_str().unwrap().to_string();
let ext = format!(".module.{}", path.extension().unwrap().to_str().unwrap());
filename.to_lowercase().contains(&ext.to_lowercase())
}
}
pub fn create_disklocation_parse(
filepath: String,
context: ParseContext,
) -> Result<FileNode, String> {
let cp_context = context.clone();
let option = {
let context_value = cp_context.lock().unwrap();
context_value.get_options()
};
let need_modules = {
let modules = context.lock().unwrap().option.modules;
Self::is_need_css_modules(filepath.as_str(), modules)
};
let (abs_path, mut content) = FileInfo::resolve(filepath, &option.include_path)?;
let content_transform = {
context
.lock()
.unwrap()
.option
.hooks
.content_interceptor
.as_ref()
.cloned()
};
if let Some(content_transform_fn) = content_transform {
content = content_transform_fn(abs_path.as_str(), content.as_str())?;
}
let node = {
let context_value = cp_context.lock().unwrap();
context_value.get_parse_cache(&abs_path)?
};
if let Some(StyleFileNode::Less(node)) = node {
return Ok(node);
}
let text_content = content.clone();
let charlist = content.to_char_vec();
let mut locmap: Option<LocMap> = None;
if option.sourcemap {
locmap = Some(FileInfo::get_loc_by_content(&charlist));
}
let ext_str_value = Path::new(abs_path.as_str())
.extension()
.unwrap()
.to_str()
.unwrap();
let ext = ext_str_value
.to_string()
.to_enum::<StyleExtension>()
.unwrap();
let obj = FileInfo {
disk_location: abs_path.clone(),
block_node: vec![],
origin_txt_content: text_content,
origin_charlist: charlist,
locmap,
context,
self_weak: None,
import_files: vec![],
modules: need_modules,
class_selector_collect: Default::default(),
hash_perfix: StyleHash::generate_css_module_hash(&abs_path, &content),
resolve_extension: ext,
};
let info = obj.toheap();
let mut obj = Self { info: info.clone() };
obj.parse_heap()?;
obj.parse_select_all_node()?;
let disk_location = info.borrow().disk_location.clone();
let file_info_json = serde_json::to_string_pretty(&obj).unwrap();
let mut context_value = cp_context.lock().unwrap();
context_value.set_parse_cache(disk_location.as_str(), file_info_json);
Ok(obj)
}
pub fn create_disklocation(
filepath: String,
context: ParseContext,
) -> Result<(String, String), String> {
let obj = Self::create_disklocation_parse(filepath, context.clone())?;
let mut res = obj.code_gen()?;
let mut sync_context = context.lock().unwrap();
sync_context.clear_codegen_record();
let mut js_content = "".to_string();
if obj.info.borrow().modules {
drop(sync_context);
let perfix_hash = obj.info.borrow().hash_perfix.clone();
res = res.replace("@@@hash_str_replace_value@@@", &perfix_hash);
let js_modules_collect = obj.collect_class_modules_set();
let mut js_modules_collect = js_modules_collect.into_iter().collect::<Vec<String>>();
js_modules_collect.sort();
js_content = format!(
r#"
const style = {}
{}
{};
export default style;
"#,
"{",
Self::output_js_with_cssmodule(&js_modules_collect, &perfix_hash),
"}"
);
}
Ok((res, js_content))
}
pub fn create_disklocation_into_hashmap(
filepath: String,
context: ParseContext,
) -> Result<(HashMap<String, String>, String), String> {
let obj = Self::create_disklocation_parse(filepath, context.clone())?;
let mut map = HashMap::new();
obj.code_gen_into_map(&mut map)?;
let mut sync_context = context.lock().unwrap();
sync_context.clear_codegen_record();
let mut js_content = "".to_string();
if obj.info.borrow().modules {
drop(sync_context);
let perfix_hash = obj.info.borrow().hash_perfix.clone();
for res in map.values_mut() {
*res = res
.replace("@@@hash_str_replace_value@@@", &perfix_hash)
.to_string()
}
let js_modules_collect = obj.collect_class_modules_set();
let mut js_modules_collect = js_modules_collect.into_iter().collect::<Vec<String>>();
js_modules_collect.sort();
js_content = format!(
r#"
const style = {}
{}
{};
export default style;
"#,
"{",
Self::output_js_with_cssmodule(&js_modules_collect, &perfix_hash),
"}"
);
}
Ok((map, js_content))
}
pub fn create_txt_content_parse(
mut content: String,
context: ParseContext,
filename: String,
) -> Result<Self, String> {
let node = {
let context_value = context.lock().unwrap();
context_value.get_parse_cache(&filename)?
};
let need_modules = {
let modules = context.lock().unwrap().option.modules;
Self::is_need_css_modules(filename.as_str(), modules)
};
if let Some(StyleFileNode::Less(node_wrap_value)) = node {
return Ok(node_wrap_value);
}
let content_transform = {
context
.lock()
.unwrap()
.option
.hooks
.content_interceptor
.as_ref()
.cloned()
};
if let Some(content_transform_fn) = content_transform {
content = content_transform_fn(filename.as_str(), content.as_str())?;
}
let text_content: String = content.clone();
let charlist = text_content.to_char_vec();
let cp_context = context.clone();
let option = {
let sync_context = cp_context.lock().unwrap();
sync_context.get_options()
};
let mut locmap: Option<LocMap> = None;
if option.sourcemap {
locmap = Some(FileInfo::get_loc_by_content(&charlist));
}
let ext_str_value = Path::new(filename.as_str())
.extension()
.unwrap()
.to_str()
.unwrap();
let ext = ext_str_value
.to_string()
.to_enum::<StyleExtension>()
.unwrap();
let obj = FileInfo {
disk_location: filename.clone(),
block_node: vec![],
origin_txt_content: text_content,
origin_charlist: charlist,
locmap,
context,
self_weak: None,
import_files: vec![],
modules: need_modules,
class_selector_collect: Default::default(),
hash_perfix: StyleHash::generate_css_module_hash(&filename, &content),
resolve_extension: ext,
};
let info = obj.toheap();
let mut obj = Self { info: info.clone() };
obj.parse_heap()?;
obj.parse_select_all_node()?;
let disk_location = info.borrow().disk_location.clone();
let file_info_json = serde_json::to_string_pretty(&obj).unwrap();
{
let mut sync_context = cp_context.lock().unwrap();
sync_context.set_parse_cache(disk_location.as_str(), file_info_json);
}
Ok(obj)
}
pub fn create_txt_content(
content: String,
filename: String,
context: ParseContext,
) -> Result<(String, String), String> {
let obj = Self::create_txt_content_parse(content, context.clone(), filename)?;
let mut sync_context = context.lock().unwrap();
let mut res = obj.code_gen()?;
sync_context.clear_codegen_record();
let mut js_content = "".to_string();
if obj.info.borrow().modules {
drop(sync_context);
let perfix_hash = obj.info.borrow().hash_perfix.clone();
res = res.replace("@@@hash_str_replace_value@@@", &perfix_hash);
let js_modules_collect = obj.collect_class_modules_set();
let mut js_modules_collect = js_modules_collect.into_iter().collect::<Vec<String>>();
js_modules_collect.sort();
js_content = format!(
r#"
const style = {}
{}
{};
export default style;
"#,
"{",
Self::output_js_with_cssmodule(&js_modules_collect, &perfix_hash),
"}"
);
}
Ok((res, js_content))
}
pub fn create_content_into_hashmap(
content: String,
filepath: String,
context: ParseContext,
) -> Result<(HashMap<String, String>, String), String> {
let obj = Self::create_txt_content_parse(content, context.clone(), filepath)?;
let mut map = HashMap::new();
obj.code_gen_into_map(&mut map)?;
let mut sync_context = context.lock().unwrap();
sync_context.clear_codegen_record();
let mut js_content = "".to_string();
if obj.info.borrow().modules {
drop(sync_context);
let perfix_hash = obj.info.borrow().hash_perfix.clone();
for res in map.values_mut() {
*res = res
.replace("@@@hash_str_replace_value@@@", &perfix_hash)
.to_string()
}
let js_modules_collect = obj.collect_class_modules_set();
let mut js_modules_collect = js_modules_collect.into_iter().collect::<Vec<String>>();
js_modules_collect.sort();
js_content = format!(
r#"
const style = {}
{}
{};
export default style;
"#,
"{",
Self::output_js_with_cssmodule(&js_modules_collect, &perfix_hash),
"}"
);
}
Ok((map, js_content))
}
pub fn collect_loc_list(&self) -> Vec<Option<Loc>> {
let mut list = vec![];
for node in &self.info.borrow().block_node {
StyleNode::collect_loc(node.clone(), &mut list);
}
list
}
pub fn collect_class_modules_set(&self) -> HashSet<String> {
let mut self_set = self.info.borrow().class_selector_collect.clone();
for file in &self.info.borrow().import_files {
crate::style_core::filenode::StyleFileNode::collect_class_modules_set(file, &mut self_set);
}
self_set
}
}