diff --git a/index.js b/index.js new file mode 100644 index 0000000000..63d1731698 --- /dev/null +++ b/index.js @@ -0,0 +1,3 @@ +import { onprops, onmount, onupdate, ondestroy } from './internal.js'; + +export { onprops, onmount, onupdate, ondestroy }; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index f2cb89889d..38f60fdf1f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -553,7 +553,7 @@ }, "camelcase-keys": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, "requires": { @@ -918,7 +918,7 @@ "dependencies": { "domelementtype": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", "dev": true } @@ -926,7 +926,7 @@ }, "domelementtype": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", + "resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", "dev": true }, @@ -1423,7 +1423,7 @@ }, "fast-deep-equal": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", "dev": true }, @@ -2422,7 +2422,7 @@ }, "is-builtin-module": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { @@ -2795,7 +2795,7 @@ }, "load-json-file": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", "dev": true, "requires": { @@ -2895,7 +2895,7 @@ }, "meow": { "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz", "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": true, "requires": { @@ -2913,7 +2913,7 @@ "dependencies": { "load-json-file": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { @@ -3092,7 +3092,7 @@ }, "multiline": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/multiline/-/multiline-1.0.2.tgz", + "resolved": "http://registry.npmjs.org/multiline/-/multiline-1.0.2.tgz", "integrity": "sha1-abHyX/B00oKJBPJE3dBrfZbvbJM=", "dev": true, "requires": { @@ -5473,7 +5473,7 @@ }, "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true }, @@ -5962,7 +5962,7 @@ }, "rollup-plugin-typescript": { "version": "0.8.1", - "resolved": "https://registry.npmjs.org/rollup-plugin-typescript/-/rollup-plugin-typescript-0.8.1.tgz", + "resolved": "http://registry.npmjs.org/rollup-plugin-typescript/-/rollup-plugin-typescript-0.8.1.tgz", "integrity": "sha1-L/fuzCHPa7K0P8J+W2iJUs5xkko=", "dev": true, "requires": { @@ -5981,7 +5981,7 @@ }, "rollup-pluginutils": { "version": "1.5.2", - "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-1.5.2.tgz", + "resolved": "http://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-1.5.2.tgz", "integrity": "sha1-HhVud4+UtyVb+hs9AXi+j1xVJAg=", "dev": true, "requires": { @@ -6156,7 +6156,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -6462,7 +6462,7 @@ }, "through": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, @@ -6484,7 +6484,7 @@ }, "readable-stream": { "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "dev": true, "requires": { diff --git a/package.json b/package.json index 346bd0f6fd..f78ba6e721 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "svelte", "version": "2.15.1", "description": "The magical disappearing UI framework", - "main": "compiler/svelte.js", + "main": "index.js", "bin": { "svelte": "svelte" }, @@ -10,10 +10,8 @@ "cli", "compiler/*.js", "ssr/*.js", - "shared.js", - "store.js", - "store.umd.js", - "store.d.ts", + "index.js", + "internal.js", "svelte", "README.md" ], diff --git a/src/compile/render-dom/index.ts b/src/compile/render-dom/index.ts index 88b16743e2..95c109e0a3 100644 --- a/src/compile/render-dom/index.ts +++ b/src/compile/render-dom/index.ts @@ -107,19 +107,26 @@ export default function dom( customElements.define("${component.tag}", ${name}); `); } else { + const refs = Array.from(component.refs); + builder.addBlock(deindent` class ${name} extends @SvelteComponent { - $$init($$set_inject_props, $$set_inject_refs, $$make_dirty) { + $$init($$make_dirty) { ${component.javascript || component.exports.map(x => `let ${x.name};`)} - $$set_inject_props(props => { - // TODO only do this for export let|var - ${(component.exports.map(name => - `if ('${name.as}' in props) ${name.as} = props.${name.as};` - ))} - }); - - return () => ({ ${(component.declarations).join(', ')} }); + return [ + () => ({ ${(component.declarations).join(', ')} }), + props => { + // TODO only do this for export let|var + ${(component.exports.map(name => + `if ('${name.as}' in props) ${name.as} = props.${name.as};` + ))} + }, + refs => { + // TODO only if we have some refs + ${refs.map(name => `${name} = refs.${name};`)} + } + ]; } $$create_fragment(${component.alias('component')}, ctx) { @@ -128,7 +135,7 @@ export default function dom( ${component.exports.map(x => deindent` get ${x.as}() { - return this.$$get_state().${x.name}; + return this.$$.get_state().${x.name}; } set ${x.as}(value) { diff --git a/src/compile/render-dom/wrappers/Element/index.ts b/src/compile/render-dom/wrappers/Element/index.ts index 7eed9dd073..e530d4f1aa 100644 --- a/src/compile/render-dom/wrappers/Element/index.ts +++ b/src/compile/render-dom/wrappers/Element/index.ts @@ -688,7 +688,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};` diff --git a/src/compile/render-dom/wrappers/InlineComponent/index.ts b/src/compile/render-dom/wrappers/InlineComponent/index.ts index c585e11169..07c2d99d68 100644 --- a/src/compile/render-dom/wrappers/InlineComponent/index.ts +++ b/src/compile/render-dom/wrappers/InlineComponent/index.ts @@ -347,7 +347,7 @@ export default class InlineComponentWrapper extends Wrapper { block.builders.mount.addBlock(deindent` if (${name}) { ${name}.$$mount(${parentNode || '#target'}, ${parentNode ? 'null' : 'anchor'}); - ${this.node.ref && `#component.refs.${this.node.ref.name} = ${name};`} + ${this.node.ref && `#component.$$refs.${this.node.ref.name} = ${name};`} } `); @@ -392,12 +392,12 @@ export default class InlineComponentWrapper extends Wrapper { ${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; + if (#component.$$refs.${this.node.ref.name} === ${name}) { + #component.$$refs.${this.node.ref.name} = null; }`} } } @@ -434,7 +434,7 @@ export default class InlineComponentWrapper extends Wrapper { }); `)} - ${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();`); @@ -459,7 +459,7 @@ export default class InlineComponentWrapper extends Wrapper { block.builders.destroy.addLine(deindent` ${name}.$destroy(${parentNode ? '' : 'detach'}); - ${this.node.ref && `if (#component.refs.${this.node.ref.name} === ${name}) #component.refs.${this.node.ref.name} = null;`} + ${this.node.ref && `if (#component.$$refs.${this.node.ref.name} === ${name}) #component.$$refs.${this.node.ref.name} = null;`} `); } diff --git a/src/internal/SvelteComponent.js b/src/internal/SvelteComponent.js index 1028ee035d..36af47e8db 100644 --- a/src/internal/SvelteComponent.js +++ b/src/internal/SvelteComponent.js @@ -1,21 +1,31 @@ -import { schedule_update, flush } from './scheduler'; +import { schedule_update, flush } from './scheduler.js'; +import { set_current_component } from './lifecycle.js' +import { run_all } from '../shared/utils.js'; export class SvelteComponent { constructor(options) { - this.$$get_state = this.$$init( - fn => this.$$inject_props = fn, - fn => this.$$inject_refs = fn, + this.$$onprops = []; + this.$$onmount = []; + this.$$onupdate = []; + this.$$ondestroy = []; + + set_current_component(this); + const [get_state, inject_props, inject_refs] = this.$$init( key => this.$$make_dirty(key) ); + this.$$ = { get_state, inject_props, inject_refs }; + + this.$$refs = {}; + this.$$dirty = null; this.$$bindingGroups = []; // TODO find a way to not have this here? if (options.props) { - this.$$inject_props(options.props); + this.$$.inject_props(options.props); } - this.$$fragment = this.$$create_fragment(this, this.$$get_state()); + this.$$fragment = this.$$create_fragment(this, this.$$.get_state()); if (options.target) { this.$$mount(options.target); @@ -42,19 +52,31 @@ export class SvelteComponent { $$mount(target, anchor) { this.$$fragment.c(); this.$$fragment.m(target, anchor); + this.$$.inject_refs(this.$$refs); + + const ondestroy = this.$$onmount.map(fn => fn()).filter(Boolean); + this.$$ondestroy.push(...ondestroy); + this.$$onmount = null; } $$set(key, value) { - this.$$inject_props({ [key]: value }); + this.$$.inject_props({ [key]: value }); + run_all(this.$$onprops); this.$$make_dirty(key); } $$update() { - this.$$fragment.p(this.$$dirty, this.$$get_state()); + this.$$fragment.p(this.$$dirty, this.$$.get_state()); + this.$$.inject_refs(this.$$refs); + run_all(this.$$onupdate); this.$$dirty = null; } $$destroy(detach) { this.$$fragment.d(detach); + run_all(this.$$ondestroy); + + // TODO null out other refs + this.$$ondestroy = null; } } \ No newline at end of file diff --git a/src/internal/await-block.js b/src/internal/await-block.js index ed4b2ccf26..c796be9ded 100644 --- a/src/internal/await-block.js +++ b/src/internal/await-block.js @@ -1,5 +1,6 @@ import { assign, isPromise } from './utils.js'; import { groupOutros } from './transitions.js'; +import { flush } from '../internal/scheduler.js'; export function handlePromise(promise, info) { var token = info.token = {}; @@ -30,7 +31,10 @@ export function handlePromise(promise, info) { block.c(); block[block.i ? 'i' : 'm'](info.mount(), info.anchor); - info.component.root.set({}); // flush any handlers that were created + // TODO is some of this redundant? + info.component.$$.inject_refs(info.component.$$refs); + run_all(info.component.$$onupdate); + flush(); } info.block = block; diff --git a/src/internal/index.js b/src/internal/index.js index 037607e487..307dccc074 100644 --- a/src/internal/index.js +++ b/src/internal/index.js @@ -2,6 +2,7 @@ export * from './animations.js'; export * from './await-block.js'; export * from './dom.js'; export * from './keyed-each.js'; +export * from './lifecycle.js'; export * from './scheduler.js'; export * from './spread.js'; export * from './ssr.js'; diff --git a/src/internal/lifecycle.js b/src/internal/lifecycle.js new file mode 100644 index 0000000000..0d733608b0 --- /dev/null +++ b/src/internal/lifecycle.js @@ -0,0 +1,21 @@ +let current_component; + +export function set_current_component(component) { + current_component = component; +} + +export function onprops(fn) { + current_component.$$onprops.push(fn); +} + +export function onmount(fn) { + current_component.$$onmount.push(fn); +} + +export function onupdate(fn) { + current_component.$$onupdate.push(fn); +} + +export function ondestroy(fn) { + current_component.$$ondestroy.push(fn); +} \ No newline at end of file diff --git a/src/shared/utils.js b/src/shared/utils.js index 2077c25e01..5a58739925 100644 --- a/src/shared/utils.js +++ b/src/shared/utils.js @@ -35,4 +35,9 @@ export function exclude(src, prop) { export function run(fn) { fn(); +} + +export function run_all(fns) { + let i = fns.length; + while (i--) fns[i](); } \ No newline at end of file