Minify JS/CSS comments; faster removal of boolean attr values; sorted generated JSON objects
This commit is contained in:
parent
9a9b543b26
commit
a9bb4c924f
13
README.md
13
README.md
|
@ -296,11 +296,9 @@ Any entities in attribute values are decoded, and then the shortest representati
|
|||
- Single quoted, with any `'` encoded.
|
||||
- Unquoted, with `"`/`'` first character (if applicable), `>` last character (if applicable), and any whitespace encoded.
|
||||
|
||||
Some attributes have their whitespace (after any decoding) trimmed and collapsed:
|
||||
`class` attributes have their whitespace (after any decoding) trimmed and collapsed.
|
||||
|
||||
- `class`
|
||||
|
||||
[Boolean attributes](./gen/boolean_attrs.json) will have their values removed.
|
||||
[Boolean attribute](./gen/boolean_attrs.json) values are removed.
|
||||
|
||||
`type` attributes on `script` tags with a value equaling a [JavaScript MIME type](https://mimesniff.spec.whatwg.org/#javascript-mime-type) are removed.
|
||||
`type` attributes on `style` tags are removed.
|
||||
|
@ -309,11 +307,16 @@ If an attribute value is empty after any processing, it is completely removed (i
|
|||
|
||||
Spaces are removed between attributes if possible.
|
||||
|
||||
# Script and style
|
||||
|
||||
Insignificant whitespace is trimmed and collapsed inside `<script>` with JS code and `<style>`.
|
||||
|
||||
JS and CSS comments are removed inside `<script>` and `<style>`.
|
||||
|
||||
### Other
|
||||
|
||||
- Comments are removed.
|
||||
- Entities are decoded if valid (see relevant parsing section).
|
||||
- Whitespace is trimmed and collapsed inside `<script>` with JS code and `<style>`.
|
||||
|
||||
### Ignored
|
||||
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
{
|
||||
"allowfullscreen": [
|
||||
"iframe"
|
||||
],
|
||||
"allowtransparency": [
|
||||
"iframe"
|
||||
],
|
||||
"async": [
|
||||
"script"
|
||||
],
|
||||
"autofocus": [
|
||||
"button",
|
||||
"input",
|
||||
|
@ -6,6 +15,21 @@
|
|||
"select",
|
||||
"textarea"
|
||||
],
|
||||
"autoplay": [
|
||||
"media"
|
||||
],
|
||||
"checked": [
|
||||
"input"
|
||||
],
|
||||
"controls": [
|
||||
"media"
|
||||
],
|
||||
"default": [
|
||||
"track"
|
||||
],
|
||||
"defer": [
|
||||
"script"
|
||||
],
|
||||
"disabled": [
|
||||
"button",
|
||||
"fieldset",
|
||||
|
@ -16,32 +40,36 @@
|
|||
"select",
|
||||
"textarea"
|
||||
],
|
||||
"disablepictureinpicture": [
|
||||
"video"
|
||||
],
|
||||
"formnovalidate": [
|
||||
"button",
|
||||
"input"
|
||||
],
|
||||
"loop": [
|
||||
"media"
|
||||
],
|
||||
"multiple": [
|
||||
"input",
|
||||
"select"
|
||||
],
|
||||
"muted": [
|
||||
"media"
|
||||
],
|
||||
"nomodule": [
|
||||
"script"
|
||||
],
|
||||
"novalidate": [
|
||||
"form"
|
||||
],
|
||||
"open": [
|
||||
"details",
|
||||
"dialog"
|
||||
],
|
||||
"novalidate": [
|
||||
"form"
|
||||
],
|
||||
"allowfullscreen": [
|
||||
"iframe"
|
||||
],
|
||||
"allowtransparency": [
|
||||
"iframe"
|
||||
],
|
||||
"seamless": [
|
||||
"iframe"
|
||||
],
|
||||
"checked": [
|
||||
"input"
|
||||
],
|
||||
"multiple": [
|
||||
"input",
|
||||
"select"
|
||||
"playsinline": [
|
||||
"media",
|
||||
"video"
|
||||
],
|
||||
"readonly": [
|
||||
"input",
|
||||
|
@ -52,44 +80,16 @@
|
|||
"select",
|
||||
"textarea"
|
||||
],
|
||||
"autoplay": [
|
||||
"media"
|
||||
],
|
||||
"controls": [
|
||||
"media"
|
||||
],
|
||||
"loop": [
|
||||
"media"
|
||||
],
|
||||
"muted": [
|
||||
"media"
|
||||
],
|
||||
"playsinline": [
|
||||
"media",
|
||||
"video"
|
||||
],
|
||||
"reversed": [
|
||||
"ol"
|
||||
],
|
||||
"selected": [
|
||||
"option"
|
||||
],
|
||||
"async": [
|
||||
"script"
|
||||
],
|
||||
"defer": [
|
||||
"script"
|
||||
],
|
||||
"nomodule": [
|
||||
"script"
|
||||
],
|
||||
"scoped": [
|
||||
"style"
|
||||
],
|
||||
"default": [
|
||||
"track"
|
||||
"seamless": [
|
||||
"iframe"
|
||||
],
|
||||
"disablepictureinpicture": [
|
||||
"video"
|
||||
"selected": [
|
||||
"option"
|
||||
]
|
||||
}
|
|
@ -63,8 +63,9 @@ const processReactTypeDeclarations = async (source) => {
|
|||
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()),
|
||||
Object.fromEntries([...booleanAttributes.entries()].sort((a, b) => a[0].localeCompare(b[0]))),
|
||||
null,
|
||||
2,
|
||||
));
|
||||
|
|
|
@ -3,7 +3,7 @@ use phf::{phf_set, Set};
|
|||
use crate::err::ProcessingResult;
|
||||
use crate::proc::{Processor, ProcessorRange};
|
||||
use crate::spec::codepoint::is_control;
|
||||
use crate::unit::attr::value::{DelimiterType, process_attr_value, ProcessedAttrValue};
|
||||
use crate::unit::attr::value::{DelimiterType, process_attr_value, ProcessedAttrValue, skip_attr_value};
|
||||
|
||||
mod value;
|
||||
|
||||
|
@ -49,15 +49,14 @@ pub fn process_attr(proc: &mut Processor, element: ProcessorRange) -> Processing
|
|||
let (typ, value) = if !has_value {
|
||||
(AttrType::NoValue, None)
|
||||
} else {
|
||||
// TODO Don't process if going to erase anyway.
|
||||
let val = process_attr_value(proc, should_collapse_and_trim_value_ws)?;
|
||||
if is_boolean {
|
||||
proc.erase_written(after_name);
|
||||
skip_attr_value(proc)?;
|
||||
(AttrType::NoValue, None)
|
||||
} else {
|
||||
match val {
|
||||
match process_attr_value(proc, should_collapse_and_trim_value_ws)? {
|
||||
ProcessedAttrValue { value: None, .. } => {
|
||||
// Value is empty, which is equivalent to no value, so discard `=` and any quotes.
|
||||
// Value is empty, which is equivalent to no value, so discard `=`.
|
||||
debug_assert_eq!(proc.written_count(after_name), 1);
|
||||
proc.erase_written(after_name);
|
||||
(AttrType::NoValue, None)
|
||||
}
|
||||
|
|
|
@ -133,6 +133,13 @@ impl Metrics {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn skip_attr_value(proc: &mut Processor) -> ProcessingResult<()> {
|
||||
let src_delimiter = chain!(proc.match_pred(is_attr_quote).require_with_reason("attribute value delimiter quote")?.discard().char());
|
||||
chain!(proc.match_while_not_char(src_delimiter).discard());
|
||||
chain!(proc.match_char(src_delimiter).require_with_reason("attribute value delimiter quote")?.discard());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub struct ProcessedAttrValue {
|
||||
pub delimiter: DelimiterType,
|
||||
pub value: Option<ProcessorRange>,
|
||||
|
|
|
@ -8,19 +8,19 @@ fn is_string_delimiter(c: u8) -> bool {
|
|||
|
||||
fn parse_comment_single(proc: &mut Processor) -> ProcessingResult<()> {
|
||||
if cfg!(debug_assertions) {
|
||||
chain!(proc.match_seq(b"//").expect().keep());
|
||||
chain!(proc.match_seq(b"//").expect().discard());
|
||||
} else {
|
||||
proc.accept_amount_expect(2);
|
||||
proc.skip_amount_expect(2);
|
||||
};
|
||||
|
||||
// Comment can end at closing </script>.
|
||||
// TODO Optimise
|
||||
while !chain!(proc.match_line_terminator().keep().matched()) {
|
||||
while !chain!(proc.match_line_terminator().discard().matched()) {
|
||||
if chain!(proc.match_seq(b"</script>").matched()) {
|
||||
break;
|
||||
}
|
||||
|
||||
proc.accept()?;
|
||||
proc.skip()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -28,19 +28,19 @@ fn parse_comment_single(proc: &mut Processor) -> ProcessingResult<()> {
|
|||
|
||||
fn parse_comment_multi(proc: &mut Processor) -> ProcessingResult<()> {
|
||||
if cfg!(debug_assertions) {
|
||||
chain!(proc.match_seq(b"/*").expect().keep());
|
||||
chain!(proc.match_seq(b"/*").expect().discard());
|
||||
} else {
|
||||
proc.accept_amount_expect(2);
|
||||
proc.skip_amount_expect(2);
|
||||
};
|
||||
|
||||
// Comment can end at closing </script>.
|
||||
// TODO Optimise
|
||||
while !chain!(proc.match_seq(b"*/").keep().matched()) {
|
||||
while !chain!(proc.match_seq(b"*/").discard().matched()) {
|
||||
if chain!(proc.match_seq(b"</script>").matched()) {
|
||||
break;
|
||||
}
|
||||
|
||||
proc.accept()?;
|
||||
proc.skip()?;
|
||||
};
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -11,14 +11,14 @@ fn is_string_delimiter(c: u8) -> bool {
|
|||
|
||||
fn parse_comment(proc: &mut Processor) -> ProcessingResult<()> {
|
||||
if cfg!(debug_assertions) {
|
||||
chain!(proc.match_seq(b"/*").expect().keep());
|
||||
chain!(proc.match_seq(b"/*").expect().discard());
|
||||
} else {
|
||||
proc.accept_amount_expect(2);
|
||||
proc.skip_amount_expect(2);
|
||||
};
|
||||
|
||||
// Unlike script tags, style comments do NOT end at closing tag.
|
||||
while !chain!(proc.match_seq(b"*/").keep().matched()) {
|
||||
proc.accept()?;
|
||||
while !chain!(proc.match_seq(b"*/").discard().matched()) {
|
||||
proc.skip()?;
|
||||
};
|
||||
|
||||
Ok(())
|
||||
|
|
Loading…
Reference in New Issue