@chronocide/hyper 0.3.1 → 0.3.2
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 +20 -16
- package/dist/hyper.d.ts +22 -24
- package/dist/hyper.js +68 -69
- package/package.json +8 -6
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
<div
|
|
1
|
+
<div align="center">
|
|
2
2
|
<h1>@chronocide/hyper</h1>
|
|
3
|
-
<p>Tiny
|
|
3
|
+
<p>Tiny DOM library</p>
|
|
4
4
|
</div>
|
|
5
5
|
|
|
6
|
-
<div
|
|
6
|
+
<div align="center">
|
|
7
7
|
<a href="/LICENSE">
|
|
8
8
|
<img alt="License AGPLv3" src="https://img.shields.io/badge/license-AGPLv3-blue.svg" />
|
|
9
9
|
</a>
|
|
@@ -13,10 +13,13 @@
|
|
|
13
13
|
</a>
|
|
14
14
|
</div>
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
---
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
`hyper` is a tiny TypeScript library designed to ease working with the DOM. `hyper` tries to be as simple as possible whilst maintaining good developer experience and currently features:
|
|
19
|
+
|
|
20
|
+
- Simple wrapper around native API's (DOM)
|
|
21
|
+
- No vDOM, all state is stored within the DOM[*](#exception)
|
|
22
|
+
- List caching
|
|
20
23
|
|
|
21
24
|
<p id="exception"><code>document</code> is stored internally, this prevents binding it for every single call.</p>
|
|
22
25
|
|
|
@@ -28,7 +31,7 @@ npm i @chronocide/hyper
|
|
|
28
31
|
|
|
29
32
|
## Usage
|
|
30
33
|
|
|
31
|
-
```
|
|
34
|
+
```ts
|
|
32
35
|
import h from '@chronocide/hyper';
|
|
33
36
|
|
|
34
37
|
const img = h('img')({ src: '/cat.png' })(); // HTMLImageElement
|
|
@@ -38,30 +41,31 @@ document.body.appendChild(img);
|
|
|
38
41
|
|
|
39
42
|
### List
|
|
40
43
|
|
|
41
|
-
|
|
44
|
+
List items can be cached using `list`, only updated if the data is changed; order does not matter. Data does not need to be unique, as duplicate nodes are cloned.
|
|
42
45
|
|
|
43
|
-
```
|
|
46
|
+
```ts
|
|
44
47
|
import type { Component } from '@chronocide/hyper';
|
|
45
|
-
|
|
48
|
+
|
|
49
|
+
import h, { list } from '@chronocide/hyper';
|
|
46
50
|
|
|
47
51
|
type Planet = { id: string; name: string };
|
|
48
52
|
|
|
49
|
-
const
|
|
53
|
+
const planets: Planet[] = [
|
|
50
54
|
{ id: 'jupiter', name: 'Jupiter' },
|
|
51
55
|
{ id: 'mars', name: 'Mars' },
|
|
52
56
|
{ id: 'pluto', name: 'Pluto' }
|
|
53
57
|
];
|
|
54
58
|
|
|
55
|
-
const ul = h('ul')()();
|
|
59
|
+
const ul = h('ul')()(); // <ul></ul>
|
|
56
60
|
const component: Component<Planet> = planet => h('li')()(planet.name);
|
|
57
61
|
|
|
58
62
|
const update = list<Planet>(component)(ul);
|
|
59
|
-
update(
|
|
63
|
+
update(planets); // <ul><li>Jupiter</li><li>Mars</li><li>Pluto</li></ul>
|
|
60
64
|
```
|
|
61
65
|
|
|
62
|
-
|
|
66
|
+
## Testing
|
|
63
67
|
|
|
64
|
-
|
|
68
|
+
`hyper` relies on the DOM, which may not always be available. In those cases, it's possible to set a custom DOM using `env`:
|
|
65
69
|
|
|
66
70
|
```TS
|
|
67
71
|
import { env } from '@chronocide/hyper';
|
|
@@ -75,7 +79,7 @@ Examples can be found in the test files, such as [hyper.spec.ts](/src/hyper.spec
|
|
|
75
79
|
|
|
76
80
|
## Development
|
|
77
81
|
|
|
78
|
-
|
|
82
|
+
`hyper` uses `puppeteer` for browser testing and does not install Chrome by default. To run tests, create an `.env` file using:
|
|
79
83
|
|
|
80
84
|
```sh
|
|
81
85
|
npm run env
|
package/dist/hyper.d.ts
CHANGED
|
@@ -1,24 +1,22 @@
|
|
|
1
|
-
type Attributes = Record<string, unknown>;
|
|
2
|
-
|
|
3
|
-
type
|
|
4
|
-
type
|
|
5
|
-
|
|
6
|
-
declare class Env {
|
|
7
|
-
private _document;
|
|
8
|
-
get document(): Document;
|
|
9
|
-
set document(document: Document);
|
|
10
|
-
constructor();
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
declare const _default
|
|
15
|
-
|
|
16
|
-
declare const
|
|
17
|
-
declare const
|
|
18
|
-
|
|
19
|
-
declare const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
export { _default as default, env, _default$1 as list, mathml, svg, xml };
|
|
24
|
-
export type { Attributes, Child, Component, HTMLVoidElementTagName };
|
|
1
|
+
type Attributes = Record<string, unknown>;
|
|
2
|
+
type Child = Node | string;
|
|
3
|
+
type HTMLVoidElementTagName = 'area' | 'base' | 'br' | 'col' | 'embed' | 'hr' | 'img' | 'input' | 'link' | 'meta' | 'source' | 'track' | 'wbr';
|
|
4
|
+
type Component<T> = (data: T) => Element;
|
|
5
|
+
|
|
6
|
+
declare class Env {
|
|
7
|
+
private _document;
|
|
8
|
+
get document(): Document;
|
|
9
|
+
set document(document: Document);
|
|
10
|
+
constructor();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
declare const env: Env;
|
|
14
|
+
declare const _default: <T extends keyof HTMLElementTagNameMap>(tag: T) => <P extends Attributes>(attributes?: P | undefined) => (...children: T extends HTMLVoidElementTagName ? never[] : Child[]) => HTMLElementTagNameMap[T];
|
|
15
|
+
|
|
16
|
+
declare const svg: <T extends keyof SVGElementTagNameMap>(tag: T) => <P extends Attributes>(attributes?: P | undefined) => (...children: Child[]) => SVGElementTagNameMap[T];
|
|
17
|
+
declare const mathml: <T extends keyof MathMLElementEventMap>(tag: T) => <P extends Attributes>(attributes?: P | undefined) => (...children: Child[]) => MathMLElement;
|
|
18
|
+
declare const xml: (tag: string) => <P extends Attributes>(attributes?: P) => (...children: Child[]) => HTMLElement;
|
|
19
|
+
declare const list: <T>(component: Component<T>) => (root: Element) => (next: T[]) => void;
|
|
20
|
+
|
|
21
|
+
export { _default as default, env, list, mathml, svg, xml };
|
|
22
|
+
export type { Attributes, Child, Component, HTMLVoidElementTagName };
|
package/dist/hyper.js
CHANGED
|
@@ -1,69 +1,68 @@
|
|
|
1
|
-
const maybe = (fn) => (x) => {
|
|
2
|
-
if (x === null || x === void 0) return null;
|
|
3
|
-
return fn(x);
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
if (typeof v === "string") element.setAttribute(k, v);
|
|
8
|
-
if (typeof v === "number") element.setAttribute(k, `${v}`);
|
|
9
|
-
if (v === true) element.toggleAttribute(k, v);
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
element
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
export { hyper as default, env, list, mathml, svg, xml };
|
|
1
|
+
const maybe = (fn) => (x) => {
|
|
2
|
+
if (x === null || x === void 0) return null;
|
|
3
|
+
return fn(x);
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
const setAttributes = (element) => (attributes) => Object.entries(attributes).forEach(([k, v]) => {
|
|
7
|
+
if (typeof v === "string") element.setAttribute(k, v);
|
|
8
|
+
if (typeof v === "number") element.setAttribute(k, `${v}`);
|
|
9
|
+
if (v === true) element.toggleAttribute(k, v);
|
|
10
|
+
});
|
|
11
|
+
const create = (element) => (attributes) => (children) => {
|
|
12
|
+
maybe(setAttributes(element))(attributes);
|
|
13
|
+
element.append(...children);
|
|
14
|
+
return element;
|
|
15
|
+
};
|
|
16
|
+
const html = (document) => (tag) => (attributes) => (...children) => create(document.createElement(tag))(attributes)(children);
|
|
17
|
+
const svg$1 = (document) => (tag) => (attributes) => (...children) => create(document.createElementNS("http://www.w3.org/2000/svg", tag))(attributes)(children);
|
|
18
|
+
const mathml$1 = (document) => (tag) => (attributes) => (...children) => create(document.createElementNS("http://www.w3.org/1998/Math/MathML", tag))(attributes)(children);
|
|
19
|
+
const xml$1 = (document) => (tag) => (attributes) => (...children) => create(document.createElementNS("http://www.w3.org/1999/xhtml", tag))(attributes)(children);
|
|
20
|
+
const list$1 = (component) => (root) => {
|
|
21
|
+
const cache = /* @__PURE__ */ new Map();
|
|
22
|
+
return (next) => {
|
|
23
|
+
const refs = /* @__PURE__ */ new WeakSet();
|
|
24
|
+
while (root.children.length > next.length) root.lastChild?.remove();
|
|
25
|
+
next.forEach((data, i) => {
|
|
26
|
+
const child = root.children.item(i);
|
|
27
|
+
let element = cache.get(data);
|
|
28
|
+
if (element === child) return;
|
|
29
|
+
if (!element) {
|
|
30
|
+
element = component(data);
|
|
31
|
+
cache.set(data, element);
|
|
32
|
+
}
|
|
33
|
+
if (refs.has(element)) {
|
|
34
|
+
element = element.cloneNode(true);
|
|
35
|
+
} else {
|
|
36
|
+
refs.add(element);
|
|
37
|
+
}
|
|
38
|
+
if (child) {
|
|
39
|
+
root.replaceChild(element, child);
|
|
40
|
+
} else {
|
|
41
|
+
root.appendChild(element);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
class Env {
|
|
48
|
+
_document;
|
|
49
|
+
get document() {
|
|
50
|
+
if (!this._document) throw new Error("Missing document");
|
|
51
|
+
return this._document;
|
|
52
|
+
}
|
|
53
|
+
set document(document2) {
|
|
54
|
+
this._document = document2;
|
|
55
|
+
}
|
|
56
|
+
constructor() {
|
|
57
|
+
this._document = typeof document === "undefined" ? null : document;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const env = new Env();
|
|
62
|
+
var hyper = (tag) => html(env.document)(tag);
|
|
63
|
+
const svg = (tag) => svg$1(env.document)(tag);
|
|
64
|
+
const mathml = (tag) => mathml$1(env.document)(tag);
|
|
65
|
+
const xml = (tag) => xml$1(env.document)(tag);
|
|
66
|
+
const list = list$1;
|
|
67
|
+
|
|
68
|
+
export { hyper as default, env, list, mathml, svg, xml };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chronocide/hyper",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"types": "dist/hyper.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -11,24 +11,26 @@
|
|
|
11
11
|
"dist/**/*"
|
|
12
12
|
],
|
|
13
13
|
"engines": {
|
|
14
|
-
"node": ">=
|
|
14
|
+
"node": ">=24.11.1"
|
|
15
15
|
},
|
|
16
16
|
"author": "Chronocide",
|
|
17
17
|
"license": "AGPL-3.0-only",
|
|
18
18
|
"scripts": {
|
|
19
|
-
"env": "node
|
|
20
|
-
"test": "node --env-file=.env --
|
|
19
|
+
"env": "node scripts/env.ts",
|
|
20
|
+
"test": "node --env-file=.env --test src/**/*.spec.ts",
|
|
21
21
|
"build": "rollup -c",
|
|
22
22
|
"preversion": "npm run test",
|
|
23
23
|
"version": "npm run build && git add -A dist",
|
|
24
24
|
"postversion": "git push && git push --tags"
|
|
25
25
|
},
|
|
26
|
+
"optionalDependencies": {
|
|
27
|
+
"eslint": "^9.28.0",
|
|
28
|
+
"@chronocide/eslint-config": "github:chronoDave/eslint-config"
|
|
29
|
+
},
|
|
26
30
|
"devDependencies": {
|
|
27
|
-
"@chronocide/eslint-config": "github:chronoDave/eslint-config",
|
|
28
31
|
"@types/jsdom": "^21.1.7",
|
|
29
32
|
"@types/node": "^22.15.29",
|
|
30
33
|
"esbuild": "^0.25.5",
|
|
31
|
-
"eslint": "^9.28.0",
|
|
32
34
|
"jsdom": "^26.1.0",
|
|
33
35
|
"puppeteer-core": "^24.14.0",
|
|
34
36
|
"rollup": "^4.42.0",
|