diff --git a/.gitignore b/.gitignore index d163863..3974640 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -build/ \ No newline at end of file +build/ +# Local Netlify folder +.netlify diff --git a/_test-site/posts/2026/02/18-first-post.md b/_test-site/posts/2026/02/18-first-post.md new file mode 100644 index 0000000..4843f0c --- /dev/null +++ b/_test-site/posts/2026/02/18-first-post.md @@ -0,0 +1,22 @@ +--- +date: 2026-02-18T22:17:00+11:00 +title: First Post +slug: /2026/02/18/first-post +--- +Hello World! + +This is the first post of Weiro, a simple blogging CMS. At this stage, I've only managed to +build the site reader and site builder, making this little more than a simple little static +site generator. But I eventually want to add a proper UI for editing posts. + +Much like Kev Quirk's Pure Blogging CMS, of which this is inspired by, the contents of this +site will be stored as plain text files on the file system (or in any `fs.FS` implementation). +But unlike Kev Quirk's CMS, the generated file will not be rendered on demand. Instead, it +will be built as a static site, a bit like Micro.blog, and served from something else. + +I start with Netlify but there's no reason it needs to be that. It could be anything that +can serve static files. + +In addition to this, I also want to make sure titleless posts are a first-class citizen. +That's how I like writing my posts, and many blogging platforms don't have great support for +it. \ No newline at end of file diff --git a/_test-site/site.yaml b/_test-site/site.yaml new file mode 100644 index 0000000..35d9cba --- /dev/null +++ b/_test-site/site.yaml @@ -0,0 +1,6 @@ +title: Weiro +tagline: A blogging CMS +base_url: https://jolly-boba-9e2486.netlify.app/ +public: + netlify: + site_id: 55c878a7-189e-42cf-aa02-5c60908143f3 \ No newline at end of file diff --git a/main.go b/main.go index 9f0ba46..e1bbbf3 100644 --- a/main.go +++ b/main.go @@ -10,8 +10,14 @@ import ( ) func main() { - sr := sitereader.New(os.DirFS("build/test-site")) - sb, err := sitebuilder.New("build/out", sitebuilder.Options{ + sr := sitereader.New(os.DirFS("_test-site")) + + site, err := sr.ReadSite() + if err != nil { + log.Fatal(err) + } + + sb, err := sitebuilder.New(site, sitebuilder.Options{ BasePosts: "/posts", TemplatesFS: site_templates.FS, }) @@ -19,12 +25,7 @@ func main() { log.Fatal(err) } - site, err := sr.ReadSite() - if err != nil { - log.Fatal(err) - } - - if err := sb.BuildSite(site); err != nil { + if err := sb.BuildSite("build/out"); err != nil { log.Fatal(err) } diff --git a/models/sites.go b/models/sites.go index 0f7e1ef..fb72648 100644 --- a/models/sites.go +++ b/models/sites.go @@ -8,9 +8,9 @@ type Site struct { } type SiteMeta struct { - Title string - Tagline string - BaseURL string + Title string `yaml:"title"` + Tagline string `yaml:"tagline"` + BaseURL string `yaml:"base_url"` } type PostMeta struct { diff --git a/providers/sitebuilder/builder.go b/providers/sitebuilder/builder.go index 4836398..a76401e 100644 --- a/providers/sitebuilder/builder.go +++ b/providers/sitebuilder/builder.go @@ -43,7 +43,6 @@ func New(site models.Site, opts Options) (*Builder, error) { parser.WithAutoHeadingID(), ), goldmark.WithRendererOptions( - html.WithHardWraps(), html.WithUnsafe(), ), ), @@ -79,7 +78,9 @@ func (b *Builder) renderPostList(ctx buildContext, postList []*models.Post) erro return postCopy[i].Meta.Date.After(postCopy[j].Meta.Date) }) - pl := postListData{} + pl := postListData{ + commonData: commonData{Site: b.site.Meta}, + } for _, post := range postCopy { rp, err := b.renderPost(post) if err != nil { @@ -105,9 +106,10 @@ func (b *Builder) renderPost(post *models.Post) (postSingleData, error) { } return postSingleData{ - Path: postPath, - Meta: post.Meta, - HTML: template.HTML(md.String()), + commonData: commonData{Site: b.site.Meta}, + Path: postPath, + Meta: post.Meta, + HTML: template.HTML(md.String()), }, nil } @@ -150,7 +152,8 @@ func (b *Builder) renderTemplate(w io.Writer, name string, data interface{}) err } return b.tmpls.ExecuteTemplate(w, tmplNameLayoutMain, layoutData{ - Body: template.HTML(buf.String()), + commonData: commonData{Site: b.site.Meta}, + Body: template.HTML(buf.String()), }) } diff --git a/providers/sitebuilder/tmpls.go b/providers/sitebuilder/tmpls.go index a425d7e..9fe2507 100644 --- a/providers/sitebuilder/tmpls.go +++ b/providers/sitebuilder/tmpls.go @@ -33,16 +33,23 @@ type Options struct { RenderTZ *time.Location } +type commonData struct { + Site models.SiteMeta +} + type postSingleData struct { + commonData Meta models.PostMeta HTML template.HTML Path string } type postListData struct { + commonData Posts []postSingleData } type layoutData struct { + commonData Body template.HTML } diff --git a/providers/sitereader/provider.go b/providers/sitereader/provider.go index 9e12d8b..c51e886 100644 --- a/providers/sitereader/provider.go +++ b/providers/sitereader/provider.go @@ -19,13 +19,23 @@ func New(fs fs.FS) *Provider { } } -func (p *Provider) ReadSite() (models.Sites, error) { +func (p *Provider) ReadSite() (models.Site, error) { posts, err := p.ListPosts() if err != nil { - return models.Sites{}, err + return models.Site{}, err } - return models.Sites{ + meta := models.SiteMeta{} + metaBytes, err := fs.ReadFile(p.fs, "site.yaml") + if err != nil { + return models.Site{}, err + } + if err := yaml.Unmarshal(metaBytes, &meta); err != nil { + return models.Site{}, err + } + + return models.Site{ + Meta: meta, Posts: posts, }, nil } @@ -60,7 +70,7 @@ func (p *Provider) ReadPost(path string) (*models.Post, error) { return nil, io.ErrUnexpectedEOF } - var meta models.Meta + var meta models.PostMeta if err := yaml.Unmarshal(parts[1], &meta); err != nil { return nil, err } diff --git a/providers/sitereader/provider_test.go b/providers/sitereader/provider_test.go index 9c48dfe..0986b9a 100644 --- a/providers/sitereader/provider_test.go +++ b/providers/sitereader/provider_test.go @@ -13,6 +13,7 @@ import ( func TestProvider_ReadPost(t *testing.T) { t.Run("with meta", func(t *testing.T) { testFS := fstest.MapFS{ + "site.yaml": {Data: []byte(`base_url: https://example.com`)}, "posts/test.md": {Data: []byte(`--- date: 2026-02-18T19:59:00Z title: Test Post Here @@ -44,7 +45,7 @@ This is just a test post. post, err := pr.ReadPost("posts/test.md") assert.NoError(t, err) - assert.Equal(t, models.Meta{}, post.Meta) + assert.Equal(t, models.PostMeta{}, post.Meta) assert.Equal(t, "This is just a test post.\n", post.Content) }) } diff --git a/site_templates/layout_main.html b/site_templates/layout_main.html index cc5f93f..5c4203e 100644 --- a/site_templates/layout_main.html +++ b/site_templates/layout_main.html @@ -3,19 +3,21 @@
-Welcome to my website!
+{{ .Site.Tagline }}