feat: implement subscribing to derived of keys instead of the whole object for spreads

pull/11290/head
paoloricciuti 2 years ago
parent 573e567db0
commit 27321f7799

@ -628,6 +628,9 @@ function serialize_inline_component(node, component_name, context) {
/** @type {Array<import('estree').Property[] | import('estree').Expression>} */
const props_and_spreads = [];
/** @type {(import('estree').Expression | null)[]} */
const spreads_keys = [];
/** @type {import('estree').ExpressionStatement[]} */
const lets = [];
@ -661,6 +664,7 @@ function serialize_inline_component(node, component_name, context) {
props.push(prop);
if (!current_is_props) {
props_and_spreads.push(props);
spreads_keys.push(null);
}
}
for (const attribute of node.attributes) {
@ -685,8 +689,19 @@ function serialize_inline_component(node, component_name, context) {
}
props_and_spreads.push(b.thunk(value));
const keys = b.call(
'$.derived',
b.thunk(
b.call(
b.member(b.call('Object.keys', b.logical('??', value, b.object([]))), b.id('join')),
b.literal(',')
)
)
);
spreads_keys.push(b.thunk(keys));
} else {
props_and_spreads.push(expression);
spreads_keys.push(null);
}
} else if (attribute.type === 'Attribute') {
if (attribute.name.startsWith('--')) {
@ -843,7 +858,8 @@ function serialize_inline_component(node, component_name, context) {
? b.object(/** @type {import('estree').Property[]} */ (props_and_spreads[0]) || [])
: b.call(
'$.spread_props',
...props_and_spreads.map((p) => (Array.isArray(p) ? b.object(p) : p))
...props_and_spreads.map((p) => (Array.isArray(p) ? b.object(p) : p)),
b.array(spreads_keys)
);
/** @param {import('estree').Identifier} node_id */
let fn = (node_id) =>
@ -2929,6 +2945,9 @@ export const template_visitors = {
/** @type {import('estree').Expression[]} */
const spreads = [];
/** @type {(import('estree').Expression | null)[]} */
const spreads_keys = [];
/** @type {import('estree').ExpressionStatement[]} */
const lets = [];
@ -2939,9 +2958,18 @@ export const template_visitors = {
for (const attribute of node.attributes) {
if (attribute.type === 'SpreadAttribute') {
spreads.push(
b.thunk(/** @type {import('estree').Expression} */ (context.visit(attribute)))
const value = /** @type {import('estree').Expression} */ (context.visit(attribute));
const keys = b.call(
'$.derived',
b.thunk(
b.call(
b.member(b.call('Object.keys', b.logical('??', value, b.object([]))), b.id('join')),
b.literal(',')
)
)
);
spreads.push(b.thunk(value));
spreads_keys.push(b.thunk(keys));
} else if (attribute.type === 'Attribute') {
const [, value] = serialize_attribute_value(attribute.value, context);
if (attribute.name === 'name') {
@ -2950,8 +2978,10 @@ export const template_visitors = {
} else if (attribute.name !== 'slot') {
if (attribute.metadata.dynamic) {
props.push(b.get(attribute.name, [b.return(value)]));
spreads_keys.push(null);
} else {
props.push(b.init(attribute.name, value));
spreads_keys.push(null);
}
}
} else if (attribute.type === 'LetDirective') {
@ -2965,7 +2995,7 @@ export const template_visitors = {
const props_expression =
spreads.length === 0
? b.object(props)
: b.call('$.spread_props', b.object(props), ...spreads);
: b.call('$.spread_props', b.object(props), ...spreads, b.array(spreads_keys));
const fallback =
node.fragment.nodes.length === 0
? b.literal(null)

@ -87,7 +87,7 @@ export function rest_props(props, exclude, name) {
* that looks like `() => { dynamic: props }, { static: prop }, ..` and wraps
* them so that the whole thing is passed to the component as the `$$props` argument.
* @template {Record<string | symbol, unknown>} T
* @type {ProxyHandler<{ props: Array<T | (() => T)> }>}}
* @type {ProxyHandler<{ props: (Array<T | (() => T)>), keys: T }>}}
*/
const spread_props_handler = {
get(target, key) {
@ -95,7 +95,6 @@ const spread_props_handler = {
while (i--) {
let p = target.props[i];
let obj = p;
debugger;
// in case the prop is the spread prop calling the function
// will track that state as a dep even if the requested key
// is not in it. to avoid that we call the function in untrack
@ -104,6 +103,8 @@ const spread_props_handler = {
untrack(() => {
if (is_function(p)) obj = p();
});
const keys_function = target.keys[i];
if (keys_function && keys_function instanceof Function) get(keys_function());
if (typeof obj === 'object' && obj !== null && key in obj) {
if (is_function(p)) p = p();
return p[key];
@ -123,6 +124,8 @@ const spread_props_handler = {
untrack(() => {
if (is_function(p)) obj = p();
});
const keys_function = target.keys[i];
if (keys_function && keys_function instanceof Function) get(keys_function());
if (typeof obj === 'object' && obj !== null && key in obj) {
if (is_function(p)) p = p();
return get_descriptor(p, key);
@ -131,17 +134,8 @@ const spread_props_handler = {
},
has(target, key) {
for (let p of target.props) {
let obj = p;
// in case the prop is the spread prop calling the function
// will track that state as a dep even if the requested key
// is not in it. to avoid that we call the function in untrack
// and check on the object. If it's there we call the function again
// to track and then access the key
untrack(() => {
if (is_function(p)) obj = p();
});
if (key in obj) {
if (is_function(p)) p();
if (is_function(p)) p = p();
if (key in p) {
return true;
}
}
@ -153,20 +147,9 @@ const spread_props_handler = {
const keys = [];
for (let p of target.props) {
let obj = p;
// in case the prop is the spread prop calling the function
// will track that state as a dep even if the requested key
// is not in it. to avoid that we call the function in untrack
// and check on the object. If it's there we call the function again
// to track and then access the key
untrack(() => {
if (is_function(p)) obj = p();
});
for (const key in obj) {
if (!keys.includes(key)) {
if (is_function(p)) obj = p();
keys.push(key);
}
if (is_function(p)) p = p();
for (const key in p) {
if (!keys.includes(key)) keys.push(key);
}
}
@ -179,7 +162,8 @@ const spread_props_handler = {
* @returns {any}
*/
export function spread_props(...props) {
return new Proxy({ props }, spread_props_handler);
const keys = props.pop();
return new Proxy({ props, keys }, spread_props_handler);
}
/**

Loading…
Cancel
Save