diff --git a/.gitignore b/.gitignore index 89f9ac04aac6c8ee66e158853e7d0439b3ec782d..c6aa2a4851f3a5271fdf9b86ce78e6e8ce1cbcef 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ -out/ +/blog* +!blog.go +!blog_test.go + +_example/out diff --git a/_example/README.md b/_example/README.md new file mode 100644 index 0000000000000000000000000000000000000000..9a7224f619191edd2bcdf5d9f5acc90976f2e60d --- /dev/null +++ b/_example/README.md @@ -0,0 +1,13 @@ +# Example blog structure + +The `articles` dir contains a list of articles, namely this one. + +The `templates` dir contains a handful of [go templates](https://pkg.go.dev/html/template) that make up how to render the blog. +- `article.tmpl` is a template for an individual article (this page). +- `index.tmpl` is the main blog overview. +- `base.tmpl` is a collection of [defined blocks](https://pkg.go.dev/text/template#hdr-Nested_template_definitions) +that are then re-used in `index.tmpl` and `article.tmpl` to create a cohesive look-and-feel. + +Finally, `config.yaml` (which can also be TOML if desired) allows you to not have to continuously re-type all the flags to define things like your +`--article-dir`, `--template-dir`, or `--out`. + diff --git a/_example/config.yaml b/_example/config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..914646cc3ba1a16edd9b65861c517ff51634acc7 --- /dev/null +++ b/_example/config.yaml @@ -0,0 +1,9 @@ +article-dir: articles +template-dir: templates +out-dir: out +author: + name: jolheiser + email: john+blog@jolheiser.com + links: + - name: GitHub + url: "https://github.com/jolheiser" diff --git a/_example/templates/article.tmpl b/_example/templates/article.tmpl new file mode 100644 index 0000000000000000000000000000000000000000..db4e9ecb5eab85d4a3d0f5bbc553b3b5f6659c02 --- /dev/null +++ b/_example/templates/article.tmpl @@ -0,0 +1,8 @@ +{{template "head" .Article.Title}} +
+I really like to hack on Gitea and I use Catppuccin (btw)
+Beep boop
+` + ) + + article, err := parseArticle(raw) + assert.NoError(t, err) + assert.Equal(t, meta, article.ArticleMeta) + assert.Equal(t, body, article.Content) +} diff --git a/cmd/blog/main.go b/cmd/blog/main.go new file mode 100644 index 0000000000000000000000000000000000000000..d5d835e2eab4153e10fc157dd07aec9cbb7507d4 --- /dev/null +++ b/cmd/blog/main.go @@ -0,0 +1,116 @@ +package main + +import ( + "flag" + "fmt" + "os" + "path/filepath" + + "github.com/pelletier/go-toml/v2" + "go.jolheiser.com/blog" + "gopkg.in/yaml.v3" +) + +type args struct { + ArticleDir string `yaml:"article-dir" toml:"article-dir"` + TemplateDir string `yaml:"template-dir" toml:"template-dir"` + OutDir string `yaml:"out-dir" toml:"out-dir"` + Author blog.Author `yaml:"author" toml:"author"` +} + +func main() { + if err := maine(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} + +func maine() error { + var args args + + fs := flag.NewFlagSet("blog", flag.ExitOnError) + + configFlag := fs.String("config", "", "Configuration, TOML or YAML") + fs.StringVar(configFlag, "c", *configFlag, "--config") + articlesFlag := fs.String("articles", "", "Path to articles") + fs.StringVar(articlesFlag, "a", *articlesFlag, "--articles") + templatesFlag := fs.String("templates", "", "Path to templates") + fs.StringVar(templatesFlag, "t", *templatesFlag, "--templates") + outFlag := fs.String("out", "", "Path to output") + fs.StringVar(outFlag, "o", *outFlag, "--out") + + if err := fs.Parse(os.Args[1:]); err != nil { + return err + } + + if *configFlag != "" { + data, err := os.ReadFile(*configFlag) + if err != nil { + return err + } + switch ext := filepath.Ext(*configFlag); ext { + case ".yaml", ".yml": + if err := yaml.Unmarshal(data, &args); err != nil { + return err + } + case ".toml": + if err := toml.Unmarshal(data, &args); err != nil { + return err + } + default: + return fmt.Errorf("could not determine config type %q", ext) + } + } + + if *articlesFlag != "" { + args.ArticleDir = *articlesFlag + } + if args.ArticleDir == "" { + args.ArticleDir = "articles" + } + if *templatesFlag != "" { + args.TemplateDir = *templatesFlag + } + if args.TemplateDir == "" { + args.TemplateDir = "templates" + } + if *outFlag != "" { + args.OutDir = *outFlag + } + if args.OutDir == "" { + args.OutDir = "out" + } + + if err := os.MkdirAll(args.OutDir, os.ModePerm); err != nil { + return err + } + + blog, err := blog.NewBlog(args.ArticleDir, args.TemplateDir, args.Author) + if err != nil { + return err + } + + indexFile, err := os.Create(filepath.Join(args.OutDir, "index.html")) + if err != nil { + return err + } + defer indexFile.Close() + if err := blog.Index(indexFile); err != nil { + return err + } + + for _, article := range blog.Articles { + if err := func() error { + articleFile, err := os.Create(filepath.Join(args.OutDir, article.Filename+".html")) + if err != nil { + return err + } + defer articleFile.Close() + return blog.Article(articleFile, article) + }(); err != nil { + return err + } + } + + return nil +} diff --git a/cmd/chromacss/main.go b/cmd/chromacss/main.go new file mode 100644 index 0000000000000000000000000000000000000000..e2b1eefe7aa3763e530c534812bee0f951a768b8 --- /dev/null +++ b/cmd/chromacss/main.go @@ -0,0 +1,72 @@ +package main + +import ( + "flag" + "fmt" + "os" + + "github.com/alecthomas/chroma/v2/formatters/html" + "github.com/alecthomas/chroma/v2/styles" + "go.jolheiser.com/blog" +) + +func main() { + if err := maine(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} + +func maine() error { + fs := flag.NewFlagSet("chromacss", flag.ExitOnError) + lightFlag := fs.String("light", "catppuccin-latte", "Light theme") + fs.StringVar(lightFlag, "l", *lightFlag, "--light") + darkFlag := fs.String("dark", "catppuccin-mocha", "Dark theme") + fs.StringVar(darkFlag, "d", *darkFlag, "--dark") + outFlag := fs.String("out", "", "Output (default: stdout)") + fs.StringVar(outFlag, "o", *outFlag, "--out") + if err := fs.Parse(os.Args[1:]); err != nil { + return err + } + + out := os.Stdout + if *outFlag != "" { + fi, err := os.Create(*outFlag) + if err != nil { + return err + } + defer fi.Close() + out = fi + } + + formatter := html.New(blog.ChromaOpts...) + + lightStyle := *lightFlag + darkStyle := *darkFlag + + if lightStyle == "" { + lightStyle = "catppuccin-latte" + if darkStyle != "" { + lightStyle = darkStyle + darkStyle = "" + } + } + + styles.Fallback = styles.Get("catppuccin-latte") + style := styles.Get(lightStyle) + if err := formatter.WriteCSS(out, style); err != nil { + return err + } + + if darkStyle != "" { + out.WriteString("@media (prefers-color-scheme: dark) {") + styles.Fallback = styles.Get("catpuccin-mocha") + style = styles.Get(darkStyle) + if err := formatter.WriteCSS(out, style); err != nil { + return err + } + out.WriteString("}") + } + + return nil +} diff --git a/go.mod b/go.mod index 3d226dae1f71f357af8b8c8cbd8f38889bcba113..8c1aa017c7924a7b8ed3b4ceab8cde26ece9de51 100644 --- a/go.mod +++ b/go.mod @@ -3,22 +3,32 @@ go 1.21.6 require ( - github.com/BurntSushi/toml v1.3.2 - github.com/a-h/templ v0.2.543 +module go.jolheiser.com/blog github.com/alecthomas/chroma/v2 v2.12.0 +module go.jolheiser.com/blog github.com/yuin/goldmark v1.7.0 +module go.jolheiser.com/blog github.com/yuin/goldmark-emoji v1.0.2 -module go.jolheiser.com/blog + + module go.jolheiser.com/blog + github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect module go.jolheiser.com/blog +go 1.21.6 + github.com/gorilla/feeds v1.1.2 // indirect + github.com/jolheiser/goldmark-meta v0.0.2 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect -module go.jolheiser.com/blog + github.com/a-h/templ v0.2.543 + github.com/alecthomas/chroma/v2 v2.12.0 -require ( + github.com/sergi/go-diff v1.2.0 // indirect -module go.jolheiser.com/blog + github.com/yuin/goldmark v1.7.0 // indirect go 1.21.6 +go 1.21.6 module go.jolheiser.com/blog -require ( - gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect + github.com/yuin/goldmark-meta v1.1.0 // indirect + golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index c4b1bc6fb037c92e5fe68ac1efae39f51b36f823..62a81853e4095cb8c64512a1f9d8ff9e25a8fc83 100644 --- a/go.sum +++ b/go.sum @@ -1,46 +1,63 @@ github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/a-h/templ v0.2.543 h1:8YyLvyUtf0/IE2nIwZ62Z/m2o2NqwhnMynzOL78Lzbk= -github.com/a-h/templ v0.2.543/go.mod h1:jP908DQCwI08IrnTalhzSEH9WJqG/Q94+EODQcJGFUA= +github.com/alecthomas/assert v1.0.0 h1:3XmGh/PSuLzDbK3W2gUbRXwgW5lqPkuqvRgeQ30FI5o= github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= -github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= +github.com/alecthomas/chroma/v2 v2.2.0 h1:Aten8jfQwUqEdadVFFjNyjx7HTexhKP0XuqBG67mRDY= github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs= github.com/alecthomas/chroma/v2 v2.12.0 h1:Wh8qLEgMMsN7mgyG8/qIpegky2Hvzr4By6gEF7cmWgw= github.com/alecthomas/chroma/v2 v2.12.0/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw= +github.com/alecthomas/colour v0.1.0 h1:nOE9rJm6dsZ66RGWYSFrXw461ZIt9A6+nHgL7FRrDUk= +github.com/alecthomas/colour v0.1.0/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0= github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I= +github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo= github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/gorilla/feeds v1.1.2 h1:pxzZ5PD3RJdhFH2FsJJ4x6PqMqbgFk1+Vez4XWBW8Iw= +github.com/gorilla/feeds v1.1.2/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y= +github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= github.com/a-h/templ v0.2.543 h1:8YyLvyUtf0/IE2nIwZ62Z/m2o2NqwhnMynzOL78Lzbk= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= github.com/a-h/templ v0.2.543/go.mod h1:jP908DQCwI08IrnTalhzSEH9WJqG/Q94+EODQcJGFUA= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= github.com/alecthomas/chroma/v2 v2.12.0 h1:Wh8qLEgMMsN7mgyG8/qIpegky2Hvzr4By6gEF7cmWgw= +github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/peterbourgon/ff/v3 v3.4.0 h1:QBvM/rizZM1cB0p0lGMdmR7HxZeI/ZrBWB4DqLkMUBc= +github.com/peterbourgon/ff/v3 v3.4.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs= github.com/a-h/templ v0.2.543 h1:8YyLvyUtf0/IE2nIwZ62Z/m2o2NqwhnMynzOL78Lzbk= -github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/yuin/goldmark v1.3.7 h1:NSaHgaeJFCtWXCBkBKXw0rhgMuJ0VoE9FB5mWldcrQ4= github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.15 h1:CFa84T0goNn/UIXYS+dmjjVxMyTAvpOmzld40N/nfK0= github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA= github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= @@ -50,12 +67,18 @@ github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ= github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I= github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc= github.com/a-h/templ v0.2.543/go.mod h1:jP908DQCwI08IrnTalhzSEH9WJqG/Q94+EODQcJGFUA= +github.com/a-h/templ v0.2.543 h1:8YyLvyUtf0/IE2nIwZ62Z/m2o2NqwhnMynzOL78Lzbk= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= +github.com/alecthomas/chroma/v2 v2.12.0 h1:Wh8qLEgMMsN7mgyG8/qIpegky2Hvzr4By6gEF7cmWgw= github.com/a-h/templ v0.2.543 h1:8YyLvyUtf0/IE2nIwZ62Z/m2o2NqwhnMynzOL78Lzbk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= github.com/a-h/templ v0.2.543/go.mod h1:jP908DQCwI08IrnTalhzSEH9WJqG/Q94+EODQcJGFUA= -github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink= +github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= +github.com/alecthomas/chroma/v2 v2.12.0 h1:Wh8qLEgMMsN7mgyG8/qIpegky2Hvzr4By6gEF7cmWgw= github.com/a-h/templ v0.2.543/go.mod h1:jP908DQCwI08IrnTalhzSEH9WJqG/Q94+EODQcJGFUA= -github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/goldmark.go b/goldmark.go new file mode 100644 index 0000000000000000000000000000000000000000..195e16337b248d03814a8d54db027cad16c6ec19 --- /dev/null +++ b/goldmark.go @@ -0,0 +1,39 @@ +package blog + +import ( + chromahtml "github.com/alecthomas/chroma/v2/formatters/html" + "github.com/yuin/goldmark" + emoji "github.com/yuin/goldmark-emoji" + highlighting "github.com/yuin/goldmark-highlighting/v2" + meta "github.com/yuin/goldmark-meta" + "github.com/yuin/goldmark/extension" + "github.com/yuin/goldmark/parser" + "github.com/yuin/goldmark/renderer/html" +) + +// Markdown is the default markdown converter +// Set over this variable to alter how the other functions convert +var Markdown = goldmark.New( + goldmark.WithParserOptions( + parser.WithAutoHeadingID(), + ), + goldmark.WithRendererOptions( + html.WithUnsafe(), + ), + goldmark.WithExtensions( + extension.GFM, + meta.Meta, + emoji.Emoji, + highlighting.NewHighlighting( + highlighting.WithFormatOptions( + ChromaOpts..., + ), + ), + ), +) + +var ChromaOpts = []chromahtml.Option{ + chromahtml.WithClasses(true), + chromahtml.WithAllClasses(true), + chromahtml.WithLineNumbers(true), +} diff --git a/main.go b/main.go deleted file mode 100644 index f0c5c9ee09a29fe144c863df27be02a3a1bf7a3e..0000000000000000000000000000000000000000 --- a/main.go +++ /dev/null @@ -1,103 +0,0 @@ -//go:generate templ generate -//go:generate go run . -package main - -import ( - "context" - "embed" - "flag" - "fmt" - iofs "io/fs" - "os" - "path/filepath" - - "github.com/alecthomas/chroma/v2/styles" -) - -//go:embed articles/* -var articleFS embed.FS - -func maine() error { - fs := flag.NewFlagSet("blog", flag.ExitOnError) - outFlag := fs.String("out", "out", "Output directory") - fs.StringVar(outFlag, "o", *outFlag, "--out") - if err := fs.Parse(os.Args[1:]); err != nil { - return err - } - - files, err := articleFS.ReadDir("articles") - if err != nil { - return err - } - - articles := make(Articles) - for _, file := range files { - if filepath.Ext(file.Name()) != ".md" { - continue - } - content, err := iofs.ReadFile(articleFS, fmt.Sprintf("articles/%s", file.Name())) - if err != nil { - return err - } - article, err := Parse(string(content)) - if err != nil { - return err - } - articles[article.Category] = append(articles[article.Category], article) - if err := writeArticle(*outFlag, article); err != nil { - return err - } - } - - if err := writeCSS(*outFlag); err != nil { - return err - } - - fi, err := os.Create(filepath.Join(*outFlag, "index.html")) - if err != nil { - return err - } - defer fi.Close() - - return IndexTemplate(articles).Render(context.Background(), fi) -} - -func writeArticle(out string, article Article) error { - dest := filepath.Join(out, article.Slug(), "index.html") - if err := os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil { - return err - } - - fi, err := os.Create(dest) - if err != nil { - return err - } - defer fi.Close() - - return ArticleTemplate(article).Render(context.Background(), fi) -} - -func writeCSS(out string) error { - fi, err := os.Create(filepath.Join(out, "chroma.css")) - if err != nil { - return err - } - defer fi.Close() - - if err := CSS.WriteCSS(fi, styles.Get("catpuccin-latte")); err != nil { - return err - } - fi.WriteString("@media (prefers-color-scheme: dark) {") - if err := CSS.WriteCSS(fi, styles.Get("catppuccin-mocha")); err != nil { - return err - } - fi.WriteString("}") - return nil -} - -func main() { - if err := maine(); err != nil { - fmt.Println(err) - os.Exit(1) - } -} diff --git a/templates.go b/templates.go deleted file mode 100644 index 8af412b693c9a8150dd0e116a5d93f31a59570d1..0000000000000000000000000000000000000000 --- a/templates.go +++ /dev/null @@ -1,119 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - "strings" - "time" - - "github.com/BurntSushi/toml" - chromahtml "github.com/alecthomas/chroma/v2/formatters/html" - "github.com/yuin/goldmark" - emoji "github.com/yuin/goldmark-emoji" - highlighting "github.com/yuin/goldmark-highlighting/v2" - meta "github.com/yuin/goldmark-meta" - "github.com/yuin/goldmark/extension" - "github.com/yuin/goldmark/parser" - "github.com/yuin/goldmark/renderer/html" -) - -// Articles by Category -type Articles map[string][]Article - -type Article struct { - Content string - Meta -} - -func (a Article) Slug() string { - slug := strings.NewReplacer(" ", "-").Replace(strings.ToLower(a.Meta.Title)) - return fmt.Sprintf("%s/%s/", a.Meta.Date.Format("2006"), slug) -} - -type Meta struct { - Title string - Summary string - Date time.Time - Category string -} - -type Author struct { - Name string - Email string -} - -func Parse(content string) (Article, error) { - lines := strings.Split(content, "\n") - - start, end := -1, -1 - for idx, line := range lines { - if strings.TrimSpace(line) == "" { - continue - } - - if isTOMLSeparator(line) { - if start == -1 { - start = idx - continue - } - end = idx - break - } - } - - var meta Meta - body := content - if start != -1 && end != -1 { - body = strings.Join(lines[end+1:], "\n") - if err := toml.Unmarshal([]byte(strings.Join(lines[start+1:end], "\n")), &meta); err != nil { - return Article{}, fmt.Errorf("could not parse frontmatter: %w", err) - } - } - - var buf bytes.Buffer - err := Markdown.Convert([]byte(body), &buf) - if err != nil { - return Article{}, fmt.Errorf("could not convert article: %w", err) - } - - return Article{ - Content: buf.String(), - Meta: meta, - }, nil -} - -func isTOMLSeparator(line string) bool { - for _, char := range line { - if char != '+' { - return false - } - } - return true -} - -var ( - opts = []chromahtml.Option{ - chromahtml.WithClasses(true), - chromahtml.WithAllClasses(true), - chromahtml.WithLineNumbers(true), - } - Markdown = goldmark.New( - goldmark.WithParserOptions( - parser.WithAutoHeadingID(), - ), - goldmark.WithRendererOptions( - html.WithUnsafe(), - ), - goldmark.WithExtensions( - extension.GFM, - meta.Meta, - emoji.Emoji, - highlighting.NewHighlighting( - highlighting.WithFormatOptions( - opts..., - ), - ), - ), - ) - CSS = chromahtml.New(opts...) -) diff --git a/templates.templ b/templates.templ deleted file mode 100644 index daee1e81f364b2c7d571ffe38b7e3eb24d24def0..0000000000000000000000000000000000000000 --- a/templates.templ +++ /dev/null @@ -1,45 +0,0 @@ -package main - -templ baseTemplate(title, description string) { - - - - - -