diff --git a/.gitignore b/.gitignore index f1febea..576c567 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -site/rendered/ anna !anna/ *.exe @@ -7,3 +6,8 @@ anna ssg/ *.txt dist/ + +#Test directories +**/rendered/ +test/**/static/ +test/**/got_sitemap.xml \ No newline at end of file diff --git a/cmd/anna/anna.go b/cmd/anna/anna.go index 0db0eec..4537c61 100644 --- a/cmd/anna/anna.go +++ b/cmd/anna/anna.go @@ -14,21 +14,24 @@ import ( type Cmd struct { RenderDrafts bool Addr string + LiveReload bool } func (cmd *Cmd) VanillaRender() { // Defining Engine and Parser Structures p := parser.Parser{ Templates: make(map[template.URL]parser.TemplateData), - TagsMap: make(map[string][]parser.TemplateData), + TagsMap: make(map[template.URL][]parser.TemplateData), ErrorLogger: log.New(os.Stderr, "ERROR\t", log.Ldate|log.Ltime|log.Lshortfile), RenderDrafts: cmd.RenderDrafts, + LiveReload: cmd.LiveReload, } + e := engine.Engine{ - Templates: make(map[template.URL]parser.TemplateData), - TagsMap: make(map[string][]parser.TemplateData), ErrorLogger: log.New(os.Stderr, "ERROR\t", log.Ldate|log.Ltime|log.Lshortfile), } + e.DeepDataMerge.Templates = make(map[template.URL]parser.TemplateData) + e.DeepDataMerge.TagsMap = make(map[template.URL][]parser.TemplateData) helper := helpers.Helper{ ErrorLogger: e.ErrorLogger, @@ -47,26 +50,26 @@ func (cmd *Cmd) VanillaRender() { p.ParseRobots(helpers.SiteDataPath+"layout/robots.txt", helpers.SiteDataPath+"rendered/robots.txt") p.ParseLayoutFiles() - e.Templates = p.Templates - e.TagsMap = p.TagsMap - e.LayoutConfig = p.LayoutConfig - e.Posts = p.Posts + e.DeepDataMerge.Templates = p.Templates + e.DeepDataMerge.TagsMap = p.TagsMap + e.DeepDataMerge.LayoutConfig = p.LayoutConfig + e.DeepDataMerge.Posts = p.Posts e.GenerateSitemap(helpers.SiteDataPath + "rendered/sitemap.xml") e.GenerateFeed() e.GenerateJSONIndex(helpers.SiteDataPath) helper.CopyDirectoryContents(helpers.SiteDataPath+"static/", helpers.SiteDataPath+"rendered/static/") - sort.Slice(e.Posts, func(i, j int) bool { - return e.Posts[i].Frontmatter.Date > e.Posts[j].Frontmatter.Date + sort.Slice(e.DeepDataMerge.Posts, func(i, j int) bool { + return e.DeepDataMerge.Posts[i].Frontmatter.Date > e.DeepDataMerge.Posts[j].Frontmatter.Date }) - templ, err := template.ParseGlob(helpers.SiteDataPath + "layout/*.layout") + templ, err := template.ParseGlob(helpers.SiteDataPath + "layout/*.html") if err != nil { e.ErrorLogger.Fatalf("%v", err) } - templ, err = templ.ParseGlob(helpers.SiteDataPath + "layout/partials/*.layout") + templ, err = templ.ParseGlob(helpers.SiteDataPath + "layout/partials/*.html") if err != nil { e.ErrorLogger.Fatalf("%v", err) } diff --git a/cmd/anna/livereload.go b/cmd/anna/livereload.go index ecf2b27..00a133f 100644 --- a/cmd/anna/livereload.go +++ b/cmd/anna/livereload.go @@ -6,11 +6,16 @@ import ( "net/http" "os" "path/filepath" + "sync/atomic" "time" "github.com/acmpesuecc/anna/pkg/helpers" ) +var reloadPage = make(chan struct{}) + +var countRequests atomic.Int32 + type liveReload struct { errorLogger *log.Logger fileTimes map[string]time.Time @@ -43,6 +48,7 @@ func (cmd *Cmd) StartLiveReload() { for _, rootDir := range lr.rootDirs { if lr.traverseDirectory(rootDir) { cmd.VanillaRender() + reloadPage <- struct{}{} } } if !lr.serverRunning { @@ -97,8 +103,35 @@ func (lr *liveReload) checkFile(path string, modTime time.Time) bool { func (lr *liveReload) startServer(addr string) { fmt.Print("Serving content at: http://localhost:", addr, "\n") - err := http.ListenAndServe(":"+addr, http.FileServer(http.Dir(helpers.SiteDataPath+"./rendered"))) + http.Handle("/", http.FileServer(http.Dir(helpers.SiteDataPath+"./rendered"))) + http.HandleFunc("/events", eventsHandler) + err := http.ListenAndServe(":"+addr, nil) if err != nil { lr.errorLogger.Fatal(err) } } + +func eventsHandler(w http.ResponseWriter, r *http.Request) { + countRequests.Add(1) + + // Set CORS headers to allow all origins. + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Expose-Headers", "Content-Type") + + w.Header().Set("Content-Type", "text/event-stream") + w.Header().Set("Cache-Control", "no-cache") + w.Header().Set("Connection", "keep-alive") + + if countRequests.Load() == 1 { + <-reloadPage + } else { + countRequests.Store(countRequests.Load() - 1) + return + } + + event := "event:\ndata:\n\n" + w.Write([]byte(event)) + w.(http.Flusher).Flush() + + countRequests.Store(countRequests.Load() - 1) +} diff --git a/main.go b/main.go index 6ece361..d84b4f3 100644 --- a/main.go +++ b/main.go @@ -29,6 +29,7 @@ func main() { } if serve { + annaCmd.LiveReload = true annaCmd.StartLiveReload() } diff --git a/pkg/engine/anna_engine.go b/pkg/engine/anna_engine.go index ba4f47e..f28ee80 100644 --- a/pkg/engine/anna_engine.go +++ b/pkg/engine/anna_engine.go @@ -16,28 +16,48 @@ import ( "github.com/acmpesuecc/anna/pkg/parser" ) +type TagRootTemplateData struct { + DeepDataMerge DeepDataMerge + PageURL template.URL + TemplateData parser.TemplateData + TagNames []string +} + func (e *Engine) RenderTags(fileOutPath string, templ *template.Template) { var tagsBuffer bytes.Buffer // Extracting tag titles - tags := make([]string, 0, len(e.TagsMap)) - for tag := range e.TagsMap { + tags := make([]template.URL, 0, len(e.DeepDataMerge.TagsMap)) + for tag := range e.DeepDataMerge.TagsMap { tags = append(tags, tag) } - slices.SortFunc(tags, func(a, b string) int { - return cmp.Compare(strings.ToLower(a), strings.ToLower(b)) + slices.SortFunc(tags, func(a, b template.URL) int { + return cmp.Compare(strings.ToLower(string(a)), strings.ToLower(string(b))) }) - tagNames := parser.TemplateData{ - FilenameWithoutExtension: "Tags", - Layout: e.LayoutConfig, - Frontmatter: parser.Frontmatter{Title: "Tags"}, - Tags: tags, + tagNames := make([]string, 0, len(tags)) + for _, tag := range tags { + tagString := string(tag) + tagString, _ = strings.CutPrefix(tagString, "tags/") + tagString, _ = strings.CutSuffix(tagString, ".html") + + tagNames = append(tagNames, tagString) + } + + tagRootTemplataData := parser.TemplateData{ + Frontmatter: parser.Frontmatter{Title: "Tags"}, + } + + tagTemplateData := TagRootTemplateData{ + DeepDataMerge: e.DeepDataMerge, + PageURL: "tags.html", + TemplateData: tagRootTemplataData, + TagNames: tagNames, } // Rendering the page displaying all tags - err := templ.ExecuteTemplate(&tagsBuffer, "all-tags", tagNames) + err := templ.ExecuteTemplate(&tagsBuffer, "all-tags", tagTemplateData) if err != nil { e.ErrorLogger.Fatal(err) } @@ -51,23 +71,27 @@ func (e *Engine) RenderTags(fileOutPath string, templ *template.Template) { // Create a wait group to wait for all goroutines to finish var wg sync.WaitGroup + e.DeepDataMerge.Tags = make(map[template.URL]parser.TemplateData) + + for tag := range e.DeepDataMerge.TagsMap { + tagString := string(tag) + tagString, _ = strings.CutPrefix(tagString, "tags/") + tagString, _ = strings.CutSuffix(tagString, ".html") + + e.DeepDataMerge.Tags[tag] = parser.TemplateData{ + Frontmatter: parser.Frontmatter{ + Title: tagString, + }, + } + } + // Rendering the subpages with merged tagged posts - for tag, taggedTemplates := range e.TagsMap { + for tag, taggedTemplates := range e.DeepDataMerge.TagsMap { wg.Add(1) - go func(tag string, taggedTemplates []parser.TemplateData) { + go func(tag template.URL, taggedTemplates []parser.TemplateData) { defer wg.Done() - pagePath := "tags/" + tag - templateData := parser.TemplateData{ - FilenameWithoutExtension: tag, - Layout: e.LayoutConfig, - Frontmatter: parser.Frontmatter{ - Title: tag, - }, - SpecificTagTemplates: taggedTemplates, - } - - e.RenderPage(fileOutPath, template.URL(pagePath), templateData, templ, "tag-subpage") + e.RenderPage(fileOutPath, template.URL(tag), templ, "tag-subpage") }(tag, taggedTemplates) } @@ -88,16 +112,15 @@ func (e *Engine) GenerateJSONIndex(outFilePath string) { // Copying contents from e.Templates to new JsonMerged struct jsonIndexTemplate := make(map[template.URL]JSONIndexTemplate) - for templateURL, templateData := range e.Templates { + for templateURL, templateData := range e.DeepDataMerge.Templates { jsonIndexTemplate[templateURL] = JSONIndexTemplate{ - CompleteURL: templateData.CompleteURL, - FilenameWithoutExtension: templateData.FilenameWithoutExtension, - Frontmatter: templateData.Frontmatter, - Tags: templateData.Frontmatter.Tags, + CompleteURL: templateData.CompleteURL, + Frontmatter: templateData.Frontmatter, + Tags: templateData.Frontmatter.Tags, } } - e.JSONIndex = jsonIndexTemplate + e.DeepDataMerge.JSONIndex = jsonIndexTemplate // Marshal the contents of jsonMergedData jsonMergedMarshaledData, err := json.Marshal(jsonIndexTemplate) @@ -117,22 +140,22 @@ func (e *Engine) GenerateSitemap(outFilePath string) { buffer.WriteString("\n") // Sorting templates by key - keys := make([]string, 0, len(e.Templates)) - for k := range e.Templates { + keys := make([]string, 0, len(e.DeepDataMerge.Templates)) + for k := range e.DeepDataMerge.Templates { keys = append(keys, string(k)) } sort.Strings(keys) tempTemplates := make(map[template.URL]parser.TemplateData) for _, templateURL := range keys { - tempTemplates[template.URL(templateURL)] = e.Templates[template.URL(templateURL)] + tempTemplates[template.URL(templateURL)] = e.DeepDataMerge.Templates[template.URL(templateURL)] } - e.Templates = tempTemplates + e.DeepDataMerge.Templates = tempTemplates // Iterate over parsed markdown files - for _, templateData := range e.Templates { - url := e.LayoutConfig.BaseURL + "/" + templateData.FilenameWithoutExtension + ".html" + for _, templateData := range e.DeepDataMerge.Templates { + url := e.DeepDataMerge.LayoutConfig.BaseURL + "/" + string(templateData.CompleteURL) buffer.WriteString("\t\n") buffer.WriteString("\t\t" + url + "\n") buffer.WriteString("\t\t" + templateData.Frontmatter.Date + "\n") @@ -156,17 +179,17 @@ func (e *Engine) GenerateFeed() { buffer.WriteString("\n") buffer.WriteString("\n") buffer.WriteString("\n") - buffer.WriteString(" " + e.LayoutConfig.SiteTitle + "\n") - buffer.WriteString(" \n") + buffer.WriteString(" " + e.DeepDataMerge.LayoutConfig.SiteTitle + "\n") + buffer.WriteString(" \n") buffer.WriteString(" " + time.Now().Format(time.RFC3339) + "\n") // iterate over parsed markdown files that are non-draft posts - for _, templateData := range e.Templates { + for _, templateData := range e.DeepDataMerge.Templates { if !templateData.Frontmatter.Draft { buffer.WriteString("\n") buffer.WriteString(" " + templateData.Frontmatter.Title + "\n") - buffer.WriteString(" \n") - buffer.WriteString(" " + e.LayoutConfig.BaseURL + "/posts/" + templateData.FilenameWithoutExtension + ".html\n") + buffer.WriteString(" \n") + buffer.WriteString(" " + e.DeepDataMerge.LayoutConfig.BaseURL + string(templateData.CompleteURL) + "\n") buffer.WriteString(" " + time.Unix(templateData.Date, 0).Format(time.RFC3339) + "\n") buffer.WriteString(" \n") buffer.WriteString(" \n") diff --git a/pkg/engine/anna_engine_test.go b/pkg/engine/anna_engine_test.go index 38ada32..4d1636b 100644 --- a/pkg/engine/anna_engine_test.go +++ b/pkg/engine/anna_engine_test.go @@ -15,15 +15,15 @@ import ( func TestRenderTags(t *testing.T) { e := engine.Engine{ - Templates: make(map[template.URL]parser.TemplateData), - TagsMap: make(map[string][]parser.TemplateData), ErrorLogger: log.New(os.Stderr, "TEST ERROR\t", log.Ldate|log.Ltime|log.Lshortfile), } - e.LayoutConfig.BaseURL = "example.org" + e.DeepDataMerge.Templates = make(map[template.URL]parser.TemplateData) + e.DeepDataMerge.TagsMap = make(map[template.URL][]parser.TemplateData) + e.DeepDataMerge.LayoutConfig.BaseURL = "example.org" fileOutPath := "../../test/engine/render_tags/" - e.TagsMap["blogs"] = []parser.TemplateData{ + e.DeepDataMerge.TagsMap["tags/blogs.html"] = []parser.TemplateData{ { CompleteURL: "posts/file1.html", Frontmatter: parser.Frontmatter{ @@ -40,7 +40,7 @@ func TestRenderTags(t *testing.T) { }, } - e.TagsMap["tech"] = []parser.TemplateData{ + e.DeepDataMerge.TagsMap["tags/tech.html"] = []parser.TemplateData{ { CompleteURL: "posts/file2.html", Frontmatter: parser.Frontmatter{ @@ -57,7 +57,7 @@ func TestRenderTags(t *testing.T) { }, } - templ, err := template.ParseFiles(TestDirPath+"render_tags/tags_template.layout", TestDirPath+"render_tags/tags_subpage_template.layout") + templ, err := template.ParseFiles(TestDirPath+"render_tags/tags_template.html", TestDirPath+"render_tags/tags_subpage_template.html") if err != nil { t.Errorf("%v", err) } @@ -112,9 +112,6 @@ func TestRenderTags(t *testing.T) { } }) - if err := os.RemoveAll(TestDirPath + "render_tags/rendered"); err != nil { - t.Errorf("%v", err) - } } func TestGenerateMergedJson(t *testing.T) { @@ -124,14 +121,13 @@ func TestGenerateMergedJson(t *testing.T) { t.Run("test json creation for the search index", func(t *testing.T) { e := engine.Engine{ - Templates: make(map[template.URL]parser.TemplateData), - TagsMap: make(map[string][]parser.TemplateData), ErrorLogger: log.New(os.Stderr, "TEST ERROR\t", log.Ldate|log.Ltime|log.Lshortfile), } + e.DeepDataMerge.Templates = make(map[template.URL]parser.TemplateData) + e.DeepDataMerge.TagsMap = make(map[template.URL][]parser.TemplateData) - e.Templates["docs.md"] = parser.TemplateData{ - FilenameWithoutExtension: "docs", - CompleteURL: "docs.html", + e.DeepDataMerge.Templates["docs.md"] = parser.TemplateData{ + CompleteURL: "docs.html", Frontmatter: parser.Frontmatter{ Title: "Anna Documentation", }, @@ -157,45 +153,42 @@ func TestGenerateMergedJson(t *testing.T) { } }) - if err := os.RemoveAll(TestDirPath + "json_index_test/static"); err != nil { - t.Errorf("%v", err) - } } func TestGenerateSitemap(t *testing.T) { t.Run("render sitemap.xml", func(t *testing.T) { engine := engine.Engine{ - Templates: make(map[template.URL]parser.TemplateData), - TagsMap: make(map[string][]parser.TemplateData), ErrorLogger: log.New(os.Stderr, "TEST ERROR\t", log.Ldate|log.Ltime|log.Lshortfile), } + engine.DeepDataMerge.Templates = make(map[template.URL]parser.TemplateData) + engine.DeepDataMerge.TagsMap = make(map[template.URL][]parser.TemplateData) t1 := parser.TemplateData{ - FilenameWithoutExtension: "index", + CompleteURL: "index.html", Frontmatter: parser.Frontmatter{ Date: "2024-02-23", }, } t2 := parser.TemplateData{ - FilenameWithoutExtension: "about", + CompleteURL: "about.html", Frontmatter: parser.Frontmatter{ Date: "2024-02-23", }, } t3 := parser.TemplateData{ - FilenameWithoutExtension: "research", + CompleteURL: "research.html", Frontmatter: parser.Frontmatter{ Date: "2024-02-23", }, } - engine.LayoutConfig.BaseURL = "example.org" + engine.DeepDataMerge.LayoutConfig.BaseURL = "example.org" // setting up engine - engine.Templates["index"] = t1 - engine.Templates["about"] = t2 - engine.Templates["research"] = t3 + engine.DeepDataMerge.Templates["index"] = t1 + engine.DeepDataMerge.Templates["about"] = t2 + engine.DeepDataMerge.Templates["research"] = t3 engine.GenerateSitemap(TestDirPath + "sitemap/got_sitemap.xml") @@ -223,7 +216,4 @@ func TestGenerateSitemap(t *testing.T) { } }) - if err := os.RemoveAll(TestDirPath + "sitemap/got_sitemap.xml"); err != nil { - t.Errorf("%v", err) - } } diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 63714a5..4bbcfda 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -10,13 +10,17 @@ import ( "github.com/acmpesuecc/anna/pkg/parser" ) -type Engine struct { +// This struct holds all of the ssg data +type DeepDataMerge struct { // Templates stores the template data of all the pages of the site // Access the data for a particular page by using the relative path to the file as the key Templates map[template.URL]parser.TemplateData - // K-V pair storing all templates correspoding to a particular tag in the site - TagsMap map[string][]parser.TemplateData + // Templates stores the template data of all tag sub-pages of the site + Tags map[template.URL]parser.TemplateData + + // K-V pair storing all templates corresponding to a particular tag in the site + TagsMap map[template.URL][]parser.TemplateData // Stores data parsed from layout/config.yml LayoutConfig parser.LayoutConfig @@ -26,40 +30,62 @@ type Engine struct { // Stores the index generated for search functionality JSONIndex map[template.URL]JSONIndexTemplate +} + +type Engine struct { + // Stores the merged ssg data + DeepDataMerge DeepDataMerge // Common logger for all engine functions ErrorLogger *log.Logger } +type PageData struct { + DeepDataMerge DeepDataMerge + + PageURL template.URL +} + // This structure is solely used for storing the JSON index type JSONIndexTemplate struct { - CompleteURL template.URL - FilenameWithoutExtension string - Frontmatter parser.Frontmatter - Tags []string + CompleteURL template.URL + Frontmatter parser.Frontmatter + Tags []string } -// fileOutPath for main.go should be refering to helpers.SiteDataPath -func (e *Engine) RenderPage(fileOutPath string, pagePath template.URL, pageTemplateData parser.TemplateData, templ *template.Template, templateStartString string) { +/* +fileOutPath - stores the parent directory to store rendered files, usually `site/` + +pagePath - stores the path to write the given page without the prefix directory +Eg: site/content/posts/file1.html to be passed as posts/file1.html + +template - stores the HTML templates parsed from the layout/ directory + +templateStartString - stores the name of the template to be passed to ExecuteTemplate() +*/ +func (e *Engine) RenderPage(fileOutPath string, pagePath template.URL, template *template.Template, templateStartString string) { // Creating subdirectories if the filepath contains '/' - dirPath := "" if strings.Contains(string(pagePath), "/") { // Extracting the directory path from the page path - dirPath, _ := strings.CutSuffix(string(pagePath), pageTemplateData.FilenameWithoutExtension) - dirPath = fileOutPath + "rendered/" + dirPath + splitPaths := strings.Split(string(pagePath), "/") + filename := splitPaths[len(splitPaths)-1] + pagePathWithoutFilename, _ := strings.CutSuffix(string(pagePath), filename) - err := os.MkdirAll(dirPath, 0750) + err := os.MkdirAll(fileOutPath+"rendered/"+pagePathWithoutFilename, 0750) if err != nil { e.ErrorLogger.Fatal(err) } } - filename, _ := strings.CutSuffix(string(pagePath), ".md") - filepath := fileOutPath + "rendered/" + dirPath + filename + ".html" + filepath := fileOutPath + "rendered/" + string(pagePath) var buffer bytes.Buffer + pageData := PageData{ + DeepDataMerge: e.DeepDataMerge, + PageURL: pagePath, + } // Storing the rendered HTML file to a buffer - err := templ.ExecuteTemplate(&buffer, templateStartString, pageTemplateData) + err := template.ExecuteTemplate(&buffer, templateStartString, pageData) if err != nil { e.ErrorLogger.Fatal(err) } diff --git a/pkg/engine/engine_integration_test.go b/pkg/engine/engine_integration_test.go index 678f379..a1e7b2a 100644 --- a/pkg/engine/engine_integration_test.go +++ b/pkg/engine/engine_integration_test.go @@ -13,22 +13,20 @@ import ( func TestRenderUserDefinedPages(t *testing.T) { engine := engine.Engine{ - Templates: make(map[template.URL]parser.TemplateData), - TagsMap: make(map[string][]parser.TemplateData), ErrorLogger: log.New(os.Stderr, "TEST ERROR\t", log.Ldate|log.Ltime|log.Lshortfile), } + engine.DeepDataMerge.Templates = make(map[template.URL]parser.TemplateData) + engine.DeepDataMerge.TagsMap = make(map[template.URL][]parser.TemplateData) - engine.Templates["index.md"] = + engine.DeepDataMerge.Templates["index.html"] = parser.TemplateData{ - FilenameWithoutExtension: "index", - Body: template.HTML("

Index Page

"), - CompleteURL: "index.html", + Body: template.HTML("

Index Page

"), + CompleteURL: "index.html", } - engine.Templates["posts/hello.md"] = parser.TemplateData{ - FilenameWithoutExtension: "hello", - Body: template.HTML("

Hello World

"), - CompleteURL: "posts/hello.html", + engine.DeepDataMerge.Templates["posts/hello.html"] = parser.TemplateData{ + Body: template.HTML("

Hello World

"), + CompleteURL: "posts/hello.html", } if err := os.MkdirAll(TestDirPath+"render_user_defined/rendered", 0750); err != nil { @@ -37,7 +35,7 @@ func TestRenderUserDefinedPages(t *testing.T) { t.Run("render a set of user defined pages", func(t *testing.T) { - templ, err := template.ParseFiles(TestDirPath + "render_user_defined/template_input.layout") + templ, err := template.ParseFiles(TestDirPath + "render_user_defined/template_input.html") if err != nil { t.Errorf("%v", err) } @@ -73,7 +71,4 @@ func TestRenderUserDefinedPages(t *testing.T) { } }) - if err := os.RemoveAll(TestDirPath + "render_user_defined/rendered"); err != nil { - t.Errorf("%v", err) - } } diff --git a/pkg/engine/engine_test.go b/pkg/engine/engine_test.go index f54ba25..a17e4cd 100644 --- a/pkg/engine/engine_test.go +++ b/pkg/engine/engine_test.go @@ -20,14 +20,13 @@ func TestRenderPage(t *testing.T) { t.Run("render a single page while creating a new directory", func(t *testing.T) { engine := engine.Engine{ - Templates: make(map[template.URL]parser.TemplateData), - TagsMap: make(map[string][]parser.TemplateData), ErrorLogger: log.New(os.Stderr, "TEST ERROR\t", log.Ldate|log.Ltime|log.Lshortfile), } + engine.DeepDataMerge.Templates = make(map[template.URL]parser.TemplateData) + engine.DeepDataMerge.TagsMap = make(map[template.URL][]parser.TemplateData) - page := parser.TemplateData{ - CompleteURL: template.URL("got"), - FilenameWithoutExtension: "got", + engine.DeepDataMerge.Templates["posts/got.html"] = parser.TemplateData{ + CompleteURL: template.URL("got.html"), Frontmatter: parser.Frontmatter{ Title: "Hello", Date: "2024-03-28", @@ -37,20 +36,20 @@ func TestRenderPage(t *testing.T) { Tags: []string{"blog", "thoughts"}, }, Body: template.HTML("

Hello World

"), - Layout: parser.LayoutConfig{ - Navbar: []string{"index", "posts"}, - BaseURL: "https://example.org", - SiteTitle: "Anna", - Author: "anna", - }, + // Layout: parser.LayoutConfig{ + // Navbar: []string{"index", "posts"}, + // BaseURL: "https://example.org", + // SiteTitle: "Anna", + // Author: "anna", + // }, } - templ, err := template.ParseFiles(TestDirPath + "render_page/template_input.layout") + templ, err := template.ParseFiles(TestDirPath + "render_page/template_input.html") if err != nil { t.Errorf("%v", err) } - engine.RenderPage(TestDirPath+"render_page/", "posts/got.md", page, templ, "page") + engine.RenderPage(TestDirPath+"render_page/", "posts/got.html", templ, "page") got_file, err := os.ReadFile(TestDirPath + "render_page/rendered/posts/got.html") if err != nil { @@ -67,7 +66,4 @@ func TestRenderPage(t *testing.T) { } }) - if err := os.RemoveAll(TestDirPath + "render_page/rendered"); err != nil { - t.Errorf("%v", err) - } } diff --git a/pkg/engine/user_engine.go b/pkg/engine/user_engine.go index 2779ffd..7254108 100644 --- a/pkg/engine/user_engine.go +++ b/pkg/engine/user_engine.go @@ -12,23 +12,32 @@ import ( ) type postsTemplateData struct { - Posts []parser.TemplateData - parser.TemplateData + DeepDataMerge DeepDataMerge + PageURL template.URL + TemplateData parser.TemplateData } -func (e *Engine) RenderEngineGeneratedFiles(fileOutPath string, templ *template.Template) { +func (e *Engine) RenderEngineGeneratedFiles(fileOutPath string, template *template.Template) { // Rendering "posts.html" var postsBuffer bytes.Buffer postsData := postsTemplateData{ - Posts: e.Posts, TemplateData: parser.TemplateData{ Frontmatter: parser.Frontmatter{Title: "Posts"}, - Layout: e.LayoutConfig, }, + DeepDataMerge: e.DeepDataMerge, + PageURL: "posts.html", } + // e.DeepDataMerge.Templates["posts.html"] = parser.TemplateData{ + // Frontmatter: parser.Frontmatter{Title: "Posts"}, + // } - err := templ.ExecuteTemplate(&postsBuffer, "posts", postsData) + // pageData := PageData{ + // DeepDataMerge: e.DeepDataMerge, + // PageURL: "posts.html", + // } + + err := template.ExecuteTemplate(&postsBuffer, "posts", postsData) if err != nil { e.ErrorLogger.Fatal(err) } @@ -42,7 +51,7 @@ func (e *Engine) RenderEngineGeneratedFiles(fileOutPath string, templ *template. func (e *Engine) RenderUserDefinedPages(fileOutPath string, templ *template.Template) { numCPU := runtime.NumCPU() - numTemplates := len(e.Templates) + numTemplates := len(e.DeepDataMerge.Templates) concurrency := numCPU * 2 // Adjust the concurrency factor based on system hardware resources if numTemplates < concurrency { @@ -50,7 +59,7 @@ func (e *Engine) RenderUserDefinedPages(fileOutPath string, templ *template.Temp } templateURLs := make([]string, 0, numTemplates) - for templateURL := range e.Templates { + for templateURL := range e.DeepDataMerge.Templates { templateURLs = append(templateURLs, string(templateURL)) } @@ -58,9 +67,8 @@ func (e *Engine) RenderUserDefinedPages(fileOutPath string, templ *template.Temp semaphore := make(chan struct{}, concurrency) for _, templateURL := range templateURLs { - templData := e.Templates[template.URL(templateURL)] - fileInPath := strings.TrimSuffix(string(templData.CompleteURL), ".html") - if fileInPath == "" { + fileInPath := strings.TrimSuffix(templateURL, ".html") + if fileInPath == ".html" { continue } @@ -73,7 +81,7 @@ func (e *Engine) RenderUserDefinedPages(fileOutPath string, templ *template.Temp wg.Done() }() - e.RenderPage(fileOutPath, template.URL(fileInPath), templData, templ, "page") + e.RenderPage(fileOutPath, template.URL(templateURL), templ, "page") }(templateURL) } diff --git a/pkg/engine/user_engine_test.go b/pkg/engine/user_engine_test.go index 1116923..9aa5e40 100644 --- a/pkg/engine/user_engine_test.go +++ b/pkg/engine/user_engine_test.go @@ -12,37 +12,38 @@ import ( ) func TestRenderEngineGeneratedFiles(t *testing.T) { + engine := engine.Engine{ - Templates: make(map[template.URL]parser.TemplateData), - TagsMap: make(map[string][]parser.TemplateData), ErrorLogger: log.New(os.Stderr, "TEST ERROR\t", log.Ldate|log.Ltime|log.Lshortfile), + } + engine.DeepDataMerge.Templates = make(map[template.URL]parser.TemplateData) + engine.DeepDataMerge.TagsMap = make(map[template.URL][]parser.TemplateData) - Posts: []parser.TemplateData{ - { - FilenameWithoutExtension: "file1", - Frontmatter: parser.Frontmatter{ - Title: "file1", - Description: "Description of file 1", - Date: "2024-03-28", - }, + engine.DeepDataMerge.Posts = []parser.TemplateData{ + { + CompleteURL: "posts/file1.html", + Frontmatter: parser.Frontmatter{ + Title: "file1", + Description: "Description of file 1", + Date: "2024-03-28", }, + }, - { - FilenameWithoutExtension: "file2", - Frontmatter: parser.Frontmatter{ - Title: "file2", - Description: "Description of file 2", - Date: "2024-03-28", - }, + { + CompleteURL: "posts/file2.html", + Frontmatter: parser.Frontmatter{ + Title: "file2", + Description: "Description of file 2", + Date: "2024-03-28", }, + }, - { - FilenameWithoutExtension: "file3", - Frontmatter: parser.Frontmatter{ - Title: "file3", - Description: "Description of file 3", - Date: "2024-03-28", - }, + { + CompleteURL: "posts/file3.html", + Frontmatter: parser.Frontmatter{ + Title: "file3", + Description: "Description of file 3", + Date: "2024-03-28", }, }, } @@ -52,7 +53,7 @@ func TestRenderEngineGeneratedFiles(t *testing.T) { } t.Run("test rendering of post.html", func(t *testing.T) { - templ, err := template.ParseFiles(TestDirPath + "render_engine_generated/posts_template.layout") + templ, err := template.ParseFiles(TestDirPath + "render_engine_generated/posts_template.html") if err != nil { t.Errorf("%v", err) } @@ -73,8 +74,4 @@ func TestRenderEngineGeneratedFiles(t *testing.T) { t.Errorf("The expected and generated posts.html can be found in test/engine/render_engine_generated/rendered/") } }) - - if err := os.RemoveAll(TestDirPath + "render_engine_generated/rendered"); err != nil { - t.Errorf("%v", err) - } } diff --git a/pkg/parser/parser.go b/pkg/parser/parser.go index db7c7e9..aa66f3e 100644 --- a/pkg/parser/parser.go +++ b/pkg/parser/parser.go @@ -40,17 +40,11 @@ type Frontmatter struct { // This struct holds all of the data required to render any page of the site type TemplateData struct { - CompleteURL template.URL - FilenameWithoutExtension string - Date int64 - Frontmatter Frontmatter - Body template.HTML - Layout LayoutConfig - - // Do not use these fields to store tags!! - // These fields are populated by the ssg to store merged tag data - Tags []string - SpecificTagTemplates []TemplateData + CompleteURL template.URL + Date int64 + Frontmatter Frontmatter + Body template.HTML + LiveReload bool } type Date int64 @@ -61,7 +55,7 @@ type Parser struct { Templates map[template.URL]TemplateData // K-V pair storing all templates correspoding to a particular tag in the site - TagsMap map[string][]TemplateData + TagsMap map[template.URL][]TemplateData // Stores data parsed from layout/config.yml LayoutConfig LayoutConfig @@ -80,6 +74,9 @@ type Parser struct { ErrorLogger *log.Logger Helper *helpers.Helper + + // Determines the injection of Live Reload JS in HTML + LiveReload bool } func (p *Parser) ParseMDDir(baseDirPath string, baseDirFS fs.FS) { @@ -128,13 +125,13 @@ func (p *Parser) AddFileAndRender(baseDirPath string, dirEntryPath string, front if frontmatter.Type == "post" { url = "posts/" + url } + page := TemplateData{ - CompleteURL: template.URL(url), - Date: date, - FilenameWithoutExtension: strings.Split(dirEntryPath, ".")[0], - Frontmatter: frontmatter, - Body: template.HTML(body), - Layout: p.LayoutConfig, + CompleteURL: template.URL(url), + Date: date, + Frontmatter: frontmatter, + Body: template.HTML(body), + LiveReload: p.LiveReload, } // Adding the page to the merged map storing all site pages @@ -142,11 +139,12 @@ func (p *Parser) AddFileAndRender(baseDirPath string, dirEntryPath string, front p.Posts = append(p.Posts, page) } - p.Templates[template.URL(key)] = page + p.Templates[template.URL(url)] = page // Adding the page to the tags map with the corresponding tags for _, tag := range page.Frontmatter.Tags { - p.TagsMap[tag] = append(p.TagsMap[tag], page) + tagsMapKey := "tags/" + tag + ".html" + p.TagsMap[template.URL(tagsMapKey)] = append(p.TagsMap[template.URL(tagsMapKey)], page) } } @@ -253,15 +251,14 @@ func (p *Parser) ParseRobots(inFilePath string, outFilePath string) { // Parse all the ".html" layout files in the layout/ directory func (p *Parser) ParseLayoutFiles() *template.Template { - // Parsing all files in the layout/ dir which match the "*.html" pattern - templ, err := template.ParseGlob(helpers.SiteDataPath + "layout/*.layout") + templ, err := template.ParseGlob(helpers.SiteDataPath + "layout/*.html") if err != nil { p.ErrorLogger.Fatal(err) } // Parsing all files in the partials/ dir which match the "*.html" pattern - templ, err = templ.ParseGlob(helpers.SiteDataPath + "layout/partials/*.layout") + templ, err = templ.ParseGlob(helpers.SiteDataPath + "layout/partials/*.html") if err != nil { p.ErrorLogger.Fatal(err) } diff --git a/pkg/parser/parser_integration_test.go b/pkg/parser/parser_integration_test.go index cf863f8..bbbb40e 100644 --- a/pkg/parser/parser_integration_test.go +++ b/pkg/parser/parser_integration_test.go @@ -13,7 +13,7 @@ func TestParseMDDir(t *testing.T) { t.Run("reading markdown files and rendering without drafts", func(t *testing.T) { p := parser.Parser{ Templates: make(map[template.URL]parser.TemplateData), - TagsMap: make(map[string][]parser.TemplateData), + TagsMap: make(map[template.URL][]parser.TemplateData), ErrorLogger: log.New(os.Stderr, "TEST ERROR\t", log.Ldate|log.Ltime|log.Lshortfile), } p.RenderDrafts = false @@ -33,7 +33,7 @@ func TestParseMDDir(t *testing.T) { t.Run("reading all markdown files inluding drafts", func(t *testing.T) { p := parser.Parser{ Templates: make(map[template.URL]parser.TemplateData), - TagsMap: make(map[string][]parser.TemplateData), + TagsMap: make(map[template.URL][]parser.TemplateData), ErrorLogger: log.New(os.Stderr, "TEST ERROR\t", log.Ldate|log.Ltime|log.Lshortfile), } p.RenderDrafts = true diff --git a/pkg/parser/parser_test.go b/pkg/parser/parser_test.go index ea60dc4..15705fe 100644 --- a/pkg/parser/parser_test.go +++ b/pkg/parser/parser_test.go @@ -16,7 +16,7 @@ const TestDirPath = "../../test/parser/" func TestAddFileandRender(t *testing.T) { got_parser := parser.Parser{ Templates: make(map[template.URL]parser.TemplateData), - TagsMap: make(map[string][]parser.TemplateData), + TagsMap: make(map[template.URL][]parser.TemplateData), ErrorLogger: log.New(os.Stderr, "TEST ERROR\t", log.Ldate|log.Ltime|log.Lshortfile), } @@ -34,10 +34,11 @@ func TestAddFileandRender(t *testing.T) { } want_parser := parser.Parser{ Templates: make(map[template.URL]parser.TemplateData), - TagsMap: make(map[string][]parser.TemplateData), + TagsMap: make(map[template.URL][]parser.TemplateData), ErrorLogger: got_parser.ErrorLogger, } - sample_frontmatter, sample_body, parseSuccess := got_parser.ParseMarkdownContent(string(inputMd)) + sample_frontmatter, _, parseSuccess := got_parser.ParseMarkdownContent(string(inputMd)) + sample_body := "sample_body" if !parseSuccess { return } @@ -47,18 +48,17 @@ func TestAddFileandRender(t *testing.T) { want_parser.MdFilesName = append(want_parser.MdFilesName, filename) want_parser.MdFilesPath = append(want_parser.MdFilesPath, filename) want_page := parser.TemplateData{ - CompleteURL: template.URL(fileurl), - Date: want_parser.DateParse(sample_frontmatter.Date).Unix(), - FilenameWithoutExtension: "testpost", - Frontmatter: sample_frontmatter, - Body: template.HTML(sample_body), - Layout: want_layout, + CompleteURL: template.URL(fileurl), + Date: want_parser.DateParse(sample_frontmatter.Date).Unix(), + Frontmatter: sample_frontmatter, + Body: template.HTML(sample_body), + // Layout: want_layout, } want_parser.LayoutConfig = want_layout - want_parser.Templates[template.URL("testpost.md")] = want_page + want_parser.Templates[template.URL("posts/testpost.html")] = want_page for _, tag := range sample_frontmatter.Tags { - want_parser.TagsMap[tag] = append(want_parser.TagsMap[tag], want_page) + want_parser.TagsMap[template.URL(tag)] = append(want_parser.TagsMap[template.URL(tag)], want_page) } if sample_frontmatter.Type == "post" { @@ -69,6 +69,7 @@ func TestAddFileandRender(t *testing.T) { if !reflect.DeepEqual(got_parser, want_parser) { t.Errorf("want %v; \ngot %v", want_parser, got_parser) + // t.Errorf("please see the files yourself") } }) } @@ -76,7 +77,7 @@ func TestAddFileandRender(t *testing.T) { func TestParseMarkdownContent(t *testing.T) { p := parser.Parser{ Templates: make(map[template.URL]parser.TemplateData), - TagsMap: make(map[string][]parser.TemplateData), + TagsMap: make(map[template.URL][]parser.TemplateData), ErrorLogger: log.New(os.Stderr, "TEST ERROR\t", log.Ldate|log.Ltime|log.Lshortfile), } t.Run("render markdown files to html", func(t *testing.T) { @@ -126,7 +127,7 @@ func TestParseConfig(t *testing.T) { t.Run("unmarshal `config.yml` to LayoutConfig", func(t *testing.T) { got_parser := parser.Parser{ Templates: make(map[template.URL]parser.TemplateData), - TagsMap: make(map[string][]parser.TemplateData), + TagsMap: make(map[template.URL][]parser.TemplateData), ErrorLogger: log.New(os.Stderr, "TEST ERROR\t", log.Ldate|log.Ltime|log.Lshortfile), } @@ -149,7 +150,7 @@ func TestParseRobots(t *testing.T) { t.Run("parse and render `robots.txt`", func(t *testing.T) { parser := parser.Parser{ Templates: make(map[template.URL]parser.TemplateData), - TagsMap: make(map[string][]parser.TemplateData), + TagsMap: make(map[template.URL][]parser.TemplateData), ErrorLogger: log.New(os.Stderr, "TEST ERROR\t", log.Ldate|log.Ltime|log.Lshortfile), } parser.LayoutConfig.BaseURL = "example.org" diff --git a/site/content/docs.md b/site/content/docs.md index deb6c82..66ca0d7 100644 --- a/site/content/docs.md +++ b/site/content/docs.md @@ -77,18 +77,39 @@ The ssg currently requires the following directory structure - The `posts.html` file defines the layout of a page displaying all the posts of the site - The layout files can be composed of smaller html files which are stored in the `partials/` folder -#### Layout +## Building layouts -The layout files can access the following rendered data from the markdown files: +Each layout file(except `posts.html` and `tags.html`) can access any data from the entire ssg -- `{{.CompleteURL}}` : Returns the complete url of the given page -- `{{.FilenameWithoutExtension}}` : Returns the name of the current file -- `{{.Date}}` : Returns the last modified date of the current file -- `{{.Frontmatter.[Tagname]}}` : Returns the value of the frontmatter tag - - Example: `{{.Frontmatter.Title}}` : Returns the value of the title tag -- `{{.Body}}` : Returns the markdown body rendered to HTML -- `{{.Layout.[Tagname]}}`: Returns the particular configuration detail of the page - - Example: `{{.Layout.Navbar}}` : Returns a string slice with the names of all the navbar elements +The URL for the current page can be accessed using `{{.PageURL}}` + +To access the data for a particular page, use Go templating syntax: + +```html +{{$PageData := index .DeepDataMerge.Templates .PageURL}} +{{$PageData.CompleteURL}} +``` + +To access the page data for `posts.html`, `tags.html` and partials, set {{$PageData := .TemplateData}} + +All of the following page data fields can be accessed in the above manner: + +- `{{$PageData.CompleteURL}}` : Returns the complete url of the given page +- `{{$PageData.Date}}` : Returns the last modified date of the current file +- `{{$PageData.Frontmatter.[Tagname]}}` : Returns the value of the frontmatter tag + - Example: `{{$PageData.Frontmatter.Title}}` : Returns the value of the title tag +- `{{$PageData.Body}}` : Returns the markdown body rendered to HTML +- `{{$PageData.Layout.[Tagname]}}`: Returns the particular configuration detail of the page + - Example: `{{$PageData.Layout.Navbar}}` : Returns a string slice with the names of all the navbar elements + +In addition to page data, the following fields can be accessed: + +- `{{.DeepDataMerge.Tags}}` - A map that stores the template of the tag sub-pages for a particular tag url +- `{{.DeepDataMerge.TagsMap}}` - A map that stores a slice of templates of all posts for a particular tag url +- `{{.DeepDataMerge.LayoutConfig}}` - Stores the layout parsed from `config.yml` +- `{{.DeepDataMerge.Posts}}` - Stores a slice of templates of all posts +- `{{.DeepDataMerge.JSONIndex}}` - Stores the JSON index generated for a particular site +(primarily used for search and graphing of tags) ## Notes @@ -110,10 +131,12 @@ The layout files can access the following rendered data from the markdown files: - `title` : The title of the current page - `date`: The date of the current page - `draft`: When set to 'true', the current page is not rendered unless the '-d' flag is used +- `scripts`: Stores the page-level scripts to be added - `type`: Sets the type of the page. Use type 'post' for posts - `description`: Stores the description of the current post previewed in posts.html - `previewimage`: Stores the preview image of the current page - `tags`: Stores the tags of the particular page +- `authors`: Stores (multiple) author/s of a particular page (**The above tags are Frontmatter tags**) @@ -143,12 +166,15 @@ siteTitle: anna siteScripts: author: Anna ``` + --- + ## Run locally ```sh go run github.com/acmpesuecc/anna@v1.0.0-alpha ``` + > If you don't have a site dir with the pre-requisite layout template; anna proceeds to fetch the default site dir from our GitHub repository ## Contributing to Anna @@ -158,9 +184,10 @@ Detailed documentation for our SSG can be found: [here](https://anna-docs.netlif If you have git installed, clone our repository and build against the latest commit ```sh -git clone github.com/acmpesuecc/anna; cd anna +git clone github.com/acmpesuecc/anna; cd anna go build ``` + ```text Usage: anna [flags] diff --git a/site/content/posts/markdown_test.md b/site/content/posts/markdown_test.md index 686ad71..4edfdce 100644 --- a/site/content/posts/markdown_test.md +++ b/site/content/posts/markdown_test.md @@ -1,27 +1,242 @@ --- title: Sample Post -author: John Doe date: 2024-02-23 -categories: - - Technology - - Programming -scripts: type: post draft: true tags: - - test-post + - testing --- -# heading L1 +# h1 Heading 8-) +## h2 Heading +### h3 Heading +#### h4 Heading +##### h5 Heading +###### h6 Heading -Lorem ipsum dolor sit amet, officia excepteur ex fugiat reprehenderit enim -labore culpa sint ad nisi Lorem pariatur mollit ex esse exercitation amet. Nisi -anim cupidatat excepteur officia. Reprehenderit nostrud nostrud ipsum Lorem est -aliquip amet voluptate voluptate dolor minim nulla est proident. Nostrud -officia pariatur ut officia. Sit irure elit esse ea nulla sunt ex occaecat -## heading L2 +## Horizontal Rules -**_bold and italics_** +___ -> quote text +--- + +*** + + +## Typographic replacements + +Enable typographer option to see result. + +(c) (C) (r) (R) (tm) (TM) (p) (P) +- + +test.. test... test..... test?..... test!.... + +!!!!!! ???? ,, -- --- + +"Smartypants, double quotes" and 'single quotes' + + +## Emphasis + +**This is bold text** + +__This is bold text__ + +*This is italic text* + +_This is italic text_ + +~~Strikethrough~~ + + +## Blockquotes + + +> Blockquotes can also be nested... +>> ...by using additional greater-than signs right next to each other... +> > > ...or with spaces between arrows. + + +## Lists + +Unordered + ++ Create a list by starting a line with `+`, `-`, or `*` ++ Sub-lists are made by indenting 2 spaces: + - Marker character change forces new list start: + * Ac tristique libero volutpat at + + Facilisis in pretium nisl aliquet + - Nulla volutpat aliquam velit ++ Very easy! + +Ordered + +1. Lorem ipsum dolor sit amet +2. Consectetur adipiscing elit +3. Integer molestie lorem at massa + + +1. You can use sequential numbers... +1. ...or keep all the numbers as `1.` + +Start numbering with offset: + +57. foo +1. bar + + +## Code + +Inline `code` + +Indented code + + // Some comments + line 1 of code + line 2 of code + line 3 of code + + +Block code "fences" + +``` +Sample text here... +``` + +Syntax highlighting + +``` js +var foo = function (bar) { + return bar++; +}; + +console.log(foo(5)); +``` + +## Tables + +| Option | Description | +| ------ | ----------- | +| data | path to data files to supply the data that will be passed into templates. | +| engine | engine to be used for processing templates. Handlebars is the default. | +| ext | extension to be used for dest files. | + +Right aligned columns + +| Option | Description | +| ------:| -----------:| +| data | path to data files to supply the data that will be passed into templates. | +| engine | engine to be used for processing templates. Handlebars is the default. | +| ext | extension to be used for dest files. | + + +## Links + +[link text](http://dev.nodeca.com) + +[link with title](http://nodeca.github.io/pica/demo/ "title text!") + +Autoconverted link https://github.com/nodeca/pica (enable linkify to see) + + +## Images + +![Minion](https://octodex.github.com/images/minion.png) +![Stormtroopocat](https://octodex.github.com/images/stormtroopocat.jpg "The Stormtroopocat") + +Like links, Images also have a footnote style syntax + +![Alt text][id] + +With a reference later in the document defining the URL location: + +[id]: https://octodex.github.com/images/dojocat.jpg "The Dojocat" + + +## Plugins + +The killer feature of `markdown-it` is very effective support of +[syntax plugins](https://www.npmjs.org/browse/keyword/markdown-it-plugin). + + +### [Emojies](https://github.com/markdown-it/markdown-it-emoji) + +> Classic markup: :wink: :cry: :laughing: :yum: +> +> Shortcuts (emoticons): :-) :-( 8-) ;) + +see [how to change output](https://github.com/markdown-it/markdown-it-emoji#change-output) with twemoji. + + +### [Subscript](https://github.com/markdown-it/markdown-it-sub) / [Superscript](https://github.com/markdown-it/markdown-it-sup) + +- 19^th^ +- H~2~O + + +### [\](https://github.com/markdown-it/markdown-it-ins) + +++Inserted text++ + + +### [\](https://github.com/markdown-it/markdown-it-mark) + +==Marked text== + + +### [Footnotes](https://github.com/markdown-it/markdown-it-footnote) + +Footnote 1 link[^first]. + +Footnote 2 link[^second]. + +Inline footnote^[Text of inline footnote] definition. + +Duplicated footnote reference[^second]. + +[^first]: Footnote **can have markup** + + and multiple paragraphs. + +[^second]: Footnote text. + + +### [Definition lists](https://github.com/markdown-it/markdown-it-deflist) + +Term 1 + +: Definition 1 +with lazy continuation. + +Term 2 with *inline markup* + +: Definition 2 + + { some code, part of Definition 2 } + + Third paragraph of definition 2. + +_Compact style:_ + +Term 1 + ~ Definition 1 + +Term 2 + ~ Definition 2a + ~ Definition 2b + + +### [Abbreviations](https://github.com/markdown-it/markdown-it-abbr) + +This is HTML abbreviation example. + +It converts "HTML", but keep intact partial entries like "xxxHTMLyyy" and so on. + +*[HTML]: Hyper Text Markup Language + +### [Custom containers](https://github.com/markdown-it/markdown-it-container) + +::: warning +*here be dragons* +::: diff --git a/site/layout/page.layout b/site/layout/page.html similarity index 61% rename from site/layout/page.layout rename to site/layout/page.html index 84a2478..32daec0 100644 --- a/site/layout/page.layout +++ b/site/layout/page.html @@ -1,4 +1,5 @@ {{ define "page"}} +{{$PageData := index .DeepDataMerge.Templates .PageURL}} {{ template "head" .}} @@ -6,24 +7,24 @@ {{template "header" .}}
- {{ if eq .Frontmatter.Type "post" }} + {{ if eq $PageData.Frontmatter.Type "post" }}
-

{{ .Frontmatter.Title }}

+

{{ $PageData.Frontmatter.Title }}

- Published on {{.Frontmatter.Date}} + Published on {{$PageData.Frontmatter.Date}} - {{ if eq (len .Frontmatter.Authors) 0 }} - {{$.Layout.Author}} + {{ if eq (len $PageData.Frontmatter.Authors) 0 }} + {{.DeepDataMerge.LayoutConfig.Author}} {{ else }} - {{range .Frontmatter.Authors }} + {{range $PageData.Frontmatter.Authors }} {{ . }}, {{ end }} {{ end }}

- {{range .Frontmatter.Tags}} + {{range $PageData.Frontmatter.Tags}} @@ -32,7 +33,7 @@

{{ .Frontmatter.Title }}

{{ else }} {{ end }} - {{.Body}} + {{$PageData.Body}}
{{template "footer" .}} @@ -40,4 +41,4 @@

{{ .Frontmatter.Title }}

-{{ end}} \ No newline at end of file +{{ end}} diff --git a/site/layout/partials/footer.layout b/site/layout/partials/footer.html similarity index 100% rename from site/layout/partials/footer.layout rename to site/layout/partials/footer.html diff --git a/site/layout/partials/head.html b/site/layout/partials/head.html new file mode 100644 index 0000000..df7ce9f --- /dev/null +++ b/site/layout/partials/head.html @@ -0,0 +1,56 @@ +{{ define "head" }} + +{{ $PageData := index .DeepDataMerge.Templates .PageURL }} +{{if eq $PageData nil }} + {{ $PageData = .TemplateData }} +{{end}} + + + + + + + + {{$PageData.Frontmatter.Title}} + + + + + + + + + + + {{ if $PageData.LiveReload }} + + {{ end }} + + + {{range $PageData.Frontmatter.JSFiles}} + + {{end}} {{range .DeepDataMerge.LayoutConfig.SiteScripts}} + + {{end}} + + + + + + + + + + + +{{end}} + + diff --git a/site/layout/partials/head.layout b/site/layout/partials/head.layout deleted file mode 100644 index 2a41124..0000000 --- a/site/layout/partials/head.layout +++ /dev/null @@ -1,36 +0,0 @@ -{{define "head"}} - - - - - - - {{.Frontmatter.Title}} - - - - - - - - {{range .Frontmatter.JSFiles}} - - {{end}} {{range .Layout.SiteScripts}} - - {{end}} - - - - - - - - - - -{{end}} - - \ No newline at end of file diff --git a/site/layout/partials/header.html b/site/layout/partials/header.html new file mode 100644 index 0000000..0716d2c --- /dev/null +++ b/site/layout/partials/header.html @@ -0,0 +1,22 @@ +{{define "header"}} + +{{ $PageData := index .DeepDataMerge.Templates .PageURL }} +{{if eq $PageData nil }} + {{ $PageData = .TemplateData }} +{{end}} + +
+ +
+{{template "search" .}} +{{end}} diff --git a/site/layout/partials/header.layout b/site/layout/partials/header.layout deleted file mode 100644 index 45cb55b..0000000 --- a/site/layout/partials/header.layout +++ /dev/null @@ -1,12 +0,0 @@ -{{define "header"}} -
- -
-{{template "search" .}} -{{end}} diff --git a/site/layout/partials/search.layout b/site/layout/partials/search.html similarity index 100% rename from site/layout/partials/search.layout rename to site/layout/partials/search.html diff --git a/site/layout/posts.layout b/site/layout/posts.html similarity index 76% rename from site/layout/posts.layout rename to site/layout/posts.html index 255fd71..81b4ff6 100644 --- a/site/layout/posts.layout +++ b/site/layout/posts.html @@ -1,4 +1,5 @@ {{ define "posts"}} +{{$PageData := .TemplateData}} {{ template "head" .}} @@ -6,8 +7,8 @@
- {{range $PostDate, $Post := .Posts}} - + {{range $PostDate, $Post := .DeepDataMerge.Posts}} +

{{$Post.Frontmatter.Title}}

{{$Post.Frontmatter.Description}}

@@ -24,4 +25,4 @@

{{$Post.Frontmatter.Title}}

-{{ end}} \ No newline at end of file +{{ end}} diff --git a/site/layout/tag-subpage.layout b/site/layout/tag-subpage.html similarity index 68% rename from site/layout/tag-subpage.layout rename to site/layout/tag-subpage.html index 2fc403c..6581886 100644 --- a/site/layout/tag-subpage.layout +++ b/site/layout/tag-subpage.html @@ -1,4 +1,5 @@ {{ define "tag-subpage"}} +{{$PageData := index .DeepDataMerge.Tags .PageURL}} {{ template "head" .}} @@ -7,7 +8,8 @@
- {{range .SpecificTagTemplates}} + {{$TagSet := index .DeepDataMerge.TagsMap .PageURL}} + {{range $TagSet }} {{.Frontmatter.Title}} {{end}}
@@ -19,4 +21,4 @@ -{{ end}} \ No newline at end of file +{{ end}} diff --git a/site/layout/tags.layout b/site/layout/tags.html similarity index 75% rename from site/layout/tags.layout rename to site/layout/tags.html index 3aa6dce..f094c0c 100644 --- a/site/layout/tags.layout +++ b/site/layout/tags.html @@ -1,14 +1,15 @@ {{ define "all-tags"}} +{{$PageData := .TemplateData}} {{ template "head" .}} - {{template "header" .}} +{{template "header" .}}
- {{range .Tags}} - {{.}} + {{range .TagNames}} + {{.}} {{end}}
@@ -20,6 +21,7 @@ {{template "footer" .}} + diff --git a/site/static/index.json b/site/static/index.json index 33cad2e..8dfa96c 100644 --- a/site/static/index.json +++ b/site/static/index.json @@ -1 +1 @@ -{"bench.md":{"CompleteURL":"posts/bench.html","FilenameWithoutExtension":"bench","Frontmatter":{"Title":"benchmark","Date":"2024-01-01","Draft":false,"JSFiles":null,"Type":"post","Description":"","PreviewImage":"","Tags":["test-post"],"Authors":null},"Tags":["test-post"]},"building_anna.md":{"CompleteURL":"posts/building_anna.html","FilenameWithoutExtension":"building_anna","Frontmatter":{"Title":"Building anna","Date":"2024-04-04","Draft":false,"JSFiles":null,"Type":"post","Description":"This page contains a post about anna, a static site generator written in Go. This team project was built as part of AIEP 2024","PreviewImage":"","Tags":["acm","hsp","go","tech","talk","aiep"],"Authors":["Adhesh","Aditya","Nathan","Anirudh"]},"Tags":["acm","hsp","go","tech","talk","aiep"]},"docs.md":{"CompleteURL":"docs.html","FilenameWithoutExtension":"docs","Frontmatter":{"Title":"Anna Documentation","Date":"2024-04-10","Draft":false,"JSFiles":null,"Type":"","Description":"","PreviewImage":"","Tags":null,"Authors":null},"Tags":null},"index.md":{"CompleteURL":"index.html","FilenameWithoutExtension":"index","Frontmatter":{"Title":"Home","Date":"2024-02-24","Draft":false,"JSFiles":null,"Type":"","Description":"homepage for our ssg","PreviewImage":"/static/plane.jpg","Tags":null,"Authors":null},"Tags":null},"week-1.md":{"CompleteURL":"posts/week-1.html","FilenameWithoutExtension":"week-1","Frontmatter":{"Title":"Week-1 Progress","Date":"2024-03-18","Draft":false,"JSFiles":null,"Type":"post","Description":"","PreviewImage":"","Tags":["progress"],"Authors":["Adhesh","Aditya","Anirudh","Nathan"]},"Tags":["progress"]},"week-2.md":{"CompleteURL":"posts/week-2.html","FilenameWithoutExtension":"week-2","Frontmatter":{"Title":"Week-2 Progress","Date":"2024-03-25","Draft":false,"JSFiles":null,"Type":"post","Description":"","PreviewImage":"","Tags":["progress"],"Authors":["Adhesh","Aditya","Anirudh","Nathan"]},"Tags":["progress"]},"week-3.md":{"CompleteURL":"posts/week-3.html","FilenameWithoutExtension":"week-3","Frontmatter":{"Title":"Week-3 Progress","Date":"2024-04-01","Draft":false,"JSFiles":null,"Type":"post","Description":"","PreviewImage":"","Tags":["progress"],"Authors":["Adhesh","Aditya","Anirudh","Nathan"]},"Tags":["progress"]}} \ No newline at end of file +{"docs.html":{"CompleteURL":"docs.html","Frontmatter":{"Title":"Anna Documentation","Date":"2024-04-10","Draft":false,"JSFiles":null,"Type":"","Description":"","PreviewImage":"","Tags":null,"Authors":null},"Tags":null},"index.html":{"CompleteURL":"index.html","Frontmatter":{"Title":"Home","Date":"2024-02-24","Draft":false,"JSFiles":null,"Type":"","Description":"homepage for our ssg","PreviewImage":"/static/plane.jpg","Tags":null,"Authors":null},"Tags":null},"posts/bench.html":{"CompleteURL":"posts/bench.html","Frontmatter":{"Title":"benchmark","Date":"2024-01-01","Draft":false,"JSFiles":null,"Type":"post","Description":"","PreviewImage":"","Tags":["test-post"],"Authors":null},"Tags":["test-post"]},"posts/building_anna.html":{"CompleteURL":"posts/building_anna.html","Frontmatter":{"Title":"Building anna","Date":"2024-04-04","Draft":false,"JSFiles":null,"Type":"post","Description":"This page contains a post about anna, a static site generator written in Go. This team project was built as part of AIEP 2024","PreviewImage":"","Tags":["acm","hsp","go","tech","talk","aiep"],"Authors":["Adhesh","Aditya","Nathan","Anirudh"]},"Tags":["acm","hsp","go","tech","talk","aiep"]},"posts/week-1.html":{"CompleteURL":"posts/week-1.html","Frontmatter":{"Title":"Week-1 Progress","Date":"2024-03-18","Draft":false,"JSFiles":null,"Type":"post","Description":"","PreviewImage":"","Tags":["progress"],"Authors":["Adhesh","Aditya","Anirudh","Nathan"]},"Tags":["progress"]},"posts/week-2.html":{"CompleteURL":"posts/week-2.html","Frontmatter":{"Title":"Week-2 Progress","Date":"2024-03-25","Draft":false,"JSFiles":null,"Type":"post","Description":"","PreviewImage":"","Tags":["progress"],"Authors":["Adhesh","Aditya","Anirudh","Nathan"]},"Tags":["progress"]},"posts/week-3.html":{"CompleteURL":"posts/week-3.html","Frontmatter":{"Title":"Week-3 Progress","Date":"2024-04-01","Draft":false,"JSFiles":null,"Type":"post","Description":"","PreviewImage":"","Tags":["progress"],"Authors":["Adhesh","Aditya","Anirudh","Nathan"]},"Tags":["progress"]}} \ No newline at end of file diff --git a/test/engine/json_index_test/want_index.json b/test/engine/json_index_test/want_index.json index c8c3fa1..317502d 100644 --- a/test/engine/json_index_test/want_index.json +++ b/test/engine/json_index_test/want_index.json @@ -1,2 +1 @@ -{"docs.md":{"CompleteURL":"docs.html","FilenameWithoutExtension":"docs","Frontmatter":{"Title":"Anna Documentation","Date":"","Draft":false,"JSFiles":null,"Type":"","Description":"","PreviewImage":"","Tags":null,"Authors":null},"Tags":null}} - +{"docs.md":{"CompleteURL":"docs.html","Frontmatter":{"Title":"Anna Documentation","Date":"","Draft":false,"JSFiles":null,"Type":"","Description":"","PreviewImage":"","Tags":null,"Authors":null},"Tags":null}} \ No newline at end of file diff --git a/test/engine/render_engine_generated/posts_template.layout b/test/engine/render_engine_generated/posts_template.html similarity index 73% rename from test/engine/render_engine_generated/posts_template.layout rename to test/engine/render_engine_generated/posts_template.html index 83673a2..f66eb58 100644 --- a/test/engine/render_engine_generated/posts_template.layout +++ b/test/engine/render_engine_generated/posts_template.html @@ -3,8 +3,8 @@
-{{range $PostDate, $Post := .Posts}} - +{{range $PostDate, $Post := .DeepDataMerge.Posts}} +

{{$Post.Frontmatter.Title}}

diff --git a/test/engine/render_page/template_input.html b/test/engine/render_page/template_input.html new file mode 100644 index 0000000..028c8e0 --- /dev/null +++ b/test/engine/render_page/template_input.html @@ -0,0 +1,39 @@ +{{ define "page"}} +{{$PageData := index .DeepDataMerge.Templates .PageURL}} + + + +
+ + +{{ end}} diff --git a/test/engine/render_page/template_input.layout b/test/engine/render_page/template_input.layout deleted file mode 100644 index d10c4ad..0000000 --- a/test/engine/render_page/template_input.layout +++ /dev/null @@ -1,15 +0,0 @@ -{{ define "page"}} - - - -
-{{range .Frontmatter.Tags}} -
-{{.}} -
-{{end}} -
-{{.Body}} - - -{{end}} diff --git a/test/engine/render_page/want.html b/test/engine/render_page/want.html index 6cfcd99..74a18c8 100644 --- a/test/engine/render_page/want.html +++ b/test/engine/render_page/want.html @@ -1,18 +1,37 @@ - - - -
-
-blog -
- + -
-

Hello World

+
+
+ +
+

Hello

+
+

+ Published on 2024-03-28 + + + + +

+
+
+ +
+ blog +
+ +
+ thoughts +
+ +
+
+ +

Hello World

+
+
- + diff --git a/test/engine/render_tags/tags_subpage_template.html b/test/engine/render_tags/tags_subpage_template.html new file mode 100644 index 0000000..db17318 --- /dev/null +++ b/test/engine/render_tags/tags_subpage_template.html @@ -0,0 +1,18 @@ +{{ define "tag-subpage"}} +{{$PageData := index .DeepDataMerge.Tags .PageURL}} + + + +
+
+
+ {{$TagSet := index .DeepDataMerge.TagsMap .PageURL}} + {{range $TagSet }} + {{.Frontmatter.Title}} + {{end}} +
+
+
+ + +{{ end}} diff --git a/test/engine/render_tags/tags_subpage_template.layout b/test/engine/render_tags/tags_subpage_template.layout deleted file mode 100644 index 3a3e44b..0000000 --- a/test/engine/render_tags/tags_subpage_template.layout +++ /dev/null @@ -1,12 +0,0 @@ -{{ define "tag-subpage"}} - -
-
-{{ range .SpecificTagTemplates }} -{{ .Frontmatter.Title }} -{{ end }} -
-
- - -{{ end }} diff --git a/test/engine/render_tags/tags_template.html b/test/engine/render_tags/tags_template.html new file mode 100644 index 0000000..0c2a372 --- /dev/null +++ b/test/engine/render_tags/tags_template.html @@ -0,0 +1,25 @@ +{{ define "all-tags"}} +{{$PageData := .TemplateData}} + + +
+
+
+
+ {{range .TagNames}} + {{.}} + {{end}} +
+
+
+
+ +
+ + + + + + + +{{ end}} diff --git a/test/engine/render_tags/tags_template.layout b/test/engine/render_tags/tags_template.layout deleted file mode 100644 index cb350e1..0000000 --- a/test/engine/render_tags/tags_template.layout +++ /dev/null @@ -1,12 +0,0 @@ -{{ define "all-tags"}} - -
-
-{{ range .Tags }} -{{.}} -{{end}} -
-
- - -{{ end }} diff --git a/test/engine/render_tags/want_blogs_tags.html b/test/engine/render_tags/want_blogs_tags.html index 3d48ac2..417ba83 100644 --- a/test/engine/render_tags/want_blogs_tags.html +++ b/test/engine/render_tags/want_blogs_tags.html @@ -1,13 +1,19 @@ - -
-
-file1 -file2 + + +
+ +
-
-
- diff --git a/test/engine/render_tags/want_tags.html b/test/engine/render_tags/want_tags.html index 1c73adc..243f780 100644 --- a/test/engine/render_tags/want_tags.html +++ b/test/engine/render_tags/want_tags.html @@ -1,13 +1,26 @@ - -
-
-blogs -tech + +
+ +
+ +
+ + -
-
+ + diff --git a/test/engine/render_tags/want_tech_tags.html b/test/engine/render_tags/want_tech_tags.html index 88497f7..1dbd4e7 100644 --- a/test/engine/render_tags/want_tech_tags.html +++ b/test/engine/render_tags/want_tech_tags.html @@ -1,13 +1,19 @@ - -
-
-file2 -file3 + + +
+ +
-
-
- diff --git a/test/engine/render_user_defined/template_input.html b/test/engine/render_user_defined/template_input.html new file mode 100644 index 0000000..86f9ef2 --- /dev/null +++ b/test/engine/render_user_defined/template_input.html @@ -0,0 +1,41 @@ +{{ define "page"}} +{{$PageData := index .DeepDataMerge.Templates .PageURL}} + + + +
+
+ {{ if eq $PageData.Frontmatter.Type "post" }} +
+

{{ $PageData.Frontmatter.Title }}

+
+

+ Published on {{$PageData.Frontmatter.Date}} + + {{ if eq (len $PageData.Frontmatter.Authors) 0 }} + {{.DeepDataMerge.LayoutConfig.Author}} + {{ else }} + {{range $PageData.Frontmatter.Authors }} + {{ . }}, + {{ end }} + {{ end }} +

+
+
+ {{range $PageData.Frontmatter.Tags}} +
+ {{.}} +
+ {{end}} +
+
+ {{ else }} + {{ end }} + {{$PageData.Body}} +
+
+ + + + +{{ end}} diff --git a/test/engine/render_user_defined/template_input.layout b/test/engine/render_user_defined/template_input.layout deleted file mode 100644 index d10c4ad..0000000 --- a/test/engine/render_user_defined/template_input.layout +++ /dev/null @@ -1,15 +0,0 @@ -{{ define "page"}} - - - -
-{{range .Frontmatter.Tags}} -
-{{.}} -
-{{end}} -
-{{.Body}} - - -{{end}} diff --git a/test/engine/render_user_defined/want_index.html b/test/engine/render_user_defined/want_index.html index c3dcf98..b6923df 100644 --- a/test/engine/render_user_defined/want_index.html +++ b/test/engine/render_user_defined/want_index.html @@ -1,10 +1,16 @@ - - + + -
-
-

Index Page

+
+
+ + +

Index Page

+
+
+ + diff --git a/test/engine/render_user_defined/want_post_hello.html b/test/engine/render_user_defined/want_post_hello.html index d4fc935..3364574 100644 --- a/test/engine/render_user_defined/want_post_hello.html +++ b/test/engine/render_user_defined/want_post_hello.html @@ -1,10 +1,16 @@ - - + + -
-
-

Hello World

+
+
+ + +

Hello World

+
+
+ + diff --git a/test_clean.sh b/test_clean.sh new file mode 100644 index 0000000..0b34da1 --- /dev/null +++ b/test_clean.sh @@ -0,0 +1,5 @@ +#This script can be utilised to clean the test output data + +cd test/ +rm -rf `find . -type d -name rendered` +cd ../ \ No newline at end of file