5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-17 09:29:30 +08:00

[v3] Fix binding generator bugs and prepare for Go 1.24 (#4045)

* Rename predicates source file

* Overhaul and document type predicates

* Fix model collection logic for named types

* Fix map key type rendering

* Fix map creation code

* Fix rendering of structs that implement marshaler interfaces

* Fix type cycle detection to take type args into account

* Fix enum and typeparam field initialisation

* Improve unsupported type warnings

* Remove internal models file

* Deduplicate template code

* Accept generic aliases in static analyser

* Support new `encoding/json` flag `omitzero`

* Handle special cases when rendering generic aliases

* Update npm test dependencies

* Test class aliases and implicit private dependencies

* Test marshaler combinations

* Test map key types

* Remove bad map keys from unrelated tests

* Test service discovery through generic aliases

* Test generic aliases

* Test warning messages

* Disable go1.24 tests

* Update changelog

* Restore rendering of injected lines in index file

* Test directives

* Add wails:ignore directive

* Fix typo

* Move injections to the bottom of service files

* Handle errors from closing files

* Do not emit messages when services define only lifecycle methods

* Add internal directive for services and models

* Update changelog

* Fix error in service templates

* Test internal directive on services/models

* Fix error in index template

* Base testdata updates

* Testdata for class aliases and implicit private dependencies

* Testdata for marshaler combinations

* Testdata for map key types

* Testdata for bad map key fixes

* Add weakly typed enums aka alias constants

* Testdata for enum and typeparam field fixes

* Testdata for generic aliases

* Testdata for warning messages

* Testdata for directives

* Testdata for weakly typed enums

* Update binding example

* Update services example

* Remove go1.24 testdata

* Update cli doc

* Fix analyser tests

* Fix windows tests... hopefully

* go mod tidy on examples

* Update bindings guide

---------

Co-authored-by: Lea Anthony <lea.anthony@gmail.com>
This commit is contained in:
Fabio Massaioli 2025-02-08 23:44:34 +01:00 committed by GitHub
parent d4096868e3
commit 37673eb24d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
859 changed files with 25099 additions and 9150 deletions

View File

@ -46,6 +46,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Support cancellation of events in standard event listeners by [@leaanthony](https://github.com/leaanthony) - Support cancellation of events in standard event listeners by [@leaanthony](https://github.com/leaanthony)
- Systray `Hide`, `Show` and `Destroy` support by [@leaanthony](https://github.com/leaanthony) - Systray `Hide`, `Show` and `Destroy` support by [@leaanthony](https://github.com/leaanthony)
- Systray `SetTooltip` support by [@leaanthony](https://github.com/leaanthony). Original idea by [@lujihong](https://github.com/wailsapp/wails/issues/3487#issuecomment-2633242304) - Systray `SetTooltip` support by [@leaanthony](https://github.com/leaanthony). Original idea by [@lujihong](https://github.com/wailsapp/wails/issues/3487#issuecomment-2633242304)
- Report package path in binding generator warnings about unsupported types by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045)
- Add binding generator support for generic aliases by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045)
- Add binding generator support for `omitzero` JSON flag by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045)
- Add `//wails:ignore` directive to prevent binding generation for chosen service methods by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045)
- Add `//wails:internal` directive on services and models to allow for types that are exported in Go but not in JS/TS by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045)
- Add binding generator support for constants of alias type to allow for weakly typed enums by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045)
### Fixed ### Fixed
@ -62,6 +68,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed a `Parameter incorrect` error in Window initialisation on Windows when HTML provided but no JS by [@leaanthony](https://github.com/leaanthony) - Fixed a `Parameter incorrect` error in Window initialisation on Windows when HTML provided but no JS by [@leaanthony](https://github.com/leaanthony)
- Fixed size of response prefix used for content type sniffing in asset server by [@fbbdev](https://github.com/fbbdev) in [#4049](https://github.com/wailsapp/wails/pull/4049) - Fixed size of response prefix used for content type sniffing in asset server by [@fbbdev](https://github.com/fbbdev) in [#4049](https://github.com/wailsapp/wails/pull/4049)
- Fixed handling of non-404 responses on root index path in asset server by [@fbbdev](https://github.com/fbbdev) in [#4049](https://github.com/wailsapp/wails/pull/4049) - Fixed handling of non-404 responses on root index path in asset server by [@fbbdev](https://github.com/fbbdev) in [#4049](https://github.com/wailsapp/wails/pull/4049)
- Fixed undefined behaviour in binding generator when testing properties of generic types by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045)
- Fixed binding generator output for models when underlying type has not the same properties as named wrapper by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045)
- Fixed binding generator output for map key types and preprocessing by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045)
- Fixed binding generator output for structs that implement marshaler interfaces by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045)
- Fixed detection of type cycles involving generic types in binding generator by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045)
- Fixed invalid references to unexported models in binding generator output by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045)
- Moved injected code to the end of service files by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045)
- Fixed handling of errors from file close operations in binding generator by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045)
- Suppressed warnings for services that define lifecycle or http methods but no other bound methods by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045)
### Changed ### Changed
@ -72,6 +87,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `application.NewService` does not accept options as an optional parameter anymore (use `application.NewServiceWithOptions` instead) by [@leaanthony](https://github.com/leaanthony) in [#4024](https://github.com/wailsapp/wails/pull/4024) - `application.NewService` does not accept options as an optional parameter anymore (use `application.NewServiceWithOptions` instead) by [@leaanthony](https://github.com/leaanthony) in [#4024](https://github.com/wailsapp/wails/pull/4024)
- Removed `nanoid` dependency by [@leaanthony](https://github.com/leaanthony) - Removed `nanoid` dependency by [@leaanthony](https://github.com/leaanthony)
- Updated Window example for mica/acrylic/tabbed window styles by [@leaanthony](https://github.com/leaanthony) - Updated Window example for mica/acrylic/tabbed window styles by [@leaanthony](https://github.com/leaanthony)
- In JS/TS bindings, `internal.js/ts` model files have been removed; all models can now be found in `models.js/ts` by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045)
- In JS/TS bindings, named types are never rendered as aliases for other named types; the old behaviour is now restricted to aliases by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045)
- In JS/TS bindings, in class mode, struct fields whose type is a type parameter are marked optional and never initialised automatically by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045)
## v3.0.0-alpha.9 - 2025-01-13 ## v3.0.0-alpha.9 - 2025-01-13

View File

@ -134,7 +134,6 @@ wails3 generate bindings [flags] [patterns...]
| `-f` | Additional Go build flags | | | `-f` | Additional Go build flags | |
| `-d` | Output directory | `frontend/bindings` | | `-d` | Output directory | `frontend/bindings` |
| `-models` | Models filename | `models` | | `-models` | Models filename | `models` |
| `-internal` | Internal filename | `internal` |
| `-index` | Index filename | `index` | | `-index` | Index filename | `index` |
| `-ts` | Generate TypeScript | `false` | | `-ts` | Generate TypeScript | `false` |
| `-i` | Use TS interfaces | `false` | | `-i` | Use TS interfaces | `false` |

View File

@ -114,7 +114,7 @@ is used to namespace the generated files.
The generated `greetservice.js` file contains the JavaScript code that mirrors The generated `greetservice.js` file contains the JavaScript code that mirrors
the Go struct and its methods: the Go struct and its methods:
```javascript ```javascript title="greetservice.js"
// @ts-check // @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT // This file is automatically generated. DO NOT EDIT
@ -212,7 +212,7 @@ If we run the bindings generator again, we should see the following output:
In the `frontend/bindings/changeme` directory, you should see a new `models.js` In the `frontend/bindings/changeme` directory, you should see a new `models.js`
file containing the following code: file containing the following code:
```javascript ```javascript title="models.js"
// @ts-check // @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT // This file is automatically generated. DO NOT EDIT
@ -270,7 +270,7 @@ Here's an example of how you can use the generated JavaScript `Person` type in
your frontend code: your frontend code:
```javascript ```javascript
import { Greet } from "./bindings/changeme/GreetService.js"; import { Greet } from "./bindings/changeme/greetservice.js";
import { Person } from "./bindings/changeme/models.js"; import { Person } from "./bindings/changeme/models.js";
const resultElement = document.getElementById("result"); const resultElement = document.getElementById("result");
@ -291,6 +291,36 @@ the `Greet` method.
Using bound models allows you to work with complex data structures and Using bound models allows you to work with complex data structures and
seamlessly pass them between the frontend and backend of your Wails application. seamlessly pass them between the frontend and backend of your Wails application.
### Index files
The generator outputs an additional `index.js` file that re-exports all services and models:
```javascript title="index.js"
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import * as GreetService from "./greetservice.js";
export {
GreetService
};
export {
Person
} from "./models.js";
```
You can take advantage of this feature
to aggregate import statements for multiple services and models.
If you are building your frontend with a bundler,
which is the default for most project templates,
you can also simplify the import path:
```javascript
import { GreetService, Person } from "./bindings/changeme";
await GreetService.Greet(new Person(/* ... */));
```
### Using Typescript ### Using Typescript
To generate TypeScript bindings instead of JavaScript, you can use the `-ts` To generate TypeScript bindings instead of JavaScript, you can use the `-ts`
@ -382,9 +412,7 @@ The context provides several powerful features:
frontend, which will raise an error through the Promise chain. frontend, which will raise an error through the Promise chain.
2. **Window Information**: You can determine which window made the call using 2. **Window Information**: You can determine which window made the call using
these context keys: the context key `application.WindowKey`.
- `application.WindowNameKey` - Returns the name of the calling window
- `application.WindowIDKey` - Returns the ID of the calling window
Here are some examples: Here are some examples:
@ -403,9 +431,8 @@ func (s *MyService) LongRunningTask(ctx context.Context, input string) (string,
// Getting caller window information // Getting caller window information
func (s *MyService) WindowAwareMethod(ctx context.Context) (string, error) { func (s *MyService) WindowAwareMethod(ctx context.Context) (string, error) {
windowName := ctx.Value(application.WindowNameKey).(string) window := ctx.Value(application.WindowKey).(application.Window)
windowID := ctx.Value(application.WindowIDKey).(string) return fmt.Sprintf("Called from window: %s (ID: %s)", window.Name(), window.ID()), nil
return fmt.Sprintf("Called from window: %s (ID: %s)", windowName, windowID), nil
} }
``` ```

View File

@ -2,4 +2,6 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT // This file is automatically generated. DO NOT EDIT
export * from "./models.js"; export {
Person
} from "./models.js";

View File

@ -1,55 +0,0 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import {Create as $Create} from "/wails/runtime.js";
/**
* Person holds someone's most important attributes
*/
export class Person {
/**
* Creates a new Person instance.
* @param {Partial<Person>} [$$source = {}] - The source object to create the Person.
*/
constructor($$source = {}) {
if (!("name" in $$source)) {
/**
* Name is the person's name
* @member
* @type {string}
*/
this["name"] = "";
}
if (!("counts" in $$source)) {
/**
* Counts tracks the number of time the person
* has been greeted in various ways
* @member
* @type {number[]}
*/
this["counts"] = [];
}
Object.assign(this, $$source);
}
/**
* Creates a new Person instance from a string or object.
* @param {any} [$$source = {}]
* @returns {Person}
*/
static createFrom($$source = {}) {
const $$createField1_0 = $$createType0;
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
if ("counts" in $$parsedSource) {
$$parsedSource["counts"] = $$createField1_0($$parsedSource["counts"]);
}
return new Person(/** @type {Partial<Person>} */($$parsedSource));
}
}
// Private type creation functions
const $$createType0 = $Create.Array($Create.Any);

View File

@ -2,6 +2,54 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT // This file is automatically generated. DO NOT EDIT
export { // eslint-disable-next-line @typescript-eslint/ban-ts-comment
Person // @ts-ignore: Unused imports
} from "./internal.js"; import {Create as $Create} from "/wails/runtime.js";
/**
* Person holds someone's most important attributes
*/
export class Person {
/**
* Creates a new Person instance.
* @param {Partial<Person>} [$$source = {}] - The source object to create the Person.
*/
constructor($$source = {}) {
if (!("name" in $$source)) {
/**
* Name is the person's name
* @member
* @type {string}
*/
this["name"] = "";
}
if (!("counts" in $$source)) {
/**
* Counts tracks the number of time the person
* has been greeted in various ways
* @member
* @type {number[]}
*/
this["counts"] = [];
}
Object.assign(this, $$source);
}
/**
* Creates a new Person instance from a string or object.
* @param {any} [$$source = {}]
* @returns {Person}
*/
static createFrom($$source = {}) {
const $$createField1_0 = $$createType0;
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
if ("counts" in $$parsedSource) {
$$parsedSource["counts"] = $$createField1_0($$parsedSource["counts"]);
}
return new Person(/** @type {Partial<Person>} */($$parsedSource));
}
}
// Private type creation functions
const $$createType0 = $Create.Array($Create.Any);

View File

@ -1,22 +1,20 @@
module changeme module changeme
go 1.22 go 1.23.4
toolchain go1.22.0
require github.com/wailsapp/wails/v3 v3.0.0-alpha.0 require github.com/wailsapp/wails/v3 v3.0.0-alpha.0
require ( require (
github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/bep/debounce v1.2.1 // indirect github.com/bep/debounce v1.2.1 // indirect
github.com/ebitengine/purego v0.4.0-alpha.4 // indirect github.com/ebitengine/purego v0.4.0-alpha.4 // indirect
github.com/emirpasic/gods v1.18.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.5.0 // indirect github.com/go-git/go-billy/v5 v5.6.2 // indirect
github.com/go-git/go-git/v5 v5.11.0 // indirect github.com/go-git/go-git/v5 v5.13.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-ole/go-ole v1.3.0 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/google/uuid v1.3.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/imdario/mergo v0.3.12 // indirect github.com/imdario/mergo v0.3.12 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
@ -25,25 +23,25 @@ require (
github.com/kr/pretty v0.3.1 // indirect github.com/kr/pretty v0.3.1 // indirect
github.com/leaanthony/go-ansi-parser v1.6.1 // indirect github.com/leaanthony/go-ansi-parser v1.6.1 // indirect
github.com/leaanthony/u v1.1.0 // indirect github.com/leaanthony/u v1.1.0 // indirect
github.com/lmittmann/tint v1.0.3 // indirect github.com/lmittmann/tint v1.0.4 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/rivo/uniseg v0.4.4 // indirect github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/samber/lo v1.38.1 // indirect github.com/samber/lo v1.38.1 // indirect
github.com/sergi/go-diff v1.2.0 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/stretchr/testify v1.8.4 // indirect github.com/stretchr/testify v1.10.0 // indirect
github.com/wailsapp/go-webview2 v1.0.9 // indirect github.com/wailsapp/go-webview2 v1.0.19 // indirect
github.com/wailsapp/mimetype v1.4.1 // indirect github.com/wailsapp/mimetype v1.4.1 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect
golang.org/x/crypto v0.21.0 // indirect golang.org/x/crypto v0.32.0 // indirect
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/net v0.21.0 // indirect golang.org/x/net v0.34.0 // indirect
golang.org/x/sys v0.18.0 // indirect golang.org/x/sys v0.29.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect
) )

View File

@ -3,6 +3,7 @@ github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
@ -31,13 +32,16 @@ github.com/go-git/go-billy/v5 v5.1.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI
github.com/go-git/go-billy/v5 v5.2.0 h1:GcoouCP9J+5slw2uXAocL70z8ml4A8B/H8nEPt6CLPk= github.com/go-git/go-billy/v5 v5.2.0 h1:GcoouCP9J+5slw2uXAocL70z8ml4A8B/H8nEPt6CLPk=
github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M= github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M=
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
github.com/go-git/go-git/v5 v5.3.0 h1:8WKMtJR2j8RntEXR/uvTKagfEt4GYlwQ7mntE4+0GWc= github.com/go-git/go-git/v5 v5.3.0 h1:8WKMtJR2j8RntEXR/uvTKagfEt4GYlwQ7mntE4+0GWc=
github.com/go-git/go-git/v5 v5.3.0/go.mod h1:xdX4bWJ48aOrdhnl2XqHYstHbbp6+LFS4r4X+lNVprw= github.com/go-git/go-git/v5 v5.3.0/go.mod h1:xdX4bWJ48aOrdhnl2XqHYstHbbp6+LFS4r4X+lNVprw=
github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY=
github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0qu3XXXVixc=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@ -46,6 +50,7 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
@ -74,6 +79,7 @@ github.com/leaanthony/u v1.1.0/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQc
github.com/lmittmann/tint v1.0.0 h1:fzEj70K1L58uyoePQxKe+ezDZJ5pybiWGdA0JeFvvyw= github.com/lmittmann/tint v1.0.0 h1:fzEj70K1L58uyoePQxKe+ezDZJ5pybiWGdA0JeFvvyw=
github.com/lmittmann/tint v1.0.0/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= github.com/lmittmann/tint v1.0.0/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
github.com/lmittmann/tint v1.0.3/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= github.com/lmittmann/tint v1.0.3/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
github.com/lmittmann/tint v1.0.4/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
@ -81,6 +87,7 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
@ -99,15 +106,18 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97 h1:3RPlVWzZ/PDqmVuf/FKHARG5EMid/tl7cv54Sw/QRVY= github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97 h1:3RPlVWzZ/PDqmVuf/FKHARG5EMid/tl7cv54Sw/QRVY=
github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -117,8 +127,10 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/wailsapp/go-webview2 v1.0.9 h1:lrU+q0cf1wgLdR69rN+ZnRtMJNaJRrcQ4ELxoO7/xjs= github.com/wailsapp/go-webview2 v1.0.9 h1:lrU+q0cf1wgLdR69rN+ZnRtMJNaJRrcQ4ELxoO7/xjs=
github.com/wailsapp/go-webview2 v1.0.9/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo= github.com/wailsapp/go-webview2 v1.0.9/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo=
github.com/wailsapp/go-webview2 v1.0.19/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc=
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
@ -132,8 +144,10 @@ golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@ -142,6 +156,7 @@ golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -157,12 +172,14 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=

View File

@ -1,27 +1,25 @@
module changeme module changeme
go 1.22.4 go 1.23.4
toolchain go1.23.0
require github.com/wailsapp/wails/v3 v3.0.0-alpha.7 require github.com/wailsapp/wails/v3 v3.0.0-alpha.7
require ( require (
dario.cat/mergo v1.0.0 // indirect dario.cat/mergo v1.0.1 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect github.com/ProtonMail/go-crypto v1.1.4 // indirect
github.com/bep/debounce v1.2.1 // indirect github.com/bep/debounce v1.2.1 // indirect
github.com/cloudflare/circl v1.3.7 // indirect github.com/cloudflare/circl v1.5.0 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/cyphar/filepath-securejoin v0.4.0 // indirect
github.com/ebitengine/purego v0.4.0-alpha.4 // indirect github.com/ebitengine/purego v0.4.0-alpha.4 // indirect
github.com/emirpasic/gods v1.18.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.5.0 // indirect github.com/go-git/go-billy/v5 v5.6.2 // indirect
github.com/go-git/go-git/v5 v5.11.0 // indirect github.com/go-git/go-git/v5 v5.13.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-ole/go-ole v1.3.0 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/google/uuid v1.3.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect
@ -30,22 +28,22 @@ require (
github.com/lmittmann/tint v1.0.4 // indirect github.com/lmittmann/tint v1.0.4 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pjbgf/sha1cd v0.3.1 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/rivo/uniseg v0.4.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect
github.com/samber/lo v1.38.1 // indirect github.com/samber/lo v1.38.1 // indirect
github.com/sergi/go-diff v1.2.0 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/skeema/knownhosts v1.2.1 // indirect github.com/skeema/knownhosts v1.3.0 // indirect
github.com/wailsapp/go-webview2 v1.0.16 // indirect github.com/wailsapp/go-webview2 v1.0.19 // indirect
github.com/wailsapp/mimetype v1.4.1 // indirect github.com/wailsapp/mimetype v1.4.1 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect
golang.org/x/crypto v0.23.0 // indirect golang.org/x/crypto v0.32.0 // indirect
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/mod v0.17.0 // indirect golang.org/x/mod v0.22.0 // indirect
golang.org/x/net v0.25.0 // indirect golang.org/x/net v0.34.0 // indirect
golang.org/x/sync v0.8.0 // indirect golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.20.0 // indirect golang.org/x/sys v0.29.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect golang.org/x/tools v0.29.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect
) )

View File

@ -1,10 +1,13 @@
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg=
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/ProtonMail/go-crypto v1.1.4/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
@ -15,8 +18,10 @@ github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7N
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/cyphar/filepath-securejoin v0.4.0/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -32,20 +37,25 @@ github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66D
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4= github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4=
github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY=
github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0qu3XXXVixc=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck= github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
@ -76,6 +86,7 @@ github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
github.com/pjbgf/sha1cd v0.3.1/go.mod h1:Y8t7jSB/dEI/lQE04A1HVKteqjj9bX5O4+Cex0TCu8s=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@ -91,9 +102,11 @@ github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ=
github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@ -101,6 +114,7 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/wailsapp/go-webview2 v1.0.16 h1:wffnvnkkLvhRex/aOrA3R7FP7rkvOqL/bir1br7BekU= github.com/wailsapp/go-webview2 v1.0.16 h1:wffnvnkkLvhRex/aOrA3R7FP7rkvOqL/bir1br7BekU=
github.com/wailsapp/go-webview2 v1.0.16/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo= github.com/wailsapp/go-webview2 v1.0.16/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo=
github.com/wailsapp/go-webview2 v1.0.19/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc=
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
@ -113,12 +127,15 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@ -129,11 +146,13 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -147,12 +166,14 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
@ -175,6 +196,7 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -184,5 +206,6 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -2,9 +2,13 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT // This file is automatically generated. DO NOT EDIT
import * as Hashes from "./hashes.js"; import * as Service from "./service.js";
export { export {
Hashes Service
}; };
export * from "./models.js"; import * as $models from "./models.js";
/**
* @typedef {$models.Hashes} Hashes
*/

View File

@ -6,44 +6,9 @@
// @ts-ignore: Unused imports // @ts-ignore: Unused imports
import {Create as $Create} from "/wails/runtime.js"; import {Create as $Create} from "/wails/runtime.js";
export class Hashes { /**
/** * @typedef {Object} Hashes
* Creates a new Hashes instance. * @property {string} md5
* @param {Partial<Hashes>} [$$source = {}] - The source object to create the Hashes. * @property {string} sha1
* @property {string} sha256
*/ */
constructor($$source = {}) {
if (!("md5" in $$source)) {
/**
* @member
* @type {string}
*/
this["md5"] = "";
}
if (!("sha1" in $$source)) {
/**
* @member
* @type {string}
*/
this["sha1"] = "";
}
if (!("sha256" in $$source)) {
/**
* @member
* @type {string}
*/
this["sha256"] = "";
}
Object.assign(this, $$source);
}
/**
* Creates a new Hashes instance from a string or object.
* @param {any} [$$source = {}]
* @returns {Hashes}
*/
static createFrom($$source = {}) {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new Hashes(/** @type {Partial<Hashes>} */($$parsedSource));
}
}

View File

@ -0,0 +1,20 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import {Call as $Call, Create as $Create} from "/wails/runtime.js";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as $models from "./models.js";
/**
* @param {string} s
* @returns {Promise<$models.Hashes> & { cancel(): void }}
*/
export function Generate(s) {
let $resultPromise = /** @type {any} */($Call.ByID(1123907498, s));
return $resultPromise;
}

View File

@ -26,15 +26,6 @@ export function Get(key) {
return $resultPromise; return $resultPromise;
} }
/**
* Name returns the name of the plugin.
* @returns {Promise<string> & { cancel(): void }}
*/
export function Name() {
let $resultPromise = /** @type {any} */($Call.ByID(2879709053));
return $resultPromise;
}
/** /**
* Save saves the store to disk * Save saves the store to disk
* @returns {Promise<void> & { cancel(): void }} * @returns {Promise<void> & { cancel(): void }}

View File

@ -40,16 +40,6 @@ export function Info(message, ...args) {
return $resultPromise; return $resultPromise;
} }
/**
* Name returns the name of the plugin.
* You should use the go module format e.g. github.com/myuser/myplugin
* @returns {Promise<string> & { cancel(): void }}
*/
export function Name() {
let $resultPromise = /** @type {any} */($Call.ByID(3407342027));
return $resultPromise;
}
/** /**
* @param {slog$0.Level} level * @param {slog$0.Level} level
* @returns {Promise<void> & { cancel(): void }} * @returns {Promise<void> & { cancel(): void }}

View File

@ -24,16 +24,6 @@ export function Execute(query, ...args) {
return $resultPromise; return $resultPromise;
} }
/**
* Name returns the name of the plugin.
* You should use the go module format e.g. github.com/myuser/myplugin
* @returns {Promise<string> & { cancel(): void }}
*/
export function Name() {
let $resultPromise = /** @type {any} */($Call.ByID(2075046103));
return $resultPromise;
}
/** /**
* @param {string} dbPath * @param {string} dbPath
* @returns {Promise<string> & { cancel(): void }} * @returns {Promise<string> & { cancel(): void }}
@ -57,16 +47,6 @@ export function Select(query, ...args) {
return $typingPromise; return $typingPromise;
} }
/**
* Shutdown is called when the app is shutting down
* You can use this to clean up any resources you have allocated
* @returns {Promise<void> & { cancel(): void }}
*/
export function Shutdown() {
let $resultPromise = /** @type {any} */($Call.ByID(846401686));
return $resultPromise;
}
// Private type creation functions // Private type creation functions
const $$createType0 = $Create.Map($Create.Any, $Create.Any); const $$createType0 = $Create.Map($Create.Any, $Create.Any);
const $$createType1 = $Create.Array($$createType0); const $$createType1 = $Create.Array($$createType0);

View File

@ -2,4 +2,10 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT // This file is automatically generated. DO NOT EDIT
export * from "./models.js"; import * as $models from "./models.js";
/**
* A Level is the importance or severity of a log event.
* The higher the level, the more important or severe the event.
* @typedef {$models.Level} Level
*/

View File

@ -9,7 +9,7 @@
import * as sqlite from './bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/service.js'; import * as sqlite from './bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/service.js';
import * as kvstore from './bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/keyvaluestore.js'; import * as kvstore from './bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/keyvaluestore.js';
import {Debug, Info, Warning, Error} from './bindings/github.com/wailsapp/wails/v3/pkg/services/log/loggerservice.js'; import {Debug, Info, Warning, Error} from './bindings/github.com/wailsapp/wails/v3/pkg/services/log/loggerservice.js';
import * as hash from './bindings/github.com/wailsapp/wails/v3/examples/services/hashes/hashes.js'; import * as hash from './bindings/github.com/wailsapp/wails/v3/examples/services/hashes/service.js';
function runHash() { function runHash() {
let hashstring = document.getElementById("hashstring").value; let hashstring = document.getElementById("hashstring").value;

View File

@ -6,16 +6,19 @@ import (
"crypto/sha1" "crypto/sha1"
"crypto/sha256" "crypto/sha256"
"encoding/hex" "encoding/hex"
"github.com/wailsapp/wails/v3/pkg/application" "github.com/wailsapp/wails/v3/pkg/application"
) )
type Hashes struct { type Hashes = struct {
MD5 string `json:"md5"` MD5 string `json:"md5"`
SHA1 string `json:"sha1"` SHA1 string `json:"sha1"`
SHA256 string `json:"sha256"` SHA256 string `json:"sha256"`
} }
func (h *Hashes) Generate(s string) Hashes { type Service struct{}
func (h *Service) Generate(s string) Hashes {
md5Hash := md5.Sum([]byte(s)) md5Hash := md5.Sum([]byte(s))
sha1Hash := sha1.Sum([]byte(s)) sha1Hash := sha1.Sum([]byte(s))
sha256Hash := sha256.Sum256([]byte(s)) sha256Hash := sha256.Sum256([]byte(s))
@ -27,16 +30,16 @@ func (h *Hashes) Generate(s string) Hashes {
} }
} }
func New() *Hashes { func New() *Service {
return &Hashes{} return &Service{}
} }
func (h *Hashes) ServiceShutdown() error { return nil } func (h *Service) ServiceName() string {
func (h *Hashes) ServiceName() string {
return "Hashes Service" return "Hashes Service"
} }
func (h *Hashes) ServiceStartup(_ context.Context, _ application.ServiceOptions) error { func (h *Service) ServiceStartup(context.Context, application.ServiceOptions) error {
return nil return nil
} }
func (h *Service) ServiceShutdown() error { return nil }

View File

@ -128,6 +128,7 @@ require (
gitlab.com/digitalxero/go-conventional-commit v1.0.7 // indirect gitlab.com/digitalxero/go-conventional-commit v1.0.7 // indirect
golang.org/x/crypto v0.32.0 // indirect golang.org/x/crypto v0.32.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/exp/typeparams v0.0.0-20250128182459-e0ece0dbea4c
golang.org/x/image v0.21.0 // indirect golang.org/x/image v0.21.0 // indirect
golang.org/x/mod v0.22.0 // indirect golang.org/x/mod v0.22.0 // indirect
golang.org/x/net v0.34.0 // indirect golang.org/x/net v0.34.0 // indirect

View File

@ -350,6 +350,8 @@ golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/exp/typeparams v0.0.0-20250128182459-e0ece0dbea4c h1:sMPlrlhFwAE8DZXzAIztseGS+N8uGlLbFQCOTsoIPmc=
golang.org/x/exp/typeparams v0.0.0-20250128182459-e0ece0dbea4c/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s= golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78= golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78=

View File

@ -11,7 +11,6 @@ type GenerateBindingsOptions struct {
BuildFlagsString string `name:"f" description:"A list of additional space-separated Go build flags. Flags (or parts of them) can be wrapped in single or double quotes to include spaces"` BuildFlagsString string `name:"f" description:"A list of additional space-separated Go build flags. Flags (or parts of them) can be wrapped in single or double quotes to include spaces"`
OutputDirectory string `name:"d" description:"The output directory" default:"frontend/bindings"` OutputDirectory string `name:"d" description:"The output directory" default:"frontend/bindings"`
ModelsFilename string `name:"models" description:"File name for exported JS/TS models (excluding the extension)" default:"models"` ModelsFilename string `name:"models" description:"File name for exported JS/TS models (excluding the extension)" default:"models"`
InternalFilename string `name:"internal" description:"File name for unexported JS/TS models (excluding the extension)" default:"internal"`
IndexFilename string `name:"index" description:"File name for JS/TS package indexes (excluding the extension)" default:"index"` IndexFilename string `name:"index" description:"File name for JS/TS package indexes (excluding the extension)" default:"index"`
TS bool `name:"ts" description:"Generate Typescript bindings"` TS bool `name:"ts" description:"Generate Typescript bindings"`
UseInterfaces bool `name:"i" description:"Generate Typescript interfaces instead of classes"` UseInterfaces bool `name:"i" description:"Generate Typescript interfaces instead of classes"`

View File

@ -1,3 +1,4 @@
.task .task
node_modules node_modules
testdata/output/**/*.got.[jt]s testdata/output/**/*.got.[jt]s
testdata/output/**/*.got.log

View File

@ -7,7 +7,7 @@ shopt: [globstar]
tasks: tasks:
clean: clean:
cmds: cmds:
- rm -rf ./testdata/output/**/*.got.[jt]s - rm -rf ./testdata/output/**/*.got.[jt]s ./testdata/output/**/*.got.log
test: test:
cmds: cmds:

View File

@ -68,16 +68,11 @@ func FindServices(pkgs []*packages.Package, systemPaths *config.SystemPaths, log
// Object seen for the first time: // Object seen for the first time:
// add type params to owner map. // add type params to owner map.
// If applicable, process methods too.
var tp *types.TypeParamList var tp *types.TypeParamList
var recv *types.Named
switch t := obj.Type().(type) { if t, ok := obj.Type().(interface{ TypeParams() *types.TypeParamList }); ok {
case *types.Named:
tp = t.TypeParams() tp = t.TypeParams()
recv = t } else {
case *types.Signature:
tp = t.TypeParams()
default:
// Instantiated object has unexpected kind: // Instantiated object has unexpected kind:
// the spec might have changed. // the spec might have changed.
logger.Warningf( logger.Warningf(
@ -94,8 +89,8 @@ func FindServices(pkgs []*packages.Package, systemPaths *config.SystemPaths, log
} }
} }
// Process methods. // If this is a named type, process methods.
if recv != nil && recv.NumMethods() > 0 { if recv, ok := obj.Type().(*types.Named); ok && recv.NumMethods() > 0 {
// Register receiver type params. // Register receiver type params.
for i := range recv.NumMethods() { for i := range recv.NumMethods() {
tp := recv.Method(i).Type().(*types.Signature).RecvTypeParams() tp := recv.Method(i).Type().(*types.Signature).RecvTypeParams()

View File

@ -3,6 +3,8 @@ package collect
import ( import (
"go/types" "go/types"
"path/filepath" "path/filepath"
"golang.org/x/tools/go/types/typeutil"
) )
type ( type (
@ -120,26 +122,26 @@ func (imports *ImportMap) Add(pkg *PackageInfo) {
// AddType does not support unsynchronised concurrent calls // AddType does not support unsynchronised concurrent calls
// on the same receiver. // on the same receiver.
func (imports *ImportMap) AddType(typ types.Type) { func (imports *ImportMap) AddType(typ types.Type) {
imports.addTypeImpl(typ, make(map[*types.TypeName]bool)) imports.addTypeImpl(typ, new(typeutil.Map))
} }
// addTypeImpl provides the actual implementation of AddType. // addTypeImpl provides the actual implementation of AddType.
// The visited parameter is used to break cycles. // The visited parameter is used to break cycles.
func (imports *ImportMap) addTypeImpl(typ types.Type, visited map[*types.TypeName]bool) { func (imports *ImportMap) addTypeImpl(typ types.Type, visited *typeutil.Map) {
collector := imports.collector collector := imports.collector
if collector == nil { if collector == nil {
panic("AddType called on ImportMap with nil importing package") panic("AddType called on ImportMap with nil collector")
} }
for { // Avoid recursion where possible. for { // Avoid recursion where possible.
switch t := typ.(type) { switch t := typ.(type) {
case *types.Alias, *types.Named: case *types.Alias, *types.Named:
obj := typ.(interface{ Obj() *types.TypeName }).Obj() if visited.Set(typ, true) != nil {
if visited[obj] { // Break type cycles.
return return
} }
visited[obj] = true
obj := typ.(interface{ Obj() *types.TypeName }).Obj()
if obj.Pkg() == nil { if obj.Pkg() == nil {
// Ignore universe type. // Ignore universe type.
return return
@ -155,7 +157,7 @@ func (imports *ImportMap) addTypeImpl(typ types.Type, visited map[*types.TypeNam
// Import parent package. // Import parent package.
imports.Add(collector.Package(obj.Pkg())) imports.Add(collector.Package(obj.Pkg()))
instance, _ := t.(interface{ TypeArgs() *types.TypeList }) instance, _ := typ.(interface{ TypeArgs() *types.TypeList })
if instance != nil { if instance != nil {
// Record type argument dependencies. // Record type argument dependencies.
if targs := instance.TypeArgs(); targs != nil { if targs := instance.TypeArgs(); targs != nil {
@ -170,25 +172,29 @@ func (imports *ImportMap) addTypeImpl(typ types.Type, visited map[*types.TypeNam
return return
} }
if _, isAlias := t.(*types.Alias); isAlias { if _, isAlias := typ.(*types.Alias); isAlias {
// Aliased type might be needed during // Aliased type might be needed during
// JS value creation and initialisation. // JS value creation and initialisation.
typ = types.Unalias(typ) typ = types.Unalias(typ)
break break
} }
if IsClass(typ) || IsString(typ) || IsAny(typ) { if IsClass(typ) || IsAny(typ) || IsStringAlias(typ) {
return return
} }
// If named type does not map to a class, string or unknown type, // If named type does not map to a class, unknown type or string,
// its underlying type may be needed during JS value creation. // its underlying type may be needed during JS value creation.
typ = typ.Underlying() typ = typ.Underlying()
case *types.Basic: case *types.Basic:
if t.Info()&types.IsComplex != 0 { switch {
// Complex types are not supported by encoding/json case t.Info()&(types.IsBoolean|types.IsInteger|types.IsUnsigned|types.IsFloat|types.IsString) != 0:
collector.logger.Warningf("complex types are not supported by encoding/json") break
case t.Info()&types.IsComplex != 0:
collector.logger.Warningf("package %s: complex types are not supported by encoding/json", imports.Self)
default:
collector.logger.Warningf("package %s: unknown basic type %s: please report this to Wails maintainers", imports.Self, typ)
} }
return return
@ -196,32 +202,40 @@ func (imports *ImportMap) addTypeImpl(typ types.Type, visited map[*types.TypeNam
typ = typ.(interface{ Elem() types.Type }).Elem() typ = typ.(interface{ Elem() types.Type }).Elem()
case *types.Chan: case *types.Chan:
collector.logger.Warningf("channel types are not supported by encoding/json") collector.logger.Warningf("package %s: channel types are not supported by encoding/json", imports.Self)
return return
case *types.Map: case *types.Map:
if IsMapKey(t.Key()) { if IsMapKey(t.Key()) {
if IsString(t.Key()) { if IsStringAlias(t.Key()) {
// This model type is always rendered as a string alias, // This model type is always rendered as a string alias,
// hence we can generate it and use it as a type for JS object keys. // hence we can generate it and use it as a type for JS object keys.
imports.addTypeImpl(t.Key(), visited) imports.addTypeImpl(t.Key(), visited)
} }
} else if IsTypeParam(t.Key()) {
// In some cases, type params or pointers to type params
// may be valid as map keys, but not for all instantiations.
// When that happens, emit a softer warning.
collector.logger.Warningf(
"package %s: type %s is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors",
imports.Self, types.TypeString(t.Key(), nil),
)
} else { } else {
collector.logger.Warningf( collector.logger.Warningf(
"%s is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors", "package %s: type %s is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors",
types.TypeString(t.Key(), nil), imports.Self, types.TypeString(t.Key(), nil),
) )
} }
typ = t.Elem() typ = t.Elem()
case *types.Signature: case *types.Signature:
collector.logger.Warningf("function types are not supported by encoding/json") collector.logger.Warningf("package %s: function types are not supported by encoding/json", imports.Self)
return return
case *types.Struct: case *types.Struct:
if t.NumFields() == 0 { if t.NumFields() == 0 || MaybeJSONMarshaler(typ) != NonMarshaler || MaybeTextMarshaler(typ) != NonMarshaler {
// Empty struct. // Struct is empty, or marshals to custom JSON (any) or string.
return return
} }
@ -246,7 +260,7 @@ func (imports *ImportMap) addTypeImpl(typ types.Type, visited map[*types.TypeNam
return return
default: default:
collector.logger.Warningf("unknown type %s: please report this to Wails maintainers", typ) collector.logger.Warningf("package %s: unknown type %s: please report this to Wails maintainers", imports.Self, typ)
return return
} }
} }

View File

@ -10,13 +10,13 @@ import (
// //
// When obtained through a call to [PackageInfo.Index], // When obtained through a call to [PackageInfo.Index],
// each service and model appears at most once; // each service and model appears at most once;
// services are sorted by name; // services and models are sorted
// exported models precede all unexported ones // by internal property (all exported first), then by name.
// and both ranges are sorted by name.
type PackageIndex struct { type PackageIndex struct {
Package *PackageInfo Package *PackageInfo
Services []*ServiceInfo Services []*ServiceInfo
HasExportedServices bool // If true, there is at least one exported service.
Models []*ModelInfo Models []*ModelInfo
HasExportedModels bool // If true, there is at least one exported model. HasExportedModels bool // If true, there is at least one exported model.
@ -47,9 +47,10 @@ func (info *PackageInfo) Index(TS bool) (index *PackageIndex) {
for _, value := range info.services.Range { for _, value := range info.services.Range {
service := value.(*ServiceInfo) service := value.(*ServiceInfo)
if !service.IsEmpty() { if !service.IsEmpty() {
if service.Object().Exported() {
// Publish non-internal service on the local index.
index.Services = append(index.Services, service) index.Services = append(index.Services, service)
// Mark presence of exported services
if !service.Internal {
index.HasExportedServices = true
} }
// Update service stats. // Update service stats.
stats.NumServices++ stats.NumServices++
@ -57,12 +58,21 @@ func (info *PackageInfo) Index(TS bool) (index *PackageIndex) {
} }
} }
// Sort services by name. // Sort services by internal property (exported first), then by name.
slices.SortFunc(index.Services, func(b1 *ServiceInfo, b2 *ServiceInfo) int { slices.SortFunc(index.Services, func(s1 *ServiceInfo, s2 *ServiceInfo) int {
if b1 == b2 { if s1 == s2 {
return 0 return 0
} }
return strings.Compare(b1.Name, b2.Name)
if s1.Internal != s2.Internal {
if s1.Internal {
return 1
} else {
return -1
}
}
return strings.Compare(s1.Name, s2.Name)
}) })
// Gather models. // Gather models.
@ -70,7 +80,7 @@ func (info *PackageInfo) Index(TS bool) (index *PackageIndex) {
model := value.(*ModelInfo) model := value.(*ModelInfo)
index.Models = append(index.Models, model) index.Models = append(index.Models, model)
// Mark presence of exported models // Mark presence of exported models
if model.Object().Exported() { if !model.Internal {
index.HasExportedModels = true index.HasExportedModels = true
} }
// Update model stats. // Update model stats.
@ -81,18 +91,17 @@ func (info *PackageInfo) Index(TS bool) (index *PackageIndex) {
} }
} }
// Sort models by exported property (exported first), then by name. // Sort models by internal property (exported first), then by name.
slices.SortFunc(index.Models, func(m1 *ModelInfo, m2 *ModelInfo) int { slices.SortFunc(index.Models, func(m1 *ModelInfo, m2 *ModelInfo) int {
if m1 == m2 { if m1 == m2 {
return 0 return 0
} }
m1e, m2e := m1.Object().Exported(), m2.Object().Exported() if m1.Internal != m2.Internal {
if m1e != m2e { if m1.Internal {
if m1e {
return -1
} else {
return 1 return 1
} else {
return -1
} }
} }
@ -108,5 +117,5 @@ func (info *PackageInfo) Index(TS bool) (index *PackageIndex) {
// IsEmpty returns true if the given index // IsEmpty returns true if the given index
// contains no data for the selected language. // contains no data for the selected language.
func (index *PackageIndex) IsEmpty() bool { func (index *PackageIndex) IsEmpty() bool {
return len(index.Package.Injections) == 0 && len(index.Services) == 0 && len(index.Models) == 0 return !index.HasExportedServices && !index.HasExportedModels && len(index.Package.Injections) == 0
} }

View File

@ -2,6 +2,7 @@ package collect
import ( import (
"cmp" "cmp"
"go/ast"
"go/constant" "go/constant"
"go/types" "go/types"
"slices" "slices"
@ -20,6 +21,10 @@ type (
ModelInfo struct { ModelInfo struct {
*TypeInfo *TypeInfo
// Internal records whether the model
// should be exported by the index file.
Internal bool
// Imports records dependencies for this model. // Imports records dependencies for this model.
Imports *ImportMap Imports *ImportMap
@ -39,6 +44,13 @@ type (
// TypeParams records type parameter names for generic models. // TypeParams records type parameter names for generic models.
TypeParams []string TypeParams []string
// Predicates caches the value of all type predicates for this model.
//
// WARN: whenever working with a generic uninstantiated model type,
// use these instead of invoking predicate functions,
// which may incur a large performance penalty.
Predicates Predicates
collector *Collector collector *Collector
once sync.Once once sync.Once
} }
@ -49,6 +61,19 @@ type (
*StructField *StructField
*FieldInfo *FieldInfo
} }
// Predicates caches the value of all type predicates.
Predicates struct {
IsJSONMarshaler MarshalerKind
MaybeJSONMarshaler MarshalerKind
IsTextMarshaler MarshalerKind
MaybeTextMarshaler MarshalerKind
IsMapKey bool
IsTypeParam bool
IsStringAlias bool
IsClass bool
IsAny bool
}
) )
func newModelInfo(collector *Collector, obj *types.TypeName) *ModelInfo { func newModelInfo(collector *Collector, obj *types.TypeName) *ModelInfo {
@ -113,15 +138,24 @@ func (info *ModelInfo) Collect() *ModelInfo {
// Setup fallback type. // Setup fallback type.
info.Type = types.Universe.Lookup("any").Type() info.Type = types.Universe.Lookup("any").Type()
// Retrieve type denotation and skip alias chains. // Record whether the model should be exported.
def := info.TypeInfo.Def info.Internal = !obj.Exported()
// Check marshalers and detect enums. // Parse directives.
var constants []*types.Const for _, doc := range []*ast.CommentGroup{info.Doc, info.Decl.Doc} {
if doc == nil {
continue
}
for _, comment := range doc.List {
if IsDirective(comment.Text, "internal") {
info.Internal = true
}
}
}
var isGeneric bool
if generic, ok := obj.Type().(interface{ TypeParams() *types.TypeParamList }); ok {
// Record type parameter names. // Record type parameter names.
var isGeneric bool
if generic, ok := typ.(interface{ TypeParams() *types.TypeParamList }); ok {
tparams := generic.TypeParams() tparams := generic.TypeParams()
isGeneric = tparams != nil isGeneric = tparams != nil
@ -133,28 +167,82 @@ func (info *ModelInfo) Collect() *ModelInfo {
} }
} }
if _, isNamed := obj.Type().(*types.Named); isNamed { // Precompute predicates.
// Model is a named type. // Preinstantiate typ to avoid repeated instantiations in predicate code.
// Check whether it implements marshaler interfaces ityp := instantiate(typ)
// or has defined constants. info.Predicates = Predicates{
IsJSONMarshaler: IsJSONMarshaler(ityp),
if IsAny(typ) { MaybeJSONMarshaler: MaybeJSONMarshaler(ityp),
// Type marshals to a custom value of unknown shape. IsTextMarshaler: IsTextMarshaler(ityp),
return MaybeTextMarshaler: MaybeTextMarshaler(ityp),
} else if MaybeTextMarshaler(typ) { IsMapKey: IsMapKey(ityp),
// Type marshals to a custom string of unknown shape. IsTypeParam: IsTypeParam(ityp),
info.Type = types.Typ[types.String] IsStringAlias: IsStringAlias(ityp),
return IsClass: IsClass(ityp),
} else if isGeneric && !collector.options.UseInterfaces && IsClass(typ) { IsAny: IsAny(ityp),
// Generic classes cannot be defined in terms of other generic classes.
// That would break class creation code,
// and I (@fbbdev) couldn't find any other satisfying workaround.
def = typ.Underlying()
} }
// Test for enums (excluding generic types). var def types.Type
basic, ok := typ.Underlying().(*types.Basic) var constants []*types.Const
if ok && !isGeneric && basic.Info()&types.IsConstType != 0 && basic.Info()&types.IsComplex == 0 {
switch t := typ.(type) {
case *types.Alias:
// Model is an alias: take rhs as definition.
// It is important not to skip alias chains with [types.Unalias]
// because in doing so we could end up with a private type from another package.
def = t.Rhs()
// Test for constants with alias type,
// but only when non-generic alias resolves to a basic type
// (hence not to e.g. a named type).
if basic, ok := types.Unalias(def).(*types.Basic); ok {
if !isGeneric && basic.Info()&types.IsConstType != 0 && basic.Info()&types.IsComplex == 0 {
// Non-generic alias resolves to a representable constant type:
// look for defined constants whose type is exactly the alias typ.
for _, name := range obj.Pkg().Scope().Names() {
if cnst, ok := obj.Pkg().Scope().Lookup(name).(*types.Const); ok {
alias, isAlias := cnst.Type().(*types.Alias)
if isAlias && cnst.Val().Kind() != constant.Unknown && alias.Obj() == t.Obj() {
constants = append(constants, cnst)
}
}
}
}
}
case *types.Named:
// Model is a named type:
// jump directly to underlying type to match go semantics,
// i.e. do not render named types as aliases for other named types.
def = typ.Underlying()
// Check whether it implements marshaler interfaces or has defined constants.
if info.Predicates.MaybeJSONMarshaler != NonMarshaler {
// Type marshals to a custom value of unknown shape.
// If it has explicit custom marshaling logic, render it as any;
// otherwise, delegate to the underlying type that must be the actual [json.Marshaler].
if info.Predicates.MaybeJSONMarshaler == ExplicitMarshaler {
return
}
} else if info.Predicates.MaybeTextMarshaler != NonMarshaler {
// Type marshals to a custom string of unknown shape.
// If it has explicit custom marshaling logic, render it as string;
// otherwise, delegate to the underlying type that must be the actual [encoding.TextMarshaler].
//
// One exception must be made for situations
// where the underlying type is a [json.Marshaler] but the model is not:
// in that case, we cannot delegate to the underlying type either.
// Note that in such a case the underlying type is never a pointer or interface,
// because those cannot have explicitly defined methods,
// hence it would not possible for the model not to be a [json.Marshaler]
// while the underlying type is.
if info.Predicates.MaybeTextMarshaler == ExplicitMarshaler || MaybeJSONMarshaler(def) != NonMarshaler {
info.Type = types.Typ[types.String]
return
}
} else if basic, ok := def.Underlying().(*types.Basic); ok {
// Test for enums (excluding marshalers and generic types).
if !isGeneric && basic.Info()&types.IsConstType != 0 && basic.Info()&types.IsComplex == 0 {
// Named type is defined as a representable constant type: // Named type is defined as a representable constant type:
// look for defined constants of that named type. // look for defined constants of that named type.
for _, name := range obj.Pkg().Scope().Names() { for _, name := range obj.Pkg().Scope().Names() {
@ -167,27 +255,30 @@ func (info *ModelInfo) Collect() *ModelInfo {
} }
} }
// Record required imports. default:
info.Imports.AddType(def) panic("model has unknown object kind (neither alias nor named type)")
// Handle enum types.
// constants slice is always empty for aliases.
if len(constants) > 0 {
// Collect information about enum values.
info.collectEnum(constants)
info.Type = def
return
} }
// Handle struct types. // Handle struct types.
strct, isStruct := def.(*types.Struct) strct, isStruct := def.(*types.Struct)
if isStruct { if isStruct && info.Predicates.MaybeJSONMarshaler == NonMarshaler && info.Predicates.MaybeTextMarshaler == NonMarshaler {
// Collect information about struct fields. // Def is struct and model is not a marshaler:
// collect information about struct fields.
info.collectStruct(strct) info.collectStruct(strct)
info.Type = nil info.Type = nil
return return
} }
// Record required imports.
info.Imports.AddType(def)
// Handle enum types.
// constants slice is always empty for structs, marshalers.
if len(constants) > 0 {
// Collect information about enum values.
info.collectEnum(constants)
}
// That's all, folks. Render as a TS alias. // That's all, folks. Render as a TS alias.
info.Type = def info.Type = def
}) })
@ -265,6 +356,9 @@ func (info *ModelInfo) collectStruct(strct *types.Struct) {
// Collect fields. // Collect fields.
for i, field := range structInfo.Fields { for i, field := range structInfo.Fields {
// Record required imports.
info.Imports.AddType(field.Type)
fields[i] = &ModelFieldInfo{ fields[i] = &ModelFieldInfo{
StructField: field, StructField: field,
FieldInfo: collector.Field(field.Object).Collect(), FieldInfo: collector.Field(field.Object).Collect(),

View File

@ -0,0 +1,478 @@
package collect
// This file gathers functions that test useful properties of model types.
// The rationale for the way things are handled here
// is given in the example file found at ./_reference/json_marshaler_behaviour.go
import (
"go/token"
"go/types"
"iter"
"golang.org/x/exp/typeparams"
)
// Cached interface types.
var (
ifaceTextMarshaler = types.NewInterfaceType([]*types.Func{
types.NewFunc(token.NoPos, nil, "MarshalText",
types.NewSignatureType(nil, nil, nil, types.NewTuple(), types.NewTuple(
types.NewParam(token.NoPos, nil, "", types.NewSlice(types.Universe.Lookup("byte").Type())),
types.NewParam(token.NoPos, nil, "", types.Universe.Lookup("error").Type()),
), false)),
}, nil).Complete()
ifaceJSONMarshaler = types.NewInterfaceType([]*types.Func{
types.NewFunc(token.NoPos, nil, "MarshalJSON",
types.NewSignatureType(nil, nil, nil, types.NewTuple(), types.NewTuple(
types.NewParam(token.NoPos, nil, "", types.NewSlice(types.Universe.Lookup("byte").Type())),
types.NewParam(token.NoPos, nil, "", types.Universe.Lookup("error").Type()),
), false)),
}, nil).Complete()
)
// MarshalerKind values describe
// whether and how a type implements a marshaler interface.
// For any one of the two marshaler interfaces, a type is
// - a NonMarshaler if it does not implement it;
// - an ImplicitMarshaler if it inherits the implementation from its underlying type;
// - an ExplicitMarshaler if it defines the relevant method explicitly.
type MarshalerKind byte
const (
NonMarshaler MarshalerKind = iota
ImplicitMarshaler
ExplicitMarshaler
)
// termlist returns an iterator over the normalised term list of the given type.
// If typ is invalid or has an empty type set, termlist returns the empty sequence.
// If typ has an empty term list
// then termlist returns a sequence with just one element: the type itself.
//
// TODO: replace with new term set API once Go 1.25 is out.
// See go.dev/issue/61013
func termlist(typ types.Type) iter.Seq[*typeparams.Term] {
terms, err := typeparams.NormalTerms(types.Unalias(typ))
return func(yield func(*typeparams.Term) bool) {
if err == nil && len(terms) == 0 {
yield(typeparams.NewTerm(false, typ))
} else {
for _, term := range terms {
if !yield(term) {
break
}
}
}
}
}
// instantiate instantiates typ if it is an uninstantiated generic type
// using its own type parameters as arguments in order to preserve genericity.
//
// If typ is not generic or already instantiated, it is returned as is.
// If typ is not an alias, then the returned type is not an alias either.
func instantiate(typ types.Type) types.Type {
if t, ok := typ.(interface {
TypeParams() *types.TypeParamList
TypeArgs() *types.TypeList
}); ok && t.TypeParams() != nil && t.TypeArgs() == nil {
args := make([]types.Type, t.TypeParams().Len())
for i := range args {
args[i] = t.TypeParams().At(i)
}
typ, _ = types.Instantiate(nil, typ, args, false)
}
return typ
}
// isMarshaler checks whether the given type
// implements one of the two marshaler interfaces,
// and whether it implements it explicitly,
// i.e. by defining the relevant method directly
// instead of inheriting it from the underlying type.
//
// If addressable is true, it checks both pointer and non-pointer receivers.
//
// The behaviour of isMarshaler is unspecified
// if marshaler is not one of [json.Marshaler] or [encoding.TextMarshaler].
func isMarshaler(typ types.Type, marshaler *types.Interface, addressable bool, visited map[*types.TypeName]MarshalerKind) MarshalerKind {
// Follow alias chain and instantiate if necessary.
//
// types.Implements does not handle generics,
// hence when typ is generic it must be instantiated.
//
// Instantiation operations may incur a large performance penalty and are usually cached,
// but doing so here would entail some complex global state and a potential memory leak.
// Because typ should be generic only during model collection,
// it should be enough to cache the result of marshaler queries for models.
typ = instantiate(types.Unalias(typ))
// Invariant: at this point, typ is not an alias.
if typ == types.Typ[types.Invalid] {
// Do not pass invalid types to [types.Implements].
return NonMarshaler
}
result := types.Implements(typ, marshaler)
ptr, isPtr := typ.Underlying().(*types.Pointer)
if !result && addressable && !isPtr {
result = types.Implements(types.NewPointer(typ), marshaler)
}
named, isNamed := typ.(*types.Named)
if result {
// Check whether marshaler method is implemented explicitly on a named type.
if isNamed {
method := marshaler.Method(0).Name()
for i := range named.NumMethods() {
if named.Method(i).Name() == method {
return ExplicitMarshaler
}
}
}
return ImplicitMarshaler
}
// Fast path: named types that fail the [types.Implements] test cannot be marshalers.
//
// WARN: currently typeparams cannot be used on the rhs of a named type declaration.
// If that changes in the future,
// this guard will become essential for correctness,
// not just a shortcut.
if isNamed {
return NonMarshaler
}
// Unwrap at most one pointer and follow alias chain.
if isPtr {
typ = types.Unalias(ptr.Elem())
}
// Invariant: at this point, typ is not an alias.
// Type parameters require special handling:
// iterate over their term list and treat them as marshalers
// if so are all their potential instantiations.
tp, ok := typ.(*types.TypeParam)
if !ok {
return NonMarshaler
}
// Init cycle detection/deduplication map.
if visited == nil {
visited = make(map[*types.TypeName]MarshalerKind)
}
// Type params cannot be embedded in constraints directly,
// but they can be embedded as pointer terms.
//
// When we hit that kind of cycle,
// we can err towards it being a marshaler:
// such a constraint is meaningless anyways,
// as no type can be simultaneously a pointer to itself.
//
// Therefore, we iterate the type set
// only for unvisited pointers-to-typeparams,
// and return the current best guess
// for those we have already visited.
//
// WARN: there has been some talk
// of allowing type parameters as embedded fields/terms.
// That might make our lives miserable here.
// The spec must be monitored for changes in that regard.
if isPtr {
if kind, ok := visited[tp.Obj()]; ok {
return kind
}
}
// Initialise kind to explicit marshaler, then decrease as needed.
kind := ExplicitMarshaler
if isPtr {
// Pointers are never explicit marshalers.
kind = ImplicitMarshaler
// Mark pointer-to-typeparam as visited and init current best guess.
visited[tp.Obj()] = kind
}
// Iterate term list.
for term := range termlist(tp) {
ttyp := types.Unalias(term.Type())
// Reject if tp has a tilde or invalid element in its term list
// or has a method-only constraint.
//
// Valid tilde terms
// can always be satisfied by named types that hide their methods
// hence fail in general to implement the required interface.
if term.Tilde() || ttyp == types.Typ[types.Invalid] || ttyp == tp {
kind = NonMarshaler
break
}
// Propagate the presence of a wrapping pointer.
if isPtr {
ttyp = types.NewPointer(ttyp)
}
kind = min(kind, isMarshaler(ttyp, marshaler, addressable && !isPtr, visited))
if kind == NonMarshaler {
// We can stop here as we've reached the minimum [MarshalerKind].
break
}
}
// Store final response for pointer-to-typeparam.
if isPtr {
visited[tp.Obj()] = kind
}
return kind
}
// IsTextMarshaler queries whether and how the given type
// implements the [encoding.TextMarshaler] interface.
func IsTextMarshaler(typ types.Type) MarshalerKind {
return isMarshaler(typ, ifaceTextMarshaler, false, nil)
}
// MaybeTextMarshaler queries whether and how the given type
// implements the [encoding.TextMarshaler] interface for at least one receiver form.
func MaybeTextMarshaler(typ types.Type) MarshalerKind {
return isMarshaler(typ, ifaceTextMarshaler, true, nil)
}
// IsJSONMarshaler queries whether and how the given type
// implements the [json.Marshaler] interface.
func IsJSONMarshaler(typ types.Type) MarshalerKind {
return isMarshaler(typ, ifaceJSONMarshaler, false, nil)
}
// MaybeJSONMarshaler queries whether and how the given type
// implements the [json.Marshaler] interface for at least one receiver form.
func MaybeJSONMarshaler(typ types.Type) MarshalerKind {
return isMarshaler(typ, ifaceJSONMarshaler, true, nil)
}
// IsMapKey returns true if the given type
// is accepted as a map key by encoding/json.
func IsMapKey(typ types.Type) bool {
// Iterate over type set and return true if all elements are valid.
//
// We cannot simply delegate to [IsTextMarshaler] here
// because a union of some basic terms and some TextMarshalers
// might still be acceptable.
//
// NOTE: If typ is not a typeparam or constraint, termlist returns just typ itself.
// If typ has an empty type set, it's safe to return true
// because the map cannot be instantiated anyways.
for term := range termlist(typ) {
ttyp := types.Unalias(term.Type())
// Types whose underlying type is a signed/unsigned integer or a string
// are always acceptable, whether they are marshalers or not.
if basic, ok := ttyp.Underlying().(*types.Basic); ok {
if basic.Info()&(types.IsInteger|types.IsUnsigned|types.IsString) != 0 {
continue
}
}
// Valid tilde terms
// can always be satisfied by named types that hide their methods
// hence fail in general to implement the required interface.
// For example one could have:
//
// type NotAKey struct{ encoding.TextMarshaler }
// func (NotAKey) MarshalText() int { ... }
//
// which satisfies ~struct{ encoding.TextMarshaler }
// but is not itself a TextMarshaler.
//
// It might still be the case that the constraint
// requires explicitly a marshaling method,
// hence we perform one last check on typ.
//
// For example, we reject interface{ ~struct{ ... } }
// but still accept interface{ ~struct{ ... }; MarshalText() ([]byte, error) }
//
// All other cases are only acceptable
// if the type implements [encoding.TextMarshaler] in non-addressable mode.
if term.Tilde() || IsTextMarshaler(ttyp) == NonMarshaler {
// When some term fails, test the input typ itself,
// but only if it has not been tested already.
//
// Note that when term.Tilde() is true
// then it is always the case that typ != term.Type(),
// because cyclic constraints are not allowed
// and naked type parameters cannot occur in type unions.
return typ != term.Type() && IsTextMarshaler(typ) != NonMarshaler
}
}
return true
}
// IsTypeParam returns true when the given type
// is either a TypeParam or a pointer to a TypeParam.
func IsTypeParam(typ types.Type) bool {
switch t := types.Unalias(typ).(type) {
case *types.TypeParam:
return true
case *types.Pointer:
_, ok := types.Unalias(t.Elem()).(*types.TypeParam)
return ok
default:
return false
}
}
// IsStringAlias returns true when
// either typ will be rendered to JS/TS as an alias for the TS type `string`,
// or typ itself (not its underlying type) is a pointer
// whose element type satisfies the property described above.
//
// This predicate is only safe to use either with map keys,
// where pointers are treated in an ad-hoc way by [json.Marshal],
// or when typ IS ALREADY KNOWN to be either [types.Alias] or [types.Named].
//
// Otherwise, the result might be incorrect:
// IsStringAlias MUST NOT be used to check
// whether an arbitrary instance of [types.Type]
// renders as a JS/TS string type.
//
// Notice that IsStringAlias returns false for all type parameters:
// detecting those that must be always instantiated as string aliases
// is technically possible, but very difficult.
func IsStringAlias(typ types.Type) bool {
// Unwrap at most one pointer.
// NOTE: do not unalias typ before testing:
// aliases whose underlying type is a pointer
// are never rendered as strings.
if ptr, ok := typ.(*types.Pointer); ok {
typ = ptr.Elem()
}
switch typ.(type) {
case *types.Alias, *types.Named:
// Aliases and named types might be rendered as string aliases.
default:
// Not a model type, hence not an alias.
return false
}
// Skip pointer and interface types: they are always nullable
// and cannot have any explicitly defined methods.
// This takes care of rejecting type params as well,
// since their underlying type is guaranteed to be an interface.
switch typ.Underlying().(type) {
case *types.Pointer, *types.Interface:
return false
}
// Follow alias chain.
typ = types.Unalias(typ)
// Aliases of the basic string type are rendered as strings.
if basic, ok := typ.(*types.Basic); ok {
return basic.Info()&types.IsString != 0
}
// json.Marshalers can only be rendered as any.
// TextMarshalers that aren't json.Marshalers render as strings.
if MaybeJSONMarshaler(typ) != NonMarshaler {
return false
} else if MaybeTextMarshaler(typ) != NonMarshaler {
return true
}
// Named types whose underlying type is a string are rendered as strings.
basic, ok := typ.Underlying().(*types.Basic)
return ok && basic.Info()&types.IsString != 0
}
// IsClass returns true if the given type will be rendered
// as a JS/TS model class (or interface).
func IsClass(typ types.Type) bool {
// Follow alias chain.
typ = types.Unalias(typ)
if _, isNamed := typ.(*types.Named); !isNamed {
// Unnamed types are never rendered as classes.
return false
}
// Struct named types without custom marshaling are rendered as classes.
_, isStruct := typ.Underlying().(*types.Struct)
return isStruct && MaybeJSONMarshaler(typ) == NonMarshaler && MaybeTextMarshaler(typ) == NonMarshaler
}
// IsAny returns true if the given type
// is guaranteed to render as the TS any type or equivalent.
//
// It might return false negatives for generic aliases,
// hence should only be used with instantiated types
// or in contexts where false negatives are acceptable.
func IsAny(typ types.Type) bool {
// Follow alias chain.
typ = types.Unalias(typ)
if MaybeJSONMarshaler(typ) != NonMarshaler {
// If typ is either a named type, an interface, a pointer or a struct,
// it will be rendered as (possibly an alias for) the TS any type.
//
// If it is a type parameter that implements json.Marshal,
// every possible concrete instantiation will implement json.Marshal,
// hence will be rendered as the TS any type.
return true
}
if MaybeTextMarshaler(typ) != NonMarshaler {
// If type is either a named type, an interface, a pointer or a struct,
// it will be rendered as (possibly an alias for)
// the (possibly nullable) TS string type.
//
// If typ is a type parameter, we know at this point
// that it does not necessarily implement json.Marshaler,
// hence it will be possible to instantiate it in a way
// that renders as the (possibly nullable) TS string type.
return false
}
if ptr, ok := typ.Underlying().(*types.Pointer); ok {
// Pointers render as the union of their element type with null.
// This is equivalent to the TS any type
// if and only if so is the element type.
return IsAny(ptr.Elem())
}
// All types listed below have rich TS equivalents,
// hence won't be equivalent to the TS any type.
//
// WARN: it is important to keep these lists explicit and up to date
// instead of listing the unsupported types (which would be much easier).
//
// By doing so, IsAny will keep working correctly
// in case future updates to the Go spec introduce new type families,
// thus buying the maintainers some time to patch the binding generator.
// Retrieve underlying type.
switch t := typ.Underlying().(type) {
case *types.Basic:
// Complex types are not supported.
return t.Info()&(types.IsBoolean|types.IsInteger|types.IsUnsigned|types.IsFloat|types.IsString) == 0
case *types.Array, *types.Slice, *types.Map, *types.Struct, *types.TypeParam:
return false
}
return true
}

View File

@ -1,152 +0,0 @@
package collect
// This file gathers functions that test useful properties of model types.
// The rationale for the way things are handled here
// is given in the example file found at ./_reference/json_marshaler_behaviour.go
import (
"go/token"
"go/types"
)
// Cached interface types.
var (
ifaceTextMarshaler = types.NewInterfaceType([]*types.Func{
types.NewFunc(token.NoPos, nil, "MarshalText",
types.NewSignatureType(nil, nil, nil, types.NewTuple(), types.NewTuple(
types.NewParam(token.NoPos, nil, "", types.NewSlice(types.Universe.Lookup("byte").Type())),
types.NewParam(token.NoPos, nil, "", types.Universe.Lookup("error").Type()),
), false)),
}, nil).Complete()
ifaceJSONMarshaler = types.NewInterfaceType([]*types.Func{
types.NewFunc(token.NoPos, nil, "MarshalJSON",
types.NewSignatureType(nil, nil, nil, types.NewTuple(), types.NewTuple(
types.NewParam(token.NoPos, nil, "", types.NewSlice(types.Universe.Lookup("byte").Type())),
types.NewParam(token.NoPos, nil, "", types.Universe.Lookup("error").Type()),
), false)),
}, nil).Complete()
)
// IsTextMarshaler tests whether the given type implements
// the encoding.TextMarshaler interface.
func IsTextMarshaler(typ types.Type) bool {
return types.Implements(typ, ifaceTextMarshaler)
}
// MaybeTextMarshaler tests whether the given type implements
// the encoding.TextMarshaler interface for at least one receiver form.
func MaybeTextMarshaler(typ types.Type) bool {
if _, ok := types.Unalias(typ).(*types.Pointer); !ok {
typ = types.NewPointer(typ)
}
return IsTextMarshaler(typ)
}
// IsJSONMarshaler tests whether the given type implements
// the json.Marshaler interface.
func IsJSONMarshaler(typ types.Type) bool {
return types.Implements(typ, ifaceJSONMarshaler)
}
// MaybeJSONMarshaler tests whether the given type implements
// the json.Marshaler interface for at least one receiver form.
func MaybeJSONMarshaler(typ types.Type) bool {
if _, ok := types.Unalias(typ).(*types.Pointer); !ok {
typ = types.NewPointer(typ)
}
return IsJSONMarshaler(typ)
}
// IsMapKey returns true if the given type
// is accepted as a map key by encoding/json.
func IsMapKey(typ types.Type) bool {
if basic, ok := typ.Underlying().(*types.Basic); ok {
return basic.Info()&(types.IsInteger|types.IsString) != 0
}
// Other types are only accepted
// if they implement encoding.TextMarshaler strictly as they are.
return IsTextMarshaler(typ)
}
// IsString returns true if the given type (or element type for pointers)
// will be rendered as an alias for the TS string type.
func IsString(typ types.Type) bool {
// Unwrap at most one pointer.
// NOTE: do not unalias typ before testing:
// aliases whose underlying type is a pointer
// are _never_ rendered as strings.
if ptr, ok := typ.(*types.Pointer); ok {
typ = ptr.Elem()
}
switch typ.(type) {
case *types.Alias, *types.Named:
// Aliases and named types might be rendered as string aliases.
default:
// Not a model type.
return false
}
// Follow alias chain.
typ = types.Unalias(typ)
if basic, ok := typ.(*types.Basic); ok {
// Test whether basic type is a string.
return basic.Info()&types.IsString != 0
}
// JSONMarshalers can only be rendered as any.
// TextMarshalers that aren't JSONMarshalers render as strings.
if MaybeJSONMarshaler(typ) {
return false
} else if MaybeTextMarshaler(typ) {
return true
}
// Named types whose underlying type is a string are rendered as strings.
basic, ok := typ.Underlying().(*types.Basic)
return ok && basic.Info()&types.IsString != 0
}
// IsClass returns true if the given type will be rendered
// as a JS/TS model class (or interface).
func IsClass(typ types.Type) bool {
// Follow alias chain.
typ = types.Unalias(typ)
if _, isNamed := typ.(*types.Named); !isNamed {
// Unnamed types are never rendered as classes.
return false
}
// Struct named types without custom marshaling are rendered as classes.
_, isStruct := typ.Underlying().(*types.Struct)
return isStruct && !MaybeJSONMarshaler(typ) && !MaybeTextMarshaler(typ)
}
// IsAny returns true if the given type will be rendered as a TS any type.
func IsAny(typ types.Type) bool {
// Follow alias chain.
typ = types.Unalias(typ)
if MaybeJSONMarshaler(typ) {
return true
}
if MaybeTextMarshaler(typ) {
return false
}
// Retrieve underlying type
switch t := typ.Underlying().(type) {
case *types.Basic:
// Complex types are not supported.
return t.Info()&types.IsComplex != 0
case *types.Chan, *types.Signature, *types.Interface:
return true
}
return false
}

View File

@ -22,9 +22,17 @@ type (
ServiceInfo struct { ServiceInfo struct {
*TypeInfo *TypeInfo
// Internal records whether the service
// should be exported by the index file.
Internal bool
Imports *ImportMap Imports *ImportMap
Methods []*ServiceMethodInfo Methods []*ServiceMethodInfo
// HasInternalMethods records whether the service
// defines lifecycle or http server methods.
HasInternalMethods bool
// Injections stores a list of JS code lines // Injections stores a list of JS code lines
// that should be injected into the generated file. // that should be injected into the generated file.
Injections []string Injections []string
@ -80,7 +88,7 @@ func (collector *Collector) Service(obj *types.TypeName) *ServiceInfo {
func (info *ServiceInfo) IsEmpty() bool { func (info *ServiceInfo) IsEmpty() bool {
// Ensure information has been collected. // Ensure information has been collected.
info.Collect() info.Collect()
return len(info.Injections) == 0 && len(info.Methods) == 0 return len(info.Methods) == 0 && len(info.Injections) == 0
} }
// Collect gathers information about the service described by its receiver. // Collect gathers information about the service described by its receiver.
@ -119,7 +127,11 @@ func (info *ServiceInfo) Collect() *ServiceInfo {
// Collect method information. // Collect method information.
info.Methods = make([]*ServiceMethodInfo, 0, len(mset)) info.Methods = make([]*ServiceMethodInfo, 0, len(mset))
for _, sel := range mset { for _, sel := range mset {
if !sel.Obj().Exported() || internalServiceMethods[sel.Obj().Name()] { switch {
case internalServiceMethods[sel.Obj().Name()]:
info.HasInternalMethods = true
continue
case !sel.Obj().Exported():
// Ignore unexported and internal methods. // Ignore unexported and internal methods.
continue continue
} }
@ -130,13 +142,20 @@ func (info *ServiceInfo) Collect() *ServiceInfo {
} }
} }
// Record whether the service should be exported.
info.Internal = !obj.Exported()
// Parse directives. // Parse directives.
for _, doc := range []*ast.CommentGroup{info.Doc, info.Decl.Doc} { for _, doc := range []*ast.CommentGroup{info.Doc, info.Decl.Doc} {
if doc == nil { if doc == nil {
continue continue
} }
for _, comment := range doc.List { for _, comment := range doc.List {
if IsDirective(comment.Text, "inject") { switch {
case IsDirective(comment.Text, "internal"):
info.Internal = true
case IsDirective(comment.Text, "inject"):
// Check condition. // Check condition.
line, cond, err := ParseCondition(ParseDirective(comment.Text, "inject")) line, cond, err := ParseCondition(ParseDirective(comment.Text, "inject"))
if err != nil { if err != nil {
@ -217,6 +236,9 @@ func (info *ServiceInfo) collectMethod(method *types.Func) *ServiceMethodInfo {
for _, comment := range methodInfo.Doc.List { for _, comment := range methodInfo.Doc.List {
switch { switch {
case IsDirective(comment.Text, "ignore"):
return nil
case IsDirective(comment.Text, "internal"): case IsDirective(comment.Text, "internal"):
methodInfo.Internal = true methodInfo.Internal = true

View File

@ -193,6 +193,14 @@ func (info *StructInfo) Collect() *StructInfo {
// or field is not structure: // or field is not structure:
// add to field list. // add to field list.
if !info.collector.options.UseInterfaces {
// In class mode, mark parametric fields as optional
// because there is no way to know their default JS value in advance.
if _, isTypeParam := types.Unalias(field.Type()).(*types.TypeParam); isTypeParam {
optional = true
}
}
finfo := fieldData{ finfo := fieldData{
StructField: &StructField{ StructField: &StructField{
JsonName: name, JsonName: name,
@ -317,7 +325,7 @@ func parseTag(tag string) (name string, optional bool, quoted bool, visible bool
for _, option := range parts[1:] { for _, option := range parts[1:] {
switch option { switch option {
case "omitempty": case "omitempty", "omitzero":
optional = true optional = true
case "string": case "string":
quoted = true quoted = true

View File

@ -22,12 +22,6 @@ type TypeInfo struct {
Doc *ast.CommentGroup Doc *ast.CommentGroup
Decl *GroupInfo Decl *GroupInfo
// Def holds the actual denotation of the type expression
// that defines the described type.
// This is not the same as the underlying type,
// which skips all intermediate aliases and named types.
Def types.Type
obj *types.TypeName obj *types.TypeName
node ast.Node node ast.Node
@ -92,13 +86,8 @@ func (info *TypeInfo) Collect() *TypeInfo {
info.Name, info.Name,
) )
// Provide dummy group and def. // Provide dummy group.
info.Decl = newGroupInfo(nil).Collect() info.Decl = newGroupInfo(nil).Collect()
if _, isAlias := info.obj.Type().(*types.Alias); isAlias {
info.Def = types.Unalias(info.obj.Type())
} else {
info.Def = info.obj.Type().Underlying()
}
return return
} }
@ -117,8 +106,6 @@ func (info *TypeInfo) Collect() *TypeInfo {
info.Decl = collector.fromCache(path[1]).(*GroupInfo).Collect() info.Decl = collector.fromCache(path[1]).(*GroupInfo).Collect()
info.Def = collector.Package(info.obj.Pkg()).TypesInfo.TypeOf(tspec.Type)
info.node = path[0] info.node = path[0]
}) })

View File

@ -209,13 +209,7 @@ func (generator *Generator) generateModelsIndexIncludes(info *collect.PackageInf
if len(index.Models) > 0 { if len(index.Models) > 0 {
generator.scheduler.Schedule(func() { generator.scheduler.Schedule(func() {
generator.generateTypedefs(info, index.Models) generator.generateModels(info, index.Models)
})
}
if index.HasExportedModels {
generator.scheduler.Schedule(func() {
generator.generateModels(index)
}) })
} }
@ -236,29 +230,17 @@ func (generator *Generator) validateFileNames() error {
case generator.options.ModelsFilename == "": case generator.options.ModelsFilename == "":
return fmt.Errorf("models filename must not be empty") return fmt.Errorf("models filename must not be empty")
case generator.options.InternalFilename == "":
return fmt.Errorf("internal models filename must not be empty")
case !generator.options.NoIndex && generator.options.IndexFilename == "": case !generator.options.NoIndex && generator.options.IndexFilename == "":
return fmt.Errorf("package index filename must not be empty") return fmt.Errorf("package index filename must not be empty")
case generator.options.ModelsFilename != strings.ToLower(generator.options.ModelsFilename): case generator.options.ModelsFilename != strings.ToLower(generator.options.ModelsFilename):
return fmt.Errorf("models filename must not contain uppercase characters") return fmt.Errorf("models filename must not contain uppercase characters")
case generator.options.InternalFilename != strings.ToLower(generator.options.InternalFilename):
return fmt.Errorf("internal models filename must not contain uppercase characters")
case generator.options.IndexFilename != strings.ToLower(generator.options.IndexFilename): case generator.options.IndexFilename != strings.ToLower(generator.options.IndexFilename):
return fmt.Errorf("package index filename must not contain uppercase characters") return fmt.Errorf("package index filename must not contain uppercase characters")
case generator.options.ModelsFilename == generator.options.InternalFilename:
return fmt.Errorf("models and internal models cannot share the same filename")
case !generator.options.NoIndex && generator.options.ModelsFilename == generator.options.IndexFilename: case !generator.options.NoIndex && generator.options.ModelsFilename == generator.options.IndexFilename:
return fmt.Errorf("models and package indexes cannot share the same filename") return fmt.Errorf("models and package indexes cannot share the same filename")
case !generator.options.NoIndex && generator.options.InternalFilename == generator.options.IndexFilename:
return fmt.Errorf("internal models and package indexes cannot share the same filename")
} }
return nil return nil

View File

@ -3,10 +3,12 @@ package generator
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/wailsapp/wails/v3/internal/generator/render"
"io" "io"
"io/fs" "io/fs"
"os" "os"
"path/filepath" "path/filepath"
"slices"
"strings" "strings"
"sync" "sync"
"testing" "testing"
@ -38,7 +40,6 @@ func TestGenerator(t *testing.T) {
for i := range tests { for i := range tests {
options := &flags.GenerateBindingsOptions{ options := &flags.GenerateBindingsOptions{
ModelsFilename: "models", ModelsFilename: "models",
InternalFilename: "internal",
IndexFilename: "index", IndexFilename: "index",
UseBundledRuntime: true, UseBundledRuntime: true,
@ -89,9 +90,11 @@ func TestGenerator(t *testing.T) {
// Run tests. // Run tests.
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
creator := outputCreator(t, test)
generator := NewGenerator( generator := NewGenerator(
test.options, test.options,
outputCreator(t, test), creator,
config.DefaultPtermLogger(nil), config.DefaultPtermLogger(nil),
) )
@ -102,6 +105,22 @@ func TestGenerator(t *testing.T) {
} else if report.HasWarnings() { } else if report.HasWarnings() {
pterm.Warning.Println(report) pterm.Warning.Println(report)
} }
// Log warnings and compare with reference output.
if log, err := creator.Create("warnings.log"); err != nil {
t.Error(err)
} else {
func() {
defer log.Close()
warnings := report.Warnings()
slices.Sort(warnings)
for _, msg := range warnings {
fmt.Fprint(log, msg, render.Newline)
}
}()
}
} else if err != nil { } else if err != nil {
t.Error(err) t.Error(err)
} }

View File

@ -26,16 +26,6 @@ func (generator *Generator) generateIncludes(index *collect.PackageIndex) {
return return
} }
case generator.renderer.InternalFile():
if len(index.Models) > 0 {
generator.logger.Errorf(
"package %s: included file '%s' collides with internal models filename; please rename the file or choose a different filename for internal models",
index.Package.Path,
path,
)
return
}
case generator.renderer.IndexFile(): case generator.renderer.IndexFile():
if !generator.options.NoIndex && !index.IsEmpty() { if !generator.options.NoIndex && !index.IsEmpty() {
generator.logger.Errorf( generator.logger.Errorf(
@ -93,7 +83,12 @@ func (generator *Generator) generateIncludes(index *collect.PackageIndex) {
generator.logger.Errorf("package %s: could not write included file '%s'", index.Package.Path, name) generator.logger.Errorf("package %s: could not write included file '%s'", index.Package.Path, name)
return return
} }
defer dst.Close() defer func() {
if err := dst.Close(); err != nil {
generator.logger.Errorf("%v", err)
generator.logger.Errorf("package %s: could not write included file '%s'", index.Package.Path, name)
}
}()
_, err = io.Copy(dst, src) _, err = io.Copy(dst, src)
if err != nil { if err != nil {

View File

@ -16,7 +16,12 @@ func (generator *Generator) generateIndex(index *collect.PackageIndex) {
generator.logger.Errorf("package %s: index generation failed", index.Package.Path) generator.logger.Errorf("package %s: index generation failed", index.Package.Path)
return return
} }
defer file.Close() defer func() {
if err := file.Close(); err != nil {
generator.logger.Errorf("%v", err)
generator.logger.Errorf("package %s: index generation failed", index.Package.Path)
}
}()
err = generator.renderer.Index(file, index) err = generator.renderer.Index(file, index)
if err != nil { if err != nil {

View File

@ -6,19 +6,34 @@ import (
"github.com/wailsapp/wails/v3/internal/generator/collect" "github.com/wailsapp/wails/v3/internal/generator/collect"
) )
// generateModels generates a file for exported models from the given index information. // generateModels generates a JS/TS models file for the given list of models.
func (generator *Generator) generateModels(index *collect.PackageIndex) { // A call to info.Collect must complete before entering generateModels.
file, err := generator.creator.Create(filepath.Join(index.Package.Path, generator.renderer.ModelsFile())) func (generator *Generator) generateModels(info *collect.PackageInfo, models []*collect.ModelInfo) {
// Merge all import maps.
imports := collect.NewImportMap(info)
for _, model := range models {
imports.Merge(model.Imports)
}
// Clear irrelevant imports.
imports.ImportModels = false
file, err := generator.creator.Create(filepath.Join(info.Path, generator.renderer.ModelsFile()))
if err != nil { if err != nil {
generator.logger.Errorf("%v", err) generator.logger.Errorf("%v", err)
generator.logger.Errorf("package %s: exported models generation failed", index.Package.Path) generator.logger.Errorf("package %s: models generation failed", info.Path)
return return
} }
defer file.Close() defer func() {
if err := file.Close(); err != nil {
generator.logger.Errorf("%v", err)
generator.logger.Errorf("package %s: models generation failed", info.Path)
}
}()
err = generator.renderer.Models(file, index) err = generator.renderer.Models(file, imports, models)
if err != nil { if err != nil {
generator.logger.Errorf("%v", err) generator.logger.Errorf("%v", err)
generator.logger.Errorf("package %s: exported models generation failed", index.Package.Path) generator.logger.Errorf("package %s: models generation failed", info.Path)
} }
} }

View File

@ -7,6 +7,7 @@ import (
"text/template" "text/template"
"github.com/wailsapp/wails/v3/internal/generator/collect" "github.com/wailsapp/wails/v3/internal/generator/collect"
"golang.org/x/tools/go/types/typeutil"
) )
// SkipCreate returns true if the given array of types needs no creation code. // SkipCreate returns true if the given array of types needs no creation code.
@ -21,31 +22,34 @@ func (m *module) SkipCreate(ts []types.Type) bool {
// NeedsCreate returns true if the given type needs some creation code. // NeedsCreate returns true if the given type needs some creation code.
func (m *module) NeedsCreate(typ types.Type) bool { func (m *module) NeedsCreate(typ types.Type) bool {
return m.needsCreateImpl(typ, make(map[*types.TypeName]bool)) return m.needsCreateImpl(typ, new(typeutil.Map))
} }
// needsCreateImpl provides the actual implementation of NeedsCreate. // needsCreateImpl provides the actual implementation of NeedsCreate.
// The visited parameter is used to break cycles. // The visited parameter is used to break cycles.
func (m *module) needsCreateImpl(typ types.Type, visited map[*types.TypeName]bool) bool { func (m *module) needsCreateImpl(typ types.Type, visited *typeutil.Map) bool {
switch t := typ.(type) { switch t := typ.(type) {
case *types.Alias, *types.Named: case *types.Alias:
obj := typ.(interface{ Obj() *types.TypeName }).Obj() return m.needsCreateImpl(types.Unalias(typ), visited)
if visited[obj] {
case *types.Named:
if visited.Set(typ, true) != nil {
// The only way to hit a cycle here
// is through a chain of structs, nested pointers and arrays (not slices).
// We can safely return false at this point
// as the final answer is independent of the cycle.
return false return false
} }
visited[obj] = true
if obj.Pkg() == nil { if t.Obj().Pkg() == nil {
// Builtin alias or named type: render underlying type. // Builtin named type: render underlying type.
return m.needsCreateImpl(t.Underlying(), visited) return m.needsCreateImpl(t.Underlying(), visited)
} }
if collect.IsAny(t) || collect.IsString(t) { if collect.IsAny(typ) || collect.IsStringAlias(typ) {
break break
} else if collect.IsClass(t) { } else if collect.IsClass(typ) {
return true return true
} else if _, isAlias := typ.(*types.Alias); isAlias {
return m.needsCreateImpl(types.Unalias(t), visited)
} else { } else {
return m.needsCreateImpl(t.Underlying(), visited) return m.needsCreateImpl(t.Underlying(), visited)
} }
@ -57,6 +61,10 @@ func (m *module) needsCreateImpl(typ types.Type, visited map[*types.TypeName]boo
return true return true
case *types.Struct: case *types.Struct:
if t.NumFields() == 0 || collect.MaybeJSONMarshaler(typ) != collect.NonMarshaler || collect.MaybeTextMarshaler(typ) != collect.NonMarshaler {
return false
}
info := m.collector.Struct(t) info := m.collector.Struct(t)
info.Collect() info.Collect()
@ -114,7 +122,6 @@ func (m *module) JSCreateWithParams(typ types.Type, params string) string {
case *types.Map: case *types.Map:
pp, ok := m.postponedCreates.At(typ).(*postponed) pp, ok := m.postponedCreates.At(typ).(*postponed)
if !ok { if !ok {
m.JSCreateWithParams(t.Key(), params)
m.JSCreateWithParams(t.Elem(), params) m.JSCreateWithParams(t.Elem(), params)
pp = &postponed{m.postponedCreates.Len(), params} pp = &postponed{m.postponedCreates.Len(), params}
m.postponedCreates.Set(typ, pp) m.postponedCreates.Set(typ, pp)
@ -128,7 +135,7 @@ func (m *module) JSCreateWithParams(typ types.Type, params string) string {
return m.JSCreateWithParams(t.Underlying(), params) return m.JSCreateWithParams(t.Underlying(), params)
} }
if collect.IsAny(typ) || collect.IsString(typ) || !m.NeedsCreate(typ) { if !m.NeedsCreate(typ) {
break break
} }
@ -166,6 +173,10 @@ func (m *module) JSCreateWithParams(typ types.Type, params string) string {
return fmt.Sprintf("$$createType%d%s", pp.index, params) return fmt.Sprintf("$$createType%d%s", pp.index, params)
case *types.Struct: case *types.Struct:
if t.NumFields() == 0 || collect.MaybeJSONMarshaler(typ) != collect.NonMarshaler || collect.MaybeTextMarshaler(typ) != collect.NonMarshaler {
break
}
pp, ok := m.postponedCreates.At(typ).(*postponed) pp, ok := m.postponedCreates.At(typ).(*postponed)
if ok { if ok {
return fmt.Sprintf("$$createType%d%s", pp.index, params) return fmt.Sprintf("$$createType%d%s", pp.index, params)
@ -212,12 +223,7 @@ func (m *module) PostponedCreates() []string {
result[pp.index] = fmt.Sprintf("%s$Create.Array(%s)", pre, m.JSCreateWithParams(t.(interface{ Elem() types.Type }).Elem(), pp.params)) result[pp.index] = fmt.Sprintf("%s$Create.Array(%s)", pre, m.JSCreateWithParams(t.(interface{ Elem() types.Type }).Elem(), pp.params))
case *types.Map: case *types.Map:
result[pp.index] = fmt.Sprintf( result[pp.index] = fmt.Sprintf("%s$Create.Map($Create.Any, %s)", pre, m.JSCreateWithParams(t.Elem(), pp.params))
"%s$Create.Map(%s, %s)",
pre,
m.JSCreateWithParams(t.Key(), pp.params),
m.JSCreateWithParams(t.Elem(), pp.params),
)
case *types.Named: case *types.Named:
if !collect.IsClass(key) { if !collect.IsClass(key) {
@ -313,10 +319,10 @@ func (m *module) PostponedCreates() []string {
} }
}) })
if newline != "\n" { if Newline != "\n" {
// Replace newlines according to local git config. // Replace newlines according to local git config.
for i := range result { for i := range result {
result[i] = strings.ReplaceAll(result[i], "\n", newline) result[i] = strings.ReplaceAll(result[i], "\n", Newline)
} }
} }

View File

@ -44,11 +44,12 @@ func (m *module) JSDefault(typ types.Type, quoted bool) (result string) {
case *types.Map: case *types.Map:
return "{}" return "{}"
case *types.Pointer:
return "null"
case *types.Struct: case *types.Struct:
return m.renderStructDefault(t) return m.renderStructDefault(t)
case *types.TypeParam:
// Should be unreachable
panic("type parameters have no default value")
} }
// Fall back to null. // Fall back to null.
@ -102,8 +103,8 @@ func (m *module) renderNamedDefault(typ aliasOrNamed, quoted bool) (result strin
} }
if quoted { if quoted {
// WARN: Do not test with IsString here!! We only want to catch marshalers. // WARN: Do not test with IsAny/IsStringAlias here!! We only want to catch marshalers.
if !collect.IsAny(typ) && !collect.MaybeTextMarshaler(typ) { if collect.MaybeJSONMarshaler(typ) == collect.NonMarshaler && collect.MaybeTextMarshaler(typ) == collect.NonMarshaler {
if basic, ok := typ.Underlying().(*types.Basic); ok { if basic, ok := typ.Underlying().(*types.Basic); ok {
// Quoted mode for basic alias/named type that is not a marshaler: delegate. // Quoted mode for basic alias/named type that is not a marshaler: delegate.
return m.renderBasicDefault(basic, quoted), true return m.renderBasicDefault(basic, quoted), true
@ -119,9 +120,9 @@ func (m *module) renderNamedDefault(typ aliasOrNamed, quoted bool) (result strin
if collect.IsAny(typ) { if collect.IsAny(typ) {
return "", false return "", false
} else if collect.MaybeTextMarshaler(typ) { } else if collect.MaybeTextMarshaler(typ) != collect.NonMarshaler {
return `""`, true return `""`, true
} else if collect.IsClass(typ) { } else if collect.IsClass(typ) && !istpalias(typ) {
if typ.Obj().Pkg().Path() == m.Imports.Self { if typ.Obj().Pkg().Path() == m.Imports.Self {
return fmt.Sprintf("(new %s%s())", prefix, jsid(typ.Obj().Name())), true return fmt.Sprintf("(new %s%s())", prefix, jsid(typ.Obj().Name())), true
} else { } else {
@ -129,29 +130,26 @@ func (m *module) renderNamedDefault(typ aliasOrNamed, quoted bool) (result strin
} }
} else if _, isAlias := typ.(*types.Alias); isAlias { } else if _, isAlias := typ.(*types.Alias); isAlias {
return m.JSDefault(types.Unalias(typ), quoted), true return m.JSDefault(types.Unalias(typ), quoted), true
} else { } else if len(m.collector.Model(typ.Obj()).Collect().Values) > 0 {
// Inject a type assertion in case we are breaking an enum.
// Using the true Go zero value is preferrable to selecting an arbitrary enum value.
value := m.JSDefault(typ.Underlying(), quoted)
if typ.Obj().Pkg().Path() == m.Imports.Self { if typ.Obj().Pkg().Path() == m.Imports.Self {
if m.TS { return fmt.Sprintf("%s%s.$zero", prefix, jsid(typ.Obj().Name())), true
return fmt.Sprintf("(%s as %s%s)", value, prefix, jsid(typ.Obj().Name())), true
} else { } else {
return fmt.Sprintf("(/** @type {%s%s} */(%s))", prefix, jsid(typ.Obj().Name()), value), true return fmt.Sprintf("%s.%s.$zero", jsimport(m.Imports.External[typ.Obj().Pkg().Path()]), jsid(typ.Obj().Name())), true
} }
} else { } else {
if m.TS { return m.JSDefault(typ.Underlying(), quoted), true
return fmt.Sprintf("(%s as %s.%s)", value, jsimport(m.Imports.External[typ.Obj().Pkg().Path()]), jsid(typ.Obj().Name())), true
} else {
return fmt.Sprintf("(/** @type {%s.%s} */(%s))", jsimport(m.Imports.External[typ.Obj().Pkg().Path()]), jsid(typ.Obj().Name()), value), true
}
}
} }
} }
// renderStructDefault outputs the Javascript representation // renderStructDefault outputs the Javascript representation
// of the zero value for the given struct type. // of the zero value for the given struct type.
func (m *module) renderStructDefault(typ *types.Struct) string { func (m *module) renderStructDefault(typ *types.Struct) string {
if collect.MaybeJSONMarshaler(typ) != collect.NonMarshaler {
return "null"
} else if collect.MaybeTextMarshaler(typ) != collect.NonMarshaler {
return `""`
}
info := m.collector.Struct(typ) info := m.collector.Struct(typ)
info.Collect() info.Collect()

View File

@ -35,7 +35,7 @@ var commentTerminator = []byte("*/")
// with the given indentation. // with the given indentation.
func jsdoc(comment string, indent string) string { func jsdoc(comment string, indent string) string {
var builder strings.Builder var builder strings.Builder
prefix := []byte(newline + indent + " * ") prefix := []byte(Newline + indent + " * ")
scanner := bufio.NewScanner(bytes.NewReader([]byte(comment))) scanner := bufio.NewScanner(bytes.NewReader([]byte(comment)))
for scanner.Scan() { for scanner.Scan() {

View File

@ -2,6 +2,7 @@ package render
import ( import (
"fmt" "fmt"
"go/types"
"math/big" "math/big"
"strconv" "strconv"
"strings" "strings"
@ -15,16 +16,18 @@ import (
var tmplFunctions = template.FuncMap{ var tmplFunctions = template.FuncMap{
"fixext": fixext, "fixext": fixext,
"hasdoc": hasdoc, "hasdoc": hasdoc,
"isclass": collect.IsClass,
"isjsdocid": isjsdocid, "isjsdocid": isjsdocid,
"isjsdocobj": isjsdocobj, "isjsdocobj": isjsdocobj,
"istpalias": istpalias,
"jsdoc": jsdoc, "jsdoc": jsdoc,
"jsdocline": jsdocline, "jsdocline": jsdocline,
"jsid": jsid, "jsid": jsid,
"jsimport": jsimport, "jsimport": jsimport,
"jsparam": jsparam, "jsparam": jsparam,
"jsvalue": jsvalue, "jsvalue": jsvalue,
"modelinfo": modelinfo,
"typeparam": typeparam, "typeparam": typeparam,
"unalias": types.Unalias,
} }
// fixext replaces a *.ts extension with *.js in the given string. // fixext replaces a *.ts extension with *.js in the given string.
@ -92,3 +95,13 @@ func jsvalue(value any) string {
// Fall back to undefined. // Fall back to undefined.
return "(void(0))" return "(void(0))"
} }
// istpalias determines whether typ is an alias
// that when uninstantiated resolves to a typeparam.
func istpalias(typ types.Type) bool {
if alias, ok := typ.(*types.Alias); ok {
return collect.IsTypeParam(alias.Origin())
}
return false
}

View File

@ -0,0 +1,70 @@
package render
import (
"strings"
"github.com/wailsapp/wails/v3/internal/generator/collect"
)
// modelInfo gathers useful information about a model.
type modelInfo struct {
HasValues bool
IsEnum bool
IsAlias bool
IsClassAlias bool
IsTypeAlias bool
IsClassOrInterface bool
IsInterface bool
IsClass bool
Template struct {
Params string
ParamList string
CreateList string
}
}
// modelinfo gathers and returns useful information about the given model.
func modelinfo(model *collect.ModelInfo, useInterfaces bool) (info modelInfo) {
info.HasValues = len(model.Values) > 0
info.IsEnum = info.HasValues && !model.Alias
info.IsAlias = !info.IsEnum && model.Type != nil
info.IsClassAlias = info.IsAlias && model.Predicates.IsClass && !useInterfaces
info.IsTypeAlias = info.IsAlias && !info.IsClassAlias
info.IsClassOrInterface = !info.IsEnum && !info.IsAlias
info.IsInterface = info.IsClassOrInterface && (model.Alias || useInterfaces)
info.IsClass = info.IsClassOrInterface && !info.IsInterface
if len(model.TypeParams) > 0 {
var params, paramList, createList strings.Builder
paramList.WriteRune('<')
createList.WriteRune('(')
for i, param := range model.TypeParams {
if i > 0 {
params.WriteRune(',')
paramList.WriteString(", ")
createList.WriteString(", ")
}
params.WriteString(param)
paramList.WriteString(param)
createList.WriteString("$$createParam")
createList.WriteString(param)
}
paramList.WriteRune('>')
createList.WriteRune(')')
info.Template.Params = params.String()
info.Template.ParamList = paramList.String()
info.Template.CreateList = createList.String()
}
return
}

View File

@ -38,7 +38,7 @@ func NewRenderer(options *flags.GenerateBindingsOptions, collector *collect.Coll
ext: ext, ext: ext,
service: tmplService[tmplLanguage(options.TS)], service: tmplService[tmplLanguage(options.TS)],
typedefs: tmplTypedefs[tmplLanguage(options.TS)], typedefs: tmplModels[tmplLanguage(options.TS)],
} }
} }
@ -54,12 +54,6 @@ func (renderer *Renderer) ModelsFile() string {
return renderer.options.ModelsFilename + renderer.ext return renderer.options.ModelsFilename + renderer.ext
} }
// InternalFile returns the standard name of an internal model file
// with the appropriate extension.
func (renderer *Renderer) InternalFile() string {
return renderer.options.InternalFilename + renderer.ext
}
// IndexFile returns the standard name of a package index file // IndexFile returns the standard name of a package index file
// with the appropriate extension. // with the appropriate extension.
func (renderer *Renderer) IndexFile() string { func (renderer *Renderer) IndexFile() string {
@ -82,7 +76,7 @@ func (renderer *Renderer) Service(w io.Writer, info *collect.ServiceInfo) error
} }
// Typedefs renders type definitions for the given list of models. // Typedefs renders type definitions for the given list of models.
func (renderer *Renderer) Typedefs(w io.Writer, imports *collect.ImportMap, models []*collect.ModelInfo) error { func (renderer *Renderer) Models(w io.Writer, imports *collect.ImportMap, models []*collect.ModelInfo) error {
if !renderer.options.UseInterfaces { if !renderer.options.UseInterfaces {
// Sort class aliases after the class they alias. // Sort class aliases after the class they alias.
// Works in amortized linear time thanks to an auxiliary map. // Works in amortized linear time thanks to an auxiliary map.
@ -92,7 +86,7 @@ func (renderer *Renderer) Typedefs(w io.Writer, imports *collect.ImportMap, mode
models = slices.Clone(models) models = slices.Clone(models)
for i, j := 0, 0; i < len(models); i++ { for i, j := 0, 0; i < len(models); i++ {
if models[i].Type != nil && collect.IsClass(models[i].Type) { if models[i].Type != nil && models[i].Predicates.IsClass {
// models[i] is a class alias: // models[i] is a class alias:
// models[i].Type is guaranteed to be // models[i].Type is guaranteed to be
// either an alias or a named type // either an alias or a named type
@ -133,19 +127,6 @@ func (renderer *Renderer) Typedefs(w io.Writer, imports *collect.ImportMap, mode
}) })
} }
// Models renders exported models for the given package index to w.
func (renderer *Renderer) Models(w io.Writer, index *collect.PackageIndex) error {
return tmplModels.Execute(w, &struct {
*collect.PackageIndex
*Renderer
*flags.GenerateBindingsOptions
}{
index,
renderer,
renderer.options,
})
}
// Index renders the given package index to w. // Index renders the given package index to w.
func (renderer *Renderer) Index(w io.Writer, index *collect.PackageIndex) error { func (renderer *Renderer) Index(w io.Writer, index *collect.PackageIndex) error {
return tmplIndex.Execute(w, &struct { return tmplIndex.Execute(w, &struct {

View File

@ -18,16 +18,14 @@ var tmplService = map[tmplLanguage]*template.Template{
tmplTS: template.Must(template.New("service.ts.tmpl").Funcs(tmplFunctions).ParseFS(templates, "templates/service.ts.tmpl")), tmplTS: template.Must(template.New("service.ts.tmpl").Funcs(tmplFunctions).ParseFS(templates, "templates/service.ts.tmpl")),
} }
var tmplTypedefs = map[tmplLanguage]*template.Template{ var tmplModels = map[tmplLanguage]*template.Template{
tmplJS: template.Must(template.New("internal.js.tmpl").Funcs(tmplFunctions).ParseFS(templates, "templates/internal.js.tmpl")), tmplJS: template.Must(template.New("models.js.tmpl").Funcs(tmplFunctions).ParseFS(templates, "templates/models.js.tmpl")),
tmplTS: template.Must(template.New("internal.ts.tmpl").Funcs(tmplFunctions).ParseFS(templates, "templates/internal.ts.tmpl")), tmplTS: template.Must(template.New("models.ts.tmpl").Funcs(tmplFunctions).ParseFS(templates, "templates/models.ts.tmpl")),
} }
var tmplModels = template.Must(template.New("models.tmpl").Funcs(tmplFunctions).ParseFS(templates, "templates/models.tmpl"))
var tmplIndex = template.Must(template.New("index.tmpl").Funcs(tmplFunctions).ParseFS(templates, "templates/index.tmpl")) var tmplIndex = template.Must(template.New("index.tmpl").Funcs(tmplFunctions).ParseFS(templates, "templates/index.tmpl"))
var newline string var Newline string
func init() { func init() {
var builder strings.Builder var builder strings.Builder
@ -37,5 +35,5 @@ func init() {
panic(err) panic(err)
} }
newline = builder.String() Newline = builder.String()
} }

View File

@ -1,4 +1,5 @@
{{$renderer := .}} {{$renderer := .}}
{{- $useInterfaces := .UseInterfaces}}
{{- $models := (fixext .ModelsFile)}} {{- $models := (fixext .ModelsFile)}}
{{- if not .TS -}} {{- if not .TS -}}
// @ts-check // @ts-check
@ -18,19 +19,83 @@
*/ */
{{end}} {{end}}
{{- if .Services}} {{- if .Services}}
{{- range .Services}} {{- range .Services}}{{if .Internal}}{{break}}{{end}}
import * as {{jsid .Name}} from "./{{js (fixext ($renderer.ServiceFile .Name))}}"; import * as {{jsid .Name}} from "./{{js (fixext ($renderer.ServiceFile .Name))}}";
{{- end}} {{- end}}
export { export {
{{- range $i, $service := .Services}} {{- range $i, $service := .Services}}
{{- if .Internal}}{{break}}{{end}}
{{- if gt $i 0}},{{end}} {{- if gt $i 0}},{{end}}
{{jsid .Name}} {{jsid .Name}}
{{- end}} {{- end}}
}; };
{{end}} {{end}}
{{- if .HasExportedModels}}
export * from "./{{js $models}}"; {{- $hasObjects := false}}
{{- $hasTypes := false}}
{{- range $model := .Models}}
{{- if $model.Internal}}{{break}}{{end}}
{{- $info := modelinfo $model $useInterfaces }}
{{- if or $info.HasValues $info.IsClassAlias $info.IsClass}}
{{- if not $hasObjects}}
{{- $hasObjects = true}}
export {
{{- else}},{{end}}
{{jsid $model.Name}}
{{- else}}
{{- $hasTypes = true}}
{{- end}}
{{- end}}
{{- if $hasObjects}}
} from "./{{js $models}}";
{{end}} {{end}}
{{- if $hasTypes}}
{{- $hasTypes = false}}
{{- if .TS}}
export type {
{{- else}}
import * as $models from "./{{js $models}}";
{{end}}
{{- range $model := .Models}}
{{- if $model.Internal}}{{break}}{{end}}
{{- $info := modelinfo $model $useInterfaces }}
{{- $template := $info.Template }}
{{- if or $info.HasValues $info.IsClassAlias $info.IsClass}}{{continue}}{{end}}
{{- if $renderer.TS}}
{{- if $hasTypes}},{{end}}
{{jsid $model.Name}}
{{- else}}
/**
{{- if hasdoc $model.Decl.Doc}}
{{- jsdoc $model.Decl.Doc.Text ""}}{{if hasdoc $model.Doc}}
*{{end}}
{{- end}}
{{- if hasdoc $model.Doc}}
{{- jsdoc $model.Doc.Text ""}}
{{- end}}
{{- if $template.ParamList}}
* @template {{$template.Params}}
{{- end}}
* @typedef {$models.{{jsid $model.Name}}{{$template.ParamList -}} } {{jsid $model.Name}}
*/
{{end}}
{{- $hasTypes = true}}
{{- end}}
{{- if .TS}}
} from "./{{js $models}}";
{{end}}
{{- end}}
{{- range .Package.Injections}} {{- range .Package.Injections}}
{{.}} {{.}}
{{- end}}{{if .Package.Injections}} {{- end}}{{if .Package.Injections}}

View File

@ -19,34 +19,10 @@ import * as {{jsimport .}} from "{{js .RelPath}}/{{js $models}}";
{{end}} {{end}}
{{- range $model := .Models}} {{- range $model := .Models}}
{{- $isEnum := $model.Values}} {{- $info := modelinfo $model $useInterfaces }}
{{- $isClassAlias := and $model.Type (not $useInterfaces) (isclass $model.Type)}} {{- $template := $info.Template }}
{{- $isTypeAlias := and $model.Type (or $useInterfaces (not (isclass $model.Type)))}}
{{- $isClassOrInterface := and (not $model.Type) (not $model.Values)}}
{{- $isInterface := and $isClassOrInterface (or $useInterfaces $model.Alias)}}
{{- /* Build type parameter list */}} {{- if or $template.ParamList (hasdoc $model.Decl.Doc) (hasdoc $model.Doc) $info.IsEnum $info.IsTypeAlias $info.IsInterface}}
{{- $typeParams := ""}}
{{- $typeParamList := ""}}
{{- $createParamList := ""}}
{{- range $i, $param := $model.TypeParams}}
{{- $param = (typeparam $i $param)}}
{{- if eq $i 0}}
{{- $typeParams = $param}}
{{- $typeParamList = (printf "<%s" $param)}}
{{- $createParamList = (printf "($$createParam%s" $param)}}
{{- else}}
{{- $typeParams = (printf "%s,%s" $typeParams $param)}}
{{- $typeParamList = (printf "%s, %s" $typeParamList $param)}}
{{- $createParamList = (printf "%s, $$createParam%s" $createParamList $param)}}
{{- end}}
{{- end}}
{{- if $typeParamList}}
{{- $typeParamList = (printf "%s>" $typeParamList)}}
{{- $createParamList = (printf "%s)" $createParamList)}}
{{- end}}
{{- if or $typeParamList (hasdoc $model.Decl.Doc) (hasdoc $model.Doc) $isEnum $isTypeAlias $isInterface}}
/** /**
{{- if hasdoc $model.Decl.Doc}} {{- if hasdoc $model.Decl.Doc}}
{{- jsdoc $model.Decl.Doc.Text ""}}{{if hasdoc $model.Doc}} {{- jsdoc $model.Decl.Doc.Text ""}}{{if hasdoc $model.Doc}}
@ -55,15 +31,15 @@ import * as {{jsimport .}} from "{{js .RelPath}}/{{js $models}}";
{{- if hasdoc $model.Doc}} {{- if hasdoc $model.Doc}}
{{- jsdoc $model.Doc.Text ""}} {{- jsdoc $model.Doc.Text ""}}
{{- end}} {{- end}}
{{- if and $typeParamList (not $isClassAlias)}} {{- if and $template.ParamList (not $info.IsClassAlias)}}
* @template {{$typeParams}} * @template {{$template.Params}}
{{- end}} {{- end}}
{{- if $isEnum}} {{- if $info.IsEnum}}
* @readonly * @readonly
* @enum { {{- $module.JSType $model.Type -}} } * @enum { {{- $module.JSType $model.Type -}} }
{{- else if $isTypeAlias}} {{- else if $info.IsTypeAlias}}
* @typedef { {{- $module.JSType $model.Type -}} } {{jsid $model.Name}} * @typedef { {{- $module.JSType $model.Type -}} } {{jsid $model.Name}}
{{- else if $isInterface}} {{- else if $info.IsInterface}}
{{- if isjsdocobj $model}} {{- if isjsdocobj $model}}
* @typedef {Object} {{jsid $model.Name}} * @typedef {Object} {{jsid $model.Name}}
{{- range $i, $decl := $model.Fields}}{{range $j, $field := $decl}} {{- range $i, $decl := $model.Fields}}{{range $j, $field := $decl}}
@ -81,13 +57,22 @@ import * as {{jsimport .}} from "{{js .RelPath}}/{{js $models}}";
{{- end}} {{- end}}
*/ */
{{- end}} {{- end}}
{{- if $isEnum}} {{- if $info.HasValues}}
{{- if not $info.IsEnum}}
/**
* Predefined constants for type {{jsid $model.Name}}.
* @namespace
*/
{{- end}}
export const {{jsid $model.Name}} = { export const {{jsid $model.Name}} = {
{{- if $info.IsEnum}}
/** /**
* The Go zero value for the underlying type of the enum. * The Go zero value for the underlying type of the enum.
*/ */
$zero: {{$module.JSDefault $model.Type false}}, $zero: {{$module.JSDefault $model.Type false}},
{{range $i, $decl := $model.Values}}{{range $j, $spec := $decl}}{{range $k, $value := $spec}} {{end}}
{{- range $i, $decl := $model.Values}}{{range $j, $spec := $decl}}{{range $k, $value := $spec}}
{{- if and (ne $i 0) (eq $j 0) (eq $k 0)}} {{- if and (ne $i 0) (eq $j 0) (eq $k 0)}}
{{end}} {{end}}
{{- if or (and (eq $j 0) (eq $k 0) (hasdoc $value.Decl.Doc)) (and (eq $k 0) (hasdoc $value.Spec.Doc))}} {{- if or (and (eq $j 0) (eq $k 0) (hasdoc $value.Decl.Doc)) (and (eq $k 0) (hasdoc $value.Spec.Doc))}}
@ -106,8 +91,12 @@ export const {{jsid $model.Name}} = {
{{jsid $value.Name}}: {{jsvalue $value.Value}}, {{jsid $value.Name}}: {{jsvalue $value.Value}},
{{- end}}{{end}}{{end}} {{- end}}{{end}}{{end}}
}; };
{{else if $isClassAlias}} {{else if $info.IsClassAlias}}
export const {{jsid $model.Name}} = {{$module.JSType $model.Type.Origin}}; export const {{jsid $model.Name}} = {{if istpalias $model.Type -}}
{{$module.JSType (unalias $model.Type).Origin}};
{{- else -}}
{{$module.JSType $model.Type.Origin}};
{{- end}}
/** /**
{{- if hasdoc $model.Decl.Doc}} {{- if hasdoc $model.Decl.Doc}}
@ -117,16 +106,16 @@ export const {{jsid $model.Name}} = {{$module.JSType $model.Type.Origin}};
{{- if hasdoc $model.Doc}} {{- if hasdoc $model.Doc}}
{{- jsdoc $model.Doc.Text ""}} {{- jsdoc $model.Doc.Text ""}}
{{- end}} {{- end}}
{{- if $typeParamList}} {{- if $template.ParamList}}
* @template {{$typeParams}} * @template {{$template.Params}}
{{- end}} {{- end}}
* @typedef { {{- $module.JSType $model.Type -}} } {{jsid $model.Name}} * @typedef { {{- $module.JSType $model.Type -}} } {{jsid $model.Name}}
*/ */
{{else if and $isClassOrInterface (not $isInterface)}} {{else if and $info.IsClass}}
export class {{jsid $model.Name}} { export class {{jsid $model.Name}} {
/** /**
* Creates a new {{jsid $model.Name}} instance. * Creates a new {{jsid $model.Name}} instance.
* @param {Partial<{{jsid $model.Name}}{{$typeParamList}}>} [$$source = {}] - The source object to create the {{jsid $model.Name}}. * @param {Partial<{{jsid $model.Name}}{{$template.ParamList}}>} [$$source = {}] - The source object to create the {{jsid $model.Name}}.
*/ */
constructor($$source = {}) { constructor($$source = {}) {
{{- range $decl := $model.Fields}}{{range $j, $field := $decl}} {{- range $decl := $model.Fields}}{{range $j, $field := $decl}}
@ -144,9 +133,9 @@ export class {{jsid $model.Name}} {
{{- jsdoc $field.Decl.Doc.Text " "}} {{- jsdoc $field.Decl.Doc.Text " "}}
{{- end}} {{- end}}
* @member * @member
* @type { {{- $module.JSFieldType $field.StructField}}{{if .Optional}} | undefined{{end -}} } * @type { {{- $module.JSFieldType $field.StructField}}{{if $field.Optional}} | undefined{{end -}} }
*/ */
this["{{js $field.JsonName}}"] = {{$module.JSDefault $field.Type $field.Quoted}}; this["{{js $field.JsonName}}"] = {{if $field.Optional}}undefined{{else}}{{$module.JSDefault $field.Type $field.Quoted}}{{end}};
} }
{{- end}}{{end}} {{- end}}{{end}}
@ -154,31 +143,31 @@ export class {{jsid $model.Name}} {
} }
/** /**
{{- if $typeParamList}} {{- if $template.ParamList}}
* Given creation functions for each type parameter, * Given creation functions for each type parameter,
* returns a creation function for a concrete instance * returns a creation function for a concrete instance
* of the generic class {{jsid $model.Name}}. * of the generic class {{jsid $model.Name}}.
* @template {{$typeParams}} * @template {{$template.Params}}
{{- range $i, $param := $model.TypeParams}} {{- range $i, $param := $model.TypeParams}}
{{- $param = (typeparam $i $param)}} {{- $param = (typeparam $i $param)}}
* @param {(source: any) => {{$param -}} } $$createParam{{$param}} * @param {(source: any) => {{$param -}} } $$createParam{{$param}}
{{- end}} {{- end}}
* @returns {($$source?: any) => {{jsid $model.Name}}{{$typeParamList -}} } * @returns {($$source?: any) => {{jsid $model.Name}}{{$template.ParamList -}} }
{{- else}} {{- else}}
* Creates a new {{jsid $model.Name}} instance from a string or object. * Creates a new {{jsid $model.Name}} instance from a string or object.
* @param {any} [$$source = {}] * @param {any} [$$source = {}]
* @returns { {{- jsid $model.Name -}} } * @returns { {{- jsid $model.Name -}} }
{{- end}} {{- end}}
*/ */
static createFrom{{if $typeParamList}}{{$createParamList}}{{else}}($$source = {}){{end}} { static createFrom{{if $template.ParamList}}{{$template.CreateList}}{{else}}($$source = {}){{end}} {
{{- range $i, $spec := $model.Fields}}{{range $j, $field := $spec}} {{- range $i, $spec := $model.Fields}}{{range $j, $field := $spec}}
{{- $create := ($module.JSCreateWithParams $field.Type $createParamList)}} {{- $create := ($module.JSCreateWithParams $field.Type $template.CreateList)}}
{{- if ne $create "$Create.Any"}} {{- if ne $create "$Create.Any"}}
const $$createField{{$i}}_{{$j}} = {{$create}}; const $$createField{{$i}}_{{$j}} = {{$create}};
{{- end}} {{- end}}
{{- end}}{{end}} {{- end}}{{end}}
{{- $indent := ""}} {{- $indent := ""}}
{{- if $typeParamList}} {{- if $template.ParamList}}
{{- $indent = " "}} {{- $indent = " "}}
return ($$source = {}) => { return ($$source = {}) => {
{{- end}} {{- end}}
@ -190,8 +179,8 @@ export class {{jsid $model.Name}} {
{{$indent -}} } {{$indent -}} }
{{- end}} {{- end}}
{{- end}}{{end}} {{- end}}{{end}}
{{$indent}}return new {{jsid $model.Name}}(/** @type {Partial<{{jsid $model.Name}}{{$typeParamList}}>} */($$parsedSource)); {{$indent}}return new {{jsid $model.Name}}(/** @type {Partial<{{jsid $model.Name}}{{$template.ParamList}}>} */($$parsedSource));
{{- if $typeParamList}} {{- if $template.ParamList}}
}; };
{{- end}} {{- end}}
} }

View File

@ -1,94 +0,0 @@
{{$renderer := .}}
{{- $useInterfaces := .UseInterfaces}}
{{- $internal := (fixext .InternalFile)}}
{{- if not .TS -}}
// @ts-check
{{end -}}
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
{{- $hasObjects := false}}
{{- $hasTypes := false}}
{{- range $model := .Models}}
{{- if not $model.Object.Exported}}{{break}}{{end}}
{{- $isEnum := $model.Values}}
{{- $isClassAlias := and $model.Type (not $useInterfaces) (isclass $model.Type)}}
{{- $isClass := and (not $model.Type) (not $model.Values) (not $useInterfaces) (not $model.Alias)}}
{{- if or $isEnum $isClassAlias $isClass}}
{{- if not $hasObjects}}
{{- $hasObjects = true}}
export {
{{- else}},{{end}}
{{jsid $model.Name}}
{{- else}}
{{- $hasTypes = true}}
{{- end}}
{{- end}}
{{- if $hasObjects}}
} from "./{{js $internal}}";
{{- end}}
{{- if $hasTypes}}
{{- $hasTypes = false}}
{{if .TS -}}
export type {
{{- else -}}
import * as $models from "./{{js $internal}}";
{{- end}}
{{- range $model := .Models}}
{{- if not $model.Object.Exported}}{{break}}{{end}}
{{- $isEnum := $model.Values}}
{{- $isClassAlias := and $model.Type (not $useInterfaces) (isclass $model.Type)}}
{{- $isClass := and (not $model.Type) (not $model.Values) (not $useInterfaces) (not $model.Alias)}}
{{- if or $isEnum $isClassAlias $isClass}}{{continue}}{{end}}
{{- /* Build type parameter list */}}
{{- $typeParams := ""}}
{{- $typeParamList := ""}}
{{- range $i, $param := $model.TypeParams}}
{{- $param = (typeparam $i $param)}}
{{- if eq $i 0}}
{{- $typeParams = $param}}
{{- $typeParamList = (printf "<%s" $param)}}
{{- else}}
{{- $typeParams = (printf "%s,%s" $typeParams $param)}}
{{- $typeParamList = (printf "%s, %s" $typeParamList $param)}}
{{- end}}
{{- end}}
{{- if $typeParamList}}
{{- $typeParamList = (printf "%s>" $typeParamList)}}
{{- end}}
{{- if $renderer.TS}}
{{- if $hasTypes}},{{end}}
{{jsid $model.Name}}
{{- else}}
/**
{{- if hasdoc $model.Decl.Doc}}
{{- jsdoc $model.Decl.Doc.Text ""}}{{if hasdoc $model.Doc}}
*{{end}}
{{- end}}
{{- if hasdoc $model.Doc}}
{{- jsdoc $model.Doc.Text ""}}
{{- end}}
{{- if $typeParamList}}
* @template {{$typeParams}}
{{- end}}
* @typedef {$models.{{jsid $model.Name}}{{$typeParamList -}} } {{jsid $model.Name}}
*/
{{- end}}
{{- $hasTypes = true}}
{{- end}}
{{- if .TS}}
} from "./{{js $internal}}";
{{- end}}
{{- end}}

View File

@ -18,29 +18,8 @@ import * as {{jsimport .}} from "{{js .RelPath}}/{{js $models}}";
{{end}} {{end}}
{{- range $model := .Models}} {{- range $model := .Models}}
{{- $isEnum := $model.Values}} {{- $info := modelinfo $model $useInterfaces }}
{{- $isClassAlias := and $model.Type (not $useInterfaces) (isclass $model.Type)}} {{- $template := $info.Template }}
{{- $isTypeAlias := and $model.Type (or $useInterfaces (not (isclass $model.Type)))}}
{{- $isClassOrInterface := and (not $model.Type) (not $model.Values)}}
{{- $isInterface := or $useInterfaces $model.Alias}}
{{- /* Build type parameter list */}}
{{- $typeParamList := ""}}
{{- $createParamList := ""}}
{{- range $i, $param := $model.TypeParams}}
{{- $param = (typeparam $i $param)}}
{{- if eq $i 0}}
{{- $typeParamList = (printf "<%s" $param)}}
{{- $createParamList = (printf "($$createParam%s" $param)}}
{{- else}}
{{- $typeParamList = (printf "%s, %s" $typeParamList $param)}}
{{- $createParamList = (printf "%s, $$createParam%s" $createParamList $param)}}
{{- end}}
{{- end}}
{{- if $typeParamList}}
{{- $typeParamList = (printf "%s>" $typeParamList)}}
{{- $createParamList = (printf "%s)" $createParamList)}}
{{- end}}
{{- if or (hasdoc $model.Decl.Doc) (hasdoc $model.Doc)}} {{- if or (hasdoc $model.Decl.Doc) (hasdoc $model.Doc)}}
/** /**
@ -53,7 +32,7 @@ import * as {{jsimport .}} from "{{js .RelPath}}/{{js $models}}";
{{- end}} {{- end}}
*/ */
{{- end}} {{- end}}
{{- if $isEnum}} {{- if $info.IsEnum}}
export enum {{jsid $model.Name}} { export enum {{jsid $model.Name}} {
/** /**
* The Go zero value for the underlying type of the enum. * The Go zero value for the underlying type of the enum.
@ -78,8 +57,12 @@ export enum {{jsid $model.Name}} {
{{jsid $value.Name}} = {{jsvalue $value.Value}}, {{jsid $value.Name}} = {{jsvalue $value.Value}},
{{- end}}{{end}}{{end}} {{- end}}{{end}}{{end}}
}; };
{{else if $isClassAlias}} {{else if $info.IsClassAlias}}
export const {{jsid $model.Name}} = {{$module.JSType $model.Type.Origin}}; export const {{jsid $model.Name}} = {{if istpalias $model.Type -}}
{{$module.JSType (unalias $model.Type).Origin}};
{{- else -}}
{{$module.JSType $model.Type.Origin}};
{{- end}}
{{- if or (hasdoc $model.Decl.Doc) (hasdoc $model.Doc)}} {{- if or (hasdoc $model.Decl.Doc) (hasdoc $model.Doc)}}
/** /**
@ -92,11 +75,38 @@ export const {{jsid $model.Name}} = {{$module.JSType $model.Type.Origin}};
{{- end}} {{- end}}
*/ */
{{- end}} {{- end}}
export type {{jsid $model.Name}}{{$typeParamList}} = {{$module.JSType $model.Type}}; export type {{jsid $model.Name}}{{$template.ParamList}} = {{$module.JSType $model.Type}};
{{else if $isTypeAlias}} {{else if $info.IsTypeAlias}}
export type {{jsid $model.Name}}{{$typeParamList}} = {{$module.JSType $model.Type}}; export type {{jsid $model.Name}}{{$template.ParamList}} = {{$module.JSType $model.Type}};
{{else if $isClassOrInterface}} {{- if $info.HasValues}}
export {{if $isInterface}}interface{{else}}class{{end}} {{jsid $model.Name}}{{$typeParamList}} {
/**
* Predefined constants for type {{jsid $model.Name}}.
* @namespace
*/
export const {{jsid $model.Name}} = {
{{- range $i, $decl := $model.Values}}{{range $j, $spec := $decl}}{{range $k, $value := $spec}}
{{- if and (ne $i 0) (eq $j 0) (eq $k 0)}}
{{end}}
{{- if or (and (eq $j 0) (eq $k 0) (hasdoc $value.Decl.Doc)) (and (eq $k 0) (hasdoc $value.Spec.Doc))}}
{{- if gt $j 0}}
{{end}}
/**
{{- if and (eq $j 0) (eq $k 0) (hasdoc $value.Decl.Doc)}}
{{- jsdoc $value.Decl.Doc.Text " "}}{{if and (eq $k 0) (hasdoc $value.Spec.Doc)}}
*{{end}}
{{- end}}
{{- if and (eq $k 0) (hasdoc $value.Spec.Doc)}}
{{- jsdoc $value.Spec.Doc.Text " "}}
{{- end}}
*/
{{- end}}
{{jsid $value.Name}}: {{jsvalue $value.Value}},
{{- end}}{{end}}{{end}}
};
{{- end}}
{{else if $info.IsClassOrInterface}}
export {{if $info.IsInterface}}interface{{else}}class{{end}} {{jsid $model.Name}}{{$template.ParamList}} {
{{- range $i, $decl := $model.Fields}}{{range $j, $field := $decl}} {{- range $i, $decl := $model.Fields}}{{range $j, $field := $decl}}
{{- if and (eq $j 0) (hasdoc $field.Decl.Doc)}} {{- if and (eq $j 0) (hasdoc $field.Decl.Doc)}}
{{- if gt $i 0}} {{- if gt $i 0}}
@ -107,11 +117,11 @@ export {{if $isInterface}}interface{{else}}class{{end}} {{jsid $model.Name}}{{$t
{{- end}} {{- end}}
"{{js $field.JsonName}}"{{if $field.Optional}}?{{end}}: {{$module.JSFieldType $field.StructField}}; "{{js $field.JsonName}}"{{if $field.Optional}}?{{end}}: {{$module.JSFieldType $field.StructField}};
{{- end}}{{end}} {{- end}}{{end}}
{{- if not $isInterface}} {{- if $info.IsClass}}
/** Creates a new {{jsid $model.Name}} instance. */ /** Creates a new {{jsid $model.Name}} instance. */
constructor($$source: Partial<{{jsid $model.Name}}{{$typeParamList}}> = {}) { constructor($$source: Partial<{{jsid $model.Name}}{{$template.ParamList}}> = {}) {
{{- range $spec := $model.Fields}}{{range $i, $field := $spec}}{{if not .Optional}} {{- range $spec := $model.Fields}}{{range $i, $field := $spec}}{{if not $field.Optional}}
if (!("{{js $field.JsonName}}" in $$source)) { if (!("{{js $field.JsonName}}" in $$source)) {
this["{{js $field.JsonName}}"] = {{$module.JSDefault $field.Type $field.Quoted}}; this["{{js $field.JsonName}}"] = {{$module.JSDefault $field.Type $field.Quoted}};
} }
@ -121,7 +131,7 @@ export {{if $isInterface}}interface{{else}}class{{end}} {{jsid $model.Name}}{{$t
} }
/** /**
{{- if $typeParamList}} {{- if $template.ParamList}}
* Given creation functions for each type parameter, * Given creation functions for each type parameter,
* returns a creation function for a concrete instance * returns a creation function for a concrete instance
* of the generic class {{jsid $model.Name}}. * of the generic class {{jsid $model.Name}}.
@ -129,21 +139,21 @@ export {{if $isInterface}}interface{{else}}class{{end}} {{jsid $model.Name}}{{$t
* Creates a new {{jsid $model.Name}} instance from a string or object. * Creates a new {{jsid $model.Name}} instance from a string or object.
{{- end}} {{- end}}
*/ */
static createFrom{{$typeParamList}}({{if $typeParamList}} static createFrom{{$template.ParamList}}({{if $template.ParamList}}
{{- range $i, $param := $model.TypeParams}} {{- range $i, $param := $model.TypeParams}}
{{- $param = (typeparam $i $param)}} {{- $param = (typeparam $i $param)}}
{{- if gt $i 0}}, {{end -}} {{- if gt $i 0}}, {{end -}}
$$createParam{{$param}}: (source: any) => {{$param}}{{end -}} $$createParam{{$param}}: (source: any) => {{$param}}{{end -}}
{{else}}$$source: any = {}{{end}}): {{else}}$$source: any = {}{{end}}):
{{- if $typeParamList}} ($$source?: any) =>{{end}} {{jsid $model.Name}}{{$typeParamList}} { {{- if $template.ParamList}} ($$source?: any) =>{{end}} {{jsid $model.Name}}{{$template.ParamList}} {
{{- range $i, $spec := $model.Fields}}{{range $j, $field := $spec}} {{- range $i, $spec := $model.Fields}}{{range $j, $field := $spec}}
{{- $create := ($module.JSCreateWithParams $field.Type $createParamList)}} {{- $create := ($module.JSCreateWithParams $field.Type $template.CreateList)}}
{{- if ne $create "$Create.Any"}} {{- if ne $create "$Create.Any"}}
const $$createField{{$i}}_{{$j}} = {{$create}}; const $$createField{{$i}}_{{$j}} = {{$create}};
{{- end}} {{- end}}
{{- end}}{{end}} {{- end}}{{end}}
{{- $indent := ""}} {{- $indent := ""}}
{{- if $typeParamList}} {{- if $template.ParamList}}
{{- $indent = " "}} {{- $indent = " "}}
return ($$source: any = {}) => { return ($$source: any = {}) => {
{{- end}} {{- end}}
@ -155,8 +165,8 @@ export {{if $isInterface}}interface{{else}}class{{end}} {{jsid $model.Name}}{{$t
{{$indent -}} } {{$indent -}} }
{{- end}} {{- end}}
{{- end}}{{end}} {{- end}}{{end}}
{{$indent}}return new {{jsid $model.Name}}{{$typeParamList}}($$parsedSource as Partial<{{jsid $model.Name}}{{$typeParamList}}>); {{$indent}}return new {{jsid $model.Name}}{{$template.ParamList}}($$parsedSource as Partial<{{jsid $model.Name}}{{$template.ParamList}}>);
{{- if $typeParamList}} {{- if $template.ParamList}}
}; };
{{- end}} {{- end}}
} }

View File

@ -1,7 +1,6 @@
{{$module := .}} {{$module := .}}
{{- $runtime := $module.Runtime}} {{- $runtime := $module.Runtime}}
{{- $models := (fixext $module.ModelsFile)}} {{- $models := (fixext $module.ModelsFile)}}
{{- $internal := (fixext $module.InternalFile)}}
{{- $useNames := $module.UseNames}} {{- $useNames := $module.UseNames}}
{{- $useInterfaces := $module.UseInterfaces}} {{- $useInterfaces := $module.UseInterfaces}}
{{- $imports := $module.Imports}} {{- $imports := $module.Imports}}
@ -33,11 +32,7 @@ import * as {{jsimport .}} from "{{js .RelPath}}/{{js $models}}";
{{- if $imports.ImportModels}} {{- if $imports.ImportModels}}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports // @ts-ignore: Unused imports
import * as $models from "./{{js $internal}}"; import * as $models from "./{{js $models}}";
{{end}}
{{- range .Injections}}
{{.}}
{{- end}}{{if .Injections}}
{{end}} {{end}}
{{- range .Methods}} {{- range .Methods}}
/** /**
@ -100,4 +95,8 @@ import * as $models from "./{{js $internal}}";
{{if and (ge (len $create) 54) (eq (slice $create 39 54) "function $$init")}}var {{else}}const {{end -}} {{if and (ge (len $create) 54) (eq (slice $create 39 54) "function $$init")}}var {{else}}const {{end -}}
$$createType{{$i}} = {{$create}}; $$createType{{$i}} = {{$create}};
{{- end}} {{- end}}
{{end}}
{{- range .Injections}}
{{.}}
{{- end}}{{if .Injections}}
{{end}}{{end -}} {{end}}{{end -}}

View File

@ -1,7 +1,6 @@
{{$module := .}} {{$module := .}}
{{- $runtime := $module.Runtime}} {{- $runtime := $module.Runtime}}
{{- $models := (fixext $module.ModelsFile)}} {{- $models := (fixext $module.ModelsFile)}}
{{- $internal := (fixext $module.InternalFile)}}
{{- $useNames := $module.UseNames}} {{- $useNames := $module.UseNames}}
{{- $useInterfaces := $module.UseInterfaces}} {{- $useInterfaces := $module.UseInterfaces}}
{{- $imports := $module.Imports}} {{- $imports := $module.Imports}}
@ -32,11 +31,7 @@ import * as {{jsimport .}} from "{{js .RelPath}}/{{js $models}}";
{{- if $imports.ImportModels}} {{- if $imports.ImportModels}}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports // @ts-ignore: Unused imports
import * as $models from "./{{js $internal}}"; import * as $models from "./{{js $models}}";
{{end}}
{{- range .Injections}}
{{.}}
{{- end}}{{if .Injections}}
{{end}} {{end}}
{{- range .Methods}} {{- range .Methods}}
{{- if or (hasdoc .Decl.Doc) (hasdoc .Doc)}} {{- if or (hasdoc .Decl.Doc) (hasdoc .Doc)}}
@ -97,4 +92,8 @@ import * as $models from "./{{js $internal}}";
{{if and (ge (len $create) 16) (eq (slice $create 1 16) "function $$init")}}var {{else}}const {{end -}} {{if and (ge (len $create) 16) (eq (slice $create 1 16) "function $$init")}}var {{else}}const {{end -}}
$$createType{{$i}} = {{$create}}; $$createType{{$i}} = {{$create}};
{{- end}} {{- end}}
{{end}}
{{- range .Injections}}
{{.}}
{{- end}}{{if .Injections}}
{{end}}{{end -}} {{end}}{{end -}}

View File

@ -72,9 +72,9 @@ func (m *module) renderType(typ types.Type, quoted bool) (result string, nullabl
return m.renderMapType(t) return m.renderMapType(t)
case *types.Pointer: case *types.Pointer:
elem, ptr := m.renderType(t.Elem(), false) elem, nullable := m.renderType(t.Elem(), false)
if ptr { if nullable {
return elem, true return elem, nullable
} else { } else {
return fmt.Sprintf("%s | null", elem), true return fmt.Sprintf("%s | null", elem), true
} }
@ -83,11 +83,11 @@ func (m *module) renderType(typ types.Type, quoted bool) (result string, nullabl
return m.renderStructType(t), false return m.renderStructType(t), false
case *types.TypeParam: case *types.TypeParam:
str := "" pre, post := "", ""
if quoted { if quoted {
str = "| string " pre, post = "(", " | string)"
} }
return fmt.Sprintf("%s %s| null", typeparam(t.Index(), t.Obj().Name()), str), true return fmt.Sprintf("%s%s%s", pre, typeparam(t.Index(), t.Obj().Name()), post), false
} }
// Fall back to untyped mode. // Fall back to untyped mode.
@ -147,29 +147,22 @@ func (m *module) renderMapType(typ *types.Map) (result string, nullable bool) {
key = m.renderBasicType(k, true) key = m.renderBasicType(k, true)
} }
case *types.Alias, *types.Named: case *types.Alias, *types.Named, *types.Pointer:
if collect.IsMapKey(typ) { if collect.IsMapKey(k) {
if collect.IsString(typ) { if collect.IsStringAlias(k) {
// Alias or named type is a string and therefore // Alias or named type is a string and therefore
// safe to use as a JS object key. // safe to use as a JS object key.
if ptr, ok := k.(*types.Pointer); ok { if ptr, ok := k.(*types.Pointer); ok {
// Unwrap pointer to named string type, but not pointer aliases. // Unwrap pointers to string aliases.
key, _ = m.renderType(ptr.Elem(), false) key, _ = m.renderType(ptr.Elem(), false)
} else { } else {
key, _ = m.renderType(k, false) key, _ = m.renderType(k, false)
} }
} else if basic, ok := typ.Underlying().(*types.Basic); ok && basic.Info()&types.IsString == 0 { } else if basic, ok := k.Underlying().(*types.Basic); ok && basic.Info()&types.IsString == 0 {
// Render non-string basic type in quoted mode. // Render non-string basic type in quoted mode.
key = m.renderBasicType(basic, true) key = m.renderBasicType(basic, true)
} }
} }
case *types.Pointer:
if collect.IsMapKey(typ) && collect.IsString(typ.Elem()) {
// Base type is a string alias and therefore
// safe to use as a JS object key.
key, _ = m.renderType(k.Elem(), false)
}
} }
return fmt.Sprintf("{ [_: %s]: %s }%s", key, elem, null), m.UseInterfaces return fmt.Sprintf("{ [_: %s]: %s }%s", key, elem, null), m.UseInterfaces
@ -193,16 +186,12 @@ func (m *module) renderNamedType(typ aliasOrNamed, quoted bool) (result string,
return m.renderType(a, quoted) return m.renderType(a, quoted)
case *types.Named: case *types.Named:
// Quoted mode for (alias of?) named type. // Quoted mode for (alias of?) named type.
// WARN: Do not test with IsString here!! We only want to catch marshalers. // WARN: Do not test with IsAny/IsStringAlias here!! We only want to catch marshalers.
if !collect.IsAny(typ) && !collect.MaybeTextMarshaler(typ) { if collect.MaybeJSONMarshaler(typ) == collect.NonMarshaler && collect.MaybeTextMarshaler(typ) == collect.NonMarshaler {
// No custom marshaling for this type. // No custom marshaling for this type.
switch u := a.Underlying().(type) { if u, ok := a.Underlying().(*types.Basic); ok {
case *types.Basic:
// Quoted mode for basic named type that is not a marshaler: delegate. // Quoted mode for basic named type that is not a marshaler: delegate.
return m.renderBasicType(u, quoted), false return m.renderBasicType(u, quoted), false
case *types.TypeParam:
// Quoted mode for generic type that maps to typeparam: delegate.
return m.renderType(u, quoted)
} }
} }
} }
@ -242,6 +231,12 @@ func (m *module) renderNamedType(typ aliasOrNamed, quoted bool) (result string,
// renderStructType outputs the TS representation // renderStructType outputs the TS representation
// of the given anonymous struct type. // of the given anonymous struct type.
func (m *module) renderStructType(typ *types.Struct) string { func (m *module) renderStructType(typ *types.Struct) string {
if collect.MaybeJSONMarshaler(typ) != collect.NonMarshaler {
return "any"
} else if collect.MaybeTextMarshaler(typ) != collect.NonMarshaler {
return "string"
}
info := m.collector.Struct(typ) info := m.collector.Struct(typ)
info.Collect() info.Collect()

View File

@ -33,11 +33,13 @@ func (generator *Generator) generateService(obj *types.TypeName) {
} }
if info.IsEmpty() { if info.IsEmpty() {
if !info.HasInternalMethods {
generator.logger.Infof( generator.logger.Infof(
"package %s: type %s: service has no valid exported methods, skipping", "package %s: type %s: service has no valid exported methods, skipping",
obj.Pkg().Path(), obj.Pkg().Path(),
obj.Name(), obj.Name(),
) )
}
success = true success = true
return return
} }
@ -53,14 +55,6 @@ func (generator *Generator) generateService(obj *types.TypeName) {
) )
return return
case generator.renderer.InternalFile():
generator.logger.Errorf(
"package %s: type %s: service filename collides with internal models filename; please rename the type or choose a different filename for internal models",
obj.Pkg().Path(),
obj.Name(),
)
return
case generator.renderer.IndexFile(): case generator.renderer.IndexFile():
if !generator.options.NoIndex { if !generator.options.NoIndex {
generator.logger.Errorf( generator.logger.Errorf(
@ -90,7 +84,12 @@ func (generator *Generator) generateService(obj *types.TypeName) {
generator.logger.Errorf("%v", err) generator.logger.Errorf("%v", err)
return return
} }
defer file.Close() defer func() {
if err := file.Close(); err != nil {
generator.logger.Errorf("%v", err)
success = false
}
}()
// Render service code. // Render service code.
err = generator.renderer.Service(file, info) err = generator.renderer.Service(file, info)

View File

@ -4,6 +4,7 @@ import (
_ "embed" _ "embed"
"log" "log"
nobindingshere "github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here"
"github.com/wailsapp/wails/v3/pkg/application" "github.com/wailsapp/wails/v3/pkg/application"
) )
@ -52,6 +53,39 @@ type GenericPerson[T any] struct {
// Another class alias, but ordered after its aliased class. // Another class alias, but ordered after its aliased class.
type StrangelyAliasedPerson = Person type StrangelyAliasedPerson = Person
// A generic alias that forwards to a type parameter.
// type GenericAlias[T any] = T
// A generic alias that wraps a pointer type.
// type GenericPtrAlias[T any] = *GenericAlias[T]
// A generic alias that wraps a map.
// type GenericMapAlias[T interface {
// comparable
// encoding.TextMarshaler
// }, U any] = map[T]U
// A generic alias that wraps a generic struct.
// type GenericPersonAlias[T any] = GenericPerson[[]GenericPtrAlias[T]]
// An alias that wraps a class through a non-typeparam alias.
// type IndirectPersonAlias = GenericPersonAlias[bool]
// An alias that wraps a class through a typeparam alias.
// type TPIndirectPersonAlias = GenericAlias[GenericPerson[bool]]
// A class whose fields have various aliased types.
// type AliasGroup struct {
// GAi GenericAlias[int]
// GAP GenericAlias[GenericPerson[bool]]
// GPAs GenericPtrAlias[[]string]
// GPAP GenericPtrAlias[GenericPerson[[]int]]
// GMA GenericMapAlias[struct{ encoding.TextMarshaler }, float32]
// GPA GenericPersonAlias[bool]
// IPA IndirectPersonAlias
// TPIPA TPIndirectPersonAlias
// }
// Get someone. // Get someone.
func (GreetService) Get(aliasValue Alias) Person { func (GreetService) Get(aliasValue Alias) Person {
return Person{"hello", aliasValue} return Person{"hello", aliasValue}
@ -67,6 +101,14 @@ func (GreetService) GetButAliased(p AliasedPerson) StrangelyAliasedPerson {
return p return p
} }
func (GreetService) GetButForeignPrivateAlias() (_ nobindingshere.PrivatePerson) {
return
}
// func (GreetService) GetButGenericAliases() (_ AliasGroup) {
// return
// }
// Greet a lot of unusual things. // Greet a lot of unusual things.
func (GreetService) Greet(EmptyAliasStruct, EmptyStruct) AliasStruct { func (GreetService) Greet(EmptyAliasStruct, EmptyStruct) AliasStruct {
return AliasStruct{} return AliasStruct{}

View File

@ -12,5 +12,5 @@
".Service11", ".Service11",
".Service12", ".Service12",
".Service13", ".Service13",
"/other.Service14" "/other.Service16"
] ]

View File

@ -21,6 +21,10 @@ type Service10 struct{}
type Service11 struct{} type Service11 struct{}
type Service12 struct{} type Service12 struct{}
type Service13 struct{} type Service13 struct{}
type Service14 struct{}
type Service15 struct{}
// type SimplifiedFactory[T any] = Factory[T, Service15]
func main() { func main() {
factory := NewFactory[Service1, Service2]() factory := NewFactory[Service1, Service2]()
@ -38,6 +42,7 @@ func main() {
other.CustomNewService(Service7{}), other.CustomNewService(Service7{}),
other.ServiceInitialiser[Service8]()(&Service8{}), other.ServiceInitialiser[Service8]()(&Service8{}),
application.NewServiceWithOptions(&Service13{}, application.ServiceOptions{Name: "custom name"}), application.NewServiceWithOptions(&Service13{}, application.ServiceOptions{Name: "custom name"}),
// SimplifiedFactory[Service14]{}.Get(),
other.LocalService, other.LocalService,
}, },
CustomNewServices[Service9, Service10]()...), CustomNewServices[Service9, Service10]()...),

View File

@ -2,6 +2,6 @@ package other
import "github.com/wailsapp/wails/v3/pkg/application" import "github.com/wailsapp/wails/v3/pkg/application"
type Service14 int type Service16 int
var LocalService = application.NewService(new(Service14)) var LocalService = application.NewService(new(Service16))

View File

@ -0,0 +1,5 @@
[
".InternalService",
".Service",
".unexportedService"
]

View File

@ -0,0 +1,11 @@
//wails:include js/test.js
//wails:include **:js/test_all.js
//wails:include *c:js/test_c.js
//wails:include *i:js/test_i.js
//wails:include j*:js/test_j.js
//wails:include jc:js/test_jc.js
//wails:include ji:js/test_ji.js
//wails:include t*:js/test_t.ts
//wails:include tc:js/test_tc.ts
//wails:include ti:js/test_ti.ts
package main

View File

@ -0,0 +1,15 @@
package main
// An exported but internal model.
//
//wails:internal
type InternalModel struct {
Field string
}
// An exported but internal service.
//
//wails:internal
type InternalService struct{}
func (InternalService) Method(InternalModel) {}

View File

@ -0,0 +1,3 @@
import { CustomMethod } from "./service.js";
CustomMethod("everywhere");

View File

@ -0,0 +1,3 @@
import { CustomMethod } from "./service.js";
CustomMethod("everywhere again");

View File

@ -0,0 +1,3 @@
import { CustomMethod } from "./service.js";
CustomMethod("Classes");

View File

@ -0,0 +1,3 @@
import { CustomMethod } from "./service.js";
CustomMethod("Interfaces");

View File

@ -0,0 +1,3 @@
import { CustomMethod } from "./service.js";
CustomMethod("JS");

View File

@ -0,0 +1,3 @@
import { CustomMethod } from "./service.js";
CustomMethod("JS Classes");

View File

@ -0,0 +1,3 @@
import { CustomMethod } from "./service.js";
CustomMethod("JS Interfaces");

View File

@ -0,0 +1,3 @@
import { CustomMethod } from "./service.js";
CustomMethod("TS");

View File

@ -0,0 +1,3 @@
import { CustomMethod } from "./service.js";
CustomMethod("TS Classes");

View File

@ -0,0 +1,3 @@
import { CustomMethod } from "./service.js";
CustomMethod("TS Interfaces");

View File

@ -0,0 +1,60 @@
//wails:inject console.log("Hello everywhere!");
//wails:inject **:console.log("Hello everywhere again!");
//wails:inject *c:console.log("Hello Classes!");
//wails:inject *i:console.log("Hello Interfaces!");
//wails:inject j*:console.log("Hello JS!");
//wails:inject jc:console.log("Hello JS Classes!");
//wails:inject ji:console.log("Hello JS Interfaces!");
//wails:inject t*:console.log("Hello TS!");
//wails:inject tc:console.log("Hello TS Classes!");
//wails:inject ti:console.log("Hello TS Interfaces!");
package main
import (
_ "embed"
"log"
"github.com/wailsapp/wails/v3/internal/generator/testcases/directives/otherpackage"
"github.com/wailsapp/wails/v3/pkg/application"
)
type IgnoredType struct {
Field int
}
//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{}
func (*Service) VisibleMethod(otherpackage.Dummy) {}
//wails:ignore
func (*Service) IgnoredMethod(IgnoredType) {}
//wails:internal
func (*Service) InternalMethod(string) {}
func main() {
app := application.New(application.Options{
Services: []application.Service{
application.NewService(&Service{}),
application.NewService(&unexportedService{}),
application.NewService(&InternalService{}),
},
})
app.NewWebviewWindow()
err := app.Run()
if err != nil {
log.Fatal(err)
}
}

View File

@ -0,0 +1,5 @@
//wails:include jc:js/*_j*.js
//wails:include tc:js/*_t*.ts
package otherpackage
type Dummy struct{}

View File

@ -0,0 +1,3 @@
import { CustomMethod } from "../service.js";
CustomMethod("JS");

View File

@ -0,0 +1,3 @@
import { CustomMethod } from "../service.js";
CustomMethod("JS Classes");

View File

@ -0,0 +1,3 @@
import { CustomMethod } from "../service.js";
CustomMethod("TS");

View File

@ -0,0 +1,3 @@
import { CustomMethod } from "../service.js";
CustomMethod("TS Classes");

View File

@ -0,0 +1,11 @@
package main
// An unexported model.
type unexportedModel struct {
Field string
}
// An unexported service.
type unexportedService struct{}
func (unexportedService) Method(unexportedModel) {}

View File

@ -19,6 +19,19 @@ const (
Dr Title = "Dr" Dr Title = "Dr"
) )
// Age is an integer with some predefined values
type Age = int
const (
NewBorn Age = 0
Teenager Age = 12
YoungAdult Age = 18
// Oh no, some grey hair!
MiddleAged Age = 50
Mathusalem Age = 1000 // Unbelievable!
)
// GreetService is great // GreetService is great
type GreetService struct { type GreetService struct {
SomeVariable int SomeVariable int
@ -30,6 +43,7 @@ type GreetService struct {
type Person struct { type Person struct {
Title Title Title Title
Name string Name string
Age Age
} }
// Greet does XYZ // Greet does XYZ

View File

@ -0,0 +1,3 @@
[
".Service"
]

View File

@ -0,0 +1,307 @@
package main
import (
_ "embed"
"encoding"
"encoding/json"
"log"
"github.com/wailsapp/wails/v3/pkg/application"
)
type Service struct{}
type NonTextMarshaler struct{}
type ValueTextMarshaler struct{}
func (ValueTextMarshaler) MarshalText() ([]byte, error) { return nil, nil }
type PointerTextMarshaler struct{}
func (*PointerTextMarshaler) MarshalText() ([]byte, error) { return nil, nil }
type JsonTextMarshaler struct{}
func (JsonTextMarshaler) MarshalJSON() ([]byte, error) { return nil, nil }
func (JsonTextMarshaler) MarshalText() ([]byte, error) { return nil, nil }
type CustomInterface interface {
MarshalText() ([]byte, error)
}
type EmbeddedInterface interface {
encoding.TextMarshaler
}
type EmbeddedInterfaces interface {
json.Marshaler
encoding.TextMarshaler
}
type BasicConstraint interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 |
~string
}
type BadTildeConstraint interface {
int | ~struct{} | string
}
type GoodTildeConstraint interface {
int | ~struct{} | string
MarshalText() ([]byte, error)
}
type NonBasicConstraint interface {
ValueTextMarshaler | *PointerTextMarshaler
}
type PointableConstraint interface {
ValueTextMarshaler | PointerTextMarshaler
}
type MixedConstraint interface {
uint | ~string | ValueTextMarshaler | *PointerTextMarshaler
}
type InterfaceConstraint interface {
comparable
encoding.TextMarshaler
}
type PointerConstraint[T comparable] interface {
*T
encoding.TextMarshaler
}
type EmbeddedValue struct{ ValueTextMarshaler }
type EmbeddedValuePtr struct{ *ValueTextMarshaler }
type EmbeddedPointer struct{ PointerTextMarshaler }
type EmbeddedPointerPtr struct{ *PointerTextMarshaler }
type EmbeddedCustomInterface struct{ CustomInterface }
type EmbeddedOriginalInterface struct{ encoding.TextMarshaler }
type WrongType bool
type WrongAlias = bool
type StringType string
type StringAlias = string
type IntType int
type IntAlias = int
type ValueType ValueTextMarshaler
type ValuePtrType *ValueTextMarshaler
type ValueAlias = ValueTextMarshaler
type ValuePtrAlias = *ValueTextMarshaler
type PointerType PointerTextMarshaler
type PointerPtrType *PointerTextMarshaler
type PointerAlias = PointerTextMarshaler
type PointerPtrAlias = *PointerTextMarshaler
type InterfaceType encoding.TextMarshaler
type InterfacePtrType *encoding.TextMarshaler
type InterfaceAlias = encoding.TextMarshaler
type InterfacePtrAlias = *encoding.TextMarshaler
// type ComparableCstrAlias[R comparable] = R
// type ComparableCstrPtrAlias[R comparable] = *R
// type BasicCstrAlias[S BasicConstraint] = S
// type BasicCstrPtrAlias[S BasicConstraint] = *S
// type BadTildeCstrAlias[T BadTildeConstraint] = T
// type BadTildeCstrPtrAlias[T BadTildeConstraint] = *T
// type GoodTildeCstrAlias[U GoodTildeConstraint] = U
// type GoodTildeCstrPtrAlias[U GoodTildeConstraint] = *U
// type NonBasicCstrAlias[V NonBasicConstraint] = V
// type NonBasicCstrPtrAlias[V NonBasicConstraint] = *V
// type PointableCstrAlias[W PointableConstraint] = W
// type PointableCstrPtrAlias[W PointableConstraint] = *W
// type MixedCstrAlias[X MixedConstraint] = X
// type MixedCstrPtrAlias[X MixedConstraint] = *X
// type InterfaceCstrAlias[Y InterfaceConstraint] = Y
// type InterfaceCstrPtrAlias[Y InterfaceConstraint] = *Y
// type PointerCstrAlias[R comparable, Z PointerConstraint[R]] = Z
// type PointerCstrPtrAlias[R comparable, Z PointerConstraint[R]] = *Z
type Maps[R comparable, S BasicConstraint, T BadTildeConstraint, U GoodTildeConstraint, V NonBasicConstraint, W PointableConstraint, X MixedConstraint, Y InterfaceConstraint, Z PointerConstraint[R]] struct {
Bool map[bool]int // Reject
Int map[int]int // Accept
Uint map[uint]int // Accept
Float map[float32]int // Reject
Complex map[complex64]int // Reject
Byte map[byte]int // Accept
Rune map[rune]int // Accept
String map[string]int // Accept
IntPtr map[*int]int // Reject
UintPtr map[*uint]int // Reject
FloatPtr map[*float32]int // Reject
ComplexPtr map[*complex64]int // Reject
StringPtr map[*string]int // Reject
NTM map[NonTextMarshaler]int // Reject
NTMPtr map[*NonTextMarshaler]int // Reject
VTM map[ValueTextMarshaler]int // Accept
VTMPtr map[*ValueTextMarshaler]int // Accept
PTM map[PointerTextMarshaler]int // Reject
PTMPtr map[*PointerTextMarshaler]int // Accept
JTM map[JsonTextMarshaler]int // Accept, hide
JTMPtr map[*JsonTextMarshaler]int // Accept, hide
A map[any]int // Reject
APtr map[*any]int // Reject
TM map[encoding.TextMarshaler]int // Accept, hide
TMPtr map[*encoding.TextMarshaler]int // Reject
CI map[CustomInterface]int // Accept, hide
CIPtr map[*CustomInterface]int // Reject
EI map[EmbeddedInterface]int // Accept, hide
EIPtr map[*EmbeddedInterface]int // Reject
EV map[EmbeddedValue]int // Accept
EVPtr map[*EmbeddedValue]int // Accept
EVP map[EmbeddedValuePtr]int // Accept
EVPPtr map[*EmbeddedValuePtr]int // Accept
EP map[EmbeddedPointer]int // Reject
EPPtr map[*EmbeddedPointer]int // Accept
EPP map[EmbeddedPointerPtr]int // Accept
EPPPtr map[*EmbeddedPointerPtr]int // Accept
ECI map[EmbeddedCustomInterface]int // Accept
ECIPtr map[*EmbeddedCustomInterface]int // Accept
EOI map[EmbeddedOriginalInterface]int // Accept
EOIPtr map[*EmbeddedOriginalInterface]int // Accept
WT map[WrongType]int // Reject
WA map[WrongAlias]int // Reject
ST map[StringType]int // Accept
SA map[StringAlias]int // Accept
IntT map[IntType]int // Accept
IntA map[IntAlias]int // Accept
VT map[ValueType]int // Reject
VTPtr map[*ValueType]int // Reject
VPT map[ValuePtrType]int // Reject
VPTPtr map[*ValuePtrType]int // Reject
VA map[ValueAlias]int // Accept
VAPtr map[*ValueAlias]int // Accept
VPA map[ValuePtrAlias]int // Accept, hide
VPAPtr map[*ValuePtrAlias]int // Reject
PT map[PointerType]int // Reject
PTPtr map[*PointerType]int // Reject
PPT map[PointerPtrType]int // Reject
PPTPtr map[*PointerPtrType]int // Reject
PA map[PointerAlias]int // Reject
PAPtr map[*PointerAlias]int // Accept
PPA map[PointerPtrAlias]int // Accept, hide
PPAPtr map[*PointerPtrAlias]int // Reject
IT map[InterfaceType]int // Accept, hide
ITPtr map[*InterfaceType]int // Reject
IPT map[InterfacePtrType]int // Reject
IPTPtr map[*InterfacePtrType]int // Reject
IA map[InterfaceAlias]int // Accept, hide
IAPtr map[*InterfaceAlias]int // Reject
IPA map[InterfacePtrAlias]int // Reject
IPAPtr map[*InterfacePtrAlias]int // Reject
TPR map[R]int // Soft reject
TPRPtr map[*R]int // Soft reject
TPS map[S]int // Accept, hide
TPSPtr map[*S]int // Soft reject
TPT map[T]int // Soft reject
TPTPtr map[*T]int // Soft reject
TPU map[U]int // Accept, hide
TPUPtr map[*U]int // Soft reject
TPV map[V]int // Accept, hide
TPVPtr map[*V]int // Soft reject
TPW map[W]int // Soft reject
TPWPtr map[*W]int // Accept, hide
TPX map[X]int // Accept, hide
TPXPtr map[*X]int // Soft reject
TPY map[Y]int // Accept, hide
TPYPtr map[*Y]int // Soft reject
TPZ map[Z]int // Accept, hide
TPZPtr map[*Z]int // Soft reject
// GAR map[ComparableCstrAlias[R]]int // Soft reject
// GARPtr map[ComparableCstrPtrAlias[R]]int // Soft reject
// GAS map[BasicCstrAlias[S]]int // Accept, hide
// GASPtr map[BasicCstrPtrAlias[S]]int // Soft reject
// GAT map[BadTildeCstrAlias[T]]int // Soft reject
// GATPtr map[BadTildeCstrPtrAlias[T]]int // Soft reject
// GAU map[GoodTildeCstrAlias[U]]int // Accept, hide
// GAUPtr map[GoodTildeCstrPtrAlias[U]]int // Soft reject
// GAV map[NonBasicCstrAlias[V]]int // Accept, hide
// GAVPtr map[NonBasicCstrPtrAlias[V]]int // Soft reject
// GAW map[PointableCstrAlias[W]]int // Soft reject
// GAWPtr map[PointableCstrPtrAlias[W]]int // Accept, hide
// GAX map[MixedCstrAlias[X]]int // Accept, hide
// GAXPtr map[MixedCstrPtrAlias[X]]int // Soft reject
// GAY map[InterfaceCstrAlias[Y]]int // Accept, hide
// GAYPtr map[InterfaceCstrPtrAlias[Y]]int // Soft reject
// GAZ map[PointerCstrAlias[R, Z]]int // Accept, hide
// GAZPtr map[PointerCstrPtrAlias[R, Z]]int // Soft reject
// GACi map[ComparableCstrAlias[int]]int // Accept, hide
// GACV map[ComparableCstrAlias[ValueTextMarshaler]]int // Accept
// GACP map[ComparableCstrAlias[PointerTextMarshaler]]int // Reject
// GACiPtr map[ComparableCstrPtrAlias[int]]int // Reject
// GACVPtr map[ComparableCstrPtrAlias[ValueTextMarshaler]]int // Accept, hide
// GACPPtr map[ComparableCstrPtrAlias[PointerTextMarshaler]]int // Accept, hide
// GABi map[BasicCstrAlias[int]]int // Accept, hide
// GABs map[BasicCstrAlias[string]]int // Accept
// GABiPtr map[BasicCstrPtrAlias[int]]int // Reject
// GABT map[BadTildeCstrAlias[struct{}]]int // Reject
// GABTPtr map[BadTildeCstrPtrAlias[struct{}]]int // Reject
// GAGT map[GoodTildeCstrAlias[ValueTextMarshaler]]int // Accept
// GAGTPtr map[GoodTildeCstrPtrAlias[ValueTextMarshaler]]int // Accept, hide
// GANBV map[NonBasicCstrAlias[ValueTextMarshaler]]int // Accept
// GANBP map[NonBasicCstrAlias[*PointerTextMarshaler]]int // Accept, hide
// GANBVPtr map[NonBasicCstrPtrAlias[ValueTextMarshaler]]int // Accept, hide
// GANBPPtr map[NonBasicCstrPtrAlias[*PointerTextMarshaler]]int // Reject
// GAPlV1 map[PointableCstrAlias[ValueTextMarshaler]]int // Accept
// GAPlV2 map[*PointableCstrAlias[ValueTextMarshaler]]int // Accept
// GAPlP1 map[PointableCstrAlias[PointerTextMarshaler]]int // Reject
// GAPlP2 map[*PointableCstrAlias[PointerTextMarshaler]]int // Accept
// GAPlVPtr map[PointableCstrPtrAlias[ValueTextMarshaler]]int // Accept, hide
// GAPlPPtr map[PointableCstrPtrAlias[PointerTextMarshaler]]int // Accept, hide
// GAMi map[MixedCstrAlias[uint]]int // Accept, hide
// GAMS map[MixedCstrAlias[StringType]]int // Accept
// GAMV map[MixedCstrAlias[ValueTextMarshaler]]int // Accept
// GAMSPtr map[MixedCstrPtrAlias[StringType]]int // Reject
// GAMVPtr map[MixedCstrPtrAlias[ValueTextMarshaler]]int // Accept, hide
// GAII map[InterfaceCstrAlias[encoding.TextMarshaler]]int // Accept, hide
// GAIV map[InterfaceCstrAlias[ValueTextMarshaler]]int // Accept
// GAIP map[InterfaceCstrAlias[*PointerTextMarshaler]]int // Accept, hide
// GAIIPtr map[InterfaceCstrPtrAlias[encoding.TextMarshaler]]int // Reject
// GAIVPtr map[InterfaceCstrPtrAlias[ValueTextMarshaler]]int // Accept, hide
// GAIPPtr map[InterfaceCstrPtrAlias[*PointerTextMarshaler]]int // Reject
// GAPrV map[PointerCstrAlias[ValueTextMarshaler, *ValueTextMarshaler]]int // Accept, hide
// GAPrP map[PointerCstrAlias[PointerTextMarshaler, *PointerTextMarshaler]]int // Accept, hide
// GAPrVPtr map[PointerCstrPtrAlias[ValueTextMarshaler, *ValueTextMarshaler]]int // Reject
// GAPrPPtr map[PointerCstrPtrAlias[PointerTextMarshaler, *PointerTextMarshaler]]int // Reject
}
func (*Service) Method() (_ Maps[PointerTextMarshaler, int, int, ValueTextMarshaler, *PointerTextMarshaler, ValueTextMarshaler, StringType, ValueTextMarshaler, *PointerTextMarshaler]) {
return
}
func main() {
app := application.New(application.Options{
Services: []application.Service{
application.NewService(&Service{}),
},
})
app.NewWebviewWindow()
err := app.Run()
if err != nil {
log.Fatal(err)
}
}

View File

@ -0,0 +1,3 @@
[
".Service"
]

View File

@ -0,0 +1,209 @@
package main
import (
_ "embed"
"encoding"
"encoding/json"
"log"
"github.com/wailsapp/wails/v3/pkg/application"
)
type Service struct{}
// class {}
type NonMarshaler struct{}
// any
type ValueJsonMarshaler struct{}
func (ValueJsonMarshaler) MarshalJSON() ([]byte, error) { return nil, nil }
// any
type PointerJsonMarshaler struct{}
func (*PointerJsonMarshaler) MarshalJSON() ([]byte, error) { return nil, nil }
// string
type ValueTextMarshaler struct{}
func (ValueTextMarshaler) MarshalText() ([]byte, error) { return nil, nil }
// string
type PointerTextMarshaler struct{}
func (*PointerTextMarshaler) MarshalText() ([]byte, error) { return nil, nil }
// any
type ValueMarshaler struct{}
func (ValueMarshaler) MarshalJSON() ([]byte, error) { return nil, nil }
func (ValueMarshaler) MarshalText() ([]byte, error) { return nil, nil }
// any
type PointerMarshaler struct{}
func (*PointerMarshaler) MarshalJSON() ([]byte, error) { return nil, nil }
func (*PointerMarshaler) MarshalText() ([]byte, error) { return nil, nil }
// any
type UnderlyingJsonMarshaler struct{ json.Marshaler }
// string
type UnderlyingTextMarshaler struct{ encoding.TextMarshaler }
// any
type UnderlyingMarshaler struct {
json.Marshaler
encoding.TextMarshaler
}
type customJsonMarshaler interface {
MarshalJSON() ([]byte, error)
}
type customTextMarshaler interface {
MarshalText() ([]byte, error)
}
type customMarshaler interface {
MarshalJSON() ([]byte, error)
MarshalText() ([]byte, error)
}
// struct{}
type AliasNonMarshaler = struct{}
// any
type AliasJsonMarshaler = struct{ json.Marshaler }
// string
type AliasTextMarshaler = struct{ encoding.TextMarshaler }
// any
type AliasMarshaler = struct {
json.Marshaler
encoding.TextMarshaler
}
// any
type ImplicitJsonMarshaler UnderlyingJsonMarshaler
// string
type ImplicitTextMarshaler UnderlyingTextMarshaler
// any
type ImplicitMarshaler UnderlyingMarshaler
// string
type ImplicitNonJson UnderlyingMarshaler
func (ImplicitNonJson) MarshalJSON() {}
// any
type ImplicitNonText UnderlyingMarshaler
func (ImplicitNonText) MarshalText() {}
// class{ Marshaler, TextMarshaler }
type ImplicitNonMarshaler UnderlyingMarshaler
func (ImplicitNonMarshaler) MarshalJSON() {}
func (ImplicitNonMarshaler) MarshalText() {}
// any
type ImplicitJsonButText UnderlyingJsonMarshaler
func (ImplicitJsonButText) MarshalText() ([]byte, error) { return nil, nil }
// any
type ImplicitTextButJson UnderlyingTextMarshaler
func (ImplicitTextButJson) MarshalJSON() ([]byte, error) { return nil, nil }
type Data struct {
NM NonMarshaler
NMPtr *NonMarshaler // NonMarshaler | null
VJM ValueJsonMarshaler
VJMPtr *ValueJsonMarshaler // ValueJsonMarshaler | null
PJM PointerJsonMarshaler
PJMPtr *PointerJsonMarshaler // PointerJsonMarshaler | null
VTM ValueTextMarshaler
VTMPtr *ValueTextMarshaler // ValueTextMarshaler | null
PTM PointerTextMarshaler
PTMPtr *PointerTextMarshaler // PointerTextMarshaler | null
VM ValueMarshaler
VMPtr *ValueMarshaler // ValueMarshaler | null
PM PointerMarshaler
PMPtr *PointerMarshaler // PointerMarshaler | null
UJM UnderlyingJsonMarshaler
UJMPtr *UnderlyingJsonMarshaler // UnderlyingJsonMarshaler | null
UTM UnderlyingTextMarshaler
UTMPtr *UnderlyingTextMarshaler // UnderlyingTextMarshaler | null
UM UnderlyingMarshaler
UMPtr *UnderlyingMarshaler // UnderlyingMarshaler | null
JM struct{ json.Marshaler } // any
JMPtr *struct{ json.Marshaler } // any | null
TM struct{ encoding.TextMarshaler } // string
TMPtr *struct{ encoding.TextMarshaler } // string | null
CJM struct{ customJsonMarshaler } // any
CJMPtr *struct{ customJsonMarshaler } // any | null
CTM struct{ customTextMarshaler } // string
CTMPtr *struct{ customTextMarshaler } // string | null
CM struct{ customMarshaler } // any
CMPtr *struct{ customMarshaler } // any | null
ANM AliasNonMarshaler
ANMPtr *AliasNonMarshaler // AliasNonMarshaler | null
AJM AliasJsonMarshaler
AJMPtr *AliasJsonMarshaler // AliasJsonMarshaler | null
ATM AliasTextMarshaler
ATMPtr *AliasTextMarshaler // AliasTextMarshaler | null
AM AliasMarshaler
AMPtr *AliasMarshaler // AliasMarshaler | null
ImJM ImplicitJsonMarshaler
ImJMPtr *ImplicitJsonMarshaler // ImplicitJsonMarshaler | null
ImTM ImplicitTextMarshaler
ImTMPtr *ImplicitTextMarshaler // ImplicitTextMarshaler | null
ImM ImplicitMarshaler
ImMPtr *ImplicitMarshaler // ImplicitMarshaler | null
ImNJ ImplicitNonJson
ImNJPtr *ImplicitNonJson // ImplicitNonJson | null
ImNT ImplicitNonText
ImNTPtr *ImplicitNonText // ImplicitNonText | null
ImNM ImplicitNonMarshaler
ImNMPtr *ImplicitNonMarshaler // ImplicitNonMarshaler | null
ImJbT ImplicitJsonButText
ImJbTPtr *ImplicitJsonButText // ImplicitJsonButText | null
ImTbJ ImplicitTextButJson
ImTbJPtr *ImplicitTextButJson // ImplicitTextButJson | null
}
func (*Service) Method() (_ Data) {
return
}
func main() {
app := application.New(application.Options{
Services: []application.Service{
application.NewService(&Service{}),
},
})
app.NewWebviewWindow()
err := app.Run()
if err != nil {
log.Fatal(err)
}
}

View File

@ -10,14 +10,14 @@ type Person struct {
} }
// Impersonator gets their fields from other people. // Impersonator gets their fields from other people.
type Impersonator other.OtherPerson[int] type Impersonator = other.OtherPerson[int]
// HowDifferent is a curious kind of person // HowDifferent is a curious kind of person
// that lets other people decide how they are different. // that lets other people decide how they are different.
type HowDifferent[How any] other.OtherPerson[map[string]How] type HowDifferent[How any] other.OtherPerson[map[string]How]
// PrivatePerson gets their fields from hidden sources. // PrivatePerson gets their fields from hidden sources.
type PrivatePerson personImpl type PrivatePerson = personImpl
type personImpl struct { type personImpl struct {
// Nickname conceals a person's identity. // Nickname conceals a person's identity.

View File

@ -174,7 +174,7 @@ func (GreetService) MapIntInt(in map[int]int) {
func (GreetService) PointerMapIntInt(in *map[int]int) { func (GreetService) PointerMapIntInt(in *map[int]int) {
} }
func (GreetService) MapIntPointerInt(in map[*int]int) { func (GreetService) MapIntIntPointer(in map[int]*int) {
} }
func (GreetService) MapIntSliceInt(in map[int][]int) { func (GreetService) MapIntSliceInt(in map[int][]int) {

View File

@ -174,7 +174,7 @@ func (*GreetService) MapIntInt(in map[int]int) {
func (*GreetService) PointerMapIntInt(in *map[int]int) { func (*GreetService) PointerMapIntInt(in *map[int]int) {
} }
func (*GreetService) MapIntPointerInt(in map[*int]int) { func (*GreetService) MapIntIntPointer(in map[int]*int) {
} }
func (*GreetService) MapIntSliceInt(in map[int][]int) { func (*GreetService) MapIntSliceInt(in map[int][]int) {

View File

@ -0,0 +1,13 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import * as $models from "./models.js";
/**
* TextMarshaler is the interface implemented by an object that can
* marshal itself into a textual form.
*
* MarshalText encodes the receiver into UTF-8-encoded text and returns the result.
* @typedef {$models.TextMarshaler} TextMarshaler
*/

View File

@ -0,0 +1,11 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import * as $models from "./models.js";
/**
* Marshaler is the interface implemented by types that
* can marshal themselves into valid JSON.
* @typedef {$models.Marshaler} Marshaler
*/

View File

@ -1,3 +1,4 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT // This file is automatically generated. DO NOT EDIT
@ -5,8 +6,8 @@
// @ts-ignore: Unused imports // @ts-ignore: Unused imports
import {Create as $Create} from "/wails/runtime.js"; import {Create as $Create} from "/wails/runtime.js";
export type Alias = Cyclic | null; /**
* Marshaler is the interface implemented by types that
export type Cyclic = { [_: string]: Alias }[]; * can marshal themselves into valid JSON.
* @typedef {any} Marshaler
export type GenericCyclic<T> = {"X": GenericCyclic<T | null> | null, "Y": (T | null)[]}[]; */

View File

@ -1,3 +1,4 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT // This file is automatically generated. DO NOT EDIT
@ -5,18 +6,10 @@
// @ts-ignore: Unused imports // @ts-ignore: Unused imports
import {Create as $Create} from "/wails/runtime.js"; import {Create as $Create} from "/wails/runtime.js";
export enum Title { /**
/** * TextMarshaler is the interface implemented by an object that can
* The Go zero value for the underlying type of the enum. * marshal itself into a textual form.
*
* MarshalText encodes the receiver into UTF-8-encoded text and returns the result.
* @typedef {any} TextMarshaler
*/ */
$zero = "",
/**
* Mister is a title
*/
Mister = "Mr",
Miss = "Miss",
Ms = "Ms",
Mrs = "Mrs",
Dr = "Dr",
};

View File

@ -13,7 +13,11 @@ import {Call as $Call, Create as $Create} from "/wails/runtime.js";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports // @ts-ignore: Unused imports
import * as $models from "./internal.js"; import * as nobindingshere$0 from "../no_bindings_here/models.js";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as $models from "./models.js";
/** /**
* Get someone. * Get someone.
@ -56,6 +60,18 @@ export function GetButDifferent() {
return $typingPromise; return $typingPromise;
} }
/**
* @returns {Promise<nobindingshere$0.PrivatePerson> & { cancel(): void }}
*/
export function GetButForeignPrivateAlias() {
let $resultPromise = /** @type {any} */($Call.ByID(643456960));
let $typingPromise = /** @type {any} */($resultPromise.then(($result) => {
return $$createType2($result);
}));
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
/** /**
* Greet a lot of unusual things. * Greet a lot of unusual things.
* @param {$models.EmptyAliasStruct} $0 * @param {$models.EmptyAliasStruct} $0
@ -65,7 +81,7 @@ export function GetButDifferent() {
export function Greet($0, $1) { export function Greet($0, $1) {
let $resultPromise = /** @type {any} */($Call.ByID(1411160069, $0, $1)); let $resultPromise = /** @type {any} */($Call.ByID(1411160069, $0, $1));
let $typingPromise = /** @type {any} */($resultPromise.then(($result) => { let $typingPromise = /** @type {any} */($resultPromise.then(($result) => {
return $$createType5($result); return $$createType6($result);
})); }));
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise); $typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise; return $typingPromise;
@ -74,12 +90,13 @@ export function Greet($0, $1) {
// Private type creation functions // Private type creation functions
const $$createType0 = $models.Person.createFrom; const $$createType0 = $models.Person.createFrom;
const $$createType1 = $models.GenericPerson.createFrom($Create.Any); const $$createType1 = $models.GenericPerson.createFrom($Create.Any);
const $$createType2 = $Create.Array($Create.Any); const $$createType2 = nobindingshere$0.personImpl.createFrom;
const $$createType3 = $Create.Array($Create.Any); const $$createType3 = $Create.Array($Create.Any);
const $$createType4 = $Create.Struct({ const $$createType4 = $Create.Array($Create.Any);
"NoMoreIdeas": $$createType3,
});
const $$createType5 = $Create.Struct({ const $$createType5 = $Create.Struct({
"Foo": $$createType2, "NoMoreIdeas": $$createType4,
"Other": $$createType4, });
const $$createType6 = $Create.Struct({
"Foo": $$createType3,
"Other": $$createType5,
}); });

View File

@ -7,4 +7,33 @@ export {
GreetService GreetService
}; };
export * from "./models.js"; export {
AliasedPerson,
EmptyStruct,
GenericPerson,
Person,
StrangelyAliasedPerson
} from "./models.js";
import * as $models from "./models.js";
/**
* A nice type Alias.
* @typedef {$models.Alias} Alias
*/
/**
* A struct alias.
* This should be rendered as a typedef or interface in every mode.
* @typedef {$models.AliasStruct} AliasStruct
*/
/**
* An empty struct alias.
* @typedef {$models.EmptyAliasStruct} EmptyAliasStruct
*/
/**
* Another struct alias.
* @typedef {$models.OtherAliasStruct} OtherAliasStruct
*/

View File

@ -1,166 +0,0 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import {Create as $Create} from "/wails/runtime.js";
/**
* A nice type Alias.
* @typedef {number} Alias
*/
/**
* A struct alias.
* This should be rendered as a typedef or interface in every mode.
* @typedef {Object} AliasStruct
* @property {number[]} Foo - A field with a comment.
* @property {string} [Bar] - Definitely not Foo.
* @property {string} [Baz] - Definitely not Foo.
* @property {OtherAliasStruct} Other - A nested alias struct.
*/
/**
* An empty struct alias.
* @typedef { {
* } } EmptyAliasStruct
*/
/**
* An empty struct.
*/
export class EmptyStruct {
/**
* Creates a new EmptyStruct instance.
* @param {Partial<EmptyStruct>} [$$source = {}] - The source object to create the EmptyStruct.
*/
constructor($$source = {}) {
Object.assign(this, $$source);
}
/**
* Creates a new EmptyStruct instance from a string or object.
* @param {any} [$$source = {}]
* @returns {EmptyStruct}
*/
static createFrom($$source = {}) {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new EmptyStruct(/** @type {Partial<EmptyStruct>} */($$parsedSource));
}
}
/**
* A generic struct containing an alias.
* @template T
*/
export class GenericPerson {
/**
* Creates a new GenericPerson instance.
* @param {Partial<GenericPerson<T>>} [$$source = {}] - The source object to create the GenericPerson.
*/
constructor($$source = {}) {
if (!("Name" in $$source)) {
/**
* @member
* @type {T | null}
*/
this["Name"] = null;
}
if (!("AliasedField" in $$source)) {
/**
* @member
* @type {Alias}
*/
this["AliasedField"] = 0;
}
Object.assign(this, $$source);
}
/**
* Given creation functions for each type parameter,
* returns a creation function for a concrete instance
* of the generic class GenericPerson.
* @template T
* @param {(source: any) => T} $$createParamT
* @returns {($$source?: any) => GenericPerson<T>}
*/
static createFrom($$createParamT) {
const $$createField0_0 = $$createParamT;
return ($$source = {}) => {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
if ("Name" in $$parsedSource) {
$$parsedSource["Name"] = $$createField0_0($$parsedSource["Name"]);
}
return new GenericPerson(/** @type {Partial<GenericPerson<T>>} */($$parsedSource));
};
}
}
/**
* Another struct alias.
* @typedef {Object} OtherAliasStruct
* @property {number[]} NoMoreIdeas
*/
/**
* A non-generic struct containing an alias.
*/
export class Person {
/**
* Creates a new Person instance.
* @param {Partial<Person>} [$$source = {}] - The source object to create the Person.
*/
constructor($$source = {}) {
if (!("Name" in $$source)) {
/**
* The Person's name.
* @member
* @type {string}
*/
this["Name"] = "";
}
if (!("AliasedField" in $$source)) {
/**
* A random alias field.
* @member
* @type {Alias}
*/
this["AliasedField"] = 0;
}
Object.assign(this, $$source);
}
/**
* Creates a new Person instance from a string or object.
* @param {any} [$$source = {}]
* @returns {Person}
*/
static createFrom($$source = {}) {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new Person(/** @type {Partial<Person>} */($$parsedSource));
}
}
/**
* A class alias.
*/
export const AliasedPerson = Person;
/**
* A class alias.
* @typedef {Person} AliasedPerson
*/
/**
* Another class alias, but ordered after its aliased class.
*/
export const StrangelyAliasedPerson = Person;
/**
* Another class alias, but ordered after its aliased class.
* @typedef {Person} StrangelyAliasedPerson
*/

View File

@ -2,33 +2,165 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT // This file is automatically generated. DO NOT EDIT
export { // eslint-disable-next-line @typescript-eslint/ban-ts-comment
AliasedPerson, // @ts-ignore: Unused imports
EmptyStruct, import {Create as $Create} from "/wails/runtime.js";
GenericPerson,
Person,
StrangelyAliasedPerson
} from "./internal.js";
import * as $models from "./internal.js";
/** /**
* A nice type Alias. * A nice type Alias.
* @typedef {$models.Alias} Alias * @typedef {number} Alias
*/ */
/** /**
* A struct alias. * A struct alias.
* This should be rendered as a typedef or interface in every mode. * This should be rendered as a typedef or interface in every mode.
* @typedef {$models.AliasStruct} AliasStruct * @typedef {Object} AliasStruct
* @property {number[]} Foo - A field with a comment.
* @property {string} [Bar] - Definitely not Foo.
* @property {string} [Baz] - Definitely not Foo.
* @property {OtherAliasStruct} Other - A nested alias struct.
*/ */
/** /**
* An empty struct alias. * An empty struct alias.
* @typedef {$models.EmptyAliasStruct} EmptyAliasStruct * @typedef { {
* } } EmptyAliasStruct
*/ */
/** /**
* Another struct alias. * An empty struct.
* @typedef {$models.OtherAliasStruct} OtherAliasStruct */
export class EmptyStruct {
/**
* Creates a new EmptyStruct instance.
* @param {Partial<EmptyStruct>} [$$source = {}] - The source object to create the EmptyStruct.
*/
constructor($$source = {}) {
Object.assign(this, $$source);
}
/**
* Creates a new EmptyStruct instance from a string or object.
* @param {any} [$$source = {}]
* @returns {EmptyStruct}
*/
static createFrom($$source = {}) {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new EmptyStruct(/** @type {Partial<EmptyStruct>} */($$parsedSource));
}
}
/**
* A generic struct containing an alias.
* @template T
*/
export class GenericPerson {
/**
* Creates a new GenericPerson instance.
* @param {Partial<GenericPerson<T>>} [$$source = {}] - The source object to create the GenericPerson.
*/
constructor($$source = {}) {
if (/** @type {any} */(false)) {
/**
* @member
* @type {T | undefined}
*/
this["Name"] = undefined;
}
if (!("AliasedField" in $$source)) {
/**
* @member
* @type {Alias}
*/
this["AliasedField"] = 0;
}
Object.assign(this, $$source);
}
/**
* Given creation functions for each type parameter,
* returns a creation function for a concrete instance
* of the generic class GenericPerson.
* @template T
* @param {(source: any) => T} $$createParamT
* @returns {($$source?: any) => GenericPerson<T>}
*/
static createFrom($$createParamT) {
const $$createField0_0 = $$createParamT;
return ($$source = {}) => {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
if ("Name" in $$parsedSource) {
$$parsedSource["Name"] = $$createField0_0($$parsedSource["Name"]);
}
return new GenericPerson(/** @type {Partial<GenericPerson<T>>} */($$parsedSource));
};
}
}
/**
* Another struct alias.
* @typedef {Object} OtherAliasStruct
* @property {number[]} NoMoreIdeas
*/
/**
* A non-generic struct containing an alias.
*/
export class Person {
/**
* Creates a new Person instance.
* @param {Partial<Person>} [$$source = {}] - The source object to create the Person.
*/
constructor($$source = {}) {
if (!("Name" in $$source)) {
/**
* The Person's name.
* @member
* @type {string}
*/
this["Name"] = "";
}
if (!("AliasedField" in $$source)) {
/**
* A random alias field.
* @member
* @type {Alias}
*/
this["AliasedField"] = 0;
}
Object.assign(this, $$source);
}
/**
* Creates a new Person instance from a string or object.
* @param {any} [$$source = {}]
* @returns {Person}
*/
static createFrom($$source = {}) {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new Person(/** @type {Partial<Person>} */($$parsedSource));
}
}
/**
* A class alias.
*/
export const AliasedPerson = Person;
/**
* A class alias.
* @typedef {Person} AliasedPerson
*/
/**
* Another class alias, but ordered after its aliased class.
*/
export const StrangelyAliasedPerson = Person;
/**
* Another class alias, but ordered after its aliased class.
* @typedef {Person} StrangelyAliasedPerson
*/ */

View File

@ -13,7 +13,7 @@ import {Call as $Call, Create as $Create} from "/wails/runtime.js";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports // @ts-ignore: Unused imports
import * as $models from "./internal.js"; import * as $models from "./models.js";
/** /**
* Greet does XYZ * Greet does XYZ

View File

@ -7,4 +7,14 @@ export {
GreetService GreetService
}; };
export * from "./models.js"; export {
Embedded1,
Person,
Title
} from "./models.js";
import * as $models from "./models.js";
/**
* @typedef {$models.Embedded3} Embedded3
*/

View File

@ -1,272 +0,0 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import {Create as $Create} from "/wails/runtime.js";
export class Embedded1 {
/**
* Creates a new Embedded1 instance.
* @param {Partial<Embedded1>} [$$source = {}] - The source object to create the Embedded1.
*/
constructor($$source = {}) {
if (!("Friends" in $$source)) {
/**
* Friends should be shadowed in Person by a field of lesser depth
* @member
* @type {number}
*/
this["Friends"] = 0;
}
if (!("Vanish" in $$source)) {
/**
* Vanish should be omitted from Person because there is another field with same depth and no tag
* @member
* @type {number}
*/
this["Vanish"] = 0;
}
if (!("StillThere" in $$source)) {
/**
* StillThere should be shadowed in Person by other field with same depth and a json tag
* @member
* @type {string}
*/
this["StillThere"] = "";
}
if (!("NamingThingsIsHard" in $$source)) {
/**
* NamingThingsIsHard is a law of programming
* @member
* @type {`${boolean}`}
*/
this["NamingThingsIsHard"] = "false";
}
Object.assign(this, $$source);
}
/**
* Creates a new Embedded1 instance from a string or object.
* @param {any} [$$source = {}]
* @returns {Embedded1}
*/
static createFrom($$source = {}) {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new Embedded1(/** @type {Partial<Embedded1>} */($$parsedSource));
}
}
/**
* @typedef {string} Embedded3
*/
/**
* Person represents a person
*/
export class Person {
/**
* Creates a new Person instance.
* @param {Partial<Person>} [$$source = {}] - The source object to create the Person.
*/
constructor($$source = {}) {
if (/** @type {any} */(false)) {
/**
* Titles is optional in JSON
* @member
* @type {Title[] | undefined}
*/
this["Titles"] = [];
}
if (!("Names" in $$source)) {
/**
* Names has a
* multiline comment
* @member
* @type {string[]}
*/
this["Names"] = [];
}
if (!("Partner" in $$source)) {
/**
* Partner has a custom and complex JSON key
* @member
* @type {Person | null}
*/
this["Partner"] = null;
}
if (!("Friends" in $$source)) {
/**
* @member
* @type {(Person | null)[]}
*/
this["Friends"] = [];
}
if (!("NamingThingsIsHard" in $$source)) {
/**
* NamingThingsIsHard is a law of programming
* @member
* @type {`${boolean}`}
*/
this["NamingThingsIsHard"] = "false";
}
if (!("StillThere" in $$source)) {
/**
* StillThereButRenamed should shadow in Person the other field with same depth and no json tag
* @member
* @type {Embedded3 | null}
*/
this["StillThere"] = null;
}
if (!("-" in $$source)) {
/**
* StrangeNumber maps to "-"
* @member
* @type {number}
*/
this["-"] = 0;
}
if (!("Embedded3" in $$source)) {
/**
* Embedded3 should appear with key "Embedded3"
* @member
* @type {Embedded3}
*/
this["Embedded3"] = (/** @type {Embedded3} */(""));
}
if (!("StrangerNumber" in $$source)) {
/**
* StrangerNumber is serialized as a string
* @member
* @type {`${number}`}
*/
this["StrangerNumber"] = "0";
}
if (/** @type {any} */(false)) {
/**
* StrangestString is optional and serialized as a JSON string
* @member
* @type {`"${string}"` | undefined}
*/
this["StrangestString"] = '""';
}
if (/** @type {any} */(false)) {
/**
* StringStrangest is serialized as a JSON string and optional
* @member
* @type {`"${string}"` | undefined}
*/
this["StringStrangest"] = '""';
}
if (/** @type {any} */(false)) {
/**
* embedded4 should be optional and appear with key "emb4"
* @member
* @type {embedded4 | undefined}
*/
this["emb4"] = (new embedded4());
}
Object.assign(this, $$source);
}
/**
* Creates a new Person instance from a string or object.
* @param {any} [$$source = {}]
* @returns {Person}
*/
static createFrom($$source = {}) {
const $$createField0_0 = $$createType0;
const $$createField1_0 = $$createType1;
const $$createField2_0 = $$createType3;
const $$createField3_0 = $$createType4;
const $$createField11_0 = $$createType5;
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
if ("Titles" in $$parsedSource) {
$$parsedSource["Titles"] = $$createField0_0($$parsedSource["Titles"]);
}
if ("Names" in $$parsedSource) {
$$parsedSource["Names"] = $$createField1_0($$parsedSource["Names"]);
}
if ("Partner" in $$parsedSource) {
$$parsedSource["Partner"] = $$createField2_0($$parsedSource["Partner"]);
}
if ("Friends" in $$parsedSource) {
$$parsedSource["Friends"] = $$createField3_0($$parsedSource["Friends"]);
}
if ("emb4" in $$parsedSource) {
$$parsedSource["emb4"] = $$createField11_0($$parsedSource["emb4"]);
}
return new Person(/** @type {Partial<Person>} */($$parsedSource));
}
}
/**
* Title is a title
* @readonly
* @enum {string}
*/
export const Title = {
/**
* The Go zero value for the underlying type of the enum.
*/
$zero: "",
/**
* Mister is a title
*/
Mister: "Mr",
Miss: "Miss",
Ms: "Ms",
Mrs: "Mrs",
Dr: "Dr",
};
export class embedded4 {
/**
* Creates a new embedded4 instance.
* @param {Partial<embedded4>} [$$source = {}] - The source object to create the embedded4.
*/
constructor($$source = {}) {
if (!("NamingThingsIsHard" in $$source)) {
/**
* NamingThingsIsHard is a law of programming
* @member
* @type {`${boolean}`}
*/
this["NamingThingsIsHard"] = "false";
}
if (!("Friends" in $$source)) {
/**
* Friends should not be shadowed in Person as embedded4 is not embedded
* from encoding/json's point of view;
* however, it should be shadowed in Embedded1
* @member
* @type {boolean}
*/
this["Friends"] = false;
}
Object.assign(this, $$source);
}
/**
* Creates a new embedded4 instance from a string or object.
* @param {any} [$$source = {}]
* @returns {embedded4}
*/
static createFrom($$source = {}) {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new embedded4(/** @type {Partial<embedded4>} */($$parsedSource));
}
}
// Private type creation functions
const $$createType0 = $Create.Array($Create.Any);
const $$createType1 = $Create.Array($Create.Any);
const $$createType2 = Person.createFrom;
const $$createType3 = $Create.Nullable($$createType2);
const $$createType4 = $Create.Array($$createType3);
const $$createType5 = embedded4.createFrom;

View File

@ -2,14 +2,271 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT // This file is automatically generated. DO NOT EDIT
export { // eslint-disable-next-line @typescript-eslint/ban-ts-comment
Embedded1, // @ts-ignore: Unused imports
Person, import {Create as $Create} from "/wails/runtime.js";
Title
} from "./internal.js";
import * as $models from "./internal.js"; export class Embedded1 {
/**
* Creates a new Embedded1 instance.
* @param {Partial<Embedded1>} [$$source = {}] - The source object to create the Embedded1.
*/
constructor($$source = {}) {
if (!("Friends" in $$source)) {
/**
* Friends should be shadowed in Person by a field of lesser depth
* @member
* @type {number}
*/
this["Friends"] = 0;
}
if (!("Vanish" in $$source)) {
/**
* Vanish should be omitted from Person because there is another field with same depth and no tag
* @member
* @type {number}
*/
this["Vanish"] = 0;
}
if (!("StillThere" in $$source)) {
/**
* StillThere should be shadowed in Person by other field with same depth and a json tag
* @member
* @type {string}
*/
this["StillThere"] = "";
}
if (!("NamingThingsIsHard" in $$source)) {
/**
* NamingThingsIsHard is a law of programming
* @member
* @type {`${boolean}`}
*/
this["NamingThingsIsHard"] = "false";
}
Object.assign(this, $$source);
}
/**
* Creates a new Embedded1 instance from a string or object.
* @param {any} [$$source = {}]
* @returns {Embedded1}
*/
static createFrom($$source = {}) {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new Embedded1(/** @type {Partial<Embedded1>} */($$parsedSource));
}
}
/** /**
* @typedef {$models.Embedded3} Embedded3 * @typedef {string} Embedded3
*/ */
/**
* Person represents a person
*/
export class Person {
/**
* Creates a new Person instance.
* @param {Partial<Person>} [$$source = {}] - The source object to create the Person.
*/
constructor($$source = {}) {
if (/** @type {any} */(false)) {
/**
* Titles is optional in JSON
* @member
* @type {Title[] | undefined}
*/
this["Titles"] = undefined;
}
if (!("Names" in $$source)) {
/**
* Names has a
* multiline comment
* @member
* @type {string[]}
*/
this["Names"] = [];
}
if (!("Partner" in $$source)) {
/**
* Partner has a custom and complex JSON key
* @member
* @type {Person | null}
*/
this["Partner"] = null;
}
if (!("Friends" in $$source)) {
/**
* @member
* @type {(Person | null)[]}
*/
this["Friends"] = [];
}
if (!("NamingThingsIsHard" in $$source)) {
/**
* NamingThingsIsHard is a law of programming
* @member
* @type {`${boolean}`}
*/
this["NamingThingsIsHard"] = "false";
}
if (!("StillThere" in $$source)) {
/**
* StillThereButRenamed should shadow in Person the other field with same depth and no json tag
* @member
* @type {Embedded3 | null}
*/
this["StillThere"] = null;
}
if (!("-" in $$source)) {
/**
* StrangeNumber maps to "-"
* @member
* @type {number}
*/
this["-"] = 0;
}
if (!("Embedded3" in $$source)) {
/**
* Embedded3 should appear with key "Embedded3"
* @member
* @type {Embedded3}
*/
this["Embedded3"] = "";
}
if (!("StrangerNumber" in $$source)) {
/**
* StrangerNumber is serialized as a string
* @member
* @type {`${number}`}
*/
this["StrangerNumber"] = "0";
}
if (/** @type {any} */(false)) {
/**
* StrangestString is optional and serialized as a JSON string
* @member
* @type {`"${string}"` | undefined}
*/
this["StrangestString"] = undefined;
}
if (/** @type {any} */(false)) {
/**
* StringStrangest is serialized as a JSON string and optional
* @member
* @type {`"${string}"` | undefined}
*/
this["StringStrangest"] = undefined;
}
if (/** @type {any} */(false)) {
/**
* embedded4 should be optional and appear with key "emb4"
* @member
* @type {embedded4 | undefined}
*/
this["emb4"] = undefined;
}
Object.assign(this, $$source);
}
/**
* Creates a new Person instance from a string or object.
* @param {any} [$$source = {}]
* @returns {Person}
*/
static createFrom($$source = {}) {
const $$createField0_0 = $$createType0;
const $$createField1_0 = $$createType1;
const $$createField2_0 = $$createType3;
const $$createField3_0 = $$createType4;
const $$createField11_0 = $$createType5;
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
if ("Titles" in $$parsedSource) {
$$parsedSource["Titles"] = $$createField0_0($$parsedSource["Titles"]);
}
if ("Names" in $$parsedSource) {
$$parsedSource["Names"] = $$createField1_0($$parsedSource["Names"]);
}
if ("Partner" in $$parsedSource) {
$$parsedSource["Partner"] = $$createField2_0($$parsedSource["Partner"]);
}
if ("Friends" in $$parsedSource) {
$$parsedSource["Friends"] = $$createField3_0($$parsedSource["Friends"]);
}
if ("emb4" in $$parsedSource) {
$$parsedSource["emb4"] = $$createField11_0($$parsedSource["emb4"]);
}
return new Person(/** @type {Partial<Person>} */($$parsedSource));
}
}
/**
* Title is a title
* @readonly
* @enum {string}
*/
export const Title = {
/**
* The Go zero value for the underlying type of the enum.
*/
$zero: "",
/**
* Mister is a title
*/
Mister: "Mr",
Miss: "Miss",
Ms: "Ms",
Mrs: "Mrs",
Dr: "Dr",
};
export class embedded4 {
/**
* Creates a new embedded4 instance.
* @param {Partial<embedded4>} [$$source = {}] - The source object to create the embedded4.
*/
constructor($$source = {}) {
if (!("NamingThingsIsHard" in $$source)) {
/**
* NamingThingsIsHard is a law of programming
* @member
* @type {`${boolean}`}
*/
this["NamingThingsIsHard"] = "false";
}
if (!("Friends" in $$source)) {
/**
* Friends should not be shadowed in Person as embedded4 is not embedded
* from encoding/json's point of view;
* however, it should be shadowed in Embedded1
* @member
* @type {boolean}
*/
this["Friends"] = false;
}
Object.assign(this, $$source);
}
/**
* Creates a new embedded4 instance from a string or object.
* @param {any} [$$source = {}]
* @returns {embedded4}
*/
static createFrom($$source = {}) {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new embedded4(/** @type {Partial<embedded4>} */($$parsedSource));
}
}
// Private type creation functions
const $$createType0 = $Create.Array($Create.Any);
const $$createType1 = $Create.Array($Create.Any);
const $$createType2 = Person.createFrom;
const $$createType3 = $Create.Nullable($$createType2);
const $$createType4 = $Create.Array($$createType3);
const $$createType5 = embedded4.createFrom;

View File

@ -13,7 +13,7 @@ import {Call as $Call, Create as $Create} from "/wails/runtime.js";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports // @ts-ignore: Unused imports
import * as $models from "./internal.js"; import * as $models from "./models.js";
/** /**
* Greet does XYZ * Greet does XYZ

View File

@ -7,4 +7,6 @@ export {
GreetService GreetService
}; };
export * from "./models.js"; export {
Person
} from "./models.js";

View File

@ -2,6 +2,37 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT // This file is automatically generated. DO NOT EDIT
export { // eslint-disable-next-line @typescript-eslint/ban-ts-comment
Person // @ts-ignore: Unused imports
} from "./internal.js"; import {Create as $Create} from "/wails/runtime.js";
/**
* Person represents a person
*/
export class Person {
/**
* Creates a new Person instance.
* @param {Partial<Person>} [$$source = {}] - The source object to create the Person.
*/
constructor($$source = {}) {
if (!("Name" in $$source)) {
/**
* @member
* @type {string}
*/
this["Name"] = "";
}
Object.assign(this, $$source);
}
/**
* Creates a new Person instance from a string or object.
* @param {any} [$$source = {}]
* @returns {Person}
*/
static createFrom($$source = {}) {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new Person(/** @type {Partial<Person>} */($$parsedSource));
}
}

Some files were not shown because too many files have changed in this diff Show More