Skip to content

Commit 72d7901

Browse files
committed
more
1 parent 431d17b commit 72d7901

File tree

3 files changed

+158
-132
lines changed

3 files changed

+158
-132
lines changed

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

Lines changed: 4 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ import { filename } from '../../../state.js';
1212
import { render_stylesheet } from '../css/index.js';
1313
import { AssignmentExpression } from './visitors/javascript/AssignmentExpression.js';
1414
import { CallExpression } from './visitors/javascript/CallExpression.js';
15+
import { ClassBodyRunes } from './visitors/javascript/ClassBody.js';
1516
import { Identifier } from './visitors/javascript/Identifier.js';
1617
import { UpdateExpression } from './visitors/javascript/UpdateExpression.js';
18+
import { PropertyDefinitionRunes } from './visitors/javascript/PropertyDefinition.js';
1719
import { AwaitBlock } from './visitors/template/AwaitBlock.js';
1820
import { Component } from './visitors/template/Component.js';
1921
import { ConstTag } from './visitors/template/ConstTag.js';
@@ -68,138 +70,8 @@ const global_visitors = {
6870

6971
/** @type {Visitors} */
7072
const javascript_visitors_runes = {
71-
ClassBody(node, { state, visit }) {
72-
/** @type {Map<string, StateField>} */
73-
const public_derived = new Map();
74-
75-
/** @type {Map<string, StateField>} */
76-
const private_derived = new Map();
77-
78-
/** @type {string[]} */
79-
const private_ids = [];
80-
81-
for (const definition of node.body) {
82-
if (
83-
definition.type === 'PropertyDefinition' &&
84-
(definition.key.type === 'Identifier' || definition.key.type === 'PrivateIdentifier')
85-
) {
86-
const { type, name } = definition.key;
87-
88-
const is_private = type === 'PrivateIdentifier';
89-
if (is_private) private_ids.push(name);
90-
91-
if (definition.value?.type === 'CallExpression') {
92-
const rune = get_rune(definition.value, state.scope);
93-
if (rune === '$derived' || rune === '$derived.by') {
94-
/** @type {StateField} */
95-
const field = {
96-
kind: rune === '$derived.by' ? 'derived_call' : 'derived',
97-
// @ts-expect-error this is set in the next pass
98-
id: is_private ? definition.key : null
99-
};
100-
101-
if (is_private) {
102-
private_derived.set(name, field);
103-
} else {
104-
public_derived.set(name, field);
105-
}
106-
}
107-
}
108-
}
109-
}
110-
111-
// each `foo = $derived()` needs a backing `#foo` field
112-
for (const [name, field] of public_derived) {
113-
let deconflicted = name;
114-
while (private_ids.includes(deconflicted)) {
115-
deconflicted = '_' + deconflicted;
116-
}
117-
118-
private_ids.push(deconflicted);
119-
field.id = b.private_id(deconflicted);
120-
}
121-
122-
/** @type {Array<MethodDefinition | PropertyDefinition>} */
123-
const body = [];
124-
125-
const child_state = { ...state, private_derived };
126-
127-
// Replace parts of the class body
128-
for (const definition of node.body) {
129-
if (
130-
definition.type === 'PropertyDefinition' &&
131-
(definition.key.type === 'Identifier' || definition.key.type === 'PrivateIdentifier')
132-
) {
133-
const name = definition.key.name;
134-
135-
const is_private = definition.key.type === 'PrivateIdentifier';
136-
const field = (is_private ? private_derived : public_derived).get(name);
137-
138-
if (definition.value?.type === 'CallExpression' && field !== undefined) {
139-
const init = /** @type {Expression} **/ (
140-
visit(definition.value.arguments[0], child_state)
141-
);
142-
const value =
143-
field.kind === 'derived_call'
144-
? b.call('$.once', init)
145-
: b.call('$.once', b.thunk(init));
146-
147-
if (is_private) {
148-
body.push(b.prop_def(field.id, value));
149-
} else {
150-
// #foo;
151-
const member = b.member(b.this, field.id);
152-
body.push(b.prop_def(field.id, value));
153-
154-
// get foo() { return this.#foo; }
155-
body.push(b.method('get', definition.key, [], [b.return(b.call(member))]));
156-
157-
if ((field.kind === 'derived' || field.kind === 'derived_call') && state.options.dev) {
158-
body.push(
159-
b.method(
160-
'set',
161-
definition.key,
162-
[b.id('_')],
163-
[b.throw_error(`Cannot update a derived property ('${name}')`)]
164-
)
165-
);
166-
}
167-
}
168-
169-
continue;
170-
}
171-
}
172-
173-
body.push(/** @type {MethodDefinition} **/ (visit(definition, child_state)));
174-
}
175-
176-
return { ...node, body };
177-
},
178-
PropertyDefinition(node, { state, next, visit }) {
179-
if (node.value != null && node.value.type === 'CallExpression') {
180-
const rune = get_rune(node.value, state.scope);
181-
182-
if (rune === '$state' || rune === '$state.frozen' || rune === '$derived') {
183-
return {
184-
...node,
185-
value:
186-
node.value.arguments.length === 0
187-
? null
188-
: /** @type {Expression} */ (visit(node.value.arguments[0]))
189-
};
190-
}
191-
if (rune === '$derived.by') {
192-
return {
193-
...node,
194-
value:
195-
node.value.arguments.length === 0
196-
? null
197-
: b.call(/** @type {Expression} */ (visit(node.value.arguments[0])))
198-
};
199-
}
200-
}
201-
next();
202-
},
73+
ClassBody: ClassBodyRunes,
74+
PropertyDefinition: PropertyDefinitionRunes,
20375
VariableDeclaration(node, { state, visit }) {
20476
const declarations = [];
20577

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/** @import { ClassBody, Expression, MethodDefinition, PropertyDefinition } from 'estree' */
2+
/** @import { Context } from '../../types' */
3+
/** @import { StateField } from '../../../client/types' */
4+
import * as b from '../../../../../utils/builders.js';
5+
import { get_rune } from '../../../../scope.js';
6+
7+
/**
8+
* @param {ClassBody} node
9+
* @param {Context} context
10+
*/
11+
export function ClassBodyRunes(node, context) {
12+
/** @type {Map<string, StateField>} */
13+
const public_derived = new Map();
14+
15+
/** @type {Map<string, StateField>} */
16+
const private_derived = new Map();
17+
18+
/** @type {string[]} */
19+
const private_ids = [];
20+
21+
for (const definition of node.body) {
22+
if (
23+
definition.type === 'PropertyDefinition' &&
24+
(definition.key.type === 'Identifier' || definition.key.type === 'PrivateIdentifier')
25+
) {
26+
const { type, name } = definition.key;
27+
28+
const is_private = type === 'PrivateIdentifier';
29+
if (is_private) private_ids.push(name);
30+
31+
if (definition.value?.type === 'CallExpression') {
32+
const rune = get_rune(definition.value, context.state.scope);
33+
if (rune === '$derived' || rune === '$derived.by') {
34+
/** @type {StateField} */
35+
const field = {
36+
kind: rune === '$derived.by' ? 'derived_call' : 'derived',
37+
// @ts-expect-error this is set in the next pass
38+
id: is_private ? definition.key : null
39+
};
40+
41+
if (is_private) {
42+
private_derived.set(name, field);
43+
} else {
44+
public_derived.set(name, field);
45+
}
46+
}
47+
}
48+
}
49+
}
50+
51+
// each `foo = $derived()` needs a backing `#foo` field
52+
for (const [name, field] of public_derived) {
53+
let deconflicted = name;
54+
while (private_ids.includes(deconflicted)) {
55+
deconflicted = '_' + deconflicted;
56+
}
57+
58+
private_ids.push(deconflicted);
59+
field.id = b.private_id(deconflicted);
60+
}
61+
62+
/** @type {Array<MethodDefinition | PropertyDefinition>} */
63+
const body = [];
64+
65+
const child_state = { ...context.state, private_derived };
66+
67+
// Replace parts of the class body
68+
for (const definition of node.body) {
69+
if (
70+
definition.type === 'PropertyDefinition' &&
71+
(definition.key.type === 'Identifier' || definition.key.type === 'PrivateIdentifier')
72+
) {
73+
const name = definition.key.name;
74+
75+
const is_private = definition.key.type === 'PrivateIdentifier';
76+
const field = (is_private ? private_derived : public_derived).get(name);
77+
78+
if (definition.value?.type === 'CallExpression' && field !== undefined) {
79+
const init = /** @type {Expression} **/ (
80+
context.visit(definition.value.arguments[0], child_state)
81+
);
82+
const value =
83+
field.kind === 'derived_call' ? b.call('$.once', init) : b.call('$.once', b.thunk(init));
84+
85+
if (is_private) {
86+
body.push(b.prop_def(field.id, value));
87+
} else {
88+
// #foo;
89+
const member = b.member(b.this, field.id);
90+
body.push(b.prop_def(field.id, value));
91+
92+
// get foo() { return this.#foo; }
93+
body.push(b.method('get', definition.key, [], [b.return(b.call(member))]));
94+
95+
if (
96+
(field.kind === 'derived' || field.kind === 'derived_call') &&
97+
context.state.options.dev
98+
) {
99+
body.push(
100+
b.method(
101+
'set',
102+
definition.key,
103+
[b.id('_')],
104+
[b.throw_error(`Cannot update a derived property ('${name}')`)]
105+
)
106+
);
107+
}
108+
}
109+
110+
continue;
111+
}
112+
}
113+
114+
body.push(/** @type {MethodDefinition} **/ (context.visit(definition, child_state)));
115+
}
116+
117+
return { ...node, body };
118+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/** @import { Expression, PropertyDefinition } from 'estree' */
2+
/** @import { Context } from '../../types' */
3+
import * as b from '../../../../../utils/builders.js';
4+
import { get_rune } from '../../../../scope.js';
5+
6+
/**
7+
* @param {PropertyDefinition} node
8+
* @param {Context} context
9+
*/
10+
export function PropertyDefinitionRunes(node, context) {
11+
if (node.value != null && node.value.type === 'CallExpression') {
12+
const rune = get_rune(node.value, context.state.scope);
13+
14+
if (rune === '$state' || rune === '$state.frozen' || rune === '$derived') {
15+
return {
16+
...node,
17+
value:
18+
node.value.arguments.length === 0
19+
? null
20+
: /** @type {Expression} */ (context.visit(node.value.arguments[0]))
21+
};
22+
}
23+
24+
if (rune === '$derived.by') {
25+
return {
26+
...node,
27+
value:
28+
node.value.arguments.length === 0
29+
? null
30+
: b.call(/** @type {Expression} */ (context.visit(node.value.arguments[0])))
31+
};
32+
}
33+
}
34+
35+
context.next();
36+
}

0 commit comments

Comments
 (0)