Skip to content

Commit 0f17bb3

Browse files
committed
chore: move computed impl to separate file
1 parent 74a9539 commit 0f17bb3

File tree

9 files changed

+135
-119
lines changed

9 files changed

+135
-119
lines changed

packages/qwik/src/core/debug.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import { isQrl } from '../server/prefetch-strategy';
22
import { isJSXNode } from './shared/jsx/jsx-runtime';
33
import { isTask } from './use/use-task';
44
import { vnode_getProp, vnode_isVNode } from './client/vnode';
5-
import { ComputedSignalImpl, WrappedSignal, isSignal } from './signal/signal';
5+
import { WrappedSignal, isSignal } from './signal/signal';
66
import { isStore } from './signal/store';
77
import { DEBUG_TYPE } from './shared/types';
8+
import { ComputedSignalImpl } from './signal/impl/computed-signal-impl';
89

910
const stringifyPath: any[] = [];
1011
export function qwikDebugToString(value: any): any {

packages/qwik/src/core/shared/scheduler.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ import {
9191
} from '../client/types';
9292
import { VNodeJournalOpCode, vnode_isVNode, vnode_setAttr } from '../client/vnode';
9393
import { vnode_diff } from '../client/vnode-diff';
94-
import { triggerEffects, type ComputedSignalImpl, type WrappedSignal } from '../signal/signal';
94+
import { triggerEffects, type WrappedSignal } from '../signal/signal';
9595
import { isSignal, type Signal } from '../signal/signal.public';
9696
import type { TargetType } from '../signal/store';
9797
import type { ISsrNode } from '../ssr/ssr-types';
@@ -120,6 +120,7 @@ import { addComponentStylePrefix } from './utils/scoped-styles';
120120
import { serializeAttribute } from './utils/styles';
121121
import type { ValueOrPromise } from './utils/types';
122122
import type { NodePropPayload } from '../signal/subscription-data';
123+
import type { ComputedSignalImpl } from '../signal/impl/computed-signal-impl';
123124

124125
// Turn this on to get debug output of what the scheduler is doing.
125126
const DEBUG: boolean = false;

packages/qwik/src/core/shared/shared-serialization.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import type { VNode } from '../client/types';
99
import { vnode_getNode, vnode_isVNode, vnode_locate, vnode_toString } from '../client/vnode';
1010
import { _EFFECT_BACK_REF, NEEDS_COMPUTATION } from '../signal/flags';
1111
import {
12-
ComputedSignalImpl,
1312
SerializerSignalImpl,
1413
WrappedSignal,
1514
isSerializerObj,
@@ -59,6 +58,7 @@ import {
5958
} from '../signal/types';
6059
import { SubscriptionData, type NodePropData } from '../signal/subscription-data';
6160
import { SignalImpl } from '../signal/impl/signal-impl';
61+
import { ComputedSignalImpl } from '../signal/impl/computed-signal-impl';
6262

6363
const deserializedProxyMap = new WeakMap<object, unknown[]>();
6464

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import type { ComputeQRL, EffectSubscription } from '../types';
2+
import { EffectProperty } from '../types';
3+
import type { BackRef } from '../signal-cleanup';
4+
import { SignalFlags } from '../types';
5+
import { SignalImpl } from './signal-impl';
6+
import { _EFFECT_BACK_REF, NEEDS_COMPUTATION } from '../flags';
7+
import type { Container } from '../../shared/types';
8+
import { ChoreType } from '../../shared/util-chore-type';
9+
import { assertFalse } from '../../shared/error/assert';
10+
import { throwIfQRLNotResolved } from '../signal';
11+
import { tryGetInvokeContext } from '../../use/use-core';
12+
import { getSubscriber } from '../subscriber';
13+
import { isPromise } from '../../shared/utils/promises';
14+
import { QError, qError } from '../../shared/error/error';
15+
import { qwikDebugToString } from '../../debug';
16+
17+
const DEBUG = false;
18+
// eslint-disable-next-line no-console
19+
const log = (...args: any[]) => console.log('COMPUTED SIGNAL', ...args.map(qwikDebugToString));
20+
21+
/**
22+
* A signal which is computed from other signals.
23+
*
24+
* The value is available synchronously, but the computation is done lazily.
25+
*/
26+
export class ComputedSignalImpl<T> extends SignalImpl<T> implements BackRef {
27+
/**
28+
* The compute function is stored here.
29+
*
30+
* The computed functions must be executed synchronously (because of this we need to eagerly
31+
* resolve the QRL during the mark dirty phase so that any call to it will be synchronous). )
32+
*/
33+
$computeQrl$: ComputeQRL<T>;
34+
$flags$: SignalFlags;
35+
$forceRunEffects$: boolean = false;
36+
[_EFFECT_BACK_REF]: Map<EffectProperty | string, EffectSubscription> | null = null;
37+
38+
constructor(
39+
container: Container | null,
40+
fn: ComputeQRL<T>,
41+
// We need a separate flag to know when the computation needs running because
42+
// we need the old value to know if effects need running after computation
43+
flags = SignalFlags.INVALID
44+
) {
45+
// The value is used for comparison when signals trigger, which can only happen
46+
// when it was calculated before. Therefore we can pass whatever we like.
47+
super(container, NEEDS_COMPUTATION);
48+
this.$computeQrl$ = fn;
49+
this.$flags$ = flags;
50+
}
51+
52+
$invalidate$() {
53+
this.$flags$ |= SignalFlags.INVALID;
54+
this.$forceRunEffects$ = false;
55+
this.$container$?.$scheduler$(ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS, null, this);
56+
}
57+
58+
/**
59+
* Use this to force running subscribers, for example when the calculated value has mutated but
60+
* remained the same object
61+
*/
62+
force() {
63+
this.$forceRunEffects$ = true;
64+
this.$container$?.$scheduler$(ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS, null, this);
65+
}
66+
67+
get untrackedValue() {
68+
const didChange = this.$computeIfNeeded$();
69+
if (didChange) {
70+
this.$forceRunEffects$ = didChange;
71+
}
72+
assertFalse(this.$untrackedValue$ === NEEDS_COMPUTATION, 'Invalid state');
73+
return this.$untrackedValue$;
74+
}
75+
76+
$computeIfNeeded$() {
77+
if (!(this.$flags$ & SignalFlags.INVALID)) {
78+
return false;
79+
}
80+
const computeQrl = this.$computeQrl$;
81+
throwIfQRLNotResolved(computeQrl);
82+
83+
const ctx = tryGetInvokeContext();
84+
const previousEffectSubscription = ctx?.$effectSubscriber$;
85+
ctx && (ctx.$effectSubscriber$ = getSubscriber(this, EffectProperty.VNODE));
86+
try {
87+
const untrackedValue = computeQrl.getFn(ctx)() as T;
88+
if (isPromise(untrackedValue)) {
89+
throw qError(QError.computedNotSync, [
90+
computeQrl.dev ? computeQrl.dev.file : '',
91+
computeQrl.$hash$,
92+
]);
93+
}
94+
DEBUG && log('Signal.$compute$', untrackedValue);
95+
96+
this.$flags$ &= ~SignalFlags.INVALID;
97+
98+
const didChange = untrackedValue !== this.$untrackedValue$;
99+
if (didChange) {
100+
this.$untrackedValue$ = untrackedValue;
101+
}
102+
return didChange;
103+
} finally {
104+
if (ctx) {
105+
ctx.$effectSubscriber$ = previousEffectSubscription;
106+
}
107+
}
108+
}
109+
110+
// Make this signal read-only
111+
set value(_: any) {
112+
throw qError(QError.computedReadOnly);
113+
}
114+
// Getters don't get inherited when overriding a setter
115+
get value() {
116+
return super.value;
117+
}
118+
}

packages/qwik/src/core/signal/signal-api.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
import type { QRLInternal } from '../shared/qrl/qrl-class';
22
import type { QRL } from '../shared/qrl/qrl.public';
33
import { SignalImpl } from './impl/signal-impl';
4-
import {
5-
ComputedSignalImpl,
6-
SerializerSignalImpl,
7-
throwIfQRLNotResolved,
8-
type SerializerArg,
9-
} from './signal';
4+
import { ComputedSignalImpl } from './impl/computed-signal-impl';
5+
import { SerializerSignalImpl, throwIfQRLNotResolved, type SerializerArg } from './signal';
106
import type { Signal } from './signal.public';
117

128
/** @internal */

packages/qwik/src/core/signal/signal.ts

Lines changed: 3 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,24 @@ import type { QRL } from '../shared/qrl/qrl.public';
2222
import type { Container, HostElement } from '../shared/types';
2323
import { ChoreType } from '../shared/util-chore-type';
2424
import { ELEMENT_PROPS, OnRenderProp } from '../shared/utils/markers';
25-
import { isPromise } from '../shared/utils/promises';
2625
import { SerializerSymbol } from '../shared/utils/serialize-utils';
2726
import type { ISsrNode, SSRContainer } from '../ssr/ssr-types';
28-
import { trackSignal, tryGetInvokeContext } from '../use/use-core';
27+
import { trackSignal } from '../use/use-core';
2928
import { TaskFlags, isTask } from '../use/use-task';
3029
import { NEEDS_COMPUTATION, _EFFECT_BACK_REF } from './flags';
30+
import { ComputedSignalImpl } from './impl/computed-signal-impl';
3131
import { SignalImpl } from './impl/signal-impl';
3232
import { type BackRef } from './signal-cleanup';
3333
import type { Signal } from './signal.public';
3434
import type { TargetType } from './store';
35-
import { getSubscriber } from './subscriber';
3635
import { SubscriptionData, type NodePropPayload } from './subscription-data';
3736
import {
3837
EffectProperty,
3938
EffectSubscriptionProp,
4039
SignalFlags,
4140
WrappedSignalFlags,
4241
type AllSignalFlags,
42+
type ComputeQRL,
4343
type EffectSubscription,
4444
} from './types';
4545

@@ -162,107 +162,6 @@ export const triggerEffects = (
162162
DEBUG && log('done scheduling');
163163
};
164164

165-
type ComputeQRL<T> = QRLInternal<() => T>;
166-
167-
/**
168-
* A signal which is computed from other signals.
169-
*
170-
* The value is available synchronously, but the computation is done lazily.
171-
*/
172-
export class ComputedSignalImpl<T> extends SignalImpl<T> implements BackRef {
173-
/**
174-
* The compute function is stored here.
175-
*
176-
* The computed functions must be executed synchronously (because of this we need to eagerly
177-
* resolve the QRL during the mark dirty phase so that any call to it will be synchronous). )
178-
*/
179-
$computeQrl$: ComputeQRL<T>;
180-
$flags$: SignalFlags;
181-
$forceRunEffects$: boolean = false;
182-
[_EFFECT_BACK_REF]: Map<EffectProperty | string, EffectSubscription> | null = null;
183-
184-
constructor(
185-
container: Container | null,
186-
fn: ComputeQRL<T>,
187-
// We need a separate flag to know when the computation needs running because
188-
// we need the old value to know if effects need running after computation
189-
flags = SignalFlags.INVALID
190-
) {
191-
// The value is used for comparison when signals trigger, which can only happen
192-
// when it was calculated before. Therefore we can pass whatever we like.
193-
super(container, NEEDS_COMPUTATION);
194-
this.$computeQrl$ = fn;
195-
this.$flags$ = flags;
196-
}
197-
198-
$invalidate$() {
199-
this.$flags$ |= SignalFlags.INVALID;
200-
this.$forceRunEffects$ = false;
201-
this.$container$?.$scheduler$(ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS, null, this);
202-
}
203-
204-
/**
205-
* Use this to force running subscribers, for example when the calculated value has mutated but
206-
* remained the same object
207-
*/
208-
force() {
209-
this.$forceRunEffects$ = true;
210-
this.$container$?.$scheduler$(ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS, null, this);
211-
}
212-
213-
get untrackedValue() {
214-
const didChange = this.$computeIfNeeded$();
215-
if (didChange) {
216-
this.$forceRunEffects$ = didChange;
217-
}
218-
assertFalse(this.$untrackedValue$ === NEEDS_COMPUTATION, 'Invalid state');
219-
return this.$untrackedValue$;
220-
}
221-
222-
$computeIfNeeded$() {
223-
if (!(this.$flags$ & SignalFlags.INVALID)) {
224-
return false;
225-
}
226-
const computeQrl = this.$computeQrl$;
227-
throwIfQRLNotResolved(computeQrl);
228-
229-
const ctx = tryGetInvokeContext();
230-
const previousEffectSubscription = ctx?.$effectSubscriber$;
231-
ctx && (ctx.$effectSubscriber$ = getSubscriber(this, EffectProperty.VNODE));
232-
try {
233-
const untrackedValue = computeQrl.getFn(ctx)() as T;
234-
if (isPromise(untrackedValue)) {
235-
throw qError(QError.computedNotSync, [
236-
computeQrl.dev ? computeQrl.dev.file : '',
237-
computeQrl.$hash$,
238-
]);
239-
}
240-
DEBUG && log('Signal.$compute$', untrackedValue);
241-
242-
this.$flags$ &= ~SignalFlags.INVALID;
243-
244-
const didChange = untrackedValue !== this.$untrackedValue$;
245-
if (didChange) {
246-
this.$untrackedValue$ = untrackedValue;
247-
}
248-
return didChange;
249-
} finally {
250-
if (ctx) {
251-
ctx.$effectSubscriber$ = previousEffectSubscription;
252-
}
253-
}
254-
}
255-
256-
// Make this signal read-only
257-
set value(_: any) {
258-
throw qError(QError.computedReadOnly);
259-
}
260-
// Getters don't get inherited when overriding a setter
261-
get value() {
262-
return super.value;
263-
}
264-
}
265-
266165
export class WrappedSignal<T> extends SignalImpl<T> implements BackRef {
267166
$args$: any[];
268167
$func$: (...args: any[]) => T;

packages/qwik/src/core/signal/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { SubscriptionData } from './subscription-data';
55
import type { ReadonlySignal } from './signal.public';
66
import type { TargetType } from './store';
77
import type { SignalImpl } from './impl/signal-impl';
8+
import type { QRLInternal } from '../shared/qrl/qrl-class';
89

910
export interface InternalReadonlySignal<T = unknown> extends ReadonlySignal<T> {
1011
readonly untrackedValue: T;
@@ -15,6 +16,8 @@ export interface InternalSignal<T = any> extends InternalReadonlySignal<T> {
1516
untrackedValue: T;
1617
}
1718

19+
export type ComputeQRL<T> = QRLInternal<() => T>;
20+
1821
export const enum SignalFlags {
1922
INVALID = 1,
2023
}

packages/qwik/src/core/use/use-computed.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { implicit$FirstArg } from '../shared/qrl/implicit_dollar';
22
import { assertQrl } from '../shared/qrl/qrl-utils';
33
import type { QRL } from '../shared/qrl/qrl.public';
4-
import { ComputedSignalImpl, throwIfQRLNotResolved } from '../signal/signal';
4+
import { ComputedSignalImpl } from '../signal/impl/computed-signal-impl';
5+
import { throwIfQRLNotResolved } from '../signal/signal';
56
import type { ReadonlySignal, Signal } from '../signal/signal.public';
67
import { useSequentialScope } from './use-sequential-scope';
78

packages/qwik/src/core/use/use-serializer.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import { implicit$FirstArg } from '../shared/qrl/implicit_dollar';
22
import type { QRL } from '../shared/qrl/qrl.public';
3-
import {
4-
SerializerSignalImpl,
5-
type ComputedSignalImpl,
6-
type SerializerArg,
7-
} from '../signal/signal';
3+
import type { ComputedSignalImpl } from '../signal/impl/computed-signal-impl';
4+
import { SerializerSignalImpl, type SerializerArg } from '../signal/signal';
85
import type { createSerializer$ } from '../signal/signal.public';
96
import { useComputedCommon } from './use-computed';
107

0 commit comments

Comments
 (0)