Skip to content
Merged
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/fix-trailing-whitespace.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@astrojs/compiler": patch
---

Fixes a bug where trailing whitespaces were preserved before `<style>` tags after transformation, in certain cases. Now trailing whitespaces are correctly removed.
1 change: 0 additions & 1 deletion internal/printer/__printer_js__/Mixed_style_siblings.snap
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ const $$Component = $$createComponent(($$result, $$props, $$slots) => {
return $$render`<head>
${$$renderHead($$result)}</head>
<div class="astro-lasntlja"></div>`;
}, undefined, undefined);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ return $$render`<html lang="en" class="astro-hmnnhvcq">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<link rel="icon" type="image/x-icon" href="/favicon.ico">

${$$renderHead($$result)}</head>
<body class="astro-hmnnhvcq">
<main class="astro-hmnnhvcq">
Expand Down
31 changes: 30 additions & 1 deletion internal/transform/transform.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,39 @@ func ExtractStyles(doc *astro.Node, opts *TransformOptions) {
})
// Important! Remove styles from original location *after* walking the doc
for _, style := range doc.Styles {
style.Parent.RemoveChild(style)
removeNodeWithTrailingWhitespace(style)
}
}

// removeNodeWithTrailingWhitespace removes a node and also removes any preceding
// whitespace-only text node if it would become trailing whitespace after removal.
// This prevents orphaned whitespace when extracting style/script tags.
func removeNodeWithTrailingWhitespace(n *astro.Node) {
prev := n.PrevSibling
next := n.NextSibling

// Check if we should remove preceding whitespace:
// 1. There is a previous sibling
// 2. It's a whitespace-only text node
// 3. After removing n, this whitespace would be trailing (no more non-whitespace siblings after)
if prev != nil && prev.Type == astro.TextNode && strings.TrimSpace(prev.Data) == "" {
// Check if there are any non-whitespace siblings after n
hasNonWhitespaceAfter := false
for sib := next; sib != nil; sib = sib.NextSibling {
if sib.Type != astro.TextNode || strings.TrimSpace(sib.Data) != "" {
hasNonWhitespaceAfter = true
break
}
}
// If no non-whitespace content follows, remove the preceding whitespace
if !hasNonWhitespaceAfter {
prev.Parent.RemoveChild(prev)
}
}

n.Parent.RemoveChild(n)
}

func NormalizeSetDirectives(doc *astro.Node, h *handler.Handler) {
var nodes []*astro.Node
var directives []*astro.Attribute
Expand Down
7 changes: 7 additions & 0 deletions internal/transform/transform_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ func TestFullTransform(t *testing.T) {
source: `<Component><h1>Hello world</h1></Component><style>:root{}</style>`,
want: `<Component><h1>Hello world</h1></Component>`,
},

{
name: "Component before html I",
source: `<Navigation /><html><body><h1>Astro</h1></body></html>`,
Expand Down Expand Up @@ -339,7 +340,13 @@ func TestTransformTrailingSpace(t *testing.T) {
source: "<html><body>\n\n\n</body></html>",
want: "<html><body>\n\n\n</body></html>",
},
{
name: "trailing whitespace before style is removed",
source: "<html><head></head><body><slot />\n<style>div { color: red; }</style></body></html>",
want: "<html><head></head><body><slot></slot></body></html>",
},
}

var b strings.Builder
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
Loading