Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 50 additions & 17 deletions src/compiler/code_generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import {
} from "./parser";
import { OwlError } from "../common/owl_error";

const zero = Symbol("zero");

type BlockType = "block" | "text" | "multi" | "list" | "html" | "comment";
const whitespaceRE = /\s+/g;

Expand Down Expand Up @@ -279,7 +281,7 @@ export class CodeGenerator {
translatableAttributes: string[] = TRANSLATABLE_ATTRS;
ast: AST;
staticDefs: { id: string; expr: string }[] = [];
slotNames: Set<String> = new Set();
slotNames: Set<String | Symbol> = new Set();
helpers: Set<string> = new Set();

constructor(ast: AST, options: CodeGenOptions) {
Expand Down Expand Up @@ -791,12 +793,22 @@ export class CodeGenerator {
return block!.varName;
}

compileZero() {
this.helpers.add("zero");
const isMultiple = this.slotNames.has(zero);
this.slotNames.add(zero);
let key = this.target.loopLevel ? `key${this.target.loopLevel}` : "key";
if (isMultiple) {
key = this.generateComponentKey(key);
}
return `ctx[zero]?.(node, ${key})`;
}

compileTEsc(ast: ASTTEsc, ctx: Context): string {
let { block, forceNewBlock } = ctx;
let expr: string;
if (ast.expr === "0") {
this.helpers.add("zero");
expr = `ctx[zero]`;
expr = this.compileZero();
} else {
expr = compileExpr(ast.expr);
if (ast.defaultValue) {
Expand Down Expand Up @@ -824,8 +836,7 @@ export class CodeGenerator {
block = this.createBlock(block, "html", ctx);
let blockStr;
if (ast.expr === "0") {
this.helpers.add("zero");
blockStr = `ctx[zero]`;
blockStr = this.compileZero();
} else if (ast.body) {
let bodyValue = null;
bodyValue = BlockDescription.nextBlockId;
Expand Down Expand Up @@ -1044,8 +1055,12 @@ export class CodeGenerator {

compileTCall(ast: ASTTCall, ctx: Context): string {
let { block, forceNewBlock } = ctx;

const attrs: string[] = ast.attrs
? this.formatPropObject(ast.attrs, ast.attrsTranslationCtx, ctx.translationCtx)
: [];
let ctxVar = ctx.ctxVar || "ctx";
if (ast.context) {
if (!ast.attrs && ast.context) {
ctxVar = generateId("ctx");
this.addLine(`let ${ctxVar} = ${compileExpr(ast.context)};`);
}
Expand All @@ -1056,38 +1071,56 @@ export class CodeGenerator {
}
block = this.createBlock(block, "multi", ctx);
if (ast.body) {
this.addLine(`${ctxVar} = Object.create(${ctxVar});`);
this.addLine(`${ctxVar}[isBoundary] = 1;`);
this.helpers.add("isBoundary");
const subCtx = createContext(ctx, { ctxVar });
const bl = this.compileMulti({ type: ASTType.Multi, content: ast.body }, subCtx);
if (bl) {
if (ast.attrs) {
const ctxStr = generateId("ctx");
this.helpers.add("capture");
this.define(ctxStr, `capture(ctx)`);
const name = this.compileInNewTarget("callBody", ast.body, ctx);
const zeroStr = generateId("lazyBlock");
this.define(zeroStr, `${name}.bind(this, Object.create(${ctxStr}))`);
this.helpers.add("zero");
this.addLine(`${ctxVar}[zero] = ${bl};`);
attrs.push(`[zero]: ${zeroStr}`);
} else {
this.addLine(`${ctxVar} = Object.create(${ctxVar});`);
this.addLine(`${ctxVar}[isBoundary] = 1;`);
this.helpers.add("isBoundary");
const subCtx = createContext(ctx, { ctxVar });
const bl = this.compileAST(ast.body, subCtx);
if (bl) {
this.helpers.add("zero");
this.addLine(`${ctxVar}[zero] = () => ${bl};`);
}
}
}

let ctxString = `{${attrs.join(", ")}}`;
if (ast.attrs && ast.context) {
const dynCtxVar = generateId("ctx");
this.addLine(`const ${dynCtxVar} = ${compileExpr(ast.context)};`);
ctxString = `Object.assign({}, ${dynCtxVar}${attrs.length ? ", " + ctxString : ""})`;
}
const ctxExpr = ast.attrs ? ctxString : ctxVar;
const key = this.generateComponentKey();
if (isDynamic) {
const templateVar = generateId("template");
if (!this.staticDefs.find((d) => d.id === "call")) {
this.staticDefs.push({ id: "call", expr: `app.callTemplate.bind(app)` });
}
this.define(templateVar, subTemplate);
this.insertBlock(`call(this, ${templateVar}, ${ctxVar}, node, ${key})`, block!, {
this.insertBlock(`call(this, ${templateVar}, ${ctxExpr}, node, ${key})`, block!, {
...ctx,
forceNewBlock: !block,
});
} else {
const id = generateId(`callTemplate_`);
this.staticDefs.push({ id, expr: `app.getTemplate(${subTemplate})` });
this.insertBlock(`${id}.call(this, ${ctxVar}, node, ${key})`, block!, {
this.insertBlock(`${id}.call(this, ${ctxExpr}, node, ${key})`, block!, {
...ctx,
forceNewBlock: !block,
});
}
if (ast.body && !ctx.isLast) {
this.addLine(`${ctxVar} = ${ctxVar}.__proto__;`);
if (!ast.attrs && ast.body && !ctx.isLast) {
this.addLine(`${ctxExpr} = ${ctxExpr}.__proto__;`);
}
return block.varName;
}
Expand Down
43 changes: 36 additions & 7 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,9 @@ export interface ASTTKey extends BaseAST {
export interface ASTTCall extends BaseAST {
type: ASTType.TCall;
name: string;
body: AST[] | null;
attrs: Attrs | null;
attrsTranslationCtx: Attrs | null;
body: AST | null;
context: string | null;
}

Expand Down Expand Up @@ -257,10 +259,10 @@ function parseNode(node: Node, ctx: ParsingContext): AST | null {
parseTForEach(node, ctx) ||
parseTIf(node, ctx) ||
parseTPortal(node, ctx) ||
parseTCall(node, ctx) ||
parseTCallBlock(node, ctx) ||
parseTTranslation(node, ctx) ||
parseTTranslationContext(node, ctx) ||
parseTCall(node, ctx) ||
parseTCallBlock(node, ctx) ||
parseTKey(node, ctx) ||
parseTEscNode(node, ctx) ||
parseTOutNode(node, ctx) ||
Expand Down Expand Up @@ -642,9 +644,35 @@ function parseTCall(node: Element, ctx: ParsingContext): AST | null {
node.removeAttribute("t-call");
node.removeAttribute("t-call-context");

let attrs: Attrs | null = null;
let attrsTranslationCtx: Attrs | null = null;
for (let attributeName of node.getAttributeNames()) {
const value = node.getAttribute(attributeName)!;
if (attributeName.startsWith("t-translation-context-")) {
const attrName = attributeName.slice(22);
attrsTranslationCtx = attrsTranslationCtx || {};
attrsTranslationCtx[attrName] = value;
} else {
attrs = attrs || {};
attrs[attributeName] = value;
}
}

if (node.tagName !== "t") {
if (attrs) {
throw new OwlError(
`Directive 't-call' with params can only be used on <t> nodes (used on a <${node.tagName}>)`
);
}
const ast = parseNode(node, ctx);
const tcall: AST = { type: ASTType.TCall, name: subTemplate, body: null, context };
const tcall: AST = {
type: ASTType.TCall,
name: subTemplate,
attrs: null,
attrsTranslationCtx: null,
body: null,
context,
};
if (ast && ast.type === ASTType.DomNode) {
ast.content = [tcall];
return ast;
Expand All @@ -664,12 +692,13 @@ function parseTCall(node: Element, ctx: ParsingContext): AST | null {
};
}
}
const body = parseChildren(node, ctx);

const body = parseChildNodes(node, ctx);
return {
type: ASTType.TCall,
name: subTemplate,
body: body.length ? body : null,
attrs,
attrsTranslationCtx,
body,
context,
};
}
Expand Down
4 changes: 2 additions & 2 deletions tests/compiler/__snapshots__/misc.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ exports[`misc global 1`] = `
setContextValue(ctx, \\"foo\\", 'bbb');
const b9 = callTemplate_3.call(this, ctx, node, key + \`__3__\${key1}\`);
const b6 = multi([b7, b8, b9]);
ctx[zero] = b6;
ctx[zero] = () => b6;
const b5 = callTemplate_4.call(this, ctx, node, key + \`__4__\${key1}\`);
ctx = ctx.__proto__;
c_block2[i1] = withKey(multi([b4, b5]), key1);
Expand Down Expand Up @@ -156,7 +156,7 @@ exports[`misc global 3`] = `
return function template(ctx, node, key = \\"\\") {
let attr1 = 'agüero';
const b2 = ctx[zero];
const b2 = ctx[zero]?.(node, key);
return block1([attr1], [b2]);
}
}"
Expand Down
Loading
Loading