[REF] move app and compiler code around

This commit is contained in:
Géry Debongnie
2021-11-13 09:02:34 +01:00
committed by Samuel Degueldre
parent 06ea6d2490
commit a187a376a0
12 changed files with 116 additions and 113 deletions
+5 -5
View File
@@ -1,8 +1,8 @@
import { Component } from "./component/component"; import { Component } from "../component/component";
import { ComponentNode } from "./component/component_node"; import { ComponentNode } from "../component/component_node";
import { MountOptions } from "./component/fibers"; import { MountOptions } from "../component/fibers";
import { Scheduler } from "./component/scheduler"; import { Scheduler } from "../component/scheduler";
import { TemplateSet } from "./compiler/template_helpers"; import { TemplateSet } from "./template_helpers";
// reimplement dev mode stuff see last change in 0f7a8289a6fb8387c3c1af41c6664b2a8448758f // reimplement dev mode stuff see last change in 0f7a8289a6fb8387c3c1af41c6664b2a8448758f
@@ -1,5 +1,5 @@
import { BDom, createBlock, html, list, multi, text, toggler } from "../blockdom"; 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 { component } from "../component/component_node";
import { validateProps } from "../component/props_validation"; import { validateProps } from "../component/props_validation";
@@ -130,7 +130,7 @@ export class TemplateSet {
if (rawTemplate === undefined) { if (rawTemplate === undefined) {
throw new Error(`Missing template: "${name}"`); throw new Error(`Missing template: "${name}"`);
} }
const templateFn = compileTemplate(rawTemplate, { const templateFn = compile(rawTemplate, {
name, name,
dev: this.dev, dev: this.dev,
translateFn: this.translateFn, translateFn: this.translateFn,
+72 -96
View File
@@ -1,4 +1,3 @@
import { BDom } from "../blockdom";
import { compileExpr, compileExprToArray, interpolate, INTERP_REGEXP } from "./inline_expressions"; import { compileExpr, compileExprToArray, interpolate, INTERP_REGEXP } from "./inline_expressions";
import { import {
AST, AST,
@@ -20,24 +19,18 @@ import {
ASTTSet, ASTTSet,
ASTTranslation, ASTTranslation,
ASTType, ASTType,
parse,
} from "./parser"; } 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"; type BlockType = "block" | "text" | "multi" | "list" | "html";
export interface CompileOptions { export interface Config {
name?: string;
translateFn?: (s: string) => string; translateFn?: (s: string) => string;
translatableAttributes?: string[]; translatableAttributes?: string[];
dev?: boolean; dev?: boolean;
} }
export function compileTemplate(template: string, options?: CompileOptions): TemplateFunction { export interface CodeGenOptions extends Config {
const compiler = new QWebCompiler(template, options); hasSafeContext?: boolean;
return compiler.compile();
} }
// using a non-html document so that <inner/outer>HTML serializes as XML instead // 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*)$/; const translationRE = /^(\s*)([\s\S]+?)(\s*)$/;
export class QWebCompiler { export class CodeGenerator {
blocks: BlockDescription[] = []; blocks: BlockDescription[] = [];
nextId = 1; nextId = 1;
nextBlockId = 1; nextBlockId = 1;
shouldProtectScope: boolean = false; shouldProtectScope: boolean = false;
shouldDefineAssign: boolean = false; shouldDefineAssign: boolean = false;
hasSafeContext: boolean | null = null; hasSafeContext: boolean;
hasRef: boolean = false; hasRef: boolean = false;
// hasTCall: boolean = false;
isDebug: boolean = false; isDebug: boolean = false;
functions: CodeTarget[] = []; functions: CodeTarget[] = [];
target = new CodeTarget("main"); target = new CodeTarget("main");
templateName: string; templateName: string;
template: string;
dev: boolean; dev: boolean;
translateFn: (s: string) => string; translateFn: (s: string) => string;
translatableAttributes: string[]; translatableAttributes: string[];
ast: AST; ast: AST;
staticCalls: { id: string; template: string }[] = []; staticCalls: { id: string; template: string }[] = [];
constructor(template: string, options: CompileOptions = {}) { constructor(name: string, ast: AST, options: CodeGenOptions) {
this.template = template;
this.translateFn = options.translateFn || ((s: string) => s); this.translateFn = options.translateFn || ((s: string) => s);
this.translatableAttributes = options.translatableAttributes || TRANSLATABLE_ATTRS; this.translatableAttributes = options.translatableAttributes || TRANSLATABLE_ATTRS;
this.hasSafeContext = options.hasSafeContext || false;
this.dev = options.dev || false; this.dev = options.dev || false;
this.ast = parse(template); this.ast = ast;
if (options.name) { this.templateName = name;
this.templateName = options.name;
} else {
if (template.length > 250) {
this.templateName = template.slice(0, 250) + "...";
} else {
this.templateName = template;
}
}
} }
compile(): TemplateFunction { generateCode(): string {
const ast = this.ast; const ast = this.ast;
this.isDebug = ast.type === ASTType.TDebug; this.isDebug = ast.type === ASTType.TDebug;
BlockDescription.nextBlockId = 1; BlockDescription.nextBlockId = 1;
@@ -218,72 +201,7 @@ export class QWebCompiler {
translate: true, translate: true,
tKeyExpr: null, 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; let mainCode = this.target.code;
this.target.code = []; this.target.code = [];
this.target.indentLevel = 0; this.target.indentLevel = 0;
@@ -353,6 +271,67 @@ export class QWebCompiler {
return code; 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) { generateFunctions(fn: CodeTarget) {
this.addLine(""); this.addLine("");
this.addLine(`const ${fn.name} = ${fn.signature}`); this.addLine(`const ${fn.name} = ${fn.signature}`);
@@ -992,9 +971,6 @@ export class QWebCompiler {
const hasSlot = !!Object.keys(ast.slots).length; const hasSlot = !!Object.keys(ast.slots).length;
let slotDef: string; let slotDef: string;
if (hasSlot) { if (hasSlot) {
if (this.hasSafeContext === null) {
this.hasSafeContext = !this.template.includes("t-set") && !this.template.includes("t-call");
}
let ctxStr = "ctx"; let ctxStr = "ctx";
if (this.target.loopLevel || !this.hasSafeContext) { if (this.target.loopLevel || !this.hasSafeContext) {
ctxStr = this.generateId("ctx"); ctxStr = this.generateId("ctx");
+26
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
import type { App } from "../app"; import type { App } from "../app/app";
import { BDom, VNode } from "../blockdom"; import { BDom, VNode } from "../blockdom";
import { Component } from "./component"; import { Component } from "./component";
import { import {
+1 -1
View File
@@ -31,7 +31,7 @@ export const blockDom = {
}; };
// import { makeBlockClass } from "./_old_bdom/element"; // import { makeBlockClass } from "./_old_bdom/element";
import { App } from "./app"; import { App } from "./app/app";
import { Component } from "./component/component"; import { Component } from "./component/component";
import { getCurrent } from "./component/component_node"; import { getCurrent } from "./component/component_node";
// import { getCurrent } from "./b_node"; // import { getCurrent } from "./b_node";
+1 -1
View File
@@ -1,5 +1,5 @@
import { registerSheet } from "./component/style"; import { registerSheet } from "./component/style";
import { globalTemplates } from "./compiler/template_helpers"; import { globalTemplates } from "./app/template_helpers";
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Global templates // Global templates
+1 -1
View File
@@ -1,4 +1,4 @@
import { TemplateSet } from "../../src/compiler/template_helpers"; import { TemplateSet } from "../../src/app/template_helpers";
import { mount } from "../../src/blockdom"; import { mount } from "../../src/blockdom";
import { makeTestFixture, renderToBdom, renderToString, snapshotEverything } from "../helpers"; import { makeTestFixture, renderToBdom, renderToString, snapshotEverything } from "../helpers";
+1 -1
View File
@@ -1,4 +1,4 @@
import { TemplateSet } from "../../src/compiler/template_helpers"; import { TemplateSet } from "../../src/app/template_helpers";
import { mount } from "../../src/blockdom"; import { mount } from "../../src/blockdom";
import { renderToBdom, snapshotEverything } from "../helpers"; import { renderToBdom, snapshotEverything } from "../helpers";
+1 -1
View File
@@ -1,4 +1,4 @@
import { TemplateSet } from "../../src/compiler/template_helpers"; import { TemplateSet } from "../../src/app/template_helpers";
import { renderToString, TestContext, compile } from "../helpers"; import { renderToString, TestContext, compile } from "../helpers";
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
+1 -1
View File
@@ -1,6 +1,6 @@
import { makeTestFixture, snapshotApp } from "../helpers"; import { makeTestFixture, snapshotApp } from "../helpers";
import { Component, xml } from "../../src"; 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"; import { validateProps } from "../../src/component/props_validation";
let fixture: HTMLElement; let fixture: HTMLElement;
+4 -3
View File
@@ -14,8 +14,9 @@ import {
} from "../src"; } from "../src";
import { BDom } from "../src/blockdom"; import { BDom } from "../src/blockdom";
import { blockDom } from "../src"; import { blockDom } from "../src";
import { CompileOptions, compileTemplate, Template } from "../src/compiler/code_generator"; import { compile as compileTemplate, Template } from "../src/compiler";
import { globalTemplates, TemplateSet, UTILS } from "../src/compiler/template_helpers"; import { CodeGenOptions } from "../src/compiler/code_generator";
import { globalTemplates, TemplateSet, UTILS } from "../src/app/template_helpers";
import { xml } from "../src/tags"; import { xml } from "../src/tags";
const mount = blockDom.mount; const mount = blockDom.mount;
@@ -36,7 +37,7 @@ export function makeTestFixture() {
return fixture; return fixture;
} }
export function snapshotTemplateCode(template: string, options?: CompileOptions) { export function snapshotTemplateCode(template: string, options?: CodeGenOptions) {
expect(compileTemplate(template, options).toString()).toMatchSnapshot(); expect(compileTemplate(template, options).toString()).toMatchSnapshot();
} }