Skip to content

Commit

Permalink
Fix DOM element filter and pretty-format
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielleHuisman committed Sep 9, 2024
1 parent 7d64ae9 commit 64fabe6
Show file tree
Hide file tree
Showing 9 changed files with 351 additions and 137 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion packages/dom/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ regex.workspace = true
thiserror.workspace = true
wasm-bindgen.workspace = true
web-sys = { workspace = true, features = [
"Attr",
"Comment",
"Document",
"DocumentFragment",
Expand All @@ -27,11 +28,13 @@ web-sys = { workspace = true, features = [
"HtmlOptionElement",
"HtmlOptionsCollection",
"HtmlSelectElement",
"NamedNodeMap",
"NodeList",
"Text",
"Window",
] }

[dev-dependencies]
wasm-bindgen-test.workspace = true
indoc = "2.0.5"
mockall = "0.13.0"
wasm-bindgen-test.workspace = true
118 changes: 88 additions & 30 deletions packages/dom/src/dom_element_filter.rs
Original file line number Diff line number Diff line change
@@ -1,50 +1,99 @@
use std::collections::HashMap;

use pretty_format::{Config, Plugin, Printer, Refs};
use regex::Regex;
use wasm_bindgen::{JsCast, JsValue};
use web_sys::{Comment, Element, Node, Text};

use crate::util::{named_node_map_to_hashmap, node_list_to_vec};

fn escape_html(text: String) -> String {
text.replace('<', "&lt;").replace('>', "&gt;")
}

fn print_props(
_config: &Config,
_indentation: String,
_depth: usize,
_refs: &Refs,
_printer: &Printer,
attributes: HashMap<String, String>,
config: &Config,
indentation: String,
depth: usize,
refs: Refs,
printer: &Printer,
) -> String {
todo!()
let indentation_next = format!("{}{}", indentation, config.indent);

attributes
.into_iter()
.map(|(key, value)| {
let printed = printer(
&JsValue::from_str(&value),
config,
indentation_next.clone(),
depth,
refs.clone(),
None,
);

format!(
"{}{}{}={}",
config.spacing_inner,
indentation,
config.colors.prop.paint(&key),
config.colors.value.paint(&printed)
)
})
.collect::<Vec<_>>()
.join("")
}

fn print_children(
_config: &Config,
_indentation: String,
_depth: usize,
_refs: &Refs,
_printer: &Printer,
children: Vec<Node>,
config: &Config,
indentation: String,
depth: usize,
refs: Refs,
printer: &Printer,
) -> String {
todo!()
children
.into_iter()
.map(|child| {
let printed_child = printer(
child.unchecked_ref::<JsValue>(),
config,
indentation.clone(),
depth,
refs.clone(),
None,
);

if printed_child.is_empty() && child.node_type() != Node::TEXT_NODE {
// A plugin serialized this Node to '' meaning we should ignore it.
"".into()
} else {
format!("{}{}{}", config.spacing_outer, indentation, printed_child)
}
})
.collect::<Vec<_>>()
.join("")
}

fn print_text(text: String, config: &Config) -> String {
let content_color = config.colors.content;
let content_color = &config.colors.content;
content_color.paint(&escape_html(text))
}

fn print_comment(text: String, config: &Config) -> String {
let comment_color = config.colors.comment;
let comment_color = &config.colors.comment;
comment_color.paint(&format!("<!--{}-->", escape_html(text)))
}

fn print_element(
r#type: String,
printed_props: String,
printed_children: String,
config: Config,
config: &Config,
indentation: String,
) -> String {
let tag_color = config.colors.tag;
let tag_color = &config.colors.tag;

tag_color.paint(&format!(
"<{}{}{}>",
Expand Down Expand Up @@ -82,7 +131,7 @@ fn print_element(
}

fn print_element_as_leaf(r#type: String, config: &Config) -> String {
let tag_color = config.colors.tag;
let tag_color = &config.colors.tag;
format!(
"{} …{}",
tag_color.paint(&format!("<{}", r#type)),
Expand Down Expand Up @@ -123,11 +172,13 @@ fn node_is_fragment(node: &Node) -> bool {
node.node_type() == Node::DOCUMENT_FRAGMENT_NODE
}

pub struct DomElementFilter {}
pub struct DomElementFilter {
filter_node: Box<dyn Fn(&Node) -> bool>,
}

impl DomElementFilter {
pub fn new() -> Self {
Self {}
pub fn new(filter_node: Box<dyn Fn(&Node) -> bool>) -> Self {
Self { filter_node }
}
}

Expand All @@ -139,7 +190,7 @@ impl Plugin for DomElementFilter {
fn serialize(
&self,
val: &JsValue,
config: Config,
config: &Config,
indentation: String,
depth: usize,
refs: Refs,
Expand All @@ -148,11 +199,11 @@ impl Plugin for DomElementFilter {
let node: &Node = val.unchecked_ref();

if node_is_text(node) {
return print_text(node.unchecked_ref::<Text>().data(), &config);
return print_text(node.unchecked_ref::<Text>().data(), config);
}

if node_is_comment(node) {
return print_comment(node.unchecked_ref::<Comment>().data(), &config);
return print_comment(node.unchecked_ref::<Comment>().data(), config);
}

let r#type = if node_is_fragment(node) {
Expand All @@ -163,25 +214,32 @@ impl Plugin for DomElementFilter {

let depth = depth + 1;
if depth > config.max_depth {
return print_element_as_leaf(r#type, &config);
return print_element_as_leaf(r#type, config);
}

print_element(
r#type,
print_props(
// TODO: props,
&config,
if node_is_fragment(node) {
HashMap::new()
} else {
named_node_map_to_hashmap(node.unchecked_ref::<Element>().attributes())
},
config,
format!("{}{}", indentation, &config.indent),
depth,
&refs,
refs.clone(),
printer,
),
print_children(
// TODO: children,
&config,
node_list_to_vec(node.child_nodes())
.into_iter()
.filter(&self.filter_node)
.collect(),
config,
format!("{}{}", indentation, &config.indent),
depth,
&refs,
refs.clone(),
printer,
),
config,
Expand Down
22 changes: 18 additions & 4 deletions packages/dom/src/pretty_dom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ use std::rc::Rc;

use pretty_format::PrettyFormatOptions;
use wasm_bindgen::{JsCast, JsValue};
use web_sys::{Document, Element};
use web_sys::{Document, Element, Node};

use crate::{dom_element_filter::DomElementFilter, helpers::get_document};
use crate::{dom_element_filter::DomElementFilter, get_config, helpers::get_document};

pub enum DocumentOrElement {
Document(Document),
Expand All @@ -25,7 +25,18 @@ impl From<Element> for DocumentOrElement {

fn should_highlight() -> bool {
// TODO
true

// Don't colorize in non-node environments (e.g. browsers).
false
}

fn filter_comments_and_default_ignore_tags_tags(node: &Node) -> bool {
node.node_type() != Node::COMMENT_NODE
&& (node.node_type() != Node::ELEMENT_NODE
|| !node
.unchecked_ref::<Element>()
.matches(&get_config().default_ignore)
.unwrap_or(false))
}

pub fn pretty_dom(dom: Option<DocumentOrElement>, max_length: Option<usize>) -> String {
Expand All @@ -44,11 +55,14 @@ pub fn pretty_dom(dom: Option<DocumentOrElement>, max_length: Option<usize>) ->
DocumentOrElement::Element(element) => element.unchecked_into(),
};

// TODO: accept as option
let filter_node = filter_comments_and_default_ignore_tags_tags;

let debug_content = pretty_format::format(
&dom,
// TODO: pass options
PrettyFormatOptions::default()
.plugins(vec![Rc::new(DomElementFilter::new())])
.plugins(vec![Rc::new(DomElementFilter::new(Box::new(filter_node)))])
.print_function_name(false)
.highlight(should_highlight()),
)
Expand Down
38 changes: 27 additions & 11 deletions packages/dom/src/util.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,50 @@
use std::collections::HashMap;

use wasm_bindgen::JsCast;
use web_sys::{HtmlCollection, NodeList};
use web_sys::{HtmlCollection, NamedNodeMap, NodeList};

pub fn node_list_to_vec<T: JsCast>(node_list: NodeList) -> Vec<T> {
pub fn html_collection_to_vec<T: JsCast>(collection: HtmlCollection) -> Vec<T> {
let mut result = Vec::with_capacity(
node_list
collection
.length()
.try_into()
.expect("usize should be at least u32."),
);
for i in 0..node_list.length() {
for i in 0..collection.length() {
result.push(
node_list
.get(i)
.expect("Node should exist.")
collection
.item(i)
.expect("Item should exist.")
.unchecked_into::<T>(),
);
}
result
}

pub fn html_collection_to_vec<T: JsCast>(collection: HtmlCollection) -> Vec<T> {
pub fn named_node_map_to_hashmap(named_node_map: NamedNodeMap) -> HashMap<String, String> {
let mut result = HashMap::with_capacity(
named_node_map
.length()
.try_into()
.expect("usize should be at least u32."),
);
for i in 0..named_node_map.length() {
let attr = named_node_map.item(i).expect("Item should exist.");
result.insert(attr.name(), attr.value());
}
result
}

pub fn node_list_to_vec<T: JsCast>(node_list: NodeList) -> Vec<T> {
let mut result = Vec::with_capacity(
collection
node_list
.length()
.try_into()
.expect("usize should be at least u32."),
);
for i in 0..collection.length() {
for i in 0..node_list.length() {
result.push(
collection
node_list
.item(i)
.expect("Item should exist.")
.unchecked_into::<T>(),
Expand Down
Loading

0 comments on commit 64fabe6

Please sign in to comment.