From f752b1e2558c34608da1ce97ed9bb61c9f578604 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 10 Jun 2024 06:51:22 -0400 Subject: [PATCH] chore: simplify SSR (#11977) * chore: simplify SSR escaping * treat solo expressions the same as sequence expressions * reduce some indirection * more * tidy * tidy * remove unused types * more * this doesnt do anything * more * Anchor is unused * simplify * simplify * move special case handling * more * simplify * simplify * simplify * more * unnecessary * simplify * remove unused arg * more * more * unnecessary * more * more * dedupe --- .../3-transform/client/visitors/javascript.js | 12 - .../3-transform/client/visitors/template.js | 38 +- .../3-transform/server/transform-server.js | 765 +++++++----------- .../phases/3-transform/server/types.d.ts | 37 +- .../samples/escaped-expression/_config.js | 5 + .../samples/escaped-expression/main.svelte | 1 + 6 files changed, 309 insertions(+), 549 deletions(-) create mode 100644 packages/svelte/tests/runtime-legacy/samples/escaped-expression/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/escaped-expression/main.svelte diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript.js index 16921e786d..e8446de17d 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript.js @@ -3,18 +3,6 @@ import { function_visitor, serialize_hoistable_params } from '../utils.js'; /** @type {import('../types.js').ComponentVisitors} */ export const javascript_visitors = { - Program(node, { visit }) { - return /** @type {import('estree').Program} */ ({ - ...node, - body: node.body.map((node) => /** @type {import('estree').Node} */ (visit(node))) - }); - }, - BlockStatement(node, { visit }) { - return /** @type {import('estree').BlockStatement} */ ({ - ...node, - body: node.body.map((node) => /** @type {import('estree').Node} */ (visit(node))) - }); - }, FunctionExpression: function_visitor, ArrowFunctionExpression: function_visitor, FunctionDeclaration(node, context) { diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js index 72f51390bc..2060364389 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js @@ -1435,37 +1435,29 @@ function get_node_id(expression, state, name) { } /** - * @param {true | Array} attribute_value + * @param {true | Array} value * @param {import('../types').ComponentContext} context - * @returns {[boolean, import('estree').Expression]} + * @returns {[contains_call_expression: boolean, import('estree').Expression]} */ -function serialize_attribute_value(attribute_value, context) { - let contains_call_expression = false; - - if (attribute_value === true) { - return [contains_call_expression, b.literal(true)]; +function serialize_attribute_value(value, context) { + if (value === true) { + return [false, b.literal(true)]; } - if (attribute_value.length === 0) { - return [contains_call_expression, b.literal('')]; // is this even possible? - } + if (value.length === 1) { + const chunk = value[0]; - if (attribute_value.length === 1) { - const value = attribute_value[0]; - if (value.type === 'Text') { - return [contains_call_expression, b.literal(value.data)]; - } else { - if (value.type === 'ExpressionTag') { - contains_call_expression = value.metadata.contains_call_expression; - } - return [ - contains_call_expression, - /** @type {import('estree').Expression} */ (context.visit(value.expression)) - ]; + if (chunk.type === 'Text') { + return [false, b.literal(chunk.data)]; } + + return [ + chunk.metadata.contains_call_expression, + /** @type {import('estree').Expression} */ (context.visit(chunk.expression)) + ]; } - return serialize_template_literal(attribute_value, context.visit, context.state); + return serialize_template_literal(value, context.visit, context.state); } /** diff --git a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js index 0f4fefa671..2d172b09bc 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js @@ -5,6 +5,7 @@ import { extract_paths, is_event_attribute, is_expression_async, + is_text_attribute, unwrap_optional } from '../../../utils/ast.js'; import * as b from '../../../utils/builders.js'; @@ -27,51 +28,42 @@ import { regex_starts_with_newline, regex_whitespaces_strict } from '../../patte import { DOMBooleanAttributes, ELEMENT_IS_NAMESPACED, - ELEMENT_PRESERVE_ATTRIBUTE_CASE, - HYDRATION_ANCHOR, - HYDRATION_END, - HYDRATION_START + ELEMENT_PRESERVE_ATTRIBUTE_CASE } from '../../../../constants.js'; import { escape_html } from '../../../../escaping.js'; import { sanitize_template_string } from '../../../utils/sanitize_template_string.js'; -import { BLOCK_CLOSE, BLOCK_CLOSE_ELSE } from '../../../../internal/server/hydration.js'; +import { + BLOCK_ANCHOR, + BLOCK_CLOSE, + BLOCK_CLOSE_ELSE, + BLOCK_OPEN +} from '../../../../internal/server/hydration.js'; import { filename, locator } from '../../../state.js'; -export const block_open = t_string(``); -export const block_close = t_string(``); -export const block_anchor = t_string(``); - -/** - * @param {string} value - * @returns {import('./types').TemplateString} - */ -function t_string(value) { - return { type: 'string', value }; -} +export const block_open = string(BLOCK_OPEN); +export const block_close = string(BLOCK_CLOSE); +export const block_anchor = string(BLOCK_ANCHOR); -/** - * @param {import('estree').Expression} value - * @param {boolean} [needs_escaping] - * @returns {import('./types').TemplateExpression} - */ -function t_expression(value, needs_escaping = false) { - return { type: 'expression', value, needs_escaping }; +/** @param {string} value */ +function string(value) { + return b.literal(sanitize_template_string(value)); } /** - * @param {import('estree').Statement} value - * @returns {import('./types').TemplateStatement} + * @param {import('estree').Node} node + * @returns {node is import('estree').Statement} */ -function t_statement(value) { - return { type: 'statement', value }; +function is_statement(node) { + return node.type.endsWith('Statement') || node.type.endsWith('Declaration'); } /** - * @param {import('./types').Template[]} template + * @param {Array} template * @param {import('estree').Identifier} out + * @param {import('estree').AssignmentOperator} operator * @returns {import('estree').Statement[]} */ -function serialize_template(template, out = b.id('out')) { +function serialize_template(template, out = b.id('$$payload.out'), operator = '+=') { /** @type {import('estree').TemplateElement[]} */ let quasis = []; @@ -81,44 +73,40 @@ function serialize_template(template, out = b.id('out')) { /** @type {import('estree').Statement[]} */ const statements = []; - const flush_payload = () => { - statements.push( - b.stmt(b.assignment('+=', b.member(b.id('$$payload'), out), b.template(quasis, expressions))) - ); + const flush = () => { + statements.push(b.stmt(b.assignment(operator, out, b.template(quasis, expressions)))); quasis = []; expressions = []; }; for (let i = 0; i < template.length; i++) { - const template_item = template[i]; - if (template_item.type === 'statement') { + const node = template[i]; + + if (is_statement(node)) { if (quasis.length !== 0) { - flush_payload(); + flush(); } - statements.push(template_item.value); + + statements.push(node); } else { let last = quasis.at(-1); if (!last) quasis.push((last = b.quasi('', false))); - if (template_item.type === 'string') { - last.value.raw += sanitize_template_string(template_item.value); - } else if (template_item.type === 'expression') { - const value = template_item.value; - if (value.type === 'TemplateLiteral') { - const raw = value.quasis[0].value.raw; - last.value.raw += template_item.needs_escaping ? sanitize_template_string(raw) : raw; - quasis.push(...value.quasis.slice(1)); - expressions.push(...value.expressions); - continue; - } - expressions.push(value); - quasis.push(b.quasi('', i + 1 === template.length || template[i + 1].type === 'statement')); + if (node.type === 'Literal') { + last.value.raw += node.value; + } else if (node.type === 'TemplateLiteral') { + last.value.raw += node.quasis[0].value.raw; + quasis.push(...node.quasis.slice(1)); + expressions.push(...node.expressions); + } else { + expressions.push(node); + quasis.push(b.quasi('', i + 1 === template.length || is_statement(template[i + 1]))); } } } if (quasis.length !== 0) { - flush_payload(); + flush(); } return statements; @@ -127,116 +115,61 @@ function serialize_template(template, out = b.id('out')) { /** * Processes an array of template nodes, joining sibling text/expression nodes and * recursing into child nodes. - * @param {Array} nodes - * @param {import('#compiler').SvelteNode} parent + * @param {Array} nodes * @param {import('./types').ComponentContext} context */ -function process_children(nodes, parent, { visit, state }) { - /** @typedef {Array} Sequence */ - - /** @type {Sequence} */ +function process_children(nodes, { visit, state }) { + /** @type {Array} */ let sequence = []; - let did_flush = false; - - /** - * @param {Sequence} sequence - * @param {boolean} final - */ - function flush_sequence(sequence, final) { - const is_single_flush = !did_flush && final; - did_flush = true; - - if (sequence.length === 1) { - const node = sequence[0]; - - if (node.type === 'Text') { - if ( - is_single_flush && - parent.type === 'RegularElement' && - (parent.name === 'script' || parent.name === 'style') - ) { - state.template.push(t_string(node.data)); - } else { - state.template.push(t_string(escape_html(node.data))); - } - return; - } - if (node.type === 'Comment') { - state.template.push(t_string(``)); - return; - } - - if (node.type === 'Anchor') { - state.template.push(t_expression(node.id)); - return; - } - - const expression = b.call( - '$.escape', - /** @type {import('estree').Expression} */ (visit(node.expression)) - ); - state.template.push(t_expression(expression)); - - return; - } - - /** @type {import('estree').TemplateElement[]} */ - const quasis = []; + function flush() { + let quasi = b.quasi('', false); + const quasis = [quasi]; /** @type {import('estree').Expression[]} */ const expressions = []; - quasis.push(b.quasi('', false)); - for (let i = 0; i < sequence.length; i++) { const node = sequence[i]; + if (node.type === 'Text' || node.type === 'Comment') { - let last = /** @type {import('estree').TemplateElement} */ (quasis.at(-1)); - last.value.raw += node.type === 'Comment' ? `` : escape_html(node.data); + quasi.value.raw += sanitize_template_string( + node.type === 'Comment' ? `` : escape_html(node.data) + ); } else if (node.type === 'ExpressionTag' && node.expression.type === 'Literal') { - let last = /** @type {import('estree').TemplateElement} */ (quasis.at(-1)); if (node.expression.value != null) { - last.value.raw += escape_html(node.expression.value + ''); + quasi.value.raw += sanitize_template_string(escape_html(node.expression.value + '')); } - } else if (node.type === 'Anchor') { - expressions.push(node.id); - quasis.push(b.quasi('', i + 1 === sequence.length)); } else { expressions.push( b.call('$.escape', /** @type {import('estree').Expression} */ (visit(node.expression))) ); - quasis.push(b.quasi('', i + 1 === sequence.length)); + + quasi = b.quasi('', i + 1 === sequence.length); + quasis.push(quasi); } } - state.template.push(t_expression(b.template(quasis, expressions), true)); + state.template.push(b.template(quasis, expressions)); } for (let i = 0; i < nodes.length; i += 1) { const node = nodes[i]; - if ( - node.type === 'Text' || - node.type === 'Comment' || - node.type === 'ExpressionTag' || - node.type === 'Anchor' - ) { + if (node.type === 'Text' || node.type === 'Comment' || node.type === 'ExpressionTag') { sequence.push(node); } else { if (sequence.length > 0) { - flush_sequence(sequence, false); + flush(); sequence = []; } - visit(node, { - ...state - }); + visit(node, { ...state }); } } if (sequence.length > 0) { - flush_sequence(sequence, true); + flush(); } } @@ -307,9 +240,9 @@ function get_assignment_value(node, { state, visit }) { serialize_get_binding(node.left, state), /** @type {import('estree').Expression} */ (visit(node.right)) ); - } else { - return /** @type {import('estree').Expression} */ (visit(node.right)); } + + return /** @type {import('estree').Expression} */ (visit(node.right)); } /** @@ -430,15 +363,11 @@ function serialize_set_binding(node, context, fallback) { /** * @param {import('#compiler').RegularElement | import('#compiler').SvelteElement} element * @param {import('#compiler').Attribute} attribute - * @param {{ state: { metadata: { namespace: import('#compiler').Namespace }}}} context + * @param {{ state: { namespace: import('#compiler').Namespace }}} context */ function get_attribute_name(element, attribute, context) { let name = attribute.name; - if ( - !element.metadata.svg && - !element.metadata.mathml && - context.state.metadata.namespace !== 'foreign' - ) { + if (!element.metadata.svg && !element.metadata.mathml && context.state.namespace !== 'foreign') { name = name.toLowerCase(); // don't lookup boolean aliases here, the server runtime function does only // check for the lowercase variants of boolean attributes @@ -464,38 +393,45 @@ const global_visitors = { const argument = node.argument; if (argument.type === 'Identifier' && state.scope.get(argument.name)?.kind === 'store_sub') { - let fn = '$.update_store'; - if (node.prefix) fn += '_pre'; - - /** @type {import('estree').Expression[]} */ - const args = [ + return b.call( + node.prefix ? '$.update_store_pre' : '$.update_store', b.assignment('??=', b.id('$$store_subs'), b.object([])), b.literal(argument.name), - b.id(argument.name.slice(1)) - ]; - if (node.operator === '--') { - args.push(b.literal(-1)); - } - - return b.call(fn, ...args); + b.id(argument.name.slice(1)), + node.operator === '--' && b.literal(-1) + ); } - return next(); - } -}; -/** @type {import('./types').Visitors} */ -const javascript_visitors = { - Program(node, { visit }) { - return /** @type {import('estree').Program} */ ({ - ...node, - body: node.body.map((node) => /** @type {import('estree').Node} */ (visit(node))) - }); + return next(); }, - BlockStatement(node, { visit }) { - return /** @type {import('estree').BlockStatement} */ ({ - ...node, - body: node.body.map((node) => /** @type {import('estree').Node} */ (visit(node))) - }); + CallExpression(node, context) { + const rune = get_rune(node, context.state.scope); + + if (rune === '$host') { + return b.id('undefined'); + } + + if (rune === '$effect.active') { + return b.literal(false); + } + + if (rune === '$state.snapshot') { + return /** @type {import('estree').Expression} */ (context.visit(node.arguments[0])); + } + + if (rune === '$state.is') { + return b.call( + 'Object.is', + /** @type {import('estree').Expression} */ (context.visit(node.arguments[0])), + /** @type {import('estree').Expression} */ (context.visit(node.arguments[1])) + ); + } + + if (rune === '$inspect' || rune === '$inspect().with') { + return transform_inspect_rune(node, context); + } + + context.next(); } }; @@ -718,35 +654,6 @@ const javascript_visitors_runes = { } context.next(); }, - CallExpression(node, context) { - const rune = get_rune(node, context.state.scope); - - if (rune === '$host') { - return b.id('undefined'); - } - - if (rune === '$effect.active') { - return b.literal(false); - } - - if (rune === '$state.snapshot') { - return /** @type {import('estree').Expression} */ (context.visit(node.arguments[0])); - } - - if (rune === '$state.is') { - return b.call( - 'Object.is', - /** @type {import('estree').Expression} */ (context.visit(node.arguments[0])), - /** @type {import('estree').Expression} */ (context.visit(node.arguments[1])) - ); - } - - if (rune === '$inspect' || rune === '$inspect().with') { - return transform_inspect_rune(node, context); - } - - context.next(); - }, MemberExpression(node, context) { if (node.object.type === 'ThisExpression' && node.property.type === 'PrivateIdentifier') { const field = context.state.private_derived.get(node.property.name); @@ -761,59 +668,44 @@ const javascript_visitors_runes = { /** * - * @param {true | Array} attribute_value + * @param {true | Array} value * @param {import('./types').ComponentContext} context * @param {boolean} trim_whitespace * @param {boolean} is_component * @returns {import('estree').Expression} */ -function serialize_attribute_value( - attribute_value, - context, - trim_whitespace = false, - is_component = false -) { - if (attribute_value === true) { +function serialize_attribute_value(value, context, trim_whitespace = false, is_component = false) { + if (value === true) { return b.true; } - if (attribute_value.length === 0) { - return b.literal(''); // is this even possible? - } + if (value.length === 1) { + const chunk = value[0]; - if (attribute_value.length === 1) { - const value = attribute_value[0]; - if (value.type === 'Text') { - let data = value.data; - if (trim_whitespace) { - data = data.replace(regex_whitespaces_strict, ' ').trim(); - } + if (chunk.type === 'Text') { + const data = trim_whitespace + ? chunk.data.replace(regex_whitespaces_strict, ' ').trim() + : chunk.data; return b.literal(is_component ? data : escape_html(data, true)); - } else { - return /** @type {import('estree').Expression} */ (context.visit(value.expression)); } + + return /** @type {import('estree').Expression} */ (context.visit(chunk.expression)); } - /** @type {import('estree').TemplateElement[]} */ - const quasis = []; + let quasi = b.quasi('', false); + const quasis = [quasi]; /** @type {import('estree').Expression[]} */ const expressions = []; - quasis.push(b.quasi('', false)); + for (let i = 0; i < value.length; i++) { + const node = value[i]; - let i = 0; - for (const node of attribute_value) { - i++; if (node.type === 'Text') { - let data = node.data; - if (trim_whitespace) { - // don't trim, space could be important to separate from expression tag - data = data.replace(regex_whitespaces_strict, ' '); - } - const last = /** @type {import('estree').TemplateElement} */ (quasis.at(-1)); - last.value.raw += data; + quasi.value.raw += trim_whitespace + ? node.data.replace(regex_whitespaces_strict, ' ') + : node.data; } else { expressions.push( b.call( @@ -821,7 +713,9 @@ function serialize_attribute_value( /** @type {import('estree').Expression} */ (context.visit(node.expression)) ) ); - quasis.push(b.quasi('', i + 1 === attribute_value.length)); + + quasi = b.quasi('', i + 1 === value.length); + quasis.push(quasi); } } @@ -900,15 +794,15 @@ function serialize_element_spread_attributes( ); const args = [object, classes, styles, flags ? b.literal(flags) : undefined]; - context.state.template.push(t_expression(b.call('$.spread_attributes', ...args))); + context.state.template.push(b.call('$.spread_attributes', ...args)); } /** * @param {import('#compiler').Component | import('#compiler').SvelteComponent | import('#compiler').SvelteSelf} node - * @param {string | import('estree').Expression} component_name + * @param {import('estree').Expression} expression * @param {import('./types').ComponentContext} context */ -function serialize_inline_component(node, component_name, context) { +function serialize_inline_component(node, expression, context) { /** @type {Array} */ const props_and_spreads = []; @@ -1090,13 +984,8 @@ function serialize_inline_component(node, component_name, context) { /** @type {import('estree').Statement} */ let statement = b.stmt( - (typeof component_name === 'string' ? b.call : b.maybe_call)( - context.state.options.dev - ? b.call( - '$.validate_component', - typeof component_name === 'string' ? b.id(component_name) : component_name - ) - : component_name, + (node.type === 'SvelteComponent' ? b.maybe_call : b.call)( + context.state.options.dev ? b.call('$.validate_component', expression) : expression, b.id('$$payload'), props_expression ) @@ -1111,31 +1000,18 @@ function serialize_inline_component(node, component_name, context) { b.call( '$.css_props', b.id('$$payload'), - b.literal(context.state.metadata.namespace === 'svg' ? false : true), + b.literal(context.state.namespace === 'svg' ? false : true), b.object(custom_css_props), b.thunk(b.block([statement])) ) ); - context.state.template.push(t_statement(statement)); + context.state.template.push(statement); } else { - context.state.template.push(block_open); - context.state.template.push(t_statement(statement)); - context.state.template.push(block_close); + context.state.template.push(block_open, statement, block_close); } } -/** - * Returns true if the attribute contains a single static text node. - * @param {import('#compiler').Attribute} attribute - * @returns {attribute is import('#compiler').Attribute & { value: [import('#compiler').Text] }} - */ -function is_text_attribute(attribute) { - return ( - attribute.value !== true && attribute.value.length === 1 && attribute.value[0].type === 'Text' - ); -} - /** @type {import('./types').Visitors} */ const javascript_visitors_legacy = { VariableDeclaration(node, { state, visit }) { @@ -1238,7 +1114,7 @@ const javascript_visitors_legacy = { const template_visitors = { Fragment(node, context) { const parent = context.path.at(-1) ?? node; - const namespace = infer_namespace(context.state.metadata.namespace, parent, node.nodes); + const namespace = infer_namespace(context.state.namespace, parent, node.nodes); const { hoisted, trimmed } = clean_nodes( parent, @@ -1250,41 +1126,25 @@ const template_visitors = { context.state.options.preserveComments ); - if (hoisted.length === 0 && trimmed.length === 0) { - return b.block([]); - } - /** @type {import('./types').ComponentServerTransformState} */ const state = { ...context.state, init: [], template: [], - metadata: { - namespace - } + namespace }; for (const node of hoisted) { context.visit(node, state); } - process_children(trimmed, parent, { ...context, state }); - - /** @type {import('estree').Statement[]} */ - const body = [...state.init]; - - if (state.template.length > 0) { - body.push(...serialize_template(state.template)); - } + process_children(trimmed, { ...context, state }); - return b.block(body); + return b.block([...state.init, ...serialize_template(state.template)]); }, HtmlTag(node, context) { - const state = context.state; - state.template.push(block_open); - const raw = /** @type {import('estree').Expression} */ (context.visit(node.expression)); - context.state.template.push(t_expression(raw)); - state.template.push(block_close); + const expression = /** @type {import('estree').Expression} */ (context.visit(node.expression)); + context.state.template.push(block_open, expression, block_close); }, ConstTag(node, { state, visit }) { const declaration = node.declaration.declarations[0]; @@ -1294,35 +1154,29 @@ const template_visitors = { }, DebugTag(node, { state, visit }) { state.template.push( - t_statement( - b.stmt( - b.call( - 'console.log', - b.object( - node.identifiers.map((identifier) => - b.prop( - 'init', - identifier, - /** @type {import('estree').Expression} */ (visit(identifier)) - ) + b.stmt( + b.call( + 'console.log', + b.object( + node.identifiers.map((identifier) => + b.prop( + 'init', + identifier, + /** @type {import('estree').Expression} */ (visit(identifier)) ) ) ) ) ), - t_statement(b.debugger) + b.debugger ); }, RenderTag(node, context) { - const state = context.state; - - state.template.push(block_open); - const callee = unwrap_optional(node.expression).callee; const raw_args = unwrap_optional(node.expression).arguments; const expression = /** @type {import('estree').Expression} */ (context.visit(callee)); - const snippet_function = state.options.dev + const snippet_function = context.state.options.dev ? b.call('$.validate_snippet', expression) : expression; @@ -1330,19 +1184,17 @@ const template_visitors = { return /** @type {import('estree').Expression} */ (context.visit(arg)); }); - state.template.push( - t_statement( - b.stmt( - (node.expression.type === 'CallExpression' ? b.call : b.maybe_call)( - snippet_function, - b.id('$$payload'), - ...snippet_args - ) + context.state.template.push( + block_open, + b.stmt( + (node.expression.type === 'CallExpression' ? b.call : b.maybe_call)( + snippet_function, + b.id('$$payload'), + ...snippet_args ) - ) + ), + block_close ); - - state.template.push(block_close); }, ClassDirective() { throw new Error('Node should have been handled elsewhere'); @@ -1351,16 +1203,25 @@ const template_visitors = { throw new Error('Node should have been handled elsewhere'); }, RegularElement(node, context) { - context.state.template.push(t_string(`<${node.name}`)); + context.state.template.push(string(`<${node.name}`)); const body = serialize_element_attributes(node, context); - context.state.template.push(t_string('>')); + context.state.template.push(string('>')); - const namespace = determine_namespace_for_children(node, context.state.metadata.namespace); + if ((node.name === 'script' || node.name === 'style') && node.fragment.nodes.length === 1) { + context.state.template.push( + string(/** @type {import('#compiler').Text} */ (node.fragment.nodes[0]).data), + string(``) + ); + + return; + } + + const namespace = determine_namespace_for_children(node, context.state.namespace); /** @type {import('./types').ComponentServerTransformState} */ const state = { ...context.state, - metadata: { ...context.state.metadata, namespace }, + namespace, preserve_whitespace: context.state.preserve_whitespace || ((node.name === 'pre' || node.name === 'textarea') && namespace !== 'foreign') @@ -1386,53 +1247,49 @@ const template_visitors = { if (state.options.dev) { const location = /** @type {import('locate-character').Location} */ (locator(node.start)); state.template.push( - t_statement( - b.stmt( - b.call( - '$.push_element', - b.id('$$payload'), - b.literal(node.name), - b.literal(location.line), - b.literal(location.column) - ) + b.stmt( + b.call( + '$.push_element', + b.id('$$payload'), + b.literal(node.name), + b.literal(location.line), + b.literal(location.column) ) ) ); } if (body === null) { - process_children(trimmed, node, { ...context, state }); + process_children(trimmed, { ...context, state }); } else { let id = body; if (body.type !== 'Identifier') { id = b.id(state.scope.generate('$$body')); - state.template.push(t_statement(b.const(id, body))); + state.template.push(b.const(id, body)); } // if this is a `