diff --git a/URGENT b/URGENT new file mode 100644 index 0000000..c128aa3 --- /dev/null +++ b/URGENT @@ -0,0 +1 @@ +IT DOSN'T LOAD FES diff --git a/core/builtin.lua b/core/builtin.lua index 95f9f8d..d862f67 100644 --- a/core/builtin.lua +++ b/core/builtin.lua @@ -13,6 +13,10 @@ function M.fes(header, footer) end end + if site_config.favicon then + site_config.favicon = '' + end + local self = { version = site_config.version, title = site_config.title, diff --git a/examples/archive/Fes.toml b/examples/archive/Fes.toml new file mode 100644 index 0000000..82c5002 --- /dev/null +++ b/examples/archive/Fes.toml @@ -0,0 +1,5 @@ +[app] + +name = "archive" +version = "0.0.1" +authors = ["vx-clutch"] \ No newline at end of file diff --git a/examples/archive/archive/2025/seal.png b/examples/archive/archive/2025/seal.png new file mode 100644 index 0000000..0ae7980 Binary files /dev/null and b/examples/archive/archive/2025/seal.png differ diff --git a/examples/archive/www/index.lua b/examples/archive/www/index.lua new file mode 100644 index 0000000..8819249 --- /dev/null +++ b/examples/archive/www/index.lua @@ -0,0 +1,8 @@ +local fes = require("fes") +local site = fes.fes() + +site.title = "archive" + +site:h1("Hello, World!") + +return site diff --git a/examples/error/Fes.toml b/examples/error/Fes.toml index 9cd794b..8327ff6 100644 --- a/examples/error/Fes.toml +++ b/examples/error/Fes.toml @@ -1,5 +1 @@ -[app] - -name = "error" -version = "0.0.1" -authors = ["vx-clutch"] \ No newline at end of file +dhasjkdhaskjdhaskhdajkshjk diff --git a/examples/static/Fes.toml b/examples/static/Fes.toml new file mode 100644 index 0000000..46996a2 --- /dev/null +++ b/examples/static/Fes.toml @@ -0,0 +1,5 @@ +[app] + +name = "static" +version = "0.0.1" +authors = ["vx-clutch"] \ No newline at end of file diff --git a/examples/static/static/foo b/examples/static/static/foo new file mode 100644 index 0000000..115e5b7 --- /dev/null +++ b/examples/static/static/foo @@ -0,0 +1 @@ +this is some neat data diff --git a/examples/static/www/index.lua b/examples/static/www/index.lua new file mode 100644 index 0000000..9e2e206 --- /dev/null +++ b/examples/static/www/index.lua @@ -0,0 +1,8 @@ +local fes = require("fes") +local site = fes.fes() + +site.title = "static" + +site:h1("Hello, World!") + +return site diff --git a/src/new/new.go b/src/new/new.go index caea2d5..bf21111 100644 --- a/src/new/new.go +++ b/src/new/new.go @@ -1,5 +1,4 @@ package new - import ( "fmt" "os" @@ -10,8 +9,7 @@ import ( ) func getName() string { - out, err := exec.Command("git", "config", "user.name").Output() - if err == nil { + out, err := exec.Command("git", "config", "user.name").Output(); if err == nil { s := strings.TrimSpace(string(out)) if s != "" { return s @@ -24,36 +22,71 @@ func getName() string { 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 := 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 +// } +// } +// 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 +// } + +func write(path string, format string, args ...interface{}) error { + dir := filepath.Dir(path) + if err := os.MkdirAll(dir, 0755); err != nil { + panic(err) + } + f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + panic(err) + } + defer f.Close() + _, err = fmt.Fprintf(f, format, args...) + return err +} + func Project(dir string) error { - if err := os.MkdirAll(filepath.Join(dir, "www"), 0755); err != nil { + if err := os.Mkdir(dir, 0755); err != nil { return err } - indexLua := filepath.Join(dir, "www", "index.lua") - if _, err := os.Stat(indexLua); os.IsNotExist(err) { - content := fmt.Sprintf(`local fes = require("fes") + if err := os.Chdir(dir); err != nil { + return err + } + write("www/index.lua", `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 - } - } - indexFes := filepath.Join(dir, "Fes.toml") - if _, err := os.Stat(indexFes); os.IsNotExist(err) { - content := fmt.Sprintf(`[app] +return site`) + write("Fes.toml", `[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 index 5b14640..7676291 100644 --- a/src/server/server.go +++ b/src/server/server.go @@ -2,78 +2,70 @@ package server import ( "fmt" - "io/fs" "net/http" "os" "path/filepath" "regexp" "strings" + "github.com/fatih/color" "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" - "github.com/fatih/color" "fes/src/config" ) type reqData struct { - path string + path string params map[string]string } -func handleDir(entries []os.DirEntry, dir string, routes map[string]string, base string) error { +func handleDir(entries []os.DirEntry, dir string, routes map[string]string, base string, isStatic bool) error { for _, entry := range entries { + path := filepath.Join(dir, entry.Name()) if entry.IsDir() { - sub := filepath.Join(dir, entry.Name()) - subs, err := os.ReadDir(sub) + nextBase := joinBase(base, entry.Name()) + subEntries, err := os.ReadDir(path) if err != nil { - return fmt.Errorf("failed to read %s: %w", sub, err) + return fmt.Errorf("failed to read directory %s: %w", path, err) } - var next string - if base == "" { - next = "/" + entry.Name() - } else { - next = base + "/" + entry.Name() - } - if err := handleDir(subs, sub, routes, next); err != nil { + if err := handleDir(subEntries, path, routes, nextBase, isStatic); err != nil { return err } continue } - if strings.HasSuffix(entry.Name(), ".lua") { + route := joinBase(base, entry.Name()) + if !isStatic && strings.HasSuffix(entry.Name(), ".lua") { name := strings.TrimSuffix(entry.Name(), ".lua") - path := filepath.Join(dir, entry.Name()) if name == "index" { - if base == "" { - routes["/"] = path - routes["/index"] = path - } else { - routes[base] = path - routes[base+"/index"] = path - } - } else { - if base == "" { - routes["/"+name] = path - } else { - routes[base+"/"+name] = path - } - } - } else { - name := entry.Name() - path := filepath.Join(dir, entry.Name()) - if base == "" { - routes["/"+name] = path - } else { - routes[base+"/"+name] = path + routes[basePath(base)] = path + routes[route] = path + continue } + route = joinBase(base, name) } + routes[route] = path } return nil } +func joinBase(base, name string) string { + if base == "" { + return "/" + name + } + return base + "/" + name +} + +func basePath(base string) string { + if base == "" { + return "/" + } + return base +} + func fixMalformedToml(content string) string { re := regexp.MustCompile(`(?m)^(\s*\w+\s*=\s*)$`) return re.ReplaceAllStringFunc(content, func(match string) string { @@ -103,17 +95,15 @@ func loadIncludeModules(L *lua.LState, includeDir string) *lua.LTable { return app } for _, e := range ents { - if e.IsDir() { + if e.IsDir() || !strings.HasSuffix(e.Name(), ".lua") { continue } - name := e.Name() - if !strings.HasSuffix(name, ".lua") { - continue - } - base := strings.TrimSuffix(name, ".lua") - path := filepath.Join(includeDir, name) + base := strings.TrimSuffix(e.Name(), ".lua") + path := filepath.Join(includeDir, e.Name()) if err := L.DoFile(path); err != nil { - fmt.Printf("Failed to load %s: %v\n", path, err) + tbl := L.NewTable() + tbl.RawSetString("error", lua.LString(err.Error())) + app.RawSetString(base, tbl) continue } val := L.Get(-1) @@ -127,35 +117,33 @@ func loadIncludeModules(L *lua.LState, includeDir string) *lua.LTable { return app } -func loadLua(luaDir string, entry string, cfg *config.MyConfig, requestData reqData) (string, error) { +func loadLua(luaDir, entry string, cfg *config.MyConfig, requestData reqData) (string, error) { L := lua.NewState() defer L.Close() - coreFiles, err := fs.ReadDir(config.Core, "core") - if err == nil { - for _, de := range coreFiles { - if de.IsDir() || !strings.HasSuffix(de.Name(), ".lua") { - continue - } - path := filepath.Join("core", de.Name()) - fileData, err := config.Core.ReadFile(path) - if err != nil { - continue - } - L.DoString(string(fileData)) - } - } - preloadLuaModule := func(name, path string) { L.PreloadModule(name, func(L *lua.LState) int { - fileData, err := config.Core.ReadFile(path) + data, err := config.Core.ReadFile(path) if err != nil { - panic(err) + tbl := L.NewTable() + tbl.RawSetString("error", lua.LString(err.Error())) + L.Push(tbl) + return 1 } - if err := L.DoString(string(fileData)); err != nil { - panic(err) + if _, err := L.LoadString(string(data)); err != nil { + tbl := L.NewTable() + tbl.RawSetString("error", lua.LString(err.Error())) + L.Push(tbl) + return 1 } - L.Push(L.Get(-1)) + if err := L.CallByParam(lua.P{Fn: L.Get(-1), NRet: 1, Protect: true}); err != nil { + tbl := L.NewTable() + tbl.RawSetString("error", lua.LString(err.Error())) + L.Push(tbl) + return 1 + } + ret := L.Get(-1) + L.Push(ret) return 1 }) } @@ -166,42 +154,8 @@ func loadLua(luaDir string, entry string, cfg *config.MyConfig, requestData reqD 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() || !strings.HasSuffix(e.Name(), ".lua") { - continue - } - coreModules = append(coreModules, strings.TrimSuffix(e.Name(), ".lua")) - } - } - - for _, modName := range coreModules { - path := filepath.Join("core", modName+".lua") - fileData, err := config.Core.ReadFile(path) - if err != nil { - continue - } - if err := L.DoString(string(fileData)); 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") mod.RawSetString("app", loadIncludeModules(L, includeDir)) - if cfg != nil { site := L.NewTable() site.RawSetString("version", lua.LString(cfg.App.Version)) @@ -213,7 +167,6 @@ func loadLua(luaDir string, entry string, cfg *config.MyConfig, requestData reqD site.RawSetString("authors", authors) mod.RawSetString("site", site) } - bus := L.NewTable() bus.RawSetString("url", lua.LString(requestData.path)) params := L.NewTable() @@ -222,12 +175,10 @@ func loadLua(luaDir string, entry string, cfg *config.MyConfig, requestData reqD } bus.RawSetString("params", params) mod.RawSetString("bus", bus) - mod.RawSetString("markdown_to_html", L.NewFunction(func(L *lua.LState) int { L.Push(lua.LString(markdownToHTML(L.ToString(1)))) return 1 })) - L.Push(mod) return 1 }) @@ -242,35 +193,31 @@ func loadLua(luaDir string, entry string, cfg *config.MyConfig, requestData reqD L.SetGlobal("__fes_result", L.Get(-1)) 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 + return "", err } - if s := L.ToString(-1); s != "" { - return s, nil - } - return "", nil + return L.ToString(-1), nil } func Start(dir string) error { - os.Chdir(dir) + if err := os.Chdir(dir); err != nil { + return fmt.Errorf("failed to change directory to %s: %w", dir, err) + } dir = "." tomlDocument, err := os.ReadFile("Fes.toml") if err != nil { - return err + return fmt.Errorf("failed to read Fes.toml: %w", err) } docStr := fixMalformedToml(string(tomlDocument)) var cfg config.MyConfig - err = toml.Unmarshal([]byte(docStr), &cfg) - if err != nil { - return fmt.Errorf("failed to parse Fes.toml: %w", err) + if err := toml.Unmarshal([]byte(docStr), &cfg); err != nil { + fmt.Printf("Warning: failed to parse Fes.toml: %v\n", err) + cfg.App.Authors = []string{"unknown"} + cfg.App.Name = "unknown" + cfg.App.Version = "unknown" } - notFoundData := `