dalila 1.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 +333 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
# Dalila Framework
|
|
2
|
+
|
|
3
|
+
**UI driven by the DOM, not re-renders.**
|
|
4
|
+
|
|
5
|
+
Dalila is a **SPA**, **DOM-first**, **HTML natural** framework based on **signals**, created to eliminate the common pitfalls and workarounds of React.
|
|
6
|
+
|
|
7
|
+
## โจ Status
|
|
8
|
+
|
|
9
|
+
### Core (runtime, stable)
|
|
10
|
+
- ๐ **Signals-based reactivity** - Automatic dependency tracking
|
|
11
|
+
- ๐ฏ **DOM-first rendering** - Direct DOM manipulation, no Virtual DOM
|
|
12
|
+
- ๐ **Scope-based lifecycle** - Automatic cleanup (best effort)
|
|
13
|
+
- ๐ฃ๏ธ **SPA router** - Basic routing with loaders and AbortSignal
|
|
14
|
+
- ๐ฆ **Context system** - Reactive dependency injection
|
|
15
|
+
- ๐ง **Scheduler & batching** - Group updates into a single frame
|
|
16
|
+
- ๐ **List rendering** - `createList` for keyed lists
|
|
17
|
+
- ๐งฑ **Resources** - Async data helpers with AbortSignal and scope cleanup
|
|
18
|
+
|
|
19
|
+
### Experimental
|
|
20
|
+
- ๐จ **Natural HTML bindings** - Only in the example dev-server (not in core)
|
|
21
|
+
- ๐ **Virtualization** - Virtual lists/tables are experimental
|
|
22
|
+
- ๐ **DevTools (console)** - Warnings and FPS monitor only
|
|
23
|
+
- ๐งช **Low-level list API** - `forEach` (experimental, prefer `createList`)
|
|
24
|
+
|
|
25
|
+
### Planned / Roadmap
|
|
26
|
+
- ๐งฐ **DevTools UI** - Visual inspection tooling
|
|
27
|
+
- ๐งฉ **HTML bindings runtime/compiler** - First-class template binding
|
|
28
|
+
|
|
29
|
+
## ๐ฆ Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install dalila
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## ๐ Quick Start
|
|
36
|
+
|
|
37
|
+
Dalila examples use HTML bindings and a controller:
|
|
38
|
+
|
|
39
|
+
```html
|
|
40
|
+
<div>
|
|
41
|
+
<span>{count}</span>
|
|
42
|
+
<button on:click={increment}>+</button>
|
|
43
|
+
</div>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { signal } from 'dalila';
|
|
48
|
+
|
|
49
|
+
export function createController() {
|
|
50
|
+
const count = signal(0);
|
|
51
|
+
const increment = () => count.update(c => c + 1);
|
|
52
|
+
|
|
53
|
+
return { count, increment };
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
> Note: HTML bindings in this example are provided by the example dev-server,
|
|
58
|
+
> not by the core runtime.
|
|
59
|
+
|
|
60
|
+
## ๐งช Local Demo Server
|
|
61
|
+
|
|
62
|
+
Run a local server with HMR from the repo root:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npm run serve
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Then open `http://localhost:3000/`.
|
|
69
|
+
|
|
70
|
+
## ๐ Core Concepts
|
|
71
|
+
|
|
72
|
+
### Signals
|
|
73
|
+
```typescript
|
|
74
|
+
const count = signal(0);
|
|
75
|
+
|
|
76
|
+
// Read value
|
|
77
|
+
console.log(count()); // 0
|
|
78
|
+
|
|
79
|
+
// Set value
|
|
80
|
+
count.set(5);
|
|
81
|
+
|
|
82
|
+
// Update with function
|
|
83
|
+
count.update(c => c + 1);
|
|
84
|
+
|
|
85
|
+
// Reactive effects
|
|
86
|
+
effect(() => {
|
|
87
|
+
console.log(`Count changed to: ${count()}`);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Async effects with cleanup
|
|
91
|
+
effectAsync(async (signal) => {
|
|
92
|
+
const response = await fetch('/api/data', { signal });
|
|
93
|
+
const data = await response.json();
|
|
94
|
+
console.log(data);
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Conditional Rendering
|
|
99
|
+
```html
|
|
100
|
+
<p when={showTips}>This shows when true.</p>
|
|
101
|
+
<div match={status}>
|
|
102
|
+
<span case="idle">Idle</span>
|
|
103
|
+
<span case="active">Active</span>
|
|
104
|
+
</div>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
> Note: `when`/`match` bindings are available only in the example dev-server today.
|
|
108
|
+
> They are not part of the core runtime yet.
|
|
109
|
+
|
|
110
|
+
### Context (Dependency Injection)
|
|
111
|
+
```ts
|
|
112
|
+
const Theme = createContext<'light' | 'dark'>('theme');
|
|
113
|
+
provide(Theme, signal('light'));
|
|
114
|
+
const theme = inject(Theme);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### SPA Router
|
|
118
|
+
```typescript
|
|
119
|
+
const router = createRouter({
|
|
120
|
+
routes: [
|
|
121
|
+
{
|
|
122
|
+
path: '/',
|
|
123
|
+
view: HomePage,
|
|
124
|
+
loader: async ({ signal }) => {
|
|
125
|
+
const res = await fetch('/api/home', { signal });
|
|
126
|
+
return res.json();
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
{ path: '/users/:id', view: UserPage }
|
|
130
|
+
]
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
router.mount(document.getElementById('app'));
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Batching & Scheduling
|
|
137
|
+
```typescript
|
|
138
|
+
// Batch multiple updates into single frame
|
|
139
|
+
batch(() => {
|
|
140
|
+
count.set(1);
|
|
141
|
+
theme.set('dark');
|
|
142
|
+
// All updates happen in one frame
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// DOM read/write discipline
|
|
146
|
+
const width = measure(() => element.offsetWidth);
|
|
147
|
+
mutate(() => {
|
|
148
|
+
element.style.width = `${width + 10}px`;
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### List Rendering with Keys
|
|
153
|
+
```typescript
|
|
154
|
+
// Primary API (stable)
|
|
155
|
+
createList(
|
|
156
|
+
todos,
|
|
157
|
+
(todo) => div(todo.text)
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
// Experimental low-level API
|
|
161
|
+
forEach(
|
|
162
|
+
items,
|
|
163
|
+
(item) => div(item.name),
|
|
164
|
+
(item) => item.id // Key function
|
|
165
|
+
);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Resource Management
|
|
169
|
+
```typescript
|
|
170
|
+
// Declarative data fetching
|
|
171
|
+
const { data, loading, error, refresh } = createResource(
|
|
172
|
+
async (signal) => {
|
|
173
|
+
const res = await fetch('/api/data', { signal });
|
|
174
|
+
return res.json();
|
|
175
|
+
}
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
// Signals
|
|
179
|
+
effect(() => {
|
|
180
|
+
console.log(data(), loading(), error());
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Cached resource
|
|
184
|
+
const cachedData = createCachedResource(
|
|
185
|
+
'user-data',
|
|
186
|
+
async (signal) => fetchUser(signal)
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
// Auto-refresh
|
|
190
|
+
const liveData = createAutoRefreshResource(
|
|
191
|
+
fetchData,
|
|
192
|
+
5000 // Refresh every 5 seconds
|
|
193
|
+
);
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Virtualization (experimental)
|
|
197
|
+
```typescript
|
|
198
|
+
// Virtual list
|
|
199
|
+
createVirtualList(
|
|
200
|
+
items,
|
|
201
|
+
50, // Item height
|
|
202
|
+
(item) => div(item.name),
|
|
203
|
+
{ container: document.getElementById('list') }
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
// Virtual table
|
|
207
|
+
createVirtualTable(
|
|
208
|
+
data,
|
|
209
|
+
[
|
|
210
|
+
{ key: 'id', header: 'ID', width: '100px' },
|
|
211
|
+
{ key: 'name', header: 'Name', width: '200px' }
|
|
212
|
+
],
|
|
213
|
+
(item, column) => div(item[column.key]),
|
|
214
|
+
document.getElementById('table')
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
// Infinite scroll
|
|
218
|
+
const { items, loading, refresh } = createInfiniteScroll(
|
|
219
|
+
(offset, limit) => fetchItems(offset, limit),
|
|
220
|
+
(item) => div(item.name),
|
|
221
|
+
document.getElementById('scroll-container')
|
|
222
|
+
);
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### DevTools & Warnings (console-only)
|
|
226
|
+
```typescript
|
|
227
|
+
// Enable dev tools
|
|
228
|
+
initDevTools();
|
|
229
|
+
|
|
230
|
+
// Inspect signals
|
|
231
|
+
const { value, subscribers } = inspectSignal(count);
|
|
232
|
+
|
|
233
|
+
// Get active effects
|
|
234
|
+
const effects = getActiveEffects();
|
|
235
|
+
|
|
236
|
+
// Performance monitoring (automatic, console warnings)
|
|
237
|
+
monitorPerformance();
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
> There is no DevTools UI yet. The current tooling is lightweight console diagnostics.
|
|
241
|
+
|
|
242
|
+
### Cleanup Utilities
|
|
243
|
+
```typescript
|
|
244
|
+
// Event listener with auto-cleanup
|
|
245
|
+
useEvent(window, 'resize', handleResize);
|
|
246
|
+
|
|
247
|
+
// Interval with auto-cleanup
|
|
248
|
+
useInterval(() => {
|
|
249
|
+
console.log('Tick');
|
|
250
|
+
}, 1000);
|
|
251
|
+
|
|
252
|
+
// Timeout with auto-cleanup
|
|
253
|
+
useTimeout(() => {
|
|
254
|
+
console.log('Delayed');
|
|
255
|
+
}, 2000);
|
|
256
|
+
|
|
257
|
+
// Fetch with auto-cleanup
|
|
258
|
+
const { data, loading, error } = useFetch('/api/data');
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## ๐๏ธ Architecture
|
|
262
|
+
|
|
263
|
+
Dalila is built around these core principles:
|
|
264
|
+
|
|
265
|
+
- **No JSX** - Core runtime doesn't require JSX
|
|
266
|
+
- **No Virtual DOM** - Direct DOM manipulation
|
|
267
|
+
- **No manual memoization** - Signals reduce manual memoization (goal)
|
|
268
|
+
- **Scope-based cleanup** - Automatic resource management (best-effort)
|
|
269
|
+
- **Signal-driven reactivity** - Localized updates where possible
|
|
270
|
+
|
|
271
|
+
## ๐ Performance
|
|
272
|
+
|
|
273
|
+
- **Localized updates**: Signals update only subscribed DOM nodes (goal)
|
|
274
|
+
- **Automatic cleanup**: Scope-based cleanup is best-effort
|
|
275
|
+
- **Bundle size**: Not yet measured/verified
|
|
276
|
+
|
|
277
|
+
## ๐ค Why Dalila vs React?
|
|
278
|
+
|
|
279
|
+
| Feature | React | Dalila |
|
|
280
|
+
|---------|-------|--------|
|
|
281
|
+
| Rendering | Virtual DOM diffing | Direct DOM manipulation |
|
|
282
|
+
| Performance | Manual optimization | Runtime scheduling (best-effort) |
|
|
283
|
+
| State management | Hooks + deps arrays | Signals + automatic tracking |
|
|
284
|
+
| Side effects | `useEffect` + deps | `effect()` + automatic cleanup |
|
|
285
|
+
| Bundle size | ~40KB | Not yet measured |
|
|
286
|
+
|
|
287
|
+
## ๐ Project Structure
|
|
288
|
+
|
|
289
|
+
```
|
|
290
|
+
dalila/
|
|
291
|
+
โโโ src/
|
|
292
|
+
โ โโโ core/ # Signals, effects, scopes
|
|
293
|
+
โ โโโ context/ # Dependency injection
|
|
294
|
+
โ โโโ router/ # SPA routing
|
|
295
|
+
โ โโโ dom/ # DOM utilities
|
|
296
|
+
โ โโโ index.ts # Main exports
|
|
297
|
+
โโโ examples/ # Example applications
|
|
298
|
+
โโโ dist/ # Compiled output
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## ๐ ๏ธ Development
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
# Install dependencies
|
|
305
|
+
npm install
|
|
306
|
+
|
|
307
|
+
# Build the framework
|
|
308
|
+
npm run build
|
|
309
|
+
|
|
310
|
+
# Development mode
|
|
311
|
+
npm run dev
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## ๐ Examples
|
|
315
|
+
|
|
316
|
+
Check out the `examples/` directory for:
|
|
317
|
+
- Counter app
|
|
318
|
+
|
|
319
|
+
## ๐ค Contributing
|
|
320
|
+
|
|
321
|
+
Contributions welcome! Focus on:
|
|
322
|
+
- Maintaining core principles (no JSX, no VDOM, no manual optimization)
|
|
323
|
+
- Adding features that reduce boilerplate
|
|
324
|
+
- Improving performance without complexity
|
|
325
|
+
- Enhancing developer experience
|
|
326
|
+
|
|
327
|
+
## ๐ License
|
|
328
|
+
|
|
329
|
+
MIT
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
**Build UI, not workarounds.**
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dalila",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "DOM-first reactive framework based on signals",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"dev": "tsc --watch",
|
|
10
|
+
"serve": "node scripts/dev-server.js",
|
|
11
|
+
"test": "jest",
|
|
12
|
+
"test:watch": "jest --watch",
|
|
13
|
+
"clean": "rm -rf dist"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"framework",
|
|
17
|
+
"reactive",
|
|
18
|
+
"signals",
|
|
19
|
+
"dom",
|
|
20
|
+
"typescript",
|
|
21
|
+
"spa"
|
|
22
|
+
],
|
|
23
|
+
"author": "Everton Da Silva Vieira",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/jest": "^29.5.0",
|
|
27
|
+
"@types/node": "^20.0.0",
|
|
28
|
+
"jest": "^29.5.0",
|
|
29
|
+
"jest-environment-jsdom": "^29.5.0",
|
|
30
|
+
"typescript": "^5.0.0"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"README.md"
|
|
35
|
+
],
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/evertondsvieira/dalila.git"
|
|
39
|
+
},
|
|
40
|
+
"bugs": {
|
|
41
|
+
"url": "https://github.com/evertondsvieira/dalila/issues"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/evertondsvieira/dalila/blob/main/README.md"
|
|
44
|
+
}
|