Skip to content

Commit 2de133c

Browse files
committed
WIP
1 parent 3d7f7f5 commit 2de133c

File tree

4 files changed

+229
-225
lines changed

4 files changed

+229
-225
lines changed

packages/core/primitives/signals/src/graph.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ let activeConsumer: ReactiveNode | null = null;
2828
let isAngularActive: boolean = false;
2929
let inNotificationPhase = false;
3030

31-
const angularLibrary: ConsumerLibrary = {
31+
export const angularLibrary: ConsumerLibrary = {
3232
onActiveChange: (active) => {
3333
isAngularActive = active;
3434
},
@@ -50,9 +50,6 @@ let epoch: Version = 1 as Version;
5050
*/
5151
export const SIGNAL = /* @__PURE__ */ Symbol('SIGNAL');
5252

53-
const hasActiveConsumer = (): boolean =>
54-
isAngularActive ? !!activeConsumer : interopHasActiveConsumer();
55-
5653
export const setActiveConsumer = (consumer: ReactiveNode | null): (() => void) => {
5754
if ((activeConsumer === consumer && isAngularActive) || (!consumer && !hasActiveConsumer())) {
5855
return noop;
@@ -66,8 +63,11 @@ export const setActiveConsumer = (consumer: ReactiveNode | null): (() => void) =
6663
};
6764
};
6865

69-
export function getActiveConsumer(): ReactiveNode | null {
70-
return activeConsumer;
66+
export const hasActiveConsumer = (): boolean =>
67+
isAngularActive ? !!activeConsumer : interopHasActiveConsumer();
68+
69+
export function getAngularActiveConsumer(): ReactiveNode | null {
70+
return isAngularActive ? activeConsumer : null;
7171
}
7272

7373
export function isInNotificationPhase(): boolean {
@@ -276,7 +276,7 @@ export function producerAccessed<T>(node: ReactiveNode): void {
276276
}
277277
}
278278

279-
function internalProducerAccessed(node: ReactiveNode, activeConsumer: ReactiveNode): void {
279+
export function internalProducerAccessed(node: ReactiveNode, activeConsumer: ReactiveNode): void {
280280
activeConsumer.consumerOnSignalRead(node);
281281

282282
// This producer is the `idx`th dependency of `activeConsumer`.

packages/core/primitives/signals/src/interop.ts

Lines changed: 0 additions & 218 deletions
This file was deleted.
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
import {
9+
angularLibrary,
10+
consumerMarkDirty,
11+
getAngularActiveConsumer,
12+
internalProducerAccessed,
13+
producerUpdateValueVersion,
14+
REACTIVE_NODE,
15+
ReactiveNode,
16+
} from './graph';
17+
import {Watcher as InteropWatcher, WatchableSignal} from './interop_lib';
18+
19+
function interopSignal<T>(signal: WatchableSignal): ReactiveNode {
20+
const node: InteropSignalNode<T> = Object.create(INTEROP_SIGNAL_NODE);
21+
node.watcher = signal.watchSignal(() => {
22+
if (!node.dirty) {
23+
consumerMarkDirty(node);
24+
}
25+
});
26+
return node;
27+
}
28+
29+
interface InteropSignalNode<T> extends ReactiveNode {
30+
computing: boolean;
31+
started: boolean;
32+
watcher: InteropWatcher;
33+
}
34+
35+
// Note: Using an IIFE here to ensure that the spread assignment is not considered
36+
// a side-effect, ending up preserving `INTEROP_SIGNAL_NODE` and `REACTIVE_NODE`.
37+
// TODO: remove when https://github.com/evanw/esbuild/issues/3392 is resolved.
38+
const INTEROP_SIGNAL_NODE: InteropSignalNode<unknown> = /* @__PURE__ */ (() => {
39+
return {
40+
...REACTIVE_NODE,
41+
dirty: true,
42+
started: false,
43+
computing: false,
44+
kind: 'interop_signal',
45+
watcher: null!,
46+
hasInteropSignalDep: true,
47+
48+
producerMustRecompute(node: InteropSignalNode<unknown>): boolean {
49+
return node.computing || !node.watcher.isUpToDate();
50+
},
51+
52+
producerRecomputeValue(node: InteropSignalNode<unknown>): void {
53+
if (node.computing) {
54+
// Our computation somehow led to a cyclic read of itself.
55+
throw new Error('Detected cycle in computations.');
56+
}
57+
58+
node.computing = true;
59+
let differentValue = true;
60+
try {
61+
differentValue = node.watcher.update();
62+
} finally {
63+
node.computing = false;
64+
if (differentValue) {
65+
node.version++;
66+
}
67+
}
68+
},
69+
70+
producerOnAccess() {
71+
producerUpdateValueVersion(this);
72+
},
73+
74+
watched() {
75+
if (!this.started) {
76+
this.started = true;
77+
this.watcher.start();
78+
this.producerRecomputeValue(this);
79+
this.dirty = false;
80+
}
81+
},
82+
83+
unwatched() {
84+
if (this.started) {
85+
this.started = false;
86+
this.watcher.stop();
87+
}
88+
},
89+
};
90+
})();
91+
92+
const interopSignalMap = new WeakMap<WatchableSignal, ReactiveNode>();
93+
94+
angularLibrary.producerAccessed = (signal: WatchableSignal) => {
95+
const activeConsumer = getAngularActiveConsumer();
96+
if (activeConsumer) {
97+
let producer = interopSignalMap.get(signal);
98+
if (!producer) {
99+
producer = interopSignal(signal);
100+
interopSignalMap.set(signal, producer);
101+
}
102+
producer.producerOnAccess?.();
103+
internalProducerAccessed(producer, activeConsumer);
104+
}
105+
};

0 commit comments

Comments
 (0)