Skip to content

Commit f74286c

Browse files
authored
Merge pull request #8121 from QwikDev/v2-fix-preload-qrls
fix(serdes): preload qrls work again
2 parents 1912f77 + fad941d commit f74286c

File tree

7 files changed

+48
-43
lines changed

7 files changed

+48
-43
lines changed

packages/docs/src/routes/playground/parser/state/index.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ export default component$(() => {
4747
getSyncFn: (id: number) => () => {},
4848
$storeProxyMap$: new WeakMap(),
4949
$forwardRefs$: null,
50-
$initialQRLsIndexes$: null,
5150
$scheduler$: null,
5251
};
5352

@@ -78,7 +77,6 @@ export default component$(() => {
7877
getSyncFn: (id: number) => () => {},
7978
$storeProxyMap$: new WeakMap(),
8079
$forwardRefs$: null,
81-
$initialQRLsIndexes$: null,
8280
$scheduler$: null,
8381
};
8482
_preprocessState(stateData, container as any);

packages/qwik/src/core/client/dom-container.ts

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ export class DomContainer extends _SharedContainer implements IClientContainer {
105105
public $qFuncs$: Array<(...args: unknown[]) => unknown>;
106106
public $instanceHash$: string;
107107
public $forwardRefs$: Array<number> | null = null;
108-
public $initialQRLs$: Array<string> | null = null;
109108
public vNodeLocate: (id: string | Element) => VNode = (id) => vnode_locate(this.rootVNode, id);
110109

111110
private $stateData$: unknown[];
@@ -156,7 +155,6 @@ export class DomContainer extends _SharedContainer implements IClientContainer {
156155
this.$rawStateData$ = JSON.parse(lastState.textContent!);
157156
preprocessState(this.$rawStateData$, this);
158157
this.$stateData$ = wrapDeserializerProxy(this, this.$rawStateData$) as unknown[];
159-
this.$scheduleInitialQRLs$();
160158
}
161159
}
162160

@@ -327,33 +325,4 @@ export class DomContainer extends _SharedContainer implements IClientContainer {
327325
}
328326
this.$serverData$ = { containerAttributes };
329327
}
330-
331-
/**
332-
* Schedule the initial QRLs to be resolved.
333-
*
334-
* Schedules the QRLs that are defined in the state data as `PreloadQRL`.
335-
*
336-
* This is done because when computed and custom serializer QRLs are called they need QRL to work.
337-
* If the QRL is not resolved at this point, it will be resolved by throwing a promise and
338-
* rerunning the whole wrapping function again. We want to avoid that, because it means that the
339-
* function can execute twice.
340-
*
341-
* ```ts
342-
* useVisibleTask$(() => {
343-
* runHeavyLogic(); // This will be called again if the QRL of `computedOrCustomSerializer` is not resolved.
344-
* console.log(computedOrCustomSerializer.value); // Throw a promise if QRL not resolved and execute visible task again.
345-
* });
346-
* ```
347-
*/
348-
private $scheduleInitialQRLs$(): void {
349-
if (this.$initialQRLs$) {
350-
for (const qrl of this.$initialQRLs$) {
351-
const match = /#(.*)_([a-zA-Z0-9]+)(\[|$)/.exec(qrl);
352-
if (match) {
353-
preload(match[2], 0.3);
354-
}
355-
}
356-
this.$initialQRLs$ = null;
357-
}
358-
}
359328
}

packages/qwik/src/core/qwik.core.api.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,6 @@ class DomContainer extends _SharedContainer implements ClientContainer {
221221
// (undocumented)
222222
$getObjectById$: (id: number | string) => unknown;
223223
// (undocumented)
224-
$initialQRLs$: Array<string> | null;
225-
// (undocumented)
226224
$instanceHash$: string;
227225
// (undocumented)
228226
$journal$: VNodeJournal;

packages/qwik/src/core/shared/serdes/preprocess-state.ts

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import { isServer } from '@qwik.dev/core/build';
2+
// @ts-expect-error we don't have types for the preloader
3+
import { p as preload } from '@qwik.dev/core/preloader';
14
import type { DeserializeContainer } from '../types';
25
import { TypeIds } from './constants';
36

@@ -95,14 +98,49 @@ export function preprocessState(data: unknown[], container: DeserializeContainer
9598
data[index + 1] = object;
9699
};
97100

101+
const toPreload: number[] | undefined = isServer ? undefined : [];
98102
for (let i = 0; i < data.length; i += 2) {
99103
if (isRootDeepRef(data[i] as TypeIds, data[i + 1])) {
100104
processRootRef(i);
101105
} else if (isForwardRefsMap(data[i] as TypeIds)) {
102106
container.$forwardRefs$ = data[i + 1] as number[];
103-
} else if (isPreloadQrlType(data[i] as TypeIds)) {
107+
} else if (!isServer && isPreloadQrlType(data[i] as TypeIds)) {
108+
// preload QRLs are always serialized as strings with chunk and symbol ids
104109
const qrl = data[i + 1] as string;
105-
(container.$initialQRLs$ ||= []).push(qrl);
110+
const chunkIdx = Number(qrl.split(' ')[0]);
111+
toPreload!.push(chunkIdx);
112+
}
113+
}
114+
115+
/**
116+
* Preloads QRLs that are defined in the state data as `PreloadQRL`.
117+
*
118+
* This is done because when computed and custom serializer QRLs are called they need QRL to work.
119+
* If the QRL is not resolved at this point, it will be resolved by throwing a promise and
120+
* rerunning the whole wrapping function again. We want to avoid that, because it means that the
121+
* function can execute twice.
122+
*
123+
* ```ts
124+
* useVisibleTask$(() => {
125+
* runHeavyLogic(); // This will be called again if the QRL of `computedOrCustomSerializer` is not resolved.
126+
* console.log(computedOrCustomSerializer.value); // Throw a promise if QRL not resolved and execute visible task again.
127+
* });
128+
* ```
129+
*/
130+
if (!isServer) {
131+
for (const idx of toPreload!) {
132+
// we preload the chunk instead of the symbol so it also works in dev
133+
const chunkType = data[idx * 2] as TypeIds;
134+
let chunk: string;
135+
if (chunkType === TypeIds.Plain) {
136+
chunk = data[idx * 2 + 1] as string;
137+
} else if (chunkType === TypeIds.RootRef) {
138+
const refIdx = data[idx * 2 + 1] as number;
139+
chunk = data[refIdx * 2 + 1] as string;
140+
} else {
141+
continue;
142+
}
143+
preload(chunk, 0.3);
106144
}
107145
}
108146
}

packages/qwik/src/core/shared/serdes/serdes.public.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ export function _createDeserializeContainer(
8282
$storeProxyMap$: new WeakMap(),
8383
element: null,
8484
$forwardRefs$: null,
85-
$initialQRLs$: null,
8685
$scheduler$: null,
8786
};
8887
preprocessState(stateData, container);

packages/qwik/src/core/shared/serdes/serialize.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import { Fragment } from '../jsx/jsx-runtime';
3030
import { isPropsProxy } from '../jsx/props-proxy';
3131
import { Slot } from '../jsx/slot.public';
3232
import type { QRLInternal } from '../qrl/qrl-class';
33-
import { isQrl } from '../qrl/qrl-utils';
33+
import { isQrl, isSyncQrl } from '../qrl/qrl-utils';
3434
import { _OWNER, _UNINITIALIZED } from '../utils/constants';
3535
import { EMPTY_ARRAY, EMPTY_OBJ } from '../utils/flyweight';
3636
import { ELEMENT_ID, ELEMENT_PROPS, QBackRefs } from '../utils/markers';
@@ -121,8 +121,10 @@ export async function serialize(serializationContext: SerializationContext): Pro
121121
};
122122

123123
const addPreloadQrl = (qrl: QRLInternal) => {
124-
preloadQrls.add(qrl);
125-
serializationContext.$addRoot$(qrl);
124+
if (!isSyncQrl(qrl)) {
125+
preloadQrls.add(qrl);
126+
serializationContext.$addRoot$(qrl);
127+
}
126128
};
127129

128130
const getSeenRefOrOutput = (
@@ -241,6 +243,7 @@ export async function serialize(serializationContext: SerializationContext): Pro
241243
if (getSeenRefOrOutput(value, index)) {
242244
const [chunk, symbol, captureIds] = qrlToString(serializationContext, value, true);
243245
let data: string | number;
246+
let type: TypeIds;
244247
if (chunk !== '') {
245248
// not a sync QRL, replace all parts with string references
246249
data = `${$addRoot$(chunk)} ${$addRoot$(symbol)}${captureIds ? ' ' + captureIds.join(' ') : ''}`;
@@ -254,11 +257,12 @@ export async function serialize(serializationContext: SerializationContext): Pro
254257
} else {
255258
qrlMap.set(data, value);
256259
}
260+
type = preloadQrls.has(value) ? TypeIds.PreloadQRL : TypeIds.QRL;
257261
} else {
258262
data = Number(symbol);
263+
type = TypeIds.QRL;
259264
}
260265

261-
const type = preloadQrls.has(value) ? TypeIds.PreloadQRL : TypeIds.QRL;
262266
output(type, data);
263267
}
264268
} else if (isQwikComponent(value)) {

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ export interface DeserializeContainer {
1111
$state$?: unknown[];
1212
$storeProxyMap$: ObjToProxyMap;
1313
$forwardRefs$: Array<number> | null;
14-
$initialQRLs$: Array<string> | null;
1514
readonly $scheduler$: Scheduler | null;
1615
}
1716

0 commit comments

Comments
 (0)