advanced bindings documentation (#4404)

* advanced bindings documentation

* update changelog

---------

Co-authored-by: Lea Anthony <lea.anthony@gmail.com>
This commit is contained in:
Atterpac 2025-07-11 05:12:03 -06:00 committed by GitHub
commit e8765607ff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 3255 additions and 1403 deletions

1240
docs/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Added
- Add distribution-specific build dependencies for Linux by @leaanthony in [PR](https://github.com/wailsapp/wails/pull/4345)
- Added bindings guide by @atterpac in [PR](https://github.com/wailsapp/wails/pull/4404)
## v3.0.0-alpha.10 - 2025-07-06

View file

@ -0,0 +1,342 @@
---
title: Advanced Binding Techniques
sidebar:
order: 22
---
import { FileTree } from "@astrojs/starlight/components";
This guide covers advanced techniques for customizing and optimizing the binding generation process in Wails v3.
## Customizing Generated Code with Directives
### Injecting Custom Code
The `//wails:inject` directive allows you to inject custom JavaScript/TypeScript code into the generated bindings:
```go
//wails:inject console.log("Hello from Wails!");
type MyService struct {}
func (s *MyService) Greet(name string) string {
return "Hello, " + name
}
```
This will inject the specified code into the generated JavaScript/TypeScript file for the `MyService` service.
You can also use conditional injection to target specific output formats:
```go
//wails:inject j*:console.log("Hello JS!"); // JavaScript only
//wails:inject t*:console.log("Hello TS!"); // TypeScript only
```
### Including Additional Files
The `//wails:include` directive allows you to include additional files with the generated bindings:
```go
//wails:include js/*.js
package mypackage
```
This directive is typically used in package documentation comments to include additional JavaScript/TypeScript files with the generated bindings.
### Marking Internal Types and Methods
The `//wails:internal` directive marks a type or method as internal, preventing it from being exported to the frontend:
```go
//wails:internal
type InternalModel struct {
Field string
}
//wails:internal
func (s *MyService) InternalMethod() {}
```
This is useful for types and methods that are only used internally by your Go code and should not be exposed to the frontend.
### Ignoring Methods
The `//wails:ignore` directive completely ignores a method during binding generation:
```go
//wails:ignore
func (s *MyService) IgnoredMethod() {}
```
This is similar to `//wails:internal`, but it completely ignores the method rather than marking it as internal.
### Custom Method IDs
The `//wails:id` directive specifies a custom ID for a method, overriding the default hash-based ID:
```go
//wails:id 42
func (s *MyService) CustomIDMethod() {}
```
This can be useful for maintaining compatibility when refactoring code.
## Working with Complex Types
### Nested Structs
The binding generator handles nested structs automatically:
```go
type Address struct {
Street string
City string
State string
Zip string
}
type Person struct {
Name string
Address Address
}
func (s *MyService) GetPerson() Person {
return Person{
Name: "John Doe",
Address: Address{
Street: "123 Main St",
City: "Anytown",
State: "CA",
Zip: "12345",
},
}
}
```
The generated JavaScript/TypeScript code will include classes for both `Person` and `Address`.
### Maps and Slices
Maps and slices are also handled automatically:
```go
type Person struct {
Name string
Attributes map[string]string
Friends []string
}
func (s *MyService) GetPerson() Person {
return Person{
Name: "John Doe",
Attributes: map[string]string{
"hair": "brown",
"eyes": "blue",
},
Friends: []string{"Jane", "Bob", "Alice"},
}
}
```
In JavaScript, maps are represented as objects and slices as arrays. In TypeScript, maps are represented as `Record<K, V>` and slices as `T[]`.
### Generic Types
The binding generator supports generic types:
```go
type Result[T any] struct {
Data T
Error string
}
func (s *MyService) GetResult() Result[string] {
return Result[string]{
Data: "Hello, World!",
Error: "",
}
}
```
The generated TypeScript code will include a generic class for `Result`:
```typescript
export class Result<T> {
"Data": T;
"Error": string;
constructor(source: Partial<Result<T>> = {}) {
if (!("Data" in source)) {
this["Data"] = null as any;
}
if (!("Error" in source)) {
this["Error"] = "";
}
Object.assign(this, source);
}
static createFrom<T>(source: string | object = {}): Result<T> {
let parsedSource = typeof source === "string" ? JSON.parse(source) : source;
return new Result<T>(parsedSource as Partial<Result<T>>);
}
}
```
### Interfaces
The binding generator can generate TypeScript interfaces instead of classes using the `-i` flag:
```bash
wails3 generate bindings -ts -i
```
This will generate TypeScript interfaces for all models:
```typescript
export interface Person {
Name: string;
Attributes: Record<string, string>;
Friends: string[];
}
```
## Optimizing Binding Generation
### Using Names Instead of IDs
By default, the binding generator uses hash-based IDs for method calls. You can use the `-names` flag to use names instead:
```bash
wails3 generate bindings -names
```
This will generate code that uses method names instead of IDs:
```javascript
export function Greet(name) {
let $resultPromise = $Call.ByName("Greet", name);
return $resultPromise;
}
```
This can make the generated code more readable and easier to debug, but it may be slightly less efficient.
### Bundling the Runtime
By default, the generated code imports the Wails runtime from the `@wailsio/runtime` npm package. You can use the `-b` flag to bundle the runtime with the generated code:
```bash
wails3 generate bindings -b
```
This will include the runtime code directly in the generated files, eliminating the need for the npm package.
### Disabling Index Files
If you don't need the index files, you can use the `-noindex` flag to disable their generation:
```bash
wails3 generate bindings -noindex
```
This can be useful if you prefer to import services and models directly from their respective files.
## Real-World Examples
### Authentication Service
Here's an example of an authentication service with custom directives:
```go
package auth
//wails:inject console.log("Auth service initialized");
type AuthService struct {
// Private fields
users map[string]User
}
type User struct {
Username string
Email string
Role string
}
type LoginRequest struct {
Username string
Password string
}
type LoginResponse struct {
Success bool
User User
Token string
Error string
}
// Login authenticates a user
func (s *AuthService) Login(req LoginRequest) LoginResponse {
// Implementation...
}
// GetCurrentUser returns the current user
func (s *AuthService) GetCurrentUser() User {
// Implementation...
}
// Internal helper method
//wails:internal
func (s *AuthService) validateCredentials(username, password string) bool {
// Implementation...
}
```
### Data Processing Service
Here's an example of a data processing service with generic types:
```go
package data
type ProcessingResult[T any] struct {
Data T
Error string
}
type DataService struct {}
// Process processes data and returns a result
func (s *DataService) Process(data string) ProcessingResult[map[string]int] {
// Implementation...
}
// ProcessBatch processes multiple data items
func (s *DataService) ProcessBatch(data []string) ProcessingResult[[]map[string]int] {
// Implementation...
}
// Internal helper method
//wails:internal
func (s *DataService) parseData(data string) (map[string]int, error) {
// Implementation...
}
```
### Conditional Code Injection
Here's an example of conditional code injection for different output formats:
```go
//wails:inject j*:/**
//wails:inject j*: * @param {string} arg
//wails:inject j*: * @returns {Promise<void>}
//wails:inject j*: */
//wails:inject j*:export async function CustomMethod(arg) {
//wails:inject t*:export async function CustomMethod(arg: string): Promise<void> {
//wails:inject await InternalMethod("Hello " + arg + "!");
//wails:inject }
type Service struct{}
```
This injects different code for JavaScript and TypeScript outputs, providing appropriate type annotations for each language.

View file

@ -0,0 +1,115 @@
---
title: Binding Best Practices
sidebar:
order: 23
---
import { FileTree } from "@astrojs/starlight/components";
This guide provides best practices and patterns for using the Wails binding system effectively in your applications.
## Service Design Patterns
### Service Organization
Organize your services based on functionality rather than technical concerns. For example, instead of having a single large service, split it into smaller, focused services:
```go
// Instead of this:
type AppService struct {}
func (s *AppService) GetUser() User { /* ... */ }
func (s *AppService) UpdateUser(user User) error { /* ... */ }
func (s *AppService) GetProducts() []Product { /* ... */ }
func (s *AppService) AddProduct(product Product) error { /* ... */ }
// Do this:
type UserService struct {}
func (s *UserService) GetUser() User { /* ... */ }
func (s *UserService) UpdateUser(user User) error { /* ... */ }
type ProductService struct {}
func (s *ProductService) GetProducts() []Product { /* ... */ }
func (s *ProductService) AddProduct(product Product) error { /* ... */ }
```
This makes your code more maintainable and easier to understand.
### Use JSON Tags
Use JSON tags to control how your models are serialized:
```go
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Password string `json:"-"` // Exclude from JSON
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
```
### Separate Frontend and Backend Models
Consider using different models for the frontend and backend:
```go
// Backend model
type User struct {
ID int
Name string
Email string
Password string // Sensitive data
CreatedAt time.Time
UpdatedAt time.Time
}
// Frontend model
type UserDTO struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
}
func (s *UserService) GetUser() UserDTO {
user := getUserFromDatabase()
return UserDTO{
ID: user.ID,
Name: user.Name,
Email: user.Email,
CreatedAt: user.CreatedAt,
}
}
```
This gives you more control over what data is exposed to the frontend.
### Use Context for Cancellation
Use context for cancellation to avoid wasting resources on abandoned requests:
```go
func (s *ProductService) GetProducts(ctx context.Context, req ProductsRequest) (ProductsResponse, error) {
// Check if the request has been cancelled
select {
case <-ctx.Done():
return ProductsResponse{}, ctx.Err()
default:
// Continue processing
}
products, total, err := getProductsFromDatabase(ctx, req.Page, req.PageSize, req.Filter)
if err != nil {
return ProductsResponse{}, err
}
return ProductsResponse{
Products: products,
Total: total,
Page: req.Page,
PageSize: req.PageSize,
}, nil
}
```

View file

@ -0,0 +1,155 @@
---
title: Binding System Internals
sidebar:
order: 21
---
import { FileTree } from "@astrojs/starlight/components";
This guide explains how the Wails binding system works internally, providing insights for developers who want to understand the mechanics behind the automatic code generation.
## Architecture Overview
The Wails binding system consists of three main components:
1. **Collection**: Analyzes Go code to extract information about services, models, and other declarations
2. **Configuration**: Manages settings and options for the binding generation process
3. **Rendering**: Generates JavaScript/TypeScript code based on the collected information
<FileTree>
- internal/generator/
- collect/ # Package analysis and information extraction
- config/ # Configuration structures and interfaces
- render/ # Code generation for JS/TS
</FileTree>
## Collection Process
The collection process is responsible for analyzing Go packages and extracting information about services, models, and other declarations. This is handled by the `collect` package.
### Key Components
- **Collector**: Manages package information and caches collected data
- **Package**: Represents a Go package being analyzed and stores collected services, models, and directives
- **Service**: Collects information about service types and their methods
- **Model**: Collects detailed information about model types, including fields, values, and type parameters
- **Directive**: Parses and interprets `//wails:` directives in Go source code
### Collection Flow
1. The collector scans the Go packages specified in the project
2. It identifies service types (structs with methods that will be exposed to the frontend)
3. For each service, it collects information about its methods
4. It identifies model types (structs used as parameters or return values in service methods)
5. For each model, it collects information about its fields and type parameters
6. It processes any `//wails:` directives found in the code
## Rendering Process
The rendering process is responsible for generating JavaScript/TypeScript code based on the collected information. This is handled by the `render` package.
### Key Components
- **Renderer**: Orchestrates the rendering of service, model, and index files
- **Module**: Represents a single generated JavaScript/TypeScript module
- **Templates**: Text templates used for code generation
### Rendering Flow
1. For each service, the renderer generates a JavaScript/TypeScript file with functions that mirror the service methods
2. For each model, the renderer generates a JavaScript/TypeScript class that mirrors the model struct
3. The renderer generates index files that re-export all services and models
4. The renderer applies any custom code injections specified by `//wails:inject` directives
## Type Mapping
One of the most important aspects of the binding system is how Go types are mapped to JavaScript/TypeScript types. Here's a summary of the mapping:
| Go Type | JavaScript Type | TypeScript Type |
|---------|----------------|----------------|
| `bool` | `boolean` | `boolean` |
| `int`, `int8`, `int16`, `int32`, `int64`, `uint`, `uint8`, `uint16`, `uint32`, `uint64`, `float32`, `float64` | `number` | `number` |
| `string` | `string` | `string` |
| `[]byte` | `Uint8Array` | `Uint8Array` |
| `[]T` | `Array<T>` | `T[]` |
| `map[K]V` | `Object` | `Record<K, V>` |
| `struct` | `Object` | Custom class |
| `interface{}` | `any` | `any` |
| `*T` | `T \| null` | `T \| null` |
| `func` | Not supported | Not supported |
| `chan` | Not supported | Not supported |
## Directives System
The binding system supports several directives that can be used to customize the generated code. These directives are added as comments in your Go code.
### Available Directives
- `//wails:inject`: Injects custom JavaScript/TypeScript code into the generated bindings
- `//wails:include`: Includes additional files with the generated bindings
- `//wails:internal`: Marks a type or method as internal, preventing it from being exported to the frontend
- `//wails:ignore`: Completely ignores a method during binding generation
- `//wails:id`: Specifies a custom ID for a method, overriding the default hash-based ID
### Directive Processing
1. During the collection phase, the collector identifies and parses directives in the Go code
2. The directives are stored with the corresponding declarations (services, methods, models, etc.)
3. During the rendering phase, the renderer applies the directives to customize the generated code
## Advanced Features
### Conditional Code Generation
The binding system supports conditional code generation using a two-character condition prefix for `include` and `inject` directives:
```
<language><style>:<content>
```
Where:
- `<language>` can be:
- `*` - Both JavaScript and TypeScript
- `j` - JavaScript only
- `t` - TypeScript only
- `<style>` can be:
- `*` - Both classes and interfaces
- `c` - Classes only
- `i` - Interfaces only
For example:
```go
//wails:inject j*:console.log("JavaScript only");
//wails:inject t*:console.log("TypeScript only");
```
### Custom Method IDs
By default, methods are identified by a hash-based ID. However, you can specify a custom ID using the `//wails:id` directive:
```go
//wails:id 42
func (s *Service) CustomIDMethod() {}
```
This can be useful for maintaining compatibility when refactoring code.
## Performance Considerations
The binding generator is designed to be efficient, but there are a few things to keep in mind:
1. The first run will be slower as it builds up a cache of packages to scan
2. Subsequent runs will be faster as they use the cached information
3. The generator processes all packages in the project, which can be time-consuming for large projects
4. You can use the `-clean` flag to clean the output directory before generation
## Debugging
If you encounter issues with the binding generation, you can use the `-v` flag to enable debug output:
```bash
wails3 generate bindings -v
```
This will provide detailed information about the collection and rendering process, which can help identify the source of the issue.

View file

@ -183,7 +183,6 @@ func main() {
},
})
// ....
app.NewWebviewWindow()
err := app.Run()
if err != nil {
log.Fatal(err)
@ -672,4 +671,4 @@ look for warnings from the binding generator to catch these.
If you see a `ReferenceError` complaining about unknown methods,
it could mean that your JS bindings have gotten out of sync with Go code
and must be regenerated.
:::
:::