Skip to content

Commit d4394c5

Browse files
committed
try adding support for individual property invalidation, might revert later
1 parent a9eb846 commit d4394c5

File tree

6 files changed

+70
-22
lines changed

6 files changed

+70
-22
lines changed

packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
/** @import { ArrowFunctionExpression, CallExpression, Expression, FunctionDeclaration, FunctionExpression, Identifier, VariableDeclarator } from 'estree' */
1+
/** @import { ArrowFunctionExpression, CallExpression, Expression, FunctionDeclaration, FunctionExpression, Identifier, MemberExpression, VariableDeclarator } from 'estree' */
22
/** @import { AST } from '#compiler' */
33
/** @import { Context } from '../types' */
44
import { get_rune } from '../../scope.js';
55
import * as e from '../../../errors.js';
6-
import { get_parent, unwrap_optional } from '../../../utils/ast.js';
6+
import { get_parent, object, unwrap_optional } from '../../../utils/ast.js';
77
import { is_pure, is_safe_identifier } from './shared/utils.js';
88
import { dev, locate_node, source } from '../../../state.js';
99
import * as b from '../../../utils/builders.js';
@@ -121,10 +121,18 @@ export function CallExpression(node, context) {
121121
}
122122
if (arg.type === 'MemberExpression') {
123123
if (arg.object.type !== 'ThisExpression') {
124-
e.state_invalidate_nonreactive_argument(node);
124+
const obj = object((arg = /** @type {MemberExpression} */ (context.visit(arg))));
125+
if (obj?.type === 'Identifier') {
126+
// there isn't really a good way to tell because of stuff like `notproxied = proxied`
127+
break;
128+
} else if (obj?.type !== 'ThisExpression') {
129+
e.state_invalidate_nonreactive_argument(node);
130+
}
131+
} else if (arg.computed) {
132+
e.state_invalidate_invalid_this_property(node);
125133
}
126134
const class_body = context.path.findLast((parent) => parent.type === 'ClassBody');
127-
if (arg.computed || !class_body) {
135+
if (!class_body) {
128136
e.state_invalidate_invalid_this_property(node);
129137
}
130138
const possible_this_bindings = context.path.filter((parent, index) => {

packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
/** @import { CallExpression, Expression } from 'estree' */
1+
/** @import { CallExpression, Expression, Identifier } from 'estree' */
22
/** @import { Context } from '../types' */
33
import { dev, is_ignored } from '../../../../state.js';
44
import * as b from '../../../../utils/builders.js';
55
import { get_rune } from '../../../scope.js';
66
import { transform_inspect_rune } from '../../utils.js';
77
import * as e from '../../../../errors.js';
8+
import { object } from '../../../../utils/ast.js';
89

910
/**
1011
* @param {CallExpression} node
@@ -29,20 +30,32 @@ export function CallExpression(node, context) {
2930
if (node.arguments[0].type === 'Identifier') {
3031
return b.call('$.invalidate', node.arguments[0]);
3132
} else if (node.arguments[0].type === 'MemberExpression') {
32-
const { property } = node.arguments[0];
33-
let field;
34-
switch (property.type) {
35-
case 'Identifier':
36-
field = context.state.public_state.get(property.name);
37-
break;
38-
case 'PrivateIdentifier':
39-
field = context.state.private_state.get(property.name);
40-
break;
33+
const { object: obj, property } = node.arguments[0];
34+
const root = object(node.arguments[0]);
35+
if (obj.type === 'ThisExpression') {
36+
let field;
37+
switch (property.type) {
38+
case 'Identifier':
39+
field = context.state.public_state.get(property.name);
40+
break;
41+
case 'PrivateIdentifier':
42+
field = context.state.private_state.get(property.name);
43+
break;
44+
}
45+
if (!field || (field.kind !== 'state' && field.kind !== 'raw_state')) {
46+
e.state_invalidate_nonreactive_argument(node);
47+
}
48+
return b.call('$.invalidate', b.member(b.this, field.id));
4149
}
42-
if (!field || (field.kind !== 'state' && field.kind !== 'raw_state')) {
43-
e.state_invalidate_nonreactive_argument(node);
44-
}
45-
return b.call('$.invalidate', b.member(b.this, field.id));
50+
/** @type {Expression[]} */
51+
const source_args = /** @type {Expression[]} */ ([
52+
context.visit(obj),
53+
node.arguments[0].computed
54+
? context.visit(property)
55+
: b.literal(/** @type {Identifier} */ (property).name)
56+
]);
57+
const arg = b.call('$.lookup_source', ...source_args);
58+
return b.call('$.invalidate', arg);
4659
}
4760

4861
case '$effect.root':

packages/svelte/src/internal/client/constants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@ export const EFFECT_IS_UPDATING = 1 << 21;
2525
export const STATE_SYMBOL = Symbol('$state');
2626
export const LEGACY_PROPS = Symbol('legacy props');
2727
export const LOADING_ATTR_SYMBOL = Symbol('');
28+
export const PROXY_SOURCES = Symbol('proxy sources');

packages/svelte/src/internal/client/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ export {
148148
} from './runtime.js';
149149
export { validate_binding, validate_each_keys } from './validate.js';
150150
export { raf } from './timing.js';
151-
export { proxy } from './proxy.js';
151+
export { proxy, lookup_source } from './proxy.js';
152152
export { create_custom_element } from './dom/elements/custom-element.js';
153153
export {
154154
child,

packages/svelte/src/internal/client/proxy.js

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
object_prototype
1010
} from '../shared/utils.js';
1111
import { state as source, set } from './reactivity/sources.js';
12-
import { STATE_SYMBOL } from './constants.js';
12+
import { STATE_SYMBOL, PROXY_SOURCES } from './constants.js';
1313
import { UNINITIALIZED } from '../../constants.js';
1414
import * as e from './errors.js';
1515
import { get_stack } from './dev/tracing.js';
@@ -124,6 +124,10 @@ export function proxy(value) {
124124
return value;
125125
}
126126

127+
if (prop === PROXY_SOURCES) {
128+
return sources;
129+
}
130+
127131
var s = sources.get(prop);
128132
var exists = prop in target;
129133

@@ -165,7 +169,7 @@ export function proxy(value) {
165169
},
166170

167171
has(target, prop) {
168-
if (prop === STATE_SYMBOL) {
172+
if (prop === STATE_SYMBOL || prop === PROXY_SOURCES) {
169173
return true;
170174
}
171175

@@ -317,3 +321,22 @@ export function get_proxied_value(value) {
317321
export function is(a, b) {
318322
return Object.is(get_proxied_value(a), get_proxied_value(b));
319323
}
324+
325+
/**
326+
* @param {Record<string | symbol, any>} object
327+
* @param {string | symbol} property
328+
* @returns {Source | null}
329+
*/
330+
export function lookup_source(object, property) {
331+
if (typeof object !== 'object' || object === null) return null;
332+
if (STATE_SYMBOL in object) {
333+
if (property in object) {
334+
/** @type {Map<string | symbol, Source>} */
335+
const sources = object[PROXY_SOURCES];
336+
if (sources.has(property)) {
337+
return /** @type {Source} */ (sources.get(property));
338+
}
339+
}
340+
}
341+
return null;
342+
}

packages/svelte/src/internal/client/reactivity/sources.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,9 +221,12 @@ export function internal_set(source, value) {
221221
}
222222

223223
/**
224-
* @param {Source} source
224+
* @param {Source | null} source
225225
*/
226226
export function invalidate(source) {
227+
if (source === null) {
228+
return;
229+
}
227230
if (
228231
active_reaction !== null &&
229232
!untracking &&

0 commit comments

Comments
 (0)