From 313a5614c394b6953343bbb4a7dc95387281b2be Mon Sep 17 00:00:00 2001 From: adiguba Date: Sat, 21 Dec 2024 21:16:43 +0100 Subject: [PATCH] support for svelte:element --- .../client/visitors/RegularElement.js | 54 ++++--------------- .../client/visitors/SvelteElement.js | 27 +++++++++- .../client/visitors/shared/element.js | 37 ++++++++++++- 3 files changed, 73 insertions(+), 45 deletions(-) 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 9e2b7c44931f..58536d055390 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 @@ -1,4 +1,4 @@ -/** @import { Expression, ExpressionStatement, Identifier, MemberExpression, ObjectExpression, Statement } from 'estree' */ +/** @import { BlockStatement, Expression, ExpressionStatement, Identifier, MemberExpression, ObjectExpression, Statement } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { SourceLocation } from '#shared' */ /** @import { ComponentClientTransformState, ComponentContext } from '../types' */ @@ -22,7 +22,8 @@ import { build_attribute_value, build_class_directives, build_style_directives, - build_set_attributes + build_set_attributes, + build_display_directive } from './shared/element.js'; import { process_children } from './shared/fragment.js'; import { @@ -422,7 +423,8 @@ export function RegularElement(node, context) { } } - if (display_directive) { + if (display_directive || node.fragment.nodes.some((node) => node.type === 'SnippetBlock')) { + // Wrap children in `{...}` to avoid declaration conflicts const block = b.block([ ...child_state.init, ...element_state.init, @@ -430,47 +432,13 @@ export function RegularElement(node, context) { ...child_state.after_update, ...element_state.after_update ]); - - const visibility = b.thunk( - /** @type {Expression} */ (context.visit(display_directive.expression)) - ); - - /** @type {Expression | undefined} */ - let value = undefined; - - /** @type {Expression | undefined} */ - let important = undefined; - - if (style_display) { - value = - style_display.value === true - ? build_getter({ name: style_display.name, type: 'Identifier' }, context.state) - : build_attribute_value(style_display.value, context).value; - - if (style_display.metadata.expression.has_call) { - const id = b.id(state.scope.generate('style_directive')); - - state.init.push(b.const(id, create_derived(state, b.thunk(value)))); - value = b.call('$.get', id); - } - value = b.thunk(value); - important = style_display.modifiers.includes('important') ? b.true : undefined; + if (display_directive) { + context.state.init.push( + build_display_directive(node_id, display_directive, style_display, block, context) + ); + } else { + context.state.init.push(block); } - - context.state.init.push( - b.stmt(b.call('$.display', node_id, visibility, b.arrow([], block), value, important)) - ); - } else if (node.fragment.nodes.some((node) => node.type === 'SnippetBlock')) { - // Wrap children in `{...}` to avoid declaration conflicts - context.state.init.push( - b.block([ - ...child_state.init, - ...element_state.init, - child_state.update.length > 0 ? build_render_statement(child_state.update) : b.empty, - ...child_state.after_update, - ...element_state.after_update - ]) - ); } else if (node.fragment.metadata.dynamic) { context.state.init.push(...child_state.init, ...element_state.init); context.state.update.push(...child_state.update); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js index ba66fe29d691..5d87bf7edad0 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js @@ -12,6 +12,7 @@ import { determine_namespace_for_children } from '../../utils.js'; import { build_attribute_value, build_class_directives, + build_display_directive, build_set_attributes, build_style_directives } from './shared/element.js'; @@ -36,6 +37,12 @@ export function SvelteElement(node, context) { /** @type {AST.StyleDirective[]} */ const style_directives = []; + /** @type {AST.DisplayDirective | null} */ + let display_directive = null; + + /** @type {AST.StyleDirective | null} */ + let style_display = null; + /** @type {ExpressionStatement[]} */ const lets = []; @@ -73,11 +80,21 @@ export function SvelteElement(node, context) { } else if (attribute.type === 'OnDirective') { const handler = /** @type {Expression} */ (context.visit(attribute, inner_context.state)); inner_context.state.after_update.push(b.stmt(handler)); + } else if (attribute.type === 'DisplayDirective') { + display_directive = attribute; } else { context.visit(attribute, inner_context.state); } } + if (display_directive !== null) { + const idx = style_directives.findIndex((d) => d.name === 'display'); + if (idx > 0) { + style_display = style_directives[idx]; + style_directives.splice(idx, 1); + } + } + // Let bindings first, they can be used on attributes context.state.init.push(...lets); // create computeds in the outer context; the dynamic element is the single child of this slot @@ -140,6 +157,14 @@ export function SvelteElement(node, context) { const location = dev && locator(node.start); + let render_element = b.block(inner); + + if (display_directive) { + render_element = b.block([ + build_display_directive(element_id, display_directive, style_display, render_element, context) + ]); + } + context.state.init.push( b.stmt( b.call( @@ -147,7 +172,7 @@ export function SvelteElement(node, context) { context.state.node, get_tag, node.metadata.svg || node.metadata.mathml ? b.true : b.false, - inner.length > 0 && b.arrow([element_id, b.id('$$anchor')], b.block(inner)), + inner.length > 0 && b.arrow([element_id, b.id('$$anchor')], render_element), dynamic_namespace && b.thunk(build_attribute_value(dynamic_namespace, context).value), location && b.array([b.literal(location.line), b.literal(location.column)]) ) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js index 1b0737e31e18..e3feaf2ca04c 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js @@ -1,4 +1,4 @@ -/** @import { Expression, Identifier, ObjectExpression } from 'estree' */ +/** @import { BlockStatement, Expression, Identifier, ObjectExpression } from 'estree' */ /** @import { AST, Namespace } from '#compiler' */ /** @import { ComponentClientTransformState, ComponentContext } from '../../types' */ import { normalize_attribute } from '../../../../../../utils.js'; @@ -214,3 +214,38 @@ export function get_attribute_name(element, attribute) { return attribute.name; } + +/** + * @param {Identifier} node_id + * @param {AST.DisplayDirective} display + * @param {AST.StyleDirective | null} style + * @param {BlockStatement} block + * @param {ComponentContext} context + */ +export function build_display_directive(node_id, display, style, block, context) { + const visibility = b.thunk(/** @type {Expression} */ (context.visit(display.expression))); + + /** @type {Expression | undefined} */ + let value = undefined; + + /** @type {Expression | undefined} */ + let important = undefined; + + if (style) { + value = + style.value === true + ? build_getter({ name: style.name, type: 'Identifier' }, context.state) + : build_attribute_value(style.value, context).value; + + if (style.metadata.expression.has_call) { + const id = b.id(context.state.scope.generate('style_directive')); + + context.state.init.push(b.const(id, create_derived(context.state, b.thunk(value)))); + value = b.call('$.get', id); + } + value = b.thunk(value); + important = style.modifiers.includes('important') ? b.true : undefined; + } + + return b.stmt(b.call('$.display', node_id, visibility, b.arrow([], block), value, important)); +}