Skip to content

Commit

Permalink
feat: implement deep data merge
Browse files Browse the repository at this point in the history
TODO:
- Pass the deep data merge struct to all the templates
- Remove redundant fields and refactor the engine pkg/
  • Loading branch information
anirudhsudhir committed Apr 11, 2024
1 parent 79cb976 commit 96f6666
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 90 deletions.
19 changes: 10 additions & 9 deletions cmd/anna/anna.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ func (cmd *Cmd) VanillaRender() {
ErrorLogger: log.New(os.Stderr, "ERROR\t", log.Ldate|log.Ltime|log.Lshortfile),
RenderDrafts: cmd.RenderDrafts,
}

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),
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[string][]parser.TemplateData)

helper := helpers.Helper{
ErrorLogger: e.ErrorLogger,
Expand All @@ -47,18 +48,18 @@ 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")
Expand Down
38 changes: 19 additions & 19 deletions pkg/engine/anna_engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ 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([]string, 0, len(e.DeepDataMerge.TagsMap))
for tag := range e.DeepDataMerge.TagsMap {
tags = append(tags, tag)
}

Expand All @@ -31,7 +31,7 @@ func (e *Engine) RenderTags(fileOutPath string, templ *template.Template) {

tagNames := parser.TemplateData{
FilenameWithoutExtension: "Tags",
Layout: e.LayoutConfig,
Layout: e.DeepDataMerge.LayoutConfig,
Frontmatter: parser.Frontmatter{Title: "Tags"},
Tags: tags,
}
Expand All @@ -52,15 +52,15 @@ func (e *Engine) RenderTags(fileOutPath string, templ *template.Template) {
var wg sync.WaitGroup

// 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) {
defer wg.Done()

pagePath := "tags/" + tag
pagePath := "tags/" + tag + ".html"
templateData := parser.TemplateData{
FilenameWithoutExtension: tag,
Layout: e.LayoutConfig,
Layout: e.DeepDataMerge.LayoutConfig,
Frontmatter: parser.Frontmatter{
Title: tag,
},
Expand Down Expand Up @@ -88,7 +88,7 @@ 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,
Expand All @@ -97,7 +97,7 @@ func (e *Engine) GenerateJSONIndex(outFilePath string) {
}
}

e.JSONIndex = jsonIndexTemplate
e.DeepDataMerge.JSONIndex = jsonIndexTemplate

// Marshal the contents of jsonMergedData
jsonMergedMarshaledData, err := json.Marshal(jsonIndexTemplate)
Expand All @@ -117,22 +117,22 @@ func (e *Engine) GenerateSitemap(outFilePath string) {
buffer.WriteString("<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\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 + "/" + templateData.FilenameWithoutExtension + ".html"
buffer.WriteString("\t<url>\n")
buffer.WriteString("\t\t<loc>" + url + "</loc>\n")
buffer.WriteString("\t\t<lastmod>" + templateData.Frontmatter.Date + "</lastmod>\n")
Expand All @@ -156,17 +156,17 @@ func (e *Engine) GenerateFeed() {
buffer.WriteString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
buffer.WriteString("<?xml-stylesheet href=\"/static/styles/feed.xsl\" type=\"text/xsl\"?>\n")
buffer.WriteString("<feed xmlns=\"http://www.w3.org/2005/Atom\">\n")
buffer.WriteString(" <title>" + e.LayoutConfig.SiteTitle + "</title>\n")
buffer.WriteString(" <link href=\"" + e.LayoutConfig.BaseURL + "/" + "\" rel=\"self\"/>\n")
buffer.WriteString(" <title>" + e.DeepDataMerge.LayoutConfig.SiteTitle + "</title>\n")
buffer.WriteString(" <link href=\"" + e.DeepDataMerge.LayoutConfig.BaseURL + "/" + "\" rel=\"self\"/>\n")
buffer.WriteString(" <updated>" + time.Now().Format(time.RFC3339) + "</updated>\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("<entry>\n")
buffer.WriteString(" <title>" + templateData.Frontmatter.Title + "</title>\n")
buffer.WriteString(" <link href=\"" + e.LayoutConfig.BaseURL + "/posts/" + templateData.FilenameWithoutExtension + ".html\"/>\n")
buffer.WriteString(" <id>" + e.LayoutConfig.BaseURL + "/posts/" + templateData.FilenameWithoutExtension + ".html</id>\n")
buffer.WriteString(" <link href=\"" + e.DeepDataMerge.LayoutConfig.BaseURL + "/posts/" + templateData.FilenameWithoutExtension + ".html\"/>\n")
buffer.WriteString(" <id>" + e.DeepDataMerge.LayoutConfig.BaseURL + "/posts/" + templateData.FilenameWithoutExtension + ".html</id>\n")
buffer.WriteString(" <updated>" + time.Unix(templateData.Date, 0).Format(time.RFC3339) + "</updated>\n")
buffer.WriteString(" <content type=\"html\"><![CDATA[" + string(templateData.Body) + "]]></content>\n")
buffer.WriteString(" </entry>\n")
Expand Down
28 changes: 14 additions & 14 deletions pkg/engine/anna_engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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[string][]parser.TemplateData)
e.DeepDataMerge.LayoutConfig.BaseURL = "example.org"

fileOutPath := "../../test/engine/render_tags/"

e.TagsMap["blogs"] = []parser.TemplateData{
e.DeepDataMerge.TagsMap["blogs"] = []parser.TemplateData{
{
CompleteURL: "posts/file1.html",
Frontmatter: parser.Frontmatter{
Expand All @@ -40,7 +40,7 @@ func TestRenderTags(t *testing.T) {
},
}

e.TagsMap["tech"] = []parser.TemplateData{
e.DeepDataMerge.TagsMap["tech"] = []parser.TemplateData{
{
CompleteURL: "posts/file2.html",
Frontmatter: parser.Frontmatter{
Expand Down Expand Up @@ -124,12 +124,12 @@ 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[string][]parser.TemplateData)

e.Templates["docs.md"] = parser.TemplateData{
e.DeepDataMerge.Templates["docs.md"] = parser.TemplateData{
FilenameWithoutExtension: "docs",
CompleteURL: "docs.html",
Frontmatter: parser.Frontmatter{
Expand Down Expand Up @@ -165,10 +165,10 @@ func TestGenerateMergedJson(t *testing.T) {
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[string][]parser.TemplateData)

t1 := parser.TemplateData{
FilenameWithoutExtension: "index",
Expand All @@ -191,11 +191,11 @@ func TestGenerateSitemap(t *testing.T) {
},
}

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")

Expand Down
36 changes: 26 additions & 10 deletions pkg/engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import (
"github.com/acmpesuecc/anna/pkg/parser"
)

type Engine struct {
// This struct holds all of the ssg data
type MergedSiteData 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
Expand All @@ -26,6 +27,11 @@ type Engine struct {

// Stores the index generated for search functionality
JSONIndex map[template.URL]JSONIndexTemplate
}

type Engine struct {
// Stores the merged ssg data
DeepDataMerge MergedSiteData

// Common logger for all engine functions
ErrorLogger *log.Logger
Expand All @@ -39,27 +45,37 @@ type JSONIndexTemplate struct {
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
pageTemplateData - an interface that accepts any type of data to be passed to ExecuteTemplate()
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, pageTemplateData interface{}, 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

// Storing the rendered HTML file to a buffer
err := templ.ExecuteTemplate(&buffer, templateStartString, pageTemplateData)
err := template.ExecuteTemplate(&buffer, templateStartString, pageTemplateData)
if err != nil {
e.ErrorLogger.Fatal(err)
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/engine/engine_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@ 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[string][]parser.TemplateData)

engine.Templates["index.md"] =
engine.DeepDataMerge.Templates["index.md"] =
parser.TemplateData{
FilenameWithoutExtension: "index",
Body: template.HTML("<h1>Index Page</h1>"),
CompleteURL: "index.html",
}

engine.Templates["posts/hello.md"] = parser.TemplateData{
engine.DeepDataMerge.Templates["posts/hello.md"] = parser.TemplateData{
FilenameWithoutExtension: "hello",
Body: template.HTML("<h1>Hello World</h1>"),
CompleteURL: "posts/hello.html",
Expand Down
6 changes: 3 additions & 3 deletions pkg/engine/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ 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[string][]parser.TemplateData)

page := parser.TemplateData{
CompleteURL: template.URL("got"),
Expand All @@ -50,7 +50,7 @@ func TestRenderPage(t *testing.T) {
t.Errorf("%v", err)
}

engine.RenderPage(TestDirPath+"render_page/", "posts/got.md", page, templ, "page")
engine.RenderPage(TestDirPath+"render_page/", "posts/got.html", page, templ, "page")

got_file, err := os.ReadFile(TestDirPath + "render_page/rendered/posts/got.html")
if err != nil {
Expand Down
14 changes: 7 additions & 7 deletions pkg/engine/user_engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ func (e *Engine) RenderEngineGeneratedFiles(fileOutPath string, templ *template.
var postsBuffer bytes.Buffer

postsData := postsTemplateData{
Posts: e.Posts,
Posts: e.DeepDataMerge.Posts,
TemplateData: parser.TemplateData{
Frontmatter: parser.Frontmatter{Title: "Posts"},
Layout: e.LayoutConfig,
Layout: e.DeepDataMerge.LayoutConfig,
},
}

Expand All @@ -42,25 +42,25 @@ 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 {
concurrency = numTemplates
}

templateURLs := make([]string, 0, numTemplates)
for templateURL := range e.Templates {
for templateURL := range e.DeepDataMerge.Templates {
templateURLs = append(templateURLs, string(templateURL))
}

var wg sync.WaitGroup
semaphore := make(chan struct{}, concurrency)

for _, templateURL := range templateURLs {
templData := e.Templates[template.URL(templateURL)]
templData := e.DeepDataMerge.Templates[template.URL(templateURL)]
fileInPath := strings.TrimSuffix(string(templData.CompleteURL), ".html")
if fileInPath == "" {
if fileInPath == ".html" {
continue
}

Expand All @@ -73,7 +73,7 @@ func (e *Engine) RenderUserDefinedPages(fileOutPath string, templ *template.Temp
wg.Done()
}()

e.RenderPage(fileOutPath, template.URL(fileInPath), templData, templ, "page")
e.RenderPage(fileOutPath, templData.CompleteURL, templData, templ, "page")
}(templateURL)
}

Expand Down
Loading

0 comments on commit 96f6666

Please sign in to comment.