diff --git a/packages/svelte/src/internal/client/magic.js b/packages/svelte/src/internal/client/magic.js new file mode 100644 index 0000000000..13bb36ac75 --- /dev/null +++ b/packages/svelte/src/internal/client/magic.js @@ -0,0 +1,75 @@ +import { effect_active, get, set, increment, source } from './runtime.js'; + +/** + * @template T + * @param {T} value + * @returns {T} + */ +export function magic(value) { + if (value && typeof value === 'object') { + if (Array.isArray(value)) { + // TODO + } + + return object(value); + } + + return value; +} + +/** + * @template {Record} T + * @param {T} value + * @returns {T} + */ +function object(value) { + /** @type {Map} */ + const sources = new Map(); + let version = source(0); + + return new Proxy(value, { + get(target, prop, receiver) { + let s = sources.get(prop); + + if (effect_active() && !s) { + s = source(magic(target[prop])); + sources.set(prop, s); + } + + let value = s ? get(s) : target[prop]; + + if (typeof value === 'function') { + return (...args) => { + return value.apply(receiver, args); + }; + } + + return value; + }, + set(target, prop, value) { + const s = sources.get(prop); + if (s) set(s, magic(value)); + + if (!(prop in target)) increment(version); + target[prop] = value; + + return true; + }, + deleteProperty(target, prop) { + const s = sources.get(prop); + if (s) set(s, undefined); + + if (prop in target) increment(version); + + return delete target[prop]; + }, + has(target, prop) { + get(version); + return Reflect.has(target, prop); + }, + ownKeys(target) { + get(version); + return Reflect.ownKeys(target); + } + }); +} diff --git a/packages/svelte/src/internal/index.js b/packages/svelte/src/internal/index.js index 3cdfbeaf4a..7824210d78 100644 --- a/packages/svelte/src/internal/index.js +++ b/packages/svelte/src/internal/index.js @@ -56,3 +56,5 @@ export { } from './client/operations.js'; export { raf } from './client/timing.js'; + +export { magic } from './client/magic.js'; diff --git a/packages/svelte/src/main/main-client.js b/packages/svelte/src/main/main-client.js index 9da264fc35..86c23ef9f2 100644 --- a/packages/svelte/src/main/main-client.js +++ b/packages/svelte/src/main/main-client.js @@ -255,4 +255,12 @@ export function afterUpdate(fn) { // TODO bring implementations in here // (except probably untrack — do we want to expose that, if there's also a rune?) -export { flushSync, createRoot, mount, tick, untrack, onDestroy } from '../internal/index.js'; +export { + flushSync, + createRoot, + mount, + tick, + untrack, + onDestroy, + magic +} from '../internal/index.js';