Skip to content

Commit 1d9e411

Browse files
Varixowmertens
andcommitted
WIP: serialization weak ref
Co-authored-by: Wout Mertens <[email protected]>
1 parent eef4622 commit 1d9e411

File tree

17 files changed

+2031
-2472
lines changed

17 files changed

+2031
-2472
lines changed

eslint.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export default tseslint.config(
104104
},
105105
},
106106
{
107-
files: ['packages/docs/**/*.{ts,tsx}'],
107+
files: ['packages/docs/demo/**/*.{ts,tsx}'],
108108
rules: {
109109
'no-console': 'off',
110110
},

packages/docs/src/routes/api/qwik/api.json

Lines changed: 3 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -324,20 +324,6 @@
324324
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-computed.ts",
325325
"mdFile": "core.computedfn.md"
326326
},
327-
{
328-
"name": "ComputedReturnType",
329-
"id": "computedreturntype",
330-
"hierarchy": [
331-
{
332-
"name": "ComputedReturnType",
333-
"id": "computedreturntype"
334-
}
335-
],
336-
"kind": "TypeAlias",
337-
"content": "```typescript\nexport type ComputedReturnType<T> = T extends Promise<infer T> ? ReadonlySignal<T> : ReadonlySignal<T>;\n```\n**References:** [ReadonlySignal](#readonlysignal)",
338-
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-computed.ts",
339-
"mdFile": "core.computedreturntype.md"
340-
},
341327
{
342328
"name": "ComputedSignal",
343329
"id": "computedsignal",
@@ -686,20 +672,6 @@
686672
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/shared/jsx/jsx-runtime.ts",
687673
"mdFile": "core.h.md"
688674
},
689-
{
690-
"name": "HTMLElementAttrs",
691-
"id": "htmlelementattrs",
692-
"hierarchy": [
693-
{
694-
"name": "HTMLElementAttrs",
695-
"id": "htmlelementattrs"
696-
}
697-
],
698-
"kind": "Interface",
699-
"content": "```typescript\nexport interface HTMLElementAttrs extends HTMLAttributesBase, FilterBase<HTMLElement> \n```\n**Extends:** HTMLAttributesBase, FilterBase&lt;HTMLElement&gt;",
700-
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/shared/jsx/types/jsx-generated.ts",
701-
"mdFile": "core.htmlelementattrs.md"
702-
},
703675
{
704676
"name": "implicit$FirstArg",
705677
"id": "implicit_firstarg",
@@ -1332,7 +1304,7 @@
13321304
}
13331305
],
13341306
"kind": "TypeAlias",
1335-
"content": "The DOM props without plain handlers, for use inside functions\n\n\n```typescript\nexport type QwikHTMLElements = {\n [tag in keyof HTMLElementTagNameMap]: Augmented<HTMLElementTagNameMap[tag], SpecialAttrs[tag]> & HTMLElementAttrs & QwikAttributes<HTMLElementTagNameMap[tag]>;\n};\n```\n**References:** [HTMLElementAttrs](#htmlelementattrs)<!-- -->, [QwikAttributes](#qwikattributes)",
1307+
"content": "The DOM props without plain handlers, for use inside functions\n\n\n```typescript\nexport type QwikHTMLElements = {\n [tag in keyof HTMLElementTagNameMap]: Augmented<HTMLElementTagNameMap[tag], SpecialAttrs[tag]> & HTMLElementAttrs & QwikAttributes<HTMLElementTagNameMap[tag]>;\n};\n```\n**References:** [QwikAttributes](#qwikattributes)",
13361308
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/shared/jsx/types/jsx-generated.ts",
13371309
"mdFile": "core.qwikhtmlelements.md"
13381310
},
@@ -1472,7 +1444,7 @@
14721444
}
14731445
],
14741446
"kind": "TypeAlias",
1475-
"content": "The SVG props without plain handlers, for use inside functions\n\n\n```typescript\nexport type QwikSVGElements = {\n [K in keyof Omit<SVGElementTagNameMap, keyof HTMLElementTagNameMap>]: SVGProps<SVGElementTagNameMap[K]>;\n};\n```\n**References:** [SVGProps](#svgprops)",
1447+
"content": "The SVG props without plain handlers, for use inside functions\n\n\n```typescript\nexport type QwikSVGElements = {\n [K in keyof Omit<SVGElementTagNameMap, keyof HTMLElementTagNameMap>]: SVGProps<SVGElementTagNameMap[K]>;\n};\n```",
14761448
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/shared/jsx/types/jsx-generated.ts",
14771449
"mdFile": "core.qwiksvgelements.md"
14781450
},
@@ -2022,20 +1994,6 @@
20221994
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/shared/jsx/types/jsx-generated.ts",
20231995
"mdFile": "core.svgattributes.md"
20241996
},
2025-
{
2026-
"name": "SVGProps",
2027-
"id": "svgprops",
2028-
"hierarchy": [
2029-
{
2030-
"name": "SVGProps",
2031-
"id": "svgprops"
2032-
}
2033-
],
2034-
"kind": "Interface",
2035-
"content": "```typescript\nexport interface SVGProps<T extends Element> extends SVGAttributes, QwikAttributes<T> \n```\n**Extends:** [SVGAttributes](#svgattributes)<!-- -->, [QwikAttributes](#qwikattributes)<!-- -->&lt;T&gt;",
2036-
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/shared/jsx/types/jsx-generated.ts",
2037-
"mdFile": "core.svgprops.md"
2038-
},
20391997
{
20401998
"name": "sync$",
20411999
"id": "sync_",
@@ -2144,7 +2102,7 @@
21442102
}
21452103
],
21462104
"kind": "Function",
2147-
"content": "Creates a computed signal which is calculated from the given function. A computed signal is a signal which is calculated from other signals. When the signals change, the computed signal is recalculated, and if the result changed, all tasks which are tracking the signal will be re-run and all components that read the signal will be re-rendered.\n\nThe function must be synchronous and must not have any side effects.\n\n\n```typescript\nuseComputed$: <T>(qrl: ComputedFn<T>) => ComputedReturnType<T>\n```\n\n\n<table><thead><tr><th>\n\nParameter\n\n\n</th><th>\n\nType\n\n\n</th><th>\n\nDescription\n\n\n</th></tr></thead>\n<tbody><tr><td>\n\nqrl\n\n\n</td><td>\n\n[ComputedFn](#computedfn)<!-- -->&lt;T&gt;\n\n\n</td><td>\n\n\n</td></tr>\n</tbody></table>\n**Returns:**\n\n[ComputedReturnType](#computedreturntype)<!-- -->&lt;T&gt;",
2105+
"content": "Creates a computed signal which is calculated from the given function. A computed signal is a signal which is calculated from other signals. When the signals change, the computed signal is recalculated, and if the result changed, all tasks which are tracking the signal will be re-run and all components that read the signal will be re-rendered.\n\nThe function must be synchronous and must not have any side effects.\n\n\n```typescript\nuseComputed$: <T>(qrl: ComputedFn<T>) => ComputedReturnType<T>\n```\n\n\n<table><thead><tr><th>\n\nParameter\n\n\n</th><th>\n\nType\n\n\n</th><th>\n\nDescription\n\n\n</th></tr></thead>\n<tbody><tr><td>\n\nqrl\n\n\n</td><td>\n\n[ComputedFn](#computedfn)<!-- -->&lt;T&gt;\n\n\n</td><td>\n\n\n</td></tr>\n</tbody></table>\n**Returns:**\n\nComputedReturnType&lt;T&gt;",
21482106
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-computed.ts",
21492107
"mdFile": "core.usecomputed_.md"
21502108
},

packages/docs/src/routes/api/qwik/index.mdx

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -353,17 +353,6 @@ export type ComputedFn<T> = () => T;
353353

354354
[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-computed.ts)
355355

356-
## ComputedReturnType
357-
358-
```typescript
359-
export type ComputedReturnType<T> =
360-
T extends Promise<infer T> ? ReadonlySignal<T> : ReadonlySignal<T>;
361-
```
362-
363-
**References:** [ReadonlySignal](#readonlysignal)
364-
365-
[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-computed.ts)
366-
367356
## ComputedSignal
368357

369358
A computed signal is a signal which is calculated from other signals. When the signals change, the computed signal is recalculated, and if the result changed, all tasks which are tracking the signal will be re-run and all components that read the signal will be re-rendered.
@@ -1344,16 +1333,6 @@ any[]
13441333
13451334
[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/shared/jsx/jsx-runtime.ts)
13461335
1347-
## HTMLElementAttrs
1348-
1349-
```typescript
1350-
export interface HTMLElementAttrs extends HTMLAttributesBase, FilterBase<HTMLElement>
1351-
```
1352-
1353-
**Extends:** HTMLAttributesBase, FilterBase&lt;HTMLElement&gt;
1354-
1355-
[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/shared/jsx/types/jsx-generated.ts)
1356-
13571336
## implicit$FirstArg
13581337
13591338
Create a `____$(...)` convenience method from `___(...)`.
@@ -2597,7 +2576,7 @@ export type QwikHTMLElements = {
25972576
};
25982577
```
25992578
2600-
**References:** [HTMLElementAttrs](#htmlelementattrs), [QwikAttributes](#qwikattributes)
2579+
**References:** [QwikAttributes](#qwikattributes)
26012580
26022581
[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/shared/jsx/types/jsx-generated.ts)
26032582
@@ -2795,8 +2774,6 @@ export type QwikSVGElements = {
27952774
};
27962775
```
27972776
2798-
**References:** [SVGProps](#svgprops)
2799-
28002777
[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/shared/jsx/types/jsx-generated.ts)
28012778
28022779
## QwikSymbolEvent
@@ -8132,16 +8109,6 @@ _(Optional)_
81328109
81338110
[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/shared/jsx/types/jsx-generated.ts)
81348111
8135-
## SVGProps
8136-
8137-
```typescript
8138-
export interface SVGProps<T extends Element> extends SVGAttributes, QwikAttributes<T>
8139-
```
8140-
8141-
**Extends:** [SVGAttributes](#svgattributes), [QwikAttributes](#qwikattributes)&lt;T&gt;
8142-
8143-
[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/shared/jsx/types/jsx-generated.ts)
8144-
81458112
## sync$
81468113
81478114
Extract function into a synchronously loadable QRL.
@@ -8481,7 +8448,7 @@ qrl
84818448
</tbody></table>
84828449
**Returns:**
84838450
8484-
[ComputedReturnType](#computedreturntype)&lt;T&gt;
8451+
ComputedReturnType&lt;T&gt;
84858452
84868453
[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-computed.ts)
84878454

packages/qwik-router/src/middleware/request-handler/resolve-request-handlers.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { type QRL } from '@qwik.dev/core';
2-
import { SerializerSymbol, _UNINITIALIZED } from '@qwik.dev/core/internal';
32
import type { Render, RenderToStringResult } from '@qwik.dev/core/server';
43
import { QACTION_KEY, QFN_KEY, QLOADER_KEY } from '../../runtime/src/constants';
54
import {
@@ -598,9 +597,6 @@ export async function renderQData(requestEv: RequestEvent) {
598597
for (const loaderId in allLoaders) {
599598
const loader = allLoaders[loaderId];
600599
if (loader !== null) {
601-
if (typeof loader === 'object' && SerializerSymbol in loader) {
602-
(loader as any)[SerializerSymbol] = undefined;
603-
}
604600
loaders[loaderId] = loader;
605601
}
606602
}

packages/qwik-router/src/middleware/request-handler/response-page.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { SerializerSymbol } from '@qwik.dev/core';
2-
import { _UNINITIALIZED } from '@qwik.dev/core/internal';
31
import type { QwikRouterEnvData } from '../../runtime/src/types';
42
import {
53
getRequestLoaders,
@@ -35,14 +33,6 @@ export function getQwikRouterServerData(requestEv: RequestEvent) {
3533

3634
const loaders = getRequestLoaders(requestEv);
3735

38-
// shallow serialize loaders data
39-
for (const key in loaders) {
40-
const loader = loaders[key];
41-
if (typeof loader === 'object' && loader !== null) {
42-
(loader as any)[SerializerSymbol] = () => _UNINITIALIZED;
43-
}
44-
}
45-
4636
return {
4737
url: reconstructedUrl.href,
4838
requestHeaders,

packages/qwik-router/src/runtime/src/contexts.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import type {
1010
RouteStateInternal,
1111
} from './types';
1212

13-
export const RouteStateContext = /*#__PURE__*/ createContextId<Record<string, any>>('qc-s');
13+
export const RouteStateContext =
14+
/*#__PURE__*/ createContextId<Record<string, Signal<unknown>>>('qc-s');
1415

1516
export const ContentContext = /*#__PURE__*/ createContextId<ContentState>('qc-c');
1617
export const ContentInternalContext =

packages/qwik-router/src/runtime/src/qwik-router-component.tsx

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,13 @@ import {
1919
import {
2020
_getContextElement,
2121
_getQContainerElement,
22+
_UNINITIALIZED,
2223
_waitUntilRendered,
24+
SerializerSymbol,
25+
_serializationWeakRef,
2326
type _ElementVNode,
27+
createSignal,
28+
type Signal,
2429
} from '@qwik.dev/core/internal';
2530
import { clientNavigate } from './client-navigate';
2631
import { CLIENT_DATA_CACHE, Q_ROUTE } from './constants';
@@ -145,7 +150,21 @@ export const QwikRouterProvider = component$<QwikRouterProps>((props) => {
145150
{ deep: false }
146151
);
147152
const navResolver: { r?: () => void } = {};
148-
const loaderState = useStore(env.response.loaders, { deep: false });
153+
const loaderState = Object.fromEntries(
154+
Object.entries(env.response.loaders).map(([k, v]) => {
155+
const value = createSignal(v);
156+
return [k, value];
157+
})
158+
);
159+
160+
(loaderState as any)[SerializerSymbol] = async (o: Record<string, unknown>) => {
161+
const resultPs = Object.entries(o).map(async ([k, val]) => {
162+
const v = await val;
163+
return [k, _serializationWeakRef(v)];
164+
});
165+
return Object.fromEntries(await Promise.all(resultPs));
166+
};
167+
149168
const routeInternal = useSignal<RouteStateInternal>({
150169
type: 'initial',
151170
dest: url,
@@ -278,7 +297,7 @@ export const QwikRouterProvider = component$<QwikRouterProps>((props) => {
278297
let scroller = document.getElementById(QWIK_ROUTER_SCROLLER);
279298
if (!scroller) {
280299
scroller = document.getElementById(QWIK_CITY_SCROLLER);
281-
if (scroller) {
300+
if (scroller && isDev) {
282301
console.warn(
283302
`Please update your scroller ID to "${QWIK_ROUTER_SCROLLER}" as "${QWIK_CITY_SCROLLER}" is deprecated and will be removed in V3`
284303
);
@@ -460,12 +479,17 @@ export const QwikRouterProvider = component$<QwikRouterProps>((props) => {
460479
}
461480

462481
const loaders = clientPageData?.loaders;
463-
const win = window as ClientSPAWindow;
464482
if (loaders) {
465-
Object.assign(loaderState, loaders);
483+
for (const [key, value] of Object.entries(loaders)) {
484+
const signal = loaderState[key] as Signal<unknown> | typeof _UNINITIALIZED;
485+
if (signal && signal !== _UNINITIALIZED) {
486+
signal.value = value;
487+
}
488+
}
466489
}
467490
CLIENT_DATA_CACHE.clear();
468491

492+
const win = window as ClientSPAWindow;
469493
if (!win._qRouterSPA) {
470494
// only add event listener once
471495
win._qRouterSPA = true;
@@ -698,7 +722,7 @@ export const QwikRouterMockProvider = component$<QwikRouterMockProps>((props) =>
698722
{ deep: false }
699723
);
700724

701-
const loaderState = useSignal({});
725+
const loaderState = {};
702726
const routeInternal = useSignal<RouteStateInternal>({ type: 'initial', dest: url });
703727

704728
const goto: RouteNavigate =

packages/qwik-router/src/runtime/src/server-functions.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import {
1515
_getContextElement,
1616
_getContextEvent,
1717
_serialize,
18-
_wrapStore,
1918
_useInvokeContext,
2019
_UNINITIALIZED,
2120
} from '@qwik.dev/core/internal';
@@ -209,15 +208,17 @@ export const routeLoaderQrl = ((
209208
If your are managing reusable logic or a library it is essential that this function is re-exported from within 'layout.tsx' or 'index.tsx file of the existing route otherwise it will not run or throw exception.
210209
For more information check: https://qwik.dev/docs/re-exporting-loaders/`);
211210
}
212-
const data = untrack(() => state[id]);
213-
if (data === _UNINITIALIZED && isBrowser) {
211+
const loaderData = untrack(() => state[id].value);
212+
if (loaderData === _UNINITIALIZED && isBrowser) {
213+
// Request the loader data from the server and throw the Promise
214+
// so the client can load it synchronously.
214215
throw loadClientData(location.url, iCtx.$hostElement$, {
215216
loaderIds: [id],
216-
}).then((data) => {
217-
state[id] = data?.loaders[id];
217+
}).then((clientData) => {
218+
state[id].value = clientData?.loaders[id];
218219
});
219220
}
220-
return _wrapStore(state, id);
221+
return state[id];
221222
}
222223
loader.__brand = 'server_loader' as const;
223224
loader.__qrl = loaderQrl;

packages/qwik-router/src/runtime/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ export type GetValidatorType<VALIDATOR extends TypedDataValidator> =
401401
export interface CommonLoaderActionOptions {
402402
readonly id?: string;
403403
readonly validation?: DataValidator[];
404+
readonly persist?: boolean;
404405
}
405406

406407
/** @public */

0 commit comments

Comments
 (0)