From 795b86ef9e164f84ac4b3a04ba4e611f66b07656 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Sep 2025 19:40:34 -0400 Subject: [PATCH 1/3] WIP fix async attributes --- .../server/visitors/RegularElement.js | 45 +++-- .../server/visitors/SvelteBoundary.js | 8 +- .../server/visitors/SvelteElement.js | 7 +- .../server/visitors/shared/element.js | 154 ++++++++++++------ .../server/visitors/shared/utils.js | 8 +- packages/svelte/src/internal/server/index.js | 4 +- .../async-no-pending-attributes/Child.svelte | 5 + .../async-no-pending-attributes/_config.js | 24 +++ .../async-no-pending-attributes/main.svelte | 12 ++ 9 files changed, 186 insertions(+), 81 deletions(-) create mode 100644 packages/svelte/tests/runtime-runes/samples/async-no-pending-attributes/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/async-no-pending-attributes/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-no-pending-attributes/main.svelte diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/RegularElement.js index 60ec158b53..3ae9e9ce88 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/RegularElement.js @@ -12,7 +12,8 @@ import { process_children, build_template, build_attribute_value, - call_child_renderer + call_child_renderer, + PromiseOptimiser } from './shared/utils.js'; /** @@ -32,8 +33,10 @@ export function RegularElement(node, context) { const node_is_void = is_void(node.name); + const optimiser = new PromiseOptimiser(); + context.state.template.push(b.literal(`<${node.name}`)); - const body = build_element_attributes(node, { ...context, state }); + const body = build_element_attributes(node, { ...context, state }, optimiser.transform); context.state.template.push(b.literal(node_is_void ? '/>' : '>')); // add `/>` for XHTML compliance if ((node.name === 'script' || node.name === 'style') && node.fragment.nodes.length === 1) { @@ -93,26 +96,26 @@ export function RegularElement(node, context) { select_with_value = true; select_with_value_async ||= spread.metadata.expression.has_await; + const { object, has_await } = build_spread_object( + node, + node.attributes.filter( + (attribute) => + attribute.type === 'Attribute' || + attribute.type === 'BindDirective' || + attribute.type === 'SpreadAttribute' + ), + context, + optimiser.transform + ); + + // TODO use has_await + state.template.push( b.stmt( b.assignment( '=', b.id('$$renderer.local.select_value'), - b.member( - build_spread_object( - node, - node.attributes.filter( - (attribute) => - attribute.type === 'Attribute' || - attribute.type === 'BindDirective' || - attribute.type === 'SpreadAttribute' - ), - context - ), - 'value', - false, - true - ) + b.member(object, 'value', false, true) ) ) ); @@ -128,7 +131,13 @@ export function RegularElement(node, context) { const left = b.id('$$renderer.local.select_value'); if (value.type === 'Attribute') { state.template.push( - b.stmt(b.assignment('=', left, build_attribute_value(value.value, context))) + b.stmt( + b.assignment( + '=', + left, + build_attribute_value(value.value, context, false, false, optimiser.transform) + ) + ) ); } else if (value.type === 'BindDirective') { state.template.push( diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js index 2e116f57d8..582d78249f 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js @@ -23,7 +23,13 @@ export function SvelteBoundary(node, context) { if (pending_attribute || pending_snippet) { const pending = pending_attribute ? b.call( - build_attribute_value(pending_attribute.value, context, false, true), + build_attribute_value( + pending_attribute.value, + context, + false, + true, + (expression) => expression + ), b.id('$$renderer') ) : /** @type {BlockStatement} */ (context.visit(pending_snippet.body)); diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteElement.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteElement.js index 6ca0997bb6..cc57a94afe 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteElement.js @@ -6,7 +6,7 @@ import { dev, locator } from '../../../../state.js'; import * as b from '#compiler/builders'; import { determine_namespace_for_children } from '../../utils.js'; import { build_element_attributes } from './shared/element.js'; -import { build_template } from './shared/utils.js'; +import { build_template, PromiseOptimiser } from './shared/utils.js'; /** * @param {AST.SvelteElement} node @@ -37,7 +37,10 @@ export function SvelteElement(node, context) { init: [] }; - build_element_attributes(node, { ...context, state }); + // TODO use this + const optimiser = new PromiseOptimiser(); + + build_element_attributes(node, { ...context, state }, optimiser.transform); if (dev) { const location = /** @type {Location} */ (locator(node.start)); diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js index fbd3a22b1c..ae71faf60b 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js @@ -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} 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} */ 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,34 @@ 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, has_await } = build_spread_object( + node, + node.attributes.filter( + (attribute) => + attribute.type === 'Attribute' || + attribute.type === 'BindDirective' || + attribute.type === 'SpreadAttribute' + ), + context, + transform + ); + + // TODO use has_await? + 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 +251,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 +289,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 +301,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 +345,25 @@ function get_attribute_name(element, attribute) { * @param {AST.RegularElement | AST.SvelteElement} element * @param {Array} 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) { + let has_await = false; + + 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 +371,17 @@ 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); } + has_await ||= attribute.metadata.expression.has_await; + return b.spread(/** @type {Expression} */ (context.visit(attribute))); }) ); + + return { object, has_await }; } /** @@ -361,39 +391,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 +445,23 @@ function build_element_spread_attributes( flags |= ELEMENT_IS_INPUT; } - const object = build_spread_object(element, attributes, context); + const spread = 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 = [spread.object, css_hash, classes, styles, flags ? b.literal(flags) : undefined]; + + let call = b.call('$.attributes', ...args); - const args = [object, css_hash, classes, styles, flags ? b.literal(flags) : undefined]; - context.state.template.push(b.call('$.spread_attributes', ...args)); + if (spread.has_await) { + call = b.call('$$renderer.push', b.thunk(call, true)); + context.state.template.push(b.stmt(call)); + } else { + context.state.template.push(call); + } } /** @@ -423,8 +470,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 +482,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 +508,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 +523,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] !== '-') { diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js index 5e2b34496f..e7a31b5f07 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js @@ -175,13 +175,7 @@ export function build_template(template) { * @param {(expression: Expression, metadata: ExpressionMetadata) => Expression} transform * @returns {Expression} */ -export function build_attribute_value( - value, - context, - trim_whitespace = false, - is_component = false, - transform = (expression) => expression -) { +export function build_attribute_value(value, context, trim_whitespace, is_component, transform) { if (value === true) { return b.true; } diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index 1e2d75d6dc..436be6c021 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -108,13 +108,13 @@ export function css_props(renderer, is_html, props, component, dynamic = false) /** * @param {Record} attrs - * @param {string | null} css_hash + * @param {string} [css_hash] * @param {Record} [classes] * @param {Record} [styles] * @param {number} [flags] * @returns {string} */ -export function spread_attributes(attrs, css_hash, classes, styles, flags = 0) { +export function attributes(attrs, css_hash, classes, styles, flags = 0) { if (styles) { attrs.style = to_style(attrs.style, styles); } diff --git a/packages/svelte/tests/runtime-runes/samples/async-no-pending-attributes/Child.svelte b/packages/svelte/tests/runtime-runes/samples/async-no-pending-attributes/Child.svelte new file mode 100644 index 0000000000..ab87364cb7 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-no-pending-attributes/Child.svelte @@ -0,0 +1,5 @@ + + +

{thing}

diff --git a/packages/svelte/tests/runtime-runes/samples/async-no-pending-attributes/_config.js b/packages/svelte/tests/runtime-runes/samples/async-no-pending-attributes/_config.js new file mode 100644 index 0000000000..8975978e17 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-no-pending-attributes/_config.js @@ -0,0 +1,24 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + skip_mode: ['async-server'], + + async test({ assert, target }) { + await tick(); + + assert.htmlEqual( + target.innerHTML, + ` +

cool

+

beans

+ +

awesome

+

sauce

+ +

neato

+

potato

+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-no-pending-attributes/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-no-pending-attributes/main.svelte new file mode 100644 index 0000000000..b0ec462bb7 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-no-pending-attributes/main.svelte @@ -0,0 +1,12 @@ + + +

cool

+ + +

awesome

+ + +

neato

+ From 3744965ac461f34099bbde3dac737eafd8fc1379 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Sep 2025 20:26:02 -0400 Subject: [PATCH 2/3] fix --- .../phases/3-transform/client/visitors/shared/component.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js index 7feeebdbbc..5c8ce897f4 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js @@ -130,7 +130,7 @@ export function build_component(node, component_name, context) { } else if (attribute.type === 'SpreadAttribute') { const expression = /** @type {Expression} */ (context.visit(attribute)); - if (attribute.metadata.expression.has_state) { + if (attribute.metadata.expression.has_state || attribute.metadata.expression.has_await) { props_and_spreads.push( b.thunk( attribute.metadata.expression.has_await || attribute.metadata.expression.has_call From f911483b9e5332cd7040d9f9f844eba75ccdf8f3 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Sep 2025 21:26:33 -0400 Subject: [PATCH 3/3] fix --- .../server/visitors/RegularElement.js | 39 +++++++++++++++---- .../server/visitors/shared/element.js | 28 ++++++------- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/RegularElement.js index 3ae9e9ce88..9440719abc 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/RegularElement.js @@ -28,23 +28,38 @@ export function RegularElement(node, context) { ...context.state, namespace, preserve_whitespace: - context.state.preserve_whitespace || node.name === 'pre' || node.name === 'textarea' + context.state.preserve_whitespace || node.name === 'pre' || node.name === 'textarea', + init: [], + template: [] }; const node_is_void = is_void(node.name); const optimiser = new PromiseOptimiser(); - context.state.template.push(b.literal(`<${node.name}`)); + state.template.push(b.literal(`<${node.name}`)); const body = build_element_attributes(node, { ...context, state }, optimiser.transform); - context.state.template.push(b.literal(node_is_void ? '/>' : '>')); // add `/>` for XHTML compliance + state.template.push(b.literal(node_is_void ? '/>' : '>')); // add `/>` for XHTML compliance if ((node.name === 'script' || node.name === 'style') && node.fragment.nodes.length === 1) { - context.state.template.push( + state.template.push( b.literal(/** @type {AST.Text} */ (node.fragment.nodes[0]).data), b.literal(``) ); + // TODO this is a real edge case, would be good to DRY this out + if (optimiser.expressions.length > 0) { + context.state.template.push( + call_child_renderer( + b.block([optimiser.apply(), ...state.init, ...build_template(state.template)]), + true + ) + ); + } else { + context.state.init.push(...state.init); + context.state.template.push(...state.template); + } + return; } @@ -96,7 +111,7 @@ export function RegularElement(node, context) { select_with_value = true; select_with_value_async ||= spread.metadata.expression.has_await; - const { object, has_await } = build_spread_object( + const object = build_spread_object( node, node.attributes.filter( (attribute) => @@ -108,8 +123,6 @@ export function RegularElement(node, context) { optimiser.transform ); - // TODO use has_await - state.template.push( b.stmt( b.assignment( @@ -236,4 +249,16 @@ export function RegularElement(node, context) { if (dev) { state.template.push(b.stmt(b.call('$.pop_element'))); } + + if (optimiser.expressions.length > 0) { + context.state.template.push( + call_child_renderer( + b.block([optimiser.apply(), ...state.init, ...build_template(state.template)]), + true + ) + ); + } else { + context.state.init.push(...state.init); + context.state.template.push(...state.template); + } } diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js index ae71faf60b..fd651e113c 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js @@ -219,7 +219,7 @@ export function build_element_attributes(node, context, transform) { if (node.name === 'option') { // TODO this is all wrong, it inlines the spread twice - const { object, has_await } = build_spread_object( + const object = build_spread_object( node, node.attributes.filter( (attribute) => @@ -231,8 +231,6 @@ export function build_element_attributes(node, context, transform) { transform ); - // TODO use has_await? - context.state.template.push( b.call('$.maybe_selected', b.id('$$renderer'), b.member(object, 'value', false, true)) ); @@ -348,8 +346,6 @@ function get_attribute_name(element, attribute) { * @param {(expression: Expression, metadata: ExpressionMetadata) => Expression} transform */ export function build_spread_object(element, attributes, context, transform) { - let has_await = false; - const object = b.object( attributes.map((attribute) => { if (attribute.type === 'Attribute') { @@ -375,13 +371,16 @@ export function build_spread_object(element, attributes, context, transform) { return b.prop('init', b.key(name), value); } - has_await ||= attribute.metadata.expression.has_await; - - return b.spread(/** @type {Expression} */ (context.visit(attribute))); + return b.spread( + transform( + /** @type {Expression} */ (context.visit(attribute)), + attribute.metadata.expression + ) + ); }) ); - return { object, has_await }; + return object; } /** @@ -445,23 +444,18 @@ function build_element_spread_attributes( flags |= ELEMENT_IS_INPUT; } - const spread = build_spread_object(element, attributes, context, transform); + 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) : undefined; - const args = [spread.object, css_hash, classes, styles, flags ? b.literal(flags) : undefined]; + const args = [object, css_hash, classes, styles, flags ? b.literal(flags) : undefined]; let call = b.call('$.attributes', ...args); - if (spread.has_await) { - call = b.call('$$renderer.push', b.thunk(call, true)); - context.state.template.push(b.stmt(call)); - } else { - context.state.template.push(call); - } + context.state.template.push(call); } /**