mirror of https://github.com/sveltejs/svelte
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…
Reference in new issue