@microsoft/webui-framework 0.0.21 → 0.0.23

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.
@@ -2,7 +2,7 @@ import { deferTemplateDefinition, getTemplate } from './template.js';
2
2
  import { hydrationStart, hydrationEnd } from './lifecycle.js';
3
3
  import { isStreamingHydrationMode, PENDING_ROOT_CONNECTED, STREAMED_HOST_ATTR, STREAMING_BOUNDARY_ACTIVATE, } from './streaming-mode.js';
4
4
  import { createRepeatKeyState, seedHydratedRepeatKeys, syncRepeat, dotWalk, } from './element/diff.js';
5
- import { collectItemMarkers, nextElement, findByOrdinal, MARKER_COND_START, MARKER_COND_END, MARKER_REPEAT_START, } from './element/markers.js';
5
+ import { collectItemMarkers, nextElement, findByOrdinal, buildSSRIndex, collectTemplateElements, MARKER_COND_START, MARKER_COND_END, } from './element/markers.js';
6
6
  import { injectModuleStyle, } from './element/styles.js';
7
7
  import { ATTR_KIND_BOOLEAN, ATTR_KIND_COMPLEX, ATTR_KIND_TEMPLATE, hasNativeLiveProperty, } from './element/types.js';
8
8
  import { templateHasRoot, templateRootForAttribute } from './template-roots.js';
@@ -11,6 +11,15 @@ const templateCache = new WeakMap();
11
11
  const templateDOMCache = new WeakMap();
12
12
  const rootTagCache = new WeakMap();
13
13
  const tplOrdinalCache = new WeakMap();
14
+ const tplElementCache = new WeakMap();
15
+ function getTemplateElements(tplRoot) {
16
+ let cached = tplElementCache.get(tplRoot);
17
+ if (!cached) {
18
+ cached = collectTemplateElements(tplRoot);
19
+ tplElementCache.set(tplRoot, cached);
20
+ }
21
+ return cached;
22
+ }
14
23
  function getTplOrdinals(tplNode) {
15
24
  let map = tplOrdinalCache.get(tplNode);
16
25
  if (map)
@@ -41,6 +50,7 @@ const ACTIVATION_ACTIVATED = 1;
41
50
  const ACTIVATION_STATIC_HOST_OPT_OUT = 2;
42
51
  const ACTIVATION_MISSING_TEMPLATE = 3;
43
52
  const templateMetaByCtor = new WeakMap();
53
+ const pendingAncestorDescendants = new WeakMap();
44
54
  function childNodesArray(parent) {
45
55
  const children = parent.childNodes;
46
56
  const len = children.length;
@@ -115,7 +125,7 @@ export class TemplateElement extends HTMLElement {
115
125
  this.$root = null;
116
126
  this.$ready = false;
117
127
  this.$hydrated = false;
118
- this.$hydratedCallbackCalled = false;
128
+ this.$hasMounted = false;
119
129
  this.$deferredSSR = false;
120
130
  this.$activatingDeferredSSR = false;
121
131
  this.$hasUnknownScopes = false;
@@ -125,13 +135,16 @@ export class TemplateElement extends HTMLElement {
125
135
  this.$preReadyWrites = null;
126
136
  this.$resolver = (p, s) => this.$resolveValue(p, s);
127
137
  }
138
+ static { this.$ancestorReleaseQueue = []; }
139
+ static { this.$ancestorReleaseIndex = 0; }
140
+ static { this.$ancestorReleaseActive = false; }
128
141
  [WEBUI_SET_STATE_KEY](key, value) {
129
142
  const wasDeferred = this.$deferredSSR;
130
143
  this.$beforeExternalStateWrite();
131
144
  const owned = this.$setStateKey(key, value);
145
+ if (owned && wasDeferred)
146
+ this.$recordDeferredWrite(key);
132
147
  this.$afterExternalStateWrite(owned);
133
- if (owned && wasDeferred && !this.$deferredSSR)
134
- this.$update(key);
135
148
  return owned;
136
149
  }
137
150
  [STREAMING_BOUNDARY_ACTIVATE](state) {
@@ -148,14 +161,35 @@ export class TemplateElement extends HTMLElement {
148
161
  this.$meta = meta;
149
162
  if (!this.$shouldActivateOnBoundaryCommit())
150
163
  return ACTIVATION_STATIC_HOST_OPT_OUT;
151
- this.$activateDeferredSSR(state);
164
+ const ancestor = this.$nearestHydrationBarrier();
165
+ if (ancestor) {
166
+ this.$deferredByAncestor = true;
167
+ this.$ancestorBoundaryState = state;
168
+ this.$hasAncestorBoundaryState = true;
169
+ this.$registerWithHydrationBarrier(ancestor);
170
+ return ACTIVATION_ACTIVATED;
171
+ }
172
+ this.$activatingDeferredSSR = true;
173
+ try {
174
+ this.$activateDeferredSSR(state);
175
+ }
176
+ finally {
177
+ this.$activatingDeferredSSR = false;
178
+ }
152
179
  return ACTIVATION_ACTIVATED;
153
180
  }
154
181
  [STREAMING_BOUNDARY_ABANDON]() {
182
+ this.$detachDeferredAncestor();
183
+ this.$abandonDeferredDescendants();
155
184
  this.$deferredSSR = false;
185
+ this.$deferredByAncestor = undefined;
186
+ this.$ancestorBoundaryState = undefined;
187
+ this.$hasAncestorBoundaryState = undefined;
156
188
  this.$activatingDeferredSSR = false;
157
189
  this.$ready = false;
158
190
  this.$preReadyWrites = null;
191
+ if (this.$deferredWrites)
192
+ this.$deferredWrites = undefined;
159
193
  }
160
194
  static define(tagName) {
161
195
  const ctor = this;
@@ -170,8 +204,20 @@ export class TemplateElement extends HTMLElement {
170
204
  }
171
205
  connectedCallback() {
172
206
  const tag = this.tagName.toLowerCase();
207
+ this.$adoptPendingDescendants();
173
208
  if (this.$deferredSSR) {
174
209
  this.$ready = true;
210
+ if (this.$deferredByAncestor) {
211
+ const ancestor = this.$nearestHydrationBarrier();
212
+ if (ancestor) {
213
+ this.$registerWithHydrationBarrier(ancestor);
214
+ return;
215
+ }
216
+ this.$detachDeferredAncestor();
217
+ this.$releaseAncestorBarrier();
218
+ return;
219
+ }
220
+ this.$didDeferSSRHydration();
175
221
  return;
176
222
  }
177
223
  if (this.$hydrated && this.$root) {
@@ -185,12 +231,15 @@ export class TemplateElement extends HTMLElement {
185
231
  }
186
232
  return;
187
233
  }
188
- if (isStreamingHydrationMode() && this.hasAttribute(STREAMED_HOST_ATTR)) {
234
+ if (!this.$hasMounted &&
235
+ isStreamingHydrationMode() &&
236
+ this.hasAttribute(STREAMED_HOST_ATTR)) {
189
237
  this.$deferredSSR = true;
190
238
  this.$ready = true;
191
239
  const resume = this[PENDING_ROOT_CONNECTED];
192
240
  if (typeof resume === 'function')
193
241
  resume.call(this);
242
+ this.$didDeferSSRHydration();
194
243
  return;
195
244
  }
196
245
  const meta = this.$templateMeta();
@@ -200,17 +249,35 @@ export class TemplateElement extends HTMLElement {
200
249
  return;
201
250
  }
202
251
  this.$meta = meta;
203
- this.$mount(meta, false);
252
+ if (this.$hasMounted) {
253
+ this.$remount(meta);
254
+ }
255
+ else {
256
+ this.$mount(meta, false);
257
+ }
204
258
  }
205
- $mount(meta, forceSSR, ssrState) {
259
+ $remount(meta) {
260
+ this.$mount(meta, false, undefined, false, true);
261
+ }
262
+ $mount(meta, forceSSR, ssrState, hasBoundaryState = false, reconnecting = false) {
206
263
  if (this.$hydrated)
207
264
  return;
208
265
  const hasShadow = !!this.shadowRoot;
209
266
  const wantShadow = hasShadow || !!meta.sd;
267
+ const remountStructuralTemplate = reconnecting &&
268
+ ((meta.c?.length ?? 0) !== 0 || (meta.r?.length ?? 0) !== 0);
210
269
  let root;
211
270
  let isSSR;
212
271
  let clientRoot = null;
213
- if (hasShadow) {
272
+ if (remountStructuralTemplate) {
273
+ const renderRoot = wantShadow
274
+ ? this.shadowRoot ?? this.attachShadow({ mode: 'open' })
275
+ : this;
276
+ renderRoot.replaceChildren();
277
+ root = renderRoot;
278
+ isSSR = false;
279
+ }
280
+ else if (hasShadow) {
214
281
  root = this.shadowRoot;
215
282
  isSSR = true;
216
283
  }
@@ -226,19 +293,36 @@ export class TemplateElement extends HTMLElement {
226
293
  root = this;
227
294
  isSSR = false;
228
295
  }
229
- if (isSSR && !forceSSR && this.$shouldDeferSSRHydration()) {
230
- this.$meta = meta;
231
- this.$deferredSSR = true;
232
- this.$ready = true;
233
- return;
296
+ if (isSSR &&
297
+ !forceSSR &&
298
+ !reconnecting) {
299
+ const ancestor = this.$nearestHydrationBarrier();
300
+ if (ancestor) {
301
+ this.$meta = meta;
302
+ this.$deferredSSR = true;
303
+ this.$deferredByAncestor = true;
304
+ this.$ready = true;
305
+ this.$primeSSRStateForDeferral();
306
+ this.$registerWithHydrationBarrier(ancestor);
307
+ return;
308
+ }
309
+ if (!this.$shouldDeferSSRHydration(meta)) {
310
+ }
311
+ else {
312
+ this.$meta = meta;
313
+ this.$deferredSSR = true;
314
+ this.$ready = true;
315
+ this.$didDeferSSRHydration();
316
+ return;
317
+ }
234
318
  }
235
319
  hydrationStart();
236
320
  try {
237
321
  if (meta.sa)
238
322
  injectModuleStyle(meta.sa, this.shadowRoot);
239
323
  if (isSSR) {
240
- if (this.$shouldApplySSRBootstrapState()) {
241
- this.$applySSRState(forceSSR ? ssrState : window.__webui?.state);
324
+ if (!reconnecting && this.$shouldApplySSRBootstrapState()) {
325
+ this.$applySSRState(hasBoundaryState ? ssrState : window.__webui?.state);
242
326
  }
243
327
  this.$root = this.$hydrate(root, meta, getTemplateDom(meta));
244
328
  }
@@ -250,6 +334,11 @@ export class TemplateElement extends HTMLElement {
250
334
  this.$hydrated = true;
251
335
  this.$ready = true;
252
336
  this.$syncAuthoredAttributes();
337
+ if (isSSR && this.$deferredWrites)
338
+ this.$replayDeferredWrites();
339
+ if (isSSR && reconnecting) {
340
+ this.$updateBindings(this.$root.texts, this.$root.attrs, this.$root.conds, this.$root.repeats, true);
341
+ }
253
342
  if (isSSR && DEV)
254
343
  this.$checkHydrationMismatch();
255
344
  else
@@ -263,13 +352,14 @@ export class TemplateElement extends HTMLElement {
263
352
  this.$appendStagedChildren(root, clientRoot);
264
353
  this.$root.container = root;
265
354
  }
266
- this.$notifyHydrated();
355
+ this.$finishHydration();
267
356
  }
268
357
  finally {
269
358
  hydrationEnd();
270
359
  }
271
360
  }
272
361
  disconnectedCallback() {
362
+ this.$detachDeferredAncestor();
273
363
  if (this.$root) {
274
364
  queueMicrotask(() => {
275
365
  if (!this.isConnected)
@@ -279,9 +369,16 @@ export class TemplateElement extends HTMLElement {
279
369
  }
280
370
  $destroy() {
281
371
  if (!this.$root) {
372
+ this.$detachDeferredAncestor();
373
+ this.$abandonDeferredDescendants();
282
374
  this.$deferredSSR = false;
375
+ this.$deferredByAncestor = undefined;
376
+ this.$ancestorBoundaryState = undefined;
377
+ this.$hasAncestorBoundaryState = undefined;
283
378
  this.$ready = false;
284
379
  this.$hasUnknownScopes = false;
380
+ if (this.$deferredWrites)
381
+ this.$deferredWrites = undefined;
285
382
  return;
286
383
  }
287
384
  this.$disposeInstance(this.$root, false);
@@ -291,6 +388,7 @@ export class TemplateElement extends HTMLElement {
291
388
  this.$dirtyPaths = null;
292
389
  this.$pendingFlush = false;
293
390
  this.$preReadyWrites = null;
391
+ this.$hydrated = false;
294
392
  this.$ready = false;
295
393
  this.$hasUnknownScopes = false;
296
394
  }
@@ -324,11 +422,11 @@ export class TemplateElement extends HTMLElement {
324
422
  const property = this.$templateRootForAttribute(name);
325
423
  let changed = false;
326
424
  if (property && this.$usesTemplateState(property)) {
425
+ if (wasDeferred)
426
+ this.$recordDeferredWrite(property);
327
427
  changed = this.$setTemplateState(property, newValue);
328
428
  }
329
429
  this.$afterExternalStateWrite(changed);
330
- if (changed && property && wasDeferred && !this.$deferredSSR)
331
- this.$update(property);
332
430
  }
333
431
  setState(state) {
334
432
  const wasDeferred = this.$deferredSSR;
@@ -337,13 +435,12 @@ export class TemplateElement extends HTMLElement {
337
435
  let owned = false;
338
436
  for (let i = 0; i < keys.length; i++) {
339
437
  const key = keys[i];
340
- owned = this.$setStateKey(key, state[key]) || owned;
438
+ const keyOwned = this.$setStateKey(key, state[key]);
439
+ if (keyOwned && wasDeferred)
440
+ this.$recordDeferredWrite(key);
441
+ owned = keyOwned || owned;
341
442
  }
342
443
  this.$afterExternalStateWrite(owned);
343
- if (owned && wasDeferred && !this.$deferredSSR) {
344
- for (let i = 0; i < keys.length; i++)
345
- this.$update(keys[i]);
346
- }
347
444
  this.$flushUpdates();
348
445
  }
349
446
  $observableNames() {
@@ -353,27 +450,220 @@ export class TemplateElement extends HTMLElement {
353
450
  }
354
451
  $afterExternalStateWrite(_applied) {
355
452
  }
356
- $shouldDeferSSRHydration() {
453
+ $shouldDeferSSRHydration(_meta) {
357
454
  return false;
358
455
  }
456
+ $didDeferSSRHydration() {
457
+ }
458
+ $didDeferStreamedSSRHydration(_state) {
459
+ this.$didDeferSSRHydration();
460
+ }
461
+ $currentTemplateMetadata() {
462
+ return this.$meta ?? this.$templateMeta();
463
+ }
464
+ $nearestHydrationBarrier() {
465
+ let current = this;
466
+ while (true) {
467
+ let parent = current.assignedSlot ?? current.parentElement;
468
+ if (!parent) {
469
+ const getRootNode = current.getRootNode;
470
+ const root = typeof getRootNode === 'function'
471
+ ? getRootNode.call(current)
472
+ : null;
473
+ parent = typeof ShadowRoot !== 'undefined' &&
474
+ root instanceof ShadowRoot
475
+ ? root.host
476
+ : null;
477
+ }
478
+ if (!parent)
479
+ return undefined;
480
+ if (parent instanceof TemplateElement) {
481
+ const parentMeta = parent.$meta ?? parent.$templateMeta();
482
+ if (parentMeta?.th) {
483
+ current = parent;
484
+ continue;
485
+ }
486
+ if (parent.$deferredSSR || !parent.$hydrated)
487
+ return parent;
488
+ }
489
+ else {
490
+ const parentMeta = getTemplate(parent.tagName.toLowerCase());
491
+ if (parentMeta && !parentMeta.th)
492
+ return parent;
493
+ }
494
+ current = parent;
495
+ }
496
+ }
497
+ $registerWithHydrationBarrier(ancestor) {
498
+ if (ancestor instanceof TemplateElement) {
499
+ ancestor.$registerDeferredDescendant(this);
500
+ return;
501
+ }
502
+ this.$detachDeferredAncestor();
503
+ this.$pendingAncestor = ancestor;
504
+ const descendants = pendingAncestorDescendants.get(ancestor);
505
+ if (descendants) {
506
+ descendants.push(this);
507
+ }
508
+ else {
509
+ pendingAncestorDescendants.set(ancestor, [this]);
510
+ }
511
+ }
512
+ $adoptPendingDescendants() {
513
+ const descendants = pendingAncestorDescendants.get(this);
514
+ if (!descendants)
515
+ return;
516
+ pendingAncestorDescendants.delete(this);
517
+ for (let i = 0; i < descendants.length; i++) {
518
+ const descendant = descendants[i];
519
+ if (descendant.$pendingAncestor !== this)
520
+ continue;
521
+ descendant.$pendingAncestor = undefined;
522
+ if (descendant.isConnected)
523
+ this.$registerDeferredDescendant(descendant);
524
+ }
525
+ }
526
+ $registerDeferredDescendant(descendant) {
527
+ if (descendant.$deferredAncestor === this)
528
+ return;
529
+ descendant.$detachDeferredAncestor();
530
+ descendant.$deferredAncestor = this;
531
+ (this.$deferredDescendants ??= []).push(descendant);
532
+ }
533
+ $detachDeferredAncestor() {
534
+ const pendingAncestor = this.$pendingAncestor;
535
+ if (pendingAncestor) {
536
+ this.$pendingAncestor = undefined;
537
+ const pending = pendingAncestorDescendants.get(pendingAncestor);
538
+ if (pending) {
539
+ const pendingIndex = pending.indexOf(this);
540
+ if (pendingIndex >= 0)
541
+ pending.splice(pendingIndex, 1);
542
+ if (pending.length === 0) {
543
+ pendingAncestorDescendants.delete(pendingAncestor);
544
+ }
545
+ }
546
+ }
547
+ const ancestor = this.$deferredAncestor;
548
+ if (!ancestor)
549
+ return;
550
+ this.$deferredAncestor = undefined;
551
+ const descendants = ancestor.$deferredDescendants;
552
+ if (!descendants)
553
+ return;
554
+ const index = descendants.indexOf(this);
555
+ if (index >= 0)
556
+ descendants.splice(index, 1);
557
+ if (descendants.length === 0)
558
+ ancestor.$deferredDescendants = undefined;
559
+ }
560
+ $releaseDeferredDescendants() {
561
+ const descendants = this.$deferredDescendants;
562
+ if (!descendants)
563
+ return;
564
+ this.$deferredDescendants = undefined;
565
+ const queue = TemplateElement.$ancestorReleaseQueue;
566
+ for (let i = 0; i < descendants.length; i++) {
567
+ const descendant = descendants[i];
568
+ if (descendant.$deferredAncestor !== this)
569
+ continue;
570
+ descendant.$deferredAncestor = undefined;
571
+ queue.push(descendant);
572
+ }
573
+ if (TemplateElement.$ancestorReleaseActive)
574
+ return;
575
+ TemplateElement.$ancestorReleaseActive = true;
576
+ let errors;
577
+ try {
578
+ while (TemplateElement.$ancestorReleaseIndex < queue.length) {
579
+ const descendant = queue[TemplateElement.$ancestorReleaseIndex];
580
+ TemplateElement.$ancestorReleaseIndex++;
581
+ try {
582
+ descendant.$releaseAncestorBarrier();
583
+ }
584
+ catch (error) {
585
+ (errors ??= []).push(error);
586
+ }
587
+ }
588
+ }
589
+ finally {
590
+ queue.length = 0;
591
+ TemplateElement.$ancestorReleaseIndex = 0;
592
+ TemplateElement.$ancestorReleaseActive = false;
593
+ }
594
+ if (errors) {
595
+ if (errors.length === 1)
596
+ throw errors[0];
597
+ throw new AggregateError(errors, 'multiple deferred descendants failed to hydrate');
598
+ }
599
+ }
600
+ $releaseAncestorBarrier() {
601
+ if (!this.$deferredByAncestor || !this.isConnected)
602
+ return;
603
+ this.$deferredByAncestor = undefined;
604
+ const hasBoundaryState = this.$hasAncestorBoundaryState === true;
605
+ const boundaryState = this.$ancestorBoundaryState;
606
+ this.$hasAncestorBoundaryState = undefined;
607
+ this.$ancestorBoundaryState = undefined;
608
+ const meta = this.$meta;
609
+ if (meta && this.$shouldDeferSSRHydration(meta)) {
610
+ if (hasBoundaryState) {
611
+ this.$didDeferStreamedSSRHydration(boundaryState);
612
+ }
613
+ else {
614
+ this.$didDeferSSRHydration();
615
+ }
616
+ return;
617
+ }
618
+ if (hasBoundaryState) {
619
+ this.$activateDeferredSSRFromBoundary(boundaryState);
620
+ }
621
+ else {
622
+ this.$activateDeferredSSR();
623
+ }
624
+ }
625
+ $abandonDeferredDescendants() {
626
+ const descendants = this.$deferredDescendants;
627
+ if (!descendants)
628
+ return;
629
+ this.$deferredDescendants = undefined;
630
+ for (let i = 0; i < descendants.length; i++) {
631
+ if (descendants[i].$deferredAncestor === this) {
632
+ descendants[i].$deferredAncestor = undefined;
633
+ }
634
+ }
635
+ }
636
+ $primeSSRStateForDeferral() {
637
+ if (this.$shouldApplySSRBootstrapState()) {
638
+ this.$applySSRState(window.__webui?.state);
639
+ }
640
+ }
359
641
  $shouldActivateOnBoundaryCommit() {
360
642
  return true;
361
643
  }
362
644
  $activateDeferredSSR(state) {
645
+ this.$activateDeferredSSRWithState(state, this.$activatingDeferredSSR);
646
+ }
647
+ $activateDeferredSSRFromBoundary(state) {
648
+ this.$activateDeferredSSRWithState(state, true);
649
+ }
650
+ $activateDeferredSSRWithState(state, hasBoundaryState) {
363
651
  if (!this.$deferredSSR)
364
652
  return;
365
653
  const meta = this.$meta;
366
654
  if (!meta)
367
655
  return;
368
656
  this.$deferredSSR = false;
657
+ this.$guardUnknownState = true;
369
658
  this.$ready = false;
370
659
  this.$preReadyWrites = null;
660
+ const wasActivating = this.$activatingDeferredSSR;
371
661
  this.$activatingDeferredSSR = true;
372
662
  try {
373
- this.$mount(meta, true, state);
663
+ this.$mount(meta, true, state, hasBoundaryState);
374
664
  }
375
665
  finally {
376
- this.$activatingDeferredSSR = false;
666
+ this.$activatingDeferredSSR = wasActivating;
377
667
  }
378
668
  }
379
669
  $shouldApplySSRBootstrapState() {
@@ -387,11 +677,33 @@ export class TemplateElement extends HTMLElement {
387
677
  hydratedCallback() {
388
678
  }
389
679
  $notifyHydrated() {
390
- if (this.$hydratedCallbackCalled)
680
+ if (this.$hasMounted)
391
681
  return;
392
- this.$hydratedCallbackCalled = true;
682
+ this.$hasMounted = true;
393
683
  this.hydratedCallback();
394
684
  }
685
+ $finishHydration() {
686
+ let callbackFailed = false;
687
+ let callbackError;
688
+ try {
689
+ this.$notifyHydrated();
690
+ }
691
+ catch (error) {
692
+ callbackFailed = true;
693
+ callbackError = error;
694
+ }
695
+ try {
696
+ this.$releaseDeferredDescendants();
697
+ }
698
+ catch (releaseError) {
699
+ if (callbackFailed) {
700
+ throw new AggregateError([callbackError, releaseError], 'component callback and deferred descendant hydration failed');
701
+ }
702
+ throw releaseError;
703
+ }
704
+ if (callbackFailed)
705
+ throw callbackError;
706
+ }
395
707
  $shouldApplyTemplateStateFromSSR(_key) {
396
708
  return true;
397
709
  }
@@ -406,7 +718,8 @@ export class TemplateElement extends HTMLElement {
406
718
  if (!this.$templateState) {
407
719
  this.$templateState = Object.create(null);
408
720
  }
409
- if (Object.is(this.$templateState[key], value))
721
+ if (Object.prototype.hasOwnProperty.call(this.$templateState, key) &&
722
+ Object.is(this.$templateState[key], value))
410
723
  return false;
411
724
  this.$templateState[key] = value;
412
725
  return true;
@@ -443,9 +756,12 @@ export class TemplateElement extends HTMLElement {
443
756
  if (!state || typeof state !== 'object')
444
757
  return;
445
758
  const observableNames = this.$observableNames();
759
+ const deferredWrites = this.$deferredWrites;
446
760
  const keys = Object.keys(state);
447
761
  for (let i = 0; i < keys.length; i++) {
448
762
  const key = keys[i];
763
+ if (deferredWrites?.has(key))
764
+ continue;
449
765
  if (observableNames.has(key)) {
450
766
  if (!this.$shouldApplySSRState(key))
451
767
  continue;
@@ -458,6 +774,8 @@ export class TemplateElement extends HTMLElement {
458
774
  }
459
775
  $update(path) {
460
776
  if (!this.$ready || !this.$root) {
777
+ if (path && this.$deferredSSR)
778
+ this.$recordDeferredWrite(path);
461
779
  if (DEV && path && this.isConnected)
462
780
  this.$recordPreReadyWrite(path);
463
781
  return;
@@ -480,11 +798,31 @@ export class TemplateElement extends HTMLElement {
480
798
  this.$dirtyPaths = null;
481
799
  this.$updateInstance(this.$root);
482
800
  }
801
+ $recordDeferredWrite(path) {
802
+ const dot = path.indexOf('.');
803
+ const root = dot === -1 ? path : path.slice(0, dot);
804
+ (this.$deferredWrites ??= new Set()).add(root);
805
+ }
806
+ $replayDeferredWrites() {
807
+ const writes = this.$deferredWrites;
808
+ if (!writes)
809
+ return;
810
+ this.$deferredWrites = undefined;
811
+ if (this.$dirtyPaths) {
812
+ for (const path of writes)
813
+ this.$dirtyPaths.add(path);
814
+ }
815
+ else {
816
+ this.$dirtyPaths = writes;
817
+ }
818
+ this.$pendingFlush = true;
819
+ this.$flush(true);
820
+ }
483
821
  $flushUpdates() {
484
822
  if (this.$pendingFlush)
485
823
  this.$flush();
486
824
  }
487
- $flush() {
825
+ $flush(requireKnownState = this.$guardUnknownState === true) {
488
826
  if (!this.$ready || !this.$root) {
489
827
  this.$dirtyPaths = null;
490
828
  this.$pendingFlush = false;
@@ -502,14 +840,14 @@ export class TemplateElement extends HTMLElement {
502
840
  this.$buildPathIndex();
503
841
  const entry = this.$pathIndex?.get(path);
504
842
  if (entry) {
505
- this.$updateBindings(entry.texts, entry.attrs, entry.conds, entry.repeats);
843
+ this.$updateBindings(entry.texts, entry.attrs, entry.conds, entry.repeats, requireKnownState);
506
844
  }
507
845
  }
508
846
  if (!this.$pathIndex)
509
847
  this.$buildPathIndex();
510
848
  if (this.$wildcardBindings) {
511
849
  const wc = this.$wildcardBindings;
512
- this.$updateBindings(wc.texts, wc.attrs, wc.conds, wc.repeats);
850
+ this.$updateBindings(wc.texts, wc.attrs, wc.conds, wc.repeats, requireKnownState);
513
851
  }
514
852
  if (!this.$pathIndex)
515
853
  this.$buildPathIndex();
@@ -541,49 +879,6 @@ export class TemplateElement extends HTMLElement {
541
879
  const tag = this.tagName.toLowerCase();
542
880
  void import('./hydration-mismatch.js').then((m) => m.reportHydrationMismatch(tag, writes, index, ctx));
543
881
  }
544
- $resolve(root, path, pathStart = 0) {
545
- let cur = root;
546
- for (let i = 0; i < pathStart; i++) {
547
- const child = cur.childNodes[path[i]];
548
- if (!child)
549
- return null;
550
- cur = child;
551
- }
552
- for (let i = pathStart; i < path.length; i++) {
553
- const child = cur.childNodes[path[i]];
554
- if (!child)
555
- return null;
556
- cur = child;
557
- }
558
- return cur;
559
- }
560
- $resolveSSR(ssrRoot, tplRoot, path, pathStart = 0) {
561
- let ssr = ssrRoot;
562
- let tpl = tplRoot;
563
- for (let i = 0; i < pathStart; i++) {
564
- const tplChild = tpl.childNodes[path[i]];
565
- if (!tplChild)
566
- return null;
567
- tpl = tplChild;
568
- }
569
- for (let i = pathStart; i < path.length; i++) {
570
- const idx = path[i];
571
- const tplChild = tpl.childNodes[idx];
572
- if (!tplChild)
573
- return null;
574
- const ordinals = getTplOrdinals(tpl);
575
- const entry = ordinals.get(idx);
576
- if (!entry)
577
- return null;
578
- const [nodeType, ordinal] = entry;
579
- const child = findByOrdinal(ssr, nodeType, ordinal);
580
- if (!child)
581
- return null;
582
- ssr = child;
583
- tpl = tplChild;
584
- }
585
- return ssr;
586
- }
587
882
  $parseTemplate(meta) {
588
883
  let cached = templateCache.get(meta);
589
884
  if (cached)
@@ -644,6 +939,7 @@ export class TemplateElement extends HTMLElement {
644
939
  scope, container: root, nodes: childNodesArray(root),
645
940
  texts: [], attrs: [], conds: [], repeats: [],
646
941
  };
942
+ const elements = collectTemplateElements(root);
647
943
  const textRefs = new Array(meta.tx?.length ?? 0);
648
944
  let textRefCount = 0;
649
945
  if (meta.tx) {
@@ -651,8 +947,8 @@ export class TemplateElement extends HTMLElement {
651
947
  const entry = meta.tx[i];
652
948
  const [slot, parts] = entry;
653
949
  const raw = entry[2] === 1;
654
- const [parentPath, beforeIndex] = slot;
655
- const parent = parentPath.length > 0 ? this.$resolve(root, parentPath) : root;
950
+ const [parentIndex, beforeIndex] = slot;
951
+ const parent = elements[parentIndex];
656
952
  if (!parent || (parent.nodeType !== 1 && parent.nodeType !== 11))
657
953
  continue;
658
954
  textRefs[textRefCount] = { parent, ref: parent.childNodes[beforeIndex] || null, parts, raw };
@@ -664,8 +960,8 @@ export class TemplateElement extends HTMLElement {
664
960
  if (meta.c) {
665
961
  for (let i = 0; i < meta.c.length; i++) {
666
962
  const [condition, blockIndex, slotMeta] = meta.c[i];
667
- const [parentPath, beforeIndex] = slotMeta;
668
- const parent = parentPath.length > 0 ? this.$resolve(root, parentPath) : root;
963
+ const [parentIndex, beforeIndex] = slotMeta;
964
+ const parent = elements[parentIndex];
669
965
  if (!parent || (parent.nodeType !== 1 && parent.nodeType !== 11))
670
966
  continue;
671
967
  condRefs[condRefCount] = { parent, ref: parent.childNodes[beforeIndex] || null, condition: condition, blockIndex };
@@ -677,8 +973,8 @@ export class TemplateElement extends HTMLElement {
677
973
  if (meta.r) {
678
974
  for (let i = 0; i < meta.r.length; i++) {
679
975
  const [collection, itemVar, blockIndex, slotMeta, keyPath] = meta.r[i];
680
- const [parentPath, beforeIndex] = slotMeta;
681
- const parent = parentPath.length > 0 ? this.$resolve(root, parentPath) : root;
976
+ const [parentIndex, beforeIndex] = slotMeta;
977
+ const parent = elements[parentIndex];
682
978
  if (!parent || (parent.nodeType !== 1 && parent.nodeType !== 11))
683
979
  continue;
684
980
  repRefs[repRefCount] = {
@@ -692,8 +988,8 @@ export class TemplateElement extends HTMLElement {
692
988
  repRefCount += 1;
693
989
  }
694
990
  }
695
- this.$wireAttrs(instance, meta, scope, (p) => this.$resolve(root, p));
696
- this.$finalize(instance, root, meta, (r, p) => this.$resolve(r, p), scope);
991
+ this.$wireAttrs(instance, meta, scope, (i) => elements[i] ?? null);
992
+ this.$finalize(instance, root, meta, (_r, i) => elements[i] ?? null, scope);
697
993
  for (let i = 0; i < textRefCount; i++) {
698
994
  const t = textRefs[i];
699
995
  const anchor = document.createComment('');
@@ -749,16 +1045,19 @@ export class TemplateElement extends HTMLElement {
749
1045
  texts: [], attrs: [], conds: [], repeats: [],
750
1046
  };
751
1047
  const staleMarkers = [];
1048
+ const ssrIndex = buildSSRIndex(tplDom, ssrRoot, meta.c !== undefined || meta.r !== undefined, pathStart > 0);
1049
+ const ssrElements = ssrIndex.elements;
1050
+ const tplElements = getTemplateElements(tplDom);
752
1051
  if (meta.tx) {
753
1052
  for (let i = 0; i < meta.tx.length; i++) {
754
1053
  const entry = meta.tx[i];
755
1054
  const [slot, parts] = entry;
756
1055
  const raw = entry[2] === 1;
757
- const [parentPath, beforeIndex] = slot;
758
- const ssrParent = this.$resolveSSR(ssrRoot, tplDom, parentPath, pathStart);
1056
+ const [parentIndex, beforeIndex] = slot;
1057
+ const ssrParent = ssrElements[parentIndex];
759
1058
  if (!ssrParent)
760
1059
  continue;
761
- const tplParent = this.$resolve(tplDom, parentPath, pathStart);
1060
+ const tplParent = tplElements[parentIndex];
762
1061
  if (!tplParent)
763
1062
  continue;
764
1063
  if (raw) {
@@ -778,21 +1077,16 @@ export class TemplateElement extends HTMLElement {
778
1077
  }
779
1078
  }
780
1079
  }
781
- this.$wireAttrs(instance, meta, scope, (p) => this.$resolveSSR(ssrRoot, tplDom, p, pathStart));
1080
+ this.$wireAttrs(instance, meta, scope, (i) => ssrElements[i]);
782
1081
  if (meta.c) {
783
- let lastCondMarker = null;
784
- let lastCondParent = null;
1082
+ const condMarkers = ssrIndex.conds.length === meta.c.length ? ssrIndex.conds : null;
785
1083
  for (let i = 0; i < meta.c.length; i++) {
786
1084
  const [condition, blockIndex, slotMeta] = meta.c[i];
787
- const [parentPath] = slotMeta;
788
- const ssrParent = this.$resolveSSR(ssrRoot, tplDom, parentPath, pathStart) ?? ssrRoot;
1085
+ const [parentIndex] = slotMeta;
789
1086
  const blockMeta = this.$block(blockIndex);
790
1087
  let condInstance = null;
791
- if (ssrParent !== lastCondParent) {
792
- lastCondMarker = null;
793
- lastCondParent = ssrParent;
794
- }
795
- const marker = this.$findMarker(ssrParent, MARKER_COND_START, lastCondMarker);
1088
+ const marker = condMarkers ? condMarkers[i] : null;
1089
+ const ssrParent = (marker ? marker.parentNode : ssrElements[parentIndex]) ?? ssrRoot;
796
1090
  let condAnchor;
797
1091
  if (marker) {
798
1092
  condAnchor = marker;
@@ -803,8 +1097,6 @@ export class TemplateElement extends HTMLElement {
803
1097
  const insertRef = ssrParent.childNodes[beforeIndex ?? ssrParent.childNodes.length] ?? null;
804
1098
  ssrParent.insertBefore(condAnchor, insertRef);
805
1099
  }
806
- if (marker)
807
- lastCondMarker = marker;
808
1100
  if (marker && blockMeta) {
809
1101
  condInstance = this.$hydrateCondContent(condAnchor, blockMeta, scope);
810
1102
  if (condInstance)
@@ -825,22 +1117,17 @@ export class TemplateElement extends HTMLElement {
825
1117
  }
826
1118
  }
827
1119
  if (meta.r) {
828
- let lastRepMarker = null;
829
- let lastRepParent = null;
1120
+ const repMarkers = ssrIndex.repeats.length === meta.r.length ? ssrIndex.repeats : null;
830
1121
  for (let i = 0; i < meta.r.length; i++) {
831
1122
  const [collection, itemVar, blockIndex, slotMeta, keyPath] = meta.r[i];
832
- const [parentPath] = slotMeta;
833
- const ssrParent = this.$resolveSSR(ssrRoot, tplDom, parentPath, pathStart) ?? ssrRoot;
834
- if (ssrParent !== lastRepParent) {
835
- lastRepMarker = null;
836
- lastRepParent = ssrParent;
837
- }
1123
+ const [parentIndex] = slotMeta;
1124
+ const marker = repMarkers ? repMarkers[i] : null;
1125
+ const ssrParent = (marker ? marker.parentNode : ssrElements[parentIndex]) ?? ssrRoot;
838
1126
  const blockMeta = this.$block(blockIndex);
839
1127
  const blockTplDom = blockMeta ? getTemplateDom(blockMeta) : null;
840
1128
  const rootTag = blockMeta && blockTplDom?.childNodes.length === 1 && blockTplDom.children.length === 1
841
1129
  ? this.$rootTag(blockMeta)
842
1130
  : null;
843
- const marker = this.$findMarker(ssrParent, MARKER_REPEAT_START, lastRepMarker);
844
1131
  let anchor;
845
1132
  if (marker) {
846
1133
  anchor = marker;
@@ -848,7 +1135,7 @@ export class TemplateElement extends HTMLElement {
848
1135
  else {
849
1136
  anchor = document.createComment('');
850
1137
  const [, beforeIndex] = slotMeta;
851
- const tplParent = this.$resolve(tplDom, parentPath, pathStart);
1138
+ const tplParent = tplElements[parentIndex];
852
1139
  const staticCount = tplParent ? tplParent.childNodes.length : 0;
853
1140
  const insertRef = ssrParent.childNodes[Math.min(beforeIndex ?? staticCount, ssrParent.childNodes.length)] ?? null;
854
1141
  ssrParent.insertBefore(anchor, insertRef);
@@ -860,7 +1147,6 @@ export class TemplateElement extends HTMLElement {
860
1147
  const { items: itemMarkers, end: endMarker } = marker
861
1148
  ? collectItemMarkers(anchor)
862
1149
  : { items: [], end: null };
863
- lastRepMarker = endMarker ?? anchor;
864
1150
  if (blockMeta && blockTplDom && anchor.parentNode && itemMarkers.length > 0) {
865
1151
  if (!this.$activatingDeferredSSR
866
1152
  && hasCollectionState
@@ -933,7 +1219,7 @@ export class TemplateElement extends HTMLElement {
933
1219
  instance.repeats.push(binding);
934
1220
  }
935
1221
  }
936
- this.$finalize(instance, ssrRoot, meta, (r, p) => this.$resolveSSR(r, tplDom, p, pathStart), scope);
1222
+ this.$finalize(instance, ssrRoot, meta, (_r, i) => ssrElements[i] ?? null, scope);
937
1223
  for (let i = 0; i < staleMarkers.length; i++) {
938
1224
  staleMarkers[i].parentNode?.removeChild(staleMarkers[i]);
939
1225
  }
@@ -964,13 +1250,13 @@ export class TemplateElement extends HTMLElement {
964
1250
  $hasRootStructuralSlot(meta) {
965
1251
  if (meta.c) {
966
1252
  for (let i = 0; i < meta.c.length; i++) {
967
- if (meta.c[i][2][0].length === 0)
1253
+ if (meta.c[i][2][0] === 0)
968
1254
  return true;
969
1255
  }
970
1256
  }
971
1257
  if (meta.r) {
972
1258
  for (let i = 0; i < meta.r.length; i++) {
973
- if (meta.r[i][3][0].length === 0)
1259
+ if (meta.r[i][3][0] === 0)
974
1260
  return true;
975
1261
  }
976
1262
  }
@@ -1004,16 +1290,6 @@ export class TemplateElement extends HTMLElement {
1004
1290
  this.$replaceInstanceContainer(inst, wrapper, container);
1005
1291
  return inst;
1006
1292
  }
1007
- $findMarker(parent, data, after) {
1008
- let child = after ? after.nextSibling : parent.firstChild;
1009
- while (child) {
1010
- if (child.nodeType === 8 && child.data === data) {
1011
- return child;
1012
- }
1013
- child = child.nextSibling;
1014
- }
1015
- return null;
1016
- }
1017
1293
  $findSSRText(ssrParent, tplParent, beforeIndex) {
1018
1294
  const ordinals = getTplOrdinals(tplParent);
1019
1295
  let textOrd = 0;
@@ -1069,8 +1345,8 @@ export class TemplateElement extends HTMLElement {
1069
1345
  if (!meta.a || !meta.ag)
1070
1346
  return;
1071
1347
  for (let g = 0; g < meta.ag.length; g++) {
1072
- const [targetPath, start, count] = meta.ag[g];
1073
- const el = resolve(targetPath);
1348
+ const [targetIndex, start, count] = meta.ag[g];
1349
+ const el = resolve(targetIndex);
1074
1350
  if (!el || el.nodeType !== 1)
1075
1351
  continue;
1076
1352
  for (let j = 0; j < count; j++) {
@@ -1178,28 +1454,64 @@ export class TemplateElement extends HTMLElement {
1178
1454
  }
1179
1455
  this.$pathIndex = index;
1180
1456
  }
1181
- $updateBindings(texts, attrs, conds, repeats) {
1457
+ $updateBindings(texts, attrs, conds, repeats, requireKnownState = false) {
1182
1458
  const gated = this.$hasUnknownScopes;
1183
1459
  for (let i = 0; i < texts.length; i++) {
1184
1460
  const binding = texts[i];
1185
- if (!gated || !binding.scope || this.$scopeIsKnown(binding.scope))
1461
+ if ((!gated || !binding.scope || this.$scopeIsKnown(binding.scope)) &&
1462
+ (!requireKnownState || this.$textStateIsKnown(binding))) {
1186
1463
  this.$patchText(binding);
1464
+ }
1187
1465
  }
1188
1466
  for (let i = 0; i < attrs.length; i++) {
1189
1467
  const binding = attrs[i];
1190
- if (!gated || !binding.scope || this.$scopeIsKnown(binding.scope))
1468
+ if ((!gated || !binding.scope || this.$scopeIsKnown(binding.scope)) &&
1469
+ (!requireKnownState || this.$attrStateIsKnown(binding))) {
1191
1470
  this.$patchAttr(binding);
1471
+ }
1192
1472
  }
1193
1473
  for (let i = 0; i < conds.length; i++) {
1194
1474
  const binding = conds[i];
1195
- if (!gated || !binding.scope || this.$scopeIsKnown(binding.scope))
1475
+ if ((!gated || !binding.scope || this.$scopeIsKnown(binding.scope)) &&
1476
+ (!requireKnownState || this.$pathsAreKnown(binding.condition[1], binding.scope))) {
1196
1477
  this.$toggleCond(binding);
1478
+ }
1197
1479
  }
1198
1480
  for (let i = 0; i < repeats.length; i++) {
1199
1481
  const binding = repeats[i];
1200
- if (!gated || !binding.scope || this.$scopeIsKnown(binding.scope))
1482
+ if ((!gated || !binding.scope || this.$scopeIsKnown(binding.scope)) &&
1483
+ (!requireKnownState || this.$hasStateRoot(binding.collection, binding.scope))) {
1201
1484
  syncRepeat(this, binding);
1485
+ }
1486
+ }
1487
+ }
1488
+ $textStateIsKnown(binding) {
1489
+ if (binding.parts)
1490
+ return this.$partsAreKnown(binding.parts, binding.scope);
1491
+ return !binding.path || this.$hasStateRoot(binding.path, binding.scope);
1492
+ }
1493
+ $attrStateIsKnown(binding) {
1494
+ if (binding.path && !this.$hasStateRoot(binding.path, binding.scope))
1495
+ return false;
1496
+ if (binding.parts && !this.$partsAreKnown(binding.parts, binding.scope))
1497
+ return false;
1498
+ return !binding.condition
1499
+ || this.$pathsAreKnown(binding.condition[1], binding.scope);
1500
+ }
1501
+ $partsAreKnown(parts, scope) {
1502
+ for (let i = 0; i < parts.length; i++) {
1503
+ const part = parts[i];
1504
+ if (typeof part !== 'string' && !this.$hasStateRoot(part[0], scope))
1505
+ return false;
1202
1506
  }
1507
+ return true;
1508
+ }
1509
+ $pathsAreKnown(paths, scope) {
1510
+ for (let i = 0; i < paths.length; i++) {
1511
+ if (!this.$hasStateRoot(paths[i], scope))
1512
+ return false;
1513
+ }
1514
+ return true;
1203
1515
  }
1204
1516
  $scopeIsKnown(scope) {
1205
1517
  let frame = scope;
@@ -1211,7 +1523,7 @@ export class TemplateElement extends HTMLElement {
1211
1523
  return true;
1212
1524
  }
1213
1525
  $updateInstance(instance) {
1214
- this.$updateBindings(instance.texts, instance.attrs, instance.conds, instance.repeats);
1526
+ this.$updateBindings(instance.texts, instance.attrs, instance.conds, instance.repeats, this.$guardUnknownState === true);
1215
1527
  }
1216
1528
  $patchText(b) {
1217
1529
  let val;
@@ -1336,7 +1648,7 @@ export class TemplateElement extends HTMLElement {
1336
1648
  || (path.length > frame.name.length
1337
1649
  && path.charCodeAt(frame.name.length) === 46
1338
1650
  && path.startsWith(frame.name))) {
1339
- return frame.value !== undefined;
1651
+ return frame.known !== false;
1340
1652
  }
1341
1653
  frame = frame.parent;
1342
1654
  }