[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 { 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,
+72 -96
View File
@@ -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");
+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 { Component } from "./component";
import {
+1 -1
View File
@@ -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
View File
@@ -1,5 +1,5 @@
import { registerSheet } from "./component/style";
import { globalTemplates } from "./compiler/template_helpers";
import { globalTemplates } from "./app/template_helpers";
// -----------------------------------------------------------------------------
// 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 { 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 { 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";
// -----------------------------------------------------------------------------
+1 -1
View File
@@ -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
View File
@@ -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();
}