gui/docs/ref/wails-v3/contributing/asset-server.mdx
Snider 4bdbb68f46
Some checks failed
Security Scan / security (push) Failing after 9s
Test / test (push) Failing after 1m21s
refactor: update import path from go-config to core/config
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-14 10:26:36 +00:00

202 lines
6.9 KiB
Text
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Asset Server
description: How Wails v3 serves and embeds your web assets in development and production
sidebar:
order: 4
---
## Overview
Every Wails application ships a **single native executable** that combines:
1. Your *Go* backend
2. A *Web* frontend (HTML + JS + CSS)
The **Asset Server** is the glue that makes this possible.
It has **two operating modes** that are selected at compile-time via Go build
tags:
| Mode | Tag | Purpose |
|------|-----|---------|
| **Development** | `//go:build dev` | Fast iteration with hot-reload |
| **Production** | `//go:build !dev` | Zero-dependency, embedded assets |
The implementation lives in
`v3/internal/assetserver/` with clear file splits:
```
assetserver_dev.go # ⬅️ runtime dev server
assetserver_production.go # ⬅️ embedded server
assetserver_darwin.go # OS-specific helpers (same for linux/windows)
asset_fileserver.go # Shared static file logic
content_type_sniffer.go # MIME type detection
ringqueue.go # Tiny LRU for mime cache
```
---
## Development Mode
### Lifecycle
1. `wails3 dev` boots and **spawns your frontend dev server**
(Vite, SvelteKit, React-SWC …) by running the task defined in
`build/Taskfile.yml` (usually `npm run dev`).
2. Wails starts the **Dev Asset Server** listening on `localhost:<random>` and
tells the Go runtime to load `http://<host>:<VITE_PORT>` as the window URL.
3. Incoming requests are handled by `assetserver_dev.go`:
```
┌─────────┐ /runtime/... ┌─────────────┐
│ Browser │ ── native bridge ───▶ │ Runtime │
├─────────┤ └─────────────┘
│ JS │ / (index.html) proxy / -> Vite
└─────────┘ ◀─────────────┐
AssetServer │
┌────────────┐
│ Vite Dev │
│ Server │
└────────────┘
```
4. Static files (`/assets/logo.svg`) are **served directly from disk** via
`asset_fileserver.go` (for speed) while anything unknown is **proxied** to
the framework dev server, giving you *instant* hot-module replacement.
### Features
* **Live Reload** Vite/Snowpack/… injects HMR WebSocket; Wails only has to
proxy it.
* **Source Map Support** because assets are not bundled, your browser devtools
map errors back to original source.
* **No Go Re-compile** Only the frontend rebuilds; Go code stays running until
you change `.go` files.
### Switching Frameworks
The dev proxy is **framework-agnostic**:
* The root `Taskfile.yml` task file injects two env vars:
`APP_NAME` & `WAILS_VITE_PORT`.
* Taskfiles for each template emit those vars before running their dev servers.
* `assetserver_dev.go` simply proxies to that target.
Add a new template → define its dev task → Asset Server just works.
---
## Production Mode
When you run `wails3 build` the pipeline:
1. Runs the frontend **production build** (`npm run build`) producing
`/frontend/dist/**`.
2. **Embeds** that folder into `go:embed` FS at compile time (see
`bundled_assetserver.go` generated file).
3. Compiles the Go binary with `-tags production` (implicit).
### Request Handling
```go
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// 1. Try embedded static assets (exact path)
// 2. Fallback to index.html for SPA routing
// 3. Sniff content-type if extension unknown
// 4. Set strong cache headers
}
```
* **MIME Detection** If the build tool produced extension-less files (e.g.
`/assets/manifest`) `content_type_sniffer.go` inspects the first 512 bytes and
caches the result in a tiny lock-free LRU.
* **Ring Queue Caching** Frequently accessed assets (logo, CSS) are kept
in-memory for the lifetime of the app, removing the need for disk or embed FS
lookups.
* **Security Headers** Disallows `file://` navigation, enables `nosniff`.
Because everything is embedded, the shipped binary has **no external
dependencies** (even on Windows).
---
## Bridging Dev ↔ Prod
Both modes expose the **same public interface**:
```go
type AssetServer interface {
URL() string // dev: http://localhost:34115, prod: wails://app
Open() error // start listening
Close() // graceful shutdown
}
```
`pkg/application` happily uses whichever implementation was compiled in, meaning
**your application code does not change** between `dev` and `build`.
---
## How Frontend Frameworks Integrate
### Templates
Each official template (React, Vue, Svelte, Solid…) contains:
* `build/Taskfile.yml`
* `frontend/vite.config.ts` (or equivalent)
They export several tasks including:
| Task | Purpose |
|------|---------|
| `dev:frontend` | Starts the framework dev server on a **random free port** and prints it to stdout (`PORT=5173`). |
| `build:frontend` | Produces static assets into `dist/` + manifest for cache-busting. |
`internal/commands/dev.go` sets the `FRONTEND_DEVSERVER_URL` env var using the hard-coded `localhost` as the `host` and the emitted `VITE_PORT` env var as the `port` and launches the **Dev Asset Server**.
Frameworks remain fully decoupled from Go:
* No need to import Wails JS SDK at build time the runtime injects it at
window creation.
* Any framework with an HTTP dev server can plug in.
---
## Extending / Customising
Need custom headers, auth, or gzip?
1. Implement `type Middleware func(http.Handler) http.Handler`
2. Register via `internal/assetserver/options.go`
3. For prod, remember to add the same middleware in `assetserver_production.go`.
---
## Key Source Files
| File | Role |
|------|------|
| `assetserver_dev.go` | Reverse proxy + disk file server |
| `assetserver_production.go` | Embedded FS handler |
| `options.go` | Config struct parsed from `pkg/options/assetserver` |
| `build_dev.go` / `build_production.go` | Build-tag wrappers selecting correct implementation |
| `bundled_assetserver.go` | Generated embed data (only present in production builds) |
---
## Gotchas & Debugging
* **White Screen in Prod** usually SPA routing: ensure `History API Fallback`
is enabled in dev and `index.html` fallback works in prod.
* **404 in Dev** mis-matched `FRONTEND_DEV_PORT`; run with
`WAILSDEV_VERBOSE=1` to print every proxied request.
* **Large Assets** they are embedded; consider
[`assetserver.WithExternalDir("/path")`](https://pkg.go.dev) to load from disk.
---
You now know how the Wails **Asset Server** feeds your web code to the native
window in both **development** and **production**.
Master this layer and you can debug loading issues, add middlewares, or even
swap in a completely different frontend tool-chain with confidence.