@devchitchat/rdbljs 0.0.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 +550 -0
- package/index.js +1 -0
- package/package.json +54 -0
- package/src/rdbl.js +877 -0
package/README.md
ADDED
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
# rdbl
|
|
2
|
+
|
|
3
|
+
**Reactive DOM Binding Library** — pronounced *"riddle"*
|
|
4
|
+
|
|
5
|
+
Signals-based reactivity wired directly to plain HTML attributes. No build step. No virtual DOM. No template expressions. Just readable markup and a single file.
|
|
6
|
+
|
|
7
|
+
```html
|
|
8
|
+
<div text="user.name"></div>
|
|
9
|
+
<input model="query">
|
|
10
|
+
<ul each="results" key="id">
|
|
11
|
+
<template><li text="title"></li></template>
|
|
12
|
+
</ul>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
import { bind, signal, computed } from 'rdbl'
|
|
17
|
+
|
|
18
|
+
const state = {
|
|
19
|
+
user: { name: signal('Joey') },
|
|
20
|
+
query: signal(''),
|
|
21
|
+
results: signal([])
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
bind(document.querySelector('#app'), state)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
That's it.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Why rdbl?
|
|
32
|
+
|
|
33
|
+
Most reactive libraries ask you to learn a new template language or move your logic into your markup. rdbl goes the other way: your HTML stays plain and readable, your JS stays in JS.
|
|
34
|
+
|
|
35
|
+
- **No `{{}}` expressions** — bindings are attribute paths, not code
|
|
36
|
+
- **No `x-` prefixes** — plain attributes: `text`, `show`, `model`, `each`
|
|
37
|
+
- **No build step** — single ES module, import and go
|
|
38
|
+
- **No virtual DOM** — direct DOM updates, batched via microtask
|
|
39
|
+
- **Signals all the way down** — `signal`, `computed`, `effect` are the entire reactive model
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
bun add rdbl
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Or copy the single file directly — rdbl has no dependencies.
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
import { bind, signal, computed, effect, batch, Context, getItemContext, createScope } from 'rdbl'
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Core Concepts
|
|
58
|
+
|
|
59
|
+
### Signals
|
|
60
|
+
|
|
61
|
+
A signal holds a value. Read it by calling it. Write it with `.set()`.
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
const count = signal(0)
|
|
65
|
+
|
|
66
|
+
count() // 0 — read
|
|
67
|
+
count.set(5) // write
|
|
68
|
+
count.peek() // 5 — read without tracking dependencies
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Computed
|
|
72
|
+
|
|
73
|
+
Derived state. Lazy-evaluated and cached until dependencies change.
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
const double = computed(() => count() * 2)
|
|
77
|
+
double() // 10
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Effect
|
|
81
|
+
|
|
82
|
+
Runs immediately and re-runs whenever its dependencies change.
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
effect(() => {
|
|
86
|
+
console.log('count is', count())
|
|
87
|
+
return () => console.log('cleanup') // optional
|
|
88
|
+
})
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Batch
|
|
92
|
+
|
|
93
|
+
Defer effect execution until all mutations are complete.
|
|
94
|
+
|
|
95
|
+
```js
|
|
96
|
+
batch(() => {
|
|
97
|
+
a.set(1)
|
|
98
|
+
b.set(2)
|
|
99
|
+
// effects fire once, after batch
|
|
100
|
+
})
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## HTML Directives
|
|
106
|
+
|
|
107
|
+
Directives are plain HTML attributes. No special syntax — just a dot-separated path into your scope.
|
|
108
|
+
|
|
109
|
+
### `text="path"`
|
|
110
|
+
|
|
111
|
+
Sets `textContent`.
|
|
112
|
+
|
|
113
|
+
```html
|
|
114
|
+
<span text="user.name"></span>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### `html="path"`
|
|
118
|
+
|
|
119
|
+
Sets `innerHTML`.
|
|
120
|
+
|
|
121
|
+
```html
|
|
122
|
+
<div html="article.body"></div>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### `show="path"`
|
|
126
|
+
|
|
127
|
+
Toggles visibility via the `hidden` attribute. Works with `<dialog>` — calls `showModal()` / `close()` automatically.
|
|
128
|
+
|
|
129
|
+
```html
|
|
130
|
+
<div show="isVisible"></div>
|
|
131
|
+
<dialog show="modalOpen">...</dialog>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### `cls="path"`
|
|
135
|
+
|
|
136
|
+
Sets classes. Accepts a string (replaces `className`) or an object (toggles individual classes).
|
|
137
|
+
|
|
138
|
+
```html
|
|
139
|
+
<div cls="statusClass"></div>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```js
|
|
143
|
+
// string form
|
|
144
|
+
statusClass: computed(() => isActive() ? 'active' : 'inactive')
|
|
145
|
+
|
|
146
|
+
// object form
|
|
147
|
+
statusClass: computed(() => ({ active: isActive(), disabled: isDisabled() }))
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### `attr="name:path; name2:path2"`
|
|
151
|
+
|
|
152
|
+
Sets arbitrary attributes. Removes the attribute when value is `false` or `null`.
|
|
153
|
+
|
|
154
|
+
```html
|
|
155
|
+
<button attr="aria-label:label; data-id:item.id"></button>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### `model="path"`
|
|
159
|
+
|
|
160
|
+
Two-way binding for form elements. Path must resolve to a signal.
|
|
161
|
+
|
|
162
|
+
```html
|
|
163
|
+
<input model="query">
|
|
164
|
+
<input type="checkbox" model="isChecked">
|
|
165
|
+
<select model="selectedTab"></select>
|
|
166
|
+
<textarea model="notes"></textarea>
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### `each="path" key="idPath"`
|
|
170
|
+
|
|
171
|
+
Keyed list rendering. Requires a `<template>` child. Efficiently diffs and reorders DOM nodes.
|
|
172
|
+
|
|
173
|
+
```html
|
|
174
|
+
<ul each="todos" key="id">
|
|
175
|
+
<template>
|
|
176
|
+
<li>
|
|
177
|
+
<input type="checkbox" model="done">
|
|
178
|
+
<span text="text"></span>
|
|
179
|
+
<button onclick="removeTodo">x</button>
|
|
180
|
+
</li>
|
|
181
|
+
</template>
|
|
182
|
+
</ul>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Inside list items, the scope includes all item properties directly, plus `$item` and `$index`.
|
|
186
|
+
|
|
187
|
+
### `on<event>="path"`
|
|
188
|
+
|
|
189
|
+
Event binding. Path resolves to a function in your scope.
|
|
190
|
+
|
|
191
|
+
```html
|
|
192
|
+
<button onclick="handleClick">Submit</button>
|
|
193
|
+
<input oninput="handleInput">
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
The handler receives `(event, element, context)`. Return `false` to call both `preventDefault()` and `stopPropagation()`.
|
|
197
|
+
|
|
198
|
+
```js
|
|
199
|
+
const state = {
|
|
200
|
+
handleClick(event, el, ctx) {
|
|
201
|
+
// do something
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## List Items and `getItemContext`
|
|
209
|
+
|
|
210
|
+
Inside an `each` list, use `getItemContext(element)` in your event handlers to access the item data for the row that was interacted with.
|
|
211
|
+
|
|
212
|
+
```js
|
|
213
|
+
import { getItemContext } from 'rdbl'
|
|
214
|
+
|
|
215
|
+
const state = {
|
|
216
|
+
todos: signal([{ id: 1, text: 'write docs', done: false }]),
|
|
217
|
+
|
|
218
|
+
toggleTodo(event, el) {
|
|
219
|
+
const { item } = getItemContext(el)
|
|
220
|
+
state.todos.set(
|
|
221
|
+
state.todos().map(t => t.id === item.id ? { ...t, done: !t.done } : t)
|
|
222
|
+
)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Context
|
|
230
|
+
|
|
231
|
+
DOM-scoped context, looked up by walking the element tree. Avoids prop drilling for shared services like routers or loggers.
|
|
232
|
+
|
|
233
|
+
```js
|
|
234
|
+
import { Context } from 'rdbl'
|
|
235
|
+
|
|
236
|
+
Context.provide(document.querySelector('#app'), {
|
|
237
|
+
router: { go: path => history.pushState({}, '', path) },
|
|
238
|
+
log: console.log
|
|
239
|
+
})
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Context is available as the third argument in event handlers:
|
|
243
|
+
|
|
244
|
+
```js
|
|
245
|
+
function handleNav(event, el, ctx) {
|
|
246
|
+
ctx.router.go('/dashboard')
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Read context directly anywhere you have an element:
|
|
251
|
+
|
|
252
|
+
```js
|
|
253
|
+
const ctx = Context.read(someElement)
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## bind()
|
|
259
|
+
|
|
260
|
+
```js
|
|
261
|
+
const app = bind(rootElement, scope, options)
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Options
|
|
265
|
+
|
|
266
|
+
| Option | Default | Description |
|
|
267
|
+
|---|---|---|
|
|
268
|
+
| `dev` | `true` | Log warnings for missing paths, bad values, missing keys |
|
|
269
|
+
| `autoBind` | `false` | Use `MutationObserver` to auto-bind dynamically added DOM |
|
|
270
|
+
| `ignoreSelector` | `[data-no-bind]` | CSS selector to opt subtrees out of binding |
|
|
271
|
+
|
|
272
|
+
### `app.bindSubtree(element, scope)`
|
|
273
|
+
|
|
274
|
+
Bind a sub-element with its own scope. The sub-scope falls back to the parent scope for unresolved paths.
|
|
275
|
+
|
|
276
|
+
```js
|
|
277
|
+
const app = bind(root, parentScope)
|
|
278
|
+
app.bindSubtree(cardElement, cardScope)
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### `app.dispose()`
|
|
282
|
+
|
|
283
|
+
Tear down all effects and event listeners created by this binding.
|
|
284
|
+
|
|
285
|
+
```js
|
|
286
|
+
app.dispose()
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## createScope()
|
|
292
|
+
|
|
293
|
+
Create a child scope that falls back to a parent for unresolved properties.
|
|
294
|
+
|
|
295
|
+
```js
|
|
296
|
+
import { createScope } from 'rdbl'
|
|
297
|
+
|
|
298
|
+
const child = createScope(parentScope, {
|
|
299
|
+
title: signal('Local Title')
|
|
300
|
+
})
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## Full Example
|
|
306
|
+
|
|
307
|
+
```html
|
|
308
|
+
<div id="app">
|
|
309
|
+
<h1 text="title"></h1>
|
|
310
|
+
<input model="newTodo" placeholder="Add a task...">
|
|
311
|
+
<button onclick="addTodo">Add</button>
|
|
312
|
+
|
|
313
|
+
<ul each="todos" key="id">
|
|
314
|
+
<template>
|
|
315
|
+
<li>
|
|
316
|
+
<input type="checkbox" model="done">
|
|
317
|
+
<span text="text" cls="itemClass"></span>
|
|
318
|
+
<button onclick="remove">Delete</button>
|
|
319
|
+
</li>
|
|
320
|
+
</template>
|
|
321
|
+
</ul>
|
|
322
|
+
|
|
323
|
+
<p text="summary"></p>
|
|
324
|
+
</div>
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
```js
|
|
328
|
+
import { bind, signal, computed, getItemContext } from 'rdbl'
|
|
329
|
+
|
|
330
|
+
const todos = signal([
|
|
331
|
+
{ id: 1, text: 'Read the docs', done: false },
|
|
332
|
+
{ id: 2, text: 'Build something', done: false }
|
|
333
|
+
])
|
|
334
|
+
|
|
335
|
+
const newTodo = signal('')
|
|
336
|
+
|
|
337
|
+
const state = {
|
|
338
|
+
title: 'My Todos',
|
|
339
|
+
todos,
|
|
340
|
+
newTodo,
|
|
341
|
+
summary: computed(() => {
|
|
342
|
+
const all = todos()
|
|
343
|
+
const done = all.filter(t => t.done).length
|
|
344
|
+
return `${done} of ${all.length} done`
|
|
345
|
+
}),
|
|
346
|
+
|
|
347
|
+
addTodo() {
|
|
348
|
+
const text = newTodo().trim()
|
|
349
|
+
if (!text) return
|
|
350
|
+
todos.set([...todos(), { id: Date.now(), text, done: false }])
|
|
351
|
+
newTodo.set('')
|
|
352
|
+
},
|
|
353
|
+
|
|
354
|
+
remove(event, el) {
|
|
355
|
+
const { item } = getItemContext(el)
|
|
356
|
+
todos.set(todos().filter(t => t.id !== item.id))
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
bind(document.querySelector('#app'), state, { dev: true })
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## Islands
|
|
366
|
+
|
|
367
|
+
Islands are independently bound components. Each element with an `[island]` attribute is its own binding root — rdbl stops traversal at nested island boundaries, so parent and child islands never interfere.
|
|
368
|
+
|
|
369
|
+
The `island` attribute value is a module path. Each island module exports a default factory function that receives the root element and `window`, reads shared state from `Context`, and returns its scope.
|
|
370
|
+
|
|
371
|
+
### Island module
|
|
372
|
+
|
|
373
|
+
```js
|
|
374
|
+
// /components/SyncStatus.js
|
|
375
|
+
import { computed, Context } from 'rdbl'
|
|
376
|
+
|
|
377
|
+
export default function syncStatus(root, window) {
|
|
378
|
+
const { pageState } = Context.read(document.body)
|
|
379
|
+
|
|
380
|
+
const statusText = computed(() =>
|
|
381
|
+
pageState.connected() ? 'Synced' : 'Connecting...'
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
const indicatorCls = computed(() =>
|
|
385
|
+
pageState.connected() ? 'dot synced' : 'dot pending'
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
return { statusText, indicatorCls }
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### Island HTML
|
|
393
|
+
|
|
394
|
+
```html
|
|
395
|
+
<section island="/components/SyncStatus.js">
|
|
396
|
+
<span cls="indicatorCls"></span>
|
|
397
|
+
<span text="statusText"></span>
|
|
398
|
+
</section>
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### init() — binding all islands at startup
|
|
402
|
+
|
|
403
|
+
This pattern auto-discovers every `[island]` element, dynamically imports its module, and calls `bind()` with the returned scope.
|
|
404
|
+
|
|
405
|
+
```js
|
|
406
|
+
import { bind, Context } from 'rdbl'
|
|
407
|
+
|
|
408
|
+
async function init(window) {
|
|
409
|
+
const roots = [...document.querySelectorAll('[island]')]
|
|
410
|
+
const instances = {}
|
|
411
|
+
let i = 0
|
|
412
|
+
|
|
413
|
+
for await (const root of roots) {
|
|
414
|
+
const key = root.getAttribute('island')
|
|
415
|
+
try {
|
|
416
|
+
const scopeFactory = (await import(key)).default
|
|
417
|
+
const scope = scopeFactory(root, window)
|
|
418
|
+
instances[`${key}:${i++}`] = bind(root, scope, { dev: true })
|
|
419
|
+
} catch (err) {
|
|
420
|
+
console.error(`Failed to load island "${key}":`, err)
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return instances
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Provide shared state on the body so any island can read it
|
|
428
|
+
Context.provide(document.body, { pageState })
|
|
429
|
+
|
|
430
|
+
const app = await init(window)
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
Each island gets its own binding instance and can be disposed independently. Shared state lives in `Context` — islands read it without being passed props.
|
|
434
|
+
|
|
435
|
+
---
|
|
436
|
+
|
|
437
|
+
## Server-Side Rendered Data
|
|
438
|
+
|
|
439
|
+
rdbl works naturally with server-rendered HTML. The key rule: **initialize your signals with server data before calling `bind()`**. On first run, effects write the same values the server already rendered — the DOM doesn't flicker.
|
|
440
|
+
|
|
441
|
+
### Embedded JSON in a script tag
|
|
442
|
+
|
|
443
|
+
The server embeds initial state as JSON. The client reads it, hydrates signals, then binds.
|
|
444
|
+
|
|
445
|
+
```html
|
|
446
|
+
<!-- Server renders this -->
|
|
447
|
+
<script type="application/json" id="page-data">
|
|
448
|
+
{ "user": { "name": "Joey", "plan": "pro" }, "posts": [...] }
|
|
449
|
+
</script>
|
|
450
|
+
|
|
451
|
+
<div id="app" island="/components/Dashboard.js">
|
|
452
|
+
<h1 text="greeting"></h1>
|
|
453
|
+
<span text="user.plan"></span>
|
|
454
|
+
</div>
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
```js
|
|
458
|
+
// /components/Dashboard.js
|
|
459
|
+
import { signal, computed, Context } from 'rdbl'
|
|
460
|
+
|
|
461
|
+
export default function dashboard(root, window) {
|
|
462
|
+
const raw = JSON.parse(document.getElementById('page-data').textContent)
|
|
463
|
+
|
|
464
|
+
const user = signal(raw.user)
|
|
465
|
+
const posts = signal(raw.posts)
|
|
466
|
+
|
|
467
|
+
const greeting = computed(() => `Hello, ${user().name}`)
|
|
468
|
+
|
|
469
|
+
return { user, posts, greeting }
|
|
470
|
+
}
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
### Data attributes on the island root
|
|
474
|
+
|
|
475
|
+
For smaller per-component values, the server can write initial data directly onto the island element.
|
|
476
|
+
|
|
477
|
+
```html
|
|
478
|
+
<section island="/components/UserBadge.js"
|
|
479
|
+
data-name="Joey"
|
|
480
|
+
data-plan="pro">
|
|
481
|
+
<span text="name"></span>
|
|
482
|
+
<span cls="badgeCls" text="plan"></span>
|
|
483
|
+
</section>
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
```js
|
|
487
|
+
// /components/UserBadge.js
|
|
488
|
+
import { signal, computed } from 'rdbl'
|
|
489
|
+
|
|
490
|
+
export default function userBadge(root, window) {
|
|
491
|
+
const name = signal(root.dataset.name)
|
|
492
|
+
const plan = signal(root.dataset.plan)
|
|
493
|
+
const badgeCls = computed(() => `badge badge-${plan()}`)
|
|
494
|
+
|
|
495
|
+
return { name, plan, badgeCls }
|
|
496
|
+
}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### Hydrating from storage
|
|
500
|
+
|
|
501
|
+
For client-persisted state (e.g. `localStorage`), read and apply the stored snapshot before binding. Effects fire with the restored values on first run, so the UI reflects the last known state immediately.
|
|
502
|
+
|
|
503
|
+
```js
|
|
504
|
+
import { bind, signal, effect, Context } from 'rdbl'
|
|
505
|
+
|
|
506
|
+
const theme = signal('light')
|
|
507
|
+
const tasks = signal([])
|
|
508
|
+
|
|
509
|
+
// Hydrate signals from storage before bind()
|
|
510
|
+
const stored = localStorage.getItem('app-state')
|
|
511
|
+
if (stored) {
|
|
512
|
+
const snapshot = JSON.parse(stored)
|
|
513
|
+
theme.set(snapshot.theme ?? 'light')
|
|
514
|
+
tasks.set(snapshot.tasks ?? [])
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// Persist on every change
|
|
518
|
+
effect(() => {
|
|
519
|
+
localStorage.setItem('app-state', JSON.stringify({
|
|
520
|
+
theme: theme(),
|
|
521
|
+
tasks: tasks()
|
|
522
|
+
}))
|
|
523
|
+
})
|
|
524
|
+
|
|
525
|
+
bind(document.querySelector('#app'), { theme, tasks })
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
In all three patterns the principle is the same: signals are the source of truth, the server (or storage) provides the initial values, and `bind()` wires the already-correct state to the DOM.
|
|
529
|
+
|
|
530
|
+
---
|
|
531
|
+
|
|
532
|
+
## Design Principles
|
|
533
|
+
|
|
534
|
+
1. **HTML is structure.** Directives describe *what* to bind, not *how*.
|
|
535
|
+
2. **No expressions in markup.** Paths only — logic lives in JS.
|
|
536
|
+
3. **Signals are the model.** One reactive primitive, no magic objects.
|
|
537
|
+
4. **DOM-first.** No virtual DOM, no diffing overhead beyond list keys.
|
|
538
|
+
5. **Single file.** Copy it, own it. No build pipeline required.
|
|
539
|
+
|
|
540
|
+
---
|
|
541
|
+
|
|
542
|
+
## Size
|
|
543
|
+
|
|
544
|
+
~4KB gzipped. Zero dependencies.
|
|
545
|
+
|
|
546
|
+
---
|
|
547
|
+
|
|
548
|
+
## License
|
|
549
|
+
|
|
550
|
+
MIT
|
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { batch, signal, effect, computed, Context, getItemContext, createScope, bind } from './src/rdbl.js'
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@devchitchat/rdbljs",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "Signals-based reactive DOM binding. Plain HTML, no build step, no virtual DOM.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"module": "index.js",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./index.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"index.js",
|
|
13
|
+
"src/rdbl.js",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"test": "bun test",
|
|
18
|
+
"docs:build:github": "cp src/rdbl.js docs/pages/public/assets/rdbl.js && bunx @devchitchat/index97@1.1.0 build docs/pages --out dist --path-prefix /rdbljs/",
|
|
19
|
+
"docs:build": "cp src/rdbl.js docs/pages/public/assets/rdbl.js && bunx @devchitchat/index97@1.1.0 build docs/pages --out dist",
|
|
20
|
+
"docs:serve": "cp src/rdbl.js docs/pages/public/assets/rdbl.js && bunx @devchitchat/index97@1.1.0 build docs/pages --out dist && bunx @devchitchat/index97@1.1.0 serve dist"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"reactive",
|
|
24
|
+
"dom",
|
|
25
|
+
"signals",
|
|
26
|
+
"binding",
|
|
27
|
+
"no-build",
|
|
28
|
+
"vanilla"
|
|
29
|
+
],
|
|
30
|
+
"author": "Joey Guerra",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/devchitchat/rdbljs.git"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/devchitchat/rdbljs#readme",
|
|
37
|
+
"engines": {
|
|
38
|
+
"bun": ">=1.0.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/bun": "latest"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"typescript": "^5"
|
|
45
|
+
},
|
|
46
|
+
"peerDependenciesMeta": {
|
|
47
|
+
"typescript": {
|
|
48
|
+
"optional": true
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public"
|
|
53
|
+
}
|
|
54
|
+
}
|