Skip to content

Commit

Permalink
Merge pull request #7236 from QwikDev/v2-warn-server-mismatch-error
Browse files Browse the repository at this point in the history
feat: log a warning instead of throwing an error for server host mismatch error
  • Loading branch information
wmertens authored Jan 20, 2025
2 parents cb22f46 + e0bc963 commit bd9dbf1
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 129 deletions.
5 changes: 5 additions & 0 deletions .changeset/shaggy-poems-appear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@qwik.dev/core': patch
---

feat: log a warning instead of throwing an error for server host mismatch error
60 changes: 0 additions & 60 deletions packages/qwik/src/core/client/vnode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1889,66 +1889,6 @@ export const vnode_getType = (vnode: VNode): 1 | 3 | 11 => {
const isElement = (node: any): node is Element =>
node && typeof node == 'object' && fastNodeType(node) === /** Node.ELEMENT_NODE* */ 1;

/// These global variables are used to avoid creating new arrays for each call to `vnode_documentPosition`.
const aPath: VNode[] = [];
const bPath: VNode[] = [];
export const vnode_documentPosition = (
a: VNode,
b: VNode,
rootVNode: ElementVNode | null
): -1 | 0 | 1 => {
if (a === b) {
return 0;
}

let aDepth = -1;
let bDepth = -1;
while (a) {
const vNode = (aPath[++aDepth] = a);
a = (vNode[VNodeProps.parent] ||
(rootVNode && vnode_getProp(a, QSlotParent, (id) => vnode_locate(rootVNode, id))))!;
}
while (b) {
const vNode = (bPath[++bDepth] = b);
b = (vNode[VNodeProps.parent] ||
(rootVNode && vnode_getProp(b, QSlotParent, (id) => vnode_locate(rootVNode, id))))!;
}

while (aDepth >= 0 && bDepth >= 0) {
a = aPath[aDepth] as VNode;
b = bPath[bDepth] as VNode;
if (a === b) {
// if the nodes are the same, we need to check the next level.
aDepth--;
bDepth--;
} else {
// We found a difference so we need to scan nodes at this level.
let cursor: VNode | null = b;
do {
cursor = vnode_getNextSibling(cursor);
if (cursor === a) {
return 1;
}
} while (cursor);
cursor = b;
do {
cursor = vnode_getPreviousSibling(cursor);
if (cursor === a) {
return -1;
}
} while (cursor);
if (rootVNode && vnode_getProp(b, QSlotParent, (id) => vnode_locate(rootVNode, id))) {
// The "b" node is a projection, so we need to set it after "a" node,
// because the "a" node could be a context provider.
return -1;
}
// The node is not in the list of siblings, that means it must be disconnected.
return 1;
}
}
return aDepth < bDepth ? -1 : 1;
};

/**
* Use this method to find the parent component for projection.
*
Expand Down
27 changes: 0 additions & 27 deletions packages/qwik/src/core/client/vnode.unit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import type {
} from './types';
import {
vnode_applyJournal,
vnode_documentPosition,
vnode_getAttr,
vnode_getFirstChild,
vnode_getNextSibling,
Expand Down Expand Up @@ -650,30 +649,4 @@ describe('vnode', () => {
});
});
});
describe('vnode_documentPosition', () => {
it('should compare two elements', () => {
parent.innerHTML = '<b></b><i></i>';
const b = vnode_getFirstChild(vParent) as ElementVNode;
const i = vnode_getNextSibling(b) as ElementVNode;
expect(vnode_documentPosition(b, i, null)).toBe(-1);
expect(vnode_documentPosition(i, b, null)).toBe(1);
});
it('should compare two virtual vNodes', () => {
parent.innerHTML = 'AB';
document.qVNodeData.set(parent, '{B}{B}');
const a = vnode_getFirstChild(vParent) as ElementVNode;
const b = vnode_getNextSibling(a) as ElementVNode;
expect(vnode_documentPosition(a, b, null)).toBe(-1);
expect(vnode_documentPosition(b, a, null)).toBe(1);
});
it('should compare two virtual vNodes', () => {
parent.innerHTML = 'AB';
document.qVNodeData.set(parent, '{{B}}{B}');
const a = vnode_getFirstChild(vParent) as ElementVNode;
const a2 = vnode_getFirstChild(a) as ElementVNode;
const b = vnode_getNextSibling(a) as ElementVNode;
expect(vnode_documentPosition(a2, b, null)).toBe(-1);
expect(vnode_documentPosition(b, a2, null)).toBe(1);
});
});
});
14 changes: 6 additions & 8 deletions packages/qwik/src/core/shared/error/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ export const codeToText = (code: number, ...parts: any[]): string => {
"Element must have 'q:container' attribute.", // 42
'Unknown vnode type {{0}}.', // 43
'Materialize error: missing element: {{0}} {{1}} {{2}}', // 44
'SsrError: {{0}}', // 45
'Cannot coerce a Signal, use `.value` instead', // 46
'useComputedSignal$ QRL {{0}} {{1}} returned a Promise', // 47
'ComputedSignal is read-only', // 48
Expand Down Expand Up @@ -121,13 +120,12 @@ export const enum QError {
elementWithoutContainer = 42,
invalidVNodeType = 43,
materializeVNodeDataError = 44,
serverHostMismatch = 45,
cannotCoerceSignal = 46,
computedNotSync = 47,
computedReadOnly = 48,
wrappedReadOnly = 49,
promisesNotExpected = 50,
unsafeAttr = 51,
cannotCoerceSignal = 45,
computedNotSync = 46,
computedReadOnly = 47,
wrappedReadOnly = 48,
promisesNotExpected = 49,
unsafeAttr = 50,
}

export const qError = (code: number, errorMessageArgs: any[] = []): Error => {
Expand Down
52 changes: 52 additions & 0 deletions packages/qwik/src/core/shared/scheduler-document-position.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import {
vnode_getFirstChild,
vnode_getNextSibling,
vnode_newUnMaterializedElement,
} from '../client/vnode';
import type { ContainerElement, ElementVNode, QDocument } from '../client/types';
import { vnode_documentPosition } from './scheduler-document-position';
import { createDocument } from '@qwik.dev/dom';

describe('vnode_documentPosition', () => {
let parent: ContainerElement;
let document: QDocument;
let vParent: ElementVNode;
beforeEach(() => {
document = createDocument() as QDocument;
document.qVNodeData = new WeakMap();
parent = document.createElement('test') as ContainerElement;
parent.qVNodeRefs = new Map();
vParent = vnode_newUnMaterializedElement(parent);
});
afterEach(() => {
parent = null!;
document = null!;
vParent = null!;
});

it('should compare two elements', () => {
parent.innerHTML = '<b></b><i></i>';
const b = vnode_getFirstChild(vParent) as ElementVNode;
const i = vnode_getNextSibling(b) as ElementVNode;
expect(vnode_documentPosition(b, i, null)).toBe(-1);
expect(vnode_documentPosition(i, b, null)).toBe(1);
});
it('should compare two virtual vNodes', () => {
parent.innerHTML = 'AB';
document.qVNodeData.set(parent, '{B}{B}');
const a = vnode_getFirstChild(vParent) as ElementVNode;
const b = vnode_getNextSibling(a) as ElementVNode;
expect(vnode_documentPosition(a, b, null)).toBe(-1);
expect(vnode_documentPosition(b, a, null)).toBe(1);
});
it('should compare two virtual vNodes', () => {
parent.innerHTML = 'AB';
document.qVNodeData.set(parent, '{{B}}{B}');
const a = vnode_getFirstChild(vParent) as ElementVNode;
const a2 = vnode_getFirstChild(a) as ElementVNode;
const b = vnode_getNextSibling(a) as ElementVNode;
expect(vnode_documentPosition(a2, b, null)).toBe(-1);
expect(vnode_documentPosition(b, a2, null)).toBe(1);
});
});
118 changes: 118 additions & 0 deletions packages/qwik/src/core/shared/scheduler-document-position.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { VNodeProps, type ElementVNode, type VNode } from '../client/types';
import {
vnode_getNextSibling,
vnode_getPreviousSibling,
vnode_getProp,
vnode_locate,
} from '../client/vnode';
import type { ISsrNode } from '../ssr/ssr-types';
import { QSlotParent } from './utils/markers';

/// These global variables are used to avoid creating new arrays for each call to `vnode_documentPosition`.
const aVNodePath: VNode[] = [];
const bVNodePath: VNode[] = [];
/**
* Compare two VNodes and determine their document position relative to each other.
*
* @param a VNode to compare
* @param b VNode to compare
* @param rootVNode - Root VNode of a container
* @returns -1 if `a` is before `b`, 0 if `a` is the same as `b`, 1 if `a` is after `b`.
*/
export const vnode_documentPosition = (
a: VNode,
b: VNode,
rootVNode: ElementVNode | null
): -1 | 0 | 1 => {
if (a === b) {
return 0;
}

let aDepth = -1;
let bDepth = -1;
while (a) {
const vNode = (aVNodePath[++aDepth] = a);
a = (vNode[VNodeProps.parent] ||
(rootVNode && vnode_getProp(a, QSlotParent, (id) => vnode_locate(rootVNode, id))))!;
}
while (b) {
const vNode = (bVNodePath[++bDepth] = b);
b = (vNode[VNodeProps.parent] ||
(rootVNode && vnode_getProp(b, QSlotParent, (id) => vnode_locate(rootVNode, id))))!;
}

while (aDepth >= 0 && bDepth >= 0) {
a = aVNodePath[aDepth] as VNode;
b = bVNodePath[bDepth] as VNode;
if (a === b) {
// if the nodes are the same, we need to check the next level.
aDepth--;
bDepth--;
} else {
// We found a difference so we need to scan nodes at this level.
let cursor: VNode | null = b;
do {
cursor = vnode_getNextSibling(cursor);
if (cursor === a) {
return 1;
}
} while (cursor);
cursor = b;
do {
cursor = vnode_getPreviousSibling(cursor);
if (cursor === a) {
return -1;
}
} while (cursor);
if (rootVNode && vnode_getProp(b, QSlotParent, (id) => vnode_locate(rootVNode, id))) {
// The "b" node is a projection, so we need to set it after "a" node,
// because the "a" node could be a context provider.
return -1;
}
// The node is not in the list of siblings, that means it must be disconnected.
return 1;
}
}
return aDepth < bDepth ? -1 : 1;
};

/// These global variables are used to avoid creating new arrays for each call to `ssrNodeDocumentPosition`.
const aSsrNodePath: ISsrNode[] = [];
const bSsrNodePath: ISsrNode[] = [];
/**
* Compare two SSR nodes and determine their document position relative to each other. Compares only
* position between parent and child.
*
* @param a SSR node to compare
* @param b SSR node to compare
* @returns -1 if `a` is before `b`, 0 if `a` is the same as `b`, 1 if `a` is after `b`.
*/
export const ssrNodeDocumentPosition = (a: ISsrNode, b: ISsrNode): -1 | 0 | 1 => {
if (a === b) {
return 0;
}

let aDepth = -1;
let bDepth = -1;
while (a) {
const ssrNode = (aSsrNodePath[++aDepth] = a);
a = ssrNode.currentComponentNode!;
}
while (b) {
const ssrNode = (bSsrNodePath[++bDepth] = b);
b = ssrNode.currentComponentNode!;
}

while (aDepth >= 0 && bDepth >= 0) {
a = aSsrNodePath[aDepth] as ISsrNode;
b = bSsrNodePath[bDepth] as ISsrNode;
if (a === b) {
// if the nodes are the same, we need to check the next level.
aDepth--;
bDepth--;
} else {
return 1;
}
}
return aDepth < bDepth ? -1 : 1;
};
Loading

0 comments on commit bd9dbf1

Please sign in to comment.