Merge remote-tracking branch 'upstream/master' into fast-hydration

pull/4309/head
Avi Marcus 6 years ago
commit 5f1bbd6cdf

@ -4,6 +4,9 @@ _output
test/*/samples/*/output.js test/*/samples/*/output.js
node_modules node_modules
# automatically generated
internal_exports.ts
# output files # output files
animate/*.js animate/*.js
esing/*.js esing/*.js

@ -1,3 +0,0 @@
useTabs: true
singleQuote: true
trailingComma: es5

@ -1,9 +1,16 @@
# Svelte changelog # Svelte changelog
## Unreleased ## 3.18.2
* Fix binding to module-level variables ([#4086](https://github.com/sveltejs/svelte/issues/4086)) * 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)) * 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))
* 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 ## 3.18.1

14
package-lock.json generated

@ -1,6 +1,6 @@
{ {
"name": "svelte", "name": "svelte",
"version": "3.18.1", "version": "3.18.2",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -597,9 +597,9 @@
"dev": true "dev": true
}, },
"code-red": { "code-red": {
"version": "0.0.32", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/code-red/-/code-red-0.0.32.tgz", "resolved": "https://registry.npmjs.org/code-red/-/code-red-0.1.1.tgz",
"integrity": "sha512-mE+EZc2vJ4HxiejW5S2CvcVDKtopFEmrqAd9DTBDLCNjLgxekPP8wLi/ZiwDTwZwwW3dzeetaubLaMlIvkhVNw==", "integrity": "sha512-2pEIyya4fxI9HxQcrl9dJl20+9He1nLeja50zkf7gK2OTTV9NpD3CgA74gzXnPqWv2zJpa6uXNSaE0haOCpOsw==",
"dev": true, "dev": true,
"requires": { "requires": {
"acorn": "^7.1.0", "acorn": "^7.1.0",
@ -1682,9 +1682,9 @@
} }
}, },
"https-proxy-agent": { "https-proxy-agent": {
"version": "2.2.2", "version": "2.2.4",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.2.tgz", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
"integrity": "sha512-c8Ndjc9Bkpfx/vCJueCPy0jlP4ccCCSNDp8xwCZzPjKJUm+B+u9WX2x98Qx4n1PiMNTWo3D7KK5ifNV/yJyRzg==", "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
"dev": true, "dev": true,
"requires": { "requires": {
"agent-base": "^4.3.0", "agent-base": "^4.3.0",

@ -1,6 +1,6 @@
{ {
"name": "svelte", "name": "svelte",
"version": "3.18.1", "version": "3.18.2",
"description": "Cybernetically enhanced web apps", "description": "Cybernetically enhanced web apps",
"module": "index.mjs", "module": "index.mjs",
"main": "index", "main": "index",
@ -70,7 +70,7 @@
"acorn": "^7.1.0", "acorn": "^7.1.0",
"agadoo": "^1.1.0", "agadoo": "^1.1.0",
"c8": "^5.0.1", "c8": "^5.0.1",
"code-red": "0.0.32", "code-red": "0.1.1",
"codecov": "^3.5.0", "codecov": "^3.5.0",
"css-tree": "1.0.0-alpha22", "css-tree": "1.0.0-alpha22",
"eslint": "^6.3.0", "eslint": "^6.3.0",

@ -53,13 +53,6 @@ export default class EventHandler extends Node {
} }
const node = this.expression.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)) { if (/FunctionExpression/.test(node.type)) {
return false; return false;
} }

@ -197,7 +197,7 @@ export default class Renderer {
return filtered return filtered
.map(n => x`$$invalidate(${this.context_lookup.get(n).index}, ${n})`) .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 { dirty(names, is_reactive_declaration = false): Expression {

@ -403,9 +403,9 @@ export default function dom(
${set && b`$$self.$set = ${set};`} ${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};`)} ${injected.map(name => b`let ${name};`)}

@ -49,7 +49,7 @@ export function invalidate(renderer: Renderer, scope: Scope, node: Node, names:
const pass_value = ( const pass_value = (
extra_args.length > 0 || extra_args.length > 0 ||
(node.type === 'AssignmentExpression' && node.left.type !== 'Identifier') || (node.type === 'AssignmentExpression' && node.left.type !== 'Identifier') ||
(node.type === 'UpdateExpression' && !node.prefix) (node.type === 'UpdateExpression' && (!node.prefix || node.argument.type !== 'Identifier'))
); );
if (pass_value) { if (pass_value) {
@ -67,7 +67,7 @@ export function invalidate(renderer: Renderer, scope: Scope, node: Node, names:
if (head.subscribable && head.reassigned) { if (head.subscribable && head.reassigned) {
const subscribe = `$$subscribe_${head.name}`; const subscribe = `$$subscribe_${head.name}`;
invalidate = x`${subscribe}(${invalidate})}`; invalidate = x`${subscribe}(${invalidate})`;
} }
return invalidate; return invalidate;

@ -205,7 +205,7 @@ export default class AwaitBlockWrapper extends Wrapper {
} else { } else {
const #child_ctx = #ctx.slice(); 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); ${info}.block.p(#child_ctx, #dirty);
} }
`); `);
@ -219,7 +219,7 @@ export default class AwaitBlockWrapper extends Wrapper {
block.chunks.update.push(b` block.chunks.update.push(b`
{ {
const #child_ctx = #ctx.slice(); 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); ${info}.block.p(#child_ctx, #dirty);
} }
`); `);

@ -97,7 +97,7 @@ export default class BindingWrapper {
const type = parent.node.get_static_attribute_value('type'); const type = parent.node.get_static_attribute_value('type');
if (type === null || type === "" || type === "text" || type === "email" || type === "password") { 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}`);
} }
} }

@ -415,7 +415,7 @@ export default class ElementWrapper extends Wrapper {
const is = this.attributes.find(attr => attr.node.name === 'is'); const is = this.attributes.find(attr => attr.node.name === 'is');
if (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}")`; return x`@element("${name}")`;
@ -628,7 +628,7 @@ export default class ElementWrapper extends Wrapper {
add_this_binding(block: Block, this_binding: Binding) { add_this_binding(block: Block, this_binding: Binding) {
const { renderer } = this; const { renderer } = this;
renderer.component.has_reactive_assignments = true; renderer.component.has_reactive_assignments = true;
const binding_callback = bind_this(renderer.component, block, this_binding.node, this.var); const binding_callback = bind_this(renderer.component, block, this_binding.node, this.var);

@ -17,7 +17,7 @@ export default class MustacheTagWrapper extends Tag {
render(block: Block, parent_node: Identifier, parent_nodes: Identifier) { render(block: Block, parent_node: Identifier, parent_nodes: Identifier) {
const { init } = this.rename_this_method( const { init } = this.rename_this_method(
block, block,
value => x`@set_data(${this.var}, ${value});` value => x`@set_data(${this.var}, ${value})`
); );
block.add_element( block.add_element(

@ -48,7 +48,7 @@ export default class RawMustacheTagWrapper extends Tag {
const { init } = this.rename_this_method( const { init } = this.rename_this_method(
block, 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'; const update_anchor = in_head ? 'null' : needs_anchor ? html_anchor : this.next ? this.next.var : 'null';

@ -12,9 +12,9 @@ export default function(node: AwaitBlock, renderer: Renderer, options: RenderOpt
const then = renderer.pop(); const then = renderer.pop();
renderer.add_expression(x` renderer.add_expression(x`
(function(__value) { function(__value) {
if (@is_promise(__value)) return ${pending}; if (@is_promise(__value)) return ${pending};
return (function(${node.value}) { return ${then}; }(__value)); return (function(${node.value}) { return ${then}; }(__value));
}(${node.expression.node})) }(${node.expression.node})
`); `);
} }

@ -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 { } else {
let add_class_attribute = !!class_expression; let add_class_attribute = !!class_expression;
node.attributes.forEach(attribute => { node.attributes.forEach(attribute => {

@ -12,5 +12,5 @@ export default function(node: Head, renderer: Renderer, options: RenderOptions)
renderer.render(node.children, head_options); renderer.render(node.children, head_options);
const result = renderer.pop(); const result = renderer.pop();
renderer.add_expression(x`($$result.head += ${result}, "")`); renderer.add_expression(x`$$result.head += ${result}, ""`);
} }

@ -12,5 +12,5 @@ export default function(node: Title, renderer: Renderer, options: RenderOptions)
renderer.add_string(`</title>`); renderer.add_string(`</title>`);
const result = renderer.pop(); const result = renderer.pop();
renderer.add_expression(x`($$result.title = ${result}, "")`); renderer.add_expression(x`$$result.title = ${result}, ""`);
} }

@ -141,7 +141,7 @@ export class Parser {
return result; return result;
} }
read_identifier() { read_identifier(allow_reserved = false) {
const start = this.index; const start = this.index;
let i = this.index; let i = this.index;
@ -160,7 +160,7 @@ export class Parser {
const identifier = this.template.slice(this.index, this.index = i); const identifier = this.template.slice(this.index, this.index = i);
if (reserved.has(identifier)) { if (!allow_reserved && reserved.has(identifier)) {
this.error({ this.error({
code: `unexpected-reserved-word`, code: `unexpected-reserved-word`,
message: `'${identifier}' is a reserved word in JavaScript and cannot be used here` message: `'${identifier}' is a reserved word in JavaScript and cannot be used here`

@ -1,4 +1,5 @@
import { Parser } from '../index'; import { Parser } from '../index';
import { reserved } from '../../utils/names';
interface Identifier { interface Identifier {
start: number; start: number;
@ -116,8 +117,11 @@ export default function read_context(parser: Parser) {
break; 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 start = parser.index;
const name = parser.read_identifier(); const name = parser.read_identifier(true);
const key: Identifier = { const key: Identifier = {
start, start,
end: parser.index, end: parser.index,
@ -126,9 +130,19 @@ export default function read_context(parser: Parser) {
}; };
parser.allow_whitespace(); parser.allow_whitespace();
const value = parser.eat(':') let value: Context;
? (parser.allow_whitespace(), read_context(parser)) if (parser.eat(':')) {
: key; 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 = { const property: Property = {
start, start,

@ -1,37 +1,9 @@
import { parse_expression_at } from '../acorn'; import { parse_expression_at } from '../acorn';
import { Parser } from '../index'; import { Parser } from '../index';
import { Identifier, Node, SimpleLiteral } from 'estree'; import { Node } from 'estree';
import { whitespace } from '../../utils/patterns'; import { whitespace } from '../../utils/patterns';
const literals = new Map([['true', true], ['false', false], ['null', null]]);
export default function read_expression(parser: Parser): Node { 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 { try {
const node = parse_expression_at(parser.template, parser.index); const node = parse_expression_at(parser.template, parser.index);

@ -3,6 +3,7 @@ import read_expression from '../read/expression';
import { closing_tag_omitted } from '../utils/html'; import { closing_tag_omitted } from '../utils/html';
import { whitespace } from '../../utils/patterns'; import { whitespace } from '../../utils/patterns';
import { trim_start, trim_end } from '../../utils/trim'; import { trim_start, trim_end } from '../../utils/trim';
import { to_string } from '../utils/node';
import { Parser } from '../index'; import { Parser } from '../index';
import { TemplateNode } from '../../interfaces'; import { TemplateNode } from '../../interfaces';
@ -106,11 +107,14 @@ export default function mustache(parser: Parser) {
// :else if // :else if
if (parser.eat('if')) { if (parser.eat('if')) {
const block = parser.current(); const block = parser.current();
if (block.type !== 'IfBlock') if (block.type !== 'IfBlock') {
parser.error({ parser.error({
code: `invalid-elseif-placement`, 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(); parser.require_whitespace();
@ -144,7 +148,9 @@ export default function mustache(parser: Parser) {
if (block.type !== 'IfBlock' && block.type !== 'EachBlock') { if (block.type !== 'IfBlock' && block.type !== 'EachBlock') {
parser.error({ parser.error({
code: `invalid-else-placement`, 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') { if (block.type !== 'PendingBlock') {
parser.error({ parser.error({
code: `invalid-then-placement`, 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 { } else {
if (block.type !== 'ThenBlock' && block.type !== 'PendingBlock') { if (block.type !== 'ThenBlock' && block.type !== 'PendingBlock') {
parser.error({ parser.error({
code: `invalid-catch-placement`, 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`
}); });
} }
} }

@ -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;
}
}

@ -31,18 +31,23 @@ export function add_flush_callback(fn) {
flush_callbacks.push(fn); flush_callbacks.push(fn);
} }
let flushing = false;
const seen_callbacks = new Set(); const seen_callbacks = new Set();
export function flush() { export function flush() {
if (flushing) return;
flushing = true;
do { do {
// first, call beforeUpdate functions // first, call beforeUpdate functions
// and update components // and update components
while (dirty_components.length) { for (let i = 0; i < dirty_components.length; i += 1) {
const component = dirty_components.shift(); const component = dirty_components[i];
set_current_component(component); set_current_component(component);
update(component.$$); update(component.$$);
} }
dirty_components.length = 0;
while (binding_callbacks.length) binding_callbacks.pop()(); while (binding_callbacks.length) binding_callbacks.pop()();
// then, once components are updated, call // then, once components are updated, call
@ -67,6 +72,7 @@ export function flush() {
} }
update_scheduled = false; update_scheduled = false;
flushing = false;
seen_callbacks.clear(); seen_callbacks.clear();
} }

@ -20,6 +20,16 @@
"type": "Identifier", "type": "Identifier",
"start": 20, "start": 20,
"end": 27, "end": 27,
"loc": {
"start": {
"line": 1,
"column": 20
},
"end": {
"line": 1,
"column": 27
}
},
"name": "message" "name": "message"
} }
} }

@ -24,6 +24,16 @@
"type": "Identifier", "type": "Identifier",
"start": 20, "start": 20,
"end": 28, "end": 28,
"loc": {
"start": {
"line": 1,
"column": 20
},
"end": {
"line": 1,
"column": 28
}
},
"name": "readonly" "name": "readonly"
} }
} }

@ -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": []
}
]
}
}

@ -31,6 +31,16 @@
"type": "Identifier", "type": "Identifier",
"start": 20, "start": 20,
"end": 25, "end": 25,
"loc": {
"start": {
"line": 1,
"column": 20
},
"end": {
"line": 1,
"column": 25
}
},
"name": "color" "name": "color"
} }
}, },
@ -53,6 +63,16 @@
"type": "Identifier", "type": "Identifier",
"start": 30, "start": 30,
"end": 35, "end": 35,
"loc": {
"start": {
"line": 1,
"column": 30
},
"end": {
"line": 1,
"column": 35
}
},
"name": "color" "name": "color"
} }
} }

@ -20,6 +20,16 @@
"type": "Identifier", "type": "Identifier",
"start": 19, "start": 19,
"end": 22, "end": 22,
"loc": {
"start": {
"line": 1,
"column": 19
},
"end": {
"line": 1,
"column": 22
}
},
"name": "foo" "name": "foo"
} }
} }

@ -27,6 +27,16 @@
"type": "Identifier", "type": "Identifier",
"start": 50, "start": 50,
"end": 54, "end": 54,
"loc": {
"start": {
"line": 5,
"column": 19
},
"end": {
"line": 5,
"column": 23
}
},
"name": "name" "name": "name"
} }
} }

@ -40,6 +40,16 @@
"type": "Identifier", "type": "Identifier",
"start": 46, "start": 46,
"end": 49, "end": 49,
"loc": {
"start": {
"line": 2,
"column": 5
},
"end": {
"line": 2,
"column": 8
}
},
"name": "key" "name": "key"
} }
}, },
@ -58,6 +68,16 @@
"type": "Identifier", "type": "Identifier",
"start": 53, "start": 53,
"end": 58, "end": 58,
"loc": {
"start": {
"line": 2,
"column": 12
},
"end": {
"line": 2,
"column": 17
}
},
"name": "value" "name": "value"
} }
} }

@ -40,6 +40,16 @@
"type": "Identifier", "type": "Identifier",
"start": 31, "start": 31,
"end": 37, "end": 37,
"loc": {
"start": {
"line": 2,
"column": 5
},
"end": {
"line": 2,
"column": 11
}
},
"name": "animal" "name": "animal"
} }
} }

@ -40,6 +40,16 @@
"type": "Identifier", "type": "Identifier",
"start": 34, "start": 34,
"end": 35, "end": 35,
"loc": {
"start": {
"line": 2,
"column": 5
},
"end": {
"line": 2,
"column": 6
}
},
"name": "i" "name": "i"
} }
}, },
@ -58,6 +68,16 @@
"type": "Identifier", "type": "Identifier",
"start": 39, "start": 39,
"end": 45, "end": 45,
"loc": {
"start": {
"line": 2,
"column": 10
},
"end": {
"line": 2,
"column": 16
}
},
"name": "animal" "name": "animal"
} }
} }

@ -40,6 +40,16 @@
"type": "Identifier", "type": "Identifier",
"start": 37, "start": 37,
"end": 41, "end": 41,
"loc": {
"start": {
"line": 2,
"column": 5
},
"end": {
"line": 2,
"column": 9
}
},
"name": "todo" "name": "todo"
} }
} }

@ -40,6 +40,16 @@
"type": "Identifier", "type": "Identifier",
"start": 31, "start": 31,
"end": 37, "end": 37,
"loc": {
"start": {
"line": 2,
"column": 5
},
"end": {
"line": 2,
"column": 11
}
},
"name": "animal" "name": "animal"
} }
} }

@ -26,6 +26,16 @@
"type": "Identifier", "type": "Identifier",
"start": 11, "start": 11,
"end": 15, "end": 15,
"loc": {
"start": {
"line": 1,
"column": 11
},
"end": {
"line": 1,
"column": 15
}
},
"name": "name" "name": "name"
} }
}, },

@ -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
}

@ -0,0 +1,4 @@
{#await true}
{#each foo as bar}
{:catch f}
{/await}

@ -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
}

@ -0,0 +1,4 @@
{#if true}
{#await p}
{:else}
{/if}

@ -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
}

@ -0,0 +1,6 @@
{
"code": "invalid-else-placement",
"message": "Expected to close <li> tag before seeing {:else} block",
"start": { "line": 3, "column": 6, "character": 23 },
"pos": 23
}

@ -0,0 +1,6 @@
{
"code": "invalid-elseif-placement",
"message": "Expected to close <p> tag before seeing {:else if ...} block",
"start": { "line": 3, "column": 9, "character": 25 },
"pos": 25
}

@ -0,0 +1,4 @@
{#if true}
<p>
{:else if false}
{/if}

@ -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
}

@ -0,0 +1,4 @@
{#if true}
{#await foo}
{:else if false}
{/if}

@ -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
}

@ -0,0 +1,4 @@
{#await foo}
{:then bar}
{:else if}
{/await}

@ -0,0 +1,6 @@
{
"code": "invalid-then-placement",
"message": "Expected to close <li> tag before seeing {:then} block",
"start": { "line": 3, "column": 6, "character": 26 },
"pos": 26
}

@ -0,0 +1,4 @@
{#await true}
<li>
{:then f}
{/await}

@ -128,6 +128,16 @@
"type": "Identifier", "type": "Identifier",
"start": 68, "start": 68,
"end": 75, "end": 75,
"loc": {
"start": {
"line": 3,
"column": 5
},
"end": {
"line": 3,
"column": 12
}
},
"name": "visible" "name": "visible"
}, },
"children": [ "children": [

@ -12,6 +12,16 @@
"type": "Identifier", "type": "Identifier",
"start": 5, "start": 5,
"end": 8, "end": 8,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 8
}
},
"name": "foo" "name": "foo"
}, },
"children": [ "children": [

@ -12,6 +12,16 @@
"type": "Identifier", "type": "Identifier",
"start": 5, "start": 5,
"end": 8, "end": 8,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 8
}
},
"name": "foo" "name": "foo"
}, },
"children": [ "children": [

@ -40,6 +40,16 @@
"type": "Literal", "type": "Literal",
"start": 18, "start": 18,
"end": 22, "end": 22,
"loc": {
"start": {
"line": 3,
"column": 6
},
"end": {
"line": 3,
"column": 10
}
},
"value": true, "value": true,
"raw": "true" "raw": "true"
}, },

@ -0,0 +1,19 @@
{#if true}
<input>
{:else}
{/if}
{#if true}
<br>
{:else}
{/if}
{#await true}
<input>
{:then f}
{/await}
{#await true}
<br>
{:then f}
{/await}

@ -0,0 +1,258 @@
{
"html": {
"start": 0,
"end": 148,
"type": "Fragment",
"children": [
{
"start": 0,
"end": 33,
"type": "IfBlock",
"expression": {
"type": "Literal",
"start": 5,
"end": 9,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 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,
"loc": {
"start": {
"line": 6,
"column": 5
},
"end": {
"line": 6,
"column": 9
}
},
"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,
"loc": {
"start": {
"line": 11,
"column": 8
},
"end": {
"line": 11,
"column": 12
}
},
"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,
"loc": {
"start": {
"line": 16,
"column": 8
},
"end": {
"line": 16,
"column": 12
}
},
"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
}
}
]
}
}

@ -27,6 +27,16 @@
"type": "Identifier", "type": "Identifier",
"start": 49, "start": 49,
"end": 52, "end": 52,
"loc": {
"start": {
"line": 5,
"column": 19
},
"end": {
"line": 5,
"column": 22
}
},
"name": "foo" "name": "foo"
} }
} }

@ -33,6 +33,16 @@
"type": "Identifier", "type": "Identifier",
"start": 90, "start": 90,
"end": 94, "end": 94,
"loc": {
"start": {
"line": 9,
"column": 11
},
"end": {
"line": 9,
"column": 15
}
},
"name": "name" "name": "name"
} }
}, },

@ -33,6 +33,16 @@
"type": "Identifier", "type": "Identifier",
"start": 79, "start": 79,
"end": 83, "end": 83,
"loc": {
"start": {
"line": 7,
"column": 11
},
"end": {
"line": 7,
"column": 15
}
},
"name": "name" "name": "name"
} }
}, },

@ -33,6 +33,16 @@
"type": "Identifier", "type": "Identifier",
"start": 52, "start": 52,
"end": 56, "end": 56,
"loc": {
"start": {
"line": 5,
"column": 11
},
"end": {
"line": 5,
"column": 15
}
},
"name": "name" "name": "name"
} }
}, },

@ -26,6 +26,16 @@
"type": "Identifier", "type": "Identifier",
"start": 5, "start": 5,
"end": 6, "end": 6,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 6
}
},
"name": "a" "name": "a"
} }
}, },
@ -44,6 +54,16 @@
"type": "Identifier", "type": "Identifier",
"start": 9, "start": 9,
"end": 10, "end": 10,
"loc": {
"start": {
"line": 1,
"column": 9
},
"end": {
"line": 1,
"column": 10
}
},
"name": "b" "name": "b"
} }
}, },
@ -62,6 +82,16 @@
"type": "Identifier", "type": "Identifier",
"start": 15, "start": 15,
"end": 16, "end": 16,
"loc": {
"start": {
"line": 1,
"column": 15
},
"end": {
"line": 1,
"column": 16
}
},
"name": "c" "name": "c"
} }
}, },

@ -18,6 +18,16 @@
"type": "Identifier", "type": "Identifier",
"start": 9, "start": 9,
"end": 14, "end": 14,
"loc": {
"start": {
"line": 1,
"column": 9
},
"end": {
"line": 1,
"column": 14
}
},
"name": "props" "name": "props"
} }
} }

@ -29,6 +29,16 @@
"type": "Identifier", "type": "Identifier",
"start": 41, "start": 41,
"end": 44, "end": 44,
"loc": {
"start": {
"line": 2,
"column": 30
},
"end": {
"line": 2,
"column": 33
}
},
"name": "foo" "name": "foo"
} }
}, },

@ -33,6 +33,16 @@
"type": "Identifier", "type": "Identifier",
"start": 19, "start": 19,
"end": 23, "end": 23,
"loc": {
"start": {
"line": 1,
"column": 19
},
"end": {
"line": 1,
"column": 23
}
},
"name": "name" "name": "name"
} }
}, },

@ -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"
}
}
]
}
}

@ -0,0 +1,7 @@
<script>
const foo = [{ in: 'bar' }];
</script>
{#each foo as { in: bar }}
<p>{bar}</p>
{/each}

@ -0,0 +1,33 @@
export default {
html: `
<button>toggle</button>
<p>0</p>
<button>handler_a</button>
<button>handler_b</button>
`,
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');
},
};

@ -0,0 +1,20 @@
<script>
import { writable } from 'svelte/store';
let number = 0;
const handler_1 = () => number = 1;
const handler_2 = () => number = 2;
let flag = true;
$: handler_a = flag ? handler_1 : handler_2;
const handler_b = writable();
$: handler_b.set(flag ? handler_1 : handler_2);
</script>
<button on:click={() => flag = !flag}>toggle</button>
<p>{number}</p>
<button on:click={handler_a}>handler_a</button>
<button on:click={$handler_b}>handler_b</button>

@ -0,0 +1,31 @@
export default {
html: `
<p>0</p>
<button>foo++</button>
<button>++foo</button>
<p>0</p>
<button>bar.bar++</button>
<button>++bar.bar</button>
`,
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');
}
};

@ -0,0 +1,14 @@
<script>
let foo = 0;
let bar = { bar: 0 };
</script>
<p>{foo}</p>
<button on:click={() => foo++}>foo++</button>
<button on:click={() => ++foo}>++foo</button>
<p>{bar.bar}</p>
<button on:click={() => bar.bar++}>bar.bar++</button>
<button on:click={() => ++bar.bar}>++bar.bar</button>

@ -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
}]
Loading…
Cancel
Save