eliminate closures

proxied-state-each-blocks
Rich Harris 11 months ago committed by Dominic Gannaway
parent 3a6ad8dabd
commit 0db3edc3e8

@ -1,107 +1,132 @@
import { effect_active, get, set, increment, source } from './runtime.js'; import { effect_active, get, set, increment, source } from './runtime.js';
/** @typedef {{ p: MagicObject | null; s: Map<string | symbol, import('./types').SourceSignal<any>>; v: import('./types').SourceSignal<number>; a: boolean }} Magic */
/** @typedef {Record<string | symbol, any> & { [MAGIC_SYMBOL]: Magic }} MagicObject */
export const MAGIC_SYMBOL = Symbol(); export const MAGIC_SYMBOL = Symbol();
export const MAGIC_EACH_SYMBOL = Symbol(); export const MAGIC_EACH_SYMBOL = Symbol();
/** /**
* @template T * @template {MagicObject} T
* @param {T} value * @param {T} value
* @returns {T} * @returns {T}
*/ */
export function magic(value) { export function magic(value) {
if (value && typeof value === 'object') { return wrap(value, null);
return wrap(value, null);
}
return value;
} }
/** /**
* @template T * @template {MagicObject} T
* @template {MagicObject} P
* @param {T} value * @param {T} value
* @param {T | null} parent * @param {P | null} parent
* @returns {T} * @returns {T}
*/ */
function wrap(value, parent) { function wrap(value, parent) {
if (value && typeof value === 'object') { if (value && typeof value === 'object') {
return object(value, parent); return proxy(value, parent);
} }
return value; return value;
} }
/** @type {ProxyHandler<MagicObject>} */
const handler = {
get(target, prop, receiver) {
if (prop === MAGIC_EACH_SYMBOL) {
return parent;
}
const metadata = target[MAGIC_SYMBOL];
let s = metadata.s.get(prop);
if (s === undefined && effect_active()) {
s = source(wrap(target[prop], receiver));
metadata.s.set(prop, s);
}
const value = s !== undefined ? get(s) : target[prop];
if (typeof value === 'function') {
// @ts-ignore
return (...args) => {
return value.apply(receiver, args);
};
}
return value;
},
set(target, prop, value) {
const metadata = target[MAGIC_SYMBOL];
const s = metadata.s.get(prop);
if (s !== undefined) set(s, wrap(value, target));
if (metadata.a && prop === 'length') {
for (let i = value; i < target.length; i += 1) {
const s = metadata.s.get(i + '');
if (s !== undefined) set(s, undefined);
}
}
if (!(prop in target)) increment(metadata.v);
// @ts-ignore
target[prop] = value;
return true;
},
deleteProperty(target, prop) {
const metadata = target[MAGIC_SYMBOL];
const s = metadata.s.get(prop);
if (s !== undefined) set(s, undefined);
if (prop in target) increment(metadata.v);
return delete target[prop];
},
has(target, prop) {
if (prop === MAGIC_SYMBOL) return true;
const metadata = target[MAGIC_SYMBOL];
get(metadata.v);
return Reflect.has(target, prop);
},
ownKeys(target) {
const metadata = target[MAGIC_SYMBOL];
get(metadata.v);
return Reflect.ownKeys(target);
}
};
/** /**
* @template {Record<string | symbol, any>} T * @template {MagicObject} T
* @template P * @template {MagicObject} P
* @param {T} value * @param {T} value
* @param {P | null} parent * @param {P | null} parent
* @returns {T} * @returns {T}
*/ */
function object(value, parent) { function proxy(value, parent) {
if (MAGIC_SYMBOL in value) return value; if (MAGIC_SYMBOL in value) return value;
/** @type {Map<string | symbol, any>} */ // @ts-expect-error
const sources = new Map(); value[MAGIC_SYMBOL] = init(value, parent);
let version = source(0);
const is_array = Array.isArray(value);
return new Proxy(value, { // @ts-expect-error not sure how to fix this
get(target, prop, receiver) { return new Proxy(value, handler);
if (prop === MAGIC_EACH_SYMBOL) { }
return parent;
}
let s = sources.get(prop);
if (s === undefined && effect_active()) {
s = source(wrap(target[prop], receiver));
sources.set(prop, s);
}
const value = s !== undefined ? get(s) : target[prop];
if (typeof value === 'function') {
// @ts-ignore
return (...args) => {
return value.apply(receiver, args);
};
}
return value;
},
set(target, prop, value) {
const s = sources.get(prop);
if (s !== undefined) set(s, wrap(value, target));
if (is_array && prop === 'length') {
for (let i = value; i < target.length; i += 1) {
const s = sources.get(i + '');
if (s !== undefined) set(s, undefined);
}
}
if (!(prop in target)) increment(version); /**
// @ts-ignore * @param {MagicObject} value
target[prop] = value; * @param {MagicObject | null} parent
* @returns {Magic}
return true; */
}, function init(value, parent) {
deleteProperty(target, prop) { return {
const s = sources.get(prop); p: parent,
if (s !== undefined) set(s, undefined); s: new Map(),
v: source(0),
if (prop in target) increment(version); a: Array.isArray(value)
};
return delete target[prop];
},
has(target, prop) {
if (prop === MAGIC_SYMBOL) return true;
get(version);
return Reflect.has(target, prop);
},
ownKeys(target) {
get(version);
return Reflect.ownKeys(target);
}
});
} }

Loading…
Cancel
Save