118 lines
4.2 KiB
Go
118 lines
4.2 KiB
Go
// 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's 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 <div id="app"> and the markup, so hydration's childNodes line up. The
|
|
// dev server injects the livereload script before </body> in watch mode.
|
|
func document(inner string) string {
|
|
return `<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>gowasm — a tiny Blazor-like engine</title>
|
|
<link rel="stylesheet" href="/app.css" />
|
|
</head>
|
|
<body class="bg-neutral-50 text-neutral-900 antialiased">
|
|
<div id="app">` + inner + `</div>
|
|
<script src="/wasm_exec.js"></script>
|
|
<script src="/wasmboot.js"></script>
|
|
</body>
|
|
</html>`
|
|
}
|
|
|
|
// 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()
|
|
}
|