solid-js
Version:
A declarative JavaScript library for building user interfaces.
127 lines (124 loc) • 3.82 kB
JavaScript
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;
;