Add js web stuff to landing page + documentation
This commit is contained in:
133
go/cmd/kjol-web/buildsteps/buildsteps.go
Normal file
133
go/cmd/kjol-web/buildsteps/buildsteps.go
Normal file
@@ -0,0 +1,133 @@
|
||||
// Package buildsteps is the example's build pipeline: directive codegen, Tailwind,
|
||||
// the wasm binary, and Go's JS shim.
|
||||
//
|
||||
// It is Go, not a shell script, for three reasons. The dev server needs to call these
|
||||
// steps on every save and cannot shell out to bash on Windows. Editors need to run
|
||||
// them as tasks, and a task that only works on one platform is a task half the team
|
||||
// cannot use. And the one-off build and the watch build must be the SAME steps — the
|
||||
// moment they are two scripts, they drift, and the bug only shows up in whichever one
|
||||
// you use less.
|
||||
//
|
||||
// Run the whole thing with `go run ./build`.
|
||||
package buildsteps
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"kjol/jsbundler"
|
||||
)
|
||||
|
||||
// kjolRoot is the kjol Go module root, relative to the example directory. The Tailwind
|
||||
// and codegen commands are run FROM there so the engine's dependencies resolve in
|
||||
// kjol's own go.mod, and this example's stays lean.
|
||||
const kjolRoot = "../.."
|
||||
|
||||
// Wwwroot is where every build artefact lands, and what the dev server serves.
|
||||
const Wwwroot = "wwwroot"
|
||||
|
||||
// Codegen regenerates app/*.gen.go from the //gowasm: directives — the routes, the
|
||||
// layouts, and the client stubs for server components. It runs FIRST: everything after
|
||||
// it compiles the code it writes.
|
||||
func Codegen() ([]byte, error) {
|
||||
return run("go", "run", "kjol/cmd/wasmgen", "./app")
|
||||
}
|
||||
|
||||
// Tailwind compiles css/app.css to wwwroot/app.css, scanning the webui kit and this
|
||||
// example's Go markup for utility candidates.
|
||||
//
|
||||
// It scans .go files, which is the whole point of kjol's native engine: the markup is
|
||||
// written in Go, so that is where the class names are. There is no JS build here at
|
||||
// all.
|
||||
func Tailwind() ([]byte, error) {
|
||||
cmd := exec.Command("go", "run", "./cmd/twcss",
|
||||
"-entry", "cmd/kjol-web/css/app.css",
|
||||
"-out", "cmd/kjol-web/wwwroot/app.css",
|
||||
"-base", ".",
|
||||
"webui/**/*.go",
|
||||
"cmd/kjol-web/app/**/*.go",
|
||||
"cmd/kjol-web/server/**/*.go",
|
||||
)
|
||||
cmd.Dir = kjolRoot
|
||||
return cmd.CombinedOutput()
|
||||
}
|
||||
|
||||
// Wasm compiles ./wasm to wwwroot/app.wasm.
|
||||
func Wasm() ([]byte, error) {
|
||||
cmd := exec.Command("go", "build", "-o", filepath.Join(Wwwroot, "app.wasm"), "./wasm")
|
||||
cmd.Env = append(os.Environ(), "GOOS=js", "GOARCH=wasm")
|
||||
return cmd.CombinedOutput()
|
||||
}
|
||||
|
||||
// JS builds the OTHER half of the site: the Solid SPA under /js, the SSR'd public
|
||||
// pages, and their stylesheet. It is kjol/jsbundler — TSX compiled to Solid by a Go
|
||||
// program, bundled by esbuild's Go API, styled by kjol/tw — run in-process rather than
|
||||
// shelled out to, so a compile error comes back as a Go error and lands in the dev
|
||||
// server's browser overlay like every other failure.
|
||||
//
|
||||
// It writes bundle.min.{js,css} and public.bundle.min.{js,css} into the SAME wwwroot as
|
||||
// the wasm build. The two halves never collide: different filenames, one static dir, one
|
||||
// server.
|
||||
//
|
||||
// -web points at the shared tree, which is where the kit, the vendored Solid runtime,
|
||||
// the icon SVGs and the @theme scaffold all live.
|
||||
func JS() ([]byte, error) {
|
||||
err := jsbundler.Build(jsbundler.Config{
|
||||
AppFrontend: "frontend",
|
||||
WebDir: filepath.Join(kjolRoot, "jsruntime"),
|
||||
Output: Wwwroot,
|
||||
GenTSDir: filepath.Join("frontend", "src", "ui", "generated"),
|
||||
})
|
||||
if err != nil {
|
||||
return []byte(err.Error()), err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Shim copies Go's wasm_exec.js into wwwroot. It is the loader the browser needs to
|
||||
// start a Go wasm binary, it ships with the toolchain, and it must match the compiler
|
||||
// that produced the binary — so it is copied from GOROOT rather than vendored.
|
||||
func Shim() ([]byte, error) {
|
||||
out, err := exec.Command("go", "env", "GOROOT").Output()
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
goroot := strings.TrimSpace(string(out))
|
||||
|
||||
for _, src := range []string{
|
||||
filepath.Join(goroot, "lib", "wasm", "wasm_exec.js"), // Go >= 1.24
|
||||
filepath.Join(goroot, "misc", "wasm", "wasm_exec.js"), // Go <= 1.23
|
||||
} {
|
||||
b, err := os.ReadFile(src)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
dst := filepath.Join(Wwwroot, "wasm_exec.js")
|
||||
// The GOROOT copy is read-only, and so is the copy we made last time. Remove it
|
||||
// first, or the write fails with a permission error that says nothing useful.
|
||||
os.Remove(dst)
|
||||
return nil, os.WriteFile(dst, b, 0o644)
|
||||
}
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
|
||||
// All is the full build, in order. It is what the dev server runs on a code change and
|
||||
// what `go run ./build` runs once.
|
||||
//
|
||||
// Returned output is the failing command's combined stdout+stderr, which the dev server
|
||||
// puts straight into the browser's error overlay — so a compile error lands in front of
|
||||
// you rather than in a terminal you were not looking at.
|
||||
func All() ([]byte, error) {
|
||||
for _, step := range []func() ([]byte, error){Codegen, Tailwind, Wasm, JS, Shim} {
|
||||
if out, err := step(); err != nil {
|
||||
return out, err
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func run(name string, args ...string) ([]byte, error) {
|
||||
return exec.Command(name, args...).CombinedOutput()
|
||||
}
|
||||
Reference in New Issue
Block a user