Skip to content

Commit

Permalink
support web component kebab case tag
Browse files Browse the repository at this point in the history
  • Loading branch information
shahrul committed Oct 14, 2024
1 parent 7680018 commit 368cdd3
Show file tree
Hide file tree
Showing 17 changed files with 73 additions and 37 deletions.
6 changes: 3 additions & 3 deletions .rockspec
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package = "luax"
version = "1.0.3-1"
version = "1.0.4-1"

source = {
url = "https://github.com/syarul/luax/archive/refs/tags/v1.0.3.tar.gz",
dir = "luax-1.0.3"
url = "https://github.com/syarul/luax/archive/refs/tags/v1.0.4.tar.gz",
dir = "luax-1.0.4"
}
description = {
summary = "HTML parse in Lua",
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
## LuaX

LuaX is Lua + XML Syntax extension with builtin decent parse. In retrospect it's akin to React JSX.
LuaX is Lua + XML Syntax extension with built-in decent parse. In retrospect it's akin to React JSX.


<a href="https://luarocks.org/modules/syarul/luax" rel="nofollow"><img alt="Luarocks Package" src="https://img.shields.io/badge/Luarocks-1.0.3-blue.svg" style="max-width:100%;"></a>
<a href="https://luarocks.org/modules/syarul/luax" rel="nofollow"><img alt="Luarocks Package" src="https://img.shields.io/badge/Luarocks-1.0.4-blue.svg" style="max-width:100%;"></a>
[![Lua CI](https://github.com/syarul/luax/actions/workflows/lua.yml/badge.svg)](https://github.com/syarul/luax/actions/workflows/lua.yml)

## Decent Parser
Expand Down Expand Up @@ -88,6 +87,9 @@ return <ul class="filters" _="on load set $filter to me">

See the test folder to see more usage cases.

## Sample Project
[https://github.com/syarul/todomvc-lua-luasocket-htmx-_hyperscript](https://github.com/syarul/todomvc-lua-luasocket-htmx-_hyperscript)

## Caveats

- Since nodeName such `div`, `p`, etc+ are used as declared variables, so do **NOT** declare a function with the same name i.e.,
Expand Down
14 changes: 11 additions & 3 deletions h.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ local function isVoidTag(tag)
return false
end

local function kebabCase(tag)
if not tag:match("^[a-z]") and tag:match("%u%u") and tag:match("[^%w]") then
return tag
end
local kebab = tag:gsub("(%u)", "-%1"):lower()
return kebab:gsub("^%-", "")
end

local function createElement(tag, atts, children)
return {
tag = tag,
Expand Down Expand Up @@ -78,11 +86,11 @@ local function h(element)
end
end
if element.tag:lower() == "doctype" then
return "<!" .. element.tag:lower() .. " " .. table.concat(element.atts, " ") .. ">" .. children
return "<!" .. kebabCase(element.tag:lower()) .. " " .. table.concat(element.atts, " ") .. ">" .. children
elseif isVoidTag(element.tag) then
return "<" .. element.tag .. atts .. ">"
return "<" .. kebabCase(element.tag) .. atts .. ">"
else
return "<" .. element.tag .. atts .. ">" .. children .. "</" .. element.tag .. ">"
return "<" .. kebabCase(element.tag) .. atts .. ">" .. children .. "</" .. kebabCase(element.tag) .. ">"
end
end

Expand Down
22 changes: 19 additions & 3 deletions luax.lua
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ function State:toggle(key, bool)
if bool ~= nil then self[key] = bool else self[key] = not self[key] end
end

local function kebabToCamel(str)
return str:gsub("%-(%w)", function(c)
return c:upper()
end)
end

local function formatDocTypeParams(input)
local result = {}
local cw = ""
Expand Down Expand Up @@ -95,16 +101,22 @@ local function decentParserAST(input)

local nextSpacingPos = input:find("%s", s.pos) or input:find("%>", s.pos)
local tagRange = input:sub(s.pos, nextSpacingPos)
local tagName = tagRange:match("<(%w+)", 0)
local tagNameEnd = tagRange:match("</(%w+)>", 0)
local tagName = tagRange:match("<([%w-]+)", 0)
local tagNameEnd = tagRange:match("</([%w-]+)>", 0)
local tagDocType = tagRange:match("<(%!%w+)", 0)
if tagDocType then
tagName = tagDocType:sub(2)
s.docType = true
docTypeStartPos = s.pos + #tagDocType + 2
s:inc()
end
if tagName then s:incDeepNode() end
if tagName then
if tagName:match("(%-+)") then
tagName = kebabToCamel(tagName)
s:inc()
end
s:incDeepNode()
end
s:inc()

if tagName and not s.deepString then
Expand All @@ -127,6 +139,10 @@ local function decentParserAST(input)
end
s:inc(#tagName)
elseif tagNameEnd then
if tagNameEnd:match("(%-+)") then
tagNameEnd = kebabToCamel(tagNameEnd)
s:inc()
end
s:decDeepNode()
if s.isTag and not s.textNode then
s:toggle("isTag")
Expand Down
2 changes: 1 addition & 1 deletion package.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
return {
name = "syarul/luax",
version = "1.0.3",
version = "1.0.4",
description = "LuaX is Lua + XML Syntax extension with builtin decent parse. In retrospect it's akin to React JSX.",
tags = { "lua", "module", "react", "jsx" },
license = "MIT",
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions test/21_web_component.luax
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
return <fluent-button>Example</fluent-button>
20 changes: 12 additions & 8 deletions test/test_ast.lua
Original file line number Diff line number Diff line change
Expand Up @@ -20,35 +20,35 @@ package.path = package.path .. ";" .. getDir() .. "/?.lua"

local h = require('luax')

local div = require('test.1_div')
local div = require('test.01_div')

h(div)

local node_value = require('test.2_node_value')
local node_value = require('test.02_node_value')

h(node_value)

local content = require('test.4_content')
local content = require('test.04_content')

h(content)

local element = require('test.5_element')
local element = require('test.05_element')

h(element)

local foo = require('test.6_foo')
local foo = require('test.06_foo')

h(foo)

local input_with_con = require('test.7_input_with_con')
local input_with_con = require('test.07_input_with_con')

h(input_with_con)

local module = require('test.8_input')
local module = require('test.08_input')

h(module.EditTodo({ editing = true, title = "task", id = "1" }))

local input2 = require('test.9_input2')
local input2 = require('test.09_input2')

h(input2)

Expand Down Expand Up @@ -100,4 +100,8 @@ local doc_type_comp = require('test.20_doctype_setter_comp')

h(doc_type_comp)

local fui = require('test.21_web_component')

h(fui)


37 changes: 21 additions & 16 deletions test/test_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ describe("LuaX", function()
assert.is.equal("<!doctype html><html><head></head><body></body></html>", h(el))
end)

it("should return type HTML doctype", function()
it("should return type HTML doctype with string attributes", function()
local el = DOCTYPE({ "HTML", "PUBLIC", "\"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\"" })
assert.is.equal([[<!doctype HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">]], h(el))
end)
Expand All @@ -51,49 +51,49 @@ describe("LuaX", function()
assert.is.equal('<!doctype html><html><head></head><body></body></html>', h(doc_type))
end)

it("should return a HTML string when given JSX like syntax", function()
it("should return type HTML doctype with string attributes", function()
local doc_type_comp = require('test.20_doctype_setter_comp')
assert.is.equal('<!doctype HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head></head><body></body></html>', h(doc_type_comp))
end)

it("should return a HTML string when given JSX like syntax", function()
local el = require("test.2_node_value")
it("should return a HTML string when given XML like syntax", function()
local el = require("test.02_node_value")
assert.is.equal('<div>xxxx</div>', h(el))
end)

it("should return a HTML string when given attributes with special characters", function()
local el = require("test.4_content")
local el = require("test.04_content")
assert.is.equal(
'<footer _="install Footer" class="footer"><span _="install TodoCount" class="todo-count" hx-trigger="load"></span>foobar</footer>',
h(el))
end)

it("should return a HTML string when given JSX like syntax", function()
local el = require("test.5_element")
it("should return a HTML string when given XML like syntax", function()
local el = require("test.05_element")
assert.is.equal('<div bar="bar" class="container" d="1" id="foo" val="value">Hello, world!</div>', h(el))
end)

it("should return a HTML string when given children prop", function()
local el = require("test.6_foo")
local el = require("test.06_foo")
assert.is.equal('<div>foobar</div>', h(el))
end)

it("should return a HTML string when have conditional statement", function()
local el = require("test.7_input_with_con")
local el = require("test.07_input_with_con")
assert.is.equal(
'<input _="install TodoEdit" class="edit" name="title" todo-id="0">',
h(el))
end)

it("should return a HTML string when given input node", function()
local module = require("test.8_input")
local module = require("test.08_input")
assert.is.equal(
'<input _="install TodoEdit" class="edit" name="title" todo-id="1" value="task">',
h(module.EditTodo({ editing = true, title = "task", id = "1" })))
end)

it("should return a HTML string when given input node", function()
local el = require("test.9_input2")
local el = require("test.09_input2")
assert.is.equal(
'<input _="install TodoCheck" class="toggle" hx-patch="/toggle-todo?id=1&done=false" hx-swap="outerHTML" hx-target="closest <li/>" type="checkbox">',
h(el))
Expand All @@ -106,7 +106,7 @@ describe("LuaX", function()
h(el))
end)

it("should return a HTML string when given JSX like syntax", function()
it("should return a HTML string when given XML like syntax", function()
local el = require("test.11_props")
assert.is.equal([[<div id="foo" style="color;red"> test
</div>]], h(el))
Expand All @@ -119,12 +119,12 @@ describe("LuaX", function()
h(el))
end)

it("should return a HTML string when given JSX like syntax with nested node", function()
it("should return a HTML string when given XML like syntax with nested node", function()
local el = require("test.13_varin")
assert.is.equal('<div class="container" id="div_1"><p class="title" id="p_2" style="border: 1px solid red;">Hello, world!</p></div>', h(el))
end)

it("should return a HTML string when given JSX like syntax with nested node", function()
it("should return a HTML string when given XML like syntax with nested node", function()
local el = require("test.14_page")
assert.is.equal([[<html data-framework="htmx" lang="en"><head><meta charSet="utf-8"><title>HTMX • TodoMVC</title><link href="https://unpkg.com/todomvc-common@1.0.5/base.css" rel="stylesheet" type="text/css"><link href="https://unpkg.com/todomvc-app-css/index.css" rel="stylesheet" type="text/css"><script src="/hs/start-me-up._hs" type="text/hyperscript"></script><script src="/hs/main._hs" type="text/hyperscript"></script><script src="/hs/behaviors/toggle-main._hs" type="text/hyperscript"></script><script src="/hs/behaviors/toggle-footer._hs" type="text/hyperscript"></script><script src="/hs/behaviors/toggle-show._hs" type="text/hyperscript"></script><script src="/hs/behaviors/add-todo._hs" type="text/hyperscript"></script><script src="/hs/behaviors/footer._hs" type="text/hyperscript"></script><script src="/hs/behaviors/toggle-all._hs" type="text/hyperscript"></script><script src="/hs/behaviors/clear-completed._hs" type="text/hyperscript"></script><script src="/hs/behaviors/destroy._hs" type="text/hyperscript"></script><script src="/hs/behaviors/todo-count._hs" type="text/hyperscript"></script><script src="/hs/behaviors/todo-dblclick._hs" type="text/hyperscript"></script><script src="/hs/behaviors/todo-check._hs" type="text/hyperscript"></script><script src="/hs/behaviors/todo-edit._hs" type="text/hyperscript"></script> </head><body><section _="install ToggleMain end install ToggleFooter end install ToggleShow end" class="todoapp"><header class="header"><h1>todos</h1><input _="install AddTodo" class="new-todo" id="add-todo" name="title" placeholder="What needs to be done?"></header>toggleMaintodoListtodoFooter</section><footer _="on load debounced at 10ms call startMeUp() hashCache()" class="info"><p>Double-click to edit a todo</p><p>Created by <a href="http://github.com/syarul/">syarul</a></p><p>Part of <a href="http://todomvc.com">TodoMVC</a></p><img height="auto" src="https://htmx.org/img/createdwith.jpeg" width="250"></footer></body><script src="https://unpkg.com/todomvc-common@1.0.5/base.js"></script><script src="https://unpkg.com/htmx.org@1.9.10"></script><script src="https://unpkg.com/hyperscript.org/dist/_hyperscript.js"></script> </html>]], h(el.Page("todos")))
end)
Expand All @@ -135,18 +135,23 @@ describe("LuaX", function()
{ url = "#/completed", name = "Completed", selected = false },
}

it("should return a HTML string when given JSX like syntax with table concat", function()
it("should return a HTML string when given XML like syntax with table concat", function()
local el = require("test.17_table")
assert.is.equal([[<ul _="on load set $filter to me" class="filters"><li><a _="on click add .selected to me" class="selected" href="#/">All </a> </li>
<li><a _="on click add .selected to me" href="#/active">Active </a> </li>
<li><a _="on click add .selected to me" href="#/completed">Completed </a> </li> </ul>]], h(el.Filter(filters)))
end)

it("should return a HTML string when given JSX like syntax with table concat", function()
it("should return a HTML string when given XML like syntax with table concat", function()
local el = require("test.18_filter")
assert.is.equal([[<ul class="todo-list"><li><a _="on click add .selected to me" class="selected" href="#/">All </a> </li>
<li><a _="on click add .selected to me" href="#/active">Active </a> </li>
<li><a _="on click add .selected to me" href="#/completed">Completed </a> </li></ul>]], h(el(filters)))
end)

it("should return a HTML string when given XML like syntax with kebab-case tag", function()
local fui = require("test.21_web_component")
assert.is.equal([[<fluent-button>Example</fluent-button>]], h(fui))
end)

end)

0 comments on commit 368cdd3

Please sign in to comment.