2022-05-08 16:09:29 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2022-05-08 18:00:38 +00:00
|
|
|
_ "embed"
|
2022-05-08 16:09:29 +00:00
|
|
|
"flag"
|
2022-05-08 17:19:48 +00:00
|
|
|
"fmt"
|
2022-05-08 16:09:29 +00:00
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
|
|
|
"os"
|
2022-05-08 17:19:48 +00:00
|
|
|
"os/signal"
|
2022-05-08 16:09:29 +00:00
|
|
|
"path/filepath"
|
2022-05-08 18:00:38 +00:00
|
|
|
"strings"
|
2022-05-08 17:19:48 +00:00
|
|
|
"syscall"
|
2022-05-08 16:09:29 +00:00
|
|
|
|
2023-12-31 03:58:29 +00:00
|
|
|
"git.foosoft.net/alex/goldsmith"
|
|
|
|
"git.foosoft.net/alex/goldsmith-components/devserver"
|
|
|
|
"git.foosoft.net/alex/goldsmith-components/filters/operator"
|
|
|
|
"git.foosoft.net/alex/goldsmith-components/filters/wildcard"
|
|
|
|
"git.foosoft.net/alex/goldsmith-components/plugins/document"
|
|
|
|
"git.foosoft.net/alex/goldsmith-components/plugins/frontmatter"
|
|
|
|
"git.foosoft.net/alex/goldsmith-components/plugins/livejs"
|
|
|
|
"git.foosoft.net/alex/goldsmith-components/plugins/markdown"
|
2022-05-08 18:00:38 +00:00
|
|
|
"github.com/PuerkitoBio/goquery"
|
2022-05-08 17:19:48 +00:00
|
|
|
"github.com/toqueteos/webbrowser"
|
2022-05-08 16:09:29 +00:00
|
|
|
"github.com/yuin/goldmark"
|
|
|
|
"github.com/yuin/goldmark/extension"
|
|
|
|
"github.com/yuin/goldmark/parser"
|
|
|
|
"github.com/yuin/goldmark/renderer/html"
|
|
|
|
)
|
|
|
|
|
2022-05-08 18:13:42 +00:00
|
|
|
//go:embed css/github-markdown.css
|
2022-05-08 18:00:38 +00:00
|
|
|
var githubStyle string
|
|
|
|
|
2022-05-08 18:13:42 +00:00
|
|
|
//go:embed css/github-fixup.css
|
2022-05-08 18:00:38 +00:00
|
|
|
var githubFixup string
|
|
|
|
|
2022-05-08 17:19:48 +00:00
|
|
|
type builder struct {
|
2022-05-08 19:06:31 +00:00
|
|
|
port int
|
|
|
|
path string
|
|
|
|
open bool
|
2022-05-08 17:19:48 +00:00
|
|
|
}
|
2022-05-08 16:09:29 +00:00
|
|
|
|
2022-05-08 18:00:38 +00:00
|
|
|
func embedCss(file *goldsmith.File, doc *goquery.Document) error {
|
|
|
|
var styleBuilder strings.Builder
|
|
|
|
styleBuilder.WriteString("<style type=\"text/css\">\n")
|
|
|
|
styleBuilder.WriteString(githubStyle)
|
|
|
|
styleBuilder.WriteString(githubFixup)
|
|
|
|
styleBuilder.WriteString("</style>")
|
|
|
|
|
|
|
|
doc.Find("body").AddClass("markdown-body")
|
|
|
|
doc.Find("head").SetHtml(styleBuilder.String())
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-05-08 16:09:29 +00:00
|
|
|
func (self *builder) Build(contentDir, buildDir, cacheDir string) {
|
|
|
|
log.Print("building...")
|
|
|
|
|
|
|
|
gm := goldmark.New(
|
|
|
|
goldmark.WithExtensions(extension.GFM, extension.Typographer),
|
|
|
|
goldmark.WithParserOptions(parser.WithAutoHeadingID()),
|
|
|
|
goldmark.WithRendererOptions(html.WithUnsafe()),
|
|
|
|
)
|
|
|
|
|
2022-05-15 03:52:32 +00:00
|
|
|
allowedPaths := []string{
|
|
|
|
"**/*.gif",
|
|
|
|
"**/*.html",
|
|
|
|
"**/*.jpeg",
|
|
|
|
"**/*.jpg",
|
|
|
|
"**/*.md",
|
|
|
|
"**/*.png",
|
|
|
|
"**/*.svg",
|
|
|
|
}
|
|
|
|
|
|
|
|
forbiddenPaths := []string{
|
|
|
|
"**/.*/**",
|
|
|
|
}
|
|
|
|
|
2022-05-08 16:09:29 +00:00
|
|
|
errs := goldsmith.Begin(contentDir).
|
|
|
|
Clean(true).
|
2022-05-15 03:52:32 +00:00
|
|
|
FilterPush(wildcard.New(allowedPaths...)).
|
|
|
|
FilterPush(operator.Not(wildcard.New(forbiddenPaths...))).
|
2022-05-09 00:51:59 +00:00
|
|
|
Chain(frontmatter.New()).
|
2022-05-08 16:09:29 +00:00
|
|
|
Chain(markdown.NewWithGoldmark(gm)).
|
|
|
|
Chain(livejs.New()).
|
2022-05-08 18:00:38 +00:00
|
|
|
Chain(document.New(embedCss)).
|
2022-05-08 16:09:29 +00:00
|
|
|
End(buildDir)
|
|
|
|
|
|
|
|
for _, err := range errs {
|
|
|
|
log.Print(err)
|
|
|
|
}
|
2022-05-08 17:19:48 +00:00
|
|
|
|
2022-05-08 19:06:31 +00:00
|
|
|
if !self.open {
|
2022-05-08 23:59:37 +00:00
|
|
|
url := fmt.Sprintf("http://127.0.0.1:%d/%s", self.port, self.path)
|
|
|
|
log.Printf("opening %s in browser...", url)
|
|
|
|
webbrowser.Open(url)
|
2022-05-08 19:06:31 +00:00
|
|
|
self.open = true
|
2022-05-08 17:19:48 +00:00
|
|
|
}
|
2022-05-08 16:09:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
2022-05-08 23:59:37 +00:00
|
|
|
flag.Usage = func() {
|
|
|
|
fmt.Fprintf(os.Stderr, "Usage %s [options] [path]\n\n", filepath.Base(os.Args[0]))
|
|
|
|
fmt.Fprintln(os.Stderr, "Parameters:")
|
|
|
|
flag.PrintDefaults()
|
|
|
|
}
|
|
|
|
|
2022-05-08 16:09:29 +00:00
|
|
|
port := flag.Int("port", 8080, "port")
|
|
|
|
flag.Parse()
|
|
|
|
|
|
|
|
if flag.NArg() != 1 {
|
2022-05-08 23:59:37 +00:00
|
|
|
flag.Usage()
|
|
|
|
os.Exit(2)
|
2022-05-08 16:09:29 +00:00
|
|
|
}
|
|
|
|
|
2022-05-08 23:59:37 +00:00
|
|
|
path := flag.Arg(0)
|
|
|
|
info, err := os.Stat(path)
|
2022-05-08 16:09:29 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2022-05-08 19:06:31 +00:00
|
|
|
var contentName string
|
2022-05-08 23:59:37 +00:00
|
|
|
contentDir := path
|
2022-05-08 16:09:29 +00:00
|
|
|
if !info.IsDir() {
|
2022-05-08 23:59:37 +00:00
|
|
|
contentName = filepath.Base(path)
|
2022-05-08 19:06:31 +00:00
|
|
|
contentExt := filepath.Ext(contentName)
|
|
|
|
switch contentExt {
|
|
|
|
case ".md", ".markdown":
|
|
|
|
contentName = strings.TrimSuffix(contentName, contentExt)
|
|
|
|
contentName += ".html"
|
|
|
|
}
|
|
|
|
|
2022-05-08 23:59:37 +00:00
|
|
|
contentDir = filepath.Dir(path)
|
2022-05-08 16:09:29 +00:00
|
|
|
}
|
|
|
|
|
2022-05-08 18:00:38 +00:00
|
|
|
buildDir, err := ioutil.TempDir("", "mvd-*")
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
log.Println("cleaning up...")
|
|
|
|
if err := os.RemoveAll(buildDir); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2022-05-08 17:19:48 +00:00
|
|
|
go func() {
|
2022-05-08 19:06:31 +00:00
|
|
|
b := &builder{port: *port, path: contentName}
|
2022-05-08 17:19:48 +00:00
|
|
|
devserver.DevServe(b, *port, contentDir, buildDir, "")
|
|
|
|
}()
|
|
|
|
|
|
|
|
sigs := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
|
|
|
|
2022-05-08 18:00:38 +00:00
|
|
|
<-sigs
|
2022-05-08 16:09:29 +00:00
|
|
|
}
|