From fbd644f9274af12a7592e3c59189c6a97028df82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Popek?= <78650133+Varixo@users.noreply.github.com> Date: Thu, 13 Jun 2024 01:38:16 +0200 Subject: [PATCH 1/5] fix(v2): inline component rendering fixes (#6428) * fix(v2): inline component rendering fixes * remove removing children from propsdiffer * fix lint * temporarly skip failing test * temporarly skip failing test --- packages/qwik/src/core/container/resume.ts | 4 +- .../qwik/src/core/render/dom/notify-render.ts | 12 +- packages/qwik/src/core/use/use-core.ts | 16 +- .../src/core/use/use-lexical-scope.public.ts | 6 +- .../qwik/src/core/v2/client/dom-render.ts | 6 +- .../qwik/src/core/v2/client/vnode-diff.ts | 50 +++--- .../core/v2/tests/inline-component.spec.tsx | 150 ++++++++++++++++++ .../src/core/v2/tests/use-signal.spec.tsx | 37 +++++ packages/qwik/src/server/v2-ssr-container.ts | 4 + packages/qwik/src/testing/element-fixture.ts | 6 +- starters/e2e/qwikcity/menu.e2e.ts | 3 +- starters/e2e/qwikcity/page.e2e.ts | 2 +- 12 files changed, 232 insertions(+), 64 deletions(-) create mode 100644 packages/qwik/src/core/v2/tests/inline-component.spec.tsx diff --git a/packages/qwik/src/core/container/resume.ts b/packages/qwik/src/core/container/resume.ts index acc22d90f3e..5c8b7371772 100644 --- a/packages/qwik/src/core/container/resume.ts +++ b/packages/qwik/src/core/container/resume.ts @@ -25,9 +25,9 @@ import { createProxy, setObjectFlags } from '../state/store'; import { qDev, qSerialize } from '../util/qdev'; import { pauseContainer } from './pause'; import { isPrimitive } from '../render/dom/render-dom'; -import { getWrappingContainer } from '../use/use-core'; import { getContext } from '../state/context'; import { EMPTY_ARRAY } from '../util/flyweight'; +import { _getQContainerElement } from '../v2/client/dom-container'; export const resumeIfNeeded = (containerEl: Element): void => { const isResumed = directGetAttribute(containerEl, QContainerAttr); @@ -63,7 +63,7 @@ export const _deserializeData = (data: string, element?: unknown) => { let doc = {} as Document; let containerState = {} as any; if (isNode(element) && isQwikElement(element)) { - const containerEl = getWrappingContainer(element); + const containerEl = _getQContainerElement(element); if (containerEl) { containerState = _getContainerState(containerEl); doc = containerEl.ownerDocument; diff --git a/packages/qwik/src/core/render/dom/notify-render.ts b/packages/qwik/src/core/render/dom/notify-render.ts index 7904d5fe32c..8644f69e2b4 100644 --- a/packages/qwik/src/core/render/dom/notify-render.ts +++ b/packages/qwik/src/core/render/dom/notify-render.ts @@ -4,7 +4,6 @@ import { assertDefined, assertTrue } from '../../error/assert'; import { getPlatform, isServerPlatform } from '../../platform/platform'; import type { SubscriberSignal, Subscriptions } from '../../state/common'; import { HOST_FLAG_DIRTY, getContext, type QContext } from '../../state/context'; -import { getWrappingContainer } from '../../use/use-core'; import { useLexicalScope } from '../../use/use-lexical-scope.public'; import { TaskFlags, @@ -136,14 +135,9 @@ const scheduleFrame = (containerState: ContainerState): Promise => { */ export const _hW = () => { const [task] = useLexicalScope<[SubscriberEffect]>(); - if (vnode_isVNode(task.$el$)) { - const containerElement = getWrappingContainer(task.$el$ as fixMeAny) as HTMLElement; - const container = getDomContainer(containerElement); - const type = task.$flags$ & TaskFlags.VISIBLE_TASK ? ChoreType.VISIBLE : ChoreType.TASK; - container.$scheduler$(type, task as Task); - } else { - notifyTask(task, _getContainerState(getWrappingContainer(task.$el$)!)); - } + const container = getDomContainer(task.$el$ as Element); + const type = task.$flags$ & TaskFlags.VISIBLE_TASK ? ChoreType.VISIBLE : ChoreType.TASK; + container.$scheduler$(type, task as Task); }; const renderMarked = async (containerState: ContainerState): Promise => { diff --git a/packages/qwik/src/core/use/use-core.ts b/packages/qwik/src/core/use/use-core.ts index bb3be721cb7..1e9a3e855e7 100644 --- a/packages/qwik/src/core/use/use-core.ts +++ b/packages/qwik/src/core/use/use-core.ts @@ -21,13 +21,8 @@ import { isArray } from '../util/types'; import { setLocale } from './use-locale'; import type { Subscriber } from '../state/common'; import type { Signal } from '../state/signal'; -import type { Container2, fixMeAny } from '../v2/shared/types'; -import { - vnode_getDomParent, - vnode_getNode, - vnode_isElementVNode, - vnode_isVNode, -} from '../v2/client/vnode'; +import type { Container2 } from '../v2/shared/types'; +import { vnode_getNode, vnode_isElementVNode, vnode_isVNode } from '../v2/client/vnode'; import { _getQContainerElement } from '../v2/client/dom-container'; import type { ContainerElement } from '../v2/client/types'; @@ -219,13 +214,6 @@ export const newInvokeContext = ( return ctx; }; -export const getWrappingContainer = (el: QwikElement): Element | null => { - if (vnode_isVNode(el)) { - el = vnode_getDomParent(el) as fixMeAny; - } - return el.closest(QContainerSelector); -}; - /** * Don't track listeners for this callback * diff --git a/packages/qwik/src/core/use/use-lexical-scope.public.ts b/packages/qwik/src/core/use/use-lexical-scope.public.ts index a847b0b2f09..033fae9524c 100644 --- a/packages/qwik/src/core/use/use-lexical-scope.public.ts +++ b/packages/qwik/src/core/use/use-lexical-scope.public.ts @@ -1,11 +1,11 @@ import { assertDefined } from '../error/assert'; import { inflateQrl, parseQRL } from '../qrl/qrl'; -import { getWrappingContainer, getInvokeContext } from './use-core'; +import { getInvokeContext } from './use-core'; import { assertQrl, type QRLInternal } from '../qrl/qrl-class'; import { getContext } from '../state/context'; import { resumeIfNeeded } from '../container/resume'; import { _getContainerState } from '../container/container'; -import { getDomContainer } from '../v2/client/dom-container'; +import { _getQContainerElement, getDomContainer } from '../v2/client/dom-container'; // // !!DO NOT EDIT THIS COMMENT DIRECTLY!!! @@ -27,7 +27,7 @@ export const useLexicalScope = (): VARS => { if (!qrl) { const el = context.$element$; assertDefined(el, 'invoke: element must be defined inside useLexicalScope()', context); - const containerElement = getWrappingContainer(el) as HTMLElement; + const containerElement = _getQContainerElement(el) as HTMLElement; assertDefined(containerElement, `invoke: cant find parent q:container of`, el); if (containerElement.getAttribute('q:runtime') == '2') { const container = getDomContainer(containerElement); diff --git a/packages/qwik/src/core/v2/client/dom-render.ts b/packages/qwik/src/core/v2/client/dom-render.ts index fac1008b433..00330413218 100644 --- a/packages/qwik/src/core/v2/client/dom-render.ts +++ b/packages/qwik/src/core/v2/client/dom-render.ts @@ -2,11 +2,11 @@ import type { JSXNode } from '@builder.io/qwik'; import type { RenderOptions, RenderResult } from '../../render/dom/render.public'; import type { FunctionComponent, JSXOutput } from '../../render/jsx/types/jsx-node'; import { isDocument, isElement } from '../../util/element'; -import { QContainerAttr } from '../../util/markers'; import { ChoreType } from '../shared/scheduler'; -import type { HostElement, fixMeAny } from '../shared/types'; +import { QContainerValue, type HostElement, type fixMeAny } from '../shared/types'; import { DomContainer, getDomContainer } from './dom-container'; import { cleanup } from './vnode-diff'; +import { QContainerAttr } from '../../util/markers'; /** * Render JSX. @@ -37,7 +37,7 @@ export const render2 = async ( } parent = child as Element; } - (parent as Element).setAttribute(QContainerAttr, 'resumed'); + (parent as Element).setAttribute(QContainerAttr, QContainerValue.RESUMED); const container = getDomContainer(parent as HTMLElement) as DomContainer; container.$serverData$ = opts.serverData!; diff --git a/packages/qwik/src/core/v2/client/vnode-diff.ts b/packages/qwik/src/core/v2/client/vnode-diff.ts index 439f74e8522..76a25e0104a 100644 --- a/packages/qwik/src/core/v2/client/vnode-diff.ts +++ b/packages/qwik/src/core/v2/client/vnode-diff.ts @@ -29,7 +29,6 @@ import { } from '../../util/markers'; import { isPromise } from '../../util/promises'; import { type ValueOrPromise } from '../../util/types'; -import { executeComponent2 } from '../shared/component-execution'; import { convertEventNameFromJsxPropToHtmlAttr, getEventNameFromJsxProp, @@ -39,7 +38,7 @@ import { } from '../shared/event-names'; import { ChoreType } from '../shared/scheduler'; import { hasClassAttr } from '../shared/scoped-styles'; -import type { QElement2, QwikLoaderEventScope, fixMeAny } from '../shared/types'; +import type { HostElement, QElement2, QwikLoaderEventScope, fixMeAny } from '../shared/types'; import { DEBUG_TYPE, QContainerValue, VirtualType } from '../shared/types'; import type { DomContainer } from './dom-container'; import { @@ -88,6 +87,7 @@ import { type VNodeJournal, } from './vnode'; import { getNewElementNamespaceData } from './vnode-namespace'; +import { executeComponent2 } from '../shared/component-execution'; export type ComponentQueue = Array; @@ -904,7 +904,7 @@ export const vnode_diff = ( function expectComponent(component: Function) { const componentMeta = (component as any)[SERIALIZABLE_STATE] as [QRLInternal>]; - let host = (vNewNode || vCurrent) as VirtualVNode; + let host = (vNewNode || vCurrent) as VirtualVNode | null; if (componentMeta) { const jsxProps = jsxValue.props; // QComponent @@ -944,31 +944,27 @@ export const vnode_diff = ( } } - const vNodeProps = vnode_getProp(host, ELEMENT_PROPS, container.$getObjectById$); - shouldRender = shouldRender || propsDiffer(jsxProps, vNodeProps); - if (shouldRender) { - container.$scheduler$(ChoreType.COMPONENT, host, componentQRL, jsxProps); + if (host) { + const vNodeProps = vnode_getProp(host, ELEMENT_PROPS, container.$getObjectById$); + shouldRender = shouldRender || propsDiffer(jsxProps, vNodeProps); + if (shouldRender) { + container.$scheduler$(ChoreType.COMPONENT, host, componentQRL, jsxProps); + } } jsxValue.children != null && descendContentToProject(jsxValue.children); } else { // Inline Component - if (!host) { - // We did not find the component, create it. - vnode_insertBefore( - journal, - vParent as VirtualVNode, - (vNewNode = vnode_newVirtual()), - vCurrent && getInsertBefore() - ); - host = vNewNode; - } - isDev && - vnode_setProp( - (vNewNode || vCurrent) as VirtualVNode, - DEBUG_TYPE, - VirtualType.InlineComponent - ); - let component$Host: VNode = host; + vnode_insertBefore( + journal, + vParent as VirtualVNode, + (vNewNode = vnode_newVirtual()), + vCurrent && getInsertBefore() + ); + isDev && vnode_setProp(vNewNode, DEBUG_TYPE, VirtualType.InlineComponent); + vnode_setProp(vNewNode, ELEMENT_PROPS, jsxValue.propsC); + + host = vNewNode; + let component$Host: VNode | null = host; // Find the closest component host which has `OnRender` prop. while ( component$Host && @@ -976,13 +972,13 @@ export const vnode_diff = ( ? vnode_getProp(component$Host, OnRenderProp, null) === null : true) ) { - component$Host = vnode_getParent(component$Host)!; + component$Host = vnode_getParent(component$Host); } const jsxOutput = executeComponent2( container, host, - (component$Host || container.rootVNode) as fixMeAny, - component as OnRenderFn, + (component$Host || container.rootVNode) as HostElement, + component as OnRenderFn, jsxValue.propsC ); asyncQueue.push(jsxOutput, host); diff --git a/packages/qwik/src/core/v2/tests/inline-component.spec.tsx b/packages/qwik/src/core/v2/tests/inline-component.spec.tsx new file mode 100644 index 00000000000..78bf1c2df76 --- /dev/null +++ b/packages/qwik/src/core/v2/tests/inline-component.spec.tsx @@ -0,0 +1,150 @@ +import { + Fragment as Component, + Fragment, + Fragment as InlineComponent, + component$, + useSignal, +} from '@builder.io/qwik'; +import { domRender, ssrRenderToDom, trigger } from '@builder.io/qwik/testing'; +import { describe, expect, it } from 'vitest'; + +const debug = false; //true; +Error.stackTraceLimit = 100; + +const MyComp = () => { + return ( + <> +

Test

+

Lorem

+

ipsum

+

foo

+

bar

+ + ); +}; + +const InlineWrapper = () => { + return ; +}; + +describe.each([ + { render: ssrRenderToDom }, // + { render: domRender }, // +])('$render.name: inline component', ({ render }) => { + it('should render inline component', async () => { + const MyComp = () => { + return <>Hello World!; + }; + + const { vNode } = await render(, { debug }); + expect(vNode).toMatchVDOM( + <> + <>Hello World! + + ); + }); + + it('should render nested component', async () => { + const Child = (props: { name: string }) => { + return <>{props.name}; + }; + + const Parent = (props: { salutation: string; name: string }) => { + return ( + <> + {props.salutation} + + ); + }; + + const { vNode } = await render(, { + debug, + }); + expect(vNode).toMatchVDOM( + + + {'Hello'}{' '} + + World + + + + ); + }); + + it('should toggle component$ and inline wrapper', async () => { + const Test = component$(() => { + return
Test
; + }); + const Wrapper = component$(() => { + const toggle = useSignal(true); + return ( + <> + + {toggle.value ? : } + + ); + }); + + const { vNode, document } = await render(, { debug }); + expect(vNode).toMatchVDOM( + + <> + + +
Test
+
+ +
+ ); + await trigger(document.body, 'button', 'click'); + expect(vNode).toMatchVDOM( + + <> + + + + +

Test

+

Lorem

+

ipsum

+

foo

+

bar

+
+
+
+ +
+ ); + await trigger(document.body, 'button', 'click'); + expect(vNode).toMatchVDOM( + + <> + + +
Test
+
+ +
+ ); + await trigger(document.body, 'button', 'click'); + expect(vNode).toMatchVDOM( + + <> + + + + +

Test

+

Lorem

+

ipsum

+

foo

+

bar

+
+
+
+ +
+ ); + }); +}); diff --git a/packages/qwik/src/core/v2/tests/use-signal.spec.tsx b/packages/qwik/src/core/v2/tests/use-signal.spec.tsx index e917f4a90d4..2d62100c00c 100644 --- a/packages/qwik/src/core/v2/tests/use-signal.spec.tsx +++ b/packages/qwik/src/core/v2/tests/use-signal.spec.tsx @@ -226,6 +226,43 @@ describe.each([ ); }); + + // TODO: should be fixed after signals v2 is implemented + it.skip('should not execute signal when not used', async () => { + const Cmp = component$(() => { + const data = useSignal<{ price: number } | null>({ price: 100 }); + return ( +
+ + {data.value == null && not found} + {data.value != null && {data.value.price}} +
+ ); + }); + const { vNode, document } = await render(, { debug }); + expect(vNode).toMatchVDOM( + +
+ + {''} + + 100 + +
+
+ ); + await trigger(document.body, 'button', 'click'); + expect(vNode).toMatchVDOM( + +
+ + not found + {''} +
+
+ ); + }); + describe('derived', () => { it('should update value directly in DOM', async () => { const log: string[] = []; diff --git a/packages/qwik/src/server/v2-ssr-container.ts b/packages/qwik/src/server/v2-ssr-container.ts index c7c09cd4997..8928d0f18e3 100644 --- a/packages/qwik/src/server/v2-ssr-container.ts +++ b/packages/qwik/src/server/v2-ssr-container.ts @@ -710,6 +710,10 @@ class SSRContainer extends _SharedContainer implements ISSRContainer { this.closeElement(); } + /** + * This is needed for the case when we have a component around the `` tag. In this case we + * start emitting the vnode script tag before the `` close tag. + */ addVNodeDataToSerializationRoots() { const vNodeAttrsStack: SsrAttrs[] = []; const vNodeData = this.vNodeData; diff --git a/packages/qwik/src/testing/element-fixture.ts b/packages/qwik/src/testing/element-fixture.ts index 6e8f2b86fbd..8129a517cf6 100644 --- a/packages/qwik/src/testing/element-fixture.ts +++ b/packages/qwik/src/testing/element-fixture.ts @@ -3,8 +3,8 @@ import { assertDefined } from '../core/error/assert'; import type { QRLInternal } from '../core/qrl/qrl-class'; import { tryGetContext, type QContext } from '../core/state/context'; import { normalizeOnProp } from '../core/state/listeners'; -import { getWrappingContainer, type PossibleEvents } from '../core/use/use-core'; -import { getDomContainer } from '@builder.io/qwik'; +import { type PossibleEvents } from '../core/use/use-core'; +import { _getQContainerElement, getDomContainer } from '@builder.io/qwik'; import { createWindow } from './document'; import { getTestPlatform } from './platform'; import type { MockDocument, MockWindow } from './types'; @@ -191,7 +191,7 @@ export function getEvent(elCtx: QContext, prop: string): any { export function qPropReadQRL(elCtx: QContext, prop: string): ((event: Event) => void) | null { const allListeners = elCtx.li; - const containerEl = getWrappingContainer(elCtx.$element$); + const containerEl = _getQContainerElement(elCtx.$element$ as Element); assertDefined(containerEl, 'container element must be defined'); return (event) => { diff --git a/starters/e2e/qwikcity/menu.e2e.ts b/starters/e2e/qwikcity/menu.e2e.ts index 823118e1e34..915d39e3c15 100644 --- a/starters/e2e/qwikcity/menu.e2e.ts +++ b/starters/e2e/qwikcity/menu.e2e.ts @@ -6,8 +6,7 @@ test.describe("Qwik City Menu", () => { test.use({ javaScriptEnabled: false }); tests(); }); - // TODO(v2): fix this - test.describe.skip("spa", () => { + test.describe("spa", () => { test.use({ javaScriptEnabled: true }); tests(); }); diff --git a/starters/e2e/qwikcity/page.e2e.ts b/starters/e2e/qwikcity/page.e2e.ts index 2710a2379c5..e89f400f23c 100644 --- a/starters/e2e/qwikcity/page.e2e.ts +++ b/starters/e2e/qwikcity/page.e2e.ts @@ -7,7 +7,7 @@ test.describe("Qwik City Page", () => { tests(); }); - // TODO(v2): fix this + // TODO(v2): this should be fixed after signals v2 is implemented test.describe.skip("spa", () => { test.use({ javaScriptEnabled: true }); tests(); From c0747707527f7abc18683523fbabb03b51bd3e5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Popek?= <78650133+Varixo@users.noreply.github.com> Date: Thu, 13 Jun 2024 01:38:37 +0200 Subject: [PATCH 2/5] fix(v2): fix setting store value with serialization constant at the start (#6456) --- .../core/v2/shared/shared-serialization.ts | 27 ++++++++++ .../qwik/src/core/v2/tests/use-store.spec.tsx | 54 +++++++++++++------ 2 files changed, 66 insertions(+), 15 deletions(-) diff --git a/packages/qwik/src/core/v2/shared/shared-serialization.ts b/packages/qwik/src/core/v2/shared/shared-serialization.ts index 124f06f0be3..ec9ce2a1733 100644 --- a/packages/qwik/src/core/v2/shared/shared-serialization.ts +++ b/packages/qwik/src/core/v2/shared/shared-serialization.ts @@ -156,6 +156,33 @@ class DeserializationHandler implements ProxyHandler { return propValue; } + set(target: object, property: string | symbol, newValue: any, receiver: any): boolean { + /** + * If we are setting a value which is a string and starts with a special character, we need to + * prefix it with a SerializationConstant character to indicate that it is a string. But only if + * the current value is an empty string. + * + * Without this later (when getting the value) we would try to deserialize the value incorrectly + * due to the special character at the start. + */ + if ( + typeof newValue === 'string' && + newValue.length >= 1 && + newValue.charCodeAt(0) < SerializationConstant.LAST_VALUE + ) { + const currentPropValue = Reflect.get(target, property, receiver); + if (typeof currentPropValue === 'string' && currentPropValue.length === 0) { + return Reflect.set( + target, + property, + SerializationConstant.String_CHAR + newValue, + receiver + ); + } + } + return Reflect.set(target, property, newValue, receiver); + } + has(target: object, property: PropertyKey) { if (property === SERIALIZER_PROXY_UNWRAP) { return true; diff --git a/packages/qwik/src/core/v2/tests/use-store.spec.tsx b/packages/qwik/src/core/v2/tests/use-store.spec.tsx index 403d2a66349..18bd7ae0d85 100644 --- a/packages/qwik/src/core/v2/tests/use-store.spec.tsx +++ b/packages/qwik/src/core/v2/tests/use-store.spec.tsx @@ -1,14 +1,15 @@ import { Fragment as Component, Fragment, Fragment as Signal } from '@builder.io/qwik'; import { describe, expect, it, vi } from 'vitest'; -import { advanceToNextTimerAndFlush, trigger } from '../../../testing/element-fixture'; -import { domRender, ssrRenderToDom } from '../../../testing/rendering.unit-util'; -import '../../../testing/vdom-diff.unit-util'; -import { component$ } from '@builder.io/qwik'; -import type { Signal as SignalType } from '../../state/signal'; -import { untrack } from '../../use/use-core'; -import { useSignal } from '../../use/use-signal'; -import { useStore } from '../../use/use-store.public'; -import { useTask$ } from '../../use/use-task'; +import { advanceToNextTimerAndFlush } from '../../../testing/element-fixture'; +import { domRender, ssrRenderToDom, trigger } from '@builder.io/qwik/testing'; +import { + component$, + type Signal as SignalType, + untrack, + useSignal, + useStore, + useTask$, +} from '@builder.io/qwik'; const debug = false; //true; Error.stackTraceLimit = 100; @@ -284,6 +285,30 @@ describe.each([ }); }); + it('should set the value with SerializationConstant at the start', async () => { + const DataCmp = component$(() => { + const data = useStore({ logs: '' }); + return ; + }); + + const { vNode, container } = await render(, { debug }); + expect(vNode).toMatchVDOM( + + + + ); + await trigger(container.element, 'button', 'click'); + expect(vNode).toMatchVDOM( + + + + ); + }); + describe('regression', () => { it('#5597 - should update value', async () => { (globalThis as any).clicks = 0; @@ -461,8 +486,7 @@ describe.each([ ); }); - // TODO(optimizer-test): this is failing also in v1 - it.skip('#5017 - should update child nodes for direct array', async () => { + it('#5017 - should update child nodes for direct array', async () => { const Child = component$<{ columns: string }>(({ columns }) => { return
Child: {columns}
; }); @@ -489,13 +513,13 @@ describe.each([
{'Child: '} - {'INITIAL'} + {'INITIAL'}
{'Child: '} - {'INITIAL'} + {'INITIAL'}
@@ -509,13 +533,13 @@ describe.each([
{'Child: '} - {'UPDATE'} + {'UPDATE'}
{'Child: '} - {'UPDATE'} + {'UPDATE'}
From 44034f7f0a6d5c36a638c30af32124f4fd6c2e50 Mon Sep 17 00:00:00 2001 From: Varixo Date: Sat, 15 Jun 2024 14:37:10 +0200 Subject: [PATCH 3/5] fix(v2): docs build --- packages/docs/check-qwik-build.ts | 3 +- .../routes/docs/(qwikcity)/server$/index.mdx | 32 ------------------- packages/docs/vite.config.mts | 3 ++ .../core/v2/shared/shared-serialization.ts | 16 +++++++++- 4 files changed, 20 insertions(+), 34 deletions(-) diff --git a/packages/docs/check-qwik-build.ts b/packages/docs/check-qwik-build.ts index 223a7236924..ebcecad8981 100644 --- a/packages/docs/check-qwik-build.ts +++ b/packages/docs/check-qwik-build.ts @@ -3,8 +3,9 @@ import fs from 'fs'; import path from 'path'; import { spawnSync } from 'child_process'; +import { fileURLToPath } from 'url'; -const __dirname = path.dirname(new URL(import.meta.url).pathname); +const __dirname = path.dirname(fileURLToPath(import.meta.url)); const qwikPkgDir = path.join(__dirname, '..', 'qwik', 'dist'); if (!fs.existsSync(path.join(qwikPkgDir, 'core.d.ts'))) { diff --git a/packages/docs/src/routes/docs/(qwikcity)/server$/index.mdx b/packages/docs/src/routes/docs/(qwikcity)/server$/index.mdx index 210025a12c1..dcb633b98f9 100644 --- a/packages/docs/src/routes/docs/(qwikcity)/server$/index.mdx +++ b/packages/docs/src/routes/docs/(qwikcity)/server$/index.mdx @@ -24,11 +24,6 @@ created_at: '2023-03-29T02:35:29Z' > `server$` can accept any number of arguments and return any value that can be serialized by Qwik, that includes primitives, objects, arrays, bigint, JSX nodes and even Promises, just to name a few. -<<<<<<< HEAD -Your new function will have the following signature: -`([AbortSignal, ] ...): Promise` -======= ->>>>>>> origin/main `AbortSignal` is optional, and allows you to cancel a long running request by terminating the connection. Your new function will have the following signature: @@ -184,19 +179,6 @@ Terminating the generator on the client side (e.g., by calling `.return()` on th import { component$, useSignal } from '@builder.io/qwik'; import { server$ } from '@builder.io/qwik-city'; -<<<<<<< HEAD -const stream = server$(async function* () { - // Creation of an array with 10 undefined values - const iterationRange = Array(10).fill().entries(); - - for (const [index] of iterationRange) { - // Yield returns the index during each iteration - yield index; - - // Waiting for 1 second before the next iteration - // This simulates a delay in the execution - await new Promise((resolve) => setTimeout(resolve, 1000)); -======= export const streamFromServer = server$( // Async Generator Function async function* () { @@ -211,7 +193,6 @@ export const streamFromServer = server$( // This simulates a delay in the execution await new Promise((resolve) => setTimeout(resolve, 1000)); } ->>>>>>> origin/main } ); @@ -222,15 +203,6 @@ export default component$(() => { return (
+ + + ); + }); + + const { vNode, document } = await render(, { debug }); + expect(vNode).toMatchVDOM( + + + +
+ {render === ssrRenderToDom ? '' : null} + + + {'DEFAULT '} + {'0'} + + + {render === ssrRenderToDom ? '' : null} +
+
+ +
+ + + {'START '} + {'0'} + + + {render === ssrRenderToDom ? '' : null} + + + {'END '} + {'0'} + + +
+
+ + +
+
+ ); + + await trigger(document.body, '#toggle', 'click'); + expect(vNode).toMatchVDOM( + + + +
+ {render === ssrRenderToDom ? '' : null} + + {render === ssrRenderToDom ? '' : null} +
+
+ +
+ + {render === ssrRenderToDom ? '' : null} + +
+
+ + +
+
+ ); + + await trigger(document.body, '#count', 'click'); + await trigger(document.body, '#toggle', 'click'); + + expect(vNode).toMatchVDOM( + + + +
+ {render === ssrRenderToDom ? '' : null} + + + {'DEFAULT '} + {'1'} + + + {render === ssrRenderToDom ? '' : null} +
+
+ +
+ + + {'START '} + {'1'} + + + {render === ssrRenderToDom ? '' : null} + + + {'END '} + {'1'} + + +
+
+ + +
+
+ ); + }); + it('should render to named slot in nested named slots', async () => { const NestedSlotCmp = component$(() => { return ( @@ -1107,8 +1242,7 @@ describe.each([ await trigger(document.body, '#reload', 'click'); }); - // TODO(slot): fix this test - it.skip('should not go into an infinity loop because of removing nodes from q:template', async () => { + it('should not go into an infinity loop because of removing nodes from q:template', async () => { const Projector = component$(() => { return (
@@ -1238,7 +1372,7 @@ describe.each([ style="width:24px;height:24px" xmlns="http://www.w3.org/2000/svg" > - {''} + diff --git a/starters/e2e/e2e.slot.e2e.ts b/starters/e2e/e2e.slot.e2e.ts index 91859ef10a2..750fb2f30b3 100644 --- a/starters/e2e/e2e.slot.e2e.ts +++ b/starters/e2e/e2e.slot.e2e.ts @@ -90,8 +90,7 @@ test.describe("slot", () => { }); }); - // TODO(v2): fix this - test.skip("should toggle content", async ({ page }) => { + test("should toggle content", async ({ page }) => { const content1 = page.locator("#btn1"); const content2 = page.locator("#btn2"); const content3 = page.locator("#btn3"); @@ -115,8 +114,7 @@ test.describe("slot", () => { }); }); - // TODO(v2): fix this - test.skip("should toggle content and buttons", async ({ page }) => { + test("should toggle content and buttons", async ({ page }) => { const content1 = page.locator("#btn1"); const content2 = page.locator("#btn2"); const content3 = page.locator("#btn3"); From eefc38b4f090ecf42cb12eaf5f537e0add964694 Mon Sep 17 00:00:00 2001 From: Naor Peled Date: Sun, 16 Jun 2024 00:46:54 +0300 Subject: [PATCH 5/5] docs(v2/ssr-container): add context to tag nesting property (#6552) --- packages/qwik/src/server/v2-ssr-container.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/qwik/src/server/v2-ssr-container.ts b/packages/qwik/src/server/v2-ssr-container.ts index 8928d0f18e3..d522f918202 100644 --- a/packages/qwik/src/server/v2-ssr-container.ts +++ b/packages/qwik/src/server/v2-ssr-container.ts @@ -127,6 +127,10 @@ class StringBufferWriter { } interface ContainerElementFrame { + /* + * Used during development mode to track the nesting of HTML tags + * in order provide error messages when the nesting is incorrect. + */ tagNesting: TagNesting; parent: ContainerElementFrame | null; /** Element name. */