@symbiotejs/symbiote 3.3.5 → 3.3.7

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/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.3.7
4
+
5
+ ### Fixed
6
+
7
+ - **SSR hydration: Itemize list duplication.**
8
+ Fixed itemize SSR hydration creating duplicate items. The processor now adopts the existing SSR item tag name and element class, skips the initial subscription fire, and sets `isoMode` on the item class so upgraded elements hydrate their existing content instead of re-rendering.
9
+
10
+ ## 3.3.6
11
+
12
+ ### Improved
13
+
14
+ - **SSR hydration: generic property preservation.**
15
+ During SSR/ISO hydration, all primitive-valued bindings (`textContent`, `innerHTML`, `style.*`, `value`, etc.) now skip the initial write, preserving server-rendered DOM. Function bindings (event handlers) and non-null object bindings (component state) still fire immediately. Previously only `textContent` and attribute bindings were preserved.
16
+
17
+ - **SSR hydration: Itemize API support.**
18
+ Auto-generated itemize item components now inherit `ssrMode`/`isoMode` from the parent. Server-rendered list items are preserved during hydration instead of being cleared and re-created.
19
+
3
20
  ## 3.3.5
4
21
 
5
22
  ### Fixed
@@ -9,6 +9,8 @@ import { initPropFallback } from './initPropFallback.js';
9
9
  * @param {T} fnCtx
10
10
  */
11
11
  export function itemizeProcessor(fr, fnCtx) {
12
+ let clientSSR = fnCtx.ssrMode && !globalThis.__SYMBIOTE_SSR;
13
+
12
14
  ownElements(fr, `[${DICT.LIST_ATTR}]`).filter((el) => {
13
15
  return !el.matches(`[${DICT.LIST_ATTR}] [${DICT.LIST_ATTR}]`);
14
16
  }).forEach((el) => {
@@ -18,19 +20,40 @@ export function itemizeProcessor(fr, fnCtx) {
18
20
  itemClass = window.customElements.get(itemTag);
19
21
  }
20
22
  if (!itemClass) {
21
- itemClass = class extends fnCtx.Symbiote {
22
- constructor() {
23
- super();
24
- if (!itemTag) {
25
- this.style.display = 'contents';
26
- }
23
+ // During hydration, adopt existing SSR item tag and derive template
24
+ if (clientSSR && el.children.length > 0) {
25
+ let ssrTag = el.children[0].localName;
26
+ itemClass = window.customElements.get(ssrTag);
27
+ if (!itemClass) {
28
+ itemClass = class extends fnCtx.Symbiote {
29
+ constructor() {
30
+ super();
31
+ this.isoMode = true;
32
+ if (!itemTag) {
33
+ this.style.display = 'contents';
34
+ }
35
+ }
36
+ };
37
+ itemClass.template = el.children[0].innerHTML;
38
+ itemClass.reg(ssrTag);
27
39
  }
28
- };
29
- itemClass.template = el.querySelector('template')?.innerHTML || el.innerHTML;
30
- itemClass.reg(itemTag);
40
+ } else {
41
+ itemClass = class extends fnCtx.Symbiote {
42
+ constructor() {
43
+ super();
44
+ if (!itemTag) {
45
+ this.style.display = 'contents';
46
+ }
47
+ }
48
+ };
49
+ itemClass.template = el.querySelector('template')?.innerHTML || el.innerHTML;
50
+ itemClass.reg(itemTag);
51
+ }
31
52
  }
32
- while (el.firstChild) {
33
- el.firstChild.remove();
53
+ if (!clientSSR) {
54
+ while (el.firstChild) {
55
+ el.firstChild.remove();
56
+ }
34
57
  }
35
58
  let repeatDataKey = el.getAttribute(DICT.LIST_ATTR);
36
59
  if (!fnCtx.has(repeatDataKey) && fnCtx.allowTemplateInits) {
@@ -87,7 +110,7 @@ export function itemizeProcessor(fr, fnCtx) {
87
110
  } else {
88
111
  console.warn(`[Symbiote] <${fnCtx.localName}>: itemize data must be Array or Object, got ${typeof data}:`, data);
89
112
  }
90
- });
113
+ }, !clientSSR);
91
114
  if (!globalThis.__SYMBIOTE_SSR) {
92
115
  el.removeAttribute(DICT.LIST_ATTR);
93
116
  el.removeAttribute(DICT.LIST_ITEM_TAG_ATTR);
@@ -70,6 +70,9 @@ function domBindProcessor(fr, fnCtx) {
70
70
  );
71
71
  }
72
72
  }
73
+ let initVal = fnCtx.$[valKey];
74
+ let skipInit = fnCtx.ssrMode && !globalThis.__SYMBIOTE_SSR
75
+ && (isAttr || (typeof initVal !== 'function' && (initVal === null || typeof initVal !== 'object')));
73
76
  fnCtx.sub(valKey, (val) => {
74
77
  if (castType === 'double') {
75
78
  val = !!val;
@@ -91,7 +94,7 @@ function domBindProcessor(fr, fnCtx) {
91
94
  el[DICT.SET_LATER_KEY][prop] = val;
92
95
  }
93
96
  }
94
- }, !(fnCtx.ssrMode && !globalThis.__SYMBIOTE_SSR && (prop === 'textContent' || isAttr)));
97
+ }, !skipInit);
95
98
  }
96
99
  });
97
100
  if (!globalThis.__SYMBIOTE_SSR) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@symbiotejs/symbiote",
4
- "version": "3.3.5",
4
+ "version": "3.3.7",
5
5
  "description": "Symbiote.js - zero-dependency close-to-platform frontend library to build super-powered web components",
6
6
  "author": "team@rnd-pro.com",
7
7
  "license": "MIT",
@@ -1 +1 @@
1
- {"version":3,"file":"itemizeProcessor.d.ts","sourceRoot":"","sources":["../../core/itemizeProcessor.js"],"names":[],"mappings":"AAUA,iCAJgD,CAAC,SAApC,qCAAkC,MACpC,gBAAgB,SAChB,CAAC,QAuFX"}
1
+ {"version":3,"file":"itemizeProcessor.d.ts","sourceRoot":"","sources":["../../core/itemizeProcessor.js"],"names":[],"mappings":"AAUA,iCAJgD,CAAC,SAApC,qCAAkC,MACpC,gBAAgB,SAChB,CAAC,QA8GX"}
@@ -1 +1 @@
1
- {"version":3,"file":"tpl-processors.d.ts","sourceRoot":"","sources":["../../core/tpl-processors.js"],"names":[],"mappings":";0BA0HgD,CAAC,SAApC,qCAAkC,MACpC,gBAAgB,SAChB,CAAC;;iCAzHqB,uBAAuB"}
1
+ {"version":3,"file":"tpl-processors.d.ts","sourceRoot":"","sources":["../../core/tpl-processors.js"],"names":[],"mappings":";0BA6HgD,CAAC,SAApC,qCAAkC,MACpC,gBAAgB,SAChB,CAAC;;iCA5HqB,uBAAuB"}