mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[REF] move app and compiler code around
This commit is contained in:
committed by
Aaron Bohy
parent
9b8c582b32
commit
8c16790471
@@ -1,8 +1,8 @@
|
||||
import { Component } from "./component/component";
|
||||
import { ComponentNode } from "./component/component_node";
|
||||
import { MountOptions } from "./component/fibers";
|
||||
import { Scheduler } from "./component/scheduler";
|
||||
import { TemplateSet } from "./compiler/template_helpers";
|
||||
import { Component } from "../component/component";
|
||||
import { ComponentNode } from "../component/component_node";
|
||||
import { MountOptions } from "../component/fibers";
|
||||
import { Scheduler } from "../component/scheduler";
|
||||
import { TemplateSet } from "./template_helpers";
|
||||
|
||||
// reimplement dev mode stuff see last change in 0f7a8289a6fb8387c3c1af41c6664b2a8448758f
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BDom, createBlock, html, list, multi, text, toggler } from "../blockdom";
|
||||
import { compileTemplate, Template } from "./code_generator";
|
||||
import { compile, Template } from "../compiler";
|
||||
import { component } from "../component/component_node";
|
||||
import { validateProps } from "../component/props_validation";
|
||||
|
||||
@@ -130,7 +130,7 @@ export class TemplateSet {
|
||||
if (rawTemplate === undefined) {
|
||||
throw new Error(`Missing template: "${name}"`);
|
||||
}
|
||||
const templateFn = compileTemplate(rawTemplate, {
|
||||
const templateFn = compile(rawTemplate, {
|
||||
name,
|
||||
dev: this.dev,
|
||||
translateFn: this.translateFn,
|
||||
@@ -1,4 +1,3 @@
|
||||
import { BDom } from "../blockdom";
|
||||
import { compileExpr, compileExprToArray, interpolate, INTERP_REGEXP } from "./inline_expressions";
|
||||
import {
|
||||
AST,
|
||||
@@ -20,24 +19,18 @@ import {
|
||||
ASTTSet,
|
||||
ASTTranslation,
|
||||
ASTType,
|
||||
parse,
|
||||
} from "./parser";
|
||||
|
||||
export type Template = (context: any, vnode: any, key?: string) => BDom;
|
||||
export type TemplateFunction = (blocks: any, utils: any) => Template;
|
||||
|
||||
type BlockType = "block" | "text" | "multi" | "list" | "html";
|
||||
|
||||
export interface CompileOptions {
|
||||
name?: string;
|
||||
export interface Config {
|
||||
translateFn?: (s: string) => string;
|
||||
translatableAttributes?: string[];
|
||||
dev?: boolean;
|
||||
}
|
||||
|
||||
export function compileTemplate(template: string, options?: CompileOptions): TemplateFunction {
|
||||
const compiler = new QWebCompiler(template, options);
|
||||
return compiler.compile();
|
||||
export interface CodeGenOptions extends Config {
|
||||
hasSafeContext?: boolean;
|
||||
}
|
||||
|
||||
// using a non-html document so that <inner/outer>HTML serializes as XML instead
|
||||
@@ -165,47 +158,37 @@ class CodeTarget {
|
||||
}
|
||||
}
|
||||
|
||||
export const TRANSLATABLE_ATTRS = ["label", "title", "placeholder", "alt"];
|
||||
const TRANSLATABLE_ATTRS = ["label", "title", "placeholder", "alt"];
|
||||
const translationRE = /^(\s*)([\s\S]+?)(\s*)$/;
|
||||
|
||||
export class QWebCompiler {
|
||||
export class CodeGenerator {
|
||||
blocks: BlockDescription[] = [];
|
||||
nextId = 1;
|
||||
nextBlockId = 1;
|
||||
shouldProtectScope: boolean = false;
|
||||
shouldDefineAssign: boolean = false;
|
||||
hasSafeContext: boolean | null = null;
|
||||
hasSafeContext: boolean;
|
||||
hasRef: boolean = false;
|
||||
// hasTCall: boolean = false;
|
||||
isDebug: boolean = false;
|
||||
functions: CodeTarget[] = [];
|
||||
target = new CodeTarget("main");
|
||||
templateName: string;
|
||||
template: string;
|
||||
dev: boolean;
|
||||
translateFn: (s: string) => string;
|
||||
translatableAttributes: string[];
|
||||
ast: AST;
|
||||
staticCalls: { id: string; template: string }[] = [];
|
||||
|
||||
constructor(template: string, options: CompileOptions = {}) {
|
||||
this.template = template;
|
||||
constructor(name: string, ast: AST, options: CodeGenOptions) {
|
||||
this.translateFn = options.translateFn || ((s: string) => s);
|
||||
this.translatableAttributes = options.translatableAttributes || TRANSLATABLE_ATTRS;
|
||||
this.hasSafeContext = options.hasSafeContext || false;
|
||||
this.dev = options.dev || false;
|
||||
this.ast = parse(template);
|
||||
if (options.name) {
|
||||
this.templateName = options.name;
|
||||
} else {
|
||||
if (template.length > 250) {
|
||||
this.templateName = template.slice(0, 250) + "...";
|
||||
} else {
|
||||
this.templateName = template;
|
||||
}
|
||||
}
|
||||
this.ast = ast;
|
||||
this.templateName = name;
|
||||
}
|
||||
|
||||
compile(): TemplateFunction {
|
||||
generateCode(): string {
|
||||
const ast = this.ast;
|
||||
this.isDebug = ast.type === ASTType.TDebug;
|
||||
BlockDescription.nextBlockId = 1;
|
||||
@@ -218,72 +201,7 @@ export class QWebCompiler {
|
||||
translate: true,
|
||||
tKeyExpr: null,
|
||||
});
|
||||
const code = this.generateCode();
|
||||
return new Function("bdom, helpers", code) as TemplateFunction;
|
||||
}
|
||||
|
||||
addLine(line: string) {
|
||||
this.target.addLine(line);
|
||||
}
|
||||
|
||||
generateId(prefix: string = ""): string {
|
||||
return `${prefix}${this.nextId++}`;
|
||||
}
|
||||
|
||||
generateBlockName(): string {
|
||||
return `block${this.blocks.length + 1}`;
|
||||
}
|
||||
|
||||
insertAnchor(block: BlockDescription) {
|
||||
const tag = `block-child-${block.children.length}`;
|
||||
const anchor = xmlDoc.createElement(tag);
|
||||
block.insert(anchor);
|
||||
}
|
||||
|
||||
createBlock(
|
||||
parentBlock: BlockDescription | null,
|
||||
type: BlockType,
|
||||
ctx: Context
|
||||
): BlockDescription {
|
||||
const hasRoot = this.target.hasRoot;
|
||||
const block = new BlockDescription(this.target, type);
|
||||
if (!hasRoot && !ctx.preventRoot) {
|
||||
this.target.hasRoot = true;
|
||||
block.isRoot = true;
|
||||
}
|
||||
if (parentBlock) {
|
||||
parentBlock.children.push(block);
|
||||
if (parentBlock.type === "list") {
|
||||
block.parentVar = `c_block${parentBlock.id}`;
|
||||
}
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
insertBlock(expression: string, block: BlockDescription, ctx: Context): void {
|
||||
let blockExpr = block.generateExpr(expression);
|
||||
const tKeyExpr = ctx.tKeyExpr;
|
||||
if (block.parentVar) {
|
||||
let keyArg = `key${this.target.loopLevel}`;
|
||||
if (tKeyExpr) {
|
||||
keyArg = `${tKeyExpr} + ${keyArg}`;
|
||||
}
|
||||
this.addLine(`${block.parentVar}[${ctx.index}] = withKey(${blockExpr}, ${keyArg});`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tKeyExpr) {
|
||||
blockExpr = `toggler(${tKeyExpr}, ${blockExpr})`;
|
||||
}
|
||||
|
||||
if (block.isRoot && !ctx.preventRoot) {
|
||||
this.addLine(`return ${blockExpr};`);
|
||||
} else {
|
||||
this.addLine(`let ${block.varName} = ${blockExpr};`);
|
||||
}
|
||||
}
|
||||
|
||||
generateCode(): string {
|
||||
let mainCode = this.target.code;
|
||||
this.target.code = [];
|
||||
this.target.indentLevel = 0;
|
||||
@@ -353,6 +271,67 @@ export class QWebCompiler {
|
||||
return code;
|
||||
}
|
||||
|
||||
addLine(line: string) {
|
||||
this.target.addLine(line);
|
||||
}
|
||||
|
||||
generateId(prefix: string = ""): string {
|
||||
return `${prefix}${this.nextId++}`;
|
||||
}
|
||||
|
||||
generateBlockName(): string {
|
||||
return `block${this.blocks.length + 1}`;
|
||||
}
|
||||
|
||||
insertAnchor(block: BlockDescription) {
|
||||
const tag = `block-child-${block.children.length}`;
|
||||
const anchor = xmlDoc.createElement(tag);
|
||||
block.insert(anchor);
|
||||
}
|
||||
|
||||
createBlock(
|
||||
parentBlock: BlockDescription | null,
|
||||
type: BlockType,
|
||||
ctx: Context
|
||||
): BlockDescription {
|
||||
const hasRoot = this.target.hasRoot;
|
||||
const block = new BlockDescription(this.target, type);
|
||||
if (!hasRoot && !ctx.preventRoot) {
|
||||
this.target.hasRoot = true;
|
||||
block.isRoot = true;
|
||||
}
|
||||
if (parentBlock) {
|
||||
parentBlock.children.push(block);
|
||||
if (parentBlock.type === "list") {
|
||||
block.parentVar = `c_block${parentBlock.id}`;
|
||||
}
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
insertBlock(expression: string, block: BlockDescription, ctx: Context): void {
|
||||
let blockExpr = block.generateExpr(expression);
|
||||
const tKeyExpr = ctx.tKeyExpr;
|
||||
if (block.parentVar) {
|
||||
let keyArg = `key${this.target.loopLevel}`;
|
||||
if (tKeyExpr) {
|
||||
keyArg = `${tKeyExpr} + ${keyArg}`;
|
||||
}
|
||||
this.addLine(`${block.parentVar}[${ctx.index}] = withKey(${blockExpr}, ${keyArg});`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tKeyExpr) {
|
||||
blockExpr = `toggler(${tKeyExpr}, ${blockExpr})`;
|
||||
}
|
||||
|
||||
if (block.isRoot && !ctx.preventRoot) {
|
||||
this.addLine(`return ${blockExpr};`);
|
||||
} else {
|
||||
this.addLine(`let ${block.varName} = ${blockExpr};`);
|
||||
}
|
||||
}
|
||||
|
||||
generateFunctions(fn: CodeTarget) {
|
||||
this.addLine("");
|
||||
this.addLine(`const ${fn.name} = ${fn.signature}`);
|
||||
@@ -992,9 +971,6 @@ export class QWebCompiler {
|
||||
const hasSlot = !!Object.keys(ast.slots).length;
|
||||
let slotDef: string;
|
||||
if (hasSlot) {
|
||||
if (this.hasSafeContext === null) {
|
||||
this.hasSafeContext = !this.template.includes("t-set") && !this.template.includes("t-call");
|
||||
}
|
||||
let ctxStr = "ctx";
|
||||
if (this.target.loopLevel || !this.hasSafeContext) {
|
||||
ctxStr = this.generateId("ctx");
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import type { BDom } from "../blockdom";
|
||||
import { CodeGenerator, Config } from "./code_generator";
|
||||
import { parse } from "./parser";
|
||||
|
||||
export type Template = (context: any, vnode: any, key?: string) => BDom;
|
||||
|
||||
export type TemplateFunction = (blocks: any, utils: any) => Template;
|
||||
|
||||
interface CompileOptions extends Config {
|
||||
name?: string;
|
||||
}
|
||||
export function compile(template: string, options: CompileOptions = {}): TemplateFunction {
|
||||
// parsing
|
||||
const ast = parse(template);
|
||||
|
||||
// some work
|
||||
const hasSafeContext = !template.includes("t-set") && !template.includes("t-call");
|
||||
const name = options.name || (template.length > 250 ? template.slice(0, 250) + "..." : template);
|
||||
|
||||
// code generation
|
||||
const codeGenerator = new CodeGenerator(name, ast, { ...options, hasSafeContext });
|
||||
const code = codeGenerator.generateCode();
|
||||
|
||||
// template function
|
||||
return new Function("bdom, helpers", code) as TemplateFunction;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { App } from "../app";
|
||||
import type { App } from "../app/app";
|
||||
import { BDom, VNode } from "../blockdom";
|
||||
import { Component } from "./component";
|
||||
import {
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ export const blockDom = {
|
||||
};
|
||||
|
||||
// import { makeBlockClass } from "./_old_bdom/element";
|
||||
import { App } from "./app";
|
||||
import { App } from "./app/app";
|
||||
import { Component } from "./component/component";
|
||||
import { getCurrent } from "./component/component_node";
|
||||
// import { getCurrent } from "./b_node";
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { registerSheet } from "./component/style";
|
||||
import { globalTemplates } from "./compiler/template_helpers";
|
||||
import { globalTemplates } from "./app/template_helpers";
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Global templates
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TemplateSet } from "../../src/compiler/template_helpers";
|
||||
import { TemplateSet } from "../../src/app/template_helpers";
|
||||
import { mount } from "../../src/blockdom";
|
||||
import { makeTestFixture, renderToBdom, renderToString, snapshotEverything } from "../helpers";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TemplateSet } from "../../src/compiler/template_helpers";
|
||||
import { TemplateSet } from "../../src/app/template_helpers";
|
||||
import { mount } from "../../src/blockdom";
|
||||
import { renderToBdom, snapshotEverything } from "../helpers";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TemplateSet } from "../../src/compiler/template_helpers";
|
||||
import { TemplateSet } from "../../src/app/template_helpers";
|
||||
import { renderToString, TestContext, compile } from "../helpers";
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { makeTestFixture, snapshotApp } from "../helpers";
|
||||
import { Component, xml } from "../../src";
|
||||
import { App, DEV_MSG } from "../../src/app";
|
||||
import { App, DEV_MSG } from "../../src/app/app";
|
||||
import { validateProps } from "../../src/component/props_validation";
|
||||
|
||||
let fixture: HTMLElement;
|
||||
|
||||
+4
-3
@@ -14,8 +14,9 @@ import {
|
||||
} from "../src";
|
||||
import { BDom } from "../src/blockdom";
|
||||
import { blockDom } from "../src";
|
||||
import { CompileOptions, compileTemplate, Template } from "../src/compiler/code_generator";
|
||||
import { globalTemplates, TemplateSet, UTILS } from "../src/compiler/template_helpers";
|
||||
import { compile as compileTemplate, Template } from "../src/compiler";
|
||||
import { CodeGenOptions } from "../src/compiler/code_generator";
|
||||
import { globalTemplates, TemplateSet, UTILS } from "../src/app/template_helpers";
|
||||
import { xml } from "../src/tags";
|
||||
|
||||
const mount = blockDom.mount;
|
||||
@@ -36,7 +37,7 @@ export function makeTestFixture() {
|
||||
return fixture;
|
||||
}
|
||||
|
||||
export function snapshotTemplateCode(template: string, options?: CompileOptions) {
|
||||
export function snapshotTemplateCode(template: string, options?: CodeGenOptions) {
|
||||
expect(compileTemplate(template, options).toString()).toMatchSnapshot();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user