127 lines
3.7 KiB
JavaScript
127 lines
3.7 KiB
JavaScript
'use strict';
|
|
|
|
const $RAW = Symbol("state-raw");
|
|
function isWrappable(obj) {
|
|
return obj != null && typeof obj === "object" && (Object.getPrototypeOf(obj) === Object.prototype || Array.isArray(obj));
|
|
}
|
|
function unwrap(item) {
|
|
return item;
|
|
}
|
|
function setProperty(state, property, value, force) {
|
|
if (!force && state[property] === value) return;
|
|
if (value === undefined) {
|
|
delete state[property];
|
|
} else state[property] = value;
|
|
}
|
|
function mergeStoreNode(state, value, force) {
|
|
const keys = Object.keys(value);
|
|
for (let i = 0; i < keys.length; i += 1) {
|
|
const key = keys[i];
|
|
setProperty(state, key, value[key], force);
|
|
}
|
|
}
|
|
function updateArray(current, next) {
|
|
if (typeof next === "function") next = next(current);
|
|
if (Array.isArray(next)) {
|
|
if (current === next) return;
|
|
let i = 0,
|
|
len = next.length;
|
|
for (; i < len; i++) {
|
|
const value = next[i];
|
|
if (current[i] !== value) setProperty(current, i, value);
|
|
}
|
|
setProperty(current, "length", len);
|
|
} else mergeStoreNode(current, next);
|
|
}
|
|
function updatePath(current, path, traversed = []) {
|
|
let part,
|
|
next = current;
|
|
if (path.length > 1) {
|
|
part = path.shift();
|
|
const partType = typeof part,
|
|
isArray = Array.isArray(current);
|
|
if (Array.isArray(part)) {
|
|
for (let i = 0; i < part.length; i++) {
|
|
updatePath(current, [part[i]].concat(path), traversed);
|
|
}
|
|
return;
|
|
} else if (isArray && partType === "function") {
|
|
for (let i = 0; i < current.length; i++) {
|
|
if (part(current[i], i)) updatePath(current, [i].concat(path), traversed);
|
|
}
|
|
return;
|
|
} else if (isArray && partType === "object") {
|
|
const {
|
|
from = 0,
|
|
to = current.length - 1,
|
|
by = 1
|
|
} = part;
|
|
for (let i = from; i <= to; i += by) {
|
|
updatePath(current, [i].concat(path), traversed);
|
|
}
|
|
return;
|
|
} else if (path.length > 1) {
|
|
updatePath(current[part], path, [part].concat(traversed));
|
|
return;
|
|
}
|
|
next = current[part];
|
|
traversed = [part].concat(traversed);
|
|
}
|
|
let value = path[0];
|
|
if (typeof value === "function") {
|
|
value = value(next, traversed);
|
|
if (value === next) return;
|
|
}
|
|
if (part === undefined && value == undefined) return;
|
|
if (part === undefined || isWrappable(next) && isWrappable(value) && !Array.isArray(value)) {
|
|
mergeStoreNode(next, value);
|
|
} else setProperty(current, part, value);
|
|
}
|
|
function createStore(state) {
|
|
const isArray = Array.isArray(state);
|
|
function setStore(...args) {
|
|
isArray && args.length === 1 ? updateArray(state, args[0]) : updatePath(state, args);
|
|
}
|
|
return [state, setStore];
|
|
}
|
|
function createMutable(state) {
|
|
return state;
|
|
}
|
|
function modifyMutable(state, modifier) {
|
|
modifier(state);
|
|
}
|
|
function reconcile(value, options = {}) {
|
|
return state => {
|
|
if (!isWrappable(state) || !isWrappable(value)) return value;
|
|
const targetKeys = Object.keys(value);
|
|
for (let i = 0, len = targetKeys.length; i < len; i++) {
|
|
const key = targetKeys[i];
|
|
setProperty(state, key, value[key]);
|
|
}
|
|
const previousKeys = Object.keys(state);
|
|
for (let i = 0, len = previousKeys.length; i < len; i++) {
|
|
if (value[previousKeys[i]] === undefined) setProperty(state, previousKeys[i], undefined);
|
|
}
|
|
return state;
|
|
};
|
|
}
|
|
function produce(fn) {
|
|
return state => {
|
|
if (isWrappable(state)) fn(state);
|
|
return state;
|
|
};
|
|
}
|
|
const DEV = undefined;
|
|
|
|
exports.$RAW = $RAW;
|
|
exports.DEV = DEV;
|
|
exports.createMutable = createMutable;
|
|
exports.createStore = createStore;
|
|
exports.isWrappable = isWrappable;
|
|
exports.modifyMutable = modifyMutable;
|
|
exports.produce = produce;
|
|
exports.reconcile = reconcile;
|
|
exports.setProperty = setProperty;
|
|
exports.unwrap = unwrap;
|
|
exports.updatePath = updatePath;
|