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;