Skip to content

Commit 78838fa

Browse files
committed
Merge branch 'master' of https://github.com/sveltejs/svelte into feature/preprocessor-sourcemaps
2 parents a0eb41f + c24b313 commit 78838fa

File tree

70 files changed

+707
-101
lines changed

Some content is hidden

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

70 files changed

+707
-101
lines changed

.mocharc.js

+6-7
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
module.exports = {
22
file: [
3-
'test/test.js'
3+
'test/test.ts'
4+
],
5+
require: [
6+
'sucrase/register'
47
]
58
};
69

710
// add coverage options when running 'npx c8 mocha'
811
if (process.env.NODE_V8_COVERAGE) {
9-
Object.assign(module.exports, {
10-
fullTrace: true,
11-
require: [
12-
'source-map-support/register'
13-
]
14-
});
12+
module.exports.fullTrace = true;
13+
module.exports.require.push('source-map-support/register');
1514
}

CHANGELOG.md

+13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
# Svelte changelog
22

3+
## 3.29.0
4+
5+
* Support `<slot slot="...">` ([#2079](https://github.com/sveltejs/svelte/issues/2079))
6+
* Fix unmounting components with a bidirectional transition with a delay ([#4954](https://github.com/sveltejs/svelte/issues/4954))
7+
* Add types to `get` function in `svelte/store` ([#5269](https://github.com/sveltejs/svelte/pull/5269))
8+
* Add a warning when a component looks like it's trying to use another component without beginning with a capital letter ([#5302](https://github.com/sveltejs/svelte/pull/5302))
9+
* Add `EventSource` to known globals ([#5463](https://github.com/sveltejs/svelte/issues/5463))
10+
* Fix compiler exception with `~`/`+` combinators and `{...spread}` attributes ([#5465](https://github.com/sveltejs/svelte/issues/5465))
11+
12+
## 3.28.0
13+
14+
* Add `{#key}` block for keying arbitrary content on an expression ([#1469](https://github.com/sveltejs/svelte/issues/1469))
15+
316
## 3.27.0
417

518
* Add `|nonpassive` event modifier, explicitly passing `passive: false` ([#2068](https://github.com/sveltejs/svelte/issues/2068))

package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

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

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

+27
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,33 @@ If you don't care about the pending state, you can also omit the initial block.
342342
{/await}
343343
```
344344

345+
### {#key ...}
346+
347+
```sv
348+
{#key expression}...{/key}
349+
```
350+
351+
Key blocks destroy and recreate their contents when the value of an expression changes.
352+
353+
---
354+
355+
This is useful if you want an element to play its transition whenever a value changes.
356+
357+
```sv
358+
{#key value}
359+
<div transition:fade>{value}</div>
360+
{/key}
361+
```
362+
363+
---
364+
365+
When used around components, this will cause them to be reinstantiated and reinitialised.
366+
367+
```sv
368+
{#key value}
369+
<Component />
370+
{/key}
371+
```
345372

346373
### {@html ...}
347374

src/compiler/compile/Component.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -664,11 +664,13 @@ export default class Component {
664664
}
665665

666666
const writable = node.type === 'VariableDeclaration' && (node.kind === 'var' || node.kind === 'let');
667+
const imported = node.type.startsWith('Import');
667668

668669
this.add_var({
669670
name,
670671
initialised: instance_scope.initialised_declarations.has(name),
671-
writable
672+
writable,
673+
imported
672674
});
673675

674676
this.node_for_declaration.set(name, node);

src/compiler/compile/css/Selector.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ function get_possible_element_siblings(node: INode, adjacent_only: boolean): Map
406406
let prev: INode = node;
407407
while (prev = prev.prev) {
408408
if (prev.type === 'Element') {
409-
if (!prev.attributes.find(attr => attr.name.toLowerCase() === 'slot')) {
409+
if (!prev.attributes.find(attr => attr.type === 'Attribute' && attr.name.toLowerCase() === 'slot')) {
410410
result.set(prev, NodeExist.Definitely);
411411
}
412412

src/compiler/compile/index.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { assign } from '../../runtime/internal/utils';
21
import Stats from '../Stats';
32
import parse from '../parse/index';
43
import render_dom from './render_dom/index';
@@ -69,7 +68,7 @@ function validate_options(options: CompileOptions, warnings: Warning[]) {
6968
}
7069

7170
export default function compile(source: string, options: CompileOptions = {}) {
72-
options = assign({ generate: 'dom', dev: false }, options);
71+
options = Object.assign({ generate: 'dom', dev: false }, options);
7372

7473
const stats = new Stats();
7574
const warnings = [];

src/compiler/compile/nodes/Element.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ export default class Element extends Node {
186186

187187
case 'Attribute':
188188
case 'Spread':
189-
// special case
189+
// special case
190190
if (node.name === 'xmlns') this.namespace = node.value[0].data;
191191

192192
this.attributes.push(new Attribute(component, this, scope, node));
@@ -241,6 +241,13 @@ export default class Element extends Node {
241241
}
242242

243243
validate() {
244+
if (this.component.var_lookup.has(this.name) && this.component.var_lookup.get(this.name).imported) {
245+
this.component.warn(this, {
246+
code: 'component-name-lowercase',
247+
message: `<${this.name}> will be treated as an HTML element unless it begins with a capital letter`
248+
});
249+
}
250+
244251
if (a11y_distracting_elements.has(this.name)) {
245252
// no-distracting-elements
246253
this.component.warn(this, {
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import Expression from "./shared/Expression";
2+
import map_children from "./shared/map_children";
3+
import AbstractBlock from "./shared/AbstractBlock";
4+
5+
export default class KeyBlock extends AbstractBlock {
6+
type: "KeyBlock";
7+
8+
expression: Expression;
9+
10+
constructor(component, parent, scope, info) {
11+
super(component, parent, scope, info);
12+
13+
this.expression = new Expression(component, this, scope, info.expression);
14+
15+
this.children = map_children(component, this, scope, info.children);
16+
17+
this.warn_if_empty_block();
18+
}
19+
}

src/compiler/compile/nodes/interfaces.ts

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import Fragment from './Fragment';
1818
import Head from './Head';
1919
import IfBlock from './IfBlock';
2020
import InlineComponent from './InlineComponent';
21+
import KeyBlock from './KeyBlock';
2122
import Let from './Let';
2223
import MustacheTag from './MustacheTag';
2324
import Options from './Options';
@@ -50,6 +51,7 @@ export type INode = Action
5051
| Head
5152
| IfBlock
5253
| InlineComponent
54+
| KeyBlock
5355
| Let
5456
| MustacheTag
5557
| Options

src/compiler/compile/nodes/shared/map_children.ts

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Element from '../Element';
66
import Head from '../Head';
77
import IfBlock from '../IfBlock';
88
import InlineComponent from '../InlineComponent';
9+
import KeyBlock from '../KeyBlock';
910
import MustacheTag from '../MustacheTag';
1011
import Options from '../Options';
1112
import RawMustacheTag from '../RawMustacheTag';
@@ -28,6 +29,7 @@ function get_constructor(type) {
2829
case 'Head': return Head;
2930
case 'IfBlock': return IfBlock;
3031
case 'InlineComponent': return InlineComponent;
32+
case 'KeyBlock': return KeyBlock;
3133
case 'MustacheTag': return MustacheTag;
3234
case 'Options': return Options;
3335
case 'RawMustacheTag': return RawMustacheTag;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import ElementWrapper from './index';
2+
import SlotWrapper from '../Slot';
3+
import Block from '../../Block';
4+
import { sanitize } from '../../../../utils/names';
5+
import InlineComponentWrapper from '../InlineComponent';
6+
import create_debugging_comment from '../shared/create_debugging_comment';
7+
import { get_slot_definition } from '../shared/get_slot_definition';
8+
9+
export default function create_slot_block(attribute, element: ElementWrapper | SlotWrapper, block: Block) {
10+
const owner = find_slot_owner(element.parent);
11+
12+
if (owner && owner.node.type === 'InlineComponent') {
13+
const name = attribute.get_static_value() as string;
14+
15+
if (!((owner as unknown) as InlineComponentWrapper).slots.has(name)) {
16+
const child_block = block.child({
17+
comment: create_debugging_comment(element.node, element.renderer.component),
18+
name: element.renderer.component.get_unique_name(
19+
`create_${sanitize(name)}_slot`
20+
),
21+
type: 'slot'
22+
});
23+
24+
const { scope, lets } = element.node;
25+
const seen = new Set(lets.map(l => l.name.name));
26+
27+
((owner as unknown) as InlineComponentWrapper).node.lets.forEach(l => {
28+
if (!seen.has(l.name.name)) lets.push(l);
29+
});
30+
31+
((owner as unknown) as InlineComponentWrapper).slots.set(
32+
name,
33+
get_slot_definition(child_block, scope, lets)
34+
);
35+
element.renderer.blocks.push(child_block);
36+
}
37+
38+
element.slot_block = ((owner as unknown) as InlineComponentWrapper).slots.get(
39+
name
40+
).block;
41+
42+
return element.slot_block;
43+
}
44+
45+
return block;
46+
}
47+
48+
function find_slot_owner(owner) {
49+
while (owner) {
50+
if (owner.node.type === 'InlineComponent') {
51+
break;
52+
}
53+
54+
if (owner.node.type === 'Element' && /-/.test(owner.node.name)) {
55+
break;
56+
}
57+
58+
owner = owner.parent;
59+
}
60+
return owner;
61+
}

src/compiler/compile/render_dom/wrappers/Element/index.ts

+3-45
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import Renderer from '../../Renderer';
22
import Element from '../../../nodes/Element';
33
import Wrapper from '../shared/Wrapper';
44
import Block from '../../Block';
5-
import { is_void, sanitize } from '../../../../utils/names';
5+
import { is_void } from '../../../../utils/names';
66
import FragmentWrapper from '../Fragment';
77
import { escape_html, string_literal } from '../../../utils/stringify';
88
import TextWrapper from '../Text';
@@ -14,12 +14,9 @@ import StyleAttributeWrapper from './StyleAttribute';
1414
import SpreadAttributeWrapper from './SpreadAttribute';
1515
import { dimensions } from '../../../../utils/patterns';
1616
import Binding from './Binding';
17-
import InlineComponentWrapper from '../InlineComponent';
1817
import add_to_set from '../../../utils/add_to_set';
1918
import { add_event_handler } from '../shared/add_event_handlers';
2019
import { add_action } from '../shared/add_actions';
21-
import create_debugging_comment from '../shared/create_debugging_comment';
22-
import { get_slot_definition } from '../shared/get_slot_definition';
2320
import bind_this from '../shared/bind_this';
2421
import { is_head } from '../shared/is_head';
2522
import { Identifier } from 'estree';
@@ -28,6 +25,7 @@ import { extract_names } from 'periscopic';
2825
import Action from '../../../nodes/Action';
2926
import MustacheTagWrapper from '../MustacheTag';
3027
import RawMustacheTagWrapper from '../RawMustacheTag';
28+
import create_slot_block from './create_slot_block';
3129

3230
interface BindingGroup {
3331
events: string[];
@@ -177,47 +175,7 @@ export default class ElementWrapper extends Wrapper {
177175

178176
this.attributes = this.node.attributes.map(attribute => {
179177
if (attribute.name === 'slot') {
180-
// TODO make separate subclass for this?
181-
let owner = this.parent;
182-
while (owner) {
183-
if (owner.node.type === 'InlineComponent') {
184-
break;
185-
}
186-
187-
if (owner.node.type === 'Element' && /-/.test(owner.node.name)) {
188-
break;
189-
}
190-
191-
owner = owner.parent;
192-
}
193-
194-
if (owner && owner.node.type === 'InlineComponent') {
195-
const name = attribute.get_static_value() as string;
196-
197-
if (!(owner as unknown as InlineComponentWrapper).slots.has(name)) {
198-
const child_block = block.child({
199-
comment: create_debugging_comment(node, this.renderer.component),
200-
name: this.renderer.component.get_unique_name(`create_${sanitize(name)}_slot`),
201-
type: 'slot'
202-
});
203-
204-
const { scope, lets } = this.node;
205-
const seen = new Set(lets.map(l => l.name.name));
206-
207-
(owner as unknown as InlineComponentWrapper).node.lets.forEach(l => {
208-
if (!seen.has(l.name.name)) lets.push(l);
209-
});
210-
211-
(owner as unknown as InlineComponentWrapper).slots.set(
212-
name,
213-
get_slot_definition(child_block, scope, lets)
214-
);
215-
this.renderer.blocks.push(child_block);
216-
}
217-
218-
this.slot_block = (owner as unknown as InlineComponentWrapper).slots.get(name).block;
219-
block = this.slot_block;
220-
}
178+
block = create_slot_block(attribute, this, block);
221179
}
222180
if (attribute.name === 'style') {
223181
return new StyleAttributeWrapper(this, block, attribute);

src/compiler/compile/render_dom/wrappers/Fragment.ts

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import EachBlock from './EachBlock';
66
import Element from './Element/index';
77
import Head from './Head';
88
import IfBlock from './IfBlock';
9+
import KeyBlock from './KeyBlock';
910
import InlineComponent from './InlineComponent/index';
1011
import MustacheTag from './MustacheTag';
1112
import RawMustacheTag from './RawMustacheTag';
@@ -30,6 +31,7 @@ const wrappers = {
3031
Head,
3132
IfBlock,
3233
InlineComponent,
34+
KeyBlock,
3335
MustacheTag,
3436
Options: null,
3537
RawMustacheTag,

0 commit comments

Comments
 (0)