From 27af2368ff0e839a5fe79c5171cfa6a5def55cbe Mon Sep 17 00:00:00 2001 From: Wilson Lin Date: Sat, 18 Jan 2020 11:42:01 +1100 Subject: [PATCH] Remove attrs with default values; create minified comparison script; remove `=` from boolean attrs; fix closing tag writing before collapsed whitespace; rebuild hyperbuild only in bench build script instead of all dependencies; conservatively collapse whitespace for html-minifier to match hyperbuild behaviour; update bench results --- bench/build.sh | 3 +- bench/compare.sh | 3 + bench/minification.json | 100 +- bench/minification.png | Bin 30490 -> 28841 bytes bench/minifiers.js | 4 + bench/run.js | 2 +- bench/speed.json | 78 +- bench/speed.png | Bin 32283 -> 32280 bytes build.rs | 76 +- gen/attrs.json | 1830 +++++++++++++++++++++++++++++ gen/boolean_attrs.json | 113 -- gen/build/attrs.js | 205 ++++ gen/build/dom.js | 93 -- gen/redundant_if_empty_attrs.json | 467 -------- src/pattern.rs | 22 - src/spec/tag/omission.rs | 2 +- src/unit/attr/mod.rs | 41 +- src/unit/content.rs | 4 +- src/unit/tag.rs | 19 +- 19 files changed, 2238 insertions(+), 824 deletions(-) create mode 100755 bench/compare.sh create mode 100644 gen/attrs.json delete mode 100644 gen/boolean_attrs.json create mode 100644 gen/build/attrs.js delete mode 100644 gen/build/dom.js delete mode 100644 gen/redundant_if_empty_attrs.json diff --git a/bench/build.sh b/bench/build.sh index e2fdf11..ad8df7b 100755 --- a/bench/build.sh +++ b/bench/build.sh @@ -6,14 +6,13 @@ pushd "$(dirname "$0")" nodejs_cargo_toml="../nodejs/native/Cargo.toml" -rm -rf node_modules if [ -f "$nodejs_cargo_toml.orig" ]; then echo 'Not altering Node.js Cargo.toml file' else cp "$nodejs_cargo_toml" "$nodejs_cargo_toml.orig" fi sed -i 's%^hyperbuild = .*$%hyperbuild = { path = "../.." }%' "$nodejs_cargo_toml" -HYPERBUILD_NODEJS_SKIP_BIN_DOWNLOAD=1 npm i +HYPERBUILD_NODEJS_SKIP_BIN_DOWNLOAD=1 npm rebuild hyperbuild mv "$nodejs_cargo_toml.orig" "$nodejs_cargo_toml" pushd hyperbuild-bench cargo build --release diff --git a/bench/compare.sh b/bench/compare.sh new file mode 100755 index 0000000..240a2a5 --- /dev/null +++ b/bench/compare.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +git --no-pager diff --no-index --word-diff=color --word-diff-regex=. "min/html-minifier/$1.html" "min/hyperbuild-nodejs/$1.html" | less diff --git a/bench/minification.json b/bench/minification.json index b17b744..15ac9d7 100644 --- a/bench/minification.json +++ b/bench/minification.json @@ -5,12 +5,12 @@ "relative": 1 }, "hyperbuild-nodejs": { - "absolute": 353526, - "relative": 0.9585430133182942 + "absolute": 353501, + "relative": 0.9584752288403974 }, "html-minifier": { - "absolute": 353730, - "relative": 0.9590961346579324 + "absolute": 355185, + "relative": 0.9630411912715283 }, "minimize": { "absolute": 359912, @@ -23,12 +23,12 @@ "relative": 1 }, "hyperbuild-nodejs": { - "absolute": 231730, - "relative": 0.941161658212065 + "absolute": 231706, + "relative": 0.9410641832204925 }, "html-minifier": { - "absolute": 234186, - "relative": 0.9511365990163149 + "absolute": 234306, + "relative": 0.9516239739741772 }, "minimize": { "absolute": 238547, @@ -41,8 +41,8 @@ "relative": 1 }, "hyperbuild-nodejs": { - "absolute": 90485, - "relative": 0.983222679807452 + "absolute": 90484, + "relative": 0.9832118136674309 }, "html-minifier": { "absolute": 90599, @@ -59,12 +59,12 @@ "relative": 1 }, "hyperbuild-nodejs": { - "absolute": 271220, - "relative": 0.8756319775813419 + "absolute": 270833, + "relative": 0.8743825506389188 }, "html-minifier": { - "absolute": 270355, - "relative": 0.8728393307978899 + "absolute": 277911, + "relative": 0.8972338268623564 }, "minimize": { "absolute": 278990, @@ -77,12 +77,12 @@ "relative": 1 }, "hyperbuild-nodejs": { - "absolute": 79834, - "relative": 0.9433520820532212 + "absolute": 79806, + "relative": 0.9430212222904949 }, "html-minifier": { - "absolute": 79273, - "relative": 0.9367230703785981 + "absolute": 81446, + "relative": 0.9624001512501772 }, "minimize": { "absolute": 81844, @@ -95,12 +95,12 @@ "relative": 1 }, "hyperbuild-nodejs": { - "absolute": 5744300, - "relative": 0.9094278392290278 + "absolute": 5744290, + "relative": 0.9094262560459782 }, "html-minifier": { - "absolute": 5663106, - "relative": 0.896573342775437 + "absolute": 5785725, + "relative": 0.9159861750123369 }, "minimize": { "absolute": 5799350, @@ -113,12 +113,12 @@ "relative": 1 }, "hyperbuild-nodejs": { - "absolute": 196589, - "relative": 0.9964468548836738 + "absolute": 196577, + "relative": 0.9963860307162046 }, "html-minifier": { - "absolute": 196568, - "relative": 0.9963404125906027 + "absolute": 196600, + "relative": 0.9965026103705206 }, "minimize": { "absolute": 196776, @@ -131,12 +131,12 @@ "relative": 1 }, "hyperbuild-nodejs": { - "absolute": 28189, - "relative": 0.8285764674759707 + "absolute": 28154, + "relative": 0.8275476911319479 }, "html-minifier": { - "absolute": 28109, - "relative": 0.8262249786896328 + "absolute": 29086, + "relative": 0.8549425354927839 }, "minimize": { "absolute": 30318, @@ -149,12 +149,12 @@ "relative": 1 }, "hyperbuild-nodejs": { - "absolute": 1263698, - "relative": 0.9967377432692293 + "absolute": 1263682, + "relative": 0.9967251233205608 }, "html-minifier": { - "absolute": 1263082, - "relative": 0.9962518752454974 + "absolute": 1263150, + "relative": 0.9963055100273379 }, "minimize": { "absolute": 1264354, @@ -167,12 +167,12 @@ "relative": 1 }, "hyperbuild-nodejs": { - "absolute": 648036, - "relative": 0.9945654926432332 + "absolute": 648004, + "relative": 0.9945163810263407 }, "html-minifier": { - "absolute": 647776, - "relative": 0.9941664607559813 + "absolute": 647822, + "relative": 0.9942370587052644 }, "minimize": { "absolute": 648422, @@ -185,12 +185,12 @@ "relative": 1 }, "hyperbuild-nodejs": { - "absolute": 86770, - "relative": 0.7731513245239644 + "absolute": 86693, + "relative": 0.7724652273476552 }, "html-minifier": { - "absolute": 86383, - "relative": 0.7697030179365405 + "absolute": 88366, + "relative": 0.7873722478147359 }, "minimize": { "absolute": 89682, @@ -203,12 +203,12 @@ "relative": 1 }, "hyperbuild-nodejs": { - "absolute": 272544, - "relative": 0.8647962583371939 + "absolute": 272480, + "relative": 0.8645931830152878 }, "html-minifier": { - "absolute": 265242, - "relative": 0.841626633328468 + "absolute": 266639, + "relative": 0.8460593868394499 }, "minimize": { "absolute": 307860, @@ -221,12 +221,12 @@ "relative": 1 }, "hyperbuild-nodejs": { - "absolute": 1319433, - "relative": 0.9350021790660842 + "absolute": 1319432, + "relative": 0.9350014704267072 }, "html-minifier": { - "absolute": 1308864, - "relative": 0.927512569490949 + "absolute": 1327244, + "relative": 0.940537361239552 }, "minimize": { "absolute": 1369798, diff --git a/bench/minification.png b/bench/minification.png index d598b131514e0573f7a6880c8dc42e3ddb41f813..2b84b37931cd3ef87a90f33adf48da15e746ee06 100644 GIT binary patch literal 28841 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

o_anrdq>uYgKj$Z9bbSaK@%C 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 Hc>8|=50S4= literal 30490 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 F`fmZ3$bJ9- diff --git a/bench/minifiers.js b/bench/minifiers.js index b3a3d04..9bd1904 100644 --- a/bench/minifiers.js +++ b/bench/minifiers.js @@ -8,6 +8,10 @@ module.exports = { collapseBooleanAttributes: true, collapseInlineTagWhitespace: true, collapseWhitespace: true, + // hyperbuild can do context-aware whitespace removal, which is safe when configured correctly to match how whitespace is used in the document. + // html-minifier cannot, so whitespace must be collapsed conservatively. + // Alternatively, hyperbuild can also be made to remove whitespace regardless of context. + conservativeCollapse: true, customEventAttributes: [], decodeEntities: true, ignoreCustomComments: [], diff --git a/bench/run.js b/bench/run.js index b7704de..a97294f 100644 --- a/bench/run.js +++ b/bench/run.js @@ -7,7 +7,7 @@ const tests = require('./tests'); for (const t of tests) { for (const p of Object.keys(programs)) { try { - const minPath = path.join(__dirname, 'min', p, t.name); + const minPath = path.join(__dirname, 'min', p, `${t.name}.html`); mkdirp.sync(path.dirname(minPath)); fs.writeFileSync(minPath, programs[p](t.contentAsString, t.contentAsBuffer)); } catch (err) { diff --git a/bench/speed.json b/bench/speed.json index d8e81b6..2c25fcf 100644 --- a/bench/speed.json +++ b/bench/speed.json @@ -1,67 +1,67 @@ { "Amazon": { - "hyperbuild-nodejs": 508.6052506017873, - "html-minifier": 42.25586310792637, - "minimize": 112.84668317196737 + "hyperbuild-nodejs": 499.9428905642954, + "html-minifier": 36.3036627447208, + "minimize": 113.33527430279831 }, "BBC": { - "hyperbuild-nodejs": 532.8102905973569, - "html-minifier": 54.543166381705994, - "minimize": 158.04711027873367 + "hyperbuild-nodejs": 534.5358584753244, + "html-minifier": 49.23362197831472, + "minimize": 161.96803168572117 }, "Bing": { - "hyperbuild-nodejs": 2163.2302078550147, - "html-minifier": 226.06394079630297, - "minimize": 545.0653574947338 + "hyperbuild-nodejs": 2137.27013270331, + "html-minifier": 223.08512226985184, + "minimize": 550.9181761277221 }, "Bootstrap": { - "hyperbuild-nodejs": 276.9069916779089, - "html-minifier": 9.088104379498866, - "minimize": 23.551904759608103 + "hyperbuild-nodejs": 277.1391080206004, + "html-minifier": 8.043255283692064, + "minimize": 22.245439492019898 }, "Coding Horror": { - "hyperbuild-nodejs": 1130.8127333329164, - "html-minifier": 57.409616058969526, - "minimize": 187.28893041569714 + "hyperbuild-nodejs": 1096.4910673601032, + "html-minifier": 49.83595257976626, + "minimize": 188.32749988717788 }, "ECMA-262": { - "hyperbuild-nodejs": 16.78425400473028, - "html-minifier": 0.5081461293026476, - "minimize": 1.3377251957362182 + "hyperbuild-nodejs": 16.200240897950334, + "html-minifier": 0.45522858062374655, + "minimize": 1.3356053866389666 }, "Google": { - "hyperbuild-nodejs": 1856.1401576820815, - "html-minifier": 326.33685434942925, - "minimize": 555.6574376056606 + "hyperbuild-nodejs": 1832.4626475818236, + "html-minifier": 242.1462398878334, + "minimize": 564.1884364813526 }, "Hacker News": { - "hyperbuild-nodejs": 2243.718482073889, - "html-minifier": 86.20536879655822, - "minimize": 272.83901988612814 + "hyperbuild-nodejs": 2127.8084431041266, + "html-minifier": 74.78979361035866, + "minimize": 272.3995630011103 }, "NY Times": { - "hyperbuild-nodejs": 281.05098122387943, - "html-minifier": 35.57258739419913, - "minimize": 85.73538788107287 + "hyperbuild-nodejs": 265.55362689112695, + "html-minifier": 37.146711151201565, + "minimize": 87.7133467873164 }, "Reddit": { - "hyperbuild-nodejs": 410.12831233429847, - "html-minifier": 45.16614107393938, - "minimize": 122.77229783626386 + "hyperbuild-nodejs": 391.75000439723124, + "html-minifier": 45.067854272152125, + "minimize": 125.87983932864549 }, "Stack Overflow": { - "hyperbuild-nodejs": 844.9492250936706, - "html-minifier": 47.752958982642866, - "minimize": 157.06321076776504 + "hyperbuild-nodejs": 818.3755008258345, + "html-minifier": 41.43093414076361, + "minimize": 159.71387801780298 }, "Twitter": { - "hyperbuild-nodejs": 279.4114354197891, - "html-minifier": 41.71822833204345, - "minimize": 164.8527211050192 + "hyperbuild-nodejs": 274.57816497268476, + "html-minifier": 36.94949014023178, + "minimize": 168.81796573617953 }, "Wikipedia": { - "hyperbuild-nodejs": 57.721598283350104, - "html-minifier": 3.1325913382989596, - "minimize": 8.741663457588 + "hyperbuild-nodejs": 54.852210553433345, + "html-minifier": 2.821530343574604, + "minimize": 8.66394750522524 } } \ No newline at end of file diff --git a/bench/speed.png b/bench/speed.png index a2fc0292cae050ea2dedb10970333da696a0f9f7..223a884b7ae33bd99f205408b3436fc63ffa5e4b 100644 GIT binary patch delta 14893 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 delta 14893 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 = read_json(file_name.as_str()); +#[derive(Serialize, Deserialize)] +struct TagAttr { + boolean: bool, + redundant_if_empty: bool, + collapse_and_trim: bool, + default_value: Option, +} + +impl TagAttr { + fn code(&self) -> String { + format!(r" + AttributeMinification {{ + boolean: {boolean}, + redundant_if_empty: {redundant_if_empty}, + collapse_and_trim: {collapse_and_trim}, + default_value: {default_value}, + }} + ", + boolean = self.boolean, + redundant_if_empty = self.redundant_if_empty, + collapse_and_trim = self.collapse_and_trim, + default_value = match &self.default_value { + Some(val) => format!("Some({})", create_byte_string_literal(val.as_bytes())), + None => "None".to_string(), + }, + ) + } +} + +fn generate_attr_map() { + let attrs: HashMap> = read_json("attrs"); let mut code = String::new(); - for (name, elems) in attrs.iter() { - if !elems.contains(&"".to_string()) { + for (attr_name, tags_map) in attrs.iter() { + if let Some(global_attr) = tags_map.get("") { code.push_str(format!( - "static {}_{}_ATTR: &phf::Set<&'static [u8]> = &phf::phf_set!({});\n\n", - name.to_uppercase(), - snake_case, - elems.iter().map(|e| format!("b\"{}\"", e)).collect::>().join(", "), + "static {}_ATTR: &AttrMapEntry = &AttrMapEntry::AllHtmlElements({});\n\n", + attr_name.to_uppercase(), + global_attr.code(), + ).as_str()); + } else { + code.push_str(format!( + "static {}_ATTR: &AttrMapEntry = &AttrMapEntry::DistinctHtmlElements(phf::phf_map! {{\n{}\n}});\n\n", + attr_name.to_uppercase(), + tags_map + .iter() + .map(|(tag_name, tag_attr)| format!( + "b\"{}\" => {}", + tag_name, + tag_attr.code(), + )) + .collect::>() + .join(",\n"), ).as_str()); }; }; - code.push_str(format!("pub static {}: crate::pattern::AttrMap = crate::pattern::AttrMap::new(phf::phf_map!{{\n", snake_case).as_str()); - for (name, elems) in attrs.iter() { - if elems.contains(&"".to_string()) { - code.push_str(format!("\tb\"{}\" => crate::pattern::AttrMapEntry::AllHtmlElements,\n", name).as_str()); - } else { - code.push_str(format!("\tb\"{}\" => crate::pattern::AttrMapEntry::SomeHtmlElements({}_{}_ATTR),\n", name, name.to_uppercase(), snake_case).as_str()); - }; + code.push_str("pub static ATTRS: AttrMap = AttrMap::new(phf::phf_map! {\n"); + for attr_name in attrs.keys() { + code.push_str(format!("\tb\"{}\" => {}_ATTR,\n", attr_name, attr_name.to_uppercase()).as_str()); }; code.push_str("});\n\n"); - write_rs(file_name.as_str(), code); + write_rs("attrs", code); } #[derive(Serialize, Deserialize)] @@ -182,8 +217,7 @@ fn generate_tries() { } fn main() { - generate_attr_map("boolean attrs"); - generate_attr_map("redundant if empty attrs"); + generate_attr_map(); generate_entities(); generate_patterns(); generate_tries(); diff --git a/gen/attrs.json b/gen/attrs.json new file mode 100644 index 0000000..8c0ada7 --- /dev/null +++ b/gen/attrs.json @@ -0,0 +1,1830 @@ +{ + "abbr": { + "td": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "th": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "about": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "accept": { + "input": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "acceptcharset": { + "form": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "accesskey": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "action": { + "form": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "align": { + "td": { + "boolean": false, + "redundant_if_empty": false, + "collapse_and_trim": false + }, + "th": { + "boolean": false, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "allow": { + "iframe": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "allowfullscreen": { + "iframe": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "allowtransparency": { + "iframe": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "alt": { + "area": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "img": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "input": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "as": { + "link": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "async": { + "script": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "autocapitalize": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "autocomplete": { + "form": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "input": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "select": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "textarea": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "autocorrect": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "autofocus": { + "button": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + }, + "input": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + }, + "keygen": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + }, + "select": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + }, + "textarea": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "autoplay": { + "media": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "autosave": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "capture": { + "input": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "cellpadding": { + "table": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "cellspacing": { + "table": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "challenge": { + "keygen": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "charset": { + "meta": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "script": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "checked": { + "input": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "cite": { + "blockquote": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "del": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "ins": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "quote": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "class": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": true + } + }, + "classid": { + "object": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "color": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "cols": { + "textarea": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "colspan": { + "td": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "th": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "content": { + "meta": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "contenteditable": { + "": { + "boolean": false, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "contextmenu": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "controls": { + "media": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "controlslist": { + "media": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "coords": { + "area": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "crossorigin": { + "img": { + "boolean": false, + "redundant_if_empty": false, + "collapse_and_trim": false + }, + "input": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "link": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "media": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "script": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "data": { + "object": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "datatype": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "datetime": { + "del": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "ins": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "time": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "decoding": { + "img": { + "boolean": false, + "redundant_if_empty": false, + "collapse_and_trim": false, + "default_value": "auto" + } + }, + "default": { + "track": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "defaultchecked": { + "": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "defaultvalue": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "defer": { + "script": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "dir": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "dirname": { + "textarea": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "disabled": { + "button": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + }, + "fieldset": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + }, + "input": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + }, + "keygen": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + }, + "optgroup": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + }, + "option": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + }, + "select": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + }, + "textarea": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "disablepictureinpicture": { + "video": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "download": { + "a": { + "boolean": false, + "redundant_if_empty": false, + "collapse_and_trim": false + }, + "area": { + "boolean": false, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "draggable": { + "": { + "boolean": false, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "enctype": { + "form": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false, + "default_value": "application/x-www-form-urlencoded" + } + }, + "form": { + "button": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "fieldset": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "input": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "keygen": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "label": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "meter": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "object": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "output": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "select": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "textarea": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "formaction": { + "button": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "input": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "formenctype": { + "button": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false, + "default_value": "application/x-www-form-urlencoded" + }, + "input": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false, + "default_value": "application/x-www-form-urlencoded" + } + }, + "formmethod": { + "button": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "input": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "formnovalidate": { + "button": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + }, + "input": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "formtarget": { + "button": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "input": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "frameborder": { + "iframe": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false, + "default_value": "1" + } + }, + "headers": { + "td": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "th": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "height": { + "canvas": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "embed": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "iframe": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false, + "default_value": "150" + }, + "img": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "input": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "object": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "video": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "hidden": { + "": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "high": { + "meter": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "href": { + "a": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "area": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "base": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "link": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "hreflang": { + "a": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "area": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "link": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "htmlfor": { + "label": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "output": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "httpequiv": { + "meta": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "id": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "inlist": { + "": { + "boolean": false, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "inputmode": { + "": { + "boolean": false, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "integrity": { + "link": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "script": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "is": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "itemid": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "itemprop": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "itemref": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "itemscope": { + "": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "itemtype": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "keyparams": { + "keygen": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "keytype": { + "keygen": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "kind": { + "track": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "label": { + "optgroup": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "option": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "track": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "lang": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "list": { + "input": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "loop": { + "media": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "low": { + "meter": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "manifest": { + "html": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "marginheight": { + "iframe": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "marginwidth": { + "iframe": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "max": { + "input": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "meter": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "progress": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "maxlength": { + "input": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "textarea": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "media": { + "a": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "area": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "link": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "source": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "style": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "mediagroup": { + "media": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "method": { + "form": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false, + "default_value": "get" + } + }, + "min": { + "input": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "meter": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "minlength": { + "input": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "textarea": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "multiple": { + "input": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + }, + "select": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "muted": { + "media": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "name": { + "button": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "fieldset": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "form": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "iframe": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "input": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "keygen": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "map": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "meta": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "object": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "output": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "param": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "select": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "textarea": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "nomodule": { + "script": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "nonce": { + "script": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "style": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "novalidate": { + "form": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "onchange": { + "input": { + "boolean": false, + "redundant_if_empty": false, + "collapse_and_trim": false + }, + "select": { + "boolean": false, + "redundant_if_empty": false, + "collapse_and_trim": false + }, + "textarea": { + "boolean": false, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "open": { + "details": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + }, + "dialog": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "optimum": { + "meter": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "pattern": { + "input": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "ping": { + "a": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "placeholder": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "playsinline": { + "media": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + }, + "video": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "poster": { + "video": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "prefix": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "preload": { + "media": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "property": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "radiogroup": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "readonly": { + "input": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + }, + "textarea": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "referrerpolicy": { + "a": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "iframe": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false, + "default_value": "no-referrer-when-downgrade" + } + }, + "rel": { + "a": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "area": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "link": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "required": { + "input": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + }, + "select": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + }, + "textarea": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "resource": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "results": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "reversed": { + "ol": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "role": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "rows": { + "textarea": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "rowspan": { + "td": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "th": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "sandbox": { + "iframe": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "scope": { + "td": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "th": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "scoped": { + "style": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "scrolling": { + "iframe": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "seamless": { + "iframe": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "security": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "selected": { + "option": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "shape": { + "area": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "size": { + "input": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "select": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "sizes": { + "img": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "link": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "source": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "slot": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "span": { + "col": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false, + "default_value": "1" + }, + "colgroup": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false, + "default_value": "1" + } + }, + "spellcheck": { + "": { + "boolean": false, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "src": { + "embed": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "iframe": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "img": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "input": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "media": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "script": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "source": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "track": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "srcdoc": { + "iframe": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "srclang": { + "track": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "srcset": { + "img": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "source": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "start": { + "ol": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "step": { + "input": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "style": { + "": { + "boolean": false, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "summary": { + "table": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "suppresscontenteditablewarning": { + "": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "suppresshydrationwarning": { + "": { + "boolean": true, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "tabindex": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "target": { + "a": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false, + "default_value": "_self" + }, + "area": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "base": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "form": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false, + "default_value": "_self" + } + }, + "title": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "translate": { + "": { + "boolean": false, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "type": { + "a": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "button": { + "boolean": false, + "redundant_if_empty": false, + "collapse_and_trim": false, + "default_value": "submit" + }, + "embed": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "input": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false, + "default_value": "text" + }, + "link": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false, + "default_value": "text/css" + }, + "menu": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "object": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "ol": { + "boolean": false, + "redundant_if_empty": false, + "collapse_and_trim": false + }, + "script": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "source": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "style": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "typeof": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "unselectable": { + "": { + "boolean": false, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "usemap": { + "img": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "object": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "valign": { + "td": { + "boolean": false, + "redundant_if_empty": false, + "collapse_and_trim": false + } + }, + "value": { + "button": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "data": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "input": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "li": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "meter": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "option": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "param": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "progress": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "select": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "textarea": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "vocab": { + "": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "width": { + "canvas": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "col": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "embed": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "iframe": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false, + "default_value": "300" + }, + "img": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "input": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "object": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + }, + "video": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "wmode": { + "object": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + }, + "wrap": { + "textarea": { + "boolean": false, + "redundant_if_empty": true, + "collapse_and_trim": false + } + } +} \ No newline at end of file diff --git a/gen/boolean_attrs.json b/gen/boolean_attrs.json deleted file mode 100644 index 51f5521..0000000 --- a/gen/boolean_attrs.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "allowfullscreen": [ - "iframe" - ], - "allowtransparency": [ - "iframe" - ], - "async": [ - "script" - ], - "autofocus": [ - "button", - "input", - "keygen", - "select", - "textarea" - ], - "autoplay": [ - "media" - ], - "capture": [ - "input" - ], - "checked": [ - "input" - ], - "controls": [ - "media" - ], - "default": [ - "track" - ], - "defaultchecked": [ - "" - ], - "defer": [ - "script" - ], - "disabled": [ - "button", - "fieldset", - "input", - "keygen", - "optgroup", - "option", - "select", - "textarea" - ], - "disablepictureinpicture": [ - "video" - ], - "formnovalidate": [ - "button", - "input" - ], - "hidden": [ - "" - ], - "itemscope": [ - "" - ], - "loop": [ - "media" - ], - "multiple": [ - "input", - "select" - ], - "muted": [ - "media" - ], - "nomodule": [ - "script" - ], - "novalidate": [ - "form" - ], - "open": [ - "details", - "dialog" - ], - "playsinline": [ - "media", - "video" - ], - "readonly": [ - "input", - "textarea" - ], - "required": [ - "input", - "select", - "textarea" - ], - "reversed": [ - "ol" - ], - "scoped": [ - "style" - ], - "seamless": [ - "iframe" - ], - "selected": [ - "option" - ], - "suppresscontenteditablewarning": [ - "" - ], - "suppresshydrationwarning": [ - "" - ] -} \ No newline at end of file diff --git a/gen/build/attrs.js b/gen/build/attrs.js new file mode 100644 index 0000000..f4af704 --- /dev/null +++ b/gen/build/attrs.js @@ -0,0 +1,205 @@ +const request = require('request-promise-native'); +const {promises: fs} = require('fs'); +const ts = require('typescript'); +const path = require('path'); + +const fromCamelCase = camelCase => camelCase.split(/(?=^|[A-Z])/).map(w => w.toLowerCase()); + +const ATTRS_PATH = path.join(__dirname, '..', 'attrs.json'); + +const REACT_TYPINGS_URL = 'https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/master/types/react/index.d.ts'; +const REACT_TYPINGS_FILE = path.join(__dirname, 'react.d.ts'); +const fetchReactTypingsSource = async () => { + try { + return await fs.readFile(REACT_TYPINGS_FILE, 'utf8'); + } catch (err) { + if (err.code !== 'ENOENT') { + throw err; + } + const source = await request(REACT_TYPINGS_URL); + await fs.writeFile(REACT_TYPINGS_FILE, source); + return source; + } +}; + +const tagNameNormalised = { + 'anchor': 'a', +}; + +const attrNameNormalised = { + 'classname': 'class', +}; + +const reactSpecificAttributes = [ + 'defaultChecked', 'defaultValue', 'suppressContentEditableWarning', 'suppressHydrationWarning', +]; + +// TODO Consider and check behaviour when value matches case insensitively, after trimming whitespace, numerically (for number values), etc. +// TODO This is currently manually sourced and written. Try to get machine-readable spec and automate. +const defaultAttributeValues = { + 'align': [{ + tags: ['img'], + defaultValue: 'bottom', + }], + 'decoding': [{ + tags: ['img'], + defaultValue: 'auto', + }], + 'enctype': [{ + tags: ['form'], + defaultValue: 'application/x-www-form-urlencoded', + }], + 'frameborder': [{ + tags: ['iframe'], + defaultValue: '1', + isPositiveInteger: true, + }], + 'formenctype': [{ + tags: ['button', 'input'], + defaultValue: 'application/x-www-form-urlencoded', + }], + 'height': [{ + tags: ['iframe'], + defaultValue: '150', + isPositiveInteger: true, + }], + 'importance': [{ + tags: ['iframe'], + defaultValue: 'auto', + }], + 'loading': [{ + tags: ['iframe', 'img'], + defaultValue: 'eager', + }], + 'method': [{ + tags: ['form'], + defaultValue: 'get', + }], + 'referrerpolicy': [{ + tags: ['iframe', 'img'], + defaultValue: 'no-referrer-when-downgrade', + }], + 'rules': [{ + tags: ['table'], + defaultValue: 'none', + }], + 'span': [{ + tags: ['col', 'colgroup'], + defaultValue: '1', + isPositiveInteger: true, + }], + 'target': [{ + tags: ['a', 'form'], + defaultValue: '_self', + }], + 'type': [{ + tags: ['button'], + defaultValue: 'submit', + }, { + tags: ['input'], + defaultValue: 'text', + }, { + tags: ['link'], + defaultValue: 'text/css', + }], + 'width': [{ + tags: ['iframe'], + defaultValue: '300', + isPositiveInteger: true, + }], +}; + +const collapsibleAndTrimmable = { + 'class': [''], +}; + +// TODO Is escapedText the API for getting name? +const getNameOfNode = n => n.name.escapedText; +const normaliseName = (name, norms) => [name.toLowerCase()].map(n => norms[n] || n)[0]; + +const processReactTypeDeclarations = async (source) => { + const nodes = [source]; + // Use index-based loop to keep iterating as nodes array grows. + for (let i = 0; i < nodes.length; i++) { + // forEachChild doesn't work if return value is number (e.g. return value of Array.prototype.push). + nodes[i].forEachChild(c => void nodes.push(c)); + } + const attributeNodes = nodes + .filter(n => n.kind === ts.SyntaxKind.InterfaceDeclaration) + .map(n => [/^([A-Za-z]*)HTMLAttributes/.exec(getNameOfNode(n)), n]) + .filter(([matches]) => matches) + .map(([matches, node]) => [normaliseName(matches[1], tagNameNormalised), node]) + .filter(([tagName]) => !['all', 'webview'].includes(tagName)) + .sort((a, b) => a[0].localeCompare(b[0])); + + // Process global attributes first as they also appear on some specific tags but we don't want to keep the specific ones if they're global. + if (attributeNodes[0][0] !== '') { + throw new Error(`Global attributes is not first to be processed`); + } + + const attributes = new Map(); + + for (const [tagName, node] of attributeNodes) { + for (const n of node.members.filter(n => n.kind === ts.SyntaxKind.PropertySignature)) { + const attrName = normaliseName(getNameOfNode(n), attrNameNormalised); + if (reactSpecificAttributes.includes(attrName)) continue; + + const types = n.type.kind === ts.SyntaxKind.UnionType + ? n.type.types.map(t => t.kind) + : [n.type.kind]; + + const boolean = types.includes(ts.SyntaxKind.BooleanKeyword); + // If types includes boolean and string, make it a boolean attr to prevent it from being removed if empty value. + const redundantIfEmpty = !boolean && + (types.includes(ts.SyntaxKind.StringKeyword) || types.includes(ts.SyntaxKind.NumberKeyword)); + const defaultValue = (defaultAttributeValues[attrName] || []) + .filter(a => a.tags.includes(tagName)) + .map(a => a.defaultValue); + const collapseAndTrim = (collapsibleAndTrimmable[attrName] || []).includes(tagName); + if (defaultValue.length > 1) { + throw new Error(`Tag-attribute combination has multiple default values: ${defaultValue}`); + } + const attr = { + boolean, + redundant_if_empty: redundantIfEmpty, + collapse_and_trim: collapseAndTrim, + default_value: defaultValue[0], + }; + + if (!attributes.has(attrName)) attributes.set(attrName, new Map()); + const tagsForAttribute = attributes.get(attrName); + if (tagsForAttribute.has(tagName)) throw new Error(`Duplicate tag-attribute combination: <${tagName} ${attrName}>`); + + const globalAttr = tagsForAttribute.get(''); + if (globalAttr) { + if (globalAttr.boolean !== attr.boolean + || globalAttr.redundant_if_empty !== attr.redundant_if_empty + || globalAttr.collapse_and_trim !== attr.collapse_and_trim + || globalAttr.default_value !== attr.default_value) { + throw new Error(`Global and tag-specific attributes conflict: ${JSON.stringify(globalAttr, null, 2)} ${JSON.stringify(attr, null, 2)}`); + } + } else { + tagsForAttribute.set(tagName, attr); + } + } + } + + // Sort output JSON object by property so diffs are clearer. + await fs.writeFile(ATTRS_PATH, JSON.stringify( + Object.fromEntries( + [...attributes.entries()] + .map(([attrName, tagsMap]) => [attrName, Object.fromEntries( + [...tagsMap.entries()] + .sort((a, b) => a[0].localeCompare(b[0])) + )]) + .sort((a, b) => a[0].localeCompare(b[0])) + ), + null, + 2, + )); +}; + +(async () => { + const source = ts.createSourceFile(`react.d.ts`, await fetchReactTypingsSource(), ts.ScriptTarget.ES2019); + await processReactTypeDeclarations(source); +})(); diff --git a/gen/build/dom.js b/gen/build/dom.js deleted file mode 100644 index ff6764c..0000000 --- a/gen/build/dom.js +++ /dev/null @@ -1,93 +0,0 @@ -const request = require('request-promise-native'); -const {promises: fs} = require('fs'); -const ts = require('typescript'); -const path = require('path'); - -const fromCamelCase = camelCase => camelCase.split(/(?=^|[A-Z])/).map(w => w.toLowerCase()); - -const BOOLEAN_ATTRS_PATH = path.join(__dirname, '..', 'boolean_attrs.json'); -const REDUNDANT_IF_EMPTY_ATTRS_PATH = path.join(__dirname, '..', 'redundant_if_empty_attrs.json'); - -const REACT_TYPINGS_URL = 'https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/master/types/react/index.d.ts'; -const REACT_TYPINGS_FILE = path.join(__dirname, 'react.d.ts'); -const fetchReactTypingsSource = async () => { - try { - return await fs.readFile(REACT_TYPINGS_FILE, 'utf8'); - } catch (err) { - if (err.code !== 'ENOENT') { - throw err; - } - const source = await request(REACT_TYPINGS_URL); - await fs.writeFile(REACT_TYPINGS_FILE, source); - return source; - } -}; - -const attrInterfaceToTagName = { - 'anchor': 'a', -}; - -const attrNameNormalised = { - 'classname': 'class', -}; - -const reactSpecificAttributes = [ - 'defaultChecked', 'defaultValue', 'suppressContentEditableWarning', 'suppressHydrationWarning', -]; - -const processReactTypeDeclarations = async (source) => { - const booleanAttributes = new Map(); - const redundantIfEmptyAttributes = new Map(); - - const unvisited = [source]; - while (unvisited.length) { - const node = unvisited.shift(); - if (node.kind === ts.SyntaxKind.InterfaceDeclaration) { - const name = node.name.escapedText; - let matches; - if ((matches = /^([A-Za-z]*)HTMLAttributes/.exec(name))) { - const tagName = [matches[1].toLowerCase()].map(n => attrInterfaceToTagName[n] || n)[0]; - if (!['all', 'webview'].includes(tagName)) { - for (const n of node.members.filter(n => n.kind === ts.SyntaxKind.PropertySignature)) { - // TODO Is escapedText the API for getting name? - const attr = [n.name.escapedText.toLowerCase()].map(n => attrNameNormalised[n] || n)[0]; - const types = n.type.kind === ts.SyntaxKind.UnionType - ? n.type.types.map(t => t.kind) - : [n.type.kind]; - // If types includes boolean and string, make it a boolean attr to prevent it from being removed if empty value. - if (types.includes(ts.SyntaxKind.BooleanKeyword)) { - if (!booleanAttributes.has(attr)) { - booleanAttributes.set(attr, []); - } - booleanAttributes.get(attr).push(tagName); - } else if (types.includes(ts.SyntaxKind.StringKeyword) || types.includes(ts.SyntaxKind.NumberKeyword)) { - if (!redundantIfEmptyAttributes.has(attr)) { - redundantIfEmptyAttributes.set(attr, []); - } - redundantIfEmptyAttributes.get(attr).push(tagName); - } - } - } - } - } - // forEachChild doesn't seem to work if return value is number (e.g. Array.prototype.push return value). - node.forEachChild(c => void unvisited.push(c)); - } - - // Sort output JSON object by property so diffs are clearer. - await fs.writeFile(BOOLEAN_ATTRS_PATH, JSON.stringify( - Object.fromEntries([...booleanAttributes.entries()].sort((a, b) => a[0].localeCompare(b[0]))), - null, - 2, - )); - await fs.writeFile(REDUNDANT_IF_EMPTY_ATTRS_PATH, JSON.stringify( - Object.fromEntries([...redundantIfEmptyAttributes.entries()].sort((a, b) => a[0].localeCompare(b[0]))), - null, - 2, - )); -}; - -(async () => { - const source = ts.createSourceFile(`react.d.ts`, await fetchReactTypingsSource(), ts.ScriptTarget.ES2019); - await processReactTypeDeclarations(source); -})(); diff --git a/gen/redundant_if_empty_attrs.json b/gen/redundant_if_empty_attrs.json deleted file mode 100644 index 4b2373f..0000000 --- a/gen/redundant_if_empty_attrs.json +++ /dev/null @@ -1,467 +0,0 @@ -{ - "abbr": [ - "td", - "th" - ], - "about": [ - "" - ], - "accept": [ - "input" - ], - "acceptcharset": [ - "form" - ], - "accesskey": [ - "" - ], - "action": [ - "form" - ], - "allow": [ - "iframe" - ], - "alt": [ - "area", - "img", - "input" - ], - "as": [ - "link" - ], - "autocapitalize": [ - "" - ], - "autocomplete": [ - "form", - "input", - "select", - "textarea" - ], - "autocorrect": [ - "" - ], - "autosave": [ - "" - ], - "cellpadding": [ - "table" - ], - "cellspacing": [ - "table" - ], - "challenge": [ - "keygen" - ], - "charset": [ - "meta", - "script" - ], - "cite": [ - "blockquote", - "del", - "ins", - "quote" - ], - "class": [ - "" - ], - "classid": [ - "object" - ], - "color": [ - "" - ], - "cols": [ - "textarea" - ], - "colspan": [ - "td", - "th" - ], - "content": [ - "meta" - ], - "contextmenu": [ - "" - ], - "controlslist": [ - "media" - ], - "coords": [ - "area" - ], - "crossorigin": [ - "input", - "link", - "media", - "script" - ], - "data": [ - "object" - ], - "datatype": [ - "" - ], - "datetime": [ - "del", - "ins", - "time" - ], - "defaultvalue": [ - "" - ], - "dir": [ - "" - ], - "dirname": [ - "textarea" - ], - "enctype": [ - "form" - ], - "form": [ - "button", - "fieldset", - "input", - "keygen", - "label", - "meter", - "object", - "output", - "select", - "textarea" - ], - "formaction": [ - "button", - "input" - ], - "formenctype": [ - "button", - "input" - ], - "formmethod": [ - "button", - "input" - ], - "formtarget": [ - "button", - "input" - ], - "frameborder": [ - "iframe" - ], - "headers": [ - "td", - "th" - ], - "height": [ - "canvas", - "embed", - "iframe", - "img", - "input", - "object", - "video" - ], - "high": [ - "meter" - ], - "href": [ - "a", - "area", - "base", - "link" - ], - "hreflang": [ - "a", - "area", - "link" - ], - "htmlfor": [ - "label", - "output" - ], - "httpequiv": [ - "meta" - ], - "id": [ - "" - ], - "integrity": [ - "link", - "script" - ], - "is": [ - "" - ], - "itemid": [ - "" - ], - "itemprop": [ - "" - ], - "itemref": [ - "" - ], - "itemtype": [ - "" - ], - "keyparams": [ - "keygen" - ], - "keytype": [ - "keygen" - ], - "kind": [ - "track" - ], - "label": [ - "optgroup", - "option", - "track" - ], - "lang": [ - "" - ], - "list": [ - "input" - ], - "low": [ - "meter" - ], - "manifest": [ - "html" - ], - "marginheight": [ - "iframe" - ], - "marginwidth": [ - "iframe" - ], - "max": [ - "input", - "meter", - "progress" - ], - "maxlength": [ - "input", - "textarea" - ], - "media": [ - "a", - "area", - "link", - "source", - "style" - ], - "mediagroup": [ - "media" - ], - "method": [ - "form" - ], - "min": [ - "input", - "meter" - ], - "minlength": [ - "input", - "textarea" - ], - "name": [ - "button", - "fieldset", - "form", - "iframe", - "input", - "keygen", - "map", - "meta", - "object", - "output", - "param", - "select", - "textarea" - ], - "nonce": [ - "script", - "style" - ], - "optimum": [ - "meter" - ], - "pattern": [ - "input" - ], - "ping": [ - "a" - ], - "placeholder": [ - "", - "input", - "textarea" - ], - "poster": [ - "video" - ], - "prefix": [ - "" - ], - "preload": [ - "media" - ], - "property": [ - "" - ], - "radiogroup": [ - "" - ], - "referrerpolicy": [ - "a", - "iframe" - ], - "rel": [ - "a", - "area", - "link" - ], - "resource": [ - "" - ], - "results": [ - "" - ], - "role": [ - "" - ], - "rows": [ - "textarea" - ], - "rowspan": [ - "td", - "th" - ], - "sandbox": [ - "iframe" - ], - "scope": [ - "td", - "th" - ], - "scrolling": [ - "iframe" - ], - "security": [ - "" - ], - "shape": [ - "area" - ], - "size": [ - "input", - "select" - ], - "sizes": [ - "img", - "link", - "source" - ], - "slot": [ - "" - ], - "span": [ - "col", - "colgroup" - ], - "src": [ - "embed", - "iframe", - "img", - "input", - "media", - "script", - "source", - "track" - ], - "srcdoc": [ - "iframe" - ], - "srclang": [ - "track" - ], - "srcset": [ - "img", - "source" - ], - "start": [ - "ol" - ], - "step": [ - "input" - ], - "summary": [ - "table" - ], - "tabindex": [ - "" - ], - "target": [ - "a", - "area", - "base", - "form" - ], - "title": [ - "" - ], - "type": [ - "a", - "embed", - "input", - "link", - "menu", - "object", - "script", - "source", - "style" - ], - "typeof": [ - "" - ], - "usemap": [ - "img", - "object" - ], - "value": [ - "button", - "data", - "input", - "li", - "meter", - "option", - "param", - "progress", - "select", - "textarea" - ], - "vocab": [ - "" - ], - "width": [ - "canvas", - "col", - "embed", - "iframe", - "img", - "input", - "object", - "video" - ], - "wmode": [ - "object" - ], - "wrap": [ - "textarea" - ] -} \ No newline at end of file diff --git a/src/pattern.rs b/src/pattern.rs index 6011459..bf1971e 100644 --- a/src/pattern.rs +++ b/src/pattern.rs @@ -1,5 +1,3 @@ -use phf::{Map, Set}; - pub struct SinglePattern { pub seq: &'static [u8], pub table: &'static [usize], @@ -31,23 +29,3 @@ impl SinglePattern { None } } - -pub enum AttrMapEntry { - AllHtmlElements, - SomeHtmlElements(&'static Set<&'static [u8]>), -} - -pub struct AttrMap(Map<&'static [u8], AttrMapEntry>); - -impl AttrMap { - pub const fn new(map: Map<&'static [u8], AttrMapEntry>) -> AttrMap { - AttrMap(map) - } - - pub fn contains(&self, tag: &[u8], attr: &[u8]) -> bool { - self.0.get(attr).filter(|elems| match elems { - AttrMapEntry::AllHtmlElements => true, - AttrMapEntry::SomeHtmlElements(set) => set.contains(tag), - }).is_some() - } -} diff --git a/src/spec/tag/omission.rs b/src/spec/tag/omission.rs index b66f453..bb9ef72 100644 --- a/src/spec/tag/omission.rs +++ b/src/spec/tag/omission.rs @@ -29,7 +29,7 @@ impl ClosingTagOmissionRule { } } - pub fn can_omit_as_prev(&self, after: &[u8]) -> bool { + pub fn can_omit_as_before(&self, after: &[u8]) -> bool { self.followed_by.contains(after) } } diff --git a/src/unit/attr/mod.rs b/src/unit/attr/mod.rs index ea65223..42454fe 100644 --- a/src/unit/attr/mod.rs +++ b/src/unit/attr/mod.rs @@ -1,4 +1,4 @@ -use phf::{phf_set, Set}; +use phf::Map; use crate::err::ProcessingResult; use crate::proc::{Processor, ProcessorRange}; @@ -7,11 +7,34 @@ use crate::unit::attr::value::{DelimiterType, process_attr_value, ProcessedAttrV mod value; -include!(concat!(env!("OUT_DIR"), "/gen_boolean_attrs.rs")); +pub struct AttributeMinification { + pub boolean: bool, + pub redundant_if_empty: bool, + pub collapse_and_trim: bool, + pub default_value: Option<&'static [u8]>, +} -static COLLAPSIBLE_AND_TRIMMABLE_ATTRS: Set<&'static [u8]> = phf_set! { - b"class", -}; +pub enum AttrMapEntry { + AllHtmlElements(AttributeMinification), + DistinctHtmlElements(Map<&'static [u8], AttributeMinification>), +} + +pub struct AttrMap(Map<&'static [u8], &'static AttrMapEntry>); + +impl AttrMap { + pub const fn new(map: Map<&'static [u8], &'static AttrMapEntry>) -> AttrMap { + AttrMap(map) + } + + pub fn get(&self, tag: &[u8], attr: &[u8]) -> Option<&AttributeMinification> { + self.0.get(attr).and_then(|entry| match entry { + AttrMapEntry::AllHtmlElements(min) => Some(min), + AttrMapEntry::DistinctHtmlElements(map) => map.get(tag), + }) + } +} + +include!(concat!(env!("OUT_DIR"), "/gen_attrs.rs")); #[derive(Clone, Copy, Eq, PartialEq)] pub enum AttrType { @@ -40,10 +63,11 @@ pub fn process_attr(proc: &mut Processor, element: ProcessorRange) -> Processing // It's possible to expect attribute name but not be called at an attribute, e.g. due to whitespace between name and // value, which causes name to be considered boolean attribute and `=` to be start of new (invalid) attribute name. let name = chain!(proc.match_while_pred(is_name_char).require_with_reason("attribute name")?.keep().out_range()); - let is_boolean = BOOLEAN_ATTRS.contains(&proc[element], &proc[name]); + let attr_cfg = ATTRS.get(&proc[element], &proc[name]); + let is_boolean = attr_cfg.filter(|attr| attr.boolean).is_some(); let after_name = proc.checkpoint(); - let should_collapse_and_trim_value_ws = COLLAPSIBLE_AND_TRIMMABLE_ATTRS.contains(&proc[name]); + let should_collapse_and_trim_value_ws = attr_cfg.filter(|attr| attr.collapse_and_trim).is_some(); chain!(proc.match_while_pred(is_whitespace).discard()); let has_value = chain!(proc.match_char(b'=').keep().matched()); @@ -53,6 +77,9 @@ pub fn process_attr(proc: &mut Processor, element: ProcessorRange) -> Processing chain!(proc.match_while_pred(is_whitespace).discard()); if is_boolean { skip_attr_value(proc)?; + // Discard `=`. + debug_assert_eq!(proc.written_count(after_name), 1); + proc.erase_written(after_name); (AttrType::NoValue, None) } else { match process_attr_value(proc, should_collapse_and_trim_value_ws)? { diff --git a/src/unit/content.rs b/src/unit/content.rs index 322d1f5..bdc7e03 100644 --- a/src/unit/content.rs +++ b/src/unit/content.rs @@ -195,10 +195,10 @@ pub fn process_content(proc: &mut Processor, parent: Option) -> // Whitespace is leading or trailing. // `trim` is on, so don't write it. } else if collapse { + // If writing space, then prev_sibling_closing_tag no longer represents immediate previous sibling node; space will be new previous sibling node (as a text node). + prev_sibling_closing_tag.take().map(|tag| tag.write_closing_tag(proc)); // Current contiguous whitespace needs to be reduced to a single space character. proc.write(b' '); - // If writing space, then prev_sibling_closing_tag no longer represents immediate previous sibling node. - prev_sibling_closing_tag.take().map(|tag| tag.write_closing_tag(proc)); } else { unreachable!(); }; diff --git a/src/unit/tag.rs b/src/unit/tag.rs index bc5cb0a..1f38fc5 100644 --- a/src/unit/tag.rs +++ b/src/unit/tag.rs @@ -5,13 +5,11 @@ use crate::proc::{Processor, ProcessorRange}; use crate::spec::codepoint::{is_alphanumeric, is_whitespace}; use crate::spec::tag::omission::CLOSING_TAG_OMISSION_RULES; use crate::spec::tag::void::VOID_TAGS; -use crate::unit::attr::{AttrType, process_attr, ProcessedAttr}; +use crate::unit::attr::{AttributeMinification, ATTRS, AttrType, process_attr, ProcessedAttr}; use crate::unit::content::process_content; use crate::unit::script::process_script; use crate::unit::style::process_style; -include!(concat!(env!("OUT_DIR"), "/gen_redundant_if_empty_attrs.rs")); - pub static JAVASCRIPT_MIME_TYPES: Set<&'static [u8]> = phf_set! { b"application/ecmascript", b"application/javascript", @@ -73,7 +71,7 @@ pub fn process_tag(proc: &mut Processor, prev_sibling_closing_tag: Option rule.can_omit_as_prev(&proc[source_tag_name]), + Some(rule) => rule.can_omit_as_before(&proc[source_tag_name]), _ => false, }; if !can_omit { @@ -142,7 +140,11 @@ pub fn process_tag(proc: &mut Processor, prev_sibling_closing_tag: Option { // TODO Check if HTML tag before checking if attribute removal applies to all elements. - erase_attr = value.is_none() && REDUNDANT_IF_EMPTY_ATTRS.contains(&proc[tag_name], name); + erase_attr = match (value, ATTRS.get(&proc[tag_name], name)) { + (None, Some(AttributeMinification { redundant_if_empty: true, .. })) => true, + (Some(val), Some(AttributeMinification { default_value: Some(defval), .. })) => proc[val].eq(*defval), + _ => false, + }; } }; if erase_attr { @@ -157,7 +159,11 @@ pub fn process_tag(proc: &mut Processor, prev_sibling_closing_tag: Option"); } else { proc.write_slice(b"/>"); }; + if is_void_tag { + proc.write_slice(b">"); + } else { + proc.write_slice(b"/>"); + }; }; return Ok(ProcessedTag { name: tag_name, has_closing_tag: false }); }; @@ -171,6 +177,7 @@ pub fn process_tag(proc: &mut Processor, prev_sibling_closing_tag: Option