module Bezel::Templates

The template compiler: HEEx-like templates (*.html.bz) made into Quarz code over the HTML DSL (Bezel.html), which bezel templates writes to src/views/generated/ and the application compiles as its own code. Nothing is interpreted at run time; the generated code is type-checked as any other, and escapes as the DSL does.

src = Bezel::Templates::Source.new("src/views/user/show.html.bz", "show",
  "{@args name: String}\n<h1 class=\"title\">Hello, {name}!</h1>\n")
Bezel::Templates.compile(src).lines[1].strip   # => "def self.show(name: String) -> Bezel::Html"

A template is HTML with:

Elements are the DSL's (an unknown one is an error) and are closed explicitly (<br> or <br/> for a void element, <div/> for an empty one). An attribute is one of the element's own, a global one, class, data-* and aria-*, or a name with a - or a : (written as it is, attrs:); another name is an error, as a misspelled one would be. A boolean attribute is written bare (<input required>) or with an expression (disabled={locked}), an integer one with digits or an expression. script and style hold their text as it is (what the DSL would refuse in it is an error here); textarea and title hold text and expressions. Entities in texts and attribute values are decoded (the DSL escapes again): the numeric ones, and the named ones of Templates::ENTITIES (another is an error; write the character). Whitespace (ASCII: not &nbsp;) with a line break is one line break (all of it is kept inside pre and textarea), and the whitespace at the template's start and end is dropped, as at a component's or a slot's (but inside pre and textarea).

An expression's code goes in the generated code as written, in parentheses, so it is one expression without # comments, heredocs, lines starting with __END__ or =begin, or backslashes outside literals; a / (or a % that could start a percent literal, %w[) is an operator with a space on each side after a value (a.size / 2, n % 3), or starts a regular expression or a percent literal after (, [, a comma, the opening brace, or one of = == =~ !~ => && || or a keyword argument's colon spaced ({name.gsub(/\s/, "")}, {%w[a b]}, {s =~ /x/}): the compiler must see where the code ends as Quarz will, and refuses what it cannot. The generated code names the builder h and the slots' contents __bz_slot_<n>, which the template's own names avoid.

A template that cannot compile is a Templates::Error naming its path and line.

src/templates.qz:76

Classes and modules

Constants

ENTITIES: Hash[String, String]

The named character references a template may use (with the numeric ones): those of markup, spaces and the common symbols.

src/templates.qz:172

Class methods

def self.compile_module(module_name: String, sources: Array[Bezel::Templates::Source]) -> String

The generated file of module module_name, a method per template (in the order given): a header naming each template with the SHA-256 of its text, then the module.

src/templates.qz:89

def self.compile(source: Bezel::Templates::Source) -> String

The method source becomes (def self.<name>(<args>) -> Bezel::Html), indented for a module's body.

src/templates.qz:114

def self.digest(text: String) -> String

The SHA-256 of text, in hexadecimal: the header's digest.

src/templates.qz:138

def self.digests(generated: String) -> Hash[String, String]

The digests a generated file's header names, by template path: what it was generated from.

src/templates.qz:142