Skip to content

Commit

Permalink
feat(html/codegen): Minify svg attributes (#4917)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-akait committed Jun 10, 2022
1 parent 99deeac commit f157aae
Show file tree
Hide file tree
Showing 10 changed files with 356 additions and 44 deletions.
82 changes: 62 additions & 20 deletions crates/swc_html_codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,25 +288,8 @@ where
_ => false,
};

if !can_omit_start_tag {
write_raw!(self, "<");
write_raw!(self, &n.tag_name);

if has_attributes {
space!(self);

self.emit_list(&n.attributes, ListFormat::SpaceDelimited)?;
}

write_raw!(self, ">");

if !self.config.minify && n.namespace == Namespace::HTML && &*n.tag_name == "html" {
newline!(self);
}
}

let no_children = n.namespace == Namespace::HTML
&& matches!(
let no_children = match n.namespace {
Namespace::HTML => matches!(
&*n.tag_name,
"area"
| "base"
Expand All @@ -326,7 +309,66 @@ where
| "source"
| "track"
| "wbr"
);
),
Namespace::SVG => {
matches!(
&*n.tag_name,
"circle"
| "ellipse"
| "line"
| "path"
| "polygon"
| "polyline"
| "rect"
| "stop"
| "use"
) && n.children.is_empty()
}
_ => false,
};

if !can_omit_start_tag {
write_raw!(self, "<");
write_raw!(self, &n.tag_name);

if has_attributes {
space!(self);

self.emit_list(&n.attributes, ListFormat::SpaceDelimited)?;
}

if no_children && n.namespace == Namespace::SVG {
if self.config.minify {
let need_space = match n.attributes.last() {
Some(Attribute {
value: Some(value), ..
}) => !value.chars().any(|c| match c {
c if c.is_ascii_whitespace() => true,
'`' | '=' | '<' | '>' | '"' | '\'' => true,
_ => false,
}),
_ => false,
};

println!("{:?}", n.attributes.last());
println!("{:?}", need_space);

if need_space {
write_raw!(self, " ");
}
} else {
write_raw!(self, " ");
}

write_raw!(self, "/");
}

write_raw!(self, ">");

if !self.config.minify && n.namespace == Namespace::HTML && &*n.tag_name == "html" {
newline!(self);
}
}

if no_children {
return Ok(());
Expand Down
6 changes: 3 additions & 3 deletions crates/swc_html_codegen/tests/fixture/broken-svg/output.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20">
<rect width="20" height="3" y="2" fill="#fff"></rect>
<rect width="20" height="3" y="8.5" fill="#fff"></rect>
<rect width="20" height="3" y="15" fill="#fff"></rect>
<rect width="20" height="3" y="2" fill="#fff" />
<rect width="20" height="3" y="8.5" fill="#fff" />
<rect width="20" height="3" y="15" fill="#fff" />
</svg>

</body></html>
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
</head>
<body>
<svg xmlns=http://www.w3.org/2000/svg width=20 height=20>
<rect width=20 height=3 y=2 fill=#fff></rect>
<rect width=20 height=3 y=8.5 fill=#fff></rect>
<rect width=20 height=3 y=15 fill=#fff></rect>
<rect width=20 height=3 y=2 fill=#fff />
<rect width=20 height=3 y=8.5 fill=#fff />
<rect width=20 height=3 y=15 fill=#fff />
</svg>

92 changes: 92 additions & 0 deletions crates/swc_html_codegen/tests/fixture/svg-tag/input.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,97 @@
test
</textarea>
</svg>

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50"/>
</svg>

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50">NOT ALLOWED< BUT POSSIBLE</circle>
</svg>

<svg viewBox="0 0 200 100" xmlns="http://www.w3.org/2000/svg">
<ellipse cx="100" cy="50" rx="100" ry="50" />
</svg>

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<line x1="0" y1="80" x2="100" y2="20" stroke="black" />

<!-- If you do not specify the stroke
color the line will not be visible -->
</svg>

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<path d="M 10,30
A 20,20 0,0,1 50,30
A 20,20 0,0,1 90,30
Q 90,60 50,90
Q 10,60 10,30 z"/>
</svg>

<svg viewBox="0 0 200 100" xmlns="http://www.w3.org/2000/svg">
<!-- Example of a polygon with the default fill -->
<polygon points="0,100 50,25 50,75 100,0" />

<!-- Example of the same polygon shape with stroke and no fill -->
<polygon points="100,100 150,25 150,75 200,0"
fill="none" stroke="black" />
</svg>

<svg viewBox="0 0 200 100" xmlns="http://www.w3.org/2000/svg">
<!-- Example of a polyline with the default fill -->
<polyline points="0,100 50,25 50,75 100,0" />

<!-- Example of the same polyline shape with stroke and no fill -->
<polyline points="100,100 150,25 150,75 200,0"
fill="none" stroke="black" />
</svg>

<svg viewBox="0 0 220 100" xmlns="http://www.w3.org/2000/svg">
<!-- Simple rectangle -->
<rect width="100" height="100" />

<!-- Rounded corner rectangle -->
<rect x="120" width="100" height="100" rx="15" />
</svg>

<svg viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="myGradient" gradientTransform="rotate(90)">
<stop offset="5%" stop-color="gold" />
<stop offset="95%" stop-color="red" />
</linearGradient>
</defs>

<!-- using my linear gradient -->
<circle cx="5" cy="5" r="4" fill="url('#myGradient')" />
</svg>

<svg viewBox="0 0 30 10" xmlns="http://www.w3.org/2000/svg">
<circle id="myCircle" cx="5" cy="5" r="4" stroke="blue"/>
<use href="#myCircle" x="10" fill="blue"/>
<use href="#myCircle" x="20" fill="white" stroke="red"/>
<!--
stroke="red" will be ignored here, as stroke was already set on myCircle.
Most attributes (except for x, y, width, height and (xlink:)href)
do not override those set in the ancestor.
That's why the circles have different x positions, but the same stroke value.
-->
</svg>

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="&amp;"/>
<circle cx="50" cy="50" r=" "/>
<circle cx="50" cy="50" r="`"/>
<circle cx="50" cy="50" r="="/>
<circle cx="50" cy="50" r="<"/>
<circle cx="50" cy="50" r=">"/>
<circle cx="50" cy="50" r='"'/>
<circle cx="50" cy="50" r="'"/>
<circle cx="50" cy="50" r="a"/>
<circle />
</svg>

</body>
</html>
91 changes: 90 additions & 1 deletion crates/swc_html_codegen/tests/fixture/svg-tag/output.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
</svg>

<svg width="100" height="100">
<circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow"></circle>
<circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" />
<style>
.color::before {
content: "&";
Expand All @@ -28,4 +28,93 @@
</textarea>
</svg>

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50" />
</svg>

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50">NOT ALLOWED&lt; BUT POSSIBLE</circle>
</svg>

<svg viewBox="0 0 200 100" xmlns="http://www.w3.org/2000/svg">
<ellipse cx="100" cy="50" rx="100" ry="50" />
</svg>

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<line x1="0" y1="80" x2="100" y2="20" stroke="black" />

<!-- If you do not specify the stroke
color the line will not be visible -->
</svg>

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<path d="M 10,30
A 20,20 0,0,1 50,30
A 20,20 0,0,1 90,30
Q 90,60 50,90
Q 10,60 10,30 z" />
</svg>

<svg viewBox="0 0 200 100" xmlns="http://www.w3.org/2000/svg">
<!-- Example of a polygon with the default fill -->
<polygon points="0,100 50,25 50,75 100,0" />

<!-- Example of the same polygon shape with stroke and no fill -->
<polygon points="100,100 150,25 150,75 200,0" fill="none" stroke="black" />
</svg>

<svg viewBox="0 0 200 100" xmlns="http://www.w3.org/2000/svg">
<!-- Example of a polyline with the default fill -->
<polyline points="0,100 50,25 50,75 100,0" />

<!-- Example of the same polyline shape with stroke and no fill -->
<polyline points="100,100 150,25 150,75 200,0" fill="none" stroke="black" />
</svg>

<svg viewBox="0 0 220 100" xmlns="http://www.w3.org/2000/svg">
<!-- Simple rectangle -->
<rect width="100" height="100" />

<!-- Rounded corner rectangle -->
<rect x="120" width="100" height="100" rx="15" />
</svg>

<svg viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="myGradient" gradientTransform="rotate(90)">
<stop offset="5%" stop-color="gold" />
<stop offset="95%" stop-color="red" />
</linearGradient>
</defs>

<!-- using my linear gradient -->
<circle cx="5" cy="5" r="4" fill="url('#myGradient')" />
</svg>

<svg viewBox="0 0 30 10" xmlns="http://www.w3.org/2000/svg">
<circle id="myCircle" cx="5" cy="5" r="4" stroke="blue" />
<use href="#myCircle" x="10" fill="blue" />
<use href="#myCircle" x="20" fill="white" stroke="red" />
<!--
stroke="red" will be ignored here, as stroke was already set on myCircle.
Most attributes (except for x, y, width, height and (xlink:)href)
do not override those set in the ancestor.
That's why the circles have different x positions, but the same stroke value.
-->
</svg>

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="&amp;" />
<circle cx="50" cy="50" r=" " />
<circle cx="50" cy="50" r="`" />
<circle cx="50" cy="50" r="=" />
<circle cx="50" cy="50" r="<" />
<circle cx="50" cy="50" r=">" />
<circle cx="50" cy="50" r="&quot;" />
<circle cx="50" cy="50" r="'" />
<circle cx="50" cy="50" r="a" />
<circle />
</svg>


</body></html>

1 comment on commit f157aae

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: f157aae Previous: be87494 Ratio
es/full/minify/libraries/antd 1465450879 ns/iter (± 16438906) 2169539617 ns/iter (± 112699746) 0.68
es/full/minify/libraries/d3 386873007 ns/iter (± 2097786) 467733245 ns/iter (± 12000666) 0.83
es/full/minify/libraries/echarts 1752969484 ns/iter (± 2492308) 2405887949 ns/iter (± 44977088) 0.73
es/full/minify/libraries/jquery 89846887 ns/iter (± 164429) 103024637 ns/iter (± 2551268) 0.87
es/full/minify/libraries/lodash 122926572 ns/iter (± 274916) 141909282 ns/iter (± 3538891) 0.87
es/full/minify/libraries/moment 52512502 ns/iter (± 133424) 60295801 ns/iter (± 2372755) 0.87
es/full/minify/libraries/react 17738124 ns/iter (± 29095) 20828610 ns/iter (± 371233) 0.85
es/full/minify/libraries/terser 463964783 ns/iter (± 1276373) 804489143 ns/iter (± 21299839) 0.58
es/full/minify/libraries/three 491264477 ns/iter (± 5951722) 620967819 ns/iter (± 20745851) 0.79
es/full/minify/libraries/typescript 3415585862 ns/iter (± 6555388) 4848204397 ns/iter (± 67355474) 0.70
es/full/minify/libraries/victory 634328377 ns/iter (± 2874669) 850312090 ns/iter (± 37206608) 0.75
es/full/minify/libraries/vue 135057827 ns/iter (± 343923) 159065269 ns/iter (± 7828137) 0.85
es/full/codegen/es3 35368 ns/iter (± 230) 38854 ns/iter (± 2790) 0.91
es/full/codegen/es5 35343 ns/iter (± 138) 38345 ns/iter (± 3242) 0.92
es/full/codegen/es2015 35330 ns/iter (± 186) 38309 ns/iter (± 2964) 0.92
es/full/codegen/es2016 35305 ns/iter (± 150) 37115 ns/iter (± 2874) 0.95
es/full/codegen/es2017 35285 ns/iter (± 161) 38171 ns/iter (± 3150) 0.92
es/full/codegen/es2018 35328 ns/iter (± 150) 38568 ns/iter (± 2373) 0.92
es/full/codegen/es2019 35283 ns/iter (± 156) 38391 ns/iter (± 4013) 0.92
es/full/codegen/es2020 35334 ns/iter (± 141) 38758 ns/iter (± 3323) 0.91
es/full/all/es3 189224425 ns/iter (± 676605) 219066602 ns/iter (± 8560829) 0.86
es/full/all/es5 178111667 ns/iter (± 543711) 208172511 ns/iter (± 9150869) 0.86
es/full/all/es2015 136785685 ns/iter (± 455436) 169281435 ns/iter (± 6758725) 0.81
es/full/all/es2016 135204806 ns/iter (± 461163) 167555935 ns/iter (± 5755413) 0.81
es/full/all/es2017 134930565 ns/iter (± 296265) 165552655 ns/iter (± 6765937) 0.82
es/full/all/es2018 132640692 ns/iter (± 359366) 161264800 ns/iter (± 6782524) 0.82
es/full/all/es2019 132197656 ns/iter (± 607292) 165185151 ns/iter (± 6999975) 0.80
es/full/all/es2020 126071550 ns/iter (± 493674) 156271945 ns/iter (± 5950974) 0.81
es/full/parser 577270 ns/iter (± 33512) 730268 ns/iter (± 50220) 0.79
es/full/base/fixer 26611 ns/iter (± 303) 35571 ns/iter (± 3903) 0.75
es/full/base/resolver_and_hygiene 140086 ns/iter (± 1879) 175663 ns/iter (± 13141) 0.80
serialization of ast node 184 ns/iter (± 0) 190 ns/iter (± 15) 0.97
serialization of serde 185 ns/iter (± 0) 191 ns/iter (± 13) 0.97

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.