2021-12-22 13:14:34 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
2023-04-03 11:16:02 +00:00
|
|
|
cp "github.com/otiai10/copy"
|
2021-12-22 13:14:34 +00:00
|
|
|
"os"
|
2023-02-04 01:39:51 +00:00
|
|
|
"path/filepath"
|
2021-12-25 14:32:20 +00:00
|
|
|
"strings"
|
2021-12-22 13:14:34 +00:00
|
|
|
"text/template"
|
|
|
|
|
|
|
|
"github.com/PuerkitoBio/goquery"
|
2022-08-30 21:52:53 +00:00
|
|
|
"github.com/avelino/awesome-go/pkg/slug"
|
2021-12-22 13:14:34 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Link struct {
|
|
|
|
Title string
|
|
|
|
Url string
|
|
|
|
Description string
|
|
|
|
}
|
|
|
|
|
|
|
|
type Object struct {
|
|
|
|
Title string
|
|
|
|
Slug string
|
|
|
|
Description string
|
|
|
|
Items []Link
|
|
|
|
}
|
|
|
|
|
2023-02-04 01:39:51 +00:00
|
|
|
// Source
|
|
|
|
const readmePath = "README.md"
|
2023-04-03 11:16:02 +00:00
|
|
|
const assetsDir = "tmpl/assets"
|
2023-02-04 01:39:51 +00:00
|
|
|
|
|
|
|
// Templates
|
2023-04-03 11:16:02 +00:00
|
|
|
const tplPath = "tmpl/tmpl.html"
|
2023-02-04 01:39:51 +00:00
|
|
|
const tmplCategory = "tmpl/cat-tmpl.html"
|
|
|
|
const tmplSitemap = "tmpl/sitemap-tmpl.xml"
|
|
|
|
|
|
|
|
// Output
|
2023-04-03 11:16:02 +00:00
|
|
|
|
|
|
|
// NOTE: trailing slash is required
|
2023-02-04 01:39:51 +00:00
|
|
|
const outDir = "out/"
|
2023-04-03 11:16:02 +00:00
|
|
|
|
|
|
|
var outAssetsDir = filepath.Join(outDir, "assets")
|
|
|
|
var outIndexFile = filepath.Join(outDir, "index.html")
|
|
|
|
var outSitemapFile = filepath.Join(outDir, "sitemap.xml")
|
2023-02-04 01:39:51 +00:00
|
|
|
|
2021-12-22 13:14:34 +00:00
|
|
|
func main() {
|
2023-02-04 02:01:22 +00:00
|
|
|
// Cleanup and re-create output directory
|
|
|
|
{
|
|
|
|
if err := os.RemoveAll(outDir); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := mkdirAll(outDir); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err := GenerateHTML(readmePath, outIndexFile)
|
2022-08-30 13:20:58 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2023-02-04 01:39:51 +00:00
|
|
|
|
2023-04-03 11:16:02 +00:00
|
|
|
input, err := os.ReadFile(outIndexFile)
|
2021-12-22 13:14:34 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2023-02-04 01:39:51 +00:00
|
|
|
|
2021-12-22 13:14:34 +00:00
|
|
|
buf := bytes.NewBuffer(input)
|
|
|
|
query, err := goquery.NewDocumentFromReader(buf)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
2022-08-30 13:20:58 +00:00
|
|
|
objs := make(map[string]*Object)
|
2022-08-30 14:09:19 +00:00
|
|
|
query.Find("body #contents").NextFiltered("ul").Find("ul").Each(func(_ int, s *goquery.Selection) {
|
2021-12-22 13:14:34 +00:00
|
|
|
s.Find("li a").Each(func(_ int, s *goquery.Selection) {
|
2022-08-30 13:20:58 +00:00
|
|
|
selector, exists := s.Attr("href")
|
|
|
|
if !exists {
|
|
|
|
return
|
|
|
|
}
|
2022-08-30 17:26:14 +00:00
|
|
|
obj := makeObjByID(selector, query.Find("body"))
|
2022-08-30 13:20:58 +00:00
|
|
|
if obj == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
objs[selector] = obj
|
2021-12-22 13:14:34 +00:00
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2023-02-04 01:39:51 +00:00
|
|
|
if err := makeSiteStruct(objs); err != nil {
|
|
|
|
// FIXME: remove all panics
|
|
|
|
panic(err)
|
|
|
|
}
|
2022-08-30 14:14:48 +00:00
|
|
|
changeLinksInIndex(string(input), query, objs)
|
2022-08-30 13:20:58 +00:00
|
|
|
|
2021-12-22 13:14:34 +00:00
|
|
|
makeSitemap(objs)
|
2023-04-03 11:16:02 +00:00
|
|
|
|
|
|
|
if err := cp.Copy(assetsDir, outAssetsDir); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2021-12-22 13:14:34 +00:00
|
|
|
}
|
|
|
|
|
2023-02-04 01:39:51 +00:00
|
|
|
func mkdirAll(path string) error {
|
|
|
|
_, err := os.Stat(path)
|
|
|
|
// NOTE: directory is exists
|
|
|
|
if err == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: unknown error
|
|
|
|
if !os.IsNotExist(err) {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: directory is not exists
|
|
|
|
if err := os.MkdirAll(path, 0o755); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func makeSiteStruct(objs map[string]*Object) error {
|
2021-12-22 13:14:34 +00:00
|
|
|
for _, obj := range objs {
|
2023-04-03 11:16:02 +00:00
|
|
|
categoryDir := filepath.Join(outDir, obj.Slug)
|
|
|
|
if err := mkdirAll(categoryDir); err != nil {
|
2023-02-04 01:39:51 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: embed templates
|
|
|
|
// FIXME: parse templates once at start
|
|
|
|
t := template.Must(template.ParseFiles(tmplCategory))
|
2023-04-03 11:16:02 +00:00
|
|
|
f, err := os.Create(filepath.Join(categoryDir, "index.html"))
|
2021-12-22 13:14:34 +00:00
|
|
|
if err != nil {
|
2023-02-04 01:39:51 +00:00
|
|
|
return err
|
2021-12-22 13:14:34 +00:00
|
|
|
}
|
|
|
|
|
2023-02-04 01:39:51 +00:00
|
|
|
if err := t.Execute(f, obj); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-12-22 13:14:34 +00:00
|
|
|
}
|
2023-02-04 01:39:51 +00:00
|
|
|
|
|
|
|
return nil
|
2021-12-22 13:14:34 +00:00
|
|
|
}
|
|
|
|
|
2022-08-30 13:20:58 +00:00
|
|
|
func makeSitemap(objs map[string]*Object) {
|
2023-02-04 01:39:51 +00:00
|
|
|
t := template.Must(template.ParseFiles(tmplSitemap))
|
2023-04-03 11:16:02 +00:00
|
|
|
f, _ := os.Create(outSitemapFile)
|
2021-12-22 13:14:34 +00:00
|
|
|
t.Execute(f, objs)
|
|
|
|
}
|
|
|
|
|
2022-08-30 17:26:14 +00:00
|
|
|
func makeObjByID(selector string, s *goquery.Selection) (obj *Object) {
|
2021-12-22 13:14:34 +00:00
|
|
|
s.Find(selector).Each(func(_ int, s *goquery.Selection) {
|
|
|
|
desc := s.NextFiltered("p")
|
2022-08-30 13:20:58 +00:00
|
|
|
ul := s.NextFilteredUntil("ul", "h2")
|
2021-12-22 13:14:34 +00:00
|
|
|
|
|
|
|
links := []Link{}
|
|
|
|
ul.Find("li").Each(func(_ int, s *goquery.Selection) {
|
|
|
|
url, _ := s.Find("a").Attr("href")
|
|
|
|
link := Link{
|
|
|
|
Title: s.Find("a").Text(),
|
|
|
|
Description: s.Text(),
|
|
|
|
Url: url,
|
|
|
|
}
|
|
|
|
links = append(links, link)
|
|
|
|
})
|
2022-08-30 14:21:44 +00:00
|
|
|
if len(links) == 0 {
|
|
|
|
return
|
|
|
|
}
|
2022-08-30 13:20:58 +00:00
|
|
|
obj = &Object{
|
2022-08-30 21:52:53 +00:00
|
|
|
Slug: slug.Generate(s.Text()),
|
2021-12-22 13:14:34 +00:00
|
|
|
Title: s.Text(),
|
|
|
|
Description: desc.Text(),
|
|
|
|
Items: links,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
return
|
|
|
|
}
|
2021-12-25 14:32:20 +00:00
|
|
|
|
2022-08-30 14:14:48 +00:00
|
|
|
func changeLinksInIndex(html string, query *goquery.Document, objs map[string]*Object) {
|
2021-12-25 14:32:20 +00:00
|
|
|
query.Find("body #content ul li ul li a").Each(func(_ int, s *goquery.Selection) {
|
2022-08-30 13:20:58 +00:00
|
|
|
href, hrefExists := s.Attr("href")
|
|
|
|
if !hrefExists {
|
|
|
|
return
|
|
|
|
}
|
2021-12-25 14:32:20 +00:00
|
|
|
|
2022-08-30 13:20:58 +00:00
|
|
|
// do not replace links if no page has been created for it
|
|
|
|
_, objExists := objs[href]
|
|
|
|
if !objExists {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
uri := strings.SplitAfter(href, "#")
|
|
|
|
if len(uri) >= 2 && uri[1] != "contents" {
|
|
|
|
html = strings.ReplaceAll(
|
|
|
|
html, fmt.Sprintf(`href="%s"`, href), fmt.Sprintf(`href="%s"`, uri[1]))
|
2021-12-25 14:32:20 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2023-04-03 11:16:02 +00:00
|
|
|
os.WriteFile(outIndexFile, []byte(html), 0644)
|
2021-12-25 14:32:20 +00:00
|
|
|
}
|