diff --git a/.changeset/fair-laws-appear.md b/.changeset/fair-laws-appear.md new file mode 100644 index 000000000000..9a1149ff279d --- /dev/null +++ b/.changeset/fair-laws-appear.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: match class and style directives against attribute selector diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js index fbe6ca1cd379..b9a5688a87d0 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js @@ -532,12 +532,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element, } case 'ClassSelector': { - if ( - !attribute_matches(element, 'class', name, '~=', false) && - !element.attributes.some( - (attribute) => attribute.type === 'ClassDirective' && attribute.name === name - ) - ) { + if (!attribute_matches(element, 'class', name, '~=', false)) { return false; } @@ -633,6 +628,16 @@ function attribute_matches(node, name, expected_value, operator, case_insensitiv if (attribute.type === 'SpreadAttribute') return true; if (attribute.type === 'BindDirective' && attribute.name === name) return true; + // match attributes against the corresponding directive but bail out on exact matching + if (attribute.type === 'StyleDirective' && name.toLowerCase() === 'style') return true; + if (attribute.type === 'ClassDirective' && name.toLowerCase() === 'class') { + if (operator == '~=') { + if (attribute.name === expected_value) return true; + } else { + return true; + } + } + if (attribute.type !== 'Attribute') continue; if (attribute.name.toLowerCase() !== name.toLowerCase()) continue; diff --git a/packages/svelte/tests/css/samples/attribute-selector-matches-derictive/expected.css b/packages/svelte/tests/css/samples/attribute-selector-matches-derictive/expected.css new file mode 100644 index 000000000000..4b5e4bfd091f --- /dev/null +++ b/packages/svelte/tests/css/samples/attribute-selector-matches-derictive/expected.css @@ -0,0 +1,2 @@ + span[class].svelte-xyz { color: green } + div[style].svelte-xyz { color: green } \ No newline at end of file diff --git a/packages/svelte/tests/css/samples/attribute-selector-matches-derictive/input.svelte b/packages/svelte/tests/css/samples/attribute-selector-matches-derictive/input.svelte new file mode 100644 index 000000000000..2f9ab202ca80 --- /dev/null +++ b/packages/svelte/tests/css/samples/attribute-selector-matches-derictive/input.svelte @@ -0,0 +1,7 @@ + +
+ + diff --git a/packages/svelte/tests/css/samples/class-directive/_config.js b/packages/svelte/tests/css/samples/class-directive/_config.js new file mode 100644 index 000000000000..28e9fbc81512 --- /dev/null +++ b/packages/svelte/tests/css/samples/class-directive/_config.js @@ -0,0 +1,20 @@ +import { test } from '../../test'; + +export default test({ + warnings: [ + { + code: 'css_unused_selector', + message: 'Unused CSS selector ".third"\nhttps://svelte.dev/e/css_unused_selector', + start: { + line: 6, + column: 2, + character: 115 + }, + end: { + line: 6, + column: 8, + character: 121 + } + } + ] +}); diff --git a/packages/svelte/tests/css/samples/class-directive/expected.css b/packages/svelte/tests/css/samples/class-directive/expected.css new file mode 100644 index 000000000000..1d7d3d4dee61 --- /dev/null +++ b/packages/svelte/tests/css/samples/class-directive/expected.css @@ -0,0 +1,3 @@ + .first.svelte-xyz { color: green } + .second.svelte-xyz { color: green } + /* (unused) .third { color: red }*/ diff --git a/packages/svelte/tests/css/samples/class-directive/input.svelte b/packages/svelte/tests/css/samples/class-directive/input.svelte new file mode 100644 index 000000000000..cf0033596415 --- /dev/null +++ b/packages/svelte/tests/css/samples/class-directive/input.svelte @@ -0,0 +1,7 @@ + + + \ No newline at end of file