ilha 0.14.0 → 0.14.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/README.md +24 -19
- package/dist/define.d.ts +1 -1
- package/dist/define.js +1 -1
- package/dist/index.d.ts +13 -11
- package/dist/index.js +62 -40
- package/dist/jsx-dev-runtime.d.ts +2 -2
- package/dist/jsx-runtime.d.ts +2 -2
- package/dist/{mount-Dcy4teaf.js → mount-CzFZBgZG.js} +231 -50
- package/dist/{types-DU46exHq.d.ts → types-Cie0Ihl6.d.ts} +1 -2
- package/dist/{vnode-BZLcG8lC.d.ts → vnode-DyFwtMVw.d.ts} +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -48,43 +48,48 @@ count.set(1); // replace
|
|
|
48
48
|
count.update((n) => n + 1); // patch
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
In JSX, `{count}` subscribes the render.
|
|
51
|
+
In JSX, `{count}` subscribes the render. Derived values use Effect's `Atom.map` or `Atom.transform`:
|
|
52
52
|
|
|
53
53
|
```tsx
|
|
54
|
+
import * as Atom from "effect/unstable/reactivity/Atom";
|
|
55
|
+
import { atom } from "ilha";
|
|
56
|
+
|
|
54
57
|
const items = atom([{ n: 1 }, { n: 2 }]);
|
|
55
|
-
const total = atom(() =>
|
|
58
|
+
const total = atom(Atom.map(items.atom, (list) => list.reduce((sum, item) => sum + item.n, 0)));
|
|
56
59
|
```
|
|
57
60
|
|
|
61
|
+
Wrap multiple writes in `batch()`. Derived and mutation atoms use Effect's `Atom.map`, `Atom.transform`, and `Atom.fn` — pass `handle.atom`, then wrap in `atom()`. Use `watch(source, fn)` for side effects on atom changes.
|
|
62
|
+
|
|
63
|
+
Use `atom.lazy(() => …)` for one-time initialization or to store a function value.
|
|
64
|
+
|
|
58
65
|
Atoms hold data. Do not store JSX in an atom.
|
|
59
66
|
|
|
60
67
|
---
|
|
61
68
|
|
|
62
|
-
## Streams
|
|
69
|
+
## Streams and when
|
|
63
70
|
|
|
64
|
-
|
|
71
|
+
Paint [Effect `Stream`](https://www.effect.website/docs/v4/api/effect/Stream) values. Yield a stream of views from a generator, or return one from an async component:
|
|
65
72
|
|
|
66
73
|
```tsx
|
|
67
74
|
import * as Stream from "effect/Stream";
|
|
68
75
|
import * as Atom from "effect/unstable/reactivity/Atom";
|
|
69
|
-
import { atom
|
|
76
|
+
import { atom } from "ilha";
|
|
70
77
|
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
yield (
|
|
74
|
-
<
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
78
|
+
function* List() {
|
|
79
|
+
const items = atom(["a", "b"]);
|
|
80
|
+
yield Stream.map(Atom.toStream(items.atom), (list) => (
|
|
81
|
+
<ul>
|
|
82
|
+
{list.map((item) => (
|
|
83
|
+
<li key={item}>{item}</li>
|
|
84
|
+
))}
|
|
85
|
+
</ul>
|
|
86
|
+
));
|
|
87
|
+
}
|
|
81
88
|
```
|
|
82
89
|
|
|
83
|
-
|
|
84
|
-
- `watch(source, fn)` — side effect on an atom or Stream.
|
|
85
|
-
- `wait(body)` — paint until `done(value)`, then continue the generator.
|
|
90
|
+
SSR takes the first emission (`take(1)`); the client keeps listening after `mount`.
|
|
86
91
|
|
|
87
|
-
|
|
92
|
+
Ilha also ships `when` for per-emission generator bodies (stale work interrupted). Side effects and pauses use Effect directly — see the [Streams guide](https://ilha.build/guide/ui/streams).
|
|
88
93
|
|
|
89
94
|
---
|
|
90
95
|
|
package/dist/define.d.ts
CHANGED
package/dist/define.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as h } from "./vnode-
|
|
1
|
+
import { i as Instruction, n as Component, o as View, r as Fragment, s as Yielded, t as AtomHandle } from "./types-Cie0Ihl6.js";
|
|
2
|
+
import { t as h } from "./vnode-DyFwtMVw.js";
|
|
3
3
|
import * as Effect from "effect/Effect";
|
|
4
4
|
import * as Stream from "effect/Stream";
|
|
5
5
|
import * as Atom from "effect/unstable/reactivity/Atom";
|
|
6
6
|
import "effect/Scope";
|
|
7
7
|
import { AtomRegistry } from "effect/unstable/reactivity/AtomRegistry";
|
|
8
|
+
//#region src/watch.d.ts
|
|
9
|
+
/** Run `fn` when `source` changes — and once on mount. Sync, async, and generator components. */
|
|
10
|
+
declare function watch<A>(source: AtomHandle<A> | Atom.Atom<A> | Stream.Stream<A, any, any>, fn: (value: A) => void): Instruction<void>;
|
|
11
|
+
//#endregion
|
|
8
12
|
//#region src/atom.d.ts
|
|
9
|
-
|
|
10
|
-
|
|
13
|
+
interface AtomFn {
|
|
14
|
+
<A>(init: A | Atom.Atom<A> | Effect.Effect<A, any, any> | Stream.Stream<A, any, any>): AtomHandle<A>;
|
|
15
|
+
lazy<A>(init: () => A): AtomHandle<A>;
|
|
16
|
+
}
|
|
17
|
+
declare const atom: AtomFn;
|
|
18
|
+
declare const batch: (f: () => void) => void;
|
|
11
19
|
//#endregion
|
|
12
20
|
//#region src/when.d.ts
|
|
13
21
|
declare function when<A, E = never, R = never>(stream: Stream.Stream<A, E, R>, body: (value: A) => Generator<Yielded, View | void, unknown>): Instruction<void, E>;
|
|
14
22
|
//#endregion
|
|
15
|
-
//#region src/watch.d.ts
|
|
16
|
-
declare function watch<A>(source: AtomHandle<A> | Atom.Atom<A> | Stream.Stream<A, any, any>, fn: (value: A) => void): Instruction<void>;
|
|
17
|
-
//#endregion
|
|
18
|
-
//#region src/wait.d.ts
|
|
19
|
-
declare function wait<A>(body: (done: Done<A>) => Generator<Yielded, View | void, unknown>): Instruction<A>;
|
|
20
|
-
//#endregion
|
|
21
23
|
//#region src/mount.d.ts
|
|
22
24
|
type RenderToStringOptions = {
|
|
23
25
|
snapshot?: boolean;
|
|
@@ -32,4 +34,4 @@ type MountOptions = {
|
|
|
32
34
|
declare function mount(el: Element, fn: Component, opts?: MountOptions): () => void;
|
|
33
35
|
declare function renderToString(fn: Component, opts?: RenderToStringOptions): Promise<string>;
|
|
34
36
|
//#endregion
|
|
35
|
-
export { type AtomHandle, type Component,
|
|
37
|
+
export { type AtomHandle, type Component, Fragment, type MountOptions, type RenderToStringOptions, type View, atom, batch, h, mount, renderToString, watch, when };
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,69 @@
|
|
|
1
|
-
import { a as atom, c as
|
|
1
|
+
import { a as atom, c as instr, d as getFiber, f as makeFiber, i as interpret, l as isAtomHandle, n as renderToString, o as batch, r as paint, s as handleOwner, t as mount, u as closeFiber } from "./mount-CzFZBgZG.js";
|
|
2
2
|
import { i as Fragment, t as h } from "./vnode-DHN2EFHi.js";
|
|
3
3
|
import * as Effect from "effect/Effect";
|
|
4
4
|
import * as Stream from "effect/Stream";
|
|
5
|
+
import * as Exit from "effect/Exit";
|
|
6
|
+
import * as Fiber from "effect/Fiber";
|
|
5
7
|
import * as Scope from "effect/Scope";
|
|
6
|
-
import * as
|
|
8
|
+
import * as Registry from "effect/unstable/reactivity/AtomRegistry";
|
|
7
9
|
|
|
10
|
+
//#region src/watch.ts
|
|
11
|
+
function runStreamWatch(fiber, effect) {
|
|
12
|
+
const scope = Scope.forkUnsafe(fiber.scope);
|
|
13
|
+
const provided = effect.pipe(Effect.provideService(Registry.AtomRegistry, fiber.registry));
|
|
14
|
+
const fork = Effect.runFork(provided);
|
|
15
|
+
Effect.runSync(Scope.addFinalizer(scope, Fiber.interrupt(fork)));
|
|
16
|
+
return () => {
|
|
17
|
+
Effect.runFork(Scope.close(scope, Exit.void));
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function useWatchSlot(fiber, key, mount, fn) {
|
|
21
|
+
const i = fiber.watchI ?? 0;
|
|
22
|
+
fiber.watchI = i + 1;
|
|
23
|
+
fiber.watchSlots ??= [];
|
|
24
|
+
const existing = fiber.watchSlots[i];
|
|
25
|
+
if (existing) {
|
|
26
|
+
if (existing.key !== key) {
|
|
27
|
+
existing.dispose();
|
|
28
|
+
fiber.watchSlots[i] = void 0;
|
|
29
|
+
} else {
|
|
30
|
+
existing.fnRef.current = fn;
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const fnRef = { current: fn };
|
|
35
|
+
const unsub = mount((value) => fnRef.current(value));
|
|
36
|
+
const dispose = () => unsub();
|
|
37
|
+
fiber.watchSlots[i] = {
|
|
38
|
+
key,
|
|
39
|
+
fnRef,
|
|
40
|
+
dispose
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function registerWatch(fiber, source, fn) {
|
|
44
|
+
if (Stream.isStream(source)) {
|
|
45
|
+
useWatchSlot(fiber, source, (run) => {
|
|
46
|
+
let active = true;
|
|
47
|
+
const stop = runStreamWatch(fiber, Stream.runForEach(source, (v) => Effect.sync(() => {
|
|
48
|
+
if (active) run(v);
|
|
49
|
+
})));
|
|
50
|
+
return () => {
|
|
51
|
+
active = false;
|
|
52
|
+
stop();
|
|
53
|
+
};
|
|
54
|
+
}, fn);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const atom = isAtomHandle(source) ? source.atom : source;
|
|
58
|
+
useWatchSlot(fiber, atom, (run) => fiber.registry.subscribe(atom, run, { immediate: true }), fn);
|
|
59
|
+
}
|
|
60
|
+
/** Run `fn` when `source` changes — and once on mount. Sync, async, and generator components. */
|
|
61
|
+
function watch(source, fn) {
|
|
62
|
+
registerWatch((isAtomHandle(source) ? handleOwner.get(source) : void 0) ?? getFiber(), source, fn);
|
|
63
|
+
return instr(Effect.void);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
//#endregion
|
|
8
67
|
//#region src/when.ts
|
|
9
68
|
function when(stream, body) {
|
|
10
69
|
return instr(Effect.gen(function* () {
|
|
@@ -25,41 +84,4 @@ function when(stream, body) {
|
|
|
25
84
|
}
|
|
26
85
|
|
|
27
86
|
//#endregion
|
|
28
|
-
|
|
29
|
-
function watch(source, fn) {
|
|
30
|
-
return instr(Effect.sync(() => {
|
|
31
|
-
const fiber = (isAtomHandle(source) ? handleOwner.get(source) : void 0) ?? getFiber();
|
|
32
|
-
if (Stream.isStream(source)) {
|
|
33
|
-
fiber.run(Stream.runForEach(source, (v) => Effect.sync(() => fn(v))));
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
const a = isAtomHandle(source) ? source.atom : source;
|
|
37
|
-
const unsub = fiber.registry.subscribe(a, fn, { immediate: true });
|
|
38
|
-
Effect.runSync(Scope.addFinalizer(fiber.scope, Effect.sync(unsub)));
|
|
39
|
-
}));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
//#endregion
|
|
43
|
-
//#region src/wait.ts
|
|
44
|
-
function wait(body) {
|
|
45
|
-
return instr(Effect.gen(function* () {
|
|
46
|
-
const parent = getFiber();
|
|
47
|
-
const deferred = yield* Deferred.make();
|
|
48
|
-
let settled = false;
|
|
49
|
-
const done = (value) => {
|
|
50
|
-
if (settled) return;
|
|
51
|
-
settled = true;
|
|
52
|
-
Effect.runSync(Deferred.succeed(deferred, value));
|
|
53
|
-
};
|
|
54
|
-
const child = makeFiber(parent.runtime, parent.root, paint, { onFail: parent.fail });
|
|
55
|
-
interpret((() => body(done)), child);
|
|
56
|
-
try {
|
|
57
|
-
return yield* Deferred.await(deferred);
|
|
58
|
-
} finally {
|
|
59
|
-
closeFiber(child);
|
|
60
|
-
}
|
|
61
|
-
}));
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
//#endregion
|
|
65
|
-
export { Fragment, atom, h, mount, renderToString, wait, watch, when };
|
|
87
|
+
export { Fragment, atom, batch, h, mount, renderToString, watch, when };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as JSX } from "./vnode-
|
|
1
|
+
import { r as Fragment } from "./types-Cie0Ihl6.js";
|
|
2
|
+
import { n as JSX } from "./vnode-DyFwtMVw.js";
|
|
3
3
|
import { jsx, jsxDEV, jsxs } from "./jsx-runtime.js";
|
|
4
4
|
export { Fragment, type JSX, jsx, jsxDEV, jsxs };
|
package/dist/jsx-runtime.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as JSX, t as h } from "./vnode-
|
|
1
|
+
import { o as View, r as Fragment } from "./types-Cie0Ihl6.js";
|
|
2
|
+
import { n as JSX, t as h } from "./vnode-DyFwtMVw.js";
|
|
3
3
|
//#region src/jsx-runtime.d.ts
|
|
4
4
|
declare const jsx: (type: string | typeof Fragment | ((p: Record<string, unknown>) => unknown), props: Record<string, unknown> | null, key?: string | number) => ReturnType<typeof h>;
|
|
5
5
|
declare const jsxs: (type: string | typeof Fragment | ((p: Record<string, unknown>) => unknown), props: Record<string, unknown> | null, key?: string | number) => ReturnType<typeof h>;
|
|
@@ -40,6 +40,12 @@ const runInScope = (effect, registry, scope, onOk, onErr, onDone) => {
|
|
|
40
40
|
function closeFiber(fiber) {
|
|
41
41
|
if (fiber.closed) return;
|
|
42
42
|
fiber.closed = true;
|
|
43
|
+
fiber.trackRestore?.();
|
|
44
|
+
fiber.trackRestore = void 0;
|
|
45
|
+
fiber.renderSub?.();
|
|
46
|
+
fiber.renderSub = void 0;
|
|
47
|
+
for (const slot of fiber.watchSlots ?? []) slot?.dispose();
|
|
48
|
+
fiber.watchSlots = [];
|
|
43
49
|
for (const h of fiber.holes) h.dispose();
|
|
44
50
|
fiber.holes = [];
|
|
45
51
|
Effect.runFork(Scope.close(fiber.scope, Exit.void));
|
|
@@ -150,11 +156,108 @@ function isAtomHandle(x) {
|
|
|
150
156
|
return typeof x === "function" && x.$$atom === 1;
|
|
151
157
|
}
|
|
152
158
|
let trackGet;
|
|
159
|
+
const trackStack = [];
|
|
160
|
+
function beginPrimitiveFrame(fiber) {
|
|
161
|
+
const used = fiber.primitiveI ?? 0;
|
|
162
|
+
const slots = fiber.primitives?.length ?? 0;
|
|
163
|
+
if (used > 0 && used !== slots) console.warn(`ilha: atom/watch slot count changed between renders (${slots} → ${used}); keep primitive order stable`);
|
|
164
|
+
fiber.primitiveI = 0;
|
|
165
|
+
fiber.watchI = 0;
|
|
166
|
+
}
|
|
167
|
+
function readTracked(atom, registry) {
|
|
168
|
+
const frame = trackStack.at(-1);
|
|
169
|
+
const active = getActiveFiber();
|
|
170
|
+
if (trackGet && frame && active === frame.fiber) return trackGet(atom);
|
|
171
|
+
return registry.get(atom);
|
|
172
|
+
}
|
|
173
|
+
function usePrimitiveSlot(fiber, make) {
|
|
174
|
+
const i = fiber.primitiveI ?? 0;
|
|
175
|
+
fiber.primitiveI = i + 1;
|
|
176
|
+
fiber.primitives ??= [];
|
|
177
|
+
const existing = fiber.primitives[i];
|
|
178
|
+
if (existing) return {
|
|
179
|
+
atom: existing,
|
|
180
|
+
fresh: false
|
|
181
|
+
};
|
|
182
|
+
const a = make();
|
|
183
|
+
fiber.primitives[i] = a;
|
|
184
|
+
return {
|
|
185
|
+
atom: a,
|
|
186
|
+
fresh: true
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function withTrackGetRun(fiber, fn, onOk, onErr) {
|
|
190
|
+
const deps = /* @__PURE__ */ new Set();
|
|
191
|
+
const get = (a) => {
|
|
192
|
+
deps.add(a);
|
|
193
|
+
return fiber.registry.get(a);
|
|
194
|
+
};
|
|
195
|
+
const restore = () => {
|
|
196
|
+
if (fiber.trackRestore !== restore) return;
|
|
197
|
+
const top = trackStack.pop();
|
|
198
|
+
if (top?.get !== get) {
|
|
199
|
+
if (top) trackStack.push(top);
|
|
200
|
+
const i = trackStack.findIndex((frame) => frame.get === get);
|
|
201
|
+
if (i >= 0) trackStack.splice(i, 1);
|
|
202
|
+
}
|
|
203
|
+
trackGet = trackStack.at(-1)?.get;
|
|
204
|
+
fiber.trackRestore = void 0;
|
|
205
|
+
};
|
|
206
|
+
trackStack.push({
|
|
207
|
+
fiber,
|
|
208
|
+
deps,
|
|
209
|
+
get
|
|
210
|
+
});
|
|
211
|
+
trackGet = get;
|
|
212
|
+
fiber.trackRestore = restore;
|
|
213
|
+
try {
|
|
214
|
+
const result = withFiber(fiber, () => fn());
|
|
215
|
+
if (result instanceof Promise) {
|
|
216
|
+
restore();
|
|
217
|
+
result.then((value) => {
|
|
218
|
+
if (fiber.closed) return;
|
|
219
|
+
onOk({
|
|
220
|
+
value,
|
|
221
|
+
deps
|
|
222
|
+
});
|
|
223
|
+
}).catch((e) => {
|
|
224
|
+
if (fiber.closed) return;
|
|
225
|
+
onErr(e);
|
|
226
|
+
});
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
restore();
|
|
230
|
+
onOk({
|
|
231
|
+
value: result,
|
|
232
|
+
deps
|
|
233
|
+
});
|
|
234
|
+
} catch (e) {
|
|
235
|
+
restore();
|
|
236
|
+
onErr(e);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function subscribeRenderDeps(fiber, deps, run) {
|
|
240
|
+
if (deps.size === 0) return () => {};
|
|
241
|
+
let scheduled = false;
|
|
242
|
+
const schedule = () => {
|
|
243
|
+
if (scheduled) return;
|
|
244
|
+
scheduled = true;
|
|
245
|
+
fiber.runtime.later(() => {
|
|
246
|
+
scheduled = false;
|
|
247
|
+
run();
|
|
248
|
+
});
|
|
249
|
+
};
|
|
250
|
+
const unsubs = [];
|
|
251
|
+
for (const dep of deps) unsubs.push(fiber.registry.subscribe(dep, schedule));
|
|
252
|
+
return () => {
|
|
253
|
+
for (const unsub of unsubs) unsub();
|
|
254
|
+
};
|
|
255
|
+
}
|
|
153
256
|
function wrapHandle(atom, fiber) {
|
|
154
257
|
const registry = fiber.registry;
|
|
155
258
|
const read = (() => {
|
|
156
259
|
guardHandleFiber(read);
|
|
157
|
-
return
|
|
260
|
+
return readTracked(atom, registry);
|
|
158
261
|
});
|
|
159
262
|
handleOwner.set(read, fiber);
|
|
160
263
|
Object.defineProperties(read, {
|
|
@@ -171,34 +274,40 @@ function wrapHandle(atom, fiber) {
|
|
|
171
274
|
});
|
|
172
275
|
return read;
|
|
173
276
|
}
|
|
174
|
-
function
|
|
277
|
+
function makeAtom(init) {
|
|
175
278
|
const fiber = getFiber();
|
|
176
|
-
const computed = typeof init === "function" && !Atom.isAtom(init) && !Effect.isEffect(init) && !Stream.isStream(init);
|
|
177
|
-
let seed = init;
|
|
178
279
|
const snap = fiber.runtime.hydrateValues;
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if (
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
280
|
+
const { atom: a, fresh } = usePrimitiveSlot(fiber, () => {
|
|
281
|
+
let seed = init;
|
|
282
|
+
if (snap && !Atom.isAtom(init) && !Effect.isEffect(init) && !Stream.isStream(init)) {
|
|
283
|
+
const v = snap[fiber.runtime.hydrateI++];
|
|
284
|
+
if (v !== void 0) seed = v;
|
|
285
|
+
}
|
|
286
|
+
if (Atom.isAtom(seed)) return seed;
|
|
287
|
+
if (Effect.isEffect(seed) || Stream.isStream(seed)) return Atom.make(seed);
|
|
288
|
+
return Atom.make(seed);
|
|
289
|
+
});
|
|
290
|
+
const handle = wrapHandle(a, fiber);
|
|
291
|
+
if (fresh && fiber.runtime.ssr) fiber.runtime.ssrValues.push(fiber.registry.get(a));
|
|
292
|
+
return handle;
|
|
293
|
+
}
|
|
294
|
+
function atomLazy(init) {
|
|
295
|
+
const fiber = getFiber();
|
|
296
|
+
const snap = fiber.runtime.hydrateValues;
|
|
297
|
+
const { atom: a, fresh } = usePrimitiveSlot(fiber, () => {
|
|
298
|
+
let seed = init();
|
|
299
|
+
if (snap) {
|
|
300
|
+
const v = snap[fiber.runtime.hydrateI++];
|
|
301
|
+
if (v !== void 0) seed = v;
|
|
302
|
+
}
|
|
303
|
+
return Atom.make(seed);
|
|
304
|
+
});
|
|
198
305
|
const handle = wrapHandle(a, fiber);
|
|
199
|
-
if (fiber.runtime.ssr) fiber.runtime.ssrValues.push(fiber.registry.get(a));
|
|
306
|
+
if (fresh && fiber.runtime.ssr) fiber.runtime.ssrValues.push(fiber.registry.get(a));
|
|
200
307
|
return handle;
|
|
201
308
|
}
|
|
309
|
+
const atom = Object.assign(makeAtom, { lazy: atomLazy });
|
|
310
|
+
const batch = Atom.batch;
|
|
202
311
|
|
|
203
312
|
//#endregion
|
|
204
313
|
//#region src/errors.ts
|
|
@@ -396,17 +505,17 @@ function morphChildren(fromParent, toParent) {
|
|
|
396
505
|
if (fromNode instanceof Element) {
|
|
397
506
|
const fromKey = morphKeyOf(fromNode);
|
|
398
507
|
if (fromKey !== null && fromKey !== toKey && toKeys.has(fromKey)) {
|
|
399
|
-
fromParent.insertBefore(toNode
|
|
508
|
+
fromParent.insertBefore(toNode, fromNode);
|
|
400
509
|
continue;
|
|
401
510
|
}
|
|
402
511
|
}
|
|
403
512
|
}
|
|
404
513
|
if (!fromNode) {
|
|
405
|
-
fromParent.appendChild(toNode
|
|
514
|
+
fromParent.appendChild(toNode);
|
|
406
515
|
continue;
|
|
407
516
|
}
|
|
408
517
|
if (fromNode.nodeType !== toNode.nodeType) {
|
|
409
|
-
fromParent.replaceChild(toNode
|
|
518
|
+
fromParent.replaceChild(toNode, fromNode);
|
|
410
519
|
continue;
|
|
411
520
|
}
|
|
412
521
|
if (fromNode.nodeType === 3 || fromNode.nodeType === 8) {
|
|
@@ -417,7 +526,7 @@ function morphChildren(fromParent, toParent) {
|
|
|
417
526
|
const fromEl = fromNode;
|
|
418
527
|
const toEl = toNode;
|
|
419
528
|
if (fromEl.localName !== toEl.localName) {
|
|
420
|
-
fromParent.replaceChild(toEl
|
|
529
|
+
fromParent.replaceChild(toEl, fromEl);
|
|
421
530
|
continue;
|
|
422
531
|
}
|
|
423
532
|
const slotId = toEl.getAttribute(SLOT_ATTR);
|
|
@@ -552,19 +661,39 @@ function finish(fiber, view) {
|
|
|
552
661
|
fiber.park();
|
|
553
662
|
}
|
|
554
663
|
function runSetup(fiber, fn) {
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
664
|
+
const disposeRenderSub = () => {
|
|
665
|
+
fiber.renderSub?.();
|
|
666
|
+
fiber.renderSub = void 0;
|
|
667
|
+
};
|
|
668
|
+
const commit = (out, deps) => {
|
|
669
|
+
if (isGen(out)) {
|
|
670
|
+
disposeRenderSub();
|
|
671
|
+
interpret((() => out), fiber);
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
disposeRenderSub();
|
|
675
|
+
if (!fiber.runtime.ssr) fiber.renderSub = subscribeRenderDeps(fiber, deps, render);
|
|
676
|
+
withFiber(fiber, () => finish(fiber, out));
|
|
677
|
+
};
|
|
678
|
+
const render = () => {
|
|
679
|
+
if (fiber.closed) return;
|
|
680
|
+
fiber.runtime.begin();
|
|
681
|
+
beginPrimitiveFrame(fiber);
|
|
682
|
+
withTrackGetRun(fiber, () => fn(), ({ value: out, deps }) => {
|
|
683
|
+
try {
|
|
684
|
+
commit(out, deps);
|
|
685
|
+
} catch (e) {
|
|
564
686
|
withFiber(fiber, () => fiber.fail(asFailure(e)));
|
|
687
|
+
} finally {
|
|
565
688
|
fiber.runtime.end();
|
|
566
|
-
}
|
|
567
|
-
}
|
|
689
|
+
}
|
|
690
|
+
}, (e) => {
|
|
691
|
+
withFiber(fiber, () => fiber.fail(asFailure(e)));
|
|
692
|
+
fiber.runtime.end();
|
|
693
|
+
});
|
|
694
|
+
};
|
|
695
|
+
try {
|
|
696
|
+
render();
|
|
568
697
|
} catch (e) {
|
|
569
698
|
fiber.fail(asFailure(e));
|
|
570
699
|
}
|
|
@@ -578,9 +707,16 @@ const ISLAND_SLOT_TAG = Symbol.for("ilha.islandSlotTag");
|
|
|
578
707
|
function insert(fiber, nodes) {
|
|
579
708
|
for (const n of nodes) fiber.root.appendChild(n);
|
|
580
709
|
}
|
|
581
|
-
function disposeHoles(fiber) {
|
|
582
|
-
|
|
583
|
-
fiber.holes
|
|
710
|
+
function disposeHoles(fiber, opts) {
|
|
711
|
+
const keep = [];
|
|
712
|
+
for (const h of fiber.holes) {
|
|
713
|
+
if (opts?.morph && h.keepOnMorph) {
|
|
714
|
+
keep.push(h);
|
|
715
|
+
continue;
|
|
716
|
+
}
|
|
717
|
+
h.dispose();
|
|
718
|
+
}
|
|
719
|
+
fiber.holes = keep;
|
|
584
720
|
}
|
|
585
721
|
function skip(x) {
|
|
586
722
|
return x === null || x === void 0 || typeof x === "boolean";
|
|
@@ -711,17 +847,56 @@ function errorView(e) {
|
|
|
711
847
|
children: [failureMessage(e)]
|
|
712
848
|
};
|
|
713
849
|
}
|
|
714
|
-
function
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
}
|
|
850
|
+
function findReusableAtomHost(fiber, atom) {
|
|
851
|
+
for (const h of fiber.holes) if (h.atom === atom && h.host?.isConnected && h.holeFiber && !h.holeFiber.closed) return {
|
|
852
|
+
host: h.host,
|
|
853
|
+
hole: h.holeFiber
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
function refreshAtomHost(fiber, atom, host, hole) {
|
|
857
|
+
if (hole.closed || !host.isConnected) return;
|
|
858
|
+
const next = unwrap(fiber.registry.get(atom), void 0);
|
|
859
|
+
if (next === KEEP) return;
|
|
860
|
+
paintHole(hole, next);
|
|
861
|
+
}
|
|
862
|
+
function refreshAllAtomHosts(fiber) {
|
|
863
|
+
for (const h of fiber.holes) if (h.atom && h.host && h.holeFiber) refreshAtomHost(fiber, h.atom, h.host, h.holeFiber);
|
|
864
|
+
}
|
|
865
|
+
function pruneDisconnectedAtomHoles(fiber) {
|
|
866
|
+
fiber.holes = fiber.holes.filter((h) => {
|
|
867
|
+
if (!h.atom || !h.host || !h.holeFiber) return true;
|
|
868
|
+
if (h.host.isConnected) return true;
|
|
869
|
+
h.dispose();
|
|
870
|
+
return false;
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
function atomHostPlaceholder(host) {
|
|
874
|
+
const placeholder = document.createElement("span");
|
|
875
|
+
const slot = host.getAttribute(SLOT_ATTR);
|
|
876
|
+
if (slot !== null) placeholder.setAttribute(SLOT_ATTR, slot);
|
|
877
|
+
placeholder.style.display = "contents";
|
|
878
|
+
return placeholder;
|
|
879
|
+
}
|
|
880
|
+
function trackHole(parent, hole, extra, meta) {
|
|
881
|
+
parent.holes.push({
|
|
882
|
+
keepOnMorph: !!(meta?.atom && meta?.host) || !!meta?.keyed,
|
|
883
|
+
atom: meta?.atom,
|
|
884
|
+
host: meta?.host,
|
|
885
|
+
holeFiber: hole,
|
|
886
|
+
dispose() {
|
|
887
|
+
extra?.();
|
|
888
|
+
closeFiber(hole);
|
|
889
|
+
}
|
|
890
|
+
});
|
|
719
891
|
}
|
|
720
892
|
function materialize(view, fiber) {
|
|
721
893
|
if (skip(view)) return [];
|
|
722
894
|
if (typeof view === "string" || typeof view === "number" || typeof view === "bigint") return [document.createTextNode(String(view))];
|
|
723
895
|
if (isAtomHandle(view)) {
|
|
896
|
+
const reused = findReusableAtomHost(fiber, view.atom);
|
|
897
|
+
if (reused) return [atomHostPlaceholder(reused.host)];
|
|
724
898
|
const { fiber: hole, nodes } = openHole(fiber);
|
|
899
|
+
const host = nodes[0];
|
|
725
900
|
let unsub = () => {};
|
|
726
901
|
fiber.runtime.later(() => {
|
|
727
902
|
if (hole.closed) return;
|
|
@@ -734,7 +909,10 @@ function materialize(view, fiber) {
|
|
|
734
909
|
paintHole(hole, next);
|
|
735
910
|
}, { immediate: true });
|
|
736
911
|
});
|
|
737
|
-
trackHole(fiber, hole, () => unsub()
|
|
912
|
+
trackHole(fiber, hole, () => unsub(), {
|
|
913
|
+
atom: view.atom,
|
|
914
|
+
host
|
|
915
|
+
});
|
|
738
916
|
return nodes;
|
|
739
917
|
}
|
|
740
918
|
if (Stream.isStream(view)) {
|
|
@@ -792,7 +970,7 @@ function materialize(view, fiber) {
|
|
|
792
970
|
fiber.runtime.later(() => {
|
|
793
971
|
if (!hole.closed) runSetup(hole, () => type(hole.propsBox.current));
|
|
794
972
|
});
|
|
795
|
-
trackHole(fiber, hole);
|
|
973
|
+
trackHole(fiber, hole, void 0, k ? { keyed: true } : void 0);
|
|
796
974
|
return nodes;
|
|
797
975
|
}
|
|
798
976
|
const el = document.createElement(view.type);
|
|
@@ -838,10 +1016,13 @@ function paint(fiber, view) {
|
|
|
838
1016
|
}
|
|
839
1017
|
}
|
|
840
1018
|
if (fiber.liveEl && isVNode(view) && typeof view.type === "string" && fiber.liveEl.localName === view.type) {
|
|
1019
|
+
disposeHoles(fiber, { morph: true });
|
|
841
1020
|
applyProps(fiber.liveEl, view.props, fiber);
|
|
842
1021
|
const tmp = document.createElement(fiber.liveEl.localName);
|
|
843
1022
|
for (const c of view.children) for (const n of materialize(c, fiber)) tmp.appendChild(n);
|
|
844
1023
|
morphInner(fiber.liveEl, tmp);
|
|
1024
|
+
refreshAllAtomHosts(fiber);
|
|
1025
|
+
pruneDisconnectedAtomHoles(fiber);
|
|
845
1026
|
return;
|
|
846
1027
|
}
|
|
847
1028
|
disposeHoles(fiber);
|
|
@@ -989,4 +1170,4 @@ async function renderToString(fn, opts) {
|
|
|
989
1170
|
}
|
|
990
1171
|
|
|
991
1172
|
//#endregion
|
|
992
|
-
export { atom as a,
|
|
1173
|
+
export { atom as a, instr as c, getFiber as d, makeFiber as f, interpret as i, isAtomHandle as l, renderToString as n, batch as o, paint as r, handleOwner as s, mount as t, closeFiber as u };
|
|
@@ -28,6 +28,5 @@ interface AtomHandle<A> {
|
|
|
28
28
|
set(next: A): void;
|
|
29
29
|
update(f: (current: A) => A): void;
|
|
30
30
|
}
|
|
31
|
-
type Done<A> = (value: A) => void;
|
|
32
31
|
//#endregion
|
|
33
|
-
export {
|
|
32
|
+
export { VNode as a, Instruction as i, Component as n, View as o, Fragment as r, Yielded as s, AtomHandle as t };
|