+
touch me
click me
or me
or me!
diff --git a/test/validator/samples/event-modifiers-invalid-nonpassive/errors.json b/test/validator/samples/event-modifiers-invalid-nonpassive/errors.json
new file mode 100644
index 0000000000..a7e5a2a76c
--- /dev/null
+++ b/test/validator/samples/event-modifiers-invalid-nonpassive/errors.json
@@ -0,0 +1,15 @@
+[{
+ "message": "The 'passive' and 'nonpassive' modifiers cannot be used together",
+ "code": "invalid-event-modifier",
+ "start": {
+ "line": 1,
+ "column": 5,
+ "character": 5
+ },
+ "end": {
+ "line": 1,
+ "column": 51,
+ "character": 51
+ },
+ "pos": 5
+}]
diff --git a/test/validator/samples/event-modifiers-invalid-nonpassive/input.svelte b/test/validator/samples/event-modifiers-invalid-nonpassive/input.svelte
new file mode 100644
index 0000000000..3557aa4b1d
--- /dev/null
+++ b/test/validator/samples/event-modifiers-invalid-nonpassive/input.svelte
@@ -0,0 +1,3 @@
+
+ oops
+
\ No newline at end of file
diff --git a/test/validator/samples/event-modifiers-invalid/errors.json b/test/validator/samples/event-modifiers-invalid/errors.json
index 8be2ca7348..59a142d632 100644
--- a/test/validator/samples/event-modifiers-invalid/errors.json
+++ b/test/validator/samples/event-modifiers-invalid/errors.json
@@ -1,5 +1,5 @@
[{
- "message": "Valid event modifiers are preventDefault, stopPropagation, capture, once, passive or self",
+ "message": "Valid event modifiers are preventDefault, stopPropagation, capture, once, passive, nonpassive or self",
"code": "invalid-event-modifier",
"start": {
"line": 1,
From 967b88186b0919cf53625eaa959dff48e6f8c509 Mon Sep 17 00:00:00 2001
From: Tan Li Hau
Date: Fri, 25 Sep 2020 04:06:32 +0800
Subject: [PATCH 50/60] scope CSS sibling combinators (#5427)
---
CHANGELOG.md | 1 +
src/compiler/compile/Component.ts | 8 +-
src/compiler/compile/css/Selector.ts | 187 +++++++++++++++++-
src/compiler/compile/css/Stylesheet.ts | 18 +-
src/compiler/compile/nodes/Element.ts | 7 +-
.../_config.js | 3 +
.../expected.css | 1 +
.../expected.html | 4 +
.../input.svelte | 41 ++++
.../_config.js | 46 +++++
.../expected.css | 1 +
.../expected.html | 3 +
.../input.svelte | 30 +++
.../_config.js | 3 +
.../expected.css | 1 +
.../expected.html | 4 +
.../input.svelte | 37 ++++
.../_config.js | 18 ++
.../expected.css | 1 +
.../expected.html | 3 +
.../input.svelte | 77 ++++++++
.../_config.js | 18 ++
.../expected.css | 1 +
.../expected.html | 3 +
.../input.svelte | 24 +++
.../_config.js | 3 +
.../expected.css | 1 +
.../expected.html | 15 ++
.../input.svelte | 113 +++++++++++
.../expected.css | 1 +
.../expected.html | 6 +
.../input.svelte | 20 ++
.../_config.js | 18 ++
.../expected.css | 1 +
.../expected.html | 3 +
.../input.svelte | 31 +++
.../_config.js | 18 ++
.../expected.css | 1 +
.../expected.html | 3 +
.../input.svelte | 25 +++
.../general-siblings-combinator-if/_config.js | 46 +++++
.../expected.css | 1 +
.../expected.html | 3 +
.../input.svelte | 31 +++
.../_config.js | 88 +++++++++
.../expected.css | 1 +
.../input.svelte | 30 +++
.../_config.js | 18 ++
.../expected.css | 1 +
.../expected.html | 7 +
.../input.svelte | 17 ++
.../general-siblings-combinator/_config.js | 60 ++++++
.../general-siblings-combinator/expected.css | 1 +
.../general-siblings-combinator/expected.html | 7 +
.../general-siblings-combinator/input.svelte | 24 +++
.../_config.js | 3 +
.../expected.css | 1 +
.../expected.html | 4 +
.../input.svelte | 41 ++++
.../siblings-combinator-await/_config.js | 60 ++++++
.../siblings-combinator-await/expected.css | 1 +
.../siblings-combinator-await/expected.html | 3 +
.../siblings-combinator-await/input.svelte | 30 +++
.../siblings-combinator-each-2/_config.js | 18 ++
.../siblings-combinator-each-2/expected.css | 1 +
.../siblings-combinator-each-2/expected.html | 4 +
.../siblings-combinator-each-2/input.svelte | 38 ++++
.../_config.js | 144 ++++++++++++++
.../expected.css | 1 +
.../expected.html | 3 +
.../input.svelte | 74 +++++++
.../siblings-combinator-each-else/_config.js | 32 +++
.../expected.css | 1 +
.../expected.html | 3 +
.../input.svelte | 24 +++
.../_config.js | 116 +++++++++++
.../expected.css | 1 +
.../expected.html | 15 ++
.../input.svelte | 113 +++++++++++
.../siblings-combinator-each/expected.css | 1 +
.../siblings-combinator-each/expected.html | 6 +
.../siblings-combinator-each/input.svelte | 20 ++
.../_config.js | 18 ++
.../expected.css | 1 +
.../expected.html | 3 +
.../input.svelte | 31 +++
.../_config.js | 18 ++
.../expected.css | 1 +
.../expected.html | 3 +
.../input.svelte | 25 +++
.../samples/siblings-combinator-if/_config.js | 60 ++++++
.../siblings-combinator-if/expected.css | 1 +
.../siblings-combinator-if/expected.html | 3 +
.../siblings-combinator-if/input.svelte | 31 +++
.../siblings-combinator-slot/_config.js | 46 +++++
.../siblings-combinator-slot/expected.css | 1 +
.../siblings-combinator-slot/input.svelte | 24 +++
.../siblings-combinator-star/_config.js | 18 ++
.../siblings-combinator-star/expected.css | 1 +
.../siblings-combinator-star/expected.html | 7 +
.../siblings-combinator-star/input.svelte | 17 ++
.../samples/siblings-combinator/_config.js | 62 ++++++
.../samples/siblings-combinator/expected.css | 1 +
.../samples/siblings-combinator/expected.html | 5 +
.../samples/siblings-combinator/input.svelte | 35 ++++
105 files changed, 2280 insertions(+), 24 deletions(-)
create mode 100644 test/css/samples/general-siblings-combinator-await-not-exhaustive/_config.js
create mode 100644 test/css/samples/general-siblings-combinator-await-not-exhaustive/expected.css
create mode 100644 test/css/samples/general-siblings-combinator-await-not-exhaustive/expected.html
create mode 100644 test/css/samples/general-siblings-combinator-await-not-exhaustive/input.svelte
create mode 100644 test/css/samples/general-siblings-combinator-await/_config.js
create mode 100644 test/css/samples/general-siblings-combinator-await/expected.css
create mode 100644 test/css/samples/general-siblings-combinator-await/expected.html
create mode 100644 test/css/samples/general-siblings-combinator-await/input.svelte
create mode 100644 test/css/samples/general-siblings-combinator-each-2/_config.js
create mode 100644 test/css/samples/general-siblings-combinator-each-2/expected.css
create mode 100644 test/css/samples/general-siblings-combinator-each-2/expected.html
create mode 100644 test/css/samples/general-siblings-combinator-each-2/input.svelte
create mode 100644 test/css/samples/general-siblings-combinator-each-else-nested/_config.js
create mode 100644 test/css/samples/general-siblings-combinator-each-else-nested/expected.css
create mode 100644 test/css/samples/general-siblings-combinator-each-else-nested/expected.html
create mode 100644 test/css/samples/general-siblings-combinator-each-else-nested/input.svelte
create mode 100644 test/css/samples/general-siblings-combinator-each-else/_config.js
create mode 100644 test/css/samples/general-siblings-combinator-each-else/expected.css
create mode 100644 test/css/samples/general-siblings-combinator-each-else/expected.html
create mode 100644 test/css/samples/general-siblings-combinator-each-else/input.svelte
create mode 100644 test/css/samples/general-siblings-combinator-each-nested/_config.js
create mode 100644 test/css/samples/general-siblings-combinator-each-nested/expected.css
create mode 100644 test/css/samples/general-siblings-combinator-each-nested/expected.html
create mode 100644 test/css/samples/general-siblings-combinator-each-nested/input.svelte
create mode 100644 test/css/samples/general-siblings-combinator-each/expected.css
create mode 100644 test/css/samples/general-siblings-combinator-each/expected.html
create mode 100644 test/css/samples/general-siblings-combinator-each/input.svelte
create mode 100644 test/css/samples/general-siblings-combinator-if-not-exhaustive-with-each/_config.js
create mode 100644 test/css/samples/general-siblings-combinator-if-not-exhaustive-with-each/expected.css
create mode 100644 test/css/samples/general-siblings-combinator-if-not-exhaustive-with-each/expected.html
create mode 100644 test/css/samples/general-siblings-combinator-if-not-exhaustive-with-each/input.svelte
create mode 100644 test/css/samples/general-siblings-combinator-if-not-exhaustive/_config.js
create mode 100644 test/css/samples/general-siblings-combinator-if-not-exhaustive/expected.css
create mode 100644 test/css/samples/general-siblings-combinator-if-not-exhaustive/expected.html
create mode 100644 test/css/samples/general-siblings-combinator-if-not-exhaustive/input.svelte
create mode 100644 test/css/samples/general-siblings-combinator-if/_config.js
create mode 100644 test/css/samples/general-siblings-combinator-if/expected.css
create mode 100644 test/css/samples/general-siblings-combinator-if/expected.html
create mode 100644 test/css/samples/general-siblings-combinator-if/input.svelte
create mode 100644 test/css/samples/general-siblings-combinator-slot/_config.js
create mode 100644 test/css/samples/general-siblings-combinator-slot/expected.css
create mode 100644 test/css/samples/general-siblings-combinator-slot/input.svelte
create mode 100644 test/css/samples/general-siblings-combinator-star/_config.js
create mode 100644 test/css/samples/general-siblings-combinator-star/expected.css
create mode 100644 test/css/samples/general-siblings-combinator-star/expected.html
create mode 100644 test/css/samples/general-siblings-combinator-star/input.svelte
create mode 100644 test/css/samples/general-siblings-combinator/_config.js
create mode 100644 test/css/samples/general-siblings-combinator/expected.css
create mode 100644 test/css/samples/general-siblings-combinator/expected.html
create mode 100644 test/css/samples/general-siblings-combinator/input.svelte
create mode 100644 test/css/samples/siblings-combinator-await-not-exhaustive/_config.js
create mode 100644 test/css/samples/siblings-combinator-await-not-exhaustive/expected.css
create mode 100644 test/css/samples/siblings-combinator-await-not-exhaustive/expected.html
create mode 100644 test/css/samples/siblings-combinator-await-not-exhaustive/input.svelte
create mode 100644 test/css/samples/siblings-combinator-await/_config.js
create mode 100644 test/css/samples/siblings-combinator-await/expected.css
create mode 100644 test/css/samples/siblings-combinator-await/expected.html
create mode 100644 test/css/samples/siblings-combinator-await/input.svelte
create mode 100644 test/css/samples/siblings-combinator-each-2/_config.js
create mode 100644 test/css/samples/siblings-combinator-each-2/expected.css
create mode 100644 test/css/samples/siblings-combinator-each-2/expected.html
create mode 100644 test/css/samples/siblings-combinator-each-2/input.svelte
create mode 100644 test/css/samples/siblings-combinator-each-else-nested/_config.js
create mode 100644 test/css/samples/siblings-combinator-each-else-nested/expected.css
create mode 100644 test/css/samples/siblings-combinator-each-else-nested/expected.html
create mode 100644 test/css/samples/siblings-combinator-each-else-nested/input.svelte
create mode 100644 test/css/samples/siblings-combinator-each-else/_config.js
create mode 100644 test/css/samples/siblings-combinator-each-else/expected.css
create mode 100644 test/css/samples/siblings-combinator-each-else/expected.html
create mode 100644 test/css/samples/siblings-combinator-each-else/input.svelte
create mode 100644 test/css/samples/siblings-combinator-each-nested/_config.js
create mode 100644 test/css/samples/siblings-combinator-each-nested/expected.css
create mode 100644 test/css/samples/siblings-combinator-each-nested/expected.html
create mode 100644 test/css/samples/siblings-combinator-each-nested/input.svelte
create mode 100644 test/css/samples/siblings-combinator-each/expected.css
create mode 100644 test/css/samples/siblings-combinator-each/expected.html
create mode 100644 test/css/samples/siblings-combinator-each/input.svelte
create mode 100644 test/css/samples/siblings-combinator-if-not-exhaustive-with-each/_config.js
create mode 100644 test/css/samples/siblings-combinator-if-not-exhaustive-with-each/expected.css
create mode 100644 test/css/samples/siblings-combinator-if-not-exhaustive-with-each/expected.html
create mode 100644 test/css/samples/siblings-combinator-if-not-exhaustive-with-each/input.svelte
create mode 100644 test/css/samples/siblings-combinator-if-not-exhaustive/_config.js
create mode 100644 test/css/samples/siblings-combinator-if-not-exhaustive/expected.css
create mode 100644 test/css/samples/siblings-combinator-if-not-exhaustive/expected.html
create mode 100644 test/css/samples/siblings-combinator-if-not-exhaustive/input.svelte
create mode 100644 test/css/samples/siblings-combinator-if/_config.js
create mode 100644 test/css/samples/siblings-combinator-if/expected.css
create mode 100644 test/css/samples/siblings-combinator-if/expected.html
create mode 100644 test/css/samples/siblings-combinator-if/input.svelte
create mode 100644 test/css/samples/siblings-combinator-slot/_config.js
create mode 100644 test/css/samples/siblings-combinator-slot/expected.css
create mode 100644 test/css/samples/siblings-combinator-slot/input.svelte
create mode 100644 test/css/samples/siblings-combinator-star/_config.js
create mode 100644 test/css/samples/siblings-combinator-star/expected.css
create mode 100644 test/css/samples/siblings-combinator-star/expected.html
create mode 100644 test/css/samples/siblings-combinator-star/input.svelte
create mode 100644 test/css/samples/siblings-combinator/_config.js
create mode 100644 test/css/samples/siblings-combinator/expected.css
create mode 100644 test/css/samples/siblings-combinator/expected.html
create mode 100644 test/css/samples/siblings-combinator/input.svelte
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0938d627e5..a18cfac6c3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
## Unreleased
* Add `|nonpassive` event modifier, explicitly passing `passive: false` ([#2068](https://github.com/sveltejs/svelte/issues/2068))
+* Scope CSS selectors with `~` and `+` combinators ([#3104](https://github.com/sveltejs/svelte/issues/3104))
* Fix keyed `{#each}` not reacting to key changing ([#5444](https://github.com/sveltejs/svelte/issues/5444))
* Fix destructuring into store values ([#5449](https://github.com/sveltejs/svelte/issues/5449))
* Fix erroneous `missing-declaration` warning with `use:obj.method` ([#5451](https://github.com/sveltejs/svelte/issues/5451))
diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts
index 078ecb8869..6c1fe89823 100644
--- a/src/compiler/compile/Component.ts
+++ b/src/compiler/compile/Component.ts
@@ -29,6 +29,7 @@ import add_to_set from './utils/add_to_set';
import check_graph_for_cycles from './utils/check_graph_for_cycles';
import { print, x, b } from 'code-red';
import { is_reserved_keyword } from './utils/reserved_keywords';
+import Element from './nodes/Element';
interface ComponentOptions {
namespace?: string;
@@ -85,6 +86,7 @@ export default class Component {
file: string;
locate: (c: number) => { line: number; column: number };
+ elements: Element[] = [];
stylesheet: Stylesheet;
aliases: Map = new Map();
@@ -171,8 +173,8 @@ export default class Component {
this.walk_instance_js_post_template();
+ this.elements.forEach(element => this.stylesheet.apply(element));
if (!compile_options.customElement) this.stylesheet.reify();
-
this.stylesheet.warn_on_unused_selectors(this);
}
@@ -221,6 +223,10 @@ export default class Component {
return this.aliases.get(name);
}
+ apply_stylesheet(element: Element) {
+ this.elements.push(element);
+ }
+
global(name: string) {
const alias = this.alias(name);
this.globals.set(name, alias);
diff --git a/src/compiler/compile/css/Selector.ts b/src/compiler/compile/css/Selector.ts
index c45cda57e9..c141f6b481 100644
--- a/src/compiler/compile/css/Selector.ts
+++ b/src/compiler/compile/css/Selector.ts
@@ -4,12 +4,20 @@ import { gather_possible_values, UNKNOWN } from './gather_possible_values';
import { CssNode } from './interfaces';
import Component from '../Component';
import Element from '../nodes/Element';
+import { INode } from '../nodes/interfaces';
+import EachBlock from '../nodes/EachBlock';
+import IfBlock from '../nodes/IfBlock';
+import AwaitBlock from '../nodes/AwaitBlock';
enum BlockAppliesToNode {
NotPossible,
Possible,
UnknownSelectorType
}
+enum NodeExist {
+ Probably = 1,
+ Definitely = 2,
+}
const whitelist_attribute_selector = new Map([
['details', new Set(['open'])]
@@ -39,10 +47,10 @@ export default class Selector {
this.used = this.local_blocks.length === 0;
}
- apply(node: Element, stack: Element[]) {
+ apply(node: Element) {
const to_encapsulate: any[] = [];
- apply_selector(this.local_blocks.slice(), node, stack.slice(), to_encapsulate);
+ apply_selector(this.local_blocks.slice(), node, to_encapsulate);
if (to_encapsulate.length > 0) {
to_encapsulate.forEach(({ node, block }) => {
@@ -149,7 +157,7 @@ export default class Selector {
}
}
-function apply_selector(blocks: Block[], node: Element, stack: Element[], to_encapsulate: any[]): boolean {
+function apply_selector(blocks: Block[], node: Element, to_encapsulate: any[]): boolean {
const block = blocks.pop();
if (!block) return false;
@@ -162,7 +170,7 @@ function apply_selector(blocks: Block[], node: Element, stack: Element[], to_enc
return false;
case BlockAppliesToNode.UnknownSelectorType:
- // bail. TODO figure out what these could be
+ // bail. TODO figure out what these could be
to_encapsulate.push({ node, block });
return true;
}
@@ -174,9 +182,10 @@ function apply_selector(blocks: Block[], node: Element, stack: Element[], to_enc
continue;
}
- for (const stack_node of stack) {
- if (block_might_apply_to_node(ancestor_block, stack_node) !== BlockAppliesToNode.NotPossible) {
- to_encapsulate.push({ node: stack_node, block: ancestor_block });
+ let parent = node;
+ while (parent = get_element_parent(parent)) {
+ if (block_might_apply_to_node(ancestor_block, parent) !== BlockAppliesToNode.NotPossible) {
+ to_encapsulate.push({ node: parent, block: ancestor_block });
}
}
@@ -193,12 +202,22 @@ function apply_selector(blocks: Block[], node: Element, stack: Element[], to_enc
return false;
} else if (block.combinator.name === '>') {
- if (apply_selector(blocks, stack.pop(), stack, to_encapsulate)) {
+ if (apply_selector(blocks, get_element_parent(node), to_encapsulate)) {
to_encapsulate.push({ node, block });
return true;
}
return false;
+ } else if (block.combinator.name === '+' || block.combinator.name === '~') {
+ const siblings = get_possible_element_siblings(node, block.combinator.name === '+');
+ let has_match = false;
+ for (const possible_sibling of siblings.keys()) {
+ if (apply_selector(blocks.slice(), possible_sibling, to_encapsulate)) {
+ to_encapsulate.push({ node, block });
+ has_match = true;
+ }
+ }
+ return has_match;
}
// TODO other combinators
@@ -376,6 +395,158 @@ function unquote(value: CssNode) {
return str;
}
+function get_element_parent(node: Element): Element | null {
+ let parent: INode = node;
+ while ((parent = parent.parent) && parent.type !== 'Element');
+ return parent as Element | null;
+}
+
+function get_possible_element_siblings(node: INode, adjacent_only: boolean): Map {
+ const result: Map = new Map();
+ let prev: INode = node;
+ while (prev = prev.prev) {
+ if (prev.type === 'Element') {
+ if (!prev.attributes.find(attr => attr.name.toLowerCase() === 'slot')) {
+ result.set(prev, NodeExist.Definitely);
+ }
+
+ if (adjacent_only) {
+ break;
+ }
+ } else if (prev.type === 'EachBlock' || prev.type === 'IfBlock' || prev.type === 'AwaitBlock') {
+ const possible_last_child = get_possible_last_child(prev, adjacent_only);
+
+ add_to_map(possible_last_child, result);
+ if (adjacent_only && has_definite_elements(possible_last_child)) {
+ return result;
+ }
+ }
+ }
+
+ if (!prev || !adjacent_only) {
+ let parent: INode = node;
+ let skip_each_for_last_child = node.type === 'ElseBlock';
+ while ((parent = parent.parent) && (parent.type === 'EachBlock' || parent.type === 'IfBlock' || parent.type === 'ElseBlock' || parent.type === 'AwaitBlock')) {
+ const possible_siblings = get_possible_element_siblings(parent, adjacent_only);
+ add_to_map(possible_siblings, result);
+
+ if (parent.type === 'EachBlock') {
+ // first child of each block can select the last child of each block as previous sibling
+ if (skip_each_for_last_child) {
+ skip_each_for_last_child = false;
+ } else {
+ add_to_map(get_possible_last_child(parent, adjacent_only), result);
+ }
+ } else if (parent.type === 'ElseBlock') {
+ skip_each_for_last_child = true;
+ parent = parent.parent;
+ }
+
+ if (adjacent_only && has_definite_elements(possible_siblings)) {
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+function get_possible_last_child(block: EachBlock | IfBlock | AwaitBlock, adjacent_only: boolean): Map {
+ const result: Map = new Map();
+
+ if (block.type === 'EachBlock') {
+ const each_result: Map = loop_child(block.children, adjacent_only);
+ const else_result: Map = block.else ? loop_child(block.else.children, adjacent_only) : new Map();
+
+ const not_exhaustive = !has_definite_elements(else_result);
+
+ if (not_exhaustive) {
+ mark_as_probably(each_result);
+ mark_as_probably(else_result);
+ }
+ add_to_map(each_result, result);
+ add_to_map(else_result, result);
+ } else if (block.type === 'IfBlock') {
+ const if_result: Map = loop_child(block.children, adjacent_only);
+ const else_result: Map = block.else ? loop_child(block.else.children, adjacent_only) : new Map();
+
+ const not_exhaustive = !has_definite_elements(if_result) || !has_definite_elements(else_result);
+
+ if (not_exhaustive) {
+ mark_as_probably(if_result);
+ mark_as_probably(else_result);
+ }
+
+ add_to_map(if_result, result);
+ add_to_map(else_result, result);
+ } else if (block.type === 'AwaitBlock') {
+ const pending_result: Map = block.pending ? loop_child(block.pending.children, adjacent_only) : new Map();
+ const then_result: Map = block.then ? loop_child(block.then.children, adjacent_only) : new Map();
+ const catch_result: Map = block.catch ? loop_child(block.catch.children, adjacent_only) : new Map();
+
+ const not_exhaustive = !has_definite_elements(pending_result) || !has_definite_elements(then_result) || !has_definite_elements(catch_result);
+
+ if (not_exhaustive) {
+ mark_as_probably(pending_result);
+ mark_as_probably(then_result);
+ mark_as_probably(catch_result);
+ }
+
+ add_to_map(pending_result, result);
+ add_to_map(then_result, result);
+ add_to_map(catch_result, result);
+ }
+
+ return result;
+}
+
+function has_definite_elements(result: Map): boolean {
+ if (result.size === 0) return false;
+ for (const exist of result.values()) {
+ if (exist === NodeExist.Definitely) {
+ return true;
+ }
+ }
+ return false;
+}
+
+function add_to_map(from: Map, to: Map) {
+ from.forEach((exist, element) => {
+ to.set(element, higher_existance(exist, to.get(element)));
+ });
+}
+
+function higher_existance(exist1: NodeExist | null, exist2: NodeExist | null): NodeExist {
+ if (exist1 === undefined || exist2 === undefined) return exist1 || exist2;
+ return exist1 > exist2 ? exist1 : exist2;
+}
+
+function mark_as_probably(result: Map) {
+ for (const key of result.keys()) {
+ result.set(key, NodeExist.Probably);
+ }
+}
+
+function loop_child(children: INode[], adjacent_only: boolean) {
+ const result: Map = new Map();
+ for (let i = children.length - 1; i >= 0; i--) {
+ const child = children[i];
+ if (child.type === 'Element') {
+ result.set(child, NodeExist.Definitely);
+ if (adjacent_only) {
+ break;
+ }
+ } else if (child.type === 'EachBlock' || child.type === 'IfBlock' || child.type === 'AwaitBlock') {
+ const child_result = get_possible_last_child(child, adjacent_only);
+ add_to_map(child_result, result);
+ if (adjacent_only && has_definite_elements(child_result)) {
+ break;
+ }
+ }
+ }
+ return result;
+}
+
class Block {
global: boolean;
combinator: CssNode;
diff --git a/src/compiler/compile/css/Stylesheet.ts b/src/compiler/compile/css/Stylesheet.ts
index 27438947ff..dc464d7df8 100644
--- a/src/compiler/compile/css/Stylesheet.ts
+++ b/src/compiler/compile/css/Stylesheet.ts
@@ -2,7 +2,7 @@ import MagicString from 'magic-string';
import { walk } from 'estree-walker';
import Selector from './Selector';
import Element from '../nodes/Element';
-import { Ast, TemplateNode } from '../../interfaces';
+import { Ast } from '../../interfaces';
import Component from '../Component';
import { CssNode } from './interfaces';
import hash from "../utils/hash";
@@ -51,8 +51,8 @@ class Rule {
this.declarations = node.block.children.map((node: CssNode) => new Declaration(node));
}
- apply(node: Element, stack: Element[]) {
- this.selectors.forEach(selector => selector.apply(node, stack)); // TODO move the logic in here?
+ apply(node: Element) {
+ this.selectors.forEach(selector => selector.apply(node)); // TODO move the logic in here?
}
is_used(dev: boolean) {
@@ -162,10 +162,10 @@ class Atrule {
this.declarations = [];
}
- apply(node: Element, stack: Element[]) {
+ apply(node: Element) {
if (this.node.name === 'media' || this.node.name === 'supports') {
this.children.forEach(child => {
- child.apply(node, stack);
+ child.apply(node);
});
}
@@ -364,15 +364,9 @@ export default class Stylesheet {
apply(node: Element) {
if (!this.has_styles) return;
- const stack: Element[] = [];
- let parent: TemplateNode = node;
- while (parent = parent.parent) {
- if (parent.type === 'Element') stack.unshift(parent as Element);
- }
-
for (let i = 0; i < this.children.length; i += 1) {
const child = this.children[i];
- child.apply(node, stack);
+ child.apply(node);
}
}
diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts
index 991f102339..7c9272e5d3 100644
--- a/src/compiler/compile/nodes/Element.ts
+++ b/src/compiler/compile/nodes/Element.ts
@@ -16,6 +16,7 @@ import list from '../../utils/list';
import Let from './Let';
import TemplateScope from './shared/TemplateScope';
import { INode } from './interfaces';
+import Component from '../Component';
const svg = /^(?:altGlyph|altGlyphDef|altGlyphItem|animate|animateColor|animateMotion|animateTransform|circle|clipPath|color-profile|cursor|defs|desc|discard|ellipse|feBlend|feColorMatrix|feComponentTransfer|feComposite|feConvolveMatrix|feDiffuseLighting|feDisplacementMap|feDistantLight|feDropShadow|feFlood|feFuncA|feFuncB|feFuncG|feFuncR|feGaussianBlur|feImage|feMerge|feMergeNode|feMorphology|feOffset|fePointLight|feSpecularLighting|feSpotLight|feTile|feTurbulence|filter|font|font-face|font-face-format|font-face-name|font-face-src|font-face-uri|foreignObject|g|glyph|glyphRef|hatch|hatchpath|hkern|image|line|linearGradient|marker|mask|mesh|meshgradient|meshpatch|meshrow|metadata|missing-glyph|mpath|path|pattern|polygon|polyline|radialGradient|rect|set|solidcolor|stop|svg|switch|symbol|text|textPath|tref|tspan|unknown|use|view|vkern)$/;
@@ -124,7 +125,7 @@ export default class Element extends Node {
namespace: string;
needs_manual_style_scoping: boolean;
- constructor(component, parent, scope, info: any) {
+ constructor(component: Component, parent, scope, info: any) {
super(component, parent, scope, info);
this.name = info.name;
@@ -185,7 +186,7 @@ export default class Element extends Node {
case 'Attribute':
case 'Spread':
- // special case
+ // special case
if (node.name === 'xmlns') this.namespace = node.value[0].data;
this.attributes.push(new Attribute(component, this, scope, node));
@@ -236,7 +237,7 @@ export default class Element extends Node {
this.validate();
- component.stylesheet.apply(this);
+ component.apply_stylesheet(this);
}
validate() {
diff --git a/test/css/samples/general-siblings-combinator-await-not-exhaustive/_config.js b/test/css/samples/general-siblings-combinator-await-not-exhaustive/_config.js
new file mode 100644
index 0000000000..c81f1a9f82
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-await-not-exhaustive/_config.js
@@ -0,0 +1,3 @@
+export default {
+ warnings: []
+};
diff --git a/test/css/samples/general-siblings-combinator-await-not-exhaustive/expected.css b/test/css/samples/general-siblings-combinator-await-not-exhaustive/expected.css
new file mode 100644
index 0000000000..54eefc6088
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-await-not-exhaustive/expected.css
@@ -0,0 +1 @@
+.a.svelte-xyz~.b.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz~.c.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz~.d.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz~.e.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz~.f.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz~.g.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz~.h.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.b.svelte-xyz~.d.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.c.svelte-xyz~.d.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.b.svelte-xyz~.e.svelte-xyz~.f.svelte-xyz~.h.svelte-xyz{color:green}.b.svelte-xyz~.d.svelte-xyz~.h.svelte-xyz.svelte-xyz{color:green}.c.svelte-xyz~.g.svelte-xyz.svelte-xyz.svelte-xyz{color:green}
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-await-not-exhaustive/expected.html b/test/css/samples/general-siblings-combinator-await-not-exhaustive/expected.html
new file mode 100644
index 0000000000..de97b02a5e
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-await-not-exhaustive/expected.html
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-await-not-exhaustive/input.svelte b/test/css/samples/general-siblings-combinator-await-not-exhaustive/input.svelte
new file mode 100644
index 0000000000..a677077c33
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-await-not-exhaustive/input.svelte
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+{#await promise then value}
+
+{:catch error}
+
+{/await}
+
+{#await promise}
+
+{:catch error}
+
+{/await}
+
+{#await promise}
+
+{:then error}
+
+{/await}
+
+
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-await/_config.js b/test/css/samples/general-siblings-combinator-await/_config.js
new file mode 100644
index 0000000000..b4ebe41828
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-await/_config.js
@@ -0,0 +1,46 @@
+export default {
+ warnings: [
+ {
+ code: "css-unused-selector",
+ frame: `
+ 13:
+ 14: /* no match */
+ 15: .b ~ .c { color: green; }
+ ^
+ 16: .c ~ .d { color: green; }
+ 17: .b ~ .d { color: green; }`,
+ message: 'Unused CSS selector ".b ~ .c"',
+ pos: 269,
+ start: { character: 269, column: 1, line: 15 },
+ end: { character: 276, column: 8, line: 15 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 14: /* no match */
+ 15: .b ~ .c { color: green; }
+ 16: .c ~ .d { color: green; }
+ ^
+ 17: .b ~ .d { color: green; }
+ 18: `,
+ message: 'Unused CSS selector ".c ~ .d"',
+ pos: 296,
+ start: { character: 296, column: 1, line: 16 },
+ end: { character: 303, column: 8, line: 16 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 15: .b ~ .c { color: green; }
+ 16: .c ~ .d { color: green; }
+ 17: .b ~ .d { color: green; }
+ ^
+ 18:
+ 19:`,
+ message: 'Unused CSS selector ".b ~ .d"',
+ pos: 323,
+ start: { character: 323, column: 1, line: 17 },
+ end: { character: 330, column: 8, line: 17 }
+ }
+ ]
+};
diff --git a/test/css/samples/general-siblings-combinator-await/expected.css b/test/css/samples/general-siblings-combinator-await/expected.css
new file mode 100644
index 0000000000..94a5494597
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-await/expected.css
@@ -0,0 +1 @@
+.a.svelte-xyz~.b.svelte-xyz{color:green}.a.svelte-xyz~.c.svelte-xyz{color:green}.a.svelte-xyz~.d.svelte-xyz{color:green}.b.svelte-xyz~.e.svelte-xyz{color:green}.c.svelte-xyz~.e.svelte-xyz{color:green}.d.svelte-xyz~.e.svelte-xyz{color:green}.a.svelte-xyz~.e.svelte-xyz{color:green}
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-await/expected.html b/test/css/samples/general-siblings-combinator-await/expected.html
new file mode 100644
index 0000000000..3d8ac9f966
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-await/expected.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-await/input.svelte b/test/css/samples/general-siblings-combinator-await/input.svelte
new file mode 100644
index 0000000000..8aeadab170
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-await/input.svelte
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+{#await promise}
+
+{:then value}
+
+{:catch error}
+
+{/await}
+
+
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-each-2/_config.js b/test/css/samples/general-siblings-combinator-each-2/_config.js
new file mode 100644
index 0000000000..c81f1a9f82
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-each-2/_config.js
@@ -0,0 +1,3 @@
+export default {
+ warnings: []
+};
diff --git a/test/css/samples/general-siblings-combinator-each-2/expected.css b/test/css/samples/general-siblings-combinator-each-2/expected.css
new file mode 100644
index 0000000000..d197058b24
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-each-2/expected.css
@@ -0,0 +1 @@
+.a.svelte-xyz~.b.svelte-xyz{color:green}.c.svelte-xyz~.d.svelte-xyz{color:green}.a.svelte-xyz~.d.svelte-xyz{color:green}.c.svelte-xyz~.b.svelte-xyz{color:green}.b.svelte-xyz~.c.svelte-xyz{color:green}.a.svelte-xyz~.c.svelte-xyz{color:green}
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-each-2/expected.html b/test/css/samples/general-siblings-combinator-each-2/expected.html
new file mode 100644
index 0000000000..331a5e4317
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-each-2/expected.html
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-each-2/input.svelte b/test/css/samples/general-siblings-combinator-each-2/input.svelte
new file mode 100644
index 0000000000..5bbdbdef66
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-each-2/input.svelte
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+{#each array as item}
+
+
+{/each}
+
+
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-each-else-nested/_config.js b/test/css/samples/general-siblings-combinator-each-else-nested/_config.js
new file mode 100644
index 0000000000..cf241d856d
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-each-else-nested/_config.js
@@ -0,0 +1,18 @@
+export default {
+ warnings: [
+ {
+ code: "css-unused-selector",
+ frame: `
+ 33:
+ 34: /* no match */
+ 35: .e ~ .f { color: green; }
+ ^
+ 36:
+ 37:`,
+ message: 'Unused CSS selector ".e ~ .f"',
+ pos: 812,
+ start: { character: 812, column: 1, line: 35 },
+ end: { character: 819, column: 8, line: 35 }
+ }
+ ]
+};
diff --git a/test/css/samples/general-siblings-combinator-each-else-nested/expected.css b/test/css/samples/general-siblings-combinator-each-else-nested/expected.css
new file mode 100644
index 0000000000..b055f35ecd
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-each-else-nested/expected.css
@@ -0,0 +1 @@
+.a.svelte-xyz~.e.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz~.f.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.b.svelte-xyz~.c.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.b.svelte-xyz~.d.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.c.svelte-xyz~.e.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.c.svelte-xyz~.f.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.d.svelte-xyz~.e.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.d.svelte-xyz~.f.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.e.svelte-xyz~.e.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.i.svelte-xyz~.j.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.g.svelte-xyz~.h.svelte-xyz~.j.svelte-xyz.svelte-xyz{color:green}.g.svelte-xyz~.i.svelte-xyz~.j.svelte-xyz.svelte-xyz{color:green}.m.svelte-xyz~.m.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.m.svelte-xyz~.l.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.l.svelte-xyz~.m.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz~.c.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz~.g.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.b.svelte-xyz~.e.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.c.svelte-xyz~.g.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.c.svelte-xyz~.k.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.d.svelte-xyz~.d.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.g.svelte-xyz~.g.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.h.svelte-xyz~.h.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.i.svelte-xyz~.i.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.j.svelte-xyz~.j.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.g.svelte-xyz~.j.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.g.svelte-xyz~.h.svelte-xyz~.i.svelte-xyz~.j.svelte-xyz{color:green}
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-each-else-nested/expected.html b/test/css/samples/general-siblings-combinator-each-else-nested/expected.html
new file mode 100644
index 0000000000..67dd05f677
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-each-else-nested/expected.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-each-else-nested/input.svelte b/test/css/samples/general-siblings-combinator-each-else-nested/input.svelte
new file mode 100644
index 0000000000..63a1123708
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-each-else-nested/input.svelte
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+{#each array as a}
+
+ {#each array as b}
+
+ {:else}
+
+ {/each}
+{/each}
+
+{#each array as c}
+ {#each array as d}
+
+ {/each}
+{:else}
+
+{/each}
+
+{#each array as x}
+
+ {#each array as y}
+ {#each array as z}
+
+ {/each}
+ {:else}
+
+ {/each}
+
+{/each}
+
+
+
+{#each array as item}
+ {#each array as item}
+
+ {:else}
+
+ {/each}
+{/each}
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-each-else/_config.js b/test/css/samples/general-siblings-combinator-each-else/_config.js
new file mode 100644
index 0000000000..4d9beceeb2
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-each-else/_config.js
@@ -0,0 +1,18 @@
+export default {
+ warnings: [
+ {
+ code: "css-unused-selector",
+ frame: `
+ 11:
+ 12: /* no match */
+ 13: .b ~ .c { color: green; }
+ ^
+ 14:
+ 15:`,
+ message: 'Unused CSS selector ".b ~ .c"',
+ pos: 199,
+ start: { character: 199, column: 1, line: 13 },
+ end: { character: 206, column: 8, line: 13 }
+ }
+ ]
+};
diff --git a/test/css/samples/general-siblings-combinator-each-else/expected.css b/test/css/samples/general-siblings-combinator-each-else/expected.css
new file mode 100644
index 0000000000..31fafa3243
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-each-else/expected.css
@@ -0,0 +1 @@
+.a.svelte-xyz~.b.svelte-xyz{color:green}.a.svelte-xyz~.c.svelte-xyz{color:green}.b.svelte-xyz~.d.svelte-xyz{color:green}.c.svelte-xyz~.d.svelte-xyz{color:green}.a.svelte-xyz~.d.svelte-xyz{color:green}
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-each-else/expected.html b/test/css/samples/general-siblings-combinator-each-else/expected.html
new file mode 100644
index 0000000000..fb838a55fd
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-each-else/expected.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-each-else/input.svelte b/test/css/samples/general-siblings-combinator-each-else/input.svelte
new file mode 100644
index 0000000000..5cd78853ca
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-each-else/input.svelte
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+{#each array as item}
+
+{:else}
+
+{/each}
+
+
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-each-nested/_config.js b/test/css/samples/general-siblings-combinator-each-nested/_config.js
new file mode 100644
index 0000000000..c81f1a9f82
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-each-nested/_config.js
@@ -0,0 +1,3 @@
+export default {
+ warnings: []
+};
diff --git a/test/css/samples/general-siblings-combinator-each-nested/expected.css b/test/css/samples/general-siblings-combinator-each-nested/expected.css
new file mode 100644
index 0000000000..5bf1f832ae
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-each-nested/expected.css
@@ -0,0 +1 @@
+.a.svelte-xyz~.d.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz~.e.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz~.f.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz~.g.svelte-xyz.svelte-xyz{color:green}.c.svelte-xyz~.d.svelte-xyz.svelte-xyz{color:green}.c.svelte-xyz~.e.svelte-xyz.svelte-xyz{color:green}.c.svelte-xyz~.f.svelte-xyz.svelte-xyz{color:green}.c.svelte-xyz~.g.svelte-xyz.svelte-xyz{color:green}.j.svelte-xyz~.m.svelte-xyz.svelte-xyz{color:green}.j.svelte-xyz~.n.svelte-xyz.svelte-xyz{color:green}.j.svelte-xyz~.o.svelte-xyz.svelte-xyz{color:green}.k.svelte-xyz~.m.svelte-xyz.svelte-xyz{color:green}.k.svelte-xyz~.n.svelte-xyz.svelte-xyz{color:green}.k.svelte-xyz~.o.svelte-xyz.svelte-xyz{color:green}.l.svelte-xyz~.m.svelte-xyz.svelte-xyz{color:green}.l.svelte-xyz~.n.svelte-xyz.svelte-xyz{color:green}.l.svelte-xyz~.o.svelte-xyz.svelte-xyz{color:green}.d.svelte-xyz~.e.svelte-xyz.svelte-xyz{color:green}.e.svelte-xyz~.f.svelte-xyz.svelte-xyz{color:green}.g.svelte-xyz~.h.svelte-xyz.svelte-xyz{color:green}.f.svelte-xyz~.d.svelte-xyz.svelte-xyz{color:green}.f.svelte-xyz~.e.svelte-xyz.svelte-xyz{color:green}.f.svelte-xyz~.f.svelte-xyz.svelte-xyz{color:green}.h.svelte-xyz~.g.svelte-xyz.svelte-xyz{color:green}.i.svelte-xyz~.h.svelte-xyz.svelte-xyz{color:green}.i.svelte-xyz~.g.svelte-xyz.svelte-xyz{color:green}.d.svelte-xyz~.d.svelte-xyz.svelte-xyz{color:green}.e.svelte-xyz~.e.svelte-xyz.svelte-xyz{color:green}.f.svelte-xyz~.f.svelte-xyz.svelte-xyz{color:green}.g.svelte-xyz~.g.svelte-xyz.svelte-xyz{color:green}.h.svelte-xyz~.h.svelte-xyz.svelte-xyz{color:green}.i.svelte-xyz~.i.svelte-xyz.svelte-xyz{color:green}.e.svelte-xyz~.e.svelte-xyz~.f.svelte-xyz{color:green}.e.svelte-xyz~.e.svelte-xyz~.d.svelte-xyz{color:green}.h.svelte-xyz~.h.svelte-xyz~.i.svelte-xyz{color:green}.h.svelte-xyz~.h.svelte-xyz~.g.svelte-xyz{color:green}.a.svelte-xyz~.h.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz~.i.svelte-xyz.svelte-xyz{color:green}.c.svelte-xyz~.h.svelte-xyz.svelte-xyz{color:green}.c.svelte-xyz~.i.svelte-xyz.svelte-xyz{color:green}.d.svelte-xyz~.f.svelte-xyz.svelte-xyz{color:green}.d.svelte-xyz~.g.svelte-xyz.svelte-xyz{color:green}.e.svelte-xyz~.g.svelte-xyz.svelte-xyz{color:green}.g.svelte-xyz~.i.svelte-xyz.svelte-xyz{color:green}
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-each-nested/expected.html b/test/css/samples/general-siblings-combinator-each-nested/expected.html
new file mode 100644
index 0000000000..340d6fc4c8
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-each-nested/expected.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-each-nested/input.svelte b/test/css/samples/general-siblings-combinator-each-nested/input.svelte
new file mode 100644
index 0000000000..b7c7377015
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-each-nested/input.svelte
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+{#each array as item}
+
+
+{/each}
+
+{#each array as item}
+ {#each array as item}
+ {#each array as item}
+
+ {/each}
+
+ {/each}
+
+{/each}
+
+{#each array as item}
+
+ {#each array as item}
+
+ {#each array as item}
+
+ {/each}
+ {/each}
+{/each}
+
+{#each array as item}
+
+ {#each array as item}
+
+ {#each array as item}
+
+ {/each}
+ {/each}
+{/each}
+
+{#each array as item}
+ {#each array as item}
+ {#each array as item}
+
+ {/each}
+
+ {/each}
+
+{/each}
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-each/expected.css b/test/css/samples/general-siblings-combinator-each/expected.css
new file mode 100644
index 0000000000..8c48251bdd
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-each/expected.css
@@ -0,0 +1 @@
+div.svelte-xyz~span.svelte-xyz{color:green}
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-each/expected.html b/test/css/samples/general-siblings-combinator-each/expected.html
new file mode 100644
index 0000000000..9d0416f01b
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-each/expected.html
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/test/css/samples/general-siblings-combinator-each/input.svelte b/test/css/samples/general-siblings-combinator-each/input.svelte
new file mode 100644
index 0000000000..ce65da109d
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-each/input.svelte
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+{#each array as item}
+
+
+
+
+{/each}
+
+
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-if-not-exhaustive-with-each/_config.js b/test/css/samples/general-siblings-combinator-if-not-exhaustive-with-each/_config.js
new file mode 100644
index 0000000000..0b5d391f50
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-if-not-exhaustive-with-each/_config.js
@@ -0,0 +1,18 @@
+export default {
+ warnings: [
+ {
+ code: "css-unused-selector",
+ frame: `
+ 16:
+ 17: /* no match */
+ 18: .b ~ .c { color: green; }
+ ^
+ 19:
+ 20:`,
+ message: 'Unused CSS selector ".b ~ .c"',
+ pos: 319,
+ start: { character: 319, column: 1, line: 18 },
+ end: { character: 326, column: 8, line: 18 }
+ }
+ ]
+};
diff --git a/test/css/samples/general-siblings-combinator-if-not-exhaustive-with-each/expected.css b/test/css/samples/general-siblings-combinator-if-not-exhaustive-with-each/expected.css
new file mode 100644
index 0000000000..e6a974efca
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-if-not-exhaustive-with-each/expected.css
@@ -0,0 +1 @@
+.a.svelte-xyz~.b.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz~.c.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz~.d.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.b.svelte-xyz~.d.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.c.svelte-xyz~.d.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz~.c.svelte-xyz~.c.svelte-xyz.svelte-xyz{color:green}.c.svelte-xyz~.c.svelte-xyz~.d.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz~.c.svelte-xyz~.c.svelte-xyz~.d.svelte-xyz{color:green}
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-if-not-exhaustive-with-each/expected.html b/test/css/samples/general-siblings-combinator-if-not-exhaustive-with-each/expected.html
new file mode 100644
index 0000000000..fb838a55fd
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-if-not-exhaustive-with-each/expected.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-if-not-exhaustive-with-each/input.svelte b/test/css/samples/general-siblings-combinator-if-not-exhaustive-with-each/input.svelte
new file mode 100644
index 0000000000..2b53f7b5b8
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-if-not-exhaustive-with-each/input.svelte
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+{#if foo}
+
+{:else}
+ {#each array as item}
+
+ {/each}
+{/if}
+
+
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-if-not-exhaustive/_config.js b/test/css/samples/general-siblings-combinator-if-not-exhaustive/_config.js
new file mode 100644
index 0000000000..81fb595d68
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-if-not-exhaustive/_config.js
@@ -0,0 +1,18 @@
+export default {
+ warnings: [
+ {
+ code: "css-unused-selector",
+ frame: `
+ 12:
+ 13: /* no match */
+ 14: .b ~ .c { color: green; }
+ ^
+ 15:
+ 16:`,
+ message: 'Unused CSS selector ".b ~ .c"',
+ pos: 215,
+ start: { character: 215, column: 1, line: 14 },
+ end: { character: 222, column: 8, line: 14 }
+ }
+ ]
+};
diff --git a/test/css/samples/general-siblings-combinator-if-not-exhaustive/expected.css b/test/css/samples/general-siblings-combinator-if-not-exhaustive/expected.css
new file mode 100644
index 0000000000..dc7ee1b62b
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-if-not-exhaustive/expected.css
@@ -0,0 +1 @@
+.a.svelte-xyz~.b.svelte-xyz{color:green}.a.svelte-xyz~.c.svelte-xyz{color:green}.a.svelte-xyz~.d.svelte-xyz{color:green}.b.svelte-xyz~.d.svelte-xyz{color:green}.c.svelte-xyz~.d.svelte-xyz{color:green}
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-if-not-exhaustive/expected.html b/test/css/samples/general-siblings-combinator-if-not-exhaustive/expected.html
new file mode 100644
index 0000000000..813e778dc6
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-if-not-exhaustive/expected.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-if-not-exhaustive/input.svelte b/test/css/samples/general-siblings-combinator-if-not-exhaustive/input.svelte
new file mode 100644
index 0000000000..3e5c5af7a2
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-if-not-exhaustive/input.svelte
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+{#if foo}
+
+{:else if bar}
+
+{/if}
+
+
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-if/_config.js b/test/css/samples/general-siblings-combinator-if/_config.js
new file mode 100644
index 0000000000..cbc3d8a784
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-if/_config.js
@@ -0,0 +1,46 @@
+export default {
+ warnings: [
+ {
+ code: "css-unused-selector",
+ frame: `
+ 14:
+ 15: /* no match */
+ 16: .b ~ .c { color: green; }
+ ^
+ 17: .b ~ .d { color: green; }
+ 18: .c ~ .d { color: green; }`,
+ message: 'Unused CSS selector ".b ~ .c"',
+ pos: 269,
+ start: { character: 269, column: 1, line: 16 },
+ end: { character: 276, column: 8, line: 16 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 15: /* no match */
+ 16: .b ~ .c { color: green; }
+ 17: .b ~ .d { color: green; }
+ ^
+ 18: .c ~ .d { color: green; }
+ 19: `,
+ message: 'Unused CSS selector ".b ~ .d"',
+ pos: 296,
+ start: { character: 296, column: 1, line: 17 },
+ end: { character: 303, column: 8, line: 17 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 16: .b ~ .c { color: green; }
+ 17: .b ~ .d { color: green; }
+ 18: .c ~ .d { color: green; }
+ ^
+ 19:
+ 20:`,
+ message: 'Unused CSS selector ".c ~ .d"',
+ pos: 323,
+ start: { character: 323, column: 1, line: 18 },
+ end: { character: 330, column: 8, line: 18 }
+ }
+ ]
+};
diff --git a/test/css/samples/general-siblings-combinator-if/expected.css b/test/css/samples/general-siblings-combinator-if/expected.css
new file mode 100644
index 0000000000..94a5494597
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-if/expected.css
@@ -0,0 +1 @@
+.a.svelte-xyz~.b.svelte-xyz{color:green}.a.svelte-xyz~.c.svelte-xyz{color:green}.a.svelte-xyz~.d.svelte-xyz{color:green}.b.svelte-xyz~.e.svelte-xyz{color:green}.c.svelte-xyz~.e.svelte-xyz{color:green}.d.svelte-xyz~.e.svelte-xyz{color:green}.a.svelte-xyz~.e.svelte-xyz{color:green}
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-if/expected.html b/test/css/samples/general-siblings-combinator-if/expected.html
new file mode 100644
index 0000000000..3d8ac9f966
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-if/expected.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-if/input.svelte b/test/css/samples/general-siblings-combinator-if/input.svelte
new file mode 100644
index 0000000000..fca5499f2e
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-if/input.svelte
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+{#if foo}
+
+{:else if bar}
+
+{:else}
+
+{/if}
+
+
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-slot/_config.js b/test/css/samples/general-siblings-combinator-slot/_config.js
new file mode 100644
index 0000000000..4cf743c0a0
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-slot/_config.js
@@ -0,0 +1,88 @@
+export default {
+ warnings: [
+ {
+ code: "css-unused-selector",
+ frame: `
+ 8:
+ 9: /* no match */
+ 10: .a ~ .b { color: green; }
+ ^
+ 11: .b ~ .c { color: green; }
+ 12: .c ~ .f { color: green; }`,
+ message: 'Unused CSS selector ".a ~ .b"',
+ pos: 111,
+ start: { character: 111, column: 1, line: 10 },
+ end: { character: 118, column: 8, line: 10 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 9: /* no match */
+ 10: .a ~ .b { color: green; }
+ 11: .b ~ .c { color: green; }
+ ^
+ 12: .c ~ .f { color: green; }
+ 13: .f ~ .g { color: green; }`,
+ message: 'Unused CSS selector ".b ~ .c"',
+ pos: 138,
+ start: { character: 138, column: 1, line: 11 },
+ end: { character: 145, column: 8, line: 11 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 10: .a ~ .b { color: green; }
+ 11: .b ~ .c { color: green; }
+ 12: .c ~ .f { color: green; }
+ ^
+ 13: .f ~ .g { color: green; }
+ 14: .b ~ .f { color: green; }`,
+ message: 'Unused CSS selector ".c ~ .f"',
+ pos: 165,
+ start: { character: 165, column: 1, line: 12 },
+ end: { character: 172, column: 8, line: 12 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 11: .b ~ .c { color: green; }
+ 12: .c ~ .f { color: green; }
+ 13: .f ~ .g { color: green; }
+ ^
+ 14: .b ~ .f { color: green; }
+ 15: .b ~ .g { color: green; }`,
+ message: 'Unused CSS selector ".f ~ .g"',
+ pos: 192,
+ start: { character: 192, column: 1, line: 13 },
+ end: { character: 199, column: 8, line: 13 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 12: .c ~ .f { color: green; }
+ 13: .f ~ .g { color: green; }
+ 14: .b ~ .f { color: green; }
+ ^
+ 15: .b ~ .g { color: green; }
+ 16: `,
+ message: 'Unused CSS selector ".b ~ .f"',
+ pos: 219,
+ start: { character: 219, column: 1, line: 14 },
+ end: { character: 226, column: 8, line: 14 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 13: .f ~ .g { color: green; }
+ 14: .b ~ .f { color: green; }
+ 15: .b ~ .g { color: green; }
+ ^
+ 16:
+ 17:`,
+ message: 'Unused CSS selector ".b ~ .g"',
+ pos: 246,
+ start: { character: 246, column: 1, line: 15 },
+ end: { character: 253, column: 8, line: 15 }
+ }
+ ]
+};
diff --git a/test/css/samples/general-siblings-combinator-slot/expected.css b/test/css/samples/general-siblings-combinator-slot/expected.css
new file mode 100644
index 0000000000..29325e0cef
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-slot/expected.css
@@ -0,0 +1 @@
+.d.svelte-xyz~.e.svelte-xyz{color:green}.a.svelte-xyz~.g.svelte-xyz{color:green}
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-slot/input.svelte b/test/css/samples/general-siblings-combinator-slot/input.svelte
new file mode 100644
index 0000000000..e5af4c0962
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-slot/input.svelte
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-star/_config.js b/test/css/samples/general-siblings-combinator-star/_config.js
new file mode 100644
index 0000000000..64e5baad19
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-star/_config.js
@@ -0,0 +1,18 @@
+export default {
+ warnings: [
+ {
+ code: "css-unused-selector",
+ frame: `
+ 3: margin-left: 4px;
+ 4: }
+ 5: .not-match > * ~ * {
+ ^
+ 6: margin-left: 4px;
+ 7: }`,
+ message: 'Unused CSS selector ".not-match > * ~ *"',
+ pos: 50,
+ start: { character: 50, column: 1, line: 5 },
+ end: { character: 68, column: 19, line: 5 }
+ }
+ ]
+};
diff --git a/test/css/samples/general-siblings-combinator-star/expected.css b/test/css/samples/general-siblings-combinator-star/expected.css
new file mode 100644
index 0000000000..de947fa47e
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-star/expected.css
@@ -0,0 +1 @@
+.match.svelte-xyz>.svelte-xyz~.svelte-xyz{margin-left:4px}
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-star/expected.html b/test/css/samples/general-siblings-combinator-star/expected.html
new file mode 100644
index 0000000000..1cfae6e6f7
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-star/expected.html
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator-star/input.svelte b/test/css/samples/general-siblings-combinator-star/input.svelte
new file mode 100644
index 0000000000..a069685d4f
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator-star/input.svelte
@@ -0,0 +1,17 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator/_config.js b/test/css/samples/general-siblings-combinator/_config.js
new file mode 100644
index 0000000000..6513366549
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator/_config.js
@@ -0,0 +1,60 @@
+export default {
+ warnings: [
+ {
+ code: "css-unused-selector",
+ frame: `
+ 10:
+ 11: /* no match */
+ 12: article ~ div { color: green; }
+ ^
+ 13: span ~ article { color: green; }
+ 14: b ~ article { color: green; }`,
+ message: 'Unused CSS selector "article ~ div"',
+ pos: 275,
+ start: { character: 275, column: 1, line: 12 },
+ end: { character: 288, column: 14, line: 12 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 11: /* no match */
+ 12: article ~ div { color: green; }
+ 13: span ~ article { color: green; }
+ ^
+ 14: b ~ article { color: green; }
+ 15: span ~ div { color: green; }`,
+ message: 'Unused CSS selector "span ~ article"',
+ pos: 308,
+ start: { character: 308, column: 1, line: 13 },
+ end: { character: 322, column: 15, line: 13 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 12: article ~ div { color: green; }
+ 13: span ~ article { color: green; }
+ 14: b ~ article { color: green; }
+ ^
+ 15: span ~ div { color: green; }
+ 16: `,
+ message: 'Unused CSS selector "b ~ article"',
+ pos: 342,
+ start: { character: 342, column: 1, line: 14 },
+ end: { character: 353, column: 12, line: 14 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 13: span ~ article { color: green; }
+ 14: b ~ article { color: green; }
+ 15: span ~ div { color: green; }
+ ^
+ 16:
+ 17:`,
+ message: 'Unused CSS selector "span ~ div"',
+ pos: 373,
+ start: { character: 373, column: 1, line: 15 },
+ end: { character: 383, column: 11, line: 15 }
+ }
+ ]
+};
diff --git a/test/css/samples/general-siblings-combinator/expected.css b/test/css/samples/general-siblings-combinator/expected.css
new file mode 100644
index 0000000000..647a53f778
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator/expected.css
@@ -0,0 +1 @@
+div.svelte-xyz~article.svelte-xyz.svelte-xyz{color:green}span.svelte-xyz~b.svelte-xyz.svelte-xyz{color:green}div.svelte-xyz span.svelte-xyz~b.svelte-xyz{color:green}.a.svelte-xyz~article.svelte-xyz.svelte-xyz{color:green}div.svelte-xyz~.b.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz~.c.svelte-xyz.svelte-xyz{color:green}article.svelte-xyz~details.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz~details.svelte-xyz.svelte-xyz{color:green}
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator/expected.html b/test/css/samples/general-siblings-combinator/expected.html
new file mode 100644
index 0000000000..01fbffcae4
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator/expected.html
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/css/samples/general-siblings-combinator/input.svelte b/test/css/samples/general-siblings-combinator/input.svelte
new file mode 100644
index 0000000000..533702a3a3
--- /dev/null
+++ b/test/css/samples/general-siblings-combinator/input.svelte
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-await-not-exhaustive/_config.js b/test/css/samples/siblings-combinator-await-not-exhaustive/_config.js
new file mode 100644
index 0000000000..c81f1a9f82
--- /dev/null
+++ b/test/css/samples/siblings-combinator-await-not-exhaustive/_config.js
@@ -0,0 +1,3 @@
+export default {
+ warnings: []
+};
diff --git a/test/css/samples/siblings-combinator-await-not-exhaustive/expected.css b/test/css/samples/siblings-combinator-await-not-exhaustive/expected.css
new file mode 100644
index 0000000000..60bb8e92dc
--- /dev/null
+++ b/test/css/samples/siblings-combinator-await-not-exhaustive/expected.css
@@ -0,0 +1 @@
+.a.svelte-xyz+.b.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz+.c.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz+.d.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz+.e.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz+.f.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz+.g.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz+.h.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.b.svelte-xyz+.d.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.c.svelte-xyz+.d.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.b.svelte-xyz+.e.svelte-xyz+.f.svelte-xyz+.h.svelte-xyz{color:green}.b.svelte-xyz+.d.svelte-xyz+.h.svelte-xyz.svelte-xyz{color:green}.c.svelte-xyz+.g.svelte-xyz.svelte-xyz.svelte-xyz{color:green}
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-await-not-exhaustive/expected.html b/test/css/samples/siblings-combinator-await-not-exhaustive/expected.html
new file mode 100644
index 0000000000..de97b02a5e
--- /dev/null
+++ b/test/css/samples/siblings-combinator-await-not-exhaustive/expected.html
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-await-not-exhaustive/input.svelte b/test/css/samples/siblings-combinator-await-not-exhaustive/input.svelte
new file mode 100644
index 0000000000..d3345c6edc
--- /dev/null
+++ b/test/css/samples/siblings-combinator-await-not-exhaustive/input.svelte
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+{#await promise then value}
+
+{:catch error}
+
+{/await}
+
+{#await promise}
+
+{:catch error}
+
+{/await}
+
+{#await promise}
+
+{:then error}
+
+{/await}
+
+
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-await/_config.js b/test/css/samples/siblings-combinator-await/_config.js
new file mode 100644
index 0000000000..5af12d38e5
--- /dev/null
+++ b/test/css/samples/siblings-combinator-await/_config.js
@@ -0,0 +1,60 @@
+export default {
+ warnings: [
+ {
+ code: "css-unused-selector",
+ frame: `
+ 12:
+ 13: /* no match */
+ 14: .a + .e { color: green; }
+ ^
+ 15: .b + .c { color: green; }
+ 16: .c + .d { color: green; }`,
+ message: 'Unused CSS selector ".a + .e"',
+ pos: 242,
+ start: { character: 242, column: 1, line: 14 },
+ end: { character: 249, column: 8, line: 14 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 13: /* no match */
+ 14: .a + .e { color: green; }
+ 15: .b + .c { color: green; }
+ ^
+ 16: .c + .d { color: green; }
+ 17: .b + .d { color: green; }`,
+ message: 'Unused CSS selector ".b + .c"',
+ pos: 269,
+ start: { character: 269, column: 1, line: 15 },
+ end: { character: 276, column: 8, line: 15 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 14: .a + .e { color: green; }
+ 15: .b + .c { color: green; }
+ 16: .c + .d { color: green; }
+ ^
+ 17: .b + .d { color: green; }
+ 18: `,
+ message: 'Unused CSS selector ".c + .d"',
+ pos: 296,
+ start: { character: 296, column: 1, line: 16 },
+ end: { character: 303, column: 8, line: 16 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 15: .b + .c { color: green; }
+ 16: .c + .d { color: green; }
+ 17: .b + .d { color: green; }
+ ^
+ 18:
+ 19:`,
+ message: 'Unused CSS selector ".b + .d"',
+ pos: 323,
+ start: { character: 323, column: 1, line: 17 },
+ end: { character: 330, column: 8, line: 17 }
+ }
+ ]
+};
diff --git a/test/css/samples/siblings-combinator-await/expected.css b/test/css/samples/siblings-combinator-await/expected.css
new file mode 100644
index 0000000000..5ea39be7c2
--- /dev/null
+++ b/test/css/samples/siblings-combinator-await/expected.css
@@ -0,0 +1 @@
+.a.svelte-xyz+.b.svelte-xyz{color:green}.a.svelte-xyz+.c.svelte-xyz{color:green}.a.svelte-xyz+.d.svelte-xyz{color:green}.b.svelte-xyz+.e.svelte-xyz{color:green}.c.svelte-xyz+.e.svelte-xyz{color:green}.d.svelte-xyz+.e.svelte-xyz{color:green}
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-await/expected.html b/test/css/samples/siblings-combinator-await/expected.html
new file mode 100644
index 0000000000..3d8ac9f966
--- /dev/null
+++ b/test/css/samples/siblings-combinator-await/expected.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-await/input.svelte b/test/css/samples/siblings-combinator-await/input.svelte
new file mode 100644
index 0000000000..07698c2a30
--- /dev/null
+++ b/test/css/samples/siblings-combinator-await/input.svelte
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+{#await promise}
+
+{:then value}
+
+{:catch error}
+
+{/await}
+
+
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-each-2/_config.js b/test/css/samples/siblings-combinator-each-2/_config.js
new file mode 100644
index 0000000000..e4799f1055
--- /dev/null
+++ b/test/css/samples/siblings-combinator-each-2/_config.js
@@ -0,0 +1,18 @@
+export default {
+ warnings: [
+ {
+ code: "css-unused-selector",
+ frame: `
+ 24: }
+ 25: /* not match */
+ 26: .a + .c {
+ ^
+ 27: color: green;
+ 28: }`,
+ message: 'Unused CSS selector ".a + .c"',
+ pos: 320,
+ start: { character: 320, column: 1, line: 26 },
+ end: { character: 327, column: 8, line: 26 }
+ }
+ ]
+};
diff --git a/test/css/samples/siblings-combinator-each-2/expected.css b/test/css/samples/siblings-combinator-each-2/expected.css
new file mode 100644
index 0000000000..60fa224269
--- /dev/null
+++ b/test/css/samples/siblings-combinator-each-2/expected.css
@@ -0,0 +1 @@
+.a.svelte-xyz+.b.svelte-xyz{color:green}.c.svelte-xyz+.d.svelte-xyz{color:green}.a.svelte-xyz+.d.svelte-xyz{color:green}.c.svelte-xyz+.b.svelte-xyz{color:green}.b.svelte-xyz+.c.svelte-xyz{color:green}
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-each-2/expected.html b/test/css/samples/siblings-combinator-each-2/expected.html
new file mode 100644
index 0000000000..331a5e4317
--- /dev/null
+++ b/test/css/samples/siblings-combinator-each-2/expected.html
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-each-2/input.svelte b/test/css/samples/siblings-combinator-each-2/input.svelte
new file mode 100644
index 0000000000..bbad045fbc
--- /dev/null
+++ b/test/css/samples/siblings-combinator-each-2/input.svelte
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+{#each array as item}
+
+
+{/each}
+
+
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-each-else-nested/_config.js b/test/css/samples/siblings-combinator-each-else-nested/_config.js
new file mode 100644
index 0000000000..ab3fac6abe
--- /dev/null
+++ b/test/css/samples/siblings-combinator-each-else-nested/_config.js
@@ -0,0 +1,144 @@
+export default {
+ warnings: [
+ {
+ code: "css-unused-selector",
+ frame: `
+ 21:
+ 22: /* no match */
+ 23: .a + .c { color: green; }
+ ^
+ 24: .a + .g { color: green; }
+ 25: .b + .e { color: green; }`,
+ message: 'Unused CSS selector ".a + .c"',
+ pos: 479,
+ start: { character: 479, column: 1, line: 23 },
+ end: { character: 486, column: 8, line: 23 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 22: /* no match */
+ 23: .a + .c { color: green; }
+ 24: .a + .g { color: green; }
+ ^
+ 25: .b + .e { color: green; }
+ 26: .c + .g { color: green; }`,
+ message: 'Unused CSS selector ".a + .g"',
+ pos: 506,
+ start: { character: 506, column: 1, line: 24 },
+ end: { character: 513, column: 8, line: 24 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 23: .a + .c { color: green; }
+ 24: .a + .g { color: green; }
+ 25: .b + .e { color: green; }
+ ^
+ 26: .c + .g { color: green; }
+ 27: .c + .k { color: green; }`,
+ message: 'Unused CSS selector ".b + .e"',
+ pos: 533,
+ start: { character: 533, column: 1, line: 25 },
+ end: { character: 540, column: 8, line: 25 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 24: .a + .g { color: green; }
+ 25: .b + .e { color: green; }
+ 26: .c + .g { color: green; }
+ ^
+ 27: .c + .k { color: green; }
+ 28: .d + .d { color: green; }`,
+ message: 'Unused CSS selector ".c + .g"',
+ pos: 560,
+ start: { character: 560, column: 1, line: 26 },
+ end: { character: 567, column: 8, line: 26 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 25: .b + .e { color: green; }
+ 26: .c + .g { color: green; }
+ 27: .c + .k { color: green; }
+ ^
+ 28: .d + .d { color: green; }
+ 29: .e + .f { color: green; }`,
+ message: 'Unused CSS selector ".c + .k"',
+ pos: 587,
+ start: { character: 587, column: 1, line: 27 },
+ end: { character: 594, column: 8, line: 27 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 26: .c + .g { color: green; }
+ 27: .c + .k { color: green; }
+ 28: .d + .d { color: green; }
+ ^
+ 29: .e + .f { color: green; }
+ 30: .f + .f { color: green; }`,
+ message: 'Unused CSS selector ".d + .d"',
+ pos: 614,
+ start: { character: 614, column: 1, line: 28 },
+ end: { character: 621, column: 8, line: 28 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 27: .c + .k { color: green; }
+ 28: .d + .d { color: green; }
+ 29: .e + .f { color: green; }
+ ^
+ 30: .f + .f { color: green; }
+ 31: .g + .j { color: green; }`,
+ message: 'Unused CSS selector ".e + .f"',
+ pos: 641,
+ start: { character: 641, column: 1, line: 29 },
+ end: { character: 648, column: 8, line: 29 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 28: .d + .d { color: green; }
+ 29: .e + .f { color: green; }
+ 30: .f + .f { color: green; }
+ ^
+ 31: .g + .j { color: green; }
+ 32: .g + .h + .i + .j { color: green; }`,
+ message: 'Unused CSS selector ".f + .f"',
+ pos: 668,
+ start: { character: 668, column: 1, line: 30 },
+ end: { character: 675, column: 8, line: 30 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 29: .e + .f { color: green; }
+ 30: .f + .f { color: green; }
+ 31: .g + .j { color: green; }
+ ^
+ 32: .g + .h + .i + .j { color: green; }
+ 33: `,
+ message: 'Unused CSS selector ".g + .j"',
+ pos: 695,
+ start: { character: 695, column: 1, line: 31 },
+ end: { character: 702, column: 8, line: 31 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 30: .f + .f { color: green; }
+ 31: .g + .j { color: green; }
+ 32: .g + .h + .i + .j { color: green; }
+ ^
+ 33:
+ 34:`,
+ message: 'Unused CSS selector ".g + .h + .i + .j"',
+ pos: 722,
+ start: { character: 722, column: 1, line: 32 },
+ end: { character: 739, column: 18, line: 32 }
+ }
+ ]
+};
diff --git a/test/css/samples/siblings-combinator-each-else-nested/expected.css b/test/css/samples/siblings-combinator-each-else-nested/expected.css
new file mode 100644
index 0000000000..aa4e04081d
--- /dev/null
+++ b/test/css/samples/siblings-combinator-each-else-nested/expected.css
@@ -0,0 +1 @@
+.a.svelte-xyz+.e.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz+.f.svelte-xyz.svelte-xyz{color:green}.b.svelte-xyz+.c.svelte-xyz.svelte-xyz{color:green}.b.svelte-xyz+.d.svelte-xyz.svelte-xyz{color:green}.c.svelte-xyz+.e.svelte-xyz.svelte-xyz{color:green}.c.svelte-xyz+.f.svelte-xyz.svelte-xyz{color:green}.d.svelte-xyz+.e.svelte-xyz.svelte-xyz{color:green}.d.svelte-xyz+.f.svelte-xyz.svelte-xyz{color:green}.e.svelte-xyz+.e.svelte-xyz.svelte-xyz{color:green}.i.svelte-xyz+.j.svelte-xyz.svelte-xyz{color:green}.g.svelte-xyz+.h.svelte-xyz+.j.svelte-xyz{color:green}.g.svelte-xyz+.i.svelte-xyz+.j.svelte-xyz{color:green}.m.svelte-xyz+.m.svelte-xyz.svelte-xyz{color:green}.m.svelte-xyz+.l.svelte-xyz.svelte-xyz{color:green}.l.svelte-xyz+.m.svelte-xyz.svelte-xyz{color:green}
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-each-else-nested/expected.html b/test/css/samples/siblings-combinator-each-else-nested/expected.html
new file mode 100644
index 0000000000..5f25a2d38a
--- /dev/null
+++ b/test/css/samples/siblings-combinator-each-else-nested/expected.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-each-else-nested/input.svelte b/test/css/samples/siblings-combinator-each-else-nested/input.svelte
new file mode 100644
index 0000000000..bee9c6b1dc
--- /dev/null
+++ b/test/css/samples/siblings-combinator-each-else-nested/input.svelte
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+{#each array as a}
+
+ {#each array as b}
+
+ {:else}
+
+ {/each}
+{/each}
+
+{#each array as c}
+ {#each array as d}
+
+ {/each}
+{:else}
+
+{/each}
+
+{#each array as item}
+
+ {#each array as item}
+ {#each array as item}
+
+ {/each}
+ {:else}
+
+ {/each}
+
+{/each}
+
+
+
+{#each array as item}
+ {#each array as item}
+
+ {:else}
+
+ {/each}
+{/each}
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-each-else/_config.js b/test/css/samples/siblings-combinator-each-else/_config.js
new file mode 100644
index 0000000000..fcd961d5fe
--- /dev/null
+++ b/test/css/samples/siblings-combinator-each-else/_config.js
@@ -0,0 +1,32 @@
+export default {
+ warnings: [
+ {
+ code: "css-unused-selector",
+ frame: `
+ 10:
+ 11: /* no match */
+ 12: .a + .d { color: green; }
+ ^
+ 13: .b + .c { color: green; }
+ 14: `,
+ message: 'Unused CSS selector ".a + .d"',
+ pos: 172,
+ start: { character: 172, column: 1, line: 12 },
+ end: { character: 179, column: 8, line: 12 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 11: /* no match */
+ 12: .a + .d { color: green; }
+ 13: .b + .c { color: green; }
+ ^
+ 14:
+ 15:`,
+ message: 'Unused CSS selector ".b + .c"',
+ pos: 199,
+ start: { character: 199, column: 1, line: 13 },
+ end: { character: 206, column: 8, line: 13 }
+ }
+ ]
+};
diff --git a/test/css/samples/siblings-combinator-each-else/expected.css b/test/css/samples/siblings-combinator-each-else/expected.css
new file mode 100644
index 0000000000..f82bca8215
--- /dev/null
+++ b/test/css/samples/siblings-combinator-each-else/expected.css
@@ -0,0 +1 @@
+.a.svelte-xyz+.b.svelte-xyz{color:green}.a.svelte-xyz+.c.svelte-xyz{color:green}.b.svelte-xyz+.d.svelte-xyz{color:green}.c.svelte-xyz+.d.svelte-xyz{color:green}
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-each-else/expected.html b/test/css/samples/siblings-combinator-each-else/expected.html
new file mode 100644
index 0000000000..fb838a55fd
--- /dev/null
+++ b/test/css/samples/siblings-combinator-each-else/expected.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-each-else/input.svelte b/test/css/samples/siblings-combinator-each-else/input.svelte
new file mode 100644
index 0000000000..ecd48968c2
--- /dev/null
+++ b/test/css/samples/siblings-combinator-each-else/input.svelte
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+{#each array as item}
+
+{:else}
+
+{/each}
+
+
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-each-nested/_config.js b/test/css/samples/siblings-combinator-each-nested/_config.js
new file mode 100644
index 0000000000..ce9c46fb06
--- /dev/null
+++ b/test/css/samples/siblings-combinator-each-nested/_config.js
@@ -0,0 +1,116 @@
+export default {
+ warnings: [
+ {
+ code: "css-unused-selector",
+ frame: `
+ 56:
+ 57: /* no match */
+ 58: .a + .h { color: green; }
+ ^
+ 59: .a + .i { color: green; }
+ 60: .c + .h { color: green; }`,
+ message: 'Unused CSS selector ".a + .h"',
+ pos: 1229,
+ start: { character: 1229, column: 1, line: 58 },
+ end: { character: 1236, column: 8, line: 58 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 57: /* no match */
+ 58: .a + .h { color: green; }
+ 59: .a + .i { color: green; }
+ ^
+ 60: .c + .h { color: green; }
+ 61: .c + .i { color: green; }`,
+ message: 'Unused CSS selector ".a + .i"',
+ pos: 1256,
+ start: { character: 1256, column: 1, line: 59 },
+ end: { character: 1263, column: 8, line: 59 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 58: .a + .h { color: green; }
+ 59: .a + .i { color: green; }
+ 60: .c + .h { color: green; }
+ ^
+ 61: .c + .i { color: green; }
+ 62: .d + .f { color: green; }`,
+ message: 'Unused CSS selector ".c + .h"',
+ pos: 1283,
+ start: { character: 1283, column: 1, line: 60 },
+ end: { character: 1290, column: 8, line: 60 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 59: .a + .i { color: green; }
+ 60: .c + .h { color: green; }
+ 61: .c + .i { color: green; }
+ ^
+ 62: .d + .f { color: green; }
+ 63: .d + .g { color: green; }`,
+ message: 'Unused CSS selector ".c + .i"',
+ pos: 1310,
+ start: { character: 1310, column: 1, line: 61 },
+ end: { character: 1317, column: 8, line: 61 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 60: .c + .h { color: green; }
+ 61: .c + .i { color: green; }
+ 62: .d + .f { color: green; }
+ ^
+ 63: .d + .g { color: green; }
+ 64: .e + .g { color: green; }`,
+ message: 'Unused CSS selector ".d + .f"',
+ pos: 1337,
+ start: { character: 1337, column: 1, line: 62 },
+ end: { character: 1344, column: 8, line: 62 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 61: .c + .i { color: green; }
+ 62: .d + .f { color: green; }
+ 63: .d + .g { color: green; }
+ ^
+ 64: .e + .g { color: green; }
+ 65: .g + .i { color: green; }`,
+ message: 'Unused CSS selector ".d + .g"',
+ pos: 1364,
+ start: { character: 1364, column: 1, line: 63 },
+ end: { character: 1371, column: 8, line: 63 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 62: .d + .f { color: green; }
+ 63: .d + .g { color: green; }
+ 64: .e + .g { color: green; }
+ ^
+ 65: .g + .i { color: green; }
+ 66: `,
+ message: 'Unused CSS selector ".e + .g"',
+ pos: 1391,
+ start: { character: 1391, column: 1, line: 64 },
+ end: { character: 1398, column: 8, line: 64 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 63: .d + .g { color: green; }
+ 64: .e + .g { color: green; }
+ 65: .g + .i { color: green; }
+ ^
+ 66:
+ 67:`,
+ message: 'Unused CSS selector ".g + .i"',
+ pos: 1418,
+ start: { character: 1418, column: 1, line: 65 },
+ end: { character: 1425, column: 8, line: 65 }
+ }
+ ]
+};
diff --git a/test/css/samples/siblings-combinator-each-nested/expected.css b/test/css/samples/siblings-combinator-each-nested/expected.css
new file mode 100644
index 0000000000..616d739670
--- /dev/null
+++ b/test/css/samples/siblings-combinator-each-nested/expected.css
@@ -0,0 +1 @@
+.a.svelte-xyz+.d.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz+.e.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz+.f.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz+.g.svelte-xyz.svelte-xyz{color:green}.c.svelte-xyz+.d.svelte-xyz.svelte-xyz{color:green}.c.svelte-xyz+.e.svelte-xyz.svelte-xyz{color:green}.c.svelte-xyz+.f.svelte-xyz.svelte-xyz{color:green}.c.svelte-xyz+.g.svelte-xyz.svelte-xyz{color:green}.j.svelte-xyz+.m.svelte-xyz.svelte-xyz{color:green}.j.svelte-xyz+.n.svelte-xyz.svelte-xyz{color:green}.j.svelte-xyz+.o.svelte-xyz.svelte-xyz{color:green}.k.svelte-xyz+.m.svelte-xyz.svelte-xyz{color:green}.k.svelte-xyz+.n.svelte-xyz.svelte-xyz{color:green}.k.svelte-xyz+.o.svelte-xyz.svelte-xyz{color:green}.l.svelte-xyz+.m.svelte-xyz.svelte-xyz{color:green}.l.svelte-xyz+.n.svelte-xyz.svelte-xyz{color:green}.l.svelte-xyz+.o.svelte-xyz.svelte-xyz{color:green}.d.svelte-xyz+.e.svelte-xyz.svelte-xyz{color:green}.e.svelte-xyz+.f.svelte-xyz.svelte-xyz{color:green}.g.svelte-xyz+.h.svelte-xyz.svelte-xyz{color:green}.f.svelte-xyz+.d.svelte-xyz.svelte-xyz{color:green}.f.svelte-xyz+.e.svelte-xyz.svelte-xyz{color:green}.f.svelte-xyz+.f.svelte-xyz.svelte-xyz{color:green}.h.svelte-xyz+.g.svelte-xyz.svelte-xyz{color:green}.i.svelte-xyz+.h.svelte-xyz.svelte-xyz{color:green}.i.svelte-xyz+.g.svelte-xyz.svelte-xyz{color:green}.d.svelte-xyz+.d.svelte-xyz.svelte-xyz{color:green}.e.svelte-xyz+.e.svelte-xyz.svelte-xyz{color:green}.f.svelte-xyz+.f.svelte-xyz.svelte-xyz{color:green}.g.svelte-xyz+.g.svelte-xyz.svelte-xyz{color:green}.h.svelte-xyz+.h.svelte-xyz.svelte-xyz{color:green}.i.svelte-xyz+.i.svelte-xyz.svelte-xyz{color:green}.e.svelte-xyz+.e.svelte-xyz+.f.svelte-xyz{color:green}.e.svelte-xyz+.e.svelte-xyz+.d.svelte-xyz{color:green}.h.svelte-xyz+.h.svelte-xyz+.i.svelte-xyz{color:green}.h.svelte-xyz+.h.svelte-xyz+.g.svelte-xyz{color:green}
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-each-nested/expected.html b/test/css/samples/siblings-combinator-each-nested/expected.html
new file mode 100644
index 0000000000..340d6fc4c8
--- /dev/null
+++ b/test/css/samples/siblings-combinator-each-nested/expected.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-each-nested/input.svelte b/test/css/samples/siblings-combinator-each-nested/input.svelte
new file mode 100644
index 0000000000..b5b9242675
--- /dev/null
+++ b/test/css/samples/siblings-combinator-each-nested/input.svelte
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+{#each array as item}
+
+
+{/each}
+
+{#each array as item}
+ {#each array as item}
+ {#each array as item}
+
+ {/each}
+
+ {/each}
+
+{/each}
+
+{#each array as item}
+
+ {#each array as item}
+
+ {#each array as item}
+
+ {/each}
+ {/each}
+{/each}
+
+{#each array as item}
+
+ {#each array as item}
+
+ {#each array as item}
+
+ {/each}
+ {/each}
+{/each}
+
+{#each array as item}
+ {#each array as item}
+ {#each array as item}
+
+ {/each}
+
+ {/each}
+
+{/each}
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-each/expected.css b/test/css/samples/siblings-combinator-each/expected.css
new file mode 100644
index 0000000000..a46fc4ed4e
--- /dev/null
+++ b/test/css/samples/siblings-combinator-each/expected.css
@@ -0,0 +1 @@
+div.svelte-xyz+span.svelte-xyz{color:green}
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-each/expected.html b/test/css/samples/siblings-combinator-each/expected.html
new file mode 100644
index 0000000000..9d0416f01b
--- /dev/null
+++ b/test/css/samples/siblings-combinator-each/expected.html
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/test/css/samples/siblings-combinator-each/input.svelte b/test/css/samples/siblings-combinator-each/input.svelte
new file mode 100644
index 0000000000..de77a25180
--- /dev/null
+++ b/test/css/samples/siblings-combinator-each/input.svelte
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+{#each array as item}
+
+
+
+
+{/each}
+
+
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-if-not-exhaustive-with-each/_config.js b/test/css/samples/siblings-combinator-if-not-exhaustive-with-each/_config.js
new file mode 100644
index 0000000000..0155bf0cf1
--- /dev/null
+++ b/test/css/samples/siblings-combinator-if-not-exhaustive-with-each/_config.js
@@ -0,0 +1,18 @@
+export default {
+ warnings: [
+ {
+ code: "css-unused-selector",
+ frame: `
+ 16:
+ 17: /* no match */
+ 18: .b + .c { color: green; }
+ ^
+ 19:
+ 20:`,
+ message: 'Unused CSS selector ".b + .c"',
+ pos: 319,
+ start: { character: 319, column: 1, line: 18 },
+ end: { character: 326, column: 8, line: 18 }
+ }
+ ]
+};
diff --git a/test/css/samples/siblings-combinator-if-not-exhaustive-with-each/expected.css b/test/css/samples/siblings-combinator-if-not-exhaustive-with-each/expected.css
new file mode 100644
index 0000000000..93cd539f00
--- /dev/null
+++ b/test/css/samples/siblings-combinator-if-not-exhaustive-with-each/expected.css
@@ -0,0 +1 @@
+.a.svelte-xyz+.b.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz+.c.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz+.d.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.b.svelte-xyz+.d.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.c.svelte-xyz+.d.svelte-xyz.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz+.c.svelte-xyz+.c.svelte-xyz.svelte-xyz{color:green}.c.svelte-xyz+.c.svelte-xyz+.d.svelte-xyz.svelte-xyz{color:green}.a.svelte-xyz+.c.svelte-xyz+.c.svelte-xyz+.d.svelte-xyz{color:green}
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-if-not-exhaustive-with-each/expected.html b/test/css/samples/siblings-combinator-if-not-exhaustive-with-each/expected.html
new file mode 100644
index 0000000000..fb838a55fd
--- /dev/null
+++ b/test/css/samples/siblings-combinator-if-not-exhaustive-with-each/expected.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-if-not-exhaustive-with-each/input.svelte b/test/css/samples/siblings-combinator-if-not-exhaustive-with-each/input.svelte
new file mode 100644
index 0000000000..ff9f10dc1c
--- /dev/null
+++ b/test/css/samples/siblings-combinator-if-not-exhaustive-with-each/input.svelte
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+{#if foo}
+
+{:else}
+ {#each array as item}
+
+ {/each}
+{/if}
+
+
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-if-not-exhaustive/_config.js b/test/css/samples/siblings-combinator-if-not-exhaustive/_config.js
new file mode 100644
index 0000000000..e01c358eff
--- /dev/null
+++ b/test/css/samples/siblings-combinator-if-not-exhaustive/_config.js
@@ -0,0 +1,18 @@
+export default {
+ warnings: [
+ {
+ code: "css-unused-selector",
+ frame: `
+ 12:
+ 13: /* no match */
+ 14: .b + .c { color: green; }
+ ^
+ 15:
+ 16:`,
+ message: 'Unused CSS selector ".b + .c"',
+ pos: 215,
+ start: { character: 215, column: 1, line: 14 },
+ end: { character: 222, column: 8, line: 14 }
+ }
+ ]
+};
diff --git a/test/css/samples/siblings-combinator-if-not-exhaustive/expected.css b/test/css/samples/siblings-combinator-if-not-exhaustive/expected.css
new file mode 100644
index 0000000000..b1225e36a1
--- /dev/null
+++ b/test/css/samples/siblings-combinator-if-not-exhaustive/expected.css
@@ -0,0 +1 @@
+.a.svelte-xyz+.b.svelte-xyz{color:green}.a.svelte-xyz+.c.svelte-xyz{color:green}.a.svelte-xyz+.d.svelte-xyz{color:green}.b.svelte-xyz+.d.svelte-xyz{color:green}.c.svelte-xyz+.d.svelte-xyz{color:green}
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-if-not-exhaustive/expected.html b/test/css/samples/siblings-combinator-if-not-exhaustive/expected.html
new file mode 100644
index 0000000000..813e778dc6
--- /dev/null
+++ b/test/css/samples/siblings-combinator-if-not-exhaustive/expected.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-if-not-exhaustive/input.svelte b/test/css/samples/siblings-combinator-if-not-exhaustive/input.svelte
new file mode 100644
index 0000000000..4f832b3b47
--- /dev/null
+++ b/test/css/samples/siblings-combinator-if-not-exhaustive/input.svelte
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+{#if foo}
+
+{:else if bar}
+
+{/if}
+
+
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-if/_config.js b/test/css/samples/siblings-combinator-if/_config.js
new file mode 100644
index 0000000000..c2396f08ef
--- /dev/null
+++ b/test/css/samples/siblings-combinator-if/_config.js
@@ -0,0 +1,60 @@
+export default {
+ warnings: [
+ {
+ code: "css-unused-selector",
+ frame: `
+ 13:
+ 14: /* no match */
+ 15: .a + .e { color: green; }
+ ^
+ 16: .b + .c { color: green; }
+ 17: .b + .d { color: green; }`,
+ message: 'Unused CSS selector ".a + .e"',
+ pos: 242,
+ start: { character: 242, column: 1, line: 15 },
+ end: { character: 249, column: 8, line: 15 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 14: /* no match */
+ 15: .a + .e { color: green; }
+ 16: .b + .c { color: green; }
+ ^
+ 17: .b + .d { color: green; }
+ 18: .c + .d { color: green; }`,
+ message: 'Unused CSS selector ".b + .c"',
+ pos: 269,
+ start: { character: 269, column: 1, line: 16 },
+ end: { character: 276, column: 8, line: 16 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 15: .a + .e { color: green; }
+ 16: .b + .c { color: green; }
+ 17: .b + .d { color: green; }
+ ^
+ 18: .c + .d { color: green; }
+ 19: `,
+ message: 'Unused CSS selector ".b + .d"',
+ pos: 296,
+ start: { character: 296, column: 1, line: 17 },
+ end: { character: 303, column: 8, line: 17 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 16: .b + .c { color: green; }
+ 17: .b + .d { color: green; }
+ 18: .c + .d { color: green; }
+ ^
+ 19:
+ 20:`,
+ message: 'Unused CSS selector ".c + .d"',
+ pos: 323,
+ start: { character: 323, column: 1, line: 18 },
+ end: { character: 330, column: 8, line: 18 }
+ }
+ ]
+};
diff --git a/test/css/samples/siblings-combinator-if/expected.css b/test/css/samples/siblings-combinator-if/expected.css
new file mode 100644
index 0000000000..5ea39be7c2
--- /dev/null
+++ b/test/css/samples/siblings-combinator-if/expected.css
@@ -0,0 +1 @@
+.a.svelte-xyz+.b.svelte-xyz{color:green}.a.svelte-xyz+.c.svelte-xyz{color:green}.a.svelte-xyz+.d.svelte-xyz{color:green}.b.svelte-xyz+.e.svelte-xyz{color:green}.c.svelte-xyz+.e.svelte-xyz{color:green}.d.svelte-xyz+.e.svelte-xyz{color:green}
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-if/expected.html b/test/css/samples/siblings-combinator-if/expected.html
new file mode 100644
index 0000000000..3d8ac9f966
--- /dev/null
+++ b/test/css/samples/siblings-combinator-if/expected.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-if/input.svelte b/test/css/samples/siblings-combinator-if/input.svelte
new file mode 100644
index 0000000000..6cfc436876
--- /dev/null
+++ b/test/css/samples/siblings-combinator-if/input.svelte
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+{#if foo}
+
+{:else if bar}
+
+{:else}
+
+{/if}
+
+
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-slot/_config.js b/test/css/samples/siblings-combinator-slot/_config.js
new file mode 100644
index 0000000000..c0a00500d4
--- /dev/null
+++ b/test/css/samples/siblings-combinator-slot/_config.js
@@ -0,0 +1,46 @@
+export default {
+ warnings: [
+ {
+ code: "css-unused-selector",
+ frame: `
+ 7:
+ 8: /* no match */
+ 9: .a + .b { color: green; }
+ ^
+ 10: .b + .c { color: green; }
+ 11: .c + .f { color: green; }`,
+ message: 'Unused CSS selector ".a + .b"',
+ pos: 84,
+ start: { character: 84, column: 1, line: 9 },
+ end: { character: 91, column: 8, line: 9 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 8: /* no match */
+ 9: .a + .b { color: green; }
+ 10: .b + .c { color: green; }
+ ^
+ 11: .c + .f { color: green; }
+ 12: `,
+ message: 'Unused CSS selector ".b + .c"',
+ pos: 111,
+ start: { character: 111, column: 1, line: 10 },
+ end: { character: 118, column: 8, line: 10 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 9: .a + .b { color: green; }
+ 10: .b + .c { color: green; }
+ 11: .c + .f { color: green; }
+ ^
+ 12:
+ 13:`,
+ message: 'Unused CSS selector ".c + .f"',
+ pos: 138,
+ start: { character: 138, column: 1, line: 11 },
+ end: { character: 145, column: 8, line: 11 }
+ }
+ ]
+};
diff --git a/test/css/samples/siblings-combinator-slot/expected.css b/test/css/samples/siblings-combinator-slot/expected.css
new file mode 100644
index 0000000000..bbdd03f274
--- /dev/null
+++ b/test/css/samples/siblings-combinator-slot/expected.css
@@ -0,0 +1 @@
+.d.svelte-xyz+.e.svelte-xyz{color:green}
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-slot/input.svelte b/test/css/samples/siblings-combinator-slot/input.svelte
new file mode 100644
index 0000000000..42fe0e4c30
--- /dev/null
+++ b/test/css/samples/siblings-combinator-slot/input.svelte
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-star/_config.js b/test/css/samples/siblings-combinator-star/_config.js
new file mode 100644
index 0000000000..32ff416981
--- /dev/null
+++ b/test/css/samples/siblings-combinator-star/_config.js
@@ -0,0 +1,18 @@
+export default {
+ warnings: [
+ {
+ code: "css-unused-selector",
+ frame: `
+ 3: margin-left: 4px;
+ 4: }
+ 5: .not-match > * + * {
+ ^
+ 6: margin-left: 4px;
+ 7: }`,
+ message: 'Unused CSS selector ".not-match > * + *"',
+ pos: 50,
+ start: { character: 50, column: 1, line: 5 },
+ end: { character: 68, column: 19, line: 5 }
+ }
+ ]
+};
diff --git a/test/css/samples/siblings-combinator-star/expected.css b/test/css/samples/siblings-combinator-star/expected.css
new file mode 100644
index 0000000000..c1a06945f9
--- /dev/null
+++ b/test/css/samples/siblings-combinator-star/expected.css
@@ -0,0 +1 @@
+.match.svelte-xyz>.svelte-xyz+.svelte-xyz{margin-left:4px}
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-star/expected.html b/test/css/samples/siblings-combinator-star/expected.html
new file mode 100644
index 0000000000..1cfae6e6f7
--- /dev/null
+++ b/test/css/samples/siblings-combinator-star/expected.html
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-star/input.svelte b/test/css/samples/siblings-combinator-star/input.svelte
new file mode 100644
index 0000000000..ca837f2239
--- /dev/null
+++ b/test/css/samples/siblings-combinator-star/input.svelte
@@ -0,0 +1,17 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator/_config.js b/test/css/samples/siblings-combinator/_config.js
new file mode 100644
index 0000000000..2285b5a837
--- /dev/null
+++ b/test/css/samples/siblings-combinator/_config.js
@@ -0,0 +1,62 @@
+export default {
+ warnings: [
+ {
+ code: "css-unused-selector",
+ frame: `
+ 3: color: green;
+ 4: }
+ 5: article + div {
+ ^
+ 6: color: green;
+ 7: }`,
+ message: 'Unused CSS selector "article + div"',
+ pos: 45,
+ start: { character: 45, column: 1, line: 5 },
+ end: { character: 58, column: 14, line: 5 }
+ },
+ {
+ code: "css-unused-selector",
+
+ frame:`
+ 6: color: green;
+ 7: }
+ 8: span + article {
+ ^
+ 9: color: green;
+ 10: }`,
+ message: 'Unused CSS selector "span + article"',
+ pos: 81,
+ start: { character: 81, column: 1, line: 8 },
+ end: { character: 95, column: 15, line: 8 }
+ },
+ {
+ code: "css-unused-selector",
+
+ frame: `
+ 9: color: green;
+ 10: }
+ 11: b + article {
+ ^
+ 12: color: green;
+ 13: }`,
+ message: 'Unused CSS selector "b + article"',
+ pos: 118,
+ start: { character: 118, column: 1, line: 11 },
+ end: { character: 129, column: 12, line: 11 }
+ },
+ {
+ code: "css-unused-selector",
+ frame: `
+ 12: color: green;
+ 13: }
+ 14: span + div {
+ ^
+ 15: color: green;
+ 16: }`,
+ message: 'Unused CSS selector "span + div"',
+ pos: 152,
+ start: { character: 152, column: 1, line: 14 },
+ end: { character: 162, column: 11, line: 14 }
+ }
+ ]
+};
diff --git a/test/css/samples/siblings-combinator/expected.css b/test/css/samples/siblings-combinator/expected.css
new file mode 100644
index 0000000000..be01048cf1
--- /dev/null
+++ b/test/css/samples/siblings-combinator/expected.css
@@ -0,0 +1 @@
+div.svelte-xyz+article.svelte-xyz.svelte-xyz{color:green}span.svelte-xyz+b.svelte-xyz.svelte-xyz{color:green}div.svelte-xyz span.svelte-xyz+b.svelte-xyz{color:green}.a.svelte-xyz+article.svelte-xyz.svelte-xyz{color:green}div.svelte-xyz+.b.svelte-xyz.svelte-xyz{color:green}
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator/expected.html b/test/css/samples/siblings-combinator/expected.html
new file mode 100644
index 0000000000..f4692365dd
--- /dev/null
+++ b/test/css/samples/siblings-combinator/expected.html
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator/input.svelte b/test/css/samples/siblings-combinator/input.svelte
new file mode 100644
index 0000000000..3e22076d52
--- /dev/null
+++ b/test/css/samples/siblings-combinator/input.svelte
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
\ No newline at end of file
From 0ca1dcd33c598dafc87e41cf363e723b9b7b6f8f Mon Sep 17 00:00:00 2001
From: Conduitry
Date: Thu, 24 Sep 2020 16:07:33 -0400
Subject: [PATCH 51/60] -> v3.27.0
---
CHANGELOG.md | 2 +-
package-lock.json | 2 +-
package.json | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a18cfac6c3..2734f9dcc4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,6 @@
# Svelte changelog
-## Unreleased
+## 3.27.0
* Add `|nonpassive` event modifier, explicitly passing `passive: false` ([#2068](https://github.com/sveltejs/svelte/issues/2068))
* Scope CSS selectors with `~` and `+` combinators ([#3104](https://github.com/sveltejs/svelte/issues/3104))
diff --git a/package-lock.json b/package-lock.json
index 6ea2f97a27..3ddddec683 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "svelte",
- "version": "3.26.0",
+ "version": "3.27.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/package.json b/package.json
index 6d558f35fc..62b15efb5f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "svelte",
- "version": "3.26.0",
+ "version": "3.27.0",
"description": "Cybernetically enhanced web apps",
"module": "index.mjs",
"main": "index",
From fa7c780bad5a71ab1c3e946ae0ad150fb182b0d3 Mon Sep 17 00:00:00 2001
From: Tan Li Hau
Date: Fri, 25 Sep 2020 22:12:49 +0800
Subject: [PATCH 52/60] {#key} block (#5397)
Co-authored-by: Conduitry
---
CHANGELOG.md | 4 +
site/content/docs/02-template-syntax.md | 27 ++++
src/compiler/compile/nodes/KeyBlock.ts | 19 +++
src/compiler/compile/nodes/interfaces.ts | 2 +
.../compile/nodes/shared/map_children.ts | 2 +
.../compile/render_dom/wrappers/Fragment.ts | 2 +
.../compile/render_dom/wrappers/KeyBlock.ts | 136 ++++++++++++++++++
src/compiler/compile/render_ssr/Renderer.ts | 2 +
.../compile/render_ssr/handlers/KeyBlock.ts | 6 +
src/compiler/parse/state/mustache.ts | 8 +-
test/runtime/samples/key-block-2/_config.js | 14 ++
test/runtime/samples/key-block-2/main.svelte | 8 ++
test/runtime/samples/key-block-3/_config.js | 11 ++
test/runtime/samples/key-block-3/main.svelte | 7 +
.../key-block-array-immutable/_config.js | 15 ++
.../key-block-array-immutable/main.svelte | 14 ++
.../samples/key-block-array/_config.js | 15 ++
.../samples/key-block-array/main.svelte | 12 ++
.../samples/key-block-expression-2/_config.js | 18 +++
.../key-block-expression-2/main.svelte | 17 +++
.../samples/key-block-expression/_config.js | 28 ++++
.../samples/key-block-expression/main.svelte | 9 ++
.../samples/key-block-static/_config.js | 9 ++
.../samples/key-block-static/main.svelte | 8 ++
.../samples/key-block-transition/_config.js | 24 ++++
.../samples/key-block-transition/main.svelte | 17 +++
test/runtime/samples/key-block/_config.js | 17 +++
test/runtime/samples/key-block/main.svelte | 10 ++
28 files changed, 459 insertions(+), 2 deletions(-)
create mode 100644 src/compiler/compile/nodes/KeyBlock.ts
create mode 100644 src/compiler/compile/render_dom/wrappers/KeyBlock.ts
create mode 100644 src/compiler/compile/render_ssr/handlers/KeyBlock.ts
create mode 100644 test/runtime/samples/key-block-2/_config.js
create mode 100644 test/runtime/samples/key-block-2/main.svelte
create mode 100644 test/runtime/samples/key-block-3/_config.js
create mode 100644 test/runtime/samples/key-block-3/main.svelte
create mode 100644 test/runtime/samples/key-block-array-immutable/_config.js
create mode 100644 test/runtime/samples/key-block-array-immutable/main.svelte
create mode 100644 test/runtime/samples/key-block-array/_config.js
create mode 100644 test/runtime/samples/key-block-array/main.svelte
create mode 100644 test/runtime/samples/key-block-expression-2/_config.js
create mode 100644 test/runtime/samples/key-block-expression-2/main.svelte
create mode 100644 test/runtime/samples/key-block-expression/_config.js
create mode 100644 test/runtime/samples/key-block-expression/main.svelte
create mode 100644 test/runtime/samples/key-block-static/_config.js
create mode 100644 test/runtime/samples/key-block-static/main.svelte
create mode 100644 test/runtime/samples/key-block-transition/_config.js
create mode 100644 test/runtime/samples/key-block-transition/main.svelte
create mode 100644 test/runtime/samples/key-block/_config.js
create mode 100644 test/runtime/samples/key-block/main.svelte
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2734f9dcc4..98fe7b876c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# Svelte changelog
+## Unreleased
+
+* Add `{#key}` block for keying arbitrary content on an expression ([#1469](https://github.com/sveltejs/svelte/issues/1469))
+
## 3.27.0
* Add `|nonpassive` event modifier, explicitly passing `passive: false` ([#2068](https://github.com/sveltejs/svelte/issues/2068))
diff --git a/site/content/docs/02-template-syntax.md b/site/content/docs/02-template-syntax.md
index f7e1ae3517..d955f650e2 100644
--- a/site/content/docs/02-template-syntax.md
+++ b/site/content/docs/02-template-syntax.md
@@ -342,6 +342,33 @@ If you don't care about the pending state, you can also omit the initial block.
{/await}
```
+### {#key ...}
+
+```sv
+{#key expression}...{/key}
+```
+
+Key blocks destroy and recreate their contents when the value of an expression changes.
+
+---
+
+This is useful if you want an element to play its transition whenever a value changes.
+
+```sv
+{#key value}
+ {value}
+{/key}
+```
+
+---
+
+When used around components, this will cause them to be reinstantiated and reinitialised.
+
+```sv
+{#key value}
+
+{/key}
+```
### {@html ...}
diff --git a/src/compiler/compile/nodes/KeyBlock.ts b/src/compiler/compile/nodes/KeyBlock.ts
new file mode 100644
index 0000000000..356210b6b0
--- /dev/null
+++ b/src/compiler/compile/nodes/KeyBlock.ts
@@ -0,0 +1,19 @@
+import Expression from "./shared/Expression";
+import map_children from "./shared/map_children";
+import AbstractBlock from "./shared/AbstractBlock";
+
+export default class KeyBlock extends AbstractBlock {
+ type: "KeyBlock";
+
+ expression: Expression;
+
+ constructor(component, parent, scope, info) {
+ super(component, parent, scope, info);
+
+ this.expression = new Expression(component, this, scope, info.expression);
+
+ this.children = map_children(component, this, scope, info.children);
+
+ this.warn_if_empty_block();
+ }
+}
diff --git a/src/compiler/compile/nodes/interfaces.ts b/src/compiler/compile/nodes/interfaces.ts
index 752168a49d..51d7b17e07 100644
--- a/src/compiler/compile/nodes/interfaces.ts
+++ b/src/compiler/compile/nodes/interfaces.ts
@@ -18,6 +18,7 @@ import Fragment from './Fragment';
import Head from './Head';
import IfBlock from './IfBlock';
import InlineComponent from './InlineComponent';
+import KeyBlock from './KeyBlock';
import Let from './Let';
import MustacheTag from './MustacheTag';
import Options from './Options';
@@ -50,6 +51,7 @@ export type INode = Action
| Head
| IfBlock
| InlineComponent
+| KeyBlock
| Let
| MustacheTag
| Options
diff --git a/src/compiler/compile/nodes/shared/map_children.ts b/src/compiler/compile/nodes/shared/map_children.ts
index dcdc52f86d..5d5da223fb 100644
--- a/src/compiler/compile/nodes/shared/map_children.ts
+++ b/src/compiler/compile/nodes/shared/map_children.ts
@@ -6,6 +6,7 @@ import Element from '../Element';
import Head from '../Head';
import IfBlock from '../IfBlock';
import InlineComponent from '../InlineComponent';
+import KeyBlock from '../KeyBlock';
import MustacheTag from '../MustacheTag';
import Options from '../Options';
import RawMustacheTag from '../RawMustacheTag';
@@ -28,6 +29,7 @@ function get_constructor(type) {
case 'Head': return Head;
case 'IfBlock': return IfBlock;
case 'InlineComponent': return InlineComponent;
+ case 'KeyBlock': return KeyBlock;
case 'MustacheTag': return MustacheTag;
case 'Options': return Options;
case 'RawMustacheTag': return RawMustacheTag;
diff --git a/src/compiler/compile/render_dom/wrappers/Fragment.ts b/src/compiler/compile/render_dom/wrappers/Fragment.ts
index a0984b69b9..853a41f3cc 100644
--- a/src/compiler/compile/render_dom/wrappers/Fragment.ts
+++ b/src/compiler/compile/render_dom/wrappers/Fragment.ts
@@ -6,6 +6,7 @@ import EachBlock from './EachBlock';
import Element from './Element/index';
import Head from './Head';
import IfBlock from './IfBlock';
+import KeyBlock from './KeyBlock';
import InlineComponent from './InlineComponent/index';
import MustacheTag from './MustacheTag';
import RawMustacheTag from './RawMustacheTag';
@@ -30,6 +31,7 @@ const wrappers = {
Head,
IfBlock,
InlineComponent,
+ KeyBlock,
MustacheTag,
Options: null,
RawMustacheTag,
diff --git a/src/compiler/compile/render_dom/wrappers/KeyBlock.ts b/src/compiler/compile/render_dom/wrappers/KeyBlock.ts
new file mode 100644
index 0000000000..359fb6946f
--- /dev/null
+++ b/src/compiler/compile/render_dom/wrappers/KeyBlock.ts
@@ -0,0 +1,136 @@
+import Wrapper from "./shared/Wrapper";
+import Renderer from "../Renderer";
+import Block from "../Block";
+import EachBlock from "../../nodes/EachBlock";
+import KeyBlock from "../../nodes/KeyBlock";
+import create_debugging_comment from "./shared/create_debugging_comment";
+import FragmentWrapper from "./Fragment";
+import { b, x } from "code-red";
+import { Identifier } from "estree";
+
+export default class KeyBlockWrapper extends Wrapper {
+ node: KeyBlock;
+ fragment: FragmentWrapper;
+ block: Block;
+ dependencies: string[];
+ var: Identifier = { type: "Identifier", name: "key_block" };
+
+ constructor(
+ renderer: Renderer,
+ block: Block,
+ parent: Wrapper,
+ node: EachBlock,
+ strip_whitespace: boolean,
+ next_sibling: Wrapper
+ ) {
+ super(renderer, block, parent, node);
+
+ this.cannot_use_innerhtml();
+ this.not_static_content();
+
+ this.dependencies = node.expression.dynamic_dependencies();
+
+ if (this.dependencies.length) {
+ block = block.child({
+ comment: create_debugging_comment(node, renderer.component),
+ name: renderer.component.get_unique_name("create_key_block"),
+ type: "key"
+ });
+ renderer.blocks.push(block);
+ }
+
+ this.block = block;
+ this.fragment = new FragmentWrapper(
+ renderer,
+ this.block,
+ node.children,
+ parent,
+ strip_whitespace,
+ next_sibling
+ );
+ }
+
+ render(block: Block, parent_node: Identifier, parent_nodes: Identifier) {
+ if (this.dependencies.length === 0) {
+ this.render_static_key(block, parent_node, parent_nodes);
+ } else {
+ this.render_dynamic_key(block, parent_node, parent_nodes);
+ }
+ }
+
+ render_static_key(_block: Block, parent_node: Identifier, parent_nodes: Identifier) {
+ this.fragment.render(this.block, parent_node, parent_nodes);
+ }
+
+ render_dynamic_key(block: Block, parent_node: Identifier, parent_nodes: Identifier) {
+ this.fragment.render(
+ this.block,
+ null,
+ (x`#nodes` as unknown) as Identifier
+ );
+
+ const has_transitions = !!(
+ this.block.has_intro_method || this.block.has_outro_method
+ );
+ const dynamic = this.block.has_update_method;
+
+ const previous_key = block.get_unique_name('previous_key');
+ const snippet = this.node.expression.manipulate(block);
+ block.add_variable(previous_key, snippet);
+
+ const not_equal = this.renderer.component.component_options.immutable ? x`@not_equal` : x`@safe_not_equal`;
+ const condition = x`${this.renderer.dirty(this.dependencies)} && ${not_equal}(${previous_key}, ${previous_key} = ${snippet})`;
+
+ block.chunks.init.push(b`
+ let ${this.var} = ${this.block.name}(#ctx);
+ `);
+ block.chunks.create.push(b`${this.var}.c();`);
+ if (this.renderer.options.hydratable) {
+ block.chunks.claim.push(b`${this.var}.l(${parent_nodes});`);
+ }
+ block.chunks.mount.push(
+ b`${this.var}.m(${parent_node || "#target"}, ${
+ parent_node ? "null" : "#anchor"
+ });`
+ );
+ const anchor = this.get_or_create_anchor(block, parent_node, parent_nodes);
+ const body = b`
+ ${
+ has_transitions
+ ? b`
+ @group_outros();
+ @transition_out(${this.var}, 1, 1, @noop);
+ @check_outros();
+ `
+ : b`${this.var}.d(1);`
+ }
+ ${this.var} = ${this.block.name}(#ctx);
+ ${this.var}.c();
+ ${has_transitions && b`@transition_in(${this.var})`}
+ ${this.var}.m(${this.get_update_mount_node(anchor)}, ${anchor});
+ `;
+
+ if (dynamic) {
+ block.chunks.update.push(b`
+ if (${condition}) {
+ ${body}
+ } else {
+ ${this.var}.p(#ctx, #dirty);
+ }
+ `);
+ } else {
+ block.chunks.update.push(b`
+ if (${condition}) {
+ ${body}
+ }
+ `);
+ }
+
+ if (has_transitions) {
+ block.chunks.intro.push(b`@transition_in(${this.var})`);
+ block.chunks.outro.push(b`@transition_out(${this.var})`);
+ }
+
+ block.chunks.destroy.push(b`${this.var}.d(detaching)`);
+ }
+}
diff --git a/src/compiler/compile/render_ssr/Renderer.ts b/src/compiler/compile/render_ssr/Renderer.ts
index fb9216327c..c633ff8b0a 100644
--- a/src/compiler/compile/render_ssr/Renderer.ts
+++ b/src/compiler/compile/render_ssr/Renderer.ts
@@ -7,6 +7,7 @@ import Head from './handlers/Head';
import HtmlTag from './handlers/HtmlTag';
import IfBlock from './handlers/IfBlock';
import InlineComponent from './handlers/InlineComponent';
+import KeyBlock from './handlers/KeyBlock';
import Slot from './handlers/Slot';
import Tag from './handlers/Tag';
import Text from './handlers/Text';
@@ -30,6 +31,7 @@ const handlers: Record = {
Head,
IfBlock,
InlineComponent,
+ KeyBlock,
MustacheTag: Tag, // TODO MustacheTag is an anachronism
Options: noop,
RawMustacheTag: HtmlTag,
diff --git a/src/compiler/compile/render_ssr/handlers/KeyBlock.ts b/src/compiler/compile/render_ssr/handlers/KeyBlock.ts
new file mode 100644
index 0000000000..33a6681280
--- /dev/null
+++ b/src/compiler/compile/render_ssr/handlers/KeyBlock.ts
@@ -0,0 +1,6 @@
+import KeyBlock from '../../nodes/KeyBlock';
+import Renderer, { RenderOptions } from '../Renderer';
+
+export default function(node: KeyBlock, renderer: Renderer, options: RenderOptions) {
+ renderer.render(node.children, options);
+}
diff --git a/src/compiler/parse/state/mustache.ts b/src/compiler/parse/state/mustache.ts
index dc26d994df..b72b77c30b 100644
--- a/src/compiler/parse/state/mustache.ts
+++ b/src/compiler/parse/state/mustache.ts
@@ -38,7 +38,7 @@ export default function mustache(parser: Parser) {
parser.allow_whitespace();
- // {/if}, {/each} or {/await}
+ // {/if}, {/each}, {/await} or {/key}
if (parser.eat('/')) {
let block = parser.current();
let expected;
@@ -63,6 +63,8 @@ export default function mustache(parser: Parser) {
expected = 'each';
} else if (block.type === 'AwaitBlock') {
expected = 'await';
+ } else if (block.type === 'KeyBlock') {
+ expected = 'key';
} else {
parser.error({
code: `unexpected-block-close`,
@@ -221,10 +223,12 @@ export default function mustache(parser: Parser) {
type = 'EachBlock';
} else if (parser.eat('await')) {
type = 'AwaitBlock';
+ } else if (parser.eat('key')) {
+ type = 'KeyBlock';
} else {
parser.error({
code: `expected-block-type`,
- message: `Expected if, each or await`
+ message: `Expected if, each, await or key`
});
}
diff --git a/test/runtime/samples/key-block-2/_config.js b/test/runtime/samples/key-block-2/_config.js
new file mode 100644
index 0000000000..a7c53bd91f
--- /dev/null
+++ b/test/runtime/samples/key-block-2/_config.js
@@ -0,0 +1,14 @@
+// with reactive content beside `key`
+export default {
+ html: `00
`,
+ async test({ assert, component, target, window }) {
+ const div = target.querySelector('div');
+ component.reactive = 2;
+ assert.htmlEqual(target.innerHTML, `02
`);
+ assert.strictEqual(div, target.querySelector('div'));
+
+ component.value = 5;
+ assert.htmlEqual(target.innerHTML, `52
`);
+ assert.notStrictEqual(div, target.querySelector('div'));
+ }
+};
diff --git a/test/runtime/samples/key-block-2/main.svelte b/test/runtime/samples/key-block-2/main.svelte
new file mode 100644
index 0000000000..466d20b10a
--- /dev/null
+++ b/test/runtime/samples/key-block-2/main.svelte
@@ -0,0 +1,8 @@
+
+
+{#key value}
+ {value}{reactive}
+{/key}
\ No newline at end of file
diff --git a/test/runtime/samples/key-block-3/_config.js b/test/runtime/samples/key-block-3/_config.js
new file mode 100644
index 0000000000..4290599cb3
--- /dev/null
+++ b/test/runtime/samples/key-block-3/_config.js
@@ -0,0 +1,11 @@
+// key is not used in the template
+export default {
+ html: `
`,
+ async test({ assert, component, target, window }) {
+ const div = target.querySelector('div');
+
+ component.value = 5;
+ assert.htmlEqual(target.innerHTML, `
`);
+ assert.notStrictEqual(div, target.querySelector('div'));
+ }
+};
diff --git a/test/runtime/samples/key-block-3/main.svelte b/test/runtime/samples/key-block-3/main.svelte
new file mode 100644
index 0000000000..1ed185c732
--- /dev/null
+++ b/test/runtime/samples/key-block-3/main.svelte
@@ -0,0 +1,7 @@
+
+
+{#key value}
+
+{/key}
\ No newline at end of file
diff --git a/test/runtime/samples/key-block-array-immutable/_config.js b/test/runtime/samples/key-block-array-immutable/_config.js
new file mode 100644
index 0000000000..fb94556c0f
--- /dev/null
+++ b/test/runtime/samples/key-block-array-immutable/_config.js
@@ -0,0 +1,15 @@
+export default {
+ html: `1
`,
+ async test({ assert, component, target, window }) {
+ let div = target.querySelector("div");
+ await component.append(2);
+ assert.htmlEqual(target.innerHTML, `1
`);
+ assert.strictEqual(div, target.querySelector("div"));
+
+ div = target.querySelector("div");
+
+ component.array = [3, 4];
+ assert.htmlEqual(target.innerHTML, `3,4
`);
+ assert.notStrictEqual(div, target.querySelector("div"));
+ }
+};
diff --git a/test/runtime/samples/key-block-array-immutable/main.svelte b/test/runtime/samples/key-block-array-immutable/main.svelte
new file mode 100644
index 0000000000..e666275af4
--- /dev/null
+++ b/test/runtime/samples/key-block-array-immutable/main.svelte
@@ -0,0 +1,14 @@
+
+
+
+
+{#key array}
+ {array.join(',')}
+{/key}
\ No newline at end of file
diff --git a/test/runtime/samples/key-block-array/_config.js b/test/runtime/samples/key-block-array/_config.js
new file mode 100644
index 0000000000..05d5fe9995
--- /dev/null
+++ b/test/runtime/samples/key-block-array/_config.js
@@ -0,0 +1,15 @@
+export default {
+ html: `1
`,
+ async test({ assert, component, target, window }) {
+ let div = target.querySelector("div");
+ await component.append(2);
+ assert.htmlEqual(target.innerHTML, `1,2
`);
+ assert.notStrictEqual(div, target.querySelector("div"));
+
+ div = target.querySelector("div");
+
+ component.array = [3, 4];
+ assert.htmlEqual(target.innerHTML, `3,4
`);
+ assert.notStrictEqual(div, target.querySelector("div"));
+ }
+};
diff --git a/test/runtime/samples/key-block-array/main.svelte b/test/runtime/samples/key-block-array/main.svelte
new file mode 100644
index 0000000000..5a4054b043
--- /dev/null
+++ b/test/runtime/samples/key-block-array/main.svelte
@@ -0,0 +1,12 @@
+
+
+{#key array}
+ {array.join(',')}
+{/key}
\ No newline at end of file
diff --git a/test/runtime/samples/key-block-expression-2/_config.js b/test/runtime/samples/key-block-expression-2/_config.js
new file mode 100644
index 0000000000..236c72fa3d
--- /dev/null
+++ b/test/runtime/samples/key-block-expression-2/_config.js
@@ -0,0 +1,18 @@
+export default {
+ html: `3
`,
+ async test({ assert, component, target, window }) {
+ const div = target.querySelector("div");
+
+ await component.mutate();
+ assert.htmlEqual(target.innerHTML, `5
`);
+ assert.strictEqual(div, target.querySelector("div"));
+
+ await component.reassign();
+ assert.htmlEqual(target.innerHTML, `7
`);
+ assert.strictEqual(div, target.querySelector("div"));
+
+ await component.changeKey();
+ assert.htmlEqual(target.innerHTML, `7
`);
+ assert.notStrictEqual(div, target.querySelector("div"));
+ }
+};
diff --git a/test/runtime/samples/key-block-expression-2/main.svelte b/test/runtime/samples/key-block-expression-2/main.svelte
new file mode 100644
index 0000000000..5525f63761
--- /dev/null
+++ b/test/runtime/samples/key-block-expression-2/main.svelte
@@ -0,0 +1,17 @@
+
+
+{#key obj.key}
+ {obj.value}
+{/key}
\ No newline at end of file
diff --git a/test/runtime/samples/key-block-expression/_config.js b/test/runtime/samples/key-block-expression/_config.js
new file mode 100644
index 0000000000..78890988ea
--- /dev/null
+++ b/test/runtime/samples/key-block-expression/_config.js
@@ -0,0 +1,28 @@
+export default {
+ html: `000
`,
+ async test({ assert, component, target, window }) {
+ let div = target.querySelector("div");
+ component.value = 2;
+ assert.htmlEqual(target.innerHTML, `200
`);
+ assert.notStrictEqual(div, target.querySelector("div"));
+
+ div = target.querySelector("div");
+
+ component.anotherValue = 5;
+ assert.htmlEqual(target.innerHTML, `250
`);
+ assert.notStrictEqual(div, target.querySelector("div"));
+
+ div = target.querySelector("div");
+
+ component.thirdValue = 9;
+ assert.htmlEqual(target.innerHTML, `259
`);
+ assert.strictEqual(div, target.querySelector("div"));
+
+ // make dirty while maintain the value of `value + anotherValue`
+ // should update the content, but not recreate the elements
+ await component.$set({ value: 4, anotherValue: 3 });
+
+ assert.htmlEqual(target.innerHTML, `439
`);
+ assert.strictEqual(div, target.querySelector("div"));
+ }
+};
diff --git a/test/runtime/samples/key-block-expression/main.svelte b/test/runtime/samples/key-block-expression/main.svelte
new file mode 100644
index 0000000000..dd752e8b8f
--- /dev/null
+++ b/test/runtime/samples/key-block-expression/main.svelte
@@ -0,0 +1,9 @@
+
+
+{#key value + anotherValue}
+ {value}{anotherValue}{thirdValue}
+{/key}
\ No newline at end of file
diff --git a/test/runtime/samples/key-block-static/_config.js b/test/runtime/samples/key-block-static/_config.js
new file mode 100644
index 0000000000..d5ea0bf687
--- /dev/null
+++ b/test/runtime/samples/key-block-static/_config.js
@@ -0,0 +1,9 @@
+export default {
+ html: `00
`,
+ async test({ assert, component, target, window }) {
+ const div = target.querySelector('div');
+ component.anotherValue = 2;
+ assert.htmlEqual(target.innerHTML, `02
`);
+ assert.strictEqual(div, target.querySelector('div'));
+ }
+};
diff --git a/test/runtime/samples/key-block-static/main.svelte b/test/runtime/samples/key-block-static/main.svelte
new file mode 100644
index 0000000000..e4ee6b5d71
--- /dev/null
+++ b/test/runtime/samples/key-block-static/main.svelte
@@ -0,0 +1,8 @@
+
+
+{#key value}
+ {value}{anotherValue}
+{/key}
\ No newline at end of file
diff --git a/test/runtime/samples/key-block-transition/_config.js b/test/runtime/samples/key-block-transition/_config.js
new file mode 100644
index 0000000000..53de6b333c
--- /dev/null
+++ b/test/runtime/samples/key-block-transition/_config.js
@@ -0,0 +1,24 @@
+export default {
+ html: '0
',
+ async test({ assert, component, target, window, raf }) {
+ component.value = 2;
+
+ const [div1, div2] = target.querySelectorAll('div');
+
+ assert.htmlEqual(div1.outerHTML, '0
');
+ assert.htmlEqual(div2.outerHTML, '2
');
+
+ raf.tick(0);
+
+ assert.equal(div1.foo, 1);
+ assert.equal(div1.oof, 0);
+
+ assert.equal(div2.foo, 0);
+ assert.equal(div2.oof, 1);
+
+ raf.tick(200);
+
+ assert.htmlEqual(target.innerHTML, '2
');
+ assert.equal(div2, target.querySelector('div'));
+ }
+};
diff --git a/test/runtime/samples/key-block-transition/main.svelte b/test/runtime/samples/key-block-transition/main.svelte
new file mode 100644
index 0000000000..d7fb6ec024
--- /dev/null
+++ b/test/runtime/samples/key-block-transition/main.svelte
@@ -0,0 +1,17 @@
+
+
+{#key value}
+ {value}
+{/key}
\ No newline at end of file
diff --git a/test/runtime/samples/key-block/_config.js b/test/runtime/samples/key-block/_config.js
new file mode 100644
index 0000000000..ad206c3b06
--- /dev/null
+++ b/test/runtime/samples/key-block/_config.js
@@ -0,0 +1,17 @@
+export default {
+ html: `0
0
`,
+ async test({ assert, component, target, window }) {
+ let [div1, div2] = target.querySelectorAll('div');
+
+ component.value = 5;
+ assert.htmlEqual(target.innerHTML, `5
0
`);
+ assert.notStrictEqual(div1, target.querySelectorAll('div')[0]);
+ assert.strictEqual(div2, target.querySelectorAll('div')[1]);
+ [div1, div2] = target.querySelectorAll('div');
+
+ component.reactive = 10;
+ assert.htmlEqual(target.innerHTML, `5
10
`);
+ assert.strictEqual(div1, target.querySelectorAll('div')[0]);
+ assert.strictEqual(div2, target.querySelectorAll('div')[1]);
+ }
+};
diff --git a/test/runtime/samples/key-block/main.svelte b/test/runtime/samples/key-block/main.svelte
new file mode 100644
index 0000000000..ac3c340770
--- /dev/null
+++ b/test/runtime/samples/key-block/main.svelte
@@ -0,0 +1,10 @@
+
+
+{#key value}
+ {value}
+{/key}
+
+{reactive}
\ No newline at end of file
From 46a83a50e3ed8f90b188cb152901cc579b189cd2 Mon Sep 17 00:00:00 2001
From: Conduitry
Date: Fri, 25 Sep 2020 10:13:32 -0400
Subject: [PATCH 53/60] -> v3.28.0
---
CHANGELOG.md | 2 +-
package-lock.json | 2 +-
package.json | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 98fe7b876c..a557769607 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,6 @@
# Svelte changelog
-## Unreleased
+## 3.28.0
* Add `{#key}` block for keying arbitrary content on an expression ([#1469](https://github.com/sveltejs/svelte/issues/1469))
diff --git a/package-lock.json b/package-lock.json
index 3ddddec683..4d6b1f8b6a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "svelte",
- "version": "3.27.0",
+ "version": "3.28.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/package.json b/package.json
index 62b15efb5f..7be5b19819 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "svelte",
- "version": "3.27.0",
+ "version": "3.28.0",
"description": "Cybernetically enhanced web apps",
"module": "index.mjs",
"main": "index",
From 296e81af4d26050f9b45fab1291f533e76e4dc2d Mon Sep 17 00:00:00 2001
From: Tan Li Hau
Date: Tue, 29 Sep 2020 22:58:00 +0800
Subject: [PATCH 54/60] add EventSource to the known globals (#5468)
---
CHANGELOG.md | 4 ++++
src/compiler/utils/names.ts | 1 +
2 files changed, 5 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a557769607..b0c5414f11 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# Svelte changelog
+## Unreleased
+
+* Add `EventSource` to known globals ([#5463](https://github.com/sveltejs/svelte/issues/5463))
+
## 3.28.0
* Add `{#key}` block for keying arbitrary content on an expression ([#1469](https://github.com/sveltejs/svelte/issues/1469))
diff --git a/src/compiler/utils/names.ts b/src/compiler/utils/names.ts
index 1fa37660f4..7b6a6f55e0 100644
--- a/src/compiler/utils/names.ts
+++ b/src/compiler/utils/names.ts
@@ -18,6 +18,7 @@ export const globals = new Set([
'Error',
'EvalError',
'Event',
+ 'EventSource',
'fetch',
'global',
'globalThis',
From 3970def5eba83d3b9086fa50fe1e9cffe478fe4d Mon Sep 17 00:00:00 2001
From: Tan Li Hau
Date: Tue, 29 Sep 2020 23:06:27 +0800
Subject: [PATCH 55/60] fix sibling combinators with spread attributes (#5467)
---
CHANGELOG.md | 1 +
src/compiler/compile/css/Selector.ts | 2 +-
.../siblings-combinator-with-spread/_config.js | 3 +++
.../siblings-combinator-with-spread/expected.css | 1 +
.../siblings-combinator-with-spread/expected.html | 2 ++
.../siblings-combinator-with-spread/input.svelte | 11 +++++++++++
6 files changed, 19 insertions(+), 1 deletion(-)
create mode 100644 test/css/samples/siblings-combinator-with-spread/_config.js
create mode 100644 test/css/samples/siblings-combinator-with-spread/expected.css
create mode 100644 test/css/samples/siblings-combinator-with-spread/expected.html
create mode 100644 test/css/samples/siblings-combinator-with-spread/input.svelte
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b0c5414f11..106d091cee 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
## Unreleased
* Add `EventSource` to known globals ([#5463](https://github.com/sveltejs/svelte/issues/5463))
+* Fix compiler exception with `~`/`+` combinators and `{...spread}` attributes ([#5465](https://github.com/sveltejs/svelte/issues/5465))
## 3.28.0
diff --git a/src/compiler/compile/css/Selector.ts b/src/compiler/compile/css/Selector.ts
index c141f6b481..46e9b4c4f0 100644
--- a/src/compiler/compile/css/Selector.ts
+++ b/src/compiler/compile/css/Selector.ts
@@ -406,7 +406,7 @@ function get_possible_element_siblings(node: INode, adjacent_only: boolean): Map
let prev: INode = node;
while (prev = prev.prev) {
if (prev.type === 'Element') {
- if (!prev.attributes.find(attr => attr.name.toLowerCase() === 'slot')) {
+ if (!prev.attributes.find(attr => attr.type === 'Attribute' && attr.name.toLowerCase() === 'slot')) {
result.set(prev, NodeExist.Definitely);
}
diff --git a/test/css/samples/siblings-combinator-with-spread/_config.js b/test/css/samples/siblings-combinator-with-spread/_config.js
new file mode 100644
index 0000000000..c81f1a9f82
--- /dev/null
+++ b/test/css/samples/siblings-combinator-with-spread/_config.js
@@ -0,0 +1,3 @@
+export default {
+ warnings: []
+};
diff --git a/test/css/samples/siblings-combinator-with-spread/expected.css b/test/css/samples/siblings-combinator-with-spread/expected.css
new file mode 100644
index 0000000000..aacf6e7db5
--- /dev/null
+++ b/test/css/samples/siblings-combinator-with-spread/expected.css
@@ -0,0 +1 @@
+input.svelte-xyz:focus+div.svelte-xyz{color:red}input.svelte-xyz:focus~div.svelte-xyz{color:red}
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-with-spread/expected.html b/test/css/samples/siblings-combinator-with-spread/expected.html
new file mode 100644
index 0000000000..d732739701
--- /dev/null
+++ b/test/css/samples/siblings-combinator-with-spread/expected.html
@@ -0,0 +1,2 @@
+
+Should be red, when input is focused
\ No newline at end of file
diff --git a/test/css/samples/siblings-combinator-with-spread/input.svelte b/test/css/samples/siblings-combinator-with-spread/input.svelte
new file mode 100644
index 0000000000..45c889c1ae
--- /dev/null
+++ b/test/css/samples/siblings-combinator-with-spread/input.svelte
@@ -0,0 +1,11 @@
+
+
+
+
+
+Should be red, when input is focused
\ No newline at end of file
From 0645631b28bc9d3429268ef5d2d1ead0feee77f9 Mon Sep 17 00:00:00 2001
From: Ben McCann <322311+benmccann@users.noreply.github.com>
Date: Tue, 29 Sep 2020 08:19:21 -0700
Subject: [PATCH 56/60] Use TypeScript in tests (#5433)
---
.mocharc.js | 13 ++++++-------
test/.eslintrc.json | 3 ++-
test/css/{index.js => index.ts} | 3 +--
test/custom-elements/{index.js => index.ts} | 6 +++---
test/{helpers.js => helpers.ts} | 7 ++++---
test/hydration/{index.js => index.ts} | 16 ++++++----------
test/js/{index.js => index.ts} | 2 +-
test/motion/{index.js => index.ts} | 0
test/parser/{index.js => index.ts} | 4 ++--
test/parser/{update.js => update.ts} | 0
test/preprocess/{index.js => index.ts} | 2 +-
test/runtime/{index.js => index.ts} | 8 ++++----
.../server-side-rendering/{index.js => index.ts} | 6 +++---
test/sourcemaps/{index.js => index.ts} | 2 +-
test/stats/{index.js => index.ts} | 2 +-
test/{test.js => test.ts} | 4 ++--
test/validator/{index.js => index.ts} | 4 ++--
test/vars/{index.js => index.ts} | 2 +-
18 files changed, 40 insertions(+), 44 deletions(-)
rename test/css/{index.js => index.ts} (97%)
rename test/custom-elements/{index.js => index.ts} (96%)
rename test/{helpers.js => helpers.ts} (95%)
rename test/hydration/{index.js => index.ts} (93%)
rename test/js/{index.js => index.ts} (99%)
rename test/motion/{index.js => index.ts} (100%)
rename test/parser/{index.js => index.ts} (96%)
rename test/parser/{update.js => update.ts} (100%)
rename test/preprocess/{index.js => index.ts} (94%)
rename test/runtime/{index.js => index.ts} (97%)
rename test/server-side-rendering/{index.js => index.ts} (98%)
rename test/sourcemaps/{index.js => index.ts} (98%)
rename test/stats/{index.js => index.ts} (95%)
rename test/{test.js => test.ts} (84%)
rename test/validator/{index.js => index.ts} (97%)
rename test/vars/{index.js => index.ts} (95%)
diff --git a/.mocharc.js b/.mocharc.js
index de41cdd353..e55f26099e 100644
--- a/.mocharc.js
+++ b/.mocharc.js
@@ -1,15 +1,14 @@
module.exports = {
file: [
- 'test/test.js'
+ 'test/test.ts'
+ ],
+ require: [
+ 'sucrase/register'
]
};
// add coverage options when running 'npx c8 mocha'
if (process.env.NODE_V8_COVERAGE) {
- Object.assign(module.exports, {
- fullTrace: true,
- require: [
- 'source-map-support/register'
- ]
- });
+ module.exports.fullTrace = true;
+ module.exports.require.push('source-map-support/register');
}
diff --git a/test/.eslintrc.json b/test/.eslintrc.json
index d5ba8f9d9c..fc493fa889 100644
--- a/test/.eslintrc.json
+++ b/test/.eslintrc.json
@@ -1,5 +1,6 @@
{
"rules": {
- "no-console": "off"
+ "no-console": "off",
+ "@typescript-eslint/no-var-requires": "off"
}
}
diff --git a/test/css/index.js b/test/css/index.ts
similarity index 97%
rename from test/css/index.js
rename to test/css/index.ts
index 81d27a94bf..c12418353d 100644
--- a/test/css/index.js
+++ b/test/css/index.ts
@@ -1,6 +1,5 @@
-import * as assert from 'assert';
import * as fs from 'fs';
-import { env, svelte, setupHtmlEqual, shouldUpdateExpected } from '../helpers.js';
+import { assert, env, svelte, setupHtmlEqual, shouldUpdateExpected } from '../helpers';
function try_require(file) {
try {
diff --git a/test/custom-elements/index.js b/test/custom-elements/index.ts
similarity index 96%
rename from test/custom-elements/index.js
rename to test/custom-elements/index.ts
index 1329dbd2cf..25cef932d8 100644
--- a/test/custom-elements/index.js
+++ b/test/custom-elements/index.ts
@@ -2,9 +2,9 @@ import * as fs from 'fs';
import * as path from 'path';
import * as http from 'http';
import { rollup } from 'rollup';
-import * as virtual from '@rollup/plugin-virtual';
-import * as puppeteer from 'puppeteer';
-import { addLineNumbers, loadConfig, loadSvelte } from "../helpers.js";
+import virtual from '@rollup/plugin-virtual';
+import puppeteer from 'puppeteer';
+import { addLineNumbers, loadConfig, loadSvelte } from "../helpers";
import { deepEqual } from 'assert';
const page = `
diff --git a/test/helpers.js b/test/helpers.ts
similarity index 95%
rename from test/helpers.js
rename to test/helpers.ts
index 9ffc4c4764..6452dbae1a 100644
--- a/test/helpers.js
+++ b/test/helpers.ts
@@ -1,9 +1,10 @@
+import * as assert$1 from 'assert';
import * as jsdom from 'jsdom';
-import * as assert from 'assert';
-import * as glob from 'tiny-glob/sync.js';
+import glob from 'tiny-glob/sync';
import * as path from 'path';
import * as fs from 'fs';
import * as colors from 'kleur';
+export const assert = (assert$1 as unknown) as typeof assert$1 & { htmlEqual: (actual, expected, message?) => void };
// for coverage purposes, we need to test source files,
// but for sanity purposes, we need to test dist files
@@ -63,7 +64,7 @@ global.window = window;
// add missing ecmascript globals to window
for (const key of Object.getOwnPropertyNames(global)) {
- window[key] = window[key] || global[key];
+ if (!(key in window)) window[key] = global[key];
}
// implement mock scroll
diff --git a/test/hydration/index.js b/test/hydration/index.ts
similarity index 93%
rename from test/hydration/index.js
rename to test/hydration/index.ts
index f57a0cdc1a..e50351c56e 100644
--- a/test/hydration/index.js
+++ b/test/hydration/index.ts
@@ -1,15 +1,15 @@
-import * as assert from 'assert';
import * as path from 'path';
import * as fs from 'fs';
import {
+ assert,
showOutput,
loadConfig,
loadSvelte,
env,
setupHtmlEqual,
shouldUpdateExpected
-} from '../helpers.js';
+} from '../helpers';
let compileOptions = null;
@@ -58,13 +58,7 @@ describe('hydration', () => {
try {
global.window = window;
- let SvelteComponent;
-
- try {
- SvelteComponent = require(`${cwd}/main.svelte`).default;
- } catch (err) {
- throw err;
- }
+ const SvelteComponent = require(`${cwd}/main.svelte`).default;
const target = window.document.body;
const head = window.document.head;
@@ -75,7 +69,9 @@ describe('hydration', () => {
try {
before_head = fs.readFileSync(`${cwd}/_before_head.html`, 'utf-8');
head.innerHTML = before_head;
- } catch (err) {}
+ } catch (err) {
+ // continue regardless of error
+ }
const snapshot = config.snapshot ? config.snapshot(target) : {};
diff --git a/test/js/index.js b/test/js/index.ts
similarity index 99%
rename from test/js/index.js
rename to test/js/index.ts
index 8aa4cc76a9..3a3643de47 100644
--- a/test/js/index.js
+++ b/test/js/index.ts
@@ -2,7 +2,7 @@ import * as assert from "assert";
import * as fs from "fs";
import * as path from "path";
import * as colors from "kleur";
-import { loadConfig, svelte, shouldUpdateExpected } from "../helpers.js";
+import { loadConfig, svelte, shouldUpdateExpected } from "../helpers";
describe("js", () => {
fs.readdirSync(`${__dirname}/samples`).forEach(dir => {
diff --git a/test/motion/index.js b/test/motion/index.ts
similarity index 100%
rename from test/motion/index.js
rename to test/motion/index.ts
diff --git a/test/parser/index.js b/test/parser/index.ts
similarity index 96%
rename from test/parser/index.js
rename to test/parser/index.ts
index 2c8d516a7d..32afcbbafc 100644
--- a/test/parser/index.js
+++ b/test/parser/index.ts
@@ -1,6 +1,6 @@
import * as assert from 'assert';
import * as fs from 'fs';
-import { svelte, tryToLoadJson, shouldUpdateExpected } from '../helpers.js';
+import { svelte, tryToLoadJson, shouldUpdateExpected } from '../helpers';
describe('parse', () => {
fs.readdirSync(`${__dirname}/samples`).forEach(dir => {
@@ -38,7 +38,7 @@ describe('parse', () => {
} catch (err) {
if (err.name !== 'ParseError') throw err;
if (!expectedError) throw err;
- const { code, message, pos, start } = err
+ const { code, message, pos, start } = err;
try {
assert.deepEqual({ code, message, pos, start }, expectedError);
} catch (err2) {
diff --git a/test/parser/update.js b/test/parser/update.ts
similarity index 100%
rename from test/parser/update.js
rename to test/parser/update.ts
diff --git a/test/preprocess/index.js b/test/preprocess/index.ts
similarity index 94%
rename from test/preprocess/index.js
rename to test/preprocess/index.ts
index 5d83bb6059..abb45012ce 100644
--- a/test/preprocess/index.js
+++ b/test/preprocess/index.ts
@@ -1,6 +1,6 @@
import * as fs from 'fs';
import * as assert from 'assert';
-import { loadConfig, svelte } from '../helpers.js';
+import { loadConfig, svelte } from '../helpers';
describe('preprocess', () => {
fs.readdirSync(`${__dirname}/samples`).forEach(dir => {
diff --git a/test/runtime/index.js b/test/runtime/index.ts
similarity index 97%
rename from test/runtime/index.js
rename to test/runtime/index.ts
index 65157196c9..9e0ba6568f 100644
--- a/test/runtime/index.js
+++ b/test/runtime/index.ts
@@ -1,12 +1,12 @@
-import * as assert from "assert";
import * as path from "path";
import * as fs from "fs";
import { rollup } from 'rollup';
-import * as virtual from '@rollup/plugin-virtual';
-import * as glob from 'tiny-glob/sync.js';
+import virtual from '@rollup/plugin-virtual';
+import glob from 'tiny-glob/sync.js';
import { clear_loops, flush, set_now, set_raf } from "../../internal";
import {
+ assert,
showOutput,
loadConfig,
loadSvelte,
@@ -14,7 +14,7 @@ import {
env,
setupHtmlEqual,
mkdirp
-} from "../helpers.js";
+} from "../helpers";
let svelte$;
let svelte;
diff --git a/test/server-side-rendering/index.js b/test/server-side-rendering/index.ts
similarity index 98%
rename from test/server-side-rendering/index.js
rename to test/server-side-rendering/index.ts
index 2d539782ff..94ab405895 100644
--- a/test/server-side-rendering/index.js
+++ b/test/server-side-rendering/index.ts
@@ -1,9 +1,9 @@
-import * as assert from "assert";
import * as fs from "fs";
import * as path from "path";
-import * as glob from 'tiny-glob/sync.js';
+import glob from 'tiny-glob/sync.js';
import {
+ assert,
showOutput,
loadConfig,
loadSvelte,
@@ -12,7 +12,7 @@ import {
cleanRequireCache,
shouldUpdateExpected,
mkdirp
-} from "../helpers.js";
+} from "../helpers";
function tryToReadFile(file) {
try {
diff --git a/test/sourcemaps/index.js b/test/sourcemaps/index.ts
similarity index 98%
rename from test/sourcemaps/index.js
rename to test/sourcemaps/index.ts
index 0b0424a764..da2d79295c 100644
--- a/test/sourcemaps/index.js
+++ b/test/sourcemaps/index.ts
@@ -1,7 +1,7 @@
import * as fs from "fs";
import * as path from "path";
import * as assert from "assert";
-import { svelte } from "../helpers.js";
+import { svelte } from "../helpers";
import { SourceMapConsumer } from "source-map";
import { getLocator } from "locate-character";
diff --git a/test/stats/index.js b/test/stats/index.ts
similarity index 95%
rename from test/stats/index.js
rename to test/stats/index.ts
index acea7a4663..a6efd93d22 100644
--- a/test/stats/index.js
+++ b/test/stats/index.ts
@@ -1,6 +1,6 @@
import * as fs from 'fs';
import * as assert from 'assert';
-import { svelte, loadConfig, tryToLoadJson } from '../helpers.js';
+import { svelte, loadConfig, tryToLoadJson } from '../helpers';
describe('stats', () => {
fs.readdirSync(`${__dirname}/samples`).forEach(dir => {
diff --git a/test/test.js b/test/test.ts
similarity index 84%
rename from test/test.js
rename to test/test.ts
index 6ea4bd60ab..181e9351c1 100644
--- a/test/test.js
+++ b/test/test.ts
@@ -3,12 +3,12 @@ const glob = require('tiny-glob/sync.js');
require('./setup');
// bind internal to jsdom
-require('./helpers');
+require('./helpers.ts');
require('../internal');
console.clear();
-const test_folders = glob('*/index.js', { cwd: 'test' });
+const test_folders = glob('*/index.ts', { cwd: 'test' });
const solo_folders = test_folders.filter(folder => /\.solo/.test(folder));
if (solo_folders.length) {
diff --git a/test/validator/index.js b/test/validator/index.ts
similarity index 97%
rename from test/validator/index.js
rename to test/validator/index.ts
index 433e1a94ae..e93cf1a4bb 100644
--- a/test/validator/index.js
+++ b/test/validator/index.ts
@@ -1,6 +1,6 @@
import * as fs from "fs";
import * as assert from "assert";
-import { svelte, loadConfig, tryToLoadJson } from "../helpers.js";
+import { svelte, loadConfig, tryToLoadJson } from "../helpers";
describe("validate", () => {
fs.readdirSync(`${__dirname}/samples`).forEach(dir => {
@@ -30,7 +30,7 @@ describe("validate", () => {
legacy: config.legacy,
generate: false,
customElement: config.customElement,
- ...options,
+ ...options
});
assert.deepEqual(warnings.map(w => ({
diff --git a/test/vars/index.js b/test/vars/index.ts
similarity index 95%
rename from test/vars/index.js
rename to test/vars/index.ts
index a12ac177f2..4cebf7fbb4 100644
--- a/test/vars/index.js
+++ b/test/vars/index.ts
@@ -1,6 +1,6 @@
import * as fs from 'fs';
import * as assert from 'assert';
-import { svelte, loadConfig, tryToLoadJson } from '../helpers.js';
+import { svelte, loadConfig, tryToLoadJson } from '../helpers';
describe('vars', () => {
fs.readdirSync(`${__dirname}/samples`).forEach(dir => {
From 8056829a9163a09930a453aff42dacf711943549 Mon Sep 17 00:00:00 2001
From: Tan Li Hau
Date: Tue, 29 Sep 2020 23:55:44 +0800
Subject: [PATCH 57/60] allow to be part of a slot (#4295)
---
CHANGELOG.md | 1 +
.../wrappers/Element/create_slot_block.ts | 61 +++++++++++++++++++
.../render_dom/wrappers/Element/index.ts | 48 +--------------
.../compile/render_dom/wrappers/Slot.ts | 10 +++
.../compile/render_ssr/handlers/Slot.ts | 24 +++++++-
.../component-slot-nested-in-slot/One.svelte | 8 +++
.../component-slot-nested-in-slot/Two.svelte | 4 ++
.../component-slot-nested-in-slot/_config.js | 18 ++++++
.../component-slot-nested-in-slot/main.svelte | 9 +++
9 files changed, 137 insertions(+), 46 deletions(-)
create mode 100644 src/compiler/compile/render_dom/wrappers/Element/create_slot_block.ts
create mode 100644 test/runtime/samples/component-slot-nested-in-slot/One.svelte
create mode 100644 test/runtime/samples/component-slot-nested-in-slot/Two.svelte
create mode 100644 test/runtime/samples/component-slot-nested-in-slot/_config.js
create mode 100644 test/runtime/samples/component-slot-nested-in-slot/main.svelte
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 106d091cee..896a550c3f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
## Unreleased
+* Support `` ([#2079](https://github.com/sveltejs/svelte/issues/2079))
* Add `EventSource` to known globals ([#5463](https://github.com/sveltejs/svelte/issues/5463))
* Fix compiler exception with `~`/`+` combinators and `{...spread}` attributes ([#5465](https://github.com/sveltejs/svelte/issues/5465))
diff --git a/src/compiler/compile/render_dom/wrappers/Element/create_slot_block.ts b/src/compiler/compile/render_dom/wrappers/Element/create_slot_block.ts
new file mode 100644
index 0000000000..7078d4eee7
--- /dev/null
+++ b/src/compiler/compile/render_dom/wrappers/Element/create_slot_block.ts
@@ -0,0 +1,61 @@
+import ElementWrapper from './index';
+import SlotWrapper from '../Slot';
+import Block from '../../Block';
+import { sanitize } from '../../../../utils/names';
+import InlineComponentWrapper from '../InlineComponent';
+import create_debugging_comment from '../shared/create_debugging_comment';
+import { get_slot_definition } from '../shared/get_slot_definition';
+
+export default function create_slot_block(attribute, element: ElementWrapper | SlotWrapper, block: Block) {
+ const owner = find_slot_owner(element.parent);
+
+ if (owner && owner.node.type === 'InlineComponent') {
+ const name = attribute.get_static_value() as string;
+
+ if (!((owner as unknown) as InlineComponentWrapper).slots.has(name)) {
+ const child_block = block.child({
+ comment: create_debugging_comment(element.node, element.renderer.component),
+ name: element.renderer.component.get_unique_name(
+ `create_${sanitize(name)}_slot`
+ ),
+ type: 'slot'
+ });
+
+ const { scope, lets } = element.node;
+ const seen = new Set(lets.map(l => l.name.name));
+
+ ((owner as unknown) as InlineComponentWrapper).node.lets.forEach(l => {
+ if (!seen.has(l.name.name)) lets.push(l);
+ });
+
+ ((owner as unknown) as InlineComponentWrapper).slots.set(
+ name,
+ get_slot_definition(child_block, scope, lets)
+ );
+ element.renderer.blocks.push(child_block);
+ }
+
+ element.slot_block = ((owner as unknown) as InlineComponentWrapper).slots.get(
+ name
+ ).block;
+
+ return element.slot_block;
+ }
+
+ return block;
+}
+
+function find_slot_owner(owner) {
+ while (owner) {
+ if (owner.node.type === 'InlineComponent') {
+ break;
+ }
+
+ if (owner.node.type === 'Element' && /-/.test(owner.node.name)) {
+ break;
+ }
+
+ owner = owner.parent;
+ }
+ return owner;
+}
diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts
index f740560bcd..b99df5242c 100644
--- a/src/compiler/compile/render_dom/wrappers/Element/index.ts
+++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts
@@ -2,7 +2,7 @@ import Renderer from '../../Renderer';
import Element from '../../../nodes/Element';
import Wrapper from '../shared/Wrapper';
import Block from '../../Block';
-import { is_void, sanitize } from '../../../../utils/names';
+import { is_void } from '../../../../utils/names';
import FragmentWrapper from '../Fragment';
import { escape_html, string_literal } from '../../../utils/stringify';
import TextWrapper from '../Text';
@@ -14,12 +14,9 @@ import StyleAttributeWrapper from './StyleAttribute';
import SpreadAttributeWrapper from './SpreadAttribute';
import { dimensions } from '../../../../utils/patterns';
import Binding from './Binding';
-import InlineComponentWrapper from '../InlineComponent';
import add_to_set from '../../../utils/add_to_set';
import { add_event_handler } from '../shared/add_event_handlers';
import { add_action } from '../shared/add_actions';
-import create_debugging_comment from '../shared/create_debugging_comment';
-import { get_slot_definition } from '../shared/get_slot_definition';
import bind_this from '../shared/bind_this';
import { is_head } from '../shared/is_head';
import { Identifier } from 'estree';
@@ -28,6 +25,7 @@ import { extract_names } from 'periscopic';
import Action from '../../../nodes/Action';
import MustacheTagWrapper from '../MustacheTag';
import RawMustacheTagWrapper from '../RawMustacheTag';
+import create_slot_block from './create_slot_block';
interface BindingGroup {
events: string[];
@@ -177,47 +175,7 @@ export default class ElementWrapper extends Wrapper {
this.attributes = this.node.attributes.map(attribute => {
if (attribute.name === 'slot') {
- // TODO make separate subclass for this?
- let owner = this.parent;
- while (owner) {
- if (owner.node.type === 'InlineComponent') {
- break;
- }
-
- if (owner.node.type === 'Element' && /-/.test(owner.node.name)) {
- break;
- }
-
- owner = owner.parent;
- }
-
- if (owner && owner.node.type === 'InlineComponent') {
- const name = attribute.get_static_value() as string;
-
- if (!(owner as unknown as InlineComponentWrapper).slots.has(name)) {
- const child_block = block.child({
- comment: create_debugging_comment(node, this.renderer.component),
- name: this.renderer.component.get_unique_name(`create_${sanitize(name)}_slot`),
- type: 'slot'
- });
-
- const { scope, lets } = this.node;
- const seen = new Set(lets.map(l => l.name.name));
-
- (owner as unknown as InlineComponentWrapper).node.lets.forEach(l => {
- if (!seen.has(l.name.name)) lets.push(l);
- });
-
- (owner as unknown as InlineComponentWrapper).slots.set(
- name,
- get_slot_definition(child_block, scope, lets)
- );
- this.renderer.blocks.push(child_block);
- }
-
- this.slot_block = (owner as unknown as InlineComponentWrapper).slots.get(name).block;
- block = this.slot_block;
- }
+ block = create_slot_block(attribute, this, block);
}
if (attribute.name === 'style') {
return new StyleAttributeWrapper(this, block, attribute);
diff --git a/src/compiler/compile/render_dom/wrappers/Slot.ts b/src/compiler/compile/render_dom/wrappers/Slot.ts
index 5c02e78158..4f8e0c8e57 100644
--- a/src/compiler/compile/render_dom/wrappers/Slot.ts
+++ b/src/compiler/compile/render_dom/wrappers/Slot.ts
@@ -12,11 +12,13 @@ import Expression from '../../nodes/shared/Expression';
import is_dynamic from './shared/is_dynamic';
import { Identifier, ObjectExpression } from 'estree';
import create_debugging_comment from './shared/create_debugging_comment';
+import create_slot_block from './Element/create_slot_block';
export default class SlotWrapper extends Wrapper {
node: Slot;
fragment: FragmentWrapper;
fallback: Block | null = null;
+ slot_block: Block;
var: Identifier = { type: 'Identifier', name: 'slot' };
dependencies: Set = new Set(['$$scope']);
@@ -42,6 +44,10 @@ export default class SlotWrapper extends Wrapper {
renderer.blocks.push(this.fallback);
}
+ if (this.node.values.has('slot')) {
+ block = create_slot_block(this.node.values.get('slot'), this, block);
+ }
+
this.fragment = new FragmentWrapper(
renderer,
this.fallback,
@@ -71,6 +77,10 @@ export default class SlotWrapper extends Wrapper {
const { slot_name } = this.node;
+ if (this.slot_block) {
+ block = this.slot_block;
+ }
+
let get_slot_changes_fn;
let get_slot_context_fn;
diff --git a/src/compiler/compile/render_ssr/handlers/Slot.ts b/src/compiler/compile/render_ssr/handlers/Slot.ts
index 32b6246e64..f89b619c46 100644
--- a/src/compiler/compile/render_ssr/handlers/Slot.ts
+++ b/src/compiler/compile/render_ssr/handlers/Slot.ts
@@ -2,9 +2,18 @@ import Renderer, { RenderOptions } from '../Renderer';
import Slot from '../../nodes/Slot';
import { x } from 'code-red';
import get_slot_data from '../../utils/get_slot_data';
+import { get_slot_scope } from './shared/get_slot_scope';
-export default function(node: Slot, renderer: Renderer, options: RenderOptions) {
+export default function(node: Slot, renderer: Renderer, options: RenderOptions & {
+ slot_scopes: Map;
+}) {
const slot_data = get_slot_data(node.values);
+ const slot = node.get_static_attribute_value('slot');
+ const nearest_inline_component = node.find_nearest(/InlineComponent/);
+
+ if (slot && nearest_inline_component) {
+ renderer.push();
+ }
renderer.push();
renderer.render(node.children, options);
@@ -15,4 +24,17 @@ export default function(node: Slot, renderer: Renderer, options: RenderOptions)
? #slots.${node.slot_name}(${slot_data})
: ${result}
`);
+
+ if (slot && nearest_inline_component) {
+ const lets = node.lets;
+ const seen = new Set(lets.map(l => l.name.name));
+
+ nearest_inline_component.lets.forEach(l => {
+ if (!seen.has(l.name.name)) lets.push(l);
+ });
+ options.slot_scopes.set(slot, {
+ input: get_slot_scope(node.lets),
+ output: renderer.pop()
+ });
+ }
}
diff --git a/test/runtime/samples/component-slot-nested-in-slot/One.svelte b/test/runtime/samples/component-slot-nested-in-slot/One.svelte
new file mode 100644
index 0000000000..e27437c450
--- /dev/null
+++ b/test/runtime/samples/component-slot-nested-in-slot/One.svelte
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git a/test/runtime/samples/component-slot-nested-in-slot/Two.svelte b/test/runtime/samples/component-slot-nested-in-slot/Two.svelte
new file mode 100644
index 0000000000..3f21e2d16f
--- /dev/null
+++ b/test/runtime/samples/component-slot-nested-in-slot/Two.svelte
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/test/runtime/samples/component-slot-nested-in-slot/_config.js b/test/runtime/samples/component-slot-nested-in-slot/_config.js
new file mode 100644
index 0000000000..56a9ef2668
--- /dev/null
+++ b/test/runtime/samples/component-slot-nested-in-slot/_config.js
@@ -0,0 +1,18 @@
+export default {
+ html: `
+ one: 1 two: 2
+ `,
+ test({ assert, component, target }) {
+ component.a = 3;
+ component.b = 4;
+ assert.htmlEqual(target.innerHTML, `
+ one: 3 two: 4
+ `);
+
+ component.a = 5;
+ component.b = 6;
+ assert.htmlEqual(target.innerHTML, `
+ one: 5 two: 6
+ `);
+ }
+};
diff --git a/test/runtime/samples/component-slot-nested-in-slot/main.svelte b/test/runtime/samples/component-slot-nested-in-slot/main.svelte
new file mode 100644
index 0000000000..38a172e668
--- /dev/null
+++ b/test/runtime/samples/component-slot-nested-in-slot/main.svelte
@@ -0,0 +1,9 @@
+
+
+
+ one: {one} two: {two}
+
\ No newline at end of file
From 2c5f1c466ebe1aab2a38e0030c4946954688e227 Mon Sep 17 00:00:00 2001
From: Tan Li Hau
Date: Wed, 30 Sep 2020 02:02:36 +0800
Subject: [PATCH 58/60] add typings to `get_store_value` (#5269)
Co-authored-by: Julian Laubstein
---
CHANGELOG.md | 1 +
src/compiler/compile/index.ts | 3 +--
src/runtime/internal/utils.ts | 6 ++++--
3 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 896a550c3f..d4daf1fc0b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
## Unreleased
* Support `` ([#2079](https://github.com/sveltejs/svelte/issues/2079))
+* Add types to `get` function in `svelte/store` ([#5269](https://github.com/sveltejs/svelte/pull/5269))
* Add `EventSource` to known globals ([#5463](https://github.com/sveltejs/svelte/issues/5463))
* Fix compiler exception with `~`/`+` combinators and `{...spread}` attributes ([#5465](https://github.com/sveltejs/svelte/issues/5465))
diff --git a/src/compiler/compile/index.ts b/src/compiler/compile/index.ts
index 9d56dfae02..c29cbc1dbf 100644
--- a/src/compiler/compile/index.ts
+++ b/src/compiler/compile/index.ts
@@ -1,4 +1,3 @@
-import { assign } from '../../runtime/internal/utils';
import Stats from '../Stats';
import parse from '../parse/index';
import render_dom from './render_dom/index';
@@ -68,7 +67,7 @@ function validate_options(options: CompileOptions, warnings: Warning[]) {
}
export default function compile(source: string, options: CompileOptions = {}) {
- options = assign({ generate: 'dom', dev: false }, options);
+ options = Object.assign({ generate: 'dom', dev: false }, options);
const stats = new Stats();
const warnings = [];
diff --git a/src/runtime/internal/utils.ts b/src/runtime/internal/utils.ts
index 633b6f054c..828cb97c27 100644
--- a/src/runtime/internal/utils.ts
+++ b/src/runtime/internal/utils.ts
@@ -1,3 +1,5 @@
+import { Readable } from "svelte/store";
+
export function noop() {}
export const identity = x => x;
@@ -60,7 +62,7 @@ export function subscribe(store, ...callbacks) {
return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;
}
-export function get_store_value(store) {
+export function get_store_value>(store: S): T {
let value;
subscribe(store, _ => value = _)();
return value;
@@ -158,4 +160,4 @@ export const has_prop = (obj, prop) => Object.prototype.hasOwnProperty.call(obj,
export function action_destroyer(action_result) {
return action_result && is_function(action_result.destroy) ? action_result.destroy : noop;
-}
\ No newline at end of file
+}
From fd0c6ee4d8dbe10c5f73922e04db170ff03d341d Mon Sep 17 00:00:00 2001
From: orange4glace
Date: Wed, 30 Sep 2020 04:13:07 +0900
Subject: [PATCH 59/60] fix unmounting components delayed bidirectional
transitions (#5353)
---
src/runtime/internal/transitions.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/runtime/internal/transitions.ts b/src/runtime/internal/transitions.ts
index ed23d3c1dd..7377802232 100644
--- a/src/runtime/internal/transitions.ts
+++ b/src/runtime/internal/transitions.ts
@@ -273,7 +273,7 @@ export function create_bidirectional_transition(node: Element & ElementCSSInline
outros.r += 1;
}
- if (running_program) {
+ if (running_program || pending_program) {
pending_program = program;
} else {
// if this is an intro, and there's a delay, we need to do
From 99e2cfca11f5dc611eec10c5a12074f51e9d1bb3 Mon Sep 17 00:00:00 2001
From: Conduitry
Date: Tue, 29 Sep 2020 15:14:39 -0400
Subject: [PATCH 60/60] update changelog
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d4daf1fc0b..a6057f7081 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
## Unreleased
* Support `` ([#2079](https://github.com/sveltejs/svelte/issues/2079))
+* Fix unmounting components with a bidirectional transition with a delay ([#4954](https://github.com/sveltejs/svelte/issues/4954))
* Add types to `get` function in `svelte/store` ([#5269](https://github.com/sveltejs/svelte/pull/5269))
* Add `EventSource` to known globals ([#5463](https://github.com/sveltejs/svelte/issues/5463))
* Fix compiler exception with `~`/`+` combinators and `{...spread}` attributes ([#5465](https://github.com/sveltejs/svelte/issues/5465))