// Command tilstream renders a folder of TIL markdown notes into a static site.
// See mercemay.top/src/tilstream/ for usage.
package main
import (
"flag"
"fmt"
"log"
"net/http"
"os"
"path/filepath"
"time"
"mercemay.top/src/tilstream/internal/feed"
"mercemay.top/src/tilstream/internal/index"
"mercemay.top/src/tilstream/internal/render"
"mercemay.top/src/tilstream/internal/watch"
)
func main() {
if len(os.Args) < 2 {
usage()
os.Exit(2)
}
switch os.Args[1] {
case "build":
runBuild(os.Args[2:])
case "serve":
runServe(os.Args[2:])
case "version":
fmt.Println("tilstream 0.6.1")
default:
usage()
os.Exit(2)
}
}
func usage() {
fmt.Fprintln(os.Stderr, "usage: tilstream <build|serve|version> [flags]")
}
type commonFlags struct {
src string
out string
drafts bool
template string
baseURL string
}
func registerCommon(fs *flag.FlagSet, c *commonFlags) {
fs.StringVar(&c.src, "src", ".", "source directory of markdown notes")
fs.StringVar(&c.out, "out", "public", "output directory")
fs.BoolVar(&c.drafts, "drafts", false, "include posts with draft: true")
fs.StringVar(&c.template, "template", "templates/post.tmpl", "post template")
fs.StringVar(&c.baseURL, "base-url", "https://example.com", "site base URL")
}
func runBuild(args []string) {
var c commonFlags
fs := flag.NewFlagSet("build", flag.ExitOnError)
registerCommon(fs, &c)
_ = fs.Parse(args)
if err := build(c); err != nil {
log.Fatalf("build: %v", err)
}
log.Printf("wrote %s", c.out)
}
func runServe(args []string) {
var c commonFlags
var port int
fs := flag.NewFlagSet("serve", flag.ExitOnError)
registerCommon(fs, &c)
fs.IntVar(&port, "port", 8080, "HTTP port")
_ = fs.Parse(args)
c.drafts = true // always preview drafts in serve mode
if err := build(c); err != nil {
log.Fatalf("initial build: %v", err)
}
go watch.Loop(c.src, 150*time.Millisecond, func() {
if err := build(c); err != nil {
log.Printf("rebuild: %v", err)
return
}
log.Println("rebuilt")
})
addr := fmt.Sprintf(":%d", port)
log.Printf("serving %s on http://localhost%s", c.out, addr)
log.Fatal(http.ListenAndServe(addr, http.FileServer(http.Dir(c.out))))
}
func build(c commonFlags) error {
if err := os.MkdirAll(c.out, 0o755); err != nil {
return err
}
posts, err := render.RenderTree(c.src, c.out, c.template, c.drafts)
if err != nil {
return err
}
if err := feed.Write(filepath.Join(c.out, "feed.xml"), posts, c.baseURL); err != nil {
return err
}
if err := index.Write(filepath.Join(c.out, "search.json"), posts); err != nil {
return err
}
return nil
}