rip-lang 2.5.1 → 2.7.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/CHANGELOG.md +33 -10
- package/README.md +133 -289
- package/docs/BROWSER.md +8 -11
- package/docs/GUIDE.md +101 -923
- package/docs/INTERNALS.md +1 -1
- package/docs/PHILOSOPHY.md +22 -77
- package/docs/REACTIVITY.md +288 -0
- package/docs/WHY-YES-RIP.md +39 -177
- package/docs/dist/rip.browser.js +605 -2453
- package/docs/dist/rip.browser.min.js +283 -359
- package/docs/dist/rip.browser.min.js.br +0 -0
- package/docs/repl.html +94 -437
- package/package.json +4 -1
- package/scripts/serve.js +2 -0
- package/src/compiler.js +73 -2160
- package/src/grammar/grammar.rip +22 -57
- package/src/lexer.js +11 -333
- package/src/parser.js +220 -223
- package/src/repl.js +202 -128
- package/src/tags.js +0 -62
package/docs/INTERNALS.md
CHANGED
|
@@ -76,7 +76,7 @@ class BinaryOp {
|
|
|
76
76
|
["+", left, right] // That's it!
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
**Result:** CoffeeScript's compiler is 17,760 LOC. Rip's is ~
|
|
79
|
+
**Result:** CoffeeScript's compiler is 17,760 LOC. Rip's is ~11,000 LOC—**smaller, yet includes a full reactive runtime.**
|
|
80
80
|
|
|
81
81
|
## S-Expression Structure
|
|
82
82
|
|
package/docs/PHILOSOPHY.md
CHANGED
|
@@ -40,7 +40,7 @@ class BinaryOp {
|
|
|
40
40
|
["+", left, right] // That's it!
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
**Result:** CoffeeScript's compiler is 17,760 LOC. Rip's is ~
|
|
43
|
+
**Result:** CoffeeScript's compiler is 17,760 LOC. Rip's is ~11,000 LOC—smaller, yet includes a complete reactive runtime with state, computed values, and effects.
|
|
44
44
|
|
|
45
45
|
## The Fundamental Rule
|
|
46
46
|
|
|
@@ -421,8 +421,8 @@ counter = ->
|
|
|
421
421
|
|-----------|------------------|-----|------------|
|
|
422
422
|
| **Lexer+Rewriter** | 3,558 LOC | 3,537 LOC | Expanded syntax |
|
|
423
423
|
| **Parser Generator** | 2,285 LOC (Jison) | ~1,000 LOC (Solar) | Built-in, ~250× faster! |
|
|
424
|
-
| **Compiler** | 10,346 LOC (AST Nodes) |
|
|
425
|
-
| **Total** | **17,760 LOC** | **~
|
|
424
|
+
| **Compiler** | 10,346 LOC (AST Nodes) | 5,500 LOC (S-expressions) | +Reactive runtime! |
|
|
425
|
+
| **Total** | **17,760 LOC** | **~11,000 LOC** | **Smaller + reactive runtime** |
|
|
426
426
|
|
|
427
427
|
## Feature Comparison Table
|
|
428
428
|
|
|
@@ -470,24 +470,20 @@ counter = ->
|
|
|
470
470
|
|
|
471
471
|
> **v2.5.1 - Production-Ready with Fine-Grained Reactivity**
|
|
472
472
|
|
|
473
|
-
## Summary
|
|
473
|
+
## Summary
|
|
474
474
|
|
|
475
475
|
| Layer | Syntax | Runtime | Features | DX | Score |
|
|
476
476
|
|-------|--------|---------|----------|-----|-------|
|
|
477
477
|
| **Reactivity** | A+ | A+ | A+ | A+ | **A+** |
|
|
478
|
-
| **Templates** | A+ | A | A | A+ | **A** |
|
|
479
|
-
| **Components** | A | A | A- | A | **A-** |
|
|
480
|
-
|
|
481
|
-
*Components A- due to missing SSR/Hydration. Context API and Error Primitives are implemented.*
|
|
482
478
|
|
|
483
479
|
## Reactivity ⭐⭐⭐⭐⭐ (Production-Ready)
|
|
484
480
|
|
|
485
481
|
**This is genuinely excellent.**
|
|
486
482
|
|
|
487
483
|
```coffee
|
|
488
|
-
count := 0
|
|
489
|
-
doubled ~= count * 2 #
|
|
490
|
-
|
|
484
|
+
count := 0 # State
|
|
485
|
+
doubled ~= count * 2 # Computed (auto-tracks)
|
|
486
|
+
log ~> console.log count # Effect (auto-runs)
|
|
491
487
|
```
|
|
492
488
|
|
|
493
489
|
| Aspect | Rating | Notes |
|
|
@@ -499,83 +495,32 @@ effect -> console.log count # Effect (auto-runs)
|
|
|
499
495
|
|
|
500
496
|
**Competitive with:** SolidJS signals, Vue 3 refs, Preact signals
|
|
501
497
|
|
|
502
|
-
##
|
|
503
|
-
|
|
504
|
-
**Innovative syntax with fine-grained performance.**
|
|
505
|
-
|
|
506
|
-
```coffee
|
|
507
|
-
render
|
|
508
|
-
div#main.card ...props
|
|
509
|
-
h1.title "Hello, #{name}!"
|
|
510
|
-
input value <=> username, @keydown.enter: submit
|
|
511
|
-
button.('btn', isActive && 'active') @click.prevent: handleClick, "Submit"
|
|
512
|
-
```
|
|
513
|
-
|
|
514
|
-
| Aspect | Rating | Notes |
|
|
515
|
-
|--------|--------|-------|
|
|
516
|
-
| Syntax | A+ | Indentation-based, clean, intuitive |
|
|
517
|
-
| Features | A | Classes, IDs, events, modifiers, spread, two-way binding |
|
|
518
|
-
| Runtime | A | Fine-grained: only dynamic parts get effects |
|
|
519
|
-
| Innovation | A | Dynamic classes `div.('a', x && 'b')`, `<=>` binding |
|
|
498
|
+
## Framework-Agnostic Design
|
|
520
499
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
**Clean syntax with Svelte-class performance.**
|
|
524
|
-
|
|
525
|
-
| Aspect | Rating | Notes |
|
|
526
|
-
|--------|--------|-------|
|
|
527
|
-
| Syntax | A | Clean, obvious structure |
|
|
528
|
-
| Props | A | Required, optional, defaults, rest props |
|
|
529
|
-
| **Performance** | **A+** | **Fine-grained O(1) updates!** |
|
|
530
|
-
|
|
531
|
-
### Performance Comparison
|
|
532
|
-
|
|
533
|
-
| Operation | Old Approach | New Approach |
|
|
534
|
-
|-----------|--------------|--------------|
|
|
535
|
-
| Update 1 text | O(n) rebuild | **O(1)** single node |
|
|
536
|
-
| Update 1 attr | O(n) rebuild | **O(1)** single attr |
|
|
537
|
-
| Add list item | O(n) rebuild | **O(1)** create 1 node |
|
|
538
|
-
| Remove list item | O(n) rebuild | **O(1)** remove 1 node |
|
|
539
|
-
|
|
540
|
-
**Result:** 30-40x faster for typical reactive updates!
|
|
500
|
+
Rip's reactivity system is **framework-agnostic** — use it with React, Vue, Svelte, or vanilla JavaScript. The reactive primitives (state, computed, effects) work independently of any UI layer.
|
|
541
501
|
|
|
542
502
|
## Competitive Analysis
|
|
543
503
|
|
|
544
|
-
| Framework | Reactivity |
|
|
545
|
-
|
|
546
|
-
| **Rip** | A+ | A | A
|
|
547
|
-
| SolidJS | A+ | A | A
|
|
548
|
-
|
|
|
549
|
-
|
|
|
550
|
-
| React | B | B+ | A | B | A- | B+ |
|
|
504
|
+
| Framework | Reactivity | DX | Performance | Overall |
|
|
505
|
+
|-----------|------------|-----|-------------|---------|
|
|
506
|
+
| **Rip** | A+ | A+ | A | **A** |
|
|
507
|
+
| SolidJS | A+ | A | A+ | A |
|
|
508
|
+
| Vue 3 | A- | A | B+ | A- |
|
|
509
|
+
| React | B | A- | B | B+ |
|
|
551
510
|
|
|
552
511
|
**Rip's Position:**
|
|
553
512
|
|
|
554
513
|
| Strength | Why |
|
|
555
514
|
|----------|-----|
|
|
556
|
-
| **Reactivity A+** |
|
|
515
|
+
| **Reactivity A+** | State, computed, effects, batching |
|
|
557
516
|
| **DX A+** | Cleanest syntax of all, no boilerplate |
|
|
558
|
-
| **
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
| **Overall A-** (not A) | No ecosystem (router, state lib, devtools) |
|
|
564
|
-
|
|
565
|
-
## Completed Features ✅
|
|
566
|
-
|
|
567
|
-
- [x] Reactivity primitives (signals, computed, effects)
|
|
568
|
-
- [x] Template syntax and features
|
|
569
|
-
- [x] Props system (`@prop`, `@prop?`, `@prop = default`, `@...rest`)
|
|
570
|
-
- [x] Component composition
|
|
571
|
-
- [x] Children/slots
|
|
572
|
-
- [x] Lifecycle hooks
|
|
573
|
-
- [x] Fine-grained DOM updates
|
|
574
|
-
- [x] Fine-grained conditionals (if/else) with effect cleanup
|
|
575
|
-
- [x] Fine-grained loops (for) with keyed reconciliation
|
|
576
|
-
- [x] **Context API** (`setContext`, `getContext`, `hasContext`)
|
|
577
|
-
- [x] **Error primitives** (`__catchErrors`, `__handleError`)
|
|
517
|
+
| **Framework-agnostic** | Use with any UI framework |
|
|
518
|
+
|
|
519
|
+
## Completed Features
|
|
520
|
+
|
|
521
|
+
- [x] Reactivity primitives (state, computed, effects)
|
|
578
522
|
- [x] **Batching** (`__batch()` for grouped updates)
|
|
523
|
+
- [x] **Error primitives** (`__catchErrors`, `__handleError`)
|
|
579
524
|
|
|
580
525
|
## Best Current Uses
|
|
581
526
|
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
# Rip Reactivity System
|
|
2
|
+
|
|
3
|
+
Rip implements a **fine-grained reactive system** that rivals and often exceeds the capabilities of major frameworks like Vue, Svelte, Solid, and React — in just ~200 lines of runtime code.
|
|
4
|
+
|
|
5
|
+
## The Reactive Triad
|
|
6
|
+
|
|
7
|
+
Rip's entire reactivity model is built on three foundational concepts:
|
|
8
|
+
|
|
9
|
+
| Primitive | Description | Read As | Role | Purpose |
|
|
10
|
+
|-----------|-------------|---------|------|---------|
|
|
11
|
+
| `:=` | state | "has state" | **Source** | Where reactive data originates |
|
|
12
|
+
| `~=` | computed | "always equals" | **Derivation** | Computed values (lazy, cached) |
|
|
13
|
+
| `~>` | effect | "reacts to" | **Reaction** | Side effects when data changes |
|
|
14
|
+
|
|
15
|
+
These three primitives are the **minimal complete set** for reactive programming — everything React, Vue, Svelte, and Solid can do with state management, Rip can do too.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Why These Three Are Complete
|
|
20
|
+
|
|
21
|
+
Every reactive system reduces to these three concepts:
|
|
22
|
+
|
|
23
|
+
| Framework | Source | Derivation | Reaction |
|
|
24
|
+
|-----------|--------|------------|----------|
|
|
25
|
+
| **Rip** | `:=` | `~=` | `~>` |
|
|
26
|
+
| Angular | `signal()` | `computed()` | `effect()` |
|
|
27
|
+
| Imba | `@property` | implicit | implicit |
|
|
28
|
+
| MobX | `observable` | `computed` | `autorun` |
|
|
29
|
+
| Next.js | `useState` | `useMemo` | `useEffect` |
|
|
30
|
+
| React | `useState` | `useMemo` | `useEffect` |
|
|
31
|
+
| Solid | `createSignal` | `createMemo` | `createEffect` |
|
|
32
|
+
| Svelte 4 | `let x` | `$: x` | `$: {}` |
|
|
33
|
+
| Svelte 5 | `$state` | `$derived` | `$effect` |
|
|
34
|
+
| Vue | `ref()` | `computed()` | `watch()` |
|
|
35
|
+
|
|
36
|
+
### What You Can Build From These Three
|
|
37
|
+
|
|
38
|
+
- **Stores** → objects with state properties
|
|
39
|
+
- **Two-way binding** → state + effect that syncs
|
|
40
|
+
- **Async resources** → state + effect that fetches
|
|
41
|
+
- **Event handling** → update state → triggers reactions
|
|
42
|
+
- **Derived stores** → computed from other state
|
|
43
|
+
|
|
44
|
+
### What's Missing Without Any One
|
|
45
|
+
|
|
46
|
+
- **Without `:=`** → No source of truth
|
|
47
|
+
- **Without `~=`** → Manual tracking or inefficient effects
|
|
48
|
+
- **Without `~>`** → Can't react to changes (no side effects)
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Quick Example
|
|
53
|
+
|
|
54
|
+
```rip
|
|
55
|
+
count := 0 # count has state 0
|
|
56
|
+
doubled ~= count * 2 # doubled always equals count * 2
|
|
57
|
+
logger ~> console.log count # reacts to count changes
|
|
58
|
+
~> console.log count # same thing, but the "fire and forget" version
|
|
59
|
+
|
|
60
|
+
increment: -> count += 1
|
|
61
|
+
|
|
62
|
+
increment() # Logs: 1
|
|
63
|
+
increment() # Logs: 2
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## How It Works
|
|
69
|
+
|
|
70
|
+
### State (`:=`) — "has state"
|
|
71
|
+
|
|
72
|
+
State creates a **reactive container** that tracks its readers and notifies them on change.
|
|
73
|
+
|
|
74
|
+
```rip
|
|
75
|
+
count := 0 # count has state 0
|
|
76
|
+
count += 1 # Update triggers dependents
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Compiles to:**
|
|
80
|
+
```javascript
|
|
81
|
+
const count = __state(0);
|
|
82
|
+
count.value += 1;
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**What happens internally:**
|
|
86
|
+
1. Reading `count.value` inside an effect/computed **tracks** the dependency
|
|
87
|
+
2. Writing to `count.value` **notifies** all subscribers
|
|
88
|
+
3. Effects re-run, computeds mark dirty
|
|
89
|
+
|
|
90
|
+
### Computed (`~=`) — "always equals"
|
|
91
|
+
|
|
92
|
+
Computed creates a **computed value** that automatically updates when dependencies change.
|
|
93
|
+
|
|
94
|
+
```rip
|
|
95
|
+
count := 0
|
|
96
|
+
doubled ~= count * 2 # doubled always equals count * 2
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Key features:**
|
|
100
|
+
- **Lazy** — only computes when read
|
|
101
|
+
- **Cached** — won't recompute unless dependencies change
|
|
102
|
+
- **Chainable** — computeds can depend on other computeds
|
|
103
|
+
|
|
104
|
+
### Effect (`~>`) — "reacts to"
|
|
105
|
+
|
|
106
|
+
The effect operator runs **side effects** when dependencies change. Dependencies are auto-tracked from reactive values read in the body.
|
|
107
|
+
|
|
108
|
+
```rip
|
|
109
|
+
~> document.title = "Count: #{count}"
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Key features:**
|
|
113
|
+
- **Auto-tracking** — dependencies detected automatically from body
|
|
114
|
+
- **Immediate** — runs once immediately to establish dependencies
|
|
115
|
+
- **Controllable** — optionally assign to a variable to control the effect
|
|
116
|
+
|
|
117
|
+
**Syntax:**
|
|
118
|
+
```rip
|
|
119
|
+
# Fire and forget (no assignment)
|
|
120
|
+
~> console.log count
|
|
121
|
+
|
|
122
|
+
# Controllable (assign to variable)
|
|
123
|
+
logger ~> console.log count
|
|
124
|
+
logger.stop! # Pause reactions
|
|
125
|
+
logger.run! # Resume reactions
|
|
126
|
+
logger.cancel! # Permanent disposal
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Comparison with Major Frameworks
|
|
132
|
+
|
|
133
|
+
### State
|
|
134
|
+
|
|
135
|
+
| Feature | Rip | Vue | Solid | React |
|
|
136
|
+
|---------|:---:|:---:|:-----:|:-----:|
|
|
137
|
+
| Auto-tracking on read | ✅ | ✅ | ✅ | ❌ |
|
|
138
|
+
| Same-value skip | ✅ | ✅ | ✅ | ✅ |
|
|
139
|
+
| Re-entry protection | ✅ | ❌ | ❌ | ❌ |
|
|
140
|
+
| Lock for SSR | ✅ | ❌ | ❌ | ❌ |
|
|
141
|
+
| Cleanup/disposal | ✅ | ✅ | ✅ | ❌ |
|
|
142
|
+
| Raw read (untracked) | ✅ | ✅ | ✅ | ❌ |
|
|
143
|
+
| Primitive coercion | ✅ | ❌ | ❌ | N/A |
|
|
144
|
+
|
|
145
|
+
**Rip advantage:** `.lock()`, `.kill()`, `.read()` utilities that others lack.
|
|
146
|
+
|
|
147
|
+
### Computed
|
|
148
|
+
|
|
149
|
+
| Feature | Rip | Vue | Solid | MobX |
|
|
150
|
+
|---------|:---:|:---:|:-----:|:----:|
|
|
151
|
+
| Lazy evaluation | ✅ | ✅ | ✅ | ✅ |
|
|
152
|
+
| Cached until deps change | ✅ | ✅ | ✅ | ✅ |
|
|
153
|
+
| Dirty propagation | ✅ | ✅ | ✅ | ✅ |
|
|
154
|
+
| Auto dependency cleanup | ✅ | ✅ | ✅ | ✅ |
|
|
155
|
+
| Chainable | ✅ | ✅ | ✅ | ✅ |
|
|
156
|
+
| Read without tracking | ✅ | ❌ | ❌ | ❌ |
|
|
157
|
+
| Lock (freeze value) | ✅ | ❌ | ❌ | ❌ |
|
|
158
|
+
|
|
159
|
+
**Rip advantage:** `.read()` for untracked access, `.lock()` to freeze.
|
|
160
|
+
|
|
161
|
+
### Effect (`~>`)
|
|
162
|
+
|
|
163
|
+
| Feature | Rip | Vue | Svelte | React |
|
|
164
|
+
|---------|:---:|:---:|:------:|:-----:|
|
|
165
|
+
| Auto-tracking | ✅ | ✅ | ✅ | ❌ |
|
|
166
|
+
| No manual deps array | ✅ | ✅ | ✅ | ❌ |
|
|
167
|
+
| Runs immediately | ✅ | ✅ | ✅ | ✅ |
|
|
168
|
+
| Controllable (stop/run) | ✅ | ✅ | ✅ | ❌ |
|
|
169
|
+
| Re-runs on change | ✅ | ✅ | ✅ | ✅ |
|
|
170
|
+
|
|
171
|
+
**Rip advantage over React:** No manual dependency arrays!
|
|
172
|
+
|
|
173
|
+
```javascript
|
|
174
|
+
// React - manual, error-prone
|
|
175
|
+
useEffect(() => {
|
|
176
|
+
document.title = `Count: ${count}`;
|
|
177
|
+
}, [count]); // 😩 Must list deps manually
|
|
178
|
+
|
|
179
|
+
// Rip - automatic
|
|
180
|
+
~> document.title = "Count: #{count}" // 🎉 Deps tracked automatically
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Bundle Size
|
|
184
|
+
|
|
185
|
+
| Framework | Runtime Size |
|
|
186
|
+
|-----------|-------------|
|
|
187
|
+
| React | ~40 KB (minified) |
|
|
188
|
+
| Vue | ~34 KB |
|
|
189
|
+
| Svelte | ~2 KB (but grows with app size) |
|
|
190
|
+
| **Rip** | **~4 KB** (full runtime) |
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Advanced Features
|
|
195
|
+
|
|
196
|
+
### Batching
|
|
197
|
+
|
|
198
|
+
Group multiple updates into a single flush:
|
|
199
|
+
|
|
200
|
+
```javascript
|
|
201
|
+
__batch(() => {
|
|
202
|
+
count.value = 1;
|
|
203
|
+
name.value = "Alice";
|
|
204
|
+
// Effects run once at the end, not twice
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Untracked Reads
|
|
209
|
+
|
|
210
|
+
Read a value without creating a dependency:
|
|
211
|
+
|
|
212
|
+
```javascript
|
|
213
|
+
const currentValue = count.read(); // No tracking
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Locking (SSR/Hydration)
|
|
217
|
+
|
|
218
|
+
Prevent writes during server-side rendering or freeze values:
|
|
219
|
+
|
|
220
|
+
```javascript
|
|
221
|
+
count.lock(); // Now immutable
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Cleanup
|
|
225
|
+
|
|
226
|
+
Dispose of reactive values:
|
|
227
|
+
|
|
228
|
+
```javascript
|
|
229
|
+
const finalValue = count.kill(); // Returns value, clears subscribers
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## The Architecture
|
|
235
|
+
|
|
236
|
+
```
|
|
237
|
+
┌─────────────┐ reads ┌─────────────┐
|
|
238
|
+
│ STATE │◄──────────────│ EFFECT │
|
|
239
|
+
│ count │ │ (side fx) │
|
|
240
|
+
└──────┬──────┘ └─────────────┘
|
|
241
|
+
│ ▲
|
|
242
|
+
│ notifies │ triggers
|
|
243
|
+
▼ │
|
|
244
|
+
┌─────────────┐ reads ┌──────┴──────┐
|
|
245
|
+
│ COMPUTED │◄──────────────│ EFFECT │
|
|
246
|
+
│ doubled │ │ (logger) │
|
|
247
|
+
└─────────────┘ └─────────────┘
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
1. **State** is the source of truth
|
|
251
|
+
2. **Computed** derives from state (lazy, cached)
|
|
252
|
+
3. **Effects** react to state/computed changes
|
|
253
|
+
4. **Changes propagate** through the dependency graph
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Why Fine-Grained Reactivity?
|
|
258
|
+
|
|
259
|
+
Rip uses **fine-grained reactivity** (like Vue/Solid), not Virtual DOM diffing (like React).
|
|
260
|
+
|
|
261
|
+
| Approach | How it works | Pros | Cons |
|
|
262
|
+
|----------|--------------|------|------|
|
|
263
|
+
| **VDOM** (React) | Re-render tree, diff, patch | Simple mental model | Overhead, requires optimization |
|
|
264
|
+
| **Fine-grained** (Rip) | Track dependencies, update directly | Surgical updates, fast | More complex internally |
|
|
265
|
+
|
|
266
|
+
### Result
|
|
267
|
+
|
|
268
|
+
- **No VDOM overhead** — changes propagate directly to subscribers
|
|
269
|
+
- **Surgical updates** — only the exact things that changed update
|
|
270
|
+
- **No `useMemo`/`useCallback` dance** — caching is automatic
|
|
271
|
+
- **Smaller bundles** — no diffing algorithm needed
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## Summary
|
|
276
|
+
|
|
277
|
+
Rip's reactivity system:
|
|
278
|
+
|
|
279
|
+
✅ **The Reactive Triad** — state (`:=`), computed (`~=`), effect (`~>`) <br>
|
|
280
|
+
✅ **Natural reading** — "has state", "always equals", "reacts to" <br>
|
|
281
|
+
✅ **Minimal complete set** — same model as Vue, Solid, MobX <br>
|
|
282
|
+
✅ **Lazy computed** — only calculates when needed <br>
|
|
283
|
+
✅ **Fine-grained** — surgical updates to subscribers <br>
|
|
284
|
+
✅ **Controllable effects** — `.stop!`, `.run!`, `.cancel!` when needed <br>
|
|
285
|
+
✅ **Tiny runtime** — ~200 lines, ~4 KB <br>
|
|
286
|
+
✅ **Extra utilities** — `.lock()`, `.read()`, `.kill()` that others lack <br>
|
|
287
|
+
|
|
288
|
+
**On par with Vue/Solid. Better than React. A fraction of the size.**
|