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 { AST } from '#compiler' */
/** @import { Context } from '../types' */ /** @import { Context } from '../types' */
import { get_rune } from '../../scope.js'; import { get_rune } from '../../scope.js';
import * as e from '../../../errors.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 { is_pure, is_safe_identifier } from './shared/utils.js';
import { dev, locate_node, source } from '../../../state.js'; import { dev, locate_node, source } from '../../../state.js';
import * as b from '../../../utils/builders.js'; import * as b from '../../../utils/builders.js';
@ -121,10 +121,18 @@ export function CallExpression(node, context) {
} }
if (arg.type === 'MemberExpression') { if (arg.type === 'MemberExpression') {
if (arg.object.type !== 'ThisExpression') { 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'); 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); e.state_invalidate_invalid_this_property(node);
} }
const possible_this_bindings = context.path.filter((parent, index) => { 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 { Context } from '../types' */
import { dev, is_ignored } from '../../../../state.js'; import { dev, is_ignored } from '../../../../state.js';
import * as b from '../../../../utils/builders.js'; import * as b from '../../../../utils/builders.js';
import { get_rune } from '../../../scope.js'; import { get_rune } from '../../../scope.js';
import { transform_inspect_rune } from '../../utils.js'; import { transform_inspect_rune } from '../../utils.js';
import * as e from '../../../../errors.js'; import * as e from '../../../../errors.js';
import { object } from '../../../../utils/ast.js';
/** /**
* @param {CallExpression} node * @param {CallExpression} node
@ -29,20 +30,32 @@ export function CallExpression(node, context) {
if (node.arguments[0].type === 'Identifier') { if (node.arguments[0].type === 'Identifier') {
return b.call('$.invalidate', node.arguments[0]); return b.call('$.invalidate', node.arguments[0]);
} else if (node.arguments[0].type === 'MemberExpression') { } else if (node.arguments[0].type === 'MemberExpression') {
const { property } = node.arguments[0]; const { object: obj, property } = node.arguments[0];
let field; const root = object(node.arguments[0]);
switch (property.type) { if (obj.type === 'ThisExpression') {
case 'Identifier': let field;
field = context.state.public_state.get(property.name); switch (property.type) {
break; case 'Identifier':
case 'PrivateIdentifier': field = context.state.public_state.get(property.name);
field = context.state.private_state.get(property.name); break;
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')) { /** @type {Expression[]} */
e.state_invalidate_nonreactive_argument(node); const source_args = /** @type {Expression[]} */ ([
} context.visit(obj),
return b.call('$.invalidate', b.member(b.this, field.id)); 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': case '$effect.root':

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

@ -148,7 +148,7 @@ export {
} from './runtime.js'; } from './runtime.js';
export { validate_binding, validate_each_keys } from './validate.js'; export { validate_binding, validate_each_keys } from './validate.js';
export { raf } from './timing.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 { create_custom_element } from './dom/elements/custom-element.js';
export { export {
child, child,

@ -9,7 +9,7 @@ import {
object_prototype object_prototype
} from '../shared/utils.js'; } from '../shared/utils.js';
import { state as source, set } from './reactivity/sources.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 { UNINITIALIZED } from '../../constants.js';
import * as e from './errors.js'; import * as e from './errors.js';
import { get_stack } from './dev/tracing.js'; import { get_stack } from './dev/tracing.js';
@ -124,6 +124,10 @@ export function proxy(value) {
return value; return value;
} }
if (prop === PROXY_SOURCES) {
return sources;
}
var s = sources.get(prop); var s = sources.get(prop);
var exists = prop in target; var exists = prop in target;
@ -165,7 +169,7 @@ export function proxy(value) {
}, },
has(target, prop) { has(target, prop) {
if (prop === STATE_SYMBOL) { if (prop === STATE_SYMBOL || prop === PROXY_SOURCES) {
return true; return true;
} }
@ -317,3 +321,22 @@ export function get_proxied_value(value) {
export function is(a, b) { export function is(a, b) {
return Object.is(get_proxied_value(a), get_proxied_value(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) { export function invalidate(source) {
if (source === null) {
return;
}
if ( if (
active_reaction !== null && active_reaction !== null &&
!untracking && !untracking &&

Loading…
Cancel
Save