Skip to content

Commit 766e6f0

Browse files
committed
init
1 parent 32ee6c1 commit 766e6f0

File tree

7 files changed

+58
-27
lines changed

7 files changed

+58
-27
lines changed

.changeset/brown-plums-prove.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: only make variables reactive if they are read (and reassigned)

packages/svelte/src/compiler/phases/2-analyze/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,10 @@ export function analyze_component(root, source, options) {
389389
});
390390

391391
const binding = instance.scope.declare(b.id(name), 'store_sub', 'synthetic');
392+
const store_binding = instance.scope.get(name.slice(1));
393+
if (store_binding) {
394+
store_binding.has_store_sub = true;
395+
}
392396
binding.references = references;
393397
instance.scope.references.set(name, references);
394398
module.scope.references.delete(name);

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ import { get_value } from './visitors/shared/declarations.js';
2323
export function is_state_source(binding, analysis) {
2424
return (
2525
(binding.kind === 'state' || binding.kind === 'raw_state') &&
26-
(!analysis.immutable || binding.reassigned || analysis.accessors)
26+
(!analysis.immutable ||
27+
(binding.reassigned && (binding.read || binding.has_store_sub)) ||
28+
analysis.accessors)
2729
);
2830
}
2931

packages/svelte/src/compiler/phases/scope.js

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ export class Binding {
5959

6060
mutated = false;
6161
reassigned = false;
62-
62+
read = false;
63+
has_store_sub = false;
6364
/**
6465
*
6566
* @param {Scope} scope
@@ -310,7 +311,7 @@ export class ScopeRoot {
310311
* @param {Scope | null} parent
311312
*/
312313
export function create_scopes(ast, root, allow_reactive_declarations, parent) {
313-
/** @typedef {{ scope: Scope }} State */
314+
/** @typedef {{ scope: Scope, reading: boolean }} State */
314315

315316
/**
316317
* A map of node->associated scope. A node appearing in this map does not necessarily mean that it created a scope
@@ -321,14 +322,16 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
321322
scopes.set(ast, scope);
322323

323324
/** @type {State} */
324-
const state = { scope };
325+
const state = { scope, reading: true };
325326

326-
/** @type {[Scope, { node: Identifier; path: AST.SvelteNode[] }][]} */
327+
/** @type {[Scope, { node: Identifier; path: AST.SvelteNode[] }, boolean][]} */
327328
const references = [];
328329

329330
/** @type {[Scope, Pattern | MemberExpression][]} */
330331
const updates = [];
331332

333+
/** @type {[Scope, Pattern | MemberExpression][]} */
334+
const read_updates = [];
332335
/**
333336
* An array of reactive declarations, i.e. the `a` in `$: a = b * 2`
334337
* @type {Identifier[]}
@@ -354,7 +357,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
354357
const scope = state.scope.child(true);
355358
scopes.set(node, scope);
356359

357-
next({ scope });
360+
next({ scope, reading: state.reading });
358361
};
359362

360363
/**
@@ -363,7 +366,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
363366
const SvelteFragment = (node, { state, next }) => {
364367
const scope = state.scope.child();
365368
scopes.set(node, scope);
366-
next({ scope });
369+
next({ scope, reading: state.reading });
367370
};
368371

369372
/**
@@ -380,7 +383,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
380383

381384
const default_state = determine_slot(node)
382385
? context.state
383-
: { scope: node.metadata.scopes.default };
386+
: { scope: node.metadata.scopes.default, reading: true };
384387

385388
for (const attribute of node.attributes) {
386389
if (attribute.type === 'LetDirective') {
@@ -399,7 +402,8 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
399402
node.metadata.scopes[slot_name] = context.state.scope.child();
400403

401404
state = {
402-
scope: node.metadata.scopes[slot_name]
405+
scope: node.metadata.scopes[slot_name],
406+
reading: true
403407
};
404408
}
405409

@@ -430,10 +434,10 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
430434
// TODO -> once migration script is gone we can remove this check
431435
!parent.type.startsWith('TS')
432436
) {
433-
references.push([state.scope, { node, path: path.slice() }]);
437+
references.push([state.scope, { node, path: path.slice() }, state.reading]);
434438
}
435439
},
436-
LabeledStatement(node, { path, next }) {
440+
LabeledStatement(node, { state, path, next }) {
437441
if (path.length > 1 || !allow_reactive_declarations) return next();
438442
if (node.label.name !== '$') return next();
439443

@@ -452,7 +456,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
452456
}
453457
}
454458

455-
next({ scope });
459+
next({ scope, reading: state.reading });
456460
},
457461

458462
SvelteFragment,
@@ -495,13 +499,22 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
495499
SvelteComponent: Component,
496500

497501
// updates
498-
AssignmentExpression(node, { state, next }) {
502+
AssignmentExpression(node, { state, next, visit }) {
499503
updates.push([state.scope, node.left]);
500-
next();
504+
let reading = state.reading;
505+
if (node.operator !== '=') {
506+
reading = true;
507+
} else {
508+
reading = false;
509+
}
510+
visit(node.left, { ...state, reading });
511+
visit(node.right, state);
501512
},
502513

503514
UpdateExpression(node, { state, next }) {
504515
updates.push([state.scope, /** @type {Identifier | MemberExpression} */ (node.argument)]);
516+
//@ts-ignore
517+
read_updates.push(updates.at(-1));
505518
next();
506519
},
507520

@@ -518,7 +531,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
518531
if (node.id) scope.declare(node.id, 'normal', 'function');
519532

520533
add_params(scope, node.params);
521-
next({ scope });
534+
next({ scope, reading: state.reading });
522535
},
523536

524537
FunctionDeclaration(node, { state, next }) {
@@ -528,15 +541,15 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
528541
scopes.set(node, scope);
529542

530543
add_params(scope, node.params);
531-
next({ scope });
544+
next({ scope, reading: state.reading });
532545
},
533546

534547
ArrowFunctionExpression(node, { state, next }) {
535548
const scope = state.scope.child();
536549
scopes.set(node, scope);
537550

538551
add_params(scope, node.params);
539-
next({ scope });
552+
next({ scope, reading: state.reading });
540553
},
541554

542555
ForStatement: create_block_scope,
@@ -593,7 +606,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
593606
scope.declare(id, 'normal', 'let');
594607
}
595608

596-
next({ scope });
609+
next({ scope, reading: state.reading });
597610
} else {
598611
next();
599612
}
@@ -631,7 +644,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
631644
}
632645

633646
// Visit to pick up references from default initializers
634-
visit(node.context, { scope });
647+
visit(node.context, { scope, reading: state.reading });
635648
}
636649

637650
if (node.index) {
@@ -640,13 +653,13 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
640653
(node.key.type !== 'Identifier' || !node.index || node.key.name !== node.index);
641654
scope.declare(b.id(node.index), is_keyed ? 'template' : 'normal', 'const', node);
642655
}
643-
if (node.key) visit(node.key, { scope });
656+
if (node.key) visit(node.key, { scope, reading: state.reading });
644657

645658
// children
646659
for (const child of node.body.nodes) {
647-
visit(child, { scope });
660+
visit(child, { scope, reading: state.reading });
648661
}
649-
if (node.fallback) visit(node.fallback, { scope });
662+
if (node.fallback) visit(node.fallback, { scope, reading: state.reading });
650663

651664
node.metadata = {
652665
expression: create_expression_metadata(),
@@ -671,7 +684,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
671684
const then_scope = /** @type {Scope} */ (scopes.get(node.then));
672685
const value_scope = context.state.scope.child();
673686
scopes.set(node.value, value_scope);
674-
context.visit(node.value, { scope: value_scope });
687+
context.visit(node.value, { scope: value_scope, reading: state.reading });
675688
for (const id of extract_identifiers(node.value)) {
676689
then_scope.declare(id, 'template', 'const');
677690
value_scope.declare(id, 'normal', 'const');
@@ -685,7 +698,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
685698
const catch_scope = /** @type {Scope} */ (scopes.get(node.catch));
686699
const error_scope = context.state.scope.child();
687700
scopes.set(node.error, error_scope);
688-
context.visit(node.error, { scope: error_scope });
701+
context.visit(node.error, { scope: error_scope, reading: state.reading });
689702
for (const id of extract_identifiers(node.error)) {
690703
catch_scope.declare(id, 'template', 'const');
691704
error_scope.declare(id, 'normal', 'const');
@@ -709,13 +722,13 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
709722
}
710723
}
711724

712-
context.next({ scope: child_scope });
725+
context.next({ scope: child_scope, reading: state.reading });
713726
},
714727

715728
Fragment: (node, context) => {
716729
const scope = context.state.scope.child(node.metadata.transparent);
717730
scopes.set(node, scope);
718-
context.next({ scope });
731+
context.next({ scope, reading: state.reading });
719732
},
720733

721734
BindDirective(node, context) {
@@ -753,8 +766,12 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
753766

754767
// we do this after the fact, so that we don't need to worry
755768
// about encountering references before their declarations
756-
for (const [scope, { node, path }] of references) {
769+
for (const [scope, { node, path }, reading] of references) {
757770
scope.reference(node, path);
771+
let binding = scope.get(node.name);
772+
if (binding && binding.node !== node && reading) {
773+
binding.read = true;
774+
}
758775
}
759776

760777
for (const [scope, node] of updates) {

packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/client/index.svelte.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ export function update(array) {
1313
);
1414

1515
[c, d] = array;
16+
console.log({ a: $.get(a), b: $.get(b) });
1617
}

packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/server/index.svelte.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ let d = 4;
99
export function update(array) {
1010
[a, b] = array;
1111
[c, d] = array;
12+
console.log({ a, b });
1213
}

packages/svelte/tests/snapshot/samples/destructured-assignments/index.svelte.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ let d = 4;
66
export function update(array) {
77
[a, b] = array;
88
[c, d] = array;
9+
console.log({ a, b });
910
}

0 commit comments

Comments
 (0)