Documentation
¶
Overview ¶
Package porang is the view layer: templ for markup, HTMX for interaction, Tailwind for style. It is a binary and it is never Node.
A project that uses it still runs with `git clone && aru dev`: no node_modules, no package.json, no lockfile of JavaScript, nothing installed beyond Go. Having a build step is allowed; being Node is not (RULE 13).
The error page deliberately does not use this package. It has to render when the rest is broken, including when the asset build failed, so it stays as html/template inline in observability/errorpage.
Index ¶
- Constants
- func Handler(w http.ResponseWriter, r *http.Request)
- func Render(ctx context.Context, w io.Writer, name string, c templ.Component) error
- func RenderFragment(ctx context.Context, w http.ResponseWriter, status int, name string, ...) error
- func RenderPage(ctx context.Context, w http.ResponseWriter, name string, c templ.Component) error
- func URL(name string) string
- func Version() string
- type Module
Constants ¶
const AssetPath = "/_arandu/assets/"
AssetPath is where assets are served from. The hash is in the path, so the response can be cached forever and a new build simply has a new URL.
Variables ¶
This section is empty.
Functions ¶
func Handler ¶
func Handler(w http.ResponseWriter, r *http.Request)
Handler serves the embedded assets.
Anything whose path carries the right hash is immutable and cached for a year; a wrong hash is served without caching, so a stale reference degrades into a slow page rather than a broken one.
func Render ¶ added in v0.3.0
Render writes a component and records how long it took.
Use it instead of calling Component.Render directly. That is the whole reason it exists: rendering through the component's own method leaves "render" at zero on the request timeline, and a timeline with an empty column is worse than no timeline -- it says the view is free when nobody measured it.
return porang.Render(r.Context(), w, "customer/list", page)
The name is what shows on the timeline, so it should say which template, not which function.
func RenderFragment ¶ added in v0.3.0
func RenderFragment(ctx context.Context, w http.ResponseWriter, status int, name string, c templ.Component) error
RenderFragment writes an HTMX fragment with a status.
The status is explicit because a rejected form is 422 and a refused action is 403, and HTMX swaps the body of both -- answering 200 for a rejection means the browser, the logs and every metric agree that it worked.
func RenderPage ¶ added in v0.3.0
RenderPage writes a full HTML page: the content type, then the component.
The header is set before rendering rather than after, because a component that fails halfway has already written bytes, and a response with markup and no content type is one browsers guess at.
Types ¶
type Module ¶ added in v0.5.0
type Module struct{}
Module serves the embedded assets.
It is a kernel.Module for one reason: it was not, and every application shipped a page that asked for its own stylesheet and got 404.
The route used to be exported as Mount, a plain function, with a comment arguing that "a module that exists only to serve two files is a module people have to remember to register". That reasoning was backwards, and the code proved it: Mount had zero call sites across every repository -- the kernel did not call it, the generated main.go did not call it, and neither did the sign-in screen that `aru make:auth` writes. That screen emits three tags, and against a real server all three answered 404: no stylesheet, no HTMX, no Alpine. Found by audit, reproduced end to end.
A function has to be remembered. A module appears in the Register call next to events, jobs and the scheduler, which is where somebody reading main.go already looks to learn what an application is made of.