diff --git a/Makefile b/Makefile
index 3fe8d26..8f11716 100644
--- a/Makefile
+++ b/Makefile
@@ -8,6 +8,10 @@ serve:
@echo "anna: serving site"
@go build
@./anna -s "site/"
+wizard:
+ @echo "anna: running wizard"
+ @go build
+ @./anna -w
tests:
@echo "anna: running all tests"
@go test ./...
diff --git a/anna.json b/anna.json
new file mode 100644
index 0000000..cb96b95
--- /dev/null
+++ b/anna.json
@@ -0,0 +1,5 @@
+{
+ "siteDataPaths": {
+ "site": "site/"
+ }
+}
diff --git a/anna.yml b/anna.yml
deleted file mode 100644
index 691a1f5..0000000
--- a/anna.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-siteDataPaths:
- - site: site/
diff --git a/cmd/anna/anna.go b/cmd/anna/anna.go
index d45b82f..1346d34 100644
--- a/cmd/anna/anna.go
+++ b/cmd/anna/anna.go
@@ -1,6 +1,7 @@
package anna
import (
+ "encoding/json"
"html/template"
"log"
"os"
@@ -9,7 +10,6 @@ import (
"github.com/anna-ssg/anna/v2/pkg/engine"
"github.com/anna-ssg/anna/v2/pkg/helpers"
"github.com/anna-ssg/anna/v2/pkg/parser"
- "gopkg.in/yaml.v3"
)
type Cmd struct {
@@ -25,7 +25,7 @@ type Cmd struct {
}
type AnnaConfig struct {
- SiteDataPaths []map[string]string `yaml:"siteDataPaths"`
+ SiteDataPaths map[string]string `json:"siteDataPaths"`
}
func (cmd *Cmd) VanillaRenderManager() {
@@ -33,21 +33,21 @@ func (cmd *Cmd) VanillaRenderManager() {
// Check if the configuration file exists
// If it does not, render only the site/ directory
- _, err := os.Stat("anna.yml")
+ _, err := os.Stat("anna.json")
if os.IsNotExist(err) {
cmd.VanillaRender("site/")
return
}
// Read and parse the configuration file
- annaConfigFile, err := os.ReadFile("anna.yml")
+ annaConfigFile, err := os.ReadFile("anna.json")
if err != nil {
cmd.ErrorLogger.Fatal(err)
}
var annaConfig AnnaConfig
- err = yaml.Unmarshal(annaConfigFile, &annaConfig)
+ err = json.Unmarshal(annaConfigFile, &annaConfig)
if err != nil {
cmd.ErrorLogger.Fatal(err)
}
@@ -56,13 +56,11 @@ func (cmd *Cmd) VanillaRenderManager() {
if cmd.RenderSpecificSite == "" {
siteRendered := false
- for _, sites := range annaConfig.SiteDataPaths {
- for _, path := range sites {
- if !siteRendered {
- siteRendered = true
- }
- cmd.VanillaRender(path)
+ for _, path := range annaConfig.SiteDataPaths {
+ if !siteRendered {
+ siteRendered = true
}
+ cmd.VanillaRender(path)
}
// If no site has been rendered due to empty "anna.yml", render the default "site/" path
@@ -72,12 +70,10 @@ func (cmd *Cmd) VanillaRenderManager() {
} else {
siteRendered := false
- for _, sites := range annaConfig.SiteDataPaths {
- for _, sitePath := range sites {
- if strings.Compare(cmd.RenderSpecificSite, sitePath) == 0 {
- cmd.VanillaRender(sitePath)
- siteRendered = true
- }
+ for _, sitePath := range annaConfig.SiteDataPaths {
+ if strings.Compare(cmd.RenderSpecificSite, sitePath) == 0 {
+ cmd.VanillaRender(sitePath)
+ siteRendered = true
}
}
@@ -96,21 +92,21 @@ func (cmd *Cmd) ValidateHTMLManager() {
// Check if the configuration file exists
// If it does not, validate only the site/ directory
- _, err := os.Stat("anna.yml")
+ _, err := os.Stat("anna.json")
if os.IsNotExist(err) {
cmd.VanillaRender("site/")
return
}
// Read and parse the configuration file
- annaConfigFile, err := os.ReadFile("anna.yml")
+ annaConfigFile, err := os.ReadFile("anna.json")
if err != nil {
cmd.ErrorLogger.Fatal(err)
}
var annaConfig AnnaConfig
- err = yaml.Unmarshal(annaConfigFile, &annaConfig)
+ err = json.Unmarshal(annaConfigFile, &annaConfig)
if err != nil {
cmd.ErrorLogger.Fatal(err)
}
@@ -118,12 +114,10 @@ func (cmd *Cmd) ValidateHTMLManager() {
// Validating sites
validatedSites := false
- for _, sites := range annaConfig.SiteDataPaths {
- for _, sitePath := range sites {
- cmd.ValidateHTMLContent(sitePath)
- if !validatedSites {
- validatedSites = true
- }
+ for _, sitePath := range annaConfig.SiteDataPaths {
+ cmd.ValidateHTMLContent(sitePath)
+ if !validatedSites {
+ validatedSites = true
}
}
@@ -139,21 +133,21 @@ func (cmd *Cmd) LiveReloadManager() {
// Check if the configuration file exists
// If it does not, serve only the site/ directory
- _, err := os.Stat("anna.yml")
+ _, err := os.Stat("anna.json")
if os.IsNotExist(err) {
cmd.StartLiveReload("site/")
return
}
// Read and parse the configuration file
- annaConfigFile, err := os.ReadFile("anna.yml")
+ annaConfigFile, err := os.ReadFile("anna.json")
if err != nil {
cmd.ErrorLogger.Fatal(err)
}
var annaConfig AnnaConfig
- err = yaml.Unmarshal(annaConfigFile, &annaConfig)
+ err = json.Unmarshal(annaConfigFile, &annaConfig)
if err != nil {
cmd.ErrorLogger.Fatal(err)
}
@@ -162,12 +156,10 @@ func (cmd *Cmd) LiveReloadManager() {
if cmd.ServeSpecificSite == "" {
cmd.StartLiveReload("site/")
} else {
- for _, sites := range annaConfig.SiteDataPaths {
- for _, sitePath := range sites {
- if strings.Compare(cmd.ServeSpecificSite, sitePath) == 0 {
- cmd.StartLiveReload(sitePath)
- return
- }
+ for _, sitePath := range annaConfig.SiteDataPaths {
+ if strings.Compare(cmd.ServeSpecificSite, sitePath) == 0 {
+ cmd.StartLiveReload(sitePath)
+ return
}
}
@@ -205,7 +197,7 @@ func (cmd *Cmd) VanillaRender(siteDirPath string) {
helper.CreateRenderedDir(siteDirPath)
- p.ParseConfig(siteDirPath + "layout/config.yml")
+ p.ParseConfig(siteDirPath + "layout/config.json")
p.ParseRobots(siteDirPath+"layout/robots.txt", siteDirPath+"rendered/robots.txt")
fileSystem := os.DirFS(siteDirPath + "content/")
diff --git a/cmd/anna/livereload.go b/cmd/anna/livereload.go
index 9d9eaf6..48d8c10 100644
--- a/cmd/anna/livereload.go
+++ b/cmd/anna/livereload.go
@@ -35,7 +35,7 @@ func newLiveReload(siteDataPath string) *liveReload {
errorLogger: log.New(os.Stderr, "ERROR\t", log.Ldate|log.Ltime|log.Lshortfile),
fileTimes: make(map[string]time.Time),
rootDirs: []string{siteDataPath},
- extensions: []string{".go", ".md", ".yml"},
+ extensions: []string{".go", ".md", ".json"},
siteDataPath: siteDataPath,
}
return &lr
diff --git a/cmd/anna/wizard.go b/cmd/anna/wizard.go
index 97b50c6..dd9b462 100644
--- a/cmd/anna/wizard.go
+++ b/cmd/anna/wizard.go
@@ -4,43 +4,47 @@ import (
"context"
"encoding/json"
"errors"
- "fmt"
"log"
"net/http"
"os"
"path/filepath"
- "gopkg.in/yaml.v3"
+ "github.com/anna-ssg/anna/v2/pkg/parser"
)
-type Config struct {
- BaseURL string `yaml:"baseURL"`
- SiteTitle string `yaml:"siteTitle"`
- SiteScripts []string `yaml:"siteScripts"`
- Author string `yaml:"author"`
- ThemeURL string `yaml:"themeURL"`
- Navbar []string `yaml:"navbar"`
-}
-
type WizardServer struct {
- server *http.Server
+ server *http.Server
+ serveMux *http.ServeMux
+
+ // Common logger for all parser functions
+ InfoLogger *log.Logger
+ // Common logger for all parser functions
+ ErrorLogger *log.Logger
}
+var FormSubmittedCh = make(chan struct{})
+
func NewWizardServer(addr string) *WizardServer {
- return &WizardServer{
- server: &http.Server{
- Addr: addr,
- },
+ serveMuxLocal := http.NewServeMux()
+
+ wizardServer := WizardServer{
+ serveMux: serveMuxLocal,
+ server: &http.Server{Addr: addr, Handler: serveMuxLocal},
+ InfoLogger: log.New(os.Stderr, "INFO\t", log.Ldate|log.Ltime),
+ ErrorLogger: log.New(os.Stderr, "ERROR\t", log.Ldate|log.Ltime|log.Lshortfile),
}
+
+ return &wizardServer
}
func (ws *WizardServer) Start() {
- http.HandleFunc("/submit", ws.handleSubmit)
+ ws.serveMux.HandleFunc("/submit", ws.handleSubmit)
fs := http.FileServer(http.Dir("./site/static/wizard"))
- http.Handle("/", fs)
- fmt.Printf("Wizard is running at: http://localhost%s\n", ws.server.Addr)
+ ws.serveMux.Handle("/", fs)
+ ws.InfoLogger.Printf("Wizard is running at: http://localhost%s\n", ws.server.Addr)
+
if err := ws.server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
- log.Fatalf("Could not start server: %v", err)
+ ws.ErrorLogger.Fatalf("Could not start server: %v", err)
}
}
@@ -51,44 +55,47 @@ func (ws *WizardServer) Stop() error {
func (ws *WizardServer) handleSubmit(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+ ws.ErrorLogger.Println("Method not allowed")
return
}
- var config Config
+
+ var config parser.LayoutConfig
err := json.NewDecoder(r.Body).Decode(&config)
+
+ err = ws.writeConfigToFile(config)
if err != nil {
- http.Error(w, err.Error(), http.StatusBadRequest)
- return
- }
- err = writeConfigToFile(config)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
+ ws.ErrorLogger.Println(err)
return
}
FormSubmittedCh <- struct{}{}
}
-func writeConfigToFile(config Config) error {
- configFilePath := "./site/layout/config.yml"
+func (ws *WizardServer) writeConfigToFile(config parser.LayoutConfig) error {
+ configFilePath := "./site/layout/config.json"
if err := os.MkdirAll(filepath.Dir(configFilePath), 0755); err != nil {
return err
}
- file, err := os.Create(configFilePath)
+ marshaledJsonConfig, err := json.Marshal(config)
+ if err != nil {
+ ws.ErrorLogger.Fatal(err)
+ }
+
+ configFile, err := os.Create(configFilePath)
if err != nil {
return err
}
defer func() {
- err = file.Close()
+ err = configFile.Close()
if err != nil {
- log.Fatal(err)
+ ws.ErrorLogger.Fatal(err)
}
}()
+ os.WriteFile(configFilePath, marshaledJsonConfig, 0666)
- // Encode the config into YAML format and write it to the file.
- if err := yaml.NewEncoder(file).Encode(&config); err != nil {
+ if err != nil {
return err
}
+
return nil
}
-
-var FormSubmittedCh = make(chan struct{})
diff --git a/main.go b/main.go
index 02153e6..ca595c1 100644
--- a/main.go
+++ b/main.go
@@ -31,7 +31,7 @@ func main() {
RenderSpecificSite: renderSpecificSite,
ServeSpecificSite: serve,
ErrorLogger: log.New(os.Stderr, "ERROR\t", log.Ldate|log.Ltime|log.Lshortfile),
- InfoLogger: log.New(os.Stderr, "LOG\t", log.Ldate|log.Ltime|log.Lshortfile),
+ InfoLogger: log.New(os.Stderr, "LOG\t", log.Ldate|log.Ltime),
}
if serve != "" {
@@ -61,9 +61,9 @@ func main() {
if err := server.Stop(); err != nil {
annaCmd.InfoLogger.Println(err)
}
- annaCmd.VanillaRenderManager()
annaCmd.LiveReloadManager()
}
+
annaCmd.VanillaRenderManager()
},
}
diff --git a/pkg/engine/anna_engine.go b/pkg/engine/anna_engine.go
index c5ceb8d..7de7b00 100644
--- a/pkg/engine/anna_engine.go
+++ b/pkg/engine/anna_engine.go
@@ -81,6 +81,9 @@ func (e *Engine) RenderTags(fileOutPath string, templ *template.Template) {
e.DeepDataMerge.Tags = make(map[template.URL]parser.TemplateData)
for tag := range e.DeepDataMerge.TagsMap {
+ slices.SortFunc(e.DeepDataMerge.TagsMap[tag], func(a, b parser.TemplateData) int {
+ return cmp.Compare(b.Date, a.Date)
+ })
tagString := string(tag)
tagString, _ = strings.CutPrefix(tagString, "tags/")
tagString, _ = strings.CutSuffix(tagString, ".html")
@@ -139,7 +142,7 @@ func (e *Engine) RenderCollections(fileOutPath string, templ *template.Template)
CollectionNames: collectionNames,
}
- // Rendering the page displaying all tags
+ // Rendering the page displaying all collections
err := templ.ExecuteTemplate(&collectionsBuffer, "all-collections", collectionTemplateData)
if err != nil {
e.ErrorLogger.Fatal(err)
@@ -157,6 +160,10 @@ func (e *Engine) RenderCollections(fileOutPath string, templ *template.Template)
e.DeepDataMerge.Collections = make(map[template.URL]parser.TemplateData)
for collection := range e.DeepDataMerge.CollectionsMap {
+ slices.SortFunc(e.DeepDataMerge.CollectionsMap[collection], func(a, b parser.TemplateData) int {
+ return cmp.Compare(b.Date, a.Date)
+ })
+
collectionString := string(collection)
collectionString, _ = strings.CutPrefix(collectionString, "collections/")
collectionString, _ = strings.CutSuffix(collectionString, ".html")
@@ -297,7 +304,6 @@ func (e *Engine) GenerateFeed() {
buffer.WriteString("