Skip to content

Commit

Permalink
fix attributes handling within backtick, added syntax highlighter
Browse files Browse the repository at this point in the history
  • Loading branch information
shahrul committed Oct 17, 2024
1 parent 892b506 commit 1849ae9
Show file tree
Hide file tree
Showing 11 changed files with 154 additions and 46 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.5-1"
version = "1.0.6-1"

source = {
url = "https://github.com/syarul/luax/archive/refs/tags/v1.0.5.tar.gz",
dir = "luax-1.0.5"
url = "https://github.com/syarul/luax/archive/refs/tags/v1.0.6.tar.gz",
dir = "luax-1.0.6"
}
description = {
summary = "HTML parse in Lua",
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

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.5-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.6-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 @@ -90,6 +90,9 @@ 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)

## VSCode Integration
Install from [VSCode Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=syarul.luax-syntax-highlighter) to support syntax highlighlight for `.luax` extension

## 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
32 changes: 22 additions & 10 deletions luax.lua
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ function State:xml(level)
if level ~= nil then return self.deepNode > level end
return self.deepNode > 0
end
function State:notStr() return not self.deepString and not self.deepStringApos end
function State:notStr() return not self.deepString and not self.deepStringApos and not self.scriptNode end
function State:toggle(key, bool)
if bool ~= nil then self[key] = bool else self[key] = not self[key] end
end
Expand Down Expand Up @@ -168,9 +168,6 @@ local function decentParserAST(input)
if s.textNodeStart then
s:toggle("textNodeStart")
s:conc("]])")
elseif s.scriptNode then
s:toggle("scriptNode")
s:conc("]])")
else
s:conc(")")
end
Expand All @@ -179,6 +176,9 @@ local function decentParserAST(input)
else
s:conc(tok, 1)
end
elseif tok == "<" and not s:notStr() and input:match("</([%w-]+)>", s.pos) == "script" then
s:toggle("scriptNode")
s:conc("]]")
elseif tok == '"' and s:xml() and not s.scriptNode then
s:toggle("deepString")
s:conc(tok, 1)
Expand All @@ -196,7 +196,7 @@ local function decentParserAST(input)
local trail = s.output:sub(#s.output - 10, #s.output):gsub("[%s\r\n]", "")
if trail:sub(#trail) == "{" then
s:toggle("scriptNode")
s:conc("}, ")
s:conc("}, [[\n")
else
s:conc("}")
end
Expand All @@ -213,11 +213,11 @@ local function decentParserAST(input)
s.output = s.output:sub(0, docTypeStartPos-1) .. output .. s.output:sub(s.pos)
end
s:inc()
elseif tok == "/" and input:sub(s.pos + 1, s.pos + 1) == ">" and s:notStr() and not s.scriptNode then
elseif tok == "/" and input:sub(s.pos + 1, s.pos + 1) == ">" and s:notStr() then
s:decDeepNode()
s:conc("})")
s:inc(2)
elseif tok == "{" and s:xml() and s:notStr() and not s.scriptNode then
elseif tok == "{" and s:xml() and s:notStr() then
var = not var
if var then
-- snapshot currentState
Expand All @@ -230,14 +230,14 @@ local function decentParserAST(input)
s:conc(", ")
end
s:inc()
elseif tok == "}" and var and s:notStr() and not s.scriptNode then
elseif tok == "}" and var and s:notStr() then
var = not var
if not var then
-- restore currentState from snapshot
resetTable(s, varStore)
end
s:inc()
elseif s:xml() and s:notStr() and not s.scriptNode then
elseif s:xml() and s:notStr() then
if tok:match("%s") then
if not s.docType and not var and s.isTag and s.output:sub(-1) ~= "{" and s.output:sub(-1) == "\"" or
s.isTag and input:sub(s.pos - 1, s.pos - 1) == "}" then
Expand Down Expand Up @@ -271,7 +271,19 @@ local function decentParserAST(input)
end
end
-- this to add [] bracket to table attributes
s.output = s.output:gsub('([%w%-_]+)%=([^%s]+)', '["%1"]=%2')
-- ignore adding within backtick javascript

s.output = s.output:gsub('([%w%-_]+)%=([^%s]+)', function (attr, value)
local pos = s.output:find(attr .. " = " .. value) or 1
local preStrInterpolation = s.output:sub(1, pos):reverse():find("%`")
local postStrInterpolation = s.output:find("%`", pos)

if not preStrInterpolation and not postStrInterpolation and attr:find('[-_]') then
return '["' .. attr .. '"]=' .. value
else
return attr .. '=' .. value
end
end)
-- encapsulate output if doctype exist
if s.docType ~= nil then s:conc(")") end
return s.output
Expand Down
10 changes: 5 additions & 5 deletions test/13_varin.luax
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
local v = {
div_class = "container",
p_class = "title",
p_inner = "Hello, world!",
span_style = "color: red;",
span_inner = "This is a span",
div_class = "container",
p_class = "title",
p_inner = "Hello, world!",
span_style = "color: red;",
span_inner = "This is a span",
}

local el = <div id="div_1" class={v.div_class}><p id="p_2" style="border: 1px solid red;" class={v.p_class}>{v.p_inner}</p></div>
Expand Down
2 changes: 1 addition & 1 deletion test/17_table.luax
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function module.Filter(filters)
href={filter.url}
_="on click add .selected to me"
>
{filter.name}
{filter.name or nil}
</a>
</li>)
end), '\n')
Expand Down
8 changes: 4 additions & 4 deletions test/18_filter.luax
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ end
local function fcn(filter)
return <li>
<a
class={filter.selected and 'selected' or nil}
href={filter.url}
_="on click add .selected to me"
class={filter.selected and 'selected' or nil}
href={filter.url}
_="on click add .selected to me"
>
{filter.name}
{filter.name}
</a>
</li>
end
Expand Down
2 changes: 1 addition & 1 deletion test/20_doctype_setter_comp.luax
Original file line number Diff line number Diff line change
@@ -1 +1 @@
return <!doctype HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head></head><body></body></html>
return <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head></head><body></body></html>
22 changes: 11 additions & 11 deletions test/22_script_tag.luax
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
return <div>
<script>
class CounterElement extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
<script>
class CounterElement extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}

connectedCallback() {
console.log(1)
}
connectedCallback() {
console.log(1)
}
}

customElements.define('counter-element', CounterElement);
</script>
customElements.define('counter-element', CounterElement);
</script>
</div>
47 changes: 47 additions & 0 deletions test/23_web_component_test.luax
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
local foo = { foo = "foo!" }
return <!DOCTYPE html>
<html>
<head>
<script type="module" src="https://unpkg.com/@fluentui/web-components"></script>
</head>
<body>
<counter-element></counter-element>
<script>
class CounterElement extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.count = 0;
this.shadowRoot.innerHTML = `
<fluent-card>
<p>Count: <span id="count">${this.count}</span></p>
<fluent-button id="increment">Increment</fluent-button>
<fluent-button id="decrement" _="foo">Decrement</fluent-button>
</fluent-card>
`;
}

connectedCallback() {
this.shadowRoot.querySelector('#increment').addEventListener('click', () => this.increment());
this.shadowRoot.querySelector('#decrement').addEventListener('click', () => this.decrement());
}

increment() {
this.count++;
this.updateCount();
}

decrement() {
this.count--;
this.updateCount();
}

updateCount() {
this.shadowRoot.querySelector('#count').textContent = this.count;
}
}

customElements.define('counter-element', CounterElement);
</script>
</body>
</html>
4 changes: 4 additions & 0 deletions test/test_ast.lua
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,8 @@ local st = require('test.22_script_tag')

h(st)

local st2 = require('test.23_web_component_test')

h(st2)


62 changes: 52 additions & 10 deletions test/test_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -157,19 +157,61 @@ describe("LuaX", function()

it("should return a HTML string when given XML like syntax with script tag", function()
local scriptTag = require("test.22_script_tag")
assert.is.equal([[<div><script> class CounterElement extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
assert.is.equal([[<div><script>
class CounterElement extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
console.log(1)
}
connectedCallback() {
console.log(1)
}
}
customElements.define('counter-element', CounterElement);
</script></div>]], h(scriptTag))
end)

it("should return a HTML string when given XML like syntax with script tag", function()
local st2 = require('test.23_web_component_test')
assert.is.equal([[<!doctype html><html><head><script src="https://unpkg.com/@fluentui/web-components" type="module"></script></head><body><counter-element></counter-element><script>
class CounterElement extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.count = 0;
this.shadowRoot.innerHTML = `
<fluent-card>
<p>Count: <span id="count">${this.count}</span></p>
<fluent-button id="increment">Increment</fluent-button>
<fluent-button id="decrement" _="foo">Decrement</fluent-button>
</fluent-card>
`;
}
connectedCallback() {
this.shadowRoot.querySelector('#increment').addEventListener('click', () => this.increment());
this.shadowRoot.querySelector('#decrement').addEventListener('click', () => this.decrement());
}
increment() {
this.count++;
this.updateCount();
}
decrement() {
this.count--;
this.updateCount();
}
updateCount() {
this.shadowRoot.querySelector('#count').textContent = this.count;
}
}
customElements.define('counter-element', CounterElement);
</script></div>]], h(scriptTag))
customElements.define('counter-element', CounterElement);
</script></body></html>]], h(st2))
end)

end)

0 comments on commit 1849ae9

Please sign in to comment.