ajo 0.0.7 → 0.0.10
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/index.cjs +24 -20
- package/index.min.js +1 -0
- package/package.json +11 -5
- package/readme.md +10 -18
- package/index.js +0 -203
- package/index.test.js +0 -63
package/index.cjs
CHANGED
|
@@ -32,10 +32,10 @@ __export(ajo_exports, {
|
|
|
32
32
|
stx: () => stx
|
|
33
33
|
});
|
|
34
34
|
module.exports = __toCommonJS(ajo_exports);
|
|
35
|
-
const Fragment = ({ children }) => children, h = (
|
|
35
|
+
const Fragment = ({ children }) => children, h = (nodeName, props, ...children) => {
|
|
36
36
|
children = children.length == 0 ? null : children.length == 1 ? children[0] : children;
|
|
37
|
-
return { children, ...props,
|
|
38
|
-
}, component = (setup) => ({ is, key,
|
|
37
|
+
return { children, ...props, nodeName };
|
|
38
|
+
}, component = (setup) => ({ is, key, host, ref, ...props }) => h(is ?? setup.is ?? "div", {
|
|
39
39
|
key,
|
|
40
40
|
...setup.host,
|
|
41
41
|
...host,
|
|
@@ -44,26 +44,27 @@ const Fragment = ({ children }) => children, h = (tagName, props, ...children) =
|
|
|
44
44
|
}), render = (h2, host) => {
|
|
45
45
|
let child = host.firstChild, node, byKey = keyed.get(host);
|
|
46
46
|
for (h2 of normalize(h2, host)) {
|
|
47
|
-
if (typeof h2 ==
|
|
47
|
+
if (typeof h2 == "string") {
|
|
48
48
|
for (node = child; node; node = node.nextSibling)
|
|
49
49
|
if (node.nodeType == 3)
|
|
50
50
|
break;
|
|
51
51
|
node ? node.data !== h2 && (node.data = h2) : node = document.createTextNode(h2);
|
|
52
|
-
} else if (
|
|
53
|
-
|
|
52
|
+
} else if (h2 instanceof Node) {
|
|
53
|
+
node = h2;
|
|
54
|
+
} else {
|
|
55
|
+
const { key, nodeName, skip, block, children, ref, ...props } = h2;
|
|
54
56
|
if (key != null && (node = byKey?.get(key)))
|
|
55
57
|
;
|
|
56
58
|
else
|
|
57
59
|
for (node = child; node; node = node.nextSibling)
|
|
58
|
-
if (node.localName ==
|
|
60
|
+
if (node.localName == nodeName)
|
|
59
61
|
break;
|
|
60
|
-
node ||= document.createElement(
|
|
61
|
-
key != null && (byKey ||= keyed.set(host,
|
|
62
|
+
node ||= document.createElement(nodeName);
|
|
63
|
+
key != null && (byKey ||= keyed.set(host, /* @__PURE__ */ new Map())).set(key, node);
|
|
62
64
|
update(props, node);
|
|
63
65
|
!(skip || block != null && every(deps.get(node), deps.set(node, block))) && render(children, node);
|
|
64
66
|
isFn(ref) && ref(node);
|
|
65
|
-
}
|
|
66
|
-
node = h2;
|
|
67
|
+
}
|
|
67
68
|
node === child ? child = child.nextSibling : before(host, child, node);
|
|
68
69
|
}
|
|
69
70
|
while (child) {
|
|
@@ -80,7 +81,7 @@ const Fragment = ({ children }) => children, h = (tagName, props, ...children) =
|
|
|
80
81
|
} catch (error) {
|
|
81
82
|
propagate(host, error);
|
|
82
83
|
}
|
|
83
|
-
}, provide = (host, key, value) => (provisions.get(host) ?? provisions.set(host,
|
|
84
|
+
}, provide = (host, key, value) => (provisions.get(host) ?? provisions.set(host, /* @__PURE__ */ new Map())).set(key, value), consume = (host, key, fallback) => {
|
|
84
85
|
let map;
|
|
85
86
|
while (host) {
|
|
86
87
|
if ((map = provisions.get(host)) && map.has(key))
|
|
@@ -102,21 +103,24 @@ const Fragment = ({ children }) => children, h = (tagName, props, ...children) =
|
|
|
102
103
|
throwTypeError("Cleaner", cleaner, fn);
|
|
103
104
|
(cleaners.get(host) ?? cleaners.set(host, /* @__PURE__ */ new Set())).add(cleaner);
|
|
104
105
|
}, clx = (o) => keys(o).filter((k) => o[k]).join(" ") || null, stx = (o) => entries(o).map((t) => t.join(":")).join(";") || null, keb = (o) => keys(o).reduce((r, k) => (r[k.replace(search, replace).toLowerCase()] = o[k], r), {});
|
|
105
|
-
const
|
|
106
|
-
const instance = new
|
|
107
|
-
const { set } = instance;
|
|
106
|
+
const wm = () => {
|
|
107
|
+
const instance = /* @__PURE__ */ new WeakMap(), { set } = instance;
|
|
108
108
|
instance.set = (key, value) => (set.call(instance, key, value), value);
|
|
109
109
|
return instance;
|
|
110
110
|
}, throwTypeError = (name, value, expected) => {
|
|
111
111
|
throw new TypeError(`Expected ${name} to be of type ${expected}, got ${typeof value} instead`);
|
|
112
|
-
}, every = (a, b) => a === b || isArray(a) && isArray(b) && a.length == b.length && a.every((v, i) => v === b[i]), apply = (o, { name, value }) => (o[name] = value, o), reduce = (list) => from(list).reduce(apply, {}), isFn = (v) => typeof v == fn, search = /([a-z0-9])([A-Z])/g, replace = "$1-$2",
|
|
112
|
+
}, every = (a, b) => a === b || isArray(a) && isArray(b) && a.length == b.length && a.every((v, i) => v === b[i]), apply = (o, { name, value }) => (o[name] = value, o), reduce = (list) => from(list).reduce(apply, {}), isFn = (v) => typeof v == fn, { keys, entries } = Object, { isArray, from } = Array, fn = "function", search = /([a-z0-9])([A-Z])/g, replace = "$1-$2", keyed = wm(), deps = wm(), memo = wm(), renders = wm(), provisions = wm(), interceptors = wm(), cleaners = wm(), cache = wm(), normalize = function* (h2, host) {
|
|
113
113
|
let type, buffer = "";
|
|
114
114
|
for (h2 of isFn(h2?.[Symbol.iterator]) ? h2 : [h2]) {
|
|
115
115
|
if (h2 == null || (type = typeof h2) == "boolean")
|
|
116
116
|
continue;
|
|
117
|
-
if (
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
if (type == "string" || type == "number") {
|
|
118
|
+
buffer += h2;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if ("nodeName" in Object(h2)) {
|
|
122
|
+
if (isFn(h2.nodeName)) {
|
|
123
|
+
yield* normalize(h2.nodeName(h2, host), host);
|
|
120
124
|
continue;
|
|
121
125
|
}
|
|
122
126
|
if (buffer) {
|
|
@@ -126,7 +130,7 @@ const createMap = (constructor, ...args) => {
|
|
|
126
130
|
yield h2;
|
|
127
131
|
continue;
|
|
128
132
|
}
|
|
129
|
-
|
|
133
|
+
isFn(h2[Symbol.iterator]) ? yield* normalize(h2, host) : buffer += h2;
|
|
130
134
|
}
|
|
131
135
|
if (buffer)
|
|
132
136
|
yield buffer;
|
package/index.min.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var ajo=(()=>{var d=Object.defineProperty;var F=Object.getOwnPropertyDescriptor;var O=Object.getOwnPropertyNames;var W=Object.prototype.hasOwnProperty;var I=(e,n)=>{for(var t in n)d(e,t,{get:n[t],enumerable:!0})},K=(e,n,t,r)=>{if(n&&typeof n=="object"||typeof n=="function")for(let i of O(n))!W.call(e,i)&&i!==t&&d(e,i,{get:()=>n[i],enumerable:!(r=F(n,i))||r.enumerable});return e};var L=e=>K(d({},"__esModule",{value:!0}),e);var re={};I(re,{Fragment:()=>Z,cleanup:()=>J,clx:()=>P,component:()=>q,consume:()=>G,h:()=>A,intercept:()=>H,keb:()=>R,propagate:()=>E,provide:()=>D,refresh:()=>j,render:()=>c,stx:()=>Q});const Z=({children:e})=>e,A=(e,n,...t)=>(t=t.length==0?null:t.length==1?t[0]:t,{children:t,...n,nodeName:e}),q=e=>({is:n,key:t,host:r,ref:i,...l})=>A(n??e.is??"div",{key:t,...e.host,...r,skip:!0,ref:o=>(j(o,l,e),f(i)&&i(o))}),c=(e,n)=>{let t=n.firstChild,r,i=x.get(n);for(e of m(e,n)){if(typeof e=="string"){for(r=t;r&&r.nodeType!=3;r=r.nextSibling);r?r.data!==e&&(r.data=e):r=document.createTextNode(e)}else if(e instanceof Node)r=e;else{const{key:l,nodeName:o,skip:M,block:p,children:z,ref:b,...B}=e;if(!(l!=null&&(r=i?.get(l))))for(r=t;r&&r.localName!=o;r=r.nextSibling);r||=document.createElement(o),l!=null&&(i||=x.set(n,new Map)).set(l,r),ne(B,r),!(M||p!=null&&U(S.get(r),S.set(r,p)))&&c(z,r),f(b)&&b(r)}r===t?t=t.nextSibling:te(n,t,r)}for(;t;){const l=t.nextSibling;n.removeChild(t).nodeType==1&&C(t),t=l}},j=(e,n,t)=>{try{t&&!f(t)&&s("Setup",t,u),n=n?v.set(e,{...t?.props,...n}):v.get(e)??{},c((N.get(e)??N.set(e,t(n,e)))(n,e),e)}catch(r){E(e,r)}},D=(e,n,t)=>(g.get(e)??g.set(e,new Map)).set(n,t),G=(e,n,t)=>{let r;for(;e;){if((r=g.get(e))&&r.has(n))return r.get(n);e=e.parentNode}return t},H=(e,n)=>{f(n)||s("Interceptor",n,u),$.set(e,n)},E=(e,n)=>{for(let t;e;e=e.parentNode)if(t=$.get(e))return c(e,t(n));throw n},J=(e,n)=>{f(n)||s("Cleaner",n,u),(y.get(e)??y.set(e,new Set)).add(n)},P=e=>T(e).filter(n=>e[n]).join(" ")||null,Q=e=>Y(e).map(n=>n.join(":")).join(";")||null,R=e=>T(e).reduce((n,t)=>(n[t.replace(h,ee).toLowerCase()]=e[t],n),{}),a=()=>{const e=new WeakMap,{set:n}=e;return e.set=(t,r)=>(n.call(e,t,r),r),e},s=(e,n,t)=>{throw new TypeError(`Expected ${e} to be of type ${t}, got ${typeof n} instead`)},U=(e,n)=>e===n||w(e)&&w(n)&&e.length==n.length&&e.every((t,r)=>t===n[r]),V=(e,{name:n,value:t})=>(e[n]=t,e),X=e=>_(e).reduce(V,{}),f=e=>typeof e==u,{keys:T,entries:Y}=Object,{isArray:w,from:_}=Array,u="function",h=/([a-z0-9])([A-Z])/g,ee="$1-$2",x=a(),S=a(),v=a(),N=a(),g=a(),$=a(),y=a(),k=a(),m=function*(e,n){let t,r="";for(e of f(e?.[Symbol.iterator])?e:[e])if(!(e==null||(t=typeof e)=="boolean")){if(t=="string"||t=="number"){r+=e;continue}if("nodeName"in Object(e)){if(f(e.nodeName)){yield*m(e.nodeName(e,n),n);continue}r&&(yield r,r=""),yield e;continue}f(e[Symbol.iterator])?yield*m(e,n):r+=e}r&&(yield r)},ne=(e,n)=>{const t=k.get(n)??(n.hasAttributes()?X(n.attributes):{});for(const r in{...t,...e}){let i=e[r];if(i!==t[r]){if(r.startsWith("set:")){n[r.slice(4)]=i;continue}if(i===!0)i="";else if(i==null||i===!1){n.removeAttribute(r);continue}n.setAttribute(r,i)}}k.set(n,e)},te=(e,n,t)=>{if(t.contains?.(document.activeElement)){const r=t.nextSibling;for(;n&&n!==t;){const i=n.nextSibling;e.insertBefore(n,r),n=i}}else e.insertBefore(t,n)},C=e=>{for(const n of e.children)C(n);for(const n of y.get(e)??[])n(e)};return L(re);})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ajo",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"description": "ajo is a JavaScript view library for building user interfaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "index.js",
|
|
@@ -8,13 +8,19 @@
|
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
10
|
"import": "./index.js",
|
|
11
|
-
"require": "./index.cjs"
|
|
11
|
+
"require": "./index.cjs",
|
|
12
|
+
"browser": "./index.min.js"
|
|
12
13
|
}
|
|
13
14
|
},
|
|
15
|
+
"files": [
|
|
16
|
+
"index.min.js"
|
|
17
|
+
],
|
|
14
18
|
"scripts": {
|
|
15
|
-
"build": "
|
|
16
|
-
"
|
|
17
|
-
"
|
|
19
|
+
"build": "npm run build:require && npm run build:browser",
|
|
20
|
+
"build:require": "esbuild --format=cjs --out-extension:.js=.cjs --outdir=. index.js",
|
|
21
|
+
"build:browser": "esbuild --format=iife --out-extension:.js=.min.js --outdir=. --global-name=ajo --minify index.js",
|
|
22
|
+
"release": "npm test && git commit -am \"$npm_package_version\" && git tag $npm_package_version && git push && git push --tags && npm publish",
|
|
23
|
+
"test": "npm run build && uvu"
|
|
18
24
|
},
|
|
19
25
|
"repository": "cristianfalcone/ajo",
|
|
20
26
|
"author": "Cristian Falcone",
|
package/readme.md
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
# ajo
|
|
2
2
|
ajo is a JavaScript view library for building user interfaces
|
|
3
3
|
|
|
4
|
+
## Install
|
|
5
|
+
|
|
4
6
|
```sh
|
|
5
7
|
npm install ajo
|
|
6
8
|
```
|
|
7
9
|
|
|
8
|
-
##
|
|
9
|
-
To keep the UI in sync, ajo uses a technique called incremental DOM.
|
|
10
|
-
It is a way to build UI components without the need to keep previous virtual DOM in memory.
|
|
11
|
-
Instead, generated virtual DOM is diffed against the actual DOM, and changes are applied along the way.
|
|
12
|
-
This reduces memory usage and makes ajo code more simple and concise. As a result, ajo is easy to read and maintain, but lacks perfomance oportunities that diffing two virtual DOM trees can provide.
|
|
10
|
+
## Render JSX to a DOM element
|
|
13
11
|
|
|
14
12
|
```jsx
|
|
15
13
|
/** @jsx h */
|
|
@@ -17,13 +15,10 @@ import { h, render } from 'ajo'
|
|
|
17
15
|
|
|
18
16
|
document.body.innerHTML = '<div>Hello World</div>'
|
|
19
17
|
|
|
20
|
-
render(
|
|
18
|
+
render(<div>Goodbye World</div>, document.body)
|
|
21
19
|
```
|
|
22
20
|
|
|
23
|
-
## Stateless
|
|
24
|
-
As a way to reuse markup snipets, ajo uses simple synchroneous functions that return virtual DOM.
|
|
25
|
-
This type of components are ment to be "consumers" of data.
|
|
26
|
-
No state is preserved between invocations, so generated virtual DOM should rely exclusively on function's arguments.
|
|
21
|
+
## Stateless Component
|
|
27
22
|
|
|
28
23
|
```jsx
|
|
29
24
|
/** @jsx h */
|
|
@@ -31,20 +26,17 @@ import { h, render } from 'ajo'
|
|
|
31
26
|
|
|
32
27
|
const Greet = ({ name }) => <div>Hello {name}</div>
|
|
33
28
|
|
|
34
|
-
render(
|
|
29
|
+
render(<Greet name="World" />, document.body)
|
|
35
30
|
```
|
|
36
31
|
|
|
37
|
-
## Stateful
|
|
38
|
-
Since ajo does not store previous virtual DOM, stateful components rely on a DOM node to preserve its state between UI updates.
|
|
39
|
-
This DOM node is called a host node (similar to a Web Component host node).
|
|
40
|
-
State is declared in a generator function local scope.
|
|
41
|
-
Then ajo asociates the returned iterator with the host, and updates host children nodes each time, retrieving iterator's next value. Lifecycle of these components are closely related to its host nodes, and generator function provides a way to manage them.
|
|
32
|
+
## Stateful Component
|
|
42
33
|
|
|
43
34
|
```jsx
|
|
44
35
|
/** @jsx h */
|
|
45
|
-
import { h, component,
|
|
36
|
+
import { h, component, refresh, render } from 'ajo'
|
|
46
37
|
|
|
47
38
|
const Counter = component(({ start = 0 }, host) => {
|
|
39
|
+
|
|
48
40
|
let count = start
|
|
49
41
|
|
|
50
42
|
const increment = () => {
|
|
@@ -58,5 +50,5 @@ const Counter = component(({ start = 0 }, host) => {
|
|
|
58
50
|
</button>
|
|
59
51
|
})
|
|
60
52
|
|
|
61
|
-
render(
|
|
53
|
+
render(<Counter start={1} />, document.body)
|
|
62
54
|
```
|
package/index.js
DELETED
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
export const
|
|
2
|
-
Fragment = ({ children }) => children,
|
|
3
|
-
|
|
4
|
-
h = (tagName, props, ...children) => {
|
|
5
|
-
children = children.length == 0 ? null : children.length == 1 ? children[0] : children
|
|
6
|
-
return { children, ...props, tagName }
|
|
7
|
-
},
|
|
8
|
-
|
|
9
|
-
component = setup => ({ is, key, ref, host, ...props }) => h(is ?? setup.is ?? 'div', {
|
|
10
|
-
key, ...setup.host, ...host, skip: true, ref: host => (refresh(host, props, setup), isFn(ref) && ref(host))
|
|
11
|
-
}),
|
|
12
|
-
|
|
13
|
-
render = (h, host) => {
|
|
14
|
-
|
|
15
|
-
let child = host.firstChild, node, byKey = keyed.get(host)
|
|
16
|
-
|
|
17
|
-
for (h of normalize(h, host)) {
|
|
18
|
-
|
|
19
|
-
if (typeof h == str) {
|
|
20
|
-
|
|
21
|
-
for (node = child; node; node = node.nextSibling) if (node.nodeType == 3) break
|
|
22
|
-
node ? node.data !== h && (node.data = h) : node = document.createTextNode(h)
|
|
23
|
-
|
|
24
|
-
} else if (hasOwn(h, 'tagName')) {
|
|
25
|
-
|
|
26
|
-
const { tagName, key, skip, ref, children, block, ...props } = h
|
|
27
|
-
|
|
28
|
-
if (key != null && (node = byKey?.get(key)));
|
|
29
|
-
else for (node = child; node; node = node.nextSibling) if (node.localName == tagName) break
|
|
30
|
-
|
|
31
|
-
node ||= document.createElement(tagName)
|
|
32
|
-
|
|
33
|
-
key != null && (byKey ||= keyed.set(host, createMap(Map))).set(key, node)
|
|
34
|
-
|
|
35
|
-
update(props, node)
|
|
36
|
-
|
|
37
|
-
!(skip || block != null && every(deps.get(node), deps.set(node, block))) && render(children, node)
|
|
38
|
-
|
|
39
|
-
isFn(ref) && ref(node)
|
|
40
|
-
|
|
41
|
-
} else node = h
|
|
42
|
-
|
|
43
|
-
node === child ? child = child.nextSibling : before(host, child, node)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
while (child) {
|
|
47
|
-
const next = child.nextSibling
|
|
48
|
-
host.removeChild(child).nodeType == 1 && dispose(child)
|
|
49
|
-
child = next
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
|
|
53
|
-
refresh = (host, props, setup) => {
|
|
54
|
-
try {
|
|
55
|
-
if (setup && !isFn(setup)) throwTypeError('Setup', setup, fn)
|
|
56
|
-
props = props ? memo.set(host, { ...setup?.props, ...props }) : memo.get(host) ?? {}
|
|
57
|
-
render((renders.get(host) ?? renders.set(host, setup(props, host)))(props, host), host)
|
|
58
|
-
} catch (error) {
|
|
59
|
-
propagate(host, error)
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
|
|
63
|
-
provide = (host, key, value) => (provisions.get(host) ?? provisions.set(host, createMap(Map))).set(key, value),
|
|
64
|
-
|
|
65
|
-
consume = (host, key, fallback) => {
|
|
66
|
-
let map
|
|
67
|
-
while (host) {
|
|
68
|
-
if ((map = provisions.get(host)) && map.has(key)) return map.get(key)
|
|
69
|
-
host = host.parentNode
|
|
70
|
-
}
|
|
71
|
-
return fallback
|
|
72
|
-
},
|
|
73
|
-
|
|
74
|
-
intercept = (host, interceptor) => {
|
|
75
|
-
if (!isFn(interceptor)) throwTypeError('Interceptor', interceptor, fn)
|
|
76
|
-
interceptors.set(host, interceptor)
|
|
77
|
-
},
|
|
78
|
-
|
|
79
|
-
propagate = (host, error) => {
|
|
80
|
-
for (let interceptor; host; host = host.parentNode)
|
|
81
|
-
if (interceptor = interceptors.get(host)) return render(host, interceptor(error))
|
|
82
|
-
throw error
|
|
83
|
-
},
|
|
84
|
-
|
|
85
|
-
cleanup = (host, cleaner) => {
|
|
86
|
-
if (!isFn(cleaner)) throwTypeError('Cleaner', cleaner, fn);
|
|
87
|
-
(cleaners.get(host) ?? cleaners.set(host, new Set)).add(cleaner)
|
|
88
|
-
},
|
|
89
|
-
|
|
90
|
-
clx = o => keys(o).filter(k => o[k]).join(' ') || null,
|
|
91
|
-
stx = o => entries(o).map(t => t.join(':')).join(';') || null,
|
|
92
|
-
keb = o => keys(o).reduce((r, k) => ((r[k.replace(search, replace).toLowerCase()] = o[k]), r), {})
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
createMap = (constructor, ...args) => {
|
|
96
|
-
const instance = new constructor(...args)
|
|
97
|
-
const { set } = instance
|
|
98
|
-
instance.set = (key, value) => (set.call(instance, key, value), value)
|
|
99
|
-
return instance
|
|
100
|
-
},
|
|
101
|
-
|
|
102
|
-
throwTypeError = (name, value, expected) => {
|
|
103
|
-
throw new TypeError(`Expected ${name} to be of type ${expected}, got ${typeof value} instead`)
|
|
104
|
-
},
|
|
105
|
-
|
|
106
|
-
every = (a, b) => a === b || isArray(a) && isArray(b) && a.length == b.length && a.every((v, i) => v === b[i]),
|
|
107
|
-
apply = (o, { name, value }) => ((o[name] = value), o),
|
|
108
|
-
reduce = list => from(list).reduce(apply, {}),
|
|
109
|
-
isFn = v => typeof v == fn,
|
|
110
|
-
|
|
111
|
-
search = /([a-z0-9])([A-Z])/g,
|
|
112
|
-
replace = '$1-$2',
|
|
113
|
-
str = 'string',
|
|
114
|
-
fn = 'function',
|
|
115
|
-
|
|
116
|
-
{ keys, entries, hasOwn } = Object,
|
|
117
|
-
{ isArray, from } = Array,
|
|
118
|
-
wm = WeakMap,
|
|
119
|
-
|
|
120
|
-
deps = createMap(wm),
|
|
121
|
-
memo = createMap(wm),
|
|
122
|
-
keyed = createMap(wm),
|
|
123
|
-
cache = createMap(wm),
|
|
124
|
-
renders = createMap(wm),
|
|
125
|
-
cleaners = createMap(wm),
|
|
126
|
-
provisions = createMap(wm),
|
|
127
|
-
interceptors = createMap(wm),
|
|
128
|
-
|
|
129
|
-
normalize = function* (h, host) {
|
|
130
|
-
|
|
131
|
-
let type, buffer = ''
|
|
132
|
-
|
|
133
|
-
for (h of isFn(h?.[Symbol.iterator]) ? h : [h]) {
|
|
134
|
-
|
|
135
|
-
if (h == null || (type = typeof h) == 'boolean') continue
|
|
136
|
-
|
|
137
|
-
if (h instanceof Node || hasOwn(h, 'tagName')) {
|
|
138
|
-
|
|
139
|
-
if (isFn(h.tagName)) {
|
|
140
|
-
yield* normalize(h.tagName(h, host), host)
|
|
141
|
-
continue
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (buffer) {
|
|
145
|
-
yield buffer
|
|
146
|
-
buffer = ''
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
yield h
|
|
150
|
-
continue
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
(type == str || !isFn(h[Symbol.iterator])) ? buffer += h : yield* normalize(h, host)
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (buffer) yield buffer
|
|
157
|
-
},
|
|
158
|
-
|
|
159
|
-
update = (props, host) => {
|
|
160
|
-
|
|
161
|
-
const prev = cache.get(host) ?? (host.hasAttributes() ? reduce(host.attributes) : {})
|
|
162
|
-
|
|
163
|
-
for (const name in { ...prev, ...props }) {
|
|
164
|
-
|
|
165
|
-
let value = props[name]
|
|
166
|
-
|
|
167
|
-
if (value === prev[name]) continue
|
|
168
|
-
|
|
169
|
-
if (name.startsWith('set:')) {
|
|
170
|
-
host[name.slice(4)] = value
|
|
171
|
-
continue
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (value === true) value = ''
|
|
175
|
-
else if (value == null || value === false) {
|
|
176
|
-
host.removeAttribute(name)
|
|
177
|
-
continue
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
host.setAttribute(name, value)
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
cache.set(host, props)
|
|
184
|
-
},
|
|
185
|
-
|
|
186
|
-
before = (host, child, node) => {
|
|
187
|
-
if (node.contains?.(document.activeElement)) {
|
|
188
|
-
|
|
189
|
-
const ref = node.nextSibling
|
|
190
|
-
|
|
191
|
-
while (child && child !== node) {
|
|
192
|
-
const next = child.nextSibling
|
|
193
|
-
host.insertBefore(child, ref)
|
|
194
|
-
child = next
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
} else host.insertBefore(node, child)
|
|
198
|
-
},
|
|
199
|
-
|
|
200
|
-
dispose = host => {
|
|
201
|
-
for (const child of host.children) dispose(child)
|
|
202
|
-
for (const cleaner of cleaners.get(host) ?? []) cleaner(host)
|
|
203
|
-
}
|
package/index.test.js
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import 'backdom/register'
|
|
2
|
-
import { suite } from 'uvu'
|
|
3
|
-
import * as assert from 'uvu/assert'
|
|
4
|
-
import { component, h, render } from './index.js'
|
|
5
|
-
|
|
6
|
-
let it
|
|
7
|
-
|
|
8
|
-
// ----------------------------------------------------------------------------
|
|
9
|
-
|
|
10
|
-
it = suite('h')
|
|
11
|
-
|
|
12
|
-
it('should create empty vnode', () => {
|
|
13
|
-
const vnode = h('div')
|
|
14
|
-
assert.equal(vnode, { tagName: 'div', children: null })
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
it('should create vnode with props', () => {
|
|
18
|
-
assert.equal(h('div', { id: 'app' }), { tagName: 'div', children: null, id: 'app' })
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
it('should create vnode with one string child', () => {
|
|
22
|
-
assert.equal(h('div', null, 'foo'), { tagName: 'div', children: 'foo' })
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
it('should create vnode with one vnode child', () => {
|
|
26
|
-
const child = h('span')
|
|
27
|
-
const vnode = h('div', null, child)
|
|
28
|
-
assert.equal(vnode.children, { tagName: 'span', children: null })
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
it('should allow children as prop', () => {
|
|
32
|
-
const child = h('span')
|
|
33
|
-
const vnode = h('div', { children: child })
|
|
34
|
-
assert.equal(vnode.children, { tagName: 'span', children: null })
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
it.run()
|
|
38
|
-
|
|
39
|
-
// ----------------------------------------------------------------------------
|
|
40
|
-
|
|
41
|
-
it = suite('render')
|
|
42
|
-
|
|
43
|
-
it('should render a vnode', () => {
|
|
44
|
-
const host = document.createElement('div')
|
|
45
|
-
render(h('div', { foo: 'bar' }, 'foobar'), host)
|
|
46
|
-
assert.snapshot(host.innerHTML, '<div foo="bar">foobar</div>')
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it('should render a stateless component', () => {
|
|
50
|
-
const host = document.createElement('div')
|
|
51
|
-
const Foo = ({ foo, children }) => h('div', { foo }, children)
|
|
52
|
-
render(h(Foo, { foo: 'bar' }, h('span', null, 'foobar')), host)
|
|
53
|
-
assert.snapshot(host.innerHTML, '<div foo="bar"><span>foobar</span></div>')
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
it('should render a stateful component', () => {
|
|
57
|
-
const host = document.createElement('div')
|
|
58
|
-
const Foo = component(() => ({ foo, children }) => h('div', { foo }, children))
|
|
59
|
-
render(h(Foo, { is: 'foo-component', foo: 'bar' }, h('span', null, 'foobar')), host)
|
|
60
|
-
assert.snapshot(host.innerHTML, '<foo-component><div foo="bar"><span>foobar</span></div></foo-component>')
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
it.run()
|