Skip to content

chore: modularize server code #12596

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Jul 25, 2024
2,023 changes: 96 additions & 1,927 deletions packages/svelte/src/compiler/phases/3-transform/server/transform-server.js

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/** @import { AssignmentExpression } from 'estree' */
/** @import { Context } from '../types.js' */
import { serialize_set_binding } from './shared/utils.js';

/**
* @param {AssignmentExpression} node
* @param {Context} context
*/
export function AssignmentExpression(node, context) {
return serialize_set_binding(node, context, context.next);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/** @import { BlockStatement, Expression, Pattern } from 'estree' */
/** @import { AwaitBlock } from '#compiler' */
/** @import { ComponentContext } from '../types.js' */
import * as b from '../../../../utils/builders.js';
import { empty_comment } from './shared/utils.js';

/**
* @param {AwaitBlock} node
* @param {ComponentContext} context
*/
export function AwaitBlock(node, context) {
context.state.template.push(
empty_comment,
b.stmt(
b.call(
'$.await',
/** @type {Expression} */ (context.visit(node.expression)),
b.thunk(
node.pending ? /** @type {BlockStatement} */ (context.visit(node.pending)) : b.block([])
),
b.arrow(
node.value ? [/** @type {Pattern} */ (context.visit(node.value))] : [],
node.then ? /** @type {BlockStatement} */ (context.visit(node.then)) : b.block([])
),
b.arrow(
node.error ? [/** @type {Pattern} */ (context.visit(node.error))] : [],
node.catch ? /** @type {BlockStatement} */ (context.visit(node.catch)) : b.block([])
)
)
),
empty_comment
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/** @import { CallExpression, Expression } from 'estree' */
/** @import { Context } from '../types.js' */
import { get_rune } from '../../../scope.js';
import * as b from '../../../../utils/builders.js';
import { transform_inspect_rune } from '../../utils.js';

/**
* @param {CallExpression} node
* @param {Context} context
*/
export function CallExpression(node, context) {
const rune = get_rune(node, context.state.scope);

if (rune === '$host') {
return b.id('undefined');
}

if (rune === '$effect.tracking') {
return b.literal(false);
}

if (rune === '$effect.root') {
// ignore $effect.root() calls, just return a noop which mimics the cleanup function
return b.arrow([], b.block([]));
}

if (rune === '$state.snapshot') {
return b.call('$.snapshot', /** @type {Expression} */ (context.visit(node.arguments[0])));
}

if (rune === '$state.is') {
return b.call(
'Object.is',
/** @type {Expression} */ (context.visit(node.arguments[0])),
/** @type {Expression} */ (context.visit(node.arguments[1]))
);
}

if (rune === '$inspect' || rune === '$inspect().with') {
return transform_inspect_rune(node, context);
}

context.next();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/** @import { ClassBody, Expression, MethodDefinition, PropertyDefinition } from 'estree' */
/** @import { Context } from '../types.js' */
/** @import { StateField } from '../../client/types.js' */
import * as b from '../../../../utils/builders.js';
import { get_rune } from '../../../scope.js';

/**
* @param {ClassBody} node
* @param {Context} context
*/
export function ClassBodyRunes(node, context) {
/** @type {Map<string, StateField>} */
const public_derived = new Map();

/** @type {Map<string, StateField>} */
const private_derived = new Map();

/** @type {string[]} */
const private_ids = [];

for (const definition of node.body) {
if (
definition.type === 'PropertyDefinition' &&
(definition.key.type === 'Identifier' || definition.key.type === 'PrivateIdentifier')
) {
const { type, name } = definition.key;

const is_private = type === 'PrivateIdentifier';
if (is_private) private_ids.push(name);

if (definition.value?.type === 'CallExpression') {
const rune = get_rune(definition.value, context.state.scope);
if (rune === '$derived' || rune === '$derived.by') {
/** @type {StateField} */
const field = {
kind: rune === '$derived.by' ? 'derived_call' : 'derived',
// @ts-expect-error this is set in the next pass
id: is_private ? definition.key : null
};

if (is_private) {
private_derived.set(name, field);
} else {
public_derived.set(name, field);
}
}
}
}
}

// each `foo = $derived()` needs a backing `#foo` field
for (const [name, field] of public_derived) {
let deconflicted = name;
while (private_ids.includes(deconflicted)) {
deconflicted = '_' + deconflicted;
}

private_ids.push(deconflicted);
field.id = b.private_id(deconflicted);
}

/** @type {Array<MethodDefinition | PropertyDefinition>} */
const body = [];

const child_state = { ...context.state, private_derived };

// Replace parts of the class body
for (const definition of node.body) {
if (
definition.type === 'PropertyDefinition' &&
(definition.key.type === 'Identifier' || definition.key.type === 'PrivateIdentifier')
) {
const name = definition.key.name;

const is_private = definition.key.type === 'PrivateIdentifier';
const field = (is_private ? private_derived : public_derived).get(name);

if (definition.value?.type === 'CallExpression' && field !== undefined) {
const init = /** @type {Expression} **/ (
context.visit(definition.value.arguments[0], child_state)
);
const value =
field.kind === 'derived_call' ? b.call('$.once', init) : b.call('$.once', b.thunk(init));

if (is_private) {
body.push(b.prop_def(field.id, value));
} else {
// #foo;
const member = b.member(b.this, field.id);
body.push(b.prop_def(field.id, value));

// get foo() { return this.#foo; }
body.push(b.method('get', definition.key, [], [b.return(b.call(member))]));

if (
(field.kind === 'derived' || field.kind === 'derived_call') &&
context.state.options.dev
) {
body.push(
b.method(
'set',
definition.key,
[b.id('_')],
[b.throw_error(`Cannot update a derived property ('${name}')`)]
)
);
}
}

continue;
}
}

body.push(/** @type {MethodDefinition} **/ (context.visit(definition, child_state)));
}

return { ...node, body };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/** @import { Component } from '#compiler' */
/** @import { ComponentContext } from '../types.js' */
import * as b from '../../../../utils/builders.js';
import { serialize_inline_component } from './shared/component.js';

/**
* @param {Component} node
* @param {ComponentContext} context
*/
export function Component(node, context) {
serialize_inline_component(node, b.id(node.name), context);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/** @import { Expression, Pattern } from 'estree' */
/** @import { ConstTag } from '#compiler' */
/** @import { ComponentContext } from '../types.js' */
import * as b from '../../../../utils/builders.js';

/**
* @param {ConstTag} node
* @param {ComponentContext} context
*/
export function ConstTag(node, context) {
const declaration = node.declaration.declarations[0];
const id = /** @type {Pattern} */ (context.visit(declaration.id));
const init = /** @type {Expression} */ (context.visit(declaration.init));

context.state.init.push(b.const(id, init));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/** @import { Expression } from 'estree' */
/** @import { DebugTag } from '#compiler' */
/** @import { ComponentContext } from '../types.js' */
import * as b from '../../../../utils/builders.js';

/**
* @param {DebugTag} node
* @param {ComponentContext} context
*/
export function DebugTag(node, context) {
context.state.template.push(
b.stmt(
b.call(
'console.log',
b.object(
node.identifiers.map((identifier) =>
b.prop('init', identifier, /** @type {Expression} */ (context.visit(identifier)))
)
)
)
),
b.debugger
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/** @import { BlockStatement, Expression, Pattern, Statement } from 'estree' */
/** @import { EachBlock } from '#compiler' */
/** @import { ComponentContext } from '../types.js' */
import { BLOCK_OPEN_ELSE } from '../../../../../internal/server/hydration.js';
import * as b from '../../../../utils/builders.js';
import { block_close, block_open } from './shared/utils.js';

/**
* @param {EachBlock} node
* @param {ComponentContext} context
*/
export function EachBlock(node, context) {
const state = context.state;

const each_node_meta = node.metadata;
const collection = /** @type {Expression} */ (context.visit(node.expression));
const item = each_node_meta.item;
const index =
each_node_meta.contains_group_binding || !node.index ? each_node_meta.index : b.id(node.index);

const array_id = state.scope.root.unique('each_array');
state.init.push(b.const(array_id, b.call('$.ensure_array_like', collection)));

/** @type {Statement[]} */
const each = [b.const(item, b.member(array_id, index, true))];

if (node.context.type !== 'Identifier') {
each.push(b.const(/** @type {Pattern} */ (node.context), item));
}
if (index.name !== node.index && node.index != null) {
each.push(b.let(node.index, index));
}

each.push(.../** @type {BlockStatement} */ (context.visit(node.body)).body);

const for_loop = b.for(
b.let(index, b.literal(0)),
b.binary('<', index, b.member(array_id, b.id('length'))),
b.update('++', index, false),
b.block(each)
);

if (node.fallback) {
const open = b.stmt(b.assignment('+=', b.id('$$payload.out'), block_open));

const fallback = /** @type {BlockStatement} */ (context.visit(node.fallback));

fallback.body.unshift(
b.stmt(b.assignment('+=', b.id('$$payload.out'), b.literal(BLOCK_OPEN_ELSE)))
);

state.template.push(
b.if(
b.binary('!==', b.member(array_id, b.id('length')), b.literal(0)),
b.block([open, for_loop]),
fallback
),
block_close
);
} else {
state.template.push(block_open, for_loop, block_close);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/** @import { ExpressionStatement } from 'estree' */
/** @import { Context } from '../types.js' */
import * as b from '../../../../utils/builders.js';

/**
* @param {ExpressionStatement} node
* @param {Context} context
*/
export function ExpressionStatementRunes(node, context) {
const expression = node.expression;

if (expression.type === 'CallExpression') {
const callee = expression.callee;

if (callee.type === 'Identifier' && callee.name === '$effect') {
return b.empty;
}

if (
callee.type === 'MemberExpression' &&
callee.object.type === 'Identifier' &&
callee.object.name === '$effect'
) {
return b.empty;
}
}

context.next();
}
Loading
Loading