From 8c167904711a5feee602ab20b25d1f3119d69942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Sat, 13 Nov 2021 09:02:34 +0100 Subject: [PATCH] [REF] move app and compiler code around --- src/{ => app}/app.ts | 10 +- src/{compiler => app}/template_helpers.ts | 4 +- src/compiler/code_generator.ts | 168 ++++++++++------------ src/compiler/index.ts | 26 ++++ src/component/component_node.ts | 2 +- src/index.ts | 2 +- src/tags.ts | 2 +- tests/compiler/event_handling.test.ts | 2 +- tests/compiler/t_ref.test.ts | 2 +- tests/compiler/validation.test.ts | 2 +- tests/components/props_validation.test.ts | 2 +- tests/helpers.ts | 7 +- 12 files changed, 116 insertions(+), 113 deletions(-) rename src/{ => app}/app.ts (86%) rename src/{compiler => app}/template_helpers.ts (97%) create mode 100644 src/compiler/index.ts diff --git a/src/app.ts b/src/app/app.ts similarity index 86% rename from src/app.ts rename to src/app/app.ts index 7950e0f2..7217f658 100644 --- a/src/app.ts +++ b/src/app/app.ts @@ -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 diff --git a/src/compiler/template_helpers.ts b/src/app/template_helpers.ts similarity index 97% rename from src/compiler/template_helpers.ts rename to src/app/template_helpers.ts index 32180646..d2bb2115 100644 --- a/src/compiler/template_helpers.ts +++ b/src/app/template_helpers.ts @@ -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, diff --git a/src/compiler/code_generator.ts b/src/compiler/code_generator.ts index 3d10510d..b8576e36 100644 --- a/src/compiler/code_generator.ts +++ b/src/compiler/code_generator.ts @@ -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 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"); diff --git a/src/compiler/index.ts b/src/compiler/index.ts new file mode 100644 index 00000000..ff4ffb04 --- /dev/null +++ b/src/compiler/index.ts @@ -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; +} diff --git a/src/component/component_node.ts b/src/component/component_node.ts index d6aa6e7e..b34e261c 100644 --- a/src/component/component_node.ts +++ b/src/component/component_node.ts @@ -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 { diff --git a/src/index.ts b/src/index.ts index 6864d328..154809f3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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"; diff --git a/src/tags.ts b/src/tags.ts index b18c2892..2195f34e 100644 --- a/src/tags.ts +++ b/src/tags.ts @@ -1,5 +1,5 @@ import { registerSheet } from "./component/style"; -import { globalTemplates } from "./compiler/template_helpers"; +import { globalTemplates } from "./app/template_helpers"; // ----------------------------------------------------------------------------- // Global templates diff --git a/tests/compiler/event_handling.test.ts b/tests/compiler/event_handling.test.ts index ee758098..6693cd70 100644 --- a/tests/compiler/event_handling.test.ts +++ b/tests/compiler/event_handling.test.ts @@ -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"; diff --git a/tests/compiler/t_ref.test.ts b/tests/compiler/t_ref.test.ts index eeab5ad5..96979094 100644 --- a/tests/compiler/t_ref.test.ts +++ b/tests/compiler/t_ref.test.ts @@ -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"; diff --git a/tests/compiler/validation.test.ts b/tests/compiler/validation.test.ts index a057b313..7c9b894e 100644 --- a/tests/compiler/validation.test.ts +++ b/tests/compiler/validation.test.ts @@ -1,4 +1,4 @@ -import { TemplateSet } from "../../src/compiler/template_helpers"; +import { TemplateSet } from "../../src/app/template_helpers"; import { renderToString, TestContext, compile } from "../helpers"; // ----------------------------------------------------------------------------- diff --git a/tests/components/props_validation.test.ts b/tests/components/props_validation.test.ts index fb77debd..c6a260a5 100644 --- a/tests/components/props_validation.test.ts +++ b/tests/components/props_validation.test.ts @@ -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; diff --git a/tests/helpers.ts b/tests/helpers.ts index 2137a2cf..167a3c53 100644 --- a/tests/helpers.ts +++ b/tests/helpers.ts @@ -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(); }