Skip to content

Commit 3bfcacc

Browse files
committed
fix
1 parent 1f52cf0 commit 3bfcacc

File tree

17 files changed

+100
-77
lines changed

17 files changed

+100
-77
lines changed

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import { Literal } from './visitors/Literal.js';
4848
import { MemberExpression } from './visitors/MemberExpression.js';
4949
import { NewExpression } from './visitors/NewExpression.js';
5050
import { OnDirective } from './visitors/OnDirective.js';
51+
import { PropertyDefinition } from './visitors/PropertyDefinition.js';
5152
import { RegularElement } from './visitors/RegularElement.js';
5253
import { RenderTag } from './visitors/RenderTag.js';
5354
import { SlotElement } from './visitors/SlotElement.js';
@@ -164,6 +165,7 @@ const visitors = {
164165
MemberExpression,
165166
NewExpression,
166167
OnDirective,
168+
PropertyDefinition,
167169
RegularElement,
168170
RenderTag,
169171
SlotElement,
@@ -266,7 +268,7 @@ export function analyze_module(ast, options) {
266268
scope,
267269
scopes,
268270
analysis: /** @type {ComponentAnalysis} */ (analysis),
269-
state_fields: null,
271+
state_fields: new Map(),
270272
// TODO the following are not needed for modules, but we have to pass them in order to avoid type error,
271273
// and reducing the type would result in a lot of tedious type casts elsewhere - find a good solution one day
272274
ast_type: /** @type {any} */ (null),
@@ -626,7 +628,7 @@ export function analyze_component(root, source, options) {
626628
has_props_rune: false,
627629
component_slots: new Set(),
628630
expression: null,
629-
state_fields: null,
631+
state_fields: new Map(),
630632
function_depth: scope.function_depth,
631633
reactive_statement: null
632634
};
@@ -693,7 +695,7 @@ export function analyze_component(root, source, options) {
693695
reactive_statement: null,
694696
component_slots: new Set(),
695697
expression: null,
696-
state_fields: null,
698+
state_fields: new Map(),
697699
function_depth: scope.function_depth
698700
};
699701

packages/svelte/src/compiler/phases/2-analyze/types.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export interface AnalysisState {
2020
expression: ExpressionMetadata | null;
2121

2222
/** Used to analyze class state */
23-
state_fields: Record<string, StateField> | null;
23+
state_fields: Map<string, StateField>;
2424

2525
function_depth: number;
2626

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

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,11 @@ export function ClassBody(node, context) {
3030
}
3131
}
3232

33-
/** @type {Record<string, StateField>} */
34-
const state_fields = {};
33+
/** @type {Map<string, StateField>} */
34+
const state_fields = new Map();
3535

3636
context.state.analysis.classes.set(node, state_fields);
3737

38-
/** @type {string[]} */
39-
const seen = [];
40-
4138
/** @type {MethodDefinition | null} */
4239
let constructor = null;
4340

@@ -53,24 +50,22 @@ export function ClassBody(node, context) {
5350
const rune = get_rune(value, context.state.scope);
5451

5552
if (rune && is_state_creation_rune(rune)) {
56-
if (seen.includes(name)) {
53+
if (state_fields.has(name)) {
5754
e.state_field_duplicate(node, name);
5855
}
5956

60-
state_fields[name] = {
57+
state_fields.set(name, {
6158
node,
6259
type: rune,
6360
// @ts-expect-error for public state this is filled out in a moment
6461
key: key.type === 'PrivateIdentifier' ? key : null,
6562
value: /** @type {CallExpression} */ (value)
66-
};
67-
68-
seen.push(name);
63+
});
6964
}
7065
}
7166

7267
for (const child of node.body) {
73-
if (child.type === 'PropertyDefinition' && !child.computed) {
68+
if (child.type === 'PropertyDefinition' && !child.computed && !child.static) {
7469
handle(child, child.key, child.value);
7570
}
7671

@@ -94,13 +89,11 @@ export function ClassBody(node, context) {
9489
}
9590
}
9691

97-
for (const name in state_fields) {
92+
for (const [name, field] of state_fields) {
9893
if (name[0] === '#') {
9994
continue;
10095
}
10196

102-
const field = state_fields[name];
103-
10497
let deconflicted = name.replace(regex_invalid_identifier_chars, '_');
10598
while (private_ids.includes(deconflicted)) {
10699
deconflicted = '_' + deconflicted;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/** @import { PropertyDefinition } from 'estree' */
2+
/** @import { Context } from '../types' */
3+
import * as e from '../../../errors.js';
4+
import { get_name } from '../../nodes.js';
5+
6+
/**
7+
* @param {PropertyDefinition} node
8+
* @param {Context} context
9+
*/
10+
export function PropertyDefinition(node, context) {
11+
const name = get_name(node.key);
12+
const field = name && context.state.state_fields.get(name);
13+
14+
if (field && node !== field.node && node.value) {
15+
if (/** @type {number} */ (node.start) < /** @type {number} */ (field.node.start)) {
16+
e.state_field_invalid_assignment(node);
17+
}
18+
}
19+
20+
context.next();
21+
}

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

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,7 @@ export function validate_assignment(node, argument, context) {
4242
? null
4343
: get_name(argument.property);
4444

45-
const field =
46-
name !== null &&
47-
context.state.state_fields &&
48-
Object.hasOwn(context.state.state_fields, name) &&
49-
context.state.state_fields[name];
45+
const field = name !== null && context.state.state_fields?.get(name);
5046

5147
// check we're not assigning to a state field before its declaration in the constructor
5248
if (field && field.node.type === 'AssignmentExpression' && node !== field.node) {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ export function client_component(analysis, options) {
163163
},
164164
events: new Set(),
165165
preserve_whitespace: options.preserveWhitespace,
166-
state_fields: {},
166+
state_fields: new Map(),
167167
transform: {},
168168
in_constructor: false,
169169
instance_level_snippets: [],
@@ -670,7 +670,7 @@ export function client_module(analysis, options) {
670670
options,
671671
scope: analysis.module.scope,
672672
scopes: analysis.module.scopes,
673-
state_fields: {},
673+
state_fields: new Map(),
674674
transform: {},
675675
in_constructor: false
676676
};

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

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,11 @@ const callees = {
5454
function build_assignment(operator, left, right, context) {
5555
if (context.state.analysis.runes && left.type === 'MemberExpression') {
5656
const name = get_name(left.property);
57+
const field = name && context.state.state_fields.get(name);
5758

58-
if (name !== null) {
59+
if (field) {
5960
// special case — state declaration in class constructor
60-
const ancestor = context.path.at(-4);
61-
62-
if (ancestor?.type === 'MethodDefinition' && ancestor.kind === 'constructor') {
61+
if (field.node.type === 'AssignmentExpression' && left === field.node.left) {
6362
const rune = get_rune(right, context.state.scope);
6463

6564
if (rune) {
@@ -70,28 +69,24 @@ function build_assignment(operator, left, right, context) {
7069

7170
return b.assignment(
7271
operator,
73-
b.member(b.this, context.state.state_fields[name].key),
72+
b.member(b.this, field.key),
7473
/** @type {Expression} */ (context.visit(right, child_state))
7574
);
7675
}
7776
}
7877

7978
// special case — assignment to private state field
8079
if (left.property.type === 'PrivateIdentifier') {
81-
const field = context.state.state_fields[name];
82-
83-
if (field) {
84-
let value = /** @type {Expression} */ (
85-
context.visit(build_assignment_value(operator, left, right))
86-
);
80+
let value = /** @type {Expression} */ (
81+
context.visit(build_assignment_value(operator, left, right))
82+
);
8783

88-
const needs_proxy =
89-
field.type === '$state' &&
90-
is_non_coercive_operator(operator) &&
91-
should_proxy(value, context.state.scope);
84+
const needs_proxy =
85+
field.type === '$state' &&
86+
is_non_coercive_operator(operator) &&
87+
should_proxy(value, context.state.scope);
9288

93-
return b.call('$.set', left, value, needs_proxy && b.true);
94-
}
89+
return b.call('$.set', left, value, needs_proxy && b.true);
9590
}
9691
}
9792
}

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

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/** @import { CallExpression, ClassBody, MethodDefinition, PropertyDefinition, StaticBlock } from 'estree' */
2+
/** @import { StateField } from '#compiler' */
23
/** @import { Context } from '../types' */
34
import * as b from '#compiler/builders';
45
import { get_name } from '../../../nodes.js';
@@ -21,13 +22,11 @@ export function ClassBody(node, context) {
2122

2223
const child_state = { ...context.state, state_fields };
2324

24-
for (const name in state_fields) {
25+
for (const [name, field] of state_fields) {
2526
if (name[0] === '#') {
2627
continue;
2728
}
2829

29-
const field = state_fields[name];
30-
3130
// insert backing fields for stuff declared in the constructor
3231
if (field.node.type === 'AssignmentExpression') {
3332
const member = b.member(b.this, field.key);
@@ -61,13 +60,13 @@ export function ClassBody(node, context) {
6160
}
6261

6362
const name = get_name(definition.key);
64-
if (name === null || !Object.hasOwn(state_fields, name)) {
63+
const field = name && /** @type {StateField} */ (state_fields.get(name));
64+
65+
if (!field) {
6566
body.push(/** @type {PropertyDefinition} */ (context.visit(definition, child_state)));
6667
continue;
6768
}
6869

69-
const field = state_fields[name];
70-
7170
if (name[0] === '#') {
7271
body.push(/** @type {PropertyDefinition} */ (context.visit(definition, child_state)));
7372
} else if (field.node === definition) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import * as b from '#compiler/builders';
99
export function MemberExpression(node, context) {
1010
// rewrite `this.#foo` as `this.#foo.v` inside a constructor
1111
if (node.property.type === 'PrivateIdentifier') {
12-
const field = context.state.state_fields['#' + node.property.name];
12+
const field = context.state.state_fields.get('#' + node.property.name);
1313

1414
if (field) {
1515
return context.state.in_constructor &&

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export function UpdateExpression(node, context) {
1515
argument.type === 'MemberExpression' &&
1616
argument.object.type === 'ThisExpression' &&
1717
argument.property.type === 'PrivateIdentifier' &&
18-
Object.hasOwn(context.state.state_fields, '#' + argument.property.name)
18+
context.state.state_fields.has('#' + argument.property.name)
1919
) {
2020
let fn = '$.update';
2121
if (node.prefix) fn += '_pre';

0 commit comments

Comments
 (0)