Releases: typelate/muxt
v0.20.0-dev.4 — File Uploads
Status: Prerelease. The headline feature for v0.20.0 is multipart file upload support; this draft will be revised as v0.20.0 approaches.
Full Changelog: v0.19.6...v0.20.0-dev.4
What's New at a Glance
File uploads, finally type-safe. A new multipart call-site parameter lets handlers receive uploaded files as typed struct fields — no more reaching for *http.Request and parsing forms by hand.
Path helpers always work. TemplateRoutePaths methods are now exported regardless of how you cased your handler method, so an unexported list(ctx) handler still produces a .Path.List you can use from templates.
Pre-built binaries. Releases now ship Linux, macOS, and Windows binaries (arm64 and amd64) via GoReleaser — no need to go install to try a release.
File Uploads
Until now, multipart/form-data requests forced you to drop down to *http.Request and call ParseMultipartForm yourself, then dig through request.MultipartForm.File for each upload. Muxt couldn't help you — it didn't know about files.
This release adds a multipart parameter that mirrors the existing form parameter, but knows about file fields.
{{define "POST /upload 201 Upload(ctx, multipart)"}}{{end}}type UploadForm struct {
Title string `name:"title"`
Tags []string `name:"tag"`
Avatar *multipart.FileHeader `name:"avatar"` // single file
Photos []*multipart.FileHeader `name:"photos"` // multiple files for one name
}
func (s Server) Upload(ctx context.Context, form UploadForm) (Result, error) {
f, err := form.Avatar.Open()
if err != nil { return Result{}, err }
defer f.Close()
// ... read and store the file ...
}Why this matters: File uploads are one of the last places muxt forced you out of the typed-handler world. Avatar uploaders, document submission forms, photo galleries, CSV importers — all of these can now be expressed as a method that takes a struct, with files as first-class fields. Errors come back as 400s the way you'd expect; your method only runs when the body parsed successfully.
Two ways to use it
- Struct mode (recommended): the receiver param is your own struct. Text fields bind by name like
formalready does; file fields must be*multipart.FileHeader(single) or[]*multipart.FileHeader(multiple). - Raw mode (escape hatch): the receiver param is
*multipart.Formand you walk it yourself. Useful for dynamic field sets where you don't know names ahead of time.
Knobs
formandmultipartare mutually exclusive in one call-site.ParseMultipartFormpopulatesrequest.PostForm, somultipartis a strict superset.- Default upload buffer is 32 MiB — same default as
http.Request.ParseMultipartForm. Override with--output-multipart-max-memory=<size>(e.g.64MB,128MiB). Data exceeding the limit spills to the OS temp directory per the standardmime/multipartsemantics. - Parse errors return 400 automatically and are captured into
td.errListwithtd.errStatusCode = http.StatusBadRequest. (formsilently ignores parse errors;multipartdoes not, because malformed multipart bodies almost always indicate a real client-side fault.)
See the new docs in docs/reference/call-parameters.md and the walkthrough in cmd/muxt/testdata/howto_multipart_file_upload.txt.
TemplateRoutePaths Methods Are Always Exported
TemplateRoutePaths is the type muxt generates so templates can write {{.Path.GetUser 42}} instead of hardcoding URLs. Previously, an unexported handler method like list(ctx) produced an unexported list() method on TemplateRoutePaths, which Go templates can't call — meaning your link helpers silently broke when you happened to use a lowercase handler name.
Generated TemplateRoutePaths methods are now always exported regardless of the handler method's casing. list(ctx) produces .Path.List, getUser(ctx, id) produces .Path.GetUser, and so on.
Why this matters: Your handler can stay package-private (a common Go convention for HTTP handlers) and your templates still get working link helpers. No more "why is .Path.list not rendering?" debugging.
New compile-time errors: Two cases that used to silently misbehave now fail at generation:
- Method name collision when an exported and unexported handler differ only in first-letter casing (e.g., both
Listandlistwould map to.Path.List). - Unexportable identifiers for handlers starting with characters that have no uppercase form (e.g.,
_list).
The fix in both cases is to rename the handler. See docs/reference/known-issues.md for the full description.
Pre-Built Binaries
Releases now include archives for Linux, macOS, and Windows (arm64 and amd64), built by GoReleaser. Grab the asset for your platform from the release page rather than running go install.
Bug Fixes
- No more panic on duplicate handler names when route definitions without function calls appear before the duplicates. The disambiguation logic also now produces correctly cased identifiers when the duplicate handler name is lowercase (e.g., handler
x()now producesXrather thanxin the generatedCallingsuffix).
Iterating Toward v0.20.0
Subsequent prereleases will refine these notes as the release approaches. Expected refinements:
- More usage examples for the
multipartparameter in the docs/skills. - Any follow-up adjustments to the
--output-multipart-max-memoryflag based on real-world feedback.
If you try the multipart support and hit something unexpected, please open an issue — the API is still malleable before the v0.20.0 final.
v0.19.0 - Add TemplateData String Method
Features
- TemplateData now implements a String() method that returns a noop, making templates safer when using {{.}} (especially with .Header method)
Dependency Bumps
- Updated golang.org/x/tools to v0.41.0
- Updated golang.org/x/net to v0.49.0
v0.18.3
Full Changelog: v0.18.1...v0.18.3
Features
- Add --output-htmx-helpers flag to muxt generate – Generate HTMX helper code alongside templates
- Show full template filepath in check output – Better error diagnostics with complete file paths
- Add absolute call path to check output – Improved error messages showing the full call path
- Print better check error package qualifiers – Enhanced error message formatting for package names
Fixes
- Hide usage information for non-flag-based errors – Only show usage when relevant
Improvements
- Flatten examples directory structure
- Update test coverage for error printing
- Update README with muxt command usage and clarified usage instructions
Dependencies
- Bump github.com/typelate/check from v0.0.6 to v0.1.0
- Bump github.com/typelate/dom from v0.7.0 to v0.7.1
- Bump github.com/crhntr/txtarfmt from v0.0.9 to v0.2.0
- Remove github.com/maxbrunsfeld/counterfeiter (replaced by testify/mock in simple example)
- Add new transitive dependencies from txtarfmt v0.2.0: goccy/go-yaml and google/go-cmp
v0.18 - New Static Analysis Commands and Improved Flag Names
Full Changelog: v0.17.4...v0.18.1
What's New at a Glance
CLI Reorganization: Flags now clearly separate what muxt searches for (--use-*) from how it generates code (--output-*).
Introspection Commands: New list-template-callers and list-template-calls commands help understand template usage patterns.
Reusable Components: Path prefix and logger parameters let library consumers mount your routes under custom paths while keeping all generated path helpers working correctly.
Code Generation Flexibility: Choose between single-file output (default) or per-template-file generation. Automatic cleanup of orphaned files. Transparent generation comments with exact command used.
Breaking Changes
CLI Flag Renaming
All flags have been renamed to clarify their purpose:
- Flags starting with
--use-control what muxt searches for in your codebase - Flags starting with
--output-control how generated code looks
Flag Mapping:
Old flags still work. So no change is required (if you don't mind warnings in your generate logs).
| Old Flag | New Flag |
|---|---|
--templates-variable |
--use-templates-variable |
--receiver-type |
--use-receiver-type |
--receiver-type-package |
--use-receiver-type-package |
--receiver-interface |
--output-receiver-interface |
--routes-func |
--output-routes-func |
--template-data-type |
--output-template-data-type |
--template-route-paths-type |
--output-template-route-paths-type |
--logger |
--output-routes-func-with-logger-param |
--path-prefix |
--output-routes-func-with-path-prefix-param |
--find-templates-variable |
--use-templates-variable |
--find-receiver-type |
--use-receiver-type |
--find-receiver-type-package |
--use-receiver-type-package |
Who's affected: Anyone with go:generate directives, Makefiles, shell scripts, or CI configuration that invokes muxt with flags.
Why this change: As muxt gained more configuration options, the old naming became ambiguous. More configuration flags require clearer (often longer) names. The new structure separates "what to search for in your code" (--use-*) from "how to generate output" (--output-*), making each flag's purpose immediately clear and scaling better as features expand.
Migration steps:
- Search your project:
grep -r "muxt generate" .orgrep -r "muxt check" . - Update flag names using the mapping above
- Verify with
muxt --help - Test locally:
go generate ./... - Update any documentation referencing old flags
Reusable Components & Libraries
The headline feature of this release: muxt-generated routes can now be mounted flexibly by library consumers.
Path Prefix Parameter
Use --output-routes-func-with-path-prefix-param to add a pathPrefix string parameter to your routes function. This enables library consumers to mount your routes under any path.
Why this matters: You can publish a reusable admin panel, authentication flow, or API component as a Go module. Consumers mount it wherever they want (/admin, /auth, /api/v1) and all generated path helpers (.Path.Login, .Path.Dashboard, etc.) automatically adjust.
How it works: The generated TemplateRoutePaths type includes methods that prepend the path prefix to all route paths. Links in templates using .Path.Foo remain correct regardless of where the component is mounted.
Example use case: An admin package exports AdminRoutes(mux, admin, pathPrefix). Consumer A mounts at /admin, Consumer B mounts at /backoffice. Both work correctly without modification.
Logger Parameter
Use --output-routes-func-with-logger-param to add an *slog.Logger parameter to your routes function. The generated code logs request information (debug level) and template errors (error level).
Why this matters: Library consumers can integrate your component with their logging infrastructure. They pass their configured logger and get structured logs with appropriate context, all without you writing logging boilerplate.
What gets logged:
- Request info: HTTP method, route pattern, parsed parameters (debug level)
- Template execution errors: template name, error details, context (error level)
Example use case: A library exports authenticated routes. The consumer passes their application logger configured with trace IDs, user context, and custom fields. All route activity appears in their unified log stream.
New Commands
muxt list-template-callers
Shows which Go functions invoke specific templates.
When to use:
- Before renaming a template, find all call sites to prevent runtime "template not found" errors
- Understand which code paths trigger specific templates
- Audit template usage across large codebases
Output: File locations, line numbers. Supports --format=json for tooling integration.
muxt list-template-calls
Lists all template.Execute and template.ExecuteTemplate calls found in your code.
When to use:
- Verify templates are actually invoked, not just defined
- Identify orphaned templates wasting binary size
- Audit which templates your code calls versus which exist
Output: Template names, call sites, file locations. Supports --format=json.
Filtering
Both commands support --filter=regex to narrow results.
Examples:
--filter="^GET /admin"- Find all admin routes--filter="POST"- Find all POST handlers--filter="api\\.v[0-9]"- Find all versioned API endpoints
JSON Output
All list commands support --format=json with complete file locations and line numbers. Use this for:
- CI checks (fail build if certain templates are called from wrong packages)
- Documentation generation
- Custom analysis scripts
- Integration with other tools
Code Generation Improvements
Single-File Output (Default)
Generated code now consolidates into one template_routes.go file per package instead of multiple *_muxt.go files scattered throughout your source tree.
Why this helps:
- Cleaner git diffs (one file changes instead of many)
- IDE file listings don't intermingle generated and handwritten code
Opt-out: Use --output-multiple-files if you prefer separate files per template source.
Automatic Cleanup
The generator removes old generated files when template patterns change or files are renamed. Previously, refactoring left orphaned generated files sometimes causing compilation errors.
How it works: Muxt tracks which files it generated (via // Code generated markers) and removes them if no longer needed. Your handwritten code is never touched.
What this fixes: No more "undefined: HandlerName" errors after refactoring because an old generated file still references deleted types.
Transparent Generation Comments
The // Code generated comment now includes the exact muxt command with all flags used.
Why this helps: Six months from now, you know exactly how to regenerate. Critical for debugging CI differences and onboarding new developers.
Example:
// Code generated by muxt generate --use-receiver-type=Server --output-routes-func-with-logger-param; DO NOT EDIT.Template Analysis
Unused Template Detection
muxt check now reports templates that are defined but never called.
What this catches:
- Templates for deleted features still taking up binary space
- Abandoned experiment templates
- Typos in template names (defined as "usre-profile" but called as "user-profile")
Impact: Smaller binaries, clearer codebase, faster template parsing.
Smarter Reference Handling
Template references in generated code are now sorted, deduplicated, and case-sensitive matched.
Fixes:
- Duplicate imports removed
- Consistent ordering produces deterministic diffs
- Case-sensitive matching prevents runtime panics from identifier mismatches
Documentation Reorganization
All documentation now follows the Diátaxis framework, organizing by user goal:
- Tutorials: Step-by-step learning paths
- How-To Guides: Task-focused recipes (forms, HTMX, testing, logging)
- Reference: Complete flag listings, syntax rules, type system
- Explanation: Design philosophy, architectural decisions, tradeoffs
Why this helps: Find answers faster. Need forms? Go to how-to guides. Need flag syntax? Check reference. Want design rationale? Read explanations.
Test files as documentation: Every test in cmd/muxt/testdata/ includes descriptive comments. Tests serve as executable examples with expected input/output.
err_*prefix for error condition testshowto_*for task-oriented examplesreference_*for feature documentationtutorial_*for learning examples
Other Improvements
Better Help
muxt helpcommand with improved flag descriptions (built with Cobra)- Command aliases:
muxt chkforcheck,muxt docfordocumentation muxt version --verboseshows dependency versions and build info
Main Package at Repository Root
The main.go entrypoint is now at the repository root in addition to cmd/muxt/, making it easier to find and run with go run github.com/typelate/muxt@latest.
Upgrading
Steps:
-
Install v0.18.1 (latest):
go install github.com/typelate/muxt/cmd/muxt@v0.18.1
-
Update flag usage:
Search for muxt invocations (grep -r "muxt generate" .) and update flag names using the mapping in Breaking Changes above. -
Regenerate code:
go generate ./...
The generator creates
template_routes.goand cleans up old files. -
Verify:
- Confirm old generated ...
v0.17 - Migration from @crhntr
Muxt moved from https://github.com/crhntr/muxt to the Typelate organization. There is still a fork over there but this is the main repository moving forward. All tags for the previous home have been removed so you will only see v0.17.0 moving forward. The previous repository has been archived.
If you vendor muxt as a tool, run go get -tool github.com/typelate/muxt/cmd/muxt@v0.17.0 to fetch the latest revision.
If you install it globally, run go install github.com/typelate/muxt/cmd/muxt@v0.17.0.
Now that muxt is packaged with other hypermedia tools, I am inspired to continue chipping away at improving it as I write hypermedia-driven applications.