@symbiotejs/symbiote 3.2.1 → 3.3.0

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/AI_REFERENCE.md CHANGED
@@ -153,7 +153,7 @@ Prefixes control which data context a binding resolves to:
153
153
  | Prefix | Meaning | Example | Description |
154
154
  |--------|---------|---------|-------------|
155
155
  | _(none)_ | Local state | `{{count}}` | Current component's local context |
156
- | `^` | Parent inherited | `{{^parentProp}}` | Walk up DOM ancestry to find nearest component that has this prop |
156
+ | `^` | Parent inherited | `{{^parentProp}}` | Walk up DOM ancestry to find nearest component that has this prop in its data context (`init$` / `add$()`) |
157
157
  | `*` | Shared context | `{{*sharedProp}}` | Shared context scoped by `ctx` attribute or CSS `--ctx` |
158
158
  | `/` | Named context | `{{APP/myProp}}` | Global named context identified by key before `/` |
159
159
  | `--` | CSS Data | `{{--my-css-var}}` | Read value from CSS custom property |
@@ -318,6 +318,7 @@ Both components access the same `*files` state — no parent component, no prop
318
318
  | `readyToDestroy` | `true` | Allow cleanup on disconnect |
319
319
  | `processInnerHtml` | `false` | Process existing inner HTML with template processors |
320
320
  | `ssrMode` | `false` | **Client-only.** Hydrate server-rendered HTML: skips template injection, attaches bindings to existing DOM. Supports both light DOM and Declarative Shadow DOM. Ignored when `__SYMBIOTE_SSR` is active (server side) |
321
+ | `isoMode` | `false` | **Client-only.** Isomorphic mode: if component has children at connect time, behaves as `ssrMode = true` (hydrate). If no children, renders template normally. Same component works for both SSR and client-only scenarios |
321
322
  | `allowCustomTemplate` | `false` | Allow `use-template="#selector"` attribute |
322
323
  | `isVirtual` | `false` | Replace element with its template fragment |
323
324
  | `allowTemplateInits` | `true` | Auto-add props found in template but not in init$ |
@@ -454,7 +455,7 @@ MyList.template = html`
454
455
  > **CRITICAL**: Inside itemize templates, items are full Symbiote components with their own state scope.
455
456
  > - `{{name}}` — item's own property
456
457
  > - `${{onclick: 'handler'}}` — binds to the item component's own method/property
457
- > - `${{onclick: '^handler'}}` — use `^` prefix to reach the **parent** component's property
458
+ > - `${{onclick: '^handler'}}` — use `^` prefix to reach the **parent** component's property (must be in parent's `init$`)
458
459
  > - Failure to use `^` for parent handlers will result in broken event bindings
459
460
 
460
461
  ### Custom item component
@@ -798,3 +799,4 @@ Symbiote.devMode = true;
798
799
  9. **DON'T** use CSS frameworks (Tailwind, etc.) — use native CSS with custom properties
799
800
  10. **DON'T** use `require()` — ESM only (import/export)
800
801
  11. **DON'T** use `*prop` without `ctx` attribute or `--ctx` CSS variable — shared context won't be created
802
+ 12. **DON'T** rely on class property fallbacks for `^`-targeted properties — the `^` walk only checks the parent's data context (`init$` / `add$()`), not own class properties
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.3.0
4
+
5
+ ### Added
6
+
7
+ - **`isoMode` flag for isomorphic rendering.**
8
+ `isoMode = true` enables automatic detection: if the component has children at connect time (server-rendered content), it hydrates existing DOM like `ssrMode`. If no children exist, it renders the template normally. Same component code works for both SSR and client-only scenarios.
9
+
3
10
  ## 3.2.0
4
11
 
5
12
  ### Added
package/README.md CHANGED
@@ -1,5 +1,9 @@
1
+ [![Tests](https://github.com/symbiotejs/symbiote.js/actions/workflows/tests.yml/badge.svg)](https://github.com/symbiotejs/symbiote.js/actions/workflows/tests.yml)
1
2
  [![npm version](https://img.shields.io/npm/v/@symbiotejs/symbiote)](https://www.npmjs.com/package/@symbiotejs/symbiote)
2
- ![License](https://img.shields.io/badge/license-MIT-green)
3
+ [![npm downloads](https://img.shields.io/npm/dm/@symbiotejs/symbiote)](https://www.npmjs.com/package/@symbiotejs/symbiote)
4
+ ![bundle size](https://img.shields.io/badge/minzip-6.5_kb-blue)
5
+ ![types](https://img.shields.io/badge/types-JSDoc+d.ts-blue)
6
+ ![license](https://img.shields.io/badge/license-MIT-green)
3
7
 
4
8
  # Symbiote.js
5
9
 
@@ -12,6 +16,7 @@ Symbiote.js gives you the convenience of a modern framework while staying close
12
16
  ## What's new in 3.x
13
17
 
14
18
  - **Server-Side Rendering** — render components to HTML with `SSR.processHtml()` or stream chunks with `SSR.renderToStream()`. Client-side hydration via `ssrMode` attaches bindings to existing DOM without re-rendering.
19
+ - **Isomorphic components** — `isoMode` flag makes components work in both SSR and client-only scenarios automatically. If server-rendered content exists, it hydrates; otherwise it renders the template from scratch. One component, zero conditional logic.
15
20
  - **Computed properties** — reactive derived state with microtask batching.
16
21
  - **Path-based router** — optional `AppRouter` module with `:param` extraction, route guards, and lazy loading.
17
22
  - **Exit animations** — `animateOut(el)` for CSS-driven exit transitions, integrated into itemize API.
@@ -70,7 +75,7 @@ let html = await SSR.processHtml('<my-app>slot content</my-app>');
70
75
  SSR.destroy(); // cleanup
71
76
  ```
72
77
 
73
- On the client, components with `ssrMode = true` skip template injection and attach bindings to the existing DOM. State mutations work immediately — no hydration step, no reconciliation, no diffing.
78
+ On the client, components with `ssrMode = true` skip template injection and attach bindings to the existing DOM. State mutations work immediately — no hydration step, no reconciliation, no diffing. For isomorphic components that may or may not be server-rendered, use `isoMode = true` — it detects children automatically.
74
79
 
75
80
  ### Streaming
76
81
 
@@ -158,15 +163,15 @@ Render lists from data arrays with efficient diffing:
158
163
 
159
164
  ```js
160
165
  class TaskList extends Symbiote {
166
+ tasks = [
167
+ { name: 'Buy groceries' },
168
+ { name: 'Write docs' },
169
+ ];
161
170
  init$ = {
162
- tasks: [
163
- { name: 'Buy groceries' },
164
- { name: 'Write docs' },
165
- ],
166
- }
167
-
168
- onItemClick() {
169
- console.log('clicked!');
171
+ // Needs to be defined in init$ for bubbling binding to work
172
+ onItemClick: () => {
173
+ console.log('clicked!');
174
+ },
170
175
  }
171
176
  }
172
177
 
@@ -179,11 +184,11 @@ TaskList.template = html`
179
184
  `;
180
185
  ```
181
186
 
182
- Items have their own state scope. Use the **`^` prefix** to reach higher-level component properties and handlers — `'^onItemClick'` binds to the parent's `onItemClick`, not the item's.
187
+ Items have their own state scope. Use the **`^` prefix** to reach higher-level component properties and handlers — `'^onItemClick'` binds to the parent's `onItemClick`, not the item's. Properties referenced via `^` must be defined in the parent's `init$`.
183
188
 
184
189
  ### Bubbling binding (`^`)
185
190
 
186
- The `^` prefix works in any nested component template — it walks up the DOM tree to find the nearest ancestor component that owns the property:
191
+ The `^` prefix works in any nested component template — it walks up the DOM tree to find the nearest ancestor that has the property registered in its data context (`init$` or `add$()`):
187
192
 
188
193
  ```html
189
194
  <!-- Text binding to parent property: -->
@@ -193,6 +198,8 @@ The `^` prefix works in any nested component template — it walks up the DOM tr
193
198
  <button ${{onclick: '^parentHandler'}}>Click</button>
194
199
  ```
195
200
 
201
+ > **Note:** Class property fallbacks are not checked by the `^` walk — the parent must define the property in `init$`.
202
+
196
203
  ### Named data contexts
197
204
 
198
205
  Share state across components without prop drilling:
@@ -355,3 +362,7 @@ All modern browsers: Chrome, Firefox, Safari, Edge, Opera.
355
362
  - [Changelog](https://github.com/symbiotejs/symbiote.js/blob/main/CHANGELOG.md)
356
363
 
357
364
  **Questions or proposals? Welcome to [Symbiote Discussions](https://github.com/symbiotejs/symbiote.js/discussions)!** ❤️
365
+
366
+ ---
367
+
368
+ © [rnd-pro.com](https://rnd-pro.com) — MIT License
package/core/Symbiote.js CHANGED
@@ -98,6 +98,10 @@ export class Symbiote extends HTMLElement {
98
98
  }
99
99
  }
100
100
  }
101
+ // Resolve isoMode: hydrate if children exist, render template otherwise
102
+ if (this.isoMode && !globalThis.__SYMBIOTE_SSR) {
103
+ this.ssrMode = this.childNodes.length > 0;
104
+ }
101
105
  let clientSSR = this.ssrMode && !globalThis.__SYMBIOTE_SSR;
102
106
  if (this.processInnerHtml || clientSSR) {
103
107
  for (let fn of this.templateProcessors) {
@@ -169,6 +173,8 @@ export class Symbiote extends HTMLElement {
169
173
  /** @type {Boolean} */
170
174
  this.ssrMode = false;
171
175
  /** @type {Boolean} */
176
+ this.isoMode = false;
177
+ /** @type {Boolean} */
172
178
  this.allowCustomTemplate = false;
173
179
  /** @type {Boolean} */
174
180
  this.isVirtual = false;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@symbiotejs/symbiote",
4
- "version": "3.2.1",
4
+ "version": "3.3.0",
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",
@@ -20,6 +20,7 @@ export class Symbiote<S> extends HTMLElement {
20
20
  initCallback(): void;
21
21
  renderCallback(): void;
22
22
  render(template?: string | DocumentFragment, shadow?: boolean): void;
23
+ ssrMode: boolean;
23
24
  init$: S;
24
25
  cssInit$: {
25
26
  [x: string]: any;
@@ -33,7 +34,7 @@ export class Symbiote<S> extends HTMLElement {
33
34
  renderShadow: boolean;
34
35
  readyToDestroy: boolean;
35
36
  processInnerHtml: boolean;
36
- ssrMode: boolean;
37
+ isoMode: boolean;
37
38
  allowCustomTemplate: boolean;
38
39
  isVirtual: boolean;
39
40
  allowTemplateInits: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"Symbiote.d.ts","sourceRoot":"","sources":["../../core/Symbiote.js"],"names":[],"mappings":";;AAmBA,sBADc,CAAC;IAuBb,cADW,mBAAmB,CACjB;IAGb,sCAAwB;IAExB,iCAGC;IAED,8BAEC;IAkBD,wBAAgB;IA8IhB,+BAJwB,CAAC,SAAZ,aAAU,uBAEZ,CAAC;;;MAuCX;IA+OD,qCAA+B;IAoC/B,iDAFa,OAAO,QAAQ,CAqB3B;IAED,wBAKC;IAGD;;aAOC;IA6GD,6BADY,SAAS,aAAa,QAOjC;IAGD,+BADY,SAAS,aAAa,QAOjC;IAGD,8BADY,SAAS,aAAa,EAIjC;IAGD,gCADY,SAAS,aAAa,EAIjC;IApiBD,cA2BC;IAzHD,gCAEC;IAED,qBAAiB;IACjB,uBAAmB;IAiBnB,kBAHW,SAAS,gBAAgB,0BAyEnC;IAKC,OADW,CAAC,CACoB;IAEhC;;MAAmC;IAEnC,oBADW,GAAG,CAAC,CAAC,EAAE,EAAE,gBAAgB,gBAAW,EAAE,KAAK,eAAU,KAAK,IAAI,CAAC,CACvC;IAEnC;;MAA8B;IAC9B,kBAAwB;IAExB,qBAAwB;IAExB,sBAAyB;IAEzB,wBAA0B;IAE1B,0BAA6B;IAE7B,iBAAoB;IAEpB,6BAAgC;IAEhC,mBAAsB;IAEtB,4BAA8B;IAIhC,yBAEC;IAGD,sBASC;IAGD,4BAKC;IAGD,6BAEC;IAoDD,IALuB,CAAC,SAAX,MAAO,CAAE,QACX,CAAC,WACD,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,wBAmB/B;IAGD,2BAGC;IAGD,uBAGC;IAQD,IALuB,CAAC,SAAX,MAAO,CAAE,qBAEX,CAAC,CAAC,CAAC,CAAC,2BAMd;IAMD,UAHW,OAAO,CAAC,CAAC,CAAC,2BAOpB;IAGD,SADc,CAAC,CA2Bd;IAMD,YAHW,OAAO,CAAC,CAAC,CAAC,mCAcpB;IAED,8BAgBC;IAdG,4CASE;IAwFF,0BAAwC;IAoB1C,uBAAyB;IAG3B,0BAEC;IAED,wBAAoB;IAUpB,yBAAuB;IACvB,6BA0BC;IA6CD,oEAeC;IAMD,yDAiBC;IAYD,0BAME;IAMF,0CAFW,GAAG,QAab;IAED,yBAGC;IAOD,8EAmBC;;CA+BF;;mBA1rBkB,aAAa;qBACX,iBAAiB;2BACX,iBAAiB"}
1
+ {"version":3,"file":"Symbiote.d.ts","sourceRoot":"","sources":["../../core/Symbiote.js"],"names":[],"mappings":";;AAmBA,sBADc,CAAC;IAuBb,cADW,mBAAmB,CACjB;IAGb,sCAAwB;IAExB,iCAGC;IAED,8BAEC;IAkBD,wBAAgB;IAoJhB,+BAJwB,CAAC,SAAZ,aAAU,uBAEZ,CAAC;;;MAuCX;IA+OD,qCAA+B;IAoC/B,iDAFa,OAAO,QAAQ,CAqB3B;IAED,wBAKC;IAGD;;aAOC;IA6GD,6BADY,SAAS,aAAa,QAOjC;IAGD,+BADY,SAAS,aAAa,QAOjC;IAGD,8BADY,SAAS,aAAa,EAIjC;IAGD,gCADY,SAAS,aAAa,EAIjC;IAtiBD,cA6BC;IA/HD,gCAEC;IAED,qBAAiB;IACjB,uBAAmB;IAiBnB,kBAHW,SAAS,gBAAgB,0BA6EnC;IAjDG,iBAAyC;IAsD3C,OADW,CAAC,CACoB;IAEhC;;MAAmC;IAEnC,oBADW,GAAG,CAAC,CAAC,EAAE,EAAE,gBAAgB,gBAAW,EAAE,KAAK,eAAU,KAAK,IAAI,CAAC,CACvC;IAEnC;;MAA8B;IAC9B,kBAAwB;IAExB,qBAAwB;IAExB,sBAAyB;IAEzB,wBAA0B;IAE1B,0BAA6B;IAI7B,iBAAoB;IAEpB,6BAAgC;IAEhC,mBAAsB;IAEtB,4BAA8B;IAIhC,yBAEC;IAGD,sBASC;IAGD,4BAKC;IAGD,6BAEC;IAoDD,IALuB,CAAC,SAAX,MAAO,CAAE,QACX,CAAC,WACD,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,wBAmB/B;IAGD,2BAGC;IAGD,uBAGC;IAQD,IALuB,CAAC,SAAX,MAAO,CAAE,qBAEX,CAAC,CAAC,CAAC,CAAC,2BAMd;IAMD,UAHW,OAAO,CAAC,CAAC,CAAC,2BAOpB;IAGD,SADc,CAAC,CA2Bd;IAMD,YAHW,OAAO,CAAC,CAAC,CAAC,mCAcpB;IAED,8BAgBC;IAdG,4CASE;IAwFF,0BAAwC;IAoB1C,uBAAyB;IAG3B,0BAEC;IAED,wBAAoB;IAUpB,yBAAuB;IACvB,6BA0BC;IA6CD,oEAeC;IAMD,yDAiBC;IAYD,0BAME;IAMF,0CAFW,GAAG,QAab;IAED,yBAGC;IAOD,8EAmBC;;CA+BF;;mBAhsBkB,aAAa;qBACX,iBAAiB;2BACX,iBAAiB"}