diff --git a/cmd/cabret/build.go b/cmd/cabret/build.go
new file mode 100644
index 0000000..06ab7d0
--- /dev/null
+++ b/cmd/cabret/build.go
@@ -0,0 +1 @@
+package main
diff --git a/cmd/cabret/main.go b/cmd/cabret/main.go
index 35f070e..80a8697 100644
--- a/cmd/cabret/main.go
+++ b/cmd/cabret/main.go
@@ -1,25 +1,185 @@
package main
import (
+ "fmt"
"log"
+ "os"
+ "strings"
+ "github.com/alecthomas/repr"
"github.com/aziis98/cabret/config"
"github.com/aziis98/cabret/runner"
"github.com/spf13/pflag"
)
+const HelpMessage = `usage: cabret [OPTIONS...]
+subcommands:
+ build [OPTIONS...] Builds the current project
+ serve [OPTIONS...] Starts an http server and watches files for changes
+
+`
+
func main() {
- log.SetFlags(0)
+ flagSet := pflag.FlagSet{
+ Usage: func() {
+ fmt.Print(HelpMessage)
+ },
+ }
+ if err := flagSet.Parse(os.Args[1:]); err != nil {
+ if err != pflag.ErrHelp {
+ log.Fatal(err)
+ }
+ return
+ }
+
+ switch flagSet.Arg(0) {
+ case "serve":
+ if err := serveSubcommand(os.Args[2:]); err != nil {
+ if err != pflag.ErrHelp {
+ log.Fatal(err)
+ }
+ return
+ }
+
+ case "build":
+ if err := buildSubcommand(os.Args[2:]); err != nil {
+ if err != pflag.ErrHelp {
+ log.Fatal(err)
+ }
+ return
+ }
+
+ default:
+ fmt.Print(HelpMessage)
+
+ }
+}
+
+const BuildHelpMessage = `usage: cabret build [OPTIONS...]
+options:
- optConfig := pflag.StringP("config", "c", "Cabretfile.yaml", `which configuration file to use`)
- pflag.Parse()
+ -c, --config Path to configuration file (default is "Cabretfile.yaml")
+ -v, --verbose Be more verbose
- cabretfile, err := config.ReadCabretfile(*optConfig)
+`
+
+func buildSubcommand(args []string) error {
+ flagSet := pflag.FlagSet{
+ Usage: func() { fmt.Print(BuildHelpMessage) },
+ }
+
+ var configFile string
+ flagSet.StringVarP(&configFile, "config", "c",
+ "Cabretfile.yaml",
+ `path to configuration file`,
+ )
+
+ var verbose bool
+ flagSet.BoolVarP(&verbose, "verbose", "v",
+ false,
+ `verbose output`,
+ )
+
+ if err := flagSet.Parse(args); err != nil {
+ return err
+ }
+
+ cabretfile, err := config.ReadCabretfile(configFile)
if err != nil {
- log.Fatal(err)
+ return err
}
if err := runner.RunConfig(cabretfile); err != nil {
- log.Fatal(err)
+ return err
+ }
+
+ return nil
+}
+
+const ServeHelpMessage = `usage: cabret serve [OPTIONS...]
+options:
+
+ -c, --config Path to configuration file (default is "Cabretfile.yaml")
+ -v, --verbose Be more verbose
+ -w, --watch List of paths to watch for rebuild
+ -m, --mount Mount a file or folder on the default static file
+ server, this will mount file or folder at ""
+ on route "".
+ -m, --mount : Mount a file or folder on the default static file
+ server, this will mount file or folder at ""
+ on route "".
+
+By default the mount list is "public/:/", "dist/:/", "out/:/".
+
+The server will also serve a special js script on "/cabret/live-reload.js" that
+will add live reload on file change for development, this can be included with
+the following tag
+
+
+
+`
+
+func serveSubcommand(args []string) error {
+ flagSet := pflag.FlagSet{
+ Usage: func() { fmt.Print(ServeHelpMessage) },
}
+
+ var configFile string
+ flagSet.StringVarP(&configFile, "config", "c",
+ "Cabretfile.yaml",
+ `path to configuration file`,
+ )
+
+ var verbose bool
+ flagSet.BoolVarP(&verbose, "verbose", "v",
+ false,
+ `verbose output`,
+ )
+
+ var watchedPatterns []string
+ flagSet.StringArrayVarP(&watchedPatterns, "watch", "w",
+ []string{"src/**/"},
+ `list of paths to watch for rebuild`,
+ )
+
+ var mounts []string
+ flagSet.StringArrayVarP(&mounts, "mount", "m",
+ []string{"public/:/", "out/:/", "dist/:/"},
+ `list of paths to mount for the static file server`,
+ )
+
+ err := flagSet.Parse(args)
+ if err != nil {
+ return err
+ }
+
+ serveMounts := make([]serveMount, len(mounts))
+ for i, m := range mounts {
+ path, route, found := strings.Cut(m, ":")
+ if !found {
+ if strings.HasPrefix(path, "/") {
+ return fmt.Errorf(`cannot mount an absolute path without a route: "%s"`, path)
+ }
+
+ route = "/" + path
+ }
+
+ if !strings.HasPrefix(route, "/") {
+ return fmt.Errorf(`route must start with a slash: "%s"`, route)
+ }
+
+ serveMounts[i] = serveMount{path, route}
+ }
+
+ log.Printf(`configFile = %v`, repr.String(configFile))
+ log.Printf(`verbose = %v`, repr.String(verbose))
+ log.Printf(`watchedPatterns = %v`, repr.String(watchedPatterns))
+ log.Printf(`serveMounts = %v`, repr.String(serveMounts))
+
+ return serve(serveConfig{
+ configFile,
+ verbose,
+ watchedPatterns,
+ serveMounts,
+ })
}
diff --git a/cmd/cabret/serve.go b/cmd/cabret/serve.go
new file mode 100644
index 0000000..8ea7578
--- /dev/null
+++ b/cmd/cabret/serve.go
@@ -0,0 +1,165 @@
+package main
+
+import (
+ "fmt"
+ "log"
+ "net"
+ "net/http"
+ "path/filepath"
+ "strings"
+ "time"
+
+ "github.com/aziis98/cabret/config"
+ "github.com/aziis98/cabret/runner"
+ "github.com/fsnotify/fsnotify"
+ "github.com/go-chi/chi/v5"
+ "github.com/go-chi/chi/v5/middleware"
+ "github.com/gobwas/ws"
+ "github.com/gobwas/ws/wsutil"
+)
+
+type serveMount struct {
+ path, route string
+}
+
+type serveConfig struct {
+ configFile string
+ verbose bool
+ watchedPatterns []string
+ mounts []serveMount
+}
+
+const LiveReloadScript = `
+const ws = new WebSocket("ws://" + location.host + "/__cabret__/websocket")
+ws.addEventListener("message", e => {
+ console.log("Server: " + e.data)
+ if (e.data === "reload") {
+ location.reload()
+ }
+})
+ws.addEventListener("close", e => {
+ console.log("Connection closed")
+ location.reload()
+})
+
+console.log("Live reload enabled")
+`
+
+func serve(c serveConfig) error {
+ cabretFile, err := config.ReadCabretfile(c.configFile)
+ if err != nil {
+ return err
+ }
+
+ listeners := map[net.Conn]struct{}{}
+
+ // Create new watcher.
+ watcher, err := fsnotify.NewWatcher()
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer watcher.Close()
+
+ // Start listening for events.
+ go func() {
+ lastRebuild := time.Now()
+
+ for {
+ select {
+ case event, ok := <-watcher.Events:
+ if !ok {
+ return
+ }
+ if event.Has(fsnotify.Write) {
+ log.Printf(`[Watcher] event: %s`, event)
+
+ if lastRebuild.Add(50 * time.Millisecond).After(time.Now()) {
+ log.Printf(`[Watcher] too fast, skipping`)
+ continue
+ }
+
+ // TODO: Make this incremental
+
+ // trigger full rebuild
+ err := runner.RunConfig(cabretFile)
+ if err != nil {
+ log.Fatal(err)
+ return
+ }
+
+ // broadcast refresh
+ for conn := range listeners {
+ err := wsutil.WriteServerText(conn, []byte("reload"))
+ if err != nil {
+ log.Printf(`[LiveReload] got "%v", removing listener`, err)
+ delete(listeners, conn)
+ }
+ }
+
+ lastRebuild = time.Now()
+ }
+ case err, ok := <-watcher.Errors:
+ if !ok {
+ return
+ }
+ log.Println("error:", err)
+ }
+ }
+ }()
+
+ // register watchers
+ for _, pattern := range c.watchedPatterns {
+ matches, err := filepath.Glob(pattern)
+ if err != nil {
+ return err
+ }
+
+ for _, m := range matches {
+ log.Printf(`[Watcher] registering "%s"`, m)
+
+ if err := watcher.Add(m); err != nil {
+ return fmt.Errorf(`cannot register pattern "%s": %w`, pattern, err)
+ }
+ }
+ }
+
+ r := chi.NewRouter()
+ r.Use(middleware.Logger)
+ r.Use(middleware.Recoverer)
+ r.Use(middleware.NoCache)
+
+ r.Get("/__cabret__/live-reload.js", func(w http.ResponseWriter, r *http.Request) {
+ http.ServeContent(w, r, "live-reload.js", time.Time{}, strings.NewReader(LiveReloadScript))
+ })
+
+ r.Get("/__cabret__/websocket", func(w http.ResponseWriter, r *http.Request) {
+ conn, _, _, err := ws.UpgradeHTTP(r, w)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ listeners[conn] = struct{}{}
+ })
+
+ for _, m := range c.mounts {
+ if strings.HasSuffix(m.route, "/") {
+ log.Printf(`[FileServer] mounting directory "%s" on "%s"`, m.path, m.route)
+ r.Handle(m.route+"*", http.StripPrefix(m.route, http.FileServer(http.Dir(m.path))))
+ } else {
+ log.Printf(`[FileServer] mounting file "%s" on "%s"`, m.path, m.route)
+ r.Get(m.route, func(w http.ResponseWriter, r *http.Request) {
+ http.ServeFile(w, r, m.path)
+ })
+ }
+ }
+
+ if err := runner.RunConfig(cabretFile); err != nil {
+ return err
+ }
+
+ server := &http.Server{Addr: ":5000", Handler: r}
+
+ log.Printf(`[FileServer] starting on ":5000"`)
+ return server.ListenAndServe()
+}
diff --git a/examples/basic/index.html b/examples/basic/index.html
index d9ab390..64ab3b7 100644
--- a/examples/basic/index.html
+++ b/examples/basic/index.html
@@ -4,3 +4,7 @@
Lorem ipsum dolor sit amet consectetur adipisicing elit. Minima, eveniet, dolorum amet, cupiditate quae excepturi aspernatur dolor
voluptatem obcaecati ratione quas? Et explicabo illum iure eius porro, dolor quos doloremque!
+
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Minima, eveniet, dolorum amet, cupiditate quae excepturi aspernatur dolor
+ voluptatem obcaecati ratione quas? Et explicabo illum iure eius porro, dolor quos doloremque!
+
diff --git a/examples/basic/layouts/base.html b/examples/basic/layouts/base.html
index 37d6dcd..4691aa0 100644
--- a/examples/basic/layouts/base.html
+++ b/examples/basic/layouts/base.html
@@ -5,6 +5,8 @@
{{- if .Title -}}{{ .Title }} | {{ else }}{{ end -}} My Blog
+
+
{{ .Content }}
diff --git a/examples/basic/posts/post-1.md b/examples/basic/posts/post-1.md
index 908df5f..4d3eee2 100644
--- a/examples/basic/posts/post-1.md
+++ b/examples/basic/posts/post-1.md
@@ -9,4 +9,5 @@ tags: [a, b]
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Beatae ratione eos saepe veritatis, temporibus nemo vero rerum earum voluptatibus minus voluptatem neque consequatur necessitatibus, error magnam deserunt! Ad, accusantium excepturi?
-Lorem ipsum dolor sit amet consectetur, adipisicing elit. Beatae ratione eos saepe veritatis, temporibus nemo vero rerum earum voluptatibus minus voluptatem neque consequatur necessitatibus, error magnam deserunt! Ad, accusantium excepturi?
\ No newline at end of file
+Lorem ipsum dolor sit amet consectetur, adipisicing elit. Beatae ratione eos saepe veritatis, temporibus nemo vero rerum earum voluptatibus minus voluptatem neque consequatur necessitatibus, error magnam deserunt! Ad, accusantium excepturi?
+
diff --git a/go.mod b/go.mod
index e33d014..42dfc59 100644
--- a/go.mod
+++ b/go.mod
@@ -8,8 +8,15 @@ require (
github.com/bradleyjkemp/cupaloy v2.3.0+incompatible // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.13.0 // indirect
+ github.com/fsnotify/fsnotify v1.6.0 // indirect
+ github.com/go-chi/chi/v5 v5.0.8 // indirect
+ github.com/gobwas/httphead v0.1.0 // indirect
+ github.com/gobwas/pool v0.2.1 // indirect
+ github.com/gobwas/ws v1.1.0 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/iancoleman/strcase v0.2.0 // indirect
+ github.com/julienschmidt/httprouter v1.3.0 // indirect
+ github.com/klauspost/compress v1.10.3 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/pkg/errors v0.9.1 // indirect
@@ -26,4 +33,5 @@ require (
gopkg.in/yaml.v2 v2.3.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gotest.tools v2.2.0+incompatible // indirect
+ nhooyr.io/websocket v1.8.7 // indirect
)
diff --git a/go.sum b/go.sum
index a7d2042..a855baa 100644
--- a/go.sum
+++ b/go.sum
@@ -4,15 +4,46 @@ github.com/alecthomas/repr v0.1.1 h1:87P60cSmareLAxMc4Hro0r2RBY4ROm0dYwkJNpS4pPs
github.com/alecthomas/repr v0.1.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/bradleyjkemp/cupaloy v2.3.0+incompatible h1:UafIjBvWQmS9i/xRg+CamMrnLTKNzo+bdmT/oH34c2Y=
github.com/bradleyjkemp/cupaloy v2.3.0+incompatible/go.mod h1:Au1Xw1sgaJ5iSFktEhYsS0dbQiS1B0/XMXl+42y9Ilk=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
+github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
+github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
+github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
+github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
+github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
+github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
+github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
+github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
+github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
+github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
+github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
+github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
+github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
+github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
+github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
+github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA=
+github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
+github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
+github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
+github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eTO0Q8=
+github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
@@ -23,6 +54,8 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -31,6 +64,11 @@ github.com/rakyll/gotest v0.0.6 h1:hBTqkO3jiuwYW/M9gL4bu0oTYcm8J6knQAAPUsJsz1I=
github.com/rakyll/gotest v0.0.6/go.mod h1:SkoesdNCWmiD4R2dljIUcfSnNdVZ12y8qK4ojDkc2Sc=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
+github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/yuin/goldmark v1.5.3 h1:3HUJmBFbQW9fhQOzMgseU134xfi6hU+mjWywx5Ty+/M=
github.com/yuin/goldmark v1.5.3/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
@@ -43,18 +81,28 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4=
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
+nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
+nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=