@symbiotejs/symbiote 3.5.6 → 3.5.8
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/README.md +36 -34
- package/core/warn.js +17 -2
- package/node/SSR.js +13 -4
- package/package.json +1 -1
- package/types/core/warn.d.ts +1 -0
- package/types/core/warn.d.ts.map +1 -1
- package/types/node/SSR.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -9,25 +9,25 @@
|
|
|
9
9
|
|
|
10
10
|
<img src="https://rnd-pro.com/svg/symbiote/index.svg" width="200" alt="Symbiote.js">
|
|
11
11
|
|
|
12
|
-
A lightweight, standards-first UI library built on Web Components. No virtual DOM, no compiler, no build step required
|
|
12
|
+
A lightweight, standards-first UI library built on Web Components. No virtual DOM, no compiler, no build step required - works directly in the browser. A bundler is recommended for production performance, but entirely optional. **~6kb** brotli / **~7kb** gzip.
|
|
13
13
|
|
|
14
|
-
Symbiote.js gives you the convenience of a modern framework while staying close to the native platform
|
|
14
|
+
Symbiote.js gives you the convenience of a modern framework while staying close to the native platform - HTML, CSS, and DOM APIs. Components are real custom elements that work everywhere: in any framework, in plain HTML, or in a micro-frontend architecture. And with **isomorphic mode**, the same component code works on the server and the client - server-rendered pages hydrate automatically, no diffing, no mismatch errors.
|
|
15
15
|
|
|
16
16
|
## What's new in v3
|
|
17
17
|
|
|
18
|
-
- **Server-Side Rendering**
|
|
19
|
-
- **Isomorphic components**
|
|
20
|
-
- **Computed properties**
|
|
21
|
-
- **Path-based router**
|
|
22
|
-
- **Exit animations**
|
|
23
|
-
- **Dev mode**
|
|
24
|
-
- **DSD hydration**
|
|
25
|
-
- **Class property fallback**
|
|
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.
|
|
20
|
+
- **Computed properties** - reactive derived state with microtask batching.
|
|
21
|
+
- **Path-based router** - optional `AppRouter` module with `:param` extraction, route guards, and lazy loading.
|
|
22
|
+
- **Exit animations** - `animateOut(el)` for CSS-driven exit transitions, integrated into itemize API.
|
|
23
|
+
- **Dev mode** - `Symbiote.devMode` enables verbose warnings; import `devMessages.js` for full human-readable messages.
|
|
24
|
+
- **DSD hydration** - `ssrMode` supports both light DOM and Declarative Shadow DOM.
|
|
25
|
+
- **Class property fallback** - binding keys not in `init$` fall back to own class properties/methods.
|
|
26
26
|
- And [more](https://github.com/symbiotejs/symbiote.js/blob/main/CHANGELOG.md).
|
|
27
27
|
|
|
28
28
|
## Quick start
|
|
29
29
|
|
|
30
|
-
No install needed
|
|
30
|
+
No install needed - run this directly in a browser:
|
|
31
31
|
|
|
32
32
|
```html
|
|
33
33
|
<script type="module">
|
|
@@ -63,7 +63,7 @@ import Symbiote, { html, css } from '@symbiotejs/symbiote';
|
|
|
63
63
|
|
|
64
64
|
## Isomorphic Web Components
|
|
65
65
|
|
|
66
|
-
One component. Server-rendered or client-rendered
|
|
66
|
+
One component. Server-rendered or client-rendered - automatically. Set `isoMode = true` and the component figures it out: if server-rendered content exists, it hydrates; otherwise it renders from template. No conditional logic, no separate server/client versions:
|
|
67
67
|
```js
|
|
68
68
|
class MyComponent extends Symbiote {
|
|
69
69
|
isoMode = true;
|
|
@@ -80,9 +80,9 @@ MyComponent.template = html`
|
|
|
80
80
|
MyComponent.reg('my-component');
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
This exact code runs **everywhere**
|
|
83
|
+
This exact code runs **everywhere** - SSR on the server, hydration on the client, or pure client rendering. No framework split, no `'use client'` directives, no hydration mismatch errors.
|
|
84
84
|
|
|
85
|
-
### SSR
|
|
85
|
+
### SSR - one class, zero config
|
|
86
86
|
|
|
87
87
|
Server rendering doesn't need a virtual DOM, a reconciler, or framework-specific packages:
|
|
88
88
|
|
|
@@ -103,12 +103,12 @@ For large pages, stream HTML chunks with `SSR.renderToStream()` for faster TTFB.
|
|
|
103
103
|
| | **Symbiote.js** | **Next.js (React)** | **Lit** (`@lit-labs/ssr`) |
|
|
104
104
|
|--|----------------|---------------------|----|
|
|
105
105
|
| **Isomorphic code** | Same code, `isoMode` auto-detects | Server Components vs Client Components split | Same code, but load-order constraints |
|
|
106
|
-
| **Hydration** | Binding-based
|
|
106
|
+
| **Hydration** | Binding-based - attaches to existing DOM, no diffing | `hydrateRoot()` - must produce identical output or errors | Requires `ssr-client` + hydrate support module |
|
|
107
107
|
| **Packages** | 1 module + `linkedom` peer dep | Full framework buy-in | 3 packages: `ssr`, `ssr-client`, `ssr-dom-shim` |
|
|
108
108
|
| **Streaming** | `renderToStream()` async generator | `renderToPipeableStream()` | Iterable `RenderResult` |
|
|
109
|
-
| **Mismatch handling** | Not needed
|
|
109
|
+
| **Mismatch handling** | Not needed - bindings attach to whatever DOM exists | Hard errors if server/client output differs | N/A |
|
|
110
110
|
| **Template output** | Clean HTML with `bind=` attributes | HTML with framework markers | HTML with `<!--lit-part-->` comment markers |
|
|
111
|
-
| **Lock-in** | None
|
|
111
|
+
| **Lock-in** | None - standard Web Components | Full framework commitment | Lit-specific, but Web Components |
|
|
112
112
|
|
|
113
113
|
**Key insight:** There are no hydration mismatches because there's no diffing. The server produces HTML with binding attributes. The client reads those attributes and adds reactivity. That's it.
|
|
114
114
|
|
|
@@ -136,11 +136,11 @@ State changes update the DOM synchronously. No virtual DOM, no scheduling, no su
|
|
|
136
136
|
document.querySelector('my-counter').$.count = 42;
|
|
137
137
|
```
|
|
138
138
|
|
|
139
|
-
This makes it easy to control Symbiote-based widgets and microfrontends from any host application
|
|
139
|
+
This makes it easy to control Symbiote-based widgets and microfrontends from any host application - no framework adapters, just DOM.
|
|
140
140
|
|
|
141
141
|
### Templates
|
|
142
142
|
|
|
143
|
-
Templates are plain HTML strings
|
|
143
|
+
Templates are plain HTML strings - context-free, easy to test, easy to move between files:
|
|
144
144
|
|
|
145
145
|
```js
|
|
146
146
|
// Separate file: my-component.template.js
|
|
@@ -182,11 +182,11 @@ TaskList.template = html`
|
|
|
182
182
|
`;
|
|
183
183
|
```
|
|
184
184
|
|
|
185
|
-
Items have their own state scope. Use the **`^` prefix** to reach higher-level component properties and handlers
|
|
185
|
+
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$`.
|
|
186
186
|
|
|
187
187
|
### Pop-up binding (`^`)
|
|
188
188
|
|
|
189
|
-
The `^` prefix works in any nested component template
|
|
189
|
+
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$()`):
|
|
190
190
|
|
|
191
191
|
```html
|
|
192
192
|
<!-- Text binding to parent property: -->
|
|
@@ -196,7 +196,7 @@ The `^` prefix works in any nested component template — it walks up the DOM tr
|
|
|
196
196
|
<button ${{onclick: '^parentHandler'}}>Click</button>
|
|
197
197
|
```
|
|
198
198
|
|
|
199
|
-
> **Note:** Class property fallbacks are not checked by the `^` walk
|
|
199
|
+
> **Note:** Class property fallbacks are not checked by the `^` walk - the parent must define the property in `init$`.
|
|
200
200
|
|
|
201
201
|
### Named data contexts
|
|
202
202
|
|
|
@@ -216,7 +216,7 @@ this.$['APP/user'] = 'New name';
|
|
|
216
216
|
|
|
217
217
|
### Shared context (`*`)
|
|
218
218
|
|
|
219
|
-
Inspired by native HTML `name` attributes
|
|
219
|
+
Inspired by native HTML `name` attributes - like how `<input name="group">` groups radio buttons - the `ctx` attribute groups components into a shared data context. Components with the same `ctx` value share `*`-prefixed properties:
|
|
220
220
|
|
|
221
221
|
```html
|
|
222
222
|
<upload-btn ctx="gallery"></upload-btn>
|
|
@@ -242,7 +242,7 @@ class StatusBar extends Symbiote {
|
|
|
242
242
|
}
|
|
243
243
|
```
|
|
244
244
|
|
|
245
|
-
All three components access the same `*files` state
|
|
245
|
+
All three components access the same `*files` state - no parent component, no prop drilling, no global store boilerplate. Just set `ctx="gallery"` in HTML and use `*`-prefixed properties. This makes it trivial to build complex component relationships purely in markup, with ready-made components that don't need to know about each other.
|
|
246
246
|
|
|
247
247
|
The context name can also be inherited via CSS custom property `--ctx`, enabling layout-driven grouping.
|
|
248
248
|
|
|
@@ -276,9 +276,9 @@ task-item {
|
|
|
276
276
|
|
|
277
277
|
### Styling
|
|
278
278
|
|
|
279
|
-
Shadow DOM is **optional** in Symbiote
|
|
279
|
+
Shadow DOM is **optional** in Symbiote - use it when you need isolation, skip it when you don't. This gives full flexibility:
|
|
280
280
|
|
|
281
|
-
**Light DOM**
|
|
281
|
+
**Light DOM** - style components with regular CSS, no barriers:
|
|
282
282
|
|
|
283
283
|
```js
|
|
284
284
|
MyComponent.rootStyles = css`
|
|
@@ -291,7 +291,7 @@ MyComponent.rootStyles = css`
|
|
|
291
291
|
`;
|
|
292
292
|
```
|
|
293
293
|
|
|
294
|
-
**Shadow DOM**
|
|
294
|
+
**Shadow DOM** - opt-in isolation when needed:
|
|
295
295
|
|
|
296
296
|
```js
|
|
297
297
|
class Isolated extends Symbiote {}
|
|
@@ -302,7 +302,7 @@ Isolated.shadowStyles = css`
|
|
|
302
302
|
`;
|
|
303
303
|
```
|
|
304
304
|
|
|
305
|
-
All native CSS features work as expected: CSS variables flow through shadow boundaries, `::part()` exposes internals, modern nesting, `@layer`, `@container`
|
|
305
|
+
All native CSS features work as expected: CSS variables flow through shadow boundaries, `::part()` exposes internals, modern nesting, `@layer`, `@container` - no framework abstractions in the way. Mix light DOM and shadow DOM components freely in the same app.
|
|
306
306
|
|
|
307
307
|
### CSS Data Binding
|
|
308
308
|
|
|
@@ -322,15 +322,15 @@ MyWidget.template = html`
|
|
|
322
322
|
`;
|
|
323
323
|
```
|
|
324
324
|
|
|
325
|
-
CSS values are parsed automatically
|
|
325
|
+
CSS values are parsed automatically - quoted strings become strings, numbers become numbers. Call `this.updateCssData()` to re-read after runtime CSS changes. This enables CSS-driven configuration: theme values, layout parameters, or localized strings - all settable from CSS without touching JS.
|
|
326
326
|
|
|
327
327
|
## Best for
|
|
328
328
|
|
|
329
329
|
- **Complex widgets** embedded in any host application
|
|
330
|
-
- **Micro frontends**
|
|
331
|
-
- **Reusable component libraries**
|
|
332
|
-
- **SSR-powered apps**
|
|
333
|
-
- **Framework-agnostic solutions**
|
|
330
|
+
- **Micro frontends** - standard custom elements, no framework coupling
|
|
331
|
+
- **Reusable component libraries** - works in React, Vue, Angular, or plain HTML
|
|
332
|
+
- **SSR-powered apps** - lightweight server rendering without framework lock-in
|
|
333
|
+
- **Framework-agnostic solutions** - one codebase, any context
|
|
334
334
|
|
|
335
335
|
## Bundle size
|
|
336
336
|
|
|
@@ -350,7 +350,9 @@ All modern browsers: Chrome, Firefox, Safari, Edge, Opera.
|
|
|
350
350
|
## Docs & Examples
|
|
351
351
|
|
|
352
352
|
- [Documentation](https://github.com/symbiotejs/symbiote.js/blob/main/docs/README.md)
|
|
353
|
+
- [Lit vs Symbiote.js](https://github.com/symbiotejs/symbiote.js/blob/main/docs/lit-vs-symbiote.md) - Side-by-side comparison
|
|
353
354
|
- [Live Examples](https://rnd-pro.com/symbiote/3x/examples/) - Interactive Code Playground
|
|
355
|
+
- [JSDA-Kit](https://github.com/rnd-pro/jsda-kit) - All-in-one companion tool: server, SSG, bundling, import maps, and native Symbiote.js SSR integration
|
|
354
356
|
- [AI Reference](https://github.com/symbiotejs/symbiote.js/blob/main/AI_REFERENCE.md)
|
|
355
357
|
- [Changelog](https://github.com/symbiotejs/symbiote.js/blob/main/CHANGELOG.md)
|
|
356
358
|
|
|
@@ -358,4 +360,4 @@ All modern browsers: Chrome, Firefox, Safari, Edge, Opera.
|
|
|
358
360
|
|
|
359
361
|
---
|
|
360
362
|
|
|
361
|
-
© [rnd-pro.com](https://rnd-pro.com)
|
|
363
|
+
© [rnd-pro.com](https://rnd-pro.com) - MIT License
|
package/core/warn.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @type {{ devMode: boolean }} */
|
|
1
|
+
/** @type {{ devMode: boolean, hintShown: boolean }} */
|
|
2
2
|
export let devState = {
|
|
3
3
|
get devMode() {
|
|
4
4
|
return !!globalThis.__SYMBIOTE_DEV_MODE;
|
|
@@ -6,11 +6,26 @@ export let devState = {
|
|
|
6
6
|
set devMode(val) {
|
|
7
7
|
globalThis.__SYMBIOTE_DEV_MODE = val;
|
|
8
8
|
},
|
|
9
|
+
hintShown: false,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/** @type {Record<number, string>} */
|
|
13
|
+
let CRITICAL = {
|
|
14
|
+
8: 'Tag already registered with different class',
|
|
15
|
+
9: 'CSS data parse error',
|
|
16
|
+
16: 'Itemize data must be Array or Object',
|
|
9
17
|
};
|
|
10
18
|
|
|
11
19
|
/** @param {string} type @param {number} code */
|
|
12
20
|
function _log(type, code) {
|
|
13
|
-
|
|
21
|
+
let msg = CRITICAL[code];
|
|
22
|
+
if (!msg) return;
|
|
23
|
+
let prefix = type === 'error' ? 'E' : 'W';
|
|
24
|
+
console[type](`[Symbiote ${prefix}${code}] ${msg}`);
|
|
25
|
+
if (!devState.hintShown) {
|
|
26
|
+
devState.hintShown = true;
|
|
27
|
+
console[type]('Import \'@symbiotejs/symbiote/core/devMessages.js\' for detailed messages.');
|
|
28
|
+
}
|
|
14
29
|
}
|
|
15
30
|
|
|
16
31
|
/**
|
package/node/SSR.js
CHANGED
|
@@ -56,6 +56,15 @@ function isInsidePreformatted(node) {
|
|
|
56
56
|
return false;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Escape HTML special characters in text content.
|
|
61
|
+
* @param {string} text
|
|
62
|
+
* @returns {string}
|
|
63
|
+
*/
|
|
64
|
+
function escapeHtml(text) {
|
|
65
|
+
return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
66
|
+
}
|
|
67
|
+
|
|
59
68
|
/**
|
|
60
69
|
* Resolve {{prop}} text node tokens by reading values from the closest custom element.
|
|
61
70
|
* @param {string} text
|
|
@@ -178,8 +187,8 @@ function serializeNode(node, emittedStyles, nonce) {
|
|
|
178
187
|
}
|
|
179
188
|
// Text node:
|
|
180
189
|
if (node.nodeType === 3) {
|
|
181
|
-
if (preformatted) return node.textContent || '';
|
|
182
|
-
return resolveTextTokens(node.textContent || '', node);
|
|
190
|
+
if (preformatted) return escapeHtml(node.textContent || '');
|
|
191
|
+
return resolveTextTokens(escapeHtml(node.textContent || ''), node);
|
|
183
192
|
}
|
|
184
193
|
// Comment:
|
|
185
194
|
if (node.nodeType === 8) {
|
|
@@ -272,9 +281,9 @@ async function* streamNode(node, emittedStyles, nonce) {
|
|
|
272
281
|
}
|
|
273
282
|
if (node.nodeType === 3) {
|
|
274
283
|
if (preformatted) {
|
|
275
|
-
yield node.textContent || '';
|
|
284
|
+
yield escapeHtml(node.textContent || '');
|
|
276
285
|
} else {
|
|
277
|
-
yield resolveTextTokens(node.textContent || '', node);
|
|
286
|
+
yield resolveTextTokens(escapeHtml(node.textContent || ''), node);
|
|
278
287
|
}
|
|
279
288
|
return;
|
|
280
289
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@symbiotejs/symbiote",
|
|
4
|
-
"version": "3.5.
|
|
4
|
+
"version": "3.5.8",
|
|
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",
|
package/types/core/warn.d.ts
CHANGED
package/types/core/warn.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"warn.d.ts","sourceRoot":"","sources":["../../core/warn.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"warn.d.ts","sourceRoot":"","sources":["../../core/warn.js"],"names":[],"mappings":"AAkCA,8BAHW,MAAM,WACF,GAAG,EAAA,QAIjB;AAMD,6BAHW,MAAM,WACF,GAAG,EAAA,QAIjB;AA3CD,qBADW;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CASjD"}
|
package/types/node/SSR.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SSR.d.ts","sourceRoot":"","sources":["../../node/SSR.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SSR.d.ts","sourceRoot":"","sources":["../../node/SSR.js"],"names":[],"mappings":"AAsSA;IAEE,8BAAmB;IACnB,8BAAmB;IAMnB;;;OAkEC;IAMD,uBAYC;IAkBD,yBAZW,MAAM,YACN;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAChB,OAAO,CAAC,MAAM,CAAC,CAgC3B;IAWD,+BALW,MAAM;;iBAEN;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAChB,MAAM,CAwBlB;IAWD,+BALW,MAAM;;iBAEN;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAChB,cAAc,CAAC,MAAM,CAAC,CAqBlC;CACF"}
|