revert bad changes
proxied-state-each-blocks
Dominic Gannaway 9 months ago
parent 575d0fcfe7
commit 80b2253cc2

@ -3,6 +3,7 @@ export const EACH_INDEX_REACTIVE = 1 << 1;
export const EACH_KEYED = 1 << 2;
export const EACH_IS_CONTROLLED = 1 << 3;
export const EACH_IS_ANIMATED = 1 << 4;
export const EACH_IS_PROXIED = 1 << 5;
/** List of Element events that will be delegated */
export const DelegatedEvents = [

@ -1,6 +1,7 @@
import { effect_active, get, set, increment, source } from './runtime.js';
const symbol = Symbol('magic');
export const MAGIC_SYMBOL = Symbol();
export const MAGIC_EACH_SYMBOL = Symbol();
/**
* @template T
@ -9,7 +10,21 @@ const symbol = Symbol('magic');
*/
export function magic(value) {
if (value && typeof value === 'object') {
return object(value);
return wrap(value, null);
}
return value;
}
/**
* @template T
* @param {T} value
* @param {T | null} parent
* @returns {T}
*/
function wrap(value, parent) {
if (value && typeof value === 'object') {
return object(value, parent);
}
return value;
@ -17,11 +32,13 @@ export function magic(value) {
/**
* @template {Record<string | symbol, any>} T
* @template P
* @param {T} value
* @param {P | null} parent
* @returns {T}
*/
function object(value) {
if (symbol in value) return value;
function object(value, parent) {
if (MAGIC_SYMBOL in value) return value;
/** @type {Map<string | symbol, any>} */
const sources = new Map();
@ -31,49 +48,53 @@ function object(value) {
return new Proxy(value, {
get(target, prop, receiver) {
if (prop === MAGIC_EACH_SYMBOL) {
return parent;
}
let s = sources.get(prop);
if (effect_active() && !s) {
s = source(magic(target[prop]));
if (s === undefined && effect_active()) {
s = source(wrap(target[prop], receiver));
sources.set(prop, s);
}
const value = s ? get(s) : target[prop];
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) set(s, magic(value));
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) set(s, undefined);
if (s !== undefined) set(s, undefined);
}
}
if (!(prop in target)) increment(version);
// @ts-ignore
target[prop] = value;
return true;
},
deleteProperty(target, prop) {
const s = sources.get(prop);
if (s) set(s, undefined);
if (s !== undefined) set(s, undefined);
if (prop in target) increment(version);
return delete target[prop];
},
has(target, prop) {
if (prop === symbol) return true;
if (prop === MAGIC_SYMBOL) return true;
get(version);
return Reflect.has(target, prop);

@ -7,7 +7,13 @@ import {
} from './hydration.js';
import { is_array } from './utils.js';
import { each_item_block, destroy_each_item_block, update_each_item_block } from './render.js';
import { EACH_INDEX_REACTIVE, EACH_IS_ANIMATED, EACH_ITEM_REACTIVE } from '../../constants.js';
import {
EACH_INDEX_REACTIVE,
EACH_IS_ANIMATED,
EACH_IS_PROXIED,
EACH_ITEM_REACTIVE
} from '../../constants.js';
import { MAGIC_SYMBOL } from './magic.js';
const NEW_BLOCK = -1;
const MOVED_BLOCK = 99999999;
@ -177,9 +183,17 @@ export function reconcile_indexed_array(
flags,
apply_transitions
) {
var is_proxied_array = MAGIC_SYMBOL in array;
var a_blocks = each_block.v;
var active_transitions = each_block.s;
if (is_proxied_array) {
if ((flags & EACH_ITEM_REACTIVE) !== 0) {
flags ^= EACH_ITEM_REACTIVE;
}
flags |= EACH_IS_PROXIED;
}
/** @type {number | void} */
var a = a_blocks.length;
@ -220,7 +234,7 @@ export function reconcile_indexed_array(
hydrating_node = /** @type {Node} */ (
/** @type {Node} */ (/** @type {Node} */ (fragment.at(-1)).nextSibling).nextSibling
);
block = each_item_block(item, null, index, render_fn, flags);
block = each_item_block(array, item, null, index, render_fn, flags);
b_blocks[index] = block;
}
} else {
@ -228,7 +242,7 @@ export function reconcile_indexed_array(
if (index >= a) {
// Add block
item = array[index];
block = each_item_block(item, null, index, render_fn, flags);
block = each_item_block(array, item, null, index, render_fn, flags);
b_blocks[index] = block;
insert_each_item_block(block, dom, is_controlled, null);
} else if (index >= b) {
@ -277,8 +291,16 @@ export function reconcile_tracked_array(
) {
var a_blocks = each_block.v;
const is_computed_key = keys !== null;
var is_proxied_array = MAGIC_SYMBOL in array;
var active_transitions = each_block.s;
if (is_proxied_array) {
if ((flags & EACH_ITEM_REACTIVE) !== 0) {
flags ^= EACH_ITEM_REACTIVE;
}
flags |= EACH_IS_PROXIED;
}
/** @type {number | void} */
var a = a_blocks.length;
@ -327,7 +349,7 @@ export function reconcile_tracked_array(
hydrating_node = /** @type {Node} */ (
/** @type {Node} */ ((fragment.at(-1) || hydrating_node).nextSibling).nextSibling
);
block = each_item_block(item, key, idx, render_fn, flags);
block = each_item_block(array, item, key, idx, render_fn, flags);
b_blocks[idx] = block;
}
} else if (a === 0) {
@ -336,7 +358,7 @@ export function reconcile_tracked_array(
idx = b_end - --b;
item = array[idx];
key = is_computed_key ? keys[idx] : item;
block = each_item_block(item, key, idx, render_fn, flags);
block = each_item_block(array, item, key, idx, render_fn, flags);
b_blocks[idx] = block;
insert_each_item_block(block, dom, is_controlled, null);
}
@ -384,7 +406,7 @@ export function reconcile_tracked_array(
while (b_end >= start) {
item = array[b_end];
key = is_computed_key ? keys[b_end] : item;
block = each_item_block(item, key, b_end, render_fn, flags);
block = each_item_block(array, item, key, b_end, render_fn, flags);
b_blocks[b_end--] = block;
sibling = insert_each_item_block(block, dom, is_controlled, sibling);
}
@ -446,7 +468,7 @@ export function reconcile_tracked_array(
item = array[b_end];
if (should_create) {
key = is_computed_key ? keys[b_end] : item;
block = each_item_block(item, key, b_end, render_fn, flags);
block = each_item_block(array, item, key, b_end, render_fn, flags);
} else {
block = b_blocks[b_end];
if (!is_animated && should_update_block) {

@ -28,7 +28,8 @@ import {
EACH_ITEM_REACTIVE,
PassiveDelegatedEvents,
DelegatedEvents,
AttributeAliases
AttributeAliases,
EACH_IS_PROXIED
} from '../../constants.js';
import {
create_fragment_from_html,
@ -61,7 +62,9 @@ import {
push,
current_component_context,
pop,
schedule_task
schedule_task,
unwrap,
lazy_property
} from './runtime.js';
import {
current_hydration_fragment,
@ -2101,6 +2104,7 @@ export function destroy_each_item_block(
/**
* @template V
* @param {V[]} array
* @param {V} item
* @param {unknown} key
* @param {number} index
@ -2108,8 +2112,13 @@ export function destroy_each_item_block(
* @param {number} flags
* @returns {import('./types.js').EachItemBlock}
*/
export function each_item_block(item, key, index, render_fn, flags) {
const item_value = (flags & EACH_ITEM_REACTIVE) === 0 ? item : source(item);
export function each_item_block(array, item, key, index, render_fn, flags) {
const item_value =
(flags & EACH_IS_PROXIED) !== 0 && (flags & EACH_KEYED) === 0
? lazy_property(array, index)
: (flags & EACH_ITEM_REACTIVE) === 0
? item
: source(item);
const index_value = (flags & EACH_INDEX_REACTIVE) === 0 ? index : source(index);
const block = create_each_item_block(item_value, index_value, key);
const effect = render_effect(
@ -2891,20 +2900,6 @@ export function spread_props(props) {
return merged_props;
}
/**
* @template V
* @param {V} value
* @returns {import('./types.js').UnwrappedSignal<V>}
*/
export function unwrap(value) {
if (is_signal(value)) {
// @ts-ignore
return get(value);
}
// @ts-ignore
return value;
}
/**
* Mounts the given component to the given target and returns a handle to the component's public accessors
* as well as a `$set` and `$destroy` method to update the props of the component or destroy it.

@ -1,7 +1,6 @@
import { DEV } from 'esm-env';
import { subscribe_to_store } from '../../store/utils.js';
import { EMPTY_FUNC, run_all } from '../common.js';
import { unwrap } from './render.js';
import { get_descriptors, is_array } from './utils.js';
export const SOURCE = 1;
@ -24,6 +23,7 @@ const FLUSH_MICROTASK = 0;
const FLUSH_SYNC = 1;
export const UNINITIALIZED = Symbol();
export const LAZY_PROPERTY = Symbol();
// Used for controlling the flush of effects.
let current_scheduler_mode = FLUSH_MICROTASK;
@ -1384,6 +1384,20 @@ export function is_signal(val) {
);
}
/**
* @template O
* @template P
* @param {any} val
* @returns {val is import('./types.js').LazyProperty<O, P>}
*/
export function is_lazy_property(val) {
return (
typeof val === 'object' &&
val !== null &&
/** @type {import('./types.js').LazyProperty<O, P>} */ (val).t === LAZY_PROPERTY
);
}
/**
* @template V
* @param {unknown} val
@ -1860,3 +1874,35 @@ export function inspect(get_value, inspect = console.log) {
};
});
}
/**
* @template O
* @template P
* @param {O} o
* @param {P} p
* @returns {import('./types.js').LazyProperty<O, P>}
*/
export function lazy_property(o, p) {
return {
o,
p,
t: LAZY_PROPERTY
};
}
/**
* @template V
* @param {V} value
* @returns {import('./types.js').UnwrappedSignal<V>}
*/
export function unwrap(value) {
if (is_signal(value)) {
// @ts-ignore
return get(value);
}
if (is_lazy_property(value)) {
return value.o[value.p];
}
// @ts-ignore
return value;
}

@ -10,7 +10,7 @@ import {
DYNAMIC_ELEMENT_BLOCK,
SNIPPET_BLOCK
} from './block.js';
import { DERIVED, EFFECT, RENDER_EFFECT, SOURCE, PRE_EFFECT } from './runtime.js';
import { DERIVED, EFFECT, RENDER_EFFECT, SOURCE, PRE_EFFECT, LAZY_PROPERTY } from './runtime.js';
// Put all internal types in this file. Once we convert to JSDoc, we can make this a d.ts file
@ -116,6 +116,12 @@ export type MaybeSignal<T = unknown> = T | Signal<T>;
export type UnwrappedSignal<T> = T extends Signal<infer U> ? U : T;
export type LazyProperty<O, P> = {
o: O;
p: P;
t: typeof LAZY_PROPERTY;
};
export type EqualsFunctions<T = any> = (a: T, v: T) => boolean;
export type BlockType =

@ -38,7 +38,8 @@ export {
reactive_import,
effect_active,
user_root_effect,
inspect
inspect,
unwrap
} from './client/runtime.js';
export * from './client/validate.js';

@ -1,8 +1,7 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
skip: true, // TODO `array.reverse()` doesn't trigger each block update
html: `
<button>1 + 2 + 3 = 6</button>
<button>clear</button>
@ -31,7 +30,10 @@ export default test({
`
);
await reverse?.click();
flushSync(() => {
reverse?.click();
});
assert.htmlEqual(
target.innerHTML,
`
@ -46,7 +48,10 @@ export default test({
`
);
await clear?.click();
flushSync(() => {
clear?.click();
});
assert.htmlEqual(
target.innerHTML,
`
@ -54,7 +59,7 @@ export default test({
<button>clear</button>
<button>reverse</button>
<span>4</span>
<strong>array[1]: undefined</strong>
<strong>array[1]:</strong>
`
);
}

Loading…
Cancel
Save