deduplicate stuff

pull/15538/head
Rich Harris 4 months ago
parent fa8e1efd5e
commit dfde4590b5

@ -2,25 +2,10 @@
/** @import { Namespace } from '#compiler' */ /** @import { Namespace } from '#compiler' */
/** @import { ComponentClientTransformState } from '../types.js' */ /** @import { ComponentClientTransformState } from '../types.js' */
/** @import { Node } from './types.js' */ /** @import { Node } from './types.js' */
import { TEMPLATE_USE_MATHML, TEMPLATE_USE_SVG } from '../../../../../constants.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';
/**
*
* @param {Namespace} namespace
* @param {ComponentClientTransformState} state
* @returns
*/
function get_template_function(namespace, state) {
return (
namespace === 'svg'
? '$.ns_template'
: namespace === 'mathml'
? '$.mathml_template'
: '$.template'
).concat(state.options.templatingMode === 'functional' ? '_fn' : '');
}
/** /**
* @param {Node[]} nodes * @param {Node[]} nodes
*/ */
@ -50,14 +35,18 @@ function build_locations(nodes) {
* @param {Namespace} namespace * @param {Namespace} namespace
* @param {number} [flags] * @param {number} [flags]
*/ */
export function transform_template(state, namespace, flags) { export function transform_template(state, namespace, flags = 0) {
const expression = const tree = state.options.templatingMode === 'functional';
state.options.templatingMode === 'functional'
? state.template.as_objects() const expression = tree ? state.template.as_tree() : state.template.as_html();
: state.template.as_string();
if (tree) {
if (namespace === 'svg') flags |= TEMPLATE_USE_SVG;
if (namespace === 'mathml') flags |= TEMPLATE_USE_MATHML;
}
let call = b.call( let call = b.call(
get_template_function(namespace, state), tree ? `$.from_tree` : `$.from_${namespace}`,
expression, expression,
flags ? b.literal(flags) : undefined flags ? b.literal(flags) : undefined
); );

@ -69,11 +69,11 @@ export class Template {
/** @type {Element} */ (this.#element).attributes[key] = value; /** @type {Element} */ (this.#element).attributes[key] = value;
} }
as_string() { as_html() {
return b.template([b.quasi(this.nodes.map(stringify).join(''), true)], []); return b.template([b.quasi(this.nodes.map(stringify).join(''), true)], []);
} }
as_objects() { as_tree() {
// if the first item is a comment we need to add another comment for effect.start // if the first item is a comment we need to add another comment for effect.start
if (this.nodes[0].type === 'comment') { if (this.nodes[0].type === 'comment') {
this.nodes.unshift({ type: 'comment', data: undefined }); this.nodes.unshift({ type: 'comment', data: undefined });

@ -17,7 +17,7 @@ export function Fragment(node, context) {
// Creates a new block which looks roughly like this: // Creates a new block which looks roughly like this:
// ```js // ```js
// // hoisted: // // hoisted:
// const block_name = $.template(`...`); // const block_name = $.from_html(`...`);
// //
// // for the main block: // // for the main block:
// const id = block_name(); // const id = block_name();

@ -324,7 +324,7 @@ export function clean_nodes(
} }
/** /**
* Infers the namespace for the children of a node that should be used when creating the `$.template(...)`. * Infers the namespace for the children of a node that should be used when creating the fragment
* @param {Namespace} namespace * @param {Namespace} namespace
* @param {AST.SvelteNode} parent * @param {AST.SvelteNode} parent
* @param {AST.SvelteNode[]} nodes * @param {AST.SvelteNode[]} nodes

@ -17,6 +17,8 @@ export const TRANSITION_GLOBAL = 1 << 2;
export const TEMPLATE_FRAGMENT = 1; export const TEMPLATE_FRAGMENT = 1;
export const TEMPLATE_USE_IMPORT_NODE = 1 << 1; export const TEMPLATE_USE_IMPORT_NODE = 1 << 1;
export const TEMPLATE_USE_SVG = 1 << 2;
export const TEMPLATE_USE_MATHML = 1 << 3;
export const HYDRATION_START = '['; export const HYDRATION_START = '[';
/** used to indicate that an `{:else}...` block was rendered */ /** used to indicate that an `{:else}...` block was rendered */

@ -16,7 +16,9 @@ import {
NAMESPACE_MATHML, NAMESPACE_MATHML,
NAMESPACE_SVG, NAMESPACE_SVG,
TEMPLATE_FRAGMENT, TEMPLATE_FRAGMENT,
TEMPLATE_USE_IMPORT_NODE TEMPLATE_USE_IMPORT_NODE,
TEMPLATE_USE_MATHML,
TEMPLATE_USE_SVG
} from '../../../constants.js'; } from '../../../constants.js';
/** /**
@ -37,7 +39,7 @@ export function assign_nodes(start, end) {
* @returns {() => Node | Node[]} * @returns {() => Node | Node[]}
*/ */
/*#__NO_SIDE_EFFECTS__*/ /*#__NO_SIDE_EFFECTS__*/
export function template(content, flags) { export function from_html(content, flags) {
var is_fragment = (flags & TEMPLATE_FRAGMENT) !== 0; var is_fragment = (flags & TEMPLATE_FRAGMENT) !== 0;
var use_import_node = (flags & TEMPLATE_USE_IMPORT_NODE) !== 0; var use_import_node = (flags & TEMPLATE_USE_IMPORT_NODE) !== 0;
@ -77,99 +79,6 @@ export function template(content, flags) {
return clone; return clone;
}; };
} }
/**
* @param {TemplateStructure[]} structure
* @param {NAMESPACE_SVG | NAMESPACE_MATHML | undefined} [ns]
*/
function structure_to_fragment(structure, ns) {
var fragment = create_fragment();
for (var item of structure) {
if (typeof item === 'string') {
fragment.append(create_text(item));
continue;
}
// if `preserveComments === true`, comments are represented as `['// <data>']`
if (item === undefined || item[0][0] === '/') {
fragment.append(create_comment(item ? item[0].slice(3) : ''));
continue;
}
const [name, attributes, ...children] = item;
const namespace = name === 'svg' ? NAMESPACE_SVG : name === 'math' ? NAMESPACE_MATHML : ns;
var element = create_element(name, namespace, attributes?.is);
for (var key in attributes) {
set_attribute(element, key, attributes[key]);
}
if (children.length > 0) {
var target =
element.tagName === 'TEMPLATE'
? /** @type {HTMLTemplateElement} */ (element).content
: element;
target.append(
structure_to_fragment(children, element.tagName === 'foreignObject' ? undefined : namespace)
);
}
fragment.append(element);
}
return fragment;
}
/**
* @param {Array<TemplateStructure>} structure
* @param {number} flags
* @returns {() => Node | Node[]}
*/
/*#__NO_SIDE_EFFECTS__*/
export function template_fn(structure, flags) {
var is_fragment = (flags & TEMPLATE_FRAGMENT) !== 0;
var use_import_node = (flags & TEMPLATE_USE_IMPORT_NODE) !== 0;
/** @type {Node} */
var node;
return () => {
if (hydrating) {
assign_nodes(hydrate_node, null);
return hydrate_node;
}
if (node === undefined) {
node = structure_to_fragment(structure);
if (!is_fragment) node = /** @type {Node} */ (get_first_child(node));
}
var clone = /** @type {TemplateNode} */ (
use_import_node || is_firefox ? document.importNode(node, true) : node.cloneNode(true)
);
if (is_fragment) {
var start = /** @type {TemplateNode} */ (get_first_child(clone));
var end = /** @type {TemplateNode} */ (clone.lastChild);
assign_nodes(start, end);
} else {
assign_nodes(clone, clone);
}
return clone;
};
}
/**
* @param {() => Element | DocumentFragment} fn
*/
export function with_script(fn) {
return () => run_scripts(fn());
}
/** /**
* @param {string} content * @param {string} content
@ -178,7 +87,7 @@ export function with_script(fn) {
* @returns {() => Node | Node[]} * @returns {() => Node | Node[]}
*/ */
/*#__NO_SIDE_EFFECTS__*/ /*#__NO_SIDE_EFFECTS__*/
export function ns_template(content, flags, ns = 'svg') { function from_namespace(content, flags, ns = 'svg') {
/** /**
* Whether or not the first item is a text/element node. If not, we need to * Whether or not the first item is a text/element node. If not, we need to
* create an additional comment node to act as `effect.nodes.start` * create an additional comment node to act as `effect.nodes.start`
@ -227,14 +136,78 @@ export function ns_template(content, flags, ns = 'svg') {
} }
/** /**
* @param {Array<TemplateStructure>} structure * @param {string} content
* @param {number} flags
*/
/*#__NO_SIDE_EFFECTS__*/
export function from_svg(content, flags) {
return from_namespace(content, flags, 'svg');
}
/**
* @param {string} content
* @param {number} flags
*/
/*#__NO_SIDE_EFFECTS__*/
export function from_mathml(content, flags) {
return from_namespace(content, flags, 'math');
}
/**
* @param {TemplateStructure[]} structure
* @param {NAMESPACE_SVG | NAMESPACE_MATHML | undefined} [ns]
*/
function fragment_from_tree(structure, ns) {
var fragment = create_fragment();
for (var item of structure) {
if (typeof item === 'string') {
fragment.append(create_text(item));
continue;
}
// if `preserveComments === true`, comments are represented as `['// <data>']`
if (item === undefined || item[0][0] === '/') {
fragment.append(create_comment(item ? item[0].slice(3) : ''));
continue;
}
const [name, attributes, ...children] = item;
const namespace = name === 'svg' ? NAMESPACE_SVG : name === 'math' ? NAMESPACE_MATHML : ns;
var element = create_element(name, namespace, attributes?.is);
for (var key in attributes) {
set_attribute(element, key, attributes[key]);
}
if (children.length > 0) {
var target =
element.tagName === 'TEMPLATE'
? /** @type {HTMLTemplateElement} */ (element).content
: element;
target.append(
fragment_from_tree(children, element.tagName === 'foreignObject' ? undefined : namespace)
);
}
fragment.append(element);
}
return fragment;
}
/**
* @param {TemplateStructure[]} structure
* @param {number} flags * @param {number} flags
* @param {'svg' | 'math'} ns
* @returns {() => Node | Node[]} * @returns {() => Node | Node[]}
*/ */
/*#__NO_SIDE_EFFECTS__*/ /*#__NO_SIDE_EFFECTS__*/
export function ns_template_fn(structure, flags, ns = 'svg') { export function from_tree(structure, flags) {
var is_fragment = (flags & TEMPLATE_FRAGMENT) !== 0; var is_fragment = (flags & TEMPLATE_FRAGMENT) !== 0;
var use_import_node = (flags & TEMPLATE_USE_IMPORT_NODE) !== 0;
/** @type {Node} */ /** @type {Node} */
var node; var node;
@ -246,11 +219,20 @@ export function ns_template_fn(structure, flags, ns = 'svg') {
} }
if (node === undefined) { if (node === undefined) {
node = structure_to_fragment(structure, ns === 'svg' ? NAMESPACE_SVG : NAMESPACE_MATHML); const ns =
(flags & TEMPLATE_USE_SVG) !== 0
? NAMESPACE_SVG
: (flags & TEMPLATE_USE_MATHML) !== 0
? NAMESPACE_MATHML
: undefined;
node = fragment_from_tree(structure, ns);
if (!is_fragment) node = /** @type {Node} */ (get_first_child(node)); if (!is_fragment) node = /** @type {Node} */ (get_first_child(node));
} }
var clone = /** @type {TemplateNode} */ (node.cloneNode(true)); var clone = /** @type {TemplateNode} */ (
use_import_node || is_firefox ? document.importNode(node, true) : node.cloneNode(true)
);
if (is_fragment) { if (is_fragment) {
var start = /** @type {TemplateNode} */ (get_first_child(clone)); var start = /** @type {TemplateNode} */ (get_first_child(clone));
@ -266,23 +248,10 @@ export function ns_template_fn(structure, flags, ns = 'svg') {
} }
/** /**
* @param {string} content * @param {() => Element | DocumentFragment} fn
* @param {number} flags
* @returns {() => Node | Node[]}
*/
/*#__NO_SIDE_EFFECTS__*/
export function mathml_template(content, flags) {
return ns_template(content, flags, 'math');
}
/**
* @param {Array<TemplateStructure>} structure
* @param {number} flags
* @returns {() => Node | Node[]}
*/ */
/*#__NO_SIDE_EFFECTS__*/ export function with_script(fn) {
export function mathml_template_fn(structure, flags) { return () => run_scripts(fn());
return ns_template_fn(structure, flags, 'math');
} }
/** /**

@ -88,12 +88,10 @@ export {
export { export {
append, append,
comment, comment,
ns_template, from_html,
ns_template_fn, from_mathml,
mathml_template, from_svg,
mathml_template_fn, from_tree,
template,
template_fn,
text, text,
props_id, props_id,
with_script with_script

@ -5,7 +5,7 @@ function increment(_, counter) {
counter.count += 1; counter.count += 1;
} }
var root = $.template(`<button> </button> <!> `, 1); var root = $.from_html(`<button> </button> <!> `, 1);
export default function Await_block_scope($$anchor) { export default function Await_block_scope($$anchor) {
let counter = $.proxy({ count: 0 }); let counter = $.proxy({ count: 0 });

@ -10,7 +10,7 @@ const snippet = ($$anchor) => {
$.append($$anchor, text); $.append($$anchor, text);
}; };
var root = $.template(`<!> `, 1); var root = $.from_html(`<!> `, 1);
export default function Bind_component_snippet($$anchor) { export default function Bind_component_snippet($$anchor) {
let value = $.state(''); let value = $.state('');

@ -1,7 +1,7 @@
import 'svelte/internal/disclose-version'; import 'svelte/internal/disclose-version';
import * as $ from 'svelte/internal/client'; import * as $ from 'svelte/internal/client';
var root = $.template(`<div></div> <svg></svg> <custom-element></custom-element> <div></div> <svg></svg> <custom-element></custom-element>`, 3); var root = $.from_html(`<div></div> <svg></svg> <custom-element></custom-element> <div></div> <svg></svg> <custom-element></custom-element>`, 3);
export default function Main($$anchor) { export default function Main($$anchor) {
// needs to be a snapshot test because jsdom does auto-correct the attribute casing // needs to be a snapshot test because jsdom does auto-correct the attribute casing

@ -2,7 +2,7 @@ import 'svelte/internal/disclose-version';
import 'svelte/internal/flags/legacy'; import 'svelte/internal/flags/legacy';
import * as $ from 'svelte/internal/client'; import * as $ from 'svelte/internal/client';
var root_1 = $.template(`<p></p>`); var root_1 = $.from_html(`<p></p>`);
export default function Each_index_non_null($$anchor) { export default function Each_index_non_null($$anchor) {
var fragment = $.comment(); var fragment = $.comment();

@ -2,7 +2,7 @@ import 'svelte/internal/disclose-version';
import 'svelte/internal/flags/legacy'; import 'svelte/internal/flags/legacy';
import * as $ from 'svelte/internal/client'; import * as $ from 'svelte/internal/client';
var root = $.template_fn( var root = $.from_tree(
[ [
['h1', null, 'hello'], ['h1', null, 'hello'],
' ', ' ',

@ -2,7 +2,7 @@ import 'svelte/internal/disclose-version';
import 'svelte/internal/flags/legacy'; import 'svelte/internal/flags/legacy';
import * as $ from 'svelte/internal/client'; import * as $ from 'svelte/internal/client';
var root = $.template(`<h1>hello world</h1>`); var root = $.from_html(`<h1>hello world</h1>`);
export default function Hello_world($$anchor) { export default function Hello_world($$anchor) {
var h1 = root(); var h1 = root();

@ -2,7 +2,7 @@ import 'svelte/internal/disclose-version';
import 'svelte/internal/flags/legacy'; import 'svelte/internal/flags/legacy';
import * as $ from 'svelte/internal/client'; import * as $ from 'svelte/internal/client';
var root = $.template(`<h1>hello world</h1>`); var root = $.from_html(`<h1>hello world</h1>`);
function Hmr($$anchor) { function Hmr($$anchor) {
var h1 = root(); var h1 = root();

@ -2,7 +2,7 @@ import 'svelte/internal/disclose-version';
import * as $ from 'svelte/internal/client'; import * as $ from 'svelte/internal/client';
var on_click = (_, count) => $.update(count); var on_click = (_, count) => $.update(count);
var root = $.template(`<h1></h1> <b></b> <button> </button> <h1></h1>`, 1); var root = $.from_html(`<h1></h1> <b></b> <button> </button> <h1></h1>`, 1);
export default function Nullish_coallescence_omittance($$anchor) { export default function Nullish_coallescence_omittance($$anchor) {
let name = 'world'; let name = 'world';

@ -2,7 +2,7 @@ import 'svelte/internal/disclose-version';
import 'svelte/internal/flags/legacy'; import 'svelte/internal/flags/legacy';
import * as $ from 'svelte/internal/client'; import * as $ from 'svelte/internal/client';
var root = $.template(`<p></p> <p></p> <!>`, 1); var root = $.from_html(`<p></p> <p></p> <!>`, 1);
export default function Purity($$anchor) { export default function Purity($$anchor) {
var fragment = root(); var fragment = root();

@ -1,7 +1,7 @@
import 'svelte/internal/disclose-version'; import 'svelte/internal/disclose-version';
import * as $ from 'svelte/internal/client'; import * as $ from 'svelte/internal/client';
var root = $.template(`<header><nav><a href="/">Home</a> <a href="/away">Away</a></nav></header> <main><h1> </h1> <div class="static"><p>we don't need to traverse these nodes</p></div> <p>or</p> <p>these</p> <p>ones</p> <!> <p>these</p> <p>trailing</p> <p>nodes</p> <p>can</p> <p>be</p> <p>completely</p> <p>ignored</p></main> <cant-skip><custom-elements></custom-elements></cant-skip> <div><input></div> <div><source></div> <select><option>a</option></select> <img src="..." alt="" loading="lazy"> <div><img src="..." alt="" loading="lazy"></div>`, 3); var root = $.from_html(`<header><nav><a href="/">Home</a> <a href="/away">Away</a></nav></header> <main><h1> </h1> <div class="static"><p>we don't need to traverse these nodes</p></div> <p>or</p> <p>these</p> <p>ones</p> <!> <p>these</p> <p>trailing</p> <p>nodes</p> <p>can</p> <p>be</p> <p>completely</p> <p>ignored</p></main> <cant-skip><custom-elements></custom-elements></cant-skip> <div><input></div> <div><source></div> <select><option>a</option></select> <img src="..." alt="" loading="lazy"> <div><img src="..." alt="" loading="lazy"></div>`, 3);
export default function Skip_static_subtree($$anchor, $$props) { export default function Skip_static_subtree($$anchor, $$props) {
var fragment = root(); var fragment = root();

@ -8,7 +8,7 @@ function reset(_, str, tpl) {
$.set(tpl, ``); $.set(tpl, ``);
} }
var root = $.template(`<input> <input> <button>reset</button>`, 1); var root = $.from_html(`<input> <input> <button>reset</button>`, 1);
export default function State_proxy_literal($$anchor) { export default function State_proxy_literal($$anchor) {
let str = $.state(''); let str = $.state('');

@ -1,7 +1,7 @@
import 'svelte/internal/disclose-version'; import 'svelte/internal/disclose-version';
import * as $ from 'svelte/internal/client'; import * as $ from 'svelte/internal/client';
var root = $.template(`<p> </p>`); var root = $.from_html(`<p> </p>`);
export default function Text_nodes_deriveds($$anchor) { export default function Text_nodes_deriveds($$anchor) {
let count1 = 0; let count1 = 0;

Loading…
Cancel
Save