From b3062d29f1601eab624df9081b506df9dfafe263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Mon, 7 Mar 2022 14:31:17 +0100 Subject: [PATCH] [REF] parser: make AST definition more consistent --- src/app/template_helpers.ts | 2 +- src/compiler/code_generator.ts | 29 +- src/compiler/parser.ts | 72 ++-- tests/compiler/parser.test.ts | 358 +++++++++++------- .../__snapshots__/error_handling.test.ts.snap | 24 +- .../__snapshots__/refs.test.ts.snap | 2 +- .../__snapshots__/slots.test.ts.snap | 150 ++++---- .../__snapshots__/t_on.test.ts.snap | 4 +- .../__snapshots__/t_set.test.ts.snap | 6 +- tests/misc/__snapshots__/portal.test.ts.snap | 2 +- 10 files changed, 356 insertions(+), 293 deletions(-) diff --git a/src/app/template_helpers.ts b/src/app/template_helpers.ts index caa2fbd4..4e35fcdb 100644 --- a/src/app/template_helpers.ts +++ b/src/app/template_helpers.ts @@ -27,7 +27,7 @@ function callSlot( const { __render, __ctx, __scope } = slots[name] || {}; const slotScope = Object.create(__ctx || {}); if (__scope) { - slotScope[__scope] = extra || {}; + slotScope[__scope] = extra; } const slotBDom = __render ? __render.call(__ctx.__owl__.component, slotScope, parent, key) : null; if (defaultContent) { diff --git a/src/compiler/code_generator.ts b/src/compiler/code_generator.ts index 403bcf0b..2b9c0e7a 100644 --- a/src/compiler/code_generator.ts +++ b/src/compiler/code_generator.ts @@ -20,6 +20,8 @@ import { ASTTranslation, ASTType, ASTTPortal, + EventHandlers, + Attrs, } from "./parser"; type BlockType = "block" | "text" | "multi" | "list" | "html" | "comment"; @@ -157,11 +159,11 @@ class CodeTarget { // maps ref name to [id, expr] refInfo: { [name: string]: [string, string] } = {}; shouldProtectScope: boolean = false; - on?: { [key: string]: string }; + on: EventHandlers | null; - constructor(name: string, on?: { [key: string]: string }) { + constructor(name: string, on?: EventHandlers | null) { this.name = name; - this.on = on; + this.on = on || null; } addLine(line: string, idx?: number) { @@ -305,12 +307,7 @@ export class CodeGenerator { return code; } - compileInNewTarget( - prefix: string, - ast: AST, - ctx: Context, - on?: { [key: string]: string } - ): string { + compileInNewTarget(prefix: string, ast: AST, ctx: Context, on?: EventHandlers | null): string { const name = this.generateId(prefix); const initialTarget = this.target; const target = new CodeTarget(name, on); @@ -562,7 +559,7 @@ export class CodeGenerator { } } // attributes - const attrs: { [key: string]: string } = {}; + const attrs: Attrs = {}; const nameSpace = ast.ns || ctx.nameSpace; if (nameSpace && isNewBlock) { // specific namespace uri @@ -1098,19 +1095,17 @@ export class CodeGenerator { compileComponent(ast: ASTComponent, ctx: Context) { let { block } = ctx; - // props - const hasSlotsProp = "slots" in ast.props; + const hasSlotsProp = "slots" in (ast.props || {}); const props: string[] = []; - const propExpr = this.formatPropObject(ast.props); + const propExpr = this.formatPropObject(ast.props || {}); if (propExpr) { props.push(propExpr); } // slots - const hasSlot = !!Object.keys(ast.slots).length; let slotDef: string = ""; - if (hasSlot) { + if (ast.slots) { let ctxStr = "ctx"; if (this.target.loopLevel || !this.hasSafeContext) { ctxStr = this.generateId("ctx"); @@ -1120,7 +1115,7 @@ export class CodeGenerator { let slotStr: string[] = []; for (let slotName in ast.slots) { const slotAst = ast.slots[slotName]; - const name = this.compileInNewTarget("slot", slotAst.content, ctx, slotAst.on || undefined); + const name = this.compileInNewTarget("slot", slotAst.content, ctx, slotAst.on); const params = [`__render: ${name}, __ctx: ${ctxStr}`]; const scope = ast.slots[slotName].scope; if (scope) { @@ -1199,7 +1194,7 @@ export class CodeGenerator { this.insertBlock(blockExpr, block, ctx); } - wrapWithEventCatcher(expr: string, on: { [key: string]: string }): string { + wrapWithEventCatcher(expr: string, on: EventHandlers): string { this.helpers.add("createCatcher"); let name = this.generateId("catcher"); let spec: any = {}; diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 55c1b2d3..16547c01 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2,6 +2,9 @@ // AST Type definition // ----------------------------------------------------------------------------- +export type EventHandlers = { [eventName: string]: string }; +export type Attrs = { [attrs: string]: string }; + export const enum ASTType { Text, Comment, @@ -34,25 +37,25 @@ export interface ASTComment { } interface TModelInfo { - hasDynamicChildren?: boolean; baseExpr: string; expr: string; targetAttr: string; - specialInitTargetAttr: string | null; eventType: "change" | "click" | "input"; shouldTrim: boolean; shouldNumberize: boolean; + hasDynamicChildren: boolean; + specialInitTargetAttr: string | null; } export interface ASTDomNode { type: ASTType.DomNode; tag: string; - dynamicTag: string | null; - attrs: { [key: string]: string }; content: AST[]; + attrs: Attrs | null; ref: string | null; - on: { [key: string]: string }; - model?: TModelInfo | null; + on: EventHandlers | null; + model: TModelInfo | null; + dynamicTag: string | null; ns: string | null; } @@ -93,13 +96,13 @@ export interface ASTTForEach { type: ASTType.TForEach; collection: string; elem: string; - key: string | null; body: AST; memo: string; hasNoFirst: boolean; hasNoLast: boolean; hasNoIndex: boolean; hasNoValue: boolean; + key: string | null; } export interface ASTTKey { @@ -116,9 +119,9 @@ export interface ASTTCall { interface SlotDefinition { content: AST; - attrs?: { [key: string]: string }; - scope?: string; - on?: null | { [key: string]: string }; + scope: string | null; + on: EventHandlers | null; + attrs: Attrs | null; } export interface ASTComponent { @@ -126,16 +129,16 @@ export interface ASTComponent { name: string; isDynamic: boolean; dynamicProps: string | null; - on: null | { [key: string]: string }; - props: { [name: string]: string }; - slots: { [name: string]: SlotDefinition }; + on: EventHandlers | null; + props: { [name: string]: string } | null; + slots: { [name: string]: SlotDefinition } | null; } export interface ASTSlot { type: ASTType.TSlot; name: string; - attrs: { [key: string]: string }; - on: null | { [key: string]: string }; + attrs: Attrs | null; + on: EventHandlers | null; defaultContent: AST | null; } @@ -328,8 +331,8 @@ function parseDOMNode(node: Element, ctx: ParsingContext): AST | null { node.removeAttribute("t-ref"); const nodeAttrsNames = node.getAttributeNames(); - const attrs: ASTDomNode["attrs"] = {}; - const on: ASTDomNode["on"] = {}; + let attrs: ASTDomNode["attrs"] = null; + let on: EventHandlers | null = null; let model: TModelInfo | null = null; for (let attr of nodeAttrsNames) { @@ -338,6 +341,7 @@ function parseDOMNode(node: Element, ctx: ParsingContext): AST | null { if (attr === "t-on") { throw new Error("Missing event name with t-on directive"); } + on = on || {}; on[attr.slice(5)] = value; } else if (attr.startsWith("t-model")) { if (!["input", "select", "textarea"].includes(tagName)) { @@ -375,6 +379,7 @@ function parseDOMNode(node: Element, ctx: ParsingContext): AST | null { targetAttr: isCheckboxInput ? "checked" : "value", specialInitTargetAttr: isRadioInput ? "checked" : null, eventType, + hasDynamicChildren: false, shouldTrim: hasTrimMod && (isOtherInput || isTextarea), shouldNumberize: hasNumberMod && (isOtherInput || isTextarea), }; @@ -393,6 +398,7 @@ function parseDOMNode(node: Element, ctx: ParsingContext): AST | null { if (tModel && ["t-att-value", "t-attf-value"].includes(attr)) { tModel.hasDynamicChildren = true; } + attrs = attrs || {}; attrs[attr] = value; } } @@ -563,7 +569,7 @@ function parseTCall(node: Element, ctx: ParsingContext): AST | null { if (ast && ast.type === ASTType.TComponent) { return { ...ast, - slots: { default: { content: tcall } }, + slots: { default: { content: tcall, scope: null, on: null, attrs: null } }, }; } } @@ -694,7 +700,7 @@ function parseComponent(node: Element, ctx: ParsingContext): AST | null { node.removeAttribute("t-slot-scope"); let on: ASTComponent["on"] = null; - const props: ASTComponent["props"] = {}; + let props: ASTComponent["props"] = null; for (let name of node.getAttributeNames()) { const value = node.getAttribute(name)!; if (name.startsWith("t-")) { @@ -706,11 +712,12 @@ function parseComponent(node: Element, ctx: ParsingContext): AST | null { throw new Error(message || `unsupported directive on Component: ${name}`); } } else { + props = props || {}; props[name] = value; } } - const slots: ASTComponent["slots"] = {}; + let slots: ASTComponent["slots"] | null = null; if (node.hasChildNodes()) { const clone = node.cloneNode(true); @@ -743,38 +750,32 @@ function parseComponent(node: Element, ctx: ParsingContext): AST | null { slotNode.remove(); const slotAst = parseNode(slotNode, ctx); if (slotAst) { - const slotInfo: any = { content: slotAst }; let on: SlotDefinition["on"] = null; - const attrs: { [key: string]: string } = {}; + let attrs: Attrs | null = null; + let scope: string | null = null; for (let attributeName of slotNode.getAttributeNames()) { const value = slotNode.getAttribute(attributeName)!; if (attributeName === "t-slot-scope") { - slotInfo.scope = value; + scope = value; continue; } else if (attributeName.startsWith("t-on-")) { on = on || {}; on[attributeName.slice(5)] = value; } else { + attrs = attrs || {}; attrs[attributeName] = value; } } - if (Object.keys(attrs).length) { - slotInfo.attrs = attrs; - } - if (on) { - slotInfo.on = on; - } - slots[name] = slotInfo; + slots = slots || {}; + slots[name] = { content: slotAst, on, attrs, scope }; } } // default slot const defaultContent = parseChildNodes(clone, ctx); if (defaultContent) { - slots.default = { content: defaultContent }; - if (defaultSlotScope) { - slots.default.scope = defaultSlotScope; - } + slots = slots || {}; + slots.default = { content: defaultContent, on, attrs: null, scope: defaultSlotScope }; } } return { type: ASTType.TComponent, name, isDynamic, dynamicProps, props, slots, on }; @@ -790,7 +791,7 @@ function parseTSlot(node: Element, ctx: ParsingContext): AST | null { } const name = node.getAttribute("t-slot")!; node.removeAttribute("t-slot"); - const attrs: { [key: string]: string } = {}; + let attrs: Attrs | null = null; let on: ASTComponent["on"] = null; for (let attributeName of node.getAttributeNames()) { const value = node.getAttribute(attributeName)!; @@ -798,6 +799,7 @@ function parseTSlot(node: Element, ctx: ParsingContext): AST | null { on = on || {}; on[attributeName.slice(5)] = value; } else { + attrs = attrs || {}; attrs[attributeName] = value; } } diff --git a/tests/compiler/parser.test.ts b/tests/compiler/parser.test.ts index ee0bad50..c8959148 100644 --- a/tests/compiler/parser.test.ts +++ b/tests/compiler/parser.test.ts @@ -42,8 +42,8 @@ describe("qweb parser", () => { tag: "div", dynamicTag: null, content: [], - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -69,8 +69,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "div", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, content: [], @@ -83,8 +83,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "div", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, content: [{ type: ASTType.Text, value: "some text" }], @@ -97,8 +97,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "div", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -108,8 +108,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "span", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -127,8 +127,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "div", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -138,8 +138,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "span", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -155,8 +155,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "div", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -180,8 +180,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "div", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -200,8 +200,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "div", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -222,8 +222,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "div", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -245,8 +245,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "span", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -262,7 +262,7 @@ describe("qweb parser", () => { tag: "div", dynamicTag: null, attrs: { class: "abc" }, - on: {}, + on: null, ref: null, model: null, ns: null, @@ -294,7 +294,7 @@ describe("qweb parser", () => { dynamicTag: null, model: null, ns: null, - on: {}, + on: null, ref: null, tag: "circle", type: 2, @@ -303,7 +303,7 @@ describe("qweb parser", () => { dynamicTag: null, model: null, ns: "http://www.w3.org/2000/svg", - on: {}, + on: null, ref: null, tag: "svg", type: 2, @@ -311,7 +311,7 @@ describe("qweb parser", () => { expect( parse(``) ).toEqual({ - attrs: {}, + attrs: null, content: [ { attrs: { @@ -326,7 +326,7 @@ describe("qweb parser", () => { dynamicTag: null, model: null, ns: null, - on: {}, + on: null, ref: null, tag: "circle", type: 2, @@ -335,7 +335,7 @@ describe("qweb parser", () => { dynamicTag: null, model: null, ns: "http://www.w3.org/2000/svg", - on: {}, + on: null, ref: null, tag: "g", type: 2, @@ -347,16 +347,16 @@ describe("qweb parser", () => { type: 2, tag: "div", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, content: [ { type: 2, tag: "pre", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, content: [], model: null, @@ -390,8 +390,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "span", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -412,8 +412,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "div", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -454,8 +454,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "div", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -468,8 +468,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "div", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -488,8 +488,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "div", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -529,8 +529,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "div", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -606,8 +606,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "div", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -625,8 +625,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "h1", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -638,8 +638,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "h2", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -684,8 +684,8 @@ describe("qweb parser", () => { body: [ { type: ASTType.DomNode, - attrs: {}, - on: {}, + attrs: null, + on: null, tag: "div", dynamicTag: null, ref: null, @@ -704,8 +704,8 @@ describe("qweb parser", () => { body: [ { type: ASTType.DomNode, - attrs: {}, - on: {}, + attrs: null, + on: null, tag: "div", dynamicTag: null, ref: null, @@ -741,8 +741,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "div", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -810,8 +810,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "div", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -852,8 +852,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "span", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -886,8 +886,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "span", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -920,7 +920,7 @@ describe("qweb parser", () => { "t-att-selected": "category.id==options.active_category_id", "t-att-value": "category.id", }, - on: {}, + on: null, ref: null, model: null, ns: null, @@ -939,8 +939,8 @@ describe("qweb parser", () => { parse(`
`) ).toEqual({ type: ASTType.DomNode, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, tag: "div", @@ -983,10 +983,10 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "span", dynamicTag: null, - on: {}, + on: null, ref: null, model: null, - attrs: {}, + attrs: null, ns: null, content: [{ type: ASTType.TEsc, expr: "item", defaultValue: "" }], }, @@ -1009,8 +1009,8 @@ describe("qweb parser", () => { isDynamic: false, name: "Comp", dynamicProps: null, - props: {}, - slots: {}, + props: null, + slots: null, on: null, }, memo: "", @@ -1086,8 +1086,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "div", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -1124,7 +1124,7 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "button", dynamicTag: null, - attrs: {}, + attrs: null, on: { click: "add" }, ref: null, model: null, @@ -1142,8 +1142,8 @@ describe("qweb parser", () => { type: 2, tag: "select", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, content: [ { @@ -1151,7 +1151,7 @@ describe("qweb parser", () => { tag: "option", dynamicTag: null, attrs: { value: "1" }, - on: {}, + on: null, ref: null, content: [], model: null, @@ -1162,6 +1162,7 @@ describe("qweb parser", () => { baseExpr: "state", expr: "'model'", targetAttr: "value", + hasDynamicChildren: false, specialInitTargetAttr: null, eventType: "change", shouldTrim: false, @@ -1178,8 +1179,8 @@ describe("qweb parser", () => { type: 2, tag: "select", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, content: [ { @@ -1187,7 +1188,7 @@ describe("qweb parser", () => { tag: "option", dynamicTag: null, attrs: { "t-att-value": "valueVar" }, - on: {}, + on: null, ref: null, content: [], model: null, @@ -1217,9 +1218,9 @@ describe("qweb parser", () => { type: ASTType.TComponent, name: "MyComponent", dynamicProps: null, - props: {}, + props: null, on: null, - slots: {}, + slots: null, isDynamic: false, }); }); @@ -1232,7 +1233,7 @@ describe("qweb parser", () => { props: { a: "1", b: "'b'" }, isDynamic: false, on: null, - slots: {}, + slots: null, }); }); @@ -1244,7 +1245,7 @@ describe("qweb parser", () => { props: { a: "1" }, isDynamic: false, on: null, - slots: {}, + slots: null, }); }); @@ -1253,10 +1254,10 @@ describe("qweb parser", () => { type: ASTType.TComponent, name: "MyComponent", dynamicProps: null, - props: {}, + props: null, isDynamic: false, on: { click: "someMethod" }, - slots: {}, + slots: null, }); }); @@ -1289,10 +1290,17 @@ describe("qweb parser", () => { type: ASTType.TComponent, name: "MyComponent", dynamicProps: null, - props: {}, + props: null, isDynamic: false, on: null, - slots: { default: { content: { type: ASTType.Text, value: "foo" } } }, + slots: { + default: { + content: { type: ASTType.Text, value: "foo" }, + attrs: null, + on: null, + scope: null, + }, + }, }); }); @@ -1303,11 +1311,16 @@ describe("qweb parser", () => { type: ASTType.TComponent, name: "MyComponent", dynamicProps: null, - props: {}, + props: null, isDynamic: false, on: null, slots: { - default: { content: { type: ASTType.Text, value: "foo" }, attrs: { param: "param" } }, + default: { + content: { type: ASTType.Text, value: "foo" }, + attrs: { param: "param" }, + on: null, + scope: null, + }, }, }); }); @@ -1318,7 +1331,7 @@ describe("qweb parser", () => { name: "MyComponent", isDynamic: false, dynamicProps: null, - props: {}, + props: null, on: null, slots: { default: { @@ -1329,26 +1342,29 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "span", dynamicTag: null, - attrs: {}, + attrs: null, content: [], ref: null, model: null, - on: {}, + on: null, ns: null, }, { type: ASTType.DomNode, tag: "div", dynamicTag: null, - attrs: {}, + attrs: null, content: [], ref: null, model: null, - on: {}, + on: null, ns: null, }, ], }, + attrs: null, + on: null, + scope: null, }, }, }); @@ -1360,9 +1376,11 @@ describe("qweb parser", () => { name: "MyComponent", isDynamic: false, dynamicProps: null, - props: {}, + props: null, on: null, - slots: { name: { content: { type: ASTType.Text, value: "foo" } } }, + slots: { + name: { content: { type: ASTType.Text, value: "foo" }, attrs: null, on: null, scope: null }, + }, }); }); @@ -1372,9 +1390,16 @@ describe("qweb parser", () => { name: "MyComponent", isDynamic: false, dynamicProps: null, - props: {}, + props: null, on: null, - slots: { name: { content: { type: ASTType.Text, value: "foo" }, attrs: { param: "param" } } }, + slots: { + name: { + content: { type: ASTType.Text, value: "foo" }, + attrs: { param: "param" }, + on: null, + scope: null, + }, + }, }); }); @@ -1386,12 +1411,14 @@ describe("qweb parser", () => { name: "MyComponent", isDynamic: false, dynamicProps: null, - props: {}, + props: null, on: null, slots: { name: { content: { type: ASTType.Text, value: "foo" }, on: { click: "doStuff" }, + attrs: null, + scope: null, }, }, }); @@ -1408,12 +1435,17 @@ describe("qweb parser", () => { type: ASTType.TComponent, name: "MyComponent", dynamicProps: null, - props: {}, + props: null, isDynamic: false, on: null, slots: { - default: { content: { type: ASTType.Text, value: " " } }, - name: { content: { type: ASTType.Text, value: "foo" } }, + default: { + content: { type: ASTType.Text, value: " " }, + attrs: null, + on: null, + scope: null, + }, + name: { content: { type: ASTType.Text, value: "foo" }, attrs: null, on: null, scope: null }, }, }); }); @@ -1428,12 +1460,12 @@ describe("qweb parser", () => { type: ASTType.TComponent, name: "MyComponent", dynamicProps: null, - props: {}, + props: null, isDynamic: false, on: null, slots: { - a: { content: { type: ASTType.Text, value: "foo" } }, - b: { content: { type: ASTType.Text, value: "bar" } }, + a: { content: { type: ASTType.Text, value: "foo" }, attrs: null, on: null, scope: null }, + b: { content: { type: ASTType.Text, value: "bar" }, attrs: null, on: null, scope: null }, }, }); }); @@ -1443,10 +1475,10 @@ describe("qweb parser", () => { type: ASTType.TComponent, name: "myComponent", dynamicProps: null, - props: {}, + props: null, isDynamic: true, on: null, - slots: {}, + slots: null, }); }); @@ -1458,7 +1490,7 @@ describe("qweb parser", () => { props: { a: "1", b: "'b'" }, isDynamic: true, on: null, - slots: {}, + slots: null, }); }); @@ -1470,7 +1502,7 @@ describe("qweb parser", () => { props: { a: "1" }, isDynamic: true, on: null, - slots: {}, + slots: null, }); }); @@ -1491,10 +1523,17 @@ describe("qweb parser", () => { type: ASTType.TComponent, name: "MyComponent", dynamicProps: null, - props: {}, + props: null, isDynamic: false, on: null, - slots: { default: { content: { body: null, name: "subTemplate", type: ASTType.TCall } } }, + slots: { + default: { + content: { body: null, name: "subTemplate", type: ASTType.TCall }, + attrs: null, + scope: null, + on: null, + }, + }, }); }); @@ -1510,19 +1549,29 @@ describe("qweb parser", () => { type: ASTType.TComponent, name: "MyComponent", dynamicProps: null, - props: {}, + props: null, isDynamic: false, on: null, slots: { default: { + attrs: null, + on: null, + scope: null, content: { type: ASTType.TComponent, isDynamic: false, name: "Child", dynamicProps: null, - props: {}, + props: null, on: null, - slots: { brol: { content: { type: ASTType.Text, value: "coucou" } } }, + slots: { + brol: { + content: { type: ASTType.Text, value: "coucou" }, + attrs: null, + scope: null, + on: null, + }, + }, }, }, }, @@ -1541,19 +1590,29 @@ describe("qweb parser", () => { type: ASTType.TComponent, name: "MyComponent", dynamicProps: null, - props: {}, + props: null, isDynamic: false, on: null, slots: { default: { + attrs: null, + on: null, + scope: null, content: { type: ASTType.TComponent, isDynamic: false, name: "Child", dynamicProps: null, - props: {}, + props: null, on: null, - slots: { brol: { content: { type: ASTType.Text, value: "coucou" } } }, + slots: { + brol: { + content: { type: ASTType.Text, value: "coucou" }, + attrs: null, + on: null, + scope: null, + }, + }, }, }, }, @@ -1568,7 +1627,7 @@ describe("qweb parser", () => { expect(parse(``)).toEqual({ type: ASTType.TSlot, name: "default", - attrs: {}, + attrs: null, on: null, defaultContent: null, }); @@ -1578,7 +1637,7 @@ describe("qweb parser", () => { expect(parse(`default content`)).toEqual({ type: ASTType.TSlot, name: "header", - attrs: {}, + attrs: null, on: null, defaultContent: { type: ASTType.Text, value: "default content" }, }); @@ -1588,7 +1647,7 @@ describe("qweb parser", () => { expect(parse(``)).toEqual({ type: ASTType.TSlot, name: "default", - attrs: {}, + attrs: null, on: { "click.prevent": "doSomething" }, defaultContent: null, }); @@ -1605,8 +1664,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "div", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -1623,8 +1682,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "div", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: null, model: null, ns: null, @@ -1642,8 +1701,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "div", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: "name", model: null, ns: null, @@ -1656,8 +1715,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "div", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: "name", model: null, ns: null, @@ -1672,8 +1731,8 @@ describe("qweb parser", () => { type: ASTType.DomNode, tag: "div", dynamicTag: null, - attrs: {}, - on: {}, + attrs: null, + on: null, ref: "name", model: null, ns: null, @@ -1708,14 +1767,14 @@ describe("qweb parser", () => { ).toEqual({ body: { content: { - attrs: {}, + attrs: null, content: [ { type: ASTType.Text, value: "word", }, ], - on: {}, + on: null, ref: null, model: null, tag: "div", @@ -1743,9 +1802,9 @@ describe("qweb parser", () => { test("t-model", async () => { expect(parse(``)).toEqual({ type: ASTType.DomNode, - attrs: {}, + attrs: null, content: [], - on: {}, + on: null, ref: null, tag: "input", dynamicTag: null, @@ -1756,15 +1815,16 @@ describe("qweb parser", () => { eventType: "input", shouldNumberize: false, shouldTrim: false, + hasDynamicChildren: false, targetAttr: "value", specialInitTargetAttr: null, }, }); expect(parse(``)).toEqual({ type: ASTType.DomNode, - attrs: {}, + attrs: null, content: [], - on: {}, + on: null, ref: null, tag: "input", dynamicTag: null, @@ -1777,13 +1837,14 @@ describe("qweb parser", () => { shouldTrim: false, targetAttr: "value", specialInitTargetAttr: null, + hasDynamicChildren: false, }, }); expect(parse(``)).toEqual({ type: ASTType.DomNode, - attrs: {}, + attrs: null, content: [], - on: {}, + on: null, ref: null, tag: "input", dynamicTag: null, @@ -1795,15 +1856,16 @@ describe("qweb parser", () => { shouldNumberize: true, shouldTrim: true, targetAttr: "value", + hasDynamicChildren: false, specialInitTargetAttr: null, }, }); }); expect(parse(`