diff --git a/TODO b/TODO index a1372fd..9b9fd43 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,2 @@ Make this static ( cannot find core.std everywhere ) -Add hotreloading Add an interval element -Compare bin version to Fes.toml -Add ways to interact with left and right diff --git a/core/builtin.lua b/core/builtin.lua index 38dd63f..41b1f0f 100644 --- a/core/builtin.lua +++ b/core/builtin.lua @@ -27,23 +27,85 @@ function M.fes(header, footer) {{TITLE}} @@ -304,6 +459,13 @@ function M:a(link, 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 @@ -454,26 +616,30 @@ end function M:lead(str) str = str or "" - self:custom('

' .. str .. '

') + self:custom(std.small(str)) return self end function M:small(str) str = str or "" - self:custom('
' .. str .. '
') + self:custom(std.small(str)) return self end function M:highlight(str) str = str or "" - self:custom('' .. str .. '') + self:custom(std.highlight(str)) return self end +function M:banner(str) + self:custom(std.banner(str)) +end + function M:build() local header = self.header:gsub("{{TITLE}}", self.title or "Document") local footer = self.footer:gsub("{{COPYRIGHT}}", self.copyright or "© The Copyright Holder") - return header .. table.concat(self.parts) .. footer + return header .. table.concat(self.parts, "\n") .. footer end M.__tostring = function(self) diff --git a/core/console.lua b/core/console.lua deleted file mode 100644 index 992dceb..0000000 --- a/core/console.lua +++ /dev/null @@ -1,19 +0,0 @@ -local M = {} - -function M.log(fmt, ...) - print(string.format("[%12f] ", os.clock()) .. string.format(fmt, ...)) -end - -function M.info(fmt, ...) - print("INFO: " .. string.format(fmt, ...)) -end - -function M.warn(fmt, ...) - print("WARN: " .. string.format(fmt, ...)) -end - -function M.error(fmt, ...) - print("ERROR: " .. string.format(fmt, ...)) -end - -return M diff --git a/core/log.lua b/core/log.lua new file mode 100644 index 0000000..0d3c011 --- /dev/null +++ b/core/log.lua @@ -0,0 +1,7 @@ +local M = {} + +function M.printl(fmt, ...) + print(string.format(fmt, ...)) +end + +return M diff --git a/core/markdown.lua b/core/markdown.lua deleted file mode 100644 index 07da252..0000000 --- a/core/markdown.lua +++ /dev/null @@ -1,19 +0,0 @@ -local M = {} - --- Markdown to HTML conversion function --- Uses the Go backend markdown parser -function M.to_html(markdown_text) - markdown_text = markdown_text or "" - - -- Get the fes module - local fes_mod = package.loaded.fes - if fes_mod and fes_mod.markdown_to_html then - return fes_mod.markdown_to_html(markdown_text) - end - - -- Fallback: return error message if Go function not available - return "

Error: markdown_to_html function not available

" -end - -return M - diff --git a/core/std.lua b/core/std.lua index 3b4ad45..c7462d7 100644 --- a/core/std.lua +++ b/core/std.lua @@ -17,9 +17,17 @@ function M.site_version() end function M.a(link, str) + link = link or "https://example.com" + str = str or link return "" .. str .. "" end +function M.ha(link, str) + link = link or "https://example.com" + str = str or link + return "" .. str .. "" +end + function M.external(link, str) return "" .. str .. "" end @@ -92,6 +100,16 @@ function M.ol(items) return html end +function M.tl(items) + items = items or {} + local html = '" + return html +end + function M.blockquote(str) return "
" .. (str or "") .. "
" end @@ -191,13 +209,13 @@ end function M.table(headers, rows) headers = headers or {} rows = rows or {} - + local html = "" for _, header in ipairs(headers) do html = html .. "" end html = html .. "" - + for _, row in ipairs(rows) do html = html .. "" for _, cell in ipairs(row) do @@ -205,7 +223,7 @@ function M.table(headers, rows) end html = html .. "" end - + html = html .. "
" .. tostring(header) .. "
" return html end @@ -215,7 +233,24 @@ function M.copyright() end function M.highlight(str) + str = str or "" return '' .. str .. "" end +function M.banner(str) + str = str or "" + return '" +end + +function M.center(str) + str = str or "" + return '
' .. str .. "
" +end + +function M.nav(link, str) + link = link or "example.com" + str = str or link + return '' .. str .. "" +end + return M diff --git a/examples/canonical/Fes.toml b/examples/canonical/Fes.toml new file mode 100644 index 0000000..24430e8 --- /dev/null +++ b/examples/canonical/Fes.toml @@ -0,0 +1,5 @@ +[app] + +name = "canonical" +version = "0.0.1" +authors = ["vx-clutch"] diff --git a/examples/canonical/include/header.lua b/examples/canonical/include/header.lua new file mode 100644 index 0000000..9c87ed0 --- /dev/null +++ b/examples/canonical/include/header.lua @@ -0,0 +1,16 @@ +local header = {} + +header.render = function(std) + return table.concat({ + std.center(std.h1("Canonical")), + std.center(table.concat({ + std.nav("example"), + std.nav("example"), + std.nav("example"), + std.nav("example"), + std.nav("example"), + })) + }) +end + +return header diff --git a/examples/canonical/www/index.lua b/examples/canonical/www/index.lua new file mode 100644 index 0000000..3cddd3c --- /dev/null +++ b/examples/canonical/www/index.lua @@ -0,0 +1,17 @@ +local fes = require("fes") +local std = fes.std + +local site = fes.fes() + +site.title = "Canonical" +site.copyright = std.copyright() .. " " .. std.external("https://git.vxserver.dev/fSD", "fSD") + +site:banner(fes.app.header.render(std)) + +site:note(table.concat({ + std.h1("Canonical"), + std.p("This is the example for the canonical 'fes' site, by canonical is meant a format and " .. std.external("https://git.vxserver.dev/fSD/fes/src/branch/master/examples/canonical/www/index.lua", "code") .. " that resembles the typical use case of the Microframework"), + std.p("This page also serves as a test for the integrity of a 'fes' build, given that it uses plenty crucial features to show everything from the HTML to CSS as well as the interactivity of certain elements."), +})) + +return site diff --git a/examples/hello-world/Fes.toml b/examples/hello-world/Fes.toml new file mode 100644 index 0000000..a2376b5 --- /dev/null +++ b/examples/hello-world/Fes.toml @@ -0,0 +1,5 @@ +[app] + +name = "hello-world" +version = "0.0.1" +authors = ["vx-clutch"] diff --git a/examples/hello-world/www/index.lua b/examples/hello-world/www/index.lua new file mode 100644 index 0000000..b8adf7e --- /dev/null +++ b/examples/hello-world/www/index.lua @@ -0,0 +1,9 @@ +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:h1("Hello, World!") + +return site diff --git a/examples/multi-page/Fes.toml b/examples/multi-page/Fes.toml new file mode 100644 index 0000000..219e8bc --- /dev/null +++ b/examples/multi-page/Fes.toml @@ -0,0 +1,5 @@ +[app] + +name = "multi-page" +version = "0.0.1" +authors = ["vx-clutch"] diff --git a/examples/multi-page/www/index.lua b/examples/multi-page/www/index.lua new file mode 100644 index 0000000..41ee99e --- /dev/null +++ b/examples/multi-page/www/index.lua @@ -0,0 +1,15 @@ +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:h1("Home") +site:note( + fes.std.ul({ + fes.std.a("page1"), + fes.std.a("page2"), + }) +) + +return site diff --git a/examples/multi-page/www/page1.lua b/examples/multi-page/www/page1.lua new file mode 100644 index 0000000..7072077 --- /dev/null +++ b/examples/multi-page/www/page1.lua @@ -0,0 +1,15 @@ +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:h1("Page 1") +site:note( + fes.std.ul({ + fes.std.a("/", "home"), + fes.std.a("page2"), + }) +) + +return site diff --git a/examples/multi-page/www/page2.lua b/examples/multi-page/www/page2.lua new file mode 100644 index 0000000..1a97048 --- /dev/null +++ b/examples/multi-page/www/page2.lua @@ -0,0 +1,15 @@ +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:h1("Page 2") +site:note( + fes.std.ul({ + fes.std.a("/", "home"), + fes.std.a("page1"), + }) +) + +return site diff --git a/main.go b/main.go index 11e6dda..55db57a 100644 --- a/main.go +++ b/main.go @@ -1,285 +1,29 @@ package main import ( + "embed" _ "embed" "flag" "fmt" - "net/http" "os" - "os/exec" - "os/user" - "path/filepath" - "regexp" - "strings" - "time" - "github.com/gomarkdown/markdown" - "github.com/gomarkdown/markdown/html" - "github.com/gomarkdown/markdown/parser" - "github.com/pelletier/go-toml/v2" - lua "github.com/yuin/gopher-lua" + "fes/src/config" + "fes/src/new" + "fes/src/server" ) -//go:embed core/builtin.lua -var builtinLua string +//go:embed core/* +var core embed.FS -//go:embed core/markdown.lua -var markdownLua string - -//go:embed core/std.lua -var stdLua string - -//go:embed core/console.lua -var consoleLua string - -const version = "1.0.0" - -type MyConfig struct { - Site struct { - Name string `toml:"name"` - Version string `toml:"version"` - Authors []string `toml:"authors"` - } `toml:"site"` - Fes struct { - Version string `toml:"version"` - CUSTOM_CSS string `toml:"CUSTOM_CSS,omitempty"` - } `toml:"fes"` -} - -var port = flag.Int("p", 3000, "set the server port") - -func markdownToHTML(mdText string) string { - extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock - p := parser.NewWithExtensions(extensions) - doc := p.Parse([]byte(mdText)) - - htmlFlags := html.CommonFlags | html.HrefTargetBlank - opts := html.RendererOptions{Flags: htmlFlags} - renderer := html.NewRenderer(opts) - - return string(markdown.Render(doc, renderer)) -} - -func loadLua(luaDir string, entry string, cfg *MyConfig) (string, error) { - L := lua.NewState() - defer L.Close() - - L.PreloadModule("core.std", func(L *lua.LState) int { - if err := L.DoString(stdLua); err != nil { - panic(err) - } - L.Push(L.Get(-1)) - return 1 - }) - - L.PreloadModule("fes", func(L *lua.LState) int { - mod := L.NewTable() - - coreModules := map[string]string{ - "builtin": builtinLua, - "markdown": markdownLua, - "std": stdLua, - "console": consoleLua, - } - - for modName, luaCode := range coreModules { - if err := L.DoString(luaCode); err != nil { - fmt.Println("error loading", modName, ":", err) - continue - } - val := L.Get(-1) - L.Pop(1) - tbl, ok := val.(*lua.LTable) - if !ok { - t := L.NewTable() - t.RawSetString("value", val) - tbl = t - } - if modName == "builtin" { - tbl.ForEach(func(key, value lua.LValue) { - mod.RawSet(key, value) - }) - } else { - mod.RawSetString(modName, tbl) - } - } - - if cfg != nil { - configTable := L.NewTable() - siteTable := L.NewTable() - siteTable.RawSetString("version", lua.LString(cfg.Site.Version)) - siteTable.RawSetString("name", lua.LString(cfg.Site.Name)) - authorsTable := L.NewTable() - for i, author := range cfg.Site.Authors { - authorsTable.RawSetInt(i+1, lua.LString(author)) - } - siteTable.RawSetString("authors", authorsTable) - configTable.RawSetString("site", siteTable) - fesTable := L.NewTable() - fesTable.RawSetString("version", lua.LString(cfg.Fes.Version)) - configTable.RawSetString("fes", fesTable) - mod.RawSetString("config", configTable) - } - - mod.RawSetString("markdown_to_html", L.NewFunction(func(L *lua.LState) int { - mdText := L.ToString(1) - html := markdownToHTML(mdText) - L.Push(lua.LString(html)) - return 1 - })) - L.Push(mod) - return 1 - }) - - if err := L.DoFile(entry); err != nil { - return "", err - } - - top := L.GetTop() - if top == 0 { - fmt.Println("warning: no return value from Lua file") - return "", nil - } - - resultVal := L.Get(-1) - L.SetGlobal("__fes_result", resultVal) - if err := L.DoString("return tostring(__fes_result)"); err != nil { - L.GetGlobal("__fes_result") - if s := L.ToString(-1); s != "" { - return s, nil - } - return "", nil - } - if s := L.ToString(-1); s != "" { - return s, nil - } - return "", nil -} - -func getName() string { - out, err := exec.Command("git", "config", "user.name").Output() - if err == nil { - s := strings.TrimSpace(string(out)) - if s != "" { - return s - } - } - u, err := user.Current() - if err == nil && u.Username != "" { - return u.Username - } - return "" -} - -func newProject(dir string) error { - if err := os.MkdirAll(filepath.Join(dir, "www"), 0755); err != nil { - return err - } - indexLua := filepath.Join(dir, "www", "index.lua") - if _, err := os.Stat(indexLua); os.IsNotExist(err) { - content := `local fes = require("fes") -local site = fes.site_builder() - -site:h1("Hello, World!") - -return site -` - if err := os.WriteFile(indexLua, []byte(content), 0644); err != nil { - return err - } - } - indexFes := filepath.Join(dir, "Fes.toml") - if _, err := os.Stat(indexFes); os.IsNotExist(err) { - content := fmt.Sprintf(`[site] - -name = "%s" -version = "0.0.1" -authors = ["%s"] - -[fes] -version = "%s" -CUSTOM_CSS = -`, dir, getName(), version) - if err := os.WriteFile(indexFes, []byte(content), 0644); err != nil { - return err - } - } - fmt.Println("Created new project at", dir) - return nil -} - -func fixMalformedToml(content string) string { - re := regexp.MustCompile(`(?m)^(\s*\w+\s*=\s*)$`) - return re.ReplaceAllStringFunc(content, func(match string) string { - parts := strings.Split(strings.TrimSpace(match), "=") - if len(parts) == 2 && strings.TrimSpace(parts[1]) == "" { - key := strings.TrimSpace(parts[0]) - return key + " = \"\"" - } - return match - }) -} - -func startServer(dir string) error { - doc, err := os.ReadFile(filepath.Join(dir, "Fes.toml")) - if err != nil { - return err - } - - docStr := fixMalformedToml(string(doc)) - - var cfg MyConfig - err = toml.Unmarshal([]byte(docStr), &cfg) - if err != nil { - return fmt.Errorf("failed to parse Fes.toml: %w", err) - } - - wwwDir := filepath.Join(dir, "www") - - entries, err := os.ReadDir(wwwDir) - if err != nil { - return fmt.Errorf("failed to read www directory: %w", err) - } - - routes := make(map[string]string) - for _, entry := range entries { - if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".lua") { - baseName := strings.TrimSuffix(entry.Name(), ".lua") - luaPath := filepath.Join(wwwDir, entry.Name()) - - if baseName == "index" { - routes["/"] = luaPath - routes["/index"] = luaPath - } else { - routes["/"+baseName] = luaPath - } - } - } - - for route, luaPath := range routes { - func(rt string, lp string) { - http.HandleFunc(rt, func(w http.ResponseWriter, r *http.Request) { - fmt.Printf("[%s] LOAD /%s\n", time.Now().Format(time.RFC1123), lp) - data, err := loadLua(dir, lp, &cfg) - if err != nil { - http.Error(w, fmt.Sprintf("Error loading page: %v", err), http.StatusInternalServerError) - return - } - w.Write([]byte(data)) - }) - }(route, luaPath) - } - - fmt.Printf("Server is running on http://localhost:%d\n", *port) - - return http.ListenAndServe(fmt.Sprintf(":%d", *port), nil) +func init() { + config.Port = flag.Int("p", 3000, "set the server port") + config.Core = core } func main() { flag.Parse() if len(os.Args) < 3 { fmt.Println("Usage: fes ") - fmt.Println("Commands: new, run") os.Exit(1) } @@ -288,11 +32,11 @@ func main() { switch cmd { case "new": - if err := newProject(dir); err != nil { + if err := new.Project(dir); err != nil { panic(err) } case "run": - if err := startServer(dir); err != nil { + if err := server.Start(dir); err != nil { panic(err) } default: diff --git a/src/config/config.go b/src/config/config.go new file mode 100644 index 0000000..ba878fe --- /dev/null +++ b/src/config/config.go @@ -0,0 +1,14 @@ +package config + +import "embed" + +var Core embed.FS +var Port *int + +type MyConfig struct { + App struct { + Name string `toml:"name"` + Version string `toml:"version"` + Authors []string `toml:"authors"` + } `toml:"app"` +} diff --git a/src/new/new.go b/src/new/new.go new file mode 100644 index 0000000..0c534f8 --- /dev/null +++ b/src/new/new.go @@ -0,0 +1,57 @@ +package new + +import ( + "fmt" + "os" + "os/exec" + "os/user" + "path/filepath" + "strings" +) + +func getName() string { + out, err := exec.Command("git", "config", "user.name").Output() + if err == nil { + s := strings.TrimSpace(string(out)) + if s != "" { + return s + } + } + u, err := user.Current() + if err == nil && u.Username != "" { + return u.Username + } + return "" +} + +func Project(dir string) error { + if err := os.MkdirAll(filepath.Join(dir, "www"), 0755); err != nil { + return err + } + indexLua := filepath.Join(dir, "www", "index.lua") + if _, err := os.Stat(indexLua); os.IsNotExist(err) { + content := `local fes = require("fes") +local site = fes.fes() + +site:h1("Hello, World!") + +return site +` + if err := os.WriteFile(indexLua, []byte(content), 0644); err != nil { + return err + } + } + indexFes := filepath.Join(dir, "Fes.toml") + if _, err := os.Stat(indexFes); os.IsNotExist(err) { + content := fmt.Sprintf(`[app] + +name = "%s" +version = "0.0.1" +authors = ["%s"]`, dir, getName()) + if err := os.WriteFile(indexFes, []byte(content), 0644); err != nil { + return err + } + } + fmt.Println("Created new project at", dir) + return nil +} diff --git a/src/server/server.go b/src/server/server.go new file mode 100644 index 0000000..522aa05 --- /dev/null +++ b/src/server/server.go @@ -0,0 +1,247 @@ +package server + +import ( + "fmt" + "io/fs" + "net/http" + "os" + "path/filepath" + "regexp" + "strings" + + "github.com/gomarkdown/markdown" + "github.com/gomarkdown/markdown/html" + "github.com/gomarkdown/markdown/parser" + "github.com/pelletier/go-toml/v2" + lua "github.com/yuin/gopher-lua" + + "fes/src/config" +) + +func fixMalformedToml(content string) string { + re := regexp.MustCompile(`(?m)^(\s*\w+\s*=\s*)$`) + return re.ReplaceAllStringFunc(content, func(match string) string { + parts := strings.Split(strings.TrimSpace(match), "=") + if len(parts) == 2 && strings.TrimSpace(parts[1]) == "" { + key := strings.TrimSpace(parts[0]) + return key + " = \"\"" + } + return match + }) +} + +func markdownToHTML(mdText string) string { + extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock + p := parser.NewWithExtensions(extensions) + doc := p.Parse([]byte(mdText)) + htmlFlags := html.CommonFlags | html.HrefTargetBlank + opts := html.RendererOptions{Flags: htmlFlags} + renderer := html.NewRenderer(opts) + return string(markdown.Render(doc, renderer)) +} + +func loadIncludeModules(L *lua.LState, includeDir string) *lua.LTable { + app := L.NewTable() + ents, err := os.ReadDir(includeDir) + if err != nil { + return app + } + for _, e := range ents { + if e.IsDir() { + continue + } + name := e.Name() + if !strings.HasSuffix(name, ".lua") { + continue + } + base := strings.TrimSuffix(name, ".lua") + path := filepath.Join(includeDir, name) + if err := L.DoFile(path); err != nil { + fmt.Printf("Failed to load %s: %v\n", path, err) + continue + } + val := L.Get(-1) + L.Pop(1) + tbl, ok := val.(*lua.LTable) + if !ok { + tbl = L.NewTable() + } + app.RawSetString(base, tbl) + } + return app +} + +func loadLua(luaDir string, entry string, cfg *config.MyConfig) (string, error) { + L := lua.NewState() + defer L.Close() + + rdents, err := fs.ReadDir(config.Core, "core") + if err == nil { + for _, de := range rdents { + if de.IsDir() { + continue + } + name := de.Name() + if !strings.HasSuffix(name, ".lua") { + continue + } + path := filepath.Join("core", name) + data, err := config.Core.ReadFile(path) + if err != nil { + continue + } + if err := L.DoString(string(data)); err != nil { + continue + } + } + } + + L.PreloadModule("core.std", func(L *lua.LState) int { + data, err := config.Core.ReadFile("core/std.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{} + if ents, err := fs.ReadDir(config.Core, "core"); err == nil { + for _, e := range ents { + if e.IsDir() { + continue + } + n := e.Name() + if strings.HasSuffix(n, ".lua") { + coreModules = append(coreModules, strings.TrimSuffix(n, ".lua")) + } + } + } + for _, modName := range coreModules { + path := filepath.Join("core", modName+".lua") + data, err := config.Core.ReadFile(path) + if err != nil { + continue + } + if err := L.DoString(string(data)); err != nil { + continue + } + val := L.Get(-1) + L.Pop(1) + tbl, ok := val.(*lua.LTable) + if !ok || tbl == nil { + tbl = L.NewTable() + } + if modName == "builtin" { + tbl.ForEach(func(k, v lua.LValue) { + mod.RawSet(k, v) + }) + } else { + mod.RawSetString(modName, tbl) + } + } + + includeDir := filepath.Join(luaDir, "include") + appTbl := loadIncludeModules(L, includeDir) + mod.RawSetString("app", appTbl) + + if cfg != nil { + siteTable := L.NewTable() + siteTable.RawSetString("version", lua.LString(cfg.App.Version)) + siteTable.RawSetString("name", lua.LString(cfg.App.Name)) + authorsTable := L.NewTable() + for i, author := range cfg.App.Authors { + authorsTable.RawSetInt(i+1, lua.LString(author)) + } + siteTable.RawSetString("authors", authorsTable) + mod.RawSetString("site", siteTable) + } + + mod.RawSetString("markdown_to_html", L.NewFunction(func(L *lua.LState) int { + mdText := L.ToString(1) + html := markdownToHTML(mdText) + L.Push(lua.LString(html)) + return 1 + })) + + L.Push(mod) + return 1 + }) + + if err := L.DoFile(entry); err != nil { + return "", err + } + + top := L.GetTop() + if top == 0 { + return "", nil + } + resultVal := L.Get(-1) + L.SetGlobal("__fes_result", resultVal) + if err := L.DoString("return tostring(__fes_result)"); err != nil { + L.GetGlobal("__fes_result") + if s := L.ToString(-1); s != "" { + return s, nil + } + return "", nil + } + if s := L.ToString(-1); s != "" { + return s, nil + } + return "", nil +} + +func Start(dir string) error { + doc, err := os.ReadFile(filepath.Join(dir, "Fes.toml")) + if err != nil { + return err + } + docStr := fixMalformedToml(string(doc)) + var cfg config.MyConfig + err = toml.Unmarshal([]byte(docStr), &cfg) + if err != nil { + return fmt.Errorf("failed to parse Fes.toml: %w", err) + } + + wwwDir := filepath.Join(dir, "www") + entries, err := os.ReadDir(wwwDir) + if err != nil { + return fmt.Errorf("failed to read www directory: %w", err) + } + + routes := make(map[string]string) + for _, entry := range entries { + if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".lua") { + baseName := strings.TrimSuffix(entry.Name(), ".lua") + luaPath := filepath.Join(wwwDir, entry.Name()) + if baseName == "index" { + routes["/"] = luaPath + routes["/index"] = luaPath + } else { + routes["/"+baseName] = luaPath + } + } + } + + for route, luaPath := range routes { + func(rt string, lp string) { + http.HandleFunc(rt, func(w http.ResponseWriter, r *http.Request) { + fmt.Printf("-> %s\n", lp) + data, err := loadLua(dir, lp, &cfg) + if err != nil { + http.Error(w, fmt.Sprintf("Error loading page: %v", err), http.StatusInternalServerError) + return + } + w.Write([]byte(data)) + }) + }(route, luaPath) + } + + fmt.Printf("Server is running on http://localhost:%d\n", *config.Port) + return http.ListenAndServe(fmt.Sprintf(":%d", *config.Port), nil) +} diff --git a/test/Fes.toml b/test/Fes.toml deleted file mode 100644 index 3698755..0000000 --- a/test/Fes.toml +++ /dev/null @@ -1,9 +0,0 @@ -[site] - -name = "test" -version = "0.0.1" -authors = ["vx-clutch"] - -[fes] -version = "1.0.0" -CUSTOM_CSS = diff --git a/test/www/index.lua b/test/www/index.lua deleted file mode 100644 index 4b8cfb1..0000000 --- a/test/www/index.lua +++ /dev/null @@ -1,11 +0,0 @@ -local fes = require("fes") -local site = fes() - -site:h1("

") -site:h2("

") -site:h3("

") -site:h4("

") -site:h5("

") -site:h6("
") - -return site