From dec07f00c16119271035f3b8908d099d70626612 Mon Sep 17 00:00:00 2001 From: paoloricciuti Date: Tue, 12 Nov 2024 17:14:03 +0100 Subject: [PATCH] feat: inline dom expression too --- .../2-analyze/visitors/ExpressionTag.js | 12 ---- .../phases/2-analyze/visitors/Identifier.js | 2 +- .../client/visitors/RegularElement.js | 37 ++++++++---- .../client/visitors/shared/fragment.js | 16 +++++- .../client/visitors/shared/utils.js | 12 +++- packages/svelte/src/compiler/phases/utils.js | 56 +++++++++++++++---- 6 files changed, 96 insertions(+), 39 deletions(-) diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js index 86a2f6a9e1..018b04eeea 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js @@ -16,17 +16,5 @@ export function ExpressionTag(node, context) { } } - const attribute_parent = context.path.find((parent) => parent.type === 'Attribute'); - /** - * if the expression tag is part of an attribute we want to check if it's inlinable before marking - * the subtree as dynamic. This is because if it's inlinable it will be inlined in the template - * directly making the whole thing actually static. - */ - if (!attribute_parent || !is_inlinable_expression(attribute_parent, context.state.scope)) { - // TODO ideally we wouldn't do this here, we'd just do it on encountering - // an `Identifier` within the tag. But we currently need to handle `{42}` etc - mark_subtree_dynamic(context.path); - } - context.next({ ...context.state, expression: node.metadata.expression }); } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js index c0509a5414..f0f9b1e1fb 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js @@ -28,7 +28,7 @@ export function Identifier(node, context) { * before marking the subtree as dynamic. This is because if it's inlinable it will be inlined in the template * directly making the whole thing actually static. */ - if (!attribute_parent || !is_inlinable_expression(attribute_parent, context.state.scope)) { + if (!attribute_parent || !is_inlinable_expression(attribute_parent.value, context.state.scope)) { mark_subtree_dynamic(context.path); } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index 649762994a..5cfc0a1496 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -31,6 +31,7 @@ import { build_template_literal, build_update, build_update_assignment, + escape_template_quasis, get_states_and_calls } from './shared/utils.js'; @@ -360,22 +361,30 @@ export function RegularElement(node, context) { get_states_and_calls(trimmed); if (states_and_calls && states_and_calls.states === 0) { - child_state.init.push( - b.stmt( - b.assignment( - '=', - b.member(context.state.node, 'textContent'), - build_template_literal(trimmed, context.visit, child_state).value - ) - ) - ); + let { value } = build_template_literal(trimmed, context.visit, child_state); + // if the expression is inlinable we just push it to the template + if (is_inlinable_expression(trimmed, context.state.scope)) { + // escaping every quasi if it's a template literal + if (value.type === 'TemplateLiteral') { + escape_template_quasis(value); + } + state.template.push(value); + } else { + // else we programmatically set the value + child_state.init.push( + b.stmt(b.assignment('=', b.member(context.state.node, 'textContent'), value)) + ); + } } else { /** @type {Expression} */ let arg = context.state.node; // If `hydrate_node` is set inside the element, we need to reset it - // after the element has been hydrated - let needs_reset = trimmed.some((node) => node.type !== 'Text'); + // after the element has been hydrated (we don't need to reset if it's been inlined) + let needs_reset = + trimmed.some((node) => node.type !== 'Text') && + (!trimmed.every((node) => node.type === 'Text' || node.type === 'ExpressionTag') || + !is_inlinable_expression(trimmed, context.state.scope)); // The same applies if it's a `