package main import ( "bytes" "os" "path" "sort" "strings" "time" "github.com/yuin/goldmark" "github.com/yuin/goldmark/extension" "github.com/yuin/goldmark/parser" "github.com/yuin/goldmark/renderer/html" mathjax "github.com/litao91/goldmark-mathjax" "gopkg.in/yaml.v3" ) var md goldmark.Markdown func init() { md = goldmark.New( goldmark.WithExtensions( extension.GFM, extension.Typographer, // Questo pacchetto ha un nome stupido perché in realtà si occupa solo del parsing lato server del Markdown mentre lato client usiamo KaTeX. mathjax.NewMathJax(), ), goldmark.WithParserOptions( parser.WithAutoHeadingID(), ), goldmark.WithRendererOptions( html.WithHardWraps(), html.WithXHTML(), ), ) } type ArticleFrontMatter struct { Title string `yaml:"title"` Description string `yaml:"description"` Tags []string `yaml:"tags,flow"` PublishDate string `yaml:"publish_date"` Important bool `yaml:"important"` } type Article struct { Id string Title string Description string Tags []string PublishDate time.Time Important bool SourceMarkdown string OutputHTML string } type ArticleRegistry struct { Articles map[string]*Article } func removeBlanks(v []string) []string { r := []string{} for _, s := range v { if len(strings.TrimSpace(s)) > 0 { r = append(r, s) } } return r } func (ar *ArticleRegistry) LoadArticle(name string) (*Article, error) { fileBytes, err := os.ReadFile(path.Join("./news/", name+".md")) if err != nil { return nil, err } source := string(fileBytes) // TODO: Ehm bugia esiste "https://github.com/yuin/goldmark-meta" però boh per ora funge tutto parts := removeBlanks(strings.Split(source, "---")) frontMatterSource := parts[0] markdownSource := strings.Join(parts[1:], "---") var frontMatter ArticleFrontMatter if err := yaml.Unmarshal([]byte(frontMatterSource), &frontMatter); err != nil { return nil, err } publishDate, err := time.Parse("2006/01/02 15:04", frontMatter.PublishDate) if err != nil { return nil, err } var buf bytes.Buffer if err := md.Convert([]byte(markdownSource), &buf); err != nil { return nil, err } article := &Article{ Id: name, Title: frontMatter.Title, Description: frontMatter.Description, Tags: frontMatter.Tags, PublishDate: publishDate, Important: frontMatter.Important, SourceMarkdown: markdownSource, OutputHTML: buf.String(), } ar.Articles[name] = article return article, nil } func (ar *ArticleRegistry) LoadAll() ([]*Article, error) { entries, err := os.ReadDir("./news") if err != nil { return nil, err } articles := []*Article{} for _, entry := range entries { if !entry.IsDir() { name := strings.TrimRight(entry.Name(), ".md") article, err := ar.LoadArticle(name) if err != nil { return nil, err } articles = append(articles, article) } } sort.Slice(articles, func(i, j int) bool { return articles[i].PublishDate.After(articles[j].PublishDate) }) return articles, nil } func NewArticleRegistry() *ArticleRegistry { return &ArticleRegistry{ map[string]*Article{}, } } func (ar *ArticleRegistry) Render(name string) (string, error) { article, err := ar.LoadArticle(name) if err != nil { return "", err } return article.OutputHTML, nil }