@symbiotejs/symbiote 3.6.0 → 3.7.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/README.md +8 -0
- package/core/html.js +38 -8
- package/core/tpl-processors.js +19 -11
- package/node/SSR.js +26 -1
- package/package.json +1 -1
- package/types/core/html.d.ts.map +1 -1
- package/types/core/tpl-processors.d.ts.map +1 -1
- package/types/node/SSR.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -330,6 +330,7 @@ CSS values are parsed automatically - quoted strings become strings, numbers bec
|
|
|
330
330
|
## Best for
|
|
331
331
|
|
|
332
332
|
- **Complex widgets** embedded in any host application
|
|
333
|
+
- **Low-code HTML-based solutions** - simple declarative everything
|
|
333
334
|
- **Micro frontends** - standard custom elements, no framework coupling
|
|
334
335
|
- **Reusable component libraries** - works in React, Vue, Angular, or plain HTML
|
|
335
336
|
- **SSR-powered apps** - lightweight server rendering without framework lock-in
|
|
@@ -359,6 +360,13 @@ All modern browsers: Chrome, Firefox, Safari, Edge, Opera.
|
|
|
359
360
|
- [AI Reference](https://github.com/symbiotejs/symbiote.js/blob/main/AI_REFERENCE.md)
|
|
360
361
|
- [Changelog](https://github.com/symbiotejs/symbiote.js/blob/main/CHANGELOG.md)
|
|
361
362
|
|
|
363
|
+
## Related articles
|
|
364
|
+
|
|
365
|
+
- [Symbiote.js: superpowers for Web Components](https://dev.to/foxeyes/symbiotejs-superpowers-for-web-components-1gid)
|
|
366
|
+
- [Symbiote.js: v3 highlights](https://dev.to/foxeyes/symbiotejs-v3-web-components-with-ssr-in-6kb-10n6)
|
|
367
|
+
- [Symbiote.js vs Lit](https://dev.to/foxeyes/lit-vs-symbiotejs-22gj)
|
|
368
|
+
- [JSDA Stack - A Revolutionary Simple Approach to Build Modern Web](https://dev.to/foxeyes/jsda-kit-a-revolutionary-simple-approach-to-build-modern-web-1dip)
|
|
369
|
+
|
|
362
370
|
**Questions or proposals? Welcome to [Symbiote Discussions](https://github.com/symbiotejs/symbiote.js/discussions)!** ❤️
|
|
363
371
|
|
|
364
372
|
---
|
package/core/html.js
CHANGED
|
@@ -10,6 +10,35 @@ export const RESERVED_ATTRIBUTES = [
|
|
|
10
10
|
DICT.CTX_NAME_ATTR,
|
|
11
11
|
];
|
|
12
12
|
|
|
13
|
+
const RESERVED_ATTRIBUTES_SET = new Set(RESERVED_ATTRIBUTES);
|
|
14
|
+
const SELF_CLOSING_CUSTOM_ELEMENT_RE = /<([a-z][.0-9_a-z]*-[\-.0-9_a-z]*)(\s+(?:"[^"]*"|'[^']*'|[^'"<>])*)?\/>/gi;
|
|
15
|
+
|
|
16
|
+
function hasImmediateClosingTag(htmlString, startIdx, tagName) {
|
|
17
|
+
let idx = startIdx;
|
|
18
|
+
let len = htmlString.length;
|
|
19
|
+
while (idx < len) {
|
|
20
|
+
let code = htmlString.charCodeAt(idx);
|
|
21
|
+
if (code !== 32 && code !== 9 && code !== 10 && code !== 12 && code !== 13) break;
|
|
22
|
+
idx++;
|
|
23
|
+
}
|
|
24
|
+
if (htmlString.charCodeAt(idx) !== 60 || htmlString.charCodeAt(idx + 1) !== 47) return false;
|
|
25
|
+
let tagStart = idx + 2;
|
|
26
|
+
if (htmlString.charCodeAt(tagStart + tagName.length) !== 62) return false;
|
|
27
|
+
let closingTagName = htmlString.slice(tagStart, tagStart + tagName.length);
|
|
28
|
+
return closingTagName === tagName || closingTagName.toLowerCase() === tagName.toLowerCase();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function closeSelfClosingCustomElements(resultHtml) {
|
|
32
|
+
if (resultHtml.indexOf('/>') === -1) return resultHtml;
|
|
33
|
+
return resultHtml.replace(SELF_CLOSING_CUSTOM_ELEMENT_RE, (match, tagName, attrs = '', offset, htmlString) => {
|
|
34
|
+
let openTag = `<${tagName}${attrs && attrs.trimEnd()}>`;
|
|
35
|
+
if (hasImmediateClosingTag(htmlString, offset + match.length, tagName)) {
|
|
36
|
+
return openTag;
|
|
37
|
+
}
|
|
38
|
+
return `${openTag}</${tagName}>`;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
13
42
|
/** @typedef {Record<keyof import('./Symbiote.js').Symbiote, String>} BindDescriptor */
|
|
14
43
|
|
|
15
44
|
/**
|
|
@@ -20,19 +49,20 @@ export const RESERVED_ATTRIBUTES = [
|
|
|
20
49
|
*/
|
|
21
50
|
export function html(parts, ...props) {
|
|
22
51
|
let resultHtml = '';
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
52
|
+
let propsLength = props.length;
|
|
53
|
+
for (let idx = 0; idx < parts.length; idx++) {
|
|
54
|
+
resultHtml += parts[idx];
|
|
55
|
+
if (idx >= propsLength) continue;
|
|
26
56
|
let val = props[idx];
|
|
27
57
|
if (val === undefined || val === null) {
|
|
28
58
|
errMsg(15, val);
|
|
29
|
-
|
|
59
|
+
continue;
|
|
30
60
|
}
|
|
31
|
-
if (val
|
|
61
|
+
if (val.constructor === Object) {
|
|
32
62
|
let bindStr = '';
|
|
33
63
|
// @ts-expect-error
|
|
34
64
|
for (let key in val) {
|
|
35
|
-
if (
|
|
65
|
+
if (RESERVED_ATTRIBUTES_SET.has(key)) {
|
|
36
66
|
resultHtml += ` ${key}="${val[key]}"`;
|
|
37
67
|
} else {
|
|
38
68
|
bindStr += `${key}:${val[key]};`;
|
|
@@ -42,8 +72,8 @@ export function html(parts, ...props) {
|
|
|
42
72
|
} else {
|
|
43
73
|
resultHtml += val;
|
|
44
74
|
}
|
|
45
|
-
}
|
|
46
|
-
return resultHtml;
|
|
75
|
+
}
|
|
76
|
+
return closeSelfClosingCustomElements(resultHtml);
|
|
47
77
|
}
|
|
48
78
|
|
|
49
79
|
export default html;
|
package/core/tpl-processors.js
CHANGED
|
@@ -105,17 +105,25 @@ function getTextNodesWithTokens(el) {
|
|
|
105
105
|
let isCustomEl = el instanceof HTMLElement && el.localName?.includes('-');
|
|
106
106
|
let node;
|
|
107
107
|
let result = [];
|
|
108
|
-
let
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
108
|
+
let acceptNode = (txt) => {
|
|
109
|
+
if (isCustomEl && !isOwnNode(txt, el)) return NodeFilter.FILTER_REJECT;
|
|
110
|
+
return !txt.parentElement?.hasAttribute(DICT.TEXT_NODE_SKIP_ATTR)
|
|
111
|
+
&& txt.textContent.includes(DICT.TEXT_NODE_OPEN_TOKEN)
|
|
112
|
+
&& txt.textContent.includes(DICT.TEXT_NODE_CLOSE_TOKEN)
|
|
113
|
+
? NodeFilter.FILTER_ACCEPT
|
|
114
|
+
: NodeFilter.FILTER_REJECT;
|
|
115
|
+
};
|
|
116
|
+
let walk = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, acceptNode);
|
|
117
|
+
if (globalThis.__SYMBIOTE_SSR) {
|
|
118
|
+
while ((node = walk.nextNode())) {
|
|
119
|
+
if (acceptNode(node) === NodeFilter.FILTER_ACCEPT) {
|
|
120
|
+
result.push(node);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
while ((node = walk.nextNode())) {
|
|
125
|
+
result.push(node);
|
|
126
|
+
}
|
|
119
127
|
}
|
|
120
128
|
return result;
|
|
121
129
|
}
|
package/node/SSR.js
CHANGED
|
@@ -304,11 +304,36 @@ export class SSR {
|
|
|
304
304
|
static async init() {
|
|
305
305
|
// @ts-ignore
|
|
306
306
|
let { parseHTML } = /** @type {any} */ (await import('linkedom'));
|
|
307
|
-
let { document, window, HTMLElement, customElements, DocumentFragment, NodeFilter, MutationObserver } = parseHTML('<!DOCTYPE html><html><head></head><body></body></html>');
|
|
307
|
+
let { document, window, HTMLElement, customElements, DocumentFragment, NodeFilter, MutationObserver, Text } = parseHTML('<!DOCTYPE html><html><head></head><body></body></html>');
|
|
308
308
|
|
|
309
309
|
SSR.#doc = document;
|
|
310
310
|
SSR.#win = window;
|
|
311
311
|
|
|
312
|
+
// Polyfill splitText for linkedom:
|
|
313
|
+
if (!Text.prototype.splitText) {
|
|
314
|
+
Text.prototype.splitText = function(offset) {
|
|
315
|
+
let nextNode = document.createTextNode(this.textContent.substring(offset));
|
|
316
|
+
this.textContent = this.textContent.substring(0, offset);
|
|
317
|
+
if (this.parentNode) {
|
|
318
|
+
this.parentNode.insertBefore(nextNode, this.nextSibling);
|
|
319
|
+
}
|
|
320
|
+
return nextNode;
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Polyfill textContent on DocumentFragment for linkedom:
|
|
325
|
+
if (!('textContent' in DocumentFragment.prototype) || !Object.getOwnPropertyDescriptor(DocumentFragment.prototype, 'textContent')?.get) {
|
|
326
|
+
Object.defineProperty(DocumentFragment.prototype, 'textContent', {
|
|
327
|
+
get() {
|
|
328
|
+
return this.childNodes.map(n => n.nodeType === 3 ? n.textContent : (n.textContent || '')).join('');
|
|
329
|
+
},
|
|
330
|
+
set(val) {
|
|
331
|
+
while (this.firstChild) this.removeChild(this.firstChild);
|
|
332
|
+
this.appendChild(document.createTextNode(val));
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
312
337
|
// Polyfill CSSStyleSheet for linkedom:
|
|
313
338
|
if (!window.CSSStyleSheet || !('replaceSync' in (window.CSSStyleSheet?.prototype || {}))) {
|
|
314
339
|
class SSRStyleSheet {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@symbiotejs/symbiote",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.7.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",
|
package/types/core/html.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../core/html.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../core/html.js"],"names":[],"mappings":"AAiDA,qBALa,CAAC,SACH,oBAAoB,YACpB,CAAC;QAAO,MAAM;CAAS,GAAG,cAAc,YAAY,CAAC,CAAC,EAAE,UA8BlE;AAxED,kCADW,QAAQ,CAOjB;;6BA+BY,MAAM,CAAC,MAAM,qCAAgC,SAAS"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tpl-processors.d.ts","sourceRoot":"","sources":["../../core/tpl-processors.js"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"tpl-processors.d.ts","sourceRoot":"","sources":["../../core/tpl-processors.js"],"names":[],"mappings":";0BAmIgD,CAAC,SAApC,qCAAkC,MACpC,gBAAgB,SAChB,CAAC;;iCAjIqB,uBAAuB"}
|
package/types/node/SSR.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SSR.d.ts","sourceRoot":"","sources":["../../node/SSR.js"],"names":[],"mappings":"AAsSA;IAEE,8BAAmB;IACnB,8BAAmB;IAMnB;;;
|
|
1
|
+
{"version":3,"file":"SSR.d.ts","sourceRoot":"","sources":["../../node/SSR.js"],"names":[],"mappings":"AAsSA;IAEE,8BAAmB;IACnB,8BAAmB;IAMnB;;;OA2FC;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"}
|