separate sync from async expressions

aaa
Rich Harris 8 months ago
parent 6ea6e2d724
commit 5ae974f47d

@ -175,6 +175,7 @@ export function client_component(analysis, options) {
init: /** @type {any} */ (null), init: /** @type {any} */ (null),
update: /** @type {any} */ (null), update: /** @type {any} */ (null),
expressions: /** @type {any} */ (null), expressions: /** @type {any} */ (null),
async_expressions: /** @type {any} */ (null),
after_update: /** @type {any} */ (null), after_update: /** @type {any} */ (null),
template: /** @type {any} */ (null), template: /** @type {any} */ (null),
locations: /** @type {any} */ (null) locations: /** @type {any} */ (null)

@ -53,7 +53,9 @@ export interface ComponentClientTransformState extends ClientTransformState {
/** Stuff that happens after the render effect (control blocks, dynamic elements, bindings, actions, etc) */ /** Stuff that happens after the render effect (control blocks, dynamic elements, bindings, actions, etc) */
readonly after_update: Statement[]; readonly after_update: Statement[];
/** Expressions used inside the render effect */ /** Expressions used inside the render effect */
readonly expressions: Array<{ id: Identifier; expression: Expression; is_async: boolean }>; readonly expressions: Array<{ id: Identifier; expression: Expression }>;
/** Expressions used inside the render effect */
readonly async_expressions: Array<{ id: Identifier; expression: Expression }>;
/** The HTML template string */ /** The HTML template string */
readonly template: Array<string | Expression>; readonly template: Array<string | Expression>;
readonly locations: SourceLocation[]; readonly locations: SourceLocation[];
@ -113,3 +115,8 @@ export type ComponentVisitors = import('zimmerframe').Visitors<
AST.SvelteNode, AST.SvelteNode,
ComponentClientTransformState ComponentClientTransformState
>; >;
export interface MemoizedExpression {
id: Identifier;
expression: Expression;
}

@ -64,6 +64,7 @@ export function Fragment(node, context) {
init: [], init: [],
update: [], update: [],
expressions: [], expressions: [],
async_expressions: [],
after_update: [], after_update: [],
template: [], template: [],
locations: [], locations: [],

@ -543,7 +543,7 @@ function build_element_attribute_update_assignment(
let { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) => let { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) =>
metadata.has_call || metadata.is_async metadata.has_call || metadata.is_async
? get_expression_id(state, value, metadata.is_async) ? get_expression_id(metadata.is_async ? state.async_expressions : state.expressions, value)
: value : value
); );
@ -673,7 +673,7 @@ function build_element_special_value_attribute(element, node_id, attribute, cont
const state = context.state; const state = context.state;
const { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) => const { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) =>
metadata.has_call || metadata.is_async metadata.has_call || metadata.is_async
? get_expression_id(state, value, metadata.is_async) ? get_expression_id(metadata.is_async ? state.async_expressions : state.expressions, value)
: value : value
); );

@ -48,6 +48,7 @@ export function SvelteElement(node, context) {
init: [], init: [],
update: [], update: [],
expressions: [], expressions: [],
async_expressions: [],
after_update: [] after_update: []
} }
}; };

@ -40,7 +40,10 @@ export function build_set_attributes(
context, context,
(value, metadata) => (value, metadata) =>
metadata.has_call || metadata.is_async metadata.has_call || metadata.is_async
? get_expression_id(context.state, value, metadata.is_async) ? get_expression_id(
metadata.is_async ? context.state.async_expressions : context.state.expressions,
value
)
: value : value
); );
@ -64,7 +67,12 @@ export function build_set_attributes(
let value = /** @type {Expression} */ (context.visit(attribute)); let value = /** @type {Expression} */ (context.visit(attribute));
if (attribute.metadata.expression.has_call || attribute.metadata.expression.is_async) { if (attribute.metadata.expression.has_call || attribute.metadata.expression.is_async) {
value = get_expression_id(context.state, value, attribute.metadata.expression.is_async); value = get_expression_id(
attribute.metadata.expression.is_async
? context.state.async_expressions
: context.state.expressions,
value
);
} }
values.push(b.spread(value)); values.push(b.spread(value));
@ -117,7 +125,10 @@ export function build_style_directives(
? build_getter({ name: directive.name, type: 'Identifier' }, context.state) ? build_getter({ name: directive.name, type: 'Identifier' }, context.state)
: build_attribute_value(directive.value, context, (value, metadata) => : build_attribute_value(directive.value, context, (value, metadata) =>
metadata.has_call || metadata.is_async metadata.has_call || metadata.is_async
? get_expression_id(context.state, value, metadata.is_async) ? get_expression_id(
metadata.is_async ? context.state.async_expressions : context.state.expressions,
value
)
: value : value
).value; ).value;
@ -159,7 +170,7 @@ export function build_class_directives(
let value = /** @type {Expression} */ (context.visit(directive.expression)); let value = /** @type {Expression} */ (context.visit(directive.expression));
if (has_call || is_async) { if (has_call || is_async) {
value = get_expression_id(state, value, is_async); value = get_expression_id(is_async ? state.async_expressions : state.expressions, value);
} }
const update = b.stmt(b.call('$.toggle_class', element_id, b.literal(directive.name), value)); const update = b.stmt(b.call('$.toggle_class', element_id, b.literal(directive.name), value));

@ -1,6 +1,6 @@
/** @import { Expression, ExpressionStatement, Identifier, MemberExpression, SequenceExpression, Statement, Super } from 'estree' */ /** @import { Expression, ExpressionStatement, Identifier, MemberExpression, SequenceExpression, Super } from 'estree' */
/** @import { AST, ExpressionMetadata } from '#compiler' */ /** @import { AST, ExpressionMetadata } from '#compiler' */
/** @import { ComponentClientTransformState } from '../../types' */ /** @import { ComponentClientTransformState, MemoizedExpression } from '../../types' */
import { walk } from 'zimmerframe'; import { walk } from 'zimmerframe';
import { object } from '../../../../../utils/ast.js'; import { object } from '../../../../../utils/ast.js';
import * as b from '../../../../../utils/builders.js'; import * as b from '../../../../../utils/builders.js';
@ -22,19 +22,18 @@ export function memoize_expression(state, value) {
/** /**
* *
* @param {ComponentClientTransformState} state * @param {MemoizedExpression[]} expressions
* @param {Expression} expression * @param {Expression} expression
* @param {boolean} is_async
*/ */
export function get_expression_id(state, expression, is_async) { export function get_expression_id(expressions, expression) {
for (let i = 0; i < state.expressions.length; i += 1) { for (let i = 0; i < expressions.length; i += 1) {
if (compare_expressions(state.expressions[i].expression, expression)) { if (compare_expressions(expressions[i].expression, expression)) {
return state.expressions[i].id; return expressions[i].id;
} }
} }
const id = b.id(''); // filled in later const id = b.id('~'); // filled in later
state.expressions.push({ id, expression, is_async }); expressions.push({ id, expression });
return id; return id;
} }
@ -92,7 +91,7 @@ export function build_template_chunk(
state, state,
memoize = (value, metadata) => memoize = (value, metadata) =>
metadata.has_call || metadata.is_async metadata.has_call || metadata.is_async
? get_expression_id(state, value, metadata.is_async) ? get_expression_id(metadata.is_async ? state.async_expressions : state.expressions, value)
: value : value
) { ) {
/** @type {Expression[]} */ /** @type {Expression[]} */
@ -163,8 +162,8 @@ export function build_template_chunk(
* @param {ComponentClientTransformState} state * @param {ComponentClientTransformState} state
*/ */
export function build_render_statement(state) { export function build_render_statement(state) {
const sync = state.expressions.filter(({ is_async }) => !is_async); const sync = state.expressions;
const async = state.expressions.filter(({ is_async }) => is_async); const async = state.async_expressions;
const all = [...sync, ...async]; const all = [...sync, ...async];

Loading…
Cancel
Save