@fictjs/runtime 0.0.10 → 0.0.11
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/dist/index.cjs +73 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.dev.js +73 -6
- package/dist/index.dev.js.map +1 -1
- package/dist/index.js +73 -6
- package/dist/index.js.map +1 -1
- package/dist/slim.cjs +73 -6
- package/dist/slim.cjs.map +1 -1
- package/dist/slim.js +73 -6
- package/dist/slim.js.map +1 -1
- package/package.json +1 -1
- package/src/effect.ts +16 -5
- package/src/signal.ts +86 -2
package/package.json
CHANGED
package/src/effect.ts
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
runCleanupList,
|
|
6
6
|
withEffectCleanups,
|
|
7
7
|
} from './lifecycle'
|
|
8
|
-
import {
|
|
8
|
+
import { effectWithCleanup } from './signal'
|
|
9
9
|
import type { Cleanup } from './types'
|
|
10
10
|
|
|
11
11
|
export type Effect = () => void | Cleanup
|
|
@@ -14,8 +14,14 @@ export function createEffect(fn: Effect): () => void {
|
|
|
14
14
|
let cleanups: Cleanup[] = []
|
|
15
15
|
const rootForError = getCurrentRoot()
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
// Cleanup runner - called by runEffect BEFORE signal values are committed
|
|
18
|
+
const doCleanup = () => {
|
|
18
19
|
runCleanupList(cleanups)
|
|
20
|
+
cleanups = []
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const run = () => {
|
|
24
|
+
// Note: cleanups are now run by signal.ts runEffect before this function is called
|
|
19
25
|
const bucket: Cleanup[] = []
|
|
20
26
|
withEffectCleanups(bucket, () => {
|
|
21
27
|
try {
|
|
@@ -33,7 +39,7 @@ export function createEffect(fn: Effect): () => void {
|
|
|
33
39
|
cleanups = bucket
|
|
34
40
|
}
|
|
35
41
|
|
|
36
|
-
const disposeEffect =
|
|
42
|
+
const disposeEffect = effectWithCleanup(run, doCleanup)
|
|
37
43
|
const teardown = () => {
|
|
38
44
|
runCleanupList(cleanups)
|
|
39
45
|
disposeEffect()
|
|
@@ -50,11 +56,16 @@ export function createRenderEffect(fn: Effect): () => void {
|
|
|
50
56
|
let cleanup: Cleanup | undefined
|
|
51
57
|
const rootForError = getCurrentRoot()
|
|
52
58
|
|
|
53
|
-
|
|
59
|
+
// Cleanup runner - called by runEffect BEFORE signal values are committed
|
|
60
|
+
const doCleanup = () => {
|
|
54
61
|
if (cleanup) {
|
|
55
62
|
cleanup()
|
|
56
63
|
cleanup = undefined
|
|
57
64
|
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const run = () => {
|
|
68
|
+
// Note: cleanups are now run by signal.ts runEffect before this function is called
|
|
58
69
|
try {
|
|
59
70
|
const maybeCleanup = fn()
|
|
60
71
|
if (typeof maybeCleanup === 'function') {
|
|
@@ -69,7 +80,7 @@ export function createRenderEffect(fn: Effect): () => void {
|
|
|
69
80
|
}
|
|
70
81
|
}
|
|
71
82
|
|
|
72
|
-
const disposeEffect =
|
|
83
|
+
const disposeEffect = effectWithCleanup(run, doCleanup)
|
|
73
84
|
const teardown = () => {
|
|
74
85
|
if (cleanup) {
|
|
75
86
|
cleanup()
|
package/src/signal.ts
CHANGED
|
@@ -96,6 +96,10 @@ export interface EffectNode extends BaseNode {
|
|
|
96
96
|
deps: Link | undefined
|
|
97
97
|
/** Last dependency link */
|
|
98
98
|
depsTail: Link | undefined
|
|
99
|
+
/** Optional cleanup runner to be called before checkDirty */
|
|
100
|
+
runCleanup?: () => void
|
|
101
|
+
/** Devtools ID */
|
|
102
|
+
__id?: number | undefined
|
|
99
103
|
}
|
|
100
104
|
|
|
101
105
|
/**
|
|
@@ -202,6 +206,8 @@ const enqueueMicrotask =
|
|
|
202
206
|
: (fn: () => void) => {
|
|
203
207
|
Promise.resolve().then(fn)
|
|
204
208
|
}
|
|
209
|
+
// Flag to indicate cleanup is running - signal reads should return currentValue without updating
|
|
210
|
+
let inCleanup = false
|
|
205
211
|
export const ReactiveFlags = {
|
|
206
212
|
None: 0,
|
|
207
213
|
Mutable,
|
|
@@ -735,7 +741,16 @@ function updateComputed<T>(c: ComputedNode<T>): boolean {
|
|
|
735
741
|
*/
|
|
736
742
|
function runEffect(e: EffectNode): void {
|
|
737
743
|
const flags = e.flags
|
|
738
|
-
|
|
744
|
+
// Run cleanup BEFORE checkDirty so cleanup sees previous signal values
|
|
745
|
+
if (flags & Dirty) {
|
|
746
|
+
if (e.runCleanup) {
|
|
747
|
+
inCleanup = true
|
|
748
|
+
try {
|
|
749
|
+
e.runCleanup()
|
|
750
|
+
} finally {
|
|
751
|
+
inCleanup = false
|
|
752
|
+
}
|
|
753
|
+
}
|
|
739
754
|
++cycle
|
|
740
755
|
effectRunDevtools(e)
|
|
741
756
|
e.depsTail = undefined
|
|
@@ -752,6 +767,36 @@ function runEffect(e: EffectNode): void {
|
|
|
752
767
|
e.flags = Watching
|
|
753
768
|
throw err
|
|
754
769
|
}
|
|
770
|
+
} else if (flags & Pending && e.deps) {
|
|
771
|
+
// Run cleanup before checkDirty which commits signal values
|
|
772
|
+
if (e.runCleanup) {
|
|
773
|
+
inCleanup = true
|
|
774
|
+
try {
|
|
775
|
+
e.runCleanup()
|
|
776
|
+
} finally {
|
|
777
|
+
inCleanup = false
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
if (checkDirty(e.deps, e)) {
|
|
781
|
+
++cycle
|
|
782
|
+
effectRunDevtools(e)
|
|
783
|
+
e.depsTail = undefined
|
|
784
|
+
e.flags = WatchingRunning
|
|
785
|
+
const prevSub = activeSub
|
|
786
|
+
activeSub = e
|
|
787
|
+
try {
|
|
788
|
+
e.fn()
|
|
789
|
+
activeSub = prevSub
|
|
790
|
+
e.flags = Watching
|
|
791
|
+
purgeDeps(e)
|
|
792
|
+
} catch (err) {
|
|
793
|
+
activeSub = prevSub
|
|
794
|
+
e.flags = Watching
|
|
795
|
+
throw err
|
|
796
|
+
}
|
|
797
|
+
} else {
|
|
798
|
+
e.flags = Watching
|
|
799
|
+
}
|
|
755
800
|
} else {
|
|
756
801
|
e.flags = Watching
|
|
757
802
|
}
|
|
@@ -870,7 +915,8 @@ function signalOper<T>(this: SignalNode<T>, value?: T): T | void {
|
|
|
870
915
|
}
|
|
871
916
|
|
|
872
917
|
const flags = this.flags
|
|
873
|
-
|
|
918
|
+
// During cleanup, don't update signal - return currentValue as-is
|
|
919
|
+
if (flags & Dirty && !inCleanup) {
|
|
874
920
|
if (updateSignal(this)) {
|
|
875
921
|
const subs = this.subs
|
|
876
922
|
if (subs !== undefined) shallowPropagate(subs)
|
|
@@ -976,6 +1022,44 @@ export function effect(fn: () => void): EffectDisposer {
|
|
|
976
1022
|
|
|
977
1023
|
return effectOper.bind(e) as EffectDisposer
|
|
978
1024
|
}
|
|
1025
|
+
|
|
1026
|
+
/**
|
|
1027
|
+
* Create a reactive effect with a custom cleanup runner
|
|
1028
|
+
* The cleanup runner is called BEFORE signal values are committed, allowing
|
|
1029
|
+
* cleanup functions to access the previous values of signals.
|
|
1030
|
+
* @param fn - The effect function
|
|
1031
|
+
* @param cleanupRunner - Function to run cleanups before signal value commit
|
|
1032
|
+
* @returns An effect disposer function
|
|
1033
|
+
*/
|
|
1034
|
+
export function effectWithCleanup(fn: () => void, cleanupRunner: () => void): EffectDisposer {
|
|
1035
|
+
const e: EffectNode = {
|
|
1036
|
+
fn,
|
|
1037
|
+
subs: undefined,
|
|
1038
|
+
subsTail: undefined,
|
|
1039
|
+
deps: undefined,
|
|
1040
|
+
depsTail: undefined,
|
|
1041
|
+
flags: WatchingRunning,
|
|
1042
|
+
runCleanup: cleanupRunner,
|
|
1043
|
+
__id: undefined as number | undefined,
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
registerEffectDevtools(e)
|
|
1047
|
+
|
|
1048
|
+
const prevSub = activeSub
|
|
1049
|
+
if (prevSub !== undefined) link(e, prevSub, 0)
|
|
1050
|
+
activeSub = e
|
|
1051
|
+
|
|
1052
|
+
try {
|
|
1053
|
+
effectRunDevtools(e)
|
|
1054
|
+
fn()
|
|
1055
|
+
} finally {
|
|
1056
|
+
activeSub = prevSub
|
|
1057
|
+
e.flags &= ~Running
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
return effectOper.bind(e) as EffectDisposer
|
|
1061
|
+
}
|
|
1062
|
+
|
|
979
1063
|
function effectOper(this: EffectNode): void {
|
|
980
1064
|
disposeNode(this)
|
|
981
1065
|
}
|