@3sln/dodo 0.0.1
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/LICENSE +21 -0
- package/README.md +128 -0
- package/dodo.js +698 -0
- package/index.js +10 -0
- package/package.json +31 -0
- package/src/html.js +128 -0
- package/src/scheduler.js +79 -0
- package/src/vdom.js +676 -0
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@3sln/dodo",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A minimal, configurable virtual DOM library.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"author": "Ray Stubbs",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/3sln/dodo.git"
|
|
11
|
+
},
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"vdom",
|
|
15
|
+
"virtual dom",
|
|
16
|
+
"ui",
|
|
17
|
+
"declarative"
|
|
18
|
+
],
|
|
19
|
+
"files": [
|
|
20
|
+
"index.js",
|
|
21
|
+
"dodo.js",
|
|
22
|
+
"src/"
|
|
23
|
+
],
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/bun": "latest",
|
|
26
|
+
"happy-dom": "^18.0.1"
|
|
27
|
+
},
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/html.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { h } from '../index.js';
|
|
2
|
+
|
|
3
|
+
// Document metadata
|
|
4
|
+
export const title = (...args) => h('title', ...args);
|
|
5
|
+
export const meta = (props) => h('meta', props);
|
|
6
|
+
export const link = (props) => h('link', props);
|
|
7
|
+
export const style = (...args) => h('style', ...args);
|
|
8
|
+
export const script = (...args) => h('script', ...args);
|
|
9
|
+
export const noscript = (...args) => h('noscript', ...args);
|
|
10
|
+
|
|
11
|
+
// Content sectioning
|
|
12
|
+
export const address = (...args) => h('address', ...args);
|
|
13
|
+
export const article = (...args) => h('article', ...args);
|
|
14
|
+
export const aside = (...args) => h('aside', ...args);
|
|
15
|
+
export const footer = (...args) => h('footer', ...args);
|
|
16
|
+
export const header = (...args) => h('header', ...args);
|
|
17
|
+
export const h1 = (...args) => h('h1', ...args);
|
|
18
|
+
export const h2 = (...args) => h('h2', ...args);
|
|
19
|
+
export const h3 = (...args) => h('h3', ...args);
|
|
20
|
+
export const h4 = (...args) => h('h4', ...args);
|
|
21
|
+
export const h5 = (...args) => h('h5', ...args);
|
|
22
|
+
export const h6 = (...args) => h('h6', ...args);
|
|
23
|
+
export const main = (...args) => h('main', ...args);
|
|
24
|
+
export const nav = (...args) => h('nav', ...args);
|
|
25
|
+
export const section = (...args) => h('section', ...args);
|
|
26
|
+
|
|
27
|
+
// Text content
|
|
28
|
+
export const blockquote = (...args) => h('blockquote', ...args);
|
|
29
|
+
export const dd = (...args) => h('dd', ...args);
|
|
30
|
+
export const div = (...args) => h('div', ...args);
|
|
31
|
+
export const dl = (...args) => h('dl', ...args);
|
|
32
|
+
export const dt = (...args) => h('dt', ...args);
|
|
33
|
+
export const figcaption = (...args) => h('figcaption', ...args);
|
|
34
|
+
export const figure = (...args) => h('figure', ...args);
|
|
35
|
+
export const hr = () => h('hr');
|
|
36
|
+
export const li = (...args) => h('li', ...args);
|
|
37
|
+
export const ol = (...args) => h('ol', ...args);
|
|
38
|
+
export const p = (...args) => h('p', ...args);
|
|
39
|
+
export const pre = (...args) => h('pre', ...args);
|
|
40
|
+
export const ul = (...args) => h('ul', ...args);
|
|
41
|
+
|
|
42
|
+
// Inline text semantics
|
|
43
|
+
export const a = (...args) => h('a', ...args);
|
|
44
|
+
export const abbr = (...args) => h('abbr', ...args);
|
|
45
|
+
export const b = (...args) => h('b', ...args);
|
|
46
|
+
export const br = () => h('br');
|
|
47
|
+
export const cite = (...args) => h('cite', ...args);
|
|
48
|
+
export const code = (...args) => h('code', ...args);
|
|
49
|
+
export const data = (...args) => h('data', ...args);
|
|
50
|
+
export const del = (...args) => h('del', ...args);
|
|
51
|
+
export const dfn = (...args) => h('dfn', ...args);
|
|
52
|
+
export const em = (...args) => h('em', ...args);
|
|
53
|
+
export const i = (...args) => h('i', ...args);
|
|
54
|
+
export const ins = (...args) => h('ins', ...args);
|
|
55
|
+
export const kbd = (...args) => h('kbd', ...args);
|
|
56
|
+
export const mark = (...args) => h('mark', ...args);
|
|
57
|
+
export const q = (...args) => h('q', ...args);
|
|
58
|
+
export const s = (...args) => h('s', ...args);
|
|
59
|
+
export const samp = (...args) => h('samp', ...args);
|
|
60
|
+
export const small = (...args) => h('small', ...args);
|
|
61
|
+
export const span = (...args) => h('span', ...args);
|
|
62
|
+
export const strong = (...args) => h('strong', ...args);
|
|
63
|
+
export const sub = (...args) => h('sub', ...args);
|
|
64
|
+
export const sup = (...args) => h('sup', ...args);
|
|
65
|
+
export const time = (...args) => h('time', ...args);
|
|
66
|
+
export const u = (...args) => h('u', ...args);
|
|
67
|
+
export const wbr = () => h('wbr');
|
|
68
|
+
|
|
69
|
+
// Image and multimedia
|
|
70
|
+
export const area = (props) => h('area', props);
|
|
71
|
+
export const audio = (...args) => h('audio', ...args);
|
|
72
|
+
export const img = (props) => h('img', props);
|
|
73
|
+
export const map = (...args) => h('map', ...args);
|
|
74
|
+
export const track = (props) => h('track', props);
|
|
75
|
+
export const video = (...args) => h('video', ...args);
|
|
76
|
+
|
|
77
|
+
// Embedded content
|
|
78
|
+
export const embed = (props) => h('embed', props);
|
|
79
|
+
export const iframe = (...args) => h('iframe', ...args);
|
|
80
|
+
export const object = (...args) => h('object', ...args);
|
|
81
|
+
export const param = (props) => h('param', props);
|
|
82
|
+
export const picture = (...args) => h('picture', ...args);
|
|
83
|
+
export const portal = (...args) => h('portal', ...args);
|
|
84
|
+
export const source = (props) => h('source', props);
|
|
85
|
+
|
|
86
|
+
// SVG and MathML
|
|
87
|
+
export const svg = (...args) => h('svg', ...args);
|
|
88
|
+
export const math = (...args) => h('math', ...args);
|
|
89
|
+
|
|
90
|
+
// Scripting
|
|
91
|
+
export const canvas = (...args) => h('canvas', ...args);
|
|
92
|
+
|
|
93
|
+
// Table content
|
|
94
|
+
export const caption = (...args) => h('caption', ...args);
|
|
95
|
+
export const col = (props) => h('col', props);
|
|
96
|
+
export const colgroup = (...args) => h('colgroup', ...args);
|
|
97
|
+
export const table = (...args) => h('table', ...args);
|
|
98
|
+
export const tbody = (...args) => h('tbody', ...args);
|
|
99
|
+
export const td = (...args) => h('td', ...args);
|
|
100
|
+
export const tfoot = (...args) => h('tfoot', ...args);
|
|
101
|
+
export const th = (...args) => h('th', ...args);
|
|
102
|
+
export const thead = (...args) => h('thead', ...args);
|
|
103
|
+
export const tr = (...args) => h('tr', ...args);
|
|
104
|
+
|
|
105
|
+
// Forms
|
|
106
|
+
export const button = (...args) => h('button', ...args);
|
|
107
|
+
export const datalist = (...args) => h('datalist', ...args);
|
|
108
|
+
export const fieldset = (...args) => h('fieldset', ...args);
|
|
109
|
+
export const form = (...args) => h('form', ...args);
|
|
110
|
+
export const input = (props) => h('input', props);
|
|
111
|
+
export const label = (...args) => h('label', ...args);
|
|
112
|
+
export const legend = (...args) => h('legend', ...args);
|
|
113
|
+
export const meter = (...args) => h('meter', ...args);
|
|
114
|
+
export const optgroup = (...args) => h('optgroup', ...args);
|
|
115
|
+
export const option = (...args) => h('option', ...args);
|
|
116
|
+
export const output = (...args) => h('output', ...args);
|
|
117
|
+
export const progress = (...args) => h('progress', ...args);
|
|
118
|
+
export const select = (...args) => h('select', ...args);
|
|
119
|
+
export const textarea = (...args) => h('textarea', ...args);
|
|
120
|
+
|
|
121
|
+
// Interactive elements
|
|
122
|
+
export const details = (...args) => h('details', ...args);
|
|
123
|
+
export const dialog = (...args) => h('dialog', ...args);
|
|
124
|
+
export const summary = (...args) => h('summary', ...args);
|
|
125
|
+
|
|
126
|
+
// Web Components
|
|
127
|
+
export const slot = (...args) => h('slot', ...args);
|
|
128
|
+
export const template = (...args) => h('template', ...args);
|
package/src/scheduler.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
let scheduled = false;
|
|
2
|
+
let frameId = 0;
|
|
3
|
+
let queue = [];
|
|
4
|
+
|
|
5
|
+
const FRAME_BUDGET = 10; // ms
|
|
6
|
+
const CHUNK_SIZE = 100;
|
|
7
|
+
|
|
8
|
+
function _runTasks(tasks) {
|
|
9
|
+
for (const f of tasks) {
|
|
10
|
+
try {
|
|
11
|
+
f();
|
|
12
|
+
} catch (err) {
|
|
13
|
+
console.error("Error in scheduled function:", err);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function runQueue() {
|
|
19
|
+
const startTime = performance.now();
|
|
20
|
+
frameId = 0;
|
|
21
|
+
|
|
22
|
+
while (queue.length > 0) {
|
|
23
|
+
const chunk = queue.splice(0, CHUNK_SIZE);
|
|
24
|
+
_runTasks(chunk);
|
|
25
|
+
|
|
26
|
+
if (performance.now() - startTime > FRAME_BUDGET && queue.length > 0) {
|
|
27
|
+
frameId = requestAnimationFrame(runQueue);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
scheduled = false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Schedules a function to be executed on the next animation frame.
|
|
36
|
+
export function schedule(f, { signal } = {}) {
|
|
37
|
+
if (signal?.aborted) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let task = f;
|
|
42
|
+
// If a signal is provided, wrap the task to check for abortion before execution.
|
|
43
|
+
if (signal) {
|
|
44
|
+
task = () => {
|
|
45
|
+
if (!signal.aborted) {
|
|
46
|
+
f();
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
queue.push(task);
|
|
52
|
+
|
|
53
|
+
if (!scheduled) {
|
|
54
|
+
scheduled = true;
|
|
55
|
+
frameId = requestAnimationFrame(runQueue);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Immediately runs all queued tasks synchronously.
|
|
60
|
+
export function flush() {
|
|
61
|
+
if (frameId) {
|
|
62
|
+
cancelAnimationFrame(frameId);
|
|
63
|
+
}
|
|
64
|
+
const toRun = queue;
|
|
65
|
+
queue = [];
|
|
66
|
+
_runTasks(toRun);
|
|
67
|
+
scheduled = false;
|
|
68
|
+
frameId = 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Clears all pending tasks.
|
|
72
|
+
export function clear() {
|
|
73
|
+
if (frameId) {
|
|
74
|
+
cancelAnimationFrame(frameId);
|
|
75
|
+
}
|
|
76
|
+
queue = [];
|
|
77
|
+
scheduled = false;
|
|
78
|
+
frameId = 0;
|
|
79
|
+
}
|