Skip to content

Commit

Permalink
chore: modularize server code (#12596)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Rich-Harris committed Jul 25, 2024
1 parent 6223a7e commit 75ea6da
Show file tree
Hide file tree
Showing 35 changed files with 2,306 additions and 1,927 deletions.
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

0 comments on commit 75ea6da

Please sign in to comment.