/html-dsl

A Lua DSL for building HTML elements

Primary LanguageLuaApache License 2.0Apache-2.0

What is this?

A DSL for Lua 5.2+ that lets you write Lua code that resembles HTML. When you're done, you can render the tree it builds to actual HTML!

Example

local html = require('html')

local document = html {
  html.head {
    html.style [[
      .red {
        background: #800;
      }
    ]]
  },
  html.body {
    html.p 'hello world',
    html.ul {
      html.li 'foo',
      html.li 'bar',
      html.li {class='red', 'baz'}
    }
  }
}

-- write html to stdout
print(document:render())

How does this abomination work?

Lua supports an alternate function call syntax for unary functions that take either string literals or table constructors:

-- these are equivalent:
print 'hello world'
print('hello world')

-- these are also equivalent:
f {a=1, b=3, c=5}
f({a=1, b=3, c=5})

(Ab)using this this feature, we can bend Lua to do some crazy things.

Reference

html module

html.<element>(data)

Substitute <element> for any valid HTML5 element. When data is a table, its dictionary elements are used as attributes and its sequence elements are used as HTML elements and text. When data is a string, it is used as the only child of the resulting element. data can also be omitted to obtain an element with no children or attributes. The return value is an element.

Example

(html.p()):render()
-- <p></p>

(html.p 'hello world'):render()
-- <p>hello world</p>

(html.ul {class='list', html.li 'a', html.li 'b'}):render()
-- <ul class="list"><li>a</li><li>b</li></ul>

html.rendermixed(t)

Accepts a sequence table of either strings or HTML elements, and passes the strings through untouched while rendering the HTML elements to strings. The result is a concatenated string with the strings and rendered elements combined.

Example

html.rendermixed {'hello ', html.span 'world'}
-- hello <span>world</span>

html(t)

The HTML module is callable. It is equivalent to calling html.html to create an <html> element.

Example

(html {html.body {html.p 'hello world'}}):render()
-- <html><body><p>hello world</p></body></html>

elements

element:render()

Converts the element to an HTML string.

Example

(html.p 'hello world'):render()
-- <p>hello world</p>

element:appendchild(data)

Appends data to the element. data should be either an element or a string.

Example

local para = html.p()
para:appendchild(html.span 'hello')
para:appendchild(' world')
para:render()
-- <p><span>hello</span> world</p>

element:addattr(name, value)

Adds an attribute to the element. Attempting to add an attribute when one already exists with the same name will result in the first one being overwritten. When value is a string, embedded quotation marks will be escaped; otherwise, value remains untouched.

Example

local para = html.p 'hello world'
para:addattr('class', 'shiny')
para:render()
-- <p class="shiny">hello world</p>
local para = html.p()
para:addattr('class', 'old')
para:addattr('class', 'new')
para:render()
-- <p class="new"></p>
local para = html.p()
para:addattr('data-desc', 'it is "cool"')
para:addattr('data-value', 123)
para:render()
-- <p data-desc="it is \"cool\"" data-value="123"></p>

element:opentag()

Renders the opening tag for this element, which includes attributes.

Example

local para = html.p {class='shiny', 'hello world'}
para:opentag()
-- <p class="shiny">

element:closetag()

Renders the closing tag for this element, regardless of whether or not it is a void element.

Example

local para = html.p {class='shiny', 'hello world'}
para:closetag()
-- </p>

html.element.type(t)

Returns the string 'element' if t is a table whose metatable indexes html.element. Otherwise, returns nil.

License

Licensed under the Apache License 2.0. Refer to the LICENSE file for more information.