From a24175ce20703d55ce3eea71ae12808ab9204e1f Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 24 Jul 2024 17:23:44 -0400 Subject: [PATCH 01/17] start modularizing server code --- .../3-transform/server/transform-server.js | 206 ++---------------- .../server/visitors/template/ConstTag.js | 16 ++ .../server/visitors/template/Fragment.js | 46 ++++ .../server/visitors/template/HtmlTag.js | 13 ++ .../server/visitors/template/shared/utils.js | 143 ++++++++++++ 5 files changed, 233 insertions(+), 191 deletions(-) create mode 100644 packages/svelte/src/compiler/phases/3-transform/server/visitors/template/ConstTag.js create mode 100644 packages/svelte/src/compiler/phases/3-transform/server/visitors/template/Fragment.js create mode 100644 packages/svelte/src/compiler/phases/3-transform/server/visitors/template/HtmlTag.js create mode 100644 packages/svelte/src/compiler/phases/3-transform/server/visitors/template/shared/utils.js 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 913206ab131f..bc1aa7a9fe0b 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 @@ -24,12 +24,7 @@ import { VoidElements, WhitespaceInsensitiveAttributes } from '../../constants.js'; -import { - clean_nodes, - determine_namespace_for_children, - infer_namespace, - transform_inspect_rune -} from '../utils.js'; +import { clean_nodes, determine_namespace_for_children, transform_inspect_rune } from '../utils.js'; import { create_attribute, is_custom_element_node, is_element_node } from '../../nodes.js'; import { binding_properties } from '../../bindings.js'; import { regex_starts_with_newline, regex_whitespaces_strict } from '../../patterns.js'; @@ -39,147 +34,19 @@ import { ELEMENT_PRESERVE_ATTRIBUTE_CASE } from '../../../../constants.js'; import { escape_html } from '../../../../escaping.js'; -import { sanitize_template_string } from '../../../utils/sanitize_template_string.js'; -import { - EMPTY_COMMENT, - BLOCK_CLOSE, - BLOCK_OPEN, - BLOCK_OPEN_ELSE -} from '../../../../internal/server/hydration.js'; +import { BLOCK_OPEN_ELSE } from '../../../../internal/server/hydration.js'; import { filename, locator } from '../../../state.js'; import { render_stylesheet } from '../css/index.js'; - -/** Opens an if/each block, so that we can remove nodes in the case of a mismatch */ -const block_open = b.literal(BLOCK_OPEN); - -/** Closes an if/each block, so that we can remove nodes in the case of a mismatch. Also serves as an anchor for these blocks */ -const block_close = b.literal(BLOCK_CLOSE); - -/** Empty comment to keep text nodes separate, or provide an anchor node for blocks */ -const empty_comment = b.literal(EMPTY_COMMENT); - -/** - * @param {Node} node - * @returns {node is Statement} - */ -function is_statement(node) { - return node.type.endsWith('Statement') || node.type.endsWith('Declaration'); -} - -/** - * @param {Array} template - * @param {Identifier} out - * @param {AssignmentOperator} operator - * @returns {Statement[]} - */ -function serialize_template(template, out = b.id('$$payload.out'), operator = '+=') { - /** @type {TemplateElement[]} */ - let quasis = []; - - /** @type {Expression[]} */ - let expressions = []; - - /** @type {Statement[]} */ - const statements = []; - - 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 node = template[i]; - - if (is_statement(node)) { - if (quasis.length !== 0) { - flush(); - } - - statements.push(node); - } else { - let last = quasis.at(-1); - if (!last) quasis.push((last = b.quasi('', false))); - - if (node.type === 'Literal') { - last.value.raw += - typeof node.value === 'string' ? sanitize_template_string(node.value) : 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(); - } - - return statements; -} - -/** - * Processes an array of template nodes, joining sibling text/expression nodes and - * recursing into child nodes. - * @param {Array} nodes - * @param {ComponentContext} context - */ -function process_children(nodes, { visit, state }) { - /** @type {Array} */ - let sequence = []; - - function flush() { - let quasi = b.quasi('', false); - const quasis = [quasi]; - - /** @type {Expression[]} */ - const expressions = []; - - for (let i = 0; i < sequence.length; i++) { - const node = sequence[i]; - - if (node.type === 'Text' || node.type === 'Comment') { - quasi.value.raw += sanitize_template_string( - node.type === 'Comment' ? `` : escape_html(node.data) - ); - } else if (node.type === 'ExpressionTag' && node.expression.type === 'Literal') { - if (node.expression.value != null) { - quasi.value.raw += sanitize_template_string(escape_html(node.expression.value + '')); - } - } else { - expressions.push(b.call('$.escape', /** @type {Expression} */ (visit(node.expression)))); - - quasi = b.quasi('', i + 1 === sequence.length); - quasis.push(quasi); - } - } - - 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') { - sequence.push(node); - } else { - if (sequence.length > 0) { - flush(); - sequence = []; - } - - visit(node, { ...state }); - } - } - - if (sequence.length > 0) { - flush(); - } -} +import { ConstTag } from './visitors/template/ConstTag.js'; +import { Fragment } from './visitors/template/Fragment.js'; +import { HtmlTag } from './visitors/template/HtmlTag.js'; +import { + block_close, + block_open, + empty_comment, + process_children, + serialize_template +} from './visitors/template/shared/utils.js'; /** * @param {VariableDeclarator} declarator @@ -1120,52 +987,9 @@ const javascript_visitors_legacy = { /** @type {ComponentVisitors} */ const template_visitors = { - Fragment(node, context) { - const parent = context.path.at(-1) ?? node; - const namespace = infer_namespace(context.state.namespace, parent, node.nodes); - - const { hoisted, trimmed, is_standalone, is_text_first } = clean_nodes( - parent, - node.nodes, - context.path, - namespace, - context.state, - context.state.preserve_whitespace, - context.state.options.preserveComments - ); - - /** @type {ComponentServerTransformState} */ - const state = { - ...context.state, - init: [], - template: [], - namespace, - skip_hydration_boundaries: is_standalone - }; - - for (const node of hoisted) { - context.visit(node, state); - } - - if (is_text_first) { - // insert `` to prevent this from being glued to the previous fragment - state.template.push(empty_comment); - } - - process_children(trimmed, { ...context, state }); - - return b.block([...state.init, ...serialize_template(state.template)]); - }, - HtmlTag(node, context) { - const expression = /** @type {Expression} */ (context.visit(node.expression)); - context.state.template.push(b.call('$.html', expression)); - }, - ConstTag(node, { state, visit }) { - const declaration = node.declaration.declarations[0]; - const pattern = /** @type {Pattern} */ (visit(declaration.id)); - const init = /** @type {Expression} */ (visit(declaration.init)); - state.init.push(b.declaration('const', pattern, init)); - }, + Fragment, + HtmlTag, + ConstTag, DebugTag(node, { state, visit }) { state.template.push( b.stmt( diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/template/ConstTag.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/template/ConstTag.js new file mode 100644 index 000000000000..4c3622ff025a --- /dev/null +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/template/ConstTag.js @@ -0,0 +1,16 @@ +/** @import { Expression, Pattern } from 'estree' */ +/** @import { ConstTag } from '#compiler' */ +/** @import { ComponentContext } from '../../types' */ +import * as b from '../../../../../utils/builders.js'; + +/** + * @param {ConstTag} node + * @param {ComponentContext} context + */ +export function ConstTag(node, context) { + const declaration = node.declaration.declarations[0]; + const id = /** @type {Pattern} */ (context.visit(declaration.id)); + const init = /** @type {Expression} */ (context.visit(declaration.init)); + + context.state.init.push(b.const(id, init)); +} diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/template/Fragment.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/template/Fragment.js new file mode 100644 index 000000000000..28f747501ace --- /dev/null +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/template/Fragment.js @@ -0,0 +1,46 @@ +/** @import { Fragment } from '#compiler' */ +/** @import { ComponentContext, ComponentServerTransformState } from '../../types' */ +import { clean_nodes, infer_namespace } from '../../../utils.js'; +import * as b from '../../../../../utils/builders.js'; +import { empty_comment, process_children, serialize_template } from './shared/utils.js'; + +/** + * @param {Fragment} node + * @param {ComponentContext} context + */ +export function Fragment(node, context) { + const parent = context.path.at(-1) ?? node; + const namespace = infer_namespace(context.state.namespace, parent, node.nodes); + + const { hoisted, trimmed, is_standalone, is_text_first } = clean_nodes( + parent, + node.nodes, + context.path, + namespace, + context.state, + context.state.preserve_whitespace, + context.state.options.preserveComments + ); + + /** @type {ComponentServerTransformState} */ + const state = { + ...context.state, + init: [], + template: [], + namespace, + skip_hydration_boundaries: is_standalone + }; + + for (const node of hoisted) { + context.visit(node, state); + } + + if (is_text_first) { + // insert `` to prevent this from being glued to the previous fragment + state.template.push(empty_comment); + } + + process_children(trimmed, { ...context, state }); + + return b.block([...state.init, ...serialize_template(state.template)]); +} diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/template/HtmlTag.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/template/HtmlTag.js new file mode 100644 index 000000000000..75df6b75467b --- /dev/null +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/template/HtmlTag.js @@ -0,0 +1,13 @@ +/** @import { Expression } from 'estree' */ +/** @import { HtmlTag } from '#compiler' */ +/** @import { ComponentContext } from '../../types' */ +import * as b from '../../../../../utils/builders.js'; + +/** + * @param {HtmlTag} node + * @param {ComponentContext} context + */ +export function HtmlTag(node, context) { + const expression = /** @type {Expression} */ (context.visit(node.expression)); + context.state.template.push(b.call('$.html', expression)); +} diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/template/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/template/shared/utils.js new file mode 100644 index 000000000000..ac305395399a --- /dev/null +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/template/shared/utils.js @@ -0,0 +1,143 @@ +/** @import { AssignmentOperator, Expression, Identifier, Node, Statement, TemplateElement } from 'estree' */ +/** @import { Comment, ExpressionTag, SvelteNode, Text } from '#compiler' */ +/** @import { ComponentContext } from '../../../types.js' */ +import { escape_html } from '../../../../../../../escaping.js'; +import { + BLOCK_CLOSE, + BLOCK_OPEN, + EMPTY_COMMENT +} from '../../../../../../../internal/server/hydration.js'; +import * as b from '../../../../../../utils/builders.js'; +import { sanitize_template_string } from '../../../../../../utils/sanitize_template_string.js'; + +/** Opens an if/each block, so that we can remove nodes in the case of a mismatch */ +export const block_open = b.literal(BLOCK_OPEN); + +/** Closes an if/each block, so that we can remove nodes in the case of a mismatch. Also serves as an anchor for these blocks */ +export const block_close = b.literal(BLOCK_CLOSE); + +/** Empty comment to keep text nodes separate, or provide an anchor node for blocks */ +export const empty_comment = b.literal(EMPTY_COMMENT); + +/** + * Processes an array of template nodes, joining sibling text/expression nodes and + * recursing into child nodes. + * @param {Array} nodes + * @param {ComponentContext} context + */ +export function process_children(nodes, { visit, state }) { + /** @type {Array} */ + let sequence = []; + + function flush() { + let quasi = b.quasi('', false); + const quasis = [quasi]; + + /** @type {Expression[]} */ + const expressions = []; + + for (let i = 0; i < sequence.length; i++) { + const node = sequence[i]; + + if (node.type === 'Text' || node.type === 'Comment') { + quasi.value.raw += sanitize_template_string( + node.type === 'Comment' ? `` : escape_html(node.data) + ); + } else if (node.type === 'ExpressionTag' && node.expression.type === 'Literal') { + if (node.expression.value != null) { + quasi.value.raw += sanitize_template_string(escape_html(node.expression.value + '')); + } + } else { + expressions.push(b.call('$.escape', /** @type {Expression} */ (visit(node.expression)))); + + quasi = b.quasi('', i + 1 === sequence.length); + quasis.push(quasi); + } + } + + 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') { + sequence.push(node); + } else { + if (sequence.length > 0) { + flush(); + sequence = []; + } + + visit(node, { ...state }); + } + } + + if (sequence.length > 0) { + flush(); + } +} + +/** + * @param {Node} node + * @returns {node is Statement} + */ +function is_statement(node) { + return node.type.endsWith('Statement') || node.type.endsWith('Declaration'); +} + +/** + * @param {Array} template + * @param {Identifier} out + * @param {AssignmentOperator} operator + * @returns {Statement[]} + */ +export function serialize_template(template, out = b.id('$$payload.out'), operator = '+=') { + /** @type {TemplateElement[]} */ + let quasis = []; + + /** @type {Expression[]} */ + let expressions = []; + + /** @type {Statement[]} */ + const statements = []; + + 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 node = template[i]; + + if (is_statement(node)) { + if (quasis.length !== 0) { + flush(); + } + + statements.push(node); + } else { + let last = quasis.at(-1); + if (!last) quasis.push((last = b.quasi('', false))); + + if (node.type === 'Literal') { + last.value.raw += + typeof node.value === 'string' ? sanitize_template_string(node.value) : 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(); + } + + return statements; +} From 58ce670f3221302e73f12da4eb098afa11fe218e Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 24 Jul 2024 18:15:22 -0400 Subject: [PATCH 02/17] more --- .../3-transform/server/transform-server.js | 651 +----------------- .../server/visitors/template/DebugTag.js | 24 + .../visitors/template/RegularElement.js | 106 +++ .../server/visitors/template/RenderTag.js | 35 + .../visitors/template/shared/element.js | 442 ++++++++++++ .../server/visitors/template/shared/utils.js | 61 +- 6 files changed, 681 insertions(+), 638 deletions(-) create mode 100644 packages/svelte/src/compiler/phases/3-transform/server/visitors/template/DebugTag.js create mode 100644 packages/svelte/src/compiler/phases/3-transform/server/visitors/template/RegularElement.js create mode 100644 packages/svelte/src/compiler/phases/3-transform/server/visitors/template/RenderTag.js create mode 100644 packages/svelte/src/compiler/phases/3-transform/server/visitors/template/shared/element.js 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 bc1aa7a9fe0b..ec7ab7798239 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 @@ -1,52 +1,34 @@ -/** @import { AssignmentExpression, AssignmentOperator, BinaryOperator, BlockStatement, CallExpression, Expression, ExpressionStatement, Identifier, Literal, MethodDefinition, Node, Pattern, Program, Property, PropertyDefinition, Statement, TemplateElement, VariableDeclarator } from 'estree' */ -/** @import { Location } from 'locate-character' */ -/** @import { Attribute, Binding, ClassDirective, Comment, Component, ExpressionTag, Namespace, RegularElement, SpreadAttribute, StyleDirective, SvelteComponent, SvelteElement, SvelteNode, SvelteSelf, TemplateNode, Text, ValidatedCompileOptions, ValidatedModuleCompileOptions } from '#compiler' */ +/** @import { AssignmentExpression, BinaryOperator, BlockStatement, CallExpression, Expression, ExpressionStatement, Identifier, MethodDefinition, Node, Pattern, Program, Property, PropertyDefinition, Statement, VariableDeclarator } from 'estree' */ +/** @import { Attribute, Binding, Component, Namespace, SvelteComponent, SvelteNode, SvelteSelf, TemplateNode, Text, ValidatedCompileOptions, ValidatedModuleCompileOptions } from '#compiler' */ /** @import { ComponentContext, ComponentServerTransformState, ComponentVisitors, ServerTransformState, Visitors } from './types.js' */ /** @import { Analysis, ComponentAnalysis } from '../../types.js' */ /** @import { Scope } from '../../scope.js' */ /** @import { StateField } from '../../3-transform/client/types.js' */ // TODO move this type import { walk } from 'zimmerframe'; import { set_scope, get_rune } from '../../scope.js'; -import { - extract_identifiers, - extract_paths, - get_attribute_chunks, - is_event_attribute, - is_expression_async, - is_text_attribute, - unwrap_optional -} from '../../../utils/ast.js'; +import { extract_identifiers, extract_paths, is_expression_async } from '../../../utils/ast.js'; import * as b from '../../../utils/builders.js'; import is_reference from 'is-reference'; -import { - ContentEditableBindings, - LoadErrorElements, - VoidElements, - WhitespaceInsensitiveAttributes -} from '../../constants.js'; -import { clean_nodes, determine_namespace_for_children, transform_inspect_rune } from '../utils.js'; -import { create_attribute, is_custom_element_node, is_element_node } from '../../nodes.js'; -import { binding_properties } from '../../bindings.js'; -import { regex_starts_with_newline, regex_whitespaces_strict } from '../../patterns.js'; -import { - DOMBooleanAttributes, - ELEMENT_IS_NAMESPACED, - ELEMENT_PRESERVE_ATTRIBUTE_CASE -} from '../../../../constants.js'; -import { escape_html } from '../../../../escaping.js'; +import { determine_namespace_for_children, transform_inspect_rune } from '../utils.js'; +import { is_element_node } from '../../nodes.js'; import { BLOCK_OPEN_ELSE } from '../../../../internal/server/hydration.js'; -import { filename, locator } from '../../../state.js'; +import { filename } from '../../../state.js'; import { render_stylesheet } from '../css/index.js'; import { ConstTag } from './visitors/template/ConstTag.js'; +import { DebugTag } from './visitors/template/DebugTag.js'; import { Fragment } from './visitors/template/Fragment.js'; import { HtmlTag } from './visitors/template/HtmlTag.js'; +import { RegularElement } from './visitors/template/RegularElement.js'; +import { RenderTag } from './visitors/template/RenderTag.js'; import { block_close, block_open, empty_comment, process_children, + serialize_attribute_value, serialize_template } from './visitors/template/shared/utils.js'; +import { serialize_element_attributes } from './visitors/template/shared/element.js'; /** * @param {VariableDeclarator} declarator @@ -232,21 +214,6 @@ function serialize_set_binding(node, context, fallback) { return fallback(); } -/** - * @param {RegularElement | SvelteElement} element - * @param {Attribute} attribute - * @param {{ state: { namespace: Namespace }}} context - */ -function get_attribute_name(element, attribute, context) { - let name = attribute.name; - 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 - } - return name; -} - /** @type {Visitors} */ const global_visitors = { Identifier(node, { path, state }) { @@ -536,134 +503,6 @@ const javascript_visitors_runes = { } }; -/** - * - * @param {Attribute['value']} value - * @param {ComponentContext} context - * @param {boolean} trim_whitespace - * @param {boolean} is_component - * @returns {Expression} - */ -function serialize_attribute_value(value, context, trim_whitespace = false, is_component = false) { - if (value === true) { - return b.true; - } - - if (!Array.isArray(value) || value.length === 1) { - const chunk = Array.isArray(value) ? value[0] : value; - - 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)); - } - - return /** @type {Expression} */ (context.visit(chunk.expression)); - } - - let quasi = b.quasi('', false); - const quasis = [quasi]; - - /** @type {Expression[]} */ - const expressions = []; - - for (let i = 0; i < value.length; i++) { - const node = value[i]; - - if (node.type === 'Text') { - quasi.value.raw += trim_whitespace - ? node.data.replace(regex_whitespaces_strict, ' ') - : node.data; - } else { - expressions.push( - b.call('$.stringify', /** @type {Expression} */ (context.visit(node.expression))) - ); - - quasi = b.quasi('', i + 1 === value.length); - quasis.push(quasi); - } - } - - return b.template(quasis, expressions); -} - -/** - * - * @param {RegularElement | SvelteElement} element - * @param {Array} attributes - * @param {StyleDirective[]} style_directives - * @param {ClassDirective[]} class_directives - * @param {ComponentContext} context - */ -function serialize_element_spread_attributes( - element, - attributes, - style_directives, - class_directives, - context -) { - let classes; - let styles; - let flags = 0; - - if (class_directives.length > 0 || context.state.analysis.css.hash) { - const properties = class_directives.map((directive) => - b.init( - directive.name, - directive.expression.type === 'Identifier' && directive.expression.name === directive.name - ? b.id(directive.name) - : /** @type {Expression} */ (context.visit(directive.expression)) - ) - ); - - if (context.state.analysis.css.hash) { - properties.unshift(b.init(context.state.analysis.css.hash, b.literal(true))); - } - - classes = b.object(properties); - } - - if (style_directives.length > 0) { - const properties = style_directives.map((directive) => - b.init( - directive.name, - directive.value === true - ? b.id(directive.name) - : serialize_attribute_value(directive.value, context, true) - ) - ); - - styles = b.object(properties); - } - - if (element.metadata.svg || element.metadata.mathml) { - flags |= ELEMENT_IS_NAMESPACED | ELEMENT_PRESERVE_ATTRIBUTE_CASE; - } else if (is_custom_element_node(element)) { - flags |= ELEMENT_PRESERVE_ATTRIBUTE_CASE; - } - - const object = b.object( - attributes.map((attribute) => { - if (attribute.type === 'Attribute') { - const name = get_attribute_name(element, attribute, context); - const value = serialize_attribute_value( - attribute.value, - context, - WhitespaceInsensitiveAttributes.includes(name) - ); - return b.prop('init', b.key(name), value); - } - - return b.spread(/** @type {Expression} */ (context.visit(attribute))); - }) - ); - - const args = [object, classes, styles, flags ? b.literal(flags) : undefined]; - context.state.template.push(b.call('$.spread_attributes', ...args)); -} - /** * @param {Component | SvelteComponent | SvelteSelf} node * @param {Expression} expression @@ -990,142 +829,9 @@ const template_visitors = { Fragment, HtmlTag, ConstTag, - DebugTag(node, { state, visit }) { - state.template.push( - b.stmt( - b.call( - 'console.log', - b.object( - node.identifiers.map((identifier) => - b.prop('init', identifier, /** @type {Expression} */ (visit(identifier))) - ) - ) - ) - ), - b.debugger - ); - }, - RenderTag(node, context) { - const callee = unwrap_optional(node.expression).callee; - const raw_args = unwrap_optional(node.expression).arguments; - - const snippet_function = /** @type {Expression} */ (context.visit(callee)); - - const snippet_args = raw_args.map((arg) => { - return /** @type {Expression} */ (context.visit(arg)); - }); - - context.state.template.push( - b.stmt( - (node.expression.type === 'CallExpression' ? b.call : b.maybe_call)( - snippet_function, - b.id('$$payload'), - ...snippet_args - ) - ) - ); - - if (!context.state.skip_hydration_boundaries) { - context.state.template.push(empty_comment); - } - }, - ClassDirective() { - throw new Error('Node should have been handled elsewhere'); - }, - StyleDirective() { - throw new Error('Node should have been handled elsewhere'); - }, - RegularElement(node, context) { - const namespace = determine_namespace_for_children(node, context.state.namespace); - - /** @type {ComponentServerTransformState} */ - const state = { - ...context.state, - getters: { ...context.state.getters }, - namespace, - preserve_whitespace: - context.state.preserve_whitespace || - ((node.name === 'pre' || node.name === 'textarea') && namespace !== 'foreign') - }; - - context.state.template.push(b.literal(`<${node.name}`)); - const body = serialize_element_attributes(node, { ...context, state }); - context.state.template.push(b.literal('>')); - - if ((node.name === 'script' || node.name === 'style') && node.fragment.nodes.length === 1) { - context.state.template.push( - b.literal(/** @type {Text} */ (node.fragment.nodes[0]).data), - b.literal(``) - ); - - return; - } - - const { hoisted, trimmed } = clean_nodes( - node, - node.fragment.nodes, - context.path, - namespace, - { - ...state, - scope: /** @type {Scope} */ (state.scopes.get(node.fragment)) - }, - state.preserve_whitespace, - state.options.preserveComments - ); - - for (const node of hoisted) { - context.visit(node, state); - } - - if (state.options.dev) { - const location = /** @type {Location} */ (locator(node.start)); - state.template.push( - 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, { ...context, state }); - } else { - let id = body; - - if (body.type !== 'Identifier') { - id = b.id(state.scope.generate('$$body')); - state.template.push(b.const(id, body)); - } - - // if this is a `