pull/15538/head
Rich Harris 4 months ago
parent 1be912071d
commit 07aed41b7b

@ -4,8 +4,6 @@
/** @import { Node } from './types.js' */ /** @import { Node } from './types.js' */
import { dev, locator } from '../../../../state.js'; import { dev, locator } from '../../../../state.js';
import * as b from '../../../../utils/builders.js'; import * as b from '../../../../utils/builders.js';
import { template_to_functions } from './to-functions.js';
import { template_to_string } from './to-string.js';
/** /**
* *
@ -60,8 +58,8 @@ function build_locations(nodes) {
export function transform_template(state, namespace, flags) { export function transform_template(state, namespace, flags) {
const expression = const expression =
state.options.templatingMode === 'functional' state.options.templatingMode === 'functional'
? template_to_functions(state.template.nodes) ? state.template.as_objects()
: template_to_string(state.template.nodes); : state.template.as_string();
let call = b.call( let call = b.call(
get_template_function(namespace, state), get_template_function(namespace, state),

@ -1,5 +1,10 @@
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { Node, Element } from './types'; */ /** @import { Node, Element } from './types'; */
import { escape_html } from '../../../../../escaping.js';
import { is_void } from '../../../../../utils.js';
import * as b from '#compiler/builders';
import fix_attribute_casing from './fix-attribute-casing.js';
import { regex_starts_with_newline } from '../../../patterns.js';
export class Template { export class Template {
/** /**
@ -65,4 +70,94 @@ export class Template {
set_prop(key, value) { set_prop(key, value) {
/** @type {Element} */ (this.#element).attributes[key] = value; /** @type {Element} */ (this.#element).attributes[key] = value;
} }
as_string() {
return b.template([b.quasi(this.nodes.map(stringify).join(''), true)], []);
}
as_objects() {
// if the first item is a comment we need to add another comment for effect.start
if (this.nodes[0].type === 'anchor') {
this.nodes.unshift({ type: 'anchor', data: undefined });
}
return b.array(this.nodes.map(objectify));
}
}
/**
* @param {Node} item
*/
function stringify(item) {
if (item.type === 'text') {
return item.nodes.map((node) => node.raw).join('');
}
if (item.type === 'anchor') {
return item.data ? `<!--${item.data}-->` : '<!>';
}
let str = `<${item.name}`;
for (const key in item.attributes) {
const value = item.attributes[key];
str += ` ${key}`;
if (value !== undefined) str += `="${escape_html(value, true)}"`;
}
str += `>`;
str += item.children.map(stringify).join('');
if (!is_void(item.name)) {
str += `</${item.name}>`;
}
return str;
}
/** @param {Node} item */
function objectify(item) {
if (item.type === 'text') {
return b.literal(item.nodes.map((node) => node.data).join(''));
}
if (item.type === 'anchor') {
return item.data ? b.array([b.literal(`// ${item.data}`)]) : null;
}
const element = b.array([b.literal(item.name)]);
const attributes = b.object([]);
for (const key in item.attributes) {
const value = item.attributes[key];
attributes.properties.push(
b.prop(
'init',
b.key(fix_attribute_casing(key)),
value === undefined ? b.void0 : b.literal(value)
)
);
}
if (attributes.properties.length > 0 || item.children.length > 0) {
element.elements.push(attributes.properties.length > 0 ? attributes : b.null);
}
if (item.children.length > 0) {
const children = item.children.map(objectify);
element.elements.push(...children);
// special case — strip leading newline from `<pre>` and `<textarea>`
if (item.name === 'pre' || item.name === 'textarea') {
const first = children[0];
if (first?.type === 'Literal') {
first.value = /** @type {string} */ (first.value).replace(regex_starts_with_newline, '');
}
}
}
return element;
} }

@ -1,62 +0,0 @@
/** @import { Node } from './types.js' */
import * as b from '../../../../utils/builders.js';
import { regex_starts_with_newline } from '../../../patterns.js';
import fix_attribute_casing from './fix-attribute-casing.js';
/**
* @param {Node[]} items
*/
export function template_to_functions(items) {
// if the first item is a comment we need to add another comment for effect.start
if (items[0].type === 'anchor') {
items.unshift({ type: 'anchor', data: undefined });
}
return b.array(items.map(build));
}
/** @param {Node} item */
function build(item) {
if (item.type === 'text') {
return b.literal(item.nodes.map((node) => node.data).join(''));
}
if (item.type === 'anchor') {
return item.data ? b.array([b.literal(`// ${item.data}`)]) : null;
}
const element = b.array([b.literal(item.name)]);
const attributes = b.object([]);
for (const key in item.attributes) {
const value = item.attributes[key];
attributes.properties.push(
b.prop(
'init',
b.key(fix_attribute_casing(key)),
value === undefined ? b.void0 : b.literal(value)
)
);
}
if (attributes.properties.length > 0 || item.children.length > 0) {
element.elements.push(attributes.properties.length > 0 ? attributes : b.null);
}
if (item.children.length > 0) {
const children = item.children.map(build);
element.elements.push(...children);
// special case — strip leading newline from `<pre>` and `<textarea>`
if (item.name === 'pre' || item.name === 'textarea') {
const first = children[0];
if (first?.type === 'Literal') {
first.value = /** @type {string} */ (first.value).replace(regex_starts_with_newline, '');
}
}
}
return element;
}

@ -1,42 +0,0 @@
/** @import { Node } from './types.js' */
import { escape_html } from '../../../../../escaping.js';
import { is_void } from '../../../../../utils.js';
import * as b from '../../../../utils/builders.js';
/**
* @param {Node[]} items
*/
export function template_to_string(items) {
return b.template([b.quasi(items.map(stringify).join(''), true)], []);
}
/**
* @param {Node} node
*/
function stringify(node) {
if (node.type === 'text') {
return node.nodes.map((node) => node.raw).join('');
}
if (node.type === 'anchor') {
return node.data ? `<!--${node.data}-->` : '<!>';
}
let str = `<${node.name}`;
for (const key in node.attributes) {
const value = node.attributes[key];
str += ` ${key}`;
if (value !== undefined) str += `="${escape_html(value, true)}"`;
}
str += `>`;
str += node.children.map(stringify).join('');
if (!is_void(node.name)) {
str += `</${node.name}>`;
}
return str;
}
Loading…
Cancel
Save