From 1ae9d514b95aeaca036a2e25bdce1ca69e4bf2b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Mon, 10 Jan 2022 14:35:57 +0100 Subject: [PATCH] [IMP] component: use reactivity to allow shallow renderings With this commit, component only render child components if they have different props (shallow equality). Otherwise, we trust the reactivity system to make sure that all impacted components are updated --- src/app/app.ts | 8 +- src/app/template_helpers.ts | 4 +- src/compiler/code_generator.ts | 14 +- src/component/component.ts | 4 +- src/component/component_node.ts | 61 ++- src/component/fibers.ts | 8 +- src/index.ts | 2 + src/reactivity.ts | 2 +- .../__snapshots__/concurrency.test.ts.snap | 29 +- .../__snapshots__/error_handling.test.ts.snap | 38 +- .../__snapshots__/lifecycle.test.ts.snap | 2 +- .../__snapshots__/refs.test.ts.snap | 4 +- .../__snapshots__/rendering.test.ts.snap | 212 +++++++++ .../__snapshots__/slots.test.ts.snap | 329 +++++++++---- .../__snapshots__/t_set.test.ts.snap | 16 +- tests/components/basics.test.ts | 4 +- tests/components/concurrency.test.ts | 96 +++- tests/components/hooks.test.ts | 4 +- tests/components/lifecycle.test.ts | 10 +- tests/components/rendering.test.ts | 442 ++++++++++++++++++ tests/components/slots.test.ts | 56 +++ tests/components/t_props.test.ts | 3 +- tests/misc/__snapshots__/memo.test.ts.snap | 9 +- tests/misc/__snapshots__/portal.test.ts.snap | 3 +- tests/misc/portal.test.ts | 4 + tests/reactivity.test.ts | 2 +- 26 files changed, 1177 insertions(+), 189 deletions(-) create mode 100644 tests/components/__snapshots__/rendering.test.ts.snap create mode 100644 tests/components/rendering.test.ts diff --git a/src/app/app.ts b/src/app/app.ts index d0c7714e..be55213e 100644 --- a/src/app/app.ts +++ b/src/app/app.ts @@ -29,7 +29,7 @@ See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration export class App< T extends abstract new (...args: any) => any = any, - P = any, + P extends object = any, E = any > extends TemplateSet { static validateTarget = validateTarget; @@ -102,7 +102,11 @@ export class App< } } -export async function mount any = any, P = any, E = any>( +export async function mount< + T extends abstract new (...args: any) => any = any, + P extends object = any, + E = any +>( C: T & ComponentConstructor, target: HTMLElement, config: AppConfig & MountOptions = {} diff --git a/src/app/template_helpers.ts b/src/app/template_helpers.ts index 36de4dad..e167551c 100644 --- a/src/app/template_helpers.ts +++ b/src/app/template_helpers.ts @@ -2,6 +2,8 @@ import { BDom, multi, text, toggler } from "../blockdom"; import { validateProps } from "../component/props_validation"; import { Markup } from "../utils"; import { html } from "../blockdom/index"; +import { TARGET } from "../reactivity"; + /** * This file contains utility functions that will be injected in each template, * to perform various useful tasks in the compiled code. @@ -21,7 +23,7 @@ function callSlot( defaultContent?: (ctx: any, node: any, key: string) => BDom ): BDom { key = key + "__slot_" + name; - const slots = (ctx.props && ctx.props.slots) || {}; + const slots = ctx.props[TARGET].slots || {}; const { __render, __ctx, __scope } = slots[name] || {}; const slotScope = Object.create(__ctx || {}); if (__scope) { diff --git a/src/compiler/code_generator.ts b/src/compiler/code_generator.ts index 5d28f5c9..6fa9136c 100644 --- a/src/compiler/code_generator.ts +++ b/src/compiler/code_generator.ts @@ -1125,18 +1125,17 @@ export class CodeGenerator { } if (slotDef && !(ast.dynamicProps || hasSlotsProp)) { - props.push(`slots: ${slotDef}`); + this.helpers.add("markRaw"); + props.push(`slots: markRaw(${slotDef})`); } const propStr = `{${props.join(",")}}`; let propString = propStr; if (ast.dynamicProps) { - if (!props.length) { - propString = `Object.assign({}, ${compileExpr(ast.dynamicProps)})`; - } else { - propString = `Object.assign({}, ${compileExpr(ast.dynamicProps)}, ${propStr})`; - } + propString = `Object.assign({}, ${compileExpr(ast.dynamicProps)}${ + props.length ? ", " + propStr : "" + })`; } let propVar: string; @@ -1147,7 +1146,8 @@ export class CodeGenerator { } if (slotDef && (ast.dynamicProps || hasSlotsProp)) { - this.addLine(`${propVar!}.slots = Object.assign(${slotDef}, ${propVar!}.slots)`); + this.helpers.add("markRaw"); + this.addLine(`${propVar!}.slots = markRaw(Object.assign(${slotDef}, ${propVar!}.slots))`); } // cmap key diff --git a/src/component/component.ts b/src/component/component.ts index f1fbe001..220fb094 100644 --- a/src/component/component.ts +++ b/src/component/component.ts @@ -36,7 +36,7 @@ export class Component { setup() {} - render() { - this.__owl__.render(); + render(deep: boolean = false) { + this.__owl__.render(deep); } } diff --git a/src/component/component_node.ts b/src/component/component_node.ts index 78e155cb..eead3fc6 100644 --- a/src/component/component_node.ts +++ b/src/component/component_node.ts @@ -1,6 +1,6 @@ import type { App, Env } from "../app/app"; import { BDom, VNode } from "../blockdom"; -import { clearReactivesForCallback, NonReactive, Reactive, reactive } from "../reactivity"; +import { clearReactivesForCallback, Reactive, reactive, TARGET, NonReactive } from "../reactivity"; import { batched, Callback } from "../utils"; import { Component, ComponentConstructor } from "./component"; import { fibersInError, handleError } from "./error_handling"; @@ -58,14 +58,24 @@ export function useState(state: T): Reactive | NonReactive< // ----------------------------------------------------------------------------- // component function (used in compiled template code) // ----------------------------------------------------------------------------- +type Props = { [key: string]: any }; -export function component( - name: string | typeof Component, - props: any, +function arePropsDifferent(props1: Props, props2: Props): boolean { + for (let k in props1) { + if (props1[k] !== props2[k]) { + return true; + } + } + return Object.keys(props1).length !== Object.keys(props2).length; +} + +export function component

( + name: string | ComponentConstructor

, + props: P, key: string, ctx: ComponentNode, parent: any -): ComponentNode { +): ComponentNode

{ let node: any = ctx.children[key]; let isDynamic = typeof name !== "string"; @@ -83,7 +93,10 @@ export function component( const parentFiber = ctx.fiber!; if (node) { - node.updateAndRender(props, parentFiber); + const currentProps = node.component.props[TARGET]; + if (parentFiber.deep || arePropsDifferent(currentProps, props)) { + node.updateAndRender(props, parentFiber); + } } else { // new component let C; @@ -98,8 +111,7 @@ export function component( node = new ComponentNode(C, props, ctx.app, ctx); ctx.children[key] = node; - const fiber = makeChildFiber(node, parentFiber); - node.initiateRender(fiber); + node.initiateRender(new Fiber(node, parentFiber)); } return node; } @@ -110,7 +122,7 @@ export function component( type LifecycleHook = Function; -export class ComponentNode

implements VNode> { +export class ComponentNode

implements VNode> { el?: HTMLElement | Text | undefined; app: App; fiber: Fiber | null = null; @@ -141,6 +153,7 @@ export class ComponentNode

implements VNode implements VNode implements VNode f.call(component, props))); await prom; if (fiber !== this.fiber) { @@ -310,6 +335,14 @@ export class ComponentNode

implements VNode 0; this.bdom!.patch(this!.fiber!.bdom!, hasChildren); if (hasChildren) { diff --git a/src/component/fibers.ts b/src/component/fibers.ts index 4d98cbaa..47d95b64 100644 --- a/src/component/fibers.ts +++ b/src/component/fibers.ts @@ -16,9 +16,8 @@ export function makeRootFiber(node: ComponentNode): Fiber { let current = node.fiber; if (current) { let root = current.root!; - root.counter -= cancelFibers(current.children); + root.counter = root.counter + 1 - cancelFibers(current.children); current.children = []; - root.counter++; current.bdom = null; if (fibersInError.has(current)) { fibersInError.delete(current); @@ -34,7 +33,6 @@ export function makeRootFiber(node: ComponentNode): Fiber { if (node.patched.length) { fiber.patched.push(fiber); } - return fiber; } @@ -60,11 +58,13 @@ export class Fiber { parent: Fiber | null; children: Fiber[] = []; appliedToDom = false; + deep: boolean = false; constructor(node: ComponentNode, parent: Fiber | null) { this.node = node; this.parent = parent; if (parent) { + this.deep = parent.deep; const root = parent.root!; root.counter++; this.root = root; @@ -107,7 +107,7 @@ export class RootFiber extends Fiber { current = undefined; // Step 2: patching the dom - node.patch(); + node._patch(); this.locked = false; // Step 4: calling all mounted lifecycle hooks diff --git a/src/index.ts b/src/index.ts index cf045b8a..b901704c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,11 +14,13 @@ import { } from "./blockdom"; import { mainEventHandler } from "./component/handler"; import { Portal } from "./portal"; +import { markRaw } from "./reactivity"; export type { Reactive } from "./reactivity"; config.shouldNormalizeDom = false; config.mainEventHandler = mainEventHandler; (UTILS as any).Portal = Portal; +(UTILS as any).markRaw = markRaw; export const blockDom = { config, diff --git a/src/reactivity.ts b/src/reactivity.ts index 7fa124b9..2c783324 100644 --- a/src/reactivity.ts +++ b/src/reactivity.ts @@ -1,7 +1,7 @@ import { Callback } from "./utils"; // Allows to get the target of a Reactive (used for making a new Reactive from the underlying object) -const TARGET = Symbol("Target"); +export const TARGET = Symbol("Target"); // Escape hatch to prevent reactivity system to turn something into a reactive const SKIP = Symbol("Skip"); // Special key to subscribe to, to be notified of key creation/deletion diff --git a/tests/components/__snapshots__/concurrency.test.ts.snap b/tests/components/__snapshots__/concurrency.test.ts.snap index c47d6ade..03fb8164 100644 --- a/tests/components/__snapshots__/concurrency.test.ts.snap +++ b/tests/components/__snapshots__/concurrency.test.ts.snap @@ -1067,6 +1067,33 @@ exports[`delay willUpdateProps with rendering grandchild 4`] = ` }" `; +exports[`destroying/recreating a subcomponent, other scenario 1`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + let b2,b3; + b2 = text(\`parent\`); + if (ctx['state'].hasChild) { + b3 = component(\`Child\`, {}, key + \`__1\`, node, ctx); + } + return multi([b2, b3]); + } +}" +`; + +exports[`destroying/recreating a subcomponent, other scenario 2`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + return text(\`child\`); + } +}" +`; + exports[`destroying/recreating a subwidget with different props (if start is not over) 1`] = ` "function anonymous(bdom, helpers ) { @@ -1145,7 +1172,7 @@ exports[`properly behave when destroyed/unmounted while rendering 2`] = ` let block1 = createBlock(\`

\`); return function template(ctx, node, key = \\"\\") { - let b2 = component(\`SubChild\`, {}, key + \`__1\`, node, ctx); + let b2 = component(\`SubChild\`, {val: ctx['props'].val}, key + \`__1\`, node, ctx); return block1([], [b2]); } }" diff --git a/tests/components/__snapshots__/error_handling.test.ts.snap b/tests/components/__snapshots__/error_handling.test.ts.snap index c393dce3..1f6bae4a 100644 --- a/tests/components/__snapshots__/error_handling.test.ts.snap +++ b/tests/components/__snapshots__/error_handling.test.ts.snap @@ -100,6 +100,7 @@ exports[`can catch errors can catch an error in a component render function 1`] "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; let block1 = createBlock(\`
\`); @@ -108,7 +109,7 @@ exports[`can catch errors can catch an error in a component render function 1`] } return function template(ctx, node, key = \\"\\") { - let b3 = component(\`ErrorBoundary\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx); + let b3 = component(\`ErrorBoundary\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, ctx); return block1([], [b3]); } }" @@ -152,6 +153,7 @@ exports[`can catch errors can catch an error in the constructor call of a compon "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; let block1 = createBlock(\`
\`); @@ -160,7 +162,7 @@ exports[`can catch errors can catch an error in the constructor call of a compon } return function template(ctx, node, key = \\"\\") { - let b3 = component(\`ErrorBoundary\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx); + let b3 = component(\`ErrorBoundary\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, ctx); return block1([], [b3]); } }" @@ -190,6 +192,7 @@ exports[`can catch errors can catch an error in the constructor call of a compon "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; let block1 = createBlock(\`
\`); @@ -200,7 +203,7 @@ exports[`can catch errors can catch an error in the constructor call of a compon } return function template(ctx, node, key = \\"\\") { - let b5 = component(\`ErrorBoundary\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__3\`, node, ctx); + let b5 = component(\`ErrorBoundary\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__3\`, node, ctx); return block1([], [b5]); } }" @@ -269,6 +272,7 @@ exports[`can catch errors can catch an error in the initial call of a component "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; let block1 = createBlock(\`
\`); @@ -277,7 +281,7 @@ exports[`can catch errors can catch an error in the initial call of a component } return function template(ctx, node, key = \\"\\") { - let b3 = component(\`ErrorBoundary\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx); + let b3 = component(\`ErrorBoundary\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, ctx); return block1([], [b3]); } }" @@ -321,6 +325,7 @@ exports[`can catch errors can catch an error in the initial call of a component "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; let block1 = createBlock(\`
\`); @@ -331,7 +336,7 @@ exports[`can catch errors can catch an error in the initial call of a component return function template(ctx, node, key = \\"\\") { let b3; if (ctx['state'].flag) { - b3 = component(\`ErrorBoundary\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx); + b3 = component(\`ErrorBoundary\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, ctx); } return block1([], [b3]); } @@ -465,6 +470,7 @@ exports[`can catch errors can catch an error in the mounted call 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; let block1 = createBlock(\`
\`); @@ -473,7 +479,7 @@ exports[`can catch errors can catch an error in the mounted call 1`] = ` } return function template(ctx, node, key = \\"\\") { - let b3 = component(\`ErrorBoundary\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx); + let b3 = component(\`ErrorBoundary\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, ctx); return block1([], [b3]); } }" @@ -516,6 +522,7 @@ exports[`can catch errors can catch an error in the willPatch call 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; let block1 = createBlock(\`
\`); @@ -525,7 +532,7 @@ exports[`can catch errors can catch an error in the willPatch call 1`] = ` return function template(ctx, node, key = \\"\\") { let txt1 = ctx['state'].message; - let b3 = component(\`ErrorBoundary\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx); + let b3 = component(\`ErrorBoundary\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, ctx); return block1([txt1], [b3]); } }" @@ -569,6 +576,7 @@ exports[`can catch errors can catch an error in the willStart call 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; let block1 = createBlock(\`
\`); @@ -577,7 +585,7 @@ exports[`can catch errors can catch an error in the willStart call 1`] = ` } return function template(ctx, node, key = \\"\\") { - let b3 = component(\`ErrorBoundary\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx); + let b3 = component(\`ErrorBoundary\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, ctx); return block1([], [b3]); } }" @@ -620,6 +628,7 @@ exports[`can catch errors can catch an error origination from a child's willStar "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; let block1 = createBlock(\`
\`); @@ -630,7 +639,7 @@ exports[`can catch errors can catch an error origination from a child's willStar } return function template(ctx, node, key = \\"\\") { - let b5 = component(\`ErrorBoundary\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__3\`, node, ctx); + let b5 = component(\`ErrorBoundary\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__3\`, node, ctx); return block1([], [b5]); } }" @@ -733,7 +742,7 @@ exports[`can catch errors catching error, rethrow, render parent -- a main comp "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { prepareList, capture, withKey } = helpers; + let { prepareList, capture, markRaw, withKey } = helpers; function slot1(ctx, node, key = \\"\\") { let Comp1 = ctx['cp'].Comp; @@ -748,7 +757,7 @@ exports[`can catch errors catching error, rethrow, render parent -- a main comp let key1 = ctx['cp'].id; const v1 = ctx['cp']; const ctx1 = capture(ctx); - c_block1[i1] = withKey(component(\`ErrorHandler\`, {onError: ()=>this.cleanUp(v1.id),slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__2__\${key1}\`, node, ctx), key1); + c_block1[i1] = withKey(component(\`ErrorHandler\`, {onError: ()=>this.cleanUp(v1.id),slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__2__\${key1}\`, node, ctx), key1); } return list(c_block1); } @@ -808,7 +817,7 @@ exports[`can catch errors catching in child makes parent render 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { prepareList, capture, withKey } = helpers; + let { prepareList, capture, markRaw, withKey } = helpers; function slot1(ctx, node, key = \\"\\") { let Comp1 = ctx['elem'][1]; @@ -823,7 +832,7 @@ exports[`can catch errors catching in child makes parent render 1`] = ` let key1 = ctx['elem'][0]; const v1 = ctx['elem']; const ctx1 = capture(ctx); - c_block1[i1] = withKey(component(\`Catch\`, {onError: (_error)=>this.onError(v1[0],_error),slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__2__\${key1}\`, node, ctx), key1); + c_block1[i1] = withKey(component(\`Catch\`, {onError: (_error)=>this.onError(v1[0],_error),slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__2__\${key1}\`, node, ctx), key1); } return list(c_block1); } @@ -873,6 +882,7 @@ exports[`can catch errors error in mounted on a component with a sibling (proper "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; let block1 = createBlock(\`
\`); @@ -882,7 +892,7 @@ exports[`can catch errors error in mounted on a component with a sibling (proper return function template(ctx, node, key = \\"\\") { let b2 = component(\`OK\`, {}, key + \`__1\`, node, ctx); - let b4 = component(\`ErrorBoundary\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__3\`, node, ctx); + let b4 = component(\`ErrorBoundary\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__3\`, node, ctx); return block1([], [b2, b4]); } }" diff --git a/tests/components/__snapshots__/lifecycle.test.ts.snap b/tests/components/__snapshots__/lifecycle.test.ts.snap index d43ae107..ccc0413c 100644 --- a/tests/components/__snapshots__/lifecycle.test.ts.snap +++ b/tests/components/__snapshots__/lifecycle.test.ts.snap @@ -527,7 +527,7 @@ exports[`lifecycle hooks onWillRender 1`] = ` let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; return function template(ctx, node, key = \\"\\") { - return component(\`Child\`, {}, key + \`__1\`, node, ctx); + return component(\`Child\`, {someValue: ctx['state'].value}, key + \`__1\`, node, ctx); } }" `; diff --git a/tests/components/__snapshots__/refs.test.ts.snap b/tests/components/__snapshots__/refs.test.ts.snap index d3fbe34d..bb44afbe 100644 --- a/tests/components/__snapshots__/refs.test.ts.snap +++ b/tests/components/__snapshots__/refs.test.ts.snap @@ -62,7 +62,7 @@ exports[`refs refs are properly bound in slots 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture } = helpers; + let { capture, markRaw } = helpers; let block1 = createBlock(\`
\`); let block2 = createBlock(\`\`); @@ -77,7 +77,7 @@ exports[`refs refs are properly bound in slots 1`] = ` return function template(ctx, node, key = \\"\\") { let txt1 = ctx['state'].val; const ctx1 = capture(ctx); - let b3 = component(\`Dialog\`, {slots: {'footer': {__render: slot1, __ctx: ctx1}}}, key + \`__1\`, node, ctx); + let b3 = component(\`Dialog\`, {slots: markRaw({'footer': {__render: slot1, __ctx: ctx1}})}, key + \`__1\`, node, ctx); return block1([txt1], [b3]); } }" diff --git a/tests/components/__snapshots__/rendering.test.ts.snap b/tests/components/__snapshots__/rendering.test.ts.snap new file mode 100644 index 00000000..70dc1975 --- /dev/null +++ b/tests/components/__snapshots__/rendering.test.ts.snap @@ -0,0 +1,212 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`force render in case of existing render 1`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + return component(\`B\`, {val: ctx['state'].val}, key + \`__1\`, node, ctx); + } +}" +`; + +exports[`force render in case of existing render 2`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + let b2 = component(\`C\`, {}, key + \`__1\`, node, ctx); + let b3 = text(ctx['props'].val); + return multi([b2, b3]); + } +}" +`; + +exports[`force render in case of existing render 3`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + return text(\`C\`); + } +}" +`; + +exports[`rendering semantics can force a render to update sub tree 1`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + let b2 = text(ctx['state'].value); + let b3 = component(\`Child\`, {}, key + \`__1\`, node, ctx); + return multi([b2, b3]); + } +}" +`; + +exports[`rendering semantics can force a render to update sub tree 2`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + return text(\`child\`); + } +}" +`; + +exports[`rendering semantics can render a parent without rendering child 1`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + let b2 = text(ctx['state'].value); + let b3 = component(\`Child\`, {}, key + \`__1\`, node, ctx); + return multi([b2, b3]); + } +}" +`; + +exports[`rendering semantics can render a parent without rendering child 2`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + return text(\`child\`); + } +}" +`; + +exports[`rendering semantics props are reactive (nested prop) 1`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + return component(\`Child\`, {a: ctx['state']}, key + \`__1\`, node, ctx); + } +}" +`; + +exports[`rendering semantics props are reactive (nested prop) 2`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + return text(ctx['props'].a.b.c); + } +}" +`; + +exports[`rendering semantics props are reactive 1`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + return component(\`Child\`, {a: ctx['state']}, key + \`__1\`, node, ctx); + } +}" +`; + +exports[`rendering semantics props are reactive 2`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + return text(ctx['props'].a.b); + } +}" +`; + +exports[`rendering semantics render with deep=true followed by render with deep=false work as expected 1`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + let b2 = text(\`parent\`); + let b3 = text(ctx['state'].value); + let b4 = component(\`Child\`, {}, key + \`__1\`, node, ctx); + return multi([b2, b3, b4]); + } +}" +`; + +exports[`rendering semantics render with deep=true followed by render with deep=false work as expected 2`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + let b2 = text(\`child\`); + let b3 = text(ctx['env'].getValue()); + return multi([b2, b3]); + } +}" +`; + +exports[`rendering semantics rendering is atomic (for one subtree) 1`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + let b2 = text(ctx['state'].obj.val); + let b3 = component(\`B\`, {obj: ctx['state'].obj}, key + \`__1\`, node, ctx); + return multi([b2, b3]); + } +}" +`; + +exports[`rendering semantics rendering is atomic (for one subtree) 2`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + return component(\`C\`, {obj: ctx['props'].obj}, key + \`__1\`, node, ctx); + } +}" +`; + +exports[`rendering semantics rendering is atomic (for one subtree) 3`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + return text(ctx['props'].obj.val); + } +}" +`; + +exports[`rendering semantics works as expected for dynamic number of props 1`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + return component(\`Child\`, Object.assign({}, ctx['state']), key + \`__1\`, node, ctx); + } +}" +`; + +exports[`rendering semantics works as expected for dynamic number of props 2`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + return text(Object.keys(ctx['props']).length); + } +}" +`; diff --git a/tests/components/__snapshots__/slots.test.ts.snap b/tests/components/__snapshots__/slots.test.ts.snap index d97cd184..ccd3616d 100644 --- a/tests/components/__snapshots__/slots.test.ts.snap +++ b/tests/components/__snapshots__/slots.test.ts.snap @@ -37,7 +37,7 @@ exports[`slots can define and call slots 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture } = helpers; + let { capture, markRaw } = helpers; let block1 = createBlock(\`
\`); let block2 = createBlock(\`header\`); @@ -53,7 +53,7 @@ exports[`slots can define and call slots 1`] = ` return function template(ctx, node, key = \\"\\") { const ctx1 = capture(ctx); - let b4 = component(\`Dialog\`, {slots: {'header': {__render: slot1, __ctx: ctx1}, 'footer': {__render: slot2, __ctx: ctx1}}}, key + \`__1\`, node, ctx); + let b4 = component(\`Dialog\`, {slots: markRaw({'header': {__render: slot1, __ctx: ctx1}, 'footer': {__render: slot2, __ctx: ctx1}})}, key + \`__1\`, node, ctx); return block1([], [b4]); } }" @@ -79,7 +79,7 @@ exports[`slots can define and call slots with bound params 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture, bind } = helpers; + let { capture, bind, markRaw } = helpers; function slot1(ctx, node, key = \\"\\") { return text(\`abc\`); @@ -87,7 +87,7 @@ exports[`slots can define and call slots with bound params 1`] = ` return function template(ctx, node, key = \\"\\") { const ctx1 = capture(ctx); - return component(\`Child\`, {slots: {'abc': {__render: slot1, __ctx: ctx1, getValue: bind(ctx, ctx['getValue'])}}}, key + \`__1\`, node, ctx); + return component(\`Child\`, {slots: markRaw({'abc': {__render: slot1, __ctx: ctx1, getValue: bind(ctx, ctx['getValue'])}})}, key + \`__1\`, node, ctx); } }" `; @@ -110,7 +110,7 @@ exports[`slots can define and call slots with params 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture } = helpers; + let { capture, markRaw } = helpers; let block1 = createBlock(\`
\`); let block2 = createBlock(\`header\`); @@ -126,7 +126,7 @@ exports[`slots can define and call slots with params 1`] = ` return function template(ctx, node, key = \\"\\") { const ctx1 = capture(ctx); - let b4 = component(\`Dialog\`, {slots: {'header': {__render: slot1, __ctx: ctx1, param: ctx['var']}, 'footer': {__render: slot2, __ctx: ctx1, param: '5'}}}, key + \`__1\`, node, ctx); + let b4 = component(\`Dialog\`, {slots: markRaw({'header': {__render: slot1, __ctx: ctx1, param: ctx['var']}, 'footer': {__render: slot2, __ctx: ctx1, param: '5'}})}, key + \`__1\`, node, ctx); return block1([], [b4]); } }" @@ -154,6 +154,7 @@ exports[`slots can render node with t-ref and Component in same slot 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; let block2 = createBlock(\`
\`); @@ -166,7 +167,7 @@ exports[`slots can render node with t-ref and Component in same slot 1`] = ` } return function template(ctx, node, key = \\"\\") { - return component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx); + return component(\`Child\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, ctx); } }" `; @@ -276,6 +277,7 @@ exports[`slots content is the default slot (variation) 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; let block1 = createBlock(\`sts rocks\`); @@ -284,7 +286,7 @@ exports[`slots content is the default slot (variation) 1`] = ` } return function template(ctx, node, key = \\"\\") { - return component(\`Dialog\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx); + return component(\`Dialog\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, ctx); } }" `; @@ -305,6 +307,7 @@ exports[`slots content is the default slot 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; let block1 = createBlock(\`
\`); let block2 = createBlock(\`sts rocks\`); @@ -314,7 +317,7 @@ exports[`slots content is the default slot 1`] = ` } return function template(ctx, node, key = \\"\\") { - let b3 = component(\`Dialog\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx); + let b3 = component(\`Dialog\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, ctx); return block1([], [b3]); } }" @@ -339,7 +342,7 @@ exports[`slots default content is not rendered if named slot is provided 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture } = helpers; + let { capture, markRaw } = helpers; let block1 = createBlock(\`
\`); @@ -349,7 +352,7 @@ exports[`slots default content is not rendered if named slot is provided 1`] = ` return function template(ctx, node, key = \\"\\") { const ctx1 = capture(ctx); - let b3 = component(\`Dialog\`, {slots: {'header': {__render: slot1, __ctx: ctx1}}}, key + \`__1\`, node, ctx); + let b3 = component(\`Dialog\`, {slots: markRaw({'header': {__render: slot1, __ctx: ctx1}})}, key + \`__1\`, node, ctx); return block1([], [b3]); } }" @@ -378,6 +381,7 @@ exports[`slots default content is not rendered if slot is provided 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; let block1 = createBlock(\`
\`); @@ -386,7 +390,7 @@ exports[`slots default content is not rendered if slot is provided 1`] = ` } return function template(ctx, node, key = \\"\\") { - let b3 = component(\`Dialog\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx); + let b3 = component(\`Dialog\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, ctx); return block1([], [b3]); } }" @@ -415,7 +419,7 @@ exports[`slots default slot next to named slot, with default content 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture } = helpers; + let { capture, markRaw } = helpers; let block1 = createBlock(\`
\`); @@ -425,7 +429,7 @@ exports[`slots default slot next to named slot, with default content 1`] = ` return function template(ctx, node, key = \\"\\") { const ctx1 = capture(ctx); - let b3 = component(\`Dialog\`, {slots: {'footer': {__render: slot1, __ctx: ctx1}}}, key + \`__1\`, node, ctx); + let b3 = component(\`Dialog\`, {slots: markRaw({'footer': {__render: slot1, __ctx: ctx1}})}, key + \`__1\`, node, ctx); return block1([], [b3]); } }" @@ -459,13 +463,14 @@ exports[`slots default slot with params with - in it 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; function slot1(ctx, node, key = \\"\\") { return text(ctx['slotScope']['some-value']); } return function template(ctx, node, key = \\"\\") { - return component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx, __scope: \\"slotScope\\"}}}, key + \`__1\`, node, ctx); + return component(\`Child\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx, __scope: \\"slotScope\\"}})}, key + \`__1\`, node, ctx); } }" `; @@ -486,6 +491,7 @@ exports[`slots default slot with slot scope: shorthand syntax 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; function slot1(ctx, node, key = \\"\\") { let b2,b3; @@ -498,7 +504,7 @@ exports[`slots default slot with slot scope: shorthand syntax 1`] = ` } return function template(ctx, node, key = \\"\\") { - return component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx, __scope: \\"slotScope\\"}}}, key + \`__1\`, node, ctx); + return component(\`Child\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx, __scope: \\"slotScope\\"}})}, key + \`__1\`, node, ctx); } }" `; @@ -522,13 +528,14 @@ exports[`slots default slot work with text nodes (variation) 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; function slot1(ctx, node, key = \\"\\") { return text(\`sts rocks\`); } return function template(ctx, node, key = \\"\\") { - return component(\`Dialog\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx); + return component(\`Dialog\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, ctx); } }" `; @@ -549,6 +556,7 @@ exports[`slots default slot work with text nodes 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; let block1 = createBlock(\`
\`); @@ -557,7 +565,7 @@ exports[`slots default slot work with text nodes 1`] = ` } return function template(ctx, node, key = \\"\\") { - let b3 = component(\`Dialog\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx); + let b3 = component(\`Dialog\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, ctx); return block1([], [b3]); } }" @@ -582,7 +590,7 @@ exports[`slots dynamic t-slot call 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture } = helpers; + let { capture, markRaw } = helpers; let block1 = createBlock(\`
\`); let block3 = createBlock(\`

slot1

\`); @@ -601,7 +609,7 @@ exports[`slots dynamic t-slot call 1`] = ` return function template(ctx, node, key = \\"\\") { const ctx1 = capture(ctx); - let b6 = component(\`Toggler\`, {slots: {'slot1': {__render: slot1, __ctx: ctx1}, 'slot2': {__render: slot2, __ctx: ctx1}}}, key + \`__1\`, node, ctx); + let b6 = component(\`Toggler\`, {slots: markRaw({'slot1': {__render: slot1, __ctx: ctx1}, 'slot2': {__render: slot2, __ctx: ctx1}})}, key + \`__1\`, node, ctx); return block1([], [b6]); } }" @@ -628,7 +636,7 @@ exports[`slots dynamic t-slot call with default 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture } = helpers; + let { capture, markRaw } = helpers; let block1 = createBlock(\`
\`); let block3 = createBlock(\`

slot1

\`); @@ -647,7 +655,7 @@ exports[`slots dynamic t-slot call with default 1`] = ` return function template(ctx, node, key = \\"\\") { const ctx1 = capture(ctx); - let b6 = component(\`Toggler\`, {slots: {'slot1': {__render: slot1, __ctx: ctx1}, 'slot2': {__render: slot2, __ctx: ctx1}}}, key + \`__1\`, node, ctx); + let b6 = component(\`Toggler\`, {slots: markRaw({'slot1': {__render: slot1, __ctx: ctx1}, 'slot2': {__render: slot2, __ctx: ctx1}})}, key + \`__1\`, node, ctx); return block1([], [b6]); } }" @@ -677,13 +685,14 @@ exports[`slots fun: two calls to the same slot 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; function slot1(ctx, node, key = \\"\\") { return text(\`some text\`); } return function template(ctx, node, key = \\"\\") { - return component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx); + return component(\`Child\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, ctx); } }" `; @@ -732,10 +741,120 @@ exports[`slots missing slots are ignored 2`] = ` }" `; +exports[`slots mix of slots, t-call, t-call with body, and giving own props child 1`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + return component(\`P\`, {}, key + \`__1\`, node, ctx); + } +}" +`; + +exports[`slots mix of slots, t-call, t-call with body, and giving own props child 2`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + let block2 = createBlock(\`\`); + + return function template(ctx, node, key = \\"\\") { + let hdlr1 = [ctx['inc'], ctx]; + let b2 = block2([hdlr1]); + let b3 = component(\`A\`, {number: ctx['state'].number}, key + \`__1\`, node, ctx); + return multi([b2, b3]); + } +}" +`; + +exports[`slots mix of slots, t-call, t-call with body, and giving own props child 3`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { capture, getTemplate, markRaw } = helpers; + const callTemplate_1 = getTemplate(\`__template__1002\`); + + function slot1(ctx, node, key = \\"\\") { + let b2 = text(\`[A]\`); + let b3 = callTemplate_1.call(this, ctx, node, key + \`__1\`); + return multi([b2, b3]); + } + + return function template(ctx, node, key = \\"\\") { + const ctx1 = capture(ctx); + return component(\`B\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__2\`, node, ctx); + } +}" +`; + +exports[`slots mix of slots, t-call, t-call with body, and giving own props child 4`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { isBoundary, withDefault, setContextValue, getTemplate } = helpers; + const callTemplate_1 = getTemplate(\`__template__1001\`); + + return function template(ctx, node, key = \\"\\") { + ctx = Object.create(ctx); + ctx[isBoundary] = 1 + let b2 = text(\`[sub1] \`); + setContextValue(ctx, \\"dummy\\", ctx['validate']); + ctx = Object.create(ctx); + ctx[isBoundary] = 1; + setContextValue(ctx, \\"v\\", ctx['props'].number); + let b3 = callTemplate_1.call(this, ctx, node, key + \`__1\`); + return multi([b2, b3]); + } +}" +`; + +exports[`slots mix of slots, t-call, t-call with body, and giving own props child 5`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + let b2 = text(\`[sub2\`); + let b3 = text(ctx['v']); + let b4 = text(\`]\`); + return multi([b2, b3, b4]); + } +}" +`; + +exports[`slots mix of slots, t-call, t-call with body, and giving own props child 6`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + let b2 = text(\`[B]\`); + let b3 = component(\`C\`, {slots: ctx['props'].slots}, key + \`__1\`, node, ctx); + return multi([b2, b3]); + } +}" +`; + +exports[`slots mix of slots, t-call, t-call with body, and giving own props child 7`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { callSlot } = helpers; + + return function template(ctx, node, key = \\"\\") { + let b2 = text(\`[C]\`); + let b3 = callSlot(ctx, node, key, 'default', false, {}); + return multi([b2, b3]); + } +}" +`; + exports[`slots multiple roots are allowed in a default slot 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; let block1 = createBlock(\`
\`); let block3 = createBlock(\`sts\`); @@ -748,7 +867,7 @@ exports[`slots multiple roots are allowed in a default slot 1`] = ` } return function template(ctx, node, key = \\"\\") { - let b5 = component(\`Dialog\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx); + let b5 = component(\`Dialog\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, ctx); return block1([], [b5]); } }" @@ -773,7 +892,7 @@ exports[`slots multiple roots are allowed in a named slot 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture } = helpers; + let { capture, markRaw } = helpers; let block1 = createBlock(\`
\`); let block3 = createBlock(\`sts\`); @@ -787,7 +906,7 @@ exports[`slots multiple roots are allowed in a named slot 1`] = ` return function template(ctx, node, key = \\"\\") { const ctx1 = capture(ctx); - let b5 = component(\`Dialog\`, {slots: {'content': {__render: slot1, __ctx: ctx1}}}, key + \`__1\`, node, ctx); + let b5 = component(\`Dialog\`, {slots: markRaw({'content': {__render: slot1, __ctx: ctx1}})}, key + \`__1\`, node, ctx); return block1([], [b5]); } }" @@ -812,7 +931,7 @@ exports[`slots multiple slots containing components 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture } = helpers; + let { capture, markRaw } = helpers; function slot1(ctx, node, key = \\"\\") { return component(\`C\`, {val: 1}, key + \`__1\`, node, ctx); @@ -824,7 +943,7 @@ exports[`slots multiple slots containing components 1`] = ` return function template(ctx, node, key = \\"\\") { const ctx1 = capture(ctx); - return component(\`B\`, {slots: {'s1': {__render: slot1, __ctx: ctx1}, 's2': {__render: slot2, __ctx: ctx1}}}, key + \`__3\`, node, ctx); + return component(\`B\`, {slots: markRaw({'s1': {__render: slot1, __ctx: ctx1}, 's2': {__render: slot2, __ctx: ctx1}})}, key + \`__3\`, node, ctx); } }" `; @@ -863,7 +982,7 @@ exports[`slots named slot inside slot 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture } = helpers; + let { capture, markRaw } = helpers; let block1 = createBlock(\`
\`); let block2 = createBlock(\`

A

\`); @@ -876,7 +995,7 @@ exports[`slots named slot inside slot 1`] = ` function slot2(ctx, node, key = \\"\\") { const ctx2 = capture(ctx); - return component(\`Child\`, {slots: {'brol': {__render: slot3, __ctx: ctx2}}}, key + \`__1\`, node, ctx); + return component(\`Child\`, {slots: markRaw({'brol': {__render: slot3, __ctx: ctx2}})}, key + \`__1\`, node, ctx); } function slot3(ctx, node, key = \\"\\") { @@ -886,7 +1005,7 @@ exports[`slots named slot inside slot 1`] = ` return function template(ctx, node, key = \\"\\") { const ctx1 = capture(ctx); - let b5 = component(\`Child\`, {slots: {'brol': {__render: slot1, __ctx: ctx1}, 'default': {__render: slot2, __ctx: ctx1}}}, key + \`__2\`, node, ctx); + let b5 = component(\`Child\`, {slots: markRaw({'brol': {__render: slot1, __ctx: ctx1}, 'default': {__render: slot2, __ctx: ctx1}})}, key + \`__2\`, node, ctx); return block1([], [b5]); } }" @@ -912,7 +1031,7 @@ exports[`slots named slot inside slot, part 3 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture } = helpers; + let { capture, markRaw } = helpers; let block1 = createBlock(\`
\`); let block2 = createBlock(\`

A

\`); @@ -925,7 +1044,7 @@ exports[`slots named slot inside slot, part 3 1`] = ` function slot2(ctx, node, key = \\"\\") { const ctx2 = capture(ctx); - return component(\`Child\`, {slots: {'brol': {__render: slot3, __ctx: ctx2}}}, key + \`__1\`, node, ctx); + return component(\`Child\`, {slots: markRaw({'brol': {__render: slot3, __ctx: ctx2}})}, key + \`__1\`, node, ctx); } function slot3(ctx, node, key = \\"\\") { @@ -935,7 +1054,7 @@ exports[`slots named slot inside slot, part 3 1`] = ` return function template(ctx, node, key = \\"\\") { const ctx1 = capture(ctx); - let b5 = component(\`Child\`, {slots: {'brol': {__render: slot1, __ctx: ctx1}, 'default': {__render: slot2, __ctx: ctx1}}}, key + \`__2\`, node, ctx); + let b5 = component(\`Child\`, {slots: markRaw({'brol': {__render: slot1, __ctx: ctx1}, 'default': {__render: slot2, __ctx: ctx1}})}, key + \`__2\`, node, ctx); return block1([], [b5]); } }" @@ -994,7 +1113,7 @@ exports[`slots named slots inside slot, again 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture } = helpers; + let { capture, markRaw } = helpers; let block1 = createBlock(\`
\`); let block2 = createBlock(\`

A

\`); @@ -1007,7 +1126,7 @@ exports[`slots named slots inside slot, again 1`] = ` function slot2(ctx, node, key = \\"\\") { const ctx2 = capture(ctx); - return component(\`Child\`, {slots: {'brol2': {__render: slot3, __ctx: ctx2}}}, key + \`__1\`, node, ctx); + return component(\`Child\`, {slots: markRaw({'brol2': {__render: slot3, __ctx: ctx2}})}, key + \`__1\`, node, ctx); } function slot3(ctx, node, key = \\"\\") { @@ -1017,7 +1136,7 @@ exports[`slots named slots inside slot, again 1`] = ` return function template(ctx, node, key = \\"\\") { const ctx1 = capture(ctx); - let b5 = component(\`Child\`, {slots: {'brol1': {__render: slot1, __ctx: ctx1}, 'default': {__render: slot2, __ctx: ctx1}}}, key + \`__2\`, node, ctx); + let b5 = component(\`Child\`, {slots: markRaw({'brol1': {__render: slot1, __ctx: ctx1}, 'default': {__render: slot2, __ctx: ctx1}})}, key + \`__2\`, node, ctx); return block1([], [b5]); } }" @@ -1052,11 +1171,12 @@ exports[`slots nested slots in same template 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; let block1 = createBlock(\`\`); function slot1(ctx, node, key = \\"\\") { - return component(\`Child2\`, {slots: {'default': {__render: slot2, __ctx: ctx}}}, key + \`__2\`, node, ctx); + return component(\`Child2\`, {slots: markRaw({'default': {__render: slot2, __ctx: ctx}})}, key + \`__2\`, node, ctx); } function slot2(ctx, node, key = \\"\\") { @@ -1064,7 +1184,7 @@ exports[`slots nested slots in same template 1`] = ` } return function template(ctx, node, key = \\"\\") { - let b4 = component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__3\`, node, ctx); + let b4 = component(\`Child\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__3\`, node, ctx); return block1([], [b4]); } }" @@ -1117,13 +1237,14 @@ exports[`slots nested slots: evaluation context and parented relationship 1`] = "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; function slot1(ctx, node, key = \\"\\") { return component(\`Slot\`, {val: ctx['state'].val}, key + \`__1\`, node, ctx); } return function template(ctx, node, key = \\"\\") { - return component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx); + return component(\`Child\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, ctx); } }" `; @@ -1132,14 +1253,14 @@ exports[`slots nested slots: evaluation context and parented relationship 2`] = "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { callSlot } = helpers; + let { callSlot, markRaw } = helpers; function slot1(ctx, node, key = \\"\\") { return callSlot(ctx, node, key, 'default', false, {}); } return function template(ctx, node, key = \\"\\") { - return component(\`GrandChild\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx); + return component(\`GrandChild\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, ctx); } }" `; @@ -1203,13 +1324,14 @@ exports[`slots simple default slot 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; function slot1(ctx, node, key = \\"\\") { return text(\`some text\`); } return function template(ctx, node, key = \\"\\") { - return component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx); + return component(\`Child\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, ctx); } }" `; @@ -1233,6 +1355,7 @@ exports[`slots simple default slot with params 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; function slot1(ctx, node, key = \\"\\") { let b2,b3; @@ -1245,7 +1368,7 @@ exports[`slots simple default slot with params 1`] = ` } return function template(ctx, node, key = \\"\\") { - return component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx); + return component(\`Child\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, ctx); } }" `; @@ -1269,13 +1392,14 @@ exports[`slots simple default slot with params and bound function 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; function slot1(ctx, node, key = \\"\\") { return text(ctx['slotScope'].fn()); } return function template(ctx, node, key = \\"\\") { - return component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx, __scope: \\"slotScope\\"}}}, key + \`__1\`, node, ctx); + return component(\`Child\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx, __scope: \\"slotScope\\"}})}, key + \`__1\`, node, ctx); } }" `; @@ -1296,13 +1420,14 @@ exports[`slots simple default slot, variation 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; function slot1(ctx, node, key = \\"\\") { return text(\`some text\`); } return function template(ctx, node, key = \\"\\") { - return component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx); + return component(\`Child\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, ctx); } }" `; @@ -1323,7 +1448,7 @@ exports[`slots simple slot with slot scope 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture } = helpers; + let { capture, markRaw } = helpers; function slot1(ctx, node, key = \\"\\") { let b2,b3; @@ -1337,7 +1462,7 @@ exports[`slots simple slot with slot scope 1`] = ` return function template(ctx, node, key = \\"\\") { const ctx1 = capture(ctx); - return component(\`Child\`, {slots: {'slotName': {__render: slot1, __ctx: ctx1, __scope: \\"slotScope\\"}}}, key + \`__1\`, node, ctx); + return component(\`Child\`, {slots: markRaw({'slotName': {__render: slot1, __ctx: ctx1, __scope: \\"slotScope\\"}})}, key + \`__1\`, node, ctx); } }" `; @@ -1361,7 +1486,7 @@ exports[`slots slot and (inline) t-call 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture, getTemplate } = helpers; + let { capture, getTemplate, markRaw } = helpers; const callTemplate_1 = getTemplate(\`__template__999\`); let block1 = createBlock(\`
\`); @@ -1372,7 +1497,7 @@ exports[`slots slot and (inline) t-call 1`] = ` return function template(ctx, node, key = \\"\\") { const ctx1 = capture(ctx); - let b3 = component(\`Dialog\`, {slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__2\`, node, ctx); + let b3 = component(\`Dialog\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__2\`, node, ctx); return block1([], [b3]); } }" @@ -1410,7 +1535,7 @@ exports[`slots slot and t-call 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture, getTemplate } = helpers; + let { capture, getTemplate, markRaw } = helpers; const callTemplate_1 = getTemplate(\`__template__999\`); let block1 = createBlock(\`
\`); @@ -1421,7 +1546,7 @@ exports[`slots slot and t-call 1`] = ` return function template(ctx, node, key = \\"\\") { const ctx1 = capture(ctx); - let b3 = component(\`Dialog\`, {slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__2\`, node, ctx); + let b3 = component(\`Dialog\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__2\`, node, ctx); return block1([], [b3]); } }" @@ -1459,6 +1584,7 @@ exports[`slots slot and t-esc 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; let block1 = createBlock(\`
\`); @@ -1467,7 +1593,7 @@ exports[`slots slot and t-esc 1`] = ` } return function template(ctx, node, key = \\"\\") { - let b3 = component(\`Dialog\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx); + let b3 = component(\`Dialog\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, ctx); return block1([], [b3]); } }" @@ -1492,6 +1618,7 @@ exports[`slots slot are properly rendered if inner props are changed 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; let block1 = createBlock(\`
\`); @@ -1502,7 +1629,7 @@ exports[`slots slot are properly rendered if inner props are changed 1`] = ` return function template(ctx, node, key = \\"\\") { let hdlr1 = [ctx['inc'], ctx]; let txt1 = ctx['state'].val; - let b3 = component(\`GenericComponent\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx); + let b3 = component(\`GenericComponent\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, ctx); return block1([hdlr1, txt1], [b3]); } }" @@ -1541,13 +1668,14 @@ exports[`slots slot content has different key from other content -- dynamic slot "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; function slot1(ctx, node, key = \\"\\") { return component(\`Child\`, {parent: 'Parent'}, key + \`__1\`, node, ctx); } return function template(ctx, node, key = \\"\\") { - return component(\`SlotDisplay\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx); + return component(\`SlotDisplay\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, ctx); } }" `; @@ -1585,13 +1713,14 @@ exports[`slots slot content has different key from other content -- static slot "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; function slot1(ctx, node, key = \\"\\") { return component(\`Child\`, {parent: 'Parent'}, key + \`__1\`, node, ctx); } return function template(ctx, node, key = \\"\\") { - return component(\`SlotDisplay\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx); + return component(\`SlotDisplay\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, ctx); } }" `; @@ -1628,7 +1757,7 @@ exports[`slots slot content is bound to caller (variation) 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture, isBoundary, withDefault, setContextValue } = helpers; + let { capture, isBoundary, withDefault, setContextValue, markRaw } = helpers; let block1 = createBlock(\`\`); @@ -1642,7 +1771,7 @@ exports[`slots slot content is bound to caller (variation) 1`] = ` return function template(ctx, node, key = \\"\\") { const ctx1 = capture(ctx); - return component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__1\`, node, ctx); + return component(\`Child\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__1\`, node, ctx); } }" `; @@ -1666,6 +1795,7 @@ exports[`slots slot content is bound to caller 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; let block1 = createBlock(\`\`); @@ -1675,7 +1805,7 @@ exports[`slots slot content is bound to caller 1`] = ` } return function template(ctx, node, key = \\"\\") { - return component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx); + return component(\`Child\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, ctx); } }" `; @@ -1699,6 +1829,7 @@ exports[`slots slot preserves properly parented relationship 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; let block1 = createBlock(\`
\`); @@ -1707,7 +1838,7 @@ exports[`slots slot preserves properly parented relationship 1`] = ` } return function template(ctx, node, key = \\"\\") { - let b3 = component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx); + let b3 = component(\`Child\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, ctx); return block1([], [b3]); } }" @@ -1740,7 +1871,7 @@ exports[`slots slot preserves properly parented relationship, even through t-cal "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture, getTemplate } = helpers; + let { capture, getTemplate, markRaw } = helpers; const callTemplate_1 = getTemplate(\`sub\`); let block1 = createBlock(\`
\`); @@ -1751,7 +1882,7 @@ exports[`slots slot preserves properly parented relationship, even through t-cal return function template(ctx, node, key = \\"\\") { const ctx1 = capture(ctx); - let b3 = component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__2\`, node, ctx); + let b3 = component(\`Child\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__2\`, node, ctx); return block1([], [b3]); } }" @@ -1795,13 +1926,14 @@ exports[`slots slots and wrapper components 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; function slot1(ctx, node, key = \\"\\") { return text(\`hey\`); } return function template(ctx, node, key = \\"\\") { - return component(\`Link\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx); + return component(\`Link\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, ctx); } }" `; @@ -1859,7 +1991,7 @@ exports[`slots slots are rendered with proper context 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture } = helpers; + let { capture, markRaw } = helpers; let block1 = createBlock(\`
\`); let block2 = createBlock(\`\`); @@ -1872,7 +2004,7 @@ exports[`slots slots are rendered with proper context 1`] = ` return function template(ctx, node, key = \\"\\") { let txt1 = ctx['state'].val; const ctx1 = capture(ctx); - let b3 = component(\`Dialog\`, {slots: {'footer': {__render: slot1, __ctx: ctx1}}}, key + \`__1\`, node, ctx); + let b3 = component(\`Dialog\`, {slots: markRaw({'footer': {__render: slot1, __ctx: ctx1}})}, key + \`__1\`, node, ctx); return block1([txt1], [b3]); } }" @@ -1897,7 +2029,7 @@ exports[`slots slots are rendered with proper context, part 2 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { prepareList, capture, withKey } = helpers; + let { prepareList, capture, markRaw, withKey } = helpers; let block1 = createBlock(\`
\`); let block3 = createBlock(\`
  • \`); @@ -1915,7 +2047,7 @@ exports[`slots slots are rendered with proper context, part 2 1`] = ` ctx[\`user\`] = v_block2[i1]; let key1 = ctx['user'].id; const ctx1 = capture(ctx); - let b7 = component(\`Link\`, {to: '/user/'+ctx['user'].id,slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx); + let b7 = component(\`Link\`, {to: '/user/'+ctx['user'].id,slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__1__\${key1}\`, node, ctx); c_block2[i1] = withKey(block3([], [b7]), key1); } let b2 = list(c_block2); @@ -1944,7 +2076,7 @@ exports[`slots slots are rendered with proper context, part 3 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { prepareList, isBoundary, withDefault, setContextValue, capture, withKey } = helpers; + let { prepareList, isBoundary, withDefault, setContextValue, capture, markRaw, withKey } = helpers; let block1 = createBlock(\`
    \`); let block3 = createBlock(\`
  • \`); @@ -1963,7 +2095,7 @@ exports[`slots slots are rendered with proper context, part 3 1`] = ` let key1 = ctx['user'].id; setContextValue(ctx, \\"userdescr\\", 'User '+ctx['user'].name); const ctx1 = capture(ctx); - let b5 = component(\`Link\`, {to: '/user/'+ctx['user'].id,slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx); + let b5 = component(\`Link\`, {to: '/user/'+ctx['user'].id,slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__1__\${key1}\`, node, ctx); c_block2[i1] = withKey(block3([], [b5]), key1); } let b2 = list(c_block2); @@ -1992,7 +2124,7 @@ exports[`slots slots are rendered with proper context, part 4 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { isBoundary, withDefault, setContextValue, capture } = helpers; + let { isBoundary, withDefault, setContextValue, capture, markRaw } = helpers; let block1 = createBlock(\`
    \`); @@ -2005,7 +2137,7 @@ exports[`slots slots are rendered with proper context, part 4 1`] = ` ctx[isBoundary] = 1 setContextValue(ctx, \\"userdescr\\", 'User '+ctx['state'].user.name); const ctx1 = capture(ctx); - let b3 = component(\`Link\`, {to: '/user/'+ctx['state'].user.id,slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__1\`, node, ctx); + let b3 = component(\`Link\`, {to: '/user/'+ctx['state'].user.id,slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__1\`, node, ctx); return block1([], [b3]); } }" @@ -2031,7 +2163,7 @@ exports[`slots slots in slots, with vars 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { isBoundary, withDefault, setContextValue, capture } = helpers; + let { isBoundary, withDefault, setContextValue, capture, markRaw } = helpers; let block1 = createBlock(\`
    \`); let block2 = createBlock(\`

    hey

    \`); @@ -2046,7 +2178,7 @@ exports[`slots slots in slots, with vars 1`] = ` ctx[isBoundary] = 1 setContextValue(ctx, \\"test\\", ctx['state'].name); const ctx1 = capture(ctx); - let b3 = component(\`A\`, {slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__1\`, node, ctx); + let b3 = component(\`A\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__1\`, node, ctx); return block1([], [b3]); } }" @@ -2056,7 +2188,7 @@ exports[`slots slots in slots, with vars 2`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { callSlot } = helpers; + let { callSlot, markRaw } = helpers; let block1 = createBlock(\`
    \`); @@ -2065,7 +2197,7 @@ exports[`slots slots in slots, with vars 2`] = ` } return function template(ctx, node, key = \\"\\") { - let b3 = component(\`B\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx); + let b3 = component(\`B\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, ctx); return block1([], [b3]); } }" @@ -2090,7 +2222,7 @@ exports[`slots slots in t-foreach and re-rendering 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { prepareList, capture, withKey } = helpers; + let { prepareList, capture, markRaw, withKey } = helpers; let block1 = createBlock(\`
    \`); @@ -2106,7 +2238,7 @@ exports[`slots slots in t-foreach and re-rendering 1`] = ` ctx[\`n_index\`] = i1; let key1 = ctx['n_index']; const ctx1 = capture(ctx); - c_block2[i1] = withKey(component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx), key1); + c_block2[i1] = withKey(component(\`Child\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__1__\${key1}\`, node, ctx), key1); } let b2 = list(c_block2); return block1([], [b2]); @@ -2134,7 +2266,7 @@ exports[`slots slots in t-foreach in t-foreach 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { prepareList, capture, withKey } = helpers; + let { prepareList, capture, markRaw, withKey } = helpers; let block1 = createBlock(\`
    \`); let block4 = createBlock(\`
    \`); @@ -2160,7 +2292,7 @@ exports[`slots slots in t-foreach in t-foreach 1`] = ` ctx[\`node2\`] = v_block6[i2]; let key2 = ctx['node2'].key; const ctx1 = capture(ctx); - c_block6[i2] = withKey(component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__1__\${key1}__\${key2}\`, node, ctx), key2); + c_block6[i2] = withKey(component(\`Child\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__1__\${key1}__\${key2}\`, node, ctx), key2); } ctx = ctx.__proto__; let b6 = list(c_block6); @@ -2192,7 +2324,7 @@ exports[`slots slots in t-foreach with t-set and re-rendering 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { prepareList, isBoundary, withDefault, setContextValue, capture, withKey } = helpers; + let { prepareList, isBoundary, withDefault, setContextValue, capture, markRaw, withKey } = helpers; let block1 = createBlock(\`
    \`); @@ -2211,7 +2343,7 @@ exports[`slots slots in t-foreach with t-set and re-rendering 1`] = ` let key1 = ctx['n_index']; setContextValue(ctx, \\"dummy\\", ctx['n_index']); const ctx1 = capture(ctx); - c_block2[i1] = withKey(component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx), key1); + c_block2[i1] = withKey(component(\`Child\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__1__\${key1}\`, node, ctx), key1); } let b2 = list(c_block2); return block1([], [b2]); @@ -2239,7 +2371,7 @@ exports[`slots t-debug on a t-set-slot (defining a slot) 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture } = helpers; + let { capture, markRaw } = helpers; let block1 = createBlock(\`
    \`); @@ -2250,7 +2382,7 @@ exports[`slots t-debug on a t-set-slot (defining a slot) 1`] = ` return function template(ctx, node, key = \\"\\") { const ctx1 = capture(ctx); - let b3 = component(\`Dialog\`, {slots: {'content': {__render: slot1, __ctx: ctx1}}}, key + \`__1\`, node, ctx); + let b3 = component(\`Dialog\`, {slots: markRaw({'content': {__render: slot1, __ctx: ctx1}})}, key + \`__1\`, node, ctx); return block1([], [b3]); } }" @@ -2275,7 +2407,7 @@ exports[`slots t-set t-value in a slot 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture, isBoundary, withDefault, setContextValue } = helpers; + let { capture, isBoundary, withDefault, setContextValue, markRaw } = helpers; let block1 = createBlock(\`
    \`); @@ -2288,7 +2420,7 @@ exports[`slots t-set t-value in a slot 1`] = ` return function template(ctx, node, key = \\"\\") { const ctx1 = capture(ctx); - let b3 = component(\`Dialog\`, {slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__1\`, node, ctx); + let b3 = component(\`Dialog\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__1\`, node, ctx); return block1([], [b3]); } }" @@ -2313,7 +2445,7 @@ exports[`slots t-slot in recursive templates 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture, prepareList, isBoundary, withDefault, setContextValue, getTemplate, withKey } = helpers; + let { capture, prepareList, isBoundary, withDefault, setContextValue, getTemplate, withKey, markRaw } = helpers; const callTemplate_1 = getTemplate(\`_test_recursive_template\`); function slot1(ctx, node, key = \\"\\") { @@ -2349,7 +2481,7 @@ exports[`slots t-slot in recursive templates 1`] = ` return function template(ctx, node, key = \\"\\") { const ctx1 = capture(ctx); - return component(\`Wrapper\`, {slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__2\`, node, ctx); + return component(\`Wrapper\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__2\`, node, ctx); } }" `; @@ -2373,6 +2505,7 @@ exports[`slots t-slot nested within another slot 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; let block1 = createBlock(\`\`); @@ -2381,7 +2514,7 @@ exports[`slots t-slot nested within another slot 1`] = ` } return function template(ctx, node, key = \\"\\") { - let b3 = component(\`Dialog\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx); + let b3 = component(\`Dialog\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, ctx); return block1([], [b3]); } }" @@ -2391,12 +2524,12 @@ exports[`slots t-slot nested within another slot 2`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { callSlot } = helpers; + let { callSlot, markRaw } = helpers; let block1 = createBlock(\`\`); function slot1(ctx, node, key = \\"\\") { - return component(\`Portal\`, {slots: {'default': {__render: slot2, __ctx: ctx}}}, key + \`__1\`, node, ctx); + return component(\`Portal\`, {slots: markRaw({'default': {__render: slot2, __ctx: ctx}})}, key + \`__1\`, node, ctx); } function slot2(ctx, node, key = \\"\\") { @@ -2404,7 +2537,7 @@ exports[`slots t-slot nested within another slot 2`] = ` } return function template(ctx, node, key = \\"\\") { - let b4 = component(\`Modal\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx); + let b4 = component(\`Modal\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, ctx); return block1([], [b4]); } }" @@ -2457,6 +2590,7 @@ exports[`slots t-slot scope context 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; let block1 = createBlock(\`\`); @@ -2465,7 +2599,7 @@ exports[`slots t-slot scope context 1`] = ` } return function template(ctx, node, key = \\"\\") { - return component(\`Dialog\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx); + return component(\`Dialog\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, ctx); } }" `; @@ -2474,7 +2608,7 @@ exports[`slots t-slot scope context 2`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { callSlot } = helpers; + let { callSlot, markRaw } = helpers; let block1 = createBlock(\`
    \`); @@ -2485,7 +2619,7 @@ exports[`slots t-slot scope context 2`] = ` } return function template(ctx, node, key = \\"\\") { - return component(\`Wrapper\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx); + return component(\`Wrapper\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, ctx); } }" `; @@ -2506,7 +2640,7 @@ exports[`slots t-slot within dynamic t-call 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture, call } = helpers; + let { capture, call, markRaw } = helpers; let block1 = createBlock(\`
    \`); @@ -2517,7 +2651,7 @@ exports[`slots t-slot within dynamic t-call 1`] = ` return function template(ctx, node, key = \\"\\") { const ctx1 = capture(ctx); - let b3 = component(\`Slotted\`, {slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__2\`, node, ctx); + let b3 = component(\`Slotted\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__2\`, node, ctx); return block1([], [b3]); } }" @@ -2569,6 +2703,7 @@ exports[`slots template can just return a slot 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; let block1 = createBlock(\`
    \`); @@ -2577,7 +2712,7 @@ exports[`slots template can just return a slot 1`] = ` } return function template(ctx, node, key = \\"\\") { - let b3 = component(\`SlotComponent\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx); + let b3 = component(\`SlotComponent\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, ctx); return block1([], [b3]); } }" diff --git a/tests/components/__snapshots__/t_set.test.ts.snap b/tests/components/__snapshots__/t_set.test.ts.snap index 7e9c7128..3f84d47a 100644 --- a/tests/components/__snapshots__/t_set.test.ts.snap +++ b/tests/components/__snapshots__/t_set.test.ts.snap @@ -4,7 +4,7 @@ exports[`t-set slot setted value (with t-set) not accessible with t-esc 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { isBoundary, withDefault, setContextValue, capture } = helpers; + let { isBoundary, withDefault, setContextValue, capture, markRaw } = helpers; let block1 = createBlock(\`

    \`); @@ -21,7 +21,7 @@ exports[`t-set slot setted value (with t-set) not accessible with t-esc 1`] = ` setContextValue(ctx, \\"iter\\", 'source'); let txt1 = ctx['iter']; const ctx1 = capture(ctx); - let b2 = component(\`Childcomp\`, {slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__1\`, node, ctx); + let b2 = component(\`Childcomp\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__1\`, node, ctx); let txt2 = ctx['iter']; return block1([txt1, txt2], [b2]); } @@ -51,7 +51,7 @@ exports[`t-set slots with a t-set with a component in body 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture, isBoundary, withDefault, LazyValue, safeOutput } = helpers; + let { capture, isBoundary, withDefault, LazyValue, safeOutput, markRaw } = helpers; function slot1(ctx, node, key = \\"\\") { ctx = Object.create(ctx); @@ -68,7 +68,7 @@ exports[`t-set slots with a t-set with a component in body 1`] = ` return function template(ctx, node, key = \\"\\") { const ctx1 = capture(ctx); - return component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__2\`, node, ctx); + return component(\`Child\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__2\`, node, ctx); } }" `; @@ -102,7 +102,7 @@ exports[`t-set slots with an t-set with a component in body 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture, isBoundary, withDefault, LazyValue, safeOutput } = helpers; + let { capture, isBoundary, withDefault, LazyValue, safeOutput, markRaw } = helpers; let block4 = createBlock(\`
    coffee
    \`); @@ -123,7 +123,7 @@ exports[`t-set slots with an t-set with a component in body 1`] = ` return function template(ctx, node, key = \\"\\") { const ctx1 = capture(ctx); - return component(\`Blorg\`, {slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__2\`, node, ctx); + return component(\`Blorg\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__2\`, node, ctx); } }" `; @@ -157,7 +157,7 @@ exports[`t-set slots with an unused t-set with a component in body 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; - let { capture, isBoundary, withDefault, LazyValue } = helpers; + let { capture, isBoundary, withDefault, LazyValue, markRaw } = helpers; function slot1(ctx, node, key = \\"\\") { ctx = Object.create(ctx); @@ -172,7 +172,7 @@ exports[`t-set slots with an unused t-set with a component in body 1`] = ` return function template(ctx, node, key = \\"\\") { const ctx1 = capture(ctx); - return component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__2\`, node, ctx); + return component(\`Child\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__2\`, node, ctx); } }" `; diff --git a/tests/components/basics.test.ts b/tests/components/basics.test.ts index a07ebf42..3d5ccb43 100644 --- a/tests/components/basics.test.ts +++ b/tests/components/basics.test.ts @@ -1,4 +1,4 @@ -import { App, Component, mount, status, useState, xml } from "../../src"; +import { App, Component, mount, status, toRaw, useState, xml } from "../../src"; import { elem, makeTestFixture, nextTick, snapshotEverything, useLogLifecycle } from "../helpers"; import { markup } from "../../src/utils"; @@ -121,7 +121,7 @@ describe("basics", () => { class Test extends Component { static template = xml`simple vnode`; setup() { - expect(this.props).toBe(p); + expect(toRaw(this.props)).toBe(p); } } diff --git a/tests/components/concurrency.test.ts b/tests/components/concurrency.test.ts index 02ee1912..106265ba 100644 --- a/tests/components/concurrency.test.ts +++ b/tests/components/concurrency.test.ts @@ -39,6 +39,8 @@ Scheduler.prototype.addFiber = function (fiber: Fiber) { afterEach(() => { if (lastScheduler && lastScheduler.tasks.size > 0) { + // we still clear the scheduler to prevent additional noise + lastScheduler.tasks.clear(); throw new Error("we got a memory leak..."); } }); @@ -131,6 +133,58 @@ test("destroying/recreating a subwidget with different props (if start is not ov ]).toBeLogged(); }); +test("destroying/recreating a subcomponent, other scenario", async () => { + let flag = false; + + class Child extends Component { + static template = xml`child`; + setup() { + if (!flag) { + flag = true; + parent.render(true); + } + useLogLifecycle(); + } + } + + class Parent extends Component { + static template = xml`parent`; + static components = { Child }; + state = useState({ hasChild: false }); + setup() { + useLogLifecycle(); + } + } + + const parent = await mount(Parent, fixture); + + expect([ + "Parent:setup", + "Parent:willStart", + "Parent:willRender", + "Parent:rendered", + "Parent:mounted", + ]).toBeLogged(); + expect(fixture.innerHTML).toBe("parent"); + + parent.state.hasChild = true; + + await nextTick(); + expect([ + "Parent:willRender", + "Child:setup", + "Child:willStart", + "Parent:rendered", + "Child:willRender", + "Child:rendered", + "Parent:willPatch", + "Child:mounted", + "Parent:patched", + ]).toBeLogged(); + + expect(fixture.innerHTML).toBe("parentchild"); +}); + test("creating two async components, scenario 1", async () => { let defA = makeDeferred(); let defB = makeDeferred(); @@ -521,7 +575,7 @@ test("properly behave when destroyed/unmounted while rendering ", async () => { } class Child extends Component { - static template = xml`
    `; + static template = xml`
    `; static components = { SubChild }; setup() { useLogLifecycle(); @@ -1907,18 +1961,13 @@ test("concurrent renderings scenario 13", async () => { await nextTick(); // wait for this change to be applied expect([ "Parent:willRender", - "Child:willUpdateProps", "Child:setup", "Child:willStart", "Parent:rendered", "Child:willRender", "Child:rendered", - "Child:willRender", - "Child:rendered", "Parent:willPatch", - "Child:willPatch", "Child:mounted", - "Child:patched", "Parent:patched", "Child:willRender", "Child:rendered", @@ -2472,9 +2521,9 @@ test("two renderings initiated between willPatch and patched", async () => { useLogLifecycle(); onMounted(() => { this.mounted = "Mounted"; - parent.render(); + parent.render(true); }); - onWillUnmount(() => parent.render()); + onWillUnmount(() => parent.render(true)); } } @@ -2507,15 +2556,11 @@ test("two renderings initiated between willPatch and patched", async () => { "Parent:rendered", ]).toBeLogged(); + await nextMicroTick(); + expect(["Panel:willRender", "Panel:rendered"]).toBeLogged(); + await nextTick(); - expect([ - "Panel:willRender", - "Panel:rendered", - "Parent:willPatch", - "Panel:willPatch", - "Panel:patched", - "Parent:patched", - ]).toBeLogged(); + expect(["Parent:willPatch", "Panel:willPatch", "Panel:patched", "Parent:patched"]).toBeLogged(); expect(fixture.innerHTML).toBe("
    Panel1Mounted
    "); parent.state.panel = "Panel2"; @@ -2753,12 +2798,20 @@ test("delay willUpdateProps with rendering grandchild", async () => { static template = xml``; static components = { Parent }; state = { value: 0 }; + setup() { + useLogLifecycle(); + } } + const parent = await mount(GrandParent, fixture); expect(fixture.innerHTML).toBe("0_0
    "); expect([ + "GrandParent:setup", + "GrandParent:willStart", + "GrandParent:willRender", "Parent:setup", "Parent:willStart", + "GrandParent:rendered", "Parent:willRender", "DelayedChild:setup", "DelayedChild:willStart", @@ -2772,20 +2825,23 @@ test("delay willUpdateProps with rendering grandchild", async () => { "ReactiveChild:mounted", "DelayedChild:mounted", "Parent:mounted", + "GrandParent:mounted", ]).toBeLogged(); promise = makeDeferred(); const prom1 = promise; parent.state.value = 1; child.render(); // trigger a root rendering first - parent.render(); + parent.render(true); reactiveChild.render(); await nextTick(); expect(fixture.innerHTML).toBe("0_0
    "); expect([ "DelayedChild:willRender", "DelayedChild:rendered", + "GrandParent:willRender", "Parent:willUpdateProps", + "GrandParent:rendered", "ReactiveChild:willRender", "ReactiveChild:rendered", "Parent:willRender", @@ -2800,12 +2856,14 @@ test("delay willUpdateProps with rendering grandchild", async () => { const prom2 = promise; child.render(); // trigger a root rendering first parent.state.value = 2; - parent.render(); + parent.render(true); reactiveChild.render(); await nextTick(); expect(fixture.innerHTML).toBe("0_0
    "); expect([ + "GrandParent:willRender", "Parent:willUpdateProps", + "GrandParent:rendered", "ReactiveChild:willRender", "ReactiveChild:rendered", "Parent:willRender", @@ -2822,12 +2880,14 @@ test("delay willUpdateProps with rendering grandchild", async () => { expect([ "DelayedChild:willRender", "DelayedChild:rendered", + "GrandParent:willPatch", "Parent:willPatch", "ReactiveChild:willPatch", "DelayedChild:willPatch", "DelayedChild:patched", "ReactiveChild:patched", "Parent:patched", + "GrandParent:patched", ]).toBeLogged(); prom1.resolve(); diff --git a/tests/components/hooks.test.ts b/tests/components/hooks.test.ts index 23515c7b..9c6cbd56 100644 --- a/tests/components/hooks.test.ts +++ b/tests/components/hooks.test.ts @@ -238,7 +238,7 @@ describe("hooks", () => { expect(fixture.innerHTML).toBe("
    maggot brain
    "); someVal = "brain"; someVal2 = "maggot"; - component.render(); + component.render(true); await nextTick(); expect(fixture.innerHTML).toBe("
    brain maggot
    "); }); @@ -272,7 +272,7 @@ describe("hooks", () => { expect(fixture.innerHTML).toBe("
    maggot brain
    "); someVal = "brain"; someVal2 = "maggot"; - component.render(); + component.render(true); await nextTick(); expect(fixture.innerHTML).toBe("
    brain maggot
    "); }); diff --git a/tests/components/lifecycle.test.ts b/tests/components/lifecycle.test.ts index 6545cdc2..8900839f 100644 --- a/tests/components/lifecycle.test.ts +++ b/tests/components/lifecycle.test.ts @@ -849,8 +849,9 @@ describe("lifecycle hooks", () => { class Parent extends Component { static template = xml` - `; + `; static components = { Child }; + state = useState({ value: 1 }); setup() { useLogLifecycle(); } @@ -871,7 +872,7 @@ describe("lifecycle hooks", () => { "Parent:mounted", ]).toBeLogged(); - parent.render(); // to block child render + parent.state.value++; // to block child render await nextTick(); expect(["Parent:willRender", "Child:willUpdateProps", "Parent:rendered"]).toBeLogged(); @@ -1008,20 +1009,15 @@ describe("lifecycle hooks", () => { await nextTick(); expect([ "C:willRender", - "D:willUpdateProps", "F:setup", "F:willStart", "C:rendered", - "D:willRender", - "D:rendered", "F:willRender", "F:rendered", "C:willPatch", - "D:willPatch", "E:willUnmount", "E:willDestroy", "F:mounted", - "D:patched", "C:patched", ]).toBeLogged(); }); diff --git a/tests/components/rendering.test.ts b/tests/components/rendering.test.ts new file mode 100644 index 00000000..ab785364 --- /dev/null +++ b/tests/components/rendering.test.ts @@ -0,0 +1,442 @@ +import { Component, mount, onRendered, onWillUpdateProps, useState, xml } from "../../src"; +import { + makeTestFixture, + snapshotEverything, + nextTick, + useLogLifecycle, + makeDeferred, + nextMicroTick, +} from "../helpers"; + +let fixture: HTMLElement; + +snapshotEverything(); + +beforeEach(() => { + fixture = makeTestFixture(); +}); + +describe("rendering semantics", () => { + test("can render a parent without rendering child", async () => { + class Child extends Component { + static template = xml`child`; + setup() { + useLogLifecycle(); + } + } + + class Parent extends Component { + static template = xml` + + + `; + static components = { Child }; + + state = useState({ value: "A" }); + setup() { + useLogLifecycle(); + } + } + + const parent = await mount(Parent, fixture); + + expect(fixture.innerHTML).toBe("Achild"); + expect([ + "Parent:setup", + "Parent:willStart", + "Parent:willRender", + "Child:setup", + "Child:willStart", + "Parent:rendered", + "Child:willRender", + "Child:rendered", + "Child:mounted", + "Parent:mounted", + ]).toBeLogged(); + + parent.state.value = "B"; + await nextTick(); + expect(fixture.innerHTML).toBe("Bchild"); + expect([ + "Parent:willRender", + "Parent:rendered", + "Parent:willPatch", + "Parent:patched", + ]).toBeLogged(); + }); + + test("can force a render to update sub tree", async () => { + let childN = 0; + let parentN = 0; + class Child extends Component { + static template = xml`child`; + setup() { + onRendered(() => childN++); + } + } + + class Parent extends Component { + static template = xml` + + + `; + static components = { Child }; + + state = { value: "A" }; + setup() { + onRendered(() => parentN++); + } + } + + const parent = await mount(Parent, fixture); + + expect(fixture.innerHTML).toBe("Achild"); + expect(parentN).toBe(1); + expect(childN).toBe(1); + + parent.state.value = "B"; + parent.render(true); + await nextTick(); + expect(fixture.innerHTML).toBe("Bchild"); + expect(parentN).toBe(2); + expect(childN).toBe(2); + }); + + test("render with deep=true followed by render with deep=false work as expected", async () => { + class Child extends Component { + static template = xml`child`; + setup() { + useLogLifecycle(); + } + } + + class Parent extends Component { + static template = xml`parent`; + static components = { Child }; + + state = useState({ value: "A" }); + + setup() { + useLogLifecycle(); + } + } + let value = 3; + const env = { + getValue() { + return value; + }, + }; + + const parent = await mount(Parent, fixture, { env }); + + expect(fixture.innerHTML).toBe("parentAchild3"); + expect([ + "Parent:setup", + "Parent:willStart", + "Parent:willRender", + "Child:setup", + "Child:willStart", + "Parent:rendered", + "Child:willRender", + "Child:rendered", + "Child:mounted", + "Parent:mounted", + ]).toBeLogged(); + + value = 4; + parent.render(true); + + // wait for child to be rendered, but dom not yet patched + await nextMicroTick(); + await nextMicroTick(); + await nextMicroTick(); + expect([ + "Parent:willRender", + "Child:willUpdateProps", + "Parent:rendered", + "Child:willRender", + "Child:rendered", + ]).toBeLogged(); + + parent.state.value = "B"; + + await nextTick(); + + expect(fixture.innerHTML).toBe("parentBchild4"); + expect([ + "Parent:willRender", + "Child:willUpdateProps", + "Parent:rendered", + "Child:willRender", + "Child:rendered", + "Parent:willPatch", + "Child:willPatch", + "Child:patched", + "Parent:patched", + ]).toBeLogged(); + }); + + test("props are reactive", async () => { + class Child extends Component { + static template = xml``; + setup() { + useLogLifecycle(); + } + } + + class Parent extends Component { + static template = xml` + + `; + static components = { Child }; + + state = useState({ b: 1 }); + setup() { + useLogLifecycle(); + } + } + + const parent = await mount(Parent, fixture); + expect(fixture.innerHTML).toBe("1"); + expect([ + "Parent:setup", + "Parent:willStart", + "Parent:willRender", + "Child:setup", + "Child:willStart", + "Parent:rendered", + "Child:willRender", + "Child:rendered", + "Child:mounted", + "Parent:mounted", + ]).toBeLogged(); + + parent.state.b = 3; + await nextTick(); + expect(fixture.innerHTML).toBe("3"); + expect(["Child:willRender", "Child:rendered", "Child:willPatch", "Child:patched"]).toBeLogged(); + }); + + test("props are reactive (nested prop)", async () => { + class Child extends Component { + static template = xml``; + + setup() { + useLogLifecycle(); + } + } + + class Parent extends Component { + static template = xml` + + `; + static components = { Child }; + + state = useState({ b: { c: 1 } }); + + setup() { + useLogLifecycle(); + } + } + + const parent = await mount(Parent, fixture); + expect(fixture.innerHTML).toBe("1"); + expect([ + "Parent:setup", + "Parent:willStart", + "Parent:willRender", + "Child:setup", + "Child:willStart", + "Parent:rendered", + "Child:willRender", + "Child:rendered", + "Child:mounted", + "Parent:mounted", + ]).toBeLogged(); + + parent.state.b.c = 3; // parent is now subscribed to 'b' key + await nextTick(); + expect(fixture.innerHTML).toBe("3"); + expect(["Child:willRender", "Child:rendered", "Child:willPatch", "Child:patched"]).toBeLogged(); + + parent.state.b = { c: 444 }; // triggers a parent and a child render + await nextTick(); + expect(fixture.innerHTML).toBe("444"); + expect([ + "Parent:willRender", + "Parent:rendered", + "Child:willRender", + "Child:rendered", + "Parent:willPatch", + "Parent:patched", + "Child:willPatch", + "Child:patched", + ]).toBeLogged(); + }); + + test("works as expected for dynamic number of props", async () => { + class Child extends Component { + static template = xml``; + } + + class Parent extends Component { + static template = xml` + + `; + static components = { Child }; + + state: any = useState({ b: 1 }); + } + + const parent = await mount(Parent, fixture); + expect(fixture.innerHTML).toBe("1"); + parent.state.newkey = 3; + await nextTick(); + expect(fixture.innerHTML).toBe("2"); + }); + + test("rendering is atomic (for one subtree)", async () => { + const def = makeDeferred(); + + class C extends Component { + static template = xml``; + + setup() { + useLogLifecycle(); + } + } + + class B extends Component { + static template = xml``; + static components = { C }; + + setup() { + useLogLifecycle(); + onWillUpdateProps(() => def); + } + } + + class A extends Component { + static template = xml``; + static components = { B }; + + state = useState({ obj: { val: 1 } }); + + setup() { + useLogLifecycle(); + } + } + + const parent = await mount(A, fixture); + expect(fixture.innerHTML).toBe("11"); + expect([ + "A:setup", + "A:willStart", + "A:willRender", + "B:setup", + "B:willStart", + "A:rendered", + "B:willRender", + "C:setup", + "C:willStart", + "B:rendered", + "C:willRender", + "C:rendered", + "C:mounted", + "B:mounted", + "A:mounted", + ]).toBeLogged(); + + parent.state.obj.val = 3; + await nextTick(); + expect(fixture.innerHTML).toBe("33"); + expect([ + "A:willRender", + "A:rendered", + "C:willRender", + "C:rendered", + "A:willPatch", + "A:patched", + "C:willPatch", + "C:patched", + ]).toBeLogged(); + + def.resolve(); + await nextTick(); + expect([]).toBeLogged(); + }); +}); + +test("force render in case of existing render", async () => { + const def = makeDeferred(); + + class C extends Component { + static template = xml`C`; + setup() { + useLogLifecycle(); + } + } + class B extends Component { + static template = xml``; + static components = { C }; + setup() { + useLogLifecycle(); + onWillUpdateProps(() => def); + } + } + class A extends Component { + static template = xml``; + static components = { B }; + state = useState({ val: 1 }); + setup() { + useLogLifecycle(); + } + } + const parent = await mount(A, fixture); + expect(fixture.innerHTML).toBe("C1"); + expect([ + "A:setup", + "A:willStart", + "A:willRender", + "B:setup", + "B:willStart", + "A:rendered", + "B:willRender", + "C:setup", + "C:willStart", + "B:rendered", + "C:willRender", + "C:rendered", + "C:mounted", + "B:mounted", + "A:mounted", + ]).toBeLogged(); + + // trigger a new rendering, blocked in B + parent.state.val = 2; + await nextTick(); + expect(["A:willRender", "B:willUpdateProps", "A:rendered"]).toBeLogged(); + + // initiate a new render with deep=true. it should cancel the current render + // and also be blocked in B + parent.render(true); + await nextTick(); + expect(["A:willRender", "B:willUpdateProps", "A:rendered"]).toBeLogged(); + + def.resolve(); + await nextTick(); + // we check here that the render reaches C (so, that it was properly forced) + expect([ + "B:willRender", + "C:willUpdateProps", + "B:rendered", + "C:willRender", + "C:rendered", + "A:willPatch", + "B:willPatch", + "C:willPatch", + "C:patched", + "B:patched", + "A:patched", + ]).toBeLogged(); +}); diff --git a/tests/components/slots.test.ts b/tests/components/slots.test.ts index 7f22f53a..729b2fda 100644 --- a/tests/components/slots.test.ts +++ b/tests/components/slots.test.ts @@ -1675,4 +1675,60 @@ describe("slots", () => { await mount(Parent, fixture); expect(fixture.innerHTML).toBe("
    SlotDisplay
    Parent
    "); }); + + test("mix of slots, t-call, t-call with body, and giving own props child", async () => { + expect.assertions(11); + + class C extends Component { + static template = xml`[C]`; + } + class B extends Component { + static template = xml`[B]`; + static components = { C }; + } + + const subTemplate2 = xml`[sub2]`; + const subTemplate1 = xml`[sub1] + + + + `; + + let a: any; + class A extends Component { + static components = { B }; + static template = xml`[A]`; + setup() { + a = this; + } + + get validate() { + // we check here that the actual component was not lost somehow + expect(this.__owl__.component === a).toBe(true); + return 1; + } + } + + class P extends Component { + static components = { A }; + static template = xml``; + + state = useState({ number: 333 }); + inc() { + this.state.number++; + } + } + + class Parent extends Component { + static template = xml`

    `; + static components = { P }; + } + + await mount(Parent, fixture); + expect(fixture.innerHTML).toBe("[B][C][A][sub1] [sub2333]"); + + fixture.querySelector("button")!.click(); + await nextTick(); + expect(fixture.innerHTML).toBe("[B][C][A][sub1] [sub2334]"); + }); }); diff --git a/tests/components/t_props.test.ts b/tests/components/t_props.test.ts index e5436da7..fec21a12 100644 --- a/tests/components/t_props.test.ts +++ b/tests/components/t_props.test.ts @@ -53,7 +53,7 @@ describe("t-props", () => { }); test("basic use", async () => { - expect.assertions(4); + expect.assertions(5); let props = { a: 1, b: 2 }; @@ -65,6 +65,7 @@ describe("t-props", () => { `; setup() { expect(this.props).toEqual({ a: 1, b: 2 }); + expect(this.props).not.toBe(props); } } class Parent extends Component { diff --git a/tests/misc/__snapshots__/memo.test.ts.snap b/tests/misc/__snapshots__/memo.test.ts.snap index 9fc02e42..6501fb70 100644 --- a/tests/misc/__snapshots__/memo.test.ts.snap +++ b/tests/misc/__snapshots__/memo.test.ts.snap @@ -4,6 +4,7 @@ exports[`Memo if no prop change, prevent renderings from above 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; function slot1(ctx, node, key = \\"\\") { let b6 = text(ctx['state'].a); @@ -16,7 +17,7 @@ exports[`Memo if no prop change, prevent renderings from above 1`] = ` let b2 = text(ctx['state'].a); let b3 = text(ctx['state'].b); let b4 = text(ctx['state'].c); - let b9 = component(\`Memo\`, {a: ctx['state'].a, b: ctx['state'].b,slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx); + let b9 = component(\`Memo\`, {a: ctx['state'].a, b: ctx['state'].b,slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, ctx); return multi([b2, b3, b4, b9]); } }" @@ -26,6 +27,7 @@ exports[`Memo if no props, prevent renderings from above 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; function slot1(ctx, node, key = \\"\\") { return component(\`Child\`, {value: ctx['state'].value}, key + \`__2\`, node, ctx); @@ -33,7 +35,7 @@ exports[`Memo if no props, prevent renderings from above 1`] = ` return function template(ctx, node, key = \\"\\") { let b2 = component(\`Child\`, {value: ctx['state'].value}, key + \`__1\`, node, ctx); - let b4 = component(\`Memo\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__3\`, node, ctx); + let b4 = component(\`Memo\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__3\`, node, ctx); return multi([b2, b4]); } }" @@ -54,6 +56,7 @@ exports[`Memo if no props, prevent renderings from above (work with simple html) "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; function slot1(ctx, node, key = \\"\\") { return text(ctx['state'].value); @@ -61,7 +64,7 @@ exports[`Memo if no props, prevent renderings from above (work with simple html) return function template(ctx, node, key = \\"\\") { let b2 = text(ctx['state'].value); - let b4 = component(\`Memo\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx); + let b4 = component(\`Memo\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, ctx); return multi([b2, b4]); } }" diff --git a/tests/misc/__snapshots__/portal.test.ts.snap b/tests/misc/__snapshots__/portal.test.ts.snap index bceeed15..4e3e798a 100644 --- a/tests/misc/__snapshots__/portal.test.ts.snap +++ b/tests/misc/__snapshots__/portal.test.ts.snap @@ -149,6 +149,7 @@ exports[`Portal Portal composed with t-slot 1`] = ` "function anonymous(bdom, helpers ) { let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + let { markRaw } = helpers; let block1 = createBlock(\`

    \`); @@ -157,7 +158,7 @@ exports[`Portal Portal composed with t-slot 1`] = ` } return function template(ctx, node, key = \\"\\") { - let b3 = component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx); + let b3 = component(\`Child\`, {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, ctx); return block1([], [b3]); } }" diff --git a/tests/misc/portal.test.ts b/tests/misc/portal.test.ts index 08436b8e..ff373a17 100644 --- a/tests/misc/portal.test.ts +++ b/tests/misc/portal.test.ts @@ -421,6 +421,7 @@ describe("Portal", () => { addOutsideDiv(fixture); const parent = await mount(Parent, fixture); expect(steps).toEqual(["parent:mounted"]); + expect(fixture.innerHTML).toBe('
    '); parent.state.hasChild = true; await nextTick(); @@ -430,6 +431,7 @@ describe("Portal", () => { "child:mounted", "parent:patched", ]); + expect(fixture.innerHTML).toBe('
    1
    '); parent.state.val = 2; await nextTick(); @@ -443,6 +445,7 @@ describe("Portal", () => { "child:patched", "parent:patched", ]); + expect(fixture.innerHTML).toBe('
    2
    '); parent.state.hasChild = false; await nextTick(); @@ -459,6 +462,7 @@ describe("Portal", () => { "child:willUnmount", "parent:patched", ]); + expect(fixture.innerHTML).toBe('
    '); }); test("portal destroys on crash", async () => { diff --git a/tests/reactivity.test.ts b/tests/reactivity.test.ts index 8f1328f9..1c9ff8d3 100644 --- a/tests/reactivity.test.ts +++ b/tests/reactivity.test.ts @@ -1657,7 +1657,7 @@ describe("Reactivity: useState", () => { expect([...steps]).toEqual(["list"]); await nextTick(); expect(fixture.innerHTML).toBe("
    3
    Total: 3 Count: 1
    "); - expect([...steps]).toEqual(["list", "quantity1"]); + expect([...steps]).toEqual(["list"]); steps.clear(); secondQuantity.quantity = 2;