diff --git a/package.json b/package.json index b3a7b4362f..0636008e2d 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "codecov": "codecov", "precodecov": "npm run coverage", "lint": "eslint src test/*.js", - "build": "node src/shared/_build.js && rollup -c", + "build": "node src/shared/_build.js && rollup -c && rollup -c rollup.store.config.js", "prepare": "npm run build", "dev": "node src/shared/_build.js && rollup -c -w", "pretest": "npm run build", diff --git a/rollup.store.config.js b/rollup.store.config.js new file mode 100644 index 0000000000..a3e4f395c6 --- /dev/null +++ b/rollup.store.config.js @@ -0,0 +1,9 @@ +export default { + input: 'store.js', + output: { + file: 'store.umd.js', + format: 'umd', + name: 'svelte', + extend: true + } +}; diff --git a/store.umd.js b/store.umd.js new file mode 100644 index 0000000000..50c7aa7469 --- /dev/null +++ b/store.umd.js @@ -0,0 +1,232 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (factory((global.svelte = global.svelte || {}))); +}(this, (function (exports) { 'use strict'; + +function assign(target) { + var k, + source, + i = 1, + len = arguments.length; + for (; i < len; i++) { + source = arguments[i]; + for (k in source) target[k] = source[k]; + } + + return target; +} + +function blankObject() { + return Object.create(null); +} + +function differs(a, b) { + return a !== b || ((a && typeof a === 'object') || typeof a === 'function'); +} + +function dispatchObservers(component, group, changed, newState, oldState) { + for (var key in group) { + if (!changed[key]) continue; + + var newValue = newState[key]; + var oldValue = oldState[key]; + + var callbacks = group[key]; + if (!callbacks) continue; + + for (var i = 0; i < callbacks.length; i += 1) { + var callback = callbacks[i]; + if (callback.__calling) continue; + + callback.__calling = true; + callback.call(component, newValue, oldValue); + callback.__calling = false; + } + } +} + +function get(key) { + return key ? this._state[key] : this._state; +} + +function observe(key, callback, options) { + var group = options && options.defer + ? this._observers.post + : this._observers.pre; + + (group[key] || (group[key] = [])).push(callback); + + if (!options || options.init !== false) { + callback.__calling = true; + callback.call(this, this._state[key]); + callback.__calling = false; + } + + return { + cancel: function() { + var index = group[key].indexOf(callback); + if (~index) group[key].splice(index, 1); + } + }; +} + +function Store(state) { + this._observers = { pre: blankObject(), post: blankObject() }; + this._changeHandlers = []; + this._dependents = []; + + this._computed = blankObject(); + this._sortedComputedProperties = []; + + this._state = assign({}, state); +} + +assign(Store.prototype, { + _add: function(component, props) { + this._dependents.push({ + component: component, + props: props + }); + }, + + _init: function(props) { + var state = {}; + for (var i = 0; i < props.length; i += 1) { + var prop = props[i]; + state['$' + prop] = this._state[prop]; + } + return state; + }, + + _remove: function(component) { + var i = this._dependents.length; + while (i--) { + if (this._dependents[i].component === component) { + this._dependents.splice(i, 1); + return; + } + } + }, + + _sortComputedProperties: function() { + var computed = this._computed; + var sorted = this._sortedComputedProperties = []; + var cycles; + var visited = blankObject(); + + function visit(key) { + if (cycles[key]) { + throw new Error('Cyclical dependency detected'); + } + + if (visited[key]) return; + visited[key] = true; + + var c = computed[key]; + + if (c) { + cycles[key] = true; + c.deps.forEach(visit); + sorted.push(c); + } + } + + for (var key in this._computed) { + cycles = blankObject(); + visit(key); + } + }, + + compute: function(key, deps, fn) { + var store = this; + var value; + + var c = { + deps: deps, + update: function(state, changed, dirty) { + var values = deps.map(function(dep) { + if (dep in changed) dirty = true; + return state[dep]; + }); + + if (dirty) { + var newValue = fn.apply(null, values); + if (differs(newValue, value)) { + value = newValue; + changed[key] = true; + state[key] = value; + } + } + } + }; + + c.update(this._state, {}, true); + + this._computed[key] = c; + this._sortComputedProperties(); + }, + + get: get, + + observe: observe, + + onchange: function(callback) { + this._changeHandlers.push(callback); + return { + cancel: function() { + var index = this._changeHandlers.indexOf(callback); + if (~index) this._changeHandlers.splice(index, 1); + } + }; + }, + + set: function(newState) { + var oldState = this._state, + changed = this._changed = {}, + dirty = false; + + for (var key in newState) { + if (this._computed[key]) throw new Error("'" + key + "' is a read-only property"); + if (differs(newState[key], oldState[key])) changed[key] = dirty = true; + } + if (!dirty) return; + + this._state = assign({}, oldState, newState); + + for (var i = 0; i < this._sortedComputedProperties.length; i += 1) { + this._sortedComputedProperties[i].update(this._state, changed); + } + + for (var i = 0; i < this._changeHandlers.length; i += 1) { + this._changeHandlers[i](this._state, changed); + } + + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + + var dependents = this._dependents.slice(); // guard against mutations + for (var i = 0; i < dependents.length; i += 1) { + var dependent = dependents[i]; + var componentState = {}; + dirty = false; + + for (var j = 0; j < dependent.props.length; j += 1) { + var prop = dependent.props[j]; + if (prop in changed) { + componentState['$' + prop] = this._state[prop]; + dirty = true; + } + } + + if (dirty) dependent.component.set(componentState); + } + + dispatchObservers(this, this._observers.post, changed, this._state, oldState); + } +}); + +exports.Store = Store; + +Object.defineProperty(exports, '__esModule', { value: true }); + +})));