diff --git a/CHANGELOG.md b/CHANGELOG.md index 7848af35be..b85ec4ba16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,14 @@ ## Unreleased +* Allow `` to be used in a slot ([#2798](https://github.com/sveltejs/svelte/issues/2798)) * Expose object of unknown props in `$$restProps` ([#2930](https://github.com/sveltejs/svelte/issues/2930)) +* Prevent passing named slots other than from the top level within a component ([#3385](https://github.com/sveltejs/svelte/issues/3385)) +* Allow transitions and animations to work within iframes ([#3624](https://github.com/sveltejs/svelte/issues/3624)) +* Fix initialising slot fallbacks when unnecessary ([#3763](https://github.com/sveltejs/svelte/issues/3763)) +* Disallow binding directly to `const` variables ([#4479](https://github.com/sveltejs/svelte/issues/4479)) +* Fix updating keyed `{#each}` blocks with `{:else}` ([#4536](https://github.com/sveltejs/svelte/issues/4536), [#4549](https://github.com/sveltejs/svelte/issues/4549)) +* Fix hydration of top-level content ([#4542](https://github.com/sveltejs/svelte/issues/4542)) ## 3.19.2 diff --git a/banner.png b/banner.png deleted file mode 100644 index 0d117c3c09..0000000000 Binary files a/banner.png and /dev/null differ diff --git a/package-lock.json b/package-lock.json index 014348f37e..858d90fd1b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -280,9 +280,9 @@ "dev": true }, "acorn": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz", - "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", + "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", "dev": true }, "acorn-globals": { diff --git a/site/package-lock.json b/site/package-lock.json index 646ca0ce53..5ce377c88c 100644 --- a/site/package-lock.json +++ b/site/package-lock.json @@ -1358,9 +1358,9 @@ } }, "acorn": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.0.0.tgz", - "integrity": "sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", + "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", "dev": true }, "ansi-colors": { diff --git a/site/static/examples/thumbnails/dom-event-forwarding.jpg b/site/static/examples/thumbnails/dom-event-forwarding.jpg index 0992aef1fd..4c23217bba 100644 Binary files a/site/static/examples/thumbnails/dom-event-forwarding.jpg and b/site/static/examples/thumbnails/dom-event-forwarding.jpg differ diff --git a/site/static/favicon.png b/site/static/favicon.png index ddbebf3b4f..21bc302b68 100644 Binary files a/site/static/favicon.png and b/site/static/favicon.png differ diff --git a/site/static/icons/arrow-right.svg b/site/static/icons/arrow-right.svg index 5229286a59..57bb51ed28 100644 --- a/site/static/icons/arrow-right.svg +++ b/site/static/icons/arrow-right.svg @@ -1,7 +1 @@ - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/site/static/icons/check.svg b/site/static/icons/check.svg index 5679642c7f..c3e46a42b6 100644 --- a/site/static/icons/check.svg +++ b/site/static/icons/check.svg @@ -1,4 +1 @@ - - - - \ No newline at end of file + \ No newline at end of file diff --git a/site/static/icons/chevron.svg b/site/static/icons/chevron.svg index 3706f53278..9d582fa803 100644 --- a/site/static/icons/chevron.svg +++ b/site/static/icons/chevron.svg @@ -1,4 +1 @@ - - - - \ No newline at end of file + \ No newline at end of file diff --git a/site/static/icons/collapse.svg b/site/static/icons/collapse.svg index 13ff9dee82..795939aec8 100644 --- a/site/static/icons/collapse.svg +++ b/site/static/icons/collapse.svg @@ -1,4 +1 @@ - - - - \ No newline at end of file + \ No newline at end of file diff --git a/site/static/icons/download.svg b/site/static/icons/download.svg index 8d255ff74b..b286a882fe 100644 --- a/site/static/icons/download.svg +++ b/site/static/icons/download.svg @@ -1,4 +1 @@ - - - - + \ No newline at end of file diff --git a/site/static/icons/dropdown.svg b/site/static/icons/dropdown.svg index b3fcdbe795..69435856b4 100644 --- a/site/static/icons/dropdown.svg +++ b/site/static/icons/dropdown.svg @@ -1,4 +1 @@ - - - - \ No newline at end of file + \ No newline at end of file diff --git a/site/static/icons/edit.svg b/site/static/icons/edit.svg index 5e7b182086..760321cd2f 100644 --- a/site/static/icons/edit.svg +++ b/site/static/icons/edit.svg @@ -1,7 +1 @@ - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/site/static/icons/expand.svg b/site/static/icons/expand.svg index b429cb53c8..a026808da3 100644 --- a/site/static/icons/expand.svg +++ b/site/static/icons/expand.svg @@ -1,4 +1 @@ - - - - \ No newline at end of file + \ No newline at end of file diff --git a/site/static/icons/flip.svg b/site/static/icons/flip.svg index 09326b1e6b..3a18d5ffa1 100644 --- a/site/static/icons/flip.svg +++ b/site/static/icons/flip.svg @@ -1,4 +1 @@ - - - - \ No newline at end of file + \ No newline at end of file diff --git a/site/static/icons/fork.svg b/site/static/icons/fork.svg index 57ad61bb2f..3e21885495 100644 --- a/site/static/icons/fork.svg +++ b/site/static/icons/fork.svg @@ -1,4 +1 @@ - - - - \ No newline at end of file + \ No newline at end of file diff --git a/site/static/icons/link.svg b/site/static/icons/link.svg index 3b36b03cab..3345bdd0ba 100644 --- a/site/static/icons/link.svg +++ b/site/static/icons/link.svg @@ -1,8 +1 @@ - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/site/static/icons/save.svg b/site/static/icons/save.svg index 4bde5edde5..a8a5fa1618 100644 --- a/site/static/icons/save.svg +++ b/site/static/icons/save.svg @@ -1,4 +1 @@ - - - - \ No newline at end of file + \ No newline at end of file diff --git a/site/static/images/svelte-apple-touch-icon.png b/site/static/images/svelte-apple-touch-icon.png index 94c9f12f03..5600445b1c 100644 Binary files a/site/static/images/svelte-apple-touch-icon.png and b/site/static/images/svelte-apple-touch-icon.png differ diff --git a/site/static/images/twitter-card.png b/site/static/images/twitter-card.png index 1e8271f655..7a66e94003 100644 Binary files a/site/static/images/twitter-card.png and b/site/static/images/twitter-card.png differ diff --git a/site/static/svelte-logo-mask.svg b/site/static/svelte-logo-mask.svg index 9e5b40598f..8572d3da6c 100644 --- a/site/static/svelte-logo-mask.svg +++ b/site/static/svelte-logo-mask.svg @@ -1,16 +1 @@ - - - - - + \ No newline at end of file diff --git a/site/static/svelte-logo-outline.svg b/site/static/svelte-logo-outline.svg index 9aac72c957..d93d71c593 100644 --- a/site/static/svelte-logo-outline.svg +++ b/site/static/svelte-logo-outline.svg @@ -1,20 +1 @@ - - - - - - - + \ No newline at end of file diff --git a/site/static/svelte-logo-vertical.svg b/site/static/svelte-logo-vertical.svg index b019147f35..7e2888234d 100644 --- a/site/static/svelte-logo-vertical.svg +++ b/site/static/svelte-logo-vertical.svg @@ -1,4 +1 @@ - - - - + \ No newline at end of file diff --git a/site/static/svelte-logo.svg b/site/static/svelte-logo.svg index 4bf279659a..d8b477bee1 100644 --- a/site/static/svelte-logo.svg +++ b/site/static/svelte-logo.svg @@ -1,20 +1 @@ - - - - - - - + \ No newline at end of file diff --git a/site/static/tutorial/icons/email.svg b/site/static/tutorial/icons/email.svg index db656054ff..70d92ca430 100644 --- a/site/static/tutorial/icons/email.svg +++ b/site/static/tutorial/icons/email.svg @@ -1,4 +1 @@ - - - - \ No newline at end of file + \ No newline at end of file diff --git a/site/static/tutorial/icons/folder-open.svg b/site/static/tutorial/icons/folder-open.svg index 9dac0ef9e9..b2a58e7432 100644 --- a/site/static/tutorial/icons/folder-open.svg +++ b/site/static/tutorial/icons/folder-open.svg @@ -1,4 +1 @@ - - - - \ No newline at end of file + \ No newline at end of file diff --git a/site/static/tutorial/icons/folder.svg b/site/static/tutorial/icons/folder.svg index 7d4a03f1d2..2e1246a7f9 100644 --- a/site/static/tutorial/icons/folder.svg +++ b/site/static/tutorial/icons/folder.svg @@ -1,4 +1 @@ - - - - \ No newline at end of file + \ No newline at end of file diff --git a/site/static/tutorial/icons/gif.svg b/site/static/tutorial/icons/gif.svg index 315860a247..4b28abcecd 100644 --- a/site/static/tutorial/icons/gif.svg +++ b/site/static/tutorial/icons/gif.svg @@ -1,4 +1 @@ - - - - \ No newline at end of file + \ No newline at end of file diff --git a/site/static/tutorial/icons/map-marker.svg b/site/static/tutorial/icons/map-marker.svg index 4e9466c7bd..40a702db9d 100644 --- a/site/static/tutorial/icons/map-marker.svg +++ b/site/static/tutorial/icons/map-marker.svg @@ -1,4 +1 @@ - - - - \ No newline at end of file + \ No newline at end of file diff --git a/site/static/tutorial/icons/md.svg b/site/static/tutorial/icons/md.svg index 60bc36f4a7..582204d3b7 100644 --- a/site/static/tutorial/icons/md.svg +++ b/site/static/tutorial/icons/md.svg @@ -1,4 +1 @@ - - - - \ No newline at end of file + \ No newline at end of file diff --git a/site/static/tutorial/icons/xlsx.svg b/site/static/tutorial/icons/xlsx.svg index 6ee32e943d..1a51093a97 100644 --- a/site/static/tutorial/icons/xlsx.svg +++ b/site/static/tutorial/icons/xlsx.svg @@ -1,4 +1 @@ - - - - \ No newline at end of file + \ No newline at end of file diff --git a/site/static/tutorial/kitten.png b/site/static/tutorial/kitten.png index 6b0cedc945..f1a0ecca19 100644 Binary files a/site/static/tutorial/kitten.png and b/site/static/tutorial/kitten.png differ diff --git a/src/compiler/compile/nodes/Binding.ts b/src/compiler/compile/nodes/Binding.ts index 7d6fad0a81..2f199709c8 100644 --- a/src/compiler/compile/nodes/Binding.ts +++ b/src/compiler/compile/nodes/Binding.ts @@ -72,6 +72,11 @@ export default class Binding extends Node { }); variable[this.expression.node.type === 'MemberExpression' ? 'mutated' : 'reassigned'] = true; + + if (info.expression.type === 'Identifier' && !variable.writable) component.error(this.expression.node, { + code: 'invalid-binding', + message: 'Cannot bind to a variable which is not writable', + }); } const type = parent.get_static_attribute_value('type'); diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index e8108858c5..8c9c0bfe18 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -278,7 +278,7 @@ export default class Element extends Node { } validate_attributes() { - const { component } = this; + const { component, parent } = this; const attribute_map = new Map(); @@ -395,26 +395,10 @@ export default class Element extends Node { component.slot_outlets.add(name); } - let ancestor = this.parent; - do { - if (ancestor.type === 'InlineComponent') break; - if (ancestor.type === 'Element' && /-/.test(ancestor.name)) break; - - if (ancestor.type === 'IfBlock' || ancestor.type === 'EachBlock') { - const type = ancestor.type === 'IfBlock' ? 'if' : 'each'; - const message = `Cannot place slotted elements inside an ${type}-block`; - - component.error(attribute, { - code: `invalid-slotted-content`, - message - }); - } - } while (ancestor = ancestor.parent); - - if (!ancestor) { + if (!(parent.type === 'InlineComponent' || within_custom_element(parent))) { component.error(attribute, { code: `invalid-slotted-content`, - message: `Element with a slot='...' attribute must be a descendant of a component or custom element` + message: `Element with a slot='...' attribute must be a child of a component or a descendant of a custom element`, }); } } @@ -776,3 +760,12 @@ function should_have_attribute( message: `A11y: <${name}> element should have ${article} ${sequence} attribute` }); } + +function within_custom_element(parent: INode) { + while (parent) { + if (parent.type === 'InlineComponent') return false; + if (parent.type === 'Element' && /-/.test(parent.name)) return true; + parent = parent.parent; + } + return false; +} \ No newline at end of file diff --git a/src/compiler/compile/render_dom/wrappers/EachBlock.ts b/src/compiler/compile/render_dom/wrappers/EachBlock.ts index f0dfa5fbcc..43a0f754f9 100644 --- a/src/compiler/compile/render_dom/wrappers/EachBlock.ts +++ b/src/compiler/compile/render_dom/wrappers/EachBlock.ts @@ -62,6 +62,8 @@ export default class EachBlockWrapper extends Wrapper { context_props: Array; index_name: Identifier; + updates: Array = []; + dependencies: Set; var: Identifier = { type: 'Identifier', name: 'each' }; @@ -235,6 +237,12 @@ export default class EachBlockWrapper extends Wrapper { update_mount_node }; + const all_dependencies = new Set(this.block.dependencies); // TODO should be dynamic deps only + this.node.expression.dynamic_dependencies().forEach((dependency: string) => { + all_dependencies.add(dependency); + }); + this.dependencies = all_dependencies; + if (this.node.key) { this.render_keyed(args); } else { @@ -291,7 +299,7 @@ export default class EachBlockWrapper extends Wrapper { `); if (this.else.block.has_update_method) { - block.chunks.update.push(b` + this.updates.push(b` if (!${this.vars.data_length} && ${each_block_else}) { ${each_block_else}.p(#ctx, #dirty); } else if (!${this.vars.data_length}) { @@ -304,7 +312,7 @@ export default class EachBlockWrapper extends Wrapper { } `); } else { - block.chunks.update.push(b` + this.updates.push(b` if (${this.vars.data_length}) { if (${each_block_else}) { ${each_block_else}.d(1); @@ -323,6 +331,14 @@ export default class EachBlockWrapper extends Wrapper { `); } + if (this.updates.length) { + block.chunks.update.push(b` + if (${block.renderer.dirty(Array.from(all_dependencies))}) { + ${this.updates} + } + `); + } + this.fragment.render(this.block, null, x`#nodes` as Identifier); if (this.else) { @@ -415,24 +431,17 @@ export default class EachBlockWrapper extends Wrapper { ? `@outro_and_destroy_block` : `@destroy_block`; - const all_dependencies = new Set(this.block.dependencies); // TODO should be dynamic deps only - this.node.expression.dynamic_dependencies().forEach((dependency: string) => { - all_dependencies.add(dependency); - }); + if (this.dependencies.size) { + this.updates.push(b` + const ${this.vars.each_block_value} = ${snippet}; + ${this.renderer.options.dev && b`@validate_each_argument(${this.vars.each_block_value});`} - if (all_dependencies.size) { - block.chunks.update.push(b` - if (${block.renderer.dirty(Array.from(all_dependencies))}) { - const ${this.vars.each_block_value} = ${snippet}; - ${this.renderer.options.dev && b`@validate_each_argument(${this.vars.each_block_value});`} - - ${this.block.has_outros && b`@group_outros();`} - ${this.node.has_animation && b`for (let #i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].r();`} - ${this.renderer.options.dev && b`@validate_each_keys(#ctx, ${this.vars.each_block_value}, ${this.vars.get_each_context}, ${get_key});`} - ${iterations} = @update_keyed_each(${iterations}, #dirty, ${get_key}, ${dynamic ? 1 : 0}, #ctx, ${this.vars.each_block_value}, ${lookup}, ${update_mount_node}, ${destroy}, ${create_each_block}, ${update_anchor_node}, ${this.vars.get_each_context}); - ${this.node.has_animation && b`for (let #i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].a();`} - ${this.block.has_outros && b`@check_outros();`} - } + ${this.block.has_outros && b`@group_outros();`} + ${this.node.has_animation && b`for (let #i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].r();`} + ${this.renderer.options.dev && b`@validate_each_keys(#ctx, ${this.vars.each_block_value}, ${this.vars.get_each_context}, ${get_key});`} + ${iterations} = @update_keyed_each(${iterations}, #dirty, ${get_key}, ${dynamic ? 1 : 0}, #ctx, ${this.vars.each_block_value}, ${lookup}, ${update_mount_node}, ${destroy}, ${create_each_block}, ${update_anchor_node}, ${this.vars.get_each_context}); + ${this.node.has_animation && b`for (let #i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].a();`} + ${this.block.has_outros && b`@check_outros();`} `); } @@ -504,12 +513,7 @@ export default class EachBlockWrapper extends Wrapper { } `); - const all_dependencies = new Set(this.block.dependencies); // TODO should be dynamic deps only - this.node.expression.dynamic_dependencies().forEach((dependency: string) => { - all_dependencies.add(dependency); - }); - - if (all_dependencies.size) { + if (this.dependencies.size) { const has_transitions = !!(this.block.has_intro_method || this.block.has_outro_method); const for_loop_body = this.block.has_update_method @@ -588,11 +592,7 @@ export default class EachBlockWrapper extends Wrapper { ${remove_old_blocks} `; - block.chunks.update.push(b` - if (${block.renderer.dirty(Array.from(all_dependencies))}) { - ${update} - } - `); + this.updates.push(update); } if (this.block.has_outros) { diff --git a/src/compiler/compile/render_dom/wrappers/Slot.ts b/src/compiler/compile/render_dom/wrappers/Slot.ts index 5f2d331839..37b10c2ea3 100644 --- a/src/compiler/compile/render_dom/wrappers/Slot.ts +++ b/src/compiler/compile/render_dom/wrappers/Slot.ts @@ -10,10 +10,12 @@ import get_slot_data from '../../utils/get_slot_data'; 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'; export default class SlotWrapper extends Wrapper { node: Slot; fragment: FragmentWrapper; + fallback: Block | null = null; var: Identifier = { type: 'Identifier', name: 'slot' }; dependencies: Set = new Set(['$$scope']); @@ -30,9 +32,17 @@ export default class SlotWrapper extends Wrapper { this.cannot_use_innerhtml(); this.not_static_content(); + if (this.node.children.length) { + this.fallback = block.child({ + comment: create_debugging_comment(this.node.children[0], this.renderer.component), + name: this.renderer.component.get_unique_name(`fallback_block`), + type: 'fallback' + }); + } + this.fragment = new FragmentWrapper( renderer, - block, + this.fallback, node.children, parent, strip_whitespace, @@ -103,86 +113,90 @@ export default class SlotWrapper extends Wrapper { get_slot_context_fn = 'null'; } + if (this.fallback) { + this.fragment.render(this.fallback, null, x`#nodes` as Identifier); + renderer.blocks.push(this.fallback); + } + const slot = block.get_unique_name(`${sanitize(slot_name)}_slot`); const slot_definition = block.get_unique_name(`${sanitize(slot_name)}_slot_template`); + const slot_or_fallback = this.fallback ? block.get_unique_name(`${sanitize(slot_name)}_slot_or_fallback`) : slot; block.chunks.init.push(b` const ${slot_definition} = ${renderer.reference('$$slots')}.${slot_name}; const ${slot} = @create_slot(${slot_definition}, #ctx, ${renderer.reference('$$scope')}, ${get_slot_context_fn}); + ${this.fallback ? b`const ${slot_or_fallback} = ${slot} || ${this.fallback.name}(#ctx);` : null} `); - // TODO this is a dreadful hack! Should probably make this nicer - const { create, claim, hydrate, mount, update, destroy } = block.chunks; - - block.chunks.create = []; - block.chunks.claim = []; - block.chunks.hydrate = []; - block.chunks.mount = []; - block.chunks.update = []; - block.chunks.destroy = []; - - const listeners = block.event_listeners; - block.event_listeners = []; - this.fragment.render(block, parent_node, parent_nodes); - block.render_listeners(`_${slot.name}`); - block.event_listeners = listeners; - - if (block.chunks.create.length) create.push(b`if (!${slot}) { ${block.chunks.create} }`); - if (block.chunks.claim.length) claim.push(b`if (!${slot}) { ${block.chunks.claim} }`); - if (block.chunks.hydrate.length) hydrate.push(b`if (!${slot}) { ${block.chunks.hydrate} }`); - if (block.chunks.mount.length) mount.push(b`if (!${slot}) { ${block.chunks.mount} }`); - if (block.chunks.update.length) update.push(b`if (!${slot}) { ${block.chunks.update} }`); - if (block.chunks.destroy.length) destroy.push(b`if (!${slot}) { ${block.chunks.destroy} }`); - - block.chunks.create = create; - block.chunks.claim = claim; - block.chunks.hydrate = hydrate; - block.chunks.mount = mount; - block.chunks.update = update; - block.chunks.destroy = destroy; - block.chunks.create.push( - b`if (${slot}) ${slot}.c();` + b`if (${slot_or_fallback}) ${slot_or_fallback}.c();` ); if (renderer.options.hydratable) { block.chunks.claim.push( - b`if (${slot}) ${slot}.l(${parent_nodes});` + b`if (${slot_or_fallback}) ${slot_or_fallback}.l(${parent_nodes});` ); } block.chunks.mount.push(b` - if (${slot}) { - ${slot}.m(${parent_node || '#target'}, ${parent_node ? 'null' : 'anchor'}); + if (${slot_or_fallback}) { + ${slot_or_fallback}.m(${parent_node || '#target'}, ${parent_node ? 'null' : 'anchor'}); } `); block.chunks.intro.push( - b`@transition_in(${slot}, #local);` + b`@transition_in(${slot_or_fallback}, #local);` ); block.chunks.outro.push( - b`@transition_out(${slot}, #local);` + b`@transition_out(${slot_or_fallback}, #local);` ); - const dynamic_dependencies = Array.from(this.dependencies).filter(name => { + const is_dependency_dynamic = name => { if (name === '$$scope') return true; if (this.node.scope.is_let(name)) return true; const variable = renderer.component.var_lookup.get(name); return is_dynamic(variable); - }); + }; + + const dynamic_dependencies = Array.from(this.dependencies).filter(is_dependency_dynamic); + + const fallback_dynamic_dependencies = this.fallback + ? Array.from(this.fallback.dependencies).filter(is_dependency_dynamic) + : []; - block.chunks.update.push(b` - if (${slot} && ${slot}.p && ${renderer.dirty(dynamic_dependencies)}) { + const slot_update = b` + if (${slot}.p && ${renderer.dirty(dynamic_dependencies)}) { ${slot}.p( @get_slot_context(${slot_definition}, #ctx, ${renderer.reference('$$scope')}, ${get_slot_context_fn}), @get_slot_changes(${slot_definition}, ${renderer.reference('$$scope')}, #dirty, ${get_slot_changes_fn}) ); } - `); + `; + const fallback_update = this.fallback && fallback_dynamic_dependencies.length > 0 && b` + if (${slot_or_fallback} && ${slot_or_fallback}.p && ${renderer.dirty(fallback_dynamic_dependencies)}) { + ${slot_or_fallback}.p(#ctx, #dirty); + } + `; + + if (fallback_update) { + block.chunks.update.push(b` + if (${slot}) { + ${slot_update} + } else { + ${fallback_update} + } + `); + } else { + block.chunks.update.push(b` + if (${slot}) { + ${slot_update} + } + `); + } block.chunks.destroy.push( - b`if (${slot}) ${slot}.d(detaching);` + b`if (${slot_or_fallback}) ${slot_or_fallback}.d(detaching);` ); } } diff --git a/src/compiler/compile/render_dom/wrappers/shared/create_debugging_comment.ts b/src/compiler/compile/render_dom/wrappers/shared/create_debugging_comment.ts index e2aa0c0b41..11f1feb841 100644 --- a/src/compiler/compile/render_dom/wrappers/shared/create_debugging_comment.ts +++ b/src/compiler/compile/render_dom/wrappers/shared/create_debugging_comment.ts @@ -16,8 +16,16 @@ export default function create_debugging_comment( let d; if (node.type === 'InlineComponent' || node.type === 'Element') { - d = node.children.length ? node.children[0].start : node.start; - while (source[d - 1] !== '>') d -= 1; + if (node.children.length) { + d = node.children[0].start; + while (source[d - 1] !== '>') d -= 1; + } else { + d = node.start; + while (source[d] !== '>') d += 1; + d += 1; + } + } else if (node.type === 'Text') { + d = node.end; } else { // @ts-ignore d = node.expression ? node.expression.node.end : c; diff --git a/src/compiler/parse/state/tag.ts b/src/compiler/parse/state/tag.ts index 40bb01de9b..ce8a0a9aa7 100644 --- a/src/compiler/parse/state/tag.ts +++ b/src/compiler/parse/state/tag.ts @@ -241,7 +241,7 @@ function read_tag_name(parser: Parser) { while (i--) { const fragment = parser.stack[i]; - if (fragment.type === 'IfBlock' || fragment.type === 'EachBlock') { + if (fragment.type === 'IfBlock' || fragment.type === 'EachBlock' || fragment.type === 'InlineComponent') { legal = true; break; } @@ -250,7 +250,7 @@ function read_tag_name(parser: Parser) { if (!legal) { parser.error({ code: `invalid-self-placement`, - message: ` components can only exist inside if-blocks or each-blocks` + message: ` components can only exist inside {#if} blocks, {#each} blocks, or slots passed to components` }, start); } diff --git a/src/runtime/internal/Component.ts b/src/runtime/internal/Component.ts index 10588a0804..7d2a92fa1b 100644 --- a/src/runtime/internal/Component.ts +++ b/src/runtime/internal/Component.ts @@ -1,7 +1,7 @@ import { add_render_callback, flush, schedule_update, dirty_components } from './scheduler'; import { current_component, set_current_component } from './lifecycle'; import { blank_object, is_function, run, run_all, noop } from './utils'; -import { children } from './dom'; +import { children, detach } from './dom'; import { transition_in } from './transitions'; interface Fragment { @@ -146,8 +146,10 @@ export function init(component, options, instance, create_fragment, not_equal, p if (options.target) { if (options.hydrate) { + const nodes = children(options.target); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - $$.fragment && $$.fragment!.l(children(options.target)); + $$.fragment && $$.fragment!.l(nodes); + nodes.forEach(detach); } else { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion $$.fragment && $$.fragment!.c(); diff --git a/src/runtime/internal/style_manager.ts b/src/runtime/internal/style_manager.ts index d9264e3c08..31d7573a76 100644 --- a/src/runtime/internal/style_manager.ts +++ b/src/runtime/internal/style_manager.ts @@ -1,9 +1,13 @@ import { element } from './dom'; import { raf } from './environment'; -let stylesheet; +interface ExtendedDoc extends Document { + __svelte_stylesheet: CSSStyleSheet; + __svelte_rules: Record; +} + +const active_docs = new Set(); let active = 0; -let current_rules = {}; // https://github.com/darkskyapp/string-hash/blob/master/index.js function hash(str: string) { @@ -25,14 +29,12 @@ export function create_rule(node: Element & ElementCSSInlineStyle, a: number, b: const rule = keyframes + `100% {${fn(b, 1 - b)}}\n}`; const name = `__svelte_${hash(rule)}_${uid}`; + const doc = node.ownerDocument as ExtendedDoc; + active_docs.add(doc); + const stylesheet = doc.__svelte_stylesheet || (doc.__svelte_stylesheet = doc.head.appendChild(element('style') as HTMLStyleElement).sheet as CSSStyleSheet); + const current_rules = doc.__svelte_rules || (doc.__svelte_rules = {}); if (!current_rules[name]) { - if (!stylesheet) { - const style = element('style'); - document.head.appendChild(style); - stylesheet = style.sheet; - } - current_rules[name] = true; stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length); } @@ -45,22 +47,28 @@ export function create_rule(node: Element & ElementCSSInlineStyle, a: number, b: } export function delete_rule(node: Element & ElementCSSInlineStyle, name?: string) { - node.style.animation = (node.style.animation || '') - .split(', ') - .filter(name - ? anim => anim.indexOf(name) < 0 // remove specific animation - : anim => anim.indexOf('__svelte') === -1 // remove all Svelte animations - ) - .join(', '); - - if (name && !--active) clear_rules(); + const previous = (node.style.animation || '').split(', '); + const next = previous.filter(name + ? anim => anim.indexOf(name) < 0 // remove specific animation + : anim => anim.indexOf('__svelte') === -1 // remove all Svelte animations + ); + const deleted = previous.length - next.length; + if (deleted) { + node.style.animation = next.join(', '); + active -= deleted; + if (!active) clear_rules(); + } } export function clear_rules() { raf(() => { if (active) return; - let i = stylesheet.cssRules.length; - while (i--) stylesheet.deleteRule(i); - current_rules = {}; + active_docs.forEach(doc => { + const stylesheet = doc.__svelte_stylesheet; + let i = stylesheet.cssRules.length; + while (i--) stylesheet.deleteRule(i); + doc.__svelte_rules = {}; + }); + active_docs.clear(); }); } diff --git a/test/hydration/samples/top-level-cleanup-2/_after.html b/test/hydration/samples/top-level-cleanup-2/_after.html new file mode 100644 index 0000000000..8dca56dd88 --- /dev/null +++ b/test/hydration/samples/top-level-cleanup-2/_after.html @@ -0,0 +1 @@ +
Hello world
\ No newline at end of file diff --git a/test/hydration/samples/top-level-cleanup-2/_before.html b/test/hydration/samples/top-level-cleanup-2/_before.html new file mode 100644 index 0000000000..fdc2379f63 --- /dev/null +++ b/test/hydration/samples/top-level-cleanup-2/_before.html @@ -0,0 +1,2 @@ +
This should be thrown away
+
hello
\ No newline at end of file diff --git a/test/hydration/samples/top-level-cleanup-2/_config.js b/test/hydration/samples/top-level-cleanup-2/_config.js new file mode 100644 index 0000000000..ff8b4c5632 --- /dev/null +++ b/test/hydration/samples/top-level-cleanup-2/_config.js @@ -0,0 +1 @@ +export default {}; diff --git a/test/hydration/samples/top-level-cleanup-2/main.svelte b/test/hydration/samples/top-level-cleanup-2/main.svelte new file mode 100644 index 0000000000..8dca56dd88 --- /dev/null +++ b/test/hydration/samples/top-level-cleanup-2/main.svelte @@ -0,0 +1 @@ +
Hello world
\ No newline at end of file diff --git a/test/hydration/samples/top-level-cleanup/_after.html b/test/hydration/samples/top-level-cleanup/_after.html new file mode 100644 index 0000000000..8dca56dd88 --- /dev/null +++ b/test/hydration/samples/top-level-cleanup/_after.html @@ -0,0 +1 @@ +
Hello world
\ No newline at end of file diff --git a/test/hydration/samples/top-level-cleanup/_before.html b/test/hydration/samples/top-level-cleanup/_before.html new file mode 100644 index 0000000000..c7e4bba233 --- /dev/null +++ b/test/hydration/samples/top-level-cleanup/_before.html @@ -0,0 +1 @@ +
This should be thrown away
\ No newline at end of file diff --git a/test/hydration/samples/top-level-cleanup/_config.js b/test/hydration/samples/top-level-cleanup/_config.js new file mode 100644 index 0000000000..ff8b4c5632 --- /dev/null +++ b/test/hydration/samples/top-level-cleanup/_config.js @@ -0,0 +1 @@ +export default {}; diff --git a/test/hydration/samples/top-level-cleanup/main.svelte b/test/hydration/samples/top-level-cleanup/main.svelte new file mode 100644 index 0000000000..8dca56dd88 --- /dev/null +++ b/test/hydration/samples/top-level-cleanup/main.svelte @@ -0,0 +1 @@ +
Hello world
\ No newline at end of file diff --git a/test/parser/samples/error-self-reference/error.json b/test/parser/samples/error-self-reference/error.json index 798b5cb300..31ecab2b12 100644 --- a/test/parser/samples/error-self-reference/error.json +++ b/test/parser/samples/error-self-reference/error.json @@ -1,6 +1,6 @@ { "code": "invalid-self-placement", - "message": " components can only exist inside if-blocks or each-blocks", + "message": " components can only exist inside {#if} blocks, {#each} blocks, or slots passed to components", "start": { "line": 1, "column": 1, diff --git a/test/runtime/samples/component-slot-fallback-2/Inner.svelte b/test/runtime/samples/component-slot-fallback-2/Inner.svelte new file mode 100644 index 0000000000..d1c247ad35 --- /dev/null +++ b/test/runtime/samples/component-slot-fallback-2/Inner.svelte @@ -0,0 +1,7 @@ + + + +{value} \ No newline at end of file diff --git a/test/runtime/samples/component-slot-fallback-2/Outer.svelte b/test/runtime/samples/component-slot-fallback-2/Outer.svelte new file mode 100644 index 0000000000..1e44ba4db7 --- /dev/null +++ b/test/runtime/samples/component-slot-fallback-2/Outer.svelte @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-fallback-2/_config.js b/test/runtime/samples/component-slot-fallback-2/_config.js new file mode 100644 index 0000000000..585e3b4c9e --- /dev/null +++ b/test/runtime/samples/component-slot-fallback-2/_config.js @@ -0,0 +1,39 @@ +export default { + html: ` `, + ssrHtml: ` `, + + async test({ assert, target, component, window }) { + const [input1, input2, inputFallback] = target.querySelectorAll("input"); + + assert.equal(component.getSubscriberCount(), 3); + + input1.value = "a"; + await input1.dispatchEvent(new window.Event("input")); + input1.value = "ab"; + await input1.dispatchEvent(new window.Event("input")); + assert.equal(input1.value, "ab"); + assert.equal(input2.value, "ab"); + assert.equal(inputFallback.value, "ab"); + + component.props = "hello"; + + assert.htmlEqual( + target.innerHTML, + ` + hello + hello + + ` + ); + + component.fallback = "world"; + assert.htmlEqual( + target.innerHTML, + ` + hello + hello + world + ` + ); + } +}; diff --git a/test/runtime/samples/component-slot-fallback-2/main.svelte b/test/runtime/samples/component-slot-fallback-2/main.svelte new file mode 100644 index 0000000000..4583667015 --- /dev/null +++ b/test/runtime/samples/component-slot-fallback-2/main.svelte @@ -0,0 +1,23 @@ + + + + + + + + + + + + diff --git a/test/runtime/samples/component-slot-fallback-2/store.svelte b/test/runtime/samples/component-slot-fallback-2/store.svelte new file mode 100644 index 0000000000..e377aaf314 --- /dev/null +++ b/test/runtime/samples/component-slot-fallback-2/store.svelte @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-nested-error-2/Nested.svelte b/test/runtime/samples/component-slot-nested-error-2/Nested.svelte new file mode 100644 index 0000000000..c6f086d96c --- /dev/null +++ b/test/runtime/samples/component-slot-nested-error-2/Nested.svelte @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-nested-error-2/_config.js b/test/runtime/samples/component-slot-nested-error-2/_config.js new file mode 100644 index 0000000000..98a9f1cab0 --- /dev/null +++ b/test/runtime/samples/component-slot-nested-error-2/_config.js @@ -0,0 +1,3 @@ +export default { + error: [`Element with a slot='...' attribute must be a child of a component or a descendant of a custom element`] +}; diff --git a/test/runtime/samples/component-slot-nested-error-2/main.svelte b/test/runtime/samples/component-slot-nested-error-2/main.svelte new file mode 100644 index 0000000000..a7142d6dc1 --- /dev/null +++ b/test/runtime/samples/component-slot-nested-error-2/main.svelte @@ -0,0 +1,11 @@ + + + +
+
+
+
+
+ diff --git a/test/runtime/samples/component-slot-nested-error-3/Nested.svelte b/test/runtime/samples/component-slot-nested-error-3/Nested.svelte new file mode 100644 index 0000000000..c6f086d96c --- /dev/null +++ b/test/runtime/samples/component-slot-nested-error-3/Nested.svelte @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-nested-error-3/_config.js b/test/runtime/samples/component-slot-nested-error-3/_config.js new file mode 100644 index 0000000000..98a9f1cab0 --- /dev/null +++ b/test/runtime/samples/component-slot-nested-error-3/_config.js @@ -0,0 +1,3 @@ +export default { + error: [`Element with a slot='...' attribute must be a child of a component or a descendant of a custom element`] +}; diff --git a/test/runtime/samples/component-slot-nested-error-3/main.svelte b/test/runtime/samples/component-slot-nested-error-3/main.svelte new file mode 100644 index 0000000000..a63b1defde --- /dev/null +++ b/test/runtime/samples/component-slot-nested-error-3/main.svelte @@ -0,0 +1,11 @@ + + + +
+
+
+
+
+ diff --git a/test/runtime/samples/component-slot-nested-error/Nested.svelte b/test/runtime/samples/component-slot-nested-error/Nested.svelte new file mode 100644 index 0000000000..c6f086d96c --- /dev/null +++ b/test/runtime/samples/component-slot-nested-error/Nested.svelte @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-nested-error/_config.js b/test/runtime/samples/component-slot-nested-error/_config.js new file mode 100644 index 0000000000..98a9f1cab0 --- /dev/null +++ b/test/runtime/samples/component-slot-nested-error/_config.js @@ -0,0 +1,3 @@ +export default { + error: [`Element with a slot='...' attribute must be a child of a component or a descendant of a custom element`] +}; diff --git a/test/runtime/samples/component-slot-nested-error/main.svelte b/test/runtime/samples/component-slot-nested-error/main.svelte new file mode 100644 index 0000000000..531c96f08c --- /dev/null +++ b/test/runtime/samples/component-slot-nested-error/main.svelte @@ -0,0 +1,9 @@ + + + +
+
+
+ diff --git a/test/runtime/samples/each-block-keyed-else/_config.js b/test/runtime/samples/each-block-keyed-else/_config.js new file mode 100644 index 0000000000..a5bf722a80 --- /dev/null +++ b/test/runtime/samples/each-block-keyed-else/_config.js @@ -0,0 +1,37 @@ +export default { + props: { + animals: ['alpaca', 'baboon', 'capybara'], + foo: 'something else' + }, + + html: ` + before +

alpaca

+

baboon

+

capybara

+ after + `, + + test({ assert, component, target }) { + component.animals = []; + assert.htmlEqual(target.innerHTML, ` + before +

no animals, but rather something else

+ after + `); + + component.foo = 'something other'; + assert.htmlEqual(target.innerHTML, ` + before +

no animals, but rather something other

+ after + `); + + component.animals = ['wombat']; + assert.htmlEqual(target.innerHTML, ` + before +

wombat

+ after + `); + } +}; diff --git a/test/runtime/samples/each-block-keyed-else/main.svelte b/test/runtime/samples/each-block-keyed-else/main.svelte new file mode 100644 index 0000000000..2a82653ff1 --- /dev/null +++ b/test/runtime/samples/each-block-keyed-else/main.svelte @@ -0,0 +1,12 @@ + + +before +{#each animals as animal (animal)} +

{animal}

+{:else} +

no animals, but rather {foo}

+{/each} +after diff --git a/test/runtime/samples/each-block-unkeyed-else-2/_config.js b/test/runtime/samples/each-block-unkeyed-else-2/_config.js new file mode 100644 index 0000000000..a5bf722a80 --- /dev/null +++ b/test/runtime/samples/each-block-unkeyed-else-2/_config.js @@ -0,0 +1,37 @@ +export default { + props: { + animals: ['alpaca', 'baboon', 'capybara'], + foo: 'something else' + }, + + html: ` + before +

alpaca

+

baboon

+

capybara

+ after + `, + + test({ assert, component, target }) { + component.animals = []; + assert.htmlEqual(target.innerHTML, ` + before +

no animals, but rather something else

+ after + `); + + component.foo = 'something other'; + assert.htmlEqual(target.innerHTML, ` + before +

no animals, but rather something other

+ after + `); + + component.animals = ['wombat']; + assert.htmlEqual(target.innerHTML, ` + before +

wombat

+ after + `); + } +}; diff --git a/test/runtime/samples/each-block-unkeyed-else-2/main.svelte b/test/runtime/samples/each-block-unkeyed-else-2/main.svelte new file mode 100644 index 0000000000..3275cb1f83 --- /dev/null +++ b/test/runtime/samples/each-block-unkeyed-else-2/main.svelte @@ -0,0 +1,12 @@ + + +before +{#each animals as animal} +

{animal}

+{:else} +

no animals, but rather {foo}

+{/each} +after diff --git a/test/runtime/samples/self-reference-component/Countdown.svelte b/test/runtime/samples/self-reference-component/Countdown.svelte new file mode 100644 index 0000000000..21178f2567 --- /dev/null +++ b/test/runtime/samples/self-reference-component/Countdown.svelte @@ -0,0 +1,7 @@ + + +{#if count > 0} + +{/if} \ No newline at end of file diff --git a/test/runtime/samples/self-reference-component/_config.js b/test/runtime/samples/self-reference-component/_config.js new file mode 100644 index 0000000000..e3e0ad3a4f --- /dev/null +++ b/test/runtime/samples/self-reference-component/_config.js @@ -0,0 +1,3 @@ +export default { + html: '5 4 3 2 1 0', +}; \ No newline at end of file diff --git a/test/runtime/samples/self-reference-component/main.svelte b/test/runtime/samples/self-reference-component/main.svelte new file mode 100644 index 0000000000..fd28ec4e40 --- /dev/null +++ b/test/runtime/samples/self-reference-component/main.svelte @@ -0,0 +1,10 @@ + + +{count} + + + + \ No newline at end of file diff --git a/test/runtime/samples/transition-css-iframe/Foo.svelte b/test/runtime/samples/transition-css-iframe/Foo.svelte new file mode 100644 index 0000000000..c79672f21b --- /dev/null +++ b/test/runtime/samples/transition-css-iframe/Foo.svelte @@ -0,0 +1,16 @@ + + +{#if visible} +
+{/if} \ No newline at end of file diff --git a/test/runtime/samples/transition-css-iframe/Frame.svelte b/test/runtime/samples/transition-css-iframe/Frame.svelte new file mode 100644 index 0000000000..c7ac9cf76f --- /dev/null +++ b/test/runtime/samples/transition-css-iframe/Frame.svelte @@ -0,0 +1,58 @@ + + + + + diff --git a/test/runtime/samples/transition-css-iframe/_config.js b/test/runtime/samples/transition-css-iframe/_config.js new file mode 100644 index 0000000000..507efe44f4 --- /dev/null +++ b/test/runtime/samples/transition-css-iframe/_config.js @@ -0,0 +1,18 @@ +export default { + skip_if_ssr: true, + + async test({ assert, component, target, window, raf }) { + const frame = target.querySelector('iframe'); + await Promise.resolve(); + + component.visible = true; + const div = frame.contentDocument.querySelector('div'); + + raf.tick(25); + + component.visible = false; + + raf.tick(26); + assert.ok(~div.style.animation.indexOf('25ms')); + }, +}; diff --git a/test/runtime/samples/transition-css-iframe/main.svelte b/test/runtime/samples/transition-css-iframe/main.svelte new file mode 100644 index 0000000000..b1686ff15e --- /dev/null +++ b/test/runtime/samples/transition-css-iframe/main.svelte @@ -0,0 +1,8 @@ + + + \ No newline at end of file diff --git a/test/validator/samples/binding-const-field/errors.json b/test/validator/samples/binding-const-field/errors.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/test/validator/samples/binding-const-field/errors.json @@ -0,0 +1 @@ +[] diff --git a/test/validator/samples/binding-const-field/input.svelte b/test/validator/samples/binding-const-field/input.svelte new file mode 100644 index 0000000000..055a16438d --- /dev/null +++ b/test/validator/samples/binding-const-field/input.svelte @@ -0,0 +1,7 @@ + + + diff --git a/test/validator/samples/binding-const/errors.json b/test/validator/samples/binding-const/errors.json new file mode 100644 index 0000000000..6d48af9c4e --- /dev/null +++ b/test/validator/samples/binding-const/errors.json @@ -0,0 +1,15 @@ +[{ + "code": "invalid-binding", + "message": "Cannot bind to a variable which is not writable", + "pos": 61, + "start": { + "line": 5, + "column": 19, + "character": 61 + }, + "end": { + "line": 5, + "column": 24, + "character": 66 + } +}] \ No newline at end of file diff --git a/test/validator/samples/binding-const/input.svelte b/test/validator/samples/binding-const/input.svelte new file mode 100644 index 0000000000..1857a1932c --- /dev/null +++ b/test/validator/samples/binding-const/input.svelte @@ -0,0 +1,5 @@ + + + diff --git a/test/validator/samples/component-slotted-custom-element-2/errors.json b/test/validator/samples/component-slotted-custom-element-2/errors.json new file mode 100644 index 0000000000..06be51d72d --- /dev/null +++ b/test/validator/samples/component-slotted-custom-element-2/errors.json @@ -0,0 +1,9 @@ +[ + { + "code": "invalid-slotted-content", + "message": "Element with a slot='...' attribute must be a child of a component or a descendant of a custom element", + "start": { "line": 10, "column": 9, "character": 138 }, + "end": { "line": 10, "column": 19, "character": 148 }, + "pos": 138 + } +] diff --git a/test/validator/samples/component-slotted-custom-element-2/input.svelte b/test/validator/samples/component-slotted-custom-element-2/input.svelte new file mode 100644 index 0000000000..221659ca4d --- /dev/null +++ b/test/validator/samples/component-slotted-custom-element-2/input.svelte @@ -0,0 +1,14 @@ + + + + + {#if thing} +
+
+
+ {/if} + + \ No newline at end of file diff --git a/test/validator/samples/component-slotted-custom-element/errors.json b/test/validator/samples/component-slotted-custom-element/errors.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/test/validator/samples/component-slotted-custom-element/errors.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/test/validator/samples/component-slotted-custom-element/input.svelte b/test/validator/samples/component-slotted-custom-element/input.svelte new file mode 100644 index 0000000000..e0e27834c1 --- /dev/null +++ b/test/validator/samples/component-slotted-custom-element/input.svelte @@ -0,0 +1,15 @@ + + + + +
+
+
+ {#if thing} +
+ {/if} + + \ No newline at end of file diff --git a/test/validator/samples/component-slotted-each-block/errors.json b/test/validator/samples/component-slotted-each-block/errors.json index 89f394bca4..2944acae17 100644 --- a/test/validator/samples/component-slotted-each-block/errors.json +++ b/test/validator/samples/component-slotted-each-block/errors.json @@ -1,6 +1,6 @@ [{ "code": "invalid-slotted-content", - "message": "Cannot place slotted elements inside an each-block", + "message": "Element with a slot='...' attribute must be a child of a component or a descendant of a custom element", "start": { "line": 7, "column": 7, diff --git a/test/validator/samples/component-slotted-if-block/errors.json b/test/validator/samples/component-slotted-if-block/errors.json index ab35a77fce..3ae07c1b3b 100644 --- a/test/validator/samples/component-slotted-if-block/errors.json +++ b/test/validator/samples/component-slotted-if-block/errors.json @@ -1,6 +1,6 @@ [{ "code": "invalid-slotted-content", - "message": "Cannot place slotted elements inside an if-block", + "message": "Element with a slot='...' attribute must be a child of a component or a descendant of a custom element", "start": { "line": 7, "column": 7, diff --git a/test/validator/samples/slot-attribute-invalid/errors.json b/test/validator/samples/slot-attribute-invalid/errors.json index fc01fa9792..a75fdc065c 100644 --- a/test/validator/samples/slot-attribute-invalid/errors.json +++ b/test/validator/samples/slot-attribute-invalid/errors.json @@ -1,6 +1,6 @@ [{ "code": "invalid-slotted-content", - "message": "Element with a slot='...' attribute must be a descendant of a component or custom element", + "message": "Element with a slot='...' attribute must be a child of a component or a descendant of a custom element", "start": { "line": 1, "column": 5,