pull/10954/head
Rich Harris 2 years ago
parent 9a4cd7e8d8
commit 6563ee2710

@ -33,6 +33,8 @@ import {
EACH_IS_STRICT_EQUALS,
EACH_ITEM_REACTIVE,
EACH_KEYED,
TEMPLATE_FRAGMENT,
TEMPLATE_USE_IMPORT_NODE,
TRANSITION_GLOBAL,
TRANSITION_IN,
TRANSITION_OUT
@ -929,9 +931,9 @@ function serialize_bind_this(bind_this, context, node) {
* const block_name = $.template(`...`);
*
* // for the main block:
* const id = $.open(block_name);
* const id = block_name();
* // init stuff and possibly render effect
* $.close(id);
* $.close($$anchor, id);
* ```
* Adds the hoisted parts to `context.state.hoisted` and returns the statements of the main block.
* @param {import('#compiler').SvelteNode} parent
@ -1001,24 +1003,18 @@ function create_block(parent, name, nodes, context) {
node: id
});
context.state.hoisted.push(
b.var(
template_name,
b.call(
get_template_function(namespace, state),
b.template([b.quasi(state.template.join(''), true)], [])
)
)
);
/** @type {import('estree').Expression[]} */
const args = [template_name];
const args = [b.template([b.quasi(state.template.join(''), true)], [])];
if (state.metadata.context.template_needs_import_node) {
args.push(b.false);
args.push(b.literal(TEMPLATE_USE_IMPORT_NODE));
}
body.push(b.var(id, b.call('$.open', ...args)), ...state.before_init, ...state.init);
context.state.hoisted.push(
b.var(template_name, b.call(get_template_function(namespace, state), ...args))
);
body.push(b.var(id, b.call(template_name)), ...state.before_init, ...state.init);
close = b.stmt(b.call('$.close', b.id('$$anchor'), id));
} else if (is_single_child_not_needing_template) {
context.visit(trimmed[0], state);
@ -1054,25 +1050,24 @@ function create_block(parent, name, nodes, context) {
// special case — we can use `$.comment` instead of creating a unique template
body.push(b.var(id, b.call('$.comment')));
} else {
let flags = TEMPLATE_FRAGMENT;
if (state.metadata.context.template_needs_import_node) {
flags |= TEMPLATE_USE_IMPORT_NODE;
}
state.hoisted.push(
b.var(
template_name,
b.call(
get_template_function(namespace, state),
b.template([b.quasi(state.template.join(''), true)], []),
b.true
b.literal(flags)
)
)
);
/** @type {import('estree').Expression[]} */
const args = [template_name];
if (state.metadata.context.template_needs_import_node) {
args.push(b.false);
}
body.push(b.var(id, b.call('$.open_frag', ...args)));
body.push(b.var(id, b.call(template_name)));
}
body.push(...state.before_init, ...state.init);

@ -16,6 +16,9 @@ export const TRANSITION_IN = 1;
export const TRANSITION_OUT = 1 << 1;
export const TRANSITION_GLOBAL = 1 << 2;
export const TEMPLATE_FRAGMENT = 1;
export const TEMPLATE_USE_IMPORT_NODE = 1 << 1;
/** List of Element events that will be delegated */
export const DelegatedEvents = [
'beforeinput',

@ -1,6 +1,6 @@
import { createClassComponent } from '../../../../legacy/legacy-client.js';
import { destroy_effect, render_effect } from '../../reactivity/effects.js';
import { open, close } from '../template.js';
import { close } from '../template.js';
import { define_property } from '../../utils.js';
/**
@ -98,14 +98,10 @@ if (typeof HTMLElement === 'function') {
* @param {Element} anchor
*/
return (anchor) => {
const node = open(() => {
const slot = document.createElement('slot');
if (name !== 'default') {
slot.name = name;
}
return slot;
});
close(anchor, /** @type {Element} */ (node));
const slot = document.createElement('slot');
if (name !== 'default') slot.name = name;
close(anchor, slot);
};
}
/** @type {Record<string, any>} */

@ -8,25 +8,6 @@ export function create_fragment_from_html(html) {
return elem.content;
}
/**
* Creating a document fragment from HTML that contains script tags will not execute
* the scripts. We need to replace the script tags with new ones so that they are executed.
* @param {string} html
*/
export function create_fragment_with_script_from_html(html) {
var content = create_fragment_from_html(html);
var scripts = content.querySelectorAll('script');
for (const script of scripts) {
var new_script = document.createElement('script');
for (var i = 0; i < script.attributes.length; i++) {
new_script.setAttribute(script.attributes[i].name, script.attributes[i].value);
}
new_script.textContent = script.textContent;
/** @type {Node} */ (script.parentNode).replaceChild(new_script, script);
}
return content;
}
/**
* @param {import('#client').Dom} current
* @param {Text | Element | Comment} sibling

@ -1,121 +1,134 @@
import { hydrate_nodes, hydrating } from './hydration.js';
import { child, clone_node, empty } from './operations.js';
import {
create_fragment_from_html,
create_fragment_with_script_from_html,
insert
} from './reconciler.js';
import { clone_node, empty } from './operations.js';
import { create_fragment_from_html, insert } from './reconciler.js';
import { current_effect } from '../runtime.js';
import { is_array } from '../utils.js';
import { TEMPLATE_FRAGMENT, TEMPLATE_USE_IMPORT_NODE } from '../../../constants.js';
/**
* @param {string} html
* @param {boolean} return_fragment
* @returns {() => Node}
* @param {number} flags
* @returns {() => Node | Node[]}
*/
/*#__NO_SIDE_EFFECTS__*/
export function template(html, return_fragment) {
/** @type {undefined | Node} */
let cached_content;
export function template(html, flags) {
var is_fragment = (flags & TEMPLATE_FRAGMENT) !== 0;
var use_import_node = (flags & TEMPLATE_USE_IMPORT_NODE) !== 0;
/** @type {Node} */
var node;
return () => {
if (cached_content === undefined) {
const content = create_fragment_from_html(html);
cached_content = return_fragment ? content : /** @type {Node} */ (child(content));
if (hydrating) {
return is_fragment ? hydrate_nodes : /** @type {Node} */ (hydrate_nodes[0]);
}
return cached_content;
if (!node) {
node = create_fragment_from_html(html);
if (!is_fragment) node = /** @type {Node} */ (node.firstChild);
}
return use_import_node ? document.importNode(node, true) : clone_node(node, true);
};
}
/**
* @param {string} html
* @param {boolean} return_fragment
* @returns {() => Node}
* @param {number} flags
* @returns {() => Node | Node[]}
*/
/*#__NO_SIDE_EFFECTS__*/
export function template_with_script(html, return_fragment) {
/** @type {undefined | Node} */
let cached_content;
export function template_with_script(html, flags) {
var first = true;
var fn = template(html, flags);
return () => {
if (cached_content === undefined) {
const content = create_fragment_with_script_from_html(html);
cached_content = return_fragment ? content : /** @type {Node} */ (child(content));
if (hydrating) return fn();
var node = /** @type {Element | DocumentFragment} */ (fn());
if (first) {
first = false;
run_scripts(node);
}
return cached_content;
return node;
};
}
/**
* @param {string} svg
* @param {boolean} return_fragment
* @returns {() => Node}
* @param {number} flags
* @returns {() => Node | Node[]}
*/
/*#__NO_SIDE_EFFECTS__*/
export function svg_template(svg, return_fragment) {
/** @type {undefined | Node} */
let cached_content;
export function svg_template(svg, flags) {
var fn = template(`<svg>${svg}</svg>`, flags & ~TEMPLATE_FRAGMENT);
/** @type {Element | DocumentFragment} */
var node;
return () => {
if (cached_content === undefined) {
const content = /** @type {Node} */ (child(create_fragment_from_html(`<svg>${svg}</svg>`)));
cached_content = return_fragment ? content : /** @type {Node} */ (child(content));
if (hydrating) {
return fn();
}
if (!node) {
var element = /** @type {Element} */ (fn());
if ((flags & TEMPLATE_FRAGMENT) === 0) {
node = /** @type {Element} */ (element.firstChild);
} else {
node = document.createDocumentFragment();
while (element.firstChild) {
node.appendChild(element.firstChild);
}
}
}
return cached_content;
return node;
};
}
/**
* @param {string} svg
* @param {boolean} return_fragment
* @returns {() => Node}
* @param {number} flags
* @returns {() => Node | Node[]}
*/
/*#__NO_SIDE_EFFECTS__*/
export function svg_template_with_script(svg, return_fragment) {
/** @type {undefined | Node} */
let cached_content;
export function svg_template_with_script(svg, flags) {
var first = true;
var fn = svg_template(svg, flags);
return () => {
if (cached_content === undefined) {
const content = /** @type {Node} */ (child(create_fragment_from_html(`<svg>${svg}</svg>`)));
cached_content = return_fragment ? content : /** @type {Node} */ (child(content));
}
return cached_content;
};
}
if (hydrating) return fn();
/**
* @param {boolean} is_fragment
* @param {boolean} use_clone_node
* @param {() => Node} [template_element_fn]
* @returns {Element | DocumentFragment | Node[]}
*/
/*#__NO_SIDE_EFFECTS__*/
function open_template(is_fragment, use_clone_node, template_element_fn) {
if (hydrating) {
return is_fragment ? hydrate_nodes : /** @type {Element} */ (hydrate_nodes[0]);
}
var node = /** @type {Element | DocumentFragment} */ (fn());
return use_clone_node
? clone_node(/** @type {() => Element} */ (template_element_fn)(), true)
: document.importNode(/** @type {() => Element} */ (template_element_fn)(), true);
}
if (first) {
first = false;
run_scripts(node);
}
/**
* @param {() => Node} template_element_fn
* @param {boolean} [use_clone_node]
* @returns {Element}
*/
export function open(template_element_fn, use_clone_node = true) {
return /** @type {Element} */ (open_template(false, use_clone_node, template_element_fn));
return node;
};
}
/**
* @param {() => Node} template_element_fn
* @param {boolean} [use_clone_node]
* @returns {Element | DocumentFragment | Node[]}
* Creating a document fragment from HTML that contains script tags will not execute
* the scripts. We need to replace the script tags with new ones so that they are executed.
* @param {Element | DocumentFragment} node
*/
export function open_frag(template_element_fn, use_clone_node = true) {
return open_template(true, use_clone_node, template_element_fn);
}
function run_scripts(node) {
for (const script of node.querySelectorAll('script')) {
var clone = document.createElement('script');
for (var attribute of script.attributes) {
clone.setAttribute(attribute.name, attribute.value);
}
const comment_template = template('<!>', true);
clone.textContent = script.textContent;
script.replaceWith(clone);
}
}
/**
* @param {Text | Comment | Element} anchor
@ -136,9 +149,7 @@ export function text(anchor) {
}
/*#__NO_SIDE_EFFECTS__*/
export function comment() {
return open_frag(comment_template);
}
export const comment = template('<!>', TEMPLATE_FRAGMENT);
/**
* Assign the created (or in hydration mode, traversed) dom elements to the current block

Loading…
Cancel
Save