try adding support for individual property invalidation, might revert later

pull/15673/head
ComputerGuy 5 months ago
parent a9eb8462b5
commit d4394c55d3

@ -1,9 +1,9 @@
/** @import { ArrowFunctionExpression, CallExpression, Expression, FunctionDeclaration, FunctionExpression, Identifier, VariableDeclarator } from 'estree' */
/** @import { ArrowFunctionExpression, CallExpression, Expression, FunctionDeclaration, FunctionExpression, Identifier, MemberExpression, VariableDeclarator } from 'estree' */
/** @import { AST } from '#compiler' */
/** @import { Context } from '../types' */
import { get_rune } from '../../scope.js';
import * as e from '../../../errors.js';
import { get_parent, unwrap_optional } from '../../../utils/ast.js';
import { get_parent, object, unwrap_optional } from '../../../utils/ast.js';
import { is_pure, is_safe_identifier } from './shared/utils.js';
import { dev, locate_node, source } from '../../../state.js';
import * as b from '../../../utils/builders.js';
@ -121,10 +121,18 @@ export function CallExpression(node, context) {
}
if (arg.type === 'MemberExpression') {
if (arg.object.type !== 'ThisExpression') {
e.state_invalidate_nonreactive_argument(node);
const obj = object((arg = /** @type {MemberExpression} */ (context.visit(arg))));
if (obj?.type === 'Identifier') {
// there isn't really a good way to tell because of stuff like `notproxied = proxied`
break;
} else if (obj?.type !== 'ThisExpression') {
e.state_invalidate_nonreactive_argument(node);
}
} else if (arg.computed) {
e.state_invalidate_invalid_this_property(node);
}
const class_body = context.path.findLast((parent) => parent.type === 'ClassBody');
if (arg.computed || !class_body) {
if (!class_body) {
e.state_invalidate_invalid_this_property(node);
}
const possible_this_bindings = context.path.filter((parent, index) => {

@ -1,10 +1,11 @@
/** @import { CallExpression, Expression } from 'estree' */
/** @import { CallExpression, Expression, Identifier } from 'estree' */
/** @import { Context } from '../types' */
import { dev, is_ignored } from '../../../../state.js';
import * as b from '../../../../utils/builders.js';
import { get_rune } from '../../../scope.js';
import { transform_inspect_rune } from '../../utils.js';
import * as e from '../../../../errors.js';
import { object } from '../../../../utils/ast.js';
/**
* @param {CallExpression} node
@ -29,20 +30,32 @@ export function CallExpression(node, context) {
if (node.arguments[0].type === 'Identifier') {
return b.call('$.invalidate', node.arguments[0]);
} else if (node.arguments[0].type === 'MemberExpression') {
const { property } = node.arguments[0];
let field;
switch (property.type) {
case 'Identifier':
field = context.state.public_state.get(property.name);
break;
case 'PrivateIdentifier':
field = context.state.private_state.get(property.name);
break;
const { object: obj, property } = node.arguments[0];
const root = object(node.arguments[0]);
if (obj.type === 'ThisExpression') {
let field;
switch (property.type) {
case 'Identifier':
field = context.state.public_state.get(property.name);
break;
case 'PrivateIdentifier':
field = context.state.private_state.get(property.name);
break;
}
if (!field || (field.kind !== 'state' && field.kind !== 'raw_state')) {
e.state_invalidate_nonreactive_argument(node);
}
return b.call('$.invalidate', b.member(b.this, field.id));
}
if (!field || (field.kind !== 'state' && field.kind !== 'raw_state')) {
e.state_invalidate_nonreactive_argument(node);
}
return b.call('$.invalidate', b.member(b.this, field.id));
/** @type {Expression[]} */
const source_args = /** @type {Expression[]} */ ([
context.visit(obj),
node.arguments[0].computed
? context.visit(property)
: b.literal(/** @type {Identifier} */ (property).name)
]);
const arg = b.call('$.lookup_source', ...source_args);
return b.call('$.invalidate', arg);
}
case '$effect.root':

@ -25,3 +25,4 @@ export const EFFECT_IS_UPDATING = 1 << 21;
export const STATE_SYMBOL = Symbol('$state');
export const LEGACY_PROPS = Symbol('legacy props');
export const LOADING_ATTR_SYMBOL = Symbol('');
export const PROXY_SOURCES = Symbol('proxy sources');

@ -148,7 +148,7 @@ export {
} from './runtime.js';
export { validate_binding, validate_each_keys } from './validate.js';
export { raf } from './timing.js';
export { proxy } from './proxy.js';
export { proxy, lookup_source } from './proxy.js';
export { create_custom_element } from './dom/elements/custom-element.js';
export {
child,

@ -9,7 +9,7 @@ import {
object_prototype
} from '../shared/utils.js';
import { state as source, set } from './reactivity/sources.js';
import { STATE_SYMBOL } from './constants.js';
import { STATE_SYMBOL, PROXY_SOURCES } from './constants.js';
import { UNINITIALIZED } from '../../constants.js';
import * as e from './errors.js';
import { get_stack } from './dev/tracing.js';
@ -124,6 +124,10 @@ export function proxy(value) {
return value;
}
if (prop === PROXY_SOURCES) {
return sources;
}
var s = sources.get(prop);
var exists = prop in target;
@ -165,7 +169,7 @@ export function proxy(value) {
},
has(target, prop) {
if (prop === STATE_SYMBOL) {
if (prop === STATE_SYMBOL || prop === PROXY_SOURCES) {
return true;
}
@ -317,3 +321,22 @@ export function get_proxied_value(value) {
export function is(a, b) {
return Object.is(get_proxied_value(a), get_proxied_value(b));
}
/**
* @param {Record<string | symbol, any>} object
* @param {string | symbol} property
* @returns {Source | null}
*/
export function lookup_source(object, property) {
if (typeof object !== 'object' || object === null) return null;
if (STATE_SYMBOL in object) {
if (property in object) {
/** @type {Map<string | symbol, Source>} */
const sources = object[PROXY_SOURCES];
if (sources.has(property)) {
return /** @type {Source} */ (sources.get(property));
}
}
}
return null;
}

@ -221,9 +221,12 @@ export function internal_set(source, value) {
}
/**
* @param {Source} source
* @param {Source | null} source
*/
export function invalidate(source) {
if (source === null) {
return;
}
if (
active_reaction !== null &&
!untracking &&

Loading…
Cancel
Save