Skip to content

Commit 60be07b

Browse files
committed
feat: use custom serialization
1 parent 377de9a commit 60be07b

File tree

10 files changed

+47
-69
lines changed

10 files changed

+47
-69
lines changed

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import type { QRL } from '@qwik.dev/core';
1+
import { type QRL } from '@qwik.dev/core';
2+
import { _UNINITIALIZED } from '@qwik.dev/core/internal';
23
import type { Render, RenderToStringResult } from '@qwik.dev/core/server';
34
import { QACTION_KEY, QFN_KEY, QLOADER_KEY } from '../../runtime/src/constants';
45
import {
@@ -30,7 +31,6 @@ import {
3031
import { getQwikRouterServerData } from './response-page';
3132
import type { QwikSerializer, RequestEvent, RequestEventBase, RequestHandler } from './types';
3233
import { IsQData, QDATA_JSON } from './user-response';
33-
import { _UNINITIALIZED } from '@qwik.dev/core/internal';
3434

3535
export const resolveRequestHandlers = (
3636
serverPlugins: RouteModule[] | undefined,

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

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { SerializerSymbol } from '@qwik.dev/core';
2+
import { _UNINITIALIZED } from '@qwik.dev/core/internal';
13
import type { QwikRouterEnvData } from '../../runtime/src/types';
24
import {
35
getRequestLoaders,
@@ -30,6 +32,21 @@ export function getQwikRouterServerData(requestEv: RequestEvent) {
3032
reconstructedUrl.protocol = protocol;
3133
}
3234

35+
const loaders = getRequestLoaders(requestEv);
36+
37+
// shallow serialize loaders data
38+
(loaders as any)[SerializerSymbol] = (loaders: Record<string, unknown>) => {
39+
const result: Record<string, unknown> = {};
40+
for (const key in loaders) {
41+
const loader = loaders[key];
42+
if (typeof loader === 'object' && loader !== null) {
43+
(loader as any)[SerializerSymbol] = () => _UNINITIALIZED;
44+
}
45+
result[key] = _UNINITIALIZED;
46+
}
47+
return result;
48+
};
49+
3350
return {
3451
url: reconstructedUrl.href,
3552
requestHeaders,
@@ -45,7 +62,7 @@ export function getQwikRouterServerData(requestEv: RequestEvent) {
4562
loadedRoute: getRequestRoute(requestEv),
4663
response: {
4764
status: status(),
48-
loaders: getRequestLoaders(requestEv),
65+
loaders,
4966
action,
5067
formData,
5168
},

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

+1-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import {
2020
_getContextElement,
2121
_getQContainerElement,
2222
_waitUntilRendered,
23-
_weakSerialize,
2423
type _ElementVNode,
2524
} from '@qwik.dev/core/internal';
2625
import { clientNavigate } from './client-navigate';
@@ -146,7 +145,7 @@ export const QwikRouterProvider = component$<QwikRouterProps>((props) => {
146145
{ deep: false }
147146
);
148147
const navResolver: { r?: () => void } = {};
149-
const loaderState = useStore(_weakSerialize(env.response.loaders), { deep: false });
148+
const loaderState = useStore(env.response.loaders, { deep: false });
150149
const routeInternal = useSignal<RouteStateInternal>({
151150
type: 'initial',
152151
dest: url,

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@ import {
77
type ValueOrPromise,
88
untrack,
99
isBrowser,
10+
isDev,
11+
isServer,
1012
} from '@qwik.dev/core';
1113
import {
1214
_deserialize,
1315
_getContextElement,
1416
_getContextEvent,
1517
_serialize,
1618
_wrapStore,
19+
_useInvokeContext,
1720
_UNINITIALIZED,
1821
} from '@qwik.dev/core/internal';
1922

@@ -54,9 +57,6 @@ import type {
5457
} from './types';
5558
import { useAction, useLocation, useQwikRouterEnv } from './use-functions';
5659

57-
import { isDev, isServer } from '@qwik.dev/core';
58-
import { _useInvokeContext } from '@qwik.dev/core/internal';
59-
6060
import type { FormSubmitCompletedDetail } from './form-component';
6161
import { deepFreeze } from './utils';
6262
import { loadClientData } from './use-endpoint';

packages/qwik/src/core/api.md

-3
Original file line numberDiff line numberDiff line change
@@ -1773,9 +1773,6 @@ export function _walkJSX(ssr: SSRContainer, value: JSXOutput, options: {
17731773
parentComponentFrame: ISsrComponentFrame | null;
17741774
}): Promise<void>;
17751775

1776-
// @internal (undocumented)
1777-
export const _weakSerialize: <T extends object>(input: T) => Partial<T>;
1778-
17791776
// @public
17801777
export function withLocale<T>(locale: string, fn: () => T): T;
17811778

packages/qwik/src/core/internal.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ export { _wrapSignal, _wrapProp, _wrapStore } from './signal/signal-utils';
77
export { _restProps } from './shared/utils/prop';
88
export { _IMMUTABLE, _UNINITIALIZED } from './shared/utils/constants';
99
export { _CONST_PROPS, _VAR_PROPS } from './shared/utils/constants';
10-
export { _weakSerialize } from './shared/utils/serialize-utils';
1110
export { verifySerializable as _verifySerializable } from './shared/utils/serialize-utils';
1211
export {
1312
_getContextElement,
@@ -21,6 +20,7 @@ export { _fnSignal } from './shared/qrl/inlined-fn';
2120
export type {
2221
ContainerElement as _ContainerElement,
2322
VNode as _VNode,
23+
VNodeFlags as _VNodeFlags,
2424
VirtualVNode as _VirtualVNode,
2525
TextVNode as _TextVNode,
2626
QDocument as _QDocument,

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

+3-25
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ import { isElement, isNode } from './utils/element';
5656
import { EMPTY_ARRAY, EMPTY_OBJ } from './utils/flyweight';
5757
import { ELEMENT_ID } from './utils/markers';
5858
import { isPromise } from './utils/promises';
59-
import { SerializerSymbol, fastSkipSerialize, fastWeakSerialize } from './utils/serialize-utils';
59+
import { SerializerSymbol, fastSkipSerialize } from './utils/serialize-utils';
6060
import { type ValueOrPromise } from './utils/types';
6161

6262
const deserializedProxyMap = new WeakMap<object, unknown[]>();
@@ -400,16 +400,6 @@ const inflate = (
400400
effectData.data.$isConst$ = (data as any[])[1];
401401
break;
402402
}
403-
case TypeIds.WeakObject: {
404-
const objectKeys = data as string[];
405-
target = Object.fromEntries(
406-
objectKeys.map((v) =>
407-
// initialize values with null
408-
[v, _UNINITIALIZED]
409-
)
410-
);
411-
break;
412-
}
413403
default:
414404
throw qError(QError.serializeErrorNotImplemented, [typeId]);
415405
}
@@ -472,7 +462,6 @@ const allocate = (container: DeserializeContainer, typeId: number, value: unknow
472462
case TypeIds.Array:
473463
return wrapDeserializerProxy(container as any, value as any[]);
474464
case TypeIds.Object:
475-
case TypeIds.WeakObject:
476465
return {};
477466
case TypeIds.QRL:
478467
const qrl = container.$getObjectById$(value as number);
@@ -827,20 +816,15 @@ export const createSerializationContext = (
827816
vnode_isVNode(obj) ||
828817
(typeof FormData !== 'undefined' && obj instanceof FormData) ||
829818
// Ignore the no serialize objects
830-
fastSkipSerialize(obj as object) ||
831-
// only keys will be serialized
832-
fastWeakSerialize(obj)
819+
fastSkipSerialize(obj as object)
833820
) {
834821
// ignore
835822
} else if (obj instanceof Error) {
836823
discoveredValues.push(...Object.values(obj));
837824
} else if (isStore(obj)) {
838825
const target = getStoreTarget(obj)!;
839826
const effects = getStoreHandler(obj)!.$effects$;
840-
if (!fastWeakSerialize(target)) {
841-
discoveredValues.push(target);
842-
}
843-
discoveredValues.push(effects);
827+
discoveredValues.push(target, effects);
844828

845829
for (const prop in target) {
846830
const propValue = (target as any)[prop];
@@ -936,8 +920,6 @@ export const createSerializationContext = (
936920
}
937921
);
938922
promises.push(obj);
939-
} else if (obj instanceof SubscriptionData) {
940-
discoveredValues.push(obj.data);
941923
} else if (Array.isArray(obj)) {
942924
discoveredValues.push(...obj);
943925
} else if (isSerializerObj(obj)) {
@@ -1225,8 +1207,6 @@ function serialize(serializationContext: SerializationContext): void {
12251207
} else if (isObjectLiteral(value)) {
12261208
if (Array.isArray(value)) {
12271209
output(TypeIds.Array, value);
1228-
} else if (fastWeakSerialize(value)) {
1229-
output(TypeIds.WeakObject, Object.keys(value));
12301210
} else {
12311211
const out: any[] = [];
12321212
for (const key in value) {
@@ -1744,7 +1724,6 @@ export const enum TypeIds {
17441724
JSXNode,
17451725
PropsProxy,
17461726
SubscriptionData,
1747-
WeakObject,
17481727
}
17491728
export const _typeIdNames = [
17501729
'RootRef',
@@ -1779,7 +1758,6 @@ export const _typeIdNames = [
17791758
'JSXNode',
17801759
'PropsProxy',
17811760
'SubscriptionData',
1782-
'WeakObject',
17831761
];
17841762

17851763
export const enum Constants {

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

+12-11
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { $, _weakSerialize, component$, noSerialize } from '@qwik.dev/core';
1+
import { $, component$, noSerialize } from '@qwik.dev/core';
22
import { describe, expect, it, vi } from 'vitest';
33
import { _fnSignal, _wrapProp } from '../internal';
44
import { SubscriptionData, type SignalImpl } from '../signal/signal';
@@ -63,15 +63,16 @@ describe('shared-serialization', () => {
6363
6 Constant EMPTY_OBJ
6464
7 Constant NEEDS_COMPUTATION
6565
8 Constant STORE_ALL_PROPS
66-
9 Constant Slot
67-
10 Constant Fragment
68-
11 Constant NaN
69-
12 Constant Infinity
70-
13 Constant -Infinity
71-
14 Constant MAX_SAFE_INTEGER
72-
15 Constant MAX_SAFE_INTEGER-1
73-
16 Constant MIN_SAFE_INTEGER
74-
(76 chars)"
66+
9 Constant _UNINITIALIZED
67+
10 Constant Slot
68+
11 Constant Fragment
69+
12 Constant NaN
70+
13 Constant Infinity
71+
14 Constant -Infinity
72+
15 Constant MAX_SAFE_INTEGER
73+
16 Constant MAX_SAFE_INTEGER-1
74+
17 Constant MIN_SAFE_INTEGER
75+
(81 chars)"
7576
`);
7677
});
7778
it(title(TypeIds.Number), async () => {
@@ -484,7 +485,7 @@ describe('shared-serialization', () => {
484485
expect(await dump(new SubscriptionData({ $isConst$: true, $scopedStyleIdPrefix$: null })))
485486
.toMatchInlineSnapshot(`
486487
"
487-
0 EffectData [
488+
0 SubscriptionData [
488489
Constant null
489490
Constant true
490491
]

packages/qwik/src/core/shared/utils/serialize-utils.ts

-19
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ const _verifySerializable = <T>(
9090
return value;
9191
};
9292
const noSerializeSet = /*#__PURE__*/ new WeakSet<object>();
93-
const weakSerializeSet = /*#__PURE__*/ new WeakSet<object>();
9493

9594
export const shouldSerialize = (obj: unknown): boolean => {
9695
if (isObject(obj) || isFunction(obj)) {
@@ -103,10 +102,6 @@ export const fastSkipSerialize = (obj: object): boolean => {
103102
return typeof obj === 'object' && obj && (NoSerializeSymbol in obj || noSerializeSet.has(obj));
104103
};
105104

106-
export const fastWeakSerialize = (obj: object): boolean => {
107-
return weakSerializeSet.has(obj);
108-
};
109-
110105
/**
111106
* Returned type of the `noSerialize()` function. It will be TYPE or undefined.
112107
*
@@ -142,20 +137,6 @@ export const noSerialize = <T extends object | undefined>(input: T): NoSerialize
142137
return input as any;
143138
};
144139

145-
/** @internal */
146-
export const _weakSerialize = <T extends object>(input: T): Partial<T> => {
147-
weakSerializeSet.add(input);
148-
if (isObject(input)) {
149-
for (const key in input) {
150-
const value = input[key];
151-
if (isObject(value)) {
152-
noSerializeSet.add(value);
153-
}
154-
}
155-
}
156-
return input;
157-
};
158-
159140
/**
160141
* If an object has this property, it will not be serialized. Use this on prototypes to avoid having
161142
* to call `noSerialize()` on every object.

starters/apps/qwikrouter-test/src/routes/loaders-serialization/index.tsx

+7-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { component$, useSignal } from "@qwik.dev/core";
22
import { routeLoader$ } from "@qwik.dev/router";
33

44
export const useTestLoader = routeLoader$(async () => {
5-
return { test: "some test value", notUsed: "should not serialize this" };
5+
return { test: "some test value", abcd: "should not serialize this" };
66
});
77

88
export default component$(() => {
@@ -21,5 +21,10 @@ export default component$(() => {
2121

2222
export const Child = component$(() => {
2323
const testSignal = useTestLoader();
24-
return <div id="prop">{testSignal.value.test}</div>;
24+
return (
25+
<>
26+
<div id="prop1">{testSignal.value.test}</div>
27+
<div id="prop2">{testSignal.value.abcd}</div>
28+
</>
29+
);
2530
});

0 commit comments

Comments
 (0)