Skip to content

Commit

Permalink
fix: css pruning producing invalid css (#14448)
Browse files Browse the repository at this point in the history
* fix: css pruning producing invalid css

* Update .changeset/big-hats-wonder.md

---------

Co-authored-by: Rich Harris <rich.harris@vercel.com>
  • Loading branch information
paoloricciuti and Rich-Harris authored Nov 26, 2024
1 parent 4f318e6 commit 19d80ad
Show file tree
Hide file tree
Showing 8 changed files with 211 additions and 13 deletions.
5 changes: 5 additions & 0 deletions .changeset/big-hats-wonder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': patch
---

fix: correctly remove unused selectors in middle of selector lists
18 changes: 8 additions & 10 deletions packages/svelte/src/compiler/phases/3-transform/css/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ const visitors = {
let pruning = false;
let prune_start = children[0].start;
let last = prune_start;
let has_previous_used = false;

for (let i = 0; i < children.length; i += 1) {
const selector = children[i];
Expand All @@ -210,9 +211,9 @@ const visitors = {
while (state.code.original[i] !== ',') i--;

if (state.minify) {
state.code.remove(prune_start, i + 1);
state.code.remove(prune_start, has_previous_used ? i : i + 1);
} else {
state.code.overwrite(i, i + 1, '*/');
state.code.appendRight(has_previous_used ? i : i + 1, '*/');
}
} else {
if (i === 0) {
Expand All @@ -222,24 +223,21 @@ const visitors = {
state.code.prependRight(selector.start, '/* (unused) ');
}
} else {
// If this is not the last selector add a separator
const separator = i !== children.length - 1 ? ',' : '';

if (state.minify) {
prune_start = last;
if (separator) {
while (state.code.original[prune_start - 1] !== ',') prune_start++;
state.code.update(last, prune_start, separator);
}
} else {
state.code.overwrite(last, selector.start, `${separator} /* (unused) `);
state.code.overwrite(last, selector.start, ` /* (unused) `);
}
}
}

pruning = !pruning;
}

if (!pruning && selector.metadata.used) {
has_previous_used = true;
}

last = selector.end;
}

Expand Down
2 changes: 1 addition & 1 deletion packages/svelte/tests/css/samples/has/expected.css
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
/* (unused) .unused x:has(y) {
color: red;
}*/
/* (unused) .unused:has(.unused)*/ x.svelte-xyz:has(y:where(.svelte-xyz)) {
/* (unused) .unused:has(.unused),*/ x.svelte-xyz:has(y:where(.svelte-xyz)) {
color: green;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

h1.svelte-xyz,
h2.svelte-xyz,
h3.svelte-xyz, /* (unused) h4*/
h3.svelte-xyz /* (unused) h4*/,
p.svelte-xyz {
color: red;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/* (unused) .foo*/ .bar.svelte-xyz /* (unused) .baz*/ {
/* (unused) .foo,*/ .bar.svelte-xyz /* (unused) .baz*/ {
color: red;
}
160 changes: 160 additions & 0 deletions packages/svelte/tests/css/samples/unused-selector-multiple/_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import { test } from '../../test';

export default test({
warnings: [
{
code: 'a11y_missing_content',
message: '`<h1>` element should contain text',
start: {
line: 1,
column: 0,
character: 0
},
end: {
line: 1,
column: 9,
character: 9
}
},
{
code: 'a11y_missing_content',
message: '`<h4>` element should contain text',
start: {
line: 2,
column: 0,
character: 10
},
end: {
line: 2,
column: 9,
character: 19
}
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector "h2"',
start: {
line: 6,
column: 5,
character: 35
},
end: {
line: 6,
column: 7,
character: 37
}
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector "h3"',
start: {
line: 6,
column: 9,
character: 39
},
end: {
line: 6,
column: 11,
character: 41
}
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector "h2"',
start: {
line: 9,
column: 5,
character: 66
},
end: {
line: 9,
column: 7,
character: 68
}
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector "h2"',
start: {
line: 13,
column: 5,
character: 110
},
end: {
line: 13,
column: 7,
character: 112
}
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector "h3"',
start: {
line: 13,
column: 9,
character: 114
},
end: {
line: 13,
column: 11,
character: 116
}
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector "h2"',
start: {
line: 17,
column: 5,
character: 161
},
end: {
line: 17,
column: 7,
character: 163
}
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector "h3"',
start: {
line: 17,
column: 9,
character: 165
},
end: {
line: 17,
column: 11,
character: 167
}
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector "h5"',
start: {
line: 17,
column: 17,
character: 173
},
end: {
line: 17,
column: 19,
character: 175
}
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector "h6"',
start: {
line: 17,
column: 21,
character: 177
},
end: {
line: 17,
column: 23,
character: 179
}
}
]
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

h1.svelte-xyz /* (unused) h2, h3*/ {
color: red;
}
h1.svelte-xyz /* (unused) h2*/ {
text-decoration: underline;
}

h1.svelte-xyz /* (unused) h2, h3*/, h4.svelte-xyz {
text-transform: uppercase;
}

h1.svelte-xyz /* (unused) h2, h3*/, h4.svelte-xyz /* (unused) h5, h6*/ {
background-color: green;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<h1></h1>
<h4></h4>


<style>
h1, h2, h3 {
color: red;
}
h1, h2 {
text-decoration: underline;
}
h1, h2, h3, h4 {
text-transform: uppercase;
}
h1, h2, h3, h4, h5, h6 {
background-color: green;
}
</style>

0 comments on commit 19d80ad

Please sign in to comment.