porang

package module
v0.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 31, 2026 License: MIT Imports: 7 Imported by: 0

README

porang

The Arandu view layer: templ for markup, HTMX for interaction, Tailwind for style. It is a binary, and it is never Node.

A project using it still runs with git clone && aru dev. No node_modules, no package.json, no JavaScript lockfile, nothing installed beyond Go. Having a build step is allowed; being Node is not.

What is in the binary

HTMX, Alpine and the compiled stylesheet are embedded with go:embed and served from /_arandu/assets/<hash>/<name>.

That is not preference. SecurityHeaders sets script-src 'self', so loading HTMX from a CDN would mean loosening the CSP — paying in security for convenience. It also keeps the deploy at one binary: no asset publishing step, no CDN to invalidate, no storage:link.

The hash comes from the content, so upgrading HTMX changes the URL and nobody serves a stale script. A request carrying an old hash still gets the file, with no-cache instead of immutable: a stale reference degrades into a slow page, never a broken one.

The line that matters most

@layout.Base(layout.Props{Title: "Sign in", CSRFToken: token})

Base puts hx-headers on <body>, carrying the CSRF token into every HTMX request made from anywhere on the page. Without it, every hx-post and hx-delete fails the CSRF check — and the failure looks like a session problem, so people spend an afternoon on it.

It lives here, in the framework's own layout, so nobody has to remember. aru doctor checks for it too.

Components are functions

@components.Button(components.ButtonProps{
    Label:   "Delete",
    Variant: components.Danger,
    HX:      components.HX{Delete: "/invoices/1", Confirm: "Delete this invoice?"},
})

There is no <nx-modal /> and no web component registered at runtime. A custom tag would be a second component system beside templ, resolved at runtime, with errors that only appear on screen. templ already gives composition, types and a compile error.

The HX struct exists for the same reason: hx-swep instead of hx-swap is a typo that silently does nothing, and a review misses it.

Where Alpine is allowed

Only when the state is all three at once: purely client-side, ephemeral (dies on reload with nothing lost), and invisible to the server.

Allowed: a dropdown being open, the selected tab, focus, an input mask, a confirmation before submitting.

Not allowed: anything that reaches the server, anything persisted, anything derived from data the server already has. If you are writing a business rule in x-data, the component should have been an HTMX fragment.

Building the stylesheet

tailwindcss -i assets/app.src.css -o assets/app.css --minify

The standalone binary, a single file, managed by aru and pinned by version — the same way Go handles its own toolchain. Never npx, never a package from npm.

What this package does not touch

The error page. It has to render when everything else is broken, including when the asset build failed, so it stays as inline html/template in observability/errorpage.

License

MIT, the same license Laravel uses. See LICENSE.md.

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

View Source
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 Mount

func Mount(r *httpx.Router)

Mount registers the asset route on a router.

It is a plain route rather than a kernel.Module: assets are not a feature, and a module that exists only to serve two files is a module people have to remember to register.

func URL

func URL(name string) string

URL returns the versioned path of an asset: /_arandu/assets/<hash>/htmx.min.js

The hash comes from the content, so upgrading HTMX changes the URL and no browser serves a stale script -- without anyone remembering to bump a version.

func Version

func Version() string

Version reports the embedded version of each asset, for `aru doctor` and for the debug page.

Types

This section is empty.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL