Skip to content

Commit 75ea6da

Browse files
authored
chore: modularize server code (#12596)
* start modularizing server code * more * more * more * more * alphabetize * start on JS visitors * more * more * more * more * less * more * alphabetize * lint * combine into single visitors folder * alphabetize
1 parent 6223a7e commit 75ea6da

35 files changed

+2306
-1927
lines changed

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

Lines changed: 96 additions & 1927 deletions
Large diffs are not rendered by default.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/** @import { AssignmentExpression } from 'estree' */
2+
/** @import { Context } from '../types.js' */
3+
import { serialize_set_binding } from './shared/utils.js';
4+
5+
/**
6+
* @param {AssignmentExpression} node
7+
* @param {Context} context
8+
*/
9+
export function AssignmentExpression(node, context) {
10+
return serialize_set_binding(node, context, context.next);
11+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/** @import { BlockStatement, Expression, Pattern } from 'estree' */
2+
/** @import { AwaitBlock } from '#compiler' */
3+
/** @import { ComponentContext } from '../types.js' */
4+
import * as b from '../../../../utils/builders.js';
5+
import { empty_comment } from './shared/utils.js';
6+
7+
/**
8+
* @param {AwaitBlock} node
9+
* @param {ComponentContext} context
10+
*/
11+
export function AwaitBlock(node, context) {
12+
context.state.template.push(
13+
empty_comment,
14+
b.stmt(
15+
b.call(
16+
'$.await',
17+
/** @type {Expression} */ (context.visit(node.expression)),
18+
b.thunk(
19+
node.pending ? /** @type {BlockStatement} */ (context.visit(node.pending)) : b.block([])
20+
),
21+
b.arrow(
22+
node.value ? [/** @type {Pattern} */ (context.visit(node.value))] : [],
23+
node.then ? /** @type {BlockStatement} */ (context.visit(node.then)) : b.block([])
24+
),
25+
b.arrow(
26+
node.error ? [/** @type {Pattern} */ (context.visit(node.error))] : [],
27+
node.catch ? /** @type {BlockStatement} */ (context.visit(node.catch)) : b.block([])
28+
)
29+
)
30+
),
31+
empty_comment
32+
);
33+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/** @import { CallExpression, Expression } from 'estree' */
2+
/** @import { Context } from '../types.js' */
3+
import { get_rune } from '../../../scope.js';
4+
import * as b from '../../../../utils/builders.js';
5+
import { transform_inspect_rune } from '../../utils.js';
6+
7+
/**
8+
* @param {CallExpression} node
9+
* @param {Context} context
10+
*/
11+
export function CallExpression(node, context) {
12+
const rune = get_rune(node, context.state.scope);
13+
14+
if (rune === '$host') {
15+
return b.id('undefined');
16+
}
17+
18+
if (rune === '$effect.tracking') {
19+
return b.literal(false);
20+
}
21+
22+
if (rune === '$effect.root') {
23+
// ignore $effect.root() calls, just return a noop which mimics the cleanup function
24+
return b.arrow([], b.block([]));
25+
}
26+
27+
if (rune === '$state.snapshot') {
28+
return b.call('$.snapshot', /** @type {Expression} */ (context.visit(node.arguments[0])));
29+
}
30+
31+
if (rune === '$state.is') {
32+
return b.call(
33+
'Object.is',
34+
/** @type {Expression} */ (context.visit(node.arguments[0])),
35+
/** @type {Expression} */ (context.visit(node.arguments[1]))
36+
);
37+
}
38+
39+
if (rune === '$inspect' || rune === '$inspect().with') {
40+
return transform_inspect_rune(node, context);
41+
}
42+
43+
context.next();
44+
}
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.js' */
3+
/** @import { StateField } from '../../client/types.js' */
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: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/** @import { Component } from '#compiler' */
2+
/** @import { ComponentContext } from '../types.js' */
3+
import * as b from '../../../../utils/builders.js';
4+
import { serialize_inline_component } from './shared/component.js';
5+
6+
/**
7+
* @param {Component} node
8+
* @param {ComponentContext} context
9+
*/
10+
export function Component(node, context) {
11+
serialize_inline_component(node, b.id(node.name), context);
12+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/** @import { Expression, Pattern } from 'estree' */
2+
/** @import { ConstTag } from '#compiler' */
3+
/** @import { ComponentContext } from '../types.js' */
4+
import * as b from '../../../../utils/builders.js';
5+
6+
/**
7+
* @param {ConstTag} node
8+
* @param {ComponentContext} context
9+
*/
10+
export function ConstTag(node, context) {
11+
const declaration = node.declaration.declarations[0];
12+
const id = /** @type {Pattern} */ (context.visit(declaration.id));
13+
const init = /** @type {Expression} */ (context.visit(declaration.init));
14+
15+
context.state.init.push(b.const(id, init));
16+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/** @import { Expression } from 'estree' */
2+
/** @import { DebugTag } from '#compiler' */
3+
/** @import { ComponentContext } from '../types.js' */
4+
import * as b from '../../../../utils/builders.js';
5+
6+
/**
7+
* @param {DebugTag} node
8+
* @param {ComponentContext} context
9+
*/
10+
export function DebugTag(node, context) {
11+
context.state.template.push(
12+
b.stmt(
13+
b.call(
14+
'console.log',
15+
b.object(
16+
node.identifiers.map((identifier) =>
17+
b.prop('init', identifier, /** @type {Expression} */ (context.visit(identifier)))
18+
)
19+
)
20+
)
21+
),
22+
b.debugger
23+
);
24+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/** @import { BlockStatement, Expression, Pattern, Statement } from 'estree' */
2+
/** @import { EachBlock } from '#compiler' */
3+
/** @import { ComponentContext } from '../types.js' */
4+
import { BLOCK_OPEN_ELSE } from '../../../../../internal/server/hydration.js';
5+
import * as b from '../../../../utils/builders.js';
6+
import { block_close, block_open } from './shared/utils.js';
7+
8+
/**
9+
* @param {EachBlock} node
10+
* @param {ComponentContext} context
11+
*/
12+
export function EachBlock(node, context) {
13+
const state = context.state;
14+
15+
const each_node_meta = node.metadata;
16+
const collection = /** @type {Expression} */ (context.visit(node.expression));
17+
const item = each_node_meta.item;
18+
const index =
19+
each_node_meta.contains_group_binding || !node.index ? each_node_meta.index : b.id(node.index);
20+
21+
const array_id = state.scope.root.unique('each_array');
22+
state.init.push(b.const(array_id, b.call('$.ensure_array_like', collection)));
23+
24+
/** @type {Statement[]} */
25+
const each = [b.const(item, b.member(array_id, index, true))];
26+
27+
if (node.context.type !== 'Identifier') {
28+
each.push(b.const(/** @type {Pattern} */ (node.context), item));
29+
}
30+
if (index.name !== node.index && node.index != null) {
31+
each.push(b.let(node.index, index));
32+
}
33+
34+
each.push(.../** @type {BlockStatement} */ (context.visit(node.body)).body);
35+
36+
const for_loop = b.for(
37+
b.let(index, b.literal(0)),
38+
b.binary('<', index, b.member(array_id, b.id('length'))),
39+
b.update('++', index, false),
40+
b.block(each)
41+
);
42+
43+
if (node.fallback) {
44+
const open = b.stmt(b.assignment('+=', b.id('$$payload.out'), block_open));
45+
46+
const fallback = /** @type {BlockStatement} */ (context.visit(node.fallback));
47+
48+
fallback.body.unshift(
49+
b.stmt(b.assignment('+=', b.id('$$payload.out'), b.literal(BLOCK_OPEN_ELSE)))
50+
);
51+
52+
state.template.push(
53+
b.if(
54+
b.binary('!==', b.member(array_id, b.id('length')), b.literal(0)),
55+
b.block([open, for_loop]),
56+
fallback
57+
),
58+
block_close
59+
);
60+
} else {
61+
state.template.push(block_open, for_loop, block_close);
62+
}
63+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/** @import { ExpressionStatement } from 'estree' */
2+
/** @import { Context } from '../types.js' */
3+
import * as b from '../../../../utils/builders.js';
4+
5+
/**
6+
* @param {ExpressionStatement} node
7+
* @param {Context} context
8+
*/
9+
export function ExpressionStatementRunes(node, context) {
10+
const expression = node.expression;
11+
12+
if (expression.type === 'CallExpression') {
13+
const callee = expression.callee;
14+
15+
if (callee.type === 'Identifier' && callee.name === '$effect') {
16+
return b.empty;
17+
}
18+
19+
if (
20+
callee.type === 'MemberExpression' &&
21+
callee.object.type === 'Identifier' &&
22+
callee.object.name === '$effect'
23+
) {
24+
return b.empty;
25+
}
26+
}
27+
28+
context.next();
29+
}

0 commit comments

Comments
 (0)