@webjsdev/core 0.7.50 → 0.7.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/webjs-core-browser.js +5 -5
- package/dist/webjs-core-browser.js.map +3 -3
- package/dist/webjs-core-testing.js +20 -20
- package/dist/webjs-core-testing.js.map +3 -3
- package/dist/webjs-core.js +7 -7
- package/dist/webjs-core.js.map +3 -3
- package/index-browser.js +1 -1
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/package.json +1 -1
- package/src/component/lifecycle.js +1481 -0
- package/src/component/reactive.js +171 -0
- package/src/component/server-element.js +248 -0
- package/src/component.js +2 -1896
- package/src/render-client/parts.js +2007 -0
- package/src/render-client/reconciler.js +388 -0
- package/src/render-client/template-compiler.js +525 -0
- package/src/render-client.js +1 -2938
- package/src/render-server/attrs.js +160 -0
- package/src/render-server/dsd.js +445 -0
- package/src/render-server/html-scan.js +402 -0
- package/src/render-server/slots.js +213 -0
- package/src/render-server/stream.js +163 -0
- package/src/render-server/template-renderer.js +923 -0
- package/src/render-server/text.js +163 -0
- package/src/render-server.js +6 -2405
- package/src/router-client/anchors.js +44 -0
- package/src/router-client/boundaries.js +221 -0
- package/src/router-client/constants.js +187 -0
- package/src/router-client/diagnostics.js +377 -0
- package/src/router-client/dom-differ.js +709 -0
- package/src/router-client/dom-parse.js +150 -0
- package/src/router-client/events.js +139 -0
- package/src/router-client/fetch-apply.js +387 -0
- package/src/router-client/form-encoder.js +135 -0
- package/src/router-client/frames.js +329 -0
- package/src/router-client/head-merge.js +344 -0
- package/src/router-client/nav-error.js +105 -0
- package/src/router-client/navigator.js +781 -0
- package/src/router-client/prefetch.js +824 -0
- package/src/router-client/scroll.js +259 -0
- package/src/router-client/snapshot-cache.js +97 -0
- package/src/router-client/state.js +92 -0
- package/src/router-client/stream.js +206 -0
- package/src/router-client/swap.js +451 -0
- package/src/router-client/upgrade.js +261 -0
- package/src/router-client/view-transition.js +223 -0
- package/src/router-client.d.ts +10 -1
- package/src/router-client.js +75 -5345
- package/src/slot/assignment.js +304 -0
- package/src/slot/interception.js +558 -0
- package/src/slot/polyfills.js +365 -0
- package/src/slot/project.js +432 -0
- package/src/slot/sensors.js +303 -0
- package/src/slot/state.js +245 -0
- package/src/slot/symbols.js +85 -0
- package/src/slot.js +7 -2199
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native slot API capture and the light-DOM implementations behind it
|
|
3
|
+
* (assignedNodes / assignedElements / assignedSlot / assign).
|
|
4
|
+
*
|
|
5
|
+
* Moved verbatim out of the pre-split `slot.js`; see that barrel for the
|
|
6
|
+
* runtime's full contract.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import { isOwnSlot } from './assignment.js';
|
|
11
|
+
import { applySlotAssignments } from './project.js';
|
|
12
|
+
import { ensureSlotState, repartition } from './state.js';
|
|
13
|
+
import { FLATTEN_MAX_DEPTH, LIGHT_SLOT_ATTR, PROJECTION_ACTUAL, PROJECTION_ATTR, SLOT_STATE } from './symbols.js';
|
|
14
|
+
|
|
15
|
+
function detectBrowser() {
|
|
16
|
+
return typeof HTMLElement !== 'undefined' && typeof HTMLSlotElement !== 'undefined';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export let inBrowser = detectBrowser();
|
|
20
|
+
|
|
21
|
+
let NATIVE_assignedNodes = null;
|
|
22
|
+
let NATIVE_assignedElements = null;
|
|
23
|
+
let NATIVE_assignedSlot_desc = null;
|
|
24
|
+
let NATIVE_assign = null;
|
|
25
|
+
let polyfillsInstalled = false;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Install the slot DOM-API polyfills on HTMLSlotElement.prototype and
|
|
29
|
+
* Element.prototype if the current realm has those globals. Idempotent.
|
|
30
|
+
* No-op when the realm has no DOM (server-side import-only path).
|
|
31
|
+
*/
|
|
32
|
+
export function installSlotPolyfills() {
|
|
33
|
+
if (polyfillsInstalled) return;
|
|
34
|
+
inBrowser = detectBrowser();
|
|
35
|
+
if (!inBrowser) return;
|
|
36
|
+
NATIVE_assignedNodes = HTMLSlotElement.prototype.assignedNodes;
|
|
37
|
+
NATIVE_assignedElements = HTMLSlotElement.prototype.assignedElements;
|
|
38
|
+
NATIVE_assignedSlot_desc = Object.getOwnPropertyDescriptor(Element.prototype, 'assignedSlot');
|
|
39
|
+
|
|
40
|
+
HTMLSlotElement.prototype.assignedNodes = function patchedAssignedNodes(options) {
|
|
41
|
+
// Two conditions must both hold for the polyfill to take over:
|
|
42
|
+
// 1. The slot carries the framework's data-webjs-light marker.
|
|
43
|
+
// 2. The slot is NOT currently inside a shadow root.
|
|
44
|
+
// Slots that end up in a shadow tree (their host has a ShadowRoot)
|
|
45
|
+
// delegate to native projection, which the browser performs from the
|
|
46
|
+
// host's light-DOM children. discoverSlots cannot tell at template
|
|
47
|
+
// compile time whether the template will be cloned into a light or
|
|
48
|
+
// shadow render root (templates cache by strings identity), so the
|
|
49
|
+
// shadow-vs-light determination has to happen on every API call.
|
|
50
|
+
if (this.hasAttribute(LIGHT_SLOT_ATTR) && !isInShadowRoot(this)) {
|
|
51
|
+
return lightAssignedNodes(this, options);
|
|
52
|
+
}
|
|
53
|
+
return NATIVE_assignedNodes ? NATIVE_assignedNodes.call(this, options) : [];
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
HTMLSlotElement.prototype.assignedElements = function patchedAssignedElements(options) {
|
|
57
|
+
if (this.hasAttribute(LIGHT_SLOT_ATTR) && !isInShadowRoot(this)) {
|
|
58
|
+
return lightAssignedNodes(this, options).filter((n) => n.nodeType === 1);
|
|
59
|
+
}
|
|
60
|
+
return NATIVE_assignedElements ? NATIVE_assignedElements.call(this, options) : [];
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
Object.defineProperty(Element.prototype, 'assignedSlot', {
|
|
64
|
+
configurable: true,
|
|
65
|
+
enumerable: true,
|
|
66
|
+
get: function patchedAssignedSlot() {
|
|
67
|
+
const native =
|
|
68
|
+
NATIVE_assignedSlot_desc && NATIVE_assignedSlot_desc.get
|
|
69
|
+
? NATIVE_assignedSlot_desc.get.call(this)
|
|
70
|
+
: null;
|
|
71
|
+
if (native) return native;
|
|
72
|
+
return findLightAssignedSlot(this);
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Native `assignedSlot` lives on the Slottable mixin, which covers Text as
|
|
77
|
+
// well as Element: a projected text child reports its slot in shadow DOM,
|
|
78
|
+
// so the light parity read must answer for Text too.
|
|
79
|
+
const NATIVE_text_assignedSlot_desc = Object.getOwnPropertyDescriptor(
|
|
80
|
+
Text.prototype,
|
|
81
|
+
'assignedSlot',
|
|
82
|
+
);
|
|
83
|
+
Object.defineProperty(Text.prototype, 'assignedSlot', {
|
|
84
|
+
configurable: true,
|
|
85
|
+
enumerable: true,
|
|
86
|
+
get: function patchedTextAssignedSlot() {
|
|
87
|
+
const native =
|
|
88
|
+
NATIVE_text_assignedSlot_desc && NATIVE_text_assignedSlot_desc.get
|
|
89
|
+
? NATIVE_text_assignedSlot_desc.get.call(this)
|
|
90
|
+
: null;
|
|
91
|
+
if (native) return native;
|
|
92
|
+
return findLightAssignedSlot(this);
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
NATIVE_assign = HTMLSlotElement.prototype.assign;
|
|
97
|
+
HTMLSlotElement.prototype.assign = function patchedAssign(...nodes) {
|
|
98
|
+
if (this.hasAttribute(LIGHT_SLOT_ATTR) && !isInShadowRoot(this)) {
|
|
99
|
+
// Manual slot assignment (imperative, overrides attribute mode). Bound
|
|
100
|
+
// to THIS slot element (native binds slottables to the receiving
|
|
101
|
+
// element, not its name), held via WeakRef (native holds manually
|
|
102
|
+
// assigned slottables weakly), honored by repartition through
|
|
103
|
+
// effectiveKeyOf and by the placement step's per-element routing.
|
|
104
|
+
// NOTE this is a deliberate EXTENSION of native: real manual mode
|
|
105
|
+
// requires slotAssignment 'manual' on the whole shadow root and turns
|
|
106
|
+
// name matching off; here assign() overlays per-node while name
|
|
107
|
+
// matching keeps working for everything else.
|
|
108
|
+
const host = hostOfSlot(this);
|
|
109
|
+
if (host) {
|
|
110
|
+
const state = ensureSlotState(host);
|
|
111
|
+
if (!state.manualAssign) state.manualAssign = new Map();
|
|
112
|
+
const list = nodes.filter(Boolean);
|
|
113
|
+
// LAST-assign-wins: a node handed to this slot leaves any other
|
|
114
|
+
// slot's manual list.
|
|
115
|
+
for (const [slotEl, refs] of state.manualAssign) {
|
|
116
|
+
if (slotEl === this) continue;
|
|
117
|
+
const kept = refs.filter((r) => {
|
|
118
|
+
const n = r.deref();
|
|
119
|
+
return n !== undefined && list.indexOf(n) === -1;
|
|
120
|
+
});
|
|
121
|
+
if (kept.length !== refs.length) {
|
|
122
|
+
if (kept.length) state.manualAssign.set(slotEl, kept);
|
|
123
|
+
else state.manualAssign.delete(slotEl);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (list.length) {
|
|
127
|
+
state.manualAssign.set(this, list.map((n) => new WeakRef(n)));
|
|
128
|
+
} else {
|
|
129
|
+
state.manualAssign.delete(this);
|
|
130
|
+
}
|
|
131
|
+
if (state.applying && state.pendingRecordNodes) {
|
|
132
|
+
for (const n of list) state.pendingRecordNodes.add(n);
|
|
133
|
+
} else {
|
|
134
|
+
state.pendingRecordNodes = new Set(list);
|
|
135
|
+
}
|
|
136
|
+
repartition(state);
|
|
137
|
+
applySlotAssignments(host);
|
|
138
|
+
}
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
return NATIVE_assign ? NATIVE_assign.apply(this, nodes) : undefined;
|
|
142
|
+
};
|
|
143
|
+
polyfillsInstalled = true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* The slot ELEMENT a node is manually assigned to via `assign()`, or null.
|
|
148
|
+
* Dead WeakRefs are compacted on the way through.
|
|
149
|
+
*
|
|
150
|
+
* @param {SlotState} state
|
|
151
|
+
* @param {Node} node
|
|
152
|
+
* @returns {HTMLSlotElement | null}
|
|
153
|
+
*/
|
|
154
|
+
export function manualSlotFor(state, node) {
|
|
155
|
+
const manual = state.manualAssign;
|
|
156
|
+
if (!manual || !manual.size) return null;
|
|
157
|
+
for (const [slotEl, refs] of manual) {
|
|
158
|
+
let found = false;
|
|
159
|
+
const live = refs.filter((r) => {
|
|
160
|
+
const n = r.deref();
|
|
161
|
+
if (n === undefined) return false;
|
|
162
|
+
if (n === node) found = true;
|
|
163
|
+
return true;
|
|
164
|
+
});
|
|
165
|
+
if (live.length !== refs.length) {
|
|
166
|
+
if (live.length) manual.set(slotEl, live);
|
|
167
|
+
else manual.delete(slotEl);
|
|
168
|
+
}
|
|
169
|
+
if (found) {
|
|
170
|
+
// A manual entry is honoured only while its RECEIVING element is still
|
|
171
|
+
// part of this host's tree. A torn-down (conditionally re-rendered)
|
|
172
|
+
// slot element leaves the entry DORMANT: the node falls back to its
|
|
173
|
+
// slot= attribute (routed by name or parked, native's
|
|
174
|
+
// unassigned-but-connected behaviour) instead of being excluded from
|
|
175
|
+
// every slot and lost. The entry is kept, not deleted, so a re-attached
|
|
176
|
+
// element (the cache directive) resumes its assignment, matching
|
|
177
|
+
// native's element-bound persistence.
|
|
178
|
+
const hostEl = state.host;
|
|
179
|
+
if (hostEl && (slotEl === hostEl || hostEl.contains(slotEl))) return slotEl;
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Walk up from a light slot to its owning host: the nearest SLOT_STATE
|
|
188
|
+
* ancestor that actually OWNS the slot. A slot separated from that ancestor
|
|
189
|
+
* by another custom element (a forwarded slot inside a foreign / elided
|
|
190
|
+
* component) is nobody's here: attributing it to the outer host would
|
|
191
|
+
* redirect the outer host's own same-named slot, so `assign()` on such a
|
|
192
|
+
* slot is inert instead (the same carve-out as every other elided-component
|
|
193
|
+
* write).
|
|
194
|
+
*/
|
|
195
|
+
function hostOfSlot(slot) {
|
|
196
|
+
for (let p = slot.parentElement; p; p = p.parentElement) {
|
|
197
|
+
if (/** @type {any} */ (p)[SLOT_STATE]) {
|
|
198
|
+
return isOwnSlot(p, slot) ? p : null;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// First-chance install at module load.
|
|
205
|
+
installSlotPolyfills();
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* True when the given node lives inside a shadow root (so native browser
|
|
209
|
+
* slot projection applies). A ShadowRoot exposes its owning element as
|
|
210
|
+
* `host`; the document does not. Walks the parentNode chain manually
|
|
211
|
+
* with a depth cap to avoid hangs on accidentally cyclic DOMs (e.g.,
|
|
212
|
+
* test fixtures that wire two slots into each other).
|
|
213
|
+
*
|
|
214
|
+
* @param {Node} node
|
|
215
|
+
* @returns {boolean}
|
|
216
|
+
*/
|
|
217
|
+
function isInShadowRoot(node) {
|
|
218
|
+
let n = node;
|
|
219
|
+
for (let depth = 0; depth < 128; depth++) {
|
|
220
|
+
const parent = n.parentNode;
|
|
221
|
+
if (!parent) return false;
|
|
222
|
+
if (parent === n) return false;
|
|
223
|
+
// A real ShadowRoot is a DocumentFragment (nodeType 11) exposing its
|
|
224
|
+
// owner as `.host`. `.host` truthiness ALONE misfires on ordinary
|
|
225
|
+
// elements: HTMLAnchorElement/HTMLAreaElement expose a URL-derived
|
|
226
|
+
// `.host`, so a slot inside an <a> card read as "in shadow DOM".
|
|
227
|
+
if (parent.nodeType === 11 && /** @type {any} */ (parent).host) return true;
|
|
228
|
+
n = parent;
|
|
229
|
+
}
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Resolve assigned nodes for a light-DOM slot. Per spec, returns []
|
|
235
|
+
* when the slot is displaying fallback content.
|
|
236
|
+
*
|
|
237
|
+
* @param {HTMLSlotElement} slot
|
|
238
|
+
* @param {{ flatten?: boolean }} [options]
|
|
239
|
+
* @returns {Node[]}
|
|
240
|
+
*/
|
|
241
|
+
function lightAssignedNodes(slot, options) {
|
|
242
|
+
// Only an APPLIED actual slot has assigned nodes. A fallback slot reports
|
|
243
|
+
// [], and so does a slot with NO data-projection at all (an orphan slot
|
|
244
|
+
// rendered outside any host, or one not yet placed): its children are
|
|
245
|
+
// cloned fallback content, and native returns [] for a slot outside a
|
|
246
|
+
// shadow tree.
|
|
247
|
+
if (slot.getAttribute(PROJECTION_ATTR) !== PROJECTION_ACTUAL) return [];
|
|
248
|
+
const direct = Array.from(slot.childNodes);
|
|
249
|
+
if (!options || !options.flatten) return direct;
|
|
250
|
+
return flattenAssignedNodes(direct, new Set(), 0);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Walk a node list, expanding any data-webjs-light slot into its assigned
|
|
255
|
+
* nodes recursively. Native shadow slots encountered in the chain delegate
|
|
256
|
+
* to their native assignedNodes({flatten: true}).
|
|
257
|
+
*
|
|
258
|
+
* @param {Node[]} nodes
|
|
259
|
+
* @param {Set<HTMLSlotElement>} visited
|
|
260
|
+
* @param {number} depth
|
|
261
|
+
* @returns {Node[]}
|
|
262
|
+
*/
|
|
263
|
+
function flattenAssignedNodes(nodes, visited, depth) {
|
|
264
|
+
if (depth >= FLATTEN_MAX_DEPTH) return nodes.slice();
|
|
265
|
+
const out = [];
|
|
266
|
+
for (const node of nodes) {
|
|
267
|
+
if (node.nodeType === 1 && /** @type {Element} */ (node).tagName === 'SLOT') {
|
|
268
|
+
const slot = /** @type {HTMLSlotElement} */ (node);
|
|
269
|
+
if (visited.has(slot)) continue;
|
|
270
|
+
visited.add(slot);
|
|
271
|
+
if (slot.hasAttribute(LIGHT_SLOT_ATTR)) {
|
|
272
|
+
const inner = lightAssignedNodes(slot, { flatten: false });
|
|
273
|
+
if (inner.length > 0) {
|
|
274
|
+
for (const n of flattenAssignedNodes(inner, visited, depth + 1)) out.push(n);
|
|
275
|
+
} else {
|
|
276
|
+
// Fallback content contributes its children.
|
|
277
|
+
for (const n of flattenAssignedNodes(Array.from(slot.childNodes), visited, depth + 1)) {
|
|
278
|
+
out.push(n);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
} else if (NATIVE_assignedNodes) {
|
|
282
|
+
const inner = NATIVE_assignedNodes.call(slot, { flatten: true });
|
|
283
|
+
for (const n of inner) out.push(n);
|
|
284
|
+
} else {
|
|
285
|
+
out.push(node);
|
|
286
|
+
}
|
|
287
|
+
} else {
|
|
288
|
+
out.push(node);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return out;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Consult a node's DIRECT parent to find a data-webjs-light slot it is
|
|
296
|
+
* currently projected into. Returns null if the element is in a fallback
|
|
297
|
+
* slot or no light slot at all.
|
|
298
|
+
*
|
|
299
|
+
* @param {Element} el
|
|
300
|
+
* @returns {HTMLSlotElement | null}
|
|
301
|
+
*/
|
|
302
|
+
function findLightAssignedSlot(el) {
|
|
303
|
+
// Native `assignedSlot` answers only for a SLOTTABLE itself. In the light
|
|
304
|
+
// parity model, assigned nodes are exactly the slot element's DIRECT
|
|
305
|
+
// children, so only the immediate parent is consulted: a DESCENDANT of
|
|
306
|
+
// assigned content correctly reads null, matching shadow DOM (where only
|
|
307
|
+
// the host's direct children are slottables).
|
|
308
|
+
const p = el.parentElement;
|
|
309
|
+
if (p && p.tagName === 'SLOT' && p.hasAttribute(LIGHT_SLOT_ATTR)) {
|
|
310
|
+
return p.getAttribute(PROJECTION_ATTR) === PROJECTION_ACTUAL
|
|
311
|
+
? /** @type {HTMLSlotElement} */ (p)
|
|
312
|
+
: null;
|
|
313
|
+
}
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// ---------------------------------------------------------------------------
|
|
318
|
+
// Per-host state: the slot record
|
|
319
|
+
// ---------------------------------------------------------------------------
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* @typedef {Object} SlotState
|
|
323
|
+
* @property {Element} host The owning host element (back-reference so
|
|
324
|
+
* state-only helpers can test containment).
|
|
325
|
+
* @property {Node[]} authored The ordered source of truth: every authored
|
|
326
|
+
* child of the host, in host-child order. `assignedByName` is DERIVED from
|
|
327
|
+
* this by `repartition` (grouping each node by its current `slot=`
|
|
328
|
+
* attribute), so there is one place a node's assignment is decided.
|
|
329
|
+
* @property {Map<string|null, Node[]>} assignedByName The DERIVED slot record:
|
|
330
|
+
* `authored` grouped per slot name (null is the default slot). Never mutated
|
|
331
|
+
* directly; always rebuilt by `repartition`.
|
|
332
|
+
* @property {WeakMap<HTMLSlotElement, Node[]>} lastSnapshot Per-slot
|
|
333
|
+
* record of the previous assigned-node set for slotchange equality.
|
|
334
|
+
* @property {Set<HTMLSlotElement>} [pendingSlotChanges] Slots whose
|
|
335
|
+
* assignment changed since the last microtask flush (coalesced slotchange).
|
|
336
|
+
* @property {boolean} [slotChangeScheduled] True while a coalesced
|
|
337
|
+
* slotchange flush is queued for this host.
|
|
338
|
+
* @property {MutationObserver} [backstop] Sensor for raw direct-child writes
|
|
339
|
+
* that bypass the patched methods (never moves nodes; folds into `authored`).
|
|
340
|
+
* @property {MutationObserver} [flipSensor] Sensor for `slot=` / `name=`
|
|
341
|
+
* attribute flips (never moves nodes; re-derives + re-places).
|
|
342
|
+
* @property {Set<Node> | undefined} [pendingRecordNodes] The nodes the
|
|
343
|
+
* current record op touched (inserted, moved, or removed by an interceptor
|
|
344
|
+
* / assign() / router splice), consumed by the next apply pass: the resync
|
|
345
|
+
* step honours record positions for exactly these nodes and adopts
|
|
346
|
+
* physical order for everything else (node-scoped order authority).
|
|
347
|
+
* @property {boolean} [applying] Re-entrancy latch: true while an apply
|
|
348
|
+
* pass runs; a nested call flags `reapply` and returns.
|
|
349
|
+
* @property {boolean} [reapply] Set by a nested apply attempt; the outer
|
|
350
|
+
* pass loops until a full pass completes without it.
|
|
351
|
+
* @property {boolean} [adopted] True when this state was populated by the
|
|
352
|
+
* ADOPT connect branch (SSR hydration / serialized restore): the host's
|
|
353
|
+
* pre-first-render children are rendered markup, so the reconnect fold
|
|
354
|
+
* must not hoover them.
|
|
355
|
+
* @property {WeakMap<Node, string|null>} [adoptedKey] The slot key a
|
|
356
|
+
* self-heal fold ADOPTED for a node a non-record writer placed inside a
|
|
357
|
+
* named slot (its own attribute would key it elsewhere); cleared when the
|
|
358
|
+
* author explicitly changes the node's slot= attribute.
|
|
359
|
+
* @property {Map<HTMLSlotElement, WeakRef<Node>[]>} [manualAssign] Overlay for
|
|
360
|
+
* `HTMLSlotElement.assign()` manual assignment, keyed by the RECEIVING slot
|
|
361
|
+
* element (native binds slottables to the element, so a rename follows the
|
|
362
|
+
* element and duplicates route correctly); nodes held via WeakRef (native
|
|
363
|
+
* holds manually assigned slottables weakly). A node here goes to its
|
|
364
|
+
* assigned slot regardless of its `slot=` attribute.
|
|
365
|
+
*/
|