golazy.dev golazy.dev / lazyassets Index | Files

package lazyassets

import "golazy.dev/lazyassets"

Package lazyassets registers, fingerprints, serves, and exports application assets.

In a GoLazy application, lazyapp registers Public files and generated asset sources into one Registry, installs view helpers such as asset_path and stylesheet, and serves registered files as the final public fallback. lazyapp passes Registry.Helpers to lazyview so templates can render the final asset URLs without naming lazyassets directly.

The package is also independently usable for static file serving or deploy preparation:

registry := lazyassets.New()
err := registry.AddFS(os.DirFS("public"))
if err != nil {
	return err
}
http.Handle("/", registry.Handler(nil))

Registered assets keep a logical path, such as /styles.css, and usually get a permanent content-hashed path, such as /styles-2c26b46b68ff.css. Handler serves both paths. View helpers return the permanent path when one exists, so browser caches can keep immutable assets while templates still use stable source names.

A Registry builds its manifest as assets are added. Callers do not create a manifest first: Add, AddReader, and AddFS compute hashes, ETags, integrity values, permanent paths, and CSS URL rewrites automatically. CSS url(...) references that point at other registered local assets are rewritten to the target asset's permanent path; remote URLs, data URLs, fragments, and missing assets are left unchanged.

For deployment, Upload writes registered files through lazystorage and Unpack writes them to a local directory. Both can choose logical paths, permanent paths, or both.

Constants

Functions

Types

type Asset

Asset describes one registered asset as it appears in a Manifest.

type Asset struct {
	// Path is the logical public path, for example /styles.css.
	Path	string	`json:"path"`
	// Permanent is the content-hashed public path, for example
	// /styles-<hash>.css. It is empty for development-mode or ignored assets.
	Permanent	string	`json:"permanent,omitempty"`
	// ContentType is the MIME type sent by Handler and written by Upload.
	ContentType	string	`json:"content_type,omitempty"`
	// Size is the served byte length, or -1 when development mode reads the
	// file each time and the size is not fixed in the manifest.
	Size	int64	`json:"size"`
	// Hash is the full SHA-256 hex digest of the final served bytes.
	Hash	string	`json:"hash,omitempty"`
	// ETag is the strong ETag for the final served bytes.
	ETag	string	`json:"etag,omitempty"`
	// Integrity is the subresource-integrity value for the final served bytes.
	Integrity	string	`json:"integrity,omitempty"`
	// Source identifies the source file or generator, when supplied.
	Source	string	`json:"source,omitempty"`
	// Generated is true for assets registered from in-memory/generated bytes.
	Generated	bool	`json:"generated,omitempty"`
	// Ignored is true for oversized assets that are served by logical path but
	// skipped by hashing, permanent URL generation, and rewriting.
	Ignored	bool	`json:"ignored,omitempty"`
	// contains filtered or unexported fields
}
func (a *Asset) Open

Open opens the asset content that Handler, Upload, and Unpack serve or write.

func (a *Asset) Open() (io.ReadCloser, error)

type AssetOption

AssetOption configures Add and AddReader.

type AssetOption func(*assetOptions)
func AssetSource

AssetSource records name as the source for one registered asset.

func AssetSource(name string) AssetOption
func ContentType

ContentType sets the asset MIME type instead of inferring it from the path and content.

func ContentType(contentType string) AssetOption
func ReplaceAsset

ReplaceAsset allows Add or AddReader to replace a previously registered asset with the same logical path.

func ReplaceAsset() AssetOption

type CachePolicy

CachePolicy is the literal Cache-Control value used when serving or uploading logical and permanent asset paths.

type CachePolicy string

type Manifest

Manifest is a snapshot of all registered assets and their computed metadata.

type Manifest struct {
	Assets []Asset `json:"assets"`
}

type OpenFunc

OpenFunc opens an asset stream for AddReader or filesystem-backed records.

type OpenFunc func() (io.ReadCloser, error)

type Option

Option configures New.

type Option func(*Options)
func WithBaseURL

WithBaseURL makes helpers return absolute asset URLs while keeping request routing and storage keys path-based.

func WithBaseURL(baseURL string) Option
func WithCSSURLRewrite

WithCSSURLRewrite enables or disables rewriting local CSS url(...) references to permanent asset URLs.

func WithCSSURLRewrite(enabled bool) Option
func WithCachePolicies

WithCachePolicies sets Cache-Control values for logical and permanent paths.

func WithCachePolicies(logical, permanent CachePolicy) Option
func WithDevelopmentMode

WithDevelopmentMode serves logical asset paths directly without permanent hashed URLs, cache headers, ETags, integrity values, or CSS URL rewriting.

func WithDevelopmentMode(enabled bool) Option
func WithHashLength

WithHashLength sets the number of hexadecimal hash characters inserted into permanent paths.

func WithHashLength(length int) Option
func WithMaxAssetSize

WithMaxAssetSize sets the largest asset read into the fingerprinting pipeline.

func WithMaxAssetSize(size int64) Option
func WithOversizePolicy

WithOversizePolicy sets the behavior for assets larger than MaxAssetSize.

func WithOversizePolicy(policy OversizePolicy) Option
func WithURLPrefix

WithURLPrefix mounts assets below prefix.

func WithURLPrefix(prefix string) Option

type Options

Options configures a Registry.

type Options struct {
	// URLPrefix mounts registered assets below a path prefix. With /assets, an
	// asset added as /app.js is served and reported as /assets/app.js.
	URLPrefix	string
	// MaxAssetSize limits assets that are read into the fingerprinting pipeline.
	// Negative values disable the limit.
	MaxAssetSize	int64
	// OversizePolicy selects behavior for assets larger than MaxAssetSize.
	OversizePolicy	OversizePolicy
	// HashLength is the number of hexadecimal hash characters inserted into
	// permanent paths.
	HashLength	int
	// LogicalCache is sent for logical paths such as /styles.css.
	LogicalCache	CachePolicy
	// PermanentCache is sent for permanent paths such as /styles-<hash>.css.
	PermanentCache	CachePolicy
	// RewriteCSSURLs rewrites local CSS url(...) references to permanent paths
	// for registered target assets.
	RewriteCSSURLs	bool
	// BaseURL makes helpers and rewritten CSS/importmap URLs absolute without
	// changing the request paths that Handler serves or Upload writes.
	BaseURL	string
	// Development disables permanent paths, cache headers, ETags, integrity
	// values, and CSS rewriting so filesystem assets can be served fresh.
	Development	bool
}

type OversizePolicy

OversizePolicy controls how disk or generated assets larger than MaxAssetSize participate in hashing, rewriting, serving, and export.

type OversizePolicy int

type Registry

Registry owns the registered asset index.

It maps logical request paths and content-hashed permanent request paths to the same Asset records. The manifest is derived from the registered records; callers do not need to load or create one before serving.

type Registry struct {
	// contains filtered or unexported fields
}
func New

New creates an empty Registry.

By default, logical paths are revalidation-friendly, permanent paths are immutable, CSS local URLs are rewritten, and permanent paths use a 12 character hash prefix.

func New(options ...Option) *Registry
func (r *Registry) Add

Add registers an in-memory asset at path.

Generated assets are fingerprinted like filesystem assets and appear in the manifest with Generated set. The content slice is copied before registration.

func (r *Registry) Add(path string, content []byte, options ...AssetOption) error
func (r *Registry) AddFS

AddFS registers every file in files under its slash-separated path.

AddFS computes permanent paths and manifest metadata immediately unless development mode or the oversize policy keeps an asset out of the fingerprinting pipeline. Directories are skipped.

func (r *Registry) AddFS(files fs.FS, options ...SourceOption) error
func (r *Registry) AddReader

AddReader registers an asset opened on demand by open.

In production mode AddReader reads the content during registration so it can compute the hash, ETag, integrity value, permanent path, and CSS rewrites. In development mode it keeps open and reads the asset for each request.

func (r *Registry) AddReader(path string, open OpenFunc, options ...AssetOption) error
func (r *Registry) Empty

Empty reports whether the Registry has no registered assets.

func (r *Registry) Empty() bool
func (r *Registry) Handler

Handler serves registered assets and falls through to next for misses.

Requests are matched against logical paths such as /styles.css and permanent paths such as /styles-<hash>.css. Requests for / and paths ending in / look for index.html below that directory. Only GET and HEAD are served; other methods for known asset paths return 405 with Allow: GET, HEAD. Unknown paths are passed to next.

The original logical files remain available through Handler unless an asset was skipped entirely by OversizeSkipServing. Permanent paths are generated when assets are added; no separate manifest creation step is required.

func (r *Registry) Handler(next http.Handler) http.Handler
func (r *Registry) Helpers

Helpers returns lazyview-compatible template helpers for registered assets.

lazyapp calls Helpers during startup and adds the returned functions to lazyview. Templates then use helpers such as asset_path, stylesheet, importmap, asset_integrity, and the compatibility alias permalink.

func (r *Registry) Helpers() map[string]any
func (r *Registry) Integrity

Integrity returns the subresource-integrity value for assetPath.

Development-mode and ignored oversized assets do not have integrity metadata, so Integrity returns an empty string for those registered assets.

func (r *Registry) Integrity(assetPath string) (string, error)
func (r *Registry) Manifest

Manifest returns a snapshot of the current registered assets.

The manifest is computed from assets already added to the Registry; callers do not need to create or load it before calling Handler, Path, Upload, or Unpack.

func (r *Registry) Manifest() Manifest
func (r *Registry) MustPath

MustPath is like Path, but panics on error.

func (r *Registry) MustPath(assetPath string) string
func (r *Registry) Path

Path returns the URL to use for assetPath in HTML or generated output.

It returns the permanent content-hashed path when the asset has one. In development mode and for ignored oversized assets, it returns the logical path. WithBaseURL makes the returned URL absolute.

func (r *Registry) Path(assetPath string) (string, error)
func (r *Registry) ServeHTTP

ServeHTTP serves the Registry as a standalone http.Handler.

func (r *Registry) ServeHTTP(w http.ResponseWriter, req *http.Request)
func (r *Registry) Unpack

Unpack writes registered assets and manifest.json into dir.

By default it writes both logical and permanent asset files. The manifest is always written and is derived from the Registry's current asset records.

func (r *Registry) Unpack(dir string, options ...UnpackOption) error
func (r *Registry) Upload

Upload writes registered assets to object storage.

The default mode writes only permanent content-hashed paths plus manifest.json, which is the usual shape for CDN or static-file ingress deployments.

func (r *Registry) Upload(ctx context.Context, storage lazystorage.Writer, options ...UploadOption) error

type Source

Source registers assets in a Registry.

Generated asset packages can implement Source so lazyapp or custom startup code can add generated files to the same Registry as filesystem assets.

type Source interface {
	Assets(*Registry) error
}