Comparing version 5.0.0-next.227 to 5.0.0-next.228
@@ -5,3 +5,3 @@ { | ||
"license": "MIT", | ||
"version": "5.0.0-next.227", | ||
"version": "5.0.0-next.228", | ||
"type": "module", | ||
@@ -8,0 +8,0 @@ "types": "./types/index.d.ts", |
/** @import { Expression } from 'estree' */ | ||
/** @import { ExpressionTag, SvelteNode, Text } from '#compiler' */ | ||
/** @import { ComponentClientTransformState, ComponentContext } from '../../types' */ | ||
import { is_event_attribute, is_text_attribute } from '../../../../../utils/ast.js'; | ||
import * as b from '../../../../../utils/builders.js'; | ||
@@ -12,34 +13,58 @@ import { build_template_literal, build_update } from './utils.js'; | ||
* @param {SvelteNode[]} nodes | ||
* @param {(is_text: boolean) => Expression} expression | ||
* @param {(is_text: boolean) => Expression} initial | ||
* @param {boolean} is_element | ||
* @param {ComponentContext} context | ||
*/ | ||
export function process_children(nodes, expression, is_element, { visit, state }) { | ||
export function process_children(nodes, initial, is_element, { visit, state }) { | ||
const within_bound_contenteditable = state.metadata.bound_contenteditable; | ||
let prev = initial; | ||
let skipped = 0; | ||
/** @typedef {Array<Text | ExpressionTag>} Sequence */ | ||
/** @type {Sequence} */ | ||
let sequence = []; | ||
/** @param {boolean} is_text */ | ||
function get_node(is_text) { | ||
if (skipped === 0) { | ||
return prev(is_text); | ||
} | ||
return b.call( | ||
'$.sibling', | ||
prev(false), | ||
(is_text || skipped !== 1) && b.literal(skipped), | ||
is_text && b.true | ||
); | ||
} | ||
/** | ||
* @param {boolean} is_text | ||
* @param {string} name | ||
*/ | ||
function flush_node(is_text, name) { | ||
const expression = get_node(is_text); | ||
let id = expression; | ||
if (id.type !== 'Identifier') { | ||
id = b.id(state.scope.generate(name)); | ||
state.init.push(b.var(id, expression)); | ||
} | ||
prev = () => id; | ||
skipped = 1; // the next node is `$.sibling(id)` | ||
return id; | ||
} | ||
/** | ||
* @param {Sequence} sequence | ||
*/ | ||
function flush_sequence(sequence) { | ||
if (sequence.length === 1) { | ||
const node = sequence[0]; | ||
if (node.type === 'Text') { | ||
let prev = expression; | ||
expression = () => b.call('$.sibling', prev(false)); | ||
state.template.push(node.raw); | ||
return; | ||
} | ||
if (sequence.length === 1 && sequence[0].type === 'Text') { | ||
skipped += 1; | ||
state.template.push(sequence[0].raw); | ||
return; | ||
} | ||
// if this is a standalone `{expression}`, make sure we handle the case where | ||
// no text node was created because the expression was empty during SSR | ||
const needs_hydration_check = sequence.length === 1; | ||
const id = get_node_id(expression(needs_hydration_check), state, 'text'); | ||
state.template.push(' '); | ||
@@ -49,2 +74,7 @@ | ||
// if this is a standalone `{expression}`, make sure we handle the case where | ||
// no text node was created because the expression was empty during SSR | ||
const is_text = sequence.length === 1; | ||
const id = flush_node(is_text, 'text'); | ||
const update = b.stmt(b.call('$.set_text', id, value)); | ||
@@ -59,9 +89,5 @@ | ||
} | ||
expression = (is_text) => b.call('$.sibling', id, is_text && b.true); | ||
} | ||
for (let i = 0; i < nodes.length; i += 1) { | ||
const node = nodes[i]; | ||
for (const node of nodes) { | ||
if (node.type === 'Text' || node.type === 'ExpressionTag') { | ||
@@ -75,30 +101,14 @@ sequence.push(node); | ||
if ( | ||
node.type === 'SvelteHead' || | ||
node.type === 'TitleElement' || | ||
node.type === 'SnippetBlock' | ||
) { | ||
// These nodes do not contribute to the sibling/child tree | ||
// TODO what about e.g. ConstTag and all the other things that | ||
// get hoisted inside clean_nodes? | ||
visit(node, state); | ||
let child_state = state; | ||
if (is_static_element(node)) { | ||
skipped += 1; | ||
} else if (node.type === 'EachBlock' && nodes.length === 1 && is_element) { | ||
node.metadata.is_controlled = true; | ||
} else { | ||
if (node.type === 'EachBlock' && nodes.length === 1 && is_element) { | ||
node.metadata.is_controlled = true; | ||
visit(node, state); | ||
} else { | ||
const id = get_node_id( | ||
expression(false), | ||
state, | ||
node.type === 'RegularElement' ? node.name : 'node' | ||
); | ||
const id = flush_node(false, node.type === 'RegularElement' ? node.name : 'node'); | ||
child_state = { ...state, node: id }; | ||
} | ||
expression = (is_text) => b.call('$.sibling', id, is_text && b.true); | ||
visit(node, { | ||
...state, | ||
node: id | ||
}); | ||
} | ||
} | ||
visit(node, child_state); | ||
} | ||
@@ -108,26 +118,44 @@ } | ||
if (sequence.length > 0) { | ||
// if the final item in a fragment is static text, | ||
// we need to force `hydrate_node` to advance | ||
if (sequence.length === 1 && sequence[0].type === 'Text' && nodes.length > 1) { | ||
state.init.push(b.stmt(b.call('$.next'))); | ||
} | ||
flush_sequence(sequence); | ||
} | ||
// if there are trailing static text nodes/elements, | ||
// traverse to the last (n - 1) one when hydrating | ||
if (skipped > 1) { | ||
skipped -= 1; | ||
state.init.push(b.stmt(get_node(false))); | ||
} | ||
} | ||
/** | ||
* @param {Expression} expression | ||
* @param {ComponentClientTransformState} state | ||
* @param {string} name | ||
* | ||
* @param {SvelteNode} node | ||
*/ | ||
function get_node_id(expression, state, name) { | ||
let id = expression; | ||
function is_static_element(node) { | ||
if (node.type !== 'RegularElement') return false; | ||
if (node.fragment.metadata.dynamic) return false; | ||
if (id.type !== 'Identifier') { | ||
id = b.id(state.scope.generate(name)); | ||
for (const attribute of node.attributes) { | ||
if (attribute.type !== 'Attribute') { | ||
return false; | ||
} | ||
state.init.push(b.var(id, expression)); | ||
if (is_event_attribute(attribute)) { | ||
return false; | ||
} | ||
if (attribute.value !== true && !is_text_attribute(attribute)) { | ||
return false; | ||
} | ||
if (node.name === 'option' && attribute.name === 'value') { | ||
return false; | ||
} | ||
if (node.name.includes('-')) { | ||
return false; // we're setting all attributes on custom elements through properties | ||
} | ||
} | ||
return id; | ||
return true; | ||
} |
@@ -73,2 +73,3 @@ /** @import { TemplateNode } from '#client' */ | ||
*/ | ||
/*@__NO_SIDE_EFFECTS__*/ | ||
export function get_first_child(node) { | ||
@@ -83,2 +84,3 @@ return first_child_getter.call(node); | ||
*/ | ||
/*@__NO_SIDE_EFFECTS__*/ | ||
export function get_next_sibling(node) { | ||
@@ -142,14 +144,18 @@ return next_sibling_getter.call(node); | ||
* Don't mark this as side-effect-free, hydration needs to walk all nodes | ||
* @template {Node} N | ||
* @param {N} node | ||
* @param {TemplateNode} node | ||
* @param {number} count | ||
* @param {boolean} is_text | ||
* @returns {Node | null} | ||
*/ | ||
export function sibling(node, is_text = false) { | ||
export function sibling(node, count = 1, is_text = false) { | ||
let next_sibling = hydrating ? hydrate_node : node; | ||
while (count--) { | ||
next_sibling = /** @type {TemplateNode} */ (get_next_sibling(next_sibling)); | ||
} | ||
if (!hydrating) { | ||
return /** @type {TemplateNode} */ (get_next_sibling(node)); | ||
return next_sibling; | ||
} | ||
var next_sibling = /** @type {TemplateNode} */ (get_next_sibling(hydrate_node)); | ||
var type = next_sibling.nodeType; | ||
@@ -156,0 +162,0 @@ |
@@ -9,3 +9,3 @@ // generated during release, do not modify | ||
*/ | ||
export const VERSION = '5.0.0-next.227'; | ||
export const VERSION = '5.0.0-next.228'; | ||
export const PUBLIC_VERSION = '5'; |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2203218
48827