5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-17 01:19:29 +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)
- 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)
- 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
@ -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 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 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
@ -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)
- Removed `nanoid` dependency 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

View File

@ -134,7 +134,6 @@ wails3 generate bindings [flags] [patterns...]
| `-f` | Additional Go build flags | |
| `-d` | Output directory | `frontend/bindings` |
| `-models` | Models filename | `models` |
| `-internal` | Internal filename | `internal` |
| `-index` | Index filename | `index` |
| `-ts` | Generate TypeScript | `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 Go struct and its methods:
```javascript
```javascript title="greetservice.js"
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// 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`
file containing the following code:
```javascript
```javascript title="models.js"
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// 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:
```javascript
import { Greet } from "./bindings/changeme/GreetService.js";
import { Greet } from "./bindings/changeme/greetservice.js";
import { Person } from "./bindings/changeme/models.js";
const resultElement = document.getElementById("result");
@ -291,6 +291,36 @@ the `Greet` method.
Using bound models allows you to work with complex data structures and
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
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.
2. **Window Information**: You can determine which window made the call using
these context keys:
- `application.WindowNameKey` - Returns the name of the calling window
- `application.WindowIDKey` - Returns the ID of the calling window
the context key `application.WindowKey`.
Here are some examples:
@ -403,9 +431,8 @@ func (s *MyService) LongRunningTask(ctx context.Context, input string) (string,
// Getting caller window information
func (s *MyService) WindowAwareMethod(ctx context.Context) (string, error) {
windowName := ctx.Value(application.WindowNameKey).(string)
windowID := ctx.Value(application.WindowIDKey).(string)
return fmt.Sprintf("Called from window: %s (ID: %s)", windowName, windowID), nil
window := ctx.Value(application.WindowKey).(application.Window)
return fmt.Sprintf("Called from window: %s (ID: %s)", window.Name(), window.ID()), nil
}
```

View File

@ -2,4 +2,6 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// 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
// This file is automatically generated. DO NOT EDIT
export {
Person
} from "./internal.js";
// 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

@ -1,22 +1,20 @@
module changeme
go 1.22
toolchain go1.22.0
go 1.23.4
require github.com/wailsapp/wails/v3 v3.0.0-alpha.0
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/ebitengine/purego v0.4.0-alpha.4 // 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/go-billy/v5 v5.5.0 // indirect
github.com/go-git/go-git/v5 v5.11.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-git/go-billy/v5 v5.6.2 // indirect
github.com/go-git/go-git/v5 v5.13.1 // indirect
github.com/go-ole/go-ole v1.3.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/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // 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/leaanthony/go-ansi-parser v1.6.1 // 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-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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/samber/lo v1.38.1 // indirect
github.com/sergi/go-diff v1.2.0 // indirect
github.com/stretchr/testify v1.8.4 // indirect
github.com/wailsapp/go-webview2 v1.0.9 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/stretchr/testify v1.10.0 // indirect
github.com/wailsapp/go-webview2 v1.0.19 // indirect
github.com/wailsapp/mimetype v1.4.1 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sys v0.29.0 // 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.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.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/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
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/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.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/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/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.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/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/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
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/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.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/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
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/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/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
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.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.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/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
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.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
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.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.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/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
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/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.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
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.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
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/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/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
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.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.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/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-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
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.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
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-20190507160741-ecd444e8653b/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-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.1.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/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.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.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=

View File

@ -1,27 +1,25 @@
module changeme
go 1.22.4
toolchain go1.23.0
go 1.23.4
require github.com/wailsapp/wails/v3 v3.0.0-alpha.7
require (
dario.cat/mergo v1.0.0 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
dario.cat/mergo v1.0.1 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.1.4 // indirect
github.com/bep/debounce v1.2.1 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/cloudflare/circl v1.5.0 // indirect
github.com/cyphar/filepath-securejoin v0.4.0 // indirect
github.com/ebitengine/purego v0.4.0-alpha.4 // 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/go-billy/v5 v5.5.0 // indirect
github.com/go-git/go-git/v5 v5.11.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-git/go-billy/v5 v5.6.2 // indirect
github.com/go-git/go-git/v5 v5.13.1 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/google/uuid v1.6.0 // 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/kevinburke/ssh_config v1.2.0 // indirect
@ -30,22 +28,22 @@ require (
github.com/lmittmann/tint v1.0.4 // indirect
github.com/mattn/go-colorable v0.1.13 // 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/rivo/uniseg v0.4.7 // indirect
github.com/samber/lo v1.38.1 // indirect
github.com/sergi/go-diff v1.2.0 // indirect
github.com/skeema/knownhosts v1.2.1 // indirect
github.com/wailsapp/go-webview2 v1.0.16 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/skeema/knownhosts v1.3.0 // indirect
github.com/wailsapp/go-webview2 v1.0.19 // indirect
github.com/wailsapp/mimetype v1.4.1 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/tools v0.29.0 // 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/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.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.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/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/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
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.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
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/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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
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/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.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/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/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/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/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/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/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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.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/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
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/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.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/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
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/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.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
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/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/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
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/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.19/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc=
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
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=
@ -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.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
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/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.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/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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
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.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
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-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.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
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-20190916202348-b4ddaad3f8a3/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-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.1.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.5.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/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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
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.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.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
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 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/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.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
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
// This file is automatically generated. DO NOT EDIT
import * as Hashes from "./hashes.js";
import * as Service from "./service.js";
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
import {Create as $Create} from "/wails/runtime.js";
export class Hashes {
/**
* Creates a new Hashes instance.
* @param {Partial<Hashes>} [$$source = {}] - The source object to create the Hashes.
*/
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));
}
}
/**
* @typedef {Object} Hashes
* @property {string} md5
* @property {string} sha1
* @property {string} sha256
*/

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;
}
/**
* 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
* @returns {Promise<void> & { cancel(): void }}

View File

@ -40,16 +40,6 @@ export function Info(message, ...args) {
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
* @returns {Promise<void> & { cancel(): void }}

View File

@ -24,16 +24,6 @@ export function Execute(query, ...args) {
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
* @returns {Promise<string> & { cancel(): void }}
@ -57,16 +47,6 @@ export function Select(query, ...args) {
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
const $$createType0 = $Create.Map($Create.Any, $Create.Any);
const $$createType1 = $Create.Array($$createType0);

View File

@ -2,4 +2,10 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// 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 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 * 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() {
let hashstring = document.getElementById("hashstring").value;

View File

@ -6,16 +6,19 @@ import (
"crypto/sha1"
"crypto/sha256"
"encoding/hex"
"github.com/wailsapp/wails/v3/pkg/application"
)
type Hashes struct {
type Hashes = struct {
MD5 string `json:"md5"`
SHA1 string `json:"sha1"`
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))
sha1Hash := sha1.Sum([]byte(s))
sha256Hash := sha256.Sum256([]byte(s))
@ -27,16 +30,16 @@ func (h *Hashes) Generate(s string) Hashes {
}
}
func New() *Hashes {
return &Hashes{}
func New() *Service {
return &Service{}
}
func (h *Hashes) ServiceShutdown() error { return nil }
func (h *Hashes) ServiceName() string {
func (h *Service) ServiceName() string {
return "Hashes Service"
}
func (h *Hashes) ServiceStartup(_ context.Context, _ application.ServiceOptions) error {
func (h *Service) ServiceStartup(context.Context, application.ServiceOptions) error {
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
golang.org/x/crypto v0.32.0 // 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/mod v0.22.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/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/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.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
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"`
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"`
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"`
TS bool `name:"ts" description:"Generate Typescript bindings"`
UseInterfaces bool `name:"i" description:"Generate Typescript interfaces instead of classes"`

View File

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

View File

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

View File

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

View File

@ -3,6 +3,8 @@ package collect
import (
"go/types"
"path/filepath"
"golang.org/x/tools/go/types/typeutil"
)
type (
@ -120,26 +122,26 @@ func (imports *ImportMap) Add(pkg *PackageInfo) {
// AddType does not support unsynchronised concurrent calls
// on the same receiver.
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.
// 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
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.
switch t := typ.(type) {
case *types.Alias, *types.Named:
obj := typ.(interface{ Obj() *types.TypeName }).Obj()
if visited[obj] {
if visited.Set(typ, true) != nil {
// Break type cycles.
return
}
visited[obj] = true
obj := typ.(interface{ Obj() *types.TypeName }).Obj()
if obj.Pkg() == nil {
// Ignore universe type.
return
@ -155,7 +157,7 @@ func (imports *ImportMap) addTypeImpl(typ types.Type, visited map[*types.TypeNam
// Import parent package.
imports.Add(collector.Package(obj.Pkg()))
instance, _ := t.(interface{ TypeArgs() *types.TypeList })
instance, _ := typ.(interface{ TypeArgs() *types.TypeList })
if instance != nil {
// Record type argument dependencies.
if targs := instance.TypeArgs(); targs != nil {
@ -170,25 +172,29 @@ func (imports *ImportMap) addTypeImpl(typ types.Type, visited map[*types.TypeNam
return
}
if _, isAlias := t.(*types.Alias); isAlias {
if _, isAlias := typ.(*types.Alias); isAlias {
// Aliased type might be needed during
// JS value creation and initialisation.
typ = types.Unalias(typ)
break
}
if IsClass(typ) || IsString(typ) || IsAny(typ) {
if IsClass(typ) || IsAny(typ) || IsStringAlias(typ) {
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.
typ = typ.Underlying()
case *types.Basic:
if t.Info()&types.IsComplex != 0 {
// Complex types are not supported by encoding/json
collector.logger.Warningf("complex types are not supported by encoding/json")
switch {
case t.Info()&(types.IsBoolean|types.IsInteger|types.IsUnsigned|types.IsFloat|types.IsString) != 0:
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
@ -196,32 +202,40 @@ func (imports *ImportMap) addTypeImpl(typ types.Type, visited map[*types.TypeNam
typ = typ.(interface{ Elem() types.Type }).Elem()
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
case *types.Map:
if IsMapKey(t.Key()) {
if IsString(t.Key()) {
if IsStringAlias(t.Key()) {
// 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.
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 {
collector.logger.Warningf(
"%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),
"package %s: type %s is used as a map key, but does not implement encoding.TextMarshaler: this will likely result in runtime errors",
imports.Self, types.TypeString(t.Key(), nil),
)
}
typ = t.Elem()
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
case *types.Struct:
if t.NumFields() == 0 {
// Empty struct.
if t.NumFields() == 0 || MaybeJSONMarshaler(typ) != NonMarshaler || MaybeTextMarshaler(typ) != NonMarshaler {
// Struct is empty, or marshals to custom JSON (any) or string.
return
}
@ -246,7 +260,7 @@ func (imports *ImportMap) addTypeImpl(typ types.Type, visited map[*types.TypeNam
return
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
}
}

View File

@ -10,13 +10,13 @@ import (
//
// When obtained through a call to [PackageInfo.Index],
// each service and model appears at most once;
// services are sorted by name;
// exported models precede all unexported ones
// and both ranges are sorted by name.
// services and models are sorted
// by internal property (all exported first), then by name.
type PackageIndex struct {
Package *PackageInfo
Services []*ServiceInfo
Services []*ServiceInfo
HasExportedServices bool // If true, there is at least one exported service.
Models []*ModelInfo
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 {
service := value.(*ServiceInfo)
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.
stats.NumServices++
@ -57,12 +58,21 @@ func (info *PackageInfo) Index(TS bool) (index *PackageIndex) {
}
}
// Sort services by name.
slices.SortFunc(index.Services, func(b1 *ServiceInfo, b2 *ServiceInfo) int {
if b1 == b2 {
// Sort services by internal property (exported first), then by name.
slices.SortFunc(index.Services, func(s1 *ServiceInfo, s2 *ServiceInfo) int {
if s1 == s2 {
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.
@ -70,7 +80,7 @@ func (info *PackageInfo) Index(TS bool) (index *PackageIndex) {
model := value.(*ModelInfo)
index.Models = append(index.Models, model)
// Mark presence of exported models
if model.Object().Exported() {
if !model.Internal {
index.HasExportedModels = true
}
// 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 {
if m1 == m2 {
return 0
}
m1e, m2e := m1.Object().Exported(), m2.Object().Exported()
if m1e != m2e {
if m1e {
return -1
} else {
if m1.Internal != m2.Internal {
if m1.Internal {
return 1
} else {
return -1
}
}
@ -108,5 +117,5 @@ func (info *PackageInfo) Index(TS bool) (index *PackageIndex) {
// IsEmpty returns true if the given index
// contains no data for the selected language.
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 (
"cmp"
"go/ast"
"go/constant"
"go/types"
"slices"
@ -20,6 +21,10 @@ type (
ModelInfo struct {
*TypeInfo
// Internal records whether the model
// should be exported by the index file.
Internal bool
// Imports records dependencies for this model.
Imports *ImportMap
@ -39,6 +44,13 @@ type (
// TypeParams records type parameter names for generic models.
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
once sync.Once
}
@ -49,6 +61,19 @@ type (
*StructField
*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 {
@ -113,15 +138,24 @@ func (info *ModelInfo) Collect() *ModelInfo {
// Setup fallback type.
info.Type = types.Universe.Lookup("any").Type()
// Retrieve type denotation and skip alias chains.
def := info.TypeInfo.Def
// Record whether the model should be exported.
info.Internal = !obj.Exported()
// Check marshalers and detect enums.
var constants []*types.Const
// Parse directives.
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
}
}
}
// Record type parameter names.
var isGeneric bool
if generic, ok := obj.Type().(interface{ TypeParams() *types.TypeParamList }); ok {
// Record type parameter names.
if generic, ok := typ.(interface{ TypeParams() *types.TypeParamList }); ok {
tparams := generic.TypeParams()
isGeneric = tparams != nil
@ -133,59 +167,116 @@ func (info *ModelInfo) Collect() *ModelInfo {
}
}
if _, isNamed := obj.Type().(*types.Named); isNamed {
// Model is a named type.
// Check whether it implements marshaler interfaces
// or has defined constants.
// Precompute predicates.
// Preinstantiate typ to avoid repeated instantiations in predicate code.
ityp := instantiate(typ)
info.Predicates = Predicates{
IsJSONMarshaler: IsJSONMarshaler(ityp),
MaybeJSONMarshaler: MaybeJSONMarshaler(ityp),
IsTextMarshaler: IsTextMarshaler(ityp),
MaybeTextMarshaler: MaybeTextMarshaler(ityp),
IsMapKey: IsMapKey(ityp),
IsTypeParam: IsTypeParam(ityp),
IsStringAlias: IsStringAlias(ityp),
IsClass: IsClass(ityp),
IsAny: IsAny(ityp),
}
if IsAny(typ) {
// Type marshals to a custom value of unknown shape.
return
} else if MaybeTextMarshaler(typ) {
// Type marshals to a custom string of unknown shape.
info.Type = types.Typ[types.String]
return
} else if isGeneric && !collector.options.UseInterfaces && IsClass(typ) {
// 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()
}
var def types.Type
var constants []*types.Const
// Test for enums (excluding generic types).
basic, ok := typ.Underlying().(*types.Basic)
if ok && !isGeneric && basic.Info()&types.IsConstType != 0 && basic.Info()&types.IsComplex == 0 {
// Named type is defined as a representable constant type:
// look for defined constants of that named type.
for _, name := range obj.Pkg().Scope().Names() {
if cnst, ok := obj.Pkg().Scope().Lookup(name).(*types.Const); ok {
if cnst.Val().Kind() != constant.Unknown && types.Identical(cnst.Type(), typ) {
constants = append(constants, cnst)
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:
// look for defined constants of that named type.
for _, name := range obj.Pkg().Scope().Names() {
if cnst, ok := obj.Pkg().Scope().Lookup(name).(*types.Const); ok {
if cnst.Val().Kind() != constant.Unknown && types.Identical(cnst.Type(), typ) {
constants = append(constants, cnst)
}
}
}
}
}
default:
panic("model has unknown object kind (neither alias nor named type)")
}
// Handle struct types.
strct, isStruct := def.(*types.Struct)
if isStruct && info.Predicates.MaybeJSONMarshaler == NonMarshaler && info.Predicates.MaybeTextMarshaler == NonMarshaler {
// Def is struct and model is not a marshaler:
// collect information about struct fields.
info.collectStruct(strct)
info.Type = nil
return
}
// Record required imports.
info.Imports.AddType(def)
// Handle enum types.
// constants slice is always empty for aliases.
// constants slice is always empty for structs, marshalers.
if len(constants) > 0 {
// Collect information about enum values.
info.collectEnum(constants)
info.Type = def
return
}
// Handle struct types.
strct, isStruct := def.(*types.Struct)
if isStruct {
// Collect information about struct fields.
info.collectStruct(strct)
info.Type = nil
return
}
// That's all, folks. Render as a TS alias.
@ -265,6 +356,9 @@ func (info *ModelInfo) collectStruct(strct *types.Struct) {
// Collect fields.
for i, field := range structInfo.Fields {
// Record required imports.
info.Imports.AddType(field.Type)
fields[i] = &ModelFieldInfo{
StructField: field,
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 {
*TypeInfo
// Internal records whether the service
// should be exported by the index file.
Internal bool
Imports *ImportMap
Methods []*ServiceMethodInfo
// HasInternalMethods records whether the service
// defines lifecycle or http server methods.
HasInternalMethods bool
// Injections stores a list of JS code lines
// that should be injected into the generated file.
Injections []string
@ -80,7 +88,7 @@ func (collector *Collector) Service(obj *types.TypeName) *ServiceInfo {
func (info *ServiceInfo) IsEmpty() bool {
// Ensure information has been collected.
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.
@ -119,7 +127,11 @@ func (info *ServiceInfo) Collect() *ServiceInfo {
// Collect method information.
info.Methods = make([]*ServiceMethodInfo, 0, len(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.
continue
}
@ -130,13 +142,20 @@ func (info *ServiceInfo) Collect() *ServiceInfo {
}
}
// Record whether the service should be exported.
info.Internal = !obj.Exported()
// Parse directives.
for _, doc := range []*ast.CommentGroup{info.Doc, info.Decl.Doc} {
if doc == nil {
continue
}
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.
line, cond, err := ParseCondition(ParseDirective(comment.Text, "inject"))
if err != nil {
@ -217,6 +236,9 @@ func (info *ServiceInfo) collectMethod(method *types.Func) *ServiceMethodInfo {
for _, comment := range methodInfo.Doc.List {
switch {
case IsDirective(comment.Text, "ignore"):
return nil
case IsDirective(comment.Text, "internal"):
methodInfo.Internal = true

View File

@ -193,6 +193,14 @@ func (info *StructInfo) Collect() *StructInfo {
// or field is not structure:
// 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{
StructField: &StructField{
JsonName: name,
@ -317,7 +325,7 @@ func parseTag(tag string) (name string, optional bool, quoted bool, visible bool
for _, option := range parts[1:] {
switch option {
case "omitempty":
case "omitempty", "omitzero":
optional = true
case "string":
quoted = true

View File

@ -22,12 +22,6 @@ type TypeInfo struct {
Doc *ast.CommentGroup
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
node ast.Node
@ -92,13 +86,8 @@ func (info *TypeInfo) Collect() *TypeInfo {
info.Name,
)
// Provide dummy group and def.
// Provide dummy group.
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
}
@ -117,8 +106,6 @@ func (info *TypeInfo) Collect() *TypeInfo {
info.Decl = collector.fromCache(path[1]).(*GroupInfo).Collect()
info.Def = collector.Package(info.obj.Pkg()).TypesInfo.TypeOf(tspec.Type)
info.node = path[0]
})

View File

@ -209,13 +209,7 @@ func (generator *Generator) generateModelsIndexIncludes(info *collect.PackageInf
if len(index.Models) > 0 {
generator.scheduler.Schedule(func() {
generator.generateTypedefs(info, index.Models)
})
}
if index.HasExportedModels {
generator.scheduler.Schedule(func() {
generator.generateModels(index)
generator.generateModels(info, index.Models)
})
}
@ -236,29 +230,17 @@ func (generator *Generator) validateFileNames() error {
case generator.options.ModelsFilename == "":
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 == "":
return fmt.Errorf("package index filename must not be empty")
case generator.options.ModelsFilename != strings.ToLower(generator.options.ModelsFilename):
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):
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:
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

View File

@ -3,10 +3,12 @@ package generator
import (
"errors"
"fmt"
"github.com/wailsapp/wails/v3/internal/generator/render"
"io"
"io/fs"
"os"
"path/filepath"
"slices"
"strings"
"sync"
"testing"
@ -37,9 +39,8 @@ func TestGenerator(t *testing.T) {
tests := make([]*testParams, 1<<3)
for i := range tests {
options := &flags.GenerateBindingsOptions{
ModelsFilename: "models",
InternalFilename: "internal",
IndexFilename: "index",
ModelsFilename: "models",
IndexFilename: "index",
UseBundledRuntime: true,
@ -89,9 +90,11 @@ func TestGenerator(t *testing.T) {
// Run tests.
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
creator := outputCreator(t, test)
generator := NewGenerator(
test.options,
outputCreator(t, test),
creator,
config.DefaultPtermLogger(nil),
)
@ -102,6 +105,22 @@ func TestGenerator(t *testing.T) {
} else if report.HasWarnings() {
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 {
t.Error(err)
}

View File

@ -26,16 +26,6 @@ func (generator *Generator) generateIncludes(index *collect.PackageIndex) {
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():
if !generator.options.NoIndex && !index.IsEmpty() {
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)
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)
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)
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)
if err != nil {

View File

@ -6,19 +6,34 @@ import (
"github.com/wailsapp/wails/v3/internal/generator/collect"
)
// generateModels generates a file for exported models from the given index information.
func (generator *Generator) generateModels(index *collect.PackageIndex) {
file, err := generator.creator.Create(filepath.Join(index.Package.Path, generator.renderer.ModelsFile()))
// generateModels generates a JS/TS models file for the given list of models.
// A call to info.Collect must complete before entering generateModels.
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 {
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
}
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 {
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"
"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.
@ -21,31 +22,34 @@ func (m *module) SkipCreate(ts []types.Type) bool {
// NeedsCreate returns true if the given type needs some creation code.
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.
// 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) {
case *types.Alias, *types.Named:
obj := typ.(interface{ Obj() *types.TypeName }).Obj()
if visited[obj] {
case *types.Alias:
return m.needsCreateImpl(types.Unalias(typ), visited)
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
}
visited[obj] = true
if obj.Pkg() == nil {
// Builtin alias or named type: render underlying type.
if t.Obj().Pkg() == nil {
// Builtin named type: render underlying type.
return m.needsCreateImpl(t.Underlying(), visited)
}
if collect.IsAny(t) || collect.IsString(t) {
if collect.IsAny(typ) || collect.IsStringAlias(typ) {
break
} else if collect.IsClass(t) {
} else if collect.IsClass(typ) {
return true
} else if _, isAlias := typ.(*types.Alias); isAlias {
return m.needsCreateImpl(types.Unalias(t), visited)
} else {
return m.needsCreateImpl(t.Underlying(), visited)
}
@ -57,6 +61,10 @@ func (m *module) needsCreateImpl(typ types.Type, visited map[*types.TypeName]boo
return true
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.Collect()
@ -114,7 +122,6 @@ func (m *module) JSCreateWithParams(typ types.Type, params string) string {
case *types.Map:
pp, ok := m.postponedCreates.At(typ).(*postponed)
if !ok {
m.JSCreateWithParams(t.Key(), params)
m.JSCreateWithParams(t.Elem(), params)
pp = &postponed{m.postponedCreates.Len(), params}
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)
}
if collect.IsAny(typ) || collect.IsString(typ) || !m.NeedsCreate(typ) {
if !m.NeedsCreate(typ) {
break
}
@ -166,6 +173,10 @@ func (m *module) JSCreateWithParams(typ types.Type, params string) string {
return fmt.Sprintf("$$createType%d%s", pp.index, params)
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)
if ok {
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))
case *types.Map:
result[pp.index] = fmt.Sprintf(
"%s$Create.Map(%s, %s)",
pre,
m.JSCreateWithParams(t.Key(), pp.params),
m.JSCreateWithParams(t.Elem(), pp.params),
)
result[pp.index] = fmt.Sprintf("%s$Create.Map($Create.Any, %s)", pre, m.JSCreateWithParams(t.Elem(), pp.params))
case *types.Named:
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.
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:
return "{}"
case *types.Pointer:
return "null"
case *types.Struct:
return m.renderStructDefault(t)
case *types.TypeParam:
// Should be unreachable
panic("type parameters have no default value")
}
// Fall back to null.
@ -102,8 +103,8 @@ func (m *module) renderNamedDefault(typ aliasOrNamed, quoted bool) (result strin
}
if quoted {
// WARN: Do not test with IsString here!! We only want to catch marshalers.
if !collect.IsAny(typ) && !collect.MaybeTextMarshaler(typ) {
// WARN: Do not test with IsAny/IsStringAlias here!! We only want to catch marshalers.
if collect.MaybeJSONMarshaler(typ) == collect.NonMarshaler && collect.MaybeTextMarshaler(typ) == collect.NonMarshaler {
if basic, ok := typ.Underlying().(*types.Basic); ok {
// Quoted mode for basic alias/named type that is not a marshaler: delegate.
return m.renderBasicDefault(basic, quoted), true
@ -119,9 +120,9 @@ func (m *module) renderNamedDefault(typ aliasOrNamed, quoted bool) (result strin
if collect.IsAny(typ) {
return "", false
} else if collect.MaybeTextMarshaler(typ) {
} else if collect.MaybeTextMarshaler(typ) != collect.NonMarshaler {
return `""`, true
} else if collect.IsClass(typ) {
} else if collect.IsClass(typ) && !istpalias(typ) {
if typ.Obj().Pkg().Path() == m.Imports.Self {
return fmt.Sprintf("(new %s%s())", prefix, jsid(typ.Obj().Name())), true
} else {
@ -129,29 +130,26 @@ func (m *module) renderNamedDefault(typ aliasOrNamed, quoted bool) (result strin
}
} else if _, isAlias := typ.(*types.Alias); isAlias {
return m.JSDefault(types.Unalias(typ), quoted), true
} else {
// 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)
} else if len(m.collector.Model(typ.Obj()).Collect().Values) > 0 {
if typ.Obj().Pkg().Path() == m.Imports.Self {
if m.TS {
return fmt.Sprintf("(%s as %s%s)", value, prefix, jsid(typ.Obj().Name())), true
} else {
return fmt.Sprintf("(/** @type {%s%s} */(%s))", prefix, jsid(typ.Obj().Name()), value), true
}
return fmt.Sprintf("%s%s.$zero", prefix, jsid(typ.Obj().Name())), true
} else {
if m.TS {
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
}
return fmt.Sprintf("%s.%s.$zero", jsimport(m.Imports.External[typ.Obj().Pkg().Path()]), jsid(typ.Obj().Name())), true
}
} else {
return m.JSDefault(typ.Underlying(), quoted), true
}
}
// renderStructDefault outputs the Javascript representation
// of the zero value for the given struct type.
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.Collect()

View File

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

View File

@ -2,6 +2,7 @@ package render
import (
"fmt"
"go/types"
"math/big"
"strconv"
"strings"
@ -15,16 +16,18 @@ import (
var tmplFunctions = template.FuncMap{
"fixext": fixext,
"hasdoc": hasdoc,
"isclass": collect.IsClass,
"isjsdocid": isjsdocid,
"isjsdocobj": isjsdocobj,
"istpalias": istpalias,
"jsdoc": jsdoc,
"jsdocline": jsdocline,
"jsid": jsid,
"jsimport": jsimport,
"jsparam": jsparam,
"jsvalue": jsvalue,
"modelinfo": modelinfo,
"typeparam": typeparam,
"unalias": types.Unalias,
}
// fixext replaces a *.ts extension with *.js in the given string.
@ -92,3 +95,13 @@ func jsvalue(value any) string {
// Fall back to undefined.
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,
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
}
// 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
// with the appropriate extension.
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.
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 {
// Sort class aliases after the class they alias.
// 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)
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].Type is guaranteed to be
// 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.
func (renderer *Renderer) Index(w io.Writer, index *collect.PackageIndex) error {
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")),
}
var tmplTypedefs = map[tmplLanguage]*template.Template{
tmplJS: template.Must(template.New("internal.js.tmpl").Funcs(tmplFunctions).ParseFS(templates, "templates/internal.js.tmpl")),
tmplTS: template.Must(template.New("internal.ts.tmpl").Funcs(tmplFunctions).ParseFS(templates, "templates/internal.ts.tmpl")),
var tmplModels = map[tmplLanguage]*template.Template{
tmplJS: template.Must(template.New("models.js.tmpl").Funcs(tmplFunctions).ParseFS(templates, "templates/models.js.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 newline string
var Newline string
func init() {
var builder strings.Builder
@ -37,5 +35,5 @@ func init() {
panic(err)
}
newline = builder.String()
Newline = builder.String()
}

View File

@ -1,4 +1,5 @@
{{$renderer := .}}
{{- $useInterfaces := .UseInterfaces}}
{{- $models := (fixext .ModelsFile)}}
{{- if not .TS -}}
// @ts-check
@ -18,19 +19,83 @@
*/
{{end}}
{{- if .Services}}
{{- range .Services}}
{{- range .Services}}{{if .Internal}}{{break}}{{end}}
import * as {{jsid .Name}} from "./{{js (fixext ($renderer.ServiceFile .Name))}}";
{{- end}}
export {
{{- range $i, $service := .Services}}
{{- if .Internal}}{{break}}{{end}}
{{- if gt $i 0}},{{end}}
{{jsid .Name}}
{{- 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}}
{{- 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}}
{{.}}
{{- end}}{{if .Package.Injections}}

View File

@ -19,34 +19,10 @@ import * as {{jsimport .}} from "{{js .RelPath}}/{{js $models}}";
{{end}}
{{- range $model := .Models}}
{{- $isEnum := $model.Values}}
{{- $isClassAlias := and $model.Type (not $useInterfaces) (isclass $model.Type)}}
{{- $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)}}
{{- $info := modelinfo $model $useInterfaces }}
{{- $template := $info.Template }}
{{- /* Build type parameter list */}}
{{- $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 or $template.ParamList (hasdoc $model.Decl.Doc) (hasdoc $model.Doc) $info.IsEnum $info.IsTypeAlias $info.IsInterface}}
/**
{{- if hasdoc $model.Decl.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}}
{{- jsdoc $model.Doc.Text ""}}
{{- end}}
{{- if and $typeParamList (not $isClassAlias)}}
* @template {{$typeParams}}
{{- if and $template.ParamList (not $info.IsClassAlias)}}
* @template {{$template.Params}}
{{- end}}
{{- if $isEnum}}
{{- if $info.IsEnum}}
* @readonly
* @enum { {{- $module.JSType $model.Type -}} }
{{- else if $isTypeAlias}}
{{- else if $info.IsTypeAlias}}
* @typedef { {{- $module.JSType $model.Type -}} } {{jsid $model.Name}}
{{- else if $isInterface}}
{{- else if $info.IsInterface}}
{{- if isjsdocobj $model}}
* @typedef {Object} {{jsid $model.Name}}
{{- range $i, $decl := $model.Fields}}{{range $j, $field := $decl}}
@ -81,13 +57,22 @@ import * as {{jsimport .}} from "{{js .RelPath}}/{{js $models}}";
{{- 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}} = {
{{- if $info.IsEnum}}
/**
* The Go zero value for the underlying type of the enum.
*/
$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)}}
{{end}}
{{- 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}},
{{- end}}{{end}}{{end}}
};
{{else if $isClassAlias}}
export const {{jsid $model.Name}} = {{$module.JSType $model.Type.Origin}};
{{else if $info.IsClassAlias}}
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}}
@ -117,16 +106,16 @@ export const {{jsid $model.Name}} = {{$module.JSType $model.Type.Origin}};
{{- if hasdoc $model.Doc}}
{{- jsdoc $model.Doc.Text ""}}
{{- end}}
{{- if $typeParamList}}
* @template {{$typeParams}}
{{- if $template.ParamList}}
* @template {{$template.Params}}
{{- end}}
* @typedef { {{- $module.JSType $model.Type -}} } {{jsid $model.Name}}
*/
{{else if and $isClassOrInterface (not $isInterface)}}
{{else if and $info.IsClass}}
export class {{jsid $model.Name}} {
/**
* 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 = {}) {
{{- range $decl := $model.Fields}}{{range $j, $field := $decl}}
@ -144,9 +133,9 @@ export class {{jsid $model.Name}} {
{{- jsdoc $field.Decl.Doc.Text " "}}
{{- end}}
* @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}}
@ -154,31 +143,31 @@ export class {{jsid $model.Name}} {
}
/**
{{- if $typeParamList}}
{{- if $template.ParamList}}
* Given creation functions for each type parameter,
* returns a creation function for a concrete instance
* of the generic class {{jsid $model.Name}}.
* @template {{$typeParams}}
* @template {{$template.Params}}
{{- range $i, $param := $model.TypeParams}}
{{- $param = (typeparam $i $param)}}
* @param {(source: any) => {{$param -}} } $$createParam{{$param}}
{{- end}}
* @returns {($$source?: any) => {{jsid $model.Name}}{{$typeParamList -}} }
* @returns {($$source?: any) => {{jsid $model.Name}}{{$template.ParamList -}} }
{{- else}}
* Creates a new {{jsid $model.Name}} instance from a string or object.
* @param {any} [$$source = {}]
* @returns { {{- jsid $model.Name -}} }
{{- 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}}
{{- $create := ($module.JSCreateWithParams $field.Type $createParamList)}}
{{- $create := ($module.JSCreateWithParams $field.Type $template.CreateList)}}
{{- if ne $create "$Create.Any"}}
const $$createField{{$i}}_{{$j}} = {{$create}};
{{- end}}
{{- end}}{{end}}
{{- $indent := ""}}
{{- if $typeParamList}}
{{- if $template.ParamList}}
{{- $indent = " "}}
return ($$source = {}) => {
{{- end}}
@ -190,8 +179,8 @@ export class {{jsid $model.Name}} {
{{$indent -}} }
{{- end}}
{{- end}}{{end}}
{{$indent}}return new {{jsid $model.Name}}(/** @type {Partial<{{jsid $model.Name}}{{$typeParamList}}>} */($$parsedSource));
{{- if $typeParamList}}
{{$indent}}return new {{jsid $model.Name}}(/** @type {Partial<{{jsid $model.Name}}{{$template.ParamList}}>} */($$parsedSource));
{{- if $template.ParamList}}
};
{{- 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}}
{{- range $model := .Models}}
{{- $isEnum := $model.Values}}
{{- $isClassAlias := and $model.Type (not $useInterfaces) (isclass $model.Type)}}
{{- $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}}
{{- $info := modelinfo $model $useInterfaces }}
{{- $template := $info.Template }}
{{- if or (hasdoc $model.Decl.Doc) (hasdoc $model.Doc)}}
/**
@ -53,7 +32,7 @@ import * as {{jsimport .}} from "{{js .RelPath}}/{{js $models}}";
{{- end}}
*/
{{- end}}
{{- if $isEnum}}
{{- if $info.IsEnum}}
export enum {{jsid $model.Name}} {
/**
* 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}},
{{- end}}{{end}}{{end}}
};
{{else if $isClassAlias}}
export const {{jsid $model.Name}} = {{$module.JSType $model.Type.Origin}};
{{else if $info.IsClassAlias}}
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)}}
/**
@ -92,11 +75,38 @@ export const {{jsid $model.Name}} = {{$module.JSType $model.Type.Origin}};
{{- end}}
*/
{{- end}}
export type {{jsid $model.Name}}{{$typeParamList}} = {{$module.JSType $model.Type}};
{{else if $isTypeAlias}}
export type {{jsid $model.Name}}{{$typeParamList}} = {{$module.JSType $model.Type}};
{{else if $isClassOrInterface}}
export {{if $isInterface}}interface{{else}}class{{end}} {{jsid $model.Name}}{{$typeParamList}} {
export type {{jsid $model.Name}}{{$template.ParamList}} = {{$module.JSType $model.Type}};
{{else if $info.IsTypeAlias}}
export type {{jsid $model.Name}}{{$template.ParamList}} = {{$module.JSType $model.Type}};
{{- if $info.HasValues}}
/**
* 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}}
{{- if and (eq $j 0) (hasdoc $field.Decl.Doc)}}
{{- if gt $i 0}}
@ -107,11 +117,11 @@ export {{if $isInterface}}interface{{else}}class{{end}} {{jsid $model.Name}}{{$t
{{- end}}
"{{js $field.JsonName}}"{{if $field.Optional}}?{{end}}: {{$module.JSFieldType $field.StructField}};
{{- end}}{{end}}
{{- if not $isInterface}}
{{- if $info.IsClass}}
/** Creates a new {{jsid $model.Name}} instance. */
constructor($$source: Partial<{{jsid $model.Name}}{{$typeParamList}}> = {}) {
{{- range $spec := $model.Fields}}{{range $i, $field := $spec}}{{if not .Optional}}
constructor($$source: Partial<{{jsid $model.Name}}{{$template.ParamList}}> = {}) {
{{- range $spec := $model.Fields}}{{range $i, $field := $spec}}{{if not $field.Optional}}
if (!("{{js $field.JsonName}}" in $$source)) {
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,
* returns a creation function for a concrete instance
* 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.
{{- end}}
*/
static createFrom{{$typeParamList}}({{if $typeParamList}}
static createFrom{{$template.ParamList}}({{if $template.ParamList}}
{{- range $i, $param := $model.TypeParams}}
{{- $param = (typeparam $i $param)}}
{{- if gt $i 0}}, {{end -}}
$$createParam{{$param}}: (source: any) => {{$param}}{{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}}
{{- $create := ($module.JSCreateWithParams $field.Type $createParamList)}}
{{- $create := ($module.JSCreateWithParams $field.Type $template.CreateList)}}
{{- if ne $create "$Create.Any"}}
const $$createField{{$i}}_{{$j}} = {{$create}};
{{- end}}
{{- end}}{{end}}
{{- $indent := ""}}
{{- if $typeParamList}}
{{- if $template.ParamList}}
{{- $indent = " "}}
return ($$source: any = {}) => {
{{- end}}
@ -155,8 +165,8 @@ export {{if $isInterface}}interface{{else}}class{{end}} {{jsid $model.Name}}{{$t
{{$indent -}} }
{{- end}}
{{- end}}{{end}}
{{$indent}}return new {{jsid $model.Name}}{{$typeParamList}}($$parsedSource as Partial<{{jsid $model.Name}}{{$typeParamList}}>);
{{- if $typeParamList}}
{{$indent}}return new {{jsid $model.Name}}{{$template.ParamList}}($$parsedSource as Partial<{{jsid $model.Name}}{{$template.ParamList}}>);
{{- if $template.ParamList}}
};
{{- end}}
}

View File

@ -1,7 +1,6 @@
{{$module := .}}
{{- $runtime := $module.Runtime}}
{{- $models := (fixext $module.ModelsFile)}}
{{- $internal := (fixext $module.InternalFile)}}
{{- $useNames := $module.UseNames}}
{{- $useInterfaces := $module.UseInterfaces}}
{{- $imports := $module.Imports}}
@ -33,11 +32,7 @@ import * as {{jsimport .}} from "{{js .RelPath}}/{{js $models}}";
{{- if $imports.ImportModels}}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as $models from "./{{js $internal}}";
{{end}}
{{- range .Injections}}
{{.}}
{{- end}}{{if .Injections}}
import * as $models from "./{{js $models}}";
{{end}}
{{- 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 -}}
$$createType{{$i}} = {{$create}};
{{- end}}
{{end}}
{{- range .Injections}}
{{.}}
{{- end}}{{if .Injections}}
{{end}}{{end -}}

View File

@ -1,7 +1,6 @@
{{$module := .}}
{{- $runtime := $module.Runtime}}
{{- $models := (fixext $module.ModelsFile)}}
{{- $internal := (fixext $module.InternalFile)}}
{{- $useNames := $module.UseNames}}
{{- $useInterfaces := $module.UseInterfaces}}
{{- $imports := $module.Imports}}
@ -32,11 +31,7 @@ import * as {{jsimport .}} from "{{js .RelPath}}/{{js $models}}";
{{- if $imports.ImportModels}}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as $models from "./{{js $internal}}";
{{end}}
{{- range .Injections}}
{{.}}
{{- end}}{{if .Injections}}
import * as $models from "./{{js $models}}";
{{end}}
{{- range .Methods}}
{{- 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 -}}
$$createType{{$i}} = {{$create}};
{{- end}}
{{end}}
{{- range .Injections}}
{{.}}
{{- end}}{{if .Injections}}
{{end}}{{end -}}

View File

@ -72,9 +72,9 @@ func (m *module) renderType(typ types.Type, quoted bool) (result string, nullabl
return m.renderMapType(t)
case *types.Pointer:
elem, ptr := m.renderType(t.Elem(), false)
if ptr {
return elem, true
elem, nullable := m.renderType(t.Elem(), false)
if nullable {
return elem, nullable
} else {
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
case *types.TypeParam:
str := ""
pre, post := "", ""
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.
@ -147,29 +147,22 @@ func (m *module) renderMapType(typ *types.Map) (result string, nullable bool) {
key = m.renderBasicType(k, true)
}
case *types.Alias, *types.Named:
if collect.IsMapKey(typ) {
if collect.IsString(typ) {
case *types.Alias, *types.Named, *types.Pointer:
if collect.IsMapKey(k) {
if collect.IsStringAlias(k) {
// Alias or named type is a string and therefore
// safe to use as a JS object key.
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)
} else {
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.
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
@ -193,16 +186,12 @@ func (m *module) renderNamedType(typ aliasOrNamed, quoted bool) (result string,
return m.renderType(a, quoted)
case *types.Named:
// Quoted mode for (alias of?) named type.
// WARN: Do not test with IsString here!! We only want to catch marshalers.
if !collect.IsAny(typ) && !collect.MaybeTextMarshaler(typ) {
// WARN: Do not test with IsAny/IsStringAlias here!! We only want to catch marshalers.
if collect.MaybeJSONMarshaler(typ) == collect.NonMarshaler && collect.MaybeTextMarshaler(typ) == collect.NonMarshaler {
// No custom marshaling for this type.
switch u := a.Underlying().(type) {
case *types.Basic:
if u, ok := a.Underlying().(*types.Basic); ok {
// Quoted mode for basic named type that is not a marshaler: delegate.
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
// of the given anonymous struct type.
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.Collect()

View File

@ -33,11 +33,13 @@ func (generator *Generator) generateService(obj *types.TypeName) {
}
if info.IsEmpty() {
generator.logger.Infof(
"package %s: type %s: service has no valid exported methods, skipping",
obj.Pkg().Path(),
obj.Name(),
)
if !info.HasInternalMethods {
generator.logger.Infof(
"package %s: type %s: service has no valid exported methods, skipping",
obj.Pkg().Path(),
obj.Name(),
)
}
success = true
return
}
@ -53,14 +55,6 @@ func (generator *Generator) generateService(obj *types.TypeName) {
)
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():
if !generator.options.NoIndex {
generator.logger.Errorf(
@ -90,7 +84,12 @@ func (generator *Generator) generateService(obj *types.TypeName) {
generator.logger.Errorf("%v", err)
return
}
defer file.Close()
defer func() {
if err := file.Close(); err != nil {
generator.logger.Errorf("%v", err)
success = false
}
}()
// Render service code.
err = generator.renderer.Service(file, info)

View File

@ -4,6 +4,7 @@ import (
_ "embed"
"log"
nobindingshere "github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here"
"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.
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.
func (GreetService) Get(aliasValue Alias) Person {
return Person{"hello", aliasValue}
@ -67,6 +101,14 @@ func (GreetService) GetButAliased(p AliasedPerson) StrangelyAliasedPerson {
return p
}
func (GreetService) GetButForeignPrivateAlias() (_ nobindingshere.PrivatePerson) {
return
}
// func (GreetService) GetButGenericAliases() (_ AliasGroup) {
// return
// }
// Greet a lot of unusual things.
func (GreetService) Greet(EmptyAliasStruct, EmptyStruct) AliasStruct {
return AliasStruct{}

View File

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

View File

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

View File

@ -2,6 +2,6 @@ package other
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"
)
// 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
type GreetService struct {
SomeVariable int
@ -30,6 +43,7 @@ type GreetService struct {
type Person struct {
Title Title
Name string
Age Age
}
// 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.
type Impersonator other.OtherPerson[int]
type Impersonator = other.OtherPerson[int]
// HowDifferent is a curious kind of person
// that lets other people decide how they are different.
type HowDifferent[How any] other.OtherPerson[map[string]How]
// PrivatePerson gets their fields from hidden sources.
type PrivatePerson personImpl
type PrivatePerson = personImpl
type personImpl struct {
// 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) MapIntPointerInt(in map[*int]int) {
func (GreetService) MapIntIntPointer(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) MapIntPointerInt(in map[*int]int) {
func (*GreetService) MapIntIntPointer(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
// This file is automatically generated. DO NOT EDIT
@ -5,8 +6,8 @@
// @ts-ignore: Unused imports
import {Create as $Create} from "/wails/runtime.js";
export type Alias = Cyclic | null;
export type Cyclic = { [_: string]: Alias }[];
export type GenericCyclic<T> = {"X": GenericCyclic<T | null> | null, "Y": (T | null)[]}[];
/**
* Marshaler is the interface implemented by types that
* can marshal themselves into valid JSON.
* @typedef {any} Marshaler
*/

View File

@ -1,3 +1,4 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
@ -5,18 +6,10 @@
// @ts-ignore: Unused imports
import {Create as $Create} from "/wails/runtime.js";
export enum 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",
};
/**
* 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 {any} TextMarshaler
*/

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
// @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.
@ -56,6 +60,18 @@ export function GetButDifferent() {
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.
* @param {$models.EmptyAliasStruct} $0
@ -65,7 +81,7 @@ export function GetButDifferent() {
export function Greet($0, $1) {
let $resultPromise = /** @type {any} */($Call.ByID(1411160069, $0, $1));
let $typingPromise = /** @type {any} */($resultPromise.then(($result) => {
return $$createType5($result);
return $$createType6($result);
}));
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
@ -74,12 +90,13 @@ export function Greet($0, $1) {
// Private type creation functions
const $$createType0 = $models.Person.createFrom;
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 $$createType4 = $Create.Struct({
"NoMoreIdeas": $$createType3,
});
const $$createType4 = $Create.Array($Create.Any);
const $$createType5 = $Create.Struct({
"Foo": $$createType2,
"Other": $$createType4,
"NoMoreIdeas": $$createType4,
});
const $$createType6 = $Create.Struct({
"Foo": $$createType3,
"Other": $$createType5,
});

View File

@ -7,4 +7,33 @@ export {
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
// This file is automatically generated. DO NOT EDIT
export {
AliasedPerson,
EmptyStruct,
GenericPerson,
Person,
StrangelyAliasedPerson
} from "./internal.js";
import * as $models from "./internal.js";
// 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 {$models.Alias} Alias
* @typedef {number} Alias
*/
/**
* A struct alias.
* 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.
* @typedef {$models.EmptyAliasStruct} EmptyAliasStruct
* @typedef { {
* } } EmptyAliasStruct
*/
/**
* Another struct alias.
* @typedef {$models.OtherAliasStruct} OtherAliasStruct
* 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 (/** @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
// @ts-ignore: Unused imports
import * as $models from "./internal.js";
import * as $models from "./models.js";
/**
* Greet does XYZ

View File

@ -7,4 +7,14 @@ export {
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
// This file is automatically generated. DO NOT EDIT
export {
Embedded1,
Person,
Title
} from "./internal.js";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import {Create as $Create} from "/wails/runtime.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
// @ts-ignore: Unused imports
import * as $models from "./internal.js";
import * as $models from "./models.js";
/**
* Greet does XYZ

View File

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

View File

@ -2,6 +2,37 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
export {
Person
} from "./internal.js";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
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