@bitstillery/mithril 3.0.1-AA → 3.0.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 +48 -121
- package/package.json +13 -3
package/README.md
CHANGED
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
# @bitstillery/mithril
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Mithril.js fork with fine-grained reactivity, SSR hydration, and built-in signals. Drop-in compatible with Mithril v2.x.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
```bash
|
|
6
|
+
bun add @bitstillery/mithril
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Why
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
Mithril.js uses global `m.redraw()` — one state change re-renders **everything**. This fork adds component-level reactivity via signals so only affected components update.
|
|
8
12
|
|
|
9
|
-
|
|
13
|
+
| Feature | Original | This Fork |
|
|
14
|
+
| ---------------- | ------------------- | ------------------------------- |
|
|
15
|
+
| Reactivity | Global `m.redraw()` | Fine-grained component updates |
|
|
16
|
+
| State Management | Manual redraw calls | Signals with automatic tracking |
|
|
17
|
+
| SSR | No hydration | State serialization + hydration |
|
|
18
|
+
| TypeScript | Community types | Native |
|
|
10
19
|
|
|
11
|
-
|
|
20
|
+
Signals are opt-in. Existing Mithril code works unchanged.
|
|
12
21
|
|
|
13
|
-
|
|
22
|
+
**Docs**: [mithril.garage44.org](https://mithril.garage44.org)
|
|
14
23
|
|
|
15
|
-
|
|
24
|
+
## Signals
|
|
25
|
+
|
|
26
|
+
Zero-dependency reactive primitives with automatic dependency tracking:
|
|
16
27
|
|
|
17
28
|
```typescript
|
|
18
29
|
import {signal, computed, effect} from '@bitstillery/mithril'
|
|
@@ -20,71 +31,36 @@ import {signal, computed, effect} from '@bitstillery/mithril'
|
|
|
20
31
|
const count = signal(0)
|
|
21
32
|
const doubled = computed(() => count() * 2)
|
|
22
33
|
|
|
23
|
-
effect(() => {
|
|
24
|
-
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
count(5) // Logs: Count: 5, Doubled: 10
|
|
34
|
+
effect(() => console.log(`${count()} × 2 = ${doubled()}`))
|
|
35
|
+
count(5) // Logs: 5 × 2 = 10
|
|
28
36
|
```
|
|
29
37
|
|
|
30
|
-
|
|
38
|
+
## Proxy State
|
|
31
39
|
|
|
32
|
-
|
|
40
|
+
Wraps signals in a proxy for ergonomic access. Components automatically track which properties they read and only re-render when those change.
|
|
33
41
|
|
|
34
42
|
```tsx
|
|
35
|
-
import {state} from '@bitstillery/mithril'
|
|
36
|
-
|
|
37
|
-
const $s = state(
|
|
38
|
-
|
|
39
|
-
count: 0,
|
|
40
|
-
user: {name: 'John'},
|
|
41
|
-
todos: [],
|
|
42
|
-
totalTodos: () => $s.todos.length, // Computed
|
|
43
|
-
},
|
|
44
|
-
'my.state',
|
|
45
|
-
) // Name required for SSR serialization
|
|
46
|
-
|
|
47
|
-
// Component only re-renders when $s.count changes
|
|
43
|
+
import m, {state, MithrilComponent} from '@bitstillery/mithril'
|
|
44
|
+
|
|
45
|
+
const $s = state({count: 0, todos: [], totalTodos: () => $s.todos.length}, 'app')
|
|
46
|
+
|
|
48
47
|
class Counter extends MithrilComponent {
|
|
49
48
|
view() {
|
|
50
49
|
return (
|
|
51
50
|
<div>
|
|
52
|
-
<p>
|
|
53
|
-
<button onclick={() => $s.count++}
|
|
51
|
+
<p>{$s.count}</p>
|
|
52
|
+
<button onclick={() => $s.count++}>+</button>
|
|
54
53
|
</div>
|
|
55
54
|
)
|
|
56
55
|
}
|
|
57
56
|
}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### Persistent Store
|
|
61
57
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
```typescript
|
|
65
|
-
import {Store} from '@bitstillery/mithril'
|
|
66
|
-
|
|
67
|
-
const store = new Store<{user: {name: string}; preferences: Record<string, any>}>()
|
|
68
|
-
|
|
69
|
-
// Define what persists vs what's volatile
|
|
70
|
-
store.blueprint(
|
|
71
|
-
{user: {name: ''}, preferences: {}},
|
|
72
|
-
{
|
|
73
|
-
user: {name: ''}, // Persistent
|
|
74
|
-
preferences: {}, // Persistent
|
|
75
|
-
},
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
// Load from storage, or initialize with defaults
|
|
79
|
-
store.load({user: {name: 'John'}, preferences: {theme: 'dark'}})
|
|
80
|
-
|
|
81
|
-
// State is reactive and automatically saves on changes
|
|
82
|
-
store.state.user.name = 'Jane' // Auto-saves to localStorage
|
|
58
|
+
m.mount(document.body, Counter)
|
|
83
59
|
```
|
|
84
60
|
|
|
85
|
-
|
|
61
|
+
The second argument to `state()` is a name used for SSR serialization.
|
|
86
62
|
|
|
87
|
-
|
|
63
|
+
## SSR Hydration
|
|
88
64
|
|
|
89
65
|
```typescript
|
|
90
66
|
// Server
|
|
@@ -94,57 +70,31 @@ const {html, state} = await m.renderToString(App)
|
|
|
94
70
|
// Client
|
|
95
71
|
import {deserializeAllStates} from '@bitstillery/mithril'
|
|
96
72
|
|
|
97
|
-
const
|
|
98
|
-
if (
|
|
99
|
-
deserializeAllStates(JSON.parse(ssrState.textContent))
|
|
100
|
-
}
|
|
73
|
+
const el = document.getElementById('__SSR_STATE__')
|
|
74
|
+
if (el?.textContent) deserializeAllStates(JSON.parse(el.textContent))
|
|
101
75
|
m.mount(root, App)
|
|
102
76
|
```
|
|
103
77
|
|
|
104
|
-
##
|
|
105
|
-
|
|
106
|
-
These features build on each other: signals provide the foundation for fine-grained reactivity, proxy-based state makes them developer-friendly, `Store` adds persistence for localStorage/sessionStorage, and SSR hydration enables search-engine friendly websites. The result? State that "just works"—from initial render through hydration, user interactions, and page refreshes—all while maintaining Mithril's familiar API.
|
|
107
|
-
|
|
108
|
-
## Quick Start
|
|
109
|
-
|
|
110
|
-
```bash
|
|
111
|
-
bun add @bitstillery/mithril
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
```tsx
|
|
115
|
-
import m, {state, MithrilComponent} from '@bitstillery/mithril'
|
|
78
|
+
## Persistent Store
|
|
116
79
|
|
|
117
|
-
|
|
80
|
+
`Store` wraps `state()` with localStorage/sessionStorage. Define a blueprint with defaults and which keys persist:
|
|
118
81
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
return (
|
|
122
|
-
<div>
|
|
123
|
-
<p>Count: {$s.count}</p>
|
|
124
|
-
<button onclick={() => $s.count++}>Increment</button>
|
|
125
|
-
</div>
|
|
126
|
-
)
|
|
127
|
-
}
|
|
128
|
-
}
|
|
82
|
+
```typescript
|
|
83
|
+
import {Store} from '@bitstillery/mithril'
|
|
129
84
|
|
|
130
|
-
|
|
85
|
+
const store = new Store<{user: {name: string}; preferences: Record<string, any>}>()
|
|
86
|
+
store.blueprint(
|
|
87
|
+
{user: {name: ''}, preferences: {}},
|
|
88
|
+
{user: {name: ''}, preferences: {}}, // Keys here persist to storage
|
|
89
|
+
)
|
|
90
|
+
store.load({user: {name: 'John'}, preferences: {theme: 'dark'}})
|
|
91
|
+
store.state.user.name = 'Jane' // Auto-saves
|
|
131
92
|
```
|
|
132
93
|
|
|
133
94
|
## Examples
|
|
134
95
|
|
|
135
|
-
-
|
|
136
|
-
-
|
|
137
|
-
|
|
138
|
-
## Differences from Mithril.js
|
|
139
|
-
|
|
140
|
-
| Feature | Original | This Fork |
|
|
141
|
-
| ---------------- | ------------------- | ------------------------------- |
|
|
142
|
-
| Reactivity | Global `m.redraw()` | Fine-grained component updates |
|
|
143
|
-
| State Management | Manual redraw calls | Signals with automatic tracking |
|
|
144
|
-
| SSR Hydration | State loss | Proper state preservation |
|
|
145
|
-
| TypeScript | Community types | Native TypeScript |
|
|
146
|
-
|
|
147
|
-
**100% API compatible** with Mithril.js v2.x. Signals are opt-in.
|
|
96
|
+
- [`examples/ssr/`](examples/ssr/) — Server-side rendering with hydration
|
|
97
|
+
- [`examples/state/`](examples/state/) — Signals, state, and Store patterns
|
|
148
98
|
|
|
149
99
|
## Development
|
|
150
100
|
|
|
@@ -155,31 +105,8 @@ bun test
|
|
|
155
105
|
|
|
156
106
|
## License
|
|
157
107
|
|
|
158
|
-
MIT
|
|
108
|
+
MIT — see [LICENSE](LICENSE).
|
|
159
109
|
|
|
160
110
|
## Credits
|
|
161
111
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
Special thanks to:
|
|
165
|
-
|
|
166
|
-
- Pat Cavit, who exposed most of the public API for Mithril.js 1.0, brought in test coverage and automated the publishing process
|
|
167
|
-
- Claudia Meadows, who brought in linting, modernized the test suite and has been a strong voice in design discussions
|
|
168
|
-
- Zoli Kahan, who replaced the original Promise implementation with one that actually worked properly
|
|
169
|
-
- Alec Embke, who single-handedly wrote the JSON-P implementation
|
|
170
|
-
- Barney Carroll, who suggested many great ideas and relentlessly pushed Mithril.js to the limit to uncover design issues prior to Mithril.js 1.0
|
|
171
|
-
- Dominic Gannaway, who offered insanely meticulous technical insight into rendering performance
|
|
172
|
-
- Boris Letocha, whose search space reduction algorithm is the basis for Mithril.js' virtual DOM engine
|
|
173
|
-
- Joel Richard, whose monomorphic virtual DOM structure is the basis for Mithril.js' vnode implementation
|
|
174
|
-
- Simon Friis Vindum, whose open source work was an inspiration to many design decisions for Mithril.js 1.0
|
|
175
|
-
- Boris Kaul, for his awesome work on the benchmarking tools used to develop Mithril.js
|
|
176
|
-
- Leon Sorokin, for writing a DOM instrumentation tool that helped improve performance in Mithril.js 1.0
|
|
177
|
-
- Jordan Walke, whose work on React was prior art to the implementation of keys in Mithril.js
|
|
178
|
-
- Pierre-Yves Gérardy, who consistently makes high quality contributions
|
|
179
|
-
- Gyandeep Singh, who contributed significant IE performance improvements
|
|
180
|
-
|
|
181
|
-
Other people who also deserve recognition:
|
|
182
|
-
|
|
183
|
-
- Arthur Clemens - creator of [Polythene](https://github.com/ArthurClemens/Polythene) and the [HTML-to-Mithril converter](https://arthurclemens.github.io/mithril-template-converter/index.html)
|
|
184
|
-
- Stephan Hoyer - creator of [mithril-node-render](https://github.com/StephanHoyer/mithril-node-render), [mithril-query](https://github.com/StephanHoyer/mithril-query) and [mithril-source-hint](https://github.com/StephanHoyer/mithril-source-hint)
|
|
185
|
-
- the countless people who have reported and fixed bugs, participated in discussions, and helped promote Mithril.js
|
|
112
|
+
Originally created by Leo Horie. See the [Mithril.js contributors](https://github.com/MithrilJS/mithril.js/graphs/contributors) for the many people who made Mithril what it is.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitstillery/mithril",
|
|
3
|
-
"version": "3.0.
|
|
4
|
-
"description": "Mithril
|
|
3
|
+
"version": "3.0.2",
|
|
4
|
+
"description": "Mithril + Signals, Store and SSR",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bitstillery",
|
|
7
7
|
"repository": {
|
|
@@ -58,9 +58,19 @@
|
|
|
58
58
|
"htm": "^3.1.1"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
|
+
"@codemirror/lang-html": "^6.4.9",
|
|
62
|
+
"@codemirror/lang-javascript": "^6.2.2",
|
|
63
|
+
"@codemirror/lang-json": "^6.0.2",
|
|
64
|
+
"@codemirror/state": "^6.4.1",
|
|
65
|
+
"@codemirror/theme-one-dark": "^6.1.2",
|
|
66
|
+
"@codemirror/view": "^6.39.1",
|
|
61
67
|
"@types/bun": "^1.3.9",
|
|
62
68
|
"@typescript/native-preview": "^7.0.0-dev.20260120.1",
|
|
69
|
+
"codemirror": "^6.0.1",
|
|
70
|
+
"marked": "^15.0.4",
|
|
71
|
+
"marked-gfm-heading-id": "^4.0.0",
|
|
63
72
|
"oxfmt": "^0.32.0",
|
|
64
|
-
"oxlint": "^1.30.0"
|
|
73
|
+
"oxlint": "^1.30.0",
|
|
74
|
+
"tippy.js": "^6.3.7"
|
|
65
75
|
}
|
|
66
76
|
}
|