diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml new file mode 100644 index 0000000..2ea4699 --- /dev/null +++ b/.github/workflows/bench.yml @@ -0,0 +1,45 @@ +# Benchmark anna v/s other SSGs +name: Benchmark + +on: + workflow_dispatch: + +jobs: + benchmark: + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Install Hyperfine + run: | + sudo apt-get update + sudo apt-get install -y hyperfine + + - name: Install Go + uses: actions/setup-go@v4 + with: + go-version-file: "go.mod" + + - name: Install Rust + uses: actions-rs/toolchain@v1.0.6 + with: + profile: minimal + toolchain: stable + + - name: Install Dependencies + run: | + sudo apt-get install -y curl git + curl https://sh.rustup.rs -sSf | sh -s -- -y + export PATH="$HOME/.cargo/bin:$PATH" + rustup default stable + rustup update + rustc --version + cargo --version + go version + + - name: Run Benchmark + run: | + chmod +x test/bench.sh + ./test/bench.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8ddfcfd..32dd1c7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,9 +1,7 @@ # Sample workflow for building and deploying an anna site to GitHub pages -name: Build +name: Build and Deploy on: - push: - branches: [main] workflow_dispatch: # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages @@ -25,6 +23,7 @@ defaults: jobs: build: runs-on: ubuntu-latest + needs: test-and-coverage environment: name: github-pages url: ${{steps.deployment.outputs.page_url}} @@ -37,19 +36,8 @@ jobs: with: go-version-file: "go.mod" - - name: test anna - run: go test ./... - - - name: build anna - run: go build - - - name: Insert commit hash in footer - run: | - sed -i "s/COMMIT_HASH/${{ github.sha }}/g" site/layout/partials/footer.layout - - name: build site with anna - run: | - ./anna + run: go run . - name: upload /rendered uses: actions/upload-artifact@master @@ -64,6 +52,7 @@ jobs: environment: name: github-pages url: ${{steps.deployment.outputs.page_url}} + steps: - name: download /rendered uses: actions/download-artifact@master diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..59873a7 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,30 @@ +name: Release + +on: + push: + tags: + - '*' + +permissions: + contents: write + +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - name: checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: setup go + uses: actions/setup-go@v4 + + - name: run goreleaser + uses: goreleaser/goreleaser-action@v5 + with: + distribution: goreleaser + version: latest + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1e768f9..acc1ce8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,32 +1,27 @@ -# Sample workflow for building and deploying an anna site to GitHub pages -name: Tests - -on: [push, pull_request] - -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages -permissions: - contents: read - pages: write - id-token: write - -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. -# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. -concurrency: - group: "pages" - cancel-in-progress: false +name: Go Test and Coverage +on: + push: + pull_request: + workflow_dispatch: + defaults: run: shell: bash jobs: - check: + test-and-coverage: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Setup Go uses: actions/setup-go@v4 with: go-version-file: "go.mod" - - run: go test ./... + - name: Run Tests + run: go test ./... + + - name: Generate Test Coverage + run: go test -cover ./... diff --git a/.gitignore b/.gitignore index 211b43d..576c567 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ -!rendered/ -site/rendered/ anna !anna/ *.exe @@ -8,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/README.md b/README.md index c8d119b..4844220 100644 --- a/README.md +++ b/README.md @@ -17,31 +17,28 @@ A static site generator in go Inspired by [Hugo](https://gohugo.io) and [Saaru](https://github.com/anirudhRowjee/saaru), this static site generator aims to take performance to the next level with parallel rendering, live reload and so much more, all in Go. -> Pronounced: `/ɐnːɐ/` which means rice in Kannada 🍚 +Pronounced: `/ɐnːɐ/` which means rice 🍚 in Kannada -This project is a part of the ACM PESU-ECC's yearly [AIEP](https://acmpesuecc.github.io/aiep) program, and is maintained by [Adhesh Athrey](https://github.com/DedLad), [Nathan Paul](https://github.com/polarhive), [Anirudh Sudhir](https://github.com/anirudhsudhir), and [Aditya Hegde](https://github.com/bwaklog) +> This project is a part of the ACM PESU-ECC's yearly [AIEP](https://acmpesuecc.github.io/aiep) program, and is maintained by [Adhesh Athrey](https://github.com/DedLad), [Nathan Paul](https://github.com/polarhive), [Anirudh Sudhir](https://github.com/anirudhsudhir), and [Aditya Hegde](https://github.com/bwaklog) --- - -## Install +## Get Started ```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 -Alternatively, clone our repository to build the latest version of anna: - -```sh -git clone github.com/acmpesuecc/anna --depth=1; cd anna -go run . -``` - -### Detailed documentation of our SSG can be found [here](https://anna-docs.netlify.app/) +## Contributing to Anna ---- +Detailed documentation for our SSG can be found: [here](https://anna-docs.netlify.app/) -## Flags +If you have git installed, clone our repository and build against the latest commit +```sh +git clone github.com/acmpesuecc/anna; cd anna +go build +``` ```text Usage: anna [flags] diff --git a/cmd/anna/anna.go b/cmd/anna/anna.go index b83b9e6..788ef70 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/go.mod b/go.mod index 221ae52..ea939a8 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/acmpesuecc/anna -go 1.22.0 +go 1.22.2 require ( github.com/PuerkitoBio/goquery v1.9.1 @@ -20,7 +20,7 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/rivo/uniseg v0.4.7 // indirect - github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/spf13/pflag v1.0.5 // indirect golang.org/x/net v0.24.0 // indirect golang.org/x/sys v0.19.0 // indirect diff --git a/go.sum b/go.sum index 8a3d170..6aceecd 100644 --- a/go.sum +++ b/go.sum @@ -31,8 +31,8 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= 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 daf6e31..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") @@ -154,18 +177,19 @@ func (e *Engine) GenerateSitemap(outFilePath string) { func (e *Engine) GenerateFeed() { var buffer bytes.Buffer 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("\n") buffer.WriteString(" " + templateData.Frontmatter.Title + "\n") - buffer.WriteString(" \n") - buffer.WriteString(" " + e.LayoutConfig.BaseURL + "/" + 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") @@ -173,7 +197,7 @@ func (e *Engine) GenerateFeed() { } buffer.WriteString("\n") - outputFile, err := os.Create(helpers.SiteDataPath + "rendered/feed.atom") + outputFile, err := os.Create(helpers.SiteDataPath + "rendered/feed.xml") if err != nil { e.ErrorLogger.Fatal(err) } diff --git a/pkg/engine/anna_engine_test.go b/pkg/engine/anna_engine_test.go index 985ccd0..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,10 +57,13 @@ 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) } + if err := os.MkdirAll(TestDirPath+"render_tags/rendered", 0750); err != nil { + t.Errorf("%v", err) + } e.RenderTags(fileOutPath, templ) t.Run("render tag.html", func(t *testing.T) { @@ -105,36 +108,39 @@ func TestRenderTags(t *testing.T) { } if !slices.Equal(got_tech_file, want_tech_file) { - t.Errorf("The expected and generated tech.html tag-subpage can be found in test/engine/render_tags/rendered/tags/") + t.Errorf("The expected and generated tech.html tag-subpage can be found in test/engine/render_tags/rendered/tags/") } }) -} +} func TestGenerateMergedJson(t *testing.T) { - t.Run("test json creation from e.Templates", func(t *testing.T) { + if err := os.MkdirAll(TestDirPath+"json_index_test/static", 0750); err != nil { + t.Errorf("%v", err) + } + + 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", }, } - e.GenerateJSONIndex(TestDirPath + "merged_data_test") + e.GenerateJSONIndex(TestDirPath + "json_index_test") - got_json, err := os.ReadFile(TestDirPath + "/merged_data_test/static/index.json") + got_json, err := os.ReadFile(TestDirPath + "/json_index_test/static/index.json") if err != nil { t.Errorf("%v", err) } - want_json, err := os.ReadFile(TestDirPath + "/merged_data_test/want_index.json") + want_json, err := os.ReadFile(TestDirPath + "/json_index_test/want_index.json") if err != nil { t.Errorf("%v", err) } @@ -143,45 +149,46 @@ func TestGenerateMergedJson(t *testing.T) { want_json = bytes.TrimSpace(want_json) if !slices.Equal(got_json, want_json) { - t.Errorf("The expected and generated json can be found in test/layout/") + t.Errorf("The expected and generated json can be found in test/engine/json_index_test") } }) + } 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") @@ -205,7 +212,8 @@ func TestGenerateSitemap(t *testing.T) { }) if strings.Compare(got_sitemap_string, want_sitemap_string) == 0 { - t.Errorf("The expected and generated sitemap can be found in test/layout/sitemap/") + t.Errorf("The expected and generated sitemap can be found in test/engine/sitemap/") } }) + } 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 906b7fb..a1e7b2a 100644 --- a/pkg/engine/engine_integration_test.go +++ b/pkg/engine/engine_integration_test.go @@ -13,28 +13,33 @@ 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

"), + Body: template.HTML("

Index Page

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

Hello World

"), + 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 { + t.Errorf("%v", err) } 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) } + engine.RenderUserDefinedPages(TestDirPath+"render_user_defined/", templ) want_index_file, err := os.ReadFile(TestDirPath + "render_user_defined/want_index.html") @@ -65,4 +70,5 @@ func TestRenderUserDefinedPages(t *testing.T) { t.Errorf("The expected and generated post/hello.html can be found in test/engine/render_user_defined/rendered/posts/") } }) + } diff --git a/pkg/engine/engine_test.go b/pkg/engine/engine_test.go index f39cf89..a17e4cd 100644 --- a/pkg/engine/engine_test.go +++ b/pkg/engine/engine_test.go @@ -14,16 +14,19 @@ import ( const TestDirPath = "../../test/engine/" func TestRenderPage(t *testing.T) { + if err := os.MkdirAll(TestDirPath+"render_page/rendered", 0750); err != nil { + t.Errorf("%v", err) + } + 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", @@ -33,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+"engine/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 { @@ -62,4 +65,5 @@ func TestRenderPage(t *testing.T) { t.Errorf("The expected and generated page.html can be found in test/engine/render_page/rendered/") } }) + } 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 3c046cc..9aa5e40 100644 --- a/pkg/engine/user_engine_test.go +++ b/pkg/engine/user_engine_test.go @@ -12,43 +12,48 @@ 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", }, }, } + if err := os.MkdirAll(TestDirPath+"render_engine_generated/rendered", 0750); err != nil { + t.Errorf("%v", err) + } + 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) } diff --git a/pkg/helpers/helper.go b/pkg/helpers/helper.go index 7ee8a20..889a73e 100644 --- a/pkg/helpers/helper.go +++ b/pkg/helpers/helper.go @@ -72,6 +72,13 @@ func (h *Helper) CreateRenderedDir(fileOutPath string) { } func (h *Helper) Bootstrap() { + fmt.Println("Are you sure you want to proceed with the bootstrap process? (y/n)") + var confirm string + fmt.Scanln(&confirm) + if confirm != "y" { + fmt.Println("Bootstrap process cancelled.") + return + } log.Println("Downloading base theme") url := "https://github.com/acmpesuecc/anna/archive/refs/heads/main.zip" output, err := os.Create("anna-repo.zip") diff --git a/pkg/parser/parser.go b/pkg/parser/parser.go index 879b795..648519d 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 be01e9c..66ca0d7 100644 --- a/site/content/docs.md +++ b/site/content/docs.md @@ -1,4 +1,5 @@ --- +date: 2024-04-10 title: Anna Documentation --- @@ -20,12 +21,12 @@ Pronounced: `/ɐnːɐ/` which means rice in Kannada 🍚 This Project is a part of the ACM PESU-ECC's yearly [AIEP](https://acmpesuecc.github.io/aiep) program, and is maintained by [Adhesh Athrey](https://github.com/DedLad), [Nathan Paul](https://github.com/polarhive), [Anirudh Sudhir](https://github.com/anirudhsudhir), and [Aditya Hegde](https://github.com/bwaklog) +--- ## Directory structure The ssg currently requires the following directory structure ```text - /anna β”œβ”€β”€ /cmd β”œβ”€β”€ /pkg @@ -76,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 + +Each layout file(except `posts.html` and `tags.html`) can access any data from the entire ssg + +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}} -The layout files can access the following rendered data from the markdown files: +All of the following page data fields can be accessed in the above manner: -- `{{.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 +- `{{$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 @@ -109,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**) @@ -134,8 +158,7 @@ navbar: - posts baseURL: http://localhost:8000/ -# Replace this with the actual canonical-url of your site. - +# Replace this with the actual canonical-url of your site # baseURL tells search-engines (SEO), web-crawlers (robots.txt) so people can discover your site on the internet. # It's also embeded in your sitemap / atom feed and can be used to change metadata about your site. @@ -144,32 +167,38 @@ siteScripts: author: Anna ``` -## Install +--- -Once you have a directory structure, install `anna` using: +## Run locally ```sh -go install github.com/acmpesuecc/anna@latest +go run github.com/acmpesuecc/anna@v1.0.0-alpha ``` -Or if you have git installed, clone our repository: +> 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 + +Detailed documentation for our SSG can be found: [here](https://anna-docs.netlify.app/) + +If you have git installed, clone our repository and build against the latest commit ```sh -git clone github.com/acmpesuecc/anna --depth=1 -cd anna -go run . +git clone github.com/acmpesuecc/anna; cd anna +go build ``` -## Flags - ```text Usage: anna [flags] Flags: - -a, --addr stringwhich sip address to serve rendered content to (default "8000") - -d, --draft renders draft posts - -h, --help help for ssg - -s, --serve serve the rendered content - -v, --validate-html validate semantic HTML + -a, --addr string ip address to serve rendered content to (default "8000") + -d, --draft renders draft posts + -h, --help help for anna + -l, --layout validates html layouts + -p, --prof enable profiling + -s, --serve serve the rendered content + -v, --version prints current version number + -w, --webconsole wizard to setup anna ``` diff --git a/site/content/posts/bench.md b/site/content/posts/bench.md index c4c8bac..8811ef1 100644 --- a/site/content/posts/bench.md +++ b/site/content/posts/bench.md @@ -2,7 +2,7 @@ title: benchmark template: post.jinja author: John Doe -date: 2024-02-23 +date: 2024-01-01 categories: - Technology - Programming 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 58% rename from site/layout/page.layout rename to site/layout/page.html index 61f8a5d..32daec0 100644 --- a/site/layout/page.layout +++ b/site/layout/page.html @@ -1,4 +1,5 @@ -{{ define "page" }} +{{ define "page"}} +{{$PageData := index .DeepDataMerge.Templates .PageURL}} {{ template "head" .}} @@ -6,33 +7,33 @@ {{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 }}

- + {{end}}
{{ else }} {{ end }} - {{.Body}} + {{$PageData.Body}}
{{template "footer" .}} 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 99b68b0..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 babaad0..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-02-23","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":"","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"]},"zettel_impl.md":{"CompleteURL":"posts/zettel_impl.html","FilenameWithoutExtension":"zettel_impl","Frontmatter":{"Title":"Implementing Zettelkasten in Anna","Date":"2024-04-10","Draft":false,"JSFiles":null,"Type":"post","Description":"This post focuses of the Proof of Concept behind how we plan to integrate zettelkasten in anna(our SSG) and implementing the new note taking functionality, supporting a new version of *Deep Data Merge* along with","PreviewImage":"","Tags":["blog","tech","aiep"],"Authors":["Aditya Hegde","Anirudh Sudhir"]},"Tags":["blog","tech","aiep"]}} \ 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/site/static/styles/feed.xsl b/site/static/styles/feed.xsl new file mode 100644 index 0000000..7b47381 --- /dev/null +++ b/site/static/styles/feed.xsl @@ -0,0 +1,77 @@ + + + + + + + + Web Feed β€’ <xsl:value-of select="atom:feed/atom:title"/> + + + +
+
+

This is a web feed, also known as an RSS feed. Subscribe by copying the URL from the address bar into your newsreader app.

+
+
+
+ +
+
+

Recent Items

+ +
+ + +
+ + +

's Web Feed Preview

+

This RSS feed provides the latest posts from 's blog. + + + + + + Visit Website → + + +

+ +

What is an RSS feed?

+

An RSS feed is a data format that contains the latest content from a website, blog, or podcast. You can use feeds to subscribe to websites and get the latest content in one place.

+
    +
  • Feeds put you in control. Unlike social media apps, there is no algorithm deciding what you see or read. You always get the latest content from the creators you care about.
  • +
  • Feed are private by design. No one owns web feeds, so no one is harvesting your personal information and profiting by selling it to advertisers.
  • +
  • Feeds are spam-proof. Had enough? Easy, just unsubscribe from the feed.
  • +
+

All you need to do to get started is to add the URL (web address) for this feed to a special app called a newsreader. Visit About Feeds to get started with newsreaders and subscribing. It’s free.

+
+ + +
+

+ + + + + + +

+

+ +

+ + Published: + +
+
+ +
\ No newline at end of file diff --git a/test/bench.sh b/test/bench.sh index d957f16..baaf2b8 100755 --- a/test/bench.sh +++ b/test/bench.sh @@ -1,24 +1,15 @@ #!/bin/bash -set -e -clear - # parameters files=1000 warm=10 # cleanup cleanup() { - echo "Cleaning up" + echo "cleaning up" rm -rf /tmp/bench } trap cleanup EXIT -# echo "bash: cleanup" -cd /tmp || exit -rm -rf bench -mkdir bench -cd bench - # check if hyperfine is installed if ! command -v hyperfine &>/dev/null; then echo "hyperfine is not installed. Please install hyperfine to continue." @@ -26,40 +17,43 @@ if ! command -v hyperfine &>/dev/null; then fi # cloning candidates -echo "Cloning SSGs" -git clone https://github.com/acmpesuecc/anna -git clone https://github.com/anirudhRowjee/saaru -sleep 1; clear +echo "clone SSGs" +git clone https://github.com/acmpesuecc/anna /tmp/bench/anna +git clone https://github.com/anirudhRowjee/saaru /tmp/bench/saaru +sleep 1 -# benchmark file -cp anna/site/content/posts/bench.md test.md +# copy benchmark file +cp /tmp/bench/anna/site/content/posts/bench.md /tmp/bench/test.md -# checkout at v1 and commit -cd anna && git checkout v1.0.0-alpha; cd .. -cd saaru && git checkout c17930724fcaad67e1cfa3cc969667d043c1b826; cd .. -sleep 1; clear +# checkout specific versions +cd /tmp/bench/anna && git checkout v1.0.0-alpha && cd .. +cd saaru && git checkout c17930724fcaad67e1cfa3cc969667d043c1b826 && cd .. +sleep 1 # build SSGs -cd anna; go build; cd .. -cd saaru; cargo build --release; mv ./target/release/saaru .; cd .. -sleep 1; clear +echo "build SSGs" +cd /tmp/bench/anna && go build && cd .. +cd saaru && cargo build --release && mv ./target/release/saaru . && cd .. +echo "finished building" -# clean content dirs (no md files other than test.md) +# clean content/* dirs echo "Cleaning content directories" -rm -rf anna/site/content/posts/* -rm -rf saaru/docs/src/* +rm -rf /tmp/bench/anna/site/content/posts/* +rm -rf /tmp/bench/saaru/docs/src/* # create multiple copies of the test file echo "Spawning $files different markdown files..." for ((i = 0; i < files; i++)); do - cp test.md "anna/site/content/posts/test_$i.md" - cp test.md "saaru/docs/src/test_$i.md" + cp /tmp/bench/test.md "/tmp/bench/anna/site/content/posts/test_$i.md" + cp /tmp/bench/test.md "/tmp/bench/saaru/docs/src/test_$i.md" done -sleep 1; clear +sleep 1 -# run the benchmark -echo "Running benchmark with $files md files and $warm warmup runs" -cd anna; hyperfine -p 'sync' -w $warm "./anna"; cd .. -cd saaru; hyperfine -p 'sync' -w $warm "./saaru --base-path ./docs"; cd .. +# cooldown +echo "cooldown for 30s" +sleep 30 -echo "Benchmarking finished." +# run hyperfine +echo "running benchmark: $files md files and $warm warmup runs" +cd /tmp/bench/anna && hyperfine -p 'sync' -w $warm "./anna" && cd .. +cd saaru && hyperfine -p 'sync' -w $warm "./saaru --base-path ./docs" && cd .. diff --git a/test/engine/engine/render_page/rendered/posts/got.html b/test/engine/engine/render_page/rendered/posts/got.html deleted file mode 100644 index 6cfcd99..0000000 --- a/test/engine/engine/render_page/rendered/posts/got.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - -
- -
-blog -
- - - -
-

Hello World

- - diff --git a/test/engine/json_index_test/want_index.json b/test/engine/json_index_test/want_index.json new file mode 100644 index 0000000..317502d --- /dev/null +++ b/test/engine/json_index_test/want_index.json @@ -0,0 +1 @@ +{"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/merged_data_test/static/index.json b/test/engine/merged_data_test/static/index.json deleted file mode 100644 index b9e300f..0000000 --- a/test/engine/merged_data_test/static/index.json +++ /dev/null @@ -1 +0,0 @@ -{"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}} \ No newline at end of file diff --git a/test/engine/merged_data_test/want_index.json b/test/engine/merged_data_test/want_index.json deleted file mode 100644 index c8c3fa1..0000000 --- a/test/engine/merged_data_test/want_index.json +++ /dev/null @@ -1,2 +0,0 @@ -{"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}} - 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_engine_generated/rendered/posts.html b/test/engine/render_engine_generated/rendered/posts.html deleted file mode 100644 index 9a5b556..0000000 --- a/test/engine/render_engine_generated/rendered/posts.html +++ /dev/null @@ -1,40 +0,0 @@ - - - -
- - diff --git a/test/engine/render_page/rendered/posts/got.html b/test/engine/render_page/rendered/posts/got.html deleted file mode 100644 index 6cfcd99..0000000 --- a/test/engine/render_page/rendered/posts/got.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - -
- -
-blog -
- - - -
-

Hello World

- - 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}} + + + +
+
+ {{ 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_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/rendered/tags.html b/test/engine/render_tags/rendered/tags.html deleted file mode 100644 index 1c73adc..0000000 --- a/test/engine/render_tags/rendered/tags.html +++ /dev/null @@ -1,13 +0,0 @@ - - -
-
- -blogs - -tech - -
-
- - diff --git a/test/engine/render_tags/rendered/tags/blogs.html b/test/engine/render_tags/rendered/tags/blogs.html deleted file mode 100644 index 3d48ac2..0000000 --- a/test/engine/render_tags/rendered/tags/blogs.html +++ /dev/null @@ -1,13 +0,0 @@ - - -
-
- -file1 - -file2 - -
-
- - diff --git a/test/engine/render_tags/rendered/tags/tech.html b/test/engine/render_tags/rendered/tags/tech.html deleted file mode 100644 index 88497f7..0000000 --- a/test/engine/render_tags/rendered/tags/tech.html +++ /dev/null @@ -1,13 +0,0 @@ - - -
-
- -file2 - -file3 - -
-
- - 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/rendered/index.html b/test/engine/render_user_defined/rendered/index.html deleted file mode 100644 index c3dcf98..0000000 --- a/test/engine/render_user_defined/rendered/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - -
- -
-

Index Page

- - diff --git a/test/engine/render_user_defined/rendered/posts/hello.html b/test/engine/render_user_defined/rendered/posts/hello.html deleted file mode 100644 index d4fc935..0000000 --- a/test/engine/render_user_defined/rendered/posts/hello.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - -
- -
-

Hello World

- - 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/engine/sitemap/got_sitemap.xml b/test/engine/sitemap/got_sitemap.xml deleted file mode 100644 index 03e81a4..0000000 --- a/test/engine/sitemap/got_sitemap.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - example.org/about.html - 2024-02-23 - - - example.org/index.html - 2024-02-23 - - - example.org/research.html - 2024-02-23 - - diff --git a/test/engine/want_sitemap.xml b/test/engine/want_sitemap.xml deleted file mode 100644 index 5578525..0000000 --- a/test/engine/want_sitemap.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - example.org/about.html - 2024-02-23 - - - example.org/index.html - 2024-02-23 - - - example.org/research.html - 2024-02-23 - - 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