Documentation
¶
Overview ¶
Package spa serves a single-page app built by Vite from the Go server, in production from the embedded build and in development by proxying to the Vite dev server, so the app has one origin in both.
Index ¶
Constants ¶
const BuildDir = "_build"
BuildDir is where the app's content-hashed build output goes, set in vite.config.ts as build.assetsDir. Handler caches everything under it as immutable, so it must hold nothing else: files copied from public/ keep their names, and cached forever they could never change.
const DevNonce = "vite-dev"
DevNonce is the CSP nonce Vite puts on the scripts and styles it injects in development, such as React's refresh preamble, when vite.config.ts sets html.cspNonce to it. A server's development CSP accepts it (see server.New), so its policy can stay strict while it proxies Vite.
Variables ¶
This section is empty.
Functions ¶
func Built ¶ added in v0.3.0
Built reports whether dist holds a Vite build rather than the placeholder that lets the Go module build before the web app has. Whether Handler serves it or proxies depends on Options.DevServer instead, so decide anything else that differs in development, such as where generated files are read from, by that too.
func Handler ¶
Handler serves the Vite build in dist. For a request path it serves, in order: the file at that path, preferring a precompressed .br or .gz sibling the client accepts; a prerendered <path>/index.html; a 404 for a missing file, a path with a file extension in a directory dist holds; and otherwise index.html, so client-side routes work on reload, even those that end in a file name. Files under BuildDir are cached for a year; everything else carries an ETag to revalidate with, and index.html is gzipped for clients that accept it.
With opts.DevServer set, Handler instead proxies every request to it, including Vite's HMR WebSocket. Without one, it returns an error if dist holds no build.
func NegotiateLocale ¶ added in v0.8.0
NegotiateLocale picks the locale for the first preferred tag that has one: an exact match, ignoring case, else a locale with the same base language, so es-MX gets es. It returns fallback when nothing matches. It is the server's half of @parallelworks/i18n's negotiateLocale, and the two agree.
Types ¶
type Locales ¶ added in v0.8.0
type Locales struct {
// Available are the app's locales as the client names them, such as
// "en" and "es". None leaves index.html as it is.
Available []string
// Fallback is chosen when nothing matches. Defaults to Available[0].
Fallback string
// Cookie names a cookie holding the person's choice, which wins over
// Accept-Language.
Cookie string
}
Locales are the languages an app is translated into, which Handler chooses from for each request and writes to index.html as <html lang>. The client reads it back, as with @parallelworks/i18n's detectLocale(locales, {injected: document.documentElement.lang, ...}), so the first paint is already in the reader's language.
type Options ¶
type Options struct {
// Index rewrites index.html for a request, for example to add
// <base href>. It applies in development too, after Locales.
Index func(r *http.Request, html []byte) []byte
// Locales sets index.html's <html lang> to the reader's language.
Locales Locales
// NotFound reports whether a path other than the root that matches no
// file and no prerendered page is unknown to the app. Its shell is then
// sent with a 404, so crawlers see an honest status while the router
// renders its own page. By default every such path is a client-side
// route, sent with a 200.
NotFound func(r *http.Request) bool
// DevServer is the Vite dev server's URL, such as "http://localhost:5173".
// When set, Handler proxies to it whatever dist holds, so a build left
// from an earlier `vite build` is never served in its place. Set it only
// in development.
DevServer string
}
Options configure Handler.