diff --git a/core/builtin.lua b/core/builtin.lua
index 41b1f0f..26321f8 100644
--- a/core/builtin.lua
+++ b/core/builtin.lua
@@ -398,242 +398,14 @@ function M:custom(str)
return self
end
-function M:h1(str)
- str = str or ""
- self:custom(std.h1(str))
- return self
-end
-
-function M:h2(str)
- str = str or ""
- self:custom(std.h2(str))
- return self
-end
-
-function M:h3(str)
- str = str or ""
- self:custom(std.h3(str))
- return self
-end
-
-function M:h4(str)
- str = str or ""
- self:custom(std.h4(str))
- return self
-end
-
-function M:h5(str)
- str = str or ""
- self:custom(std.h5(str))
- return self
-end
-
-function M:h6(str)
- str = str or ""
- self:custom(std.h6(str))
- return self
-end
-
-function M:p(str)
- str = str or ""
- table.insert(self.parts, "
" .. str .. "
")
- return self
-end
-
-function M:note(content)
- content = content or ""
- self:custom('' .. content .. '
')
- return self
-end
-
-function M:muted(content)
- content = content or ""
- self:custom('' .. content .. '
')
- return self
-end
-
-function M:a(link, str)
- link = link or "example.com"
- str = str or link
- table.insert(self.parts, "" .. str .. "")
- return self
-end
-
-function M:ha(link, str)
- link = link or "example.com"
- str = str or link
- table.insert(self.parts, "" .. str .. "")
- return self
-end
-
-function M:external(link, str)
- link = link or "example.com"
- str = str or link
- table.insert(self.parts, "" .. str .. "")
- return self
-end
-
-function M:version()
- return self.version
-end
-
-function M:ul(items)
- items = items or {}
- local html = ""
- for _, item in ipairs(items) do
- html = html .. "- " .. tostring(item) .. "
"
- end
- html = html .. "
"
- self:custom(html)
- return self
-end
-
-function M:ol(items)
- items = items or {}
- local html = ""
- for _, item in ipairs(items) do
- html = html .. "- " .. tostring(item) .. "
"
- end
- html = html .. "
"
- self:custom(html)
- return self
-end
-
-function M:li(str)
- str = str or ""
- self:custom("" .. str .. "")
- return self
-end
-
-function M:code(str)
- str = str or ""
- self:custom("" .. str .. "
")
- return self
-end
-
-function M:blockquote(str)
- str = str or ""
- self:custom("" .. str .. "
")
- return self
-end
-
-function M:hr()
- self:custom("
")
- return self
-end
-
-function M:divider()
- self:custom('')
- return self
-end
-
-function M:img(src, alt)
- src = src or ""
- alt = alt or ""
- self:custom('
')
- return self
-end
-
-function M:table(headers, rows)
- headers = headers or {}
- rows = rows or {}
-
- local html = ""
- for _, header in ipairs(headers) do
- html = html .. "| " .. tostring(header) .. " | "
- end
- html = html .. "
"
-
- for _, row in ipairs(rows) do
- html = html .. ""
- for _, cell in ipairs(row) do
- html = html .. "| " .. tostring(cell) .. " | "
- end
- html = html .. "
"
- end
-
- html = html .. "
"
- self:custom(html)
- return self
-end
-
-function M:div(content, class)
- content = content or ""
- class = class or ""
- local class_attr = class ~= "" and (' class="' .. class .. '"') or ""
- self:custom("" .. content .. "
")
- return self
-end
-
-function M:span(content, class)
- content = content or ""
- class = class or ""
- local class_attr = class ~= "" and (' class="' .. class .. '"') or ""
- self:custom("" .. content .. "")
- return self
-end
-
-function M:strong(str)
- str = str or ""
- self:custom("" .. str .. "")
- return self
-end
-
-function M:em(str)
- str = str or ""
- self:custom("" .. str .. "")
- return self
-end
-
-function M:br()
- self:custom("
")
- return self
-end
-
-function M:section(content)
- content = content or ""
- self:custom('' .. content .. '
')
- return self
-end
-
-function M:links(link_list)
- link_list = link_list or {}
- local html = ''
- for _, link_data in ipairs(link_list) do
- local link = link_data.link or link_data[1] or "#"
- local text = link_data.text or link_data[2] or link
- local external = link_data.external or false
- if external then
- html = html .. '
' .. text .. ''
- else
- html = html .. '
' .. text .. ''
+for name, func in pairs(std) do
+ if type(func) == "function" then
+ M[name] = function(self, ...)
+ local result = func(...)
+ table.insert(self.parts, result)
+ return self
end
end
- html = html .. '
'
- self:custom(html)
- return self
-end
-
-function M:lead(str)
- str = str or ""
- self:custom(std.small(str))
- return self
-end
-
-function M:small(str)
- str = str or ""
- self:custom(std.small(str))
- return self
-end
-
-function M:highlight(str)
- str = str or ""
- self:custom(std.highlight(str))
- return self
-end
-
-function M:banner(str)
- self:custom(std.banner(str))
end
function M:build()
diff --git a/core/log.lua b/core/log.lua
deleted file mode 100644
index 0d3c011..0000000
--- a/core/log.lua
+++ /dev/null
@@ -1,7 +0,0 @@
-local M = {}
-
-function M.printl(fmt, ...)
- print(string.format(fmt, ...))
-end
-
-return M
diff --git a/core/middleware.lua b/core/middleware.lua
new file mode 100644
index 0000000..7f8679b
--- /dev/null
+++ b/core/middleware.lua
@@ -0,0 +1,169 @@
+local M = {}
+
+function M.json_decode(json)
+ if type(json) ~= "string" then
+ return nil, "input must be a string"
+ end
+
+ local pos = 1
+ local len = #json
+
+ local function skip_ws()
+ while pos <= len and json:sub(pos,pos):match("%s") do
+ pos = pos + 1
+ end
+ end
+
+ local function parse_value()
+ skip_ws()
+ local c = json:sub(pos,pos)
+ if c == "{" then return parse_object()
+ elseif c == "[" then return parse_array()
+ elseif c == '"' then return parse_string()
+ elseif c:match("[%d%-]") then return parse_number()
+ elseif json:sub(pos,pos+3) == "true" then pos=pos+4; return true
+ elseif json:sub(pos,pos+4) == "false" then pos=pos+5; return false
+ elseif json:sub(pos,pos+3) == "null" then pos=pos+4; return nil
+ else return nil, "invalid value at position "..pos
+ end
+ end
+
+ function parse_string()
+ pos = pos + 1
+ local start_pos = pos
+ local str = ""
+ while pos <= len do
+ local c = json:sub(pos,pos)
+ if c == '"' then
+ str = str .. json:sub(start_pos,pos-1)
+ pos = pos + 1
+ return str
+ elseif c == "\\" then
+ str = str .. json:sub(start_pos,pos-1)
+ pos = pos + 1
+ local esc = json:sub(pos,pos)
+ local map = {b="\b", f="\f", n="\n", r="\r", t="\t", ['"']='"', ["\\"]="\\", ["/"]="/"}
+ str = str .. (map[esc] or esc)
+ pos = pos + 1
+ start_pos = pos
+ else
+ pos = pos + 1
+ end
+ end
+ return nil, "unterminated string"
+ end
+
+ function parse_number()
+ local start_pos = pos
+ while pos <= len and json:sub(pos,pos):match("[%d%+%-eE%.]") do
+ pos = pos + 1
+ end
+ local n = tonumber(json:sub(start_pos,pos-1))
+ if not n then return nil, "invalid number at position "..start_pos end
+ return n
+ end
+
+ function parse_array()
+ pos = pos + 1
+ local arr = {}
+ skip_ws()
+ if json:sub(pos,pos) == "]" then pos=pos+1; return arr end
+ while true do
+ local val, err = parse_value()
+ if err then return nil, err end
+ table.insert(arr,val)
+ skip_ws()
+ local c = json:sub(pos,pos)
+ if c == "]" then pos=pos+1; break
+ elseif c == "," then pos=pos+1
+ else return nil, "expected ',' or ']' at position "..pos
+ end
+ end
+ return arr
+ end
+
+ function parse_object()
+ pos = pos + 1
+ local obj = {}
+ skip_ws()
+ if json:sub(pos,pos) == "}" then pos=pos+1; return obj end
+ while true do
+ skip_ws()
+ if json:sub(pos,pos) ~= '"' then return nil, "expected string key at "..pos end
+ local key, err = parse_string()
+ if err then return nil, err end
+ skip_ws()
+ if json:sub(pos,pos) ~= ":" then return nil, "expected ':' at "..pos end
+ pos = pos + 1
+ local val, err = parse_value()
+ if err then return nil, err end
+ obj[key] = val
+ skip_ws()
+ local c = json:sub(pos,pos)
+ if c == "}" then pos=pos+1; break
+ elseif c == "," then pos=pos+1
+ else return nil, "expected ',' or '}' at "..pos
+ end
+ end
+ return obj
+ end
+
+ local result, err = parse_value()
+ if err then return nil, err end
+ skip_ws()
+ if pos <= len then return nil, "trailing characters at "..pos end
+ return result
+end
+
+function M.json_encode(value)
+ local t = type(value)
+ if t == "nil" then
+ return "null"
+ elseif t == "boolean" then
+ return tostring(value)
+ elseif t == "number" then
+ return tostring(value)
+ elseif t == "string" then
+ return '"' .. value:gsub('[%z\1-\31\\"]', {
+ ['\\'] = '\\\\',
+ ['"'] = '\\"',
+ ['\b'] = '\\b',
+ ['\f'] = '\\f',
+ ['\n'] = '\\n',
+ ['\r'] = '\\r',
+ ['\t'] = '\\t'
+ }):gsub("[%z\1-\31]", function(c)
+ return string.format("\\u%04x", c:byte())
+ end) .. '"'
+ elseif t == "table" then
+ local is_array = true
+ local max_index = 0
+ for k,v in pairs(value) do
+ if type(k) ~= "number" then
+ is_array = false
+ else
+ if k > max_index then max_index = k end
+ end
+ end
+
+ local items = {}
+ if is_array then
+ for i = 1, max_index do
+ table.insert(items, M.json_encode(value[i]))
+ end
+ return "[" .. table.concat(items,",") .. "]"
+ else
+ for k,v in pairs(value) do
+ if type(k) ~= "string" then
+ return nil, "object keys must be strings"
+ end
+ table.insert(items, M.json_encode(k) .. ":" .. M.json_encode(v))
+ end
+ return "{" .. table.concat(items,",") .. "}"
+ end
+ else
+ return nil, "unsupported type: " .. t
+ end
+end
+
+return M
diff --git a/core/std.lua b/core/std.lua
index c7462d7..38131e4 100644
--- a/core/std.lua
+++ b/core/std.lua
@@ -55,9 +55,7 @@ end
function M.h3(str)
return "" .. (str or "") .. "
"
end
-
-function M.h4(str)
- return "" .. (str or "") .. "
"
+function M.h4(str) return "" .. (str or "") .. "
"
end
function M.h5(str)
@@ -72,11 +70,11 @@ function M.p(str)
return "" .. (str or "") .. "
"
end
-function M.code(str)
- return "" .. (str or "") .. ""
+function M.pre(str)
+ return "" .. (str or "") .. "
"
end
-function M.pre(str)
+function M.code(str)
return "" .. (str or "") .. "
"
end
@@ -228,23 +226,16 @@ function M.table(headers, rows)
return html
end
-function M.copyright()
- return "©"
-end
-
function M.highlight(str)
- str = str or ""
- return '' .. str .. ""
+ return '' .. (str or "") .. ""
end
function M.banner(str)
- str = str or ""
- return '' .. str .. "
"
+ return '' .. (str or "") .. "
"
end
function M.center(str)
- str = str or ""
- return '' .. str .. "
"
+ return '' .. (str or "") .. "
"
end
function M.nav(link, str)
diff --git a/core/symbol.lua b/core/symbol.lua
new file mode 100644
index 0000000..b3fac61
--- /dev/null
+++ b/core/symbol.lua
@@ -0,0 +1,7 @@
+local M = {}
+
+M.copyright = "©"
+M.registered_trademark = "®"
+M.trademark = "™"
+
+return M
diff --git a/core/util.lua b/core/util.lua
new file mode 100644
index 0000000..39c6a97
--- /dev/null
+++ b/core/util.lua
@@ -0,0 +1,14 @@
+local std = require("core.std")
+local symbol = require("core.symbol")
+
+local M = {}
+
+function M.cc(tbl)
+ return table.concat(tbl)
+end
+
+function M.copyright(link, holder)
+ return symbol.copyright .. " " .. std.external(link, holder)
+end
+
+return M
diff --git a/examples/canonical/www/index.lua b/examples/canonical/www/index.lua
index 3cddd3c..480d637 100644
--- a/examples/canonical/www/index.lua
+++ b/examples/canonical/www/index.lua
@@ -4,7 +4,7 @@ local std = fes.std
local site = fes.fes()
site.title = "Canonical"
-site.copyright = std.copyright() .. " " .. std.external("https://git.vxserver.dev/fSD", "fSD")
+site.copyright = fes.util.copyright("https://git.vxserver.dev/fSD", "fSD")
site:banner(fes.app.header.render(std))
diff --git a/examples/hello-world/www/index.lua b/examples/hello-world/www/index.lua
index b8adf7e..4720737 100644
--- a/examples/hello-world/www/index.lua
+++ b/examples/hello-world/www/index.lua
@@ -2,7 +2,7 @@ local fes = require("fes")
local site = fes.fes()
site.title = "Hello, World!"
-site.copyright = fes.std.copyright() .. " " .. fes.std.external("https://git.vxserver.dev/fSD", "fSD")
+site.copyright = fes.util.copyright("https://git.vxserver.dev/fSD", "fSD")
site:h1("Hello, World!")
diff --git a/examples/json/Fes.toml b/examples/json/Fes.toml
new file mode 100644
index 0000000..e519140
--- /dev/null
+++ b/examples/json/Fes.toml
@@ -0,0 +1,5 @@
+[app]
+
+name = "json"
+version = "0.0.1"
+authors = ["vx-clutch"]
\ No newline at end of file
diff --git a/examples/json/www/index.lua b/examples/json/www/index.lua
new file mode 100644
index 0000000..980e6d7
--- /dev/null
+++ b/examples/json/www/index.lua
@@ -0,0 +1,34 @@
+local fes = require("fes")
+local site = fes.fes()
+
+site.title = "JSON"
+site.copyright = fes.util.copyright("https://git.vxserver.dev/fSD", "fSD")
+
+local json_pre = [[
+{
+ "userId": 1,
+ "id": 1,
+ "title": "delectus aut autem",
+ "completed": false
+}
+]]
+
+local json = fes.middleware.json_decode(json_pre)
+
+site:h1("JSON")
+
+site:note(fes.util.cc({
+ fes.std.h2("Before"),
+ fes.std.code(json_pre),
+}))
+site:note(fes.util.cc({
+ fes.std.h2("After"),
+ fes.std.ul({
+ json["userId"],
+ json["id"],
+ json["title"],
+ json["completed"],
+ })
+}))
+
+return site
diff --git a/examples/multi-page/www/index.lua b/examples/multi-page/www/index.lua
index 41ee99e..e95b94b 100644
--- a/examples/multi-page/www/index.lua
+++ b/examples/multi-page/www/index.lua
@@ -2,7 +2,7 @@ local fes = require("fes")
local site = fes.fes()
site.title = "Home"
-site.copyright = fes.std.copyright() .. " " .. fes.std.external("https://git.vxserver.dev/fSD", "fSD")
+site.copyright = fes.util.copyright("https://git.vxserver.dev/fSD", "fSD")
site:h1("Home")
site:note(
diff --git a/examples/multi-page/www/page1.lua b/examples/multi-page/www/page1.lua
index 7072077..3b2c7c6 100644
--- a/examples/multi-page/www/page1.lua
+++ b/examples/multi-page/www/page1.lua
@@ -2,7 +2,7 @@ local fes = require("fes")
local site = fes.fes()
site.title = "Page 1"
-site.copyright = fes.std.copyright() .. " " .. fes.std.external("https://git.vxserver.dev/fSD", "fSD")
+site.copyright = fes.util.copyright("https://git.vxserver.dev/fSD", "fSD")
site:h1("Page 1")
site:note(
diff --git a/examples/multi-page/www/page2.lua b/examples/multi-page/www/page2.lua
index 1a97048..535bf68 100644
--- a/examples/multi-page/www/page2.lua
+++ b/examples/multi-page/www/page2.lua
@@ -2,7 +2,7 @@ local fes = require("fes")
local site = fes.fes()
site.title = "Page 2"
-site.copyright = fes.std.copyright() .. " " .. fes.std.external("https://git.vxserver.dev/fSD", "fSD")
+site.copyright = fes.util.copyright("https://git.vxserver.dev/fSD", "fSD")
site:h1("Page 2")
site:note(
diff --git a/examples/simple/Fes.toml b/examples/simple/Fes.toml
new file mode 100644
index 0000000..8027800
--- /dev/null
+++ b/examples/simple/Fes.toml
@@ -0,0 +1,5 @@
+[app]
+
+name = "simple"
+version = "0.0.1"
+authors = ["vx-clutch"]
diff --git a/examples/simple/www/index.lua b/examples/simple/www/index.lua
new file mode 100644
index 0000000..5e0506d
--- /dev/null
+++ b/examples/simple/www/index.lua
@@ -0,0 +1 @@
+return "Hello, World!"
diff --git a/src/new/new.go b/src/new/new.go
index 0c534f8..caea2d5 100644
--- a/src/new/new.go
+++ b/src/new/new.go
@@ -30,13 +30,15 @@ func Project(dir string) error {
}
indexLua := filepath.Join(dir, "www", "index.lua")
if _, err := os.Stat(indexLua); os.IsNotExist(err) {
- content := `local fes = require("fes")
+ content := fmt.Sprintf(`local fes = require("fes")
local site = fes.fes()
+site.title = "%s"
+
site:h1("Hello, World!")
return site
-`
+`, dir)
if err := os.WriteFile(indexLua, []byte(content), 0644); err != nil {
return err
}
diff --git a/src/server/server.go b/src/server/server.go
index 522aa05..d0d443d 100644
--- a/src/server/server.go
+++ b/src/server/server.go
@@ -108,6 +108,18 @@ func loadLua(luaDir string, entry string, cfg *config.MyConfig) (string, error)
return 1
})
+ L.PreloadModule("core.symbol", func(L *lua.LState) int {
+ data, err := config.Core.ReadFile("core/symbol.lua")
+ if err != nil {
+ panic(err)
+ }
+ if err := L.DoString(string(data)); err != nil {
+ panic(err)
+ }
+ L.Push(L.Get(-1))
+ return 1
+ })
+
L.PreloadModule("fes", func(L *lua.LState) int {
mod := L.NewTable()
coreModules := []string{}