Skip to content

Commit e7abdfa

Browse files
authored
Merge branch 'master' into feature/preprocessor-sourcemaps
2 parents b739bdb + 0ca1dcd commit e7abdfa

File tree

128 files changed

+2523
-96
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

128 files changed

+2523
-96
lines changed

CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
# Svelte changelog
22

3-
## Unreleased
3+
## 3.27.0
4+
5+
* Add `|nonpassive` event modifier, explicitly passing `passive: false` ([#2068](https://github.com/sveltejs/svelte/issues/2068))
6+
* Scope CSS selectors with `~` and `+` combinators ([#3104](https://github.com/sveltejs/svelte/issues/3104))
7+
* Fix keyed `{#each}` not reacting to key changing ([#5444](https://github.com/sveltejs/svelte/issues/5444))
8+
* Fix destructuring into store values ([#5449](https://github.com/sveltejs/svelte/issues/5449))
9+
* Fix erroneous `missing-declaration` warning with `use:obj.method` ([#5451](https://github.com/sveltejs/svelte/issues/5451))
10+
11+
## 3.26.0
412

513
* Support `use:obj.method` as actions ([#3935](https://github.com/sveltejs/svelte/issues/3935))
614
* Support `_` as numeric separator ([#5407](https://github.com/sveltejs/svelte/issues/5407))

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "svelte",
3-
"version": "3.25.1",
3+
"version": "3.27.0",
44
"description": "Cybernetically enhanced web apps",
55
"module": "index.mjs",
66
"main": "index",

site/content/blog/2019-04-15-setting-up-your-editor.md

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ To treat `*.svelte` files as HTML, open *__Edit → Config...__* and add the fol
3030

3131
## Vim/Neovim
3232

33-
To treat all `*.svelte` files as HTML, add the following line to your `init.vim`:
33+
You can use the [coc-svelte extension](https://github.com/coc-extensions/coc-svelte) which utilises the official language-server.
34+
35+
As an alternative you can treat all `*.svelte` files as HTML. Add the following line to your `init.vim`:
3436

3537
```
3638
au! BufNewFile,BufRead *.svelte set ft=html
@@ -50,13 +52,7 @@ To set the filetype for a single file, use a [modeline](https://vim.fandom.com/w
5052

5153
## Visual Studio Code
5254

53-
To treat `*.svelte` files as HTML, add the following lines to your `settings.json` file:
54-
55-
```cson
56-
"files.associations": {
57-
"*.svelte": "html"
58-
}
59-
```
55+
We recommend using the official [Svelte for VS Code extension](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode).
6056

6157
## JetBrains WebStorm
6258

site/content/docs/02-template-syntax.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,7 @@ The following modifiers are available:
471471
* `preventDefault` — calls `event.preventDefault()` before running the handler
472472
* `stopPropagation` — calls `event.stopPropagation()`, preventing the event reaching the next element
473473
* `passive` — improves scrolling performance on touch/wheel events (Svelte will add it automatically where it's safe to do so)
474+
* `nonpassive` — explicitly set `passive: false`
474475
* `capture` — fires the handler during the *capture* phase instead of the *bubbling* phase
475476
* `once` — remove the handler after the first time it runs
476477
* `self` — only trigger handler if event.target is the element itself

site/content/tutorial/01-introduction/07-making-an-app/text.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ First, you'll need to integrate Svelte with a build tool. There are officially m
1313

1414
Don't worry if you're relatively new to web development and haven't used these tools before. We've prepared a simple step-by-step guide, [Svelte for new developers](blog/svelte-for-new-developers), which walks you through the process.
1515

16-
You'll also want to configure your text editor to treat `.svelte` files the same as `.html` for the sake of syntax highlighting. [Read this guide to learn how](blog/setting-up-your-editor).
16+
You'll also want to configure your text editor. If you're using VS Code, install the [Svelte extension](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode), otherwise follow [this guide](blog/setting-up-your-editor) to configure your text editor to treat `.svelte` files the same as `.html` for the sake of syntax highlighting.
1717

1818
Then, once you've got your project set up, using Svelte components is easy. The compiler turns each component into a regular JavaScript class — just import it and instantiate with `new`:
1919

site/content/tutorial/05-events/03-event-modifiers/text.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ The full list of modifiers:
2121
* `preventDefault` — calls `event.preventDefault()` before running the handler. Useful for client-side form handling, for example.
2222
* `stopPropagation` — calls `event.stopPropagation()`, preventing the event reaching the next element
2323
* `passive` — improves scrolling performance on touch/wheel events (Svelte will add it automatically where it's safe to do so)
24+
* `nonpassive` — explicitly set `passive: false`
2425
* `capture` — fires the handler during the *capture* phase instead of the *bubbling* phase ([MDN docs](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture))
2526
* `once` — remove the handler after the first time it runs
2627
* `self` — only trigger handler if event.target is the element itself

src/compiler/compile/Component.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import check_graph_for_cycles from './utils/check_graph_for_cycles';
3030
import { print, x, b } from 'code-red';
3131
import { is_reserved_keyword } from './utils/reserved_keywords';
3232
import remapping from '@ampproject/remapping';
33+
import Element from './nodes/Element';
3334

3435
interface ComponentOptions {
3536
namespace?: string;
@@ -86,6 +87,7 @@ export default class Component {
8687
file: string;
8788
locate: (c: number) => { line: number; column: number };
8889

90+
elements: Element[] = [];
8991
stylesheet: Stylesheet;
9092

9193
aliases: Map<string, Identifier> = new Map();
@@ -172,8 +174,8 @@ export default class Component {
172174

173175
this.walk_instance_js_post_template();
174176

177+
this.elements.forEach(element => this.stylesheet.apply(element));
175178
if (!compile_options.customElement) this.stylesheet.reify();
176-
177179
this.stylesheet.warn_on_unused_selectors(this);
178180
}
179181

@@ -222,6 +224,10 @@ export default class Component {
222224
return this.aliases.get(name);
223225
}
224226

227+
apply_stylesheet(element: Element) {
228+
this.elements.push(element);
229+
}
230+
225231
global(name: string) {
226232
const alias = this.alias(name);
227233
this.globals.set(name, alias);

src/compiler/compile/css/Selector.ts

Lines changed: 179 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,20 @@ import { gather_possible_values, UNKNOWN } from './gather_possible_values';
44
import { CssNode } from './interfaces';
55
import Component from '../Component';
66
import Element from '../nodes/Element';
7+
import { INode } from '../nodes/interfaces';
8+
import EachBlock from '../nodes/EachBlock';
9+
import IfBlock from '../nodes/IfBlock';
10+
import AwaitBlock from '../nodes/AwaitBlock';
711

812
enum BlockAppliesToNode {
913
NotPossible,
1014
Possible,
1115
UnknownSelectorType
1216
}
17+
enum NodeExist {
18+
Probably = 1,
19+
Definitely = 2,
20+
}
1321

1422
const whitelist_attribute_selector = new Map([
1523
['details', new Set(['open'])]
@@ -39,10 +47,10 @@ export default class Selector {
3947
this.used = this.local_blocks.length === 0;
4048
}
4149

42-
apply(node: Element, stack: Element[]) {
50+
apply(node: Element) {
4351
const to_encapsulate: any[] = [];
4452

45-
apply_selector(this.local_blocks.slice(), node, stack.slice(), to_encapsulate);
53+
apply_selector(this.local_blocks.slice(), node, to_encapsulate);
4654

4755
if (to_encapsulate.length > 0) {
4856
to_encapsulate.forEach(({ node, block }) => {
@@ -149,7 +157,7 @@ export default class Selector {
149157
}
150158
}
151159

152-
function apply_selector(blocks: Block[], node: Element, stack: Element[], to_encapsulate: any[]): boolean {
160+
function apply_selector(blocks: Block[], node: Element, to_encapsulate: any[]): boolean {
153161
const block = blocks.pop();
154162
if (!block) return false;
155163

@@ -162,7 +170,7 @@ function apply_selector(blocks: Block[], node: Element, stack: Element[], to_enc
162170
return false;
163171

164172
case BlockAppliesToNode.UnknownSelectorType:
165-
// bail. TODO figure out what these could be
173+
// bail. TODO figure out what these could be
166174
to_encapsulate.push({ node, block });
167175
return true;
168176
}
@@ -174,9 +182,10 @@ function apply_selector(blocks: Block[], node: Element, stack: Element[], to_enc
174182
continue;
175183
}
176184

177-
for (const stack_node of stack) {
178-
if (block_might_apply_to_node(ancestor_block, stack_node) !== BlockAppliesToNode.NotPossible) {
179-
to_encapsulate.push({ node: stack_node, block: ancestor_block });
185+
let parent = node;
186+
while (parent = get_element_parent(parent)) {
187+
if (block_might_apply_to_node(ancestor_block, parent) !== BlockAppliesToNode.NotPossible) {
188+
to_encapsulate.push({ node: parent, block: ancestor_block });
180189
}
181190
}
182191

@@ -193,12 +202,22 @@ function apply_selector(blocks: Block[], node: Element, stack: Element[], to_enc
193202

194203
return false;
195204
} else if (block.combinator.name === '>') {
196-
if (apply_selector(blocks, stack.pop(), stack, to_encapsulate)) {
205+
if (apply_selector(blocks, get_element_parent(node), to_encapsulate)) {
197206
to_encapsulate.push({ node, block });
198207
return true;
199208
}
200209

201210
return false;
211+
} else if (block.combinator.name === '+' || block.combinator.name === '~') {
212+
const siblings = get_possible_element_siblings(node, block.combinator.name === '+');
213+
let has_match = false;
214+
for (const possible_sibling of siblings.keys()) {
215+
if (apply_selector(blocks.slice(), possible_sibling, to_encapsulate)) {
216+
to_encapsulate.push({ node, block });
217+
has_match = true;
218+
}
219+
}
220+
return has_match;
202221
}
203222

204223
// TODO other combinators
@@ -376,6 +395,158 @@ function unquote(value: CssNode) {
376395
return str;
377396
}
378397

398+
function get_element_parent(node: Element): Element | null {
399+
let parent: INode = node;
400+
while ((parent = parent.parent) && parent.type !== 'Element');
401+
return parent as Element | null;
402+
}
403+
404+
function get_possible_element_siblings(node: INode, adjacent_only: boolean): Map<Element, NodeExist> {
405+
const result: Map<Element, NodeExist> = new Map();
406+
let prev: INode = node;
407+
while (prev = prev.prev) {
408+
if (prev.type === 'Element') {
409+
if (!prev.attributes.find(attr => attr.name.toLowerCase() === 'slot')) {
410+
result.set(prev, NodeExist.Definitely);
411+
}
412+
413+
if (adjacent_only) {
414+
break;
415+
}
416+
} else if (prev.type === 'EachBlock' || prev.type === 'IfBlock' || prev.type === 'AwaitBlock') {
417+
const possible_last_child = get_possible_last_child(prev, adjacent_only);
418+
419+
add_to_map(possible_last_child, result);
420+
if (adjacent_only && has_definite_elements(possible_last_child)) {
421+
return result;
422+
}
423+
}
424+
}
425+
426+
if (!prev || !adjacent_only) {
427+
let parent: INode = node;
428+
let skip_each_for_last_child = node.type === 'ElseBlock';
429+
while ((parent = parent.parent) && (parent.type === 'EachBlock' || parent.type === 'IfBlock' || parent.type === 'ElseBlock' || parent.type === 'AwaitBlock')) {
430+
const possible_siblings = get_possible_element_siblings(parent, adjacent_only);
431+
add_to_map(possible_siblings, result);
432+
433+
if (parent.type === 'EachBlock') {
434+
// first child of each block can select the last child of each block as previous sibling
435+
if (skip_each_for_last_child) {
436+
skip_each_for_last_child = false;
437+
} else {
438+
add_to_map(get_possible_last_child(parent, adjacent_only), result);
439+
}
440+
} else if (parent.type === 'ElseBlock') {
441+
skip_each_for_last_child = true;
442+
parent = parent.parent;
443+
}
444+
445+
if (adjacent_only && has_definite_elements(possible_siblings)) {
446+
break;
447+
}
448+
}
449+
}
450+
451+
return result;
452+
}
453+
454+
function get_possible_last_child(block: EachBlock | IfBlock | AwaitBlock, adjacent_only: boolean): Map<Element, NodeExist> {
455+
const result: Map<Element, NodeExist> = new Map();
456+
457+
if (block.type === 'EachBlock') {
458+
const each_result: Map<Element, NodeExist> = loop_child(block.children, adjacent_only);
459+
const else_result: Map<Element, NodeExist> = block.else ? loop_child(block.else.children, adjacent_only) : new Map();
460+
461+
const not_exhaustive = !has_definite_elements(else_result);
462+
463+
if (not_exhaustive) {
464+
mark_as_probably(each_result);
465+
mark_as_probably(else_result);
466+
}
467+
add_to_map(each_result, result);
468+
add_to_map(else_result, result);
469+
} else if (block.type === 'IfBlock') {
470+
const if_result: Map<Element, NodeExist> = loop_child(block.children, adjacent_only);
471+
const else_result: Map<Element, NodeExist> = block.else ? loop_child(block.else.children, adjacent_only) : new Map();
472+
473+
const not_exhaustive = !has_definite_elements(if_result) || !has_definite_elements(else_result);
474+
475+
if (not_exhaustive) {
476+
mark_as_probably(if_result);
477+
mark_as_probably(else_result);
478+
}
479+
480+
add_to_map(if_result, result);
481+
add_to_map(else_result, result);
482+
} else if (block.type === 'AwaitBlock') {
483+
const pending_result: Map<Element, NodeExist> = block.pending ? loop_child(block.pending.children, adjacent_only) : new Map();
484+
const then_result: Map<Element, NodeExist> = block.then ? loop_child(block.then.children, adjacent_only) : new Map();
485+
const catch_result: Map<Element, NodeExist> = block.catch ? loop_child(block.catch.children, adjacent_only) : new Map();
486+
487+
const not_exhaustive = !has_definite_elements(pending_result) || !has_definite_elements(then_result) || !has_definite_elements(catch_result);
488+
489+
if (not_exhaustive) {
490+
mark_as_probably(pending_result);
491+
mark_as_probably(then_result);
492+
mark_as_probably(catch_result);
493+
}
494+
495+
add_to_map(pending_result, result);
496+
add_to_map(then_result, result);
497+
add_to_map(catch_result, result);
498+
}
499+
500+
return result;
501+
}
502+
503+
function has_definite_elements(result: Map<Element, NodeExist>): boolean {
504+
if (result.size === 0) return false;
505+
for (const exist of result.values()) {
506+
if (exist === NodeExist.Definitely) {
507+
return true;
508+
}
509+
}
510+
return false;
511+
}
512+
513+
function add_to_map(from: Map<Element, NodeExist>, to: Map<Element, NodeExist>) {
514+
from.forEach((exist, element) => {
515+
to.set(element, higher_existance(exist, to.get(element)));
516+
});
517+
}
518+
519+
function higher_existance(exist1: NodeExist | null, exist2: NodeExist | null): NodeExist {
520+
if (exist1 === undefined || exist2 === undefined) return exist1 || exist2;
521+
return exist1 > exist2 ? exist1 : exist2;
522+
}
523+
524+
function mark_as_probably(result: Map<Element, NodeExist>) {
525+
for (const key of result.keys()) {
526+
result.set(key, NodeExist.Probably);
527+
}
528+
}
529+
530+
function loop_child(children: INode[], adjacent_only: boolean) {
531+
const result: Map<Element, NodeExist> = new Map();
532+
for (let i = children.length - 1; i >= 0; i--) {
533+
const child = children[i];
534+
if (child.type === 'Element') {
535+
result.set(child, NodeExist.Definitely);
536+
if (adjacent_only) {
537+
break;
538+
}
539+
} else if (child.type === 'EachBlock' || child.type === 'IfBlock' || child.type === 'AwaitBlock') {
540+
const child_result = get_possible_last_child(child, adjacent_only);
541+
add_to_map(child_result, result);
542+
if (adjacent_only && has_definite_elements(child_result)) {
543+
break;
544+
}
545+
}
546+
}
547+
return result;
548+
}
549+
379550
class Block {
380551
global: boolean;
381552
combinator: CssNode;

0 commit comments

Comments
 (0)