Documentation
¶
Overview ¶
Package route provides declarative server routing with layouts for GoSX apps.
Routes map URL patterns to component handlers. Layouts wrap pages with shared UI. Nested layouts compose from outermost to innermost.
Index ¶
- Variables
- func DefaultFileRenderer(ctx *RouteContext, page FilePage) (gosx.Node, error)
- func IsNotFound(err error) bool
- func LoadFileProgram(path string) (*ir.Program, error)
- func LoadFileProgramHere(name string) (*ir.Program, error)
- func MustRegisterDirModule(module DirModule) errordeprecated
- func MustRegisterDirModuleCaller(skip int, opts DirModuleOptions) errordeprecated
- func MustRegisterDirModuleHere(opts DirModuleOptions) errordeprecated
- func MustRegisterFileModule(module FileModule) errordeprecated
- func MustRegisterFileModuleCaller(skip int, opts FileModuleOptions) errordeprecated
- func MustRegisterFileModuleHere(opts FileModuleOptions) errordeprecated
- func NotFound(message string) error
- func RegisterDirModule(module DirModule) error
- func RegisterDirModuleCaller(skip int, opts DirModuleOptions) error
- func RegisterDirModuleHere(opts DirModuleOptions) error
- func RegisterFileModule(module FileModule) error
- func RegisterFileModuleCaller(skip int, opts FileModuleOptions) error
- func RegisterFileModuleHere(opts FileModuleOptions) error
- func RenderProgramComponent(prog *ir.Program, component string, env ProgramRenderEnv, ...) (string, error)
- func RenderProgramComponentNode(prog *ir.Program, component string, env ProgramRenderEnv, ...) (gosx.Node, error)
- type AttrWriter
- type DataLoader
- type DirConfigureFunc
- type DirModule
- type DirModuleOptions
- type DirModuleRegistry
- func (r *DirModuleRegistry) Lookup(source string) (DirModule, bool)
- func (r *DirModuleRegistry) MustRegisterCaller(skip int, opts DirModuleOptions) errordeprecated
- func (r *DirModuleRegistry) MustRegisterHere(opts DirModuleOptions) errordeprecated
- func (r *DirModuleRegistry) Register(module DirModule) error
- func (r *DirModuleRegistry) RegisterCaller(skip int, opts DirModuleOptions) error
- func (r *DirModuleRegistry) RegisterHere(opts DirModuleOptions) error
- type ErrorHandler
- type FileActions
- type FileBindingsFunc
- type FileLayoutOptions
- type FileLoadFunc
- type FileMetadataAsset
- type FileMetadataAssetKind
- type FileMetadataFunc
- type FileModule
- type FileModuleOptions
- type FileModuleRegistry
- func (r *FileModuleRegistry) Lookup(source string) (FileModule, bool)
- func (r *FileModuleRegistry) MustRegisterCaller(skip int, opts FileModuleOptions) errordeprecated
- func (r *FileModuleRegistry) MustRegisterHere(opts FileModuleOptions) errordeprecated
- func (r *FileModuleRegistry) Register(module FileModule) error
- func (r *FileModuleRegistry) RegisterCaller(skip int, opts FileModuleOptions) error
- func (r *FileModuleRegistry) RegisterHere(opts FileModuleOptions) error
- type FilePage
- type FilePageMetadataAssets
- type FileRenderDataFunc
- type FileRenderFunc
- type FileRouteCacheConfig
- type FileRouteConfig
- type FileRouteScope
- type FileRoutes
- type FileRoutesOptions
- type FileTemplateBindings
- type LayoutFunc
- func FileLayout(file string) (LayoutFunc, error)
- func FileLayoutWithOptions(file string, opts FileLayoutOptions) (LayoutFunc, error)
- func FileLayoutWithOptionsAndRegistry(file string, registry *FileModuleRegistry, opts FileLayoutOptions) (LayoutFunc, error)
- func FileLayoutWithRegistry(file string, registry *FileModuleRegistry) (LayoutFunc, error)
- type Middleware
- type PageHandler
- type ProgramRenderEnv
- type RenderAttr
- type RenderProfile
- type RenderProfileError
- type Route
- type RouteContext
- func (ctx *RouteContext) ActionForm(name string, args ...any) gosx.Node
- func (ctx *RouteContext) ActionPath(name string) string
- func (ctx *RouteContext) ActionState(name string) (action.View, bool)
- func (ctx *RouteContext) ActionStates() map[string]action.View
- func (ctx *RouteContext) Document(defaultTitle string, body gosx.Node) *server.DocumentContext
- func (ctx *RouteContext) Form(args ...any) gosx.Node
- func (ctx *RouteContext) Param(name string) string
- func (ctx *RouteContext) ParentData(key string) any
- func (ctx *RouteContext) Query(name string) string
- func (ctx *RouteContext) QueryInto(dst any) error
- func (ctx *RouteContext) SetHandlerError(err error)
- type Router
- func (r *Router) Add(routes ...Route)
- func (r *Router) AddDir(root string, opts FileRoutesOptions) error
- func (r *Router) Build() http.Handler
- func (r *Router) BuildChecked() (http.Handler, error)
- func (r *Router) Handle(pattern string, handler http.Handler, middleware ...Middleware)
- func (r *Router) RevalidatePath(target string) uint64
- func (r *Router) RevalidateTag(tag string) uint64
- func (r *Router) Revalidator() *server.Revalidator
- func (r *Router) SetError(handler ErrorHandler)
- func (r *Router) SetLayout(layout LayoutFunc)
- func (r *Router) SetNavigationHead(fn func(nonce string) gosx.Node)
- func (r *Router) SetNotFound(handler PageHandler)
- func (r *Router) SetRevalidator(revalidator *server.Revalidator)
- func (r *Router) UseObserver(observer server.RequestObserver)
Constants ¶
This section is empty.
Variables ¶
var ErrNotFound = errors.New("route not found")
ErrNotFound marks a loader failure that should render the router's 404 flow instead of the route error handler.
Functions ¶
func DefaultFileRenderer ¶
func DefaultFileRenderer(ctx *RouteContext, page FilePage) (gosx.Node, error)
DefaultFileRenderer renders `.gsx` and `.html` page files directly.
func IsNotFound ¶
IsNotFound reports whether err should be treated as a route-level 404.
func LoadFileProgram ¶ added in v0.48.0
LoadFileProgram compiles the .gsx file at path and returns its IR program, through the same stat-keyed cache (gosx#226) a file-routed page or layout renders through (route/filelayout.go's gsxCompileCache): a request that hits this file again before its next edit gets the cached *ir.Program back without re-parsing, and an edit invalidates the cache exactly the way it invalidates a running page's own hot reload — there is no second, independently-stale cache for a caller of this function to fall behind.
This is the other half of gosx#226's fix: a plain http.Handler in the same package as a page.gsx file — for example, a fragment endpoint that data- gosx-region polls — loads that file's compiled program with this function and renders any component it declares, including one the page's own Page/Layout entry never renders directly, with RenderProgramComponent.
path resolves relative to the process's current working directory, same as os.Open; pass an absolute path to make it independent of the working directory the binary starts in.
Do not build that absolute path from runtime.Caller's own file value: a `-trimpath` build — `gosx build`'s default, and the shape of most container images — replaces that value with the calling module's own declared path, not a path that exists on the machine running the binary, so path resolves to a file that was never there (gosx#239). This mistake passes every local check: a plain `go run` or `go build` still records the real path, so it only surfaces once something builds with -trimpath. LoadFileProgramHere resolves a sibling of the calling file without this problem; prefer it when path is a sibling of the calling source file.
func LoadFileProgramHere ¶ added in v0.49.0
LoadFileProgramHere compiles the .gsx file named name in the same directory as the calling source file and returns its IR program, through the same cache LoadFileProgram uses (gosx#226).
Use this in place of building a path from runtime.Caller and filepath.Dir yourself, the pattern the "Rendering a fragment from a Go handler" example used before this function existed: see LoadFileProgram's own doc comment for why that pattern breaks under a `-trimpath` build (gosx#239). LoadFileProgramHere resolves the calling file the same trimpath-safe way RegisterFileModuleHere resolves a page's sibling .gsx file, so it does not have that problem.
name is a bare file name, such as "page.gsx" — a sibling of the calling file, not a path elsewhere in the tree. For that, resolve an absolute path yourself and call LoadFileProgram directly.
func MustRegisterDirModule
deprecated
func MustRegisterDirModuleCaller
deprecated
func MustRegisterDirModuleCaller(skip int, opts DirModuleOptions) error
MustRegisterDirModuleCaller registers a directory module using a caller higher in the stack.
Deprecated: use RegisterDirModuleCaller and handle the returned error.
func MustRegisterDirModuleHere
deprecated
func MustRegisterDirModuleHere(opts DirModuleOptions) error
MustRegisterDirModuleHere infers the route directory from the calling file and registers the module in the shared registry.
Deprecated: use RegisterDirModuleHere and handle the returned error.
func MustRegisterFileModule
deprecated
func MustRegisterFileModule(module FileModule) error
MustRegisterFileModule adds a file-route module to the shared registry.
Deprecated: use RegisterFileModule and handle the returned error.
func MustRegisterFileModuleCaller
deprecated
func MustRegisterFileModuleCaller(skip int, opts FileModuleOptions) error
MustRegisterFileModuleCaller registers a file module using a caller higher in the stack. `skip=0` means the immediate caller, `skip=1` skips one wrapper, and so on.
Deprecated: use RegisterFileModuleCaller and handle the returned error.
func MustRegisterFileModuleHere
deprecated
func MustRegisterFileModuleHere(opts FileModuleOptions) error
MustRegisterFileModuleHere infers the sibling page source path from the calling file and registers the module in the shared registry.
Deprecated: use RegisterFileModuleHere and handle the returned error.
func NotFound ¶
NotFound returns an error that instructs the router to render the not-found page for the current request path.
func RegisterDirModule ¶
RegisterDirModule adds a directory module to the shared registry.
func RegisterDirModuleCaller ¶
func RegisterDirModuleCaller(skip int, opts DirModuleOptions) error
RegisterDirModuleCaller registers a directory module using a caller higher in the stack. `skip=0` means the immediate caller.
func RegisterDirModuleHere ¶
func RegisterDirModuleHere(opts DirModuleOptions) error
RegisterDirModuleHere infers the route directory from the calling file and registers the module in the shared registry.
func RegisterFileModule ¶
func RegisterFileModule(module FileModule) error
RegisterFileModule adds a file-route module to the shared registry.
func RegisterFileModuleCaller ¶
func RegisterFileModuleCaller(skip int, opts FileModuleOptions) error
RegisterFileModuleCaller registers a file module using a caller higher in the stack. `skip=0` means the immediate caller.
func RegisterFileModuleHere ¶
func RegisterFileModuleHere(opts FileModuleOptions) error
RegisterFileModuleHere infers the sibling page source path from the calling file and registers the module in the shared registry.
func RenderProgramComponent ¶ added in v0.24.3
func RenderProgramComponent(prog *ir.Program, component string, env ProgramRenderEnv, children ...gosx.Node) (string, error)
RenderProgramComponent renders the named component of a compiled program (from gosx.Compile) to server-side static HTML: it evaluates {expr}, resolves local <Component/> references, and inlines local island children via env.RenderIsland.
It is the public entry to the file-program renderer that powers file-based pages, for callers that compile and render components directly — e.g. a slide deck that lowers each slide to a generated component, or a plain http.Handler in the same package as a page.gsx file rendering one of that page's own components into a fragment (gosx#226, see LoadFileProgram) — instead of from on-disk page files. A single compiled source may declare the rendered component plus any sibling components and islands it references; cross-references resolve here at render time.
component is a strict component's own render entry exactly the same way a nested call to it is: see ProgramRenderEnv.Props for the typed-props contract this requires.
children places one or more Go-computed gosx.Node values wherever component's body writes {children} (gosx#226, gosx#246) — the same "one opaque node, emitted where written" contract a nested <Component>...</Component> call's children get, not a prop: children are never proved against component's declared schema, and cannot overwrite a proved props field. Passing no children reproduces every pre-#246 call's behavior exactly: an unresolved "children" identifier fails soft to empty, the same as today. Passing children against a legacy (non-strict) component fails closed with an error, since a legacy render entry has no {children} hole to bind them to.
When env.Profile is set and its Validate hook reports any diagnostic, RenderProgramComponent returns a *RenderProfileError and an empty string; no partial HTML is ever returned alongside that error.
func RenderProgramComponentNode ¶ added in v0.50.0
func RenderProgramComponentNode(prog *ir.Program, component string, env ProgramRenderEnv, children ...gosx.Node) (gosx.Node, error)
RenderProgramComponentNode is RenderProgramComponent's Node-returning sibling (gosx#226, gosx#246): it renders the same way, but returns a gosx.Node instead of a string, so the result composes directly into a gosx.El(...) tree instead of forcing a caller to wrap a rendered string in gosx.RawHTML by hand — the pattern examples/dashboard's chrome() helper used before this function existed (see examples/dashboard/chrome.go).
The returned Node wraps the rendered HTML with gosx.RawHTML, the same wrapping RenderProgramComponent's own callers were already doing, and the same wrapping writeLocalComponent uses internally for a nested call's own children — the render still happens exactly once; nothing here re-renders or re-escapes it. On error, the returned Node is the zero Node, and the caller must not render it: like RenderProgramComponent, no partial HTML is ever returned alongside an error.
Types ¶
type AttrWriter ¶ added in v0.43.0
type AttrWriter func(tag string, attrs []RenderAttr) []RenderAttr
AttrWriter rewrites, vetoes, or appends an element's attributes before they render. See RenderProfile.AttrWriter for exactly when it runs and what it can and cannot see.
type DataLoader ¶
type DataLoader func(ctx *RouteContext) (any, error)
DataLoader fetches data for a route before rendering.
type DirConfigureFunc ¶
type DirConfigureFunc func(ctx *RouteContext, page FilePage) error
DirConfigureFunc applies request-scoped subtree configuration before a file route loads data or renders.
type DirModule ¶
type DirModule struct {
Source string
Middleware []Middleware
Configure DirConfigureFunc
}
DirModule wires inherited middleware and request setup to a file-route directory.
func DirModuleCaller ¶
func DirModuleCaller(skip int, opts DirModuleOptions) DirModule
DirModuleCaller infers the route directory from a caller higher in the stack.
func DirModuleFor ¶
func DirModuleFor(source string, opts DirModuleOptions) DirModule
DirModuleFor builds a directory-scoped route module definition.
func DirModuleHere ¶
func DirModuleHere(opts DirModuleOptions) DirModule
DirModuleHere infers the route directory from the calling file and returns a directory-scoped route module.
type DirModuleOptions ¶
type DirModuleOptions struct {
Middleware []Middleware
Configure DirConfigureFunc
}
DirModuleOptions configures a directory-scoped route module.
type DirModuleRegistry ¶
type DirModuleRegistry struct {
// contains filtered or unexported fields
}
DirModuleRegistry stores directory-scoped route modules keyed by source path.
func DefaultDirModuleRegistry ¶
func DefaultDirModuleRegistry() *DirModuleRegistry
DefaultDirModuleRegistry returns the shared process-wide directory-module registry.
func NewDirModuleRegistry ¶
func NewDirModuleRegistry() *DirModuleRegistry
NewDirModuleRegistry creates an empty directory-module registry.
func (*DirModuleRegistry) Lookup ¶
func (r *DirModuleRegistry) Lookup(source string) (DirModule, bool)
Lookup finds a registered directory module by source path.
func (*DirModuleRegistry) MustRegisterCaller
deprecated
func (r *DirModuleRegistry) MustRegisterCaller(skip int, opts DirModuleOptions) error
MustRegisterCaller registers a directory module using a caller higher in the stack.
Deprecated: use RegisterCaller and handle the returned error.
func (*DirModuleRegistry) MustRegisterHere
deprecated
func (r *DirModuleRegistry) MustRegisterHere(opts DirModuleOptions) error
MustRegisterHere registers a directory module inferred from the calling file.
Deprecated: use RegisterHere and handle the returned error.
func (*DirModuleRegistry) Register ¶
func (r *DirModuleRegistry) Register(module DirModule) error
Register adds a directory module to the registry.
func (*DirModuleRegistry) RegisterCaller ¶
func (r *DirModuleRegistry) RegisterCaller(skip int, opts DirModuleOptions) error
RegisterCaller registers a directory module using a caller higher in the stack. `skip=0` means the immediate caller.
func (*DirModuleRegistry) RegisterHere ¶
func (r *DirModuleRegistry) RegisterHere(opts DirModuleOptions) error
RegisterHere infers the route directory from the calling file and registers the module in this registry.
type ErrorHandler ¶
type ErrorHandler func(ctx *RouteContext, err error) gosx.Node
ErrorHandler renders a route error page.
type FileActions ¶
FileActions maps action names to handlers for a file-routed page.
type FileBindingsFunc ¶
type FileBindingsFunc func(ctx *RouteContext, page FilePage, data any) FileTemplateBindings
FileBindingsFunc returns request-scoped bindings for the default file renderer.
type FileLayoutOptions ¶
FileLayoutOptions configures how a file-backed layout injects the page body.
type FileLoadFunc ¶
type FileLoadFunc func(ctx *RouteContext, page FilePage) (any, error)
FileLoadFunc loads request-scoped data for a file-routed page.
type FileMetadataAsset ¶
type FileMetadataAsset struct {
Kind FileMetadataAssetKind
FilePath string
Source string
Dir string
RoutePath string
Pattern string
Params []string
}
FileMetadataAsset describes a discovered metadata convention file.
func (FileMetadataAsset) RequestPath ¶
func (a FileMetadataAsset) RequestPath(params map[string]string) string
func (FileMetadataAsset) StaticExportPath ¶
func (a FileMetadataAsset) StaticExportPath() (string, bool)
type FileMetadataAssetKind ¶
type FileMetadataAssetKind string
FileMetadataAssetKind identifies a convention-driven metadata asset.
const ( FileMetadataAssetOpenGraphImage FileMetadataAssetKind = "opengraph_image" FileMetadataAssetTwitterImage FileMetadataAssetKind = "twitter_image" FileMetadataAssetFavicon FileMetadataAssetKind = "favicon" FileMetadataAssetIcon FileMetadataAssetKind = "icon" FileMetadataAssetAppleIcon FileMetadataAssetKind = "apple_icon" FileMetadataAssetManifest FileMetadataAssetKind = "manifest" FileMetadataAssetRobots FileMetadataAssetKind = "robots" FileMetadataAssetSitemap FileMetadataAssetKind = "sitemap" )
type FileMetadataFunc ¶
FileMetadataFunc derives metadata for a file-routed page after Load runs.
type FileModule ¶
type FileModule struct {
Source string
Load FileLoadFunc
Metadata FileMetadataFunc
Render FileRenderDataFunc
Actions FileActions
Bindings FileBindingsFunc
// MaxActionBodyBytes caps the request body accepted by this module's
// actions, enforced with http.MaxBytesReader semantics (an oversized
// request fails with 413 rather than being silently truncated). Zero
// keeps the action package default of 1 MiB. Set this to accept larger
// uploads, such as a file, through an action route.
MaxActionBodyBytes int64
}
FileModule wires server-side hooks to a file-routed page source file.
func FileModuleCaller ¶
func FileModuleCaller(skip int, opts FileModuleOptions) FileModule
FileModuleCaller infers the sibling page source path from a caller higher in the stack. Use this when wrapping file-module registration in helper functions so the outer `page.server.go` remains the registered source.
func FileModuleFor ¶
func FileModuleFor(source string, opts FileModuleOptions) FileModule
FileModuleFor builds a file-routed server module definition.
func FileModuleHere ¶
func FileModuleHere(opts FileModuleOptions) FileModule
FileModuleHere infers the sibling page source path from the calling `*.server.go` file so callers do not need to repeat `"page.gsx"` strings.
type FileModuleOptions ¶
type FileModuleOptions struct {
Load FileLoadFunc
Metadata FileMetadataFunc
Render FileRenderDataFunc
Actions FileActions
Bindings FileBindingsFunc
// MaxActionBodyBytes caps the request body accepted by this module's
// actions, enforced with http.MaxBytesReader semantics (an oversized
// request fails with 413 rather than being silently truncated). Zero
// keeps the action package default of 1 MiB. Set this to accept larger
// uploads, such as a file, through an action route.
MaxActionBodyBytes int64
}
FileModuleOptions configures a file-routed server module.
type FileModuleRegistry ¶
type FileModuleRegistry struct {
// contains filtered or unexported fields
}
FileModuleRegistry stores file-route server modules keyed by source path.
func DefaultFileModuleRegistry ¶
func DefaultFileModuleRegistry() *FileModuleRegistry
DefaultFileModuleRegistry returns the shared process-wide module registry.
func NewFileModuleRegistry ¶
func NewFileModuleRegistry() *FileModuleRegistry
NewFileModuleRegistry creates an empty file-route module registry.
func (*FileModuleRegistry) Lookup ¶
func (r *FileModuleRegistry) Lookup(source string) (FileModule, bool)
Lookup finds a registered file-route module by source path.
func (*FileModuleRegistry) MustRegisterCaller
deprecated
func (r *FileModuleRegistry) MustRegisterCaller(skip int, opts FileModuleOptions) error
MustRegisterCaller registers a file module inferred from a caller higher in the stack.
Deprecated: use RegisterCaller and handle the returned error.
func (*FileModuleRegistry) MustRegisterHere
deprecated
func (r *FileModuleRegistry) MustRegisterHere(opts FileModuleOptions) error
MustRegisterHere registers a file module inferred from the calling file.
Deprecated: use RegisterHere and handle the returned error.
func (*FileModuleRegistry) Register ¶
func (r *FileModuleRegistry) Register(module FileModule) error
Register adds a file-route module to the registry.
func (*FileModuleRegistry) RegisterCaller ¶
func (r *FileModuleRegistry) RegisterCaller(skip int, opts FileModuleOptions) error
RegisterCaller registers a file module inferred from a caller higher in the stack. `skip=0` means the immediate caller.
func (*FileModuleRegistry) RegisterHere ¶
func (r *FileModuleRegistry) RegisterHere(opts FileModuleOptions) error
RegisterHere infers the sibling page source path from the calling file and registers the module in this registry.
type FilePage ¶
type FilePage struct {
Root string
FilePath string
Source string
Dir string
RoutePath string
Pattern string
Params []string
Layouts []string
Config FileRouteConfig
ErrorPage *FilePage
}
FilePage describes a discovered file-based page route.
type FilePageMetadataAssets ¶
type FilePageMetadataAssets struct {
OpenGraphImage *FileMetadataAsset
TwitterImage *FileMetadataAsset
}
FilePageMetadataAssets holds the nearest discovered page-scoped metadata assets.
type FileRenderDataFunc ¶
FileRenderDataFunc overrides the default page-file renderer.
type FileRenderFunc ¶
type FileRenderFunc func(ctx *RouteContext, page FilePage) (gosx.Node, error)
FileRenderFunc renders a discovered file page for a request.
type FileRouteCacheConfig ¶
type FileRouteCacheConfig struct {
Public *bool `json:"public,omitempty"`
Private *bool `json:"private,omitempty"`
NoStore *bool `json:"noStore,omitempty"`
NoCache *bool `json:"noCache,omitempty"`
MaxAge string `json:"maxAge,omitempty"`
SMaxAge string `json:"sMaxAge,omitempty"`
StaleWhileRevalidate string `json:"staleWhileRevalidate,omitempty"`
StaleIfError string `json:"staleIfError,omitempty"`
MustRevalidate *bool `json:"mustRevalidate,omitempty"`
Immutable *bool `json:"immutable,omitempty"`
}
FileRouteCacheConfig maps `route.config.json` cache directives onto `server.CachePolicy`.
type FileRouteConfig ¶
type FileRouteConfig struct {
Cache *FileRouteCacheConfig `json:"cache,omitempty"`
CacheTags []string `json:"cacheTags,omitempty"`
Headers map[string]string `json:"headers,omitempty"`
Prerender *bool `json:"prerender,omitempty"`
}
FileRouteConfig describes inheritable directory-scoped configuration loaded from `route.config.json`.
func (FileRouteConfig) CachePolicy ¶
func (c FileRouteConfig) CachePolicy() (server.CachePolicy, bool, error)
CachePolicy exposes the resolved cache policy for a file-route config.
func (FileRouteConfig) PrerenderEnabled ¶
func (c FileRouteConfig) PrerenderEnabled(defaultValue bool) bool
PrerenderEnabled reports whether routes in this scope should be exported when the caller provides a default policy.
type FileRouteScope ¶
type FileRouteScope struct {
Dir string
RoutePath string
Pattern string
Params []string
Page FilePage
}
FileRouteScope describes a directory-scoped special page such as not-found.
type FileRoutes ¶
type FileRoutes struct {
Pages []FilePage
NotFound *FilePage
Error *FilePage
NotFoundScopes []FileRouteScope
}
FileRoutes is the discovered result of scanning a file-based route tree.
func ScanDir ¶
func ScanDir(root string) (FileRoutes, error)
ScanDir discovers file-based routes from a directory tree.
type FileRoutesOptions ¶
type FileRoutesOptions struct {
Render FileRenderFunc
Modules *FileModuleRegistry
DirModules *DirModuleRegistry
Middleware []Middleware
Layout LayoutFunc
ErrorHandler ErrorHandler
}
FileRoutesOptions configures AddDir.
type FileTemplateBindings ¶
type FileTemplateBindings struct {
Values map[string]any
Funcs map[string]any
Components map[string]any
}
FileTemplateBindings exposes request-scoped values, helpers, and renderable Go component functions to a file-routed `.gsx` page.
`Components` remains available for explicit component binding, but exported Go component functions exposed through `Funcs` or `Values` are also resolved automatically by the file renderer.
type LayoutFunc ¶
type LayoutFunc func(ctx *RouteContext, content gosx.Node) gosx.Node
LayoutFunc wraps page content with shared layout.
func FileLayout ¶
func FileLayout(file string) (LayoutFunc, error)
FileLayout loads a .gsx or .html layout file and returns a LayoutFunc that injects page content into <Slot /> / <Outlet /> markers or HTML placeholders.
func FileLayoutWithOptions ¶
func FileLayoutWithOptions(file string, opts FileLayoutOptions) (LayoutFunc, error)
FileLayoutWithOptions loads a file-backed layout with custom slot markers.
func FileLayoutWithOptionsAndRegistry ¶
func FileLayoutWithOptionsAndRegistry(file string, registry *FileModuleRegistry, opts FileLayoutOptions) (LayoutFunc, error)
FileLayoutWithOptionsAndRegistry loads a file-backed layout with custom slot markers and an explicit file module registry.
func FileLayoutWithRegistry ¶
func FileLayoutWithRegistry(file string, registry *FileModuleRegistry) (LayoutFunc, error)
FileLayoutWithRegistry loads a file-backed layout using an explicit file module registry instead of the shared default registry.
type Middleware ¶
Middleware runs before page handling.
type PageHandler ¶
type PageHandler func(ctx *RouteContext) gosx.Node
PageHandler renders a page, receiving route context.
type ProgramRenderEnv ¶ added in v0.24.3
type ProgramRenderEnv struct {
Values map[string]any
Funcs map[string]any
Props any
RenderIsland func(*islandprogram.Program, any) gosx.Node
Profile *RenderProfile
// IslandPreloadHints and IslandPageHead compute the two framework-filled
// named slots a strict component may declare — {slotPreloadHints} and
// {slotPageHead} (gosx#249) — from the same island runtime RenderIsland
// renders through. Unlike Slots below, neither is a caller-authored
// value: this env supplies the two callbacks, and the renderer decides
// when to call them and what to bind the result to, the same way it
// binds children. A nested <Layout>{content}</Layout> call inside a
// compiled program's own body renders content (and every RenderIsland
// call it makes) BEFORE calling either of these, so the value they
// return reflects every island content registered — see
// writeLocalComponentWithChildren (route/fileprogram.go). Each is
// called only for a component that actually declares the matching slot
// (ir.Component.AcceptsSlot), so a page with no island runtime, or a
// component that never places {slotPreloadHints}/{slotPageHead}, pays
// nothing for it. Nil is the default: no pre-gosx#249 caller sets
// these, so none takes a new branch.
IslandPreloadHints func() gosx.Node
IslandPageHead func() gosx.Node
// Slots supplies named-slot values for a strict render entry that
// declares more than the one anonymous children hole (gosx#249) — a
// per-route title and an end-of-body script are two different
// injection points a layout-shaped component needs, and repeating
// {children} cannot express that (TestStrictComponentRendersChildrenTwice
// pins every repeat to the same content). Keyed by slot name ("Title",
// not "slotTitle" — see strictcomponent.SlotBindingName for the
// reserved identifier a name binds to in the component's body).
//
// A nil or empty Slots reproduces every pre-gosx#249 call's behavior
// exactly, the same "take no new branch" contract Children keeps
// (RenderProgramComponent's own doc comment). A key naming a slot the
// entry component's body does not declare fails closed with a
// descriptive error; a slot the body declares but this map does not
// supply stays unresolved and renders empty, exactly like an
// unsupplied {children} does today. Slots are unproven, the same way
// Children is: never entering PropsFields, PropsPaths, or PropsSlices,
// and never overwriting a proved props field.
Slots map[string]gosx.Node
}
ProgramRenderEnv supplies the expression bindings and inline-island renderer for RenderProgramComponent. All fields are optional:
- Values / Funcs bind identifiers and functions referenced by {expr} (e.g. Funcs["strings"] = map[string]any{"ToUpper": strings.ToUpper}). Unresolved identifiers render empty rather than erroring, so missing bindings fail soft.
- Props supplies the typed props value when component is a strict component (gosx#226): a `component Foo(props: FooProps)` declaration compiles to an ir.Component with a declared PropsType, and — same as a nested `<Foo {...props}/>` call inside another component's body — rendering it as the entry component must prove a real struct value, not a map. RenderProgramComponent proves Props at that identical boundary (see strictSpreadProps): a nil Props, any map[string]any (including one shaped exactly like FooProps — a map can omit keys and has no field types to check ahead of the values, so it never proves coverage), or a struct missing a rendered field or holding one of the wrong type all fail closed with a descriptive error instead of rendering. A same-shaped struct under a different Go type name is accepted, matching a generated-Go spread caller's own boundary (strictSpreadProps proves field coverage, not the source struct's own type identity — see TestStrictSpreadProps). Ignored for a legacy (non-strict) component, and for a strict component with no declared props.
- RenderIsland turns a local //gosx:island child (referenced as <Name/>) into a hydrated server-rendered mount — pass server.PageRuntime.Island or island.(*Renderer).RenderIslandFromProgram. When nil, island children degrade to inert placeholders.
- Profile installs an EXPERIMENTAL render-profile hook: an attribute writer that can rewrite, veto, or append an element's attributes, and a pre-render validation pass that can refuse to render the program (gosx#185). A nil Profile reproduces today's rendering exactly, byte for byte. See RenderProfile.
type RenderAttr ¶ added in v0.43.0
type RenderAttr struct {
// Name is the attribute name, for example "class" or "href".
Name string
// Value is the attribute's text value, for example a URL or a class
// list. Value is ignored when Boolean is true.
Value string
// Boolean marks a valueless, presence-only attribute, for example the
// `disabled` in `<button disabled>`. The renderer emits only Name for a
// boolean attribute, matching how an AttrBool or a boolean-typed
// expression attribute renders without a profile.
//
// Boolean, not Value, is what makes an attribute absent from a
// rendered element: appending RenderAttr{Name: "disabled", Value:
// "false"} with Boolean left false renders disabled="false", and HTML
// treats ANY value on a boolean attribute — including the literal text
// "false" — as present, so the button stays disabled (gosx#185 m3). A
// profile that wants an element to render as not-disabled must omit
// the attribute entirely, not set Value to a falsy-looking string.
Boolean bool
}
RenderAttr is one resolved HTML attribute: an attribute name plus either a text value or a boolean-presence marker, after expression evaluation and spread/shorthand expansion, before HTML escaping.
RenderAttr is the only type an AttrWriter hook exchanges with the renderer. It carries no raw-HTML or pre-escaped-string variant, so an AttrWriter has no way to bypass the unconditional escaping the renderer applies to Name and Value after the hook returns.
type RenderProfile ¶ added in v0.43.0
type RenderProfile struct {
// AttrWriter, when set, runs once per rendered ir.NodeElement, after
// every attribute on it has been evaluated ({expr} attributes resolved,
// {...spread} attributes expanded and flattened, and any managed-form
// shorthand attribute removed — the hook never sees the shorthand
// itself, or the runtime-contract attributes it expands into) and
// before HTML escaping. It receives the element's tag name and its
// resolved attributes, and returns the attributes to emit: change a
// Value to rewrite, omit an entry to veto, or append a new RenderAttr
// to add one.
//
// The renderer escapes every returned attribute's Name and Value
// unconditionally after AttrWriter returns. RenderAttr's Value field is
// a plain string with no "pre-escaped" or "raw" variant, so there is no
// value AttrWriter can return that skips escaping. A returned Name that
// is not a valid HTML attribute name — empty, whitespace-only, or
// containing a character (space, a control character, `"`, `'`, `>`,
// `/`, or `=`) that would end an attribute-name token early — fails the
// whole render with a *RenderProfileError naming the offending tag and
// Name instead of being escaped and emitted (gosx#185 M1): unlike an
// ordinary attribute value, a Name is never sanitized for you, because
// a profile is trusted code and a bad Name here is a bug worth
// stopping the render for, not input to defend against.
//
// See the Coverage map above for exactly which elements this hook
// reaches and does not, and the contract-attribute-name rule that
// applies to what it can do to a #179 managed-form contract attribute
// specifically.
//
// A panic inside AttrWriter is recovered and converted into a
// *RenderProfileError naming the hook and the tag it was called for
// (gosx#185 m5); it does not crash the calling process.
AttrWriter AttrWriter
// Validate, when set, runs once per render, before any output is
// written, against the compiled *ir.Program. A non-empty return value
// aborts the render: RenderProgramComponent returns a *RenderProfileError
// wrapping the diagnostics and an empty HTML string. Rendering is fail
// closed — a profile that finds a problem never lets partial or
// unvalidated output reach the caller. Validate runs before
// RenderProgramComponent even checks that the named component exists,
// so a Validate refusal is reported ahead of a "component not found"
// error for the same call (gosx#185 n2).
//
// Validate must not modify prog. The renderer may run concurrently
// with the same *ir.Program across independent requests or goroutines,
// so a mutation here would race every other render sharing it.
//
// Validate walks the WHOLE program, not only the component this render
// call is about to render: a component this call never reaches still
// gets checked, and can still fail the render on its own. This catches
// a problem before it ships in some other, unrelated page, but it has
// two costs to weigh — a large program with many components pays this
// pass's full cost on every single render, and a genuine problem in a
// component this particular render path never exercises can still
// refuse an otherwise entirely fine render (a false positive from this
// call's point of view, even though the diagnostic itself is real).
//
// A panic inside Validate is recovered and converted into a
// *RenderProfileError naming the hook (gosx#185 m5); it does not crash
// the calling process.
Validate func(*ir.Program) []ir.Diagnostic
}
RenderProfile is an EXPERIMENTAL render-time hook for RenderProgramComponent. It exists so a downstream renderer that targets a stricter or different HTML dialect — for example an email renderer, which needs inline styles instead of classes and a stricter element allowlist — can rewrite attribute emission and refuse to render an unsafe program, without forking the file-program renderer (gosx#185).
The surface is intentionally small: an attribute-writer hook and a pre-render validation pass. There is no plugin registry and no CLI surface. It may change or be removed in a future minor release; a caller that depends on it directly should pin an exact gosx version.
A nil *RenderProfile reproduces today's rendering exactly, byte for byte — every profile-aware code path in the file-program renderer is gated on a non-nil field of this struct, so an empty *RenderProfile{} (both fields nil) also reproduces it exactly.
Only RenderProgramComponent sets fileRenderOptions.Profile from ProgramRenderEnv.Profile: a file-routed page or layout, rendered through renderFileNode instead, has no way to install a profile (gosx#185 m6).
Coverage map (gosx#185 M2) ¶
AttrWriter reaches every ir.NodeElement (`<div>`, `<a>`, `<script>`, a hand-written `<form>`, ...) the renderer writes, wherever it sits: a plain top-level element, an element that is a strict or legacy nested component's own markup, an element inside an If/Show/When branch, and each per-iteration element inside an Each/For loop all get their own call.
AttrWriter does NOT reach:
- A builtin component's own markup — Link's rendered `<a>`, Image's rendered `<img>`, and every other builtin (Form, ActionForm, Motion, Video, TextBlock, Stylesheet, Surface, Worker, Scene3D) writes its element directly, bypassing this hook entirely.
- An unknown, engine, or bound component's markup, rendered through defaultRenderedComponent. Name this one explicitly: it is different in kind from the builtins above, because it emits the author's own attributes verbatim, with no escaping or contract logic of its own for AttrWriter to stand in for.
- An ir.NodeRawHTML node, or a runtime gosx.RawHTML value returned from an expression: both are opaque strings the renderer copies through unexamined. Validate cannot see inside either kind of raw HTML any more than AttrWriter can — a profile that must inspect or reject raw HTML has no hook for it today.
- An island subtree rendered through env.island (ProgramRenderEnv's RenderIsland): island markup comes from a separate renderer, outside this file-program renderer's write path.
It also may see an author-written copy of a #179 managed-form runtime-contract attribute (data-gosx-form, its -state/-mode/-project variants, the client-runtime -form-error-describedby wiring attribute, the shared -enhance/-enhance-layer/-fallback progressive-enhancement attributes, and the data-gosx-managed shorthand) in AttrWriter's input, but the renderer discards any add, change, or removal the hook's returned copy makes to one of those names, reinserts the original value at its original position, and computes the contract's own presence check from that reconciled, effective list — a profile cannot weaken the managed-form contract by vetoing or rewriting it (gosx#185 B1).
Text-node escaping and the void-element list (ir.VoidElements) are not configurable through a profile either. Both are cheap to reach from here, but a profile that could turn off text escaping would violate this type's own escape-after-the-hook guarantee, and the HTML5 void-element set is a fact about the format, not a per-consumer policy. A consumer that needs a different void-element set for a non-HTML target should treat that as a lowering-time concern, upstream of RenderProgramComponent.
type RenderProfileError ¶ added in v0.43.0
type RenderProfileError struct {
Diagnostics []ir.Diagnostic
}
RenderProfileError reports that a RenderProfile's Validate pass refused to render a program. Diagnostics is never empty when this error is returned. RenderProgramComponent and the file-program renderer both return it before writing any output, so a refusal is total: the render never returns partial HTML alongside this error.
func (*RenderProfileError) Error ¶ added in v0.43.0
func (e *RenderProfileError) Error() string
type Route ¶
type Route struct {
// Pattern is the URL path pattern (e.g., "/", "/dashboard", "/users/{id}").
Pattern string
// Handler renders the page component.
Handler PageHandler
// Layout wraps the page output (optional, overrides app-level layout).
Layout LayoutFunc
// Middleware runs before the handler.
Middleware []Middleware
// Children are nested routes under this pattern.
Children []Route
// DataLoader fetches data before rendering (optional).
DataLoader DataLoader
// ErrorHandler renders a route-scoped 500 page when the handler panics or the
// data loader returns an error.
ErrorHandler ErrorHandler
}
Route defines a URL pattern → component mapping.
type RouteContext ¶
type RouteContext struct {
Request *http.Request
Params map[string]string
Data any
server.PageState
// contains filtered or unexported fields
}
RouteContext provides request context to handlers.
func (*RouteContext) ActionForm ¶
func (ctx *RouteContext) ActionForm(name string, args ...any) gosx.Node
ActionForm renders a POST form targeting the current route's named action.
func (*RouteContext) ActionPath ¶
func (ctx *RouteContext) ActionPath(name string) string
ActionPath returns the current page-relative action endpoint for the given action name.
func (*RouteContext) ActionState ¶
func (ctx *RouteContext) ActionState(name string) (action.View, bool)
ActionState returns the flashed state for a named browser action.
func (*RouteContext) ActionStates ¶
func (ctx *RouteContext) ActionStates() map[string]action.View
ActionStates returns all flashed action states for the current request.
func (*RouteContext) Document ¶ added in v0.50.0
func (ctx *RouteContext) Document(defaultTitle string, body gosx.Node) *server.DocumentContext
Document composes the complete native document context for this route. It preserves request, route pattern, response status, title, language, page identity/path, request ID, metadata, runtime, navigation, head, body attributes, nonce, and the framework document contract. Pass the returned context to server.HTMLDocument from a document-owning layout.
func (*RouteContext) Form ¶
func (ctx *RouteContext) Form(args ...any) gosx.Node
Form renders a form tag opted into the GoSX navigation/runtime submission layer while preserving native HTML fallback behavior.
func (*RouteContext) Param ¶
func (ctx *RouteContext) Param(name string) string
Param returns a URL path parameter.
func (*RouteContext) ParentData ¶
func (ctx *RouteContext) ParentData(key string) any
ParentData returns data loaded by a parent route's DataLoader.
func (*RouteContext) Query ¶
func (ctx *RouteContext) Query(name string) string
Query returns a URL query parameter.
func (*RouteContext) QueryInto ¶ added in v0.27.0
func (ctx *RouteContext) QueryInto(dst any) error
QueryInto decodes the request's URL query parameters into dst (a pointer to a struct) using `query` struct tags — the typed companion to Query.
Where Query(name) returns a single raw string, QueryInto binds the entire query string into a typed struct, applying tag defaults and reporting a field-named error on a malformed value:
type Filters struct {
Q string `query:"q"`
Page int `query:"page,default=1"`
Tags []string `query:"tags"`
}
var f Filters
if err := ctx.QueryInto(&f); err != nil { /* 400 */ }
The same query package powers island-side URL state, so a server loader and a browser island agree on one typed representation of the URL. See package query.
func (*RouteContext) SetHandlerError ¶
func (ctx *RouteContext) SetHandlerError(err error)
SetHandlerError records an error to be dispatched through the error handler after the handler returns. This replaces panic-based error propagation.
type Router ¶
type Router struct {
// contains filtered or unexported fields
}
Router builds an http.Handler from a route tree.
func (*Router) AddDir ¶
func (r *Router) AddDir(root string, opts FileRoutesOptions) error
AddDir scans a directory tree and registers its file-based routes.
func (*Router) Build ¶
Build compiles the router into an http.Handler. If route registration fails, the returned handler reports the build error as HTTP 500 instead of crashing the process.
func (*Router) BuildChecked ¶
BuildChecked compiles the router into an http.Handler and returns route registration errors such as invalid or conflicting patterns.
func (*Router) Handle ¶
func (r *Router) Handle(pattern string, handler http.Handler, middleware ...Middleware)
Handle registers a raw HTTP handler alongside page routes.
func (*Router) RevalidatePath ¶
RevalidatePath invalidates cache validators for the provided path prefix.
func (*Router) RevalidateTag ¶
RevalidateTag invalidates cache validators for the provided tag.
func (*Router) Revalidator ¶
func (r *Router) Revalidator() *server.Revalidator
Revalidator returns the router-wide in-memory revalidator.
func (*Router) SetError ¶
func (r *Router) SetError(handler ErrorHandler)
SetError sets the default 500 handler.
func (*Router) SetLayout ¶
func (r *Router) SetLayout(layout LayoutFunc)
SetLayout sets the default layout for all routes.
func (*Router) SetNavigationHead ¶ added in v0.42.0
SetNavigationHead registers the framework-owned navigation-runtime head builder RouteContext carries into PageState.Head. server.App.Mount calls this automatically (via server.NavigationConfigurable) when the owning App has EnableNavigation set, so a file-routed app needs only app.EnableNavigation() in its composition root.
func (*Router) SetNotFound ¶
func (r *Router) SetNotFound(handler PageHandler)
SetNotFound sets the 404 handler.
func (*Router) SetRevalidator ¶
func (r *Router) SetRevalidator(revalidator *server.Revalidator)
SetRevalidator replaces the router-wide in-memory revalidator used for automatic ETags and explicit path/tag invalidation.
func (*Router) UseObserver ¶
func (r *Router) UseObserver(observer server.RequestObserver)
UseObserver appends a request observer to the supported router extension surface.
Source Files
¶
- callerpath.go
- dirmodule.go
- exprcache.go
- exprlower.go
- fileconfig.go
- filecss.go
- fileeval.go
- filelayout.go
- filemeta.go
- filemodule.go
- filemodule_warnings.go
- fileprogram.go
- filesystem.go
- image_picture.go
- metadata_conventions.go
- modulesource.go
- program_render.go
- query.go
- renderprofile.go
- route.go
- shared_component.go
- test_helper.server.go