start implementing bitmask-based change tracking (#1943)

pull/3945/head
Richard Harris 6 years ago
parent 669101dfc1
commit 7118318224

@ -889,48 +889,8 @@ export default class Component {
return null;
}
invalidate(name, value?) {
const variable = this.var_lookup.get(name);
if (variable && (variable.subscribable && (variable.reassigned || variable.export_name))) {
return x`${`$$subscribe_${name}`}($$invalidate('${name}', ${value || name}))`;
}
if (name[0] === '$' && name[1] !== '$') {
return x`${name.slice(1)}.set(${value || name})`;
}
if (
variable &&
!variable.referenced &&
!variable.is_reactive_dependency &&
!variable.export_name &&
!name.startsWith('$$')
) {
return value || name;
}
if (value) {
return x`$$invalidate('${name}', ${value})`;
}
// if this is a reactive declaration, invalidate dependencies recursively
const deps = new Set([name]);
deps.forEach(name => {
const reactive_declarations = this.reactive_declarations.filter(x =>
x.assignees.has(name)
);
reactive_declarations.forEach(declaration => {
declaration.dependencies.forEach(name => {
deps.add(name);
});
});
});
return Array.from(deps)
.map(n => x`$$invalidate('${n}', ${n})`)
.reduce((lhs, rhs) => x`${lhs}, ${rhs}}`);
invalidate(_name, _value?) {
throw new Error(`invalidate method now belongs to Renderer`);
}
rewrite_props(get_insert: (variable: Var) => Node[]) {
@ -1325,23 +1285,8 @@ export default class Component {
});
}
qualify(name) {
if (name === `$$props`) return x`#ctx.$$props`;
let [head, ...tail] = name.split('.');
const variable = this.var_lookup.get(head);
if (variable) {
this.add_reference(name); // TODO we can probably remove most other occurrences of this
if (!variable.hoistable) {
tail.unshift(head);
head = '#ctx';
}
}
return [head, ...tail].reduce((lhs, rhs) => x`${lhs}.${rhs}`);
qualify(_name) {
throw new Error(`component.qualify is now renderer.reference`);
}
warn_if_undefined(name: string, node, template_scope: TemplateScope) {

@ -14,7 +14,7 @@ export default class Action extends Node {
component.warn_if_undefined(info.name, info, scope);
this.name = info.name;
component.qualify(info.name);
component.add_reference(info.name.split('.')[0]);
this.expression = info.expression
? new Expression(component, this, scope, info.expression)

@ -13,7 +13,7 @@ export default class Animation extends Node {
component.warn_if_undefined(info.name, info, scope);
this.name = info.name;
component.qualify(info.name);
component.add_reference(info.name.split('.')[0]);
if (parent.animation) {
component.error(this, {

@ -15,7 +15,7 @@ export default class Transition extends Node {
component.warn_if_undefined(info.name, info, scope);
this.name = info.name;
component.qualify(info.name);
component.add_reference(info.name.split('.')[0]);
this.directive = info.intro && info.outro ? 'transition' : info.intro ? 'in' : 'out';
this.is_local = info.modifiers.includes('local');

@ -9,9 +9,9 @@ import TemplateScope from './TemplateScope';
import get_object from '../../utils/get_object';
import Block from '../../render_dom/Block';
import is_dynamic from '../../render_dom/wrappers/shared/is_dynamic';
import { x, b, p } from 'code-red';
import { invalidate } from '../../utils/invalidate';
import { Node, FunctionExpression } from 'estree';
import { x, b } from 'code-red';
import { invalidate } from '../../render_dom/invalidate';
import { Node, FunctionExpression, Identifier } from 'estree';
import { TemplateNode } from '../../../interfaces';
type Owner = Wrapper | TemplateNode;
@ -213,7 +213,9 @@ export default class Expression {
component.add_reference(name); // TODO is this redundant/misplaced?
}
} else if (is_contextual(component, template_scope, name)) {
this.replace(x`#ctx.${node}`);
if (block) { // TODO not sure what's going on here — DOM only, maybe?
this.replace(block.renderer.reference(name));
}
}
this.skip();
@ -260,42 +262,37 @@ export default class Expression {
// function can be hoisted inside the component init
component.partly_hoisted.push(declaration);
this.replace(x`#ctx.${id}` as any);
component.add_var({
name: id.name,
internal: true,
referenced: true
});
const i = block.renderer.add_to_context(id.name);
this.replace(x`#ctx[${i}]` as any);
}
else {
// we need a combo block/init recipe
(node as FunctionExpression).params.unshift({
type: 'ObjectPattern',
properties: Array.from(contextual_dependencies).map(name => p`${name}` as any)
});
const deps = Array.from(contextual_dependencies);
(node as FunctionExpression).params = [
...deps.map(name => ({ type: 'Identifier', name } as Identifier)),
...(node as FunctionExpression).params
];
const context_args = deps.map(name => block.renderer.reference(name));
component.partly_hoisted.push(declaration);
this.replace(id as any);
const i = block.renderer.add_to_context(id.name);
component.add_var({
name: id.name,
internal: true,
referenced: true
});
this.replace(id as any);
if ((node as FunctionExpression).params.length > 0) {
declarations.push(b`
function ${id}(...args) {
return #ctx.${id}(#ctx, ...args);
return #ctx[${i}](${context_args}, ...args);
}
`);
} else {
declarations.push(b`
function ${id}() {
return #ctx.${id}(#ctx);
return #ctx[${i}](${context_args});
}
`);
}
@ -329,7 +326,7 @@ export default class Expression {
}
});
this.replace(invalidate(component, scope, node, traced));
this.replace(invalidate(block.renderer, scope, node, traced));
}
}
});

@ -302,7 +302,7 @@ export default class Block {
properties.update = noop;
} else {
const ctx = this.maintain_context ? x`#new_ctx` : x`#ctx`;
properties.update = x`function #update(#changed, ${ctx}) {
properties.update = x`function #update(${ctx}, #changed) {
${this.maintain_context && b`#ctx = ${ctx};`}
${this.chunks.update}
}`;

@ -9,6 +9,8 @@ export default class Renderer {
component: Component; // TODO Maybe Renderer shouldn't know about Component?
options: CompileOptions;
context: string[] = [];
context_lookup: Map<string, number> = new Map();
blocks: Array<Block | Node | Node[]> = [];
readonly: Set<string> = new Set();
meta_bindings: Array<Node | Node[]> = []; // initial values for e.g. window.innerWidth, if there's a <svelte:window> meta tag
@ -27,6 +29,11 @@ export default class Renderer {
this.file_var = options.dev && this.component.get_unique_name('file');
// TODO sort vars, most frequently referenced first?
component.vars
.filter(v => ((v.referenced || v.export_name) && !v.hoistable))
.forEach(v => this.add_to_context(v.name));
// main block
this.block = new Block({
renderer: this,
@ -61,4 +68,93 @@ export default class Renderer {
this.fragment.render(this.block, null, x`#nodes` as Identifier);
}
add_to_context(name: string, contextual = false) {
if (!this.context_lookup.has(name)) {
const i = this.context.length;
this.context_lookup.set(name, i);
this.context.push(contextual ? null : name);
}
return this.context_lookup.get(name);
}
invalidate(name: string, value?) {
const variable = this.component.var_lookup.get(name);
const i = this.context_lookup.get(name);
if (variable && (variable.subscribable && (variable.reassigned || variable.export_name))) {
return x`${`$$subscribe_${name}`}($$invalidate(${i}, ${value || name}))`;
}
if (name[0] === '$' && name[1] !== '$') {
return x`${name.slice(1)}.set(${value || name})`;
}
if (
variable &&
!variable.referenced &&
!variable.is_reactive_dependency &&
!variable.export_name &&
!name.startsWith('$$')
) {
return value || name;
}
if (value) {
return x`$$invalidate(${i}, ${value})`;
}
// if this is a reactive declaration, invalidate dependencies recursively
const deps = new Set([name]);
deps.forEach(name => {
const reactive_declarations = this.component.reactive_declarations.filter(x =>
x.assignees.has(name)
);
reactive_declarations.forEach(declaration => {
declaration.dependencies.forEach(name => {
deps.add(name);
});
});
});
return Array.from(deps)
.map(n => x`$$invalidate(${i}, ${n})`)
.reduce((lhs, rhs) => x`${lhs}, ${rhs}}`);
}
changed(names) {
const bitmask = names.reduce((bits, name) => {
const bit = 1 << this.context_lookup.get(name);
return bits | bit;
}, 0);
return x`#changed & ${bitmask}`;
}
reference(name) {
const i = this.context_lookup.get(name);
if (name === `$$props`) return x`#ctx[${i}]`;
let [head, ...tail] = name.split('.');
const variable = this.component.var_lookup.get(head);
// TODO this feels woolly. might encounter false positive
// if each context shadows top-level var
if (variable) {
this.component.add_reference(name); // TODO we can probably remove most other occurrences of this
if (!variable.hoistable) {
head = x`#ctx[${i}]`;
}
} else {
head = x`#ctx[${i}]`;
}
return [head, ...tail].reduce((lhs, rhs) => x`${lhs}.${rhs}`);
}
}

@ -5,9 +5,9 @@ import { CompileOptions } from '../../interfaces';
import { walk } from 'estree-walker';
import add_to_set from '../utils/add_to_set';
import { extract_names } from '../utils/scope';
import { invalidate } from '../utils/invalidate';
import { invalidate } from './invalidate';
import Block from './Block';
import { ClassDeclaration, FunctionExpression, Node, Statement, ObjectExpression } from 'estree';
import { ClassDeclaration, FunctionExpression, Node, Statement, ObjectExpression, Expression } from 'estree';
export default function dom(
component: Component,
@ -80,12 +80,12 @@ export default function dom(
const set = (uses_props || writable_props.length > 0 || component.slots.size > 0)
? x`
${$$props} => {
${uses_props && component.invalidate('$$props', x`$$props = @assign(@assign({}, $$props), $$new_props)`)}
${uses_props && renderer.invalidate('$$props', x`$$props = @assign(@assign({}, $$props), $$new_props)`)}
${writable_props.map(prop =>
b`if ('${prop.export_name}' in ${$$props}) ${component.invalidate(prop.name, x`${prop.name} = ${$$props}.${prop.export_name}`)};`
b`if ('${prop.export_name}' in ${$$props}) ${renderer.invalidate(prop.name, x`${prop.name} = ${$$props}.${prop.export_name}`)};`
)}
${component.slots.size > 0 &&
b`if ('$$scope' in ${$$props}) ${component.invalidate('$$scope', x`$$scope = ${$$props}.$$scope`)};`}
b`if ('$$scope' in ${$$props}) ${renderer.invalidate('$$scope', x`$$scope = ${$$props}.$$scope`)};`}
}
`
: null;
@ -105,7 +105,7 @@ export default function dom(
kind: 'get',
key: { type: 'Identifier', name: prop.export_name },
value: x`function() {
return ${prop.hoistable ? prop.name : x`this.$$.ctx.${prop.name}`}
return ${prop.hoistable ? prop.name : x`this.$$.ctx[${renderer.context_lookup.get(prop.name)}]`}
}`
});
} else if (component.compile_options.dev) {
@ -180,9 +180,9 @@ export default function dom(
const writable_vars = component.vars.filter(variable => !variable.module && variable.writable);
inject_state = (uses_props || writable_vars.length > 0) ? x`
${$$props} => {
${uses_props && component.invalidate('$$props', x`$$props = @assign(@assign({}, $$props), $$new_props)`)}
${uses_props && renderer.invalidate('$$props', x`$$props = @assign(@assign({}, $$props), $$new_props)`)}
${writable_vars.map(prop => b`
if ('${prop.name}' in $$props) ${component.invalidate(prop.name, x`${prop.name} = ${$$props}.${prop.name}`)};
if ('${prop.name}' in $$props) ${renderer.invalidate(prop.name, x`${prop.name} = ${$$props}.${prop.name}`)};
`)}
}
` : x`
@ -216,17 +216,18 @@ export default function dom(
// onto the initial function call
const names = new Set(extract_names(assignee));
this.replace(invalidate(component, scope, node, names));
this.replace(invalidate(renderer, scope, node, names));
}
}
});
component.rewrite_props(({ name, reassigned, export_name }) => {
const value = `$${name}`;
const i = renderer.context_lookup.get(name);
const insert = (reassigned || export_name)
? b`${`$$subscribe_${name}`}()`
: b`@component_subscribe($$self, ${name}, #value => $$invalidate('${value}', ${value} = #value))`;
: b`@component_subscribe($$self, ${name}, #value => $$invalidate(${i}, ${value} = #value))`;
if (component.compile_options.dev) {
return b`@validate_store(${name}, '${name}'); ${insert}`;
@ -256,11 +257,13 @@ export default function dom(
${component.fully_hoisted}
`);
const filtered_declarations = component.vars
.filter(v => ((v.referenced || v.export_name) && !v.hoistable))
.map(v => p`${v.name}`);
const filtered_declarations = renderer.context
.map(name => name ? ({
type: 'Identifier',
name
}) as Expression : x`null`);
if (uses_props) filtered_declarations.push(p`$$props: $$props = @exclude_internal_props($$props)`);
if (uses_props) filtered_declarations.push(x`$$props = @exclude_internal_props($$props)`);
const filtered_props = props.filter(prop => {
const variable = component.var_lookup.get(prop.name);
@ -273,11 +276,11 @@ export default function dom(
const reactive_stores = component.vars.filter(variable => variable.name[0] === '$' && variable.name[1] !== '$');
if (component.slots.size > 0) {
filtered_declarations.push(p`$$slots`, p`$$scope`);
filtered_declarations.push(x`$$slots`, x`$$scope`);
}
if (renderer.binding_groups.length > 0) {
filtered_declarations.push(p`$$binding_groups`);
filtered_declarations.push(x`$$binding_groups`);
}
const instance_javascript = component.extract_javascript(component.ast.instance);
@ -307,7 +310,7 @@ export default function dom(
})
.map(({ name }) => b`
${component.compile_options.dev && b`@validate_store(${name.slice(1)}, '${name.slice(1)}');`}
@component_subscribe($$self, ${name.slice(1)}, $$value => $$invalidate('${name}', ${name} = $$value));
@component_subscribe($$self, ${name.slice(1)}, $$value => $$invalidate(${renderer.context_lookup.get(name)}, ${name} = $$value));
`);
const resubscribable_reactive_store_unsubscribers = reactive_stores
@ -330,9 +333,7 @@ export default function dom(
return variable && (variable.writable || variable.mutated);
});
const condition = !uses_props && writable.length > 0 && (writable
.map(n => x`#changed.${n}`)
.reduce((lhs, rhs) => x`${lhs} || ${rhs}`));
const condition = !uses_props && writable.length > 0 && renderer.changed(writable);
let statement = d.node; // TODO remove label (use d.node.body) if it's not referenced
@ -358,7 +359,9 @@ export default function dom(
if (store && (store.reassigned || store.export_name)) {
const unsubscribe = `$$unsubscribe_${name}`;
const subscribe = `$$subscribe_${name}`;
return b`let ${$name}, ${unsubscribe} = @noop, ${subscribe} = () => (${unsubscribe}(), ${unsubscribe} = @subscribe(${name}, $$value => $$invalidate('${$name}', ${$name} = $$value)), ${name})`;
const i = renderer.context_lookup.get($name);
return b`let ${$name}, ${unsubscribe} = @noop, ${subscribe} = () => (${unsubscribe}(), ${unsubscribe} = @subscribe(${name}, $$value => $$invalidate(${i}, ${$name} = $$value)), ${name})`;
}
return b`let ${$name};`;
@ -375,8 +378,8 @@ export default function dom(
}
const return_value = {
type: 'ObjectExpression',
properties: filtered_declarations
type: 'ArrayExpression',
elements: filtered_declarations
};
const reactive_dependencies = {

@ -1,10 +1,12 @@
import Component from '../Component';
import { nodes_match } from '../../utils/nodes_match';
import { Scope } from './scope';
import { Scope } from '../utils/scope';
import { x } from 'code-red';
import { Node } from 'estree';
import Renderer from './Renderer';
export function invalidate(renderer: Renderer, scope: Scope, node: Node, names: Set<string>) {
const { component } = renderer;
export function invalidate(component: Component, scope: Scope, node: Node, names: Set<string>) {
const [head, ...tail] = Array.from(names).filter(name => {
const owner = scope.find_owner(name);
if (owner && owner !== component.instance_scope) return false;
@ -28,12 +30,12 @@ export function invalidate(component: Component, scope: Scope, node: Node, names
component.has_reactive_assignments = true;
if (node.type === 'AssignmentExpression' && node.operator === '=' && nodes_match(node.left, node.right) && tail.length === 0) {
return component.invalidate(head);
return renderer.invalidate(head);
} else {
const is_store_value = head[0] === '$';
const variable = component.var_lookup.get(head);
const extra_args = tail.map(name => component.invalidate(name));
const extra_args = tail.map(name => renderer.invalidate(name));
const pass_value = (
extra_args.length > 0 ||
@ -48,8 +50,9 @@ export function invalidate(component: Component, scope: Scope, node: Node, names
});
}
const callee = is_store_value ? `@set_store_value` : `$$invalidate`;
let invalidate = x`${callee}(${is_store_value ? head.slice(1) : x`"${head}"`}, ${node}, ${extra_args})`;
let invalidate = is_store_value
? x`@set_store_value(${head.slice(1)}, ${node}, ${extra_args})`
: x`$$invalidate(${renderer.context_lookup.get(head)}, ${node}, ${extra_args})`;
if (variable.subscribable && variable.reassigned) {
const subscribe = `$$subscribe_${head}`;

@ -8,7 +8,6 @@ import FragmentWrapper from './Fragment';
import PendingBlock from '../../nodes/PendingBlock';
import ThenBlock from '../../nodes/ThenBlock';
import CatchBlock from '../../nodes/CatchBlock';
import { changed } from './shared/changed';
import { Identifier } from 'estree';
class AwaitBlockBranch extends Wrapper {
@ -187,7 +186,7 @@ export default class AwaitBlockWrapper extends Wrapper {
if (dependencies.length > 0) {
const condition = x`
${changed(dependencies)} &&
${block.renderer.changed(dependencies)} &&
${promise} !== (${promise} = ${snippet}) &&
@handle_promise(${promise}, ${info})`;
@ -200,7 +199,7 @@ export default class AwaitBlockWrapper extends Wrapper {
if (${condition}) {
// nothing
} else {
${info}.block.p(#changed, @assign(@assign({}, #ctx), ${info}.resolved));
${info}.block.p(@assign(@assign({}, #ctx), ${info}.resolved), #changed);
}
`);
} else {
@ -211,7 +210,7 @@ export default class AwaitBlockWrapper extends Wrapper {
} else {
if (this.pending.block.has_update_method) {
block.chunks.update.push(b`
${info}.block.p(#changed, @assign(@assign({}, #ctx), ${info}.resolved));
${info}.block.p(@assign(@assign({}, #ctx), ${info}.resolved), #changed);
`);
}
}

@ -5,7 +5,6 @@ import DebugTag from '../../nodes/DebugTag';
import add_to_set from '../../utils/add_to_set';
import { b, p } from 'code-red';
import { Identifier, DebuggerStatement } from 'estree';
import { changed } from './shared/changed';
export default class DebugTagWrapper extends Wrapper {
node: DebugTag;
@ -70,7 +69,7 @@ export default class DebugTagWrapper extends Wrapper {
debugger;`;
if (dependencies.size) {
const condition = changed(Array.from(dependencies));
const condition = renderer.changed(Array.from(dependencies));
block.chunks.update.push(b`
if (${condition}) {

@ -7,7 +7,6 @@ import FragmentWrapper from './Fragment';
import { b, x } from 'code-red';
import ElseBlock from '../../nodes/ElseBlock';
import { Identifier, Node } from 'estree';
import { changed } from './shared/changed';
export class ElseBlockWrapper extends Wrapper {
node: ElseBlock;
@ -81,6 +80,10 @@ export default class EachBlockWrapper extends Wrapper {
const { dependencies } = node.expression;
block.add_dependencies(dependencies);
this.node.contexts.forEach(context => {
renderer.add_to_context(context.key.name, true);
});
this.block = block.child({
comment: create_debugging_comment(this.node, this.renderer.component),
name: renderer.component.get_unique_name('create_each_block'),
@ -119,6 +122,9 @@ export default class EachBlockWrapper extends Wrapper {
const each_block_value = renderer.component.get_unique_name(`${this.var.name}_value`);
const iterations = block.get_unique_name(`${this.var.name}_blocks`);
renderer.add_to_context(each_block_value.name, true);
renderer.add_to_context(this.index_name.name, true);
this.vars = {
create_each_block: this.block.name,
each_block_value,
@ -190,18 +196,19 @@ export default class EachBlockWrapper extends Wrapper {
? !this.next.is_dom_node() :
!parent_node || !this.parent.is_dom_node();
this.context_props = this.node.contexts.map(prop => b`child_ctx.${prop.key.name} = ${prop.modifier(x`list[i]`)};`);
this.context_props = this.node.contexts.map(prop => b`child_ctx[${renderer.context_lookup.get(prop.key.name)}] = ${prop.modifier(x`list[i]`)};`);
if (this.node.has_binding) this.context_props.push(b`child_ctx.${this.vars.each_block_value} = list;`);
if (this.node.has_binding || this.node.index) this.context_props.push(b`child_ctx.${this.index_name} = i;`);
if (this.node.has_binding) this.context_props.push(b`child_ctx[${renderer.context_lookup.get(this.vars.each_block_value.name)}] = list;`);
if (this.node.has_binding || this.node.index) this.context_props.push(b`child_ctx[${renderer.context_lookup.get(this.index_name.name)}] = i;`);
const snippet = this.node.expression.manipulate(block);
block.chunks.init.push(b`let ${this.vars.each_block_value} = ${snippet};`);
// TODO which is better — Object.create(array) or array.slice()?
renderer.blocks.push(b`
function ${this.vars.get_each_context}(#ctx, list, i) {
const child_ctx = @_Object.create(#ctx);
const child_ctx = #ctx.slice();
${this.context_props}
return child_ctx;
}
@ -270,7 +277,7 @@ export default class EachBlockWrapper extends Wrapper {
if (this.else.block.has_update_method) {
block.chunks.update.push(b`
if (!${this.vars.data_length} && ${each_block_else}) {
${each_block_else}.p(#changed, #ctx);
${each_block_else}.p(#ctx, #changed);
} else if (!${this.vars.data_length}) {
${each_block_else} = ${this.else.block.name}(#ctx);
${each_block_else}.c();
@ -481,7 +488,7 @@ export default class EachBlockWrapper extends Wrapper {
const for_loop_body = this.block.has_update_method
? b`
if (${iterations}[#i]) {
${iterations}[#i].p(#changed, child_ctx);
${iterations}[#i].p(child_ctx, #changed);
${has_transitions && b`@transition_in(${this.vars.iterations}[#i], 1);`}
} else {
${iterations}[#i] = ${create_each_block}(child_ctx);
@ -554,7 +561,7 @@ export default class EachBlockWrapper extends Wrapper {
`;
block.chunks.update.push(b`
if (${changed(Array.from(all_dependencies))}) {
if (${block.renderer.changed(Array.from(all_dependencies))}) {
${update}
}
`);

@ -6,7 +6,6 @@ import { string_literal } from '../../../utils/stringify';
import { b, x } from 'code-red';
import Expression from '../../../nodes/shared/Expression';
import Text from '../../../nodes/Text';
import { changed } from '../shared/changed';
export default class AttributeWrapper {
node: Attribute;
@ -140,7 +139,7 @@ export default class AttributeWrapper {
}
if (dependencies.length > 0) {
let condition = changed(dependencies);
let condition = block.renderer.changed(dependencies);
if (should_cache) {
condition = is_src
@ -197,8 +196,8 @@ export default class AttributeWrapper {
}
let value = this.node.name === 'class'
? this.get_class_name_text()
: this.render_chunks().reduce((lhs, rhs) => x`${lhs} + ${rhs}`);
? this.get_class_name_text(block)
: this.render_chunks(block).reduce((lhs, rhs) => x`${lhs} + ${rhs}`);
// '{foo} {bar}' — treat as string concatenation
if (this.node.chunks[0].type !== 'Text') {
@ -208,9 +207,9 @@ export default class AttributeWrapper {
return value;
}
get_class_name_text() {
get_class_name_text(block) {
const scoped_css = this.node.chunks.some((chunk: Text) => chunk.synthetic);
const rendered = this.render_chunks();
const rendered = this.render_chunks(block);
if (scoped_css && rendered.length === 2) {
// we have a situation like class={possiblyUndefined}
@ -220,13 +219,13 @@ export default class AttributeWrapper {
return rendered.reduce((lhs, rhs) => x`${lhs} + ${rhs}`);
}
render_chunks() {
render_chunks(block: Block) {
return this.node.chunks.map((chunk) => {
if (chunk.type === 'Text') {
return string_literal(chunk.data);
}
return chunk.manipulate();
return chunk.manipulate(block);
});
}

@ -6,7 +6,6 @@ import Block from '../../Block';
import Renderer from '../../Renderer';
import flatten_reference from '../../../utils/flatten_reference';
import EachBlock from '../../../nodes/EachBlock';
import { changed } from '../shared/changed';
import { Node, Identifier } from 'estree';
export default class BindingWrapper {
@ -91,7 +90,7 @@ export default class BindingWrapper {
const dependency_array = [...this.node.expression.dependencies];
if (dependency_array.length > 0) {
update_conditions.push(changed(dependency_array));
update_conditions.push(block.renderer.changed(dependency_array));
}
if (parent.node.name === 'input') {

@ -15,6 +15,7 @@ export default class EventHandlerWrapper {
this.parent = parent;
if (!node.expression) {
// TODO use renderer.add_to_context
this.parent.renderer.component.add_var({
name: node.handler_name.name,
internal: true,

@ -7,7 +7,6 @@ import { string_literal } from '../../../utils/stringify';
import add_to_set from '../../../utils/add_to_set';
import Expression from '../../../nodes/shared/Expression';
import Text from '../../../nodes/Text';
import { changed } from '../shared/changed';
export interface StyleProp {
key: string;
@ -46,7 +45,7 @@ export default class StyleAttributeWrapper extends AttributeWrapper {
// }
if (prop_dependencies.size) {
let condition = changed(Array.from(prop_dependencies));
let condition = block.renderer.changed(Array.from(prop_dependencies));
if (block.has_outros) {
condition = x`!#current || ${condition}`;

@ -21,7 +21,6 @@ import add_actions from '../shared/add_actions';
import create_debugging_comment from '../shared/create_debugging_comment';
import { get_context_merger } from '../shared/get_context_merger';
import bind_this from '../shared/bind_this';
import { changed } from '../shared/changed';
import { is_head } from '../shared/is_head';
import { Identifier } from 'estree';
import EventHandler from './EventHandler';
@ -282,7 +281,7 @@ export default class ElementWrapper extends Wrapper {
const children = x`@children(${this.node.name === 'template' ? x`${node}.content` : node})`;
block.add_variable(node);
const render_statement = this.get_render_statement();
const render_statement = this.get_render_statement(block);
block.chunks.create.push(
b`${node} = ${render_statement};`
);
@ -398,7 +397,7 @@ export default class ElementWrapper extends Wrapper {
return this.is_static_content && this.fragment.nodes.every(node => node.node.type === 'Text' || node.node.type === 'MustacheTag');
}
get_render_statement() {
get_render_statement(block: Block) {
const { name, namespace } = this.node;
if (namespace === 'http://www.w3.org/2000/svg') {
@ -411,7 +410,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().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}")`;
@ -455,18 +454,13 @@ export default class ElementWrapper extends Wrapper {
groups.forEach(group => {
const handler = renderer.component.get_unique_name(`${this.var.name}_${group.events.join('_')}_handler`);
renderer.component.add_var({
name: handler.name,
internal: true,
referenced: true
});
const i = renderer.add_to_context(handler.name);
// TODO figure out how to handle locks
const needs_lock = group.bindings.some(binding => binding.needs_lock);
const dependencies = new Set();
const contextual_dependencies = new Set();
const dependencies: Set<string> = new Set();
const contextual_dependencies: Set<string> = new Set();
group.bindings.forEach(binding => {
// TODO this is a mess
@ -501,21 +495,21 @@ export default class ElementWrapper extends Wrapper {
${animation_frame} = @raf(${handler});
${needs_lock && b`${lock} = true;`}
}
#ctx.${handler}.call(${this.var}, ${contextual_dependencies.size > 0 ? '#ctx' : null});
#ctx[${i}].call(${this.var}, ${contextual_dependencies.size > 0 ? '#ctx' : null});
}
`);
} else {
block.chunks.init.push(b`
function ${handler}() {
${needs_lock && b`${lock} = true;`}
#ctx.${handler}.call(${this.var}, ${contextual_dependencies.size > 0 ? '#ctx' : null});
#ctx[${i}].call(${this.var}, ${contextual_dependencies.size > 0 ? '#ctx' : null});
}
`);
}
callee = handler;
} else {
callee = x`#ctx.${handler}`;
callee = x`#ctx[${i}]`;
}
const arg = contextual_dependencies.size > 0 && {
@ -537,7 +531,7 @@ export default class ElementWrapper extends Wrapper {
${Array.from(dependencies)
.filter(dep => dep[0] !== '$')
.filter(dep => !contextual_dependencies.has(dep))
.map(dep => b`${this.renderer.component.invalidate(dep)};`)}
.map(dep => b`${this.renderer.invalidate(dep)};`)}
}
`);
@ -632,7 +626,7 @@ export default class ElementWrapper extends Wrapper {
this.attributes
.forEach(attr => {
const condition = attr.node.dependencies.size > 0
? changed(Array.from(attr.node.dependencies))
? block.renderer.changed(Array.from(attr.node.dependencies))
: null;
if (attr.node.is_spread) {
@ -685,8 +679,6 @@ export default class ElementWrapper extends Wrapper {
const { intro, outro } = this.node;
if (!intro && !outro) return;
const { component } = this.renderer;
if (intro === outro) {
// bidirectional transition
const name = block.get_unique_name(`${this.var.name}_transition`);
@ -696,7 +688,7 @@ export default class ElementWrapper extends Wrapper {
block.add_variable(name);
const fn = component.qualify(intro.name);
const fn = this.renderer.reference(intro.name);
const intro_block = b`
@add_render_callback(() => {
@ -740,7 +732,7 @@ export default class ElementWrapper extends Wrapper {
? intro.expression.manipulate(block)
: x`{}`;
const fn = component.qualify(intro.name);
const fn = this.renderer.reference(intro.name);
let intro_block;
@ -782,7 +774,7 @@ export default class ElementWrapper extends Wrapper {
? outro.expression.manipulate(block)
: x`{}`;
const fn = component.qualify(outro.name);
const fn = this.renderer.reference(outro.name);
if (!intro) {
block.chunks.intro.push(b`
@ -814,7 +806,6 @@ export default class ElementWrapper extends Wrapper {
add_animation(block: Block) {
if (!this.node.animation) return;
const { component } = this.renderer;
const { outro } = this.node;
const rect = block.get_unique_name('rect');
@ -835,7 +826,7 @@ export default class ElementWrapper extends Wrapper {
const params = this.node.animation.expression ? this.node.animation.expression.manipulate(block) : x`{}`;
const name = component.qualify(this.node.animation.name);
const name = this.renderer.reference(this.node.animation.name);
block.chunks.animate.push(b`
${stop_animation}();
@ -844,7 +835,7 @@ export default class ElementWrapper extends Wrapper {
}
add_actions(block: Block) {
add_actions(this.renderer.component, block, this.var, this.node.actions);
add_actions(block, this.var, this.node.actions);
}
add_classes(block: Block) {
@ -868,7 +859,7 @@ export default class ElementWrapper extends Wrapper {
block.chunks.update.push(updater);
} else if ((dependencies && dependencies.size > 0) || this.class_dependencies.length) {
const all_dependencies = this.class_dependencies.concat(...dependencies);
const condition = changed(all_dependencies);
const condition = block.renderer.changed(all_dependencies);
block.chunks.update.push(b`
if (${condition}) {

@ -10,7 +10,6 @@ import { b, x } from 'code-red';
import { walk } from 'estree-walker';
import { is_head } from './shared/is_head';
import { Identifier, Node } from 'estree';
import { changed } from './shared/changed';
function is_else_if(node: ElseBlock) {
return (
@ -272,7 +271,7 @@ export default class IfBlockWrapper extends Wrapper {
? b`
${snippet && (
dependencies.length > 0
? b`if (${condition} == null || ${changed(dependencies)}) ${condition} = !!${snippet}`
? b`if (${condition} == null || ${block.renderer.changed(dependencies)}) ${condition} = !!${snippet}`
: b`if (${condition} == null) ${condition} = !!${snippet}`
)}
if (${condition}) return ${block.name};`
@ -323,21 +322,21 @@ export default class IfBlockWrapper extends Wrapper {
if (dynamic) {
block.chunks.update.push(b`
if (${current_block_type} === (${current_block_type} = ${select_block_type}(#changed, #ctx)) && ${name}) {
${name}.p(#changed, #ctx);
if (${current_block_type} === (${current_block_type} = ${select_block_type}(#ctx, #changed)) && ${name}) {
${name}.p(#ctx, #changed);
} else {
${change_block}
}
`);
} else {
block.chunks.update.push(b`
if (${current_block_type} !== (${current_block_type} = ${select_block_type}(#changed, #ctx))) {
if (${current_block_type} !== (${current_block_type} = ${select_block_type}(#ctx, #changed))) {
${change_block}
}
`);
}
} else if (dynamic) {
block.chunks.update.push(b`${name}.p(#changed, #ctx);`);
block.chunks.update.push(b`${name}.p(#ctx, #changed);`);
}
if (if_exists_condition) {
@ -391,7 +390,7 @@ export default class IfBlockWrapper extends Wrapper {
? b`
${snippet && (
dependencies.length > 0
? b`if (${condition} == null || ${changed(dependencies)}) ${condition} = !!${snippet}`
? b`if (${condition} == null || ${block.renderer.changed(dependencies)}) ${condition} = !!${snippet}`
: b`if (${condition} == null) ${condition} = !!${snippet}`
)}
if (${condition}) return ${i};`
@ -476,7 +475,7 @@ export default class IfBlockWrapper extends Wrapper {
let ${previous_block_index} = ${current_block_type_index};
${current_block_type_index} = ${select_block_type}(#changed, #ctx);
if (${current_block_type_index} === ${previous_block_index}) {
${if_current_block_type_index(b`${if_blocks}[${current_block_type_index}].p(#changed, #ctx);`)}
${if_current_block_type_index(b`${if_blocks}[${current_block_type_index}].p(#ctx, #changed);`)}
} else {
${change_block}
}
@ -491,7 +490,7 @@ export default class IfBlockWrapper extends Wrapper {
`);
}
} else if (dynamic) {
block.chunks.update.push(b`${name}.p(#changed, #ctx);`);
block.chunks.update.push(b`${name}.p(#ctx, #changed);`);
}
block.chunks.destroy.push(
@ -528,7 +527,7 @@ export default class IfBlockWrapper extends Wrapper {
const enter = dynamic
? b`
if (${name}) {
${name}.p(#changed, #ctx);
${name}.p(#ctx, #changed);
${has_transitions && b`@transition_in(${name}, 1);`}
} else {
${name} = ${branch.block.name}(#ctx);
@ -549,7 +548,7 @@ export default class IfBlockWrapper extends Wrapper {
`;
if (branch.snippet) {
block.chunks.update.push(b`if (${changed(branch.dependencies)}) ${branch.condition} = ${branch.snippet}`);
block.chunks.update.push(b`if (${block.renderer.changed(branch.dependencies)}) ${branch.condition} = ${branch.snippet}`);
}
// no `p()` here — we don't want to update outroing nodes,
@ -578,7 +577,7 @@ export default class IfBlockWrapper extends Wrapper {
}
} else if (dynamic) {
block.chunks.update.push(b`
if (${branch.condition}) ${name}.p(#changed, #ctx);
if (${branch.condition}) ${name}.p(#ctx, #changed);
`);
}

@ -14,7 +14,6 @@ import EachBlock from '../../../nodes/EachBlock';
import TemplateScope from '../../../nodes/shared/TemplateScope';
import is_dynamic from '../shared/is_dynamic';
import bind_this from '../shared/bind_this';
import { changed } from '../shared/changed';
import { Node, Identifier, ObjectExpression } from 'estree';
import EventHandler from '../Element/EventHandler';
@ -206,7 +205,7 @@ export default class InlineComponentWrapper extends Wrapper {
const { name, dependencies } = attr;
const condition = dependencies.size > 0 && (dependencies.size !== all_dependencies.size)
? changed(Array.from(dependencies))
? renderer.changed(Array.from(dependencies))
: null;
if (attr.is_spread) {
@ -239,7 +238,7 @@ export default class InlineComponentWrapper extends Wrapper {
`);
if (all_dependencies.size) {
const condition = changed(Array.from(all_dependencies));
const condition = renderer.changed(Array.from(all_dependencies));
updates.push(b`
const ${name_changes} = ${condition} ? @get_spread_update(${levels}, [
@ -255,7 +254,7 @@ export default class InlineComponentWrapper extends Wrapper {
dynamic_attributes.forEach((attribute: Attribute) => {
const dependencies = attribute.get_dependencies();
if (dependencies.length > 0) {
const condition = changed(dependencies);
const condition = renderer.changed(dependencies);
updates.push(b`
if (${condition}) ${name_changes}.${attribute.name} = ${attribute.get_value(block)};
@ -267,7 +266,7 @@ export default class InlineComponentWrapper extends Wrapper {
if (non_let_dependencies.length > 0) {
updates.push(b`
if (${changed(non_let_dependencies)}) {
if (${renderer.changed(non_let_dependencies)}) {
${name_changes}.$$scope = { changed: #changed, ctx: #ctx };
}`);
}
@ -280,12 +279,7 @@ export default class InlineComponentWrapper extends Wrapper {
}
const id = component.get_unique_name(`${this.var.name}_${binding.name}_binding`);
component.add_var({
name: id.name,
internal: true,
referenced: true
});
const i = renderer.add_to_context(id.name);
const updating = block.get_unique_name(`updating_${binding.name}`);
block.add_variable(updating);
@ -299,7 +293,7 @@ export default class InlineComponentWrapper extends Wrapper {
);
updates.push(b`
if (!${updating} && ${changed(Array.from(binding.expression.dependencies))}) {
if (!${updating} && ${renderer.changed(Array.from(binding.expression.dependencies))}) {
${updating} = true;
${name_changes}.${binding.name} = ${snippet};
@add_flush_callback(() => ${updating} = false);
@ -338,7 +332,7 @@ export default class InlineComponentWrapper extends Wrapper {
block.chunks.init.push(b`
function ${id}(${value}) {
#ctx.${id}.call(null, ${value}, #ctx);
#ctx[${i}].call(null, ${value}, #ctx);
}
`);
@ -346,7 +340,7 @@ export default class InlineComponentWrapper extends Wrapper {
} else {
block.chunks.init.push(b`
function ${id}(${value}) {
#ctx.${id}.call(null, ${value});
#ctx[${i}].call(null, ${value});
}
`);
}
@ -354,7 +348,7 @@ export default class InlineComponentWrapper extends Wrapper {
const body = b`
function ${id}(${args}) {
${lhs} = ${value};
${component.invalidate(dependencies[0])};
${renderer.invalidate(dependencies[0])};
}
`;
@ -460,7 +454,7 @@ export default class InlineComponentWrapper extends Wrapper {
} else {
const expression = this.node.name === 'svelte:self'
? component.name
: component.qualify(this.node.name);
: this.renderer.reference(this.node.name);
block.chunks.init.push(b`
${(this.node.attributes.length > 0 || this.node.bindings.length > 0) && b`

@ -10,7 +10,6 @@ import get_slot_data from '../../utils/get_slot_data';
import Expression from '../../nodes/shared/Expression';
import is_dynamic from './shared/is_dynamic';
import { Identifier, ObjectExpression } from 'estree';
import { changed } from './shared/changed';
export default class SlotWrapper extends Wrapper {
node: Slot;
@ -184,10 +183,10 @@ export default class SlotWrapper extends Wrapper {
});
block.chunks.update.push(b`
if (${slot} && ${slot}.p && ${changed(dynamic_dependencies)}) {
if (${slot} && ${slot}.p && ${renderer.changed(dynamic_dependencies)}) {
${slot}.p(
@get_slot_changes(${slot_definition}, #ctx, #changed, ${get_slot_changes}),
@get_slot_context(${slot_definition}, #ctx, ${get_slot_context})
@get_slot_context(${slot_definition}, #ctx, ${get_slot_context}),
@get_slot_changes(${slot_definition}, #ctx, #changed, ${get_slot_changes})
);
}
`);

@ -7,7 +7,6 @@ import { string_literal } from '../../utils/stringify';
import add_to_set from '../../utils/add_to_set';
import Text from '../../nodes/Text';
import { Identifier } from 'estree';
import { changed } from './shared/changed';
import MustacheTag from '../../nodes/MustacheTag';
export default class TitleWrapper extends Wrapper {
@ -76,7 +75,7 @@ export default class TitleWrapper extends Wrapper {
if (all_dependencies.size) {
const dependencies = Array.from(all_dependencies);
let condition = changed(dependencies);
let condition = block.renderer.changed(dependencies);
if (block.has_outros) {
condition = x`!#current || ${condition}`;

@ -5,7 +5,6 @@ import { b, x } from 'code-red';
import add_event_handlers from './shared/add_event_handlers';
import Window from '../../nodes/Window';
import add_actions from './shared/add_actions';
import { changed } from './shared/changed';
import { Identifier } from 'estree';
import { TemplateNode } from '../../../interfaces';
import EventHandler from './Element/EventHandler';
@ -49,7 +48,7 @@ export default class WindowWrapper extends Wrapper {
const events = {};
const bindings: Record<string, string> = {};
add_actions(component, block, '@_window', this.node.actions);
add_actions(block, '@_window', this.node.actions);
add_event_handlers(block, '@_window', this.handlers);
this.node.bindings.forEach(binding => {
@ -122,20 +121,16 @@ export default class WindowWrapper extends Wrapper {
`);
}
component.add_var({
name: id.name,
internal: true,
referenced: true
});
const i = renderer.add_to_context(id.name);
component.partly_hoisted.push(b`
function ${id}() {
${props.map(prop => component.invalidate(prop.name, x`${prop.name} = @_window.${prop.value}`))}
${props.map(prop => renderer.invalidate(prop.name, x`${prop.name} = @_window.${prop.value}`))}
}
`);
block.chunks.init.push(b`
@add_render_callback(#ctx.${id});
@add_render_callback(#ctx[${i}]);
`);
component.has_reactive_assignments = true;
@ -143,7 +138,7 @@ export default class WindowWrapper extends Wrapper {
// special case... might need to abstract this out if we add more special cases
if (bindings.scrollX || bindings.scrollY) {
const condition = changed([bindings.scrollX, bindings.scrollY].filter(Boolean));
const condition = renderer.changed([bindings.scrollX, bindings.scrollY].filter(Boolean));
const scrollX = bindings.scrollX ? x`#ctx.${bindings.scrollX}` : x`@_window.pageXOffset`;
const scrollY = bindings.scrollY ? x`#ctx.${bindings.scrollY}` : x`@_window.pageYOffset`;
@ -162,25 +157,21 @@ export default class WindowWrapper extends Wrapper {
const id = block.get_unique_name(`onlinestatuschanged`);
const name = bindings.online;
component.add_var({
name: id.name,
internal: true,
referenced: true
});
const i = renderer.add_to_context(id.name);
component.partly_hoisted.push(b`
function ${id}() {
${component.invalidate(name, x`${name} = @_navigator.onLine`)}
${renderer.invalidate(name, x`${name} = @_navigator.onLine`)}
}
`);
block.chunks.init.push(b`
@add_render_callback(#ctx.${id});
@add_render_callback(#ctx[${i}]);
`);
block.event_listeners.push(
x`@listen(@_window, "online", #ctx.${id})`,
x`@listen(@_window, "offline", #ctx.${id})`
x`@listen(@_window, "online", #ctx[${i}])`,
x`@listen(@_window, "offline", #ctx[${i}])`
);
component.has_reactive_assignments = true;

@ -5,7 +5,6 @@ import Block from '../../Block';
import MustacheTag from '../../../nodes/MustacheTag';
import RawMustacheTag from '../../../nodes/RawMustacheTag';
import { Node } from 'estree';
import { changed } from './changed';
export default class Tag extends Wrapper {
node: MustacheTag | RawMustacheTag;
@ -40,7 +39,7 @@ export default class Tag extends Wrapper {
if (this.node.should_cache) block.add_variable(value, snippet); // TODO may need to coerce snippet to string
if (dependencies.length > 0) {
let condition = changed(dependencies);
let condition = block.renderer.changed(dependencies);
if (block.has_outros) {
condition = x`!#current || ${condition}`;

@ -1,10 +1,8 @@
import { b, x } from 'code-red';
import Block from '../../Block';
import Action from '../../../nodes/Action';
import Component from '../../../Component';
export default function add_actions(
component: Component,
block: Block,
target: string,
actions: Action[]
@ -25,7 +23,7 @@ export default function add_actions(
block.add_variable(id);
const fn = component.qualify(action.name);
const fn = block.renderer.reference(action.name);
block.chunks.mount.push(
b`${id} = ${fn}.call(null, ${target}, ${snippet}) || {};`
@ -34,14 +32,8 @@ export default function add_actions(
if (dependencies && dependencies.length > 0) {
let condition = x`@is_function(${id}.update)`;
// TODO can this case be handled more elegantly?
if (dependencies.length > 0) {
let changed = x`#changed.${dependencies[0]}`;
for (let i = 1; i < dependencies.length; i += 1) {
changed = x`${changed} || #changed.${dependencies[i]}`;
}
condition = x`${condition} && ${changed}`;
condition = x`${condition} && ${block.renderer.changed(dependencies)}`;
}
block.chunks.update.push(

@ -7,12 +7,7 @@ import { Identifier } from 'estree';
export default function bind_this(component: Component, block: Block, binding: Binding, variable: Identifier) {
const fn = component.get_unique_name(`${variable.name}_binding`);
component.add_var({
name: fn.name,
internal: true,
referenced: true
});
const i = block.renderer.add_to_context(fn.name);
let lhs;
let object;
@ -32,11 +27,11 @@ export default function bind_this(component: Component, block: Block, binding: B
body = binding.raw_expression.type === 'Identifier'
? b`
${component.invalidate(object, x`${lhs} = $$value`)};
${block.renderer.invalidate(object, x`${lhs} = $$value`)};
`
: b`
${lhs} = $$value;
${component.invalidate(object)};
${block.renderer.invalidate(object)};
`;
}
@ -65,12 +60,12 @@ export default function bind_this(component: Component, block: Block, binding: B
const unassign = block.get_unique_name(`unassign_${variable.name}`);
block.chunks.init.push(b`
const ${assign} = () => #ctx.${fn}(${variable}, ${args});
const ${unassign} = () => #ctx.${fn}(null, ${args});
const ${assign} = () => #ctx[${i}](${variable}, ${args});
const ${unassign} = () => #ctx[${i}](null, ${args});
`);
const condition = Array.from(contextual_dependencies)
.map(name => x`${name} !== #ctx.${name}`)
.map(name => x`${name} !== #ctx.${name}`) // TODO figure out contextual deps
.reduce((lhs, rhs) => x`${lhs} || ${rhs}`);
// we push unassign and unshift assign so that references are
@ -96,6 +91,6 @@ export default function bind_this(component: Component, block: Block, binding: B
}
`);
block.chunks.destroy.push(b`#ctx.${fn}(null);`);
return b`#ctx.${fn}(${variable});`;
block.chunks.destroy.push(b`#ctx[${i}](null);`);
return b`#ctx[${i}](${variable});`;
}

@ -1,7 +0,0 @@
import { x } from 'code-red';
export function changed(dependencies: string[]) {
return dependencies
.map(d => x`#changed.${d}`)
.reduce((lhs, rhs) => x`${lhs} || ${rhs}`);
}

@ -21,7 +21,7 @@ interface Fragment {
}
// eslint-disable-next-line @typescript-eslint/class-name-casing
interface T$$ {
dirty: null;
dirty: number;
ctx: null|any;
bound: any;
update: () => void;
@ -87,13 +87,13 @@ export function destroy_component(component, detaching) {
}
}
function make_dirty(component, key) {
if (!component.$$.dirty) {
function make_dirty(component, i) {
if (component.$$.dirty === -1) {
dirty_components.push(component);
schedule_update();
component.$$.dirty = blank_object();
component.$$.dirty = 0;
}
component.$$.dirty[key] = true;
component.$$.dirty |= (1 << i);
}
export function init(component, options, instance, create_fragment, not_equal, props) {
@ -121,16 +121,16 @@ export function init(component, options, instance, create_fragment, not_equal, p
// everything else
callbacks: blank_object(),
dirty: null
dirty: -1
};
let ready = false;
$$.ctx = instance
? instance(component, prop_values, (key, ret, value = ret) => {
if ($$.ctx && not_equal($$.ctx[key], $$.ctx[key] = value)) {
if ($$.bound[key]) $$.bound[key](value);
if (ready) make_dirty(component, key);
? instance(component, prop_values, (i, ret, value = ret) => {
if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = value)) {
if ($$.bound[i]) $$.bound[i](value);
if (ready) make_dirty(component, i);
}
return ret;
})

@ -43,7 +43,7 @@ export function update_keyed_each(old_blocks, changed, get_key, dynamic, ctx, li
block = create_each_block(key, child_ctx);
block.c();
} else if (dynamic) {
block.p(changed, child_ctx);
block.p(child_ctx, changed);
}
new_lookup.set(key, new_blocks[i] = block);

@ -73,8 +73,8 @@ function update($$) {
if ($$.fragment !== null) {
$$.update($$.dirty);
run_all($$.before_update);
$$.fragment && $$.fragment.p($$.dirty, $$.ctx);
$$.dirty = null;
$$.fragment && $$.fragment.p($$.ctx, $$.dirty);
$$.dirty = -1;
$$.after_update.forEach(add_render_callback);
}

@ -23,8 +23,8 @@ function create_fragment(ctx) {
insert(target, button, anchor);
foo_action = foo.call(null, button, ctx[1]) || ({});
},
p(ctx) {
if (is_function(foo_action.update)) foo_action.update.call(null, ctx[1]);
p(ctx, changed) {
if (is_function(foo_action.update) && changed.bar) foo_action.update.call(null, ctx[1]);
},
i: noop,
o: noop,

@ -26,7 +26,7 @@ process.on('unhandledRejection', err => {
unhandled_rejection = err;
});
describe("runtime", () => {
describe.only("runtime", () => {
before(() => {
svelte = loadSvelte(false);
svelte$ = loadSvelte(true);

@ -1,7 +1,7 @@
export default {
html: '<button>0, 0</button>',
async test({ assert, component, target, window }) {
async test({ assert, target, window }) {
const event = new window.MouseEvent('click', {
clientX: 42,
clientY: 42

Loading…
Cancel
Save