Skip to content

Commit 9f2a257

Browse files
committed
beefier static analysis
1 parent 7840a2c commit 9f2a257

File tree

4 files changed

+60
-11
lines changed

4 files changed

+60
-11
lines changed

packages/svelte/src/compiler/phases/2-analyze/css/utils.js

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,56 @@ const UNKNOWN = {};
66
* @param {Node} node
77
* @param {boolean} is_class
88
* @param {Set<any>} set
9+
* @param {boolean} is_nested
910
*/
10-
function gather_possible_values(node, is_class, set) {
11+
function gather_possible_values(node, is_class, set, is_nested = false) {
12+
if (set.has(UNKNOWN)) {
13+
// no point traversing any further
14+
return;
15+
}
16+
1117
if (node.type === 'Literal') {
1218
set.add(String(node.value));
1319
} else if (node.type === 'ConditionalExpression') {
14-
gather_possible_values(node.consequent, is_class, set);
15-
gather_possible_values(node.alternate, is_class, set);
20+
gather_possible_values(node.consequent, is_class, set, is_nested);
21+
gather_possible_values(node.alternate, is_class, set, is_nested);
22+
} else if (node.type === 'LogicalExpression') {
23+
if (node.operator === '&&') {
24+
// && is a special case, because the only way the left
25+
// hand value can be included is if it's falsy. this is
26+
// a bit of extra work but it's worth it because
27+
// `class={[condition && 'blah']}` is common,
28+
// and we don't want to deopt on `condition`
29+
const left = new Set();
30+
gather_possible_values(node.left, is_class, left, is_nested);
31+
32+
if (left.has(UNKNOWN)) {
33+
// add all non-nullish falsy values, unless this is a `class` attribute that
34+
// will be processed by cslx, in which case falsy values are removed, unless
35+
// they're not inside an array/object (TODO 6.0 remove that last part)
36+
if (!is_class || !is_nested) {
37+
set.add('');
38+
set.add(false);
39+
set.add(NaN);
40+
set.add(0); // -0 and 0n are also falsy, but stringify to '0'
41+
}
42+
} else {
43+
for (const value of left) {
44+
if (!value) {
45+
set.add(value);
46+
}
47+
}
48+
}
49+
50+
gather_possible_values(node.right, is_class, set, is_nested);
51+
} else {
52+
gather_possible_values(node.left, is_class, set, is_nested);
53+
gather_possible_values(node.right, is_class, set, is_nested);
54+
}
1655
} else if (is_class && node.type === 'ArrayExpression') {
1756
for (const entry of node.elements) {
1857
if (entry) {
19-
gather_possible_values(entry, is_class, set);
20-
} else {
21-
set.add(UNKNOWN);
58+
gather_possible_values(entry, is_class, set, true);
2259
}
2360
}
2461
} else if (is_class && node.type === 'ObjectExpression') {
@@ -43,7 +80,7 @@ function gather_possible_values(node, is_class, set) {
4380
/**
4481
* @param {AST.Text | AST.ExpressionTag} chunk
4582
* @param {boolean} is_class
46-
* @returns {Set<string> | null}
83+
* @returns {Set<string | number | boolean> | null}
4784
*/
4885
export function get_possible_values(chunk, is_class) {
4986
const values = new Set();

packages/svelte/tests/css/samples/clsx-can-prune/_config.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ export default test({
66
code: 'css_unused_selector',
77
message: 'Unused CSS selector ".unused"\nhttps://svelte.dev/e/css_unused_selector',
88
start: {
9-
line: 15,
9+
line: 24,
1010
column: 1,
11-
character: 325
11+
character: 548
1212
},
1313
end: {
14-
line: 15,
14+
line: 24,
1515
column: 8,
16-
character: 332
16+
character: 555
1717
}
1818
}
1919
]

packages/svelte/tests/css/samples/clsx-can-prune/expected.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,8 @@
55
.used4.svelte-xyz { color: green; }
66
.used5.svelte-xyz { color: green; }
77
.used6.svelte-xyz { color: green; }
8+
.used7.svelte-xyz { color: green; }
9+
.used8.svelte-xyz { color: green; }
10+
.used9.svelte-xyz { color: green; }
811

912
/* (unused) .unused { color: red; }*/

packages/svelte/tests/css/samples/clsx-can-prune/input.svelte

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1+
<script>
2+
let condition = Math.random() < 0.5;
3+
</script>
4+
15
<p class={['used1']}></p>
26
<p class={[{ used2: true }]}></p>
37
<p class={{ used3: true }}></p>
48
<p class={{ 'used4 used5': true }}></p>
59
<p class={{ used6 }}></p>
10+
<p class={[condition ? 'used7' : 'used8']}></p>
11+
<p class={[condition && 'used9']}></p>
612

713
<style>
814
.used1 { color: green; }
@@ -11,6 +17,9 @@
1117
.used4 { color: green; }
1218
.used5 { color: green; }
1319
.used6 { color: green; }
20+
.used7 { color: green; }
21+
.used8 { color: green; }
22+
.used9 { color: green; }
1423
1524
.unused { color: red; }
1625
</style>

0 commit comments

Comments
 (0)