Fixed some bugs for publishing a first site
This commit is contained in:
parent
77d3ff4852
commit
3591e0c723
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -1 +1,3 @@
|
|||
build/
|
||||
build/
|
||||
# Local Netlify folder
|
||||
.netlify
|
||||
|
|
|
|||
22
_test-site/posts/2026/02/18-first-post.md
Normal file
22
_test-site/posts/2026/02/18-first-post.md
Normal file
|
|
@ -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.
|
||||
6
_test-site/site.yaml
Normal file
6
_test-site/site.yaml
Normal file
|
|
@ -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
|
||||
17
main.go
17
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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,19 +3,21 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>My New Website</title>
|
||||
<title>{{ .Site.Title }}</title>
|
||||
<link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Hello, world</h1>
|
||||
<p>Welcome to my website!</p>
|
||||
<h1>{{ .Site.Title }}</h1>
|
||||
<p>{{ .Site.Tagline }}</p>
|
||||
</header>
|
||||
|
||||
{{ .Body }}
|
||||
<main>
|
||||
{{ .Body }}
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>Test stuff</p>
|
||||
<p>This site under construction.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
{{ range .Posts }}
|
||||
{{ if .Meta.Title }}<h3>{{ .Meta.Title }}</h3>{{ end }}
|
||||
{{ .HTML }}
|
||||
<a href="{{ .Path }}">{{ format_date .Meta.Date }}</a>
|
||||
<a href="{{ url_abs .Path }}">{{ format_date .Meta.Date }}</a>
|
||||
{{ end }}
|
||||
|
|
@ -1,2 +1,3 @@
|
|||
{{ if .Meta.Title }}<h3>{{ .Meta.Title }}</h3>{{ end }}
|
||||
{{ .HTML }}
|
||||
<a href="{{ .Path }}">{{ format_date .Meta.Date }}</a>
|
||||
<a href="{{ url_abs .Path }}">{{ format_date .Meta.Date }}</a>
|
||||
Loading…
Reference in a new issue