From 30c8fe0c6ec40223b2c0f5dccdd838059361d09c Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Thu, 11 Apr 2019 23:29:02 -0400 Subject: [PATCH 1/4] allow reactive declarations without dependencies - fixes #2285 --- src/compile/Component.ts | 7 --- src/compile/render-dom/index.ts | 52 +++++++++++-------- .../expected.js | 4 +- .../expected.js | 8 +-- .../expected.js | 4 +- .../runtime/samples/props-reactive/_config.js | 2 - .../_config.js | 12 +++++ .../main.svelte | 10 ++++ .../reactive-declaration-no-deps/errors.json | 7 --- .../reactive-declaration-no-deps/input.svelte | 3 -- 10 files changed, 56 insertions(+), 53 deletions(-) create mode 100644 test/runtime/samples/reactive-values-no-dependencies/_config.js create mode 100644 test/runtime/samples/reactive-values-no-dependencies/main.svelte delete mode 100644 test/validator/samples/reactive-declaration-no-deps/errors.json delete mode 100644 test/validator/samples/reactive-declaration-no-deps/input.svelte diff --git a/src/compile/Component.ts b/src/compile/Component.ts index e55af65de6ff..78715415f239 100644 --- a/src/compile/Component.ts +++ b/src/compile/Component.ts @@ -1134,13 +1134,6 @@ export default class Component { seen.add(declaration); - if (declaration.dependencies.size === 0) { - this.error(declaration.node, { - code: 'invalid-reactive-declaration', - message: 'Invalid reactive declaration — must depend on local state' - }); - } - declaration.dependencies.forEach(name => { if (declaration.assignees.has(name)) return; const earlier_declarations = lookup.get(name); diff --git a/src/compile/render-dom/index.ts b/src/compile/render-dom/index.ts index 363d80791600..6aa36d88f739 100644 --- a/src/compile/render-dom/index.ts +++ b/src/compile/render-dom/index.ts @@ -347,28 +347,34 @@ export default function dom( .map(({ name }) => `$$self.$$.on_destroy.push(() => $$unsubscribe_${name.slice(1)}());`); if (has_definition) { - const reactive_declarations = component.reactive_declarations.map(d => { - const condition = Array.from(d.dependencies) - .filter(n => { - if (n === '$$props') return false; - const variable = component.var_lookup.get(n); - return variable && variable.writable; - }) - .map(n => `$$dirty.${n}`).join(' || '); - - const snippet = d.node.body.type === 'BlockStatement' - ? `[✂${d.node.body.start}-${d.node.end}✂]` - : deindent` - { - [✂${d.node.body.start}-${d.node.end}✂] - }`; - - return condition - ? deindent` - if (${condition}) ${snippet}` - : deindent` - ${snippet}` - }); + const reactive_declarations = []; + const fixed_reactive_declarations = []; // not really 'reactive' but whatever + + component.reactive_declarations + .forEach(d => { + let uses_props; + + const condition = Array.from(d.dependencies) + .filter(n => { + if (n === '$$props') { + uses_props = true; + return false; + } + + const variable = component.var_lookup.get(n); + return variable && variable.writable; + }) + .map(n => `$$dirty.${n}`).join(' || '); + + let snippet = `[✂${d.node.body.start}-${d.node.end}✂]`; + if (condition) snippet = `if (${condition}) { ${snippet} }`; + + if (condition || uses_props) { + reactive_declarations.push(snippet); + } else { + fixed_reactive_declarations.push(snippet); + } + }); const injected = Array.from(component.injected_reactive_declaration_vars).filter(name => { const variable = component.var_lookup.get(name); @@ -410,6 +416,8 @@ export default function dom( $$self.$$.update = ($$dirty = { ${Array.from(all_reactive_dependencies).map(n => `${n}: 1`).join(', ')} }) => { ${reactive_declarations} }; + + ${fixed_reactive_declarations} `} return ${stringify_props(filtered_declarations)}; diff --git a/test/js/samples/dev-warning-missing-data-computed/expected.js b/test/js/samples/dev-warning-missing-data-computed/expected.js index ff1f48e2be9c..1585e1ea273d 100644 --- a/test/js/samples/dev-warning-missing-data-computed/expected.js +++ b/test/js/samples/dev-warning-missing-data-computed/expected.js @@ -70,9 +70,7 @@ function instance($$self, $$props, $$invalidate) { }; $$self.$$.update = ($$dirty = { foo: 1 }) => { - if ($$dirty.foo) { - bar = foo * 2; $$invalidate('bar', bar); - } + if ($$dirty.foo) { bar = foo * 2; $$invalidate('bar', bar); } }; return { foo, bar }; diff --git a/test/js/samples/reactive-values-non-topologically-ordered/expected.js b/test/js/samples/reactive-values-non-topologically-ordered/expected.js index a0a5363d6f00..b0bd493d0f4d 100644 --- a/test/js/samples/reactive-values-non-topologically-ordered/expected.js +++ b/test/js/samples/reactive-values-non-topologically-ordered/expected.js @@ -28,12 +28,8 @@ function instance($$self, $$props, $$invalidate) { }; $$self.$$.update = ($$dirty = { x: 1, b: 1 }) => { - if ($$dirty.x) { - b = x; $$invalidate('b', b); - } - if ($$dirty.b) { - a = b; $$invalidate('a', a); - } + if ($$dirty.x) { b = x; $$invalidate('b', b); } + if ($$dirty.b) { a = b; $$invalidate('a', a); } }; return { x }; diff --git a/test/js/samples/reactive-values-non-writable-dependencies/expected.js b/test/js/samples/reactive-values-non-writable-dependencies/expected.js index 2e4507222f54..fc6f059d4e5f 100644 --- a/test/js/samples/reactive-values-non-writable-dependencies/expected.js +++ b/test/js/samples/reactive-values-non-writable-dependencies/expected.js @@ -27,9 +27,7 @@ function instance($$self, $$props, $$invalidate) { let max; $$self.$$.update = ($$dirty = { a: 1, b: 1 }) => { - if ($$dirty.a || $$dirty.b) { - max = Math.max(a, b); $$invalidate('max', max); - } + if ($$dirty.a || $$dirty.b) { max = Math.max(a, b); $$invalidate('max', max); } }; return {}; diff --git a/test/runtime/samples/props-reactive/_config.js b/test/runtime/samples/props-reactive/_config.js index 49e18546c7c5..a44687069e76 100644 --- a/test/runtime/samples/props-reactive/_config.js +++ b/test/runtime/samples/props-reactive/_config.js @@ -1,6 +1,4 @@ export default { - show: 1, - props: { a: 1, b: 2, diff --git a/test/runtime/samples/reactive-values-no-dependencies/_config.js b/test/runtime/samples/reactive-values-no-dependencies/_config.js new file mode 100644 index 000000000000..71a63c918939 --- /dev/null +++ b/test/runtime/samples/reactive-values-no-dependencies/_config.js @@ -0,0 +1,12 @@ +export default { + html: ` +

10 - 90

+ `, + + test({ assert, component, target }) { + component.width = 50; + assert.htmlEqual(target.innerHTML, ` +

10 - 40

+ `); + } +}; diff --git a/test/runtime/samples/reactive-values-no-dependencies/main.svelte b/test/runtime/samples/reactive-values-no-dependencies/main.svelte new file mode 100644 index 000000000000..bf54b8e2417a --- /dev/null +++ b/test/runtime/samples/reactive-values-no-dependencies/main.svelte @@ -0,0 +1,10 @@ + + +

{x1} - {x2}

\ No newline at end of file diff --git a/test/validator/samples/reactive-declaration-no-deps/errors.json b/test/validator/samples/reactive-declaration-no-deps/errors.json deleted file mode 100644 index 0063d4005e22..000000000000 --- a/test/validator/samples/reactive-declaration-no-deps/errors.json +++ /dev/null @@ -1,7 +0,0 @@ -[{ - "message": "Invalid reactive declaration — must depend on local state", - "code": "invalid-reactive-declaration", - "start": { "line": 2, "column": 1, "character": 10 }, - "end": { "line": 2, "column": 23, "character": 32 }, - "pos": 10 -}] diff --git a/test/validator/samples/reactive-declaration-no-deps/input.svelte b/test/validator/samples/reactive-declaration-no-deps/input.svelte deleted file mode 100644 index c334f92ab53b..000000000000 --- a/test/validator/samples/reactive-declaration-no-deps/input.svelte +++ /dev/null @@ -1,3 +0,0 @@ - From b36d24032b1d9787673e6a796af23b6b3df88196 Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Thu, 11 Apr 2019 23:46:38 -0400 Subject: [PATCH 2/4] optimise certain $$invalidate calls --- src/compile/render-dom/index.ts | 14 +++++++++++++- .../dev-warning-missing-data-computed/expected.js | 2 +- .../instrumentation-script-if-no-block/expected.js | 2 +- .../expected.js | 4 ++-- .../expected.js | 2 +- 5 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/compile/render-dom/index.ts b/src/compile/render-dom/index.ts index 6aa36d88f739..d7649897c065 100644 --- a/src/compile/render-dom/index.ts +++ b/src/compile/render-dom/index.ts @@ -188,6 +188,12 @@ export default function dom( code.overwrite(node.start, node.end, dirty.map(n => component.invalidate(n)).join('; ')); } else { + const single = ( + node.left.type === 'Identifier' && + parent.type === 'ExpressionStatement' && + node.left.name[0] !== '$' + ); + names.forEach(name => { const owner = scope.find_owner(name); if (owner && owner !== component.instance_scope) return; @@ -195,7 +201,13 @@ export default function dom( const variable = component.var_lookup.get(name); if (variable && (variable.hoistable || variable.global || variable.module)) return; - pending_assignments.add(name); + if (single) { + code.prependRight(node.start, `$$invalidate('${name}', `); + code.appendLeft(node.end, `)`); + } else { + pending_assignments.add(name); + } + component.has_reactive_assignments = true; }); } diff --git a/test/js/samples/dev-warning-missing-data-computed/expected.js b/test/js/samples/dev-warning-missing-data-computed/expected.js index 1585e1ea273d..12e8a983d00c 100644 --- a/test/js/samples/dev-warning-missing-data-computed/expected.js +++ b/test/js/samples/dev-warning-missing-data-computed/expected.js @@ -70,7 +70,7 @@ function instance($$self, $$props, $$invalidate) { }; $$self.$$.update = ($$dirty = { foo: 1 }) => { - if ($$dirty.foo) { bar = foo * 2; $$invalidate('bar', bar); } + if ($$dirty.foo) { $$invalidate('bar', bar = foo * 2); } }; return { foo, bar }; diff --git a/test/js/samples/instrumentation-script-if-no-block/expected.js b/test/js/samples/instrumentation-script-if-no-block/expected.js index 6ab3e37f1f3c..ce5948de731b 100644 --- a/test/js/samples/instrumentation-script-if-no-block/expected.js +++ b/test/js/samples/instrumentation-script-if-no-block/expected.js @@ -61,7 +61,7 @@ function instance($$self, $$props, $$invalidate) { let x = 0; function foo() { - if (true) { x += 1; $$invalidate('x', x); } + if (true) $$invalidate('x', x += 1); } return { x, foo }; diff --git a/test/js/samples/reactive-values-non-topologically-ordered/expected.js b/test/js/samples/reactive-values-non-topologically-ordered/expected.js index b0bd493d0f4d..2f729362b002 100644 --- a/test/js/samples/reactive-values-non-topologically-ordered/expected.js +++ b/test/js/samples/reactive-values-non-topologically-ordered/expected.js @@ -28,8 +28,8 @@ function instance($$self, $$props, $$invalidate) { }; $$self.$$.update = ($$dirty = { x: 1, b: 1 }) => { - if ($$dirty.x) { b = x; $$invalidate('b', b); } - if ($$dirty.b) { a = b; $$invalidate('a', a); } + if ($$dirty.x) { $$invalidate('b', b = x); } + if ($$dirty.b) { $$invalidate('a', a = b); } }; return { x }; diff --git a/test/js/samples/reactive-values-non-writable-dependencies/expected.js b/test/js/samples/reactive-values-non-writable-dependencies/expected.js index fc6f059d4e5f..62057cc6f06f 100644 --- a/test/js/samples/reactive-values-non-writable-dependencies/expected.js +++ b/test/js/samples/reactive-values-non-writable-dependencies/expected.js @@ -27,7 +27,7 @@ function instance($$self, $$props, $$invalidate) { let max; $$self.$$.update = ($$dirty = { a: 1, b: 1 }) => { - if ($$dirty.a || $$dirty.b) { max = Math.max(a, b); $$invalidate('max', max); } + if ($$dirty.a || $$dirty.b) { $$invalidate('max', max = Math.max(a, b)); } }; return {}; From d7b32fb22c7eb6e3a5bd8b25b0fb9a5fbc9a6206 Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Fri, 12 Apr 2019 00:04:38 -0400 Subject: [PATCH 3/4] inject lets for destructured reactive declarations - fixes #2396 --- src/compile/Component.ts | 28 +++++++++++-------- .../_config.js | 16 +++++++++++ .../main.svelte | 7 +++++ 3 files changed, 40 insertions(+), 11 deletions(-) create mode 100644 test/runtime/samples/reactive-values-implicit-destructured/_config.js create mode 100644 test/runtime/samples/reactive-values-implicit-destructured/main.svelte diff --git a/src/compile/Component.ts b/src/compile/Component.ts index 78715415f239..a0ee6a556469 100644 --- a/src/compile/Component.ts +++ b/src/compile/Component.ts @@ -592,11 +592,11 @@ export default class Component { if (node.body.type !== 'ExpressionStatement') return; if (node.body.expression.type !== 'AssignmentExpression') return; - const { type, name } = node.body.expression.left; - - if (type === 'Identifier' && !this.var_lookup.has(name) && name[0] !== '$') { - this.injected_reactive_declaration_vars.add(name); - } + extract_names(node.body.expression.left).forEach(name => { + if (!this.var_lookup.has(name) && name[0] !== '$') { + this.injected_reactive_declaration_vars.add(name); + } + }); }); let { scope: instance_scope, map, globals } = create_scopes(script.content); @@ -1058,9 +1058,15 @@ export default class Component { } if (node.type === 'AssignmentExpression') { - const identifier = get_object(node.left) - assignee_nodes.add(identifier); - assignees.add(identifier.name); + if (node.left.type === 'MemberExpression' || node.left.type === 'Identifier') { + const identifier = get_object(node.left) + assignee_nodes.add(identifier); + assignees.add(identifier.name); + } else { + extract_names(node.left).forEach(name => { + assignees.add(name); + }); + } } else if (node.type === 'UpdateExpression') { const identifier = get_object(node.argument); assignees.add(identifier.name); @@ -1097,9 +1103,9 @@ export default class Component { injected: ( node.body.type === 'ExpressionStatement' && node.body.expression.type === 'AssignmentExpression' && - node.body.expression.left.type === 'Identifier' && - node.body.expression.left.name[0] !== '$' && - this.var_lookup.get(node.body.expression.left.name).injected + extract_names(node.body.expression.left).every(name => { + return name[0] !== '$' && this.var_lookup.get(name).injected; + }) ) }); } diff --git a/test/runtime/samples/reactive-values-implicit-destructured/_config.js b/test/runtime/samples/reactive-values-implicit-destructured/_config.js new file mode 100644 index 000000000000..f5e6b1784ca2 --- /dev/null +++ b/test/runtime/samples/reactive-values-implicit-destructured/_config.js @@ -0,0 +1,16 @@ +export default { + props: { + coords: [0, 0] + }, + + html: ` +

0,0

+ `, + + test({ assert, component, target }) { + component.coords = [1, 2]; + assert.htmlEqual(target.innerHTML, ` +

1,2

+ `); + } +}; diff --git a/test/runtime/samples/reactive-values-implicit-destructured/main.svelte b/test/runtime/samples/reactive-values-implicit-destructured/main.svelte new file mode 100644 index 000000000000..0f49ebb5905b --- /dev/null +++ b/test/runtime/samples/reactive-values-implicit-destructured/main.svelte @@ -0,0 +1,7 @@ + + +

{x},{y}

\ No newline at end of file From a88749af27312b1784318e6f3a5cb247ab07551b Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Fri, 12 Apr 2019 09:31:09 -0400 Subject: [PATCH 4/4] allow reactive declarations to reference self - fixes #2386 --- src/compile/Component.ts | 40 +++++++------------ src/compile/render-ssr/index.ts | 29 +++++++++++++- src/compile/utils/get_object.ts | 3 +- src/compile/utils/scope.ts | 32 ++++++++------- src/compile/utils/unwrap_parens.ts | 6 +++ .../_config.js | 11 ++++- .../main.svelte | 5 ++- .../_config.js | 20 ++++++++++ .../main.svelte | 7 ++++ 9 files changed, 109 insertions(+), 44 deletions(-) create mode 100644 src/compile/utils/unwrap_parens.ts create mode 100644 test/runtime/samples/reactive-values-implicit-self-dependency/_config.js create mode 100644 test/runtime/samples/reactive-values-implicit-self-dependency/main.svelte diff --git a/src/compile/Component.ts b/src/compile/Component.ts index a0ee6a556469..f1bf634786c8 100644 --- a/src/compile/Component.ts +++ b/src/compile/Component.ts @@ -5,7 +5,7 @@ import Stats from '../Stats'; import { globals, reserved } from '../utils/names'; import { namespaces, valid_namespaces } from '../utils/namespaces'; import create_module from './create_module'; -import { create_scopes, extract_names, Scope } from './utils/scope'; +import { create_scopes, extract_names, Scope, extract_identifiers } from './utils/scope'; import Stylesheet from './css/Stylesheet'; import { test } from '../config'; import Fragment from './nodes/Fragment'; @@ -19,6 +19,7 @@ import TemplateScope from './nodes/shared/TemplateScope'; import fuzzymatch from '../utils/fuzzymatch'; import { remove_indentation, add_indentation } from '../utils/indentation'; import get_object from './utils/get_object'; +import unwrap_parens from './utils/unwrap_parens'; type ComponentOptions = { namespace?: string; @@ -93,7 +94,7 @@ export default class Component { node_for_declaration: Map = new Map(); partly_hoisted: string[] = []; fully_hoisted: string[] = []; - reactive_declarations: Array<{ assignees: Set, dependencies: Set, node: Node, injected: boolean }> = []; + reactive_declarations: Array<{ assignees: Set, dependencies: Set, node: Node, declaration: Node }> = []; reactive_declaration_nodes: Set = new Set(); has_reactive_assignments = false; injected_reactive_declaration_vars: Set = new Set(); @@ -590,9 +591,11 @@ export default class Component { script.content.body.forEach(node => { if (node.type !== 'LabeledStatement') return; if (node.body.type !== 'ExpressionStatement') return; - if (node.body.expression.type !== 'AssignmentExpression') return; - extract_names(node.body.expression.left).forEach(name => { + const expression = unwrap_parens(node.body.expression); + if (expression.type !== 'AssignmentExpression') return; + + extract_names(expression.left).forEach(name => { if (!this.var_lookup.has(name) && name[0] !== '$') { this.injected_reactive_declaration_vars.add(name); } @@ -1058,15 +1061,10 @@ export default class Component { } if (node.type === 'AssignmentExpression') { - if (node.left.type === 'MemberExpression' || node.left.type === 'Identifier') { - const identifier = get_object(node.left) - assignee_nodes.add(identifier); - assignees.add(identifier.name); - } else { - extract_names(node.left).forEach(name => { - assignees.add(name); - }); - } + extract_identifiers(get_object(node.left)).forEach(node => { + assignee_nodes.add(node); + assignees.add(node.name); + }); } else if (node.type === 'UpdateExpression') { const identifier = get_object(node.argument); assignees.add(identifier.name); @@ -1096,18 +1094,10 @@ export default class Component { add_indentation(this.code, node.body, 2); - unsorted_reactive_declarations.push({ - assignees, - dependencies, - node, - injected: ( - node.body.type === 'ExpressionStatement' && - node.body.expression.type === 'AssignmentExpression' && - extract_names(node.body.expression.left).every(name => { - return name[0] !== '$' && this.var_lookup.get(name).injected; - }) - ) - }); + const expression = node.body.expression && unwrap_parens(node.body.expression); + const declaration = expression && expression.left; + + unsorted_reactive_declarations.push({ assignees, dependencies, node, declaration }); } }); diff --git a/src/compile/render-ssr/index.ts b/src/compile/render-ssr/index.ts index 1566962f6dfa..10f82beb5e89 100644 --- a/src/compile/render-ssr/index.ts +++ b/src/compile/render-ssr/index.ts @@ -3,6 +3,7 @@ import Component from '../Component'; import { CompileOptions } from '../../interfaces'; import { stringify } from '../utils/stringify'; import Renderer from './Renderer'; +import { extract_names } from '../utils/scope'; export default function ssr( component: Component, @@ -63,8 +64,32 @@ export default function ssr( : []; const reactive_declarations = component.reactive_declarations.map(d => { - const snippet = `[✂${d.node.body.start}-${d.node.end}✂]`; - return d.injected ? `let ${snippet}` : snippet; + let snippet = `[✂${d.node.body.start}-${d.node.end}✂]`; + + if (d.declaration) { + const declared = extract_names(d.declaration); + const injected = declared.filter(name => { + return name[0] !== '$' && component.var_lookup.get(name).injected; + }); + + const self_dependencies = injected.filter(name => d.dependencies.has(name)); + + if (injected.length) { + // in some cases we need to do `let foo; [expression]`, in + // others we can do `let [expression]` + const separate = ( + self_dependencies.length > 0 || + declared.length > injected.length || + d.node.body.expression.type === 'ParenthesizedExpression' + ); + + snippet = separate + ? `let ${injected.join(', ')}; ${snippet}` + : `let ${snippet}`; + } + } + + return snippet; }); const main = renderer.has_bindings diff --git a/src/compile/utils/get_object.ts b/src/compile/utils/get_object.ts index 48883285624c..641ebdcb9ca8 100644 --- a/src/compile/utils/get_object.ts +++ b/src/compile/utils/get_object.ts @@ -1,7 +1,8 @@ import { Node } from '../../interfaces'; +import unwrap_parens from './unwrap_parens'; export default function get_object(node: Node) { - while (node.type === 'ParenthesizedExpression') node = node.expression; + node = unwrap_parens(node); while (node.type === 'MemberExpression') node = node.object; return node; } diff --git a/src/compile/utils/scope.ts b/src/compile/utils/scope.ts index 6c9371b243df..d4427c44ff38 100644 --- a/src/compile/utils/scope.ts +++ b/src/compile/utils/scope.ts @@ -101,37 +101,41 @@ export class Scope { } export function extract_names(param: Node) { - const names: string[] = []; - extractors[param.type](names, param); - return names; + return extract_identifiers(param).map(node => node.name); +} + +export function extract_identifiers(param: Node) { + const nodes: Node[] = []; + extractors[param.type](nodes, param); + return nodes; } const extractors = { - Identifier(names: string[], param: Node) { - names.push(param.name); + Identifier(nodes: Node[], param: Node) { + nodes.push(param); }, - ObjectPattern(names: string[], param: Node) { + ObjectPattern(nodes: Node[], param: Node) { param.properties.forEach((prop: Node) => { if (prop.type === 'RestElement') { - names.push(prop.argument.name); + nodes.push(prop.argument); } else { - extractors[prop.value.type](names, prop.value); + extractors[prop.value.type](nodes, prop.value); } }); }, - ArrayPattern(names: string[], param: Node) { + ArrayPattern(nodes: Node[], param: Node) { param.elements.forEach((element: Node) => { - if (element) extractors[element.type](names, element); + if (element) extractors[element.type](nodes, element); }); }, - RestElement(names: string[], param: Node) { - extractors[param.argument.type](names, param.argument); + RestElement(nodes: Node[], param: Node) { + extractors[param.argument.type](nodes, param.argument); }, - AssignmentPattern(names: string[], param: Node) { - extractors[param.left.type](names, param.left); + AssignmentPattern(nodes: Node[], param: Node) { + extractors[param.left.type](nodes, param.left); } }; diff --git a/src/compile/utils/unwrap_parens.ts b/src/compile/utils/unwrap_parens.ts new file mode 100644 index 000000000000..71a03b2b6916 --- /dev/null +++ b/src/compile/utils/unwrap_parens.ts @@ -0,0 +1,6 @@ +import { Node } from '../../interfaces'; + +export default function unwrap_parens(node: Node) { + while (node.type === 'ParenthesizedExpression') node = node.expression; + return node; +} \ No newline at end of file diff --git a/test/runtime/samples/reactive-values-implicit-destructured/_config.js b/test/runtime/samples/reactive-values-implicit-destructured/_config.js index f5e6b1784ca2..80e4b8ea44d0 100644 --- a/test/runtime/samples/reactive-values-implicit-destructured/_config.js +++ b/test/runtime/samples/reactive-values-implicit-destructured/_config.js @@ -1,16 +1,25 @@ export default { props: { - coords: [0, 0] + coords: [0, 0], + numbers: { answer: 42 } }, html: `

0,0

+

42

`, test({ assert, component, target }) { component.coords = [1, 2]; assert.htmlEqual(target.innerHTML, `

1,2

+

42

+ `); + + component.numbers = { answer: 43 }; + assert.htmlEqual(target.innerHTML, ` +

1,2

+

43

`); } }; diff --git a/test/runtime/samples/reactive-values-implicit-destructured/main.svelte b/test/runtime/samples/reactive-values-implicit-destructured/main.svelte index 0f49ebb5905b..abf5f01471d6 100644 --- a/test/runtime/samples/reactive-values-implicit-destructured/main.svelte +++ b/test/runtime/samples/reactive-values-implicit-destructured/main.svelte @@ -1,7 +1,10 @@ -

{x},{y}

\ No newline at end of file +

{x},{y}

+

{answer}

\ No newline at end of file diff --git a/test/runtime/samples/reactive-values-implicit-self-dependency/_config.js b/test/runtime/samples/reactive-values-implicit-self-dependency/_config.js new file mode 100644 index 000000000000..d7f2bbf920ac --- /dev/null +++ b/test/runtime/samples/reactive-values-implicit-self-dependency/_config.js @@ -0,0 +1,20 @@ +export default { + show: 1, + html: ` +

1 / 1

+ `, + + test({ assert, component, target }) { + component.num = 3; + + assert.htmlEqual(target.innerHTML, ` +

3 / 3

+ `); + + component.num = 2; + + assert.htmlEqual(target.innerHTML, ` +

2 / 3

+ `); + } +}; diff --git a/test/runtime/samples/reactive-values-implicit-self-dependency/main.svelte b/test/runtime/samples/reactive-values-implicit-self-dependency/main.svelte new file mode 100644 index 000000000000..378b71d326bb --- /dev/null +++ b/test/runtime/samples/reactive-values-implicit-self-dependency/main.svelte @@ -0,0 +1,7 @@ + + +

{num} / {max}

\ No newline at end of file