From 8b9b2c266e55c7a3e589a32cecf8f208e430c10a Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Thu, 16 Jan 2020 22:25:12 +0800 Subject: [PATCH 01/37] fix allow let scoped to root element (#4266) --- CHANGELOG.md | 4 +++ src/compiler/compile/nodes/Element.ts | 31 +++++++++---------- .../samples/component-slot-let-g/A.svelte | 5 +++ .../samples/component-slot-let-g/_config.js | 22 +++++++++++++ .../samples/component-slot-let-g/main.svelte | 17 ++++++++++ 5 files changed, 63 insertions(+), 16 deletions(-) create mode 100644 test/runtime/samples/component-slot-let-g/A.svelte create mode 100644 test/runtime/samples/component-slot-let-g/_config.js create mode 100644 test/runtime/samples/component-slot-let-g/main.svelte diff --git a/CHANGELOG.md b/CHANGELOG.md index dce6d47858..1f93815185 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Svelte changelog +## Unreleased + +* Allow access to `let:` variables in sibling attributes on slot root ([#4173](https://github.com/sveltejs/svelte/issues/4173)) + ## 3.17.1 * Only attach SSR mode markers to a component's `` elements when compiling with `hydratable: true` ([#4258](https://github.com/sveltejs/svelte/issues/4258)) diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index a3b8dc7286..e8108858c5 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -151,6 +151,11 @@ export default class Element extends Node { } } + const has_let = info.attributes.some(node => node.type === 'Let'); + if (has_let) { + scope = scope.child(); + } + // Binding relies on Attribute, defer its evaluation const order = ['Binding']; // everything else is -1 info.attributes.sort((a, b) => order.indexOf(a.type) - order.indexOf(b.type)); @@ -181,9 +186,16 @@ export default class Element extends Node { this.handlers.push(new EventHandler(component, this, scope, node)); break; - case 'Let': - this.lets.push(new Let(component, this, scope, node)); + case 'Let': { + const l = new Let(component, this, scope, node); + this.lets.push(l); + const dependencies = new Set([l.name.name]); + + l.names.forEach(name => { + scope.add(name, dependencies, this); + }); break; + } case 'Transition': { @@ -202,20 +214,7 @@ export default class Element extends Node { } }); - if (this.lets.length > 0) { - this.scope = scope.child(); - - this.lets.forEach(l => { - const dependencies = new Set([l.name.name]); - - l.names.forEach(name => { - this.scope.add(name, dependencies, this); - }); - }); - } else { - this.scope = scope; - } - + this.scope = scope; this.children = map_children(component, this, this.scope, info.children); this.validate(); diff --git a/test/runtime/samples/component-slot-let-g/A.svelte b/test/runtime/samples/component-slot-let-g/A.svelte new file mode 100644 index 0000000000..4f4ac95014 --- /dev/null +++ b/test/runtime/samples/component-slot-let-g/A.svelte @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-let-g/_config.js b/test/runtime/samples/component-slot-let-g/_config.js new file mode 100644 index 0000000000..aaa9895ea8 --- /dev/null +++ b/test/runtime/samples/component-slot-let-g/_config.js @@ -0,0 +1,22 @@ +export default { + html: ` + 1 + 0 + `, + async test({ assert, target, component, window }) { + component.x = 2; + + assert.htmlEqual(target.innerHTML, ` + 2 + 0 + `); + + const span = target.querySelector('span'); + await span.dispatchEvent(new window.MouseEvent('click')); + + assert.htmlEqual(target.innerHTML, ` + 2 + 2 + `); + } +}; diff --git a/test/runtime/samples/component-slot-let-g/main.svelte b/test/runtime/samples/component-slot-let-g/main.svelte new file mode 100644 index 0000000000..e7d4890e6b --- /dev/null +++ b/test/runtime/samples/component-slot-let-g/main.svelte @@ -0,0 +1,17 @@ + + + + y = reflected} + slot="foo" + let:reflected + class={reflected} + > + {reflected} + + +{ y } \ No newline at end of file From 2fd593f9d55d242cfcd4a6aafa09ce919d05ba7c Mon Sep 17 00:00:00 2001 From: John Muhl Date: Thu, 16 Jan 2020 20:27:43 -0600 Subject: [PATCH 02/37] Add more globals --- src/compiler/utils/names.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/compiler/utils/names.ts b/src/compiler/utils/names.ts index 88d4d4a319..02cc12e087 100644 --- a/src/compiler/utils/names.ts +++ b/src/compiler/utils/names.ts @@ -5,6 +5,8 @@ export const globals = new Set([ 'alert', 'Array', 'Boolean', + 'clearInterval', + 'clearTimeout', 'confirm', 'console', 'Date', @@ -16,6 +18,9 @@ export const globals = new Set([ 'Error', 'EvalError', 'Event', + 'fetch', + 'global', + 'globalThis', 'history', 'Infinity', 'InternalError', @@ -41,11 +46,14 @@ export const globals = new Set([ 'RegExp', 'sessionStorage', 'Set', + 'setInterval', + 'setTimeout', 'String', 'SyntaxError', 'TypeError', 'undefined', 'URIError', + 'URL', 'window' ]); From 1438994bd4f51633ba5c869c26d16be2e8224213 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Thu, 16 Jan 2020 22:13:41 -0500 Subject: [PATCH 03/37] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f93815185..f833dfae2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased * Allow access to `let:` variables in sibling attributes on slot root ([#4173](https://github.com/sveltejs/svelte/issues/4173)) +* Add some more known globals ([#4276](https://github.com/sveltejs/svelte/pull/4276)) ## 3.17.1 From 527ddea289196f3dc4c8ca5e0d001b9c7e5ce2a0 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Fri, 17 Jan 2020 08:07:48 +0800 Subject: [PATCH 04/37] disallow binding variables declared in await and catch --- src/compiler/compile/nodes/Binding.ts | 7 +++++++ src/compiler/compile/nodes/shared/TemplateScope.ts | 5 +++++ test/validator/samples/binding-await-catch/errors.json | 9 +++++++++ test/validator/samples/binding-await-catch/input.svelte | 7 +++++++ test/validator/samples/binding-await-then-2/errors.json | 9 +++++++++ test/validator/samples/binding-await-then-2/input.svelte | 7 +++++++ test/validator/samples/binding-await-then/errors.json | 9 +++++++++ test/validator/samples/binding-await-then/input.svelte | 6 ++++++ 8 files changed, 59 insertions(+) create mode 100644 test/validator/samples/binding-await-catch/errors.json create mode 100644 test/validator/samples/binding-await-catch/input.svelte create mode 100644 test/validator/samples/binding-await-then-2/errors.json create mode 100644 test/validator/samples/binding-await-then-2/input.svelte create mode 100644 test/validator/samples/binding-await-then/errors.json create mode 100644 test/validator/samples/binding-await-then/input.svelte diff --git a/src/compiler/compile/nodes/Binding.ts b/src/compiler/compile/nodes/Binding.ts index 28e6af5aa1..7d6fad0a81 100644 --- a/src/compiler/compile/nodes/Binding.ts +++ b/src/compiler/compile/nodes/Binding.ts @@ -50,6 +50,13 @@ export default class Binding extends Node { message: 'Cannot bind to a variable declared with the let: directive' }); } else if (this.is_contextual) { + if (scope.is_await(name)) { + component.error(this, { + code: 'invalid-binding', + message: 'Cannot bind to a variable declared with {#await ... then} or {:catch} blocks' + }); + } + scope.dependencies_for_name.get(name).forEach(name => { const variable = component.var_lookup.get(name); if (variable) { diff --git a/src/compiler/compile/nodes/shared/TemplateScope.ts b/src/compiler/compile/nodes/shared/TemplateScope.ts index 5f30d0c883..4e087eedf5 100644 --- a/src/compiler/compile/nodes/shared/TemplateScope.ts +++ b/src/compiler/compile/nodes/shared/TemplateScope.ts @@ -42,4 +42,9 @@ export default class TemplateScope { const owner = this.get_owner(name); return owner && (owner.type === 'Element' || owner.type === 'InlineComponent'); } + + is_await(name: string) { + const owner = this.get_owner(name); + return owner && (owner.type === 'ThenBlock' || owner.type === 'CatchBlock'); + } } diff --git a/test/validator/samples/binding-await-catch/errors.json b/test/validator/samples/binding-await-catch/errors.json new file mode 100644 index 0000000000..00141686f3 --- /dev/null +++ b/test/validator/samples/binding-await-catch/errors.json @@ -0,0 +1,9 @@ +[ + { + "code": "invalid-binding", + "message": "Cannot bind to a variable declared with {#await ... then} or {:catch} blocks", + "pos": 79, + "start": { "line": 6, "column": 9, "character": 79 }, + "end": { "line": 6, "column": 27, "character": 97 } + } +] diff --git a/test/validator/samples/binding-await-catch/input.svelte b/test/validator/samples/binding-await-catch/input.svelte new file mode 100644 index 0000000000..b640f6305b --- /dev/null +++ b/test/validator/samples/binding-await-catch/input.svelte @@ -0,0 +1,7 @@ + +{#await promise} +{:catch error} + +{/await} diff --git a/test/validator/samples/binding-await-then-2/errors.json b/test/validator/samples/binding-await-then-2/errors.json new file mode 100644 index 0000000000..e734ed4717 --- /dev/null +++ b/test/validator/samples/binding-await-then-2/errors.json @@ -0,0 +1,9 @@ +[ + { + "code": "invalid-binding", + "message": "Cannot bind to a variable declared with {#await ... then} or {:catch} blocks", + "pos": 78, + "start": { "line": 6, "column": 9, "character": 78 }, + "end": { "line": 6, "column": 19, "character": 88 } + } +] diff --git a/test/validator/samples/binding-await-then-2/input.svelte b/test/validator/samples/binding-await-then-2/input.svelte new file mode 100644 index 0000000000..e8c56c8e0b --- /dev/null +++ b/test/validator/samples/binding-await-then-2/input.svelte @@ -0,0 +1,7 @@ + +{#await promise} +{:then value} + +{/await} diff --git a/test/validator/samples/binding-await-then/errors.json b/test/validator/samples/binding-await-then/errors.json new file mode 100644 index 0000000000..a611e7731f --- /dev/null +++ b/test/validator/samples/binding-await-then/errors.json @@ -0,0 +1,9 @@ +[ + { + "code": "invalid-binding", + "message": "Cannot bind to a variable declared with {#await ... then} or {:catch} blocks", + "pos": 75, + "start": { "line": 5, "column": 9, "character": 75 }, + "end": { "line": 5, "column": 19, "character": 85 } + } +] diff --git a/test/validator/samples/binding-await-then/input.svelte b/test/validator/samples/binding-await-then/input.svelte new file mode 100644 index 0000000000..bc55ef97e4 --- /dev/null +++ b/test/validator/samples/binding-await-then/input.svelte @@ -0,0 +1,6 @@ + +{#await promise then value} + +{/await} From bda254e250e737a1dd7acf6c732d656ce52034a1 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Thu, 16 Jan 2020 22:17:25 -0500 Subject: [PATCH 05/37] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f833dfae2a..bc6cf36109 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +* Disallow two-way binding to a variable declared by an `{#await}` block ([#4012](https://github.com/sveltejs/svelte/issues/4012)) * Allow access to `let:` variables in sibling attributes on slot root ([#4173](https://github.com/sveltejs/svelte/issues/4173)) * Add some more known globals ([#4276](https://github.com/sveltejs/svelte/pull/4276)) From b39282a9188027903fdb99cdf762b640f11c6cc1 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Fri, 17 Jan 2020 07:22:40 -0500 Subject: [PATCH 06/37] apply event modifiers to events (#4279) --- CHANGELOG.md | 1 + .../compile/render_dom/wrappers/Body.ts | 25 ++++++++----------- .../wrappers/Element/EventHandler.ts | 3 ++- .../wrappers/shared/add_event_handlers.ts | 5 ++-- .../_config.js | 11 ++++++++ .../main.svelte | 5 ++++ 6 files changed, 33 insertions(+), 17 deletions(-) create mode 100644 test/runtime/samples/event-handler-modifier-body-once/_config.js create mode 100644 test/runtime/samples/event-handler-modifier-body-once/main.svelte diff --git a/CHANGELOG.md b/CHANGELOG.md index bc6cf36109..9b2c8f5b07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Disallow two-way binding to a variable declared by an `{#await}` block ([#4012](https://github.com/sveltejs/svelte/issues/4012)) * Allow access to `let:` variables in sibling attributes on slot root ([#4173](https://github.com/sveltejs/svelte/issues/4173)) * Add some more known globals ([#4276](https://github.com/sveltejs/svelte/pull/4276)) +* Correctly apply event modifiers to `` events ([#4278](https://github.com/sveltejs/svelte/issues/4278)) ## 3.17.1 diff --git a/src/compiler/compile/render_dom/wrappers/Body.ts b/src/compiler/compile/render_dom/wrappers/Body.ts index e16ebc25bd..d80ef6c4a0 100644 --- a/src/compiler/compile/render_dom/wrappers/Body.ts +++ b/src/compiler/compile/render_dom/wrappers/Body.ts @@ -1,26 +1,23 @@ import Block from '../Block'; import Wrapper from './shared/Wrapper'; -import { b } from 'code-red'; +import { x } from 'code-red'; import Body from '../../nodes/Body'; import { Identifier } from 'estree'; import EventHandler from './Element/EventHandler'; +import add_event_handlers from './shared/add_event_handlers'; +import { TemplateNode } from '../../../interfaces'; +import Renderer from '../Renderer'; export default class BodyWrapper extends Wrapper { node: Body; + handlers: EventHandler[]; - render(block: Block, _parent_node: Identifier, _parent_nodes: Identifier) { - this.node.handlers - .map(handler => new EventHandler(handler, this)) - .forEach(handler => { - const snippet = handler.get_snippet(block); - - block.chunks.init.push(b` - @_document.body.addEventListener("${handler.node.name}", ${snippet}); - `); + constructor(renderer: Renderer, block: Block, parent: Wrapper, node: TemplateNode) { + super(renderer, block, parent, node); + this.handlers = this.node.handlers.map(handler => new EventHandler(handler, this)); + } - block.chunks.destroy.push(b` - @_document.body.removeEventListener("${handler.node.name}", ${snippet}); - `); - }); + render(block: Block, _parent_node: Identifier, _parent_nodes: Identifier) { + add_event_handlers(block, x`@_document.body`, this.handlers); } } diff --git a/src/compiler/compile/render_dom/wrappers/Element/EventHandler.ts b/src/compiler/compile/render_dom/wrappers/Element/EventHandler.ts index 03183ee576..157e186ea6 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/EventHandler.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/EventHandler.ts @@ -2,6 +2,7 @@ import EventHandler from '../../../nodes/EventHandler'; import Wrapper from '../shared/Wrapper'; import Block from '../../Block'; import { b, x, p } from 'code-red'; +import { Expression } from 'estree'; const TRUE = x`true`; const FALSE = x`false`; @@ -35,7 +36,7 @@ export default class EventHandlerWrapper { return snippet; } - render(block: Block, target: string) { + render(block: Block, target: string | Expression) { let snippet = this.get_snippet(block); if (this.node.modifiers.has('preventDefault')) snippet = x`@prevent_default(${snippet})`; diff --git a/src/compiler/compile/render_dom/wrappers/shared/add_event_handlers.ts b/src/compiler/compile/render_dom/wrappers/shared/add_event_handlers.ts index 23a37715cc..99b8080b17 100644 --- a/src/compiler/compile/render_dom/wrappers/shared/add_event_handlers.ts +++ b/src/compiler/compile/render_dom/wrappers/shared/add_event_handlers.ts @@ -1,9 +1,10 @@ import Block from '../../Block'; import EventHandler from '../Element/EventHandler'; +import { Expression } from 'estree'; export default function add_event_handlers( block: Block, - target: string, + target: string | Expression, handlers: EventHandler[] ) { handlers.forEach(handler => add_event_handler(block, target, handler)); @@ -11,7 +12,7 @@ export default function add_event_handlers( export function add_event_handler( block: Block, - target: string, + target: string | Expression, handler: EventHandler ) { handler.render(block, target); diff --git a/test/runtime/samples/event-handler-modifier-body-once/_config.js b/test/runtime/samples/event-handler-modifier-body-once/_config.js new file mode 100644 index 0000000000..4127034010 --- /dev/null +++ b/test/runtime/samples/event-handler-modifier-body-once/_config.js @@ -0,0 +1,11 @@ +export default { + async test({ assert, component, window }) { + const event = new window.MouseEvent('click'); + + await window.document.body.dispatchEvent(event); + assert.equal(component.count, 1); + + await window.document.body.dispatchEvent(event); + assert.equal(component.count, 1); + } +}; diff --git a/test/runtime/samples/event-handler-modifier-body-once/main.svelte b/test/runtime/samples/event-handler-modifier-body-once/main.svelte new file mode 100644 index 0000000000..423a75d1f0 --- /dev/null +++ b/test/runtime/samples/event-handler-modifier-body-once/main.svelte @@ -0,0 +1,5 @@ + + + \ No newline at end of file From 2f81365e44f585c28295cb3937910eef00a35b0e Mon Sep 17 00:00:00 2001 From: Conduitry Date: Sat, 18 Jan 2020 07:10:13 -0500 Subject: [PATCH 07/37] fix awaited expressions that need parentheses (#4283) --- CHANGELOG.md | 1 + package-lock.json | 8 ++++---- package.json | 2 +- test/js/samples/debug-empty/expected.js | 2 +- test/js/samples/debug-foo-bar-baz-things/expected.js | 2 +- test/js/samples/debug-foo/expected.js | 2 +- .../samples/dev-warning-missing-data-computed/expected.js | 2 +- 7 files changed, 10 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b2c8f5b07..5264f35504 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Disallow two-way binding to a variable declared by an `{#await}` block ([#4012](https://github.com/sveltejs/svelte/issues/4012)) * Allow access to `let:` variables in sibling attributes on slot root ([#4173](https://github.com/sveltejs/svelte/issues/4173)) +* Fix code generation for `await`ed expressions that need parentheses ([#4267](https://github.com/sveltejs/svelte/issues/4267)) * Add some more known globals ([#4276](https://github.com/sveltejs/svelte/pull/4276)) * Correctly apply event modifiers to `` events ([#4278](https://github.com/sveltejs/svelte/issues/4278)) diff --git a/package-lock.json b/package-lock.json index 9ee0b53af6..4d902d5c48 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.17.0", + "version": "3.17.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -597,9 +597,9 @@ "dev": true }, "code-red": { - "version": "0.0.28", - "resolved": "https://registry.npmjs.org/code-red/-/code-red-0.0.28.tgz", - "integrity": "sha512-k9L7Sp85HNt3f/CvfrZKXoZOaO0tWOJCw2kU5GKc/c5pDj52OgBa0J+krMRmYtnGzS2dk4Xrn0EjjsJaM51hWQ==", + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/code-red/-/code-red-0.0.30.tgz", + "integrity": "sha512-nsScy3A59tbV5uzndcedIEGHmdWNEByJrC7DUyb0Wh7qZcoPfAlcYsr0ZINkAEhZofSI26F1mi+lRO0/6EV2/g==", "dev": true, "requires": { "acorn": "^7.1.0", diff --git a/package.json b/package.json index c7ac32f3ae..671a7e5025 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "acorn": "^7.1.0", "agadoo": "^1.1.0", "c8": "^5.0.1", - "code-red": "0.0.28", + "code-red": "0.0.30", "codecov": "^3.5.0", "css-tree": "1.0.0-alpha22", "eslint": "^6.3.0", diff --git a/test/js/samples/debug-empty/expected.js b/test/js/samples/debug-empty/expected.js index 5821faadf1..358363c661 100644 --- a/test/js/samples/debug-empty/expected.js +++ b/test/js/samples/debug-empty/expected.js @@ -103,7 +103,7 @@ class Component extends SvelteComponentDev { }); const { ctx } = this.$$; - const props = options.props || ({}); + const props = options.props || {}; if (/*name*/ ctx[0] === undefined && !("name" in props)) { console.warn(" was created without expected prop 'name'"); diff --git a/test/js/samples/debug-foo-bar-baz-things/expected.js b/test/js/samples/debug-foo-bar-baz-things/expected.js index a1d6dba3b3..fc65c59eda 100644 --- a/test/js/samples/debug-foo-bar-baz-things/expected.js +++ b/test/js/samples/debug-foo-bar-baz-things/expected.js @@ -210,7 +210,7 @@ class Component extends SvelteComponentDev { }); const { ctx } = this.$$; - const props = options.props || ({}); + const props = options.props || {}; if (/*things*/ ctx[0] === undefined && !("things" in props)) { console.warn(" was created without expected prop 'things'"); diff --git a/test/js/samples/debug-foo/expected.js b/test/js/samples/debug-foo/expected.js index af79667cc2..710d6b2232 100644 --- a/test/js/samples/debug-foo/expected.js +++ b/test/js/samples/debug-foo/expected.js @@ -198,7 +198,7 @@ class Component extends SvelteComponentDev { }); const { ctx } = this.$$; - const props = options.props || ({}); + const props = options.props || {}; if (/*things*/ ctx[0] === undefined && !("things" in props)) { console.warn(" was created without expected prop 'things'"); diff --git a/test/js/samples/dev-warning-missing-data-computed/expected.js b/test/js/samples/dev-warning-missing-data-computed/expected.js index beb794a50c..cc16de67e6 100644 --- a/test/js/samples/dev-warning-missing-data-computed/expected.js +++ b/test/js/samples/dev-warning-missing-data-computed/expected.js @@ -107,7 +107,7 @@ class Component extends SvelteComponentDev { }); const { ctx } = this.$$; - const props = options.props || ({}); + const props = options.props || {}; if (/*foo*/ ctx[0] === undefined && !("foo" in props)) { console.warn(" was created without expected prop 'foo'"); From e4460e38ba58d5209514f0fa93dc61b6bb1ebb54 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Sun, 19 Jan 2020 13:36:22 -0500 Subject: [PATCH 08/37] fix '~=' and class selectors with arbitrary whitespace (#4286) --- CHANGELOG.md | 1 + src/compiler/compile/css/Selector.ts | 6 +++--- .../expected.css | 1 + .../input.svelte | 13 +++++++++++++ 4 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 test/css/samples/attribute-selector-word-arbitrary-whitespace/expected.css create mode 100644 test/css/samples/attribute-selector-word-arbitrary-whitespace/input.svelte diff --git a/CHANGELOG.md b/CHANGELOG.md index 5264f35504..5b0bb4751d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Disallow two-way binding to a variable declared by an `{#await}` block ([#4012](https://github.com/sveltejs/svelte/issues/4012)) * Allow access to `let:` variables in sibling attributes on slot root ([#4173](https://github.com/sveltejs/svelte/issues/4173)) +* Fix `~=` and class selector matching against values separated by any whitespace characters ([#4242](https://github.com/sveltejs/svelte/issues/4242)) * Fix code generation for `await`ed expressions that need parentheses ([#4267](https://github.com/sveltejs/svelte/issues/4267)) * Add some more known globals ([#4276](https://github.com/sveltejs/svelte/pull/4276)) * Correctly apply event modifiers to `` events ([#4278](https://github.com/sveltejs/svelte/issues/4278)) diff --git a/src/compiler/compile/css/Selector.ts b/src/compiler/compile/css/Selector.ts index eecb0cd975..39bbc2e8d1 100644 --- a/src/compiler/compile/css/Selector.ts +++ b/src/compiler/compile/css/Selector.ts @@ -171,7 +171,7 @@ function apply_selector(blocks: Block[], node: Element, stack: Element[], to_enc 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 }); @@ -256,7 +256,7 @@ function test_attribute(operator, expected_value, case_insensitive, value) { } switch (operator) { case '=': return value === expected_value; - case '~=': return ` ${value} `.includes(` ${expected_value} `); + case '~=': return value.split(/\s/).includes(expected_value); case '|=': return `${value}-`.startsWith(`${expected_value}-`); case '^=': return value.startsWith(expected_value); case '$=': return value.endsWith(expected_value); @@ -295,7 +295,7 @@ function attribute_matches(node: CssNode, name: string, expected_value: string, // impossible to find out all combinations if (current_possible_values.has(UNKNOWN)) return true; - + if (prev_values.length > 0) { const start_with_space = []; const remaining = []; diff --git a/test/css/samples/attribute-selector-word-arbitrary-whitespace/expected.css b/test/css/samples/attribute-selector-word-arbitrary-whitespace/expected.css new file mode 100644 index 0000000000..c1c0f3fdf6 --- /dev/null +++ b/test/css/samples/attribute-selector-word-arbitrary-whitespace/expected.css @@ -0,0 +1 @@ +.foo.svelte-xyz{color:red}[class~="bar"].svelte-xyz{background:blue} \ No newline at end of file diff --git a/test/css/samples/attribute-selector-word-arbitrary-whitespace/input.svelte b/test/css/samples/attribute-selector-word-arbitrary-whitespace/input.svelte new file mode 100644 index 0000000000..758aa27e0b --- /dev/null +++ b/test/css/samples/attribute-selector-word-arbitrary-whitespace/input.svelte @@ -0,0 +1,13 @@ +
+ + From 33c3a02ee4b9ac3ee9d9dbec1c7328db0d49d6c0 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Mon, 20 Jan 2020 10:31:31 -0500 Subject: [PATCH 09/37] failing test --- test/hydration/samples/element-attribute-removed/_before.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hydration/samples/element-attribute-removed/_before.html b/test/hydration/samples/element-attribute-removed/_before.html index c6a8a8c95d..80c0591a4d 100644 --- a/test/hydration/samples/element-attribute-removed/_before.html +++ b/test/hydration/samples/element-attribute-removed/_before.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file From 91e0d1b720e209833f11e894b956ea361ef8878a Mon Sep 17 00:00:00 2001 From: Conduitry Date: Mon, 20 Jan 2020 10:34:29 -0500 Subject: [PATCH 10/37] fix removing attributes during hydration (#1733) --- src/runtime/internal/dom.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/runtime/internal/dom.ts b/src/runtime/internal/dom.ts index f9e89f41b9..5a165136ce 100644 --- a/src/runtime/internal/dom.ts +++ b/src/runtime/internal/dom.ts @@ -152,11 +152,16 @@ export function claim_element(nodes, name, attributes, svg) { for (let i = 0; i < nodes.length; i += 1) { const node = nodes[i]; if (node.nodeName === name) { - for (let j = 0; j < node.attributes.length; j += 1) { + let j = 0; + while (j < node.attributes.length) { const attribute = node.attributes[j]; - if (!attributes[attribute.name]) node.removeAttribute(attribute.name); + if (attributes[attribute.name]) { + j++; + } else { + node.removeAttribute(attribute.name); + } } - return nodes.splice(i, 1)[0]; // TODO strip unwanted attributes + return nodes.splice(i, 1)[0]; } } From b0415a269bb4918048508b97399f97a71f6499fe Mon Sep 17 00:00:00 2001 From: Conduitry Date: Mon, 20 Jan 2020 10:35:13 -0500 Subject: [PATCH 11/37] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b0bb4751d..d9ec5900b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +* Fix removing attributes during hydration ([#1733](https://github.com/sveltejs/svelte/issues/1733)) * Disallow two-way binding to a variable declared by an `{#await}` block ([#4012](https://github.com/sveltejs/svelte/issues/4012)) * Allow access to `let:` variables in sibling attributes on slot root ([#4173](https://github.com/sveltejs/svelte/issues/4173)) * Fix `~=` and class selector matching against values separated by any whitespace characters ([#4242](https://github.com/sveltejs/svelte/issues/4242)) From f12340acf02e6c8e7dc23cafe0469eb72d278fc8 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Mon, 20 Jan 2020 21:17:24 -0500 Subject: [PATCH 12/37] preserve js comments where possible (#4293) --- CHANGELOG.md | 1 + package-lock.json | 6 +++--- package.json | 2 +- src/compiler/parse/acorn.ts | 9 ++++----- .../js/samples/action-custom-event-handler/expected.js | 2 +- .../samples/collapses-text-around-comments/expected.js | 2 +- test/js/samples/css-media-query/expected.js | 2 +- test/js/samples/event-modifiers/expected.js | 4 ++-- test/js/samples/ssr-no-oncreate-etc/expected.js | 2 +- test/parser/samples/script-comment-only/output.json | 10 +++++++++- .../script-comment-trailing-multiline/output.json | 10 +++++++++- .../parser/samples/script-comment-trailing/output.json | 10 +++++++++- 12 files changed, 42 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9ec5900b4..59fa25682e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Allow access to `let:` variables in sibling attributes on slot root ([#4173](https://github.com/sveltejs/svelte/issues/4173)) * Fix `~=` and class selector matching against values separated by any whitespace characters ([#4242](https://github.com/sveltejs/svelte/issues/4242)) * Fix code generation for `await`ed expressions that need parentheses ([#4267](https://github.com/sveltejs/svelte/issues/4267)) +* Preserve JavaScript comments from the original component source where possible ([#4268](https://github.com/sveltejs/svelte/issues/4268)) * Add some more known globals ([#4276](https://github.com/sveltejs/svelte/pull/4276)) * Correctly apply event modifiers to `` events ([#4278](https://github.com/sveltejs/svelte/issues/4278)) diff --git a/package-lock.json b/package-lock.json index 4d902d5c48..90f5a331ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -597,9 +597,9 @@ "dev": true }, "code-red": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/code-red/-/code-red-0.0.30.tgz", - "integrity": "sha512-nsScy3A59tbV5uzndcedIEGHmdWNEByJrC7DUyb0Wh7qZcoPfAlcYsr0ZINkAEhZofSI26F1mi+lRO0/6EV2/g==", + "version": "0.0.31", + "resolved": "https://registry.npmjs.org/code-red/-/code-red-0.0.31.tgz", + "integrity": "sha512-7Gf3vm8pDbs+H/hKsaqOZe0xKlE9Neah12GCfs7qun3fBUaOXwexAMjn0Eo9cvJJvhRMaL0jgPiY9ZGLTWoe8A==", "dev": true, "requires": { "acorn": "^7.1.0", diff --git a/package.json b/package.json index 671a7e5025..6a66276c2d 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "acorn": "^7.1.0", "agadoo": "^1.1.0", "c8": "^5.0.1", - "code-red": "0.0.30", + "code-red": "0.0.31", "codecov": "^3.5.0", "css-tree": "1.0.0-alpha22", "eslint": "^6.3.0", diff --git a/src/compiler/parse/acorn.ts b/src/compiler/parse/acorn.ts index 30ab3c398c..d14721cdab 100644 --- a/src/compiler/parse/acorn.ts +++ b/src/compiler/parse/acorn.ts @@ -1,14 +1,13 @@ -import * as acorn from 'acorn'; +import { Node } from 'acorn'; +import * as code_red from 'code-red'; -const Parser = acorn.Parser; - -export const parse = (source: string) => Parser.parse(source, { +export const parse = (source: string): Node => code_red.parse(source, { sourceType: 'module', ecmaVersion: 11, locations: true }); -export const parse_expression_at = (source: string, index: number) => Parser.parseExpressionAt(source, index, { +export const parse_expression_at = (source: string, index: number): Node => code_red.parseExpressionAt(source, index, { ecmaVersion: 11, locations: true }); \ No newline at end of file diff --git a/test/js/samples/action-custom-event-handler/expected.js b/test/js/samples/action-custom-event-handler/expected.js index 968b5965d5..ead6d90e06 100644 --- a/test/js/samples/action-custom-event-handler/expected.js +++ b/test/js/samples/action-custom-event-handler/expected.js @@ -43,7 +43,7 @@ function handleFoo(bar) { function foo(node, callback) { -} +} // code goes here function instance($$self, $$props, $$invalidate) { let { bar } = $$props; diff --git a/test/js/samples/collapses-text-around-comments/expected.js b/test/js/samples/collapses-text-around-comments/expected.js index 8c0e9d5373..6fef0f9490 100644 --- a/test/js/samples/collapses-text-around-comments/expected.js +++ b/test/js/samples/collapses-text-around-comments/expected.js @@ -63,4 +63,4 @@ class Component extends SvelteComponent { } } -export default Component; +export default Component; \ No newline at end of file diff --git a/test/js/samples/css-media-query/expected.js b/test/js/samples/css-media-query/expected.js index 0566c22ddd..f477670059 100644 --- a/test/js/samples/css-media-query/expected.js +++ b/test/js/samples/css-media-query/expected.js @@ -46,4 +46,4 @@ class Component extends SvelteComponent { } } -export default Component; +export default Component; \ No newline at end of file diff --git a/test/js/samples/event-modifiers/expected.js b/test/js/samples/event-modifiers/expected.js index 252034a431..c12c3523a0 100644 --- a/test/js/samples/event-modifiers/expected.js +++ b/test/js/samples/event-modifiers/expected.js @@ -63,11 +63,11 @@ function create_fragment(ctx) { function handleTouchstart() { -} +} // ... function handleClick() { -} +} // ... class Component extends SvelteComponent { constructor(options) { diff --git a/test/js/samples/ssr-no-oncreate-etc/expected.js b/test/js/samples/ssr-no-oncreate-etc/expected.js index 276587eeca..803f06a882 100644 --- a/test/js/samples/ssr-no-oncreate-etc/expected.js +++ b/test/js/samples/ssr-no-oncreate-etc/expected.js @@ -13,7 +13,7 @@ function foo() { function swipe(node, callback) { -} +} // TODO implement const Component = create_ssr_component(($$result, $$props, $$bindings, $$slots) => { onMount(() => { diff --git a/test/parser/samples/script-comment-only/output.json b/test/parser/samples/script-comment-only/output.json index 3441d8a7bb..e090b30ae6 100644 --- a/test/parser/samples/script-comment-only/output.json +++ b/test/parser/samples/script-comment-only/output.json @@ -41,7 +41,15 @@ } }, "body": [], - "sourceType": "module" + "sourceType": "module", + "trailingComments": [ + { + "type": "Line", + "value": " TODO write some code", + "start": 10, + "end": 33 + } + ] } } } \ No newline at end of file diff --git a/test/parser/samples/script-comment-trailing-multiline/output.json b/test/parser/samples/script-comment-trailing-multiline/output.json index 3c02b1fbde..be83eeea8b 100644 --- a/test/parser/samples/script-comment-trailing-multiline/output.json +++ b/test/parser/samples/script-comment-trailing-multiline/output.json @@ -134,7 +134,15 @@ "kind": "let" } ], - "sourceType": "module" + "sourceType": "module", + "trailingComments": [ + { + "type": "Block", + "value": "\n\ttrailing multiline comment\n", + "start": 32, + "end": 67 + } + ] } } } \ No newline at end of file diff --git a/test/parser/samples/script-comment-trailing/output.json b/test/parser/samples/script-comment-trailing/output.json index beca001042..a2bc00bc00 100644 --- a/test/parser/samples/script-comment-trailing/output.json +++ b/test/parser/samples/script-comment-trailing/output.json @@ -134,7 +134,15 @@ "kind": "let" } ], - "sourceType": "module" + "sourceType": "module", + "trailingComments": [ + { + "type": "Line", + "value": " trailing line comment", + "start": 32, + "end": 56 + } + ] } } } \ No newline at end of file From e93c9913623ba43d8424191fe33f8bebb36e7dea Mon Sep 17 00:00:00 2001 From: Conduitry Date: Mon, 20 Jan 2020 21:18:15 -0500 Subject: [PATCH 13/37] -> v3.17.2 --- CHANGELOG.md | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59fa25682e..92e61719d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Svelte changelog -## Unreleased +## 3.17.2 * Fix removing attributes during hydration ([#1733](https://github.com/sveltejs/svelte/issues/1733)) * Disallow two-way binding to a variable declared by an `{#await}` block ([#4012](https://github.com/sveltejs/svelte/issues/4012)) diff --git a/package-lock.json b/package-lock.json index 90f5a331ed..ca225baa1b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.17.1", + "version": "3.17.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 6a66276c2d..0910dca8c0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.17.1", + "version": "3.17.2", "description": "Cybernetically enhanced web apps", "module": "index.mjs", "main": "index", From e4daaccd06cfe8fe1f4885fcb10942132153b521 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Wed, 22 Jan 2020 19:32:24 +0800 Subject: [PATCH 14/37] fix nested block not reactive (#4294) --- CHANGELOG.md | 4 +++ src/compiler/compile/render_dom/Block.ts | 3 ++ .../component-slot-nested-if/Display.svelte | 2 ++ .../component-slot-nested-if/Input.svelte | 6 ++++ .../component-slot-nested-if/_config.js | 30 +++++++++++++++++++ .../component-slot-nested-if/main.svelte | 10 +++++++ 6 files changed, 55 insertions(+) create mode 100644 test/runtime/samples/component-slot-nested-if/Display.svelte create mode 100644 test/runtime/samples/component-slot-nested-if/Input.svelte create mode 100644 test/runtime/samples/component-slot-nested-if/_config.js create mode 100644 test/runtime/samples/component-slot-nested-if/main.svelte diff --git a/CHANGELOG.md b/CHANGELOG.md index 92e61719d0..60153dcaba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Svelte changelog +## Unreleased + +* Fix updating a `` inside an `{#if}` or other block ([#4292](https://github.com/sveltejs/svelte/issues/4292)) + ## 3.17.2 * Fix removing attributes during hydration ([#1733](https://github.com/sveltejs/svelte/issues/1733)) diff --git a/src/compiler/compile/render_dom/Block.ts b/src/compiler/compile/render_dom/Block.ts index 68d28024fe..62bdc5bdd9 100644 --- a/src/compiler/compile/render_dom/Block.ts +++ b/src/compiler/compile/render_dom/Block.ts @@ -160,6 +160,9 @@ export default class Block { }); this.has_update_method = true; + if (this.parent) { + this.parent.add_dependencies(dependencies); + } } add_element( diff --git a/test/runtime/samples/component-slot-nested-if/Display.svelte b/test/runtime/samples/component-slot-nested-if/Display.svelte new file mode 100644 index 0000000000..d9034e4be2 --- /dev/null +++ b/test/runtime/samples/component-slot-nested-if/Display.svelte @@ -0,0 +1,2 @@ +Display: + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-nested-if/Input.svelte b/test/runtime/samples/component-slot-nested-if/Input.svelte new file mode 100644 index 0000000000..fd8f22db00 --- /dev/null +++ b/test/runtime/samples/component-slot-nested-if/Input.svelte @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-nested-if/_config.js b/test/runtime/samples/component-slot-nested-if/_config.js new file mode 100644 index 0000000000..89dfd006cc --- /dev/null +++ b/test/runtime/samples/component-slot-nested-if/_config.js @@ -0,0 +1,30 @@ +export default { + html: ` + + `, + async test({ assert, target, snapshot, component, window }) { + const input = target.querySelector('input'); + + input.value = 'a'; + await input.dispatchEvent(new window.Event('input')); + + assert.htmlEqual( + target.innerHTML, + ` + + Display: a + ` + ); + + input.value = 'abc'; + await input.dispatchEvent(new window.Event('input')); + + assert.htmlEqual( + target.innerHTML, + ` + + Display: abc + ` + ); + }, +}; diff --git a/test/runtime/samples/component-slot-nested-if/main.svelte b/test/runtime/samples/component-slot-nested-if/main.svelte new file mode 100644 index 0000000000..727927b157 --- /dev/null +++ b/test/runtime/samples/component-slot-nested-if/main.svelte @@ -0,0 +1,10 @@ + + + + {#if foo} + {foo} + {/if} + From 5107ad38b6ce0e388cb2eb99361c5000c4b6de91 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Wed, 22 Jan 2020 09:19:32 -0500 Subject: [PATCH 15/37] fix deriving from RxJS observables (#4300) --- CHANGELOG.md | 1 + src/runtime/internal/utils.ts | 4 ++-- src/runtime/store/index.ts | 5 +++-- test/store/index.js | 25 +++++++++++++++---------- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60153dcaba..388fe61397 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased * Fix updating a `` inside an `{#if}` or other block ([#4292](https://github.com/sveltejs/svelte/issues/4292)) +* Fix using RxJS observables in `derived` stores ([#4298](https://github.com/sveltejs/svelte/issues/4298)) ## 3.17.2 diff --git a/src/runtime/internal/utils.ts b/src/runtime/internal/utils.ts index c94a135869..c7722e1a07 100644 --- a/src/runtime/internal/utils.ts +++ b/src/runtime/internal/utils.ts @@ -48,8 +48,8 @@ export function validate_store(store, name) { } } -export function subscribe(store, callback) { - const unsub = store.subscribe(callback); +export function subscribe(store, ...callbacks) { + const unsub = store.subscribe(...callbacks); return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub; } diff --git a/src/runtime/store/index.ts b/src/runtime/store/index.ts index 64a63d4179..2bbfdfcd60 100644 --- a/src/runtime/store/index.ts +++ b/src/runtime/store/index.ts @@ -1,4 +1,4 @@ -import { run_all, noop, safe_not_equal, is_function, get_store_value } from 'svelte/internal'; +import { run_all, subscribe, noop, safe_not_equal, is_function, get_store_value } from 'svelte/internal'; /** Callback to inform of a value updates. */ type Subscriber = (value: T) => void; @@ -173,7 +173,8 @@ export function derived(stores: Stores, fn: Function, initial_value?: T): Rea } }; - const unsubscribers = stores_array.map((store, i) => store.subscribe( + const unsubscribers = stores_array.map((store, i) => subscribe( + store, (value) => { values[i] = value; pending &= ~(1 << i); diff --git a/test/store/index.js b/test/store/index.js index a39fab86e6..2af4a6f35d 100644 --- a/test/store/index.js +++ b/test/store/index.js @@ -116,6 +116,15 @@ describe('store', () => { }); }); + const fake_observable = { + subscribe(fn) { + fn(42); + return { + unsubscribe: () => {} + }; + } + }; + describe('derived', () => { it('maps a single store', () => { const a = writable(1); @@ -346,6 +355,11 @@ describe('store', () => { b.set(2); assert.deepEqual(get(c), 'two 2'); }); + + it('works with RxJS-style observables', () => { + const d = derived(fake_observable, _ => _); + assert.equal(get(d), 42); + }); }); describe('get', () => { @@ -355,16 +369,7 @@ describe('store', () => { }); it('works with RxJS-style observables', () => { - const observable = { - subscribe(fn) { - fn(42); - return { - unsubscribe: () => {} - }; - } - }; - - assert.equal(get(observable), 42); + assert.equal(get(fake_observable), 42); }); }); }); From 1a343b165c577429e968cea48607cccabf714b9b Mon Sep 17 00:00:00 2001 From: Conduitry Date: Wed, 22 Jan 2020 11:05:31 -0500 Subject: [PATCH 16/37] disallow duplicate each keys in dev mode (#4303) --- CHANGELOG.md | 1 + .../compile/render_dom/wrappers/EachBlock.ts | 2 ++ src/runtime/internal/keyed_each.ts | 14 +++++++++----- .../samples/keyed-each-dev-unique/_config.js | 7 +++++++ .../samples/keyed-each-dev-unique/main.svelte | 7 +++++++ 5 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 test/runtime/samples/keyed-each-dev-unique/_config.js create mode 100644 test/runtime/samples/keyed-each-dev-unique/main.svelte diff --git a/CHANGELOG.md b/CHANGELOG.md index 388fe61397..5838dcac80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Fix updating a `` inside an `{#if}` or other block ([#4292](https://github.com/sveltejs/svelte/issues/4292)) * Fix using RxJS observables in `derived` stores ([#4298](https://github.com/sveltejs/svelte/issues/4298)) +* Add dev mode check to disallow duplicate keys in a keyed `{#each}` ([#4301](https://github.com/sveltejs/svelte/issues/4301)) ## 3.17.2 diff --git a/src/compiler/compile/render_dom/wrappers/EachBlock.ts b/src/compiler/compile/render_dom/wrappers/EachBlock.ts index 4928b5a38c..639a8831bd 100644 --- a/src/compiler/compile/render_dom/wrappers/EachBlock.ts +++ b/src/compiler/compile/render_dom/wrappers/EachBlock.ts @@ -374,6 +374,7 @@ export default class EachBlockWrapper extends Wrapper { block.chunks.init.push(b` const ${get_key} = #ctx => ${this.node.key.manipulate(block)}; + ${this.renderer.options.dev && b`@validate_each_keys(#ctx, ${this.vars.each_block_value}, ${this.vars.get_each_context}, ${get_key});`} for (let #i = 0; #i < ${data_length}; #i += 1) { let child_ctx = ${this.vars.get_each_context}(#ctx, ${this.vars.each_block_value}, #i); let key = ${get_key}(child_ctx); @@ -416,6 +417,7 @@ export default class EachBlockWrapper extends Wrapper { ${this.block.has_outros && b`@group_outros();`} ${this.node.has_animation && b`for (let #i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].r();`} + ${this.renderer.options.dev && b`@validate_each_keys(#ctx, ${this.vars.each_block_value}, ${this.vars.get_each_context}, ${get_key});`} ${iterations} = @update_keyed_each(${iterations}, #dirty, ${get_key}, ${dynamic ? 1 : 0}, #ctx, ${this.vars.each_block_value}, ${lookup}, ${update_mount_node}, ${destroy}, ${create_each_block}, ${update_anchor_node}, ${this.vars.get_each_context}); ${this.node.has_animation && b`for (let #i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].a();`} ${this.block.has_outros && b`@check_outros();`} diff --git a/src/runtime/internal/keyed_each.ts b/src/runtime/internal/keyed_each.ts index 8382a2f76a..b397335c87 100644 --- a/src/runtime/internal/keyed_each.ts +++ b/src/runtime/internal/keyed_each.ts @@ -108,9 +108,13 @@ export function update_keyed_each(old_blocks, dirty, get_key, dynamic, ctx, list return new_blocks; } -export function measure(blocks) { - const rects = {}; - let i = blocks.length; - while (i--) rects[blocks[i].key] = blocks[i].node.getBoundingClientRect(); - return rects; +export function validate_each_keys(ctx, list, get_context, get_key) { + const keys = new Set(); + for (let i = 0; i < list.length; i++) { + const key = get_key(get_context(ctx, list, i)); + if (keys.has(key)) { + throw new Error(`Cannot have duplicate keys in a keyed each`); + } + keys.add(key); + } } diff --git a/test/runtime/samples/keyed-each-dev-unique/_config.js b/test/runtime/samples/keyed-each-dev-unique/_config.js new file mode 100644 index 0000000000..8f46af9d52 --- /dev/null +++ b/test/runtime/samples/keyed-each-dev-unique/_config.js @@ -0,0 +1,7 @@ +export default { + compileOptions: { + dev: true + }, + + error: `Cannot have duplicate keys in a keyed each` +}; diff --git a/test/runtime/samples/keyed-each-dev-unique/main.svelte b/test/runtime/samples/keyed-each-dev-unique/main.svelte new file mode 100644 index 0000000000..870e7beaa1 --- /dev/null +++ b/test/runtime/samples/keyed-each-dev-unique/main.svelte @@ -0,0 +1,7 @@ + + +{#each array as item (item)} + {item} +{/each} From 3ba4f8abcad48b0380c9046d0f9230c1fc330964 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Wed, 22 Jan 2020 20:11:45 -0500 Subject: [PATCH 17/37] site: fix a couple absolute links --- site/content/blog/2019-04-20-write-less-code.md | 2 +- site/content/docs/01-component-format.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/site/content/blog/2019-04-20-write-less-code.md b/site/content/blog/2019-04-20-write-less-code.md index d6c8b77193..d56c5312e8 100644 --- a/site/content/blog/2019-04-20-write-less-code.md +++ b/site/content/blog/2019-04-20-write-less-code.md @@ -159,6 +159,6 @@ In Vue, meanwhile, we have a default export with a `data` function that returns ## Death to boilerplate -These are just some of the ways that Svelte helps you build user interfaces with a minimum of fuss. There are plenty of others — for example, [reactive declarations](https://svelte.dev/tutorial/reactive-declarations) essentially do the work of React's `useMemo`, `useCallback` and `useEffect` without the boilerplate (or indeed the garbage collection overhead of creating inline functions and arrays on each state change). +These are just some of the ways that Svelte helps you build user interfaces with a minimum of fuss. There are plenty of others — for example, [reactive declarations](tutorial/reactive-declarations) essentially do the work of React's `useMemo`, `useCallback` and `useEffect` without the boilerplate (or indeed the garbage collection overhead of creating inline functions and arrays on each state change). How? By choosing a different set of constraints. Because [Svelte is a compiler](blog/frameworks-without-the-framework), we're not bound to the peculiarities of JavaScript: we can *design* a component authoring experience, rather than having to fit it around the semantics of the language. Paradoxically, this results in *more* idiomatic code — for example using variables naturally rather than via proxies or hooks — while delivering significantly more performant apps. diff --git a/site/content/docs/01-component-format.md b/site/content/docs/01-component-format.md index e541292e13..026a2da5b3 100644 --- a/site/content/docs/01-component-format.md +++ b/site/content/docs/01-component-format.md @@ -197,7 +197,7 @@ You can `export` bindings from this block, and they will become exports of the c You cannot `export default`, since the default export is the component itself. -> Variables defined in `module` scripts are not reactive — reassigning them will not trigger a rerender even though the variable itself will update. For values shared between multiple components, consider using a [store](https://svelte.dev/docs#svelte_store). +> Variables defined in `module` scripts are not reactive — reassigning them will not trigger a rerender even though the variable itself will update. For values shared between multiple components, consider using a [store](docs#svelte_store). ```html + +

{$store}

From 9cdf0da160d906948a4b4bbaebaf856751906177 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Thu, 23 Jan 2020 10:44:35 -0500 Subject: [PATCH 22/37] fix changelog --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ff7d7603b..44253ce59c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,11 @@ # Svelte changelog -## 3.17.3 +## Unreleased * Make autosubscribing to a nullish store a no-op ([#2181](https://github.com/sveltejs/svelte/issues/2181)) + +## 3.17.3 + * Fix updating a `` inside an `{#if}` or other block ([#4292](https://github.com/sveltejs/svelte/issues/4292)) * Fix using RxJS observables in `derived` stores ([#4298](https://github.com/sveltejs/svelte/issues/4298)) * Add dev mode check to disallow duplicate keys in a keyed `{#each}` ([#4301](https://github.com/sveltejs/svelte/issues/4301)) From bf006a43e5fd2339adb6cfca1a322fdedcbf48da Mon Sep 17 00:00:00 2001 From: Dave Lunny <4298089+himynameisdave@users.noreply.github.com> Date: Thu, 23 Jan 2020 17:51:29 -0800 Subject: [PATCH 23/37] [Internals] Improve unit tests (#4262) --- .github/workflows/ci.yml | 6 ++++++ src/compiler/compile/utils/__test__.ts | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2488902b24..14824ecdfa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,3 +23,9 @@ jobs: - uses: actions/checkout@v1 - uses: actions/setup-node@v1 - run: 'npm i && npm run lint' + Unit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: actions/setup-node@v1 + - run: 'npm i && npm run test:unit' diff --git a/src/compiler/compile/utils/__test__.ts b/src/compiler/compile/utils/__test__.ts index 60ad681b47..7777bc6afb 100644 --- a/src/compiler/compile/utils/__test__.ts +++ b/src/compiler/compile/utils/__test__.ts @@ -10,7 +10,7 @@ describe('get_name_from_filename', () => { assert.equal(get_name_from_filename('path/to/Widget/index.svelte'), 'Widget'); }); - it('handles unusual filenames', () => { - assert.equal(get_name_from_filename('path/to/[...parts].svelte'), 'Parts'); + it('handles Windows filenames', () => { + assert.equal(get_name_from_filename('path\\to\\Widget.svelte'), 'Widget'); }); }); From 8e245dc30ea971da953b1806ed980044546a03a9 Mon Sep 17 00:00:00 2001 From: David Kondrad Date: Sat, 25 Jan 2020 12:21:55 -0500 Subject: [PATCH 24/37] Internals: Scheduler: Fix infinite loop in flush (#4316) --- src/runtime/internal/scheduler.ts | 7 ++++--- .../lifecycle-onmount-infinite-loop/Child.svelte | 1 + .../lifecycle-onmount-infinite-loop/_config.js | 6 ++++++ .../lifecycle-onmount-infinite-loop/main.svelte | 16 ++++++++++++++++ 4 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 test/runtime/samples/lifecycle-onmount-infinite-loop/Child.svelte create mode 100644 test/runtime/samples/lifecycle-onmount-infinite-loop/_config.js create mode 100644 test/runtime/samples/lifecycle-onmount-infinite-loop/main.svelte diff --git a/src/runtime/internal/scheduler.ts b/src/runtime/internal/scheduler.ts index 04c1264ab1..1ce255b217 100644 --- a/src/runtime/internal/scheduler.ts +++ b/src/runtime/internal/scheduler.ts @@ -31,8 +31,8 @@ export function add_flush_callback(fn) { flush_callbacks.push(fn); } +const seen_callbacks = new Set(); export function flush() { - const seen_callbacks = new Set(); do { // first, call beforeUpdate functions @@ -52,10 +52,10 @@ export function flush() { const callback = render_callbacks[i]; if (!seen_callbacks.has(callback)) { - callback(); - // ...so guard against infinite loops seen_callbacks.add(callback); + + callback(); } } @@ -67,6 +67,7 @@ export function flush() { } update_scheduled = false; + seen_callbacks.clear(); } function update($$) { diff --git a/test/runtime/samples/lifecycle-onmount-infinite-loop/Child.svelte b/test/runtime/samples/lifecycle-onmount-infinite-loop/Child.svelte new file mode 100644 index 0000000000..ef16875b64 --- /dev/null +++ b/test/runtime/samples/lifecycle-onmount-infinite-loop/Child.svelte @@ -0,0 +1 @@ +Child diff --git a/test/runtime/samples/lifecycle-onmount-infinite-loop/_config.js b/test/runtime/samples/lifecycle-onmount-infinite-loop/_config.js new file mode 100644 index 0000000000..a76a2686ac --- /dev/null +++ b/test/runtime/samples/lifecycle-onmount-infinite-loop/_config.js @@ -0,0 +1,6 @@ +export default { + test({ assert, component }) { + const { count } = component; + assert.deepEqual(count, 1); + } +}; diff --git a/test/runtime/samples/lifecycle-onmount-infinite-loop/main.svelte b/test/runtime/samples/lifecycle-onmount-infinite-loop/main.svelte new file mode 100644 index 0000000000..1fa4263e39 --- /dev/null +++ b/test/runtime/samples/lifecycle-onmount-infinite-loop/main.svelte @@ -0,0 +1,16 @@ + + +
From 5d4408ba23655c54e8b921e27461273dc5e63610 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Sat, 25 Jan 2020 12:23:50 -0500 Subject: [PATCH 25/37] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44253ce59c..b5784f4148 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +* Fix infinite loop when instantiating another component during `onMount` ([#3218](https://github.com/sveltejs/svelte/issues/3218)) * Make autosubscribing to a nullish store a no-op ([#2181](https://github.com/sveltejs/svelte/issues/2181)) ## 3.17.3 From efe8ab9ca569b8cc706cb4c231b39c160ba479e3 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Sat, 25 Jan 2020 12:26:19 -0500 Subject: [PATCH 26/37] -> v3.18.0 --- CHANGELOG.md | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5784f4148..ed6feef9d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Svelte changelog -## Unreleased +## 3.18.0 * Fix infinite loop when instantiating another component during `onMount` ([#3218](https://github.com/sveltejs/svelte/issues/3218)) * Make autosubscribing to a nullish store a no-op ([#2181](https://github.com/sveltejs/svelte/issues/2181)) diff --git a/package-lock.json b/package-lock.json index cfbc71cdd2..6300fd40ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.17.3", + "version": "3.18.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 2b0b09299b..791327d3f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.17.3", + "version": "3.18.0", "description": "Cybernetically enhanced web apps", "module": "index.mjs", "main": "index", From 3eb298fd28ff68f782f632a9029b430745cb0c2b Mon Sep 17 00:00:00 2001 From: Conduitry Date: Mon, 27 Jan 2020 14:02:30 -0500 Subject: [PATCH 27/37] fix code generation with adjacent inline and block comments (#4327) --- CHANGELOG.md | 4 ++++ package-lock.json | 6 +++--- package.json | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed6feef9d4..217651e20b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Svelte changelog +## Unreleased + +* Fix code generation error with adjacent inline and block comments ([#4312](https://github.com/sveltejs/svelte/issues/4312)) + ## 3.18.0 * Fix infinite loop when instantiating another component during `onMount` ([#3218](https://github.com/sveltejs/svelte/issues/3218)) diff --git a/package-lock.json b/package-lock.json index 6300fd40ac..5ffb1ba0da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -597,9 +597,9 @@ "dev": true }, "code-red": { - "version": "0.0.31", - "resolved": "https://registry.npmjs.org/code-red/-/code-red-0.0.31.tgz", - "integrity": "sha512-7Gf3vm8pDbs+H/hKsaqOZe0xKlE9Neah12GCfs7qun3fBUaOXwexAMjn0Eo9cvJJvhRMaL0jgPiY9ZGLTWoe8A==", + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/code-red/-/code-red-0.0.32.tgz", + "integrity": "sha512-mE+EZc2vJ4HxiejW5S2CvcVDKtopFEmrqAd9DTBDLCNjLgxekPP8wLi/ZiwDTwZwwW3dzeetaubLaMlIvkhVNw==", "dev": true, "requires": { "acorn": "^7.1.0", diff --git a/package.json b/package.json index 791327d3f0..3b43d6fa46 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "acorn": "^7.1.0", "agadoo": "^1.1.0", "c8": "^5.0.1", - "code-red": "0.0.31", + "code-red": "0.0.32", "codecov": "^3.5.0", "css-tree": "1.0.0-alpha22", "eslint": "^6.3.0", From 43f19d93a9fe33ec71856d7b080aca1425460a3e Mon Sep 17 00:00:00 2001 From: Wolfr Date: Mon, 27 Jan 2020 20:28:06 +0100 Subject: [PATCH 28/37] site: Improve open graph images for website (#4328) --- site/src/template.html | 1 + 1 file changed, 1 insertion(+) diff --git a/site/src/template.html b/site/src/template.html index dc930a7f72..344684d766 100644 --- a/site/src/template.html +++ b/site/src/template.html @@ -15,6 +15,7 @@ +