Skip to content

Update compressHTML logic #916

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/slimy-suits-sparkle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/compiler': patch
---

Updates `compressHTML` logic to account for elements which are known to be whitespace sensitive
65 changes: 62 additions & 3 deletions internal/transform/transform.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,62 @@ func isWhitespaceInsensitiveElement(n *astro.Node) bool {
return n.Data == "head"
}

var knownTextElements = map[string]bool{
"a": true,
"abbr": true,
"b": true,
"bdi": true,
"bdo": true,
"blockquote": true,
"br": true,
"caption": true,
"cite": true,
"code": true,
"col": true,
"colgroup": true,
"data": true,
"del": true,
"dfn": true,
"em": true,
"h1": true,
"h2": true,
"h3": true,
"h4": true,
"h5": true,
"h6": true,
"i": true,
"ins": true,
"kbd": true,
"mark": true,
"p": true,
"q": true,
"rp": true,
"rt": true,
"ruby": true,
"s": true,
"samp": true,
"small": true,
"span": true,
"strong": true,
"sub": true,
"sup": true,
"table": true,
"tbody": true,
"td": true,
"tfoot": true,
"th": true,
"thead": true,
"time": true,
"tr": true,
"u": true,
"var": true,
"wbr": true,
}

func isTextElement(n *astro.Node) bool {
return knownTextElements[n.Data]
}

func collapseWhitespace(doc *astro.Node) {
walk(doc, func(n *astro.Node) {
if n.Type == astro.TextNode {
Expand All @@ -304,10 +360,13 @@ func collapseWhitespace(doc *astro.Node) {

// If the node is only whitespace, clear it
if len(strings.TrimFunc(n.Data, unicode.IsSpace)) == 0 {
// If it's a lone text node, or if it's within a whitespace-insensitive element, clear completely
if (n.PrevSibling == nil && n.NextSibling == nil) || n.Closest(isWhitespaceInsensitiveElement) != nil {
if isTextElement(n) {
// Known textual elements can be handled early
n.Data = " "
} else if (n.PrevSibling == nil && n.NextSibling == nil) || n.Closest(isWhitespaceInsensitiveElement) != nil {
// If it's a lone text node, or if it's within a whitespace-insensitive element, clear completely
n.Data = ""
} else {
} else if n.Parent != nil {
n.Data = " "
}
return
Expand Down
25 changes: 25 additions & 0 deletions internal/transform/transform_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,16 @@ func TestCompactTransform(t *testing.T) {
source: "<div>Click <a>here</a> <span>space</span></div>",
want: "<div>Click <a>here</a> <span>space</span></div>",
},
{
name: "preserve in-between inline elements I",
source: "<p>{'text'} <span>text</span></p>",
want: "<p>{'text'} <span>text</span></p>",
},
{
name: "preserve in-between inline elements II",
source: "<p><span>text</span> <span>text</span></p>",
want: "<p><span>text</span> <span>text</span></p>",
},
{
name: "expression trim first",
source: "<div>{\n() => {\n\t\treturn <span />}}</div>",
Expand Down Expand Up @@ -463,6 +473,21 @@ func TestCompactTransform(t *testing.T) {
source: "<div>{ a + b }</div>",
want: "<div>{a + b}</div>",
},
{
name: "meaningful whitspace regression I",
source: "<a href={``}>\n<span />\n<span />\n<span>{title}</span></a>",
want: "<a href={``}> <span></span> <span></span> <span>{title}</span></a>",
},
{
name: "meaningful whitspace regression I",
source: "<a href={``}>\n<span />\n<span />\n<span>{title}</span></a>",
want: "<a href={``}> <span></span> <span></span> <span>{title}</span></a>",
},
{
name: "meaningful whitespace regression II",
source: "<h2>\n <span>Contributors with this achievement</span>\n <span>Badge</span>\n</h2>",
want: "<h2> <span>Contributors with this achievement</span> <span>Badge</span> </h2>",
},
}
var b strings.Builder
for _, tt := range tests {
Expand Down
4 changes: 4 additions & 0 deletions packages/compiler/test/compact/minify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ test('collapsing', async () => {
assert.match(await minify(`<span> inline </span>`), '$$render`<span> inline </span>`');
assert.match(await minify(`<span>\n inline \t{\t expression \t}</span>`), '$$render`<span>\ninline ${expression}</span>`');
assert.match(await minify(`<span> inline { expression }</span>`), '$$render`<span> inline ${expression}</span>`');
assert.match(
await minify('<div>\n <p>{"text"} <span>text</span></p>\n <p><span>text</span> <span>text</span></p>\n</div>'),
'<div> <p>${"text"} <span>text</span></p> <p><span>text</span> <span>text</span></p> </div>'
);
});

test('space normalization between attributes', async () => {
Expand Down