Skip to content

Commit

Permalink
wip: hotreload iter 3
Browse files Browse the repository at this point in the history
Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com>
  • Loading branch information
gfanton committed Jan 6, 2024
1 parent a68aeb9 commit 05c2a4f
Show file tree
Hide file tree
Showing 11 changed files with 157 additions and 134 deletions.
2 changes: 1 addition & 1 deletion contribs/gnodev/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ replace github.com/gnolang/gno => ../..
require (
github.com/fsnotify/fsnotify v1.7.0
github.com/gnolang/gno v0.0.0-00010101000000-000000000000
github.com/gorilla/websocket v1.5.1
golang.org/x/term v0.15.0
)

Expand All @@ -32,7 +33,6 @@ require (
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect
github.com/gorilla/sessions v1.2.1 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/gotuna/gotuna v0.6.0 // indirect
github.com/jaekwon/testify v1.6.1 // indirect
github.com/jmhodges/levigo v1.0.0 // indirect
Expand Down
52 changes: 21 additions & 31 deletions contribs/gnodev/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,12 @@ func execDev(cfg *devCfg, args []string, io commands.IO) error {
cancel(nil)
})

emitter := events.NewEmitter()
loggerHotReload := tmlog.NewTMLogger(rt.NamespacedWriter(HotReloadLogName))
emitterServer := events.NewEmitterServer(loggerHotReload)

// Setup Dev Node
// XXX: find a good way to export or display node logs
devNode, err := setupDevNode(ctx, rt, pkgpaths, gnoroot)
devNode, err := setupDevNode(ctx, emitterServer, rt, pkgpaths)
if err != nil {
return err
}
Expand All @@ -142,15 +143,6 @@ func execDev(cfg *devCfg, args []string, io commands.IO) error {
rt.Taskf(NodeLogName, "Default Address: %s\n", gnodev.DefaultCreator.String())
rt.Taskf(NodeLogName, "Chain ID: %s\n", devNode.Config().ChainID())

// Setup packages watcher
// pathChangeCh := make(chan []string, 1)
// go func() {
// defer close(pathChangeCh)

// err := runPkgsWatcher(ctx, cfg, devNode.ListPkgs(), pathChangeCh)
// cancel(err)
// }()

// Create server
mux := http.NewServeMux()
server := http.Server{
Expand All @@ -163,11 +155,9 @@ func execDev(cfg *devCfg, args []string, io commands.IO) error {
webhandler := setupGnoWebServer(cfg, devNode, rt)

// Setup HotReload if needed
loggerHotReload := tmlog.NewTMLogger(rt.NamespacedWriter(HotReloadLogName))
eventsSrv := events.NewServer(loggerHotReload)
if !cfg.noWatch {
evtstarget := fmt.Sprintf("%s/_events", server.Addr)
mux.Handle("/_events", eventsSrv)
mux.Handle("/_events", emitterServer)
mux.Handle("/", events.NewMiddleware(evtstarget, webhandler))
} else {
mux.Handle("/", webhandler)
Expand All @@ -180,17 +170,20 @@ func execDev(cfg *devCfg, args []string, io commands.IO) error {

rt.Taskf(WebLogName, "Listener: http://%s\n", server.Addr)

watcher, err := watcher.NewPackageWatcher(loggerHotReload, eventsSrv)
watcher, err := watcher.NewPackageWatcher(loggerHotReload, emitterServer)
if err != nil {
return fmt.Errorf("unable to setup packages watcher")
}
defer watcher.Stop()

// add node pkgs to watcher
watcher.AddPackages(devNode.ListPkgs()...)

// GnoDev should be ready, run event loop
rt.Taskf("[Ready]", "for commands and help, press `h`")

// Run the main event loop
return runEventLoop(ctx, cfg, rt, eventsSrv, devNode, pathChangeCh)
return runEventLoop(ctx, cfg, rt, devNode, watcher)
}

// XXX: Automatize this the same way command does
Expand All @@ -204,12 +197,7 @@ Gno Dev Helper:
`)
}

func runEventLoop(ctx context.Context,
cfg *devCfg,
rt *rawterm.RawTerm,
dnode *dev.Node,
watch *watcher.PackageWatcher,
) error {
func runEventLoop(ctx context.Context, cfg *devCfg, rt *rawterm.RawTerm, dnode *dev.Node, watch *watcher.PackageWatcher) error {
nodeOut := rt.NamespacedWriter(NodeLogName)
keyOut := rt.NamespacedWriter(KeyPressLogName)

Expand All @@ -220,12 +208,6 @@ func runEventLoop(ctx context.Context,
select {
case <-ctx.Done():
return context.Cause(ctx)
case err, ok := <-watch.PackagesUpdate:
if !ok {
return nil
}

return fmt.Errorf("watch errors: %w", err)
case pkgs, ok := <-watch.PackagesUpdate:
if !ok {
return nil
Expand Down Expand Up @@ -326,16 +308,24 @@ func setupRawTerm(io commands.IO) (rt *rawterm.RawTerm, restore func() error, er
// correctly format output for terminal
io.SetOut(commands.WriteNopCloser(rt))

return rt, restore, nil
restoreWithRecover := func() error {
if r := recover(); r != nil {
rt.Taskf("panic", "%v\n", r)
}

return restore()
}

return rt, restoreWithRecover, nil
}

// setupDevNode initializes and returns a new DevNode.
func setupDevNode(ctx context.Context, rt *rawterm.RawTerm, pkgspath []string, gnoroot string) (*gnodev.Node, error) {
func setupDevNode(ctx context.Context, emitter events.Emitter, rt *rawterm.RawTerm, pkgspath []string) (*gnodev.Node, error) {
nodeOut := rt.NamespacedWriter("Node")

logger := tmlog.NewTMLogger(nodeOut)
logger.SetLevel(tmlog.LevelError)
return gnodev.NewDevNode(ctx, logger, pkgspath)
return gnodev.NewDevNode(ctx, emitter, logger, pkgspath)
}

// setupGnowebServer initializes and starts the Gnoweb server.
Expand Down
26 changes: 0 additions & 26 deletions contribs/gnodev/pkg/dev/events.go

This file was deleted.

20 changes: 10 additions & 10 deletions contribs/gnodev/pkg/dev/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ const gnoDevChainID = "tendermint_test" // XXX: this is hardcoded and cannot be
type Node struct {
*node.Node

evtEmitter events.Emitter
logger log.Logger
pkgs PkgsMap // path -> pkg
events events.Emitter
logger log.Logger
pkgs PkgsMap // path -> pkg
}

var (
Expand All @@ -50,7 +50,7 @@ var (
}
)

func NewDevNode(ctx context.Context, evtEmitter events.Emitter, logger log.Logger, pkgslist []string) (*Node, error) {
func NewDevNode(ctx context.Context, emitter events.Emitter, logger log.Logger, pkgslist []string) (*Node, error) {
mpkgs, err := newPkgsMap(pkgslist)
if err != nil {
return nil, fmt.Errorf("unable map pkgs list: %w", err)
Expand Down Expand Up @@ -84,10 +84,10 @@ func NewDevNode(ctx context.Context, evtEmitter events.Emitter, logger log.Logge
}

return &Node{
evtEmitter: evtEmitter,
Node: node,
pkgs: mpkgs,
logger: logger,
events: emitter,
Node: node,
pkgs: mpkgs,
logger: logger,
}, nil
}

Expand Down Expand Up @@ -149,7 +149,7 @@ func (d *Node) Reset(ctx context.Context) error {
return fmt.Errorf("unable to reset the node: %w", err)
}

d.evtEmitter.Emit(newResetEvent())
d.events.Emit(events.NewEventReset())
return nil
}

Expand Down Expand Up @@ -212,7 +212,7 @@ func (d *Node) Reload(ctx context.Context) error {
return fmt.Errorf("unable to reload the node: %w", err)
}

d.evtEmitter.Emit(newReloadEvent())
d.events.Emit(events.NewEventReload())
return nil
}

Expand Down
48 changes: 48 additions & 0 deletions contribs/gnodev/pkg/events/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,55 @@ package events

type EventType string

const (
EvtReload EventType = "NODE_RELOAD"
EvtReset EventType = "NODE_RESET"
EvtPackagesUpdate EventType = "PACKAGES_UPDATE"
)

type Event struct {
Type EventType `json:"type"`
Data interface{} `json:"data"`
}

// Event Reload

type EventReload struct{}

func NewEventReload() *Event {
return &Event{
Type: EvtReload,
Data: &EventReload{},
}
}

// Event Reset

type EventReset struct{}

func NewEventReset() *Event {
return &Event{
Type: EvtReload,
Data: &EventReset{},
}
}

// Event Packages Update

type PackageUpdate struct {
Package string `json:"package"`
Files []string `json:"files"`
}

type PackagesUpdateEvent struct {
Pkgs []PackageUpdate `json:"packages"`
}

func NewPackagesUpdateEvent(pkgs []PackageUpdate) *Event {
return &Event{
Type: EvtPackagesUpdate,
Data: &PackagesUpdateEvent{
Pkgs: pkgs,
},
}
}
15 changes: 10 additions & 5 deletions contribs/gnodev/pkg/events/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ type Middleware struct {
}

func NewMiddleware(remote string, next http.Handler) *Middleware {
tmpl := template.Must(template.New("reloadscript").Parse(reloadscript))
tmpl := template.Must(template.New("reloadscript").
Funcs(tmplFuncs).
Parse(reloadscript))

return &Middleware{
tmpl: tmpl,
remote: remote,
Expand All @@ -32,7 +35,6 @@ func NewMiddleware(remote string, next http.Handler) *Middleware {
}
}

// middlewareResponseWriter is a custom http.ResponseWriter that writes to a buffer
type middlewareResponseWriter struct {
http.ResponseWriter
buffer *bytes.Buffer
Expand All @@ -50,7 +52,8 @@ func (m *Middleware) UpdateRemote(remote string) {
}

type data struct {
Remote string
Remote string
ReloadEvents []EventType
}

func (m *Middleware) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
Expand All @@ -69,16 +72,18 @@ func (m *Middleware) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
// Call the next handler, which writes to our buffer
m.next.ServeHTTP(mw, req)

// Check for any "text/html" answer
if content := mw.ResponseWriter.Header().Get("Content-Type"); !strings.Contains(content, "text/html") {
rw.Write(buffer.Bytes())
return
}

m.onceExec.Do(func() {
script := &bytes.Buffer{}
script.WriteString("<script type=\"text/javascript\">")
script.WriteString(`<script type="text/javascript">`)
err := m.tmpl.Execute(script, &data{
Remote: m.remote,
Remote: m.remote,
ReloadEvents: []EventType{EvtReload, EvtReset},
})

if err != nil {
Expand Down
10 changes: 5 additions & 5 deletions contribs/gnodev/pkg/events/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ type Emitter interface {
Emit(evt *Event)
}

type Server struct {
type EmitterServer struct {
logger log.Logger
upgrader websocket.Upgrader
clients map[*websocket.Conn]struct{}
muClients sync.Mutex
}

func NewServer(logger log.Logger) *Server {
return &Server{
func NewEmitterServer(logger log.Logger) *EmitterServer {
return &EmitterServer{
logger: logger,
clients: make(map[*websocket.Conn]struct{}),
upgrader: websocket.Upgrader{
Expand All @@ -32,7 +32,7 @@ func NewServer(logger log.Logger) *Server {
}

// ws handler
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
func (s *EmitterServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
conn, err := s.upgrader.Upgrade(w, r, nil)
if err != nil {
s.logger.Error("unable to upgrade connection", "error", err)
Expand All @@ -55,7 +55,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}

func (s *Server) Emit(evt *Event) {
func (s *EmitterServer) Emit(evt *Event) {
if len(s.clients) == 0 {
return
}
Expand Down
3 changes: 2 additions & 1 deletion contribs/gnodev/pkg/events/static/hotreload.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
(function() {
var eventsReload = {{ .ReloadEvents | jsEventsArray }};
var ws = new WebSocket('ws://{{- .Remote -}}');

ws.onmessage = function(event) {
try {
var message = JSON.parse(event.data);
console.log('receiving message:', message);
if (message.type === 'FILE_UPDATE') {
if (eventsReload.includes(message.type)) {
window.location.reload();
}
} catch (e) {
Expand Down
Loading

0 comments on commit 05c2a4f

Please sign in to comment.