Skip to content

feat: add $state.invalidate rune #15673

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
try adding support for individual property invalidation, might revert…
… later
  • Loading branch information
Ocean-OS committed Apr 11, 2025
commit d4394c55d3ffae1b2d4a59692adab37f2c264ebc
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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':
Expand Down
1 change: 1 addition & 0 deletions packages/svelte/src/internal/client/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
2 changes: 1 addition & 1 deletion packages/svelte/src/internal/client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
27 changes: 25 additions & 2 deletions packages/svelte/src/internal/client/proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -165,7 +169,7 @@ export function proxy(value) {
},

has(target, prop) {
if (prop === STATE_SYMBOL) {
if (prop === STATE_SYMBOL || prop === PROXY_SOURCES) {
return true;
}

Expand Down Expand Up @@ -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;
}
5 changes: 4 additions & 1 deletion packages/svelte/src/internal/client/reactivity/sources.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 &&
Expand Down
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy