@bitstillery/mithril 3.3.3 → 3.4.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 CHANGED
@@ -36,7 +36,7 @@ count(5) // Logs: 5 × 2 = 10
36
36
 
37
37
  ## Proxy State
38
38
 
39
- `state()` creates reactive objects. Components track which properties they read and only re-render when those change.
39
+ `state()` creates reactive objects. Components track which properties they read and only re-render when those change. **Function properties become computed values**—they re-evaluate when their dependencies change.
40
40
 
41
41
  ```tsx
42
42
  import m, {state, MithrilComponent} from '@bitstillery/mithril'
@@ -47,7 +47,9 @@ class Counter extends MithrilComponent {
47
47
  view() {
48
48
  return (
49
49
  <div>
50
- <p>{$s.count}</p>
50
+ <p>
51
+ {$s.count} / {$s.totalTodos}
52
+ </p>
51
53
  <button onclick={() => $s.count++}>+</button>
52
54
  </div>
53
55
  )
@@ -57,7 +59,9 @@ class Counter extends MithrilComponent {
57
59
  m.mount(document.body, Counter)
58
60
  ```
59
61
 
60
- The second argument to `state()` is a name used for SSR serialization.
62
+ - **Computed properties**: Any function in state (e.g. `totalTodos: () => $s.todos.length`) is a computed—read it like a normal property, it updates when dependencies change.
63
+ - **`$` prefix**: Use `$s.$count` for the raw signal (e.g. for `watch()`).
64
+ - The second argument is a name used for SSR serialization.
61
65
 
62
66
  ## Watchers
63
67
 
package/index.ts CHANGED
@@ -12,7 +12,7 @@ import censor from './util/censor'
12
12
  import nextTick from './util/next_tick'
13
13
  import domFor from './render/domFor'
14
14
  import {signal, computed, effect, Signal, ComputedSignal, setSignalRedrawCallback, getSignalComponents} from './signal'
15
- import {state, watch, registerState, getRegisteredStates, copyGlobalStatesToContext} from './state'
15
+ import {state, watch, registerState, getRegisteredStates, clearStateRegistry, copyGlobalStatesToContext} from './state'
16
16
 
17
17
  import type {Vnode, Children, ComponentType} from './render/vnode'
18
18
  import type {Hyperscript} from './render/hyperscript'
@@ -101,7 +101,7 @@ setSignalRedrawCallback((sig: Signal<any>) => {
101
101
  })
102
102
 
103
103
  // Export signals API
104
- export {signal, computed, effect, Signal, ComputedSignal, state, watch, registerState, getRegisteredStates}
104
+ export {signal, computed, effect, Signal, ComputedSignal, state, watch, registerState, getRegisteredStates, clearStateRegistry}
105
105
  export type {State, StateOptions, StateSignals, Unwatch} from './state'
106
106
 
107
107
  // Export Store class
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitstillery/mithril",
3
- "version": "3.3.3",
3
+ "version": "3.4.0",
4
4
  "description": "Mithril + Signals, Store and SSR",
5
5
  "license": "MIT",
6
6
  "author": "Bitstillery",
@@ -54,9 +54,14 @@
54
54
  "lint:ts-syntax": "oxlint -c .oxlintrc.json",
55
55
  "lint:ts-types": "tsgo --noEmit",
56
56
  "test": "bun test",
57
+ "bench": "NODE_ENV=production bun run bench/index.ts",
58
+ "bench:hyperscript": "NODE_ENV=production bun run bench/index.ts hyperscript",
59
+ "bench:render": "NODE_ENV=production bun run bench/index.ts render",
60
+ "bench:signal": "NODE_ENV=production bun run bench/index.ts signal",
61
+ "bench:state": "NODE_ENV=production bun run bench/index.ts state",
62
+ "bench:profile": "bun --cpu-prof-md run bench/index.ts",
57
63
  "prepublishOnly": "bun run lint:ts",
58
64
  "pack:dry-run": "npm pack --dry-run",
59
- "publish": "./scripts/release.sh",
60
65
  "release": "./scripts/release.sh",
61
66
  "release:patch": "./scripts/release.sh --release-as patch",
62
67
  "release:minor": "./scripts/release.sh --release-as minor",
@@ -77,6 +82,7 @@
77
82
  "codemirror": "^6.0.1",
78
83
  "marked": "^15.0.4",
79
84
  "marked-gfm-heading-id": "^4.0.0",
85
+ "mitata": "^1.0.34",
80
86
  "oxfmt": "^0.32.0",
81
87
  "oxlint": "^1.30.0",
82
88
  "standard-version": "^9.5.0",
package/state.ts CHANGED
@@ -27,14 +27,27 @@ function createStateComputed<T>(wrapped: any, computeFn: () => T, shouldDefer: b
27
27
  function markAllComputedsDirty(stateObj: any): void {
28
28
  if (!stateObj || !(stateObj as any).__isState) return
29
29
  const signalMap = (stateObj as any).__signalMap
30
- if (!signalMap || !(signalMap instanceof Map)) return
31
- signalMap.forEach((sig: any) => {
32
- if (sig instanceof ComputedSignal) {
33
- sig.markDirty()
34
- } else if (sig && typeof sig === 'object' && sig.value && (sig.value as any).__isState) {
35
- markAllComputedsDirty(sig.value)
30
+ if (signalMap && signalMap instanceof Map) {
31
+ signalMap.forEach((sig: any) => {
32
+ if (sig instanceof ComputedSignal) {
33
+ sig.markDirty()
34
+ } else if (sig && typeof sig === 'object' && sig.value && (sig.value as any).__isState) {
35
+ markAllComputedsDirty(sig.value)
36
+ }
37
+ })
38
+ }
39
+ // Arrays use __signals instead of __signalMap; recurse into nested state elements
40
+ const signals = (stateObj as any).__signals
41
+ if (Array.isArray(signals)) {
42
+ for (const sig of signals) {
43
+ if (sig && typeof sig === 'object' && (sig as any).__isState) {
44
+ markAllComputedsDirty(sig)
45
+ } else if (sig && typeof sig === 'object' && !isSignal(sig)) {
46
+ // Array elements that are Proxies (not signals) - they're nested states
47
+ markAllComputedsDirty(sig)
48
+ }
36
49
  }
37
- })
50
+ }
38
51
  }
39
52
 
40
53
  // Type guard to check if value is a Signal