Skip to content

Commit d470e13

Browse files
committed
merge main
2 parents e3b84d5 + 302c379 commit d470e13

File tree

13 files changed

+203
-109
lines changed

13 files changed

+203
-109
lines changed

.changeset/old-jokes-deliver.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte": patch
3+
---
4+
5+
fix: prevent infinite loop when writing to store using shorthand

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

Lines changed: 89 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { get_possible_values } from './gather_possible_values.js';
2-
import { regex_starts_with_whitespace, regex_ends_with_whitespace } from '../../patterns.js';
3-
import { error } from '../../../errors.js';
1+
import { get_possible_values } from './utils.js';
2+
import { regex_starts_with_whitespace, regex_ends_with_whitespace } from '../phases/patterns.js';
3+
import { error } from '../errors.js';
44
import { Stylesheet, Rule } from './Stylesheet.js';
55

66
const NO_MATCH = 'NO_MATCH';
@@ -14,8 +14,6 @@ const NodeExist = /** @type {const} */ ({
1414

1515
/**
1616
* @typedef {typeof NodeExist[keyof typeof NodeExist]} NodeExistsValue
17-
* @typedef {Array<RelativeSelector>} ComplexSelector
18-
* @typedef {Array<ComplexSelector>} SelectorList
1917
* @typedef {import("#compiler").Css.SimpleSelector & { use_wrapper: { used: boolean }, visible: boolean }} SimpleSelectorWithData
2018
* */
2119

@@ -24,23 +22,23 @@ const whitelist_attribute_selector = new Map([
2422
['dialog', new Set(['open'])]
2523
]);
2624

27-
export default class Selector {
28-
/** @type {import('#compiler').Css.Selector} */
25+
export class ComplexSelector {
26+
/** @type {import('#compiler').Css.ComplexSelector} */
2927
node;
3028

3129
/** @type {Stylesheet} */
3230
stylesheet;
3331

34-
/** @type {SelectorList} */
32+
/** @type {RelativeSelector[][]} */
3533
selector_list;
3634

37-
/** @type {SelectorList} */
35+
/** @type {RelativeSelector[][]} */
3836
local_selector_list;
3937

4038
used = false;
4139

4240
/**
43-
* @param {import('#compiler').Css.Selector} node
41+
* @param {import('#compiler').Css.ComplexSelector} node
4442
* @param {Stylesheet} stylesheet
4543
* @param {Rule} rule
4644
*/
@@ -150,7 +148,7 @@ export default class Selector {
150148
}
151149
}
152150

153-
/** @param {import('../../types.js').ComponentAnalysis} analysis */
151+
/** @param {import('../phases/types.js').ComponentAnalysis} analysis */
154152
validate(analysis) {
155153
this.validate_global_placement();
156154
this.validate_global_with_multiple_selectors();
@@ -197,7 +195,7 @@ export default class Selector {
197195
}
198196
}
199197

200-
/** @param {import('../../types.js').ComponentAnalysis} analysis */
198+
/** @param {import('../phases/types.js').ComponentAnalysis} analysis */
201199
validate_invalid_combinator_without_selector(analysis) {
202200
for (const complex_selector of this.selector_list) {
203201
for (const relative_selector of complex_selector) {
@@ -238,20 +236,22 @@ export default class Selector {
238236
}
239237

240238
/**
241-
* @param {RelativeSelector[]} blocks
239+
* @param {RelativeSelector[]} relative_selectors
242240
* @param {import('#compiler').RegularElement | import('#compiler').SvelteElement | null} node
243241
* @param {Stylesheet} stylesheet
244242
* @returns {boolean}
245243
*/
246-
function apply_selector(blocks, node, stylesheet) {
247-
const block = blocks.pop();
248-
if (!block) return false;
244+
function apply_selector(relative_selectors, node, stylesheet) {
245+
const relative_selector = relative_selectors.pop();
246+
if (!relative_selector) return false;
249247
if (!node) {
250248
return (
251-
(block.global && blocks.every((block) => block.global)) || (block.host && blocks.length === 0)
249+
(relative_selector.global &&
250+
relative_selectors.every((relative_selector) => relative_selector.global)) ||
251+
(relative_selector.host && relative_selectors.length === 0)
252252
);
253253
}
254-
const applies = block_might_apply_to_node(block, node);
254+
const applies = block_might_apply_to_node(relative_selector, node);
255255

256256
if (applies === NO_MATCH) {
257257
return false;
@@ -260,28 +260,31 @@ function apply_selector(blocks, node, stylesheet) {
260260
/**
261261
* Mark both the compound selector and the node it selects as encapsulated,
262262
* for transformation in a later step
263-
* @param {RelativeSelector} block
263+
* @param {RelativeSelector} relative_selector
264264
* @param {import('#compiler').RegularElement | import('#compiler').SvelteElement} node
265265
*/
266-
function mark(block, node) {
267-
block.should_encapsulate = true;
266+
function mark(relative_selector, node) {
267+
relative_selector.should_encapsulate = true;
268268
stylesheet.nodes_with_css_class.add(node);
269269
return true;
270270
}
271271

272272
if (applies === UNKNOWN_SELECTOR) {
273-
return mark(block, node);
273+
return mark(relative_selector, node);
274274
}
275275

276-
if (block.combinator) {
277-
if (block.combinator.type === 'Combinator' && block.combinator.name === ' ') {
278-
for (const ancestor_block of blocks) {
276+
if (relative_selector.combinator) {
277+
if (
278+
relative_selector.combinator.type === 'Combinator' &&
279+
relative_selector.combinator.name === ' '
280+
) {
281+
for (const ancestor_block of relative_selectors) {
279282
if (ancestor_block.global) {
280283
continue;
281284
}
282285

283286
if (ancestor_block.host) {
284-
return mark(block, node);
287+
return mark(relative_selector, node);
285288
}
286289

287290
/** @type {import('#compiler').RegularElement | import('#compiler').SvelteElement | null} */
@@ -294,40 +297,51 @@ function apply_selector(blocks, node, stylesheet) {
294297
}
295298
}
296299
if (matched) {
297-
return mark(block, node);
300+
return mark(relative_selector, node);
298301
}
299302
}
300303

301-
if (blocks.every((block) => block.global)) {
302-
return mark(block, node);
304+
if (relative_selectors.every((relative_selector) => relative_selector.global)) {
305+
return mark(relative_selector, node);
303306
}
304307

305308
return false;
306-
} else if (block.combinator.name === '>') {
307-
const has_global_parent = blocks.every((block) => block.global);
308-
if (has_global_parent || apply_selector(blocks, get_element_parent(node), stylesheet)) {
309-
return mark(block, node);
309+
} else if (relative_selector.combinator.name === '>') {
310+
const has_global_parent = relative_selectors.every(
311+
(relative_selector) => relative_selector.global
312+
);
313+
if (
314+
has_global_parent ||
315+
apply_selector(relative_selectors, get_element_parent(node), stylesheet)
316+
) {
317+
return mark(relative_selector, node);
310318
}
311319

312320
return false;
313-
} else if (block.combinator.name === '+' || block.combinator.name === '~') {
314-
const siblings = get_possible_element_siblings(node, block.combinator.name === '+');
321+
} else if (
322+
relative_selector.combinator.name === '+' ||
323+
relative_selector.combinator.name === '~'
324+
) {
325+
const siblings = get_possible_element_siblings(
326+
node,
327+
relative_selector.combinator.name === '+'
328+
);
315329
let has_match = false;
316330

317331
// NOTE: if we have :global(), we couldn't figure out what is selected within `:global` due to the
318332
// css-tree limitation that does not parse the inner selector of :global
319333
// so unless we are sure there will be no sibling to match, we will consider it as matched
320-
const has_global = blocks.some((block) => block.global);
334+
const has_global = relative_selectors.some((relative_selector) => relative_selector.global);
321335
if (has_global) {
322336
if (siblings.size === 0 && get_element_parent(node) !== null) {
323337
return false;
324338
}
325-
return mark(block, node);
339+
return mark(relative_selector, node);
326340
}
327341

328342
for (const possible_sibling of siblings.keys()) {
329-
if (apply_selector(blocks.slice(), possible_sibling, stylesheet)) {
330-
mark(block, node);
343+
if (apply_selector(relative_selectors.slice(), possible_sibling, stylesheet)) {
344+
mark(relative_selector, node);
331345
has_match = true;
332346
}
333347
}
@@ -336,25 +350,25 @@ function apply_selector(blocks, node, stylesheet) {
336350
}
337351

338352
// TODO other combinators
339-
return mark(block, node);
353+
return mark(relative_selector, node);
340354
}
341355

342-
return mark(block, node);
356+
return mark(relative_selector, node);
343357
}
344358

345359
const regex_backslash_and_following_character = /\\(.)/g;
346360

347361
/**
348-
* @param {RelativeSelector} block
362+
* @param {RelativeSelector} relative_selector
349363
* @param {import('#compiler').RegularElement | import('#compiler').SvelteElement} node
350364
* @returns {NO_MATCH | POSSIBLE_MATCH | UNKNOWN_SELECTOR}
351365
*/
352-
function block_might_apply_to_node(block, node) {
353-
if (block.host || block.root) return NO_MATCH;
366+
function block_might_apply_to_node(relative_selector, node) {
367+
if (relative_selector.host || relative_selector.root) return NO_MATCH;
354368

355-
let i = block.compound.selectors.length;
369+
let i = relative_selector.compound.selectors.length;
356370
while (i--) {
357-
const selector = block.compound.selectors[i];
371+
const selector = relative_selector.compound.selectors[i];
358372

359373
if (selector.type === 'Percentage' || selector.type === 'Nth') continue;
360374

@@ -364,7 +378,7 @@ function block_might_apply_to_node(block, node) {
364378
return NO_MATCH;
365379
}
366380
if (
367-
block.compound.selectors.length === 1 &&
381+
relative_selector.compound.selectors.length === 1 &&
368382
selector.type === 'PseudoClassSelector' &&
369383
name === 'global'
370384
) {
@@ -683,22 +697,22 @@ function get_possible_element_siblings(node, adjacent_only) {
683697
}
684698

685699
/**
686-
* @param {import('#compiler').EachBlock | import('#compiler').IfBlock | import('#compiler').AwaitBlock} block
700+
* @param {import('#compiler').EachBlock | import('#compiler').IfBlock | import('#compiler').AwaitBlock} relative_selector
687701
* @param {boolean} adjacent_only
688702
* @returns {Map<import('#compiler').RegularElement, NodeExistsValue>}
689703
*/
690-
function get_possible_last_child(block, adjacent_only) {
704+
function get_possible_last_child(relative_selector, adjacent_only) {
691705
/** @typedef {Map<import('#compiler').RegularElement, NodeExistsValue>} NodeMap */
692706

693707
/** @type {NodeMap} */
694708
const result = new Map();
695-
if (block.type === 'EachBlock') {
709+
if (relative_selector.type === 'EachBlock') {
696710
/** @type {NodeMap} */
697-
const each_result = loop_child(block.body.nodes, adjacent_only);
711+
const each_result = loop_child(relative_selector.body.nodes, adjacent_only);
698712

699713
/** @type {NodeMap} */
700-
const else_result = block.fallback
701-
? loop_child(block.fallback.nodes, adjacent_only)
714+
const else_result = relative_selector.fallback
715+
? loop_child(relative_selector.fallback.nodes, adjacent_only)
702716
: new Map();
703717
const not_exhaustive = !has_definite_elements(else_result);
704718
if (not_exhaustive) {
@@ -707,13 +721,13 @@ function get_possible_last_child(block, adjacent_only) {
707721
}
708722
add_to_map(each_result, result);
709723
add_to_map(else_result, result);
710-
} else if (block.type === 'IfBlock') {
724+
} else if (relative_selector.type === 'IfBlock') {
711725
/** @type {NodeMap} */
712-
const if_result = loop_child(block.consequent.nodes, adjacent_only);
726+
const if_result = loop_child(relative_selector.consequent.nodes, adjacent_only);
713727

714728
/** @type {NodeMap} */
715-
const else_result = block.alternate
716-
? loop_child(block.alternate.nodes, adjacent_only)
729+
const else_result = relative_selector.alternate
730+
? loop_child(relative_selector.alternate.nodes, adjacent_only)
717731
: new Map();
718732
const not_exhaustive = !has_definite_elements(if_result) || !has_definite_elements(else_result);
719733
if (not_exhaustive) {
@@ -722,17 +736,21 @@ function get_possible_last_child(block, adjacent_only) {
722736
}
723737
add_to_map(if_result, result);
724738
add_to_map(else_result, result);
725-
} else if (block.type === 'AwaitBlock') {
739+
} else if (relative_selector.type === 'AwaitBlock') {
726740
/** @type {NodeMap} */
727-
const pending_result = block.pending
728-
? loop_child(block.pending.nodes, adjacent_only)
741+
const pending_result = relative_selector.pending
742+
? loop_child(relative_selector.pending.nodes, adjacent_only)
729743
: new Map();
730744

731745
/** @type {NodeMap} */
732-
const then_result = block.then ? loop_child(block.then.nodes, adjacent_only) : new Map();
746+
const then_result = relative_selector.then
747+
? loop_child(relative_selector.then.nodes, adjacent_only)
748+
: new Map();
733749

734750
/** @type {NodeMap} */
735-
const catch_result = block.catch ? loop_child(block.catch.nodes, adjacent_only) : new Map();
751+
const catch_result = relative_selector.catch
752+
? loop_child(relative_selector.catch.nodes, adjacent_only)
753+
: new Map();
736754
const not_exhaustive =
737755
!has_definite_elements(pending_result) ||
738756
!has_definite_elements(then_result) ||
@@ -933,9 +951,9 @@ class CompoundSelector {
933951
/**
934952
* Groups selectors and inserts parent blocks into nested rules.
935953
*
936-
* @param {import('#compiler').Css.Selector} selector - The selector to group and analyze.
954+
* @param {import('#compiler').Css.ComplexSelector} selector - The selector to group and analyze.
937955
* @param {Rule} rule
938-
* @returns {SelectorList} - The grouped selectors with parent's blocks inserted if nested.
956+
* @returns {RelativeSelector[][]} - The grouped selectors with parent's blocks inserted if nested.
939957
*/
940958
function group_selectors(selector, rule) {
941959
// TODO this logic isn't quite right, as it doesn't properly account for atrules
@@ -945,21 +963,19 @@ function group_selectors(selector, rule) {
945963
.flat();
946964

947965
return parent_selector_list.map((parent_complex_selector) => {
948-
const block_group = selector_to_blocks(
966+
return selector_to_blocks(
949967
[...selector.children],
950968
[...parent_complex_selector] // Clone the parent's blocks to avoid modifying the original array
951969
);
952-
953-
return block_group;
954970
});
955971
}
956972

957973
return [selector_to_blocks([...selector.children], null)];
958974
}
959975

960976
/**
961-
* @param {import('#compiler').Css.Selector["children"]} children
962-
* @param {ComplexSelector | null} parent_complex_selector - The parent rule's selectors to insert/swap into the nesting selector positions.
977+
* @param {import('#compiler').Css.ComplexSelector["children"]} children
978+
* @param {RelativeSelector[] | null} parent_complex_selector - The parent rule's selectors to insert/swap into the nesting selector positions.
963979
*/
964980
function selector_to_blocks(children, parent_complex_selector) {
965981
let block = new RelativeSelector(null, new CompoundSelector());
@@ -997,8 +1013,8 @@ function selector_to_blocks(children, parent_complex_selector) {
9971013
}
9981014

9991015
/**
1000-
* @param {ComplexSelector} parent_complex_selector - The parent blocks to insert into the nesting selector positions.
1001-
* @returns {import('#compiler').Css.Selector["children"]} - The parent selectors to insert into the nesting selector positions.
1016+
* @param {RelativeSelector[]} parent_complex_selector - The parent blocks to insert into the nesting selector positions.
1017+
* @returns {import('#compiler').Css.ComplexSelector["children"]} - The parent selectors to insert into the nesting selector positions.
10021018
*/
10031019
function get_parent_selectors(parent_complex_selector) {
10041020
const parent_selectors = [];
@@ -1027,8 +1043,8 @@ function get_parent_selectors(parent_complex_selector) {
10271043
* b { c & { color: red }} -> so we need to insert ' ' after c so children needs to look like [c, " ",b]
10281044
* .x { & { color: red }} -> no combinator, so children needs to look like .x.x
10291045
*
1030-
* @param {import('#compiler').Css.Selector["children"]} children
1031-
* @param {ComplexSelector} parent_complex_selector - The parent blocks to insert into the nesting selector positions.
1046+
* @param {import('#compiler').Css.ComplexSelector["children"]} children
1047+
* @param {RelativeSelector[]} parent_complex_selector - The parent blocks to insert into the nesting selector positions.
10321048
*/
10331049
function nest_fake_parents(children, parent_complex_selector) {
10341050
const nested_selector_indexes = children.reduce((indexes, child, index) => {

0 commit comments

Comments
 (0)