@microsoft/webui-framework 0.0.25 → 0.0.27

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.
Files changed (53) hide show
  1. package/README.md +209 -53
  2. package/dist/component-asset/asset.d.ts +4 -2
  3. package/dist/component-asset/asset.js +19 -3
  4. package/dist/component-asset/generated-manifest.d.ts +1 -0
  5. package/dist/component-asset/generated-manifest.js +32 -0
  6. package/dist/component-asset/loader.d.ts +2 -1
  7. package/dist/component-asset/loader.js +98 -22
  8. package/dist/component-asset/manifest.d.ts +2 -1
  9. package/dist/component-asset.d.ts +1 -1
  10. package/dist/component-asset.js +12 -2
  11. package/dist/element/link-styles.d.ts +20 -0
  12. package/dist/element/link-styles.js +1042 -0
  13. package/dist/element/markers.d.ts +4 -0
  14. package/dist/element/markers.js +60 -5
  15. package/dist/element/styles.d.ts +39 -1
  16. package/dist/element/styles.js +623 -13
  17. package/dist/element/types.d.ts +5 -3
  18. package/dist/element.js +9 -1
  19. package/dist/index.d.ts +2 -0
  20. package/dist/index.js +1 -0
  21. package/dist/interaction-hydration.d.ts +8 -0
  22. package/dist/interaction-hydration.js +167 -0
  23. package/dist/static-host.js +43 -1
  24. package/dist/streaming-activation.d.ts +2 -2
  25. package/dist/streaming-activation.js +4 -2
  26. package/dist/streaming-bootstrap.d.ts +2 -2
  27. package/dist/streaming-bootstrap.js +7 -1
  28. package/dist/streaming-cleanup.d.ts +1 -0
  29. package/dist/streaming-cleanup.js +66 -14
  30. package/dist/streaming-coordinator.d.ts +3 -2
  31. package/dist/streaming-coordinator.js +107 -47
  32. package/dist/streaming-deferred.d.ts +14 -4
  33. package/dist/streaming-deferred.js +200 -87
  34. package/dist/streaming-dom.d.ts +12 -3
  35. package/dist/streaming-dom.js +41 -27
  36. package/dist/streaming-mode.d.ts +5 -0
  37. package/dist/streaming-mode.js +4 -0
  38. package/dist/streaming-protocol.d.ts +19 -3
  39. package/dist/streaming-protocol.js +4 -3
  40. package/dist/streaming-spans.d.ts +9 -0
  41. package/dist/streaming-spans.js +200 -0
  42. package/dist/template-content.d.ts +17 -0
  43. package/dist/template-content.js +106 -0
  44. package/dist/template-element.d.ts +17 -2
  45. package/dist/template-element.js +493 -177
  46. package/dist/template-events.d.ts +7 -0
  47. package/dist/template-events.js +7 -1
  48. package/dist/template-types.d.ts +1 -2
  49. package/dist/template.d.ts +7 -1
  50. package/dist/template.js +79 -16
  51. package/package.json +9 -3
  52. package/dist/component-asset/resources.d.ts +0 -4
  53. package/dist/component-asset/resources.js +0 -83
@@ -1,17 +1,36 @@
1
1
  import { deferTemplateDefinition, getTemplate } from './template.js';
2
+ import { cloneTemplateContent, } from './template-content.js';
2
3
  import { hydrationStart, hydrationEnd } from './lifecycle.js';
3
- import { isStreamingHydrationMode, PENDING_ROOT_CONNECTED, STREAMED_HOST_ATTR, STREAMING_BOUNDARY_ACTIVATE, } from './streaming-mode.js';
4
+ import { ACTIVATION_ACTIVATED, ACTIVATION_ANCESTOR_BARRIER, ACTIVATION_MISSING_TEMPLATE, ACTIVATION_STATIC_HOST_OPT_OUT, isStreamingHydrationMode, PENDING_ROOT_CONNECTED, STREAMED_HOST_ATTR, STREAMING_BOUNDARY_ACTIVATE, } from './streaming-mode.js';
4
5
  import { createRepeatKeyState, seedHydratedRepeatKeys, syncRepeat, dotWalk, } from './element/diff.js';
5
- import { collectItemMarkers, nextElement, findByOrdinal, buildSSRIndex, collectTemplateElements, MARKER_COND_START, MARKER_COND_END, } from './element/markers.js';
6
- import { injectModuleStyle, } from './element/styles.js';
6
+ import { collectItemMarkers, nextElement, findByOrdinal, buildSSRIndex, collectTemplateElements, MARKER_COND_START, MARKER_COND_END, rawMarker, } from './element/markers.js';
7
+ import { claimSsrComponentStyles, installComponentStyles, isComponentStyleMarker, } from './element/styles.js';
8
+ import { cancelTemplateLinkStyleMount, installTemplateLinkStyles, prepareTemplateLinkStyles, templateMayContainLinkStyles, } from './element/link-styles.js';
7
9
  import { ATTR_KIND_BOOLEAN, ATTR_KIND_COMPLEX, ATTR_KIND_TEMPLATE, hasNativeLiveProperty, } from './element/types.js';
8
10
  import { templateHasRoot, templateRootForAttribute } from './template-roots.js';
9
11
  const DEV = typeof __WEBUI_DEV__ === 'undefined' || __WEBUI_DEV__;
10
- const templateCache = new WeakMap();
11
- const templateDOMCache = new WeakMap();
12
12
  const rootTagCache = new WeakMap();
13
+ const templateDOMCache = new WeakMap();
13
14
  const tplOrdinalCache = new WeakMap();
15
+ const textMarkerBoundaryCache = new WeakMap();
16
+ const NO_TEXT_MARKER_BOUNDARIES = new Int32Array(0);
17
+ const RAW_MARKER_BOUNDARY_BASE = 0x40000000;
14
18
  const tplElementCache = new WeakMap();
19
+ function comparePendingSlots(left, right) {
20
+ return left.order - right.order;
21
+ }
22
+ function insertPendingSlots(slots, count, needsOrdering) {
23
+ if (needsOrdering) {
24
+ slots.length = count;
25
+ slots.sort(comparePendingSlots);
26
+ }
27
+ for (let i = 0; i < count; i++) {
28
+ const slot = slots[i];
29
+ slot.parent.insertBefore(slot.node, slot.before);
30
+ if (slot.end)
31
+ slot.parent.insertBefore(slot.end, slot.before);
32
+ }
33
+ }
15
34
  function getTemplateElements(tplRoot) {
16
35
  let cached = tplElementCache.get(tplRoot);
17
36
  if (!cached) {
@@ -27,6 +46,7 @@ function getTplOrdinals(tplNode) {
27
46
  map = new Map();
28
47
  let elemOrd = 0;
29
48
  let textOrd = 0;
49
+ let commentOrd = 0;
30
50
  const children = tplNode.childNodes;
31
51
  for (let k = 0; k < children.length; k++) {
32
52
  const type = children[k].nodeType;
@@ -38,20 +58,61 @@ function getTplOrdinals(tplNode) {
38
58
  map.set(k, [3, textOrd]);
39
59
  textOrd++;
40
60
  }
61
+ else if (type === 8) {
62
+ map.set(k, [8, commentOrd]);
63
+ commentOrd++;
64
+ }
41
65
  }
42
66
  tplOrdinalCache.set(tplNode, map);
43
67
  return map;
44
68
  }
69
+ function slotKey(slot, orderOffset = 0) {
70
+ return `${slot[0]},${slot[1]},${(slot[2] ?? 0) + orderOffset}`;
71
+ }
72
+ function getTextMarkerBoundaries(meta) {
73
+ const cached = textMarkerBoundaryCache.get(meta);
74
+ if (cached)
75
+ return cached;
76
+ const texts = meta.tx;
77
+ if (!texts) {
78
+ textMarkerBoundaryCache.set(meta, NO_TEXT_MARKER_BOUNDARIES);
79
+ return NO_TEXT_MARKER_BOUNDARIES;
80
+ }
81
+ const markers = new Map();
82
+ if (meta.c) {
83
+ for (let i = 0; i < meta.c.length; i++) {
84
+ markers.set(slotKey(meta.c[i][2]), i + 1);
85
+ }
86
+ }
87
+ if (meta.r) {
88
+ for (let i = 0; i < meta.r.length; i++) {
89
+ markers.set(slotKey(meta.r[i][3]), -(i + 1));
90
+ }
91
+ }
92
+ let rawIndex = 0;
93
+ for (let i = 0; i < texts.length; i++) {
94
+ if (texts[i][2] !== 1)
95
+ continue;
96
+ markers.set(slotKey(texts[i][0]), RAW_MARKER_BOUNDARY_BASE + rawIndex + 1);
97
+ rawIndex++;
98
+ }
99
+ const boundaries = new Int32Array(texts.length);
100
+ for (let i = 0; i < texts.length; i++) {
101
+ boundaries[i] = markers.get(slotKey(texts[i][0], 1)) ?? 0;
102
+ }
103
+ textMarkerBoundaryCache.set(meta, boundaries);
104
+ return boundaries;
105
+ }
45
106
  const EMPTY_ARR = [];
46
107
  const EMPTY_SET = Object.freeze(new Set());
47
108
  const WEBUI_SET_STATE_KEY = Symbol.for('microsoft.webui.setStateKey');
48
109
  const STREAMING_BOUNDARY_ABANDON = Symbol.for('microsoft.webui.boundaryAbandon');
49
- const ACTIVATION_ACTIVATED = 1;
50
- const ACTIVATION_STATIC_HOST_OPT_OUT = 2;
51
- const ACTIVATION_MISSING_TEMPLATE = 3;
52
110
  const templateMetaByCtor = new WeakMap();
53
111
  const pendingAncestorDescendants = new WeakMap();
54
112
  const pendingParentStateByElement = new WeakMap();
113
+ function bindingArray(count) {
114
+ return count > 0 ? [] : EMPTY_ARR;
115
+ }
55
116
  function queuePendingParentState(element, name, value, replayAfterHydration) {
56
117
  let pending = pendingParentStateByElement.get(element);
57
118
  if (!pending) {
@@ -65,6 +126,12 @@ function queuePendingParentState(element, name, value, replayAfterHydration) {
65
126
  (pending.replay ??= new Set()).add(name);
66
127
  }
67
128
  }
129
+ export function consumePendingParentState(element) {
130
+ const pending = pendingParentStateByElement.get(element);
131
+ if (pending)
132
+ pendingParentStateByElement.delete(element);
133
+ return pending;
134
+ }
68
135
  function isUnupgradedWebUITarget(element) {
69
136
  const tagName = element.localName;
70
137
  if (tagName.indexOf('-') === -1)
@@ -129,6 +196,11 @@ function installTemplateObservedAttributes(ctor, tagName, meta = getTemplate(tag
129
196
  }
130
197
  function defineTemplateConstructor(ctor, tagName, meta) {
131
198
  installTemplateObservedAttributes(ctor, tagName, meta);
199
+ if (meta) {
200
+ const ready = prepareTemplateLinkStyles(meta);
201
+ if (ready)
202
+ void ready;
203
+ }
132
204
  customElements.define(tagName, ctor);
133
205
  }
134
206
  function hasAuthoredMember(instance, key) {
@@ -148,6 +220,8 @@ export class TemplateElement extends HTMLElement {
148
220
  this.$root = null;
149
221
  this.$ready = false;
150
222
  this.$hydrated = false;
223
+ this.$deferredClientMount = false;
224
+ this.$resetClientShadow = false;
151
225
  this.$hasMounted = false;
152
226
  this.$deferredSSR = false;
153
227
  this.$activatingDeferredSSR = false;
@@ -156,7 +230,6 @@ export class TemplateElement extends HTMLElement {
156
230
  this.$dirtyPaths = null;
157
231
  this.$pendingFlush = false;
158
232
  this.$preReadyWrites = null;
159
- this.$resolver = (p, s) => this.$resolveValue(p, s);
160
233
  }
161
234
  static { this.$ancestorReleaseQueue = []; }
162
235
  static { this.$ancestorReleaseIndex = 0; }
@@ -170,7 +243,7 @@ export class TemplateElement extends HTMLElement {
170
243
  this.$afterExternalStateWrite(owned);
171
244
  return owned;
172
245
  }
173
- [STREAMING_BOUNDARY_ACTIVATE](state) {
246
+ [STREAMING_BOUNDARY_ACTIVATE](state, bypassAncestor) {
174
247
  if (!this.$deferredSSR && this.hasAttribute(STREAMED_HOST_ATTR)) {
175
248
  this.$deferredSSR = true;
176
249
  this.$ready = true;
@@ -182,16 +255,20 @@ export class TemplateElement extends HTMLElement {
182
255
  return ACTIVATION_MISSING_TEMPLATE;
183
256
  }
184
257
  this.$meta = meta;
185
- if (!this.$shouldActivateOnBoundaryCommit())
258
+ if (!this.$shouldActivateOnBoundaryCommit()) {
259
+ this.$installStyles(meta);
186
260
  return ACTIVATION_STATIC_HOST_OPT_OUT;
187
- const ancestor = this.$nearestHydrationBarrier();
261
+ }
262
+ const ancestor = this.$nearestHydrationBarrier(bypassAncestor);
188
263
  if (ancestor) {
189
264
  this.$deferredByAncestor = true;
190
265
  this.$ancestorBoundaryState = state;
191
266
  this.$hasAncestorBoundaryState = true;
192
267
  this.$registerWithHydrationBarrier(ancestor);
193
- return ACTIVATION_ACTIVATED;
268
+ return ACTIVATION_ANCESTOR_BARRIER;
194
269
  }
270
+ if (this.$deferredByAncestor)
271
+ this.$clearAncestorDeferral();
195
272
  this.$activatingDeferredSSR = true;
196
273
  try {
197
274
  this.$activateDeferredSSR(state);
@@ -202,22 +279,32 @@ export class TemplateElement extends HTMLElement {
202
279
  return ACTIVATION_ACTIVATED;
203
280
  }
204
281
  [STREAMING_BOUNDARY_ABANDON]() {
205
- this.$detachDeferredAncestor();
282
+ this.$clearAncestorDeferral();
206
283
  this.$abandonDeferredDescendants();
207
284
  this.$deferredSSR = false;
208
- this.$deferredByAncestor = undefined;
209
- this.$ancestorBoundaryState = undefined;
210
- this.$hasAncestorBoundaryState = undefined;
211
285
  this.$activatingDeferredSSR = false;
212
286
  this.$ready = false;
213
287
  this.$preReadyWrites = null;
214
288
  if (this.$deferredWrites)
215
289
  this.$deferredWrites = undefined;
216
290
  }
291
+ $clearAncestorDeferral() {
292
+ this.$detachDeferredAncestor();
293
+ this.$deferredByAncestor = undefined;
294
+ this.$ancestorBoundaryState = undefined;
295
+ this.$hasAncestorBoundaryState = undefined;
296
+ }
297
+ $resumeRetainedRoot() {
298
+ const resume = this[PENDING_ROOT_CONNECTED];
299
+ if (typeof resume !== 'function')
300
+ return false;
301
+ resume.call(this);
302
+ return true;
303
+ }
217
304
  static define(tagName) {
218
305
  const ctor = this;
219
306
  const meta = getTemplate(tagName);
220
- if (!meta && isStreamingHydrationMode()) {
307
+ if (!meta) {
221
308
  deferTemplateDefinition(tagName, ctor, () => {
222
309
  defineTemplateConstructor(ctor, tagName, getTemplate(tagName));
223
310
  });
@@ -244,6 +331,8 @@ export class TemplateElement extends HTMLElement {
244
331
  return;
245
332
  }
246
333
  if (this.$hydrated && this.$root) {
334
+ if (this.$deferredClientMount)
335
+ return;
247
336
  hydrationStart();
248
337
  try {
249
338
  this.$ready = true;
@@ -259,9 +348,8 @@ export class TemplateElement extends HTMLElement {
259
348
  this.hasAttribute(STREAMED_HOST_ATTR)) {
260
349
  this.$deferredSSR = true;
261
350
  this.$ready = true;
262
- const resume = this[PENDING_ROOT_CONNECTED];
263
- if (typeof resume === 'function')
264
- resume.call(this);
351
+ if (this.$resumeRetainedRoot())
352
+ return;
265
353
  this.$didDeferSSRHydration();
266
354
  return;
267
355
  }
@@ -287,6 +375,8 @@ export class TemplateElement extends HTMLElement {
287
375
  return;
288
376
  const hasShadow = !!this.shadowRoot;
289
377
  const wantShadow = hasShadow || !!meta.sd;
378
+ const resetClientShadow = this.$resetClientShadow;
379
+ this.$resetClientShadow = false;
290
380
  const remountStructuralTemplate = reconnecting &&
291
381
  ((meta.c?.length ?? 0) !== 0 || (meta.r?.length ?? 0) !== 0);
292
382
  let root;
@@ -296,11 +386,18 @@ export class TemplateElement extends HTMLElement {
296
386
  const renderRoot = wantShadow
297
387
  ? this.shadowRoot ?? this.attachShadow({ mode: 'open' })
298
388
  : this;
299
- renderRoot.replaceChildren();
389
+ const retainedStyles = [];
390
+ let child = renderRoot.firstElementChild;
391
+ while (child) {
392
+ if (isComponentStyleMarker(child))
393
+ retainedStyles.push(child);
394
+ child = child.nextElementSibling;
395
+ }
396
+ renderRoot.replaceChildren(...retainedStyles);
300
397
  root = renderRoot;
301
398
  isSSR = false;
302
399
  }
303
- else if (hasShadow) {
400
+ else if (hasShadow && !resetClientShadow) {
304
401
  root = this.shadowRoot;
305
402
  isSSR = true;
306
403
  }
@@ -309,16 +406,18 @@ export class TemplateElement extends HTMLElement {
309
406
  isSSR = true;
310
407
  }
311
408
  else if (wantShadow) {
312
- root = this.attachShadow({ mode: 'open' });
409
+ const renderRoot = this.shadowRoot ?? this.attachShadow({ mode: 'open' });
410
+ if (resetClientShadow)
411
+ renderRoot.replaceChildren();
412
+ root = renderRoot;
313
413
  isSSR = false;
314
414
  }
315
415
  else {
316
416
  root = this;
317
417
  isSSR = false;
318
418
  }
319
- if (isSSR &&
320
- !forceSSR &&
321
- !reconnecting) {
419
+ this.$installStyles(meta);
420
+ if (isSSR && !forceSSR && !reconnecting) {
322
421
  const ancestor = this.$nearestHydrationBarrier();
323
422
  if (ancestor) {
324
423
  this.$meta = meta;
@@ -329,9 +428,7 @@ export class TemplateElement extends HTMLElement {
329
428
  this.$registerWithHydrationBarrier(ancestor);
330
429
  return;
331
430
  }
332
- if (!this.$shouldDeferSSRHydration(meta)) {
333
- }
334
- else {
431
+ if (this.$shouldDeferSSRHydration(meta)) {
335
432
  this.$meta = meta;
336
433
  this.$deferredSSR = true;
337
434
  this.$ready = true;
@@ -339,10 +436,9 @@ export class TemplateElement extends HTMLElement {
339
436
  return;
340
437
  }
341
438
  }
439
+ let deferredHydrationFinish = false;
342
440
  hydrationStart();
343
441
  try {
344
- if (meta.sa)
345
- injectModuleStyle(meta.sa, this.shadowRoot);
346
442
  if (!reconnecting) {
347
443
  if (isSSR) {
348
444
  if (this.$shouldApplySSRBootstrapState()) {
@@ -365,6 +461,7 @@ export class TemplateElement extends HTMLElement {
365
461
  if (isSSR && this.$deferredWrites)
366
462
  this.$replayDeferredWrites();
367
463
  if (isSSR && reconnecting) {
464
+ this.$invalidateRawValues(this.$root);
368
465
  this.$updateBindings(this.$root.texts, this.$root.attrs, this.$root.conds, this.$root.repeats, true);
369
466
  }
370
467
  if (isSSR && DEV)
@@ -373,19 +470,72 @@ export class TemplateElement extends HTMLElement {
373
470
  this.$preReadyWrites = null;
374
471
  if (!isSSR && clientRoot) {
375
472
  this.$updateInstance(this.$root);
376
- if (this.$root.repeats.length !== 0 || this.$root.conds.length !== 0) {
473
+ const hasStructuralBindings = this.$root.repeats.length !== 0 || this.$root.conds.length !== 0;
474
+ if (hasStructuralBindings) {
377
475
  this.$root.nodes = childNodesArray(clientRoot);
476
+ }
477
+ let deferredStyles = false;
478
+ if (templateMayContainLinkStyles(meta)) {
479
+ const stagingRoot = clientRoot;
480
+ const mountedInstance = this.$root;
481
+ deferredStyles = installTemplateLinkStyles(this, meta, this.shadowRoot, clientRoot, this.$root.attrs, {
482
+ hasAuthorHydrationLifecycle: this.hydratedCallback !==
483
+ TemplateElement.prototype.hydratedCallback,
484
+ beforeAppend: () => {
485
+ if (this.$root !== mountedInstance || !this.$hydrated)
486
+ return;
487
+ this.$updateInstance(mountedInstance);
488
+ if (hasStructuralBindings) {
489
+ mountedInstance.nodes = childNodesArray(stagingRoot);
490
+ }
491
+ },
492
+ afterAppend: () => {
493
+ if (this.$root !== mountedInstance || !this.$hydrated)
494
+ return;
495
+ this.$replaceInstanceContainer(mountedInstance, clientRoot, root);
496
+ mountedInstance.container = root;
497
+ this.$deferredClientMount = false;
498
+ this.$ready = true;
499
+ this.$finishHydration();
500
+ },
501
+ });
502
+ }
503
+ if (deferredStyles) {
504
+ this.$deferredClientMount = true;
505
+ this.$ready = false;
506
+ deferredHydrationFinish = true;
507
+ }
508
+ if (!deferredStyles && hasStructuralBindings) {
378
509
  this.$replaceInstanceContainer(this.$root, clientRoot, root);
379
510
  }
380
- this.$appendStagedChildren(root, clientRoot);
381
- this.$root.container = root;
511
+ if (!deferredStyles)
512
+ this.$appendStagedChildren(root, clientRoot);
513
+ if (!deferredStyles)
514
+ this.$root.container = root;
382
515
  }
383
- this.$finishHydration();
516
+ if (!deferredHydrationFinish)
517
+ this.$finishHydration();
384
518
  }
385
519
  finally {
386
520
  hydrationEnd();
387
521
  }
388
522
  }
523
+ $installStyles(meta) {
524
+ const wantShadow = !!this.shadowRoot || !!meta.sd;
525
+ const containingRoot = this.getRootNode();
526
+ const styleTarget = wantShadow
527
+ ? this.shadowRoot
528
+ : containingRoot.nodeType === 11 && 'host' in containingRoot
529
+ ? containingRoot
530
+ : this.ownerDocument;
531
+ claimSsrComponentStyles(this, styleTarget);
532
+ const installation = installComponentStyles(this.localName, styleTarget, this);
533
+ if (installation) {
534
+ void installation.catch((error) => {
535
+ console.error(error);
536
+ });
537
+ }
538
+ }
389
539
  disconnectedCallback() {
390
540
  this.$detachDeferredAncestor();
391
541
  if (this.$root) {
@@ -396,13 +546,14 @@ export class TemplateElement extends HTMLElement {
396
546
  }
397
547
  }
398
548
  $destroy() {
549
+ const shadowRoot = this.shadowRoot;
550
+ if (shadowRoot && cancelTemplateLinkStyleMount(shadowRoot)) {
551
+ this.$resetClientShadow = true;
552
+ }
399
553
  if (!this.$root) {
400
- this.$detachDeferredAncestor();
554
+ this.$clearAncestorDeferral();
401
555
  this.$abandonDeferredDescendants();
402
556
  this.$deferredSSR = false;
403
- this.$deferredByAncestor = undefined;
404
- this.$ancestorBoundaryState = undefined;
405
- this.$hasAncestorBoundaryState = undefined;
406
557
  this.$ready = false;
407
558
  this.$hasUnknownScopes = false;
408
559
  if (this.$deferredWrites)
@@ -417,6 +568,7 @@ export class TemplateElement extends HTMLElement {
417
568
  this.$pendingFlush = false;
418
569
  this.$preReadyWrites = null;
419
570
  this.$hydrated = false;
571
+ this.$deferredClientMount = false;
420
572
  this.$ready = false;
421
573
  this.$hasUnknownScopes = false;
422
574
  }
@@ -437,10 +589,14 @@ export class TemplateElement extends HTMLElement {
437
589
  instance.parent = undefined;
438
590
  instance.container = null;
439
591
  instance.nodes.length = 0;
440
- instance.texts.length = 0;
441
- instance.attrs.length = 0;
442
- instance.conds.length = 0;
443
- instance.repeats.length = 0;
592
+ if (instance.texts.length > 0)
593
+ instance.texts.length = 0;
594
+ if (instance.attrs.length > 0)
595
+ instance.attrs.length = 0;
596
+ if (instance.conds.length > 0)
597
+ instance.conds.length = 0;
598
+ if (instance.repeats.length > 0)
599
+ instance.repeats.length = 0;
444
600
  }
445
601
  attributeChangedCallback(name, oldValue, newValue) {
446
602
  if (Object.is(oldValue, newValue))
@@ -489,7 +645,8 @@ export class TemplateElement extends HTMLElement {
489
645
  $currentTemplateMetadata() {
490
646
  return this.$meta ?? this.$templateMeta();
491
647
  }
492
- $nearestHydrationBarrier() {
648
+ $nearestHydrationBarrier(bypassAncestor) {
649
+ let bypass = bypassAncestor;
493
650
  let current = this;
494
651
  while (true) {
495
652
  let parent = current.assignedSlot ?? current.parentElement;
@@ -505,6 +662,11 @@ export class TemplateElement extends HTMLElement {
505
662
  }
506
663
  if (!parent)
507
664
  return undefined;
665
+ if (parent === bypass) {
666
+ bypass = undefined;
667
+ current = parent;
668
+ continue;
669
+ }
508
670
  if (parent instanceof TemplateElement) {
509
671
  const parentMeta = parent.$meta ?? parent.$templateMeta();
510
672
  if (parentMeta?.th) {
@@ -633,6 +795,8 @@ export class TemplateElement extends HTMLElement {
633
795
  const boundaryState = this.$ancestorBoundaryState;
634
796
  this.$hasAncestorBoundaryState = undefined;
635
797
  this.$ancestorBoundaryState = undefined;
798
+ if (this.$resumeRetainedRoot())
799
+ return;
636
800
  const meta = this.$meta;
637
801
  if (meta && this.$shouldDeferSSRHydration(meta)) {
638
802
  if (hasBoundaryState) {
@@ -802,10 +966,9 @@ export class TemplateElement extends HTMLElement {
802
966
  }
803
967
  }
804
968
  $applyPendingParentState(replayAfterHydration) {
805
- const pending = pendingParentStateByElement.get(this);
969
+ const pending = consumePendingParentState(this);
806
970
  if (!pending)
807
971
  return;
808
- pendingParentStateByElement.delete(this);
809
972
  const state = pending.values;
810
973
  const observableNames = this.$observableNames();
811
974
  const keys = Object.keys(state);
@@ -835,6 +998,8 @@ export class TemplateElement extends HTMLElement {
835
998
  }
836
999
  $update(path) {
837
1000
  if (!this.$ready || !this.$root) {
1001
+ if (this.$deferredClientMount)
1002
+ return;
838
1003
  if (path && this.$deferredSSR)
839
1004
  this.$recordDeferredWrite(path);
840
1005
  if (DEV && path && this.isConnected)
@@ -933,7 +1098,7 @@ export class TemplateElement extends HTMLElement {
933
1098
  if (!index)
934
1099
  return;
935
1100
  const ctx = {
936
- resolver: this.$resolver,
1101
+ resolver: this.$conditionResolver(),
937
1102
  resolveParts: (parts, scope) => this.$resolveParts(parts, scope),
938
1103
  resolveValue: (path, scope) => this.$resolveValue(path, scope),
939
1104
  };
@@ -941,13 +1106,7 @@ export class TemplateElement extends HTMLElement {
941
1106
  void import('./hydration-mismatch.js').then((m) => m.reportHydrationMismatch(tag, writes, index, ctx));
942
1107
  }
943
1108
  $parseTemplate(meta) {
944
- let cached = templateCache.get(meta);
945
- if (cached)
946
- return cached.cloneNode(true);
947
- const tpl = document.createElement('template');
948
- tpl.innerHTML = meta.h;
949
- templateCache.set(meta, tpl.content);
950
- return tpl.content.cloneNode(true);
1109
+ return cloneTemplateContent(meta);
951
1110
  }
952
1111
  $createStagingRoot(meta) {
953
1112
  const wrapper = document.createElement('div');
@@ -998,102 +1157,113 @@ export class TemplateElement extends HTMLElement {
998
1157
  $wire(root, meta, scope) {
999
1158
  const instance = {
1000
1159
  scope, container: root, nodes: childNodesArray(root),
1001
- texts: [], attrs: [], conds: [], repeats: [],
1160
+ texts: bindingArray(meta.tx?.length ?? 0),
1161
+ attrs: bindingArray(meta.a?.length ?? 0),
1162
+ conds: bindingArray(meta.c?.length ?? 0),
1163
+ repeats: bindingArray(meta.r?.length ?? 0),
1002
1164
  };
1003
1165
  const elements = collectTemplateElements(root);
1004
- const textRefs = new Array(meta.tx?.length ?? 0);
1005
- let textRefCount = 0;
1166
+ const pendingSlots = new Array((meta.tx?.length ?? 0) + (meta.c?.length ?? 0) + (meta.r?.length ?? 0));
1167
+ let pendingSlotCount = 0;
1168
+ let needsSlotOrdering = false;
1169
+ let rawIndex = 0;
1006
1170
  if (meta.tx) {
1007
1171
  for (let i = 0; i < meta.tx.length; i++) {
1008
1172
  const entry = meta.tx[i];
1009
1173
  const [slot, parts] = entry;
1010
1174
  const raw = entry[2] === 1;
1011
- const [parentIndex, beforeIndex] = slot;
1175
+ const [parentIndex, beforeIndex, order = 0] = slot;
1012
1176
  const parent = elements[parentIndex];
1013
1177
  if (!parent || (parent.nodeType !== 1 && parent.nodeType !== 11))
1014
1178
  continue;
1015
- textRefs[textRefCount] = { parent, ref: parent.childNodes[beforeIndex] || null, parts, raw };
1016
- textRefCount += 1;
1179
+ let node;
1180
+ let end;
1181
+ if (raw) {
1182
+ const start = document.createComment(rawMarker(rawIndex));
1183
+ end = document.createComment(rawMarker(rawIndex, true));
1184
+ rawIndex++;
1185
+ instance.texts.push({
1186
+ node: start,
1187
+ parts,
1188
+ scope,
1189
+ raw: true,
1190
+ rawEnd: end,
1191
+ rawOwner: instance,
1192
+ });
1193
+ node = start;
1194
+ }
1195
+ else {
1196
+ const textNode = document.createTextNode('');
1197
+ instance.texts.push({ node: textNode, parts, scope });
1198
+ node = textNode;
1199
+ }
1200
+ pendingSlots[pendingSlotCount++] = {
1201
+ parent,
1202
+ before: parent.childNodes[beforeIndex] || null,
1203
+ order,
1204
+ node,
1205
+ end,
1206
+ };
1207
+ if (order > 0)
1208
+ needsSlotOrdering = true;
1017
1209
  }
1018
1210
  }
1019
- const condRefs = new Array(meta.c?.length ?? 0);
1020
- let condRefCount = 0;
1021
1211
  if (meta.c) {
1022
1212
  for (let i = 0; i < meta.c.length; i++) {
1023
1213
  const [condition, blockIndex, slotMeta] = meta.c[i];
1024
- const [parentIndex, beforeIndex] = slotMeta;
1214
+ const [parentIndex, beforeIndex, order = 0] = slotMeta;
1025
1215
  const parent = elements[parentIndex];
1026
1216
  if (!parent || (parent.nodeType !== 1 && parent.nodeType !== 11))
1027
1217
  continue;
1028
- condRefs[condRefCount] = { parent, ref: parent.childNodes[beforeIndex] || null, condition: condition, blockIndex };
1029
- condRefCount += 1;
1218
+ const anchor = document.createComment('');
1219
+ instance.conds.push({
1220
+ condition: condition,
1221
+ blockIndex,
1222
+ anchor,
1223
+ scope,
1224
+ owner: instance,
1225
+ instance: null,
1226
+ });
1227
+ pendingSlots[pendingSlotCount++] = {
1228
+ parent,
1229
+ before: parent.childNodes[beforeIndex] || null,
1230
+ order,
1231
+ node: anchor,
1232
+ };
1233
+ if (order > 0)
1234
+ needsSlotOrdering = true;
1030
1235
  }
1031
1236
  }
1032
- const repRefs = new Array(meta.r?.length ?? 0);
1033
- let repRefCount = 0;
1034
1237
  if (meta.r) {
1035
1238
  for (let i = 0; i < meta.r.length; i++) {
1036
1239
  const [collection, itemVar, blockIndex, slotMeta, keyPath] = meta.r[i];
1037
- const [parentIndex, beforeIndex] = slotMeta;
1240
+ const [parentIndex, beforeIndex, order = 0] = slotMeta;
1038
1241
  const parent = elements[parentIndex];
1039
1242
  if (!parent || (parent.nodeType !== 1 && parent.nodeType !== 11))
1040
1243
  continue;
1041
- repRefs[repRefCount] = {
1244
+ const anchor = document.createComment('');
1245
+ const binding = {
1246
+ markerId: i, collection, itemVar, blockIndex,
1247
+ container: parent, start: anchor, end: null,
1248
+ scope, owner: instance, instances: [],
1249
+ };
1250
+ if (keyPath !== undefined) {
1251
+ binding.keyState = createRepeatKeyState(keyPath);
1252
+ }
1253
+ instance.repeats.push(binding);
1254
+ pendingSlots[pendingSlotCount++] = {
1042
1255
  parent,
1043
- ref: parent.childNodes[beforeIndex] || null,
1044
- collection,
1045
- itemVar,
1046
- blockIndex,
1047
- keyPath,
1256
+ before: parent.childNodes[beforeIndex] || null,
1257
+ order,
1258
+ node: anchor,
1048
1259
  };
1049
- repRefCount += 1;
1260
+ if (order > 0)
1261
+ needsSlotOrdering = true;
1050
1262
  }
1051
1263
  }
1052
1264
  this.$wireAttrs(instance, meta, scope, (i) => elements[i] ?? null);
1053
1265
  this.$finalize(instance, root, meta, (_r, i) => elements[i] ?? null, scope);
1054
- for (let i = 0; i < textRefCount; i++) {
1055
- const t = textRefs[i];
1056
- const anchor = document.createComment('');
1057
- t.parent.insertBefore(anchor, t.ref);
1058
- if (t.raw) {
1059
- const container = document.createElement('span');
1060
- t.parent.insertBefore(container, anchor);
1061
- const textNode = document.createTextNode('');
1062
- instance.texts.push({ node: textNode, parts: t.parts, scope, raw: true, rawParent: container });
1063
- }
1064
- else {
1065
- const textNode = document.createTextNode('');
1066
- t.parent.insertBefore(textNode, anchor);
1067
- instance.texts.push({ node: textNode, parts: t.parts, scope });
1068
- }
1069
- }
1070
- for (let i = 0; i < condRefCount; i++) {
1071
- const c = condRefs[i];
1072
- const anchor = document.createComment('');
1073
- c.parent.insertBefore(anchor, c.ref);
1074
- instance.conds.push({
1075
- condition: c.condition,
1076
- blockIndex: c.blockIndex,
1077
- anchor,
1078
- scope,
1079
- owner: instance,
1080
- instance: null,
1081
- });
1082
- }
1083
- for (let i = 0; i < repRefCount; i++) {
1084
- const r = repRefs[i];
1085
- const anchor = document.createComment('');
1086
- r.parent.insertBefore(anchor, r.ref);
1087
- const binding = {
1088
- markerId: i, collection: r.collection, itemVar: r.itemVar, blockIndex: r.blockIndex,
1089
- container: r.parent, start: anchor, end: null,
1090
- scope, owner: instance, instances: [],
1091
- };
1092
- if (r.keyPath !== undefined) {
1093
- binding.keyState = createRepeatKeyState(r.keyPath);
1094
- }
1095
- instance.repeats.push(binding);
1096
- }
1266
+ insertPendingSlots(pendingSlots, pendingSlotCount, needsSlotOrdering);
1097
1267
  for (let i = 0; i < instance.conds.length; i++)
1098
1268
  this.$toggleCond(instance.conds[i]);
1099
1269
  return instance;
@@ -1103,18 +1273,31 @@ export class TemplateElement extends HTMLElement {
1103
1273
  scope,
1104
1274
  container: (pathStart > 0 ? ssrRoot.parentNode : ssrRoot),
1105
1275
  nodes: pathStart > 0 ? [ssrRoot] : childNodesArray(ssrRoot),
1106
- texts: [], attrs: [], conds: [], repeats: [],
1276
+ texts: bindingArray(meta.tx?.length ?? 0),
1277
+ attrs: bindingArray(meta.a?.length ?? 0),
1278
+ conds: bindingArray(meta.c?.length ?? 0),
1279
+ repeats: bindingArray(meta.r?.length ?? 0),
1107
1280
  };
1108
1281
  const staleMarkers = [];
1109
- const ssrIndex = buildSSRIndex(tplDom, ssrRoot, meta.c !== undefined || meta.r !== undefined, pathStart > 0);
1282
+ let rawTextCount = 0;
1283
+ if (meta.tx) {
1284
+ for (let i = 0; i < meta.tx.length; i++) {
1285
+ if (meta.tx[i][2] === 1)
1286
+ rawTextCount++;
1287
+ }
1288
+ }
1289
+ const ssrIndex = buildSSRIndex(tplDom, ssrRoot, meta.c !== undefined || meta.r !== undefined || rawTextCount !== 0, pathStart > 0);
1110
1290
  const ssrElements = ssrIndex.elements;
1111
1291
  const tplElements = getTemplateElements(tplDom);
1292
+ const textMarkerBoundaries = getTextMarkerBoundaries(meta);
1112
1293
  if (meta.tx) {
1294
+ const rawRanges = ssrIndex.raws.length === rawTextCount ? ssrIndex.raws : null;
1295
+ let rawIndex = 0;
1113
1296
  for (let i = 0; i < meta.tx.length; i++) {
1114
1297
  const entry = meta.tx[i];
1115
1298
  const [slot, parts] = entry;
1116
1299
  const raw = entry[2] === 1;
1117
- const [parentIndex, beforeIndex] = slot;
1300
+ const [parentIndex] = slot;
1118
1301
  const ssrParent = ssrElements[parentIndex];
1119
1302
  if (!ssrParent)
1120
1303
  continue;
@@ -1122,19 +1305,37 @@ export class TemplateElement extends HTMLElement {
1122
1305
  if (!tplParent)
1123
1306
  continue;
1124
1307
  if (raw) {
1125
- const rawParent = ssrParent;
1126
- const textNode = document.createTextNode('');
1127
- instance.texts.push({ node: textNode, parts, scope, raw: true, rawParent });
1308
+ const rawRange = rawRanges?.[rawIndex++];
1309
+ if (!rawRange)
1310
+ continue;
1311
+ const binding = {
1312
+ node: rawRange[0],
1313
+ parts,
1314
+ scope,
1315
+ raw: true,
1316
+ rawEnd: rawRange[1],
1317
+ rawOwner: instance,
1318
+ };
1319
+ if ((!scope || this.$scopeIsKnown(scope))
1320
+ && this.$textStateIsKnown(binding)) {
1321
+ binding.rawValue = this.$resolveParts(parts, scope);
1322
+ }
1323
+ instance.texts.push(binding);
1128
1324
  }
1129
1325
  else {
1130
- let textNode = this.$findSSRText(ssrParent, tplParent, beforeIndex);
1131
- if (!textNode) {
1326
+ const insertRef = this.$findSSRSlotRef(ssrParent, tplParent, meta, ssrIndex, slot, textMarkerBoundaries[i] ?? 0);
1327
+ let previous = insertRef ? insertRef.previousSibling : ssrParent.lastChild;
1328
+ if (previous?.nodeType === 8
1329
+ && previous.data === ''
1330
+ && previous.previousSibling?.nodeType === 3) {
1331
+ previous = previous.previousSibling;
1332
+ }
1333
+ let textNode = previous?.nodeType === 3 ? previous : null;
1334
+ if (textNode === null) {
1132
1335
  textNode = document.createTextNode('');
1133
- const insertRef = this.$findSSRSlotRef(ssrParent, tplParent, beforeIndex);
1134
1336
  ssrParent.insertBefore(textNode, insertRef);
1135
1337
  }
1136
- if (textNode)
1137
- instance.texts.push({ node: textNode, parts, scope });
1338
+ instance.texts.push({ node: textNode, parts, scope });
1138
1339
  }
1139
1340
  }
1140
1341
  }
@@ -1161,7 +1362,7 @@ export class TemplateElement extends HTMLElement {
1161
1362
  if (marker && blockMeta) {
1162
1363
  condInstance = this.$hydrateCondContent(condAnchor, blockMeta, scope);
1163
1364
  if (condInstance)
1164
- condInstance.parent = instance;
1365
+ this.$setInstanceParent(condInstance, instance);
1165
1366
  }
1166
1367
  if (marker) {
1167
1368
  const lastNode = condInstance ? condInstance.nodes[condInstance.nodes.length - 1] : condAnchor;
@@ -1170,9 +1371,14 @@ export class TemplateElement extends HTMLElement {
1170
1371
  staleMarkers.push(endMarker);
1171
1372
  }
1172
1373
  }
1374
+ let liveAnchor = condAnchor;
1375
+ if (condInstance && condInstance.nodes.length > 0) {
1376
+ staleMarkers.push(condAnchor);
1377
+ liveAnchor = null;
1378
+ }
1173
1379
  instance.conds.push({
1174
1380
  condition: condition, blockIndex,
1175
- anchor: condAnchor,
1381
+ anchor: liveAnchor,
1176
1382
  scope, owner: instance, instance: condInstance,
1177
1383
  });
1178
1384
  }
@@ -1186,7 +1392,10 @@ export class TemplateElement extends HTMLElement {
1186
1392
  const ssrParent = (marker ? marker.parentNode : ssrElements[parentIndex]) ?? ssrRoot;
1187
1393
  const blockMeta = this.$block(blockIndex);
1188
1394
  const blockTplDom = blockMeta ? getTemplateDom(blockMeta) : null;
1189
- const rootTag = blockMeta && blockTplDom?.childNodes.length === 1 && blockTplDom.children.length === 1
1395
+ const rootTag = blockMeta
1396
+ && blockTplDom?.childNodes.length === 1
1397
+ && blockTplDom.children.length === 1
1398
+ && !this.$hasRootStructuralSlot(blockMeta)
1190
1399
  ? this.$rootTag(blockMeta)
1191
1400
  : null;
1192
1401
  let anchor;
@@ -1229,7 +1438,7 @@ export class TemplateElement extends HTMLElement {
1229
1438
  const itemEl = nextElement(itemMarkers[j]);
1230
1439
  if (itemEl) {
1231
1440
  const childInstance = this.$hydrate(itemEl, blockMeta, blockTplDom, itemScope, 1);
1232
- childInstance.parent = instance;
1441
+ this.$setInstanceParent(childInstance, instance);
1233
1442
  repeatInsts.push(childInstance);
1234
1443
  }
1235
1444
  }
@@ -1244,7 +1453,6 @@ export class TemplateElement extends HTMLElement {
1244
1453
  cursor = next;
1245
1454
  }
1246
1455
  const inst = this.$hydrate(wrapper, blockMeta, blockTplDom, itemScope);
1247
- inst.parent = instance;
1248
1456
  inst.nodes = childNodesArray(wrapper);
1249
1457
  let afterNode = itemMarkers[j];
1250
1458
  for (let nodeIndex = 0; nodeIndex < inst.nodes.length; nodeIndex++) {
@@ -1255,6 +1463,7 @@ export class TemplateElement extends HTMLElement {
1255
1463
  if (itemParent) {
1256
1464
  this.$replaceInstanceContainer(inst, wrapper, itemParent);
1257
1465
  }
1466
+ this.$setInstanceParent(inst, instance);
1258
1467
  repeatInsts.push(inst);
1259
1468
  }
1260
1469
  }
@@ -1321,6 +1530,12 @@ export class TemplateElement extends HTMLElement {
1321
1530
  return true;
1322
1531
  }
1323
1532
  }
1533
+ if (meta.tx) {
1534
+ for (let i = 0; i < meta.tx.length; i++) {
1535
+ if (meta.tx[i][2] === 1 && meta.tx[i][0][0] === 0)
1536
+ return true;
1537
+ }
1538
+ }
1324
1539
  return false;
1325
1540
  }
1326
1541
  $hydrateCondContent(condAnchor, blockMeta, scope) {
@@ -1351,27 +1566,25 @@ export class TemplateElement extends HTMLElement {
1351
1566
  this.$replaceInstanceContainer(inst, wrapper, container);
1352
1567
  return inst;
1353
1568
  }
1354
- $findSSRText(ssrParent, tplParent, beforeIndex) {
1355
- const ordinals = getTplOrdinals(tplParent);
1356
- let textOrd = 0;
1357
- for (let k = 0; k < beforeIndex; k++) {
1358
- const entry = ordinals.get(k);
1359
- if (entry && entry[0] === 3)
1360
- textOrd++;
1361
- }
1362
- const found = findByOrdinal(ssrParent, 3, textOrd);
1363
- if (found)
1364
- return found;
1365
- let child = ssrParent.firstChild;
1366
- while (child) {
1367
- if (child.nodeType === 3 && child.data && child.data.trim()) {
1368
- return child;
1369
- }
1370
- child = child.nextSibling;
1371
- }
1372
- return null;
1373
- }
1374
- $findSSRSlotRef(ssrParent, tplParent, beforeIndex) {
1569
+ $findSSRSlotRef(ssrParent, tplParent, meta, ssrIndex, slot, markerBoundary) {
1570
+ const [, beforeIndex] = slot;
1571
+ let marker = null;
1572
+ if (markerBoundary >= RAW_MARKER_BOUNDARY_BASE) {
1573
+ marker =
1574
+ ssrIndex.raws[markerBoundary - RAW_MARKER_BOUNDARY_BASE - 1]?.[0] ?? null;
1575
+ }
1576
+ else if (markerBoundary > 0
1577
+ && meta.c
1578
+ && ssrIndex.conds.length === meta.c.length) {
1579
+ marker = ssrIndex.conds[markerBoundary - 1];
1580
+ }
1581
+ else if (markerBoundary < 0
1582
+ && meta.r
1583
+ && ssrIndex.repeats.length === meta.r.length) {
1584
+ marker = ssrIndex.repeats[-markerBoundary - 1];
1585
+ }
1586
+ if (marker?.parentNode === ssrParent)
1587
+ return marker;
1375
1588
  const ordinals = getTplOrdinals(tplParent);
1376
1589
  const children = tplParent.childNodes;
1377
1590
  for (let i = beforeIndex; i < children.length; i++) {
@@ -1582,6 +1795,29 @@ export class TemplateElement extends HTMLElement {
1582
1795
  return this.$partsAreKnown(binding.parts, binding.scope);
1583
1796
  return !binding.path || this.$hasStateRoot(binding.path, binding.scope);
1584
1797
  }
1798
+ $invalidateRawValues(root) {
1799
+ const stack = [root];
1800
+ while (stack.length > 0) {
1801
+ const instance = stack.pop();
1802
+ if (!instance)
1803
+ continue;
1804
+ for (let i = 0; i < instance.texts.length; i++) {
1805
+ const binding = instance.texts[i];
1806
+ if (binding.raw)
1807
+ binding.rawValue = undefined;
1808
+ }
1809
+ for (let i = 0; i < instance.conds.length; i++) {
1810
+ const child = instance.conds[i].instance;
1811
+ if (child)
1812
+ stack.push(child);
1813
+ }
1814
+ for (let i = 0; i < instance.repeats.length; i++) {
1815
+ const children = instance.repeats[i].instances;
1816
+ for (let j = 0; j < children.length; j++)
1817
+ stack.push(children[j]);
1818
+ }
1819
+ }
1820
+ }
1585
1821
  $attrStateIsKnown(binding) {
1586
1822
  if (binding.path && !this.$hasStateRoot(binding.path, binding.scope))
1587
1823
  return false;
@@ -1629,9 +1865,39 @@ export class TemplateElement extends HTMLElement {
1629
1865
  else {
1630
1866
  return;
1631
1867
  }
1632
- if (b.raw && b.rawParent) {
1633
- if (b.rawParent.innerHTML !== val)
1634
- b.rawParent.innerHTML = val;
1868
+ if (b.raw && b.rawEnd) {
1869
+ if (b.rawValue === val)
1870
+ return;
1871
+ const range = document.createRange();
1872
+ range.setStartAfter(b.node);
1873
+ range.setEndBefore(b.rawEnd);
1874
+ range.deleteContents();
1875
+ let rawNodes;
1876
+ const rawOwner = b.rawOwner;
1877
+ if (val !== '') {
1878
+ const fragment = range.createContextualFragment(val);
1879
+ if (rawOwner && b.node.parentNode === rawOwner.container) {
1880
+ rawNodes = childNodesArray(fragment);
1881
+ }
1882
+ range.insertNode(fragment);
1883
+ }
1884
+ else if (rawOwner && b.node.parentNode === rawOwner.container) {
1885
+ rawNodes = [];
1886
+ }
1887
+ if (rawNodes && rawOwner) {
1888
+ const container = b.node.parentNode;
1889
+ let owner = rawOwner;
1890
+ while (owner && owner.container === container) {
1891
+ const ownerNodes = owner.nodes;
1892
+ const startIndex = ownerNodes.indexOf(b.node);
1893
+ const endIndex = ownerNodes.indexOf(b.rawEnd, startIndex + 1);
1894
+ if (startIndex >= 0 && endIndex > startIndex) {
1895
+ ownerNodes.splice(startIndex + 1, endIndex - startIndex - 1, ...rawNodes);
1896
+ }
1897
+ owner = owner.parent;
1898
+ }
1899
+ }
1900
+ b.rawValue = val;
1635
1901
  }
1636
1902
  else {
1637
1903
  if (b.node.data !== val)
@@ -1647,7 +1913,7 @@ export class TemplateElement extends HTMLElement {
1647
1913
  break;
1648
1914
  }
1649
1915
  case ATTR_KIND_BOOLEAN: {
1650
- const show = b.condition[0](this.$resolver, b.scope);
1916
+ const show = b.condition[0](this.$conditionResolver(), b.scope);
1651
1917
  if (show)
1652
1918
  el.setAttribute(b.name, '');
1653
1919
  else
@@ -1684,28 +1950,68 @@ export class TemplateElement extends HTMLElement {
1684
1950
  }
1685
1951
  }
1686
1952
  }
1953
+ $swapOwnedRange(owner, current, replacement) {
1954
+ const currentIsRange = Array.isArray(current);
1955
+ const first = currentIsRange ? current[0] : current;
1956
+ if (!first)
1957
+ return;
1958
+ const count = currentIsRange ? current.length : 1;
1959
+ const last = currentIsRange ? current[count - 1] : first;
1960
+ const container = first.parentNode;
1961
+ let instance = owner;
1962
+ while (container && instance && instance.container === container) {
1963
+ const nodes = instance.nodes;
1964
+ const index = nodes.indexOf(first);
1965
+ if (index >= 0 && nodes[index + count - 1] === last) {
1966
+ if (Array.isArray(replacement)) {
1967
+ nodes.splice(index, count, ...replacement);
1968
+ }
1969
+ else {
1970
+ nodes.splice(index, count, replacement);
1971
+ }
1972
+ }
1973
+ instance = instance.parent;
1974
+ }
1975
+ }
1687
1976
  $toggleCond(c) {
1688
- const show = c.condition[0](this.$resolver, c.scope);
1977
+ const show = c.condition[0](this.$conditionResolver(), c.scope);
1689
1978
  if (show) {
1690
- let created = false;
1691
- const container = c.anchor.parentNode;
1692
- if (!c.instance) {
1693
- c.instance = this.$createBlockInstance(c.blockIndex, c.scope, c.owner, container ?? undefined);
1694
- created = c.instance !== null;
1695
- }
1696
- else {
1979
+ if (c.instance) {
1697
1980
  this.$updateInstance(c.instance);
1981
+ return;
1698
1982
  }
1699
- if (c.instance && container) {
1700
- this.$insertInstanceAfter(c.anchor, container, c.instance);
1701
- if (created)
1702
- this.$changeStructure();
1983
+ const anchor = c.anchor;
1984
+ const container = anchor?.parentNode;
1985
+ if (!anchor || !container)
1986
+ return;
1987
+ const instance = this.$createBlockInstance(c.blockIndex, c.scope, c.owner, container);
1988
+ if (!instance)
1989
+ return;
1990
+ c.instance = instance;
1991
+ const nodes = instance.nodes;
1992
+ this.$insertInstanceAfter(anchor, container, instance);
1993
+ if (nodes.length > 0) {
1994
+ this.$swapOwnedRange(c.owner, anchor, nodes);
1995
+ anchor.remove();
1996
+ c.anchor = null;
1703
1997
  }
1998
+ this.$changeStructure();
1704
1999
  }
1705
2000
  else if (c.instance) {
1706
- this.$removeInstance(c.instance);
2001
+ const instance = c.instance;
2002
+ const block = this.$block(c.blockIndex);
2003
+ const compactOwners = !!block && this.$hasRootStructuralSlot(block);
2004
+ const first = instance.nodes[0] ?? null;
2005
+ const container = first?.parentNode;
2006
+ if (first && container) {
2007
+ const anchor = document.createComment('');
2008
+ container.insertBefore(anchor, first);
2009
+ this.$swapOwnedRange(c.owner, instance.nodes, anchor);
2010
+ c.anchor = anchor;
2011
+ }
2012
+ this.$removeInstance(instance);
1707
2013
  c.instance = null;
1708
- this.$changeStructure(c.owner);
2014
+ this.$changeStructure(compactOwners ? c.owner : undefined);
1709
2015
  }
1710
2016
  }
1711
2017
  $resolveValue(path, scope) {
@@ -1723,6 +2029,9 @@ export class TemplateElement extends HTMLElement {
1723
2029
  return this.$resolveComponentRoot(path);
1724
2030
  return dotWalk(this.$resolveComponentRoot(path.substring(0, dot)), path, dot + 1);
1725
2031
  }
2032
+ $conditionResolver() {
2033
+ return this.$resolver ??= (path, scope) => this.$resolveValue(path, scope);
2034
+ }
1726
2035
  $hasStateRoot(path, scope) {
1727
2036
  let frame = scope;
1728
2037
  while (frame) {
@@ -1762,6 +2071,9 @@ export class TemplateElement extends HTMLElement {
1762
2071
  }
1763
2072
  return result;
1764
2073
  }
2074
+ $setInstanceParent(instance, parent) {
2075
+ instance.parent = parent;
2076
+ }
1765
2077
  $block(blockIndex) {
1766
2078
  return this.$meta?.b?.[blockIndex];
1767
2079
  }
@@ -1771,7 +2083,6 @@ export class TemplateElement extends HTMLElement {
1771
2083
  return null;
1772
2084
  const wrapper = this.$createStagingRoot(bm);
1773
2085
  const inst = this.$wire(wrapper, bm, scope);
1774
- inst.parent = parent;
1775
2086
  inst.nodes = childNodesArray(wrapper);
1776
2087
  this.$updateInstance(inst);
1777
2088
  if (inst.repeats.length !== 0 || inst.conds.length !== 0) {
@@ -1781,6 +2092,7 @@ export class TemplateElement extends HTMLElement {
1781
2092
  else if (container) {
1782
2093
  inst.container = container;
1783
2094
  }
2095
+ this.$setInstanceParent(inst, parent);
1784
2096
  return inst;
1785
2097
  }
1786
2098
  $removeInstance(instance) {
@@ -1799,8 +2111,7 @@ export class TemplateElement extends HTMLElement {
1799
2111
  cleanups.length = 0;
1800
2112
  }
1801
2113
  if (removeNodes) {
1802
- for (const node of instance.nodes)
1803
- node.parentNode?.removeChild(node);
2114
+ this.$removeInstanceNodes(instance);
1804
2115
  }
1805
2116
  for (const binding of instance.conds) {
1806
2117
  if (binding.instance)
@@ -1815,6 +2126,11 @@ export class TemplateElement extends HTMLElement {
1815
2126
  this.$clearInstance(instance);
1816
2127
  }
1817
2128
  }
2129
+ $removeInstanceNodes(instance) {
2130
+ const nodes = instance.nodes;
2131
+ for (let i = 0; i < nodes.length; i++)
2132
+ nodes[i].parentNode?.removeChild(nodes[i]);
2133
+ }
1818
2134
  $compactNodeArray(instance) {
1819
2135
  const container = instance.container;
1820
2136
  if (!container)