diff --git a/src/generators/dom/index.ts b/src/generators/dom/index.ts index f6c007c79d..2bf0c21c4b 100644 --- a/src/generators/dom/index.ts +++ b/src/generators/dom/index.ts @@ -184,15 +184,23 @@ export default function dom( const debugName = `<${generator.customElement ? generator.tag : name}>`; // generate initial state object - const globals = Array.from(generator.expectedProperties).filter(prop => globalWhitelist.has(prop)); + const expectedProperties = Array.from(generator.expectedProperties); + const globals = expectedProperties.filter(prop => globalWhitelist.has(prop)); + const storeProps = options.store ? expectedProperties.filter(prop => prop[0] === '$') : []; + const initialState = []; + if (globals.length > 0) { initialState.push(`{ ${globals.map(prop => `${prop} : ${prop}`).join(', ')} }`); } + if (storeProps.length > 0) { + initialState.push(`this.store._init([${storeProps.map(prop => `"${prop.slice(1)}"`)}])`); + } + if (templateProperties.data) { initialState.push(`%data()`); - } else if (globals.length === 0) { + } else if (globals.length === 0 && storeProps.length === 0) { initialState.push('{}'); } @@ -205,6 +213,7 @@ export default function dom( @init(this, options); ${generator.usesRefs && `this.refs = {};`} this._state = @assign(${initialState.join(', ')}); + ${storeProps.length > 0 && `this.store._add(this, [${storeProps.map(prop => `"${prop.slice(1)}"`)}]);`} ${generator.metaBindings} ${computations.length && `this._recompute({ ${Array.from(computationDeps).map(dep => `${dep}: 1`).join(', ')} }, this._state);`} ${options.dev && @@ -215,7 +224,11 @@ export default function dom( ${generator.bindingGroups.length && `this._bindingGroups = [${Array(generator.bindingGroups.length).fill('[]').join(', ')}];`} - ${templateProperties.ondestroy && `this._handlers.destroy = [%ondestroy]`} + ${(templateProperties.ondestroy || storeProps.length) && ( + `this._handlers.destroy = [${ + [templateProperties.ondestroy && `%ondestroy`, storeProps.length && `@removeFromStore`].filter(Boolean).join(', ') + }]` + )} ${generator.slots.size && `this._slotted = options.slots || {};`} diff --git a/src/interfaces.ts b/src/interfaces.ts index fff52e8da4..3a9280df3c 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -56,6 +56,7 @@ export interface CompileOptions { legacy?: boolean; customElement?: CustomElementOptions | true; css?: boolean; + store?: boolean; onerror?: (error: Error) => void; onwarn?: (warning: Warning) => void; diff --git a/src/shared/index.js b/src/shared/index.js index f9d0f91998..9825972b82 100644 --- a/src/shared/index.js +++ b/src/shared/index.js @@ -65,12 +65,13 @@ export function get(key) { } export function init(component, options) { - component.options = options; - component._observers = { pre: blankObject(), post: blankObject() }; component._handlers = blankObject(); component._root = options._root || component; component._bind = options._bind; + + component.options = options; + component.store = component._root.options.store; } export function observe(key, callback, options) { @@ -187,6 +188,10 @@ export function _unmount() { this._fragment.u(); } +export function removeFromStore() { + this.store._remove(this); +} + export var proto = { destroy: destroy, get: get, diff --git a/test/runtime/index.js b/test/runtime/index.js index 122fe645b6..a82f5b6975 100644 --- a/test/runtime/index.js +++ b/test/runtime/index.js @@ -63,6 +63,7 @@ describe("runtime", () => { compileOptions.shared = shared; compileOptions.hydratable = hydrate; compileOptions.dev = config.dev; + compileOptions.store = !!config.store; // check that no ES2015+ syntax slipped in if (!config.allowES2015) { @@ -88,7 +89,7 @@ describe("runtime", () => { } } catch (err) { failed.add(dir); - showOutput(cwd, { shared }, svelte); // eslint-disable-line no-console + showOutput(cwd, { shared, store: !!compileOptions.store }, svelte); // eslint-disable-line no-console throw err; } } @@ -134,7 +135,7 @@ describe("runtime", () => { try { SvelteComponent = require(`./samples/${dir}/main.html`); } catch (err) { - showOutput(cwd, { shared, hydratable: hydrate }, svelte); // eslint-disable-line no-console + showOutput(cwd, { shared, hydratable: hydrate, store: !!compileOptions.store }, svelte); // eslint-disable-line no-console throw err; } @@ -154,7 +155,8 @@ describe("runtime", () => { const options = Object.assign({}, { target, hydrate, - data: config.data + data: config.data, + store: config.store }, config.options || {}); const component = new SvelteComponent(options); @@ -188,12 +190,12 @@ describe("runtime", () => { config.error(assert, err); } else { failed.add(dir); - showOutput(cwd, { shared, hydratable: hydrate }, svelte); // eslint-disable-line no-console + showOutput(cwd, { shared, hydratable: hydrate, store: !!compileOptions.store }, svelte); // eslint-disable-line no-console throw err; } } - if (config.show) showOutput(cwd, { shared, hydratable: hydrate }, svelte); + if (config.show) showOutput(cwd, { shared, hydratable: hydrate, store: !!compileOptions.store }, svelte); }); } diff --git a/test/runtime/samples/store/_config.js b/test/runtime/samples/store/_config.js new file mode 100644 index 0000000000..55d01fc125 --- /dev/null +++ b/test/runtime/samples/store/_config.js @@ -0,0 +1,19 @@ +import Store from '../../../../store.js'; + +const store = new Store({ + name: 'world' +}); + +export default { + solo: true, + + store, + + html: `

Hello world!

`, + + test(assert, component, target) { + store.set({ name: 'everybody' }); + + assert.htmlEqual(target.innerHTML, `

Hello everybody!

`); + } +}; \ No newline at end of file diff --git a/test/runtime/samples/store/main.html b/test/runtime/samples/store/main.html new file mode 100644 index 0000000000..28154934b8 --- /dev/null +++ b/test/runtime/samples/store/main.html @@ -0,0 +1 @@ +

Hello {{$name}}!

\ No newline at end of file