go_solid

package module
v1.0.13 Latest Latest
Warning

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

Go to latest
Published: Aug 23, 2026 License: MIT Imports: 30 Imported by: 0

README

GO Solid

Native SolidJS templating for Go with HMR and Typesafety.

Maturity

Core API is defined and not likely to change significantly.

Tagged versions are available.

This library uses a "release-if-green" methodology. That means that the readiness of the codebase is only given by how well the testing suites have been written. Thus, always depend on a tagged version, never depend on latest.

Registry

This library expects a common folder for components and allows these components to be used as templates in Go using a qualified path from said folder.

By default, the registry folder is also where the library expects node_modules to be located if not overwritte (and if required)

bundler, err := solid.New(solid.Config{
    ...
    Components:   "./where/are/your/solid-components",
    ...
})

A given template auth/LoginForm.tsx are then referenced from the components dir as such:

renderedTemplate, err := bundler.Prepare("auth/LoginForm", props).Render()

Templating

Data is passed to the template using the standard props input for solidjs components.

In Go, this is expressed as any struct that can be json serialized, for instance a map[string]any:

rendered, err := bundler.Prepare("path/to/Component", map[string]any{"title": "Hello World"}).Render()

ForRequest(writer, request) sets status code and content type headers and handles most standard networking automatically

rendered, err := bundler.Prepare("path/to/Component", map[string]any{"title": "Hello World"}).
    ForRequest(writer, request).
    Render()

All networking behaviour can be altered on a per bundler#Prepare basis using SetHTTPBehaviour(configurator):

bundler.Prepare("ComponentName", props).
		ForRequest(writer, request).
		SetHTTPBehaviour(func(builder networking.RequestBehaviourBuilder) {
			builder.TransmitRenderedTemplate(/*...custom handler...*/).
				UponPropsMarshalingError(/*...custom handler...*/).
				UponRegistryReloadError(/*...custom handler...*/).
                SetSuccessCode(201 /*created*/)
		}).
		Render()
Caching & Workspace

To try and remain performant, the library uses a mem cache, but also writes bundled and parsed components to disk.

Caching can be configured in the Config and likewise can the location for the cached js, html, and css files and metafiles be set or overwritten there.

Given that this library does interact with your filesystem, and potentially project structure, directly, testing suites are run in docker on both linux and windows. More testing environments can be added.

To direct the library where to place its cache, set the Workspace field in the Config:

bundler, err := solid.New(solid.Config{
    ...
    Workspace: "./somewhere/with/write/access",
    ...
})

A new .go_solid directory will always be created in said workspace.

HMR

go_solid builds a two-way dependency index from esbuilds metafile output when a component is bundled. This index is used to, among other things, do module replacement during runtime if such is configured.

To enable HMR, provide your server's method for adding endpoints in the Bundler Config. Various adapters are already available in the hmr package.

// MuxLike is anything that can register an http.Handler under a string pattern
type MuxLike interface {
	Handle(pattern string, handler http.Handler)
}
// Same as above but with any return type
type RouterLike[T any] interface {
	Handle(pattern string, handler http.Handler) T
}
mux := http.DefaultServeMux();
bundler, err := go_solid.New(solid.New(solid.Config{
    ...
    HMR:    go_solid.HMRConfig{
        Mux: mux,
    },
})

To avoid potential cors issues, do ensure that the WS connections the library will make to any client visiting your servers endpoints, is directed at the same origin as the template the client has been served. (You can set HMR up over another port, but browsers may be perturbed by this)

Typesafety

go-solid introspects the types you have defined for your components and cross-references these definitions with the data provided when you call Prepare(component, props) from your code.

In case of a missing parameter or incompatible type, an error will be raised. This error is surfaced as the result of RenderCallBuilder#Render or, if a networking request has been provided (with RenderCallBuilder#ForRequest), as response to said request.

To alter when, or if, these checks should happen, a setting is exposed in the config:

Config{
    Types: &types.TypesConfig{
        Check: CHECK_RUNTIME_AND_BOOT // CHECK_BOOT, CHECK_RUNTIME, CHECK_NEVER
    }
}

How

Since 1.0.8 this library was moved to lilybw/go-solid-compiler which in turn uses a condensed tsgo fork (lilybw/typescript-go). That means that this library parses and transforms solidjs jsx components natively.

It is currently not possible to choose what version of solidjs/web to use for templating, as that is bundled with go-solid-compiler. However various options for what dev/prod variant to use are extended and customizable.

Roadmap

Ever since the introduction of tsgo, it is now possible to do rather sophisticated typegen and introspection. Likewise with the move to go 1.27 it is now possible to define rather sophisticated apis.

In v1.2.0 go-solid will introduce generated types and data validation in development to assure typesafety and ease of debugging.

From hereon, various "plug-in" like features will be made available, accessed as fields on a components props.

The already hinted-at "static" feature will introduce easy, yet secure, management and retrieval of static assets but relies on the former and has as such been slightly postponed.

Version 1.3.0 will introduce "navigation", allowing a reduced endpoint repressentaiton be delivered to this library from your code (however you see fit), then formatting that as nothing but fields on the "navigation" props property.

Note on version numbering: I dont know how to do versioning.

Adapters

go-solid is rather self-contained and should work with most existing projects.

One thing that is not, is the HMR implimentation and Bundler#ForRequest, which has to make certain assumptions to work.

If you find that these does not work for your project, I will gladly accept any PR adding support. You may also make an issue, however then it will depend on when I got time to see to it.

Documentation

Index

Constants

This section is empty.

Variables

Functions

This section is empty.

Types

type BehaviouralDefaults

type BehaviouralDefaults struct {
	// Define the default elements of the <head> tag to be included in every page.
	// These defaults can be modified upon any Bundler#Prepare call by using the method: WithHTMLHeadTags
	HeadSegment meta.Configurator[networking.HTMLHeadSegmentBuilder] `json:"-"`
	// Define the default behaviour of the http request handling. These defaults can be modified upon any Bundler#Prepare call by using the method: SetHTTPBehaviour
	Requests meta.Configurator[networking.RequestBehaviourBuilder] `json:"-"`
}

type Bundler

type Bundler struct {
	// contains filtered or unexported fields
}

func New

func New(cfg *Config) (*Bundler, error)

func (*Bundler) Close

func (b *Bundler) Close()

func (*Bundler) FromDisk added in v1.0.0

func (b *Bundler) FromDisk(key *caching.CacheKey) (*caching.Rendered, bool)

func (*Bundler) Prepare

func (this *Bundler) Prepare(component meta.QualifiedName, props any) RenderCallBuilder

TODO: expand props to varparam, construct props js object from safe-made reflect.Type and let an interface be implemented to enable a props property key overwrite

func (*Bundler) Registry

func (b *Bundler) Registry() *internal.ComponentRegistry

func (*Bundler) Render added in v0.0.12

func (this *Bundler) Render(component meta.QualifiedName, configurator meta.Configurator[RenderCallBuilder], props any) (*caching.Rendered, error)

type Config

type Config struct {
	// The absolute path to the directory containing the solidjs components.
	// The registry will scan this and all subdirectories, skipping directories prefixed with a dot (.)
	// or with the exact name "node_modules"
	Components meta.AbsoluteDirectoryPath

	// The absolute path to the directory where go_solid will place its .go_solid workspace directory, which contains the worker script and the disk cache.
	// Defaults to Components if not specified.
	Workspace meta.AbsoluteDirectoryPath

	// LogLevel gates all diagnostic output. Left unset it resolves to
	// logging.DEFAULT_LEVEL (errors only); set logging.LEVEL_DEBUG to have the
	// normalized config dumped at construction.
	//
	// The logger is process-global, so the most recent New wins.
	LogLevel logging.LogLevel

	// DisableCaching bypasses both the in-memory and on-disk caches, so every
	// render rebuilds from source.
	DisableCaching bool

	// Settings for code generation, solidjs transform application, worker pool size, and the like.
	//
	// Expects node_modules to be located within Config#Components by default. Can be overwritten using this sub-config.
	Generation *esbuild.BundlerConfig

	// Enable a filewatcher that watches the component dir to trigger registry updates when new component files are added.
	// This enables usecases which may attempt to ask for procedural component names, since the registry is constant otherwise.
	ReactiveRegistry bool

	// If you provide this config, bundle and cache all components in the registry on next boot (may take a moment).
	// This is purely a performance measure — every component is pre-built, so no request pays bundling cost.
	// With ExpectCompleted set, esbuild is skipped entirely and components are served straight from the cache.
	//
	// Do be aware that this disables HMR, ReactiveRegistry and DisableCaching (caches are now mandatory).
	//
	// Enabled by default; set Rasterization.Disabled to opt out.
	Rasterization *rasterization.RasterizationConfig

	// Types governs how go_solid checks the Go props a template is rendered
	// with against the type its component declares for them.
	//
	// The component is the contract. Shapes extracted from it are cached under
	// the workspace whatever this holds; Types.Check only selects when the
	// props are held against them.
	Types *types.TypesConfig

	// !! NOT IMPLEMENTED !! Enable component-integrated static content serving. If provided, any component's props (if any) will gain a "static" property of a type
	// that is a 1 to 1 recreation of the structure of the Static.Location directory. This places some limitations upon names of files and sub-directories.
	//
	// In the resulting graph-like js object at props.static, each file becomes a function that returns a corresponding Promise. I.e. font at:
	//
	// <StaticConfig.Location>/svg/homeIcon.svg
	//
	// becomes accessible in a component as:
	//
	// props.static.svg.homeIcon()
	Static *static.StaticConfig

	// HMR enables hot browser reload in development. When non-nil and not
	// Disabled, go_solid watches the components tree and pushes reloads to the
	// tabs viewing an affected template. Requires HMR.Mux so go_solid can mount
	// its WebSocket handler itself.
	HMR      *hmr.HMRConfig
	Defaults *BehaviouralDefaults
}

type RenderCallBuilder

type RenderCallBuilder interface {
	WithCtx(ctx context.Context) RenderCallBuilder
	MountOnRootID(id string) RenderCallBuilder
	WithHTMLHeadTags(fn meta.Configurator[networking.HTMLHeadSegmentBuilder]) RenderCallBuilder
	// Automatically route the render call to the given request and response writer.
	// Includes basic http request handling, status codes and error handling. To
	// customize the behaviour, use WithHTTPBehaviour(configurator).
	//
	// Using this method will automatically set the context for the render call to the request's context.
	ForRequest(w http.ResponseWriter, r *http.Request) RenderCallBuilder
	// Alter default http request behaviour.
	// If a ResponseWriter and Request have been provided previously, these will carry over, but can be overwritten
	SetHTTPBehaviour(fn meta.Configurator[networking.RequestBehaviourBuilder]) RenderCallBuilder

	Render() (*caching.Rendered, error)
}

Jump to

Keyboard shortcuts

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