From d78f32ee5a65c6c7cdbaa21eb28d164a1c3a3f92 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Fri, 7 Feb 2020 19:15:40 -0500 Subject: [PATCH 01/11] fix arrow function precedence (#4385) * update code-red * fix code-red usage * update changelog --- CHANGELOG.md | 1 + package-lock.json | 6 +++--- package.json | 2 +- src/compiler/compile/render_dom/Renderer.ts | 2 +- src/compiler/compile/render_dom/index.ts | 4 ++-- src/compiler/compile/render_dom/invalidate.ts | 2 +- src/compiler/compile/render_dom/wrappers/AwaitBlock.ts | 4 ++-- src/compiler/compile/render_dom/wrappers/Element/Binding.ts | 2 +- src/compiler/compile/render_dom/wrappers/Element/index.ts | 4 ++-- src/compiler/compile/render_dom/wrappers/MustacheTag.ts | 2 +- src/compiler/compile/render_dom/wrappers/RawMustacheTag.ts | 2 +- src/compiler/compile/render_ssr/handlers/AwaitBlock.ts | 4 ++-- src/compiler/compile/render_ssr/handlers/Element.ts | 2 +- src/compiler/compile/render_ssr/handlers/Head.ts | 2 +- src/compiler/compile/render_ssr/handlers/Title.ts | 2 +- 15 files changed, 21 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 359ab910f5..a906065883 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Fix binding to module-level variables ([#4086](https://github.com/sveltejs/svelte/issues/4086)) * Disallow attribute/prop names from matching two-way-bound names or `{shorthand}` attribute/prop names ([#4325](https://github.com/sveltejs/svelte/issues/4325)) +* Fix code generation error with precedence of arrow functions ([#4384](https://github.com/sveltejs/svelte/issues/4384)) ## 3.18.1 diff --git a/package-lock.json b/package-lock.json index 2872c1b8dd..82af35d13f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -597,9 +597,9 @@ "dev": true }, "code-red": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/code-red/-/code-red-0.0.32.tgz", - "integrity": "sha512-mE+EZc2vJ4HxiejW5S2CvcVDKtopFEmrqAd9DTBDLCNjLgxekPP8wLi/ZiwDTwZwwW3dzeetaubLaMlIvkhVNw==", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/code-red/-/code-red-0.1.1.tgz", + "integrity": "sha512-2pEIyya4fxI9HxQcrl9dJl20+9He1nLeja50zkf7gK2OTTV9NpD3CgA74gzXnPqWv2zJpa6uXNSaE0haOCpOsw==", "dev": true, "requires": { "acorn": "^7.1.0", diff --git a/package.json b/package.json index ff8b819668..4f7acb0fd7 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.32", + "code-red": "0.1.1", "codecov": "^3.5.0", "css-tree": "1.0.0-alpha22", "eslint": "^6.3.0", diff --git a/src/compiler/compile/render_dom/Renderer.ts b/src/compiler/compile/render_dom/Renderer.ts index 8fa36a9120..fbb0c76d7d 100644 --- a/src/compiler/compile/render_dom/Renderer.ts +++ b/src/compiler/compile/render_dom/Renderer.ts @@ -197,7 +197,7 @@ export default class Renderer { return filtered .map(n => x`$$invalidate(${this.context_lookup.get(n).index}, ${n})`) - .reduce((lhs, rhs) => x`${lhs}, ${rhs}}`); + .reduce((lhs, rhs) => x`${lhs}, ${rhs}`); } dirty(names, is_reactive_declaration = false): Expression { diff --git a/src/compiler/compile/render_dom/index.ts b/src/compiler/compile/render_dom/index.ts index 26fa4a70f8..a7a55b8843 100644 --- a/src/compiler/compile/render_dom/index.ts +++ b/src/compiler/compile/render_dom/index.ts @@ -403,9 +403,9 @@ export default function dom( ${set && b`$$self.$set = ${set};`} - ${capture_state && x`$$self.$capture_state = ${capture_state};`} + ${capture_state && b`$$self.$capture_state = ${capture_state};`} - ${inject_state && x`$$self.$inject_state = ${inject_state};`} + ${inject_state && b`$$self.$inject_state = ${inject_state};`} ${injected.map(name => b`let ${name};`)} diff --git a/src/compiler/compile/render_dom/invalidate.ts b/src/compiler/compile/render_dom/invalidate.ts index 65fb73afc3..dc28501863 100644 --- a/src/compiler/compile/render_dom/invalidate.ts +++ b/src/compiler/compile/render_dom/invalidate.ts @@ -67,7 +67,7 @@ export function invalidate(renderer: Renderer, scope: Scope, node: Node, names: if (head.subscribable && head.reassigned) { const subscribe = `$$subscribe_${head.name}`; - invalidate = x`${subscribe}(${invalidate})}`; + invalidate = x`${subscribe}(${invalidate})`; } return invalidate; diff --git a/src/compiler/compile/render_dom/wrappers/AwaitBlock.ts b/src/compiler/compile/render_dom/wrappers/AwaitBlock.ts index 25c5312e65..12b7fc2cef 100644 --- a/src/compiler/compile/render_dom/wrappers/AwaitBlock.ts +++ b/src/compiler/compile/render_dom/wrappers/AwaitBlock.ts @@ -205,7 +205,7 @@ export default class AwaitBlockWrapper extends Wrapper { } else { const #child_ctx = #ctx.slice(); - ${this.node.value && x`#child_ctx[${value_index}] = ${info}.resolved;`} + ${this.node.value && b`#child_ctx[${value_index}] = ${info}.resolved;`} ${info}.block.p(#child_ctx, #dirty); } `); @@ -219,7 +219,7 @@ export default class AwaitBlockWrapper extends Wrapper { block.chunks.update.push(b` { const #child_ctx = #ctx.slice(); - ${this.node.value && x`#child_ctx[${value_index}] = ${info}.resolved;`} + ${this.node.value && b`#child_ctx[${value_index}] = ${info}.resolved;`} ${info}.block.p(#child_ctx, #dirty); } `); diff --git a/src/compiler/compile/render_dom/wrappers/Element/Binding.ts b/src/compiler/compile/render_dom/wrappers/Element/Binding.ts index 7244d9203d..eca6e9d325 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/Binding.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/Binding.ts @@ -97,7 +97,7 @@ export default class BindingWrapper { const type = parent.node.get_static_attribute_value('type'); if (type === null || type === "" || type === "text" || type === "email" || type === "password") { - update_conditions.push(x`(${parent.var}.${this.node.name} !== ${this.snippet})`); + update_conditions.push(x`${parent.var}.${this.node.name} !== ${this.snippet}`); } } diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts index ef33022402..8894308fdf 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts @@ -415,7 +415,7 @@ export default class ElementWrapper extends Wrapper { const is = this.attributes.find(attr => attr.node.name === 'is'); if (is) { - return x`@element_is("${name}", ${is.render_chunks(block).reduce((lhs, rhs) => x`${lhs} + ${rhs}`)});`; + return x`@element_is("${name}", ${is.render_chunks(block).reduce((lhs, rhs) => x`${lhs} + ${rhs}`)})`; } return x`@element("${name}")`; @@ -632,7 +632,7 @@ export default class ElementWrapper extends Wrapper { add_this_binding(block: Block, this_binding: Binding) { const { renderer } = this; - + renderer.component.has_reactive_assignments = true; const binding_callback = bind_this(renderer.component, block, this_binding.node, this.var); diff --git a/src/compiler/compile/render_dom/wrappers/MustacheTag.ts b/src/compiler/compile/render_dom/wrappers/MustacheTag.ts index b5ac6505f8..ea6634ec80 100644 --- a/src/compiler/compile/render_dom/wrappers/MustacheTag.ts +++ b/src/compiler/compile/render_dom/wrappers/MustacheTag.ts @@ -17,7 +17,7 @@ export default class MustacheTagWrapper extends Tag { render(block: Block, parent_node: Identifier, parent_nodes: Identifier) { const { init } = this.rename_this_method( block, - value => x`@set_data(${this.var}, ${value});` + value => x`@set_data(${this.var}, ${value})` ); block.add_element( diff --git a/src/compiler/compile/render_dom/wrappers/RawMustacheTag.ts b/src/compiler/compile/render_dom/wrappers/RawMustacheTag.ts index c6ddde5da2..3b13e6c68a 100644 --- a/src/compiler/compile/render_dom/wrappers/RawMustacheTag.ts +++ b/src/compiler/compile/render_dom/wrappers/RawMustacheTag.ts @@ -48,7 +48,7 @@ export default class RawMustacheTagWrapper extends Tag { const { init } = this.rename_this_method( block, - content => x`${html_tag}.p(${content});` + content => x`${html_tag}.p(${content})` ); const update_anchor = in_head ? 'null' : needs_anchor ? html_anchor : this.next ? this.next.var : 'null'; diff --git a/src/compiler/compile/render_ssr/handlers/AwaitBlock.ts b/src/compiler/compile/render_ssr/handlers/AwaitBlock.ts index 6dd38bfd2e..f637826280 100644 --- a/src/compiler/compile/render_ssr/handlers/AwaitBlock.ts +++ b/src/compiler/compile/render_ssr/handlers/AwaitBlock.ts @@ -12,9 +12,9 @@ export default function(node: AwaitBlock, renderer: Renderer, options: RenderOpt const then = renderer.pop(); renderer.add_expression(x` - (function(__value) { + function(__value) { if (@is_promise(__value)) return ${pending}; return (function(${node.value}) { return ${then}; }(__value)); - }(${node.expression.node})) + }(${node.expression.node}) `); } diff --git a/src/compiler/compile/render_ssr/handlers/Element.ts b/src/compiler/compile/render_ssr/handlers/Element.ts index e0982a0415..6e6a61974a 100644 --- a/src/compiler/compile/render_ssr/handlers/Element.ts +++ b/src/compiler/compile/render_ssr/handlers/Element.ts @@ -65,7 +65,7 @@ export default function(node: Element, renderer: Renderer, options: RenderOption } }); - renderer.add_expression(x`@spread([${args}], ${class_expression});`); + renderer.add_expression(x`@spread([${args}], ${class_expression})`); } else { let add_class_attribute = !!class_expression; node.attributes.forEach(attribute => { diff --git a/src/compiler/compile/render_ssr/handlers/Head.ts b/src/compiler/compile/render_ssr/handlers/Head.ts index 456e5c279b..cf1e6bd555 100644 --- a/src/compiler/compile/render_ssr/handlers/Head.ts +++ b/src/compiler/compile/render_ssr/handlers/Head.ts @@ -12,5 +12,5 @@ export default function(node: Head, renderer: Renderer, options: RenderOptions) renderer.render(node.children, head_options); const result = renderer.pop(); - renderer.add_expression(x`($$result.head += ${result}, "")`); + renderer.add_expression(x`$$result.head += ${result}, ""`); } diff --git a/src/compiler/compile/render_ssr/handlers/Title.ts b/src/compiler/compile/render_ssr/handlers/Title.ts index f1f458ed5b..c4c7897185 100644 --- a/src/compiler/compile/render_ssr/handlers/Title.ts +++ b/src/compiler/compile/render_ssr/handlers/Title.ts @@ -12,5 +12,5 @@ export default function(node: Title, renderer: Renderer, options: RenderOptions) renderer.add_string(``); const result = renderer.pop(); - renderer.add_expression(x`($$result.title = ${result}, "")`); + renderer.add_expression(x`$$result.title = ${result}, ""`); } From 12a9ff2ac15562647722ffe095e601cf14f822f8 Mon Sep 17 00:00:00 2001 From: Christian Kaisermann Date: Fri, 7 Feb 2020 21:25:35 -0300 Subject: [PATCH 02/11] chore: fix .eslintignore and remove .prettierrc (#4378) --- .eslintignore | 3 +++ .prettierrc | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 .prettierrc diff --git a/.eslintignore b/.eslintignore index bfe7b1fa95..b5cb03ae6e 100644 --- a/.eslintignore +++ b/.eslintignore @@ -4,6 +4,9 @@ _output test/*/samples/*/output.js node_modules +# automatically generated +internal_exports.ts + # output files animate/*.js esing/*.js diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 81451039fa..0000000000 --- a/.prettierrc +++ /dev/null @@ -1,3 +0,0 @@ -useTabs: true -singleQuote: true -trailingComma: es5 From a40f4ad5e315bab5569d98c48a818cf3bbadc3a3 Mon Sep 17 00:00:00 2001 From: Christian Heine <40512168+christianheine@users.noreply.github.com> Date: Sat, 8 Feb 2020 08:27:28 +0800 Subject: [PATCH 03/11] npm audit fix related to https-proxy-agent (#4359) --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 82af35d13f..73f49c08e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1682,9 +1682,9 @@ } }, "https-proxy-agent": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.2.tgz", - "integrity": "sha512-c8Ndjc9Bkpfx/vCJueCPy0jlP4ccCCSNDp8xwCZzPjKJUm+B+u9WX2x98Qx4n1PiMNTWo3D7KK5ifNV/yJyRzg==", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", "dev": true, "requires": { "agent-base": "^4.3.0", From 4b04b16fe060cc00224bbff93ad9c345c390132e Mon Sep 17 00:00:00 2001 From: Jordan Gensler Date: Sat, 8 Feb 2020 06:22:38 -0800 Subject: [PATCH 04/11] avoid using .shift() in flush() (#4356) --- src/runtime/internal/scheduler.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/runtime/internal/scheduler.ts b/src/runtime/internal/scheduler.ts index 1ce255b217..b0db71035a 100644 --- a/src/runtime/internal/scheduler.ts +++ b/src/runtime/internal/scheduler.ts @@ -31,18 +31,23 @@ export function add_flush_callback(fn) { flush_callbacks.push(fn); } +let flushing = false; const seen_callbacks = new Set(); export function flush() { + if (flushing) return; + flushing = true; do { // first, call beforeUpdate functions // and update components - while (dirty_components.length) { - const component = dirty_components.shift(); + for (let i = 0; i < dirty_components.length; i += 1) { + const component = dirty_components[i]; set_current_component(component); update(component.$$); } + dirty_components.length = 0; + while (binding_callbacks.length) binding_callbacks.pop()(); // then, once components are updated, call @@ -67,6 +72,7 @@ export function flush() { } update_scheduled = false; + flushing = false; seen_callbacks.clear(); } From cb67a53e51b4859debefb720aceb4d3255a76967 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Sat, 8 Feb 2020 09:24:13 -0500 Subject: [PATCH 05/11] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a906065883..899e0d5b83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Fix binding to module-level variables ([#4086](https://github.com/sveltejs/svelte/issues/4086)) * Disallow attribute/prop names from matching two-way-bound names or `{shorthand}` attribute/prop names ([#4325](https://github.com/sveltejs/svelte/issues/4325)) +* Improve performance of `flush()` by not using `.shift()` ([#4356](https://github.com/sveltejs/svelte/pull/4356)) * Fix code generation error with precedence of arrow functions ([#4384](https://github.com/sveltejs/svelte/issues/4384)) ## 3.18.1 From 2195832ecce1bb179162806e983a8c29412fead9 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Sun, 9 Feb 2020 12:59:43 +0800 Subject: [PATCH 06/11] better error messages for else, elseif, then, catch with unclosed tag (#4136) --- CHANGELOG.md | 1 + src/compiler/parse/state/mustache.ts | 20 +- src/compiler/parse/utils/node.ts | 30 +++ .../error-catch-before-closing/error.json | 6 + .../error-catch-before-closing/input.svelte | 4 + .../error-else-before-closing-2/error.json | 6 + .../error-else-before-closing-2/input.svelte | 4 + .../error-else-before-closing-3/error.json | 6 + .../error-else-before-closing-3/input.svelte | 2 + .../error-else-before-closing/error.json | 6 + .../error-else-before-closing/input.svelte | 4 + .../error-else-if-before-closing-2/error.json | 6 + .../input.svelte | 4 + .../error-else-if-before-closing/error.json | 6 + .../error-else-if-before-closing/input.svelte | 4 + .../error-else-if-without-if/error.json | 6 + .../error-else-if-without-if/input.svelte | 4 + .../error-then-before-closing/error.json | 6 + .../error-then-before-closing/input.svelte | 4 + .../no-error-if-before-closing/input.svelte | 19 ++ .../no-error-if-before-closing/output.json | 218 ++++++++++++++++++ 21 files changed, 361 insertions(+), 5 deletions(-) create mode 100644 src/compiler/parse/utils/node.ts create mode 100644 test/parser/samples/error-catch-before-closing/error.json create mode 100644 test/parser/samples/error-catch-before-closing/input.svelte create mode 100644 test/parser/samples/error-else-before-closing-2/error.json create mode 100644 test/parser/samples/error-else-before-closing-2/input.svelte create mode 100644 test/parser/samples/error-else-before-closing-3/error.json create mode 100644 test/parser/samples/error-else-before-closing-3/input.svelte create mode 100644 test/parser/samples/error-else-before-closing/error.json create mode 100644 test/parser/samples/error-else-before-closing/input.svelte create mode 100644 test/parser/samples/error-else-if-before-closing-2/error.json create mode 100644 test/parser/samples/error-else-if-before-closing-2/input.svelte create mode 100644 test/parser/samples/error-else-if-before-closing/error.json create mode 100644 test/parser/samples/error-else-if-before-closing/input.svelte create mode 100644 test/parser/samples/error-else-if-without-if/error.json create mode 100644 test/parser/samples/error-else-if-without-if/input.svelte create mode 100644 test/parser/samples/error-then-before-closing/error.json create mode 100644 test/parser/samples/error-then-before-closing/input.svelte create mode 100644 test/parser/samples/no-error-if-before-closing/input.svelte create mode 100644 test/parser/samples/no-error-if-before-closing/output.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 899e0d5b83..6337653fb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased * Fix binding to module-level variables ([#4086](https://github.com/sveltejs/svelte/issues/4086)) +* Improve parsing error messages when there is a pending unclosed tag ([#4131](https://github.com/sveltejs/svelte/issues/4131)) * Disallow attribute/prop names from matching two-way-bound names or `{shorthand}` attribute/prop names ([#4325](https://github.com/sveltejs/svelte/issues/4325)) * Improve performance of `flush()` by not using `.shift()` ([#4356](https://github.com/sveltejs/svelte/pull/4356)) * Fix code generation error with precedence of arrow functions ([#4384](https://github.com/sveltejs/svelte/issues/4384)) diff --git a/src/compiler/parse/state/mustache.ts b/src/compiler/parse/state/mustache.ts index 140e722b10..e5e365dddf 100644 --- a/src/compiler/parse/state/mustache.ts +++ b/src/compiler/parse/state/mustache.ts @@ -3,6 +3,7 @@ import read_expression from '../read/expression'; import { closing_tag_omitted } from '../utils/html'; import { whitespace } from '../../utils/patterns'; import { trim_start, trim_end } from '../../utils/trim'; +import { to_string } from '../utils/node'; import { Parser } from '../index'; import { TemplateNode } from '../../interfaces'; @@ -106,11 +107,14 @@ export default function mustache(parser: Parser) { // :else if if (parser.eat('if')) { const block = parser.current(); - if (block.type !== 'IfBlock') + if (block.type !== 'IfBlock') { parser.error({ code: `invalid-elseif-placement`, - message: 'Cannot have an {:else if ...} block outside an {#if ...} block' + message: parser.stack.some(block => block.type === 'IfBlock') + ? `Expected to close ${to_string(block)} before seeing {:else if ...} block` + : `Cannot have an {:else if ...} block outside an {#if ...} block` }); + } parser.require_whitespace(); @@ -144,7 +148,9 @@ export default function mustache(parser: Parser) { if (block.type !== 'IfBlock' && block.type !== 'EachBlock') { parser.error({ code: `invalid-else-placement`, - message: 'Cannot have an {:else} block outside an {#if ...} or {#each ...} block' + message: parser.stack.some(block => block.type === 'IfBlock' || block.type === 'EachBlock') + ? `Expected to close ${to_string(block)} before seeing {:else} block` + : `Cannot have an {:else} block outside an {#if ...} or {#each ...} block` }); } @@ -168,14 +174,18 @@ export default function mustache(parser: Parser) { if (block.type !== 'PendingBlock') { parser.error({ code: `invalid-then-placement`, - message: 'Cannot have an {:then} block outside an {#await ...} block' + message: parser.stack.some(block => block.type === 'PendingBlock') + ? `Expected to close ${to_string(block)} before seeing {:then} block` + : `Cannot have an {:then} block outside an {#await ...} block` }); } } else { if (block.type !== 'ThenBlock' && block.type !== 'PendingBlock') { parser.error({ code: `invalid-catch-placement`, - message: 'Cannot have an {:catch} block outside an {#await ...} block' + message: parser.stack.some(block => block.type === 'ThenBlock' || block.type === 'PendingBlock') + ? `Expected to close ${to_string(block)} before seeing {:catch} block` + : `Cannot have an {:catch} block outside an {#await ...} block` }); } } diff --git a/src/compiler/parse/utils/node.ts b/src/compiler/parse/utils/node.ts new file mode 100644 index 0000000000..0d39529b59 --- /dev/null +++ b/src/compiler/parse/utils/node.ts @@ -0,0 +1,30 @@ +import { TemplateNode } from '../../interfaces'; + +export function to_string(node: TemplateNode) { + switch (node.type) { + case 'IfBlock': + return '{#if} block'; + case 'ThenBlock': + return '{:then} block'; + case 'ElseBlock': + return '{:else} block'; + case 'PendingBlock': + case 'AwaitBlock': + return '{#await} block'; + case 'CatchBlock': + return '{:catch} block'; + case 'EachBlock': + return '{#each} block'; + case 'RawMustacheTag': + return '{@html} block'; + case 'DebugTag': + return '{@debug} block'; + case 'Element': + case 'InlineComponent': + case 'Slot': + case 'Title': + return `<${node.name}> tag`; + default: + return node.type; + } +} diff --git a/test/parser/samples/error-catch-before-closing/error.json b/test/parser/samples/error-catch-before-closing/error.json new file mode 100644 index 0000000000..30fbf5fbd5 --- /dev/null +++ b/test/parser/samples/error-catch-before-closing/error.json @@ -0,0 +1,6 @@ +{ + "code": "invalid-catch-placement", + "message": "Expected to close {#each} block before seeing {:catch} block", + "start": { "line": 3, "column": 7, "character": 41 }, + "pos": 41 +} diff --git a/test/parser/samples/error-catch-before-closing/input.svelte b/test/parser/samples/error-catch-before-closing/input.svelte new file mode 100644 index 0000000000..bd771701e2 --- /dev/null +++ b/test/parser/samples/error-catch-before-closing/input.svelte @@ -0,0 +1,4 @@ +{#await true} + {#each foo as bar} +{:catch f} +{/await} \ No newline at end of file diff --git a/test/parser/samples/error-else-before-closing-2/error.json b/test/parser/samples/error-else-before-closing-2/error.json new file mode 100644 index 0000000000..d35be1abf5 --- /dev/null +++ b/test/parser/samples/error-else-before-closing-2/error.json @@ -0,0 +1,6 @@ +{ + "code": "invalid-else-placement", + "message": "Expected to close {#await} block before seeing {:else} block", + "start": { "line": 3, "column": 6, "character": 29 }, + "pos": 29 +} diff --git a/test/parser/samples/error-else-before-closing-2/input.svelte b/test/parser/samples/error-else-before-closing-2/input.svelte new file mode 100644 index 0000000000..f865a9a533 --- /dev/null +++ b/test/parser/samples/error-else-before-closing-2/input.svelte @@ -0,0 +1,4 @@ +{#if true} + {#await p} +{:else} +{/if} \ No newline at end of file diff --git a/test/parser/samples/error-else-before-closing-3/error.json b/test/parser/samples/error-else-before-closing-3/error.json new file mode 100644 index 0000000000..e5ec210c75 --- /dev/null +++ b/test/parser/samples/error-else-before-closing-3/error.json @@ -0,0 +1,6 @@ +{ + "code": "invalid-else-placement", + "message": "Cannot have an {:else} block outside an {#if ...} or {#each ...} block", + "start": { "line": 2, "column": 6, "character": 11 }, + "pos": 11 +} diff --git a/test/parser/samples/error-else-before-closing-3/input.svelte b/test/parser/samples/error-else-before-closing-3/input.svelte new file mode 100644 index 0000000000..fb434f26a3 --- /dev/null +++ b/test/parser/samples/error-else-before-closing-3/input.svelte @@ -0,0 +1,2 @@ +
  • +{:else} \ No newline at end of file diff --git a/test/parser/samples/error-else-before-closing/error.json b/test/parser/samples/error-else-before-closing/error.json new file mode 100644 index 0000000000..5b6e490c0f --- /dev/null +++ b/test/parser/samples/error-else-before-closing/error.json @@ -0,0 +1,6 @@ +{ + "code": "invalid-else-placement", + "message": "Expected to close
  • tag before seeing {:else} block", + "start": { "line": 3, "column": 6, "character": 23 }, + "pos": 23 +} diff --git a/test/parser/samples/error-else-before-closing/input.svelte b/test/parser/samples/error-else-before-closing/input.svelte new file mode 100644 index 0000000000..a22863192d --- /dev/null +++ b/test/parser/samples/error-else-before-closing/input.svelte @@ -0,0 +1,4 @@ +{#if true} +
  • +{:else} +{/if} \ No newline at end of file diff --git a/test/parser/samples/error-else-if-before-closing-2/error.json b/test/parser/samples/error-else-if-before-closing-2/error.json new file mode 100644 index 0000000000..e55f4f11e5 --- /dev/null +++ b/test/parser/samples/error-else-if-before-closing-2/error.json @@ -0,0 +1,6 @@ +{ + "code": "invalid-elseif-placement", + "message": "Expected to close

    tag before seeing {:else if ...} block", + "start": { "line": 3, "column": 9, "character": 25 }, + "pos": 25 +} diff --git a/test/parser/samples/error-else-if-before-closing-2/input.svelte b/test/parser/samples/error-else-if-before-closing-2/input.svelte new file mode 100644 index 0000000000..5ae36df4eb --- /dev/null +++ b/test/parser/samples/error-else-if-before-closing-2/input.svelte @@ -0,0 +1,4 @@ +{#if true} +

    +{:else if false} +{/if} \ No newline at end of file diff --git a/test/parser/samples/error-else-if-before-closing/error.json b/test/parser/samples/error-else-if-before-closing/error.json new file mode 100644 index 0000000000..21d16c72a4 --- /dev/null +++ b/test/parser/samples/error-else-if-before-closing/error.json @@ -0,0 +1,6 @@ +{ + "code": "invalid-elseif-placement", + "message": "Expected to close {#await} block before seeing {:else if ...} block", + "start": { "line": 3, "column": 9, "character": 34 }, + "pos": 34 +} diff --git a/test/parser/samples/error-else-if-before-closing/input.svelte b/test/parser/samples/error-else-if-before-closing/input.svelte new file mode 100644 index 0000000000..486a921a9a --- /dev/null +++ b/test/parser/samples/error-else-if-before-closing/input.svelte @@ -0,0 +1,4 @@ +{#if true} + {#await foo} +{:else if false} +{/if} \ No newline at end of file diff --git a/test/parser/samples/error-else-if-without-if/error.json b/test/parser/samples/error-else-if-without-if/error.json new file mode 100644 index 0000000000..dd69df6772 --- /dev/null +++ b/test/parser/samples/error-else-if-without-if/error.json @@ -0,0 +1,6 @@ +{ + "code": "invalid-elseif-placement", + "message": "Cannot have an {:else if ...} block outside an {#if ...} block", + "start": { "line": 3, "column": 10, "character": 35 }, + "pos": 35 +} diff --git a/test/parser/samples/error-else-if-without-if/input.svelte b/test/parser/samples/error-else-if-without-if/input.svelte new file mode 100644 index 0000000000..06baf33511 --- /dev/null +++ b/test/parser/samples/error-else-if-without-if/input.svelte @@ -0,0 +1,4 @@ +{#await foo} +{:then bar} + {:else if} +{/await} \ No newline at end of file diff --git a/test/parser/samples/error-then-before-closing/error.json b/test/parser/samples/error-then-before-closing/error.json new file mode 100644 index 0000000000..07310c93fa --- /dev/null +++ b/test/parser/samples/error-then-before-closing/error.json @@ -0,0 +1,6 @@ +{ + "code": "invalid-then-placement", + "message": "Expected to close

  • tag before seeing {:then} block", + "start": { "line": 3, "column": 6, "character": 26 }, + "pos": 26 +} diff --git a/test/parser/samples/error-then-before-closing/input.svelte b/test/parser/samples/error-then-before-closing/input.svelte new file mode 100644 index 0000000000..bb0b993bdf --- /dev/null +++ b/test/parser/samples/error-then-before-closing/input.svelte @@ -0,0 +1,4 @@ +{#await true} +
  • +{:then f} +{/await} \ No newline at end of file diff --git a/test/parser/samples/no-error-if-before-closing/input.svelte b/test/parser/samples/no-error-if-before-closing/input.svelte new file mode 100644 index 0000000000..4059e0d4d1 --- /dev/null +++ b/test/parser/samples/no-error-if-before-closing/input.svelte @@ -0,0 +1,19 @@ +{#if true} + +{:else} +{/if} + +{#if true} +
    +{:else} +{/if} + +{#await true} + +{:then f} +{/await} + +{#await true} +
    +{:then f} +{/await} \ No newline at end of file diff --git a/test/parser/samples/no-error-if-before-closing/output.json b/test/parser/samples/no-error-if-before-closing/output.json new file mode 100644 index 0000000000..ad8ccd0f91 --- /dev/null +++ b/test/parser/samples/no-error-if-before-closing/output.json @@ -0,0 +1,218 @@ +{ + "html": { + "start": 0, + "end": 148, + "type": "Fragment", + "children": [ + { + "start": 0, + "end": 33, + "type": "IfBlock", + "expression": { + "type": "Literal", + "start": 5, + "end": 9, + "value": true, + "raw": "true" + }, + "children": [ + { + "start": 12, + "end": 19, + "type": "Element", + "name": "input", + "attributes": [], + "children": [] + } + ], + "else": { + "start": 27, + "end": 28, + "type": "ElseBlock", + "children": [] + } + }, + { + "start": 33, + "end": 35, + "type": "Text", + "raw": "\n\n", + "data": "\n\n" + }, + { + "start": 35, + "end": 65, + "type": "IfBlock", + "expression": { + "type": "Literal", + "start": 40, + "end": 44, + "value": true, + "raw": "true" + }, + "children": [ + { + "start": 47, + "end": 51, + "type": "Element", + "name": "br", + "attributes": [], + "children": [] + } + ], + "else": { + "start": 59, + "end": 60, + "type": "ElseBlock", + "children": [] + } + }, + { + "start": 65, + "end": 67, + "type": "Text", + "raw": "\n\n", + "data": "\n\n" + }, + { + "start": 67, + "end": 108, + "type": "AwaitBlock", + "expression": { + "type": "Literal", + "start": 75, + "end": 79, + "value": true, + "raw": "true" + }, + "value": "f", + "error": null, + "pending": { + "start": 80, + "end": 90, + "type": "PendingBlock", + "children": [ + { + "start": 80, + "end": 82, + "type": "Text", + "raw": "\n\t", + "data": "\n\t" + }, + { + "start": 82, + "end": 89, + "type": "Element", + "name": "input", + "attributes": [], + "children": [] + }, + { + "start": 89, + "end": 90, + "type": "Text", + "raw": "\n", + "data": "\n" + } + ], + "skip": false + }, + "then": { + "start": 90, + "end": 100, + "type": "ThenBlock", + "children": [ + { + "start": 99, + "end": 100, + "type": "Text", + "raw": "\n", + "data": "\n" + } + ], + "skip": false + }, + "catch": { + "start": null, + "end": null, + "type": "CatchBlock", + "children": [], + "skip": true + } + }, + { + "start": 108, + "end": 110, + "type": "Text", + "raw": "\n\n", + "data": "\n\n" + }, + { + "start": 110, + "end": 148, + "type": "AwaitBlock", + "expression": { + "type": "Literal", + "start": 118, + "end": 122, + "value": true, + "raw": "true" + }, + "value": "f", + "error": null, + "pending": { + "start": 123, + "end": 130, + "type": "PendingBlock", + "children": [ + { + "start": 123, + "end": 125, + "type": "Text", + "raw": "\n\t", + "data": "\n\t" + }, + { + "start": 125, + "end": 129, + "type": "Element", + "name": "br", + "attributes": [], + "children": [] + }, + { + "start": 129, + "end": 130, + "type": "Text", + "raw": "\n", + "data": "\n" + } + ], + "skip": false + }, + "then": { + "start": 130, + "end": 140, + "type": "ThenBlock", + "children": [ + { + "start": 139, + "end": 140, + "type": "Text", + "raw": "\n", + "data": "\n" + } + ], + "skip": false + }, + "catch": { + "start": null, + "end": null, + "type": "CatchBlock", + "children": [], + "skip": true + } + } + ] + } +} \ No newline at end of file From f5f489984f49b2442e48e667e9634f1ef90e36d8 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Sun, 9 Feb 2020 00:04:21 -0500 Subject: [PATCH 07/11] fix handling of reserved keywords when parsing (#4390) --- CHANGELOG.md | 2 + src/compiler/parse/index.ts | 4 +- src/compiler/parse/read/context.ts | 22 +++++++++-- src/compiler/parse/read/expression.ts | 30 +-------------- .../action-with-identifier/output.json | 10 +++++ .../attribute-dynamic-boolean/output.json | 10 +++++ .../attribute-dynamic-reserved/input.svelte | 1 - .../attribute-dynamic-reserved/output.json | 37 ------------------- .../samples/attribute-dynamic/output.json | 20 ++++++++++ .../attribute-with-whitespace/output.json | 10 +++++ test/parser/samples/binding/output.json | 10 +++++ .../each-block-destructured/output.json | 20 ++++++++++ .../samples/each-block-else/output.json | 10 +++++ .../samples/each-block-indexed/output.json | 20 ++++++++++ .../samples/each-block-keyed/output.json | 10 +++++ test/parser/samples/each-block/output.json | 10 +++++ .../samples/element-with-mustache/output.json | 10 +++++ test/parser/samples/event-handler/output.json | 10 +++++ test/parser/samples/if-block-else/output.json | 10 +++++ test/parser/samples/if-block/output.json | 10 +++++ .../implicitly-closed-li-block/output.json | 10 +++++ test/parser/samples/refs/output.json | 10 +++++ .../output.json | 10 +++++ .../script-comment-trailing/output.json | 10 +++++ test/parser/samples/script/output.json | 10 +++++ .../space-between-mustaches/output.json | 30 +++++++++++++++ test/parser/samples/spread/output.json | 10 +++++ .../samples/textarea-children/output.json | 10 +++++ .../samples/whitespace-normal/output.json | 10 +++++ test/parser/samples/yield/input.svelte | 1 - test/parser/samples/yield/output.json | 20 ---------- .../_config.js | 5 +++ .../main.svelte | 7 ++++ .../errors.json | 15 ++++++++ .../input.svelte | 3 ++ 35 files changed, 333 insertions(+), 94 deletions(-) delete mode 100644 test/parser/samples/attribute-dynamic-reserved/input.svelte delete mode 100644 test/parser/samples/attribute-dynamic-reserved/output.json delete mode 100644 test/parser/samples/yield/input.svelte delete mode 100644 test/parser/samples/yield/output.json create mode 100644 test/runtime/samples/each-block-destructured-object-reserved-key/_config.js create mode 100644 test/runtime/samples/each-block-destructured-object-reserved-key/main.svelte create mode 100644 test/validator/samples/each-block-invalid-context-destructured-object/errors.json create mode 100644 test/validator/samples/each-block-invalid-context-destructured-object/input.svelte diff --git a/CHANGELOG.md b/CHANGELOG.md index 6337653fb1..16e6484ab3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ * Improve parsing error messages when there is a pending unclosed tag ([#4131](https://github.com/sveltejs/svelte/issues/4131)) * Disallow attribute/prop names from matching two-way-bound names or `{shorthand}` attribute/prop names ([#4325](https://github.com/sveltejs/svelte/issues/4325)) * Improve performance of `flush()` by not using `.shift()` ([#4356](https://github.com/sveltejs/svelte/pull/4356)) +* Permit reserved keywords as destructuring keys in `{#each}` ([#4372](https://github.com/sveltejs/svelte/issues/4372)) +* Disallow reserved keywords in `{expressions}` ([#4372](https://github.com/sveltejs/svelte/issues/4372)) * Fix code generation error with precedence of arrow functions ([#4384](https://github.com/sveltejs/svelte/issues/4384)) ## 3.18.1 diff --git a/src/compiler/parse/index.ts b/src/compiler/parse/index.ts index e552374698..a809eeebeb 100644 --- a/src/compiler/parse/index.ts +++ b/src/compiler/parse/index.ts @@ -141,7 +141,7 @@ export class Parser { return result; } - read_identifier() { + read_identifier(allow_reserved = false) { const start = this.index; let i = this.index; @@ -160,7 +160,7 @@ export class Parser { const identifier = this.template.slice(this.index, this.index = i); - if (reserved.has(identifier)) { + if (!allow_reserved && reserved.has(identifier)) { this.error({ code: `unexpected-reserved-word`, message: `'${identifier}' is a reserved word in JavaScript and cannot be used here` diff --git a/src/compiler/parse/read/context.ts b/src/compiler/parse/read/context.ts index 0db0c676a3..fe666467f8 100644 --- a/src/compiler/parse/read/context.ts +++ b/src/compiler/parse/read/context.ts @@ -1,4 +1,5 @@ import { Parser } from '../index'; +import { reserved } from '../../utils/names'; interface Identifier { start: number; @@ -116,8 +117,11 @@ export default function read_context(parser: Parser) { break; } + // TODO: DRY this out somehow + // We don't know whether we want to allow reserved words until we see whether there's a ':' after it + // Probably ideally we'd use Acorn to do all of this const start = parser.index; - const name = parser.read_identifier(); + const name = parser.read_identifier(true); const key: Identifier = { start, end: parser.index, @@ -126,9 +130,19 @@ export default function read_context(parser: Parser) { }; parser.allow_whitespace(); - const value = parser.eat(':') - ? (parser.allow_whitespace(), read_context(parser)) - : key; + let value: Context; + if (parser.eat(':')) { + parser.allow_whitespace(); + value = read_context(parser); + } else { + if (reserved.has(name)) { + parser.error({ + code: `unexpected-reserved-word`, + message: `'${name}' is a reserved word in JavaScript and cannot be used here` + }, start); + } + value = key; + } const property: Property = { start, diff --git a/src/compiler/parse/read/expression.ts b/src/compiler/parse/read/expression.ts index c8e955b719..f0f4e15c7a 100644 --- a/src/compiler/parse/read/expression.ts +++ b/src/compiler/parse/read/expression.ts @@ -1,37 +1,9 @@ import { parse_expression_at } from '../acorn'; import { Parser } from '../index'; -import { Identifier, Node, SimpleLiteral } from 'estree'; +import { Node } from 'estree'; import { whitespace } from '../../utils/patterns'; -const literals = new Map([['true', true], ['false', false], ['null', null]]); - export default function read_expression(parser: Parser): Node { - const start = parser.index; - - const name = parser.read_until(/\s*}/); - if (name && /^[a-z]+$/.test(name)) { - const end = start + name.length; - - if (literals.has(name)) { - return { - type: 'Literal', - start, - end, - value: literals.get(name), - raw: name, - } as SimpleLiteral; - } - - return { - type: 'Identifier', - start, - end: start + name.length, - name, - } as Identifier; - } - - parser.index = start; - try { const node = parse_expression_at(parser.template, parser.index); diff --git a/test/parser/samples/action-with-identifier/output.json b/test/parser/samples/action-with-identifier/output.json index 435aee4444..9ae0011c52 100644 --- a/test/parser/samples/action-with-identifier/output.json +++ b/test/parser/samples/action-with-identifier/output.json @@ -20,6 +20,16 @@ "type": "Identifier", "start": 20, "end": 27, + "loc": { + "start": { + "line": 1, + "column": 20 + }, + "end": { + "line": 1, + "column": 27 + } + }, "name": "message" } } diff --git a/test/parser/samples/attribute-dynamic-boolean/output.json b/test/parser/samples/attribute-dynamic-boolean/output.json index 478144152a..ab6b3927ce 100644 --- a/test/parser/samples/attribute-dynamic-boolean/output.json +++ b/test/parser/samples/attribute-dynamic-boolean/output.json @@ -24,6 +24,16 @@ "type": "Identifier", "start": 20, "end": 28, + "loc": { + "start": { + "line": 1, + "column": 20 + }, + "end": { + "line": 1, + "column": 28 + } + }, "name": "readonly" } } diff --git a/test/parser/samples/attribute-dynamic-reserved/input.svelte b/test/parser/samples/attribute-dynamic-reserved/input.svelte deleted file mode 100644 index d973a9dea0..0000000000 --- a/test/parser/samples/attribute-dynamic-reserved/input.svelte +++ /dev/null @@ -1 +0,0 @@ -
    \ No newline at end of file diff --git a/test/parser/samples/attribute-dynamic-reserved/output.json b/test/parser/samples/attribute-dynamic-reserved/output.json deleted file mode 100644 index 22be3c9087..0000000000 --- a/test/parser/samples/attribute-dynamic-reserved/output.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "html": { - "start": 0, - "end": 25, - "type": "Fragment", - "children": [ - { - "start": 0, - "end": 25, - "type": "Element", - "name": "div", - "attributes": [ - { - "start": 5, - "end": 18, - "type": "Attribute", - "name": "class", - "value": [ - { - "start": 11, - "end": 18, - "type": "MustacheTag", - "expression": { - "type": "Identifier", - "start": 12, - "end": 17, - "name": "class" - } - } - ] - } - ], - "children": [] - } - ] - } -} \ No newline at end of file diff --git a/test/parser/samples/attribute-dynamic/output.json b/test/parser/samples/attribute-dynamic/output.json index a48f376876..28b9da1dea 100644 --- a/test/parser/samples/attribute-dynamic/output.json +++ b/test/parser/samples/attribute-dynamic/output.json @@ -31,6 +31,16 @@ "type": "Identifier", "start": 20, "end": 25, + "loc": { + "start": { + "line": 1, + "column": 20 + }, + "end": { + "line": 1, + "column": 25 + } + }, "name": "color" } }, @@ -53,6 +63,16 @@ "type": "Identifier", "start": 30, "end": 35, + "loc": { + "start": { + "line": 1, + "column": 30 + }, + "end": { + "line": 1, + "column": 35 + } + }, "name": "color" } } diff --git a/test/parser/samples/attribute-with-whitespace/output.json b/test/parser/samples/attribute-with-whitespace/output.json index 11d5aa5e06..edf05ca32e 100644 --- a/test/parser/samples/attribute-with-whitespace/output.json +++ b/test/parser/samples/attribute-with-whitespace/output.json @@ -20,6 +20,16 @@ "type": "Identifier", "start": 19, "end": 22, + "loc": { + "start": { + "line": 1, + "column": 19 + }, + "end": { + "line": 1, + "column": 22 + } + }, "name": "foo" } } diff --git a/test/parser/samples/binding/output.json b/test/parser/samples/binding/output.json index 558215f41a..c980b7f3eb 100644 --- a/test/parser/samples/binding/output.json +++ b/test/parser/samples/binding/output.json @@ -27,6 +27,16 @@ "type": "Identifier", "start": 50, "end": 54, + "loc": { + "start": { + "line": 5, + "column": 19 + }, + "end": { + "line": 5, + "column": 23 + } + }, "name": "name" } } diff --git a/test/parser/samples/each-block-destructured/output.json b/test/parser/samples/each-block-destructured/output.json index bafde1324d..425c609a2c 100644 --- a/test/parser/samples/each-block-destructured/output.json +++ b/test/parser/samples/each-block-destructured/output.json @@ -40,6 +40,16 @@ "type": "Identifier", "start": 46, "end": 49, + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 2, + "column": 8 + } + }, "name": "key" } }, @@ -58,6 +68,16 @@ "type": "Identifier", "start": 53, "end": 58, + "loc": { + "start": { + "line": 2, + "column": 12 + }, + "end": { + "line": 2, + "column": 17 + } + }, "name": "value" } } diff --git a/test/parser/samples/each-block-else/output.json b/test/parser/samples/each-block-else/output.json index 19b5af19b4..2720ce5292 100644 --- a/test/parser/samples/each-block-else/output.json +++ b/test/parser/samples/each-block-else/output.json @@ -40,6 +40,16 @@ "type": "Identifier", "start": 31, "end": 37, + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 2, + "column": 11 + } + }, "name": "animal" } } diff --git a/test/parser/samples/each-block-indexed/output.json b/test/parser/samples/each-block-indexed/output.json index 68a12ea31c..50f2000a36 100644 --- a/test/parser/samples/each-block-indexed/output.json +++ b/test/parser/samples/each-block-indexed/output.json @@ -40,6 +40,16 @@ "type": "Identifier", "start": 34, "end": 35, + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 2, + "column": 6 + } + }, "name": "i" } }, @@ -58,6 +68,16 @@ "type": "Identifier", "start": 39, "end": 45, + "loc": { + "start": { + "line": 2, + "column": 10 + }, + "end": { + "line": 2, + "column": 16 + } + }, "name": "animal" } } diff --git a/test/parser/samples/each-block-keyed/output.json b/test/parser/samples/each-block-keyed/output.json index 61b5442c0d..7dc8681453 100644 --- a/test/parser/samples/each-block-keyed/output.json +++ b/test/parser/samples/each-block-keyed/output.json @@ -40,6 +40,16 @@ "type": "Identifier", "start": 37, "end": 41, + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 2, + "column": 9 + } + }, "name": "todo" } } diff --git a/test/parser/samples/each-block/output.json b/test/parser/samples/each-block/output.json index 38bc7e1d04..6594fb50a6 100644 --- a/test/parser/samples/each-block/output.json +++ b/test/parser/samples/each-block/output.json @@ -40,6 +40,16 @@ "type": "Identifier", "start": 31, "end": 37, + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 2, + "column": 11 + } + }, "name": "animal" } } diff --git a/test/parser/samples/element-with-mustache/output.json b/test/parser/samples/element-with-mustache/output.json index f7c2e9936b..049e373100 100644 --- a/test/parser/samples/element-with-mustache/output.json +++ b/test/parser/samples/element-with-mustache/output.json @@ -26,6 +26,16 @@ "type": "Identifier", "start": 11, "end": 15, + "loc": { + "start": { + "line": 1, + "column": 11 + }, + "end": { + "line": 1, + "column": 15 + } + }, "name": "name" } }, diff --git a/test/parser/samples/event-handler/output.json b/test/parser/samples/event-handler/output.json index 44bb83de7d..95db9998f4 100644 --- a/test/parser/samples/event-handler/output.json +++ b/test/parser/samples/event-handler/output.json @@ -128,6 +128,16 @@ "type": "Identifier", "start": 68, "end": 75, + "loc": { + "start": { + "line": 3, + "column": 5 + }, + "end": { + "line": 3, + "column": 12 + } + }, "name": "visible" }, "children": [ diff --git a/test/parser/samples/if-block-else/output.json b/test/parser/samples/if-block-else/output.json index 8b22cfa26b..1f2cff3ff9 100644 --- a/test/parser/samples/if-block-else/output.json +++ b/test/parser/samples/if-block-else/output.json @@ -12,6 +12,16 @@ "type": "Identifier", "start": 5, "end": 8, + "loc": { + "start": { + "line": 1, + "column": 5 + }, + "end": { + "line": 1, + "column": 8 + } + }, "name": "foo" }, "children": [ diff --git a/test/parser/samples/if-block/output.json b/test/parser/samples/if-block/output.json index 1714bb7141..ef895a366a 100644 --- a/test/parser/samples/if-block/output.json +++ b/test/parser/samples/if-block/output.json @@ -12,6 +12,16 @@ "type": "Identifier", "start": 5, "end": 8, + "loc": { + "start": { + "line": 1, + "column": 5 + }, + "end": { + "line": 1, + "column": 8 + } + }, "name": "foo" }, "children": [ diff --git a/test/parser/samples/implicitly-closed-li-block/output.json b/test/parser/samples/implicitly-closed-li-block/output.json index 4a75a34223..767f64053c 100644 --- a/test/parser/samples/implicitly-closed-li-block/output.json +++ b/test/parser/samples/implicitly-closed-li-block/output.json @@ -40,6 +40,16 @@ "type": "Literal", "start": 18, "end": 22, + "loc": { + "start": { + "line": 3, + "column": 6 + }, + "end": { + "line": 3, + "column": 10 + } + }, "value": true, "raw": "true" }, diff --git a/test/parser/samples/refs/output.json b/test/parser/samples/refs/output.json index 72a3d7689c..fb94ffd701 100644 --- a/test/parser/samples/refs/output.json +++ b/test/parser/samples/refs/output.json @@ -27,6 +27,16 @@ "type": "Identifier", "start": 49, "end": 52, + "loc": { + "start": { + "line": 5, + "column": 19 + }, + "end": { + "line": 5, + "column": 22 + } + }, "name": "foo" } } diff --git a/test/parser/samples/script-comment-trailing-multiline/output.json b/test/parser/samples/script-comment-trailing-multiline/output.json index be83eeea8b..184a29978d 100644 --- a/test/parser/samples/script-comment-trailing-multiline/output.json +++ b/test/parser/samples/script-comment-trailing-multiline/output.json @@ -33,6 +33,16 @@ "type": "Identifier", "start": 90, "end": 94, + "loc": { + "start": { + "line": 9, + "column": 11 + }, + "end": { + "line": 9, + "column": 15 + } + }, "name": "name" } }, diff --git a/test/parser/samples/script-comment-trailing/output.json b/test/parser/samples/script-comment-trailing/output.json index a2bc00bc00..2aca23f667 100644 --- a/test/parser/samples/script-comment-trailing/output.json +++ b/test/parser/samples/script-comment-trailing/output.json @@ -33,6 +33,16 @@ "type": "Identifier", "start": 79, "end": 83, + "loc": { + "start": { + "line": 7, + "column": 11 + }, + "end": { + "line": 7, + "column": 15 + } + }, "name": "name" } }, diff --git a/test/parser/samples/script/output.json b/test/parser/samples/script/output.json index 00b7073a19..4d0aa9cf36 100644 --- a/test/parser/samples/script/output.json +++ b/test/parser/samples/script/output.json @@ -33,6 +33,16 @@ "type": "Identifier", "start": 52, "end": 56, + "loc": { + "start": { + "line": 5, + "column": 11 + }, + "end": { + "line": 5, + "column": 15 + } + }, "name": "name" } }, diff --git a/test/parser/samples/space-between-mustaches/output.json b/test/parser/samples/space-between-mustaches/output.json index 9a367bd2c1..b89f4d6c83 100644 --- a/test/parser/samples/space-between-mustaches/output.json +++ b/test/parser/samples/space-between-mustaches/output.json @@ -26,6 +26,16 @@ "type": "Identifier", "start": 5, "end": 6, + "loc": { + "start": { + "line": 1, + "column": 5 + }, + "end": { + "line": 1, + "column": 6 + } + }, "name": "a" } }, @@ -44,6 +54,16 @@ "type": "Identifier", "start": 9, "end": 10, + "loc": { + "start": { + "line": 1, + "column": 9 + }, + "end": { + "line": 1, + "column": 10 + } + }, "name": "b" } }, @@ -62,6 +82,16 @@ "type": "Identifier", "start": 15, "end": 16, + "loc": { + "start": { + "line": 1, + "column": 15 + }, + "end": { + "line": 1, + "column": 16 + } + }, "name": "c" } }, diff --git a/test/parser/samples/spread/output.json b/test/parser/samples/spread/output.json index 73a0dc9777..3a5c471905 100644 --- a/test/parser/samples/spread/output.json +++ b/test/parser/samples/spread/output.json @@ -18,6 +18,16 @@ "type": "Identifier", "start": 9, "end": 14, + "loc": { + "start": { + "line": 1, + "column": 9 + }, + "end": { + "line": 1, + "column": 14 + } + }, "name": "props" } } diff --git a/test/parser/samples/textarea-children/output.json b/test/parser/samples/textarea-children/output.json index 90f31f3caf..919fa33fde 100644 --- a/test/parser/samples/textarea-children/output.json +++ b/test/parser/samples/textarea-children/output.json @@ -29,6 +29,16 @@ "type": "Identifier", "start": 41, "end": 44, + "loc": { + "start": { + "line": 2, + "column": 30 + }, + "end": { + "line": 2, + "column": 33 + } + }, "name": "foo" } }, diff --git a/test/parser/samples/whitespace-normal/output.json b/test/parser/samples/whitespace-normal/output.json index acbae7ae17..61eca1329f 100644 --- a/test/parser/samples/whitespace-normal/output.json +++ b/test/parser/samples/whitespace-normal/output.json @@ -33,6 +33,16 @@ "type": "Identifier", "start": 19, "end": 23, + "loc": { + "start": { + "line": 1, + "column": 19 + }, + "end": { + "line": 1, + "column": 23 + } + }, "name": "name" } }, diff --git a/test/parser/samples/yield/input.svelte b/test/parser/samples/yield/input.svelte deleted file mode 100644 index f5e8aad053..0000000000 --- a/test/parser/samples/yield/input.svelte +++ /dev/null @@ -1 +0,0 @@ -{yield} \ No newline at end of file diff --git a/test/parser/samples/yield/output.json b/test/parser/samples/yield/output.json deleted file mode 100644 index b2e4b9430f..0000000000 --- a/test/parser/samples/yield/output.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "html": { - "start": 0, - "end": 7, - "type": "Fragment", - "children": [ - { - "start": 0, - "end": 7, - "type": "MustacheTag", - "expression": { - "type": "Identifier", - "start": 1, - "end": 6, - "name": "yield" - } - } - ] - } -} \ No newline at end of file diff --git a/test/runtime/samples/each-block-destructured-object-reserved-key/_config.js b/test/runtime/samples/each-block-destructured-object-reserved-key/_config.js new file mode 100644 index 0000000000..c04e984691 --- /dev/null +++ b/test/runtime/samples/each-block-destructured-object-reserved-key/_config.js @@ -0,0 +1,5 @@ +export default { + html: ` +

    bar

    + ` +}; diff --git a/test/runtime/samples/each-block-destructured-object-reserved-key/main.svelte b/test/runtime/samples/each-block-destructured-object-reserved-key/main.svelte new file mode 100644 index 0000000000..c3e11c3ea1 --- /dev/null +++ b/test/runtime/samples/each-block-destructured-object-reserved-key/main.svelte @@ -0,0 +1,7 @@ + + +{#each foo as { in: bar }} +

    {bar}

    +{/each} diff --git a/test/validator/samples/each-block-invalid-context-destructured-object/errors.json b/test/validator/samples/each-block-invalid-context-destructured-object/errors.json new file mode 100644 index 0000000000..085021ff5a --- /dev/null +++ b/test/validator/samples/each-block-invalid-context-destructured-object/errors.json @@ -0,0 +1,15 @@ +[{ + "code": "unexpected-reserved-word", + "message": "'case' is a reserved word in JavaScript and cannot be used here", + "start": { + "line": 1, + "column": 18, + "character": 18 + }, + "end": { + "line": 1, + "column": 18, + "character": 18 + }, + "pos": 18 +}] diff --git a/test/validator/samples/each-block-invalid-context-destructured-object/input.svelte b/test/validator/samples/each-block-invalid-context-destructured-object/input.svelte new file mode 100644 index 0000000000..a891f131a0 --- /dev/null +++ b/test/validator/samples/each-block-invalid-context-destructured-object/input.svelte @@ -0,0 +1,3 @@ +{#each cases as { case }} + {case.title} +{/each} From 59a5d4a52c801b3c1b5b5033007ab8fad3fd9257 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Sun, 9 Feb 2020 06:48:22 -0500 Subject: [PATCH 08/11] fix tests --- .../no-error-if-before-closing/output.json | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/test/parser/samples/no-error-if-before-closing/output.json b/test/parser/samples/no-error-if-before-closing/output.json index ad8ccd0f91..251cefc0ca 100644 --- a/test/parser/samples/no-error-if-before-closing/output.json +++ b/test/parser/samples/no-error-if-before-closing/output.json @@ -12,6 +12,16 @@ "type": "Literal", "start": 5, "end": 9, + "loc": { + "start": { + "line": 1, + "column": 5 + }, + "end": { + "line": 1, + "column": 9 + } + }, "value": true, "raw": "true" }, @@ -47,6 +57,16 @@ "type": "Literal", "start": 40, "end": 44, + "loc": { + "start": { + "line": 6, + "column": 5 + }, + "end": { + "line": 6, + "column": 9 + } + }, "value": true, "raw": "true" }, @@ -82,6 +102,16 @@ "type": "Literal", "start": 75, "end": 79, + "loc": { + "start": { + "line": 11, + "column": 8 + }, + "end": { + "line": 11, + "column": 12 + } + }, "value": true, "raw": "true" }, @@ -155,6 +185,16 @@ "type": "Literal", "start": 118, "end": 122, + "loc": { + "start": { + "line": 16, + "column": 8 + }, + "end": { + "line": 16, + "column": 12 + } + }, "value": true, "raw": "true" }, From 0625fc218bac769a2724454880e03b85202e37bc Mon Sep 17 00:00:00 2001 From: Conduitry Date: Sun, 9 Feb 2020 09:24:59 -0500 Subject: [PATCH 09/11] fix invalidation in ++foo.bar (#4395) --- CHANGELOG.md | 1 + src/compiler/compile/render_dom/invalidate.ts | 2 +- .../_config.js | 31 +++++++++++++++++++ .../main.svelte | 14 +++++++++ 4 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 test/runtime/samples/instrumentation-update-expression/_config.js create mode 100644 test/runtime/samples/instrumentation-update-expression/main.svelte diff --git a/CHANGELOG.md b/CHANGELOG.md index 16e6484ab3..ee84f7b799 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * Permit reserved keywords as destructuring keys in `{#each}` ([#4372](https://github.com/sveltejs/svelte/issues/4372)) * Disallow reserved keywords in `{expressions}` ([#4372](https://github.com/sveltejs/svelte/issues/4372)) * Fix code generation error with precedence of arrow functions ([#4384](https://github.com/sveltejs/svelte/issues/4384)) +* Fix invalidation in expressions like `++foo.bar` ([#4393](https://github.com/sveltejs/svelte/issues/4393)) ## 3.18.1 diff --git a/src/compiler/compile/render_dom/invalidate.ts b/src/compiler/compile/render_dom/invalidate.ts index dc28501863..20aff05d1e 100644 --- a/src/compiler/compile/render_dom/invalidate.ts +++ b/src/compiler/compile/render_dom/invalidate.ts @@ -49,7 +49,7 @@ export function invalidate(renderer: Renderer, scope: Scope, node: Node, names: const pass_value = ( extra_args.length > 0 || (node.type === 'AssignmentExpression' && node.left.type !== 'Identifier') || - (node.type === 'UpdateExpression' && !node.prefix) + (node.type === 'UpdateExpression' && (!node.prefix || node.argument.type !== 'Identifier')) ); if (pass_value) { diff --git a/test/runtime/samples/instrumentation-update-expression/_config.js b/test/runtime/samples/instrumentation-update-expression/_config.js new file mode 100644 index 0000000000..cc33422f6f --- /dev/null +++ b/test/runtime/samples/instrumentation-update-expression/_config.js @@ -0,0 +1,31 @@ +export default { + html: ` +

    0

    + + +

    0

    + + + `, + async test({ assert, target, window }) { + const [foo, bar] = target.querySelectorAll('p'); + const [button1, button2, button3, button4] = target.querySelectorAll('button'); + const event = new window.MouseEvent('click'); + + await button1.dispatchEvent(event); + assert.equal(foo.innerHTML, '1'); + assert.equal(bar.innerHTML, '0'); + + await button2.dispatchEvent(event); + assert.equal(foo.innerHTML, '2'); + assert.equal(bar.innerHTML, '0'); + + await button3.dispatchEvent(event); + assert.equal(foo.innerHTML, '2'); + assert.equal(bar.innerHTML, '1'); + + await button4.dispatchEvent(event); + assert.equal(foo.innerHTML, '2'); + assert.equal(bar.innerHTML, '2'); + } +}; diff --git a/test/runtime/samples/instrumentation-update-expression/main.svelte b/test/runtime/samples/instrumentation-update-expression/main.svelte new file mode 100644 index 0000000000..0672f6330d --- /dev/null +++ b/test/runtime/samples/instrumentation-update-expression/main.svelte @@ -0,0 +1,14 @@ + + +

    {foo}

    + + + + +

    {bar.bar}

    + + + From 7f2ffb29779581ad7ec26044fbf5aeda24523aeb Mon Sep 17 00:00:00 2001 From: Conduitry Date: Sun, 9 Feb 2020 09:30:57 -0500 Subject: [PATCH 10/11] fix event handlers that are dynamic via reactive declarations or stores (#4394) --- CHANGELOG.md | 1 + src/compiler/compile/nodes/EventHandler.ts | 7 ---- .../event-handler-dynamic-2/_config.js | 33 +++++++++++++++++++ .../event-handler-dynamic-2/main.svelte | 20 +++++++++++ 4 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 test/runtime/samples/event-handler-dynamic-2/_config.js create mode 100644 test/runtime/samples/event-handler-dynamic-2/main.svelte diff --git a/CHANGELOG.md b/CHANGELOG.md index ee84f7b799..f6aa7eca07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * Permit reserved keywords as destructuring keys in `{#each}` ([#4372](https://github.com/sveltejs/svelte/issues/4372)) * Disallow reserved keywords in `{expressions}` ([#4372](https://github.com/sveltejs/svelte/issues/4372)) * Fix code generation error with precedence of arrow functions ([#4384](https://github.com/sveltejs/svelte/issues/4384)) +* Fix event handlers that are dynamic via reactive declarations or stores ([#4388](https://github.com/sveltejs/svelte/issues/4388)) * Fix invalidation in expressions like `++foo.bar` ([#4393](https://github.com/sveltejs/svelte/issues/4393)) ## 3.18.1 diff --git a/src/compiler/compile/nodes/EventHandler.ts b/src/compiler/compile/nodes/EventHandler.ts index 4242c82394..110542d0b8 100644 --- a/src/compiler/compile/nodes/EventHandler.ts +++ b/src/compiler/compile/nodes/EventHandler.ts @@ -53,13 +53,6 @@ export default class EventHandler extends Node { } const node = this.expression.node; - if (node.type === 'Identifier') { - return ( - this.component.node_for_declaration.get(node.name) && - this.component.var_lookup.get(node.name).reassigned - ); - } - if (/FunctionExpression/.test(node.type)) { return false; } diff --git a/test/runtime/samples/event-handler-dynamic-2/_config.js b/test/runtime/samples/event-handler-dynamic-2/_config.js new file mode 100644 index 0000000000..c996d8f2aa --- /dev/null +++ b/test/runtime/samples/event-handler-dynamic-2/_config.js @@ -0,0 +1,33 @@ +export default { + html: ` + +

    0

    + + + `, + + async test({ assert, target, window }) { + const [toggle, handler_a, handler_b] = target.querySelectorAll('button'); + const p = target.querySelector('p'); + + const event = new window.MouseEvent('click'); + + await handler_a.dispatchEvent(event); + assert.equal(p.innerHTML, '1'); + + await toggle.dispatchEvent(event); + + await handler_a.dispatchEvent(event); + assert.equal(p.innerHTML, '2'); + + await toggle.dispatchEvent(event); + + await handler_b.dispatchEvent(event); + assert.equal(p.innerHTML, '1'); + + await toggle.dispatchEvent(event); + + await handler_b.dispatchEvent(event); + assert.equal(p.innerHTML, '2'); + }, +}; diff --git a/test/runtime/samples/event-handler-dynamic-2/main.svelte b/test/runtime/samples/event-handler-dynamic-2/main.svelte new file mode 100644 index 0000000000..1b2041d3b8 --- /dev/null +++ b/test/runtime/samples/event-handler-dynamic-2/main.svelte @@ -0,0 +1,20 @@ + + + + +

    {number}

    + + + From c3232826d4690bcf9fea780bdb0b14a56e87c20c Mon Sep 17 00:00:00 2001 From: Conduitry Date: Sun, 9 Feb 2020 09:32:11 -0500 Subject: [PATCH 11/11] -> v3.18.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 f6aa7eca07..c94c24dd65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Svelte changelog -## Unreleased +## 3.18.2 * Fix binding to module-level variables ([#4086](https://github.com/sveltejs/svelte/issues/4086)) * Improve parsing error messages when there is a pending unclosed tag ([#4131](https://github.com/sveltejs/svelte/issues/4131)) diff --git a/package-lock.json b/package-lock.json index 73f49c08e8..8f0b7469f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.18.1", + "version": "3.18.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 4f7acb0fd7..6000e9d1c4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.18.1", + "version": "3.18.2", "description": "Cybernetically enhanced web apps", "module": "index.mjs", "main": "index",