mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 22:55:48 +01:00
Merge branch 'v3-alpha' into v3-alpha-fe-npm-gen-fix
This commit is contained in:
commit
bcb28bc14c
332 changed files with 4911 additions and 2253 deletions
36
.github/workflows/build-and-test-v3.yml
vendored
36
.github/workflows/build-and-test-v3.yml
vendored
|
|
@ -79,6 +79,10 @@ jobs:
|
|||
working-directory: v3/internal/runtime/desktop/@wailsio/runtime
|
||||
run: npm run build
|
||||
|
||||
- name: Pack runtime for template tests
|
||||
working-directory: v3/internal/runtime/desktop/@wailsio/runtime
|
||||
run: npm pack
|
||||
|
||||
- name: Store runtime build artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
|
|
@ -88,6 +92,12 @@ jobs:
|
|||
v3/internal/runtime/desktop/@wailsio/runtime/types/
|
||||
v3/internal/runtime/desktop/@wailsio/runtime/tsconfig.tsbuildinfo
|
||||
|
||||
- name: Store runtime package
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: runtime-package
|
||||
path: v3/internal/runtime/desktop/@wailsio/runtime/*.tgz
|
||||
|
||||
test_go:
|
||||
name: Run Go Tests v3
|
||||
needs: [check_approval, test_js]
|
||||
|
|
@ -165,17 +175,19 @@ jobs:
|
|||
cleanup:
|
||||
name: Cleanup build artifacts
|
||||
if: always()
|
||||
needs: [test_js, test_go]
|
||||
needs: [test_js, test_go, test_templates]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: geekyeggo/delete-artifact@v5
|
||||
with:
|
||||
name: runtime-build-artifacts
|
||||
name: |
|
||||
runtime-build-artifacts
|
||||
runtime-package
|
||||
failOnError: false
|
||||
|
||||
test_templates:
|
||||
name: Test Templates
|
||||
needs: test_go
|
||||
needs: [test_js, test_go]
|
||||
runs-on: ${{ matrix.os }}
|
||||
if: github.base_ref == 'v3-alpha'
|
||||
strategy:
|
||||
|
|
@ -226,12 +238,28 @@ jobs:
|
|||
task install
|
||||
wails3 doctor
|
||||
|
||||
- name: Download runtime package
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: runtime-package
|
||||
path: wails-runtime-temp
|
||||
|
||||
- name: Generate template '${{ matrix.template }}'
|
||||
shell: bash
|
||||
run: |
|
||||
# Get absolute path - use pwd -W on Windows for native paths, pwd elsewhere
|
||||
if [[ "$RUNNER_OS" == "Windows" ]]; then
|
||||
RUNTIME_TGZ="$(cd wails-runtime-temp && pwd -W)/$(ls wails-runtime-temp/*.tgz | xargs basename)"
|
||||
else
|
||||
RUNTIME_TGZ="$(cd wails-runtime-temp && pwd)/$(ls wails-runtime-temp/*.tgz | xargs basename)"
|
||||
fi
|
||||
mkdir -p ./test-${{ matrix.template }}
|
||||
cd ./test-${{ matrix.template }}
|
||||
wails3 init -n ${{ matrix.template }} -t ${{ matrix.template }}
|
||||
cd ${{ matrix.template }}
|
||||
cd ${{ matrix.template }}/frontend
|
||||
# Replace @wailsio/runtime version with local tarball
|
||||
npm pkg set dependencies.@wailsio/runtime="file://$RUNTIME_TGZ"
|
||||
cd ..
|
||||
wails3 build
|
||||
|
||||
build_results:
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -77,21 +77,38 @@ func (s *Service) UpdateData() {
|
|||
|
||||
// Notify the frontend
|
||||
app := application.Get()
|
||||
app.Event.EmitEvent(&application.CustomEvent{
|
||||
Name: "my-app:data-updated",
|
||||
Data: map[string]interface{}{
|
||||
app.Event.Emit("my-app:data-updated",
|
||||
map[string]interface{}{
|
||||
"timestamp": time.Now(),
|
||||
"count": 42,
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Emitting Events (Frontend)
|
||||
|
||||
While not as commonly used, you can also emit events from your frontend that your Go code can listen to:
|
||||
|
||||
```javascript
|
||||
import { Events } from '@wailsio/runtime';
|
||||
|
||||
// Event without data
|
||||
Events.Emit('myapp:close-window')
|
||||
|
||||
// Event with data
|
||||
Events.Emit('myapp:disconnect-requested', 'id-123')
|
||||
```
|
||||
|
||||
If you are using typescript in your frontend and [application.RegisterEvent](/learn/events#custom-event-registration) in your Go code, you will get event name autocomplete / checking and data type checking.
|
||||
|
||||
### Removing Event Listeners
|
||||
|
||||
Always clean up your event listeners when they're no longer needed:
|
||||
|
||||
```javascript
|
||||
import { Events } from '@wailsio/runtime';
|
||||
|
||||
// Store the handler reference
|
||||
const focusHandler = () => {
|
||||
console.log('Window focused');
|
||||
|
|
@ -114,6 +131,8 @@ Events.Off('common:WindowFocus');
|
|||
Many applications need to pause certain activities when the window loses focus:
|
||||
|
||||
```javascript
|
||||
import { Events } from '@wailsio/runtime';
|
||||
|
||||
let animationRunning = true;
|
||||
|
||||
Events.On('common:WindowLostFocus', () => {
|
||||
|
|
@ -132,6 +151,8 @@ Events.On('common:WindowFocus', () => {
|
|||
Keep your app in sync with the system theme:
|
||||
|
||||
```javascript
|
||||
import { Events } from '@wailsio/runtime';
|
||||
|
||||
Events.On('common:ThemeChanged', (event) => {
|
||||
const isDarkMode = event.data.isDark;
|
||||
|
||||
|
|
@ -150,6 +171,8 @@ Events.On('common:ThemeChanged', (event) => {
|
|||
Make your app accept dragged files:
|
||||
|
||||
```javascript
|
||||
import { Events } from '@wailsio/runtime';
|
||||
|
||||
Events.On('common:WindowFilesDropped', (event) => {
|
||||
const files = event.data.files;
|
||||
|
||||
|
|
@ -166,6 +189,8 @@ Events.On('common:WindowFilesDropped', (event) => {
|
|||
Respond to window state changes:
|
||||
|
||||
```javascript
|
||||
import { Events } from '@wailsio/runtime';
|
||||
|
||||
Events.On('common:WindowClosing', () => {
|
||||
// Save user data before closing
|
||||
saveApplicationState();
|
||||
|
|
@ -190,6 +215,8 @@ Events.On('common:WindowRestore', () => {
|
|||
Handle platform-specific events when needed:
|
||||
|
||||
```javascript
|
||||
import { Events } from '@wailsio/runtime';
|
||||
|
||||
// Windows-specific power management
|
||||
Events.On('windows:APMSuspend', () => {
|
||||
console.log('System is going to sleep');
|
||||
|
|
@ -210,7 +237,7 @@ Events.On('mac:ApplicationWillTerminate', () => {
|
|||
|
||||
## Creating Custom Events
|
||||
|
||||
You can create your own events for application-specific needs:
|
||||
You can create your own events for application-specific needs. See [application.RegisterEvent](/learn/events#custom-event-registration) to learn how to register these events and enable type checking of event data.
|
||||
|
||||
### Backend (Go)
|
||||
|
||||
|
|
@ -222,14 +249,13 @@ func (s *Service) ProcessUserData(userData UserData) error {
|
|||
|
||||
app := application.Get()
|
||||
// Notify all listeners
|
||||
app.Event.EmitEvent(&application.CustomEvent{
|
||||
Name: "user:data-processed",
|
||||
Data: map[string]interface{}{
|
||||
app.Event.Emit("user:data-processed",
|
||||
map[string]interface{}{
|
||||
"userId": userData.ID,
|
||||
"status": "completed",
|
||||
"timestamp": time.Now(),
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -250,6 +276,8 @@ func (s *Service) StartMonitoring() {
|
|||
### Frontend (JavaScript)
|
||||
|
||||
```javascript
|
||||
import { Events } from '@wailsio/runtime';
|
||||
|
||||
// Listen for your custom events
|
||||
Events.On('user:data-processed', (event) => {
|
||||
const { userId, status, timestamp } = event.data;
|
||||
|
|
@ -341,6 +369,8 @@ Events.Emit('update');
|
|||
Always remove event listeners when components unmount:
|
||||
|
||||
```javascript
|
||||
import { Events } from '@wailsio/runtime';
|
||||
|
||||
// React example
|
||||
useEffect(() => {
|
||||
const handler = (event) => {
|
||||
|
|
@ -361,7 +391,7 @@ useEffect(() => {
|
|||
Check platform availability when using platform-specific events:
|
||||
|
||||
```javascript
|
||||
import { Platform } from '@wailsio/runtime';
|
||||
import { Platform, Events } from '@wailsio/runtime';
|
||||
|
||||
if (Platform.isWindows) {
|
||||
Events.On('windows:APMSuspend', handleSuspend);
|
||||
|
|
@ -382,6 +412,8 @@ While events are powerful, don't use them for everything:
|
|||
To debug event issues:
|
||||
|
||||
```javascript
|
||||
import { Events } from '@wailsio/runtime';
|
||||
|
||||
// Log all events (development only)
|
||||
if (isDevelopment) {
|
||||
const originalOn = Events.On;
|
||||
|
|
|
|||
|
|
@ -127,13 +127,12 @@ func (s *GinService) ServiceStartup(ctx context.Context, options application.Ser
|
|||
s.app.Logger.Info("Received event from frontend", "data", event.Data)
|
||||
|
||||
// Emit an event back to the frontend
|
||||
s.app.Event.EmitEvent(&application.CustomEvent{
|
||||
Name: "gin-api-response",
|
||||
Data: map[string]interface{}{
|
||||
s.app.Event.Emit("gin-api-response",
|
||||
map[string]interface{}{
|
||||
"message": "Response from Gin API Service",
|
||||
"time": time.Now().Format(time.RFC3339),
|
||||
},
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
return nil
|
||||
|
|
@ -316,13 +315,12 @@ s.app.Event.On("gin-api-event", func(event *application.CustomEvent) {
|
|||
s.app.Logger.Info("Received event from frontend", "data", event.Data)
|
||||
|
||||
// Emit an event back to the frontend
|
||||
s.app.Event.EmitEvent(&application.CustomEvent{
|
||||
Name: "gin-api-response",
|
||||
Data: map[string]interface{}{
|
||||
s.app.Event.Emit("gin-api-response",
|
||||
map[string]interface{}{
|
||||
"message": "Response from Gin API Service",
|
||||
"time": time.Now().Format(time.RFC3339),
|
||||
},
|
||||
})
|
||||
)
|
||||
})
|
||||
```
|
||||
|
||||
|
|
@ -349,10 +347,7 @@ Then use it in your code:
|
|||
import * as wails from '@wailsio/runtime';
|
||||
|
||||
// Event emission
|
||||
wails.Events.Emit({
|
||||
name: 'gin-api-event',
|
||||
data: eventData,
|
||||
});
|
||||
wails.Events.Emit('gin-api-event', eventData);
|
||||
```
|
||||
|
||||
Here's an example of how to set up frontend integration:
|
||||
|
|
@ -528,7 +523,7 @@ Here's an example of how to set up frontend integration:
|
|||
message: "Hello from the frontend!",
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
wails.Events.Emit({name: 'gin-api-event', data: eventData});
|
||||
wails.Events.Emit('gin-api-event', eventData);
|
||||
});
|
||||
|
||||
// Set up event listener for responses from the backend
|
||||
|
|
|
|||
|
|
@ -313,7 +313,7 @@ win.OnWindowEvent(events.Common.WindowDropZoneFilesDropped, func(event *applicat
|
|||
"dropY": details.Y,
|
||||
"attributes": details.Attributes,
|
||||
}
|
||||
application.Get().EmitEvent("frontend:FileDropInfo", payload) // Emits globally
|
||||
application.Get().Event.Emit("frontend:FileDropInfo", payload) // Emits globally
|
||||
// or win.EmitEvent("frontend:FileDropInfoForWindow", payload) // Emits to this specific window
|
||||
} else {
|
||||
log.Println("Drop occurred, but DropZoneDetails were nil.")
|
||||
|
|
@ -565,6 +565,9 @@ You can emit custom events from anywhere in your application:
|
|||
// NEW: Using the Event Manager (recommended)
|
||||
app.Event.Emit("myevent", "hello")
|
||||
|
||||
// Emit an event without data
|
||||
app.Event.Emit("datalessevent")
|
||||
|
||||
// Emit from a specific window
|
||||
window.EmitEvent("windowevent", "window specific data")
|
||||
```
|
||||
|
|
@ -574,8 +577,7 @@ window.EmitEvent("windowevent", "window specific data")
|
|||
// Traditional API (no longer works)
|
||||
app.EmitEvent("myevent", "hello")
|
||||
|
||||
// Emit from a specific window
|
||||
window.EmitEvent("windowevent", "window specific data")
|
||||
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
|
@ -633,6 +635,90 @@ app.OnMultipleEvent("myevent", func(e *application.CustomEvent) {
|
|||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
You can also register a hook that will run synchronously when the event is emitted.
|
||||
Hooks are useful [to cancel custom events and stop their propagation early](#event-cancellation):
|
||||
|
||||
```go
|
||||
app.Event.RegisterHook("myevent", func(e *application.CustomEvent) {
|
||||
// Do something.
|
||||
})
|
||||
```
|
||||
|
||||
### Custom Event Registration
|
||||
|
||||
You can call the `application.RegisterEvent` function at init time to register custom event names:
|
||||
|
||||
```go
|
||||
type MyEventData struct {
|
||||
Num int
|
||||
Text string
|
||||
}
|
||||
|
||||
func init() {
|
||||
application.RegisterEvent[MyEventData]("myevent")
|
||||
}
|
||||
```
|
||||
|
||||
:::caution
|
||||
Because this function is meant to be called at init time, it will panic when the arguments are not valid
|
||||
or the same event name is registered twice with two distinct data types.
|
||||
:::
|
||||
|
||||
:::note
|
||||
It is safe to register the same event multiple times as long as the data type is always the same.
|
||||
This might be useful to ensure a certain event is registered as soon as any one of many packages is loaded.
|
||||
:::
|
||||
|
||||
Once the event is registered, data arguments passed to the `Event.Emit` method will be checked against the specified type.
|
||||
In case of a mismatch, an error will be emitted and either logged or passed on to the registered error handled, if any.
|
||||
The offending event will not be propagated. Thanks to this mechanism, it is safe to assume that the data field
|
||||
of registered custom events will always be assignable to the declared type.
|
||||
|
||||
:::tip[Strict mode]
|
||||
If you use the `strictevents` build tag, using unregistered events will trigger a warning message in development mode.
|
||||
The runtime emits at most one warning per event name, to avoid spamming the log.
|
||||
:::
|
||||
|
||||
### Binding generator support
|
||||
|
||||
The binding generator outputs TypeScript definitions and internal glue code
|
||||
to provide transparent support for registered events on the frontend.
|
||||
The specified data type will be rendered to TypeScript
|
||||
and the data field will be typed by the corresponding model:
|
||||
|
||||
```js
|
||||
import { Events } from "@wailsio/runtime";
|
||||
|
||||
Events.On("myevent", (ev) => {
|
||||
ev.data; // Has type MyEventData
|
||||
ev.data.Text; // Has type string
|
||||
})
|
||||
```
|
||||
|
||||
:::caution
|
||||
Static typing of event data on the frontend will only be available when the event name
|
||||
is specified by a constant expression, as shown above.
|
||||
:::
|
||||
|
||||
When using the `Events.On*/Events.Off*` family of runtime methods or the `WailsEvent` constructor,
|
||||
IDEs with TypeScript support will offer autocompletion for registered event names.
|
||||
|
||||
You can look at built-in application templates
|
||||
for an example of how to configure your project for typed events.
|
||||
|
||||
### Events without data
|
||||
|
||||
The `application.Void` interface can be used to register events without associated data:
|
||||
|
||||
```go
|
||||
func init() {
|
||||
application.RegisterEvent[application.Void]("dataless")
|
||||
}
|
||||
```
|
||||
|
||||
On the Go side, the runtime will check that the data field for the `"dataless"` event is always `nil`.
|
||||
On the frontend, the binding generator will translate `application.Void` as the TypeScript `void` type.
|
||||
|
||||
## Event Cancellation
|
||||
|
||||
Events can be cancelled to prevent their default behaviour or stop propagation to other listeners. This is particularly useful for hooks that need to control window closing, menu actions, or other system events.
|
||||
|
|
@ -646,8 +732,17 @@ window.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent
|
|||
// Prevent the window from closing
|
||||
e.Cancel()
|
||||
})
|
||||
|
||||
// For custom events
|
||||
app.RegisterHook("myevent", func(e *application.CustomEvent) {
|
||||
e.Cancel()
|
||||
})
|
||||
```
|
||||
|
||||
:::tip[Pro Tip]
|
||||
Remember that event cancellation in hooks affects all subsequent hooks and listeners, whilst cancellation in standard listeners only affects listeners that haven't yet been called.
|
||||
:::
|
||||
|
||||
### Checking Event Cancellation
|
||||
|
||||
You can check if an event has been cancelled using the `IsCancelled()` method:
|
||||
|
|
@ -671,6 +766,13 @@ app.Event.On("myevent", func(e *application.CustomEvent) {
|
|||
})
|
||||
```
|
||||
|
||||
:::tip[Pro Tip]
|
||||
Remember that event cancellation in hooks affects all subsequent hooks and listeners, whilst cancellation in standard listeners only affects listeners that haven't yet been called.
|
||||
:::
|
||||
When emitting a custom event, you can also check the value returned by the `Event.Emit` method (for the app and windows):
|
||||
|
||||
```go
|
||||
if app.Event.Emit("myevent") {
|
||||
app.Logger.Info("Event was cancelled")
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
The returned value will take into account all hooks, but may or may not take into account cancellations that happen asynchronously in listeners.
|
||||
|
|
|
|||
|
|
@ -43,14 +43,11 @@ removeButton.addEventListener('click', () => {
|
|||
|
||||
setButtonUsingGo.addEventListener('click', () => {
|
||||
let label = (labelElement as HTMLInputElement).value
|
||||
void Events.Emit({
|
||||
name: "set:badge",
|
||||
data: label,
|
||||
})
|
||||
void Events.Emit("set:badge", label);
|
||||
})
|
||||
|
||||
removeButtonUsingGo.addEventListener('click', () => {
|
||||
void Events.Emit({name:"remove:badge", data: null})
|
||||
void Events.Emit("remove:badge");
|
||||
})
|
||||
|
||||
Events.On('time', (time: {data: any}) => {
|
||||
|
|
|
|||
|
|
@ -19,14 +19,11 @@ removeButton.addEventListener('click', () => {
|
|||
|
||||
setButtonUsingGo.addEventListener('click', () => {
|
||||
let label = (labelElement as HTMLInputElement).value
|
||||
void Events.Emit({
|
||||
name: "set:badge",
|
||||
data: label,
|
||||
})
|
||||
void Events.Emit("set:badge", label)
|
||||
})
|
||||
|
||||
removeButtonUsingGo.addEventListener('click', () => {
|
||||
void Events.Emit({name:"remove:badge", data: null})
|
||||
void Events.Emit("remove:badge")
|
||||
})
|
||||
|
||||
Events.On('time', (time: {data: any}) => {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,6 @@
|
|||
"vite": "^6.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wailsio/runtime": "^3.0.0-alpha.66"
|
||||
"@wailsio/runtime": "latest"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"moduleResolution": "Node",
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
|
|
@ -29,5 +30,5 @@
|
|||
* Use global.d.ts instead of compilerOptions.types
|
||||
* to avoid limiting type declarations.
|
||||
*/
|
||||
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"]
|
||||
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte", "bindings"]
|
||||
}
|
||||
|
|
@ -223,7 +223,7 @@
|
|||
message: "Hello from the frontend!",
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
wails.Events.Emit({name: 'gin-api-event', data: eventData});
|
||||
wails.Events.Emit('gin-api-event', eventData);
|
||||
});
|
||||
|
||||
// Set up event listener for responses from the backend
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -62,12 +62,13 @@ func GenerateBindings(options *flags.GenerateBindingsOptions, patterns []string)
|
|||
// Stop spinner and print summary.
|
||||
term.StopSpinner(spinner)
|
||||
term.Infof(
|
||||
"Processed: %s, %s, %s, %s, %s in %s.",
|
||||
"Processed: %s, %s, %s, %s, %s, %s in %s.",
|
||||
pluralise(stats.NumPackages, "Package"),
|
||||
pluralise(stats.NumServices, "Service"),
|
||||
pluralise(stats.NumMethods, "Method"),
|
||||
pluralise(stats.NumEnums, "Enum"),
|
||||
pluralise(stats.NumModels, "Model"),
|
||||
pluralise(stats.NumEvents, "Event"),
|
||||
stats.Elapsed().String(),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -30,16 +30,14 @@ dev_mode:
|
|||
- .gitkeep
|
||||
watched_extension:
|
||||
- "*.go"
|
||||
- "*.js" # Watch for changes to JS/TS files included using the //wails:include directive.
|
||||
- "*.ts" # The frontend directory will be excluded entirely by the setting above.
|
||||
git_ignore: true
|
||||
executes:
|
||||
- cmd: wails3 task common:install:frontend:deps
|
||||
type: once
|
||||
- cmd: wails3 task common:dev:frontend
|
||||
type: background
|
||||
- cmd: go mod tidy
|
||||
type: blocking
|
||||
- cmd: wails3 task build
|
||||
type: blocking
|
||||
- cmd: wails3 task common:dev:frontend
|
||||
type: background
|
||||
- cmd: wails3 task run
|
||||
type: primary
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/go-git/go-git/v5/config"
|
||||
"github.com/wailsapp/wails/v3/internal/term"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/go-git/go-git/v5/config"
|
||||
"github.com/wailsapp/wails/v3/internal/term"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/pterm/pterm"
|
||||
"github.com/wailsapp/wails/v3/internal/flags"
|
||||
|
|
@ -17,36 +20,52 @@ import (
|
|||
|
||||
var DisableFooter bool
|
||||
|
||||
// GitURLToModuleName converts a git URL to a Go module name by removing common prefixes
|
||||
// See https://github.com/git/git/blob/master/Documentation/urls.adoc
|
||||
var (
|
||||
gitProtocolFormat = regexp.MustCompile(`^(?:ssh|git|https?|ftps?|file)://`)
|
||||
gitScpLikeGuard = regexp.MustCompile(`^[^/:]+:`)
|
||||
gitScpLikeFormat = regexp.MustCompile(`^(?:([^@/:]+)@)?([^@/:]+):([^\\].*)$`)
|
||||
)
|
||||
|
||||
// gitURLToModulePath converts a git URL to a Go module name by removing common prefixes
|
||||
// and suffixes. It handles HTTPS, SSH, Git protocol, and filesystem URLs.
|
||||
func GitURLToModuleName(gitURL string) string {
|
||||
moduleName := gitURL
|
||||
if strings.HasSuffix(moduleName, ".git") {
|
||||
moduleName = moduleName[:len(moduleName)-4]
|
||||
}
|
||||
// Handle various URL schemes
|
||||
for _, prefix := range []string{
|
||||
"https://",
|
||||
"http://",
|
||||
"git://",
|
||||
"ssh://",
|
||||
"file://",
|
||||
} {
|
||||
if strings.HasPrefix(moduleName, prefix) {
|
||||
moduleName = moduleName[len(prefix):]
|
||||
break
|
||||
func gitURLToModulePath(gitURL string) string {
|
||||
var path string
|
||||
|
||||
if gitProtocolFormat.MatchString(gitURL) {
|
||||
// Standard URL
|
||||
parsed, err := url.Parse(gitURL)
|
||||
if err != nil {
|
||||
term.Warningf("invalid Git repository URL: %s; module path will default to 'changeme'", err)
|
||||
return "changeme"
|
||||
}
|
||||
|
||||
path = parsed.Host + parsed.Path
|
||||
} else if gitScpLikeGuard.MatchString(gitURL) {
|
||||
// SCP-like URL
|
||||
match := gitScpLikeFormat.FindStringSubmatch(gitURL)
|
||||
if match != nil {
|
||||
sep := ""
|
||||
if !strings.HasPrefix(match[3], "/") {
|
||||
// Add slash between host and path if missing
|
||||
sep = "/"
|
||||
}
|
||||
|
||||
path = match[2] + sep + match[3]
|
||||
}
|
||||
}
|
||||
// Handle SSH URLs (git@github.com:username/project.git)
|
||||
if strings.HasPrefix(moduleName, "git@") {
|
||||
// Remove the 'git@' prefix
|
||||
moduleName = moduleName[4:]
|
||||
// Replace ':' with '/' for proper module path
|
||||
moduleName = strings.Replace(moduleName, ":", "/", 1)
|
||||
|
||||
if path == "" {
|
||||
// Filesystem path
|
||||
path = gitURL
|
||||
}
|
||||
|
||||
if strings.HasSuffix(path, ".git") {
|
||||
path = path[:len(path)-4]
|
||||
}
|
||||
|
||||
// Remove leading forward slash for file system paths
|
||||
moduleName = strings.TrimPrefix(moduleName, "/")
|
||||
return moduleName
|
||||
return strings.TrimPrefix(path, "/")
|
||||
}
|
||||
|
||||
func initGitRepository(projectDir string, gitURL string) error {
|
||||
|
|
@ -65,28 +84,6 @@ func initGitRepository(projectDir string, gitURL string) error {
|
|||
return fmt.Errorf("failed to create git remote: %w", err)
|
||||
}
|
||||
|
||||
// Update go.mod with the module name
|
||||
moduleName := GitURLToModuleName(gitURL)
|
||||
|
||||
goModPath := filepath.Join(projectDir, "go.mod")
|
||||
content, err := os.ReadFile(goModPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read go.mod: %w", err)
|
||||
}
|
||||
|
||||
// Replace module name
|
||||
lines := strings.Split(string(content), "\n")
|
||||
if len(lines) == 0 {
|
||||
return fmt.Errorf("go.mod is empty")
|
||||
}
|
||||
lines[0] = fmt.Sprintf("module %s", moduleName)
|
||||
newContent := strings.Join(lines, "\n")
|
||||
|
||||
err = os.WriteFile(goModPath, []byte(newContent), 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write go.mod: %w", err)
|
||||
}
|
||||
|
||||
// Stage all files
|
||||
worktree, err := repo.Worktree()
|
||||
if err != nil {
|
||||
|
|
@ -102,7 +99,6 @@ func initGitRepository(projectDir string, gitURL string) error {
|
|||
}
|
||||
|
||||
func Init(options *flags.Init) error {
|
||||
|
||||
if options.List {
|
||||
term.Header("Available templates")
|
||||
return printTemplates()
|
||||
|
|
@ -120,11 +116,19 @@ func Init(options *flags.Init) error {
|
|||
}
|
||||
|
||||
if options.ProjectName == "" {
|
||||
return fmt.Errorf("please use the -n flag to specify a project name")
|
||||
return errors.New("please use the -n flag to specify a project name")
|
||||
}
|
||||
|
||||
options.ProjectName = sanitizeFileName(options.ProjectName)
|
||||
|
||||
if options.ModulePath == "" {
|
||||
if options.Git == "" {
|
||||
options.ModulePath = "changeme"
|
||||
} else {
|
||||
options.ModulePath = gitURLToModulePath(options.Git)
|
||||
}
|
||||
}
|
||||
|
||||
err := templates.Install(options)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package commands
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_GitURLToModuleName(t *testing.T) {
|
||||
func TestGitURLToModulePath(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
gitURL string
|
||||
|
|
@ -106,8 +108,8 @@ func Test_GitURLToModuleName(t *testing.T) {
|
|||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := GitURLToModuleName(tt.gitURL); got != tt.want {
|
||||
t.Errorf("GitURLToModuleName() = %v, want %v", got, tt.want)
|
||||
if got := gitURLToModulePath(tt.gitURL); got != tt.want {
|
||||
t.Errorf("gitURLToModulePath() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ type GenerateBindingsOptions struct {
|
|||
UseInterfaces bool `name:"i" description:"Generate Typescript interfaces instead of classes"`
|
||||
UseBundledRuntime bool `name:"b" description:"Use the bundled runtime instead of importing the npm package"`
|
||||
UseNames bool `name:"names" description:"Use names instead of IDs for the binding calls"`
|
||||
NoEvents bool `name:"noevents" description:"Do not generate types for registered custom events"`
|
||||
NoIndex bool `name:"noindex" description:"Do not generate JS/TS index files"`
|
||||
DryRun bool `name:"dry" description:"Do not write output files"`
|
||||
Silent bool `name:"silent" description:"Silent mode"`
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ type Init struct {
|
|||
List bool `name:"l" description:"List templates"`
|
||||
SkipGoModTidy bool `name:"skipgomodtidy" description:"Skip running go mod tidy"`
|
||||
Git string `name:"git" description:"Git repository URL to initialize (e.g. github.com/username/project)"`
|
||||
ModulePath string `name:"mod" description:"The Go module path for the project. Will be computed from the Git URL if unspecified."`
|
||||
ProductName string `description:"The name of the product" default:"My Product"`
|
||||
ProductDescription string `description:"The description of the product" default:"My Product Description"`
|
||||
ProductVersion string `description:"The version of the product" default:"0.1.0"`
|
||||
|
|
|
|||
|
|
@ -15,12 +15,10 @@ import (
|
|||
//
|
||||
// Whenever one is found and the type of its unique argument
|
||||
// is a valid service type, the corresponding named type object
|
||||
// is passed to yield.
|
||||
// is fed into the returned iterator.
|
||||
//
|
||||
// Results are deduplicated, i.e. yield is called at most once per object.
|
||||
//
|
||||
// If yield returns false, FindBoundTypes returns immediately.
|
||||
func FindServices(pkgs []*packages.Package, systemPaths *config.SystemPaths, logger config.Logger) (iter.Seq[*types.TypeName], error) {
|
||||
// Results are deduplicated, i.e. the iterator yields any given object at most once.
|
||||
func FindServices(pkgs []*packages.Package, systemPaths *config.SystemPaths, logger config.Logger) (iter.Seq[*types.TypeName], types.Object, error) {
|
||||
type instanceInfo struct {
|
||||
args *types.TypeList
|
||||
pos token.Position
|
||||
|
|
@ -47,6 +45,9 @@ func FindServices(pkgs []*packages.Package, systemPaths *config.SystemPaths, log
|
|||
// for deduplication.
|
||||
scheduled := make(map[target]bool)
|
||||
|
||||
// registerEvent holds the `application.RegisterEvent` function if found.
|
||||
var registerEvent types.Object
|
||||
|
||||
// next lists type parameter objects that have yet to be analysed.
|
||||
var next []targetInfo
|
||||
|
||||
|
|
@ -102,29 +103,46 @@ func FindServices(pkgs []*packages.Package, systemPaths *config.SystemPaths, log
|
|||
}
|
||||
}
|
||||
|
||||
if len(next) > 0 {
|
||||
// application.NewService has been found already.
|
||||
continue
|
||||
}
|
||||
// Detect application.RegisterEvent
|
||||
if registerEvent == nil && obj.Name() == "RegisterEvent" && obj.Pkg().Path() == systemPaths.ApplicationPackage {
|
||||
fn, ok := obj.(*types.Func)
|
||||
if !ok {
|
||||
return nil, nil, ErrBadApplicationPackage
|
||||
}
|
||||
|
||||
fn, ok := obj.(*types.Func)
|
||||
if !ok {
|
||||
signature := fn.Type().(*types.Signature)
|
||||
if signature.Params().Len() != 1 || signature.Results().Len() != 0 || signature.TypeParams().Len() != 1 {
|
||||
logger.Warningf("application.RegisterService params: %d, results: %d, typeparams: %d", signature.Params().Len(), signature.Results().Len(), signature.TypeParams().Len())
|
||||
return nil, nil, ErrBadApplicationPackage
|
||||
}
|
||||
|
||||
if !types.Identical(signature.Params().At(0).Type(), types.Universe.Lookup("string").Type()) {
|
||||
logger.Warningf("application.RegisterService parameter type: %v", signature.Params().At(0).Type())
|
||||
return nil, nil, ErrBadApplicationPackage
|
||||
}
|
||||
|
||||
registerEvent = obj
|
||||
continue
|
||||
}
|
||||
|
||||
// Detect application.NewService
|
||||
if fn.Name() == "NewService" && fn.Pkg().Path() == systemPaths.ApplicationPackage {
|
||||
// Check signature.
|
||||
if len(next) == 0 && obj.Name() == "NewService" && obj.Pkg().Path() == systemPaths.ApplicationPackage {
|
||||
fn, ok := obj.(*types.Func)
|
||||
if !ok {
|
||||
return nil, nil, ErrBadApplicationPackage
|
||||
}
|
||||
|
||||
signature := fn.Type().(*types.Signature)
|
||||
if signature.Params().Len() > 2 || signature.Results().Len() != 1 || tp.Len() != 1 || tp.At(0).Obj() == nil {
|
||||
logger.Warningf("Param Len: %d, Results Len: %d, tp.Len: %d, tp.At(0).Obj(): %v", signature.Params().Len(), signature.Results().Len(), tp.Len(), tp.At(0).Obj())
|
||||
return nil, ErrBadApplicationPackage
|
||||
if signature.Params().Len() != 1 || signature.Results().Len() != 1 || tp.Len() != 1 {
|
||||
logger.Warningf("application.NewService params: %d, results: %d, typeparams: %d", signature.Params().Len(), signature.Results().Len(), tp.Len())
|
||||
return nil, nil, ErrBadApplicationPackage
|
||||
}
|
||||
|
||||
// Schedule unique type param for analysis.
|
||||
tgt := target{obj, 0}
|
||||
scheduled[tgt] = true
|
||||
next = append(next, targetInfo{target: tgt})
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -185,7 +203,7 @@ func FindServices(pkgs []*packages.Package, systemPaths *config.SystemPaths, log
|
|||
logger.Warningf("%s: ignoring interface service type %s%s", instance.pos, named, indirectMsg)
|
||||
continue
|
||||
} else if named.TypeParams() != nil {
|
||||
logger.Warningf("%s: ignoring generic service type %s", instance.pos, named, indirectMsg)
|
||||
logger.Warningf("%s: ignoring generic service type %s%s", instance.pos, named, indirectMsg)
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -198,5 +216,5 @@ func FindServices(pkgs []*packages.Package, systemPaths *config.SystemPaths, log
|
|||
}
|
||||
}
|
||||
}
|
||||
}, nil
|
||||
}, registerEvent, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,9 @@ import (
|
|||
|
||||
func TestAnalyser(t *testing.T) {
|
||||
type testParams struct {
|
||||
name string
|
||||
want []string
|
||||
name string
|
||||
want []string
|
||||
events bool
|
||||
}
|
||||
|
||||
// Gather tests from cases directory.
|
||||
|
|
@ -57,6 +58,19 @@ func TestAnalyser(t *testing.T) {
|
|||
}
|
||||
slices.Sort(test.want)
|
||||
|
||||
events, err := os.Open(filepath.Join("testcases", entry.Name(), "events.json"))
|
||||
if err != nil {
|
||||
if !errors.Is(err, os.ErrNotExist) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
err = json.NewDecoder(events).Decode(&test.events)
|
||||
events.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
tests = append(tests, test)
|
||||
}
|
||||
|
||||
|
|
@ -68,6 +82,7 @@ func TestAnalyser(t *testing.T) {
|
|||
|
||||
for _, test := range tests {
|
||||
all.want = append(all.want, test.want...)
|
||||
all.events = all.events || test.events
|
||||
}
|
||||
slices.Sort(all.want)
|
||||
|
||||
|
|
@ -97,7 +112,7 @@ func TestAnalyser(t *testing.T) {
|
|||
|
||||
got := make([]string, 0)
|
||||
|
||||
services, err := FindServices(pkgs, systemPaths, config.DefaultPtermLogger(nil))
|
||||
services, registerService, err := FindServices(pkgs, systemPaths, config.DefaultPtermLogger(nil))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
|
@ -111,6 +126,10 @@ func TestAnalyser(t *testing.T) {
|
|||
if diff := cmp.Diff(test.want, got); diff != "" {
|
||||
t.Errorf("Found services mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
if test.events != (registerService != nil) {
|
||||
t.Errorf("Found events mismatch: wanted=%t, got=%t", test.events, registerService != nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,9 @@ type Collector struct {
|
|||
// and declaration groups. Elements are [Info] instances.
|
||||
cache sync.Map
|
||||
|
||||
// events holds collected information about registered custom events.
|
||||
events *EventMap
|
||||
|
||||
systemPaths *config.SystemPaths
|
||||
options *flags.GenerateBindingsOptions
|
||||
scheduler Scheduler
|
||||
|
|
@ -47,7 +50,7 @@ type Collector struct {
|
|||
}
|
||||
|
||||
// NewCollector initialises a new Collector instance for the given package set.
|
||||
func NewCollector(pkgs []*packages.Package, systemPaths *config.SystemPaths, options *flags.GenerateBindingsOptions, scheduler Scheduler, logger config.Logger) *Collector {
|
||||
func NewCollector(pkgs []*packages.Package, registerEvent types.Object, systemPaths *config.SystemPaths, options *flags.GenerateBindingsOptions, scheduler Scheduler, logger config.Logger) *Collector {
|
||||
collector := &Collector{
|
||||
pkgs: make(map[*types.Package]*PackageInfo, len(pkgs)),
|
||||
|
||||
|
|
@ -62,6 +65,11 @@ func NewCollector(pkgs []*packages.Package, systemPaths *config.SystemPaths, opt
|
|||
collector.pkgs[pkg.Types] = newPackageInfo(pkg, collector)
|
||||
}
|
||||
|
||||
// Initialise event map.
|
||||
if !options.NoEvents {
|
||||
collector.events = newEventMap(collector, registerEvent)
|
||||
}
|
||||
|
||||
return collector
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,25 +39,11 @@ func (collector *Collector) findDeclaration(obj types.Object) (path []ast.Node)
|
|||
return nil
|
||||
}
|
||||
|
||||
// Perform a binary search to find the file enclosing the node.
|
||||
// We can't use findEnclosingNode here because it is less accurate and less efficient with files.
|
||||
fileIndex, exact := slices.BinarySearchFunc(pkg.Files, obj.Pos(), func(f *ast.File, p token.Pos) int {
|
||||
return cmp.Compare(f.FileStart, p)
|
||||
})
|
||||
|
||||
// If exact is true, pkg.Files[fileIndex] is the file we are looking for;
|
||||
// otherwise, it is the first file whose start position is _after_ obj.Pos().
|
||||
if !exact {
|
||||
fileIndex--
|
||||
}
|
||||
|
||||
// When exact is false, the position might lie within an empty segment in between two files.
|
||||
if fileIndex < 0 || pkg.Files[fileIndex].FileEnd <= obj.Pos() {
|
||||
file := findEnclosingFile(pkg.Files, obj.Pos())
|
||||
if file == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
file := pkg.Files[fileIndex]
|
||||
|
||||
// Find enclosing declaration.
|
||||
decl := findEnclosingNode(file.Decls, obj.Pos())
|
||||
if decl == nil {
|
||||
|
|
@ -212,6 +198,28 @@ func (collector *Collector) findDeclaration(obj types.Object) (path []ast.Node)
|
|||
}
|
||||
}
|
||||
|
||||
// findEnclosingFile finds the unique file in files, if any, that encloses the given position.
|
||||
func findEnclosingFile(files []*ast.File, pos token.Pos) *ast.File {
|
||||
// Perform a binary search to find the file enclosing the node.
|
||||
// We can't use findEnclosingNode here because it is less accurate and less efficient with files.
|
||||
fileIndex, exact := slices.BinarySearchFunc(files, pos, func(f *ast.File, p token.Pos) int {
|
||||
return cmp.Compare(f.FileStart, p)
|
||||
})
|
||||
|
||||
// If exact is true, pkg.Files[fileIndex] is the file we are looking for;
|
||||
// otherwise, it is the first file whose start position is _after_ obj.Pos().
|
||||
if !exact {
|
||||
fileIndex--
|
||||
}
|
||||
|
||||
// When exact is false, the position might lie within an empty segment in between two files.
|
||||
if fileIndex < 0 || files[fileIndex].FileEnd <= pos {
|
||||
return nil
|
||||
}
|
||||
|
||||
return files[fileIndex]
|
||||
}
|
||||
|
||||
// findEnclosingNode finds the unique node in nodes, if any,
|
||||
// that encloses the given position.
|
||||
//
|
||||
|
|
|
|||
283
v3/internal/generator/collect/events.go
Normal file
283
v3/internal/generator/collect/events.go
Normal file
|
|
@ -0,0 +1,283 @@
|
|||
package collect
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
)
|
||||
|
||||
type (
|
||||
// EventMap holds information about a set of custom events
|
||||
// and their associated data types.
|
||||
//
|
||||
// Read accesses to any public field are only safe
|
||||
// if a call to [EventMap.Collect] has completed before the access,
|
||||
// for example by calling it in the accessing goroutine
|
||||
// or before spawning the accessing goroutine.
|
||||
EventMap struct {
|
||||
Imports *ImportMap
|
||||
Defs []*EventInfo
|
||||
|
||||
registerEvent types.Object
|
||||
collector *Collector
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
// EventInfo holds information about a single event definition.
|
||||
EventInfo struct {
|
||||
// Name is the name of the event.
|
||||
Name string
|
||||
|
||||
// Data is the data type the event has been registered with.
|
||||
// It may be nil in case of conflicting definitions.
|
||||
Data types.Type
|
||||
|
||||
// Pos records the position
|
||||
// of the first discovered definition for this event.
|
||||
Pos token.Position
|
||||
}
|
||||
)
|
||||
|
||||
func newEventMap(collector *Collector, registerEvent types.Object) *EventMap {
|
||||
return &EventMap{
|
||||
registerEvent: registerEvent,
|
||||
collector: collector,
|
||||
}
|
||||
}
|
||||
|
||||
// EventMap returns the unique event map associated with the given collector,
|
||||
// or nil if event collection is disabled.
|
||||
func (collector *Collector) EventMap() *EventMap {
|
||||
return collector.events
|
||||
}
|
||||
|
||||
// Stats returns statistics for this event map.
|
||||
// It is an error to call stats before a call to [EventMap.Collect] has completed.
|
||||
func (em *EventMap) Stats() *Stats {
|
||||
return &Stats{
|
||||
NumEvents: len(em.Defs),
|
||||
}
|
||||
}
|
||||
|
||||
// Collect gathers information for the event map described by its receiver.
|
||||
// It can be called concurrently by multiple goroutines;
|
||||
// the computation will be performed just once.
|
||||
//
|
||||
// Collect returns the receiver for chaining.
|
||||
// It is safe to call Collect with nil receiver.
|
||||
//
|
||||
// After Collect returns, the calling goroutine and all goroutines
|
||||
// it might spawn afterwards are free to access
|
||||
// the receiver's fields indefinitely.
|
||||
func (em *EventMap) Collect() *EventMap {
|
||||
if em == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
em.once.Do(func() {
|
||||
// XXX: initialise the import map with a fake package.
|
||||
// At present this works fine; let's hope it doesn't come back and haunt us later.
|
||||
em.Imports = NewImportMap(&PackageInfo{
|
||||
Path: em.collector.systemPaths.InternalPackage,
|
||||
collector: em.collector,
|
||||
})
|
||||
|
||||
if em.registerEvent == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
defs sync.Map
|
||||
)
|
||||
|
||||
for pkg := range em.collector.Iterate {
|
||||
if !pkg.IsOrImportsApp {
|
||||
// Packages that are not, and do not import, the Wails application package
|
||||
// cannot define any events.
|
||||
continue
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
|
||||
// Process packages in parallel.
|
||||
em.collector.scheduler.Schedule(func() {
|
||||
em.collectEventsInPackage(pkg, &defs)
|
||||
wg.Done()
|
||||
})
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
// Collect valid events.
|
||||
em.Defs = slices.Collect(func(yield func(*EventInfo) bool) {
|
||||
for _, v := range defs.Range {
|
||||
event := v.(*EventInfo)
|
||||
if event.Data != nil && !IsParametric(event.Data) {
|
||||
if !yield(event) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Sort by name, ascending.
|
||||
slices.SortFunc(em.Defs, func(a, b *EventInfo) int {
|
||||
return strings.Compare(a.Name, b.Name)
|
||||
})
|
||||
|
||||
// Record required types.
|
||||
// This must be done at the end because:
|
||||
// - [ImportMap.AddType] does not support concurrent calls, and
|
||||
// - we only know the set of valid events after inspecting all definitions.
|
||||
for _, def := range em.Defs {
|
||||
em.Imports.AddType(def.Data)
|
||||
}
|
||||
})
|
||||
|
||||
return em
|
||||
}
|
||||
|
||||
func (em *EventMap) collectEventsInPackage(pkg *PackageInfo, defs *sync.Map) {
|
||||
for ident, inst := range pkg.TypesInfo.Instances {
|
||||
if pkg.TypesInfo.Uses[ident] != em.registerEvent {
|
||||
continue
|
||||
}
|
||||
|
||||
file := findEnclosingFile(pkg.Collect().Files, ident.Pos())
|
||||
if file == nil {
|
||||
em.collector.logger.Warningf(
|
||||
"package %s: found event declaration with no associated source file",
|
||||
pkg.Path,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
path, _ := astutil.PathEnclosingInterval(file, ident.Pos(), ident.End())
|
||||
if path[0] != ident {
|
||||
em.collector.logger.Warningf(
|
||||
"%v: event declaration not found in source file",
|
||||
pkg.Fset.Position(ident.Pos()),
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
// Walk up the path: *ast.Ident -> *ast.SelectorExpr? -> (*ast.IndexExpr | *ast.IndexListExpr)? -> *ast.CallExpr?
|
||||
path = path[1:]
|
||||
|
||||
if _, ok := path[0].(*ast.SelectorExpr); ok {
|
||||
path = path[1:]
|
||||
}
|
||||
|
||||
if _, ok := path[0].(*ast.IndexExpr); ok {
|
||||
path = path[1:]
|
||||
} else if _, ok := path[0].(*ast.IndexListExpr); ok {
|
||||
path = path[1:]
|
||||
}
|
||||
|
||||
call, ok := path[0].(*ast.CallExpr)
|
||||
if !ok {
|
||||
em.collector.logger.Warningf(
|
||||
"%v: `application.RegisterEvent` is instantiated here but not called",
|
||||
pkg.Fset.Position(path[0].Pos()),
|
||||
)
|
||||
em.collector.logger.Warningf("events registered through indirect calls are not discoverable by the binding generator: it is recommended to invoke `application.RegisterEvent` directly")
|
||||
continue
|
||||
}
|
||||
|
||||
if len(call.Args) == 0 {
|
||||
// Invalid calls result in compile-time failures and can be ignored safely.
|
||||
continue
|
||||
}
|
||||
|
||||
eventName, ok := pkg.TypesInfo.Types[call.Args[0]]
|
||||
if !ok || !types.AssignableTo(eventName.Type, types.Universe.Lookup("string").Type()) {
|
||||
// Mistyped calls result in compile-time failures and can be ignored safely.
|
||||
continue
|
||||
}
|
||||
|
||||
if eventName.Value == nil {
|
||||
em.collector.logger.Warningf(
|
||||
"%v: `application.RegisterEvent` called here with non-constant event name",
|
||||
pkg.Fset.Position(call.Pos()),
|
||||
)
|
||||
em.collector.logger.Warningf("dynamically registered event names are not discoverable by the binding generator: it is recommended to invoke `application.RegisterEvent` with constant arguments only")
|
||||
continue
|
||||
}
|
||||
|
||||
event := &EventInfo{
|
||||
Data: inst.TypeArgs.At(0),
|
||||
Pos: pkg.Fset.Position(call.Pos()),
|
||||
}
|
||||
if eventName.Value.Kind() == constant.String {
|
||||
event.Name = constant.StringVal(eventName.Value)
|
||||
} else {
|
||||
event.Name = eventName.Value.ExactString()
|
||||
}
|
||||
|
||||
if IsKnownEvent(event.Name) {
|
||||
em.collector.logger.Warningf(
|
||||
"%v: event '%s' is a known system event and cannot be overridden; this call to `application.RegisterEvent` will panic",
|
||||
event.Pos,
|
||||
event.Name,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
if v, ok := defs.LoadOrStore(event.Name, event); ok {
|
||||
prev := v.(*EventInfo)
|
||||
if prev.Data != nil && !types.Identical(event.Data, prev.Data) {
|
||||
next := &EventInfo{
|
||||
Name: prev.Name,
|
||||
Pos: prev.Pos,
|
||||
}
|
||||
|
||||
if defs.CompareAndSwap(prev.Name, prev, next) {
|
||||
em.collector.logger.Warningf("event '%s' has multiple conflicting definitions and will be ignored", event.Name)
|
||||
em.collector.logger.Warningf(
|
||||
"%v: event '%s' has one of multiple definitions here with data type %s",
|
||||
prev.Pos,
|
||||
prev.Name,
|
||||
prev.Data,
|
||||
)
|
||||
}
|
||||
|
||||
prev = next
|
||||
}
|
||||
if prev.Data == nil {
|
||||
em.collector.logger.Warningf(
|
||||
"%v: event '%s' has one of multiple definitions here with data type %s",
|
||||
event.Pos,
|
||||
event.Name,
|
||||
event.Data,
|
||||
)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Emit unsupported type warnings only for first definition
|
||||
if IsParametric(event.Data) {
|
||||
em.collector.logger.Warningf(
|
||||
"%v: data type %s for event '%s' contains unresolved type parameters and will be ignored`",
|
||||
event.Pos,
|
||||
event.Data,
|
||||
event.Name,
|
||||
)
|
||||
em.collector.logger.Warningf("generic wrappers for calls to `application.RegisterEvent` are not analysable by the binding generator: it is recommended to call `application.RegisterEvent` with concrete types only")
|
||||
} else if types.IsInterface(event.Data) && !types.Identical(event.Data.Underlying(), typeAny) && !em.collector.IsVoidAlias(event.Data) {
|
||||
em.collector.logger.Warningf(
|
||||
"%v: data type %s for event '%s' is a non-empty interface: emitting events from the frontend with data other than `null` is not supported by encoding/json and will likely result in runtime errors",
|
||||
event.Pos,
|
||||
event.Data,
|
||||
event.Name,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -147,6 +147,11 @@ func (imports *ImportMap) addTypeImpl(typ types.Type, visited *typeutil.Map) {
|
|||
return
|
||||
}
|
||||
|
||||
// Special case: application.Void will render as TS void hence no dependencies and no model
|
||||
if collector.IsVoidAlias(obj) {
|
||||
return
|
||||
}
|
||||
|
||||
if obj.Pkg().Path() == imports.Self {
|
||||
imports.ImportModels = true
|
||||
}
|
||||
|
|
@ -260,7 +265,7 @@ func (imports *ImportMap) addTypeImpl(typ types.Type, visited *typeutil.Map) {
|
|||
return
|
||||
|
||||
default:
|
||||
collector.logger.Warningf("package %s: unknown type %s: please report this to Wails maintainers", imports.Self, typ)
|
||||
collector.logger.Warningf("package %s: unknown or unexpected type %s: please report this to Wails maintainers", imports.Self, typ)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
|||
220
v3/internal/generator/collect/known_events.go
Normal file
220
v3/internal/generator/collect/known_events.go
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
package collect
|
||||
|
||||
func IsKnownEvent(name string) bool {
|
||||
_, ok := knownEvents[name]
|
||||
return ok
|
||||
}
|
||||
|
||||
var knownEvents = map[string]struct{}{
|
||||
"common:ApplicationOpenedWithFile": {},
|
||||
"common:ApplicationStarted": {},
|
||||
"common:ApplicationLaunchedWithUrl": {},
|
||||
"common:ThemeChanged": {},
|
||||
"common:WindowClosing": {},
|
||||
"common:WindowDidMove": {},
|
||||
"common:WindowDidResize": {},
|
||||
"common:WindowDPIChanged": {},
|
||||
"common:WindowFilesDropped": {},
|
||||
"common:WindowFocus": {},
|
||||
"common:WindowFullscreen": {},
|
||||
"common:WindowHide": {},
|
||||
"common:WindowLostFocus": {},
|
||||
"common:WindowMaximise": {},
|
||||
"common:WindowMinimise": {},
|
||||
"common:WindowToggleFrameless": {},
|
||||
"common:WindowRestore": {},
|
||||
"common:WindowRuntimeReady": {},
|
||||
"common:WindowShow": {},
|
||||
"common:WindowUnFullscreen": {},
|
||||
"common:WindowUnMaximise": {},
|
||||
"common:WindowUnMinimise": {},
|
||||
"common:WindowZoom": {},
|
||||
"common:WindowZoomIn": {},
|
||||
"common:WindowZoomOut": {},
|
||||
"common:WindowZoomReset": {},
|
||||
"common:WindowDropZoneFilesDropped": {},
|
||||
"linux:ApplicationStartup": {},
|
||||
"linux:SystemThemeChanged": {},
|
||||
"linux:WindowDeleteEvent": {},
|
||||
"linux:WindowDidMove": {},
|
||||
"linux:WindowDidResize": {},
|
||||
"linux:WindowFocusIn": {},
|
||||
"linux:WindowFocusOut": {},
|
||||
"linux:WindowLoadChanged": {},
|
||||
"mac:ApplicationDidBecomeActive": {},
|
||||
"mac:ApplicationDidChangeBackingProperties": {},
|
||||
"mac:ApplicationDidChangeEffectiveAppearance": {},
|
||||
"mac:ApplicationDidChangeIcon": {},
|
||||
"mac:ApplicationDidChangeOcclusionState": {},
|
||||
"mac:ApplicationDidChangeScreenParameters": {},
|
||||
"mac:ApplicationDidChangeStatusBarFrame": {},
|
||||
"mac:ApplicationDidChangeStatusBarOrientation": {},
|
||||
"mac:ApplicationDidChangeTheme": {},
|
||||
"mac:ApplicationDidFinishLaunching": {},
|
||||
"mac:ApplicationDidHide": {},
|
||||
"mac:ApplicationDidResignActive": {},
|
||||
"mac:ApplicationDidUnhide": {},
|
||||
"mac:ApplicationDidUpdate": {},
|
||||
"mac:ApplicationShouldHandleReopen": {},
|
||||
"mac:ApplicationWillBecomeActive": {},
|
||||
"mac:ApplicationWillFinishLaunching": {},
|
||||
"mac:ApplicationWillHide": {},
|
||||
"mac:ApplicationWillResignActive": {},
|
||||
"mac:ApplicationWillTerminate": {},
|
||||
"mac:ApplicationWillUnhide": {},
|
||||
"mac:ApplicationWillUpdate": {},
|
||||
"mac:MenuDidAddItem": {},
|
||||
"mac:MenuDidBeginTracking": {},
|
||||
"mac:MenuDidClose": {},
|
||||
"mac:MenuDidDisplayItem": {},
|
||||
"mac:MenuDidEndTracking": {},
|
||||
"mac:MenuDidHighlightItem": {},
|
||||
"mac:MenuDidOpen": {},
|
||||
"mac:MenuDidPopUp": {},
|
||||
"mac:MenuDidRemoveItem": {},
|
||||
"mac:MenuDidSendAction": {},
|
||||
"mac:MenuDidSendActionToItem": {},
|
||||
"mac:MenuDidUpdate": {},
|
||||
"mac:MenuWillAddItem": {},
|
||||
"mac:MenuWillBeginTracking": {},
|
||||
"mac:MenuWillDisplayItem": {},
|
||||
"mac:MenuWillEndTracking": {},
|
||||
"mac:MenuWillHighlightItem": {},
|
||||
"mac:MenuWillOpen": {},
|
||||
"mac:MenuWillPopUp": {},
|
||||
"mac:MenuWillRemoveItem": {},
|
||||
"mac:MenuWillSendAction": {},
|
||||
"mac:MenuWillSendActionToItem": {},
|
||||
"mac:MenuWillUpdate": {},
|
||||
"mac:WebViewDidCommitNavigation": {},
|
||||
"mac:WebViewDidFinishNavigation": {},
|
||||
"mac:WebViewDidReceiveServerRedirectForProvisionalNavigation": {},
|
||||
"mac:WebViewDidStartProvisionalNavigation": {},
|
||||
"mac:WindowDidBecomeKey": {},
|
||||
"mac:WindowDidBecomeMain": {},
|
||||
"mac:WindowDidBeginSheet": {},
|
||||
"mac:WindowDidChangeAlpha": {},
|
||||
"mac:WindowDidChangeBackingLocation": {},
|
||||
"mac:WindowDidChangeBackingProperties": {},
|
||||
"mac:WindowDidChangeCollectionBehavior": {},
|
||||
"mac:WindowDidChangeEffectiveAppearance": {},
|
||||
"mac:WindowDidChangeOcclusionState": {},
|
||||
"mac:WindowDidChangeOrderingMode": {},
|
||||
"mac:WindowDidChangeScreen": {},
|
||||
"mac:WindowDidChangeScreenParameters": {},
|
||||
"mac:WindowDidChangeScreenProfile": {},
|
||||
"mac:WindowDidChangeScreenSpace": {},
|
||||
"mac:WindowDidChangeScreenSpaceProperties": {},
|
||||
"mac:WindowDidChangeSharingType": {},
|
||||
"mac:WindowDidChangeSpace": {},
|
||||
"mac:WindowDidChangeSpaceOrderingMode": {},
|
||||
"mac:WindowDidChangeTitle": {},
|
||||
"mac:WindowDidChangeToolbar": {},
|
||||
"mac:WindowDidDeminiaturize": {},
|
||||
"mac:WindowDidEndSheet": {},
|
||||
"mac:WindowDidEnterFullScreen": {},
|
||||
"mac:WindowDidEnterVersionBrowser": {},
|
||||
"mac:WindowDidExitFullScreen": {},
|
||||
"mac:WindowDidExitVersionBrowser": {},
|
||||
"mac:WindowDidExpose": {},
|
||||
"mac:WindowDidFocus": {},
|
||||
"mac:WindowDidMiniaturize": {},
|
||||
"mac:WindowDidMove": {},
|
||||
"mac:WindowDidOrderOffScreen": {},
|
||||
"mac:WindowDidOrderOnScreen": {},
|
||||
"mac:WindowDidResignKey": {},
|
||||
"mac:WindowDidResignMain": {},
|
||||
"mac:WindowDidResize": {},
|
||||
"mac:WindowDidUpdate": {},
|
||||
"mac:WindowDidUpdateAlpha": {},
|
||||
"mac:WindowDidUpdateCollectionBehavior": {},
|
||||
"mac:WindowDidUpdateCollectionProperties": {},
|
||||
"mac:WindowDidUpdateShadow": {},
|
||||
"mac:WindowDidUpdateTitle": {},
|
||||
"mac:WindowDidUpdateToolbar": {},
|
||||
"mac:WindowDidZoom": {},
|
||||
"mac:WindowFileDraggingEntered": {},
|
||||
"mac:WindowFileDraggingExited": {},
|
||||
"mac:WindowFileDraggingPerformed": {},
|
||||
"mac:WindowHide": {},
|
||||
"mac:WindowMaximise": {},
|
||||
"mac:WindowUnMaximise": {},
|
||||
"mac:WindowMinimise": {},
|
||||
"mac:WindowUnMinimise": {},
|
||||
"mac:WindowShouldClose": {},
|
||||
"mac:WindowShow": {},
|
||||
"mac:WindowWillBecomeKey": {},
|
||||
"mac:WindowWillBecomeMain": {},
|
||||
"mac:WindowWillBeginSheet": {},
|
||||
"mac:WindowWillChangeOrderingMode": {},
|
||||
"mac:WindowWillClose": {},
|
||||
"mac:WindowWillDeminiaturize": {},
|
||||
"mac:WindowWillEnterFullScreen": {},
|
||||
"mac:WindowWillEnterVersionBrowser": {},
|
||||
"mac:WindowWillExitFullScreen": {},
|
||||
"mac:WindowWillExitVersionBrowser": {},
|
||||
"mac:WindowWillFocus": {},
|
||||
"mac:WindowWillMiniaturize": {},
|
||||
"mac:WindowWillMove": {},
|
||||
"mac:WindowWillOrderOffScreen": {},
|
||||
"mac:WindowWillOrderOnScreen": {},
|
||||
"mac:WindowWillResignMain": {},
|
||||
"mac:WindowWillResize": {},
|
||||
"mac:WindowWillUnfocus": {},
|
||||
"mac:WindowWillUpdate": {},
|
||||
"mac:WindowWillUpdateAlpha": {},
|
||||
"mac:WindowWillUpdateCollectionBehavior": {},
|
||||
"mac:WindowWillUpdateCollectionProperties": {},
|
||||
"mac:WindowWillUpdateShadow": {},
|
||||
"mac:WindowWillUpdateTitle": {},
|
||||
"mac:WindowWillUpdateToolbar": {},
|
||||
"mac:WindowWillUpdateVisibility": {},
|
||||
"mac:WindowWillUseStandardFrame": {},
|
||||
"mac:WindowZoomIn": {},
|
||||
"mac:WindowZoomOut": {},
|
||||
"mac:WindowZoomReset": {},
|
||||
"windows:APMPowerSettingChange": {},
|
||||
"windows:APMPowerStatusChange": {},
|
||||
"windows:APMResumeAutomatic": {},
|
||||
"windows:APMResumeSuspend": {},
|
||||
"windows:APMSuspend": {},
|
||||
"windows:ApplicationStarted": {},
|
||||
"windows:SystemThemeChanged": {},
|
||||
"windows:WebViewNavigationCompleted": {},
|
||||
"windows:WindowActive": {},
|
||||
"windows:WindowBackgroundErase": {},
|
||||
"windows:WindowClickActive": {},
|
||||
"windows:WindowClosing": {},
|
||||
"windows:WindowDidMove": {},
|
||||
"windows:WindowDidResize": {},
|
||||
"windows:WindowDPIChanged": {},
|
||||
"windows:WindowDragDrop": {},
|
||||
"windows:WindowDragEnter": {},
|
||||
"windows:WindowDragLeave": {},
|
||||
"windows:WindowDragOver": {},
|
||||
"windows:WindowEndMove": {},
|
||||
"windows:WindowEndResize": {},
|
||||
"windows:WindowFullscreen": {},
|
||||
"windows:WindowHide": {},
|
||||
"windows:WindowInactive": {},
|
||||
"windows:WindowKeyDown": {},
|
||||
"windows:WindowKeyUp": {},
|
||||
"windows:WindowKillFocus": {},
|
||||
"windows:WindowNonClientHit": {},
|
||||
"windows:WindowNonClientMouseDown": {},
|
||||
"windows:WindowNonClientMouseLeave": {},
|
||||
"windows:WindowNonClientMouseMove": {},
|
||||
"windows:WindowNonClientMouseUp": {},
|
||||
"windows:WindowPaint": {},
|
||||
"windows:WindowRestore": {},
|
||||
"windows:WindowSetFocus": {},
|
||||
"windows:WindowShow": {},
|
||||
"windows:WindowStartMove": {},
|
||||
"windows:WindowStartResize": {},
|
||||
"windows:WindowUnFullscreen": {},
|
||||
"windows:WindowZOrderChanged": {},
|
||||
"windows:WindowMinimise": {},
|
||||
"windows:WindowUnMinimise": {},
|
||||
"windows:WindowMaximise": {},
|
||||
"windows:WindowUnMaximise": {},
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@ import (
|
|||
|
||||
// PackageInfo records information about a package.
|
||||
//
|
||||
// Read accesses to fields Path, Name, Types, TypesInfo, Fset
|
||||
// Read accesses to fields Path, Name, IsOrImportsApp, Types, TypesInfo, Fset,
|
||||
// are safe at any time without any synchronisation.
|
||||
//
|
||||
// Read accesses to all other fields are only safe
|
||||
|
|
@ -32,6 +32,9 @@ type PackageInfo struct {
|
|||
// Name holds the import name of the described package.
|
||||
Name string
|
||||
|
||||
// IsOrImportsApp is true if this package is, or depends upon, the Wails application package.
|
||||
IsOrImportsApp bool
|
||||
|
||||
// Types and TypesInfo hold type information for this package.
|
||||
Types *types.Package
|
||||
TypesInfo *types.Info
|
||||
|
|
@ -73,10 +76,14 @@ type PackageInfo struct {
|
|||
}
|
||||
|
||||
func newPackageInfo(pkg *packages.Package, collector *Collector) *PackageInfo {
|
||||
_, importsApp := pkg.Imports[collector.systemPaths.ApplicationPackage]
|
||||
|
||||
return &PackageInfo{
|
||||
Path: pkg.PkgPath,
|
||||
Name: pkg.Name,
|
||||
|
||||
IsOrImportsApp: importsApp || pkg.PkgPath == collector.systemPaths.ApplicationPackage,
|
||||
|
||||
Types: pkg.Types,
|
||||
TypesInfo: pkg.TypesInfo,
|
||||
|
||||
|
|
@ -87,7 +94,7 @@ func newPackageInfo(pkg *packages.Package, collector *Collector) *PackageInfo {
|
|||
}
|
||||
}
|
||||
|
||||
// Package retrieves the the unique [PackageInfo] instance, if any,
|
||||
// Package retrieves the unique [PackageInfo] instance, if any,
|
||||
// associated to the given package object within a Collector.
|
||||
//
|
||||
// Package is safe for concurrent use.
|
||||
|
|
|
|||
|
|
@ -476,3 +476,123 @@ func IsAny(typ types.Type) bool {
|
|||
|
||||
return true
|
||||
}
|
||||
|
||||
// IsParametric returns true if the given type
|
||||
// contains unresolved type parameters.
|
||||
func IsParametric(typ types.Type) bool {
|
||||
for { // Avoid recursion where possible
|
||||
switch t := typ.(type) {
|
||||
case *types.Alias, *types.Named:
|
||||
tp := t.(interface{ TypeParams() *types.TypeParamList }).TypeParams()
|
||||
ta := t.(interface{ TypeArgs() *types.TypeList }).TypeArgs()
|
||||
|
||||
if tp.Len() == 0 {
|
||||
// Not a generic alias/named type.
|
||||
return false
|
||||
}
|
||||
|
||||
if ta.Len() == 0 {
|
||||
// Uninstantiated generic.
|
||||
return true
|
||||
}
|
||||
|
||||
for i := range ta.Len() - 1 {
|
||||
if IsParametric(ta.At(i)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
typ = ta.At(ta.Len() - 1)
|
||||
|
||||
case *types.Basic:
|
||||
return false
|
||||
|
||||
case *types.Array, *types.Pointer, *types.Slice, *types.Chan:
|
||||
typ = typ.(interface{ Elem() types.Type }).Elem()
|
||||
|
||||
case *types.Map:
|
||||
if IsParametric(t.Key()) {
|
||||
return true
|
||||
}
|
||||
|
||||
typ = t.Elem()
|
||||
|
||||
case *types.Signature:
|
||||
if IsParametric(t.Params()) {
|
||||
return true
|
||||
}
|
||||
|
||||
typ = t.Results()
|
||||
|
||||
case *types.Struct:
|
||||
if t.NumFields() == 0 {
|
||||
// No more subtypes to check.
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range t.NumFields() - 1 {
|
||||
if IsParametric(t.Field(i).Type()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
typ = t.Field(t.NumFields() - 1).Type()
|
||||
|
||||
case *types.Interface:
|
||||
for m := range t.ExplicitMethods() {
|
||||
if IsParametric(m.Type()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if t.NumEmbeddeds() == 0 {
|
||||
// No more subtypes to check.
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range t.NumEmbeddeds() - 1 {
|
||||
if IsParametric(t.EmbeddedType(i)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
typ = t.EmbeddedType(t.NumEmbeddeds() - 1)
|
||||
|
||||
case *types.Tuple:
|
||||
if t.Len() == 0 {
|
||||
// No more subtypes to check.
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range t.Len() - 1 {
|
||||
if IsParametric(t.At(i).Type()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
typ = t.At(t.Len() - 1).Type()
|
||||
|
||||
case *types.TypeParam:
|
||||
return true
|
||||
|
||||
case *types.Union:
|
||||
if t.Len() == 0 {
|
||||
// No more subtypes to check.
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range t.Len() - 1 {
|
||||
if IsParametric(t.Term(i).Type()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
typ = t.Term(t.Len() - 1).Type()
|
||||
|
||||
default:
|
||||
// Unknown new type.
|
||||
// This is wrong but [ImportMap.AddType] will take care of reporting it eventually.
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -282,14 +282,14 @@ func (info *ServiceInfo) collectMethod(method *types.Func) *ServiceMethodInfo {
|
|||
}
|
||||
}
|
||||
|
||||
if types.IsInterface(param.Type()) && !types.Identical(param.Type(), typeAny) {
|
||||
if types.IsInterface(param.Type()) && !types.Identical(param.Type().Underlying(), typeAny) {
|
||||
paramName := param.Name()
|
||||
if paramName == "" || paramName == "_" {
|
||||
paramName = fmt.Sprintf("#%d", i+1)
|
||||
}
|
||||
|
||||
collector.logger.Warningf(
|
||||
"%s: parameter %s has non-empty interface type %s: this is not supported by encoding/json and will likely result in runtime errors",
|
||||
"%s: parameter %s has non-empty interface type %s: passing values other than `null` is not supported by encoding/json and will likely result in runtime errors",
|
||||
collector.Package(method.Pkg()).Fset.Position(param.Pos()),
|
||||
paramName,
|
||||
param.Type(),
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ type Stats struct {
|
|||
NumMethods int
|
||||
NumEnums int
|
||||
NumModels int
|
||||
NumEvents int
|
||||
StartTime time.Time
|
||||
EndTime time.Time
|
||||
}
|
||||
|
|
@ -31,4 +32,5 @@ func (stats *Stats) Add(other *Stats) {
|
|||
stats.NumMethods += other.NumMethods
|
||||
stats.NumEnums += other.NumEnums
|
||||
stats.NumModels += other.NumModels
|
||||
stats.NumEvents += other.NumEvents
|
||||
}
|
||||
|
|
|
|||
32
v3/internal/generator/collect/void.go
Normal file
32
v3/internal/generator/collect/void.go
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package collect
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// appVoidType caches the application.Void named type that stands in for the void TS type.
|
||||
var appVoidType atomic.Value
|
||||
|
||||
// IsVoidAlias returns true when the given type or object is the application.Void named type that stands in for the void TS type.
|
||||
func (collector *Collector) IsVoidAlias(typOrObj any) bool {
|
||||
var obj types.Object
|
||||
switch to := typOrObj.(type) {
|
||||
case types.Object:
|
||||
obj = to
|
||||
case interface{ Obj() *types.TypeName }:
|
||||
obj = to.Obj()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
if vt := appVoidType.Load(); obj == vt {
|
||||
return true
|
||||
} else if vt == nil && obj.Name() == "Void" && obj.Pkg().Path() == collector.systemPaths.ApplicationPackage { // Check name before package to fail fast
|
||||
// Cache void alias for faster checking
|
||||
appVoidType.Store(obj)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
@ -3,8 +3,12 @@ package config
|
|||
// WailsAppPkgPath is the official import path of Wails v3's application package.
|
||||
const WailsAppPkgPath = "github.com/wailsapp/wails/v3/pkg/application"
|
||||
|
||||
// WailsInternalPkgPath is the official import path of Wails v3's internal package.
|
||||
const WailsInternalPkgPath = "github.com/wailsapp/wails/v3/internal"
|
||||
|
||||
// SystemPaths holds resolved paths of required system packages.
|
||||
type SystemPaths struct {
|
||||
ContextPackage string
|
||||
ApplicationPackage string
|
||||
InternalPackage string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,11 @@ var ErrNoContextPackage = errors.New("standard context package not found at cano
|
|||
// did not match any actual package.
|
||||
var ErrNoApplicationPackage = errors.New("Wails application package not found at canonical import path ('" + config.WailsAppPkgPath + "'): is the Wails v3 module properly installed? ")
|
||||
|
||||
// ErrNoInternalPackage indicates that
|
||||
// the canonical path for the Wails internal package
|
||||
// did not match any actual package.
|
||||
var ErrNoInternalPackage = errors.New("Wails internal package not found at canonical import path ('" + config.WailsInternalPkgPath + "'): is the Wails v3 module properly installed? ")
|
||||
|
||||
// ErrBadApplicationPackage indicates that
|
||||
// the Wails application package has invalid content.
|
||||
var ErrBadApplicationPackage = errors.New("package " + config.WailsAppPkgPath + ": function NewService has wrong signature: is the Wails v3 module properly installed? ")
|
||||
|
|
|
|||
53
v3/internal/generator/events.go
Normal file
53
v3/internal/generator/events.go
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
package generator
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/wailsapp/wails/v3/internal/generator/collect"
|
||||
)
|
||||
|
||||
func (generator *Generator) generateEvents(events *collect.EventMap) {
|
||||
// Generate event data table.
|
||||
generator.scheduler.Schedule(func() {
|
||||
file, err := generator.creator.Create(filepath.Join(events.Imports.Self, generator.renderer.EventDataFile()))
|
||||
if err != nil {
|
||||
generator.logger.Errorf("%v", err)
|
||||
generator.logger.Errorf("event data table generation failed")
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err := file.Close(); err != nil {
|
||||
generator.logger.Errorf("%v", err)
|
||||
generator.logger.Errorf("event data table generation failed")
|
||||
}
|
||||
}()
|
||||
|
||||
err = generator.renderer.EventData(file, events)
|
||||
if err != nil {
|
||||
generator.logger.Errorf("%v", err)
|
||||
generator.logger.Errorf("event data table generation failed")
|
||||
}
|
||||
})
|
||||
|
||||
// Generate event creation code.
|
||||
generator.scheduler.Schedule(func() {
|
||||
file, err := generator.creator.Create(filepath.Join(events.Imports.Self, generator.renderer.EventCreateFile()))
|
||||
if err != nil {
|
||||
generator.logger.Errorf("%v", err)
|
||||
generator.logger.Errorf("event creation code generation failed")
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err := file.Close(); err != nil {
|
||||
generator.logger.Errorf("%v", err)
|
||||
generator.logger.Errorf("event creation code generation failed")
|
||||
}
|
||||
}()
|
||||
|
||||
err = generator.renderer.EventCreate(file, events)
|
||||
if err != nil {
|
||||
generator.logger.Errorf("%v", err)
|
||||
generator.logger.Errorf("event creation code generation failed")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -132,16 +132,20 @@ func (generator *Generator) Generate(patterns ...string) (stats *collect.Stats,
|
|||
panic("Generate() must not be called more than once on the same receiver")
|
||||
}
|
||||
|
||||
// Initialise subcomponents.
|
||||
generator.collector = collect.NewCollector(pkgs, systemPaths, generator.options, &generator.scheduler, generator.logger)
|
||||
generator.renderer = render.NewRenderer(generator.options, generator.collector)
|
||||
|
||||
// Update status.
|
||||
generator.logger.Statusf("Looking for services...")
|
||||
serviceFound := sync.OnceFunc(func() { generator.logger.Statusf("Generating service bindings...") })
|
||||
if generator.options.NoEvents {
|
||||
generator.logger.Statusf("Looking for services...")
|
||||
} else {
|
||||
generator.logger.Statusf("Looking for services and events...")
|
||||
}
|
||||
serviceOrEventFound := sync.OnceFunc(func() { generator.logger.Statusf("Generating bindings...") })
|
||||
|
||||
// Run static analysis.
|
||||
services, err := FindServices(pkgs, systemPaths, generator.logger)
|
||||
services, registerEvent, err := FindServices(pkgs, systemPaths, generator.logger)
|
||||
|
||||
// Initialise subcomponents.
|
||||
generator.collector = collect.NewCollector(pkgs, registerEvent, systemPaths, generator.options, &generator.scheduler, generator.logger)
|
||||
generator.renderer = render.NewRenderer(generator.collector, generator.options)
|
||||
|
||||
// Check for analyser errors.
|
||||
if err != nil {
|
||||
|
|
@ -151,9 +155,23 @@ func (generator *Generator) Generate(patterns ...string) (stats *collect.Stats,
|
|||
// Discard unneeded data.
|
||||
pkgs = nil
|
||||
|
||||
// Schedule collection and code generation for event data types.
|
||||
if !generator.options.NoEvents {
|
||||
generator.scheduler.Schedule(func() {
|
||||
events := generator.collector.EventMap().Collect()
|
||||
if len(events.Defs) > 0 {
|
||||
serviceOrEventFound()
|
||||
// Not a data race because we wait for this scheduled task
|
||||
// to complete before accessing stats again.
|
||||
stats.Add(events.Stats())
|
||||
}
|
||||
generator.generateEvents(events)
|
||||
})
|
||||
}
|
||||
|
||||
// Schedule code generation for each found service.
|
||||
for obj := range services {
|
||||
serviceFound()
|
||||
serviceOrEventFound()
|
||||
generator.scheduler.Schedule(func() {
|
||||
generator.generateService(obj)
|
||||
})
|
||||
|
|
@ -175,9 +193,9 @@ func (generator *Generator) Generate(patterns ...string) (stats *collect.Stats,
|
|||
}
|
||||
|
||||
// Schedule models, index and included files generation for each package.
|
||||
for info := range generator.collector.Iterate {
|
||||
for pkg := range generator.collector.Iterate {
|
||||
generator.scheduler.Schedule(func() {
|
||||
generator.generateModelsIndexIncludes(info)
|
||||
generator.generateModelsIndexIncludes(pkg)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -201,15 +219,15 @@ func (generator *Generator) Generate(patterns ...string) (stats *collect.Stats,
|
|||
// generateModelsIndexIncludes schedules generation of public/private model files,
|
||||
// included files and, if allowed by the options,
|
||||
// of an index file for the given package.
|
||||
func (generator *Generator) generateModelsIndexIncludes(info *collect.PackageInfo) {
|
||||
index := info.Index(generator.options.TS)
|
||||
func (generator *Generator) generateModelsIndexIncludes(pkg *collect.PackageInfo) {
|
||||
index := pkg.Index(generator.options.TS)
|
||||
|
||||
// info.Index implies info.Collect: goroutines spawned below
|
||||
// can access package information freely.
|
||||
|
||||
if len(index.Models) > 0 {
|
||||
generator.scheduler.Schedule(func() {
|
||||
generator.generateModels(info, index.Models)
|
||||
generator.generateModels(pkg, index.Models)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package generator
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/wailsapp/wails/v3/internal/generator/render"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
|
|
@ -17,6 +16,7 @@ import (
|
|||
"github.com/pterm/pterm"
|
||||
"github.com/wailsapp/wails/v3/internal/flags"
|
||||
"github.com/wailsapp/wails/v3/internal/generator/config"
|
||||
"github.com/wailsapp/wails/v3/internal/generator/render"
|
||||
)
|
||||
|
||||
const testcases = "github.com/wailsapp/wails/v3/internal/generator/testcases/..."
|
||||
|
|
@ -73,7 +73,7 @@ func TestGenerator(t *testing.T) {
|
|||
}
|
||||
|
||||
// Skip got files.
|
||||
if strings.HasSuffix(d.Name(), ".got.js") || strings.HasSuffix(d.Name(), ".got.ts") {
|
||||
if strings.HasSuffix(d.Name(), ".got.js") || strings.HasSuffix(d.Name(), ".got.ts") || strings.HasSuffix(d.Name(), ".got.log") {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -116,6 +116,35 @@ func TestGenerator(t *testing.T) {
|
|||
warnings := report.Warnings()
|
||||
slices.Sort(warnings)
|
||||
|
||||
// Normalize paths in warnings to be relative to the testcases directory
|
||||
// This ensures consistent output across different development environments and CI
|
||||
for i, msg := range warnings {
|
||||
// Handle both Unix and Windows path separators
|
||||
msg = strings.ReplaceAll(msg, "\\", "/")
|
||||
|
||||
// Check if this is a file path (contains line:column position)
|
||||
// File paths look like: /path/to/file.go:123:45: message
|
||||
// Package paths look like: package github.com/...: message
|
||||
if strings.HasPrefix(msg, "package ") {
|
||||
// Keep package warnings as-is
|
||||
warnings[i] = msg
|
||||
} else if idx := strings.Index(msg, "testcases/"); idx >= 0 {
|
||||
// Check if it's a file path by looking for :line:column pattern after testcases/
|
||||
testcasesEnd := idx + len("testcases/")
|
||||
colonIdx := strings.Index(msg[testcasesEnd:], ":")
|
||||
if colonIdx > 0 {
|
||||
// This looks like a file path, normalize it
|
||||
warnings[i] = "/testcases/" + msg[testcasesEnd:]
|
||||
} else {
|
||||
// Not a file path, keep as-is
|
||||
warnings[i] = msg
|
||||
}
|
||||
} else {
|
||||
// Keep other warnings as-is
|
||||
warnings[i] = msg
|
||||
}
|
||||
}
|
||||
|
||||
for _, msg := range warnings {
|
||||
fmt.Fprint(log, msg, render.Newline)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import (
|
|||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
// ResolveSystemPaths resolves paths for the Wails system
|
||||
// ResolveSystemPaths resolves paths for stdlib and Wails packages.
|
||||
func ResolveSystemPaths(buildFlags []string) (paths *config.SystemPaths, err error) {
|
||||
// Resolve context pkg path.
|
||||
contextPkgPaths, err := ResolvePatterns(buildFlags, "context")
|
||||
|
|
@ -35,9 +35,22 @@ func ResolveSystemPaths(buildFlags []string) (paths *config.SystemPaths, err err
|
|||
panic("wails application package path matched multiple packages")
|
||||
}
|
||||
|
||||
// Resolve wails internal pkg path.
|
||||
wailsInternalPkgPaths, err := ResolvePatterns(buildFlags, config.WailsInternalPkgPath)
|
||||
if err != nil {
|
||||
return
|
||||
} else if len(wailsInternalPkgPaths) < 1 {
|
||||
err = ErrNoInternalPackage
|
||||
return
|
||||
} else if len(wailsInternalPkgPaths) > 1 {
|
||||
// This should never happen...
|
||||
panic("wails internal package path matched multiple packages")
|
||||
}
|
||||
|
||||
paths = &config.SystemPaths{
|
||||
ContextPackage: contextPkgPaths[0],
|
||||
ApplicationPackage: wailsAppPkgPaths[0],
|
||||
InternalPackage: wailsInternalPkgPaths[0],
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,10 @@ func (m *module) NeedsCreate(typ types.Type) bool {
|
|||
func (m *module) needsCreateImpl(typ types.Type, visited *typeutil.Map) bool {
|
||||
switch t := typ.(type) {
|
||||
case *types.Alias:
|
||||
if m.collector.IsVoidAlias(t.Obj()) {
|
||||
return false
|
||||
}
|
||||
|
||||
return m.needsCreateImpl(types.Unalias(typ), visited)
|
||||
|
||||
case *types.Named:
|
||||
|
|
@ -46,6 +50,10 @@ func (m *module) needsCreateImpl(typ types.Type, visited *typeutil.Map) bool {
|
|||
return m.needsCreateImpl(t.Underlying(), visited)
|
||||
}
|
||||
|
||||
if m.collector.IsVoidAlias(t.Obj()) {
|
||||
return false
|
||||
}
|
||||
|
||||
if collect.IsAny(typ) || collect.IsStringAlias(typ) {
|
||||
break
|
||||
} else if collect.IsClass(typ) {
|
||||
|
|
@ -97,13 +105,17 @@ func (m *module) JSCreate(typ types.Type) string {
|
|||
// JSCreateWithParams's output may be incorrect
|
||||
// if m.Imports.AddType has not been called for the given type.
|
||||
func (m *module) JSCreateWithParams(typ types.Type, params string) string {
|
||||
if len(params) > 0 && !m.hasTypeParams(typ) {
|
||||
if len(params) > 0 && !collect.IsParametric(typ) {
|
||||
// Forget params for non-generic types.
|
||||
params = ""
|
||||
}
|
||||
|
||||
switch t := typ.(type) {
|
||||
case *types.Alias:
|
||||
if m.collector.IsVoidAlias(t.Obj()) {
|
||||
return "$Create.Any"
|
||||
}
|
||||
|
||||
return m.JSCreateWithParams(types.Unalias(typ), params)
|
||||
|
||||
case *types.Array, *types.Pointer:
|
||||
|
|
@ -135,6 +147,10 @@ func (m *module) JSCreateWithParams(typ types.Type, params string) string {
|
|||
return m.JSCreateWithParams(t.Underlying(), params)
|
||||
}
|
||||
|
||||
if m.collector.IsVoidAlias(t.Obj()) {
|
||||
return "$Create.Any"
|
||||
}
|
||||
|
||||
if !m.NeedsCreate(typ) {
|
||||
break
|
||||
}
|
||||
|
|
@ -341,51 +357,3 @@ type postponed struct {
|
|||
index int
|
||||
params string
|
||||
}
|
||||
|
||||
// hasTypeParams returns true if the given type depends upon type parameters.
|
||||
func (m *module) hasTypeParams(typ types.Type) bool {
|
||||
switch t := typ.(type) {
|
||||
case *types.Alias:
|
||||
if t.Obj().Pkg() == nil {
|
||||
// Builtin alias: these are never rendered as templates.
|
||||
return false
|
||||
}
|
||||
|
||||
return m.hasTypeParams(types.Unalias(typ))
|
||||
|
||||
case *types.Array, *types.Pointer, *types.Slice:
|
||||
return m.hasTypeParams(typ.(interface{ Elem() types.Type }).Elem())
|
||||
|
||||
case *types.Map:
|
||||
return m.hasTypeParams(t.Key()) || m.hasTypeParams(t.Elem())
|
||||
|
||||
case *types.Named:
|
||||
if t.Obj().Pkg() == nil {
|
||||
// Builtin named type: these are never rendered as templates.
|
||||
return false
|
||||
}
|
||||
|
||||
if targs := t.TypeArgs(); targs != nil {
|
||||
for i := range targs.Len() {
|
||||
if m.hasTypeParams(targs.At(i)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case *types.Struct:
|
||||
info := m.collector.Struct(t)
|
||||
info.Collect()
|
||||
|
||||
for _, field := range info.Fields {
|
||||
if m.hasTypeParams(field.Type) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
case *types.TypeParam:
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,31 +14,31 @@ import (
|
|||
// Renderer holds the template set for a given configuration.
|
||||
// It provides methods for rendering various output modules.
|
||||
type Renderer struct {
|
||||
options *flags.GenerateBindingsOptions
|
||||
collector *collect.Collector
|
||||
options *flags.GenerateBindingsOptions
|
||||
|
||||
ext string
|
||||
|
||||
service *template.Template
|
||||
typedefs *template.Template
|
||||
service *template.Template
|
||||
models *template.Template
|
||||
}
|
||||
|
||||
// NewRenderer initialises a code renderer
|
||||
// for the given configuration and data collector.
|
||||
func NewRenderer(options *flags.GenerateBindingsOptions, collector *collect.Collector) *Renderer {
|
||||
func NewRenderer(collector *collect.Collector, options *flags.GenerateBindingsOptions) *Renderer {
|
||||
ext := ".js"
|
||||
if options.TS {
|
||||
ext = ".ts"
|
||||
}
|
||||
|
||||
return &Renderer{
|
||||
options: options,
|
||||
collector: collector,
|
||||
options: options,
|
||||
|
||||
ext: ext,
|
||||
|
||||
service: tmplService[tmplLanguage(options.TS)],
|
||||
typedefs: tmplModels[tmplLanguage(options.TS)],
|
||||
service: tmplService[tmplLanguage(options.TS)],
|
||||
models: tmplModels[tmplLanguage(options.TS)],
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -54,6 +54,18 @@ func (renderer *Renderer) ModelsFile() string {
|
|||
return renderer.options.ModelsFilename + renderer.ext
|
||||
}
|
||||
|
||||
// EventDataFile returns the standard name of the event data definitions file
|
||||
// with the appropriate extension.
|
||||
func (renderer *Renderer) EventDataFile() string {
|
||||
return "eventdata.d.ts"
|
||||
}
|
||||
|
||||
// EventCreateFile returns the standard name of the event data creation file
|
||||
// with the appropriate extension.
|
||||
func (renderer *Renderer) EventCreateFile() string {
|
||||
return "eventcreate" + renderer.ext
|
||||
}
|
||||
|
||||
// IndexFile returns the standard name of a package index file
|
||||
// with the appropriate extension.
|
||||
func (renderer *Renderer) IndexFile() string {
|
||||
|
|
@ -75,7 +87,7 @@ func (renderer *Renderer) Service(w io.Writer, info *collect.ServiceInfo) error
|
|||
})
|
||||
}
|
||||
|
||||
// Typedefs renders type definitions for the given list of models.
|
||||
// Models renders type definitions for the given list of models.
|
||||
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.
|
||||
|
|
@ -114,7 +126,7 @@ func (renderer *Renderer) Models(w io.Writer, imports *collect.ImportMap, models
|
|||
}
|
||||
}
|
||||
|
||||
return renderer.typedefs.Execute(w, &struct {
|
||||
return renderer.models.Execute(w, &struct {
|
||||
module
|
||||
Models []*collect.ModelInfo
|
||||
}{
|
||||
|
|
@ -127,6 +139,36 @@ func (renderer *Renderer) Models(w io.Writer, imports *collect.ImportMap, models
|
|||
})
|
||||
}
|
||||
|
||||
// EventData renders the given event map to w as an event data table.
|
||||
func (renderer *Renderer) EventData(w io.Writer, events *collect.EventMap) error {
|
||||
return tmplEventData.Execute(w, &struct {
|
||||
module
|
||||
Events *collect.EventMap
|
||||
}{
|
||||
module{
|
||||
Renderer: renderer,
|
||||
GenerateBindingsOptions: renderer.options,
|
||||
Imports: events.Imports,
|
||||
},
|
||||
events,
|
||||
})
|
||||
}
|
||||
|
||||
// EventCreate renders the given event map to w as event data creation code.
|
||||
func (renderer *Renderer) EventCreate(w io.Writer, events *collect.EventMap) error {
|
||||
return tmplEventCreate.Execute(w, &struct {
|
||||
module
|
||||
Events *collect.EventMap
|
||||
}{
|
||||
module{
|
||||
Renderer: renderer,
|
||||
GenerateBindingsOptions: renderer.options,
|
||||
Imports: events.Imports,
|
||||
},
|
||||
events,
|
||||
})
|
||||
}
|
||||
|
||||
// Index renders the given package index to w.
|
||||
func (renderer *Renderer) Index(w io.Writer, index *collect.PackageIndex) error {
|
||||
return tmplIndex.Execute(w, &struct {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ var tmplModels = map[tmplLanguage]*template.Template{
|
|||
tmplTS: template.Must(template.New("models.ts.tmpl").Funcs(tmplFunctions).ParseFS(templates, "templates/models.ts.tmpl")),
|
||||
}
|
||||
|
||||
var tmplEventData = template.Must(template.New("eventdata.d.ts.tmpl").Funcs(tmplFunctions).ParseFS(templates, "templates/eventdata.d.ts.tmpl"))
|
||||
var tmplEventCreate = template.Must(template.New("eventcreate.js.tmpl").Funcs(tmplFunctions).ParseFS(templates, "templates/eventcreate.js.tmpl"))
|
||||
|
||||
var tmplIndex = template.Must(template.New("index.tmpl").Funcs(tmplFunctions).ParseFS(templates, "templates/index.tmpl"))
|
||||
|
||||
var Newline string
|
||||
|
|
|
|||
47
v3/internal/generator/render/templates/eventcreate.js.tmpl
Normal file
47
v3/internal/generator/render/templates/eventcreate.js.tmpl
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
{{$module := .}}
|
||||
{{- $runtime := $module.Runtime}}
|
||||
{{- $models := (fixext $module.ModelsFile)}}
|
||||
{{- $useInterfaces := $module.UseInterfaces}}
|
||||
{{- $imports := $module.Imports}}
|
||||
{{- with .Events -}}
|
||||
//@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 "{{js $runtime}}";
|
||||
{{if (and (not $useInterfaces) .Defs)}}
|
||||
{{- range $imports.External}}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as {{jsimport .}} from "{{js .RelPath}}/{{js $models}}";
|
||||
{{- end}}{{if $imports.External}}
|
||||
{{end}}
|
||||
{{- if $imports.ImportModels}}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as $models from "./{{js $models}}";
|
||||
{{end}}
|
||||
function configure() {
|
||||
Object.freeze(Object.assign($Create.Events, {
|
||||
{{- range .Defs}}
|
||||
{{- $create := ($module.JSCreate .Data)}}
|
||||
{{- if ne $create "$Create.Any"}}
|
||||
"{{js .Name}}": {{$create}},
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
}));
|
||||
}
|
||||
{{$postponed := $module.PostponedCreates}}
|
||||
{{- if $postponed}}
|
||||
// Private type creation functions
|
||||
{{- range $i, $create := $postponed}}
|
||||
{{if and (ge (len $create) 54) (eq (slice $create 39 54) "function $$init")}}var {{else}}const {{end -}}
|
||||
$$createType{{$i}} = {{$create}};
|
||||
{{- end}}
|
||||
{{end}}
|
||||
configure();
|
||||
{{else}}
|
||||
Object.freeze($Create.Events);
|
||||
{{end}}{{end -}}
|
||||
32
v3/internal/generator/render/templates/eventdata.d.ts.tmpl
Normal file
32
v3/internal/generator/render/templates/eventdata.d.ts.tmpl
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
{{$module := .}}
|
||||
{{- $runtime := $module.Runtime}}
|
||||
{{- $models := (fixext $module.ModelsFile)}}
|
||||
{{- $imports := $module.Imports}}
|
||||
{{- with .Events -}}
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
{{if .Defs}}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type { Events } from "{{js $runtime}}";
|
||||
{{range $imports.External}}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type * as {{jsimport .}} from "{{js .RelPath}}/{{js $models}}";
|
||||
{{- end}}{{if $imports.External}}
|
||||
{{end}}
|
||||
{{- if $imports.ImportModels}}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type * as $models from "./{{js $models}}";
|
||||
{{end}}
|
||||
declare module "{{js $runtime}}" {
|
||||
namespace Events {
|
||||
interface CustomEvents {
|
||||
{{- range .Defs}}
|
||||
"{{js .Name}}": {{$module.JSType .Data}};
|
||||
{{- end}}
|
||||
}
|
||||
}
|
||||
}
|
||||
{{end}}{{end -}}
|
||||
|
|
@ -176,6 +176,11 @@ func (m *module) renderNamedType(typ aliasOrNamed, quoted bool) (result string,
|
|||
return m.renderType(typ.Underlying(), quoted)
|
||||
}
|
||||
|
||||
// Special case: application.Void renders as TS void
|
||||
if m.collector.IsVoidAlias(typ.Obj()) {
|
||||
return "void", false
|
||||
}
|
||||
|
||||
if quoted {
|
||||
switch a := types.Unalias(typ).(type) {
|
||||
case *types.Basic:
|
||||
|
|
|
|||
1
v3/internal/generator/testcases/complex_json/events.json
Normal file
1
v3/internal/generator/testcases/complex_json/events.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
true
|
||||
|
|
@ -122,3 +122,8 @@ func main() {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
func init() {
|
||||
application.RegisterEvent[map[string]int]("collision")
|
||||
application.RegisterEvent[*struct{ Field []bool }]("overlap")
|
||||
}
|
||||
|
|
|
|||
26
v3/internal/generator/testcases/events_only/events.go
Normal file
26
v3/internal/generator/testcases/events_only/events.go
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package events_only
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
nobindingshere "github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here"
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
)
|
||||
|
||||
// SomeClass renders as a TS class.
|
||||
type SomeClass struct {
|
||||
Field string
|
||||
Meadow nobindingshere.HowDifferent[rune]
|
||||
}
|
||||
|
||||
func init() {
|
||||
application.RegisterEvent[string]("events_only:string")
|
||||
application.RegisterEvent[map[string][]int]("events_only:map")
|
||||
application.RegisterEvent[SomeClass]("events_only:class")
|
||||
application.RegisterEvent[int]("collision")
|
||||
application.RegisterEvent[bool](fmt.Sprintf("events_only:%s%d", "dynamic", 3))
|
||||
}
|
||||
|
||||
func init() {
|
||||
application.RegisterEvent[application.Void]("events_only:nodata")
|
||||
}
|
||||
1
v3/internal/generator/testcases/events_only/events.json
Normal file
1
v3/internal/generator/testcases/events_only/events.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
true
|
||||
26
v3/internal/generator/testcases/events_only/other.go
Normal file
26
v3/internal/generator/testcases/events_only/other.go
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package events_only
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v3/internal/generator/testcases/no_bindings_here/more"
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
)
|
||||
|
||||
const eventPrefix = "events_only" + `:`
|
||||
|
||||
var registerStringEvent = application.RegisterEvent[string]
|
||||
|
||||
func registerIntEvent(name string) {
|
||||
application.RegisterEvent[int](name)
|
||||
}
|
||||
|
||||
func registerSliceEvent[T any]() {
|
||||
application.RegisterEvent[[]T]("parametric")
|
||||
}
|
||||
|
||||
func init() {
|
||||
application.RegisterEvent[[]more.StringPtr](eventPrefix + "other")
|
||||
application.RegisterEvent[string]("common:ApplicationStarted")
|
||||
registerStringEvent("indirect_var")
|
||||
registerIntEvent("indirect_fn")
|
||||
registerSliceEvent[uintptr]()
|
||||
}
|
||||
1
v3/internal/generator/testcases/marshalers/events.json
Normal file
1
v3/internal/generator/testcases/marshalers/events.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
true
|
||||
|
|
@ -207,3 +207,9 @@ func main() {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
func init() {
|
||||
application.RegisterEvent[*struct{ Field []bool }]("collision")
|
||||
application.RegisterEvent[*struct{ Field []bool }]("overlap")
|
||||
application.RegisterEvent[json.Marshaler]("interface")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
package more
|
||||
|
||||
// StringPtr is a nullable string.
|
||||
type StringPtr *string
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
//@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";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as json$0 from "../../../../../encoding/json/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as events_only$0 from "./generator/testcases/events_only/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as more$0 from "./generator/testcases/no_bindings_here/more/models.js";
|
||||
|
||||
function configure() {
|
||||
Object.freeze(Object.assign($Create.Events, {
|
||||
"events_only:class": $$createType0,
|
||||
"events_only:map": $$createType2,
|
||||
"events_only:other": $$createType3,
|
||||
"overlap": $$createType6,
|
||||
}));
|
||||
}
|
||||
|
||||
// Private type creation functions
|
||||
const $$createType0 = events_only$0.SomeClass.createFrom;
|
||||
const $$createType1 = $Create.Array($Create.Any);
|
||||
const $$createType2 = $Create.Map($Create.Any, $$createType1);
|
||||
const $$createType3 = $Create.Array($Create.Any);
|
||||
const $$createType4 = $Create.Array($Create.Any);
|
||||
const $$createType5 = $Create.Struct({
|
||||
"Field": $$createType4,
|
||||
});
|
||||
const $$createType6 = $Create.Nullable($$createType5);
|
||||
|
||||
configure();
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
// 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 type { Events } from "/wails/runtime.js";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type * as json$0 from "../../../../../encoding/json/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type * as events_only$0 from "./generator/testcases/events_only/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type * as more$0 from "./generator/testcases/no_bindings_here/more/models.js";
|
||||
|
||||
declare module "/wails/runtime.js" {
|
||||
namespace Events {
|
||||
interface CustomEvents {
|
||||
"events_only:class": events_only$0.SomeClass;
|
||||
"events_only:map": { [_: string]: number[] };
|
||||
"events_only:nodata": void;
|
||||
"events_only:other": more$0.StringPtr[];
|
||||
"events_only:string": string;
|
||||
"interface": json$0.Marshaler;
|
||||
"overlap": {"Field": boolean[]} | null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
export {
|
||||
SomeClass
|
||||
} from "./models.js";
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
// @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";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as nobindingshere$0 from "../no_bindings_here/models.js";
|
||||
|
||||
/**
|
||||
* SomeClass renders as a TS class.
|
||||
*/
|
||||
export class SomeClass {
|
||||
/**
|
||||
* Creates a new SomeClass instance.
|
||||
* @param {Partial<SomeClass>} [$$source = {}] - The source object to create the SomeClass.
|
||||
*/
|
||||
constructor($$source = {}) {
|
||||
if (!("Field" in $$source)) {
|
||||
/**
|
||||
* @member
|
||||
* @type {string}
|
||||
*/
|
||||
this["Field"] = "";
|
||||
}
|
||||
if (!("Meadow" in $$source)) {
|
||||
/**
|
||||
* @member
|
||||
* @type {nobindingshere$0.HowDifferent<number>}
|
||||
*/
|
||||
this["Meadow"] = (new nobindingshere$0.HowDifferent());
|
||||
}
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SomeClass instance from a string or object.
|
||||
* @param {any} [$$source = {}]
|
||||
* @returns {SomeClass}
|
||||
*/
|
||||
static createFrom($$source = {}) {
|
||||
const $$createField1_0 = $$createType0;
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
if ("Meadow" in $$parsedSource) {
|
||||
$$parsedSource["Meadow"] = $$createField1_0($$parsedSource["Meadow"]);
|
||||
}
|
||||
return new SomeClass(/** @type {Partial<SomeClass>} */($$parsedSource));
|
||||
}
|
||||
}
|
||||
|
||||
// Private type creation functions
|
||||
const $$createType0 = nobindingshere$0.HowDifferent.createFrom($Create.Any);
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
import * as $models from "./models.js";
|
||||
|
||||
/**
|
||||
* StringPtr is a nullable string.
|
||||
* @typedef {$models.StringPtr} StringPtr
|
||||
*/
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// @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";
|
||||
|
||||
/**
|
||||
* StringPtr is a nullable string.
|
||||
* @typedef {string | null} StringPtr
|
||||
*/
|
||||
|
|
@ -1,3 +1,16 @@
|
|||
/testcases/complex_json/main.go:127:2: event 'collision' has one of multiple definitions here with data type map[string]int
|
||||
/testcases/events_only/events.go:20:2: event 'collision' has one of multiple definitions here with data type int
|
||||
/testcases/events_only/events.go:21:2: `application.RegisterEvent` called here with non-constant event name
|
||||
/testcases/events_only/other.go:10:5: `application.RegisterEvent` is instantiated here but not called
|
||||
/testcases/events_only/other.go:13:2: `application.RegisterEvent` called here with non-constant event name
|
||||
/testcases/events_only/other.go:17:2: data type []T for event 'parametric' contains unresolved type parameters and will be ignored`
|
||||
/testcases/events_only/other.go:22:2: event 'common:ApplicationStarted' is a known system event and cannot be overridden; this call to `application.RegisterEvent` will panic
|
||||
/testcases/marshalers/main.go:212:2: event 'collision' has one of multiple definitions here with data type *struct{Field []bool}
|
||||
/testcases/marshalers/main.go:214:2: data type encoding/json.Marshaler for event 'interface' is a non-empty interface: emitting events from the frontend with data other than `null` is not supported by encoding/json and will likely result in runtime errors
|
||||
dynamically registered event names are not discoverable by the binding generator: it is recommended to invoke `application.RegisterEvent` with constant arguments only
|
||||
event 'collision' has multiple conflicting definitions and will be ignored
|
||||
events registered through indirect calls are not discoverable by the binding generator: it is recommended to invoke `application.RegisterEvent` directly
|
||||
generic wrappers for calls to `application.RegisterEvent` are not analysable by the binding generator: it is recommended to call `application.RegisterEvent` with concrete types only
|
||||
package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *R is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors
|
||||
package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *S is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors
|
||||
package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *T is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
//@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";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as json$0 from "../../../../../encoding/json/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as events_only$0 from "./generator/testcases/events_only/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as more$0 from "./generator/testcases/no_bindings_here/more/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as application$0 from "../pkg/application/models.js";
|
||||
|
||||
function configure() {
|
||||
Object.freeze(Object.assign($Create.Events, {
|
||||
"events_only:class": $$createType0,
|
||||
"events_only:map": $$createType2,
|
||||
"events_only:other": $$createType3,
|
||||
"overlap": $$createType6,
|
||||
}));
|
||||
}
|
||||
|
||||
// Private type creation functions
|
||||
const $$createType0 = events_only$0.SomeClass.createFrom;
|
||||
const $$createType1 = $Create.Array($Create.Any);
|
||||
const $$createType2 = $Create.Map($Create.Any, $$createType1);
|
||||
const $$createType3 = $Create.Array($Create.Any);
|
||||
const $$createType4 = $Create.Array($Create.Any);
|
||||
const $$createType5 = $Create.Struct({
|
||||
"Field": $$createType4,
|
||||
});
|
||||
const $$createType6 = $Create.Nullable($$createType5);
|
||||
|
||||
configure();
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// 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 type { Events } from "/wails/runtime.js";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type * as json$0 from "../../../../../encoding/json/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type * as events_only$0 from "./generator/testcases/events_only/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type * as more$0 from "./generator/testcases/no_bindings_here/more/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type * as application$0 from "../pkg/application/models.js";
|
||||
|
||||
declare module "/wails/runtime.js" {
|
||||
namespace Events {
|
||||
interface CustomEvents {
|
||||
"events_only:class": events_only$0.SomeClass;
|
||||
"events_only:map": { [_: string]: number[] };
|
||||
"events_only:nodata": application$0.Void;
|
||||
"events_only:other": more$0.StringPtr[];
|
||||
"events_only:string": string;
|
||||
"interface": json$0.Marshaler;
|
||||
"overlap": {"Field": boolean[]} | null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
export {
|
||||
SomeClass
|
||||
} from "./models.js";
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
// @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";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as nobindingshere$0 from "../no_bindings_here/models.js";
|
||||
|
||||
/**
|
||||
* SomeClass renders as a TS class.
|
||||
*/
|
||||
export class SomeClass {
|
||||
/**
|
||||
* Creates a new SomeClass instance.
|
||||
* @param {Partial<SomeClass>} [$$source = {}] - The source object to create the SomeClass.
|
||||
*/
|
||||
constructor($$source = {}) {
|
||||
if (!("Field" in $$source)) {
|
||||
/**
|
||||
* @member
|
||||
* @type {string}
|
||||
*/
|
||||
this["Field"] = "";
|
||||
}
|
||||
if (!("Meadow" in $$source)) {
|
||||
/**
|
||||
* @member
|
||||
* @type {nobindingshere$0.HowDifferent<number>}
|
||||
*/
|
||||
this["Meadow"] = (new nobindingshere$0.HowDifferent());
|
||||
}
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SomeClass instance from a string or object.
|
||||
* @param {any} [$$source = {}]
|
||||
* @returns {SomeClass}
|
||||
*/
|
||||
static createFrom($$source = {}) {
|
||||
const $$createField1_0 = $$createType0;
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
if ("Meadow" in $$parsedSource) {
|
||||
$$parsedSource["Meadow"] = $$createField1_0($$parsedSource["Meadow"]);
|
||||
}
|
||||
return new SomeClass(/** @type {Partial<SomeClass>} */($$parsedSource));
|
||||
}
|
||||
}
|
||||
|
||||
// Private type creation functions
|
||||
const $$createType0 = nobindingshere$0.HowDifferent.createFrom($Create.Any);
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
import * as $models from "./models.js";
|
||||
|
||||
/**
|
||||
* StringPtr is a nullable string.
|
||||
* @typedef {$models.StringPtr} StringPtr
|
||||
*/
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// @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";
|
||||
|
||||
/**
|
||||
* StringPtr is a nullable string.
|
||||
* @typedef {string | null} StringPtr
|
||||
*/
|
||||
|
|
@ -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";
|
||||
|
||||
/**
|
||||
* Void will be translated by the binding generator to the TypeScript type 'void'.
|
||||
* It can be used as an event data type to register events that must not have any associated data.
|
||||
* @typedef {$models.Void} Void
|
||||
*/
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// @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";
|
||||
|
||||
/**
|
||||
* Void will be translated by the binding generator to the TypeScript type 'void'.
|
||||
* It can be used as an event data type to register events that must not have any associated data.
|
||||
* @typedef {any} Void
|
||||
*/
|
||||
|
|
@ -1,3 +1,17 @@
|
|||
/testcases/complex_json/main.go:127:2: event 'collision' has one of multiple definitions here with data type map[string]int
|
||||
/testcases/events_only/events.go:20:2: event 'collision' has one of multiple definitions here with data type int
|
||||
/testcases/events_only/events.go:21:2: `application.RegisterEvent` called here with non-constant event name
|
||||
/testcases/events_only/events.go:25:2: data type github.com/wailsapp/wails/v3/pkg/application.Void for event 'events_only:nodata' is a non-empty interface: emitting events from the frontend with data other than `null` is not supported by encoding/json and will likely result in runtime errors
|
||||
/testcases/events_only/other.go:10:5: `application.RegisterEvent` is instantiated here but not called
|
||||
/testcases/events_only/other.go:13:2: `application.RegisterEvent` called here with non-constant event name
|
||||
/testcases/events_only/other.go:17:2: data type []T for event 'parametric' contains unresolved type parameters and will be ignored`
|
||||
/testcases/events_only/other.go:22:2: event 'common:ApplicationStarted' is a known system event and cannot be overridden; this call to `application.RegisterEvent` will panic
|
||||
/testcases/marshalers/main.go:212:2: event 'collision' has one of multiple definitions here with data type *struct{Field []bool}
|
||||
/testcases/marshalers/main.go:214:2: data type encoding/json.Marshaler for event 'interface' is a non-empty interface: emitting events from the frontend with data other than `null` is not supported by encoding/json and will likely result in runtime errors
|
||||
dynamically registered event names are not discoverable by the binding generator: it is recommended to invoke `application.RegisterEvent` with constant arguments only
|
||||
event 'collision' has multiple conflicting definitions and will be ignored
|
||||
events registered through indirect calls are not discoverable by the binding generator: it is recommended to invoke `application.RegisterEvent` directly
|
||||
generic wrappers for calls to `application.RegisterEvent` are not analysable by the binding generator: it is recommended to call `application.RegisterEvent` with concrete types only
|
||||
package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *R is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors
|
||||
package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *S is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors
|
||||
package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *T is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
//@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";
|
||||
|
||||
Object.freeze($Create.Events);
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// 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 type { Events } from "/wails/runtime.js";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type * as json$0 from "../../../../../encoding/json/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type * as events_only$0 from "./generator/testcases/events_only/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type * as more$0 from "./generator/testcases/no_bindings_here/more/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type * as application$0 from "../pkg/application/models.js";
|
||||
|
||||
declare module "/wails/runtime.js" {
|
||||
namespace Events {
|
||||
interface CustomEvents {
|
||||
"events_only:class": events_only$0.SomeClass;
|
||||
"events_only:map": { [_: string]: number[] | null } | null;
|
||||
"events_only:nodata": application$0.Void;
|
||||
"events_only:other": more$0.StringPtr[] | null;
|
||||
"events_only:string": string;
|
||||
"interface": json$0.Marshaler;
|
||||
"overlap": {"Field": boolean[] | null} | null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
import * as $models from "./models.js";
|
||||
|
||||
/**
|
||||
* SomeClass renders as a TS class.
|
||||
* @typedef {$models.SomeClass} SomeClass
|
||||
*/
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// @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 * as nobindingshere$0 from "../no_bindings_here/models.js";
|
||||
|
||||
/**
|
||||
* SomeClass renders as a TS class.
|
||||
* @typedef {Object} SomeClass
|
||||
* @property {string} Field
|
||||
* @property {nobindingshere$0.HowDifferent<number>} Meadow
|
||||
*/
|
||||
|
||||
// In interface mode, this file is likely to contain just comments.
|
||||
// We add a dummy export statement to ensure it is recognised as an ES module.
|
||||
export {};
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
import * as $models from "./models.js";
|
||||
|
||||
/**
|
||||
* StringPtr is a nullable string.
|
||||
* @typedef {$models.StringPtr} StringPtr
|
||||
*/
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
/**
|
||||
* StringPtr is a nullable string.
|
||||
* @typedef {string | null} StringPtr
|
||||
*/
|
||||
|
||||
// In interface mode, this file is likely to contain just comments.
|
||||
// We add a dummy export statement to ensure it is recognised as an ES module.
|
||||
export {};
|
||||
|
|
@ -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";
|
||||
|
||||
/**
|
||||
* Void will be translated by the binding generator to the TypeScript type 'void'.
|
||||
* It can be used as an event data type to register events that must not have any associated data.
|
||||
* @typedef {$models.Void} Void
|
||||
*/
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
/**
|
||||
* Void will be translated by the binding generator to the TypeScript type 'void'.
|
||||
* It can be used as an event data type to register events that must not have any associated data.
|
||||
* @typedef {any} Void
|
||||
*/
|
||||
|
||||
// In interface mode, this file is likely to contain just comments.
|
||||
// We add a dummy export statement to ensure it is recognised as an ES module.
|
||||
export {};
|
||||
|
|
@ -1,3 +1,17 @@
|
|||
/testcases/complex_json/main.go:127:2: event 'collision' has one of multiple definitions here with data type map[string]int
|
||||
/testcases/events_only/events.go:20:2: event 'collision' has one of multiple definitions here with data type int
|
||||
/testcases/events_only/events.go:21:2: `application.RegisterEvent` called here with non-constant event name
|
||||
/testcases/events_only/events.go:25:2: data type github.com/wailsapp/wails/v3/pkg/application.Void for event 'events_only:nodata' is a non-empty interface: emitting events from the frontend with data other than `null` is not supported by encoding/json and will likely result in runtime errors
|
||||
/testcases/events_only/other.go:10:5: `application.RegisterEvent` is instantiated here but not called
|
||||
/testcases/events_only/other.go:13:2: `application.RegisterEvent` called here with non-constant event name
|
||||
/testcases/events_only/other.go:17:2: data type []T for event 'parametric' contains unresolved type parameters and will be ignored`
|
||||
/testcases/events_only/other.go:22:2: event 'common:ApplicationStarted' is a known system event and cannot be overridden; this call to `application.RegisterEvent` will panic
|
||||
/testcases/marshalers/main.go:212:2: event 'collision' has one of multiple definitions here with data type *struct{Field []bool}
|
||||
/testcases/marshalers/main.go:214:2: data type encoding/json.Marshaler for event 'interface' is a non-empty interface: emitting events from the frontend with data other than `null` is not supported by encoding/json and will likely result in runtime errors
|
||||
dynamically registered event names are not discoverable by the binding generator: it is recommended to invoke `application.RegisterEvent` with constant arguments only
|
||||
event 'collision' has multiple conflicting definitions and will be ignored
|
||||
events registered through indirect calls are not discoverable by the binding generator: it is recommended to invoke `application.RegisterEvent` directly
|
||||
generic wrappers for calls to `application.RegisterEvent` are not analysable by the binding generator: it is recommended to call `application.RegisterEvent` with concrete types only
|
||||
package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *R is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors
|
||||
package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *S is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors
|
||||
package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *T is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
//@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";
|
||||
|
||||
Object.freeze($Create.Events);
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// 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 type { Events } from "/wails/runtime.js";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type * as json$0 from "../../../../../encoding/json/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type * as events_only$0 from "./generator/testcases/events_only/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type * as more$0 from "./generator/testcases/no_bindings_here/more/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type * as application$0 from "../pkg/application/models.js";
|
||||
|
||||
declare module "/wails/runtime.js" {
|
||||
namespace Events {
|
||||
interface CustomEvents {
|
||||
"events_only:class": events_only$0.SomeClass;
|
||||
"events_only:map": { [_: string]: number[] | null } | null;
|
||||
"events_only:nodata": application$0.Void;
|
||||
"events_only:other": more$0.StringPtr[] | null;
|
||||
"events_only:string": string;
|
||||
"interface": json$0.Marshaler;
|
||||
"overlap": {"Field": boolean[] | null} | null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
import * as $models from "./models.js";
|
||||
|
||||
/**
|
||||
* SomeClass renders as a TS class.
|
||||
* @typedef {$models.SomeClass} SomeClass
|
||||
*/
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// @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 * as nobindingshere$0 from "../no_bindings_here/models.js";
|
||||
|
||||
/**
|
||||
* SomeClass renders as a TS class.
|
||||
* @typedef {Object} SomeClass
|
||||
* @property {string} Field
|
||||
* @property {nobindingshere$0.HowDifferent<number>} Meadow
|
||||
*/
|
||||
|
||||
// In interface mode, this file is likely to contain just comments.
|
||||
// We add a dummy export statement to ensure it is recognised as an ES module.
|
||||
export {};
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
import * as $models from "./models.js";
|
||||
|
||||
/**
|
||||
* StringPtr is a nullable string.
|
||||
* @typedef {$models.StringPtr} StringPtr
|
||||
*/
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
/**
|
||||
* StringPtr is a nullable string.
|
||||
* @typedef {string | null} StringPtr
|
||||
*/
|
||||
|
||||
// In interface mode, this file is likely to contain just comments.
|
||||
// We add a dummy export statement to ensure it is recognised as an ES module.
|
||||
export {};
|
||||
|
|
@ -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";
|
||||
|
||||
/**
|
||||
* Void will be translated by the binding generator to the TypeScript type 'void'.
|
||||
* It can be used as an event data type to register events that must not have any associated data.
|
||||
* @typedef {$models.Void} Void
|
||||
*/
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
/**
|
||||
* Void will be translated by the binding generator to the TypeScript type 'void'.
|
||||
* It can be used as an event data type to register events that must not have any associated data.
|
||||
* @typedef {any} Void
|
||||
*/
|
||||
|
||||
// In interface mode, this file is likely to contain just comments.
|
||||
// We add a dummy export statement to ensure it is recognised as an ES module.
|
||||
export {};
|
||||
|
|
@ -1,3 +1,17 @@
|
|||
/testcases/complex_json/main.go:127:2: event 'collision' has one of multiple definitions here with data type map[string]int
|
||||
/testcases/events_only/events.go:20:2: event 'collision' has one of multiple definitions here with data type int
|
||||
/testcases/events_only/events.go:21:2: `application.RegisterEvent` called here with non-constant event name
|
||||
/testcases/events_only/events.go:25:2: data type github.com/wailsapp/wails/v3/pkg/application.Void for event 'events_only:nodata' is a non-empty interface: emitting events from the frontend with data other than `null` is not supported by encoding/json and will likely result in runtime errors
|
||||
/testcases/events_only/other.go:10:5: `application.RegisterEvent` is instantiated here but not called
|
||||
/testcases/events_only/other.go:13:2: `application.RegisterEvent` called here with non-constant event name
|
||||
/testcases/events_only/other.go:17:2: data type []T for event 'parametric' contains unresolved type parameters and will be ignored`
|
||||
/testcases/events_only/other.go:22:2: event 'common:ApplicationStarted' is a known system event and cannot be overridden; this call to `application.RegisterEvent` will panic
|
||||
/testcases/marshalers/main.go:212:2: event 'collision' has one of multiple definitions here with data type *struct{Field []bool}
|
||||
/testcases/marshalers/main.go:214:2: data type encoding/json.Marshaler for event 'interface' is a non-empty interface: emitting events from the frontend with data other than `null` is not supported by encoding/json and will likely result in runtime errors
|
||||
dynamically registered event names are not discoverable by the binding generator: it is recommended to invoke `application.RegisterEvent` with constant arguments only
|
||||
event 'collision' has multiple conflicting definitions and will be ignored
|
||||
events registered through indirect calls are not discoverable by the binding generator: it is recommended to invoke `application.RegisterEvent` directly
|
||||
generic wrappers for calls to `application.RegisterEvent` are not analysable by the binding generator: it is recommended to call `application.RegisterEvent` with concrete types only
|
||||
package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *R is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors
|
||||
package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *S is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors
|
||||
package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *T is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
//@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";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as json$0 from "../../../../../encoding/json/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as events_only$0 from "./generator/testcases/events_only/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as more$0 from "./generator/testcases/no_bindings_here/more/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as application$0 from "../pkg/application/models.js";
|
||||
|
||||
function configure() {
|
||||
Object.freeze(Object.assign($Create.Events, {
|
||||
"events_only:class": $$createType0,
|
||||
"events_only:map": $$createType2,
|
||||
"events_only:other": $$createType3,
|
||||
"overlap": $$createType6,
|
||||
}));
|
||||
}
|
||||
|
||||
// Private type creation functions
|
||||
const $$createType0 = events_only$0.SomeClass.createFrom;
|
||||
const $$createType1 = $Create.Array($Create.Any);
|
||||
const $$createType2 = $Create.Map($Create.Any, $$createType1);
|
||||
const $$createType3 = $Create.Array($Create.Any);
|
||||
const $$createType4 = $Create.Array($Create.Any);
|
||||
const $$createType5 = $Create.Struct({
|
||||
"Field": $$createType4,
|
||||
});
|
||||
const $$createType6 = $Create.Nullable($$createType5);
|
||||
|
||||
configure();
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// 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 type { Events } from "/wails/runtime.js";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type * as json$0 from "../../../../../encoding/json/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type * as events_only$0 from "./generator/testcases/events_only/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type * as more$0 from "./generator/testcases/no_bindings_here/more/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type * as application$0 from "../pkg/application/models.js";
|
||||
|
||||
declare module "/wails/runtime.js" {
|
||||
namespace Events {
|
||||
interface CustomEvents {
|
||||
"events_only:class": events_only$0.SomeClass;
|
||||
"events_only:map": { [_: string]: number[] };
|
||||
"events_only:nodata": application$0.Void;
|
||||
"events_only:other": more$0.StringPtr[];
|
||||
"events_only:string": string;
|
||||
"interface": json$0.Marshaler;
|
||||
"overlap": {"Field": boolean[]} | null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
export {
|
||||
SomeClass
|
||||
} from "./models.js";
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
// 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";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as nobindingshere$0 from "../no_bindings_here/models.js";
|
||||
|
||||
/**
|
||||
* SomeClass renders as a TS class.
|
||||
*/
|
||||
export class SomeClass {
|
||||
"Field": string;
|
||||
"Meadow": nobindingshere$0.HowDifferent<number>;
|
||||
|
||||
/** Creates a new SomeClass instance. */
|
||||
constructor($$source: Partial<SomeClass> = {}) {
|
||||
if (!("Field" in $$source)) {
|
||||
this["Field"] = "";
|
||||
}
|
||||
if (!("Meadow" in $$source)) {
|
||||
this["Meadow"] = (new nobindingshere$0.HowDifferent());
|
||||
}
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SomeClass instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): SomeClass {
|
||||
const $$createField1_0 = $$createType0;
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
if ("Meadow" in $$parsedSource) {
|
||||
$$parsedSource["Meadow"] = $$createField1_0($$parsedSource["Meadow"]);
|
||||
}
|
||||
return new SomeClass($$parsedSource as Partial<SomeClass>);
|
||||
}
|
||||
}
|
||||
|
||||
// Private type creation functions
|
||||
const $$createType0 = nobindingshere$0.HowDifferent.createFrom($Create.Any);
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
export type {
|
||||
StringPtr
|
||||
} from "./models.js";
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
// 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";
|
||||
|
||||
/**
|
||||
* StringPtr is a nullable string.
|
||||
*/
|
||||
export type StringPtr = string | null;
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
export type {
|
||||
Void
|
||||
} from "./models.js";
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// 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";
|
||||
|
||||
/**
|
||||
* Void will be translated by the binding generator to the TypeScript type 'void'.
|
||||
* It can be used as an event data type to register events that must not have any associated data.
|
||||
*/
|
||||
export type Void = any;
|
||||
|
|
@ -1,3 +1,17 @@
|
|||
/testcases/complex_json/main.go:127:2: event 'collision' has one of multiple definitions here with data type map[string]int
|
||||
/testcases/events_only/events.go:20:2: event 'collision' has one of multiple definitions here with data type int
|
||||
/testcases/events_only/events.go:21:2: `application.RegisterEvent` called here with non-constant event name
|
||||
/testcases/events_only/events.go:25:2: data type github.com/wailsapp/wails/v3/pkg/application.Void for event 'events_only:nodata' is a non-empty interface: emitting events from the frontend with data other than `null` is not supported by encoding/json and will likely result in runtime errors
|
||||
/testcases/events_only/other.go:10:5: `application.RegisterEvent` is instantiated here but not called
|
||||
/testcases/events_only/other.go:13:2: `application.RegisterEvent` called here with non-constant event name
|
||||
/testcases/events_only/other.go:17:2: data type []T for event 'parametric' contains unresolved type parameters and will be ignored`
|
||||
/testcases/events_only/other.go:22:2: event 'common:ApplicationStarted' is a known system event and cannot be overridden; this call to `application.RegisterEvent` will panic
|
||||
/testcases/marshalers/main.go:212:2: event 'collision' has one of multiple definitions here with data type *struct{Field []bool}
|
||||
/testcases/marshalers/main.go:214:2: data type encoding/json.Marshaler for event 'interface' is a non-empty interface: emitting events from the frontend with data other than `null` is not supported by encoding/json and will likely result in runtime errors
|
||||
dynamically registered event names are not discoverable by the binding generator: it is recommended to invoke `application.RegisterEvent` with constant arguments only
|
||||
event 'collision' has multiple conflicting definitions and will be ignored
|
||||
events registered through indirect calls are not discoverable by the binding generator: it is recommended to invoke `application.RegisterEvent` directly
|
||||
generic wrappers for calls to `application.RegisterEvent` are not analysable by the binding generator: it is recommended to call `application.RegisterEvent` with concrete types only
|
||||
package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *R is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors
|
||||
package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *S is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors
|
||||
package github.com/wailsapp/wails/v3/internal/generator/testcases/map_keys: type *T is used as a map key, but some of its instantiations might not implement encoding.TextMarshaler: this might result in runtime errors
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
//@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";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as json$0 from "../../../../../encoding/json/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as events_only$0 from "./generator/testcases/events_only/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as more$0 from "./generator/testcases/no_bindings_here/more/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as application$0 from "../pkg/application/models.js";
|
||||
|
||||
function configure() {
|
||||
Object.freeze(Object.assign($Create.Events, {
|
||||
"events_only:class": $$createType0,
|
||||
"events_only:map": $$createType2,
|
||||
"events_only:other": $$createType3,
|
||||
"overlap": $$createType6,
|
||||
}));
|
||||
}
|
||||
|
||||
// Private type creation functions
|
||||
const $$createType0 = events_only$0.SomeClass.createFrom;
|
||||
const $$createType1 = $Create.Array($Create.Any);
|
||||
const $$createType2 = $Create.Map($Create.Any, $$createType1);
|
||||
const $$createType3 = $Create.Array($Create.Any);
|
||||
const $$createType4 = $Create.Array($Create.Any);
|
||||
const $$createType5 = $Create.Struct({
|
||||
"Field": $$createType4,
|
||||
});
|
||||
const $$createType6 = $Create.Nullable($$createType5);
|
||||
|
||||
configure();
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// 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 type { Events } from "/wails/runtime.js";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type * as json$0 from "../../../../../encoding/json/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type * as events_only$0 from "./generator/testcases/events_only/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type * as more$0 from "./generator/testcases/no_bindings_here/more/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import type * as application$0 from "../pkg/application/models.js";
|
||||
|
||||
declare module "/wails/runtime.js" {
|
||||
namespace Events {
|
||||
interface CustomEvents {
|
||||
"events_only:class": events_only$0.SomeClass;
|
||||
"events_only:map": { [_: string]: number[] };
|
||||
"events_only:nodata": application$0.Void;
|
||||
"events_only:other": more$0.StringPtr[];
|
||||
"events_only:string": string;
|
||||
"interface": json$0.Marshaler;
|
||||
"overlap": {"Field": boolean[]} | null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
export {
|
||||
SomeClass
|
||||
} from "./models.js";
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
// 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";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as nobindingshere$0 from "../no_bindings_here/models.js";
|
||||
|
||||
/**
|
||||
* SomeClass renders as a TS class.
|
||||
*/
|
||||
export class SomeClass {
|
||||
"Field": string;
|
||||
"Meadow": nobindingshere$0.HowDifferent<number>;
|
||||
|
||||
/** Creates a new SomeClass instance. */
|
||||
constructor($$source: Partial<SomeClass> = {}) {
|
||||
if (!("Field" in $$source)) {
|
||||
this["Field"] = "";
|
||||
}
|
||||
if (!("Meadow" in $$source)) {
|
||||
this["Meadow"] = (new nobindingshere$0.HowDifferent());
|
||||
}
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SomeClass instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): SomeClass {
|
||||
const $$createField1_0 = $$createType0;
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
if ("Meadow" in $$parsedSource) {
|
||||
$$parsedSource["Meadow"] = $$createField1_0($$parsedSource["Meadow"]);
|
||||
}
|
||||
return new SomeClass($$parsedSource as Partial<SomeClass>);
|
||||
}
|
||||
}
|
||||
|
||||
// Private type creation functions
|
||||
const $$createType0 = nobindingshere$0.HowDifferent.createFrom($Create.Any);
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
export type {
|
||||
StringPtr
|
||||
} from "./models.js";
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
// 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";
|
||||
|
||||
/**
|
||||
* StringPtr is a nullable string.
|
||||
*/
|
||||
export type StringPtr = string | null;
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
export type {
|
||||
Void
|
||||
} from "./models.js";
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue