Skip to content

Commit e208544

Browse files
committed
Merge remote-tracking branch 'origin/build/v2' into v2-merge-main
2 parents 7a70b76 + c647c56 commit e208544

23 files changed

+580
-216
lines changed

.changeset/cold-rice-slide.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: proper empty props diffing

.changeset/olive-yaks-prove.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: don't wrap static objects with signal

.changeset/pretty-trees-check.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: ensure components are only rendered when necessary

.changeset/rare-candies-join.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: skip serialize functions wrapped with the `noSerialize`

.changeset/shy-walls-shake.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: reactivity for type-asserted variables in templates

.changeset/some-birds-juggle.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: reactivity for logical expressions in templates

packages/qwik/src/core/client/vnode-diff.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,9 +1053,9 @@ export const vnode_diff = (
10531053
} else {
10541054
// We did not find the component, create it.
10551055
insertNewComponent(host, componentQRL, jsxProps);
1056+
shouldRender = true;
10561057
}
10571058
host = vNewNode as VirtualVNode;
1058-
shouldRender = true;
10591059
} else if (!hashesAreEqual || !jsxNode.key) {
10601060
insertNewComponent(host, componentQRL, jsxProps);
10611061
host = vNewNode as VirtualVNode;
@@ -1237,7 +1237,12 @@ function getComponentHash(vNode: VNode | null, getObject: (id: string) => any):
12371237
function Projection() {}
12381238

12391239
function propsDiffer(src: Record<string, any>, dst: Record<string, any>): boolean {
1240-
if (!src || !dst) {
1240+
const srcEmpty = isPropsEmpty(src);
1241+
const dstEmpty = isPropsEmpty(dst);
1242+
if (srcEmpty && dstEmpty) {
1243+
return false;
1244+
}
1245+
if (srcEmpty || dstEmpty) {
12411246
return true;
12421247
}
12431248
let srcKeys = removePropsKeys(Object.keys(src), ['children', QBackRefs]);
@@ -1257,6 +1262,13 @@ function propsDiffer(src: Record<string, any>, dst: Record<string, any>): boolea
12571262
return false;
12581263
}
12591264

1265+
function isPropsEmpty(props: Record<string, any>): boolean {
1266+
if (!props) {
1267+
return true;
1268+
}
1269+
return Object.keys(props).length === 0;
1270+
}
1271+
12601272
function removePropsKeys(keys: string[], propKeys: string[]): string[] {
12611273
for (let i = propKeys.length - 1; i >= 0; i--) {
12621274
const propKey = propKeys[i];

packages/qwik/src/core/reactive-primitives/internal-api.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ export const _wrapProp = <T extends Record<any, any>, P extends keyof T>(...args
5656
return wrappedValue;
5757
}
5858
}
59-
// We need to forward the access to the original object
60-
return getWrapped(args);
59+
// the object is not reactive, so we can just return the value
60+
return obj[prop];
6161
};
6262

6363
/** @internal */

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

Lines changed: 62 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -9,53 +9,37 @@ export const codeToText = (code: number, ...parts: any[]): string => {
99
'Scheduler not found', // 1
1010
'track() received object, without prop to track', // 2
1111
'Only primitive and object literals can be serialized. {{0}}', // 3
12-
'', // 4 unused
13-
'You can render over a existing q:container. Skipping render().', // 5
14-
'', // 6 unused
15-
'', // 7 unused
16-
'', // 8 unused
17-
'', // 9 unused
18-
'QRL is not a function', // 10
19-
'Dynamic import not found', // 11
20-
'Unknown type argument', // 12
21-
`Actual value for useContext({{0}}) can not be found, make sure some ancestor component has set a value using useContextProvider(). In the browser make sure that the context was used during SSR so its state was serialized.`, // 13
22-
"Invoking 'use*()' method outside of invocation context.", // 14
23-
'', // 15 unused
24-
'', // 16 unused
25-
'', // 17 unused
26-
'', // 18 unused
27-
'', // 19 unused
28-
`Calling a 'use*()' method outside 'component$(() => { HERE })' is not allowed. 'use*()' methods provide hooks to the 'component$' state and lifecycle, ie 'use' hooks can only be called synchronously within the 'component$' function or another 'use' method.\nSee https://qwik.dev/docs/components/tasks/#use-method-rules`, // 20
29-
'', // 21 unused
30-
'', // 22 unused
31-
'', // 23 unused
32-
'', // 24 unused
33-
'', // 25 unused
34-
'', // 26 unused
35-
'', // 27 unused
36-
'The provided Context reference "{{0}}" is not a valid context created by createContextId()', // 28
37-
'SsrError(tag): {{0}}', // 29
38-
'QRLs can not be resolved because it does not have an attached container. This means that the QRL does not know where it belongs inside the DOM, so it cant dynamically import() from a relative path.', // 30
39-
'QRLs can not be dynamically resolved, because it does not have a chunk path', // 31
40-
'{{0}}\nThe JSX ref attribute must be a Signal', // 32
41-
'Serialization Error: Deserialization of data type {{0}} is not implemented', // 33
42-
'Serialization Error: Expected vnode for ref prop, but got {{0}}', // 34
43-
'Serialization Error: Cannot allocate data type {{0}}', // 35
44-
'Serialization Error: Missing root id for {{0}}', // 36
45-
'Serialization Error: Serialization of data type {{0}} is not implemented', // 37
46-
'Serialization Error: Unvisited {{0}}', // 38
47-
'Serialization Error: Missing QRL chunk for {{0}}', // 39
48-
'{{0}}\nThe value of the textarea must be a string found {{1}}', // 40
49-
'Unable to find q:container', // 41
50-
"Element must have 'q:container' attribute.", // 42
51-
'Unknown vnode type {{0}}.', // 43
52-
'Materialize error: missing element: {{0}} {{1}} {{2}}', // 44
53-
'Cannot coerce a Signal, use `.value` instead', // 45
54-
'useComputed$ QRL {{0}} {{1}} cannot return a Promise', // 46
55-
'ComputedSignal is read-only', // 47
56-
'WrappedSignal is read-only', // 48
57-
'Attribute value is unsafe for SSR', // 49
58-
'SerializerSymbol function returned rejected promise', // 50
12+
'You can render over a existing q:container. Skipping render().', // 4
13+
'QRL is not a function', // 5
14+
'Dynamic import not found', // 6
15+
'Unknown type argument', // 7
16+
`Actual value for useContext({{0}}) can not be found, make sure some ancestor component has set a value using useContextProvider(). In the browser make sure that the context was used during SSR so its state was serialized.`, // 8
17+
"Invoking 'use*()' method outside of invocation context.", // 9
18+
`Calling a 'use*()' method outside 'component$(() => { HERE })' is not allowed. 'use*()' methods provide hooks to the 'component$' state and lifecycle, ie 'use' hooks can only be called synchronously within the 'component$' function or another 'use' method.\nSee https://qwik.dev/docs/components/tasks/#use-method-rules`, // 10
19+
'The provided Context reference "{{0}}" is not a valid context created by createContextId()', // 11
20+
'SsrError(tag): {{0}}', // 12
21+
'QRLs can not be resolved because it does not have an attached container. This means that the QRL does not know where it belongs inside the DOM, so it cant dynamically import() from a relative path.', // 13
22+
'QRLs can not be dynamically resolved, because it does not have a chunk path', // 14
23+
'{{0}}\nThe JSX ref attribute must be a Signal', // 15
24+
'Serialization Error: Deserialization of data type {{0}} is not implemented', // 16
25+
'Serialization Error: Expected vnode for ref prop, but got {{0}}', // 17
26+
'Serialization Error: Cannot allocate data type {{0}}', // 18
27+
'Serialization Error: Missing root id for {{0}}', // 19
28+
'Serialization Error: Serialization of data type {{0}} is not implemented', // 20
29+
'Serialization Error: Unvisited {{0}}', // 21
30+
'Serialization Error: Missing QRL chunk for {{0}}', // 22
31+
'{{0}}\nThe value of the textarea must be a string found {{1}}', // 23
32+
'Unable to find q:container', // 24
33+
"Element must have 'q:container' attribute.", // 25
34+
'Unknown vnode type {{0}}.', // 26
35+
'Materialize error: missing element: {{0}} {{1}} {{2}}', // 27
36+
'Cannot coerce a Signal, use `.value` instead', // 28
37+
'useComputed$ QRL {{0}} {{1}} cannot return a Promise', // 29
38+
'ComputedSignal is read-only', // 30
39+
'WrappedSignal is read-only', // 31
40+
'Attribute value is unsafe for SSR', // 32
41+
'SerializerSymbol function returned rejected promise', // 33
42+
'Serialization Error: Cannot serialize function: {{0}}', // 34
5943
];
6044
let text = MAP[code] ?? '';
6145
if (parts.length) {
@@ -79,53 +63,37 @@ export const enum QError {
7963
schedulerNotFound = 1,
8064
trackObjectWithoutProp = 2,
8165
verifySerializable = 3,
82-
UNUSED_4 = 4,
83-
cannotRenderOverExistingContainer = 5,
84-
UNUSED_6 = 6,
85-
UNUSED_7 = 7,
86-
UNUSED_8 = 8,
87-
UNUSED_9 = 9,
88-
qrlIsNotFunction = 10,
89-
dynamicImportFailed = 11,
90-
unknownTypeArgument = 12,
91-
notFoundContext = 13,
92-
useMethodOutsideContext = 14,
93-
UNUSED_15 = 15,
94-
UNUSED_16 = 16,
95-
UNUSED_17 = 17,
96-
UNUSED_18 = 18,
97-
UNUSED_19 = 19,
98-
useInvokeContext = 20,
99-
UNUSED_21 = 21,
100-
UNUSED_22 = 22,
101-
UNUSED_23 = 23,
102-
UNUSED_24 = 24,
103-
UNUSED_25 = 25,
104-
UNUSED_26 = 26,
105-
UNUSED_27 = 27,
106-
invalidContext = 28,
107-
tagError = 29,
108-
qrlMissingContainer = 30,
109-
qrlMissingChunk = 31,
110-
invalidRefValue = 32,
111-
serializeErrorNotImplemented = 33,
112-
serializeErrorExpectedVNode = 34,
113-
serializeErrorCannotAllocate = 35,
114-
serializeErrorMissingRootId = 36,
115-
serializeErrorUnknownType = 37,
116-
serializeErrorUnvisited = 38,
117-
serializeErrorMissingChunk = 39,
118-
wrongTextareaValue = 40,
119-
containerNotFound = 41,
120-
elementWithoutContainer = 42,
121-
invalidVNodeType = 43,
122-
materializeVNodeDataError = 44,
123-
cannotCoerceSignal = 45,
124-
computedNotSync = 46,
125-
computedReadOnly = 47,
126-
wrappedReadOnly = 48,
127-
unsafeAttr = 49,
128-
serializerSymbolRejectedPromise = 50,
66+
cannotRenderOverExistingContainer = 4,
67+
qrlIsNotFunction = 5,
68+
dynamicImportFailed = 6,
69+
unknownTypeArgument = 7,
70+
notFoundContext = 8,
71+
useMethodOutsideContext = 9,
72+
useInvokeContext = 10,
73+
invalidContext = 11,
74+
tagError = 12,
75+
qrlMissingContainer = 13,
76+
qrlMissingChunk = 14,
77+
invalidRefValue = 15,
78+
serializeErrorNotImplemented = 16,
79+
serializeErrorExpectedVNode = 17,
80+
serializeErrorCannotAllocate = 18,
81+
serializeErrorMissingRootId = 19,
82+
serializeErrorUnknownType = 20,
83+
serializeErrorUnvisited = 21,
84+
serializeErrorMissingChunk = 22,
85+
wrongTextareaValue = 23,
86+
containerNotFound = 24,
87+
elementWithoutContainer = 25,
88+
invalidVNodeType = 26,
89+
materializeVNodeDataError = 27,
90+
cannotCoerceSignal = 28,
91+
computedNotSync = 29,
92+
computedReadOnly = 30,
93+
wrappedReadOnly = 31,
94+
unsafeAttr = 32,
95+
serializerSymbolRejectedPromise = 33,
96+
serializeErrorCannotSerializeFunction = 34,
12997
}
13098

13199
export const qError = (code: number, errorMessageArgs: any[] = []): Error => {

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

Lines changed: 15 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -933,20 +933,21 @@ async function serialize(serializationContext: SerializationContext): Promise<vo
933933
serializationContext.$addRoot$(qrl, null);
934934
};
935935

936-
const outputRootRef = (value: unknown, elseCallback: () => void) => {
936+
const outputRootRef = (value: unknown, rootDepth = 0) => {
937937
const seen = $wasSeen$(value);
938938
const rootRefPath = $pathMap$.get(value);
939-
if (isRootObject() && seen && seen.$parent$ !== null && rootRefPath) {
939+
if (rootDepth === depth && seen && seen.$parent$ !== null && rootRefPath) {
940940
output(TypeIds.RootRef, rootRefPath);
941-
} else if (depth > 0 && seen && seen.$rootIndex$ !== -1) {
941+
return true;
942+
} else if (depth > rootDepth && seen && seen.$rootIndex$ !== -1) {
942943
output(TypeIds.RootRef, seen.$rootIndex$);
943-
} else {
944-
elseCallback();
944+
return true;
945945
}
946+
return false;
946947
};
947948

948949
const writeValue = (value: unknown) => {
949-
if (fastSkipSerialize(value as object)) {
950+
if (fastSkipSerialize(value as object | Function)) {
950951
output(TypeIds.Constant, Constants.Undefined);
951952
} else if (typeof value === 'bigint') {
952953
output(TypeIds.BigInt, value.toString());
@@ -958,7 +959,7 @@ async function serialize(serializationContext: SerializationContext): Promise<vo
958959
} else if (value === Fragment) {
959960
output(TypeIds.Constant, Constants.Fragment);
960961
} else if (isQrl(value)) {
961-
outputRootRef(value, () => {
962+
if (!outputRootRef(value)) {
962963
const qrl = qrlToString(serializationContext, value);
963964
const type = preloadQrls.has(value) ? TypeIds.PreloadQRL : TypeIds.QRL;
964965
if (isRootObject()) {
@@ -967,15 +968,13 @@ async function serialize(serializationContext: SerializationContext): Promise<vo
967968
const id = serializationContext.$addRoot$(qrl);
968969
output(type, id);
969970
}
970-
});
971+
}
971972
} else if (isQwikComponent(value)) {
972973
const [qrl]: [QRLInternal] = (value as any)[SERIALIZABLE_STATE];
973974
serializationContext.$renderSymbols$.add(qrl.$symbol$);
974975
output(TypeIds.Component, [qrl]);
975976
} else {
976-
// TODO this happens for inline components with render props like Resource
977-
console.error('Cannot serialize function (ignoring for now): ' + value.toString());
978-
output(TypeIds.Constant, Constants.Undefined);
977+
throw qError(QError.serializeErrorCannotSerializeFunction, [value.toString()]);
979978
}
980979
} else if (typeof value === 'number') {
981980
if (Number.isNaN(value)) {
@@ -1013,9 +1012,9 @@ async function serialize(serializationContext: SerializationContext): Promise<vo
10131012
if (value.length === 0) {
10141013
output(TypeIds.Constant, Constants.EmptyString);
10151014
} else {
1016-
outputRootRef(value, () => {
1015+
if (!outputRootRef(value)) {
10171016
output(TypeIds.String, value);
1018-
});
1017+
}
10191018
}
10201019
} else if (typeof value === 'undefined') {
10211020
output(TypeIds.Constant, Constants.Undefined);
@@ -1033,28 +1032,15 @@ async function serialize(serializationContext: SerializationContext): Promise<vo
10331032
* The object writer outputs an array object (without type prefix) and this increases the depth
10341033
* for the objects within (depth 1).
10351034
*/
1036-
const isRootObject = depth === 1;
10371035
// Objects are the only way to create circular dependencies.
10381036
// So the first thing to to is to see if we have a circular dependency.
10391037
// (NOTE: For root objects we need to serialize them regardless if we have seen
10401038
// them before, otherwise the root object reference will point to itself.)
10411039
// Also note that depth will be 1 for objects in root
1042-
if (isRootObject) {
1043-
const seen = $wasSeen$(value);
1044-
const rootPath = $pathMap$.get(value);
1045-
if (rootPath && seen && seen.$parent$ !== null) {
1046-
output(TypeIds.RootRef, rootPath);
1047-
return;
1048-
}
1049-
} else if (depth > 1) {
1050-
const seen = $wasSeen$(value);
1051-
if (seen && seen.$rootIndex$ !== -1) {
1052-
// We have seen this object before, so we can serialize it as a reference.
1053-
// Otherwise serialize as normal
1054-
output(TypeIds.RootRef, seen.$rootIndex$);
1055-
return;
1056-
}
1040+
if (outputRootRef(value, 1)) {
1041+
return;
10571042
}
1043+
10581044
if (isPropsProxy(value)) {
10591045
const varProps = value[_VAR_PROPS];
10601046
const constProps = value[_CONST_PROPS];

0 commit comments

Comments
 (0)