// Command server runs the go-wasm-web example on kjol's reusable wasmdevserver: // it SSRs the app's static routes, hosts the /rsc server-component endpoint, and // hot-swaps the wasm into the browser on change. It shows the coupling // inversion — the framework (wasmdevserver) imports no app code; the app injects // Build/Render/Document here. // // Run it from THIS directory (the relative paths below are resolved against it): // // go run ./server # from cmd/examples/go-wasm-web package main import ( "flag" "log" "net/http" "os" "os/exec" "path/filepath" "kjol/httputil" "kjol/vdom" "kjol/wasmdevserver" "gowasmweb/app" ) func main() { addr := flag.String("addr", ":8085", "listen address") watch := flag.Bool("watch", true, "watch sources, rebuild wasm, hot-reload") flag.Parse() log.Fatal(wasmdevserver.Serve(wasmdevserver.Config{ Addr: *addr, Dir: "./wwwroot", Watch: *watch, WatchDirs: []string{"app", "wasm", "css", "../../../webui", "../../../vdom", "../../../wasmruntime", "../../../rsc"}, // example + kjol engine + kit Build: buildWasm, Render: render, Document: document, Handle: apiRoutes, })) } // apiRoutes registers the example's API endpoints. /api/quotes responds with a // gob-encoded []app.Quote (via httputil.RespondGob) — the /data page fetches and // decodes it on the client with encoding/gob (Go types end to end, no JSON). func apiRoutes(mux *http.ServeMux) { mux.HandleFunc("GET /api/quotes", func(w http.ResponseWriter, r *http.Request) { httputil.RespondGob(w, http.StatusOK, sampleQuotes()) }) } func sampleQuotes() []app.Quote { return []app.Quote{ {Author: "Rob Pike", Text: "A little copying is better than a little dependency."}, {Author: "Rob Pike", Text: "Don't communicate by sharing memory; share memory by communicating."}, {Author: "Ken Thompson", Text: "When in doubt, use brute force."}, {Author: "Alan Kay", Text: "The best way to predict the future is to invent it."}, } } // render SSRs a static route's #app inner HTML; ok=false ships an empty #app // (client-rendered). It is the same neutral render the client runs, so the client // hydrates it. func render(path string) (string, bool) { if !app.StaticPaths[path] { return "", false } deps := app.Deps{Path: func() string { return path }} // Navigate is nil on the server return vdom.RenderHTML(app.Shell(deps, app.Routes(deps))), true } // document wraps the server-rendered inner HTML in the page shell. No whitespace // between
and the markup, so hydration's childNodes line up. The // dev server injects the livereload script before in watch mode. func document(inner string) string { return ` gowasm — a tiny Blazor-like engine
` + inner + `
` } // buildWasm runs the directive codegen (kjol/cmd/wasmgen), compiles the Tailwind // CSS (kjol/cmd/twcss, scanning the Go markup + webui kit), then compiles ./wasm // to wwwroot/app.wasm. Returned combined output is shown in the browser overlay // on failure. func buildWasm() ([]byte, error) { if out, err := exec.Command("go", "run", "kjol/cmd/wasmgen", "./app").CombinedOutput(); err != nil { return out, err } // Compile Tailwind from the kjol module root (so the engine's deps resolve), // scanning the webui kit + this example's Go markup for utility candidates. tw := exec.Command("go", "run", "./cmd/twcss", "-entry", "cmd/examples/go-wasm-web/css/app.css", "-out", "cmd/examples/go-wasm-web/wwwroot/app.css", "-base", ".", "webui/**/*.go", "cmd/examples/go-wasm-web/app/**/*.go", "cmd/examples/go-wasm-web/server/**/*.go") tw.Dir = "../../.." // kjol/go if out, err := tw.CombinedOutput(); err != nil { return out, err } cmd := exec.Command("go", "build", "-o", filepath.Join("wwwroot", "app.wasm"), "./wasm") cmd.Env = append(os.Environ(), "GOOS=js", "GOARCH=wasm") return cmd.CombinedOutput() }