Skip to content

Commit e2eb140

Browse files
authored
Merge pull request #7636 from QwikDev/v2-serialize-vnode-data-change
fix: serialize less vnode data
2 parents c647c56 + c1baa6c commit e2eb140

17 files changed

+164
-203
lines changed

.changeset/honest-pears-sniff.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@qwik.dev/core': patch
3+
---
4+
5+
fix: serialize less vnode data
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { component$ } from '@qwik.dev/core';
2+
import { EditIcon } from '../svgs/edit-icon';
3+
import { AlertIcon } from '../svgs/alert-icon';
4+
import { ChatIcon } from '../svgs/chat-icon';
5+
import { GithubLogo } from '../svgs/github-logo';
6+
import { TwitterLogo } from '../svgs/twitter-logo';
7+
8+
type OnThisPageMoreProps = {
9+
theme: 'light' | 'dark' | 'auto';
10+
editUrl: string;
11+
};
12+
13+
export const OnThisPageMore = component$<OnThisPageMoreProps>(({ theme, editUrl }) => {
14+
const OnThisPageMore = [
15+
{
16+
href: editUrl,
17+
text: 'Edit this Page',
18+
icon: EditIcon,
19+
},
20+
{
21+
href: 'https://github.com/QwikDev/qwik/issues/new/choose',
22+
text: 'Create an issue',
23+
icon: AlertIcon,
24+
},
25+
{
26+
href: 'https://qwik.dev/chat',
27+
text: 'Join our community',
28+
icon: ChatIcon,
29+
},
30+
{
31+
href: 'https://github.com/QwikDev/qwik',
32+
text: 'GitHub',
33+
icon: GithubLogo,
34+
},
35+
{
36+
href: 'https://twitter.com/QwikDev',
37+
text: '@QwikDev',
38+
icon: TwitterLogo,
39+
},
40+
];
41+
return (
42+
<>
43+
<h6>More</h6>
44+
<ul class="px-2 font-medium text-[var(--interactive-text-color)]">
45+
{OnThisPageMore.map((el, index) => {
46+
return (
47+
<li
48+
class={`${
49+
theme === 'light'
50+
? 'hover:bg-[var(--qwik-light-blue)]'
51+
: 'hover:bg-[var(--on-this-page-hover-bg-color)]'
52+
} rounded-lg`}
53+
key={`more-items-on-this-page-${index}`}
54+
>
55+
<a class="more-item" href={el.href} rel="noopener" target="_blank">
56+
{el.icon && <el.icon width={20} height={20} />}
57+
<span>{el.text}</span>
58+
</a>
59+
</li>
60+
);
61+
})}
62+
</ul>
63+
</>
64+
);
65+
});

packages/docs/src/components/on-this-page/on-this-page.tsx

Lines changed: 2 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
import { $, component$, useContext, useOnDocument, useSignal, useStyles$ } from '@qwik.dev/core';
22
import { useContent, useLocation } from '@qwik.dev/router';
33
import { GlobalStore } from '../../context';
4-
import { AlertIcon } from '../svgs/alert-icon';
5-
import { ChatIcon } from '../svgs/chat-icon';
6-
import { EditIcon } from '../svgs/edit-icon';
7-
import { GithubLogo } from '../svgs/github-logo';
8-
import { TwitterLogo } from '../svgs/twitter-logo';
94
import styles from './on-this-page.css?inline';
5+
import { OnThisPageMore } from './on-this-page-more';
106

117
const QWIK_GROUP = [
128
'components',
@@ -117,39 +113,9 @@ export const OnThisPage = component$(() => {
117113
const contentHeadings = headings?.filter((h) => h.level <= 3) || [];
118114

119115
const { url } = useLocation();
120-
121116
const githubEditRoute = makeEditPageUrl(url.pathname);
122-
123117
const editUrl = `https://github.com/QwikDev/qwik/edit/main/packages/docs/src/routes/${githubEditRoute}/index.mdx`;
124118

125-
const OnThisPageMore = [
126-
{
127-
href: editUrl,
128-
text: 'Edit this Page',
129-
icon: EditIcon,
130-
},
131-
{
132-
href: 'https://github.com/QwikDev/qwik/issues/new/choose',
133-
text: 'Create an issue',
134-
icon: AlertIcon,
135-
},
136-
{
137-
href: 'https://qwik.dev/chat',
138-
text: 'Join our community',
139-
icon: ChatIcon,
140-
},
141-
{
142-
href: 'https://github.com/QwikDev/qwik',
143-
text: 'GitHub',
144-
icon: GithubLogo,
145-
},
146-
{
147-
href: 'https://twitter.com/QwikDev',
148-
text: '@QwikDev',
149-
icon: TwitterLogo,
150-
},
151-
];
152-
153119
const useActiveItem = (itemIds: string[]) => {
154120
const activeId = useSignal<string | null>(null);
155121
useOnDocument(
@@ -214,29 +180,9 @@ export const OnThisPage = component$(() => {
214180
</li>
215181
))}
216182
</ul>
183+
<OnThisPageMore theme={theme.theme} editUrl={editUrl} />
217184
</>
218185
) : null}
219-
220-
<h6>More</h6>
221-
<ul class="px-2 font-medium text-[var(--interactive-text-color)]">
222-
{OnThisPageMore.map((el, index) => {
223-
return (
224-
<li
225-
class={`${
226-
theme.theme === 'light'
227-
? 'hover:bg-[var(--qwik-light-blue)]'
228-
: 'hover:bg-[var(--on-this-page-hover-bg-color)]'
229-
} rounded-lg`}
230-
key={`more-items-on-this-page-${index}`}
231-
>
232-
<a class="more-item" href={el.href} rel="noopener" target="_blank">
233-
<el.icon width={20} height={20} />
234-
<span>{el.text}</span>
235-
</a>
236-
</li>
237-
);
238-
})}
239-
</ul>
240186
</aside>
241187
);
242188
});

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -892,14 +892,13 @@ export abstract class _SharedContainer implements Container {
892892
// (undocumented)
893893
serializationCtxFactory(NodeConstructor: {
894894
new (...rest: any[]): {
895-
nodeType: number;
896-
id: string;
895+
__brand__: 'SsrNode';
897896
};
898897
} | null, DomRefConstructor: {
899898
new (...rest: any[]): {
900-
$ssrNode$: ISsrNode;
899+
__brand__: 'DomRef';
901900
};
902-
} | null, symbolToChunkResolver: SymbolToChunkResolver, writer?: StreamWriter, prepVNodeData?: (vNode: any) => void): SerializationContext;
901+
} | null, symbolToChunkResolver: SymbolToChunkResolver, writer?: StreamWriter): SerializationContext;
903902
// (undocumented)
904903
abstract setContext<T>(host: HostElement, context: ContextId<T>, value: T): void;
905904
// (undocumented)

packages/qwik/src/core/reactive-primitives/subscriber.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,5 @@ export function getSubscriber(
3030
}
3131

3232
function isSsrNode(value: any): value is ISsrNode {
33-
return '__brand__' in value && 'currentComponentNode' in value;
33+
return '__brand__' in value && value.__brand__ === 'SsrNode';
3434
}

packages/qwik/src/core/shared/scheduler-document-position.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,11 @@ export const ssrNodeDocumentPosition = (a: ISsrNode, b: ISsrNode): -1 | 0 | 1 =>
9696
let bDepth = -1;
9797
while (a) {
9898
const ssrNode = (aSsrNodePath[++aDepth] = a);
99-
a = ssrNode.currentComponentNode!;
99+
a = ssrNode.parentSsrNode!;
100100
}
101101
while (b) {
102102
const ssrNode = (bSsrNodePath[++bDepth] = b);
103-
b = ssrNode.currentComponentNode!;
103+
b = ssrNode.parentSsrNode!;
104104
}
105105

106106
while (aDepth >= 0 && bDepth >= 0) {

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

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { trackSignalAndAssignHost } from '../use/use-core';
33
import { version } from '../version';
44
import type { SubscriptionData } from '../reactive-primitives/subscription-data';
55
import type { Signal } from '../reactive-primitives/signal.public';
6-
import type { ISsrNode, StreamWriter, SymbolToChunkResolver } from '../ssr/ssr-types';
6+
import type { StreamWriter, SymbolToChunkResolver } from '../ssr/ssr-types';
77
import type { Scheduler } from './scheduler';
88
import { createScheduler } from './scheduler';
99
import { createSerializationContext, type SerializationContext } from './shared-serialization';
@@ -51,14 +51,13 @@ export abstract class _SharedContainer implements Container {
5151

5252
serializationCtxFactory(
5353
NodeConstructor: {
54-
new (...rest: any[]): { nodeType: number; id: string };
54+
new (...rest: any[]): { __brand__: 'SsrNode' };
5555
} | null,
5656
DomRefConstructor: {
57-
new (...rest: any[]): { $ssrNode$: ISsrNode };
57+
new (...rest: any[]): { __brand__: 'DomRef' };
5858
} | null,
5959
symbolToChunkResolver: SymbolToChunkResolver,
60-
writer?: StreamWriter,
61-
prepVNodeData?: (vNode: any) => void
60+
writer?: StreamWriter
6261
): SerializationContext {
6362
return createSerializationContext(
6463
NodeConstructor,
@@ -67,8 +66,7 @@ export abstract class _SharedContainer implements Container {
6766
this.getHostProp.bind(this),
6867
this.setHostProp.bind(this),
6968
this.$storeProxyMap$,
70-
writer,
71-
prepVNodeData
69+
writer
7270
);
7371
}
7472

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

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import type { DeserializeContainer, HostElement, ObjToProxyMap } from './types';
3838
import { _CONST_PROPS, _VAR_PROPS } from './utils/constants';
3939
import { isElement, isNode } from './utils/element';
4040
import { EMPTY_ARRAY, EMPTY_OBJ } from './utils/flyweight';
41-
import { ELEMENT_ID } from './utils/markers';
41+
import { ELEMENT_ID, ELEMENT_PROPS, QBackRefs } from './utils/markers';
4242
import { isPromise } from './utils/promises';
4343
import { SerializerSymbol, fastSkipSerialize } from './utils/serialize-utils';
4444
import {
@@ -601,9 +601,8 @@ export function inflateQRL(container: DeserializeContainer, qrl: QRLInternal<any
601601

602602
/** A selection of attributes of the real thing */
603603
type SsrNode = {
604-
nodeType: number;
605604
id: string;
606-
childrenVNodeData: VNodeData[] | null;
605+
children: ISsrNode[] | null;
607606
vnodeData: VNodeData;
608607
[_EFFECT_BACK_REF]: Map<EffectProperty | string, EffectSubscription> | null;
609608
};
@@ -679,7 +678,6 @@ export interface SerializationContext {
679678

680679
$getProp$: (obj: any, prop: string) => any;
681680
$setProp$: (obj: any, prop: string, value: any) => void;
682-
$prepVNodeData$?: (vNodeData: VNodeData) => void;
683681
}
684682

685683
export const createSerializationContext = (
@@ -690,19 +688,17 @@ export const createSerializationContext = (
690688
* server will not know what to do with them.
691689
*/
692690
NodeConstructor: {
693-
new (...rest: any[]): { nodeType: number; id: string };
691+
new (...rest: any[]): { __brand__: 'SsrNode' };
694692
} | null,
695693
/** DomRef constructor, for instanceof checks. */
696694
DomRefConstructor: {
697-
new (...rest: any[]): { $ssrNode$: ISsrNode };
695+
new (...rest: any[]): { __brand__: 'DomRef' };
698696
} | null,
699697
symbolToChunkResolver: SymbolToChunkResolver,
700698
getProp: (obj: any, prop: string) => any,
701699
setProp: (obj: any, prop: string, value: any) => void,
702700
storeProxyMap: ObjToProxyMap,
703-
writer?: StreamWriter,
704-
// temporary until we serdes the vnode data here
705-
prepVNodeData?: (vNodeData: VNodeData) => void
701+
writer?: StreamWriter
706702
): SerializationContext => {
707703
if (!writer) {
708704
const buffer: string[] = [];
@@ -763,9 +759,10 @@ export const createSerializationContext = (
763759
return seen.$rootIndex$;
764760
};
765761

766-
const isSsrNode = (NodeConstructor ? (obj) => obj instanceof NodeConstructor : () => false) as (
767-
obj: unknown
768-
) => obj is SsrNode;
762+
const isSsrNode = (
763+
NodeConstructor ? (obj) => obj instanceof NodeConstructor : ((() => false) as any)
764+
) as (obj: unknown) => obj is SsrNode;
765+
769766
isDomRef = (
770767
DomRefConstructor ? (obj) => obj instanceof DomRefConstructor : ((() => false) as any)
771768
) as (obj: unknown) => obj is DomRef;
@@ -816,7 +813,6 @@ export const createSerializationContext = (
816813
$storeProxyMap$: storeProxyMap,
817814
$getProp$: getProp,
818815
$setProp$: setProp,
819-
$prepVNodeData$: prepVNodeData,
820816
$pathMap$: rootsPathMap,
821817
};
822818
};
@@ -847,8 +843,14 @@ const discoverValuesForVNodeData = (vnodeData: VNodeData, callback: (value: unkn
847843
for (const value of vnodeData) {
848844
if (isSsrAttrs(value)) {
849845
for (let i = 1; i < value.length; i += 2) {
846+
const keyValue = value[i - 1];
850847
const attrValue = value[i];
851-
if (typeof attrValue === 'string') {
848+
if (
849+
typeof attrValue === 'string' ||
850+
// skip empty props
851+
(keyValue === ELEMENT_PROPS &&
852+
Object.keys(attrValue as Record<string, unknown>).length === 0)
853+
) {
852854
continue;
853855
}
854856
callback(attrValue);
@@ -1192,14 +1194,25 @@ async function serialize(serializationContext: SerializationContext): Promise<vo
11921194
output(TypeIds.VNode, value.id);
11931195
const vNodeData = value.vnodeData;
11941196
if (vNodeData) {
1195-
serializationContext.$prepVNodeData$?.(vNodeData);
11961197
discoverValuesForVNodeData(vNodeData, (vNodeDataValue) => $addRoot$(vNodeDataValue));
11971198
vNodeData[0] |= VNodeDataFlag.SERIALIZE;
11981199
}
1199-
if (value.childrenVNodeData) {
1200-
for (const vNodeData of value.childrenVNodeData) {
1201-
discoverValuesForVNodeData(vNodeData, (vNodeDataValue) => $addRoot$(vNodeDataValue));
1202-
vNodeData[0] |= VNodeDataFlag.SERIALIZE;
1200+
if (value.children) {
1201+
// can be static, but we need to save vnode data structure + discover the back refs
1202+
for (const child of value.children) {
1203+
const childVNodeData = child.vnodeData;
1204+
if (childVNodeData) {
1205+
// add all back refs to the roots
1206+
for (const value of childVNodeData) {
1207+
if (isSsrAttrs(value)) {
1208+
const backRefKeyIndex = value.findIndex((v) => v === QBackRefs);
1209+
if (backRefKeyIndex !== -1) {
1210+
$addRoot$(value[backRefKeyIndex + 1]);
1211+
}
1212+
}
1213+
}
1214+
childVNodeData[0] |= VNodeDataFlag.SERIALIZE;
1215+
}
12031216
}
12041217
}
12051218
} else if (typeof FormData !== 'undefined' && value instanceof FormData) {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ export interface Container {
4545
ensureProjectionResolved(host: HostElement): void;
4646
serializationCtxFactory(
4747
NodeConstructor: {
48-
new (...rest: any[]): { nodeType: number; id: string };
48+
new (...rest: any[]): { __brand__: 'SsrNode' };
4949
} | null,
5050
DomRefConstructor: {
51-
new (...rest: any[]): { $ssrNode$: ISsrNode };
51+
new (...rest: any[]): { __brand__: 'DomRef' };
5252
} | null,
5353
symbolToChunkResolver: SymbolToChunkResolver,
5454
writer?: StreamWriter

packages/qwik/src/core/ssr/ssr-render-component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export const applyInlineComponent = (
1414
inlineComponentFunction: OnRenderFn<any>,
1515
jsx: JSXNode
1616
) => {
17-
const host = ssr.getLastNode();
17+
const host = ssr.getOrCreateLastNode();
1818
return executeComponent(ssr, host, componentHost, inlineComponentFunction, jsx.props);
1919
};
2020

@@ -23,7 +23,7 @@ export const applyQwikComponentBody = (
2323
jsx: JSXNode,
2424
component: Component
2525
): ValueOrPromise<JSXOutput> => {
26-
const host = ssr.getLastNode();
26+
const host = ssr.getOrCreateLastNode();
2727
const [componentQrl] = (component as any)[SERIALIZABLE_STATE] as [QRLInternal<OnRenderFn<any>>];
2828
const srcProps = jsx.props;
2929
if (srcProps && srcProps.children) {

0 commit comments

Comments
 (0)