From 9db9b8025c4789b0fec9cf4e2d7a9cf249301890 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Thu, 4 Apr 2024 20:50:14 -0700 Subject: [PATCH] WIP --- main.go | 186 +++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 117 insertions(+), 69 deletions(-) diff --git a/main.go b/main.go index 2183de6..2835683 100644 --- a/main.go +++ b/main.go @@ -2,17 +2,18 @@ package main import ( _ "embed" + "errors" "flag" "fmt" "log" "os" "os/signal" + "path" "path/filepath" "strings" "syscall" "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" @@ -20,7 +21,7 @@ import ( "git.foosoft.net/alex/goldsmith-components/plugins/livejs" "git.foosoft.net/alex/goldsmith-components/plugins/markdown" "github.com/PuerkitoBio/goquery" - "github.com/toqueteos/webbrowser" + "github.com/fsnotify/fsnotify" "github.com/yuin/goldmark" "github.com/yuin/goldmark/extension" "github.com/yuin/goldmark/parser" @@ -33,34 +34,85 @@ var githubStyle string //go:embed css/github-fixup.css var githubFixup string -type builder struct { - port int - path string - open bool -} +func watch(dir string, watcher *fsnotify.Watcher) error { + watcher.Add(dir) -func embedCss(file *goldsmith.File, doc *goquery.Document) error { - var styleBuilder strings.Builder - styleBuilder.WriteString("") + items, err := os.ReadDir(dir) + if err != nil { + return err + } - doc.Find("body").AddClass("markdown-body") - doc.Find("head").SetHtml(styleBuilder.String()) + for _, item := range items { + fullPath := path.Join(dir, item.Name()) + if item.IsDir() { + watch(fullPath, watcher) + } else { + watcher.Add(fullPath) + } + } return nil } -func (self *builder) Build(contentDir, buildDir, cacheDir string) { - log.Print("building...") +func builder(dir string, callback func()) error { + watcher, err := fsnotify.NewWatcher() + if err != nil { + return err + } - gm := goldmark.New( - goldmark.WithExtensions(extension.GFM, extension.Typographer), - goldmark.WithParserOptions(parser.WithAutoHeadingID()), - goldmark.WithRendererOptions(html.WithUnsafe()), + if err := watch(dir, watcher); err != nil { + return err + } + + var terminate bool + signaler := make(chan os.Signal, 1) + signal.Notify( + signaler, + os.Interrupt, + syscall.SIGINT, + syscall.SIGTERM, ) + callback() + + for !terminate { + select { + case event := <-watcher.Events: + callback() + if event.Op&fsnotify.Create == fsnotify.Create { + if info, _ := os.Stat(event.Name); info != nil { + if info.IsDir() { + watch(event.Name, watcher) + } else { + watcher.Add(event.Name) + } + } + } + case <-signaler: + log.Println("terminating...") + terminate = true + } + } + + return nil +} + +func build(sourceDir, targetDir string) { + log.Println("building...") + + embedCss := func(file *goldsmith.File, doc *goquery.Document) error { + var styleBuilder strings.Builder + styleBuilder.WriteString("") + + doc.Find("body").AddClass("markdown-body") + doc.Find("head").SetHtml(styleBuilder.String()) + + return nil + } + allowedPaths := []string{ "**/*.gif", "**/*.html", @@ -75,8 +127,14 @@ func (self *builder) Build(contentDir, buildDir, cacheDir string) { "**/.*/**", } + gm := goldmark.New( + goldmark.WithExtensions(extension.GFM, extension.Typographer), + goldmark.WithParserOptions(parser.WithAutoHeadingID()), + goldmark.WithRendererOptions(html.WithUnsafe()), + ) + var gs goldsmith.Goldsmith - errs := gs.Begin(contentDir). + errs := gs.Begin(sourceDir). Clean(true). FilterPush(wildcard.New(allowedPaths...)). FilterPush(operator.Not(wildcard.New(forbiddenPaths...))). @@ -84,28 +142,55 @@ func (self *builder) Build(contentDir, buildDir, cacheDir string) { Chain(markdown.NewWithGoldmark(gm)). Chain(livejs.New()). Chain(document.New(embedCss)). - End(buildDir) + End(targetDir) for _, err := range errs { - log.Print(err) + log.Println(err) + } +} + +func run(path string) error { + switch strings.ToLower(filepath.Ext(path)) { + case ".md", ".markdown": + break + default: + return errors.New("unexpected file type") } - if !self.open { - url := fmt.Sprintf("http://127.0.0.1:%d/%s", self.port, self.path) - log.Printf("opening %s in browser...", url) - webbrowser.Open(url) - self.open = true + if info, err := os.Stat(path); err != nil { + return err + } else if info.IsDir() { + return errors.New("unexpected directory") } + + targetDir, err := os.MkdirTemp("", "mdv-*") + if err != nil { + return err + } + defer os.RemoveAll(targetDir) + + sourceDir := filepath.Dir(path) + builder(sourceDir, func() { + build(sourceDir, targetDir) + }) + + // if !self.open { + // url := fmt.Sprintf("http://127.0.0.1:%d/%s", self.port, self.path) + // log.Printf("opening %s in browser...", url) + // webbrowser.Open(url) + // self.open = true + // } + + return nil } func main() { flag.Usage = func() { - fmt.Fprintf(os.Stderr, "Usage %s [options] [path]\n\n", filepath.Base(os.Args[0])) + fmt.Fprintf(os.Stderr, "Usage %s [options] \n\n", filepath.Base(os.Args[0])) fmt.Fprintln(os.Stderr, "Parameters:") flag.PrintDefaults() } - port := flag.Int("port", 8080, "port") flag.Parse() if flag.NArg() != 1 { @@ -113,45 +198,8 @@ func main() { os.Exit(2) } - path := flag.Arg(0) - info, err := os.Stat(path) - if err != nil { + if err := run(flag.Arg(0)); err != nil { log.Fatal(err) } - var contentName string - contentDir := path - if !info.IsDir() { - contentName = filepath.Base(path) - contentExt := filepath.Ext(contentName) - switch contentExt { - case ".md", ".markdown": - contentName = strings.TrimSuffix(contentName, contentExt) - contentName += ".html" - } - - contentDir = filepath.Dir(path) - } - - buildDir, err := os.MkdirTemp("", "mvd-*") - if err != nil { - log.Fatal(err) - } - - defer func() { - log.Println("cleaning up...") - if err := os.RemoveAll(buildDir); err != nil { - log.Fatal(err) - } - }() - - go func() { - b := &builder{port: *port, path: contentName} - devserver.DevServe(b, *port, contentDir, buildDir, "") - }() - - sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) - - <-sigs }