support for svelte:element

pull/14795/head
adiguba 2 years ago
parent d7c92d9e90
commit 313a5614c3

@ -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(
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
])
build_display_directive(node_id, display_directive, style_display, block, context)
);
} else {
context.state.init.push(block);
}
} else if (node.fragment.metadata.dynamic) {
context.state.init.push(...child_state.init, ...element_state.init);
context.state.update.push(...child_state.update);

@ -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)])
)

@ -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));
}

Loading…
Cancel
Save