Skip to content

Commit 87d4b12

Browse files
authored
chore: tidy up CSS stuff (#10478)
Co-authored-by: Rich Harris <[email protected]>
1 parent 643edc3 commit 87d4b12

File tree

10 files changed

+111
-110
lines changed

10 files changed

+111
-110
lines changed

packages/svelte/src/compiler/phases/1-parse/index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export class Parser {
4747
throw new TypeError('Template must be a string');
4848
}
4949

50-
this.template = template.trimRight();
50+
this.template = template.trimEnd();
5151

5252
let match_lang;
5353

@@ -147,9 +147,9 @@ export class Parser {
147147

148148
/**
149149
* @param {string} str
150-
* @param {boolean} [required]
150+
* @param {boolean} required
151151
*/
152-
eat(str, required) {
152+
eat(str, required = false) {
153153
if (this.match(str)) {
154154
this.index += str.length;
155155
return true;

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

Lines changed: 54 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { get_possible_values } from './gather_possible_values.js';
22
import { regex_starts_with_whitespace, regex_ends_with_whitespace } from '../../patterns.js';
33
import { error } from '../../../errors.js';
4+
import { Stylesheet } from './Stylesheet.js';
45

56
const NO_MATCH = 'NO_MATCH';
67
const POSSIBLE_MATCH = 'POSSIBLE_MATCH';
@@ -22,7 +23,7 @@ export default class Selector {
2223
/** @type {import('#compiler').Css.Selector} */
2324
node;
2425

25-
/** @type {import('./Stylesheet.js').default} */
26+
/** @type {import('./Stylesheet.js').Stylesheet} */
2627
stylesheet;
2728

2829
/** @type {Block[]} */
@@ -31,39 +32,28 @@ export default class Selector {
3132
/** @type {Block[]} */
3233
local_blocks;
3334

34-
/** @type {boolean} */
35-
used;
35+
used = false;
3636

3737
/**
3838
* @param {import('#compiler').Css.Selector} node
39-
* @param {import('./Stylesheet.js').default} stylesheet
39+
* @param {import('./Stylesheet.js').Stylesheet} stylesheet
4040
*/
4141
constructor(node, stylesheet) {
4242
this.node = node;
4343
this.stylesheet = stylesheet;
4444
this.blocks = group_selectors(node);
4545
// take trailing :global(...) selectors out of consideration
46-
let i = this.blocks.length;
47-
while (i > 0) {
48-
if (!this.blocks[i - 1].global) break;
49-
i -= 1;
50-
}
51-
this.local_blocks = this.blocks.slice(0, i);
52-
const host_only = this.blocks.length === 1 && this.blocks[0].host;
53-
const root_only = this.blocks.length === 1 && this.blocks[0].root;
54-
this.used = this.local_blocks.length === 0 || host_only || root_only;
46+
const i = this.blocks.findLastIndex((block) => !block.can_ignore());
47+
this.local_blocks = this.blocks.slice(0, i + 1);
48+
49+
// if we have a `:root {...}` or `:global(...) {...}` selector, we need to mark
50+
// this selector as `used` even if the component doesn't contain any nodes
51+
this.used = this.local_blocks.length === 0;
5552
}
5653

5754
/** @param {import('#compiler').RegularElement | import('#compiler').SvelteElement} node */
5855
apply(node) {
59-
/** @type {Array<{ node: import('#compiler').RegularElement | import('#compiler').SvelteElement; block: Block }>} */
60-
const to_encapsulate = [];
61-
apply_selector(this.local_blocks.slice(), node, to_encapsulate);
62-
if (to_encapsulate.length > 0) {
63-
to_encapsulate.forEach(({ node, block }) => {
64-
this.stylesheet.nodes_with_css_class.add(node);
65-
block.should_encapsulate = true;
66-
});
56+
if (apply_selector(this.local_blocks.slice(), node, this.stylesheet)) {
6757
this.used = true;
6858
}
6959
}
@@ -130,6 +120,13 @@ export default class Selector {
130120

131121
/** @param {import('../../types.js').ComponentAnalysis} analysis */
132122
validate(analysis) {
123+
this.validate_global_placement();
124+
this.validate_global_with_multiple_selectors();
125+
this.validate_global_compound_selector();
126+
this.validate_invalid_combinator_without_selector(analysis);
127+
}
128+
129+
validate_global_placement() {
133130
let start = 0;
134131
let end = this.blocks.length;
135132
for (; start < end; start += 1) {
@@ -143,9 +140,6 @@ export default class Selector {
143140
error(this.blocks[i].selectors[0], 'invalid-css-global-placement');
144141
}
145142
}
146-
this.validate_global_with_multiple_selectors();
147-
this.validate_global_compound_selector();
148-
this.validate_invalid_combinator_without_selector(analysis);
149143
}
150144

151145
validate_global_with_multiple_selectors() {
@@ -207,10 +201,10 @@ export default class Selector {
207201
/**
208202
* @param {Block[]} blocks
209203
* @param {import('#compiler').RegularElement | import('#compiler').SvelteElement | null} node
210-
* @param {Array<{ node: import('#compiler').RegularElement | import('#compiler').SvelteElement; block: Block }>} to_encapsulate
204+
* @param {Stylesheet} stylesheet
211205
* @returns {boolean}
212206
*/
213-
function apply_selector(blocks, node, to_encapsulate) {
207+
function apply_selector(blocks, node, stylesheet) {
214208
const block = blocks.pop();
215209
if (!block) return false;
216210
if (!node) {
@@ -224,43 +218,52 @@ function apply_selector(blocks, node, to_encapsulate) {
224218
return false;
225219
}
226220

227-
if (applies === UNKNOWN_SELECTOR) {
228-
to_encapsulate.push({ node, block });
221+
/**
222+
* Mark both the compound selector and the node it selects as encapsulated,
223+
* for transformation in a later step
224+
* @param {Block} block
225+
* @param {import('#compiler').RegularElement | import('#compiler').SvelteElement} node
226+
*/
227+
function mark(block, node) {
228+
block.should_encapsulate = true;
229+
stylesheet.nodes_with_css_class.add(node);
229230
return true;
230231
}
231232

233+
if (applies === UNKNOWN_SELECTOR) {
234+
return mark(block, node);
235+
}
236+
232237
if (block.combinator) {
233238
if (block.combinator.type === 'Combinator' && block.combinator.name === ' ') {
234239
for (const ancestor_block of blocks) {
235240
if (ancestor_block.global) {
236241
continue;
237242
}
238243
if (ancestor_block.host) {
239-
to_encapsulate.push({ node, block });
240-
return true;
244+
return mark(block, node);
241245
}
242246
/** @type {import('#compiler').RegularElement | import('#compiler').SvelteElement | null} */
243247
let parent = node;
248+
let matched = false;
244249
while ((parent = get_element_parent(parent))) {
245250
if (block_might_apply_to_node(ancestor_block, parent) !== NO_MATCH) {
246-
to_encapsulate.push({ node: parent, block: ancestor_block });
251+
mark(ancestor_block, parent);
252+
matched = true;
247253
}
248254
}
249-
if (to_encapsulate.length) {
250-
to_encapsulate.push({ node, block });
251-
return true;
255+
if (matched) {
256+
return mark(block, node);
252257
}
253258
}
254259
if (blocks.every((block) => block.global)) {
255-
to_encapsulate.push({ node, block });
256-
return true;
260+
return mark(block, node);
257261
}
258262
return false;
259263
} else if (block.combinator.name === '>') {
260264
const has_global_parent = blocks.every((block) => block.global);
261-
if (has_global_parent || apply_selector(blocks, get_element_parent(node), to_encapsulate)) {
262-
to_encapsulate.push({ node, block });
263-
return true;
265+
if (has_global_parent || apply_selector(blocks, get_element_parent(node), stylesheet)) {
266+
return mark(block, node);
264267
}
265268
return false;
266269
} else if (block.combinator.name === '+' || block.combinator.name === '~') {
@@ -274,23 +277,22 @@ function apply_selector(blocks, node, to_encapsulate) {
274277
if (siblings.size === 0 && get_element_parent(node) !== null) {
275278
return false;
276279
}
277-
to_encapsulate.push({ node, block });
278-
return true;
280+
return mark(block, node);
279281
}
280282
for (const possible_sibling of siblings.keys()) {
281-
if (apply_selector(blocks.slice(), possible_sibling, to_encapsulate)) {
282-
to_encapsulate.push({ node, block });
283+
if (apply_selector(blocks.slice(), possible_sibling, stylesheet)) {
284+
mark(block, node);
283285
has_match = true;
284286
}
285287
}
286288
return has_match;
287289
}
290+
288291
// TODO other combinators
289-
to_encapsulate.push({ node, block });
290-
return true;
292+
return mark(block, node);
291293
}
292-
to_encapsulate.push({ node, block });
293-
return true;
294+
295+
return mark(block, node);
294296
}
295297

296298
const regex_backslash_and_following_character = /\\(.)/g;
@@ -815,6 +817,11 @@ class Block {
815817
this.selectors.push(selector);
816818
this.end = selector.end;
817819
}
820+
821+
can_ignore() {
822+
return this.global || this.host || this.root;
823+
}
824+
818825
get global() {
819826
return (
820827
this.selectors.length >= 1 &&

0 commit comments

Comments
 (0)