From fbf4cbf22c144ef849dce6d3630a54344406d11a Mon Sep 17 00:00:00 2001 From: simeydotme Date: Tue, 24 Sep 2019 22:38:55 +0800 Subject: [PATCH 001/395] Calculate scale factor of elements in FLIP animations - divide the getBoundingClientRect dimensions by clientHeight or clientWidth - this gives us the element's scaled values as scaleX and scaleY - divide the "dx" and "dy" by the "scaleX" and "scaleY" This aims to fix #3555 by using the node's un-scaled width/height to calculate the amount it has been scaled. Then dividing the distance by this scale factor removes the janky start position which was shown in the test case. resolves #3555 --- src/runtime/animate/index.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/runtime/animate/index.ts b/src/runtime/animate/index.ts index d00d23c98d..3f841154a2 100644 --- a/src/runtime/animate/index.ts +++ b/src/runtime/animate/index.ts @@ -19,9 +19,11 @@ interface FlipParams { export function flip(node: Element, animation: { from: DOMRect; to: DOMRect }, params: FlipParams): AnimationConfig { const style = getComputedStyle(node); const transform = style.transform === 'none' ? '' : style.transform; + const scaleX = animation.from.width / node.clientWidth; + const scaleY = animation.from.height / node.clientHeight; - const dx = animation.from.left - animation.to.left; - const dy = animation.from.top - animation.to.top; + const dx = (animation.from.left - animation.to.left) / scaleX; + const dy = (animation.from.top - animation.to.top) / scaleY; const d = Math.sqrt(dx * dx + dy * dy); From 914d155d9f2b8f93f5ef44ed84c76581187f9111 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Kishi?= Date: Sat, 19 Oct 2019 16:20:46 -0300 Subject: [PATCH 002/395] fix store validation code generation (#3735) --- src/compiler/compile/render_dom/index.ts | 2 +- test/runtime/samples/store-imported/_config.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/compiler/compile/render_dom/index.ts b/src/compiler/compile/render_dom/index.ts index ba070beee9..72f81cfb05 100644 --- a/src/compiler/compile/render_dom/index.ts +++ b/src/compiler/compile/render_dom/index.ts @@ -301,7 +301,7 @@ export default function dom( return !variable || variable.hoistable; }) .map(({ name }) => b` - ${component.compile_options.dev && `@validate_store(${name.slice(1)}, '${name.slice(1)}');`} + ${component.compile_options.dev && b`@validate_store(${name.slice(1)}, '${name.slice(1)}');`} @component_subscribe($$self, ${name.slice(1)}, $$value => $$invalidate('${name}', ${name} = $$value)); `); diff --git a/test/runtime/samples/store-imported/_config.js b/test/runtime/samples/store-imported/_config.js index c2d471a329..251866e5ba 100644 --- a/test/runtime/samples/store-imported/_config.js +++ b/test/runtime/samples/store-imported/_config.js @@ -1,4 +1,6 @@ export default { + compileOptions: { dev: true }, // tests `@validate_store` code generation + html: `

42

` From 3e02b954887514983a62a7b14bf12309ac1fac02 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Sun, 20 Oct 2019 07:29:33 -0400 Subject: [PATCH 003/395] fix compound ifs with outros and no dependencies (#3595) --- CHANGELOG.md | 1 + .../compile/render_dom/wrappers/IfBlock.ts | 10 ++++-- .../_config.js | 3 ++ .../main.svelte | 32 +++++++++++++++++++ 4 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 test/runtime/samples/if-block-compound-outro-no-dependencies/_config.js create mode 100644 test/runtime/samples/if-block-compound-outro-no-dependencies/main.svelte diff --git a/CHANGELOG.md b/CHANGELOG.md index c2611da704..1328066397 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Allow exiting a reactive block early with `break $` ([#2828](https://github.com/sveltejs/svelte/issues/2828)) * Check attributes have changed before setting them to avoid image flicker ([#3579](https://github.com/sveltejs/svelte/pull/3579)) * Fix generating malformed code for `{@debug}` tags with no dependencies ([#3588](https://github.com/sveltejs/svelte/issue/3588)) +* Fix generated code in specific case involving compound ifs and child components ([#3595](https://github.com/sveltejs/svelte/issue/3595)) * Fix `bind:this` binding to a store ([#3591](https://github.com/sveltejs/svelte/issue/3591)) * Use safer `HTMLElement` check before extending class ([#3608](https://github.com/sveltejs/svelte/issue/3608)) * Add `location` as a known global ([#3619](https://github.com/sveltejs/svelte/pull/3619)) diff --git a/src/compiler/compile/render_dom/wrappers/IfBlock.ts b/src/compiler/compile/render_dom/wrappers/IfBlock.ts index c54994a36c..dff7851b5a 100644 --- a/src/compiler/compile/render_dom/wrappers/IfBlock.ts +++ b/src/compiler/compile/render_dom/wrappers/IfBlock.ts @@ -271,8 +271,8 @@ export default class IfBlockWrapper extends Wrapper { ? b` ${snippet && ( dependencies.length > 0 - ? b`if ((${condition} == null) || ${changed(dependencies)}) ${condition} = !!(${snippet})` - : b`if (${condition} == null) ${condition} = !!(${snippet})` + ? b`if (${condition} == null || ${changed(dependencies)}) ${condition} = !!${snippet}` + : b`if (${condition} == null) ${condition} = !!${snippet}` )} if (${condition}) return ${block.name};` : b`return ${block.name};`)} @@ -388,7 +388,11 @@ export default class IfBlockWrapper extends Wrapper { function ${select_block_type}(#changed, #ctx) { ${this.branches.map(({ dependencies, condition, snippet }, i) => condition ? b` - ${snippet && b`if ((${condition} == null) || ${changed(dependencies)}) ${condition} = !!(${snippet})`} + ${snippet && ( + dependencies.length > 0 + ? b`if (${condition} == null || ${changed(dependencies)}) ${condition} = !!${snippet}` + : b`if (${condition} == null) ${condition} = !!${snippet}` + )} if (${condition}) return ${i};` : b`return ${i};`)} ${!has_else && b`return -1;`} diff --git a/test/runtime/samples/if-block-compound-outro-no-dependencies/_config.js b/test/runtime/samples/if-block-compound-outro-no-dependencies/_config.js new file mode 100644 index 0000000000..58b0521022 --- /dev/null +++ b/test/runtime/samples/if-block-compound-outro-no-dependencies/_config.js @@ -0,0 +1,3 @@ +export default { + html: `blah blah blah blah` +}; diff --git a/test/runtime/samples/if-block-compound-outro-no-dependencies/main.svelte b/test/runtime/samples/if-block-compound-outro-no-dependencies/main.svelte new file mode 100644 index 0000000000..84224116da --- /dev/null +++ b/test/runtime/samples/if-block-compound-outro-no-dependencies/main.svelte @@ -0,0 +1,32 @@ + + +{#if $foo} + blah +{:else} + {#if bar()} + + {/if} +{/if} + +{#if $foo} + blah +{:else} + {#if bar} + + {/if} +{/if} + +{#if $foo} + blah +{:else if bar()} + +{/if} + +{#if $foo} + blah +{:else if bar} + +{/if} From 39b387a4546d36cf6a4d045f4439e8197cd6cda4 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Sat, 19 Oct 2019 03:40:59 -0400 Subject: [PATCH 004/395] hoist globals even if they are mentioned in a script block (#3607) --- src/compiler/compile/Component.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index 8d1384e7a1..b22b351da0 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -569,6 +569,7 @@ export default class Component { this.add_var({ name, global: true, + hoistable: true }); } }); @@ -661,6 +662,7 @@ export default class Component { this.add_var({ name, global: true, + hoistable: true }); } }); From 0d36f1a7941dc11b75d44f329ae7efddffffd25d Mon Sep 17 00:00:00 2001 From: Conduitry Date: Sat, 19 Oct 2019 03:46:46 -0400 Subject: [PATCH 005/395] don't consult known globals when walking Expression nodes (#3708) --- src/compiler/compile/nodes/shared/Expression.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/compiler/compile/nodes/shared/Expression.ts b/src/compiler/compile/nodes/shared/Expression.ts index 4a3e96a282..ad4a1bc24d 100644 --- a/src/compiler/compile/nodes/shared/Expression.ts +++ b/src/compiler/compile/nodes/shared/Expression.ts @@ -3,7 +3,7 @@ import { walk } from 'estree-walker'; import is_reference from 'is-reference'; import flatten_reference from '../../utils/flatten_reference'; import { create_scopes, Scope, extract_names } from '../../utils/scope'; -import { globals, sanitize } from '../../../utils/names'; +import { sanitize } from '../../../utils/names'; import Wrapper from '../../render_dom/wrappers/shared/Wrapper'; import TemplateScope from './TemplateScope'; import get_object from '../../utils/get_object'; @@ -75,8 +75,6 @@ export default class Expression { if (scope.has(name)) return; - if (globals.has(name) && !(component.var_lookup.has(name) || template_scope.names.has(name))) return; - if (name[0] === '$' && template_scope.names.has(name.slice(1))) { component.error(node, { code: `contextual-store`, @@ -202,7 +200,6 @@ export default class Expression { const { name } = flatten_reference(node); if (scope.has(name)) return; - if (globals.has(name) && !(component.var_lookup.has(name) || template_scope.names.has(name))) return; if (function_expression) { if (template_scope.names.has(name)) { From 8ef32d766ad12c14aa922083af136e9d7d027206 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Sun, 20 Oct 2019 07:39:11 -0400 Subject: [PATCH 006/395] produce better code for (#3631) --- CHANGELOG.md | 1 + .../compile/render_dom/wrappers/Slot.ts | 20 ++++++++++--------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1328066397..e39b8b4972 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ * Use safer `HTMLElement` check before extending class ([#3608](https://github.com/sveltejs/svelte/issue/3608)) * Add `location` as a known global ([#3619](https://github.com/sveltejs/svelte/pull/3619)) * Support `{#await}` with `{:catch}` but no `{:then}` ([#3623](https://github.com/sveltejs/svelte/issues/3623)) +* Clean up dead code emitted for ``s ([#3631](https://github.com/sveltejs/svelte/issues/3631)) * Fix tracking of dependencies of compound assignments in reactive statements ([#3634](https://github.com/sveltejs/svelte/issues/3634)) * Flush changes in newly attached block when using `{#await}` ([#3660](https://github.com/sveltejs/svelte/issues/3660)) * Throw exception immediately when calling `createEventDispatcher()` after component instantiation ([#3667](https://github.com/sveltejs/svelte/pull/3667)) diff --git a/src/compiler/compile/render_dom/wrappers/Slot.ts b/src/compiler/compile/render_dom/wrappers/Slot.ts index 1ab1b6abcb..fb90afab5b 100644 --- a/src/compiler/compile/render_dom/wrappers/Slot.ts +++ b/src/compiler/compile/render_dom/wrappers/Slot.ts @@ -137,12 +137,12 @@ export default class SlotWrapper extends Wrapper { block.render_listeners(`_${slot.name}`); block.event_listeners = listeners; - if (block.chunks.create) create.push(b`if (!${slot}) { ${block.chunks.create} }`); - if (block.chunks.claim) claim.push(b`if (!${slot}) { ${block.chunks.claim} }`); - if (block.chunks.hydrate) hydrate.push(b`if (!${slot}) { ${block.chunks.hydrate} }`); - if (block.chunks.mount) mount.push(b`if (!${slot}) { ${block.chunks.mount} }`); - if (block.chunks.update) update.push(b`if (!${slot}) { ${block.chunks.update} }`); - if (block.chunks.destroy) destroy.push(b`if (!${slot}) { ${block.chunks.destroy} }`); + 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; @@ -155,9 +155,11 @@ export default class SlotWrapper extends Wrapper { b`if (${slot}) ${slot}.c();` ); - block.chunks.claim.push( - b`if (${slot}) ${slot}.l(${parent_nodes});` - ); + if (renderer.options.hydratable) { + block.chunks.claim.push( + b`if (${slot}) ${slot}.l(${parent_nodes});` + ); + } block.chunks.mount.push(b` if (${slot}) { From e3953b234c47c1337e3fc17ca33332f537182bc6 Mon Sep 17 00:00:00 2001 From: vages Date: Sun, 20 Oct 2019 21:26:26 +0200 Subject: [PATCH 007/395] Add Entur to "Who is using Svelte?" (#3754) --- site/src/routes/_components/WhosUsingSvelte.svelte | 1 + site/static/organisations/entur.svg | 1 + 2 files changed, 2 insertions(+) create mode 100644 site/static/organisations/entur.svg diff --git a/site/src/routes/_components/WhosUsingSvelte.svelte b/site/src/routes/_components/WhosUsingSvelte.svelte index fe9a2fcc9d..2a944fe289 100644 --- a/site/src/routes/_components/WhosUsingSvelte.svelte +++ b/site/src/routes/_components/WhosUsingSvelte.svelte @@ -55,6 +55,7 @@ Deck logo Dextra logo Entriwise logo + Entur logo From-Now-On logo FusionCharts logo GoDaddy logo diff --git a/site/static/organisations/entur.svg b/site/static/organisations/entur.svg new file mode 100644 index 0000000000..98bb5cc937 --- /dev/null +++ b/site/static/organisations/entur.svg @@ -0,0 +1 @@ + \ No newline at end of file From fca35def5389c8c132b80882d3cf4154d77bf4e9 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Mon, 21 Oct 2019 23:19:15 +0800 Subject: [PATCH 008/395] deconflict with builtins (#3724) --- src/compiler/compile/render_dom/Block.ts | 4 +++- test/js/samples/debug-no-dependencies/expected.js | 10 +++++----- test/js/samples/deconflict-builtins/expected.js | 10 +++++----- .../samples/each-block-array-literal/expected.js | 10 +++++----- .../samples/each-block-keyed-animated/expected.js | 14 +++++++------- test/js/samples/each-block-keyed/expected.js | 14 +++++++------- .../samples/deconflict-builtins-2/_config.js | 4 ++++ .../samples/deconflict-builtins-2/main.svelte | 5 +++++ 8 files changed, 41 insertions(+), 30 deletions(-) create mode 100644 test/runtime/samples/deconflict-builtins-2/_config.js create mode 100644 test/runtime/samples/deconflict-builtins-2/main.svelte diff --git a/src/compiler/compile/render_dom/Block.ts b/src/compiler/compile/render_dom/Block.ts index f73212f3ba..b268954dd0 100644 --- a/src/compiler/compile/render_dom/Block.ts +++ b/src/compiler/compile/render_dom/Block.ts @@ -146,11 +146,13 @@ export default class Block { if (!wrapper.var) continue; + let suffix = ''; if (dupes.has(wrapper.var.name)) { const i = counts.get(wrapper.var.name) || 0; counts.set(wrapper.var.name, i + 1); - wrapper.var.name = this.get_unique_name(wrapper.var.name + i).name; + suffix = i; } + wrapper.var.name = this.get_unique_name(wrapper.var.name + suffix).name; } } diff --git a/test/js/samples/debug-no-dependencies/expected.js b/test/js/samples/debug-no-dependencies/expected.js index 2212f3e0bb..57a67e9ea9 100644 --- a/test/js/samples/debug-no-dependencies/expected.js +++ b/test/js/samples/debug-no-dependencies/expected.js @@ -60,7 +60,7 @@ function create_each_block(ctx) { } function create_fragment(ctx) { - let each_anchor; + let each_1_anchor; let each_value = things; let each_blocks = []; @@ -74,7 +74,7 @@ function create_fragment(ctx) { each_blocks[i].c(); } - each_anchor = empty(); + each_1_anchor = empty(); }, l: function claim(nodes) { throw new Error("options.hydrate only works if the component was compiled with the `hydratable: true` option"); @@ -84,7 +84,7 @@ function create_fragment(ctx) { each_blocks[i].m(target, anchor); } - insert_dev(target, each_anchor, anchor); + insert_dev(target, each_1_anchor, anchor); }, p: function update(changed, ctx) { if (changed.things) { @@ -99,7 +99,7 @@ function create_fragment(ctx) { } else { each_blocks[i] = create_each_block(child_ctx); each_blocks[i].c(); - each_blocks[i].m(each_anchor.parentNode, each_anchor); + each_blocks[i].m(each_1_anchor.parentNode, each_1_anchor); } } @@ -114,7 +114,7 @@ function create_fragment(ctx) { o: noop, d: function destroy(detaching) { destroy_each(each_blocks, detaching); - if (detaching) detach_dev(each_anchor); + if (detaching) detach_dev(each_1_anchor); } }; diff --git a/test/js/samples/deconflict-builtins/expected.js b/test/js/samples/deconflict-builtins/expected.js index 222d473201..194188ad4e 100644 --- a/test/js/samples/deconflict-builtins/expected.js +++ b/test/js/samples/deconflict-builtins/expected.js @@ -43,7 +43,7 @@ function create_each_block(ctx) { } function create_fragment(ctx) { - let each_anchor; + let each_1_anchor; let each_value = ctx.createElement; let each_blocks = []; @@ -57,14 +57,14 @@ function create_fragment(ctx) { each_blocks[i].c(); } - each_anchor = empty(); + each_1_anchor = empty(); }, m(target, anchor) { for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].m(target, anchor); } - insert(target, each_anchor, anchor); + insert(target, each_1_anchor, anchor); }, p(changed, ctx) { if (changed.createElement) { @@ -79,7 +79,7 @@ function create_fragment(ctx) { } else { each_blocks[i] = create_each_block(child_ctx); each_blocks[i].c(); - each_blocks[i].m(each_anchor.parentNode, each_anchor); + each_blocks[i].m(each_1_anchor.parentNode, each_1_anchor); } } @@ -94,7 +94,7 @@ function create_fragment(ctx) { o: noop, d(detaching) { destroy_each(each_blocks, detaching); - if (detaching) detach(each_anchor); + if (detaching) detach(each_1_anchor); } }; } diff --git a/test/js/samples/each-block-array-literal/expected.js b/test/js/samples/each-block-array-literal/expected.js index 2eb1d903de..cd8e8b97c2 100644 --- a/test/js/samples/each-block-array-literal/expected.js +++ b/test/js/samples/each-block-array-literal/expected.js @@ -43,7 +43,7 @@ function create_each_block(ctx) { } function create_fragment(ctx) { - let each_anchor; + let each_1_anchor; let each_value = [ctx.a, ctx.b, ctx.c, ctx.d, ctx.e]; let each_blocks = []; @@ -57,14 +57,14 @@ function create_fragment(ctx) { each_blocks[i].c(); } - each_anchor = empty(); + each_1_anchor = empty(); }, m(target, anchor) { for (let i = 0; i < 5; i += 1) { each_blocks[i].m(target, anchor); } - insert(target, each_anchor, anchor); + insert(target, each_1_anchor, anchor); }, p(changed, ctx) { if (changed.a || changed.b || changed.c || changed.d || changed.e) { @@ -79,7 +79,7 @@ function create_fragment(ctx) { } else { each_blocks[i] = create_each_block(child_ctx); each_blocks[i].c(); - each_blocks[i].m(each_anchor.parentNode, each_anchor); + each_blocks[i].m(each_1_anchor.parentNode, each_1_anchor); } } @@ -92,7 +92,7 @@ function create_fragment(ctx) { o: noop, d(detaching) { destroy_each(each_blocks, detaching); - if (detaching) detach(each_anchor); + if (detaching) detach(each_1_anchor); } }; } diff --git a/test/js/samples/each-block-keyed-animated/expected.js b/test/js/samples/each-block-keyed-animated/expected.js index e2e8357805..02022d7d67 100644 --- a/test/js/samples/each-block-keyed-animated/expected.js +++ b/test/js/samples/each-block-keyed-animated/expected.js @@ -63,15 +63,15 @@ function create_each_block(key_1, ctx) { function create_fragment(ctx) { let each_blocks = []; - let each_lookup = new Map(); - let each_anchor; + let each_1_lookup = new Map(); + let each_1_anchor; let each_value = ctx.things; const get_key = ctx => ctx.thing.id; for (let i = 0; i < each_value.length; i += 1) { let child_ctx = get_each_context(ctx, each_value, i); let key = get_key(child_ctx); - each_lookup.set(key, each_blocks[i] = create_each_block(key, child_ctx)); + each_1_lookup.set(key, each_blocks[i] = create_each_block(key, child_ctx)); } return { @@ -80,19 +80,19 @@ function create_fragment(ctx) { each_blocks[i].c(); } - each_anchor = empty(); + each_1_anchor = empty(); }, m(target, anchor) { for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].m(target, anchor); } - insert(target, each_anchor, anchor); + insert(target, each_1_anchor, anchor); }, p(changed, ctx) { const each_value = ctx.things; for (let i = 0; i < each_blocks.length; i += 1) each_blocks[i].r(); - each_blocks = update_keyed_each(each_blocks, changed, get_key, 1, ctx, each_value, each_lookup, each_anchor.parentNode, fix_and_destroy_block, create_each_block, each_anchor, get_each_context); + each_blocks = update_keyed_each(each_blocks, changed, get_key, 1, ctx, each_value, each_1_lookup, each_1_anchor.parentNode, fix_and_destroy_block, create_each_block, each_1_anchor, get_each_context); for (let i = 0; i < each_blocks.length; i += 1) each_blocks[i].a(); }, i: noop, @@ -102,7 +102,7 @@ function create_fragment(ctx) { each_blocks[i].d(detaching); } - if (detaching) detach(each_anchor); + if (detaching) detach(each_1_anchor); } }; } diff --git a/test/js/samples/each-block-keyed/expected.js b/test/js/samples/each-block-keyed/expected.js index 5e149826b8..050c499e01 100644 --- a/test/js/samples/each-block-keyed/expected.js +++ b/test/js/samples/each-block-keyed/expected.js @@ -48,15 +48,15 @@ function create_each_block(key_1, ctx) { function create_fragment(ctx) { let each_blocks = []; - let each_lookup = new Map(); - let each_anchor; + let each_1_lookup = new Map(); + let each_1_anchor; let each_value = ctx.things; const get_key = ctx => ctx.thing.id; for (let i = 0; i < each_value.length; i += 1) { let child_ctx = get_each_context(ctx, each_value, i); let key = get_key(child_ctx); - each_lookup.set(key, each_blocks[i] = create_each_block(key, child_ctx)); + each_1_lookup.set(key, each_blocks[i] = create_each_block(key, child_ctx)); } return { @@ -65,18 +65,18 @@ function create_fragment(ctx) { each_blocks[i].c(); } - each_anchor = empty(); + each_1_anchor = empty(); }, m(target, anchor) { for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].m(target, anchor); } - insert(target, each_anchor, anchor); + insert(target, each_1_anchor, anchor); }, p(changed, ctx) { const each_value = ctx.things; - each_blocks = update_keyed_each(each_blocks, changed, get_key, 1, ctx, each_value, each_lookup, each_anchor.parentNode, destroy_block, create_each_block, each_anchor, get_each_context); + each_blocks = update_keyed_each(each_blocks, changed, get_key, 1, ctx, each_value, each_1_lookup, each_1_anchor.parentNode, destroy_block, create_each_block, each_1_anchor, get_each_context); }, i: noop, o: noop, @@ -85,7 +85,7 @@ function create_fragment(ctx) { each_blocks[i].d(detaching); } - if (detaching) detach(each_anchor); + if (detaching) detach(each_1_anchor); } }; } diff --git a/test/runtime/samples/deconflict-builtins-2/_config.js b/test/runtime/samples/deconflict-builtins-2/_config.js new file mode 100644 index 0000000000..5870ff073b --- /dev/null +++ b/test/runtime/samples/deconflict-builtins-2/_config.js @@ -0,0 +1,4 @@ +export default { + html: `hello world`, + preserveIdentifiers: true, +}; \ No newline at end of file diff --git a/test/runtime/samples/deconflict-builtins-2/main.svelte b/test/runtime/samples/deconflict-builtins-2/main.svelte new file mode 100644 index 0000000000..82f9213045 --- /dev/null +++ b/test/runtime/samples/deconflict-builtins-2/main.svelte @@ -0,0 +1,5 @@ + + +{foo} \ No newline at end of file From d976203da8d8feb41d870580aa1edc86c6ea5161 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Sun, 20 Oct 2019 22:42:59 -0400 Subject: [PATCH 009/395] update code-red etc (#3752) --- package-lock.json | 53 ++++++++----------------------- package.json | 6 ++-- src/compiler/parse/acorn.ts | 2 -- src/compiler/parse/read/script.ts | 2 +- 4 files changed, 17 insertions(+), 46 deletions(-) diff --git a/package-lock.json b/package-lock.json index 97fa374f9d..f1eb8739b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.12.1", + "version": "3.13.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -167,9 +167,9 @@ "dev": true }, "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.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz", + "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==", "dev": true }, "acorn-globals": { @@ -490,14 +490,14 @@ "dev": true }, "code-red": { - "version": "0.0.17", - "resolved": "https://registry.npmjs.org/code-red/-/code-red-0.0.17.tgz", - "integrity": "sha512-RJJ48sXYOqyd0J4QelF4dRdYb+4DaLV/jHs4mNoxOdLroUGB840cMc9pMtEAbGKjFFzoTKREypFzqphBD8knMg==", + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/code-red/-/code-red-0.0.18.tgz", + "integrity": "sha512-g7W6RwRqBbQTtMaUqrNWDyyl2GK0Uulk/uZPzGdgTXpOGX/LA8bW67EKQLdQgpYfd6APhZVwoX2lrL7mnJOWkA==", "dev": true, "requires": { - "acorn": "^7.0.0", - "is-reference": "^1.1.3", - "periscopic": "^1.0.1", + "acorn": "^7.1.0", + "is-reference": "^1.1.4", + "periscopic": "^1.0.2", "sourcemap-codec": "^1.4.6" } }, @@ -1147,14 +1147,6 @@ "acorn": "^7.0.0", "acorn-jsx": "^5.0.2", "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "acorn": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.0.0.tgz", - "integrity": "sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ==", - "dev": true - } } }, "esprima": { @@ -1768,9 +1760,9 @@ "dev": true }, "is-reference": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.1.3.tgz", - "integrity": "sha512-W1iHHv/oyBb2pPxkBxtaewxa1BC58Pn5J0hogyCdefwUIvb6R+TGbAcIa4qPNYLqLhb3EnOgUf2MQkkF76BcKw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.1.4.tgz", + "integrity": "sha512-uJA/CDPO3Tao3GTrxYn6AwkM4nUPJiGGYu5+cB8qbC7WGFlrKZbiRo7SFKxUAEpFUfiHofWCXBUNhvYJMh+6zw==", "dev": true, "requires": { "@types/estree": "0.0.39" @@ -2727,17 +2719,6 @@ "dev": true, "requires": { "is-reference": "^1.1.4" - }, - "dependencies": { - "is-reference": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.1.4.tgz", - "integrity": "sha512-uJA/CDPO3Tao3GTrxYn6AwkM4nUPJiGGYu5+cB8qbC7WGFlrKZbiRo7SFKxUAEpFUfiHofWCXBUNhvYJMh+6zw==", - "dev": true, - "requires": { - "@types/estree": "0.0.39" - } - } } }, "pify": { @@ -3646,14 +3627,6 @@ "@types/istanbul-lib-coverage": "^2.0.1", "convert-source-map": "^1.6.0", "source-map": "^0.7.3" - }, - "dependencies": { - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - } } }, "validate-npm-package-license": { diff --git a/package.json b/package.json index c4f1269139..0cdc30eccb 100644 --- a/package.json +++ b/package.json @@ -60,17 +60,17 @@ "@types/node": "^8.10.53", "@typescript-eslint/eslint-plugin": "^1.13.0", "@typescript-eslint/parser": "^2.1.0", - "acorn": "^7.0.0", + "acorn": "^7.1.0", "agadoo": "^1.1.0", "c8": "^5.0.1", - "code-red": "0.0.17", + "code-red": "0.0.18", "codecov": "^3.5.0", "css-tree": "1.0.0-alpha22", "eslint": "^6.3.0", "eslint-plugin-import": "^2.18.2", "eslint-plugin-svelte3": "^2.7.3", "estree-walker": "^0.8.1", - "is-reference": "^1.1.3", + "is-reference": "^1.1.4", "jsdom": "^15.1.1", "kleur": "^3.0.3", "locate-character": "^2.0.5", diff --git a/src/compiler/parse/acorn.ts b/src/compiler/parse/acorn.ts index a7d2703956..30ab3c398c 100644 --- a/src/compiler/parse/acorn.ts +++ b/src/compiler/parse/acorn.ts @@ -4,13 +4,11 @@ const Parser = acorn.Parser; export const parse = (source: string) => Parser.parse(source, { sourceType: 'module', - // @ts-ignore TODO pending release of fixed types ecmaVersion: 11, locations: true }); export const parse_expression_at = (source: string, index: number) => Parser.parseExpressionAt(source, index, { - // @ts-ignore TODO pending release of fixed types ecmaVersion: 11, locations: true }); \ No newline at end of file diff --git a/src/compiler/parse/read/script.ts b/src/compiler/parse/read/script.ts index efc9306677..0c416be6e8 100644 --- a/src/compiler/parse/read/script.ts +++ b/src/compiler/parse/read/script.ts @@ -45,7 +45,7 @@ export default function read_script(parser: Parser, start: number, attributes: N let ast: Program; try { - ast = acorn.parse(source); + ast = acorn.parse(source) as any as Program; } catch (err) { parser.acorn_error(err); } From ebf7a9024a47a5dd40a9e43c6c589bb6c1ffa449 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Mon, 21 Oct 2019 11:21:54 -0400 Subject: [PATCH 010/395] alpha.1 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index f1eb8739b5..1ca8b7cdb5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.13.0-alpha.0", + "version": "3.13.0-alpha.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 0cdc30eccb..950c44d965 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.13.0-alpha.0", + "version": "3.13.0-alpha.1", "description": "Cybernetically enhanced web apps", "module": "index.mjs", "main": "index", From 714508ccc549ab5790eefaa59502d173619cf7c8 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 21 Oct 2019 15:33:19 -0400 Subject: [PATCH 011/395] allow spring/tweened values to be initially undefined - closes #3761 --- src/runtime/motion/spring.ts | 4 ++-- src/runtime/motion/tweened.ts | 7 ++++++- test/motion/index.js | 23 +++++++++++++++++++++++ 3 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 test/motion/index.js diff --git a/src/runtime/motion/spring.ts b/src/runtime/motion/spring.ts index 7742dd4106..20fa6ffcde 100644 --- a/src/runtime/motion/spring.ts +++ b/src/runtime/motion/spring.ts @@ -65,7 +65,7 @@ interface Spring extends Readable{ stiffness: number; } -export function spring(value: T, opts: SpringOpts = {}): Spring { +export function spring(value?: T, opts: SpringOpts = {}): Spring { const store = writable(value); const { stiffness = 0.15, damping = 0.8, precision = 0.01 } = opts; @@ -84,7 +84,7 @@ export function spring(value: T, opts: SpringOpts = {}): Spring { target_value = new_value; const token = current_token = {}; - if (opts.hard || (spring.stiffness >= 1 && spring.damping >= 1)) { + if (value == null || opts.hard || (spring.stiffness >= 1 && spring.damping >= 1)) { cancel_task = true; // cancel any running animation last_time = now(); last_value = value; diff --git a/src/runtime/motion/tweened.ts b/src/runtime/motion/tweened.ts index 7159c17eb5..e33f0f79f9 100644 --- a/src/runtime/motion/tweened.ts +++ b/src/runtime/motion/tweened.ts @@ -69,13 +69,18 @@ interface Tweened extends Readable { update(updater: Updater, opts: Options): Promise; } -export function tweened(value: T, defaults: Options = {}): Tweened { +export function tweened(value?: T, defaults: Options = {}): Tweened { const store = writable(value); let task: Task; let target_value = value; function set(new_value: T, opts: Options) { + if (value == null) { + store.set(value = new_value); + return Promise.resolve(); + } + target_value = new_value; let previous_task = task; diff --git a/test/motion/index.js b/test/motion/index.js new file mode 100644 index 0000000000..33489b18e1 --- /dev/null +++ b/test/motion/index.js @@ -0,0 +1,23 @@ +import * as assert from 'assert'; +import { get } from '../../store'; +import { spring, tweened } from '../../motion'; + +describe('motion', () => { + describe('spring', () => { + it('handles initially undefined values', () => { + const size = spring(); + + size.set(100); + assert.equal(get(size), 100); + }); + }); + + describe('tweened', () => { + it('handles initially undefined values', () => { + const size = tweened(); + + size.set(100); + assert.equal(get(size), 100); + }); + }); +}); From 488d06860d0788afb2ef180f2de35501e98f4b2e Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Tue, 22 Oct 2019 09:22:58 +0800 Subject: [PATCH 012/395] Adding contributing.md --- CONTRIBUTING.md | 128 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..d698d02cd2 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,128 @@ +# Contributing to Svelte + +Svelte is a new way to build web applications. It's a compiler that takes your declarative components and converts them into efficient JavaScript that surgically updates the DOM. + +The [Open Source Guides](https://opensource.guide/) website has a collection of resources for individuals, communities, and companies who want to learn how to run and contribute to an open source project. Contributors and people new to open source alike will find the following guides especially useful: + +* [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) +* [Building Welcoming Communities](https://opensource.guide/building-community/) + +## Get Involved + +There are many ways to contribute to Svelte, and many of them do not involve writing any code. Here's a few ideas to get started: + +- Simply start using Svelte. Go through the [Getting Started](https://svelte.dev/blog/the-easiest-way-to-get-started) guide. Does everything work as expected? If not, we're always looking for improvements. Let us know by [opening an issue](#reporting-new-issues). +- Look through the [open issues](https://github.com/sveltejs/svelte/issues). Provide workarounds, ask for clarification, or suggest labels. Help [triage issues](#triaging-issues-and-pull-requests). +- If you find an issue you would like to fix, [open a pull request](#your-first-pull-request). +- Read through our amazing [Tutorials](https://svelte.dev/tutorial/basics). If you find anything that is confusing or can be improved, you can make edits by clicking "Edit this chapter" at the bottom left of the tutorials. +- Take a look at the [features requested](https://github.com/sveltejs/svelte/labels/enhancement) by others in the community and consider opening a pull request if you see something you want to work on. + +Contributions are very welcome. If you think you need help planning your contribution, please ping us on Twitter at [@sveltejs](https://twitter.com/sveltejs) and let us know you are looking for a bit of help. + +### Triaging Issues and Pull Requests + +One great way you can contribute to the project without writing any code is to help triage issues and pull requests as they come in. + +- Ask for more information if you believe the issue does not provide all the details required to solve it. +- Suggest [labels](https://github.com/sveltejs/svelte/labels) that can help categorize issues. +- Flag issues that are stale or that should be closed. +- Ask for test plans and review code. + +## Bugs + +We use [GitHub Issues](https://github.com/sveltejs/svelte/issues) for our public bugs. If you would like to report a problem, take a look around and see if someone already opened an issue about it. If you a are certain this is a new, unreported bug, you can submit a [bug report](#reporting-new-issues). + +If you have questions about using Svelte, contact the Svelte Twitter account at [@sveltejs](https://twitter.com/sveltejs), and we will do our best to answer your questions. + +If you see anything you'd like to be implemented, create a [feature request issue](https://github.com/sveltejs/svelte/issues/new?template=feature_request.md) + +## Reporting New Issues + +When [opening a new issue](https://github.com/sveltejs/svelte/issues/new/choose), always make sure to fill out the issue template. **This step is very important!** Not doing so may result in your issue not managed in a timely fashion. Don't take this personally if this happens, and feel free to open a new issue once you've gathered all the information required by the template. + +- **One issue, one bug:** Please report a single bug per issue. +- **Provide reproduction steps:** List all the steps necessary to reproduce the issue. The person reading your bug report should be able to follow these steps to reproduce your issue with minimal effort. + +## Installation + +1. Ensure you have [npm](https://www.npmjs.com/get-npm) installed. +1. After cloning the repository, run `npm install` in the root of the repository. +1. To start a development server, run `npm run dev`. + + +## Pull Requests + +### Your First Pull Request + +So you have decided to contribute code back to upstream by opening a pull request. You've invested a good chunk of time, and we appreciate it. We will do our best to work with you and get the PR looked at. + +Working on your first Pull Request? You can learn how from this free video series: + +[**How to Contribute to an Open Source Project on GitHub**](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github) + +### Proposing a Change + +If you would like to request a new feature or enhancement but are not yet thinking about opening a pull request, you can also file an issue with [feature template](https://github.com/sveltejs/svelte/issues/new?template=feature_request.md). + +If you're only fixing a bug, it's fine to submit a pull request right away but we still recommend to file an issue detailing what you're fixing. This is helpful in case we don't accept that specific fix but want to keep track of the issue. + +### Sending a Pull Request + +Small pull requests are much easier to review and more likely to get merged. Make sure the PR does only one thing, otherwise please split it. + +Please make sure the following is done when submitting a pull request: + +1. Fork [the repository](https://github.com/sveltejs/svelte) and create your branch from `master`. +1. Describe your **test plan** in your pull request description. Make sure to test your changes. +1. Make sure your code lints (`npm run lint`). +1. Make sure your tests pass (`npm run test`). + +All pull requests should be opened against the `master` branch. + +#### Test Plan + +A good test plan has the exact commands you ran and their output, provides screenshots or videos if the pull request changes UI. + +- If you've changed APIs, update the documentation. + +#### Writing Tests + +All tests are located in `/test` folder. + +Tests samples are kept in `/test/xxx/samples` folder. + +#### Running Tests + +1. To run test, run `npm run test` +1. To run test for a specific feature, you can use the `-g` (aka `--grep`) option. For example, to only run test invloving transitions, run `npm run test -- -g transition`. +1. To run only 1 test sample, append the `.solo` to the test sample folder name, for example, `test/runtime/samples/action.solo`. + +#### Breaking Changes + +When adding a new breaking change, follow this template in your pull request: + +```md +### New breaking change here + +- **Who does this affect**: +- **How to migrate**: +- **Why make this breaking change**: +- **Severity (number of people affected x effort)**: +``` + +### What Happens Next? + +The core Svelte team will be monitoring for pull requests. Do help us by making your pull request easy to review by following the guidelines above. + +## Style Guide + +[Eslint](https://eslint.org) will catch most styling issues that may exist in your code. You can check the status of your code styling by simply running `npm run lint`. + +### Code Conventions + +- `snake_case` for internal variable names and methods +- `camelCase` for public variable names and methods. + +## License + +By contributing to Svelte, you agree that your contributions will be licensed under its [MIT license](https://github.com/sveltejs/svelte/blob/master/LICENSE). \ No newline at end of file From 614393edcba4806300d0601919e8b70d5b81be68 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Tue, 22 Oct 2019 09:17:23 -0400 Subject: [PATCH 013/395] add sigil-expression-function-body test against regression (#3756) --- test/runtime/samples/sigil-expression-function-body/_config.js | 3 +++ .../runtime/samples/sigil-expression-function-body/main.svelte | 1 + 2 files changed, 4 insertions(+) create mode 100644 test/runtime/samples/sigil-expression-function-body/_config.js create mode 100644 test/runtime/samples/sigil-expression-function-body/main.svelte diff --git a/test/runtime/samples/sigil-expression-function-body/_config.js b/test/runtime/samples/sigil-expression-function-body/_config.js new file mode 100644 index 0000000000..fccd0db2d0 --- /dev/null +++ b/test/runtime/samples/sigil-expression-function-body/_config.js @@ -0,0 +1,3 @@ +export default { + html: `@foo` +}; diff --git a/test/runtime/samples/sigil-expression-function-body/main.svelte b/test/runtime/samples/sigil-expression-function-body/main.svelte new file mode 100644 index 0000000000..074496d6f4 --- /dev/null +++ b/test/runtime/samples/sigil-expression-function-body/main.svelte @@ -0,0 +1 @@ +{(() => '@foo')()} From d91e9afab6fbaf85d0263111ce4b64932b7e5e09 Mon Sep 17 00:00:00 2001 From: Paul Murray Date: Tue, 22 Oct 2019 16:45:43 -0400 Subject: [PATCH 014/395] Fixes #3008: Better SSR docs --- site/content/docs/03-run-time.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/site/content/docs/03-run-time.md b/site/content/docs/03-run-time.md index 551945eade..fd709dd9d5 100644 --- a/site/content/docs/03-run-time.md +++ b/site/content/docs/03-run-time.md @@ -1019,8 +1019,12 @@ Unlike client-side components, server-side components don't have a lifespan afte A server-side component exposes a `render` method that can be called with optional props. It returns an object with `head`, `html`, and `css` properties, where `head` contains the contents of any `` elements encountered. +You can import a Svelte component directly into Node using [`svelte/register`](docs#svelte_register). + ```js -const App = require('./App.svelte'); +require('svelte/register'); + +const App = require('./App.svelte').default; const { head, html, css } = App.render({ answer: 42 From f68b3a3b8c7bd3e023a3576f6f6a7ac84580267f Mon Sep 17 00:00:00 2001 From: Conduitry Date: Wed, 23 Oct 2019 12:43:20 -0400 Subject: [PATCH 015/395] Fix boolean attributes in presence of spread attributes (#3775) * add failing tests * fix boolean attributes along with spreads (DOM mode) * fix boolean attributes along with spreads (SSR mode) * update changelog (#3764) * fix removing attributes in spreads --- CHANGELOG.md | 1 + src/compiler/compile/nodes/Attribute.ts | 2 +- .../render_dom/wrappers/Element/Attribute.ts | 11 +++++-- .../render_dom/wrappers/Element/index.ts | 20 ++++++------ .../compile/render_ssr/handlers/Element.ts | 32 +++++++++---------- src/runtime/internal/dom.ts | 4 ++- src/runtime/internal/ssr.ts | 2 +- .../_config.js | 3 ++ .../main.svelte | 1 + .../attribute-boolean-with-spread/_config.js | 3 ++ .../attribute-boolean-with-spread/main.svelte | 1 + .../samples/spread-element-removal/_config.js | 3 ++ .../spread-element-removal/main.svelte | 1 + 13 files changed, 52 insertions(+), 32 deletions(-) create mode 100644 test/runtime/samples/attribute-boolean-case-insensitive/_config.js create mode 100644 test/runtime/samples/attribute-boolean-case-insensitive/main.svelte create mode 100644 test/runtime/samples/attribute-boolean-with-spread/_config.js create mode 100644 test/runtime/samples/attribute-boolean-with-spread/main.svelte create mode 100644 test/runtime/samples/spread-element-removal/_config.js create mode 100644 test/runtime/samples/spread-element-removal/main.svelte diff --git a/CHANGELOG.md b/CHANGELOG.md index e39b8b4972..31fc1eddd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ * Throw exception immediately when calling `createEventDispatcher()` after component instantiation ([#3667](https://github.com/sveltejs/svelte/pull/3667)) * Fix globals shadowing contextual template scope ([#3674](https://github.com/sveltejs/svelte/issues/3674)) * Fix error resulting from trying to set a read-only property when spreading element attributes ([#3681](https://github.com/sveltejs/svelte/issues/3681)) +* Fix handling of boolean attributes in presence of other spread attributes ([#3764](https://github.com/sveltejs/svelte/issues/3764)) ## 3.12.1 diff --git a/src/compiler/compile/nodes/Attribute.ts b/src/compiler/compile/nodes/Attribute.ts index c09f9c3074..97d2fd7b2e 100644 --- a/src/compiler/compile/nodes/Attribute.ts +++ b/src/compiler/compile/nodes/Attribute.ts @@ -9,7 +9,7 @@ import TemplateScope from './shared/TemplateScope'; import { x } from 'code-red'; export default class Attribute extends Node { - type: 'Attribute'; + type: 'Attribute' | 'Spread'; start: number; end: number; scope: TemplateScope; diff --git a/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts b/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts index d2def7e5cf..2cd284108c 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts @@ -44,9 +44,7 @@ export default class AttributeWrapper { const element = this.parent; const name = fix_attribute_casing(this.node.name); - let metadata = element.node.namespace ? null : attribute_lookup[name]; - if (metadata && metadata.applies_to && !~metadata.applies_to.indexOf(element.node.name)) - metadata = null; + const metadata = this.get_metadata(); const is_indirectly_bound_value = name === 'value' && @@ -193,6 +191,13 @@ export default class AttributeWrapper { } } + get_metadata() { + if (this.parent.node.namespace) return null; + const metadata = attribute_lookup[fix_attribute_casing(this.node.name)]; + if (metadata && metadata.applies_to && !metadata.applies_to.includes(this.parent.node.name)) return null; + return metadata; + } + get_class_name_text() { const scoped_css = this.node.chunks.some((chunk: Text) => chunk.synthetic); const rendered = this.render_chunks(); diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts index d7c1c7686b..bb4c2d310a 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts @@ -573,8 +573,7 @@ export default class ElementWrapper extends Wrapper { } }); - // @ts-ignore todo: - if (this.node.attributes.find(attr => attr.type === 'Spread')) { + if (this.node.attributes.some(attr => attr.is_spread)) { this.add_spread_attributes(block); return; } @@ -591,21 +590,24 @@ export default class ElementWrapper extends Wrapper { const initial_props = []; const updates = []; - this.node.attributes - .filter(attr => attr.type === 'Attribute' || attr.type === 'Spread') + this.attributes .forEach(attr => { - const condition = attr.dependencies.size > 0 - ? changed(Array.from(attr.dependencies)) + const condition = attr.node.dependencies.size > 0 + ? changed(Array.from(attr.node.dependencies)) : null; - if (attr.is_spread) { - const snippet = attr.expression.manipulate(block); + if (attr.node.is_spread) { + const snippet = attr.node.expression.manipulate(block); initial_props.push(snippet); updates.push(condition ? x`${condition} && ${snippet}` : snippet); } else { - const snippet = x`{ ${attr.name}: ${attr.get_value(block)} }`; + const metadata = attr.get_metadata(); + const snippet = x`{ ${ + (metadata && metadata.property_name) || + fix_attribute_casing(attr.node.name) + }: ${attr.node.get_value(block)} }`; initial_props.push(snippet); updates.push(condition ? x`${condition} && ${snippet}` : snippet); diff --git a/src/compiler/compile/render_ssr/handlers/Element.ts b/src/compiler/compile/render_ssr/handlers/Element.ts index 762152f4d5..1f7c0b7b9f 100644 --- a/src/compiler/compile/render_ssr/handlers/Element.ts +++ b/src/compiler/compile/render_ssr/handlers/Element.ts @@ -1,5 +1,4 @@ import { is_void } from '../../../utils/names'; -import Attribute from '../../nodes/Attribute'; import Class from '../../nodes/Class'; import { get_attribute_value, get_class_attribute_value } from './shared/get_attribute_value'; import { get_slot_scope } from './shared/get_slot_scope'; @@ -80,62 +79,61 @@ export default function(node: Element, renderer: Renderer, options: RenderOption let add_class_attribute = class_expression ? true : false; - if (node.attributes.find(attr => attr.is_spread)) { + if (node.attributes.some(attr => attr.is_spread)) { // TODO dry this out const args = []; node.attributes.forEach(attribute => { if (attribute.is_spread) { args.push(attribute.expression.node); } else { - if (attribute.name === 'value' && node.name === 'textarea') { + const name = attribute.name.toLowerCase(); + if (name === 'value' && node.name.toLowerCase() === 'textarea') { node_contents = get_attribute_value(attribute); } else if (attribute.is_true) { args.push(x`{ ${attribute.name}: true }`); } else if ( - boolean_attributes.has(attribute.name) && + boolean_attributes.has(name) && attribute.chunks.length === 1 && attribute.chunks[0].type !== 'Text' ) { // a boolean attribute with one non-Text chunk - args.push(x`{ ${attribute.name}: ${(attribute.chunks[0] as Expression).node} }`); - } else if (attribute.name === 'class' && class_expression) { + args.push(x`{ ${attribute.name}: ${(attribute.chunks[0] as Expression).node} || null }`); + } else if (name === 'class' && class_expression) { // Add class expression args.push(x`{ ${attribute.name}: [${get_class_attribute_value(attribute)}, ${class_expression}].join(' ').trim() }`); } else { - args.push(x`{ ${attribute.name}: ${attribute.name === 'class' ? get_class_attribute_value(attribute) : get_attribute_value(attribute)} }`); + args.push(x`{ ${attribute.name}: ${(name === 'class' ? get_class_attribute_value : get_attribute_value)(attribute)} }`); } } }); renderer.add_expression(x`@spread([${args}])`); } else { - node.attributes.forEach((attribute: Attribute) => { - if (attribute.type !== 'Attribute') return; - - if (attribute.name === 'value' && node.name === 'textarea') { + node.attributes.forEach(attribute => { + const name = attribute.name.toLowerCase(); + if (name === 'value' && node.name.toLowerCase() === 'textarea') { node_contents = get_attribute_value(attribute); } else if (attribute.is_true) { renderer.add_string(` ${attribute.name}`); } else if ( - boolean_attributes.has(attribute.name) && + boolean_attributes.has(name) && attribute.chunks.length === 1 && attribute.chunks[0].type !== 'Text' ) { // a boolean attribute with one non-Text chunk renderer.add_string(` `); renderer.add_expression(x`${(attribute.chunks[0] as Expression).node} ? "${attribute.name}" : ""`); - } else if (attribute.name === 'class' && class_expression) { + } else if (name === 'class' && class_expression) { add_class_attribute = false; - renderer.add_string(` class="`); + renderer.add_string(` ${attribute.name}="`); renderer.add_expression(x`[${get_class_attribute_value(attribute)}, ${class_expression}].join(' ').trim()`); renderer.add_string(`"`); } else if (attribute.chunks.length === 1 && attribute.chunks[0].type !== 'Text') { - const { name } = attribute; const snippet = (attribute.chunks[0] as Expression).node; - renderer.add_expression(x`@add_attribute("${name}", ${snippet}, ${boolean_attributes.has(name) ? 1 : 0})`); + renderer.add_expression(x`@add_attribute("${attribute.name}", ${snippet}, ${boolean_attributes.has(name) ? 1 : 0})`); } else { renderer.add_string(` ${attribute.name}="`); - renderer.add_expression(attribute.name === 'class' ? get_class_attribute_value(attribute) : get_attribute_value(attribute)); + renderer.add_expression((name === 'class' ? get_class_attribute_value : get_attribute_value)(attribute)); renderer.add_string(`"`); } }); diff --git a/src/runtime/internal/dom.ts b/src/runtime/internal/dom.ts index 481fb7d74a..c60f437863 100644 --- a/src/runtime/internal/dom.ts +++ b/src/runtime/internal/dom.ts @@ -93,7 +93,9 @@ export function set_attributes(node: Element & ElementCSSInlineStyle, attributes // @ts-ignore const descriptors = Object.getOwnPropertyDescriptors(node.__proto__); for (const key in attributes) { - if (key === 'style') { + if (attributes[key] == null) { + node.removeAttribute(key); + } else if (key === 'style') { node.style.cssText = attributes[key]; } else if (descriptors[key] && descriptors[key].set) { node[key] = attributes[key]; diff --git a/src/runtime/internal/ssr.ts b/src/runtime/internal/ssr.ts index d8fbf15f0a..208a4637c7 100644 --- a/src/runtime/internal/ssr.ts +++ b/src/runtime/internal/ssr.ts @@ -13,7 +13,7 @@ export function spread(args) { if (invalid_attribute_name_character.test(name)) return; const value = attributes[name]; - if (value === undefined) return; + if (value == null) return; if (value === true) str += " " + name; const escaped = String(value) diff --git a/test/runtime/samples/attribute-boolean-case-insensitive/_config.js b/test/runtime/samples/attribute-boolean-case-insensitive/_config.js new file mode 100644 index 0000000000..16e5ade6d2 --- /dev/null +++ b/test/runtime/samples/attribute-boolean-case-insensitive/_config.js @@ -0,0 +1,3 @@ +export default { + html: `` +}; diff --git a/test/runtime/samples/attribute-boolean-case-insensitive/main.svelte b/test/runtime/samples/attribute-boolean-case-insensitive/main.svelte new file mode 100644 index 0000000000..9020894086 --- /dev/null +++ b/test/runtime/samples/attribute-boolean-case-insensitive/main.svelte @@ -0,0 +1 @@ + diff --git a/test/runtime/samples/attribute-boolean-with-spread/_config.js b/test/runtime/samples/attribute-boolean-with-spread/_config.js new file mode 100644 index 0000000000..270804170d --- /dev/null +++ b/test/runtime/samples/attribute-boolean-with-spread/_config.js @@ -0,0 +1,3 @@ +export default { + html: `` +}; diff --git a/test/runtime/samples/attribute-boolean-with-spread/main.svelte b/test/runtime/samples/attribute-boolean-with-spread/main.svelte new file mode 100644 index 0000000000..d0616e3024 --- /dev/null +++ b/test/runtime/samples/attribute-boolean-with-spread/main.svelte @@ -0,0 +1 @@ + diff --git a/test/runtime/samples/spread-element-removal/_config.js b/test/runtime/samples/spread-element-removal/_config.js new file mode 100644 index 0000000000..270804170d --- /dev/null +++ b/test/runtime/samples/spread-element-removal/_config.js @@ -0,0 +1,3 @@ +export default { + html: `` +}; diff --git a/test/runtime/samples/spread-element-removal/main.svelte b/test/runtime/samples/spread-element-removal/main.svelte new file mode 100644 index 0000000000..f6adc82c80 --- /dev/null +++ b/test/runtime/samples/spread-element-removal/main.svelte @@ -0,0 +1 @@ + From 5dbb08d19b9ec85b49cc90f0a091a5638ee33631 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Thu, 24 Oct 2019 02:52:49 +0800 Subject: [PATCH 016/395] allow solo for test suite (#3747) --- test/css/index.js | 12 ++++++------ test/custom-elements/index.js | 6 +++--- test/hydration/index.js | 4 ++-- test/js/index.js | 4 ++-- test/parser/index.js | 12 ++++++------ test/preprocess/index.js | 10 +++++----- test/runtime/index.js | 6 +++--- test/server-side-rendering/index.js | 4 ++-- test/sourcemaps/index.js | 6 +++--- test/stats/index.js | 8 ++++---- test/test.js | 17 ++++++++++++++--- test/validator/index.js | 10 +++++----- test/vars/index.js | 8 ++++---- 13 files changed, 59 insertions(+), 48 deletions(-) diff --git a/test/css/index.js b/test/css/index.js index 61e8a0c6cb..1f90d243be 100644 --- a/test/css/index.js +++ b/test/css/index.js @@ -37,7 +37,7 @@ function create(code) { } describe('css', () => { - fs.readdirSync('test/css/samples').forEach(dir => { + fs.readdirSync(`${__dirname}/samples`).forEach(dir => { if (dir[0] === '.') return; // add .solo to a sample directory name to only run that test @@ -51,7 +51,7 @@ describe('css', () => { (solo ? it.only : skip ? it.skip : it)(dir, () => { const config = try_require(`./samples/${dir}/_config.js`) || {}; const input = fs - .readFileSync(`test/css/samples/${dir}/input.svelte`, 'utf-8') + .readFileSync(`${__dirname}/samples/${dir}/input.svelte`, 'utf-8') .replace(/\s+$/, ''); const expected_warnings = (config.warnings || []).map(normalize_warning); @@ -74,10 +74,10 @@ describe('css', () => { assert.deepEqual(dom_warnings, ssr_warnings); assert.deepEqual(dom_warnings.map(normalize_warning), expected_warnings); - fs.writeFileSync(`test/css/samples/${dir}/_actual.css`, dom.css.code); + fs.writeFileSync(`${__dirname}/samples/${dir}/_actual.css`, dom.css.code); const expected = { - html: read(`test/css/samples/${dir}/expected.html`), - css: read(`test/css/samples/${dir}/expected.css`) + html: read(`${__dirname}/samples/${dir}/expected.html`), + css: read(`${__dirname}/samples/${dir}/expected.css`) }; assert.equal(dom.css.code.replace(/svelte(-ref)?-[a-z0-9]+/g, (m, $1) => $1 ? m : 'svelte-xyz'), expected.css); @@ -112,7 +112,7 @@ describe('css', () => { new ClientComponent({ target, props: config.props }); const html = target.innerHTML; - fs.writeFileSync(`test/css/samples/${dir}/_actual.html`, html); + fs.writeFileSync(`${__dirname}/samples/${dir}/_actual.html`, html); assert.equal( normalizeHtml(window, html.replace(/svelte(-ref)?-[a-z0-9]+/g, (m, $1) => $1 ? m : 'svelte-xyz')), diff --git a/test/custom-elements/index.js b/test/custom-elements/index.js index 7fe47f2ecd..5d2847bfab 100644 --- a/test/custom-elements/index.js +++ b/test/custom-elements/index.js @@ -14,7 +14,7 @@ const page = ` `; -const assert = fs.readFileSync('test/custom-elements/assert.js', 'utf-8'); +const assert = fs.readFileSync(`${__dirname}/assert.js`, 'utf-8'); describe('custom-elements', function() { this.timeout(10000); @@ -53,7 +53,7 @@ describe('custom-elements', function() { await browser.close(); }); - fs.readdirSync('test/custom-elements/samples').forEach(dir => { + fs.readdirSync(`${__dirname}/samples`).forEach(dir => { if (dir[0] === '.') return; const solo = /\.solo$/.test(dir); @@ -67,7 +67,7 @@ describe('custom-elements', function() { const expected_warnings = config.warnings || []; const bundle = await rollup({ - input: `test/custom-elements/samples/${dir}/test.js`, + input: `${__dirname}/samples/${dir}/test.js`, plugins: [ { resolveId(importee) { diff --git a/test/hydration/index.js b/test/hydration/index.js index 856052d69e..fda799ef2d 100644 --- a/test/hydration/index.js +++ b/test/hydration/index.js @@ -47,7 +47,7 @@ describe('hydration', () => { } (config.skip ? it.skip : config.solo ? it.only : it)(dir, () => { - const cwd = path.resolve(`test/hydration/samples/${dir}`); + const cwd = path.resolve(`${__dirname}/samples/${dir}`); compileOptions = config.compileOptions || {}; @@ -96,7 +96,7 @@ describe('hydration', () => { }); } - fs.readdirSync('test/hydration/samples').forEach(dir => { + fs.readdirSync(`${__dirname}/samples`).forEach(dir => { runTest(dir, null); }); }); diff --git a/test/js/index.js b/test/js/index.js index 14d73d6c65..5fd632d606 100644 --- a/test/js/index.js +++ b/test/js/index.js @@ -4,7 +4,7 @@ import * as path from "path"; import { loadConfig, svelte } from "../helpers.js"; describe("js", () => { - fs.readdirSync("test/js/samples").forEach(dir => { + fs.readdirSync(`${__dirname}/samples`).forEach(dir => { if (dir[0] === ".") return; // add .solo to a sample directory name to only run that test @@ -15,7 +15,7 @@ describe("js", () => { } (solo ? it.only : it)(dir, () => { - dir = path.resolve("test/js/samples", dir); + dir = path.resolve(`${__dirname}/samples`, dir); const config = loadConfig(`${dir}/_config.js`); const input = fs.readFileSync(`${dir}/input.svelte`, "utf-8").replace(/\s+$/, ""); diff --git a/test/parser/index.js b/test/parser/index.js index 990a8751ef..0188fac431 100644 --- a/test/parser/index.js +++ b/test/parser/index.js @@ -3,7 +3,7 @@ import * as fs from 'fs'; import { svelte, tryToLoadJson } from '../helpers.js'; describe('parse', () => { - fs.readdirSync('test/parser/samples').forEach(dir => { + fs.readdirSync(`${__dirname}/samples`).forEach(dir => { if (dir[0] === '.') return; // add .solo to a sample directory name to only run that test @@ -16,18 +16,18 @@ describe('parse', () => { } (solo ? it.only : it)(dir, () => { - const options = tryToLoadJson(`test/parser/samples/${dir}/options.json`) || {}; + const options = tryToLoadJson(`${__dirname}/samples/${dir}/options.json`) || {}; - const input = fs.readFileSync(`test/parser/samples/${dir}/input.svelte`, 'utf-8').replace(/\s+$/, ''); - const expectedOutput = tryToLoadJson(`test/parser/samples/${dir}/output.json`); - const expectedError = tryToLoadJson(`test/parser/samples/${dir}/error.json`); + const input = fs.readFileSync(`${__dirname}/samples/${dir}/input.svelte`, 'utf-8').replace(/\s+$/, ''); + const expectedOutput = tryToLoadJson(`${__dirname}/samples/${dir}/output.json`); + const expectedError = tryToLoadJson(`${__dirname}/samples/${dir}/error.json`); try { const { ast } = svelte.compile(input, Object.assign(options, { generate: false })); - fs.writeFileSync(`test/parser/samples/${dir}/_actual.json`, JSON.stringify(ast, null, '\t')); + fs.writeFileSync(`${__dirname}/samples/${dir}/_actual.json`, JSON.stringify(ast, null, '\t')); assert.deepEqual(ast.html, expectedOutput.html); assert.deepEqual(ast.css, expectedOutput.css); diff --git a/test/preprocess/index.js b/test/preprocess/index.js index 8d114ab7b3..ea24645295 100644 --- a/test/preprocess/index.js +++ b/test/preprocess/index.js @@ -3,21 +3,21 @@ import * as assert from 'assert'; import { loadConfig, svelte } from '../helpers.js'; describe('preprocess', () => { - fs.readdirSync('test/preprocess/samples').forEach(dir => { + fs.readdirSync(`${__dirname}/samples`).forEach(dir => { if (dir[0] === '.') return; - const config = loadConfig(`./preprocess/samples/${dir}/_config.js`); + const config = loadConfig(`${__dirname}/samples/${dir}/_config.js`); if (config.solo && process.env.CI) { throw new Error('Forgot to remove `solo: true` from test'); } (config.skip ? it.skip : config.solo ? it.only : it)(dir, async () => { - const input = fs.readFileSync(`test/preprocess/samples/${dir}/input.svelte`, 'utf-8'); - const expected = fs.readFileSync(`test/preprocess/samples/${dir}/output.svelte`, 'utf-8'); + const input = fs.readFileSync(`${__dirname}/samples/${dir}/input.svelte`, 'utf-8'); + const expected = fs.readFileSync(`${__dirname}/samples/${dir}/output.svelte`, 'utf-8'); const result = await svelte.preprocess(input, config.preprocess); - fs.writeFileSync(`test/preprocess/samples/${dir}/_actual.html`, result.code); + fs.writeFileSync(`${__dirname}/samples/${dir}/_actual.html`, result.code); assert.equal(result.code, expected); diff --git a/test/runtime/index.js b/test/runtime/index.js index 1480e058a3..397cfef172 100644 --- a/test/runtime/index.js +++ b/test/runtime/index.js @@ -49,7 +49,7 @@ describe("runtime", () => { function runTest(dir, hydrate) { if (dir[0] === ".") return; - const config = loadConfig(`./runtime/samples/${dir}/_config.js`); + const config = loadConfig(`${__dirname}/samples/${dir}/_config.js`); if (hydrate && config.skip_if_hydrate) return; @@ -67,7 +67,7 @@ describe("runtime", () => { compile = (config.preserveIdentifiers ? svelte : svelte$).compile; - const cwd = path.resolve(`test/runtime/samples/${dir}`); + const cwd = path.resolve(`${__dirname}/samples/${dir}`); compileOptions = config.compileOptions || {}; compileOptions.format = 'cjs'; @@ -215,7 +215,7 @@ describe("runtime", () => { }); } - fs.readdirSync("test/runtime/samples").forEach(dir => { + fs.readdirSync(`${__dirname}/samples`).forEach(dir => { runTest(dir, false); runTest(dir, true); }); diff --git a/test/server-side-rendering/index.js b/test/server-side-rendering/index.js index 1097486124..0a4b6f7496 100644 --- a/test/server-side-rendering/index.js +++ b/test/server-side-rendering/index.js @@ -30,7 +30,7 @@ describe("ssr", () => { return setupHtmlEqual(); }); - fs.readdirSync("test/server-side-rendering/samples").forEach(dir => { + fs.readdirSync(`${__dirname}/samples`).forEach(dir => { if (dir[0] === ".") return; // add .solo to a sample directory name to only run that test, or @@ -43,7 +43,7 @@ describe("ssr", () => { } (solo ? it.only : it)(dir, () => { - dir = path.resolve("test/server-side-rendering/samples", dir); + dir = path.resolve(`${__dirname}/samples`, dir); try { const Component = require(`${dir}/main.svelte`).default; diff --git a/test/sourcemaps/index.js b/test/sourcemaps/index.js index e5915780c6..0b0424a764 100644 --- a/test/sourcemaps/index.js +++ b/test/sourcemaps/index.js @@ -6,7 +6,7 @@ import { SourceMapConsumer } from "source-map"; import { getLocator } from "locate-character"; describe("sourcemaps", () => { - fs.readdirSync("test/sourcemaps/samples").forEach(dir => { + fs.readdirSync(`${__dirname}/samples`).forEach(dir => { if (dir[0] === ".") return; // add .solo to a sample directory name to only run that test @@ -19,10 +19,10 @@ describe("sourcemaps", () => { (solo ? it.only : skip ? it.skip : it)(dir, async () => { const filename = path.resolve( - `test/sourcemaps/samples/${dir}/input.svelte` + `${__dirname}/samples/${dir}/input.svelte` ); const outputFilename = path.resolve( - `test/sourcemaps/samples/${dir}/output` + `${__dirname}/samples/${dir}/output` ); const input = fs.readFileSync(filename, "utf-8").replace(/\s+$/, ""); diff --git a/test/stats/index.js b/test/stats/index.js index aafc58c926..acea7a4663 100644 --- a/test/stats/index.js +++ b/test/stats/index.js @@ -3,7 +3,7 @@ import * as assert from 'assert'; import { svelte, loadConfig, tryToLoadJson } from '../helpers.js'; describe('stats', () => { - fs.readdirSync('test/stats/samples').forEach(dir => { + fs.readdirSync(`${__dirname}/samples`).forEach(dir => { if (dir[0] === '.') return; // add .solo to a sample directory name to only run that test @@ -15,12 +15,12 @@ describe('stats', () => { } (solo ? it.only : skip ? it.skip : it)(dir, () => { - const config = loadConfig(`./stats/samples/${dir}/_config.js`); - const filename = `test/stats/samples/${dir}/input.svelte`; + const config = loadConfig(`${__dirname}/samples/${dir}/_config.js`); + const filename = `${__dirname}/samples/${dir}/input.svelte`; const input = fs.readFileSync(filename, 'utf-8').replace(/\s+$/, ''); const expectedError = tryToLoadJson( - `test/stats/samples/${dir}/error.json` + `${__dirname}/samples/${dir}/error.json` ); let result; diff --git a/test/test.js b/test/test.js index 9480ae7836..df6585f7da 100644 --- a/test/test.js +++ b/test/test.js @@ -8,6 +8,17 @@ require('../internal'); console.clear(); -glob('*/index.js', { cwd: 'test' }).forEach((file) => { - require('./' + file); -}); +const testFolders = glob('*/index.js', { cwd: 'test' }); +const solo = testFolders.find(folder => /\.solo/.test(folder)); + +if (solo) { + if (process.env.CI) { + throw new Error('Forgot to remove `solo: true` from test'); + } + require('./' + solo); +} else { + testFolders.forEach(file => { + console.log('file', file); + require('./' + file); + }); +} diff --git a/test/validator/index.js b/test/validator/index.js index 1e54cc20db..9f991df4f2 100644 --- a/test/validator/index.js +++ b/test/validator/index.js @@ -3,7 +3,7 @@ import * as assert from "assert"; import { svelte, loadConfig, tryToLoadJson } from "../helpers.js"; describe("validate", () => { - fs.readdirSync("test/validator/samples").forEach(dir => { + fs.readdirSync(`${__dirname}/samples`).forEach(dir => { if (dir[0] === ".") return; // add .solo to a sample directory name to only run that test @@ -15,11 +15,11 @@ describe("validate", () => { } (solo ? it.only : skip ? it.skip : it)(dir, () => { - const config = loadConfig(`./validator/samples/${dir}/_config.js`); + const config = loadConfig(`${__dirname}/samples/${dir}/_config.js`); - const input = fs.readFileSync(`test/validator/samples/${dir}/input.svelte`, "utf-8").replace(/\s+$/, ""); - const expected_warnings = tryToLoadJson(`test/validator/samples/${dir}/warnings.json`) || []; - const expected_errors = tryToLoadJson(`test/validator/samples/${dir}/errors.json`); + const input = fs.readFileSync(`${__dirname}/samples/${dir}/input.svelte`, "utf-8").replace(/\s+$/, ""); + const expected_warnings = tryToLoadJson(`${__dirname}/samples/${dir}/warnings.json`) || []; + const expected_errors = tryToLoadJson(`${__dirname}/samples/${dir}/errors.json`); let error; diff --git a/test/vars/index.js b/test/vars/index.js index 66ffd94c70..a12ac177f2 100644 --- a/test/vars/index.js +++ b/test/vars/index.js @@ -3,7 +3,7 @@ import * as assert from 'assert'; import { svelte, loadConfig, tryToLoadJson } from '../helpers.js'; describe('vars', () => { - fs.readdirSync('test/vars/samples').forEach(dir => { + fs.readdirSync(`${__dirname}/samples`).forEach(dir => { if (dir[0] === '.') return; // add .solo to a sample directory name to only run that test @@ -16,12 +16,12 @@ describe('vars', () => { for (const generate of ['dom', 'ssr', false]) { (solo ? it.only : skip ? it.skip : it)(`${dir}, generate: ${generate}`, () => { - const config = loadConfig(`./vars/samples/${dir}/_config.js`); - const filename = `test/vars/samples/${dir}/input.svelte`; + const config = loadConfig(`${__dirname}/samples/${dir}/_config.js`); + const filename = `${__dirname}/samples/${dir}/input.svelte`; const input = fs.readFileSync(filename, 'utf-8').replace(/\s+$/, ''); const expectedError = tryToLoadJson( - `test/vars/samples/${dir}/error.json` + `${__dirname}/samples/${dir}/error.json` ); let result; From 6d2d025d3b0b6fcdbca51a4e9131a244cba3a65e Mon Sep 17 00:00:00 2001 From: Conduitry Date: Wed, 23 Oct 2019 17:04:21 -0400 Subject: [PATCH 017/395] tidy up core test.js script --- test/test.js | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/test/test.js b/test/test.js index df6585f7da..6ea4bd60ab 100644 --- a/test/test.js +++ b/test/test.js @@ -8,17 +8,14 @@ require('../internal'); console.clear(); -const testFolders = glob('*/index.js', { cwd: 'test' }); -const solo = testFolders.find(folder => /\.solo/.test(folder)); +const test_folders = glob('*/index.js', { cwd: 'test' }); +const solo_folders = test_folders.filter(folder => /\.solo/.test(folder)); -if (solo) { +if (solo_folders.length) { if (process.env.CI) { - throw new Error('Forgot to remove `solo: true` from test'); + throw new Error('Forgot to remove `.solo` from test'); } - require('./' + solo); + solo_folders.forEach(name => require('./' + name)); } else { - testFolders.forEach(file => { - console.log('file', file); - require('./' + file); - }); + test_folders.forEach(name => require('./' + name)); } From 1f6e0eb316adb20804480b407f3cdf0986580245 Mon Sep 17 00:00:00 2001 From: mrkishi Date: Tue, 22 Oct 2019 23:00:57 -0300 Subject: [PATCH 018/395] improve derived store typing for users --- src/runtime/store/index.ts | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/runtime/store/index.ts b/src/runtime/store/index.ts index 0aff706a1b..64a63d4179 100644 --- a/src/runtime/store/index.ts +++ b/src/runtime/store/index.ts @@ -122,16 +122,30 @@ type StoresValues = T extends Readable ? U : /** * Derived value store by synchronizing one or more readable stores and * applying an aggregation function over its input values. - * @param {Stores} stores input stores - * @param {function(Stores=, function(*)=):*}fn function callback that aggregates the values - * @param {*=}initial_value when used asynchronously + * + * @param stores - input stores + * @param fn - function callback that aggregates the values */ -export function derived( +export function derived( stores: S, - fn: (values: StoresValues, set: Subscriber) => T | Unsubscriber | void, - initial_value?: T, -): Readable { + fn: (values: StoresValues) => T +): Readable; +/** + * Derived value store by synchronizing one or more readable stores and + * applying an aggregation function over its input values. + * + * @param stores - input stores + * @param fn - function callback that aggregates the values + * @param initial_value - when used asynchronously + */ +export function derived( + stores: S, + fn: (values: StoresValues, set: (value: T) => void) => Unsubscriber | void, + initial_value?: T +): Readable; + +export function derived(stores: Stores, fn: Function, initial_value?: T): Readable { const single = !Array.isArray(stores); const stores_array: Array> = single ? [stores as Readable] @@ -141,7 +155,7 @@ export function derived( return readable(initial_value, (set) => { let inited = false; - const values: StoresValues = [] as StoresValues; + const values = []; let pending = 0; let cleanup = noop; From cfd3b63b9bb87395b9725e676372cc090d3c66d1 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Thu, 24 Oct 2019 11:54:50 +0800 Subject: [PATCH 019/395] warn if using svelte:options tag without compile_options.customElement --- src/compiler/compile/Component.ts | 7 +++++++ .../input.svelte | 1 + .../warnings.json | 17 +++++++++++++++++ 3 files changed, 25 insertions(+) create mode 100644 test/validator/samples/missing-custom-element-compile-options/input.svelte create mode 100644 test/validator/samples/missing-custom-element-compile-options/warnings.json diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index b22b351da0..e76696c53d 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -1307,6 +1307,13 @@ function process_component_options(component: Component, nodes) { }); } + if (tag && !component.compile_options.customElement) { + component.warn(attribute, { + code: 'missing-custom-element-compile-options', + message: `tag name is used when compiling the compenent as a custom element. Did you forgot to add "customElement" for compile options?` + }); + } + component_options.tag = tag; break; } diff --git a/test/validator/samples/missing-custom-element-compile-options/input.svelte b/test/validator/samples/missing-custom-element-compile-options/input.svelte new file mode 100644 index 0000000000..94ecce3ef6 --- /dev/null +++ b/test/validator/samples/missing-custom-element-compile-options/input.svelte @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/validator/samples/missing-custom-element-compile-options/warnings.json b/test/validator/samples/missing-custom-element-compile-options/warnings.json new file mode 100644 index 0000000000..a4c52792ab --- /dev/null +++ b/test/validator/samples/missing-custom-element-compile-options/warnings.json @@ -0,0 +1,17 @@ +[ + { + "code": "missing-custom-element-compile-options", + "end": { + "character": 36, + "column": 36, + "line": 1 + }, + "message": "tag name is used when compiling the compenent as a custom element. Did you forgot to add \"customElement\" for compile options?", + "pos": 16, + "start": { + "character": 16, + "column": 16, + "line": 1 + } + } +] From 8c0c15c3aa12ba73225e83954c7c59d0599913dd Mon Sep 17 00:00:00 2001 From: "Alessandro (Ale) Segala" <43508+ItalyPaleAle@users.noreply.github.com> Date: Wed, 23 Oct 2019 22:30:54 -0700 Subject: [PATCH 020/395] Fixed anchor link in documentation --- site/content/docs/03-run-time.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/docs/03-run-time.md b/site/content/docs/03-run-time.md index fd709dd9d5..19a19c4f41 100644 --- a/site/content/docs/03-run-time.md +++ b/site/content/docs/03-run-time.md @@ -184,7 +184,7 @@ dispatch: ((name: string, detail?: any) => void) = createEventDispatcher(); --- -Creates an event dispatcher that can be used to dispatch [component events](docs#Component_events). Event dispatchers are functions that can take two arguments: `name` and `detail`. +Creates an event dispatcher that can be used to dispatch [component events](docs#on_component_event). Event dispatchers are functions that can take two arguments: `name` and `detail`. Component events created with `createEventDispatcher` create a [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent). These events do not [bubble](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture) and are not cancellable with `event.preventDefault()`. The `detail` argument corresponds to the [CustomEvent.detail](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail) property and can contain any type of data. From 0419039d26cd9556f94c79c1dc3e584e192f3f32 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Thu, 24 Oct 2019 08:18:33 -0400 Subject: [PATCH 021/395] Don't lose `class:` directive classes on an element with `{...spread}` attributes when updating (#3781) * include all class: directive updates on elements with spreads (#3421) * add test * update changelog --- CHANGELOG.md | 1 + src/compiler/compile/render_dom/wrappers/Element/index.ts | 5 ++++- test/runtime/samples/spread-element-class/_config.js | 7 +++++++ test/runtime/samples/spread-element-class/main.svelte | 5 +++++ 4 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 test/runtime/samples/spread-element-class/_config.js create mode 100644 test/runtime/samples/spread-element-class/main.svelte diff --git a/CHANGELOG.md b/CHANGELOG.md index 31fc1eddd1..a3ce06485e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Fix `{#each}` context not shadowing outer scope when using `bind:` ([#1565](https://github.com/sveltejs/svelte/issues/1565)) * Fix edge cases in matching selectors against elements ([#1710](https://github.com/sveltejs/svelte/issues/1710)) * Allow exiting a reactive block early with `break $` ([#2828](https://github.com/sveltejs/svelte/issues/2828)) +* Don't lose `class:` directive classes on an element with `{...spread}` attributes when updating ([#3421](https://github.com/sveltejs/svelte/issues/3421)) * Check attributes have changed before setting them to avoid image flicker ([#3579](https://github.com/sveltejs/svelte/pull/3579)) * Fix generating malformed code for `{@debug}` tags with no dependencies ([#3588](https://github.com/sveltejs/svelte/issue/3588)) * Fix generated code in specific case involving compound ifs and child components ([#3595](https://github.com/sveltejs/svelte/issue/3595)) diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts index bb4c2d310a..8f54ebf468 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts @@ -809,6 +809,7 @@ export default class ElementWrapper extends Wrapper { } add_classes(block: Block) { + const has_spread = this.node.attributes.some(attr => attr.is_spread); this.node.classes.forEach(class_directive => { const { expression, name } = class_directive; let snippet; @@ -824,7 +825,9 @@ export default class ElementWrapper extends Wrapper { block.chunks.hydrate.push(updater); - if ((dependencies && dependencies.size > 0) || this.class_dependencies.length) { + if (has_spread) { + block.chunks.update.push(updater); + } else if ((dependencies && dependencies.size > 0) || this.class_dependencies.length) { const all_dependencies = this.class_dependencies.concat(...dependencies); const condition = changed(all_dependencies); diff --git a/test/runtime/samples/spread-element-class/_config.js b/test/runtime/samples/spread-element-class/_config.js new file mode 100644 index 0000000000..7fc3d49012 --- /dev/null +++ b/test/runtime/samples/spread-element-class/_config.js @@ -0,0 +1,7 @@ +export default { + html: `
hello
`, + test({ assert, component, target }) { + component.blah = 'goodbye'; + assert.htmlEqual(target.innerHTML, `
goodbye
`); + } +}; diff --git a/test/runtime/samples/spread-element-class/main.svelte b/test/runtime/samples/spread-element-class/main.svelte new file mode 100644 index 0000000000..a678659013 --- /dev/null +++ b/test/runtime/samples/spread-element-class/main.svelte @@ -0,0 +1,5 @@ + + +
{blah}
From af0557a2d41e468e98f179607693eba28c652e2e Mon Sep 17 00:00:00 2001 From: Mike Lewis Date: Thu, 24 Oct 2019 13:45:03 +0100 Subject: [PATCH 022/395] add regression test for missing class on elem with bind and spread (#3668) relates to #2707 --- .../class-with-spread-and-bind/_config.js | 18 ++++++++++++++++++ .../class-with-spread-and-bind/main.svelte | 11 +++++++++++ 2 files changed, 29 insertions(+) create mode 100644 test/runtime/samples/class-with-spread-and-bind/_config.js create mode 100644 test/runtime/samples/class-with-spread-and-bind/main.svelte diff --git a/test/runtime/samples/class-with-spread-and-bind/_config.js b/test/runtime/samples/class-with-spread-and-bind/_config.js new file mode 100644 index 0000000000..f3c54e8c52 --- /dev/null +++ b/test/runtime/samples/class-with-spread-and-bind/_config.js @@ -0,0 +1,18 @@ +export default { + props: { + primary: true, + }, + + html: `
`, + + test({ assert, component, target, window }) { + component.primary = true; + + assert.htmlEqual( + target.innerHTML, + ` +
+ ` + ); + }, +}; diff --git a/test/runtime/samples/class-with-spread-and-bind/main.svelte b/test/runtime/samples/class-with-spread-and-bind/main.svelte new file mode 100644 index 0000000000..c7c24a2e7b --- /dev/null +++ b/test/runtime/samples/class-with-spread-and-bind/main.svelte @@ -0,0 +1,11 @@ + + +
From b605f3867e9f98cde9db71e43df6c9dcc7017c66 Mon Sep 17 00:00:00 2001 From: mustafa0x Date: Thu, 24 Oct 2019 18:34:23 +0300 Subject: [PATCH 023/395] reduce confusion mentioned in #2452 --- site/src/routes/tutorial/[slug]/index.svelte | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/site/src/routes/tutorial/[slug]/index.svelte b/site/src/routes/tutorial/[slug]/index.svelte index ec23906057..ff6451d129 100644 --- a/site/src/routes/tutorial/[slug]/index.svelte +++ b/site/src/routes/tutorial/[slug]/index.svelte @@ -218,18 +218,20 @@ } .show { - background: rgba(0,0,0,.4); + background: var(--prime); padding: .3em .7em; border-radius: var(--border-r); top: .1em; position: relative; font-size: var(--h5); font-weight: 300; + box-shadow: 0 0 20px #444; + color: #fff; + transition: box-shadow .2s; } .show:hover { - background: rgba(0,0,0,.65); - color: white; + box-shadow: 0 0 30px 2px #333; } a.next { From b6798e522171f94cded4f06103f03321809be2b7 Mon Sep 17 00:00:00 2001 From: Jesse Skinner Date: Thu, 24 Oct 2019 13:34:58 -0400 Subject: [PATCH 024/395] allow multiple ancestors to be scoped with class (#3544) --- src/compiler/compile/css/Selector.ts | 104 +++++++++++------- .../expected.css | 1 + .../expected.html | 4 + .../input.svelte | 16 +++ .../expected.css | 1 + .../expected.html | 5 + .../input.svelte | 16 +++ .../expected.css | 1 + .../expected.html | 5 + .../input.svelte | 16 +++ 10 files changed, 131 insertions(+), 38 deletions(-) create mode 100644 test/css/samples/omit-scoping-attribute-global-children/expected.css create mode 100644 test/css/samples/omit-scoping-attribute-global-children/expected.html create mode 100644 test/css/samples/omit-scoping-attribute-global-children/input.svelte create mode 100644 test/css/samples/omit-scoping-attribute-global-descendants/expected.css create mode 100644 test/css/samples/omit-scoping-attribute-global-descendants/expected.html create mode 100644 test/css/samples/omit-scoping-attribute-global-descendants/input.svelte create mode 100644 test/css/samples/omit-scoping-attribute-multiple-descendants/expected.css create mode 100644 test/css/samples/omit-scoping-attribute-multiple-descendants/expected.html create mode 100644 test/css/samples/omit-scoping-attribute-multiple-descendants/input.svelte diff --git a/src/compiler/compile/css/Selector.ts b/src/compiler/compile/css/Selector.ts index 95eeaaf8cd..ab19ebd1e1 100644 --- a/src/compiler/compile/css/Selector.ts +++ b/src/compiler/compile/css/Selector.ts @@ -4,6 +4,12 @@ import { gather_possible_values, UNKNOWN } from './gather_possible_values'; import { CssNode } from './interfaces'; import Component from '../Component'; +enum BlockAppliesToNode { + NotPossible, + Possible, + UnknownSelectorType +} + export default class Selector { node: CssNode; stylesheet: Stylesheet; @@ -31,10 +37,10 @@ export default class Selector { apply(node: CssNode, stack: CssNode[]) { const to_encapsulate: CssNode[] = []; - apply_selector(this.stylesheet, this.local_blocks.slice(), node, stack.slice(), to_encapsulate); + apply_selector(this.local_blocks.slice(), node, stack.slice(), to_encapsulate); if (to_encapsulate.length > 0) { - to_encapsulate.filter((_, i) => i === 0 || i === to_encapsulate.length - 1).forEach(({ node, block }) => { + to_encapsulate.forEach(({ node, block }) => { this.stylesheet.nodes_with_css_class.add(node); block.should_encapsulate = true; }); @@ -126,7 +132,7 @@ export default class Selector { } } -function apply_selector(stylesheet: Stylesheet, blocks: Block[], node: CssNode, stack: CssNode[], to_encapsulate: any[]): boolean { +function apply_selector(blocks: Block[], node: CssNode, stack: CssNode[], to_encapsulate: any[]): boolean { const block = blocks.pop(); if (!block) return false; @@ -134,49 +140,30 @@ function apply_selector(stylesheet: Stylesheet, blocks: Block[], node: CssNode, return blocks.every(block => block.global); } - let i = block.selectors.length; - - while (i--) { - const selector = block.selectors[i]; - const name = typeof selector.name === 'string' && selector.name.replace(/\\(.)/g, '$1'); - - if (selector.type === 'PseudoClassSelector' && name === 'global') { - // TODO shouldn't see this here... maybe we should enforce that :global(...) - // cannot be sandwiched between non-global selectors? + switch (block_might_apply_to_node(block, node)) { + case BlockAppliesToNode.NotPossible: return false; - } - - if (selector.type === 'PseudoClassSelector' || selector.type === 'PseudoElementSelector') { - continue; - } - - if (selector.type === 'ClassSelector') { - if (!attribute_matches(node, 'class', name, '~=', false) && !node.classes.some(c => c.name === name)) return false; - } - - else if (selector.type === 'IdSelector') { - if (!attribute_matches(node, 'id', name, '=', false)) return false; - } - else if (selector.type === 'AttributeSelector') { - if (!attribute_matches(node, selector.name.name, selector.value && unquote(selector.value), selector.matcher, selector.flags)) return false; - } - - else if (selector.type === 'TypeSelector') { - if (node.name.toLowerCase() !== name.toLowerCase() && name !== '*') return false; - } - - else { + case BlockAppliesToNode.UnknownSelectorType: // bail. TODO figure out what these could be to_encapsulate.push({ node, block }); return true; - } } if (block.combinator) { if (block.combinator.type === 'WhiteSpace') { - while (stack.length) { - if (apply_selector(stylesheet, blocks.slice(), stack.pop(), stack, to_encapsulate)) { + for (const ancestor_block of blocks) { + if (ancestor_block.global) { + 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 }); + } + } + + if (to_encapsulate.length) { to_encapsulate.push({ node, block }); return true; } @@ -189,7 +176,7 @@ function apply_selector(stylesheet: Stylesheet, blocks: Block[], node: CssNode, return false; } else if (block.combinator.name === '>') { - if (apply_selector(stylesheet, blocks, stack.pop(), stack, to_encapsulate)) { + if (apply_selector(blocks, stack.pop(), stack, to_encapsulate)) { to_encapsulate.push({ node, block }); return true; } @@ -206,6 +193,47 @@ function apply_selector(stylesheet: Stylesheet, blocks: Block[], node: CssNode, return true; } +function block_might_apply_to_node(block, node): BlockAppliesToNode { + let i = block.selectors.length; + + while (i--) { + const selector = block.selectors[i]; + const name = typeof selector.name === 'string' && selector.name.replace(/\\(.)/g, '$1'); + + if (selector.type === 'PseudoClassSelector' || selector.type === 'PseudoElementSelector') { + continue; + } + + if (selector.type === 'PseudoClassSelector' && name === 'global') { + // TODO shouldn't see this here... maybe we should enforce that :global(...) + // cannot be sandwiched between non-global selectors? + return BlockAppliesToNode.NotPossible; + } + + if (selector.type === 'ClassSelector') { + if (!attribute_matches(node, 'class', name, '~=', false) && !node.classes.some(c => c.name === name)) return BlockAppliesToNode.NotPossible; + } + + else if (selector.type === 'IdSelector') { + if (!attribute_matches(node, 'id', name, '=', false)) return BlockAppliesToNode.NotPossible; + } + + else if (selector.type === 'AttributeSelector') { + if (!attribute_matches(node, selector.name.name, selector.value && unquote(selector.value), selector.matcher, selector.flags)) return BlockAppliesToNode.NotPossible; + } + + else if (selector.type === 'TypeSelector') { + if (node.name.toLowerCase() !== name.toLowerCase() && name !== '*') return BlockAppliesToNode.NotPossible; + } + + else { + return BlockAppliesToNode.UnknownSelectorType; + } + } + + return BlockAppliesToNode.Possible; +} + function test_attribute(operator, expected_value, case_insensitive, value) { if (case_insensitive) { expected_value = expected_value.toLowerCase(); diff --git a/test/css/samples/omit-scoping-attribute-global-children/expected.css b/test/css/samples/omit-scoping-attribute-global-children/expected.css new file mode 100644 index 0000000000..2a7a510449 --- /dev/null +++ b/test/css/samples/omit-scoping-attribute-global-children/expected.css @@ -0,0 +1 @@ +.root.svelte-xyz p{color:red} \ No newline at end of file diff --git a/test/css/samples/omit-scoping-attribute-global-children/expected.html b/test/css/samples/omit-scoping-attribute-global-children/expected.html new file mode 100644 index 0000000000..f7ee02fd83 --- /dev/null +++ b/test/css/samples/omit-scoping-attribute-global-children/expected.html @@ -0,0 +1,4 @@ +
+
+
+
\ No newline at end of file diff --git a/test/css/samples/omit-scoping-attribute-global-children/input.svelte b/test/css/samples/omit-scoping-attribute-global-children/input.svelte new file mode 100644 index 0000000000..5e52d48738 --- /dev/null +++ b/test/css/samples/omit-scoping-attribute-global-children/input.svelte @@ -0,0 +1,16 @@ + + + + +
+
+ +
+
diff --git a/test/css/samples/omit-scoping-attribute-global-descendants/expected.css b/test/css/samples/omit-scoping-attribute-global-descendants/expected.css new file mode 100644 index 0000000000..c1fd7da897 --- /dev/null +++ b/test/css/samples/omit-scoping-attribute-global-descendants/expected.css @@ -0,0 +1 @@ +html body .root.svelte-xyz p.svelte-xyz{color:red} \ No newline at end of file diff --git a/test/css/samples/omit-scoping-attribute-global-descendants/expected.html b/test/css/samples/omit-scoping-attribute-global-descendants/expected.html new file mode 100644 index 0000000000..3750091bc7 --- /dev/null +++ b/test/css/samples/omit-scoping-attribute-global-descendants/expected.html @@ -0,0 +1,5 @@ +
+
+

hello

+
+
\ No newline at end of file diff --git a/test/css/samples/omit-scoping-attribute-global-descendants/input.svelte b/test/css/samples/omit-scoping-attribute-global-descendants/input.svelte new file mode 100644 index 0000000000..7c9782ebad --- /dev/null +++ b/test/css/samples/omit-scoping-attribute-global-descendants/input.svelte @@ -0,0 +1,16 @@ + + + + +
+
+

hello

+
+
diff --git a/test/css/samples/omit-scoping-attribute-multiple-descendants/expected.css b/test/css/samples/omit-scoping-attribute-multiple-descendants/expected.css new file mode 100644 index 0000000000..5452f68073 --- /dev/null +++ b/test/css/samples/omit-scoping-attribute-multiple-descendants/expected.css @@ -0,0 +1 @@ +.root.svelte-xyz p.svelte-xyz{color:red} \ No newline at end of file diff --git a/test/css/samples/omit-scoping-attribute-multiple-descendants/expected.html b/test/css/samples/omit-scoping-attribute-multiple-descendants/expected.html new file mode 100644 index 0000000000..3750091bc7 --- /dev/null +++ b/test/css/samples/omit-scoping-attribute-multiple-descendants/expected.html @@ -0,0 +1,5 @@ +
+
+

hello

+
+
\ No newline at end of file diff --git a/test/css/samples/omit-scoping-attribute-multiple-descendants/input.svelte b/test/css/samples/omit-scoping-attribute-multiple-descendants/input.svelte new file mode 100644 index 0000000000..dc77a6c794 --- /dev/null +++ b/test/css/samples/omit-scoping-attribute-multiple-descendants/input.svelte @@ -0,0 +1,16 @@ + + + + +
+
+

hello

+
+
From 07b9d0378ec2a73f6e99fe8e4c2e8ce35bac69cb Mon Sep 17 00:00:00 2001 From: Conduitry Date: Thu, 24 Oct 2019 13:36:28 -0400 Subject: [PATCH 025/395] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3ce06485e..69bd58c47b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * Fix edge cases in matching selectors against elements ([#1710](https://github.com/sveltejs/svelte/issues/1710)) * Allow exiting a reactive block early with `break $` ([#2828](https://github.com/sveltejs/svelte/issues/2828)) * Don't lose `class:` directive classes on an element with `{...spread}` attributes when updating ([#3421](https://github.com/sveltejs/svelte/issues/3421)) +* Fix application of style scoping class in cases of ambiguity ([#3544](https://github.com/sveltejs/svelte/issues/3544)) * Check attributes have changed before setting them to avoid image flicker ([#3579](https://github.com/sveltejs/svelte/pull/3579)) * Fix generating malformed code for `{@debug}` tags with no dependencies ([#3588](https://github.com/sveltejs/svelte/issue/3588)) * Fix generated code in specific case involving compound ifs and child components ([#3595](https://github.com/sveltejs/svelte/issue/3595)) From aa3dab93db17c38c4efef31b65e68260ef62b971 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Thu, 24 Oct 2019 13:55:53 -0400 Subject: [PATCH 026/395] fix running tests on github actions --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e035c86b2..2488902b24 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,8 @@ jobs: - uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - - run: 'npm i && npm test' + - run: npm install + - run: npm test env: CI: true Lint: From d4fd91cbb96206404b7f6260d0a8d572dd3a530d Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 24 Oct 2019 15:06:01 -0400 Subject: [PATCH 027/395] tweak 'show me' styles --- site/src/routes/tutorial/[slug]/index.svelte | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/site/src/routes/tutorial/[slug]/index.svelte b/site/src/routes/tutorial/[slug]/index.svelte index ff6451d129..be59665970 100644 --- a/site/src/routes/tutorial/[slug]/index.svelte +++ b/site/src/routes/tutorial/[slug]/index.svelte @@ -225,13 +225,11 @@ position: relative; font-size: var(--h5); font-weight: 300; - box-shadow: 0 0 20px #444; - color: #fff; - transition: box-shadow .2s; + color: rgba(255,255,255,0.7); } .show:hover { - box-shadow: 0 0 30px 2px #333; + color: white; } a.next { From ad0e8670caed3830b2ccb95830cadb7adfebc69a Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Thu, 24 Oct 2019 21:47:05 -0400 Subject: [PATCH 028/395] tweak text of warning --- src/compiler/compile/Component.ts | 2 +- .../missing-custom-element-compile-options/warnings.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index e76696c53d..3bd7373f7f 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -1310,7 +1310,7 @@ function process_component_options(component: Component, nodes) { if (tag && !component.compile_options.customElement) { component.warn(attribute, { code: 'missing-custom-element-compile-options', - message: `tag name is used when compiling the compenent as a custom element. Did you forgot to add "customElement" for compile options?` + message: `The 'tag' option is used when generating a custom element. Did you forget the 'customElement: true' compile option?` }); } diff --git a/test/validator/samples/missing-custom-element-compile-options/warnings.json b/test/validator/samples/missing-custom-element-compile-options/warnings.json index a4c52792ab..8d6d574fb2 100644 --- a/test/validator/samples/missing-custom-element-compile-options/warnings.json +++ b/test/validator/samples/missing-custom-element-compile-options/warnings.json @@ -6,7 +6,7 @@ "column": 36, "line": 1 }, - "message": "tag name is used when compiling the compenent as a custom element. Did you forgot to add \"customElement\" for compile options?", + "message": "The 'tag' option is used when generating a custom element. Did you forget the 'customElement: true' compile option?", "pos": 16, "start": { "character": 16, From 9f83a2ad23047c8e5f2d9ba2efd95f9397d8c6b9 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 24 Oct 2019 22:12:21 -0400 Subject: [PATCH 029/395] address a few nits --- CONTRIBUTING.md | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d698d02cd2..f00ebc07b9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,19 +7,19 @@ The [Open Source Guides](https://opensource.guide/) website has a collection of * [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) * [Building Welcoming Communities](https://opensource.guide/building-community/) -## Get Involved +## Get involved There are many ways to contribute to Svelte, and many of them do not involve writing any code. Here's a few ideas to get started: - Simply start using Svelte. Go through the [Getting Started](https://svelte.dev/blog/the-easiest-way-to-get-started) guide. Does everything work as expected? If not, we're always looking for improvements. Let us know by [opening an issue](#reporting-new-issues). - Look through the [open issues](https://github.com/sveltejs/svelte/issues). Provide workarounds, ask for clarification, or suggest labels. Help [triage issues](#triaging-issues-and-pull-requests). - If you find an issue you would like to fix, [open a pull request](#your-first-pull-request). -- Read through our amazing [Tutorials](https://svelte.dev/tutorial/basics). If you find anything that is confusing or can be improved, you can make edits by clicking "Edit this chapter" at the bottom left of the tutorials. +- Read through our [tutorials](https://svelte.dev/tutorial/basics). If you find anything that is confusing or can be improved, you can make edits by clicking "Edit this chapter" at the bottom left of the tutorial page. - Take a look at the [features requested](https://github.com/sveltejs/svelte/labels/enhancement) by others in the community and consider opening a pull request if you see something you want to work on. -Contributions are very welcome. If you think you need help planning your contribution, please ping us on Twitter at [@sveltejs](https://twitter.com/sveltejs) and let us know you are looking for a bit of help. +Contributions are very welcome. If you think you need help planning your contribution, please ping us on Discord at [svelte.dev/chat](https://svelte.dev/chat) and let us know you are looking for a bit of help. -### Triaging Issues and Pull Requests +### Triaging issues and pull requests One great way you can contribute to the project without writing any code is to help triage issues and pull requests as they come in. @@ -30,18 +30,18 @@ One great way you can contribute to the project without writing any code is to h ## Bugs -We use [GitHub Issues](https://github.com/sveltejs/svelte/issues) for our public bugs. If you would like to report a problem, take a look around and see if someone already opened an issue about it. If you a are certain this is a new, unreported bug, you can submit a [bug report](#reporting-new-issues). +We use [GitHub issues](https://github.com/sveltejs/svelte/issues) for our public bugs. If you would like to report a problem, take a look around and see if someone already opened an issue about it. If you a are certain this is a new, unreported bug, you can submit a [bug report](#reporting-new-issues). -If you have questions about using Svelte, contact the Svelte Twitter account at [@sveltejs](https://twitter.com/sveltejs), and we will do our best to answer your questions. +If you have questions about using Svelte, contact us on Discord at [svelte.dev/chat](https://svelte.dev/chat), and we will do our best to answer your questions. If you see anything you'd like to be implemented, create a [feature request issue](https://github.com/sveltejs/svelte/issues/new?template=feature_request.md) -## Reporting New Issues +## Reporting new issues -When [opening a new issue](https://github.com/sveltejs/svelte/issues/new/choose), always make sure to fill out the issue template. **This step is very important!** Not doing so may result in your issue not managed in a timely fashion. Don't take this personally if this happens, and feel free to open a new issue once you've gathered all the information required by the template. +When [opening a new issue](https://github.com/sveltejs/svelte/issues/new/choose), always make sure to fill out the issue template. **This step is very important!** Not doing so may result in your issue not being managed in a timely fashion. Don't take this personally if this happens, and feel free to open a new issue once you've gathered all the information required by the template. - **One issue, one bug:** Please report a single bug per issue. -- **Provide reproduction steps:** List all the steps necessary to reproduce the issue. The person reading your bug report should be able to follow these steps to reproduce your issue with minimal effort. +- **Provide reproduction steps:** List all the steps necessary to reproduce the issue. The person reading your bug report should be able to follow these steps to reproduce your issue with minimal effort. If possible, use the [REPL](https://svelte.dev/repl) to create your reproduction. ## Installation @@ -50,9 +50,9 @@ When [opening a new issue](https://github.com/sveltejs/svelte/issues/new/choose) 1. To start a development server, run `npm run dev`. -## Pull Requests +## Pull requests -### Your First Pull Request +### Your first pull request So you have decided to contribute code back to upstream by opening a pull request. You've invested a good chunk of time, and we appreciate it. We will do our best to work with you and get the PR looked at. @@ -60,13 +60,13 @@ Working on your first Pull Request? You can learn how from this free video serie [**How to Contribute to an Open Source Project on GitHub**](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github) -### Proposing a Change +### Proposing a change If you would like to request a new feature or enhancement but are not yet thinking about opening a pull request, you can also file an issue with [feature template](https://github.com/sveltejs/svelte/issues/new?template=feature_request.md). If you're only fixing a bug, it's fine to submit a pull request right away but we still recommend to file an issue detailing what you're fixing. This is helpful in case we don't accept that specific fix but want to keep track of the issue. -### Sending a Pull Request +### Sending a pull request Small pull requests are much easier to review and more likely to get merged. Make sure the PR does only one thing, otherwise please split it. @@ -79,25 +79,24 @@ Please make sure the following is done when submitting a pull request: All pull requests should be opened against the `master` branch. -#### Test Plan +#### Test plan A good test plan has the exact commands you ran and their output, provides screenshots or videos if the pull request changes UI. - If you've changed APIs, update the documentation. -#### Writing Tests +#### Writing tests All tests are located in `/test` folder. -Tests samples are kept in `/test/xxx/samples` folder. +Test samples are kept in `/test/xxx/samples` folder. -#### Running Tests +#### Running tests 1. To run test, run `npm run test` -1. To run test for a specific feature, you can use the `-g` (aka `--grep`) option. For example, to only run test invloving transitions, run `npm run test -- -g transition`. -1. To run only 1 test sample, append the `.solo` to the test sample folder name, for example, `test/runtime/samples/action.solo`. +1. To run test for a specific feature, you can use the `-g` (aka `--grep`) option. For example, to only run test involving transitions, run `npm run test -- -g transition`. -#### Breaking Changes +#### Breaking changes When adding a new breaking change, follow this template in your pull request: @@ -110,19 +109,19 @@ When adding a new breaking change, follow this template in your pull request: - **Severity (number of people affected x effort)**: ``` -### What Happens Next? +### What happens next? The core Svelte team will be monitoring for pull requests. Do help us by making your pull request easy to review by following the guidelines above. -## Style Guide +## Style guide [Eslint](https://eslint.org) will catch most styling issues that may exist in your code. You can check the status of your code styling by simply running `npm run lint`. -### Code Conventions +### Code conventions - `snake_case` for internal variable names and methods - `camelCase` for public variable names and methods. ## License -By contributing to Svelte, you agree that your contributions will be licensed under its [MIT license](https://github.com/sveltejs/svelte/blob/master/LICENSE). \ No newline at end of file +By contributing to Svelte, you agree that your contributions will be licensed under its [MIT license](https://github.com/sveltejs/svelte/blob/master/LICENSE). From 85692cbd5ac9f24f02c37f641feb54cdac54ca8c Mon Sep 17 00:00:00 2001 From: Conduitry Date: Fri, 25 Oct 2019 13:03:09 -0400 Subject: [PATCH 030/395] fix handling of style scoping and `class:` with spread scopes (#3792) --- CHANGELOG.md | 4 +-- src/compiler/compile/nodes/Element.ts | 6 ++++ .../render_dom/wrappers/Element/index.ts | 9 +++++ .../compile/render_ssr/handlers/Element.ts | 36 +++++++++---------- src/runtime/internal/ssr.ts | 9 ++++- .../samples/spread-element-class/main.svelte | 2 +- .../samples/spread-element-scope/_config.js | 7 ++++ .../samples/spread-element-scope/main.svelte | 10 ++++++ 8 files changed, 59 insertions(+), 24 deletions(-) create mode 100644 test/runtime/samples/spread-element-scope/_config.js create mode 100644 test/runtime/samples/spread-element-scope/main.svelte diff --git a/CHANGELOG.md b/CHANGELOG.md index 69bd58c47b..4f5ab9acf8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,8 @@ * Fix `{#each}` context not shadowing outer scope when using `bind:` ([#1565](https://github.com/sveltejs/svelte/issues/1565)) * Fix edge cases in matching selectors against elements ([#1710](https://github.com/sveltejs/svelte/issues/1710)) +* Fix several bugs related to interaction of `{...spread}` attributes with other features ([#2721](https://github.com/sveltejs/svelte/issues/2721), [#3421](https://github.com/sveltejs/svelte/issues/3421), [#3681](https://github.com/sveltejs/svelte/issues/3681), [#3764](https://github.com/sveltejs/svelte/issues/3764), [#3790](https://github.com/sveltejs/svelte/issues/3790)) * Allow exiting a reactive block early with `break $` ([#2828](https://github.com/sveltejs/svelte/issues/2828)) -* Don't lose `class:` directive classes on an element with `{...spread}` attributes when updating ([#3421](https://github.com/sveltejs/svelte/issues/3421)) * Fix application of style scoping class in cases of ambiguity ([#3544](https://github.com/sveltejs/svelte/issues/3544)) * Check attributes have changed before setting them to avoid image flicker ([#3579](https://github.com/sveltejs/svelte/pull/3579)) * Fix generating malformed code for `{@debug}` tags with no dependencies ([#3588](https://github.com/sveltejs/svelte/issue/3588)) @@ -19,8 +19,6 @@ * Flush changes in newly attached block when using `{#await}` ([#3660](https://github.com/sveltejs/svelte/issues/3660)) * Throw exception immediately when calling `createEventDispatcher()` after component instantiation ([#3667](https://github.com/sveltejs/svelte/pull/3667)) * Fix globals shadowing contextual template scope ([#3674](https://github.com/sveltejs/svelte/issues/3674)) -* Fix error resulting from trying to set a read-only property when spreading element attributes ([#3681](https://github.com/sveltejs/svelte/issues/3681)) -* Fix handling of boolean attributes in presence of other spread attributes ([#3764](https://github.com/sveltejs/svelte/issues/3764)) ## 3.12.1 diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index 2981741fa0..d353d20158 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -105,6 +105,7 @@ export default class Element extends Node { animation?: Animation = null; children: INode[]; namespace: string; + needs_manual_style_scoping: boolean; constructor(component, parent, scope, info: any) { super(component, parent, scope, info); @@ -712,6 +713,11 @@ export default class Element extends Node { } add_css_class() { + if (this.attributes.some(attr => attr.is_spread)) { + this.needs_manual_style_scoping = true; + return; + } + const { id } = this.component.stylesheet; const class_attribute = this.attributes.find(a => a.name === 'class'); diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts index 8f54ebf468..253ab107d8 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts @@ -344,6 +344,7 @@ export default class ElementWrapper extends Wrapper { this.add_animation(block); this.add_actions(block); this.add_classes(block); + this.add_manual_style_scoping(block); if (nodes && this.renderer.options.hydratable) { block.chunks.claim.push( @@ -838,6 +839,14 @@ export default class ElementWrapper extends Wrapper { } }); } + + add_manual_style_scoping(block) { + if (this.node.needs_manual_style_scoping) { + const updater = b`@toggle_class(${this.var}, "${this.node.component.stylesheet.id}", true);`; + block.chunks.hydrate.push(updater); + block.chunks.update.push(updater); + } + } } function to_html(wrappers: Array, block: Block, literal: any, state: any) { diff --git a/src/compiler/compile/render_ssr/handlers/Element.ts b/src/compiler/compile/render_ssr/handlers/Element.ts index 1f7c0b7b9f..65013a8d07 100644 --- a/src/compiler/compile/render_ssr/handlers/Element.ts +++ b/src/compiler/compile/render_ssr/handlers/Element.ts @@ -1,5 +1,4 @@ import { is_void } from '../../../utils/names'; -import Class from '../../nodes/Class'; import { get_attribute_value, get_class_attribute_value } from './shared/get_attribute_value'; import { get_slot_scope } from './shared/get_slot_scope'; import Renderer, { RenderOptions } from '../Renderer'; @@ -69,15 +68,17 @@ export default function(node: Element, renderer: Renderer, options: RenderOption renderer.add_string(`<${node.name}`); - const class_expression = node.classes.length > 0 && node.classes - .map((class_directive: Class) => { - const { expression, name } = class_directive; - const snippet = expression ? expression.node : x`#ctx.${name}`; - return x`${snippet} ? "${name}" : ""`; - }) - .reduce((lhs, rhs) => x`${lhs} + ' ' + ${rhs}`); - - let add_class_attribute = class_expression ? true : false; + const class_expression_list = node.classes.map(class_directive => { + const { expression, name } = class_directive; + const snippet = expression ? expression.node : x`#ctx.${name}`; + return x`${snippet} ? "${name}" : ""`; + }); + if (node.needs_manual_style_scoping) { + class_expression_list.push(x`"${node.component.stylesheet.id}"`); + } + const class_expression = + class_expression_list.length > 0 && + class_expression_list.reduce((lhs, rhs) => x`${lhs} + ' ' + ${rhs}`); if (node.attributes.some(attr => attr.is_spread)) { // TODO dry this out @@ -98,17 +99,15 @@ export default function(node: Element, renderer: Renderer, options: RenderOption ) { // a boolean attribute with one non-Text chunk args.push(x`{ ${attribute.name}: ${(attribute.chunks[0] as Expression).node} || null }`); - } else if (name === 'class' && class_expression) { - // Add class expression - args.push(x`{ ${attribute.name}: [${get_class_attribute_value(attribute)}, ${class_expression}].join(' ').trim() }`); } else { - args.push(x`{ ${attribute.name}: ${(name === 'class' ? get_class_attribute_value : get_attribute_value)(attribute)} }`); + args.push(x`{ ${attribute.name}: ${get_attribute_value(attribute)} }`); } } }); - renderer.add_expression(x`@spread([${args}])`); + renderer.add_expression(x`@spread([${args}], ${class_expression});`); } else { + let add_class_attribute = !!class_expression; node.attributes.forEach(attribute => { const name = attribute.name.toLowerCase(); if (name === 'value' && node.name.toLowerCase() === 'textarea') { @@ -137,6 +136,9 @@ export default function(node: Element, renderer: Renderer, options: RenderOption renderer.add_string(`"`); } }); + if (add_class_attribute) { + renderer.add_expression(x`@add_classes([${class_expression}].join(' ').trim())`); + } } node.bindings.forEach(binding => { @@ -162,10 +164,6 @@ export default function(node: Element, renderer: Renderer, options: RenderOption } }); - if (add_class_attribute) { - renderer.add_expression(x`@add_classes([${class_expression}].join(' ').trim())`); - } - renderer.add_string('>'); if (node_contents !== undefined) { diff --git a/src/runtime/internal/ssr.ts b/src/runtime/internal/ssr.ts index 208a4637c7..83e585a899 100644 --- a/src/runtime/internal/ssr.ts +++ b/src/runtime/internal/ssr.ts @@ -5,8 +5,15 @@ export const invalid_attribute_name_character = /[\s'">/=\u{FDD0}-\u{FDEF}\u{FFF // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 // https://infra.spec.whatwg.org/#noncharacter -export function spread(args) { +export function spread(args, classes_to_add) { const attributes = Object.assign({}, ...args); + if (classes_to_add) { + if (attributes.class == null) { + attributes.class = classes_to_add; + } else { + attributes.class += ' ' + classes_to_add; + } + } let str = ''; Object.keys(attributes).forEach(name => { diff --git a/test/runtime/samples/spread-element-class/main.svelte b/test/runtime/samples/spread-element-class/main.svelte index a678659013..026132287b 100644 --- a/test/runtime/samples/spread-element-class/main.svelte +++ b/test/runtime/samples/spread-element-class/main.svelte @@ -2,4 +2,4 @@ export let blah = 'hello'; -
{blah}
+
{blah}
diff --git a/test/runtime/samples/spread-element-scope/_config.js b/test/runtime/samples/spread-element-scope/_config.js new file mode 100644 index 0000000000..457524ca37 --- /dev/null +++ b/test/runtime/samples/spread-element-scope/_config.js @@ -0,0 +1,7 @@ +export default { + html: ` +
red
+
red
+
red and bold
+ ` +}; diff --git a/test/runtime/samples/spread-element-scope/main.svelte b/test/runtime/samples/spread-element-scope/main.svelte new file mode 100644 index 0000000000..7a25f90939 --- /dev/null +++ b/test/runtime/samples/spread-element-scope/main.svelte @@ -0,0 +1,10 @@ + + +
red
+ +
red
+ +
red and bold
From 4c239974c391d1c44adcdebaa0cd01bffd5602c4 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Fri, 25 Oct 2019 13:04:18 -0400 Subject: [PATCH 031/395] alpha.2 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1ca8b7cdb5..cc5bd88a6d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.13.0-alpha.1", + "version": "3.13.0-alpha.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 950c44d965..a061cdf742 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.13.0-alpha.1", + "version": "3.13.0-alpha.2", "description": "Cybernetically enhanced web apps", "module": "index.mjs", "main": "index", From 3f6b743dd01d952c9b6e79ccad1b5b9f46d06efa Mon Sep 17 00:00:00 2001 From: Conduitry Date: Sat, 26 Oct 2019 16:15:51 -0400 Subject: [PATCH 032/395] site: add REPL imports to .zip's pkg.devDependencies (#3795) --- .../routes/repl/[id]/_components/AppControls/index.svelte | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/site/src/routes/repl/[id]/_components/AppControls/index.svelte b/site/src/routes/repl/[id]/_components/AppControls/index.svelte index 4cfd5d43b2..007dba3461 100644 --- a/site/src/routes/repl/[id]/_components/AppControls/index.svelte +++ b/site/src/routes/repl/[id]/_components/AppControls/index.svelte @@ -140,12 +140,12 @@ if (imports.length > 0) { const idx = files.findIndex(({ path }) => path === 'package.json'); const pkg = JSON.parse(files[idx].data); - const deps = {}; + const { devDependencies } = pkg; imports.forEach(mod => { const match = /^(@[^/]+\/)?[^@/]+/.exec(mod); - deps[match[0]] = 'latest'; + devDependencies[match[0]] = 'latest'; }); - pkg.dependencies = deps; + pkg.devDependencies = devDependencies; files[idx].data = JSON.stringify(pkg, null, ' '); } From b8107e7fa73979cd4eeae3e2b20f42f250c97226 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Sat, 26 Oct 2019 17:05:10 -0400 Subject: [PATCH 033/395] update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f5ab9acf8..a3c4adac7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +* New structured code generation, which eliminates a number of edge cases and obscure bugs ([#3539](https://github.com/sveltejs/svelte/pull/3539)) + +Also: + * Fix `{#each}` context not shadowing outer scope when using `bind:` ([#1565](https://github.com/sveltejs/svelte/issues/1565)) * Fix edge cases in matching selectors against elements ([#1710](https://github.com/sveltejs/svelte/issues/1710)) * Fix several bugs related to interaction of `{...spread}` attributes with other features ([#2721](https://github.com/sveltejs/svelte/issues/2721), [#3421](https://github.com/sveltejs/svelte/issues/3421), [#3681](https://github.com/sveltejs/svelte/issues/3681), [#3764](https://github.com/sveltejs/svelte/issues/3764), [#3790](https://github.com/sveltejs/svelte/issues/3790)) From 1e55d46bc0c710730069509eaf04a39cacab1ce3 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Sat, 26 Oct 2019 23:53:13 +0100 Subject: [PATCH 034/395] Add Key handling to modal example This is a basic accessibility requirement for key access --- .../15-composition/04-modal/Modal.svelte | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/site/content/examples/15-composition/04-modal/Modal.svelte b/site/content/examples/15-composition/04-modal/Modal.svelte index 5ffa5989a4..8ddaf55921 100644 --- a/site/content/examples/15-composition/04-modal/Modal.svelte +++ b/site/content/examples/15-composition/04-modal/Modal.svelte @@ -2,7 +2,23 @@ import { createEventDispatcher } from 'svelte'; const dispatch = createEventDispatcher(); - + + const handleClose = () => dispatch('close') + + function handleKeydown(event) { + if (event.key == 'Escape') { + handleClose() + } else if (event.key == 'Tab') { + event.preventDefault() + } + } + + let closeButton + onMount(() => { + closeButton.focus() + }) + + - + From 81c5c480e896b079aafe704e99699b65973f9db2 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Sun, 27 Oct 2019 13:42:50 +0800 Subject: [PATCH 035/395] feat: warn unused exports --- src/compiler/compile/Component.ts | 253 ++++++++++++------ .../compile/utils/is_used_as_reference.ts | 33 +++ src/compiler/interfaces.ts | 3 +- .../unreferenced-variables/input.svelte | 21 ++ .../unreferenced-variables/warnings.json | 77 ++++++ .../vars/samples/$$props-logicless/_config.js | 1 + test/vars/samples/$$props/_config.js | 1 + test/vars/samples/actions/_config.js | 2 + test/vars/samples/animations/_config.js | 2 + .../samples/component-namespaced/_config.js | 1 + .../duplicate-non-hoistable/_config.js | 1 + test/vars/samples/duplicate-vars/_config.js | 2 + .../vars/samples/implicit-reactive/_config.js | 2 + test/vars/samples/imports/_config.js | 3 + .../mutated-vs-reassigned-bindings/_config.js | 2 + .../samples/mutated-vs-reassigned/_config.js | 2 + test/vars/samples/props/_config.js | 4 + .../samples/referenced-from-script/_config.js | 160 +++++++++++ .../referenced-from-script/input.svelte | 16 ++ test/vars/samples/store-referenced/_config.js | 2 + .../samples/store-unreferenced/_config.js | 2 + .../samples/template-references/_config.js | 3 + test/vars/samples/transitions/_config.js | 6 + 23 files changed, 511 insertions(+), 88 deletions(-) create mode 100644 src/compiler/compile/utils/is_used_as_reference.ts create mode 100644 test/validator/samples/unreferenced-variables/input.svelte create mode 100644 test/validator/samples/unreferenced-variables/warnings.json create mode 100644 test/vars/samples/referenced-from-script/_config.js create mode 100644 test/vars/samples/referenced-from-script/input.svelte diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index 3bd7373f7f..cc3bf2e308 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -18,6 +18,7 @@ import { Ast, CompileOptions, Var, Warning } from '../interfaces'; import error from '../utils/error'; import get_code_frame from '../utils/get_code_frame'; import flatten_reference from './utils/flatten_reference'; +import is_used_as_reference from './utils/is_used_as_reference'; import is_reference from 'is-reference'; import TemplateScope from './nodes/shared/TemplateScope'; import fuzzymatch from '../utils/fuzzymatch'; @@ -168,12 +169,13 @@ export default class Component { this.tag = this.name.name; } - this.walk_module_js(); + this.walk_module_js_pre_template(); this.walk_instance_js_pre_template(); this.fragment = new Fragment(this, ast.html); this.name = this.get_unique_name(name); + this.walk_module_js_post_template(); this.walk_instance_js_post_template(); if (!compile_options.customElement) this.stylesheet.reify(); @@ -346,6 +348,7 @@ export default class Component { reassigned: v.reassigned || false, referenced: v.referenced || false, writable: v.writable || false, + referenced_from_script: v.referenced_from_script || false, })), stats: this.stats.render(), }; @@ -447,63 +450,64 @@ export default class Component { }); } - extract_imports(content) { - for (let i = 0; i < content.body.length; i += 1) { - const node = content.body[i]; - - if (node.type === 'ImportDeclaration') { - content.body.splice(i--, 1); - this.imports.push(node); - } - } + extract_imports(node) { + this.imports.push(node); } - extract_exports(content) { - let i = content.body.length; - while (i--) { - const node = content.body[i]; + extract_exports(node) { + if (node.type === 'ExportDefaultDeclaration') { + this.error(node, { + code: `default-export`, + message: `A component cannot have a default export`, + }); + } - if (node.type === 'ExportDefaultDeclaration') { + if (node.type === 'ExportNamedDeclaration') { + if (node.source) { this.error(node, { - code: `default-export`, - message: `A component cannot have a default export`, + code: `not-implemented`, + message: `A component currently cannot have an export ... from`, }); } - - if (node.type === 'ExportNamedDeclaration') { - if (node.source) { - this.error(node, { - code: `not-implemented`, - message: `A component currently cannot have an export ... from`, + if (node.declaration) { + if (node.declaration.type === 'VariableDeclaration') { + node.declaration.declarations.forEach(declarator => { + extract_names(declarator.id).forEach(name => { + const variable = this.var_lookup.get(name); + variable.export_name = name; + if (variable.writable && !(variable.referenced || variable.referenced_from_script)) { + this.warn(declarator, { + code: `unused-export-let`, + message: `${this.name.name} has unused export property '${name}'. If it is for external reference only, please consider using \`export const '${name}'\`` + }); + } + }); }); + } else { + const { name } = node.declaration.id; + + const variable = this.var_lookup.get(name); + variable.export_name = name; } - if (node.declaration) { - if (node.declaration.type === 'VariableDeclaration') { - node.declaration.declarations.forEach(declarator => { - extract_names(declarator.id).forEach(name => { - const variable = this.var_lookup.get(name); - variable.export_name = name; - }); - }); - } else { - const { name } = node.declaration.id; - const variable = this.var_lookup.get(name); - variable.export_name = name; - } + return node.declaration; + } else { + node.specifiers.forEach(specifier => { + const variable = this.var_lookup.get(specifier.local.name); - content.body[i] = node.declaration; - } else { - node.specifiers.forEach(specifier => { - const variable = this.var_lookup.get(specifier.local.name); + if (variable) { + variable.export_name = specifier.exported.name; - if (variable) { - variable.export_name = specifier.exported.name; + if (variable.writable && !(variable.referenced || variable.referenced_from_script)) { + this.warn(specifier, { + code: `unused-export-let`, + message: `${this.name.name} has unused export property '${specifier.exported.name}'. If it is for external reference only, please consider using \`export const '${specifier.exported.name}'\`` + }); } - }); + } + }); - content.body.splice(i, 1); - } + return null; } } } @@ -522,7 +526,7 @@ export default class Component { }); } - walk_module_js() { + walk_module_js_pre_template() { const component = this; const script = this.ast.module; if (!script) return; @@ -573,9 +577,6 @@ export default class Component { }); } }); - - this.extract_imports(script.content); - this.extract_exports(script.content); } walk_instance_js_pre_template() { @@ -657,7 +658,10 @@ export default class Component { this.add_reference(name.slice(1)); const variable = this.var_lookup.get(name.slice(1)); - if (variable) variable.subscribable = true; + if (variable) { + variable.subscribable = true; + variable.referenced_from_script = true; + } } else { this.add_var({ name, @@ -667,46 +671,83 @@ export default class Component { } }); - this.extract_imports(script.content); - this.extract_exports(script.content); - this.track_mutations(); + this.track_references_and_mutations(); + } + + walk_module_js_post_template() { + const script = this.ast.module; + if (!script) return; + + const { body } = script.content; + let i = body.length; + while(--i >= 0) { + const node = body[i]; + if (node.type === 'ImportDeclaration') { + this.extract_imports(node); + body.splice(i, 1); + } + + if (/^Export/.test(node.type)) { + const replacement = this.extract_exports(node); + if (replacement) { + body[i] = replacement; + } else { + body.splice(i, 1); + } + } + } } walk_instance_js_post_template() { const script = this.ast.instance; if (!script) return; - this.warn_on_undefined_store_value_references(); + this.post_template_walk(); + this.hoist_instance_declarations(); this.extract_reactive_declarations(); } - // TODO merge this with other walks that are independent - track_mutations() { + post_template_walk() { + const script = this.ast.instance; + if (!script) return; + const component = this; + const { content } = script; const { instance_scope, instance_scope_map: map } = this; let scope = instance_scope; - walk(this.ast.instance.content, { - enter(node) { + const toRemove = []; + const remove = (parent, prop, index) => { + toRemove.unshift([parent, prop, index]); + } + + walk(content, { + enter(node, parent, prop, index) { if (map.has(node)) { scope = map.get(node); } - if (node.type === 'AssignmentExpression' || node.type === 'UpdateExpression') { - const assignee = node.type === 'AssignmentExpression' ? node.left : node.argument; - const names = extract_names(assignee); - - const deep = assignee.type === 'MemberExpression'; + if (node.type === 'ImportDeclaration') { + component.extract_imports(node); + // TODO: to use actual remove + remove(parent, prop, index); + return this.skip(); + } - names.forEach(name => { - if (scope.find_owner(name) === instance_scope) { - const variable = component.var_lookup.get(name); - variable[deep ? 'mutated' : 'reassigned'] = true; - } - }); + if (/^Export/.test(node.type)) { + const replacement = component.extract_exports(node); + if (replacement) { + this.replace(replacement); + } else { + // TODO: to use actual remove + remove(parent, prop, index); + } + return this.skip(); } + + component.warn_on_undefined_store_value_references(node, parent, scope); }, leave(node) { @@ -715,37 +756,53 @@ export default class Component { } }, }); + + for(const [parent, prop, index] of toRemove) { + if (parent) { + if (index !== null) { + parent[prop].splice(index, 1); + } else { + delete parent[prop]; + } + } + } } - warn_on_undefined_store_value_references() { - // TODO this pattern happens a lot... can we abstract it - // (or better still, do fewer AST walks)? + track_references_and_mutations() { + const script = this.ast.instance; + if (!script) return; + const component = this; - let { instance_scope: scope, instance_scope_map: map } = this; + const { content } = script; + const { instance_scope, instance_scope_map: map } = this; - walk(this.ast.instance.content, { + let scope = instance_scope; + + walk(content, { enter(node, parent) { if (map.has(node)) { scope = map.get(node); } - if ( - node.type === 'LabeledStatement' && - node.label.name === '$' && - parent.type !== 'Program' - ) { - component.warn(node as any, { - code: 'non-top-level-reactive-declaration', - message: '$: has no effect outside of the top-level', + if (node.type === 'AssignmentExpression' || node.type === 'UpdateExpression') { + const assignee = node.type === 'AssignmentExpression' ? node.left : node.argument; + const names = extract_names(assignee); + + const deep = assignee.type === 'MemberExpression'; + + names.forEach(name => { + if (scope.find_owner(name) === instance_scope) { + const variable = component.var_lookup.get(name); + variable[deep ? 'mutated' : 'reassigned'] = true; + } }); } - if (is_reference(node as Node, parent as Node)) { + if (is_used_as_reference(node, parent)) { const object = get_object(node); - const { name } = object; - - if (name[0] === '$' && !scope.has(name)) { - component.warn_if_undefined(name, object, null); + if (scope.find_owner(object.name) === instance_scope) { + const variable = component.var_lookup.get(object.name); + variable.referenced_from_script = true; } } }, @@ -758,6 +815,28 @@ export default class Component { }); } + warn_on_undefined_store_value_references(node, parent, scope) { + if ( + node.type === 'LabeledStatement' && + node.label.name === '$' && + parent.type !== 'Program' + ) { + this.warn(node as any, { + code: 'non-top-level-reactive-declaration', + message: '$: has no effect outside of the top-level', + }); + } + + if (is_reference(node as Node, parent as Node)) { + const object = get_object(node); + const { name } = object; + + if (name[0] === '$' && !scope.has(name)) { + this.warn_if_undefined(name, object, null); + } + } + } + invalidate(name, value?) { const variable = this.var_lookup.get(name); diff --git a/src/compiler/compile/utils/is_used_as_reference.ts b/src/compiler/compile/utils/is_used_as_reference.ts new file mode 100644 index 0000000000..8d55182794 --- /dev/null +++ b/src/compiler/compile/utils/is_used_as_reference.ts @@ -0,0 +1,33 @@ +import { Node } from 'estree'; +import is_reference from 'is-reference'; + +export default function is_used_as_reference( + node: Node, + parent: Node +): boolean { + if (!is_reference(node, parent)) { + return false; + } + if (!parent) { + return true; + } + + switch (parent.type) { + // disregard the `foo` in `const foo = bar` + case 'VariableDeclarator': + return node !== parent.id; + // disregard the `foo`, `bar` in `function foo(bar){}` + case 'FunctionDeclaration': + // disregard the `foo` in `import { foo } from 'foo'` + case 'ImportSpecifier': + // disregard the `foo` in `import foo from 'foo'` + case 'ImportDefaultSpecifier': + // disregard the `foo` in `import * as foo from 'foo'` + case 'ImportNamespaceSpecifier': + // disregard the `foo` in `export { foo }` + case 'ExportSpecifier': + return false; + default: + return true; + } +} diff --git a/src/compiler/interfaces.ts b/src/compiler/interfaces.ts index 1e0028e8aa..f877b93b56 100644 --- a/src/compiler/interfaces.ts +++ b/src/compiler/interfaces.ts @@ -148,7 +148,8 @@ export interface Var { module?: boolean; mutated?: boolean; reassigned?: boolean; - referenced?: boolean; + referenced?: boolean; // referenced from template scope + referenced_from_script?: boolean; // referenced from script writable?: boolean; // used internally, but not exposed diff --git a/test/validator/samples/unreferenced-variables/input.svelte b/test/validator/samples/unreferenced-variables/input.svelte new file mode 100644 index 0000000000..1180f6ef1a --- /dev/null +++ b/test/validator/samples/unreferenced-variables/input.svelte @@ -0,0 +1,21 @@ + diff --git a/test/validator/samples/unreferenced-variables/warnings.json b/test/validator/samples/unreferenced-variables/warnings.json new file mode 100644 index 0000000000..7d9e0111bf --- /dev/null +++ b/test/validator/samples/unreferenced-variables/warnings.json @@ -0,0 +1,77 @@ +[ + { + "code": "unused-export-let", + "end": { + "character": 103, + "column": 12, + "line": 8 + }, + "message": "Component has unused export property 'd'. If it is for external reference only, please consider using `export const 'd'`", + "pos": 102, + "start": { + "character": 102, + "column": 11, + "line": 8 + } + }, + { + "code": "unused-export-let", + "end": { + "character": 106, + "column": 15, + "line": 8 + }, + "message": "Component has unused export property 'e'. If it is for external reference only, please consider using `export const 'e'`", + "pos": 105, + "start": { + "character": 105, + "column": 14, + "line": 8 + } + }, + { + "code": "unused-export-let", + "end": { + "character": 130, + "column": 18, + "line": 9 + }, + "message": "Component has unused export property 'g'. If it is for external reference only, please consider using `export const 'g'`", + "pos": 125, + "start": { + "character": 125, + "column": 13, + "line": 9 + } + }, + { + "code": "unused-export-let", + "end": { + "character": 150, + "column": 18, + "line": 10 + }, + "message": "Component has unused export property 'h'. If it is for external reference only, please consider using `export const 'h'`", + "pos": 145, + "start": { + "character": 145, + "column": 13, + "line": 10 + } + }, + { + "code": "unused-export-let", + "end": { + "character": 199, + "column": 25, + "line": 12 + }, + "message": "Component has unused export property 'j'. If it is for external reference only, please consider using `export const 'j'`", + "pos": 187, + "start": { + "character": 187, + "column": 13, + "line": 12 + } + } +] diff --git a/test/vars/samples/$$props-logicless/_config.js b/test/vars/samples/$$props-logicless/_config.js index b0d76bebbd..20f9b99466 100644 --- a/test/vars/samples/$$props-logicless/_config.js +++ b/test/vars/samples/$$props-logicless/_config.js @@ -9,6 +9,7 @@ export default { mutated: false, reassigned: false, referenced: true, + referenced_from_script: false, writable: false } ]); diff --git a/test/vars/samples/$$props/_config.js b/test/vars/samples/$$props/_config.js index b0d76bebbd..20f9b99466 100644 --- a/test/vars/samples/$$props/_config.js +++ b/test/vars/samples/$$props/_config.js @@ -9,6 +9,7 @@ export default { mutated: false, reassigned: false, referenced: true, + referenced_from_script: false, writable: false } ]); diff --git a/test/vars/samples/actions/_config.js b/test/vars/samples/actions/_config.js index 6b6f8696e7..4cc2b9d18c 100644 --- a/test/vars/samples/actions/_config.js +++ b/test/vars/samples/actions/_config.js @@ -9,6 +9,7 @@ export default { name: 'hoistable_foo', reassigned: false, referenced: true, + referenced_from_script: false, writable: false }, { @@ -19,6 +20,7 @@ export default { name: 'foo', reassigned: false, referenced: true, + referenced_from_script: false, writable: true } ]); diff --git a/test/vars/samples/animations/_config.js b/test/vars/samples/animations/_config.js index 6b6f8696e7..4cc2b9d18c 100644 --- a/test/vars/samples/animations/_config.js +++ b/test/vars/samples/animations/_config.js @@ -9,6 +9,7 @@ export default { name: 'hoistable_foo', reassigned: false, referenced: true, + referenced_from_script: false, writable: false }, { @@ -19,6 +20,7 @@ export default { name: 'foo', reassigned: false, referenced: true, + referenced_from_script: false, writable: true } ]); diff --git a/test/vars/samples/component-namespaced/_config.js b/test/vars/samples/component-namespaced/_config.js index ac63873967..a801c52780 100644 --- a/test/vars/samples/component-namespaced/_config.js +++ b/test/vars/samples/component-namespaced/_config.js @@ -9,6 +9,7 @@ export default { mutated: false, reassigned: false, referenced: true, + referenced_from_script: false, writable: false } ]); diff --git a/test/vars/samples/duplicate-non-hoistable/_config.js b/test/vars/samples/duplicate-non-hoistable/_config.js index bb7f52d4db..4ebc5b00cf 100644 --- a/test/vars/samples/duplicate-non-hoistable/_config.js +++ b/test/vars/samples/duplicate-non-hoistable/_config.js @@ -9,6 +9,7 @@ export default { mutated: false, reassigned: false, referenced: true, + referenced_from_script: false, writable: true } ]); diff --git a/test/vars/samples/duplicate-vars/_config.js b/test/vars/samples/duplicate-vars/_config.js index 276ae130a1..eb10c44a9a 100644 --- a/test/vars/samples/duplicate-vars/_config.js +++ b/test/vars/samples/duplicate-vars/_config.js @@ -9,6 +9,7 @@ export default { mutated: false, reassigned: false, referenced: false, + referenced_from_script: false, writable: true }, { @@ -19,6 +20,7 @@ export default { mutated: false, reassigned: false, referenced: true, + referenced_from_script: false, writable: true } ]); diff --git a/test/vars/samples/implicit-reactive/_config.js b/test/vars/samples/implicit-reactive/_config.js index fd67e8b249..770de590e6 100644 --- a/test/vars/samples/implicit-reactive/_config.js +++ b/test/vars/samples/implicit-reactive/_config.js @@ -9,6 +9,7 @@ export default { mutated: false, reassigned: false, referenced: true, + referenced_from_script: true, writable: true }, { @@ -19,6 +20,7 @@ export default { mutated: false, reassigned: true, referenced: true, + referenced_from_script: false, writable: true } ]); diff --git a/test/vars/samples/imports/_config.js b/test/vars/samples/imports/_config.js index 90f2468b54..ecf120c7d6 100644 --- a/test/vars/samples/imports/_config.js +++ b/test/vars/samples/imports/_config.js @@ -9,6 +9,7 @@ export default { mutated: false, reassigned: false, referenced: false, + referenced_from_script: false, writable: false }, { @@ -19,6 +20,7 @@ export default { mutated: false, reassigned: false, referenced: false, + referenced_from_script: false, writable: false }, { @@ -29,6 +31,7 @@ export default { mutated: false, reassigned: false, referenced: false, + referenced_from_script: false, writable: false } ]); diff --git a/test/vars/samples/mutated-vs-reassigned-bindings/_config.js b/test/vars/samples/mutated-vs-reassigned-bindings/_config.js index 21fc14d820..ba499674d2 100644 --- a/test/vars/samples/mutated-vs-reassigned-bindings/_config.js +++ b/test/vars/samples/mutated-vs-reassigned-bindings/_config.js @@ -9,6 +9,7 @@ export default { mutated: false, reassigned: true, referenced: true, + referenced_from_script: false, writable: true }, { @@ -19,6 +20,7 @@ export default { mutated: true, reassigned: false, referenced: true, + referenced_from_script: false, writable: false } ]); diff --git a/test/vars/samples/mutated-vs-reassigned/_config.js b/test/vars/samples/mutated-vs-reassigned/_config.js index 21fc14d820..ba499674d2 100644 --- a/test/vars/samples/mutated-vs-reassigned/_config.js +++ b/test/vars/samples/mutated-vs-reassigned/_config.js @@ -9,6 +9,7 @@ export default { mutated: false, reassigned: true, referenced: true, + referenced_from_script: false, writable: true }, { @@ -19,6 +20,7 @@ export default { mutated: true, reassigned: false, referenced: true, + referenced_from_script: false, writable: false } ]); diff --git a/test/vars/samples/props/_config.js b/test/vars/samples/props/_config.js index 0c5c6f7e2a..a51c93fb03 100644 --- a/test/vars/samples/props/_config.js +++ b/test/vars/samples/props/_config.js @@ -9,6 +9,7 @@ export default { mutated: false, reassigned: false, referenced: true, + referenced_from_script: false, writable: true }, { @@ -19,6 +20,7 @@ export default { mutated: false, reassigned: false, referenced: true, + referenced_from_script: false, writable: true }, { @@ -29,6 +31,7 @@ export default { mutated: false, reassigned: false, referenced: false, + referenced_from_script: true, writable: true }, { @@ -39,6 +42,7 @@ export default { mutated: false, reassigned: true, referenced: true, + referenced_from_script: true, writable: true } ]); diff --git a/test/vars/samples/referenced-from-script/_config.js b/test/vars/samples/referenced-from-script/_config.js new file mode 100644 index 0000000000..191a52f8cc --- /dev/null +++ b/test/vars/samples/referenced-from-script/_config.js @@ -0,0 +1,160 @@ +export default { + test(assert, vars) { + assert.deepEqual(vars, [ + { + name: 'i', + export_name: null, + injected: false, + module: false, + mutated: false, + reassigned: false, + referenced: false, + writable: false, + referenced_from_script: false, + }, + { + name: 'j', + export_name: null, + injected: false, + module: false, + mutated: false, + reassigned: false, + referenced: false, + writable: false, + referenced_from_script: false, + }, + { + name: 'k', + export_name: null, + injected: false, + module: false, + mutated: false, + reassigned: false, + referenced: false, + writable: false, + referenced_from_script: false, + }, + { + name: 'a', + export_name: null, + injected: false, + module: false, + mutated: false, + reassigned: true, + referenced: false, + writable: true, + referenced_from_script: true, + }, + { + name: 'b', + export_name: null, + injected: false, + module: false, + mutated: false, + reassigned: false, + referenced: false, + writable: true, + referenced_from_script: true, + }, + { + name: 'c', + export_name: null, + injected: false, + module: false, + mutated: false, + reassigned: false, + referenced: false, + writable: true, + referenced_from_script: true, + }, + { + name: 'd', + export_name: null, + injected: false, + module: false, + mutated: false, + reassigned: false, + referenced: false, + writable: true, + referenced_from_script: true, + }, + { + name: 'e', + export_name: null, + injected: false, + module: false, + mutated: false, + reassigned: false, + referenced: false, + writable: true, + referenced_from_script: false, + }, + { + name: 'f', + export_name: 'f', + injected: false, + module: false, + mutated: false, + reassigned: false, + referenced: false, + writable: true, + referenced_from_script: false, + }, + { + name: 'g', + export_name: null, + injected: false, + module: false, + mutated: false, + reassigned: false, + referenced: false, + writable: true, + referenced_from_script: true, + }, + { + name: 'h', + export_name: null, + injected: false, + module: false, + mutated: false, + reassigned: true, + referenced: false, + writable: true, + referenced_from_script: true, + }, + { + name: 'foo', + export_name: null, + injected: false, + module: false, + mutated: false, + reassigned: false, + referenced: false, + writable: false, + referenced_from_script: false, + }, + { + name: 'l', + export_name: null, + injected: false, + module: false, + mutated: false, + reassigned: false, + referenced: false, + referenced_from_script: true, + writable: false, + }, + { + name: 'bar', + export_name: 'bar', + injected: false, + module: false, + mutated: false, + reassigned: false, + referenced: false, + writable: false, + referenced_from_script: false, + }, + ]); + }, +}; diff --git a/test/vars/samples/referenced-from-script/input.svelte b/test/vars/samples/referenced-from-script/input.svelte new file mode 100644 index 0000000000..fbec4e6cf7 --- /dev/null +++ b/test/vars/samples/referenced-from-script/input.svelte @@ -0,0 +1,16 @@ + diff --git a/test/vars/samples/store-referenced/_config.js b/test/vars/samples/store-referenced/_config.js index 632a9e5b3f..bac35d1dba 100644 --- a/test/vars/samples/store-referenced/_config.js +++ b/test/vars/samples/store-referenced/_config.js @@ -9,6 +9,7 @@ export default { mutated: false, reassigned: false, referenced: true, + referenced_from_script: false, writable: true }, { @@ -19,6 +20,7 @@ export default { mutated: true, reassigned: false, referenced: true, + referenced_from_script: false, writable: true } ]); diff --git a/test/vars/samples/store-unreferenced/_config.js b/test/vars/samples/store-unreferenced/_config.js index 86e467c859..4965e52fec 100644 --- a/test/vars/samples/store-unreferenced/_config.js +++ b/test/vars/samples/store-unreferenced/_config.js @@ -9,6 +9,7 @@ export default { mutated: false, reassigned: false, referenced: true, + referenced_from_script: true, writable: true }, { @@ -19,6 +20,7 @@ export default { mutated: true, reassigned: false, referenced: false, + referenced_from_script: false, writable: true } ]); diff --git a/test/vars/samples/template-references/_config.js b/test/vars/samples/template-references/_config.js index adacc0d2ef..674e351517 100644 --- a/test/vars/samples/template-references/_config.js +++ b/test/vars/samples/template-references/_config.js @@ -9,6 +9,7 @@ export default { name: 'Bar', reassigned: false, referenced: true, + referenced_from_script: false, writable: false, }, { @@ -19,6 +20,7 @@ export default { name: 'foo', reassigned: false, referenced: true, + referenced_from_script: false, writable: true, }, { @@ -29,6 +31,7 @@ export default { name: 'baz', reassigned: false, referenced: true, + referenced_from_script: false, writable: true, }, ]); diff --git a/test/vars/samples/transitions/_config.js b/test/vars/samples/transitions/_config.js index b2522a1b56..29a99b16cc 100644 --- a/test/vars/samples/transitions/_config.js +++ b/test/vars/samples/transitions/_config.js @@ -9,6 +9,7 @@ export default { name: 'hoistable_foo', reassigned: false, referenced: true, + referenced_from_script: false, writable: false }, { @@ -19,6 +20,7 @@ export default { name: 'hoistable_bar', reassigned: false, referenced: true, + referenced_from_script: false, writable: false }, { @@ -29,6 +31,7 @@ export default { name: 'hoistable_baz', reassigned: false, referenced: true, + referenced_from_script: false, writable: false }, { @@ -39,6 +42,7 @@ export default { name: 'foo', reassigned: false, referenced: true, + referenced_from_script: false, writable: true }, { @@ -49,6 +53,7 @@ export default { name: 'bar', reassigned: false, referenced: true, + referenced_from_script: false, writable: true }, { @@ -59,6 +64,7 @@ export default { name: 'baz', reassigned: false, referenced: true, + referenced_from_script: false, writable: true } ]); From 9f7565cf198803acb4bd5a0c1b901b8137a94ab7 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Sun, 27 Oct 2019 13:53:17 +0800 Subject: [PATCH 036/395] fix changelog issue link --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3c4adac7b..924413bc02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,10 +12,10 @@ Also: * Allow exiting a reactive block early with `break $` ([#2828](https://github.com/sveltejs/svelte/issues/2828)) * Fix application of style scoping class in cases of ambiguity ([#3544](https://github.com/sveltejs/svelte/issues/3544)) * Check attributes have changed before setting them to avoid image flicker ([#3579](https://github.com/sveltejs/svelte/pull/3579)) -* Fix generating malformed code for `{@debug}` tags with no dependencies ([#3588](https://github.com/sveltejs/svelte/issue/3588)) -* Fix generated code in specific case involving compound ifs and child components ([#3595](https://github.com/sveltejs/svelte/issue/3595)) -* Fix `bind:this` binding to a store ([#3591](https://github.com/sveltejs/svelte/issue/3591)) -* Use safer `HTMLElement` check before extending class ([#3608](https://github.com/sveltejs/svelte/issue/3608)) +* Fix generating malformed code for `{@debug}` tags with no dependencies ([#3588](https://github.com/sveltejs/svelte/issues/3588)) +* Fix generated code in specific case involving compound ifs and child components ([#3595](https://github.com/sveltejs/svelte/issues/3595)) +* Fix `bind:this` binding to a store ([#3591](https://github.com/sveltejs/svelte/issues/3591)) +* Use safer `HTMLElement` check before extending class ([#3608](https://github.com/sveltejs/svelte/issues/3608)) * Add `location` as a known global ([#3619](https://github.com/sveltejs/svelte/pull/3619)) * Support `{#await}` with `{:catch}` but no `{:then}` ([#3623](https://github.com/sveltejs/svelte/issues/3623)) * Clean up dead code emitted for ``s ([#3631](https://github.com/sveltejs/svelte/issues/3631)) From 9f48d1a5fd9ec50d5eb630ad97fb11e84f39855a Mon Sep 17 00:00:00 2001 From: Conduitry Date: Sun, 27 Oct 2019 08:53:01 -0400 Subject: [PATCH 037/395] fix SSR spread with boolean attributes (#3797) * in SSR, adjust spread with boolean attributes (#2916) * add tests * update changelog --- CHANGELOG.md | 2 +- .../compile/render_ssr/handlers/Element.ts | 42 +------------------ .../handlers/shared/boolean_attributes.ts | 27 ++++++++++++ src/runtime/internal/ssr.ts | 15 +++---- .../samples/attribute-boolean/_expected.html | 1 + .../samples/attribute-boolean/main.svelte | 1 + .../spread-attributes-boolean/_expected.html | 2 + .../spread-attributes-boolean/main.svelte | 2 + 8 files changed, 43 insertions(+), 49 deletions(-) create mode 100644 src/compiler/compile/render_ssr/handlers/shared/boolean_attributes.ts create mode 100644 test/server-side-rendering/samples/spread-attributes-boolean/_expected.html create mode 100644 test/server-side-rendering/samples/spread-attributes-boolean/main.svelte diff --git a/CHANGELOG.md b/CHANGELOG.md index 924413bc02..40acabf61c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ Also: * Fix `{#each}` context not shadowing outer scope when using `bind:` ([#1565](https://github.com/sveltejs/svelte/issues/1565)) * Fix edge cases in matching selectors against elements ([#1710](https://github.com/sveltejs/svelte/issues/1710)) -* Fix several bugs related to interaction of `{...spread}` attributes with other features ([#2721](https://github.com/sveltejs/svelte/issues/2721), [#3421](https://github.com/sveltejs/svelte/issues/3421), [#3681](https://github.com/sveltejs/svelte/issues/3681), [#3764](https://github.com/sveltejs/svelte/issues/3764), [#3790](https://github.com/sveltejs/svelte/issues/3790)) +* Fix several bugs related to interaction of `{...spread}` attributes with other features ([#2721](https://github.com/sveltejs/svelte/issues/2721), [#2916](https://github.com/sveltejs/svelte/issues/2916), [#3421](https://github.com/sveltejs/svelte/issues/3421), [#3681](https://github.com/sveltejs/svelte/issues/3681), [#3764](https://github.com/sveltejs/svelte/issues/3764), [#3790](https://github.com/sveltejs/svelte/issues/3790)) * Allow exiting a reactive block early with `break $` ([#2828](https://github.com/sveltejs/svelte/issues/2828)) * Fix application of style scoping class in cases of ambiguity ([#3544](https://github.com/sveltejs/svelte/issues/3544)) * Check attributes have changed before setting them to avoid image flicker ([#3579](https://github.com/sveltejs/svelte/pull/3579)) diff --git a/src/compiler/compile/render_ssr/handlers/Element.ts b/src/compiler/compile/render_ssr/handlers/Element.ts index 65013a8d07..c77a44990c 100644 --- a/src/compiler/compile/render_ssr/handlers/Element.ts +++ b/src/compiler/compile/render_ssr/handlers/Element.ts @@ -1,52 +1,12 @@ import { is_void } from '../../../utils/names'; import { get_attribute_value, get_class_attribute_value } from './shared/get_attribute_value'; import { get_slot_scope } from './shared/get_slot_scope'; +import { boolean_attributes } from './shared/boolean_attributes'; import Renderer, { RenderOptions } from '../Renderer'; import Element from '../../nodes/Element'; import { x } from 'code-red'; import Expression from '../../nodes/shared/Expression'; -// source: https://gist.github.com/ArjanSchouten/0b8574a6ad7f5065a5e7 -const boolean_attributes = new Set([ - 'async', - 'autocomplete', - 'autofocus', - 'autoplay', - 'border', - 'challenge', - 'checked', - 'compact', - 'contenteditable', - 'controls', - 'default', - 'defer', - 'disabled', - 'formnovalidate', - 'frameborder', - 'hidden', - 'indeterminate', - 'ismap', - 'loop', - 'multiple', - 'muted', - 'nohref', - 'noresize', - 'noshade', - 'novalidate', - 'nowrap', - 'open', - 'readonly', - 'required', - 'reversed', - 'scoped', - 'scrolling', - 'seamless', - 'selected', - 'sortable', - 'spellcheck', - 'translate' -]); - export default function(node: Element, renderer: Renderer, options: RenderOptions & { slot_scopes: Map; }) { diff --git a/src/compiler/compile/render_ssr/handlers/shared/boolean_attributes.ts b/src/compiler/compile/render_ssr/handlers/shared/boolean_attributes.ts new file mode 100644 index 0000000000..4520a2064e --- /dev/null +++ b/src/compiler/compile/render_ssr/handlers/shared/boolean_attributes.ts @@ -0,0 +1,27 @@ +// source: https://html.spec.whatwg.org/multipage/indices.html +export const boolean_attributes = new Set([ + 'allowfullscreen', + 'allowpaymentrequest', + 'async', + 'autofocus', + 'autoplay', + 'checked', + 'controls', + 'default', + 'defer', + 'disabled', + 'formnovalidate', + 'hidden', + 'ismap', + 'loop', + 'multiple', + 'muted', + 'nomodule', + 'novalidate', + 'open', + 'playsinline', + 'readonly', + 'required', + 'reversed', + 'selected' +]); diff --git a/src/runtime/internal/ssr.ts b/src/runtime/internal/ssr.ts index 83e585a899..274006f243 100644 --- a/src/runtime/internal/ssr.ts +++ b/src/runtime/internal/ssr.ts @@ -1,5 +1,6 @@ import { set_current_component, current_component } from './lifecycle'; import { run_all, blank_object } from './utils'; +import { boolean_attributes } from '../../compiler/compile/render_ssr/handlers/shared/boolean_attributes'; export const invalid_attribute_name_character = /[\s'">/=\u{FDD0}-\u{FDEF}\u{FFFE}\u{FFFF}\u{1FFFE}\u{1FFFF}\u{2FFFE}\u{2FFFF}\u{3FFFE}\u{3FFFF}\u{4FFFE}\u{4FFFF}\u{5FFFE}\u{5FFFF}\u{6FFFE}\u{6FFFF}\u{7FFFE}\u{7FFFF}\u{8FFFE}\u{8FFFF}\u{9FFFE}\u{9FFFF}\u{AFFFE}\u{AFFFF}\u{BFFFE}\u{BFFFF}\u{CFFFE}\u{CFFFF}\u{DFFFE}\u{DFFFF}\u{EFFFE}\u{EFFFF}\u{FFFFE}\u{FFFFF}\u{10FFFE}\u{10FFFF}]/u; // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 @@ -20,14 +21,14 @@ export function spread(args, classes_to_add) { if (invalid_attribute_name_character.test(name)) return; const value = attributes[name]; - if (value == null) return; if (value === true) str += " " + name; - - const escaped = String(value) - .replace(/"/g, '"') - .replace(/'/g, '''); - - str += " " + name + "=" + JSON.stringify(escaped); + else if (boolean_attributes.has(name.toLowerCase())) { + if (value) str += " " + name; + } else if (value != null) { + str += " " + name + "=" + JSON.stringify(String(value) + .replace(/"/g, '"') + .replace(/'/g, ''')); + } }); return str; diff --git a/test/server-side-rendering/samples/attribute-boolean/_expected.html b/test/server-side-rendering/samples/attribute-boolean/_expected.html index 3ca3bfd9a8..5b36bb6c3f 100644 --- a/test/server-side-rendering/samples/attribute-boolean/_expected.html +++ b/test/server-side-rendering/samples/attribute-boolean/_expected.html @@ -1 +1,2 @@ + diff --git a/test/server-side-rendering/samples/attribute-boolean/main.svelte b/test/server-side-rendering/samples/attribute-boolean/main.svelte index 3ca3bfd9a8..0852e12418 100644 --- a/test/server-side-rendering/samples/attribute-boolean/main.svelte +++ b/test/server-side-rendering/samples/attribute-boolean/main.svelte @@ -1 +1,2 @@ + diff --git a/test/server-side-rendering/samples/spread-attributes-boolean/_expected.html b/test/server-side-rendering/samples/spread-attributes-boolean/_expected.html new file mode 100644 index 0000000000..06048d3efa --- /dev/null +++ b/test/server-side-rendering/samples/spread-attributes-boolean/_expected.html @@ -0,0 +1,2 @@ + + diff --git a/test/server-side-rendering/samples/spread-attributes-boolean/main.svelte b/test/server-side-rendering/samples/spread-attributes-boolean/main.svelte new file mode 100644 index 0000000000..3d865c0082 --- /dev/null +++ b/test/server-side-rendering/samples/spread-attributes-boolean/main.svelte @@ -0,0 +1,2 @@ + + From 7c445093fb97c2073c10dd6711ea3205eca328b9 Mon Sep 17 00:00:00 2001 From: pngwn Date: Sun, 27 Oct 2019 12:57:21 +0000 Subject: [PATCH 038/395] Document bind:playbackRate in the API documentation. Closes #3806. --- site/content/docs/02-template-syntax.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/site/content/docs/02-template-syntax.md b/site/content/docs/02-template-syntax.md index da731d920a..694010dfe7 100644 --- a/site/content/docs/02-template-syntax.md +++ b/site/content/docs/02-template-syntax.md @@ -570,9 +570,10 @@ Media elements (`