diff --git a/src/compile/nodes/Element.ts b/src/compile/nodes/Element.ts index 406e9ec174..7a3e9b340e 100644 --- a/src/compile/nodes/Element.ts +++ b/src/compile/nodes/Element.ts @@ -658,10 +658,10 @@ export default class Element extends Node { const slot = this.attributes.find(attribute => attribute.name === 'slot'); if (slot) { const prop = quotePropIfNecessary(slot.chunks[0].data); - return `@append(${name}.$$slotted${prop}, ${this.var});`; + return `@append(${name}.$$.slotted${prop}, ${this.var});`; } - return `@append(${name}.$$slotted.default, ${this.var});`; + return `@append(${name}.$$.slotted.default, ${this.var});`; } addCssClass(className = this.component.stylesheet.id) { diff --git a/src/compile/nodes/EventHandler.ts b/src/compile/nodes/EventHandler.ts index 04163d8d28..73f59be939 100644 --- a/src/compile/nodes/EventHandler.ts +++ b/src/compile/nodes/EventHandler.ts @@ -1,8 +1,5 @@ import Node from './shared/Node'; import Expression from './shared/Expression'; -import flattenReference from '../../utils/flattenReference'; -import { createScopes } from '../../utils/annotateWithScopes'; -import { walk } from 'estree-walker'; import Component from '../Component'; import deindent from '../../utils/deindent'; diff --git a/src/compile/nodes/shared/Node.ts b/src/compile/nodes/shared/Node.ts index 4ce51b46c9..d08efe1fcc 100644 --- a/src/compile/nodes/shared/Node.ts +++ b/src/compile/nodes/shared/Node.ts @@ -51,7 +51,7 @@ export default class Node { } remount(name: string) { - return `${this.var}.m(${name}.$$slotted.default, null);`; + return `${this.var}.m(${name}.$$.slotted.default, null);`; } warnIfEmptyBlock() { diff --git a/src/compile/render-dom/index.ts b/src/compile/render-dom/index.ts index 16a9f8f5d7..ea6c21462d 100644 --- a/src/compile/render-dom/index.ts +++ b/src/compile/render-dom/index.ts @@ -72,35 +72,19 @@ export default function dom( return ${JSON.stringify(props)}; } - ${props.map(prop => deindent` - get ${prop}() { - return this.get().${prop}; - } - - set ${prop}(value) { - this.set({ ${prop}: value }); - } - `).join('\n\n')} - ${renderer.slots.size && deindent` - connectedCallback() { - Object.keys(this.$$slotted).forEach(key => { - this.appendChild(this.$$slotted[key]); - }); - }`} + connectedCallback() { + Object.keys(this.$$.slotted).forEach(key => { + this.appendChild(this.$$.slotted[key]); + }); + }`} attributeChangedCallback(attr, oldValue, newValue) { - this.set({ [attr]: newValue }); + this[attr] = newValue; } - - ${(component.hasComponents || renderer.hasComplexBindings || templateProperties.oncreate || renderer.hasIntroTransitions) && deindent` - connectedCallback() { - @flush(this); - } - `} } - customElements.define("${component.tag}", ${name}); + customElements.define("${component.customElement.tag}", ${name}); `); } else { const refs = Array.from(component.refs); @@ -123,7 +107,7 @@ export default function dom( const props = component.exports.filter(x => component.writable_declarations.has(x.name)); - const inject_props = component.meta.props || props.length > 0 + const set = component.meta.props || props.length > 0 ? deindent` $$props => { ${component.meta.props && deindent` @@ -135,7 +119,7 @@ export default function dom( `if ('${prop.as}' in $$props) ${prop.name} = $$props.${prop.as};`)} } ` - : `@noop`; + : null; const inject_refs = refs.length > 0 ? deindent` @@ -143,7 +127,7 @@ export default function dom( ${refs.map(name => `${name} = $$refs.${name};`)} } ` - : `@noop`; + : null; const body = []; @@ -160,7 +144,7 @@ export default function dom( if (expected.length) { body.push(deindent` $$checkProps() { - const state = this.$$.get_state(); + const state = this.$$.get(); ${expected.map(name => deindent` if (state.${name} === undefined) { @@ -175,7 +159,7 @@ export default function dom( component.exports.forEach(x => { body.push(deindent` get ${x.as}() { - return this.$$.get_state().${x.name}; + return this.$$.get().${x.name}; } `); @@ -212,12 +196,12 @@ export default function dom( ${component.partly_hoisted.length > 0 && component.partly_hoisted.join('\n\n')} - return [ - // TODO only what's needed by the template - () => ({ ${component.declarations.join(', ')} }), - ${inject_props}, - ${inject_refs} - ]; + // TODO only what's needed by the template + $$self.$$.get = () => ({ ${component.declarations.join(', ')} }); + + ${set && `$$self.$$.set = ${set};`} + + ${inject_refs && `$$self.$$.inject_refs = ${inject_refs};`} } class ${name} extends ${superclass} { diff --git a/src/compile/render-dom/wrappers/EachBlock.ts b/src/compile/render-dom/wrappers/EachBlock.ts index 37f36abdf3..b92423a8ba 100644 --- a/src/compile/render-dom/wrappers/EachBlock.ts +++ b/src/compile/render-dom/wrappers/EachBlock.ts @@ -515,6 +515,6 @@ export default class EachBlockWrapper extends Wrapper { remount(name: string) { // TODO consider keyed blocks - return `for (var #i = 0; #i < ${this.vars.iterations}.length; #i += 1) ${this.vars.iterations}[#i].m(${name}.$$slotted.default, null);`; + return `for (var #i = 0; #i < ${this.vars.iterations}.length; #i += 1) ${this.vars.iterations}[#i].m(${name}.$$.slotted.default, null);`; } } \ No newline at end of file diff --git a/src/compile/render-dom/wrappers/Element/Binding.ts b/src/compile/render-dom/wrappers/Element/Binding.ts index 7b787b0d4f..43845c0db4 100644 --- a/src/compile/render-dom/wrappers/Element/Binding.ts +++ b/src/compile/render-dom/wrappers/Element/Binding.ts @@ -110,11 +110,11 @@ export default class BindingWrapper { const bindingGroup = getBindingGroup(renderer, this.node.expression.node); block.builders.hydrate.addLine( - `(#component.$$bindingGroups[${bindingGroup}] || (#component.$$bindingGroups[${bindingGroup}] = [])).push(${parent.var});` + `(#component.$$.binding_groups[${bindingGroup}] || (#component.$$.binding_groups[${bindingGroup}] = [])).push(${parent.var});` ); block.builders.destroy.addLine( - `#component.$$bindingGroups[${bindingGroup}].splice(#component.$$bindingGroups[${bindingGroup}].indexOf(${parent.var}), 1);` + `#component.$$.binding_groups[${bindingGroup}].splice(#component.$$.binding_groups[${bindingGroup}].indexOf(${parent.var}), 1);` ); } @@ -273,7 +273,7 @@ function getValueFromDom( const bindingGroup = getBindingGroup(renderer, binding.node.expression.node); if (type === 'checkbox') { renderer.component.init_uses_self = true; - return `@getBindingGroupValue($$self.$$bindingGroups[${bindingGroup}])`; + return `@getBindingGroupValue($$self.$$.binding_groups[${bindingGroup}])`; } return `this.__value`; diff --git a/src/compile/render-dom/wrappers/Element/index.ts b/src/compile/render-dom/wrappers/Element/index.ts index 85cc3b19b7..358820ce3c 100644 --- a/src/compile/render-dom/wrappers/Element/index.ts +++ b/src/compile/render-dom/wrappers/Element/index.ts @@ -216,7 +216,7 @@ export default class ElementWrapper extends Wrapper { let initialMountNode; if (this.slotOwner) { - initialMountNode = `${this.slotOwner.var}.$$slotted${prop}`; + initialMountNode = `${this.slotOwner.var}.$$.slotted${prop}`; } else { initialMountNode = parentNode; } @@ -635,7 +635,7 @@ export default class ElementWrapper extends Wrapper { } addRef(block: Block) { - const ref = `#component.$$refs.${this.node.ref.name}`; + const ref = `#component.$$.refs.${this.node.ref.name}`; block.builders.mount.addLine( `${ref} = ${this.var};` @@ -644,7 +644,7 @@ export default class ElementWrapper extends Wrapper { block.builders.destroy.addLine( `if (${ref} === ${this.var}) { ${ref} = null; - #component.$$.inject_refs(#component.$$refs); + #component.$$.inject_refs(#component.$$.refs); }` ); } @@ -817,10 +817,10 @@ export default class ElementWrapper extends Wrapper { const slot = this.attributes.find(attribute => attribute.node.name === 'slot'); if (slot) { const prop = quotePropIfNecessary(slot.node.chunks[0].data); - return `@append(${name}.$$slotted${prop}, ${this.var});`; + return `@append(${name}.$$.slotted${prop}, ${this.var});`; } - return `@append(${name}.$$slotted.default, ${this.var});`; + return `@append(${name}.$$.slotted.default, ${this.var});`; } addCssClass(className = this.component.stylesheet.id) { diff --git a/src/compile/render-dom/wrappers/InlineComponent/index.ts b/src/compile/render-dom/wrappers/InlineComponent/index.ts index 81ff8b1d2b..c19c9a6f27 100644 --- a/src/compile/render-dom/wrappers/InlineComponent/index.ts +++ b/src/compile/render-dom/wrappers/InlineComponent/index.ts @@ -88,7 +88,7 @@ export default class InlineComponentWrapper extends Wrapper { componentInitProperties.push(`slots: { ${slots.join(', ')} }`); this.fragment.nodes.forEach((child: Wrapper) => { - child.render(block, `${this.var}.$$slotted.default`, 'nodes'); + child.render(block, `${this.var}.$$.slotted.default`, 'nodes'); }); } @@ -257,7 +257,7 @@ export default class InlineComponentWrapper extends Wrapper { component.partly_hoisted.push(body); - return `@add_binding_callback(() => ${this.var}.$$bind('${binding.name}', ${name}));`; + return `@add_binding_callback(() => @bind(${this.var}, '${binding.name}', ${name}));`; }); const munged_handlers = this.node.handlers.map(handler => { @@ -297,19 +297,19 @@ export default class InlineComponentWrapper extends Wrapper { `); block.builders.create.addLine( - `if (${name}) ${name}.$$fragment.c();` + `if (${name}) ${name}.$$.fragment.c();` ); if (parentNodes && this.renderer.options.hydratable) { block.builders.claim.addLine( - `if (${name}) ${name}.$$fragment.l(${parentNodes});` + `if (${name}) ${name}.$$.fragment.l(${parentNodes});` ); } block.builders.mount.addBlock(deindent` if (${name}) { - ${name}.$$mount(${parentNode || '#target'}, ${parentNode ? 'null' : 'anchor'}); - ${this.node.ref && `#component.$$refs.${this.node.ref.name} = ${name};`} + @mount_component(${name}, ${parentNode || '#target'}, ${parentNode ? 'null' : 'anchor'}); + ${this.node.ref && `#component.$$.refs.${this.node.ref.name} = ${name};`} } `); @@ -327,7 +327,7 @@ export default class InlineComponentWrapper extends Wrapper { if (${name}) { @groupOutros(); const old_component = ${name}; - old_component.$$fragment.o(() => { + old_component.$$.fragment.o(() => { old_component.$destroy(); }); } @@ -339,19 +339,19 @@ export default class InlineComponentWrapper extends Wrapper { ${munged_handlers} ${this.fragment && this.fragment.nodes.map(child => child.remount(name))} - ${name}.$$mount(${updateMountNode}, ${anchor}); + @mount_component(${name}, ${updateMountNode}, ${anchor}); ${this.node.handlers.map(handler => deindent` ${name}.$on("${handler.name}", ${handler.var}); `)} - ${this.node.ref && `#component.$$refs.${this.node.ref.name} = ${name};`} + ${this.node.ref && `#component.$$.refs.${this.node.ref.name} = ${name};`} } else { ${name} = null; ${this.node.ref && deindent` - if (#component.$$refs.${this.node.ref.name} === ${name}) { - #component.$$refs.${this.node.ref.name} = null; - #component.$$.inject_refs(#component.$$refs); + if (#component.$$.refs.${this.node.ref.name} === ${name}) { + #component.$$.refs.${this.node.ref.name} = null; + #component.$$.inject_refs(#component.$$.refs); }`} } } @@ -384,19 +384,19 @@ export default class InlineComponentWrapper extends Wrapper { ${munged_bindings} ${munged_handlers} - ${this.node.ref && `#component.$$refs.${this.node.ref.name} = ${name};`} + ${this.node.ref && `#component.$$.refs.${this.node.ref.name} = ${name};`} `); - block.builders.create.addLine(`${name}.$$fragment.c();`); + block.builders.create.addLine(`${name}.$$.fragment.c();`); if (parentNodes && this.renderer.options.hydratable) { block.builders.claim.addLine( - `${name}.$$fragment.l(${parentNodes});` + `${name}.$$.fragment.l(${parentNodes});` ); } block.builders.mount.addLine( - `${name}.$$mount(${parentNode || '#target'}, ${parentNode ? 'null' : 'anchor'});` + `@mount_component(${name}, ${parentNode || '#target'}, ${parentNode ? 'null' : 'anchor'});` ); if (updates.length) { @@ -410,21 +410,21 @@ export default class InlineComponentWrapper extends Wrapper { block.builders.destroy.addBlock(deindent` ${name}.$destroy(${parentNode ? '' : 'detach'}); ${this.node.ref && deindent` - if (#component.$$refs.${this.node.ref.name} === ${name}) { - #component.$$refs.${this.node.ref.name} = null; - #component.$$.inject_refs(#component.$$refs); + if (#component.$$.refs.${this.node.ref.name} === ${name}) { + #component.$$.refs.${this.node.ref.name} = null; + #component.$$.inject_refs(#component.$$.refs); } `} `); } block.builders.outro.addLine( - `if (${name}) ${name}.$$fragment.o(#outrocallback);` + `if (${name}) ${name}.$$.fragment.o(#outrocallback);` ); } remount(name: string) { - return `${this.var}.$$fragment.m(${name}.$$slotted.default, null);`; + return `${this.var}.$$.fragment.m(${name}.$$.slotted.default, null);`; } } diff --git a/src/compile/render-dom/wrappers/Slot.ts b/src/compile/render-dom/wrappers/Slot.ts index 33d5f5227b..10052f17e0 100644 --- a/src/compile/render-dom/wrappers/Slot.ts +++ b/src/compile/render-dom/wrappers/Slot.ts @@ -46,7 +46,7 @@ export default class SlotWrapper extends Wrapper { const content_name = block.getUniqueName(`slot_content_${sanitize(slotName)}`); const prop = quotePropIfNecessary(slotName); - block.addVariable(content_name, `#component.$$slotted${prop}`); + block.addVariable(content_name, `#component.$$.slotted${prop}`); // TODO can we use isDomNode instead of type === 'Element'? const needsAnchorBefore = this.prev ? this.prev.node.type !== 'Element' : !parentNode; @@ -104,7 +104,7 @@ export default class SlotWrapper extends Wrapper { // if the slot is unmounted, move nodes back into the document fragment, // so that it can be reinserted later - // TODO so that this can work with public API, component.$$slotted should + // TODO so that this can work with public API, component.$$.slotted should // be all fragments, derived from options.slots. Not === options.slots const unmountLeadin = block.builders.destroy.toString() !== destroyBefore ? `else` diff --git a/src/compile/render-dom/wrappers/Text.ts b/src/compile/render-dom/wrappers/Text.ts index a32e908f63..fea357f1d5 100644 --- a/src/compile/render-dom/wrappers/Text.ts +++ b/src/compile/render-dom/wrappers/Text.ts @@ -62,6 +62,6 @@ export default class TextWrapper extends Wrapper { } remount(name: string) { - return `@append(${name}.$$slotted.default, ${this.var});`; + return `@append(${name}.$$.slotted.default, ${this.var});`; } } \ No newline at end of file diff --git a/src/compile/render-dom/wrappers/shared/Tag.ts b/src/compile/render-dom/wrappers/shared/Tag.ts index a49d2e2267..c3aa6a3047 100644 --- a/src/compile/render-dom/wrappers/shared/Tag.ts +++ b/src/compile/render-dom/wrappers/shared/Tag.ts @@ -52,6 +52,6 @@ export default class Tag extends Wrapper { } remount(name: string) { - return `@append(${name}.$$slotted.default, ${this.var});`; + return `@append(${name}.$$.slotted.default, ${this.var});`; } } \ No newline at end of file diff --git a/src/compile/render-dom/wrappers/shared/Wrapper.ts b/src/compile/render-dom/wrappers/shared/Wrapper.ts index 21112f3f55..3ee2bbbb9f 100644 --- a/src/compile/render-dom/wrappers/shared/Wrapper.ts +++ b/src/compile/render-dom/wrappers/shared/Wrapper.ts @@ -87,6 +87,6 @@ export default class Wrapper { } remount(name: string) { - return `${this.var}.m(${name}.$$slotted.default, null);`; + return `${this.var}.m(${name}.$$.slotted.default, null);`; } } \ No newline at end of file diff --git a/src/internal/Component.js b/src/internal/Component.js index 965bcb19b3..cfab792a51 100644 --- a/src/internal/Component.js +++ b/src/internal/Component.js @@ -1,59 +1,121 @@ import { add_render_callback, flush, intro, schedule_update } from './scheduler.js'; -import { set_current_component } from './lifecycle.js' +import { current_component, set_current_component } from './lifecycle.js' import { is_function, run, run_all, noop } from './utils.js'; import { blankObject } from './utils.js'; import { children } from './dom.js'; -export class $$Component { - constructor(options, init, create_fragment, not_equal) { - this.$$beforeRender = []; - this.$$onMount = []; - this.$$afterRender = []; - this.$$onDestroy = []; +export function bind(component, name, callback) { + component.$$.bound[name] = callback; + callback(component.$$.get()[name]); +} - this.$$bindings = blankObject(); - this.$$callbacks = blankObject(); - this.$$slotted = options.slots || {}; +export function mount_component({ $$: { fragment }}, target, anchor, hydrate) { + if (hydrate) { + fragment.l(children(target)); + fragment.m(target, anchor); // TODO can we avoid moving DOM? + } else { + fragment.c(); + fragment[fragment.i ? 'i' : 'm'](target, anchor); + } - set_current_component(this); - const [get_state, inject_props, inject_refs] = init( - this, - key => { - this.$$make_dirty(key); - if (this.$$bindings[key]) this.$$bindings[key](get_state()[key]); - } - ); + component.$$.inject_refs(component.$$.refs); - this.$$ = { get_state, inject_props, inject_refs, not_equal }; + // onMount happens after the initial afterRender. Because + // afterRender callbacks happen in reverse order (inner first) + // we schedule onMount callbacks before afterRender callbacks + add_render_callback(() => { + const onDestroy = component.$$.on_mount.map(run).filter(is_function); + if (component.$$.on_destroy) { + component.$$.on_destroy.push(...onDestroy); + } else { + // Edge case — component was destroyed immediately, + // most likely as a result of a binding initialising + run_all(onDestroy); + } + component.$$.on_mount = []; + }); + + component.$$.after_render.forEach(add_render_callback); +} - this.$$refs = {}; +function destroy(component, detach) { + if (component.$$) { + run_all(component.$$.on_destroy); + component.$$.fragment.d(detach); - this.$$dirty = null; - this.$$bindingGroups = []; // TODO find a way to not have this here? + // TODO null out other refs, including component.$$ (but need to + // preserve final state?) + component.$$.on_destroy = component.$$.fragment = null; + component.$$.get = () => ({}); + } +} + +function make_dirty(component, key) { + if (!component.$$.dirty) { + schedule_update(component); + component.$$.dirty = {}; + } + component.$$.dirty[key] = true; +} + +export class $$Component { + constructor(options, init, create_fragment, not_equal) { + const previous_component = current_component; + set_current_component(this); + + this.$$ = { + fragment: null, + + // state + get: null, + set: noop, + inject_refs: noop, + not_equal, + bound: blankObject(), + + // lifecycle + on_mount: [], + on_destroy: [], + before_render: [], + after_render: [], + + // everything else + callbacks: blankObject(), + slotted: options.slots || {}, + refs: {}, + dirty: null, + binding_groups: [] + }; + + init(this, key => { + make_dirty(this, key); + if (this.$$.bound[key]) this.$$.bound[key](this.$$.get()[key]); + }); if (options.props) { - this.$$.inject_props(options.props); + this.$$.set(options.props); } - run_all(this.$$beforeRender); - this.$$fragment = create_fragment(this, this.$$.get_state()); + run_all(this.$$.before_render); + this.$$.fragment = create_fragment(this, this.$$.get()); if (options.target) { intro.enabled = !!options.intro; - this.$$mount(options.target, options.anchor, options.hydrate); - + mount_component(this, options.target, options.anchor, options.hydrate); flush(); intro.enabled = true; } + + set_current_component(previous_component); } $destroy() { - this.$$destroy(true); - this.$$update = this.$$destroy = noop; + destroy(this, true); + this.$destroy = noop; } $on(type, callback) { - const callbacks = (this.$$callbacks[type] || (this.$$callbacks[type] = [])); + const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = [])); callbacks.push(callback); return () => { @@ -64,76 +126,13 @@ export class $$Component { $set(values) { if (this.$$) { - const state = this.$$.get_state(); - this.$$.inject_props(values); + const state = this.$$.get(); + this.$$.set(values); for (const key in values) { - if (this.$$.not_equal(state[key], values[key])) this.$$make_dirty(key); + if (this.$$.not_equal(state[key], values[key])) make_dirty(this, key); } } } - - $$bind(name, callback) { - this.$$bindings[name] = callback; - callback(this.$$.get_state()[name]); - } - - $$destroy(detach) { - if (this.$$) { - run_all(this.$$onDestroy); - this.$$fragment.d(detach); - - // TODO null out other refs, including this.$$ (but need to - // preserve final state?) - this.$$onDestroy = this.$$fragment = null; - this.$$.get_state = () => ({}); - } - } - - $$make_dirty(key) { - if (!this.$$dirty) { - schedule_update(this); - this.$$dirty = {}; - } - this.$$dirty[key] = true; - } - - $$mount(target, anchor, hydrate) { - if (hydrate) { - this.$$fragment.l(children(target)); - this.$$fragment.m(target, anchor); // TODO can we avoid moving DOM? - } else { - this.$$fragment.c(); - this.$$fragment[this.$$fragment.i ? 'i' : 'm'](target, anchor); - } - - this.$$.inject_refs(this.$$refs); - - // onMount happens after the initial afterRender. Because - // afterRender callbacks happen in reverse order (inner first) - // we schedule onMount callbacks before afterRender callbacks - add_render_callback(() => { - const onDestroy = this.$$onMount.map(run).filter(is_function); - if (this.$$onDestroy) { - this.$$onDestroy.push(...onDestroy); - } else { - // Edge case — component was destroyed immediately, - // most likely as a result of a binding initialising - run_all(onDestroy); - } - this.$$onMount = []; - }); - - this.$$afterRender.forEach(add_render_callback); - } - - $$update() { - run_all(this.$$beforeRender); - this.$$fragment.p(this.$$dirty, this.$$.get_state()); - this.$$.inject_refs(this.$$refs); - this.$$dirty = null; - - this.$$afterRender.forEach(add_render_callback); - } } export class $$ComponentDev extends $$Component { @@ -148,7 +147,7 @@ export class $$ComponentDev extends $$Component { $destroy() { super.$destroy(); - this.$$destroy = () => { + this.$destroy = () => { console.warn(`Component was already destroyed`); }; } diff --git a/src/internal/await-block.js b/src/internal/await-block.js index 18f18946df..0d7274f2eb 100644 --- a/src/internal/await-block.js +++ b/src/internal/await-block.js @@ -32,8 +32,8 @@ export function handlePromise(promise, info) { block[block.i ? 'i' : 'm'](info.mount(), info.anchor); // TODO is some of this redundant? - info.component.$$.inject_refs(info.component.$$refs); - run_all(info.component.$$afterRender); + info.component.$$.inject_refs(info.component.$$.refs); + run_all(info.component.$$.after_render); flush(); } diff --git a/src/internal/lifecycle.js b/src/internal/lifecycle.js index 01b08f94dc..ac8be2ed6c 100644 --- a/src/internal/lifecycle.js +++ b/src/internal/lifecycle.js @@ -1,30 +1,30 @@ -let current_component; +export let current_component; export function set_current_component(component) { current_component = component; } export function beforeRender(fn) { - current_component.$$beforeRender.push(fn); + current_component.$$.before_render.push(fn); } export function onMount(fn) { - current_component.$$onMount.push(fn); + current_component.$$.on_mount.push(fn); } export function afterRender(fn) { - current_component.$$afterRender.push(fn); + current_component.$$.after_render.push(fn); } export function onDestroy(fn) { - current_component.$$onDestroy.push(fn); + current_component.$$.on_destroy.push(fn); } export function createEventDispatcher() { const component = current_component; return (type, detail) => { - const callbacks = component.$$callbacks[type]; + const callbacks = component.$$.callbacks[type]; if (callbacks) { // TODO are there situations where events could be dispatched @@ -41,7 +41,7 @@ export function createEventDispatcher() { // shorthand events, or if we want to implement // a real bubbling mechanism export function bubble(component, event) { - const callbacks = component.$$callbacks[event.type]; + const callbacks = component.$$.callbacks[event.type]; if (callbacks) { callbacks.slice().forEach(fn => fn(event)); diff --git a/src/internal/scheduler.js b/src/internal/scheduler.js index e02fa35edb..109ca9cb55 100644 --- a/src/internal/scheduler.js +++ b/src/internal/scheduler.js @@ -1,3 +1,5 @@ +import { run_all } from './utils.js'; + let update_scheduled = false; let dirty_components = []; @@ -29,7 +31,7 @@ export function flush() { // first, call beforeRender functions // and update components while (dirty_components.length) { - dirty_components.shift().$$update(); + update(dirty_components.shift().$$); } while (binding_callbacks.length) binding_callbacks.pop()(); @@ -51,6 +53,17 @@ export function flush() { update_scheduled = false; } +function update($$) { + if ($$.fragment) { + run_all($$.before_render); + $$.fragment.p($$.dirty, $$.get()); + $$.inject_refs($$.refs); + $$.dirty = null; + + $$.after_render.forEach(add_render_callback); + } +} + function queue_microtask(callback) { Promise.resolve().then(() => { if (update_scheduled) callback();