|
|
|
@ -1,5 +1,5 @@
|
|
|
|
|
/** @import { ArrayExpression, Expression, Literal, ObjectExpression } from 'estree' */
|
|
|
|
|
/** @import { AST } from '#compiler' */
|
|
|
|
|
/** @import { AST, ExpressionMetadata } from '#compiler' */
|
|
|
|
|
/** @import { ComponentContext, ComponentServerTransformState } from '../../types.js' */
|
|
|
|
|
import { is_event_attribute, is_text_attribute } from '../../../../../utils/ast.js';
|
|
|
|
|
import { binding_properties } from '../../../../bindings.js';
|
|
|
|
@ -30,8 +30,9 @@ const WHITESPACE_INSENSITIVE_ATTRIBUTES = ['class', 'style'];
|
|
|
|
|
* their output to be the child content instead. In this case, an object is returned.
|
|
|
|
|
* @param {AST.RegularElement | AST.SvelteElement} node
|
|
|
|
|
* @param {import('zimmerframe').Context<AST.SvelteNode, ComponentServerTransformState>} context
|
|
|
|
|
* @param {(expression: Expression, metadata: ExpressionMetadata) => Expression} transform
|
|
|
|
|
*/
|
|
|
|
|
export function build_element_attributes(node, context) {
|
|
|
|
|
export function build_element_attributes(node, context, transform) {
|
|
|
|
|
/** @type {Array<AST.Attribute | AST.SpreadAttribute>} */
|
|
|
|
|
const attributes = [];
|
|
|
|
|
|
|
|
|
@ -62,7 +63,11 @@ export function build_element_attributes(node, context) {
|
|
|
|
|
// also see related code in analysis phase
|
|
|
|
|
attribute.value[0].data = '\n' + attribute.value[0].data;
|
|
|
|
|
}
|
|
|
|
|
content = b.call('$.escape', build_attribute_value(attribute.value, context));
|
|
|
|
|
|
|
|
|
|
content = b.call(
|
|
|
|
|
'$.escape',
|
|
|
|
|
build_attribute_value(attribute.value, context, false, false, transform)
|
|
|
|
|
);
|
|
|
|
|
} else if (node.name !== 'select') {
|
|
|
|
|
// omit value attribute for select elements, it's irrelevant for the initially selected value and has no
|
|
|
|
|
// effect on the selected value after the user interacts with the select element (the value _property_ does, but not the attribute)
|
|
|
|
@ -150,12 +155,12 @@ export function build_element_attributes(node, context) {
|
|
|
|
|
expression: is_checkbox
|
|
|
|
|
? b.call(
|
|
|
|
|
b.member(attribute.expression, 'includes'),
|
|
|
|
|
build_attribute_value(value_attribute.value, context)
|
|
|
|
|
build_attribute_value(value_attribute.value, context, false, false, transform)
|
|
|
|
|
)
|
|
|
|
|
: b.binary(
|
|
|
|
|
'===',
|
|
|
|
|
attribute.expression,
|
|
|
|
|
build_attribute_value(value_attribute.value, context)
|
|
|
|
|
build_attribute_value(value_attribute.value, context, false, false, transform)
|
|
|
|
|
),
|
|
|
|
|
metadata: {
|
|
|
|
|
expression: create_expression_metadata()
|
|
|
|
@ -202,28 +207,32 @@ export function build_element_attributes(node, context) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (has_spread) {
|
|
|
|
|
build_element_spread_attributes(node, attributes, style_directives, class_directives, context);
|
|
|
|
|
build_element_spread_attributes(
|
|
|
|
|
node,
|
|
|
|
|
attributes,
|
|
|
|
|
style_directives,
|
|
|
|
|
class_directives,
|
|
|
|
|
context,
|
|
|
|
|
transform
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (node.name === 'option') {
|
|
|
|
|
// TODO this is all wrong, it inlines the spread twice
|
|
|
|
|
|
|
|
|
|
const object = build_spread_object(
|
|
|
|
|
node,
|
|
|
|
|
node.attributes.filter(
|
|
|
|
|
(attribute) =>
|
|
|
|
|
attribute.type === 'Attribute' ||
|
|
|
|
|
attribute.type === 'BindDirective' ||
|
|
|
|
|
attribute.type === 'SpreadAttribute'
|
|
|
|
|
),
|
|
|
|
|
context,
|
|
|
|
|
transform
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
context.state.template.push(
|
|
|
|
|
b.call(
|
|
|
|
|
'$.maybe_selected',
|
|
|
|
|
b.id('$$renderer'),
|
|
|
|
|
b.member(
|
|
|
|
|
build_spread_object(
|
|
|
|
|
node,
|
|
|
|
|
node.attributes.filter(
|
|
|
|
|
(attribute) =>
|
|
|
|
|
attribute.type === 'Attribute' ||
|
|
|
|
|
attribute.type === 'BindDirective' ||
|
|
|
|
|
attribute.type === 'SpreadAttribute'
|
|
|
|
|
),
|
|
|
|
|
context
|
|
|
|
|
),
|
|
|
|
|
'value',
|
|
|
|
|
false,
|
|
|
|
|
true
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
b.call('$.maybe_selected', b.id('$$renderer'), b.member(object, 'value', false, true))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
@ -240,7 +249,9 @@ export function build_element_attributes(node, context) {
|
|
|
|
|
build_attribute_value(
|
|
|
|
|
attribute.value,
|
|
|
|
|
context,
|
|
|
|
|
WHITESPACE_INSENSITIVE_ATTRIBUTES.includes(name)
|
|
|
|
|
WHITESPACE_INSENSITIVE_ATTRIBUTES.includes(name),
|
|
|
|
|
false,
|
|
|
|
|
transform
|
|
|
|
|
)
|
|
|
|
|
).value;
|
|
|
|
|
|
|
|
|
@ -276,7 +287,9 @@ export function build_element_attributes(node, context) {
|
|
|
|
|
const value = build_attribute_value(
|
|
|
|
|
attribute.value,
|
|
|
|
|
context,
|
|
|
|
|
WHITESPACE_INSENSITIVE_ATTRIBUTES.includes(name)
|
|
|
|
|
WHITESPACE_INSENSITIVE_ATTRIBUTES.includes(name),
|
|
|
|
|
false,
|
|
|
|
|
transform
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// pre-escape and inline literal attributes :
|
|
|
|
@ -286,9 +299,11 @@ export function build_element_attributes(node, context) {
|
|
|
|
|
}
|
|
|
|
|
context.state.template.push(b.literal(` ${name}="${escape_html(value.value, true)}"`));
|
|
|
|
|
} else if (name === 'class') {
|
|
|
|
|
context.state.template.push(build_attr_class(class_directives, value, context, css_hash));
|
|
|
|
|
context.state.template.push(
|
|
|
|
|
build_attr_class(class_directives, value, context, css_hash, transform)
|
|
|
|
|
);
|
|
|
|
|
} else if (name === 'style') {
|
|
|
|
|
context.state.template.push(build_attr_style(style_directives, value, context));
|
|
|
|
|
context.state.template.push(build_attr_style(style_directives, value, context, transform));
|
|
|
|
|
} else {
|
|
|
|
|
context.state.template.push(
|
|
|
|
|
b.call('$.attr', b.literal(name), value, is_boolean_attribute(name) && b.true)
|
|
|
|
@ -328,17 +343,23 @@ function get_attribute_name(element, attribute) {
|
|
|
|
|
* @param {AST.RegularElement | AST.SvelteElement} element
|
|
|
|
|
* @param {Array<AST.Attribute | AST.SpreadAttribute | AST.BindDirective>} attributes
|
|
|
|
|
* @param {ComponentContext} context
|
|
|
|
|
* @param {(expression: Expression, metadata: ExpressionMetadata) => Expression} transform
|
|
|
|
|
*/
|
|
|
|
|
export function build_spread_object(element, attributes, context) {
|
|
|
|
|
return b.object(
|
|
|
|
|
export function build_spread_object(element, attributes, context, transform) {
|
|
|
|
|
const object = b.object(
|
|
|
|
|
attributes.map((attribute) => {
|
|
|
|
|
if (attribute.type === 'Attribute') {
|
|
|
|
|
const name = get_attribute_name(element, attribute);
|
|
|
|
|
const value = build_attribute_value(
|
|
|
|
|
attribute.value,
|
|
|
|
|
context,
|
|
|
|
|
WHITESPACE_INSENSITIVE_ATTRIBUTES.includes(name)
|
|
|
|
|
WHITESPACE_INSENSITIVE_ATTRIBUTES.includes(name),
|
|
|
|
|
false,
|
|
|
|
|
transform
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// TODO check has_await
|
|
|
|
|
|
|
|
|
|
return b.prop('init', b.key(name), value);
|
|
|
|
|
} else if (attribute.type === 'BindDirective') {
|
|
|
|
|
const name = get_attribute_name(element, attribute);
|
|
|
|
@ -346,12 +367,20 @@ export function build_spread_object(element, attributes, context) {
|
|
|
|
|
attribute.expression.type === 'SequenceExpression'
|
|
|
|
|
? b.call(attribute.expression.expressions[0])
|
|
|
|
|
: /** @type {Expression} */ (context.visit(attribute.expression));
|
|
|
|
|
|
|
|
|
|
return b.prop('init', b.key(name), value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return b.spread(/** @type {Expression} */ (context.visit(attribute)));
|
|
|
|
|
return b.spread(
|
|
|
|
|
transform(
|
|
|
|
|
/** @type {Expression} */ (context.visit(attribute)),
|
|
|
|
|
attribute.metadata.expression
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return object;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -361,39 +390,48 @@ export function build_spread_object(element, attributes, context) {
|
|
|
|
|
* @param {AST.StyleDirective[]} style_directives
|
|
|
|
|
* @param {AST.ClassDirective[]} class_directives
|
|
|
|
|
* @param {ComponentContext} context
|
|
|
|
|
* @param {(expression: Expression, metadata: ExpressionMetadata) => Expression} transform
|
|
|
|
|
*/
|
|
|
|
|
function build_element_spread_attributes(
|
|
|
|
|
element,
|
|
|
|
|
attributes,
|
|
|
|
|
style_directives,
|
|
|
|
|
class_directives,
|
|
|
|
|
context
|
|
|
|
|
context,
|
|
|
|
|
transform
|
|
|
|
|
) {
|
|
|
|
|
let classes;
|
|
|
|
|
let styles;
|
|
|
|
|
let flags = 0;
|
|
|
|
|
|
|
|
|
|
let has_await = false;
|
|
|
|
|
|
|
|
|
|
if (class_directives.length) {
|
|
|
|
|
const properties = class_directives.map((directive) =>
|
|
|
|
|
b.init(
|
|
|
|
|
const properties = class_directives.map((directive) => {
|
|
|
|
|
has_await ||= directive.metadata.expression.has_await;
|
|
|
|
|
|
|
|
|
|
return b.init(
|
|
|
|
|
directive.name,
|
|
|
|
|
directive.expression.type === 'Identifier' && directive.expression.name === directive.name
|
|
|
|
|
? b.id(directive.name)
|
|
|
|
|
: /** @type {Expression} */ (context.visit(directive.expression))
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
classes = b.object(properties);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (style_directives.length > 0) {
|
|
|
|
|
const properties = style_directives.map((directive) =>
|
|
|
|
|
b.init(
|
|
|
|
|
const properties = style_directives.map((directive) => {
|
|
|
|
|
has_await ||= directive.metadata.expression.has_await;
|
|
|
|
|
|
|
|
|
|
return b.init(
|
|
|
|
|
directive.name,
|
|
|
|
|
directive.value === true
|
|
|
|
|
? b.id(directive.name)
|
|
|
|
|
: build_attribute_value(directive.value, context, true)
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
: build_attribute_value(directive.value, context, true, false, transform)
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
styles = b.object(properties);
|
|
|
|
|
}
|
|
|
|
@ -406,15 +444,18 @@ function build_element_spread_attributes(
|
|
|
|
|
flags |= ELEMENT_IS_INPUT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const object = build_spread_object(element, attributes, context);
|
|
|
|
|
const object = build_spread_object(element, attributes, context, transform);
|
|
|
|
|
|
|
|
|
|
const css_hash =
|
|
|
|
|
element.metadata.scoped && context.state.analysis.css.hash
|
|
|
|
|
? b.literal(context.state.analysis.css.hash)
|
|
|
|
|
: b.null;
|
|
|
|
|
: undefined;
|
|
|
|
|
|
|
|
|
|
const args = [object, css_hash, classes, styles, flags ? b.literal(flags) : undefined];
|
|
|
|
|
context.state.template.push(b.call('$.spread_attributes', ...args));
|
|
|
|
|
|
|
|
|
|
let call = b.call('$.attributes', ...args);
|
|
|
|
|
|
|
|
|
|
context.state.template.push(call);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -423,8 +464,9 @@ function build_element_spread_attributes(
|
|
|
|
|
* @param {Expression} expression
|
|
|
|
|
* @param {ComponentContext} context
|
|
|
|
|
* @param {string | null} hash
|
|
|
|
|
* @param {(expression: Expression, metadata: ExpressionMetadata) => Expression} transform
|
|
|
|
|
*/
|
|
|
|
|
function build_attr_class(class_directives, expression, context, hash) {
|
|
|
|
|
function build_attr_class(class_directives, expression, context, hash, transform) {
|
|
|
|
|
/** @type {ObjectExpression | undefined} */
|
|
|
|
|
let directives;
|
|
|
|
|
|
|
|
|
@ -434,7 +476,10 @@ function build_attr_class(class_directives, expression, context, hash) {
|
|
|
|
|
b.prop(
|
|
|
|
|
'init',
|
|
|
|
|
b.literal(directive.name),
|
|
|
|
|
/** @type {Expression} */ (context.visit(directive.expression, context.state))
|
|
|
|
|
transform(
|
|
|
|
|
/** @type {Expression} */ (context.visit(directive.expression, context.state)),
|
|
|
|
|
directive.metadata.expression
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
@ -457,9 +502,10 @@ function build_attr_class(class_directives, expression, context, hash) {
|
|
|
|
|
*
|
|
|
|
|
* @param {AST.StyleDirective[]} style_directives
|
|
|
|
|
* @param {Expression} expression
|
|
|
|
|
* @param {ComponentContext} context
|
|
|
|
|
* @param {ComponentContext} context,
|
|
|
|
|
* @param {(expression: Expression, metadata: ExpressionMetadata) => Expression} transform
|
|
|
|
|
*/
|
|
|
|
|
function build_attr_style(style_directives, expression, context) {
|
|
|
|
|
function build_attr_style(style_directives, expression, context, transform) {
|
|
|
|
|
/** @type {ArrayExpression | ObjectExpression | undefined} */
|
|
|
|
|
let directives;
|
|
|
|
|
|
|
|
|
@ -471,7 +517,7 @@ function build_attr_style(style_directives, expression, context) {
|
|
|
|
|
const expression =
|
|
|
|
|
directive.value === true
|
|
|
|
|
? b.id(directive.name)
|
|
|
|
|
: build_attribute_value(directive.value, context, true);
|
|
|
|
|
: build_attribute_value(directive.value, context, true, false, transform);
|
|
|
|
|
|
|
|
|
|
let name = directive.name;
|
|
|
|
|
if (name[0] !== '-' || name[1] !== '-') {
|
|
|
|
|