From 28d36a0d19b74b09792b7fff1f32ac6e906d6779 Mon Sep 17 00:00:00 2001 From: Matthew Mueller Date: Sat, 23 Jul 2022 14:35:43 -0500 Subject: [PATCH] viewer has been extracted, but still depends on internal svelte rendering --- example/basic/viewer/svelte/svelte.go | 51 +++++++++++++++++++++++--- framework/controller/controller.gotext | 2 +- framework/web/web.gotext | 7 +++- package/budclient/client.go | 26 ++++++++++++- package/budserver/server.go | 23 ++++++++++++ 5 files changed, 99 insertions(+), 10 deletions(-) diff --git a/example/basic/viewer/svelte/svelte.go b/example/basic/viewer/svelte/svelte.go index 421c210f..96477146 100644 --- a/example/basic/viewer/svelte/svelte.go +++ b/example/basic/viewer/svelte/svelte.go @@ -5,17 +5,20 @@ import ( "fmt" "io/fs" "net/http" + "strings" + "github.com/livebud/bud/framework/view/ssr" "github.com/livebud/bud/package/js" "github.com/livebud/bud/package/router" ) func New(fsys fs.FS, vm js.VM) *Viewer { - return &Viewer{fsys, vm} + return &Viewer{fsys, http.FS(fsys), vm} } type Viewer struct { fsys fs.FS + hfs http.FileSystem vm js.VM } @@ -41,10 +44,48 @@ func (v *Viewer) Render(w http.ResponseWriter, viewPath string, props interface{ http.Error(w, err.Error(), http.StatusInternalServerError) return } - fmt.Println("got result", result) - return + // Unmarshal the response + res := new(ssr.Response) + if err := json.Unmarshal([]byte(result), res); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if res.Status < 100 || res.Status > 999 { + err := fmt.Errorf("view: invalid status code %d", res.Status) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + headers := w.Header() + for key, value := range res.Headers { + headers.Set(key, value) + } + w.WriteHeader(res.Status) + w.Write([]byte(res.Body)) +} + +func (v *Viewer) Serve(router *router.Router) { + router.Get("/bud/view/:path*", http.HandlerFunc(v.serveClient)) + router.Get("/bud/node_modules/:path*", http.HandlerFunc(v.serveClient)) } -func (v *Viewer) Serve(r *router.Router) { - // Serving files! +func (v *Viewer) serveClient(w http.ResponseWriter, r *http.Request) { + file, err := v.hfs.Open(r.URL.Path) + if err != nil { + // TODO: swap with logger + fmt.Println("view: open error", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + stat, err := file.Stat() + if err != nil { + // TODO: swap with logger + fmt.Println("view: stat error", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + // Maintain support to resolve and run "/bud/node_modules/livebud/runtime". + if strings.HasPrefix(r.URL.Path, "/bud/node_modules/") { + w.Header().Add("Content-Type", "text/javascript") + } + http.ServeContent(w, r, r.URL.Path, stat.ModTime(), file) } diff --git a/framework/controller/controller.gotext b/framework/controller/controller.gotext index 1e293f34..8cadcc71 100644 --- a/framework/controller/controller.gotext +++ b/framework/controller/controller.gotext @@ -26,7 +26,7 @@ type {{ $.Pascal }}Controller struct { // {{ $.Pascal }}{{$action.Pascal}}Action struct type {{ $.Pascal }}{{$action.Pascal}}Action struct { {{- if $action.View }} - View view.Server + {{/* View view.Server */}} Viewer view.Viewer {{- end }} {{- with $provider := $action.Provider }} diff --git a/framework/web/web.gotext b/framework/web/web.gotext index 1b3cc9d2..e855938d 100644 --- a/framework/web/web.gotext +++ b/framework/web/web.gotext @@ -34,6 +34,9 @@ func New( router.{{ $action.Method }}(`{{ $action.Route }}`, controller.{{ $action.CallName }}) {{- end }} {{- end }} + {{- if $.HasView }} + viewer.Serve(router) + {{- end }} // Compose the middleware together middleware := middleware.Compose( middleware.MethodOverride(), @@ -41,9 +44,9 @@ func New( {{- if $.ShowWelcome }} welcome, {{- end }} - {{- if $.HasView }} + {{/* {{- if $.HasView }} view, - {{- end }} + {{- end }} */}} {{- if $.HasPublic }} public, {{- end }} diff --git a/package/budclient/client.go b/package/budclient/client.go index 1bceaf1d..0a5f0133 100644 --- a/package/budclient/client.go +++ b/package/budclient/client.go @@ -87,8 +87,30 @@ func (c *client) Script(path, script string) error { return fmt.Errorf("budclient: script not implemented yet") } -func (c *client) Eval(path, expression string) (string, error) { - return "", fmt.Errorf("budclient: eval not implemented yet") +type JS struct { + Path string + Expr string +} + +func (c *client) Eval(name, expr string) (string, error) { + body, err := json.Marshal(JS{name, expr}) + if err != nil { + return "", err + } + url := c.baseURL + "/eval" + res, err := c.httpClient.Post(url, "application/json", bytes.NewReader(body)) + if err != nil { + return "", err + } + defer res.Body.Close() + resBody, err := io.ReadAll(res.Body) + if err != nil { + return "", err + } + if res.StatusCode != http.StatusOK { + return "", fmt.Errorf("budclient: eval returned unexpected %d. %s", res.StatusCode, resBody) + } + return string(resBody), nil } // Render a path with props on the dev server diff --git a/package/budserver/server.go b/package/budserver/server.go index b5a6d17e..9b3e1201 100644 --- a/package/budserver/server.go +++ b/package/budserver/server.go @@ -40,6 +40,8 @@ func New(fsys fs.FS, bus pubsub.Client, log log.Interface, vm js.VM) *Server { router.Post("/bud/events", http.HandlerFunc(server.createEvent)) // Open a file router.Get("/open/:path*", http.HandlerFunc(server.openFile)) + // Eval some JS + router.Post("/eval", http.HandlerFunc(server.evalJS)) return server } @@ -145,6 +147,27 @@ func (s *Server) openFile(w http.ResponseWriter, r *http.Request) { s.log.Debug("devserver: opened", "file", filePath) } +func (s *Server) evalJS(w http.ResponseWriter, r *http.Request) { + s.log.Debug("devserver: evaling") + body, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + var input budclient.JS + if err := json.Unmarshal(body, &input); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + result, err := s.vm.Eval(input.Path, input.Expr) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + w.Write([]byte(result)) + s.log.Debug("devserver: evaled") +} + func (s *Server) createEvent(w http.ResponseWriter, r *http.Request) { // Read the body body, err := io.ReadAll(r.Body)