5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-02 19:31:20 +08:00

feat: significant overhaul of runtime

This commit is contained in:
Lea Anthony 2019-07-20 19:32:30 +10:00
parent d2f114e44e
commit 030e911ea4
No known key found for this signature in database
GPG Key ID: 33DAF7BB90A58405
58 changed files with 759 additions and 291 deletions

View File

@ -1 +1 @@
runtime/js/dist/wails.js runtime/assets/default.html

View File

@ -1,7 +1,8 @@
{ {
"env": { "env": {
"browser": true, "browser": true,
"es6": true "es6": true,
"node": true,
}, },
"extends": "eslint:recommended", "extends": "eslint:recommended",
"parserOptions": { "parserOptions": {

13
app.go
View File

@ -2,13 +2,12 @@ package wails
import ( import (
"github.com/wailsapp/wails/cmd" "github.com/wailsapp/wails/cmd"
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/runtime/go/runtime"
"github.com/wailsapp/wails/lib/renderer"
"github.com/wailsapp/wails/lib/binding" "github.com/wailsapp/wails/lib/binding"
"github.com/wailsapp/wails/lib/ipc"
"github.com/wailsapp/wails/lib/event" "github.com/wailsapp/wails/lib/event"
"github.com/wailsapp/wails/lib/interfaces" "github.com/wailsapp/wails/lib/interfaces"
"github.com/wailsapp/wails/lib/ipc"
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/lib/renderer"
) )
// -------------------------------- Compile time Flags ------------------------------ // -------------------------------- Compile time Flags ------------------------------
@ -87,9 +86,9 @@ func (a *App) start() error {
// Log starup // Log starup
a.log.Info("Starting") a.log.Info("Starting")
// Check if we are to run in headless mode // Check if we are to run in bridge mode
if BuildMode == cmd.BuildModeBridge { if BuildMode == cmd.BuildModeBridge {
a.renderer = &renderer.Headless{} a.renderer = &renderer.Bridge{}
} }
// Initialise the renderer // Initialise the renderer
@ -105,7 +104,7 @@ func (a *App) start() error {
a.ipc.Start(a.eventManager, a.bindingManager) a.ipc.Start(a.eventManager, a.bindingManager)
// Create the runtime // Create the runtime
a.runtime = runtime.NewRuntime(a.eventManager, a.renderer) a.runtime = NewRuntime(a.eventManager, a.renderer)
// Start binding manager and give it our renderer // Start binding manager and give it our renderer
err = a.bindingManager.Start(a.renderer, a.runtime) err = a.bindingManager.Start(a.renderer, a.runtime)

3
cli.go
View File

@ -11,10 +11,9 @@ func (app *App) setupCli() *cmd.Cli {
result := cmd.NewCli(app.config.Title, "Debug build") result := cmd.NewCli(app.config.Title, "Debug build")
result.Version(cmd.Version) result.Version(cmd.Version)
// Setup cli to handle loglevel and headless flags // Setup cli to handle loglevel
result. result.
StringFlag("loglevel", "Sets the log level [debug|info|error|panic|fatal]. Default debug", &app.logLevel). StringFlag("loglevel", "Sets the log level [debug|info|error|panic|fatal]. Default debug", &app.logLevel).
// BoolFlag("headless", "Runs the app in headless mode", &app.headless).
Action(app.start) Action(app.start)
// Banner // Banner

File diff suppressed because one or more lines are too long

View File

@ -9,7 +9,7 @@ import (
"runtime" "runtime"
"time" "time"
mewn "github.com/leaanthony/mewn" "github.com/leaanthony/mewn"
"github.com/leaanthony/slicer" "github.com/leaanthony/slicer"
"github.com/leaanthony/spinner" "github.com/leaanthony/spinner"
) )
@ -249,8 +249,8 @@ func InstallFrontendDeps(projectDir string, projectOptions *ProjectOptions, forc
ioutil.WriteFile(md5sumFile, []byte(packageJSONMD5), 0644) ioutil.WriteFile(md5sumFile, []byte(packageJSONMD5), 0644)
} }
// Install the bridge library // Install the runtime
err = InstallBridge(caller, projectDir, projectOptions) err = InstallRuntime(caller, projectDir, projectOptions)
if err != nil { if err != nil {
return err return err
} }
@ -263,22 +263,29 @@ func InstallFrontendDeps(projectDir string, projectOptions *ProjectOptions, forc
return nil return nil
} }
// InstallBridge installs the relevant bridge javascript library // InstallRuntime installs the correct runtime for the type of build
func InstallBridge(caller string, projectDir string, projectOptions *ProjectOptions) error { func InstallRuntime(caller string, projectDir string, projectOptions *ProjectOptions) error {
bridgeFile := "wailsbridge.prod.js" if caller == "build" {
if caller == "serve" { return InstallProdRuntime(projectDir, projectOptions)
bridgeFile = "wailsbridge.js"
} }
// Copy bridge to project return InstallBridge(projectDir, projectOptions)
bridgeAssets := mewn.Group("../runtime/bridge/") }
bridgeFileData := bridgeAssets.Bytes(bridgeFile)
bridgeFileTarget := filepath.Join(projectDir, projectOptions.FrontEnd.Dir, projectOptions.FrontEnd.Bridge, "wailsbridge.js") // InstallBridge installs the relevant bridge javascript library
err := fs.CreateFile(bridgeFileTarget, bridgeFileData) func InstallBridge(projectDir string, projectOptions *ProjectOptions) error {
if err != nil { bridgeFileData := mewn.String("../runtime/assets/bridge.js")
bridgeFileTarget := filepath.Join(projectDir, projectOptions.FrontEnd.Dir, "node_modules", "@wailsapp", "runtime", "main.js")
err := fs.CreateFile(bridgeFileTarget, []byte(bridgeFileData))
return err return err
} }
return nil
// InstallProdRuntime installs the production runtime
func InstallProdRuntime(projectDir string, projectOptions *ProjectOptions) error {
prodInit := mewn.String("../runtime/js/runtime/init.js")
bridgeFileTarget := filepath.Join(projectDir, projectOptions.FrontEnd.Dir, "node_modules", "@wailsapp", "runtime", "main.js")
err := fs.CreateFile(bridgeFileTarget, []byte(prodInit))
return err
} }
// ServeProject attempts to serve up the current project so that it may be connected to // ServeProject attempts to serve up the current project so that it may be connected to

View File

@ -21,6 +21,7 @@
"@angular/platform-browser": "~8.0.1", "@angular/platform-browser": "~8.0.1",
"@angular/platform-browser-dynamic": "~8.0.1", "@angular/platform-browser-dynamic": "~8.0.1",
"@angular/router": "~8.0.1", "@angular/router": "~8.0.1",
"@wailsapp/runtime": "^1.0.0",
"ngx-build-plus": "^8.0.3", "ngx-build-plus": "^8.0.3",
"rxjs": "~6.4.0", "rxjs": "~6.4.0",
"tslib": "^1.9.0", "tslib": "^1.9.0",

View File

@ -6,13 +6,13 @@ import { environment } from './environments/environment';
import 'zone.js' import 'zone.js'
import Bridge from './wailsbridge'; import Wails from '@wailsapp/runtime';
if (environment.production) { if (environment.production) {
enableProdMode(); enableProdMode();
} }
Bridge.Start(() => { Wails.Init(() => {
platformBrowserDynamic().bootstrapModule(AppModule) platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err)); .catch(err => console.error(err));
}); });

View File

@ -7,7 +7,8 @@
"react": "^16.8.6", "react": "^16.8.6",
"react-dom": "^16.8.6", "react-dom": "^16.8.6",
"wails-react-scripts": "3.0.1-2", "wails-react-scripts": "3.0.1-2",
"react-modal": "3.8.1" "react-modal": "3.8.1",
"@wailsapp/runtime": "^1.0.0"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "react-scripts start",

View File

@ -3,8 +3,8 @@ import ReactDOM from 'react-dom';
import './index.css'; import './index.css';
import App from './App'; import App from './App';
import Bridge from "./wailsbridge"; import Wails from '@wailsapp/runtime';
Bridge.Start(() => { Wails.Init(() => {
ReactDOM.render(<App />, document.getElementById('app')); ReactDOM.render(<App />, document.getElementById('app'));
}); });

View File

@ -9,7 +9,8 @@
}, },
"dependencies": { "dependencies": {
"core-js": "^2.6.4", "core-js": "^2.6.4",
"vue": "^2.5.22" "vue": "^2.5.22",
"@wailsapp/runtime": "^1.0.0"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-babel": "^3.4.0", "@vue/cli-plugin-babel": "^3.4.0",

View File

@ -1,13 +1,13 @@
import Vue from "vue"; import Vue from 'vue';
import App from "./App.vue"; import App from './App.vue';
Vue.config.productionTip = false; Vue.config.productionTip = false;
Vue.config.devtools = true; Vue.config.devtools = true;
import Bridge from "./wailsbridge"; import Wails from '@wailsapp/runtime';
Bridge.Start(() => { Wails.Init(() => {
new Vue({ new Vue({
render: h => h(App) render: h => h(App)
}).$mount("#app"); }).$mount('#app');
}); });

View File

@ -1,17 +0,0 @@
/*
Wails Bridge (c) 2019-present Lea Anthony
This prod version is to get around having to rewrite your code
for production. When doing a release build, this file will be used
instead of the full version.
*/
export default {
// The main function
// Passes the main Wails object to the callback if given.
Start: function (callback) {
if (callback) {
window.wails.Events.On("wails:ready", callback);
}
}
};

View File

@ -12,7 +12,8 @@
"core-js": "^2.6.4", "core-js": "^2.6.4",
"material-design-icons-iconfont": "^5.0.1", "material-design-icons-iconfont": "^5.0.1",
"vue": "^2.5.22", "vue": "^2.5.22",
"vuetify": "^1.5.14" "vuetify": "^1.5.14",
"@wailsapp/runtime": "^1.0.0"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-babel": "^3.4.0", "@vue/cli-plugin-babel": "^3.4.0",

View File

@ -1,5 +1,5 @@
import 'babel-polyfill'; import 'babel-polyfill';
import Vue from "vue"; import Vue from 'vue';
// Setup Vuetify // Setup Vuetify
import Vuetify from 'vuetify'; import Vuetify from 'vuetify';
@ -7,15 +7,15 @@ Vue.use(Vuetify);
import 'vuetify/dist/vuetify.min.css'; import 'vuetify/dist/vuetify.min.css';
import 'material-design-icons-iconfont'; import 'material-design-icons-iconfont';
import App from "./App.vue"; import App from './App.vue';
Vue.config.productionTip = false; Vue.config.productionTip = false;
Vue.config.devtools = true; Vue.config.devtools = true;
import Bridge from "./wailsbridge"; import Wails from '@wailsapp/runtime';
Bridge.Start(() => { Wails.Init(() => {
new Vue({ new Vue({
render: h => h(App) render: h => h(App)
}).$mount("#app"); }).$mount('#app');
}); });

View File

@ -72,7 +72,16 @@ func init() {
if err != nil { if err != nil {
return err return err
} }
// Ensure that runtime init.js is the production version
err = cmd.InstallProdRuntime(projectDir, projectOptions)
if err != nil {
return err
} }
}
// Move to project directory // Move to project directory
err = os.Chdir(projectDir) err = os.Chdir(projectDir)

View File

@ -45,7 +45,7 @@ func init() {
projectDir := fs.Cwd() projectDir := fs.Cwd()
// Install the bridge library // Install the bridge library
err = cmd.InstallBridge("serve", projectDir, projectOptions) err = cmd.InstallBridge(projectDir, projectOptions)
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,5 +1,6 @@
package wails package wails
import "github.com/leaanthony/mewn"
// AppConfig is the configuration structure used when creating a Wails App object // AppConfig is the configuration structure used when creating a Wails App object
type AppConfig struct { type AppConfig struct {
@ -29,7 +30,7 @@ func (a *AppConfig) GetTitle() string {
return a.Title return a.Title
} }
// GetDefaultHTML returns the desired window title // GetDefaultHTML returns the default HTML
func (a *AppConfig) GetDefaultHTML() string { func (a *AppConfig) GetDefaultHTML() string {
return a.defaultHTML return a.defaultHTML
} }
@ -95,6 +96,7 @@ func newConfig(userConfig *AppConfig) (*AppConfig, error) {
Resizable: true, Resizable: true,
Title: "My Wails App", Title: "My Wails App",
Colour: "#FFF", // White by default Colour: "#FFF", // White by default
HTML: mewn.String("./runtime/assets/default.html"),
} }
if userConfig != nil { if userConfig != nil {

2
go.sum
View File

@ -73,8 +73,6 @@ github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/wailsapp/webview v0.2.7 h1:fN5L5H9Oivg9IJPk7uaXQnjqB68Fny11ZWkIaTIZHmk=
github.com/wailsapp/webview v0.2.7/go.mod h1:XO9HJbKWokDxUYTWQEBCYg95n/To1v7PxvanDNVf8hY=
github.com/zserge/webview v0.0.0-20190123072648-16c93bcaeaeb/go.mod h1:a1CV8KR4Dd1eP2g+mEijGOp+HKczwdKHWyx0aPHKvo4= github.com/zserge/webview v0.0.0-20190123072648-16c93bcaeaeb/go.mod h1:a1CV8KR4Dd1eP2g+mEijGOp+HKczwdKHWyx0aPHKvo4=
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=

View File

@ -6,7 +6,7 @@ import (
"github.com/wailsapp/wails/lib/logger" "github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/lib/messages" "github.com/wailsapp/wails/lib/messages"
"github.com/wailsapp/wails/runtime/go/runtime" "github.com/wailsapp/wails/runtime"
) )
type internalMethods struct { type internalMethods struct {

View File

@ -31,9 +31,9 @@ func (m messageType) toString() string {
return [...]string{"j", "s", "h", "n", "b", "c", "w"}[m] return [...]string{"j", "s", "h", "n", "b", "c", "w"}[m]
} }
// Headless is a backend that opens a local web server // Bridge is a backend that opens a local web server
// and renders the files over a websocket // and renders the files over a websocket
type Headless struct { type Bridge struct {
// Common // Common
log *logger.CustomLogger log *logger.CustomLogger
ipcManager interfaces.IPCManager ipcManager interfaces.IPCManager
@ -41,7 +41,7 @@ type Headless struct {
eventManager interfaces.EventManager eventManager interfaces.EventManager
bindingCache []string bindingCache []string
// Headless specific // Bridge specific
initialisationJS []string initialisationJS []string
server *http.Server server *http.Server
theConnection *websocket.Conn theConnection *websocket.Conn
@ -50,8 +50,8 @@ type Headless struct {
lock sync.Mutex lock sync.Mutex
} }
// Initialise the Headless Renderer // Initialise the Bridge Renderer
func (h *Headless) Initialise(appConfig interfaces.AppConfig, ipcManager interfaces.IPCManager, eventManager interfaces.EventManager) error { func (h *Bridge) Initialise(appConfig interfaces.AppConfig, ipcManager interfaces.IPCManager, eventManager interfaces.EventManager) error {
h.ipcManager = ipcManager h.ipcManager = ipcManager
h.appConfig = appConfig h.appConfig = appConfig
h.eventManager = eventManager h.eventManager = eventManager
@ -60,7 +60,7 @@ func (h *Headless) Initialise(appConfig interfaces.AppConfig, ipcManager interfa
return nil return nil
} }
func (h *Headless) evalJS(js string, mtype messageType) error { func (h *Bridge) evalJS(js string, mtype messageType) error {
message := mtype.toString() + js message := mtype.toString() + js
@ -74,7 +74,7 @@ func (h *Headless) evalJS(js string, mtype messageType) error {
return nil return nil
} }
func (h *Headless) injectCSS(css string) { func (h *Bridge) injectCSS(css string) {
// Minify css to overcome issues in the browser with carriage returns // Minify css to overcome issues in the browser with carriage returns
minified, err := htmlmin.Minify([]byte(css), &htmlmin.Options{ minified, err := htmlmin.Minify([]byte(css), &htmlmin.Options{
MinifyStyles: true, MinifyStyles: true,
@ -90,7 +90,7 @@ func (h *Headless) injectCSS(css string) {
h.evalJS(inject, cssMessage) h.evalJS(inject, cssMessage)
} }
func (h *Headless) wsBridgeHandler(w http.ResponseWriter, r *http.Request) { func (h *Bridge) wsBridgeHandler(w http.ResponseWriter, r *http.Request) {
conn, err := websocket.Upgrade(w, r, w.Header(), 1024, 1024) conn, err := websocket.Upgrade(w, r, w.Header(), 1024, 1024)
if err != nil { if err != nil {
http.Error(w, "Could not open websocket connection", http.StatusBadRequest) http.Error(w, "Could not open websocket connection", http.StatusBadRequest)
@ -105,7 +105,7 @@ func (h *Headless) wsBridgeHandler(w http.ResponseWriter, r *http.Request) {
go h.start(conn) go h.start(conn)
} }
func (h *Headless) sendMessage(conn *websocket.Conn, msg string) { func (h *Bridge) sendMessage(conn *websocket.Conn, msg string) {
h.lock.Lock() h.lock.Lock()
defer h.lock.Unlock() defer h.lock.Unlock()
@ -115,12 +115,12 @@ func (h *Headless) sendMessage(conn *websocket.Conn, msg string) {
} }
} }
func (h *Headless) start(conn *websocket.Conn) { func (h *Bridge) start(conn *websocket.Conn) {
// set external.invoke // set external.invoke
h.log.Infof("Connected to frontend.") h.log.Infof("Connected to frontend.")
wailsRuntime := mewn.String("../../runtime/js/dist/wails.js") wailsRuntime := mewn.String("../../runtime/assets/wails.js")
h.evalJS(wailsRuntime, wailsRuntimeMessage) h.evalJS(wailsRuntime, wailsRuntimeMessage)
// Inject bindings // Inject bindings
@ -147,8 +147,8 @@ func (h *Headless) start(conn *websocket.Conn) {
} }
} }
// Run the app in headless mode! // Run the app in Bridge mode!
func (h *Headless) Run() error { func (h *Bridge) Run() error {
h.server = &http.Server{Addr: ":34115"} h.server = &http.Server{Addr: ":34115"}
http.HandleFunc("/bridge", h.wsBridgeHandler) http.HandleFunc("/bridge", h.wsBridgeHandler)
@ -163,39 +163,39 @@ func (h *Headless) Run() error {
} }
// NewBinding creates a new binding with the frontend // NewBinding creates a new binding with the frontend
func (h *Headless) NewBinding(methodName string) error { func (h *Bridge) NewBinding(methodName string) error {
h.bindingCache = append(h.bindingCache, methodName) h.bindingCache = append(h.bindingCache, methodName)
return nil return nil
} }
// SelectFile is unsupported for Headless but required // SelectFile is unsupported for Bridge but required
// for the Renderer interface // for the Renderer interface
func (h *Headless) SelectFile() string { func (h *Bridge) SelectFile() string {
h.log.Warn("SelectFile() unsupported in bridge mode") h.log.Warn("SelectFile() unsupported in bridge mode")
return "" return ""
} }
// SelectDirectory is unsupported for Headless but required // SelectDirectory is unsupported for Bridge but required
// for the Renderer interface // for the Renderer interface
func (h *Headless) SelectDirectory() string { func (h *Bridge) SelectDirectory() string {
h.log.Warn("SelectDirectory() unsupported in bridge mode") h.log.Warn("SelectDirectory() unsupported in bridge mode")
return "" return ""
} }
// SelectSaveFile is unsupported for Headless but required // SelectSaveFile is unsupported for Bridge but required
// for the Renderer interface // for the Renderer interface
func (h *Headless) SelectSaveFile() string { func (h *Bridge) SelectSaveFile() string {
h.log.Warn("SelectSaveFile() unsupported in bridge mode") h.log.Warn("SelectSaveFile() unsupported in bridge mode")
return "" return ""
} }
// Callback sends a callback to the frontend // Callback sends a callback to the frontend
func (h *Headless) Callback(data string) error { func (h *Bridge) Callback(data string) error {
return h.evalJS(data, callbackMessage) return h.evalJS(data, callbackMessage)
} }
// NotifyEvent notifies the frontend of an event // NotifyEvent notifies the frontend of an event
func (h *Headless) NotifyEvent(event *messages.EventData) error { func (h *Bridge) NotifyEvent(event *messages.EventData) error {
// Look out! Nils about! // Look out! Nils about!
var err error var err error
@ -222,33 +222,33 @@ func (h *Headless) NotifyEvent(event *messages.EventData) error {
return h.evalJS(message, notifyMessage) return h.evalJS(message, notifyMessage)
} }
// SetColour is unsupported for Headless but required // SetColour is unsupported for Bridge but required
// for the Renderer interface // for the Renderer interface
func (h *Headless) SetColour(colour string) error { func (h *Bridge) SetColour(colour string) error {
h.log.WarnFields("SetColour ignored for headless more", logger.Fields{"col": colour}) h.log.WarnFields("SetColour ignored for Bridge more", logger.Fields{"col": colour})
return nil return nil
} }
// Fullscreen is unsupported for Headless but required // Fullscreen is unsupported for Bridge but required
// for the Renderer interface // for the Renderer interface
func (h *Headless) Fullscreen() { func (h *Bridge) Fullscreen() {
h.log.Warn("Fullscreen() unsupported in bridge mode") h.log.Warn("Fullscreen() unsupported in bridge mode")
} }
// UnFullscreen is unsupported for Headless but required // UnFullscreen is unsupported for Bridge but required
// for the Renderer interface // for the Renderer interface
func (h *Headless) UnFullscreen() { func (h *Bridge) UnFullscreen() {
h.log.Warn("UnFullscreen() unsupported in bridge mode") h.log.Warn("UnFullscreen() unsupported in bridge mode")
} }
// SetTitle is currently unsupported for Headless but required // SetTitle is currently unsupported for Bridge but required
// for the Renderer interface // for the Renderer interface
func (h *Headless) SetTitle(title string) { func (h *Bridge) SetTitle(title string) {
h.log.WarnFields("SetTitle() unsupported in bridge mode", logger.Fields{"title": title}) h.log.WarnFields("SetTitle() unsupported in bridge mode", logger.Fields{"title": title})
} }
// Close is unsupported for Headless but required // Close is unsupported for Bridge but required
// for the Renderer interface // for the Renderer interface
func (h *Headless) Close() { func (h *Bridge) Close() {
h.log.Warn("Close() unsupported in bridge mode") h.log.Warn("Close() unsupported in bridge mode")
} }

File diff suppressed because one or more lines are too long

View File

@ -10,9 +10,9 @@ import (
"github.com/go-playground/colors" "github.com/go-playground/colors"
"github.com/leaanthony/mewn" "github.com/leaanthony/mewn"
"github.com/wailsapp/wails/lib/interfaces"
"github.com/wailsapp/wails/lib/logger" "github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/lib/messages" "github.com/wailsapp/wails/lib/messages"
"github.com/wailsapp/wails/lib/interfaces"
"github.com/wailsapp/webview" "github.com/wailsapp/webview"
) )
@ -29,7 +29,7 @@ type WebView struct {
// NewWebView returns a new WebView struct // NewWebView returns a new WebView struct
func NewWebView() *WebView { func NewWebView() *WebView {
return &WebView{}; return &WebView{}
} }
// Initialise sets up the WebView // Initialise sets up the WebView
@ -173,9 +173,13 @@ func (w *WebView) Run() error {
w.log.Info("Run()") w.log.Info("Run()")
// Runtime assets // Runtime assets
wailsRuntime := mewn.String("../../runtime/js/dist/wails.js") wailsRuntime := mewn.String("../../runtime/assets/wails.js")
w.log.Info("1")
w.evalJS(wailsRuntime) w.evalJS(wailsRuntime)
w.log.Info("2")
// Ping the wait channel when the wails runtime is loaded // Ping the wait channel when the wails runtime is loaded
w.eventManager.On("wails:loaded", func(...interface{}) { w.eventManager.On("wails:loaded", func(...interface{}) {

32
runtime.go Normal file
View File

@ -0,0 +1,32 @@
package wails
import (
"github.com/wailsapp/wails/lib/interfaces"
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/runtime"
)
// CustomLogger type alias
type CustomLogger = logger.CustomLogger
// Runtime is the Wails Runtime Interface, given to a user who has defined the WailsInit method
type Runtime struct {
Events *runtime.Events
Log *runtime.Log
Dialog *runtime.Dialog
Window *runtime.Window
Browser *runtime.Browser
FileSystem *runtime.FileSystem
}
// NewRuntime creates a new Runtime struct
func NewRuntime(eventManager interfaces.EventManager, renderer interfaces.Renderer) *Runtime {
return &Runtime{
Events: runtime.NewEvents(eventManager),
Log: runtime.NewLog(),
Dialog: runtime.NewDialog(renderer),
Window: runtime.NewWindow(renderer),
Browser: runtime.NewBrowser(),
FileSystem: runtime.NewFileSystem(),
}
}

View File

@ -1,21 +1,15 @@
/* /*
Wails Bridge (c) 2019-present Lea Anthony _ __ _ __
| | / /___ _(_) /____
This library creates a bridge between your application | | /| / / __ `/ / / ___/
and the frontend, allowing you to develop your app using | |/ |/ / /_/ / / (__ )
standard tooling (browser extensions, live reload, etc). |__/|__/\__,_/_/_/____/
The lightweight framework for web-like apps
Usage: (c) Lea Anthony 2019-present
```
import Bridge from "./wailsbridge";
Bridge.Start(startApp);
```
The given callback (startApp in the example) will be called
when the bridge has successfully initialised. It passes the
window.wails object back, in case it is not accessible directly.
*/ */
/* jshint esversion: 6 */
function init() {
// Bridge object // Bridge object
window.wailsbridge = { window.wailsbridge = {
reconnectOverlay: null, reconnectOverlay: null,
@ -38,6 +32,7 @@ window.wailsbridge = {
); );
} }
}; };
}
// Adapted from webview - thanks zserge! // Adapted from webview - thanks zserge!
function injectCSS(css) { function injectCSS(css) {
@ -203,14 +198,20 @@ function startBridge() {
connect(); connect();
} }
export default { function start(callback) {
// The main function
// Passes the main Wails object to the callback if given. // Set up the bridge
Start: function (callback) { init();
// Save the callback // Save the callback
window.wailsbridge.callback = callback; window.wailsbridge.callback = callback;
// Start Bridge // Start Bridge
startBridge(); startBridge();
} }
};
function Init(callback) {
start(callback);
}
module.exports = { Init };

View File

@ -1,5 +1,18 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"></head>
<body><div id="app"></div><script type="text/javascript"></script></body> <head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
</head>
<body>
<div id="app"></div>
<script type="text/javascript">function AddScript(js, callbackID) {
var script = document.createElement('script');
script.text = js;
document.body.appendChild(script);
}</script>
</body>
</html> </html>

1
runtime/assets/wails.js Normal file
View File

@ -0,0 +1 @@
!function(n){var t={};function e(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return n[r].call(o.exports,o,o.exports,e),o.l=!0,o.exports}e.m=n,e.c=t,e.d=function(n,t,r){e.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:r})},e.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},e.t=function(n,t){if(1&t&&(n=e(n)),8&t)return n;if(4&t&&"object"==typeof n&&n&&n.__esModule)return n;var r=Object.create(null);if(e.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:n}),2&t&&"string"!=typeof n)for(var o in n)e.d(r,o,function(t){return n[t]}.bind(null,o));return r},e.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return e.d(t,"a",t),t},e.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},e.p="",e(e.s=0)}([function(n,t,e){"use strict";e.r(t);var r={};e.r(r),e.d(r,"Debug",function(){return c}),e.d(r,"Info",function(){return u}),e.d(r,"Warning",function(){return l}),e.d(r,"Error",function(){return f}),e.d(r,"Fatal",function(){return d});var o={};function a(n,t,e){var r={type:n,callbackID:e,payload:t};!function(n){window.external.invoke(n)}(JSON.stringify(r))}function i(n,t){a("log",{level:n,message:t})}function c(n){i("debug",n)}function u(n){i("info",n)}function l(n){i("warning",n)}function f(n){i("error",n)}function d(n){i("fatal",n)}e.r(o),e.d(o,"OpenURL",function(){return y}),e.d(o,"OpenFile",function(){return g});var s,p={};function v(n,t,e){return null!=e&&null!=e||(e=0),new Promise(function(r,o){var i;do{i=n+"-"+s()}while(p[i]);if(e>0)var c=setTimeout(function(){o(Error("Call to "+n+" timed out. Request ID: "+i))},e);p[i]={timeoutHandle:c,reject:o,resolve:r};try{a("call",{bindingName:n,data:JSON.stringify(t)},i)}catch(n){console.error(n)}})}function w(n,t){return v(".wails."+n,t)}function y(n){return w("Browser.OpenURL",n)}function g(n){return w("Browser.OpenFile",n)}s=window.crypto?function(){var n=new Uint32Array(1);return window.crypto.getRandomValues(n)[0]}:function(){return 9007199254740991*Math.random()};var m=function n(t,e){!function(n,t){if(!(n instanceof t))throw new TypeError("Cannot call a class as a function")}(this,n),e=e||-1,this.Callback=function(n){return t.apply(null,n),-1!==e&&0===(e-=1)}},b={};function h(n,t,e){b[n]=b[n]||[];var r=new m(t,e);b[n].push(r)}function O(n){a("event",{name:n,data:JSON.stringify([].slice.apply(arguments).slice(1))})}var S={};function j(n){try{return new Function("var "+n),!0}catch(n){return!1}}function k(){return(k=Object.assign||function(n){for(var t=1;t<arguments.length;t++){var e=arguments[t];for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r])}return n}).apply(this,arguments)}window.backend={},e.d(t,"Init",function(){return N}),window.wails=window.wails||{},window.backend={};var E={NewBinding:function(n){var t=[].concat(n.split(".").splice(1)),e=window.backend;if(t.length>1)for(var r=0;r<t.length-1;r+=1){var o=t[r];if(!j(o))return new Error("".concat(o," is not a valid javascript identifier."));e[o]={},e=e[o]}var a=t.pop();if(!j(a))return new Error("".concat(a," is not a valid javascript identifier."));e[a]=function(){var t=0;function e(){var e=[].slice.call(arguments);return v(n,e,t)}return e.setTimeout=function(n){t=n},e.getTimeout=function(){return t},e}()},Callback:function(n){var t;n=decodeURIComponent(n.replace(/\s+/g,"").replace(/[0-9a-f]{2}/g,"%$&"));try{t=JSON.parse(n)}catch(t){var e="Invalid JSON passed to callback: ".concat(t.message,". Message: ").concat(n);throw c(e),new Error(e)}var r=t.callbackid,o=p[r];if(!o){var a="Callback '".concat(r,"' not registed!!!");throw console.error(a),new Error(a)}clearTimeout(o.timeoutHandle),delete p[r],t.error?o.reject(t.error):o.resolve(t.data)},Notify:function(n,t){if(b[n]){for(var e=b[n].slice(),r=0;r<b[n].length;r+=1){var o=b[n][r],a=[];if(t)try{a=JSON.parse(t)}catch(t){f("Invalid JSON data sent to notify. Event name = "+n)}o.Callback(a)&&e.splice(r,1)}b[n]=e}},AddScript:function(n,t){var e=document.createElement("script");e.text=n,document.body.appendChild(e),t&&O(t)},InjectCSS:function(n){var t=document.createElement("style");t.setAttribute("type","text/css"),t.styleSheet?t.styleSheet.cssText=n:t.appendChild(document.createTextNode(n)),(document.head||document.getElementsByTagName("head")[0]).appendChild(t)},Init:N},C={Log:r,Browser:o,Events:{On:function(n,t){h(n,t)},Emit:O,Heartbeat:function(n,t,e){var r=null;S[n]=function(){clearInterval(r),e()},r=setInterval(function(){O(n)},t)},Acknowledge:function(n){if(!S[n])throw new f("Cannot acknowledge unknown heartbeat '".concat(n,"'"));S[n]()}},_:E};function N(n){n()}k(window.wails,C),O("wails:loaded")}]);

View File

@ -1,17 +0,0 @@
/*
Wails Bridge (c) 2019-present Lea Anthony
This prod version is to get around having to rewrite your code
for production. When doing a release build, this file will be used
instead of the full version.
*/
export default {
// The main function
// Passes the main Wails object to the callback if given.
Start: function (callback) {
if (callback) {
window.wails.Events.On('wails:ready', callback);
}
}
};

View File

@ -7,8 +7,8 @@ type Dialog struct {
renderer interfaces.Renderer renderer interfaces.Renderer
} }
// newDialog creates a new Dialog struct // NewDialog creates a new Dialog struct
func newDialog(renderer interfaces.Renderer) *Dialog { func NewDialog(renderer interfaces.Renderer) *Dialog {
return &Dialog{ return &Dialog{
renderer: renderer, renderer: renderer,
} }

View File

@ -7,7 +7,8 @@ type Events struct {
eventManager interfaces.EventManager eventManager interfaces.EventManager
} }
func newEvents(eventManager interfaces.EventManager) *Events { // NewEvents creates a new Events struct
func NewEvents(eventManager interfaces.EventManager) *Events {
return &Events{ return &Events{
eventManager: eventManager, eventManager: eventManager,
} }

View File

@ -5,8 +5,8 @@ import homedir "github.com/mitchellh/go-homedir"
// FileSystem exposes file system utilities to the runtime // FileSystem exposes file system utilities to the runtime
type FileSystem struct {} type FileSystem struct {}
// Creates a new FileSystem struct // NewFileSystem creates a new FileSystem struct
func newFileSystem() *FileSystem { func NewFileSystem() *FileSystem {
return &FileSystem{} return &FileSystem{}
} }

View File

@ -1,25 +0,0 @@
package runtime
import "github.com/wailsapp/wails/lib/interfaces"
// Runtime is the Wails Runtime Interface, given to a user who has defined the WailsInit method
type Runtime struct {
Events *Events
Log *Log
Dialog *Dialog
Window *Window
Browser *Browser
FileSystem *FileSystem
}
// NewRuntime creates a new Runtime struct
func NewRuntime(eventManager interfaces.EventManager, renderer interfaces.Renderer) *Runtime {
return &Runtime{
Events: newEvents(eventManager),
Log: newLog(),
Dialog: newDialog(renderer),
Window: newWindow(renderer),
Browser: NewBrowser(),
FileSystem: newFileSystem(),
}
}

View File

@ -1,7 +1,9 @@
{ {
"env": { "env": {
"browser": true, "browser": true,
"es6": true "es6": true,
"amd": true,
"node": true,
}, },
"extends": "eslint:recommended", "extends": "eslint:recommended",
"parserOptions": { "parserOptions": {

View File

@ -13,7 +13,12 @@ import { Call } from './calls';
window.backend = {}; window.backend = {};
// Determines if the given identifier is valid Javascript /**
* Determines if the given identifier is valid Javascript
*
* @param {boolean} name
* @returns
*/
function isValidIdentifier(name) { function isValidIdentifier(name) {
// Don't xss yourself :-) // Don't xss yourself :-)
try { try {
@ -24,6 +29,13 @@ function isValidIdentifier(name) {
} }
} }
/**
* NewBinding creates a new binding from the given binding name
*
* @export
* @param {string} bindingName
* @returns
*/
// eslint-disable-next-line max-lines-per-function // eslint-disable-next-line max-lines-per-function
export function NewBinding(bindingName) { export function NewBinding(bindingName) {

View File

@ -11,10 +11,24 @@ The lightweight framework for web-like apps
import { SystemCall } from './calls'; import { SystemCall } from './calls';
/**
* Opens the given URL in the system browser
*
* @export
* @param {string} url
* @returns
*/
export function OpenURL(url) { export function OpenURL(url) {
return SystemCall('Browser.OpenURL', url); return SystemCall('Browser.OpenURL', url);
} }
/**
* Opens the given filename using the system's default file handler
*
* @export
* @param {sting} filename
* @returns
*/
export function OpenFile(filename) { export function OpenFile(filename) {
return SystemCall('Browser.OpenFile', filename); return SystemCall('Browser.OpenFile', filename);
} }

View File

@ -14,18 +14,27 @@ import { SendMessage } from './ipc';
var callbacks = {}; var callbacks = {};
// AwesomeRandom /**
* Returns a number from the native browser random function
*
* @returns number
*/
function cryptoRandom() { function cryptoRandom() {
var array = new Uint32Array(1); var array = new Uint32Array(1);
return window.crypto.getRandomValues(array)[0]; return window.crypto.getRandomValues(array)[0];
} }
// LOLRandom /**
* Returns a number using da old-skool Math.Random
* I likes to call it LOLRandom
*
* @returns number
*/
function basicRandom() { function basicRandom() {
return Math.random() * 9007199254740991; return Math.random() * 9007199254740991;
} }
// Pick one based on browser capability // Pick a random number function based on browser capability
var randomFunc; var randomFunc;
if (window.crypto) { if (window.crypto) {
randomFunc = cryptoRandom; randomFunc = cryptoRandom;
@ -34,13 +43,22 @@ if (window.crypto) {
} }
// Call sends a message to the backend to call the binding with the
// given data. A promise is returned and will be completed when the
// backend responds. This will be resolved when the call was successful
// or rejected if an error is passed back.
// There is a timeout mechanism. If the call doesn't respond in the given
// time (in milliseconds) then the promise is rejected.
/**
* Call sends a message to the backend to call the binding with the
* given data. A promise is returned and will be completed when the
* backend responds. This will be resolved when the call was successful
* or rejected if an error is passed back.
* There is a timeout mechanism. If the call doesn't respond in the given
* time (in milliseconds) then the promise is rejected.
*
* @export
* @param {string} bindingName
* @param {string} data
* @param {number=} timeout
* @returns
*/
export function Call(bindingName, data, timeout) { export function Call(bindingName, data, timeout) {
// Timeout infinite by default // Timeout infinite by default
@ -87,8 +105,14 @@ export function Call(bindingName, data, timeout) {
} }
// Called by the backend to return data to a previously called
// binding invocation /**
* Called by the backend to return data to a previously called
* binding invocation
*
* @export
* @param {string} incomingMessage
*/
export function Callback(incomingMessage) { export function Callback(incomingMessage) {
// Decode the message - Credit: https://stackoverflow.com/a/13865680 // Decode the message - Credit: https://stackoverflow.com/a/13865680
@ -115,12 +139,20 @@ export function Callback(incomingMessage) {
delete callbacks[callbackID]; delete callbacks[callbackID];
if (message.error) { if (message.error) {
return callbackData.reject(message.error); callbackData.reject(message.error);
} else {
callbackData.resolve(message.data);
} }
return callbackData.resolve(message.data);
} }
// systemCall is used to call wails methods from the frontend /**
* SystemCall is used to call wails methods from the frontend
*
* @export
* @param {string} method
* @param {any[]=} data
* @returns
*/
export function SystemCall(method, data) { export function SystemCall(method, data) {
return Call('.wails.' + method, data); return Call('.wails.' + method, data);
} }

View File

@ -13,7 +13,18 @@ import { Error } from './log';
import { SendMessage } from './ipc'; import { SendMessage } from './ipc';
// Defines a single listener with a maximum number of times to callback // Defines a single listener with a maximum number of times to callback
/**
* The Listener class defines a listener! :-)
*
* @class Listener
*/
class Listener { class Listener {
/**
* Creates an instance of Listener.
* @param {function} callback
* @param {number} maxCallbacks
* @memberof Listener
*/
constructor(callback, maxCallbacks) { constructor(callback, maxCallbacks) {
// Default of -1 means infinite // Default of -1 means infinite
maxCallbacks = maxCallbacks || -1; maxCallbacks = maxCallbacks || -1;
@ -34,24 +45,49 @@ class Listener {
var eventListeners = {}; var eventListeners = {};
// Registers an event listener that will be invoked `maxCallbacks` times before being destroyed /**
* Registers an event listener that will be invoked `maxCallbacks` times before being destroyed
*
* @export
* @param {string} eventName
* @param {function} callback
* @param {number} maxCallbacks
*/
export function OnMultiple(eventName, callback, maxCallbacks) { export function OnMultiple(eventName, callback, maxCallbacks) {
eventListeners[eventName] = eventListeners[eventName] || []; eventListeners[eventName] = eventListeners[eventName] || [];
const thisListener = new Listener(callback, maxCallbacks); const thisListener = new Listener(callback, maxCallbacks);
eventListeners[eventName].push(thisListener); eventListeners[eventName].push(thisListener);
} }
// Registers an event listener that will be invoked every time the event is emitted /**
* Registers an event listener that will be invoked every time the event is emitted
*
* @export
* @param {string} eventName
* @param {function} callback
*/
export function On(eventName, callback) { export function On(eventName, callback) {
OnMultiple(eventName, callback); OnMultiple(eventName, callback);
} }
// Registers an event listener that will be invoked once then destroyed /**
* Registers an event listener that will be invoked once then destroyed
*
* @export
* @param {string} eventName
* @param {function} callback
*/
export function Once(eventName, callback) { export function Once(eventName, callback) {
OnMultiple(eventName, callback, 1); OnMultiple(eventName, callback, 1);
} }
// Notify informs frontend listeners that an event was emitted with the given data /**
* Notify informs frontend listeners that an event was emitted with the given data
*
* @export
* @param {string} eventName
* @param {string} data
*/
export function Notify(eventName, data) { export function Notify(eventName, data) {
// Check if we have any listeners for this event // Check if we have any listeners for this event
@ -88,7 +124,12 @@ export function Notify(eventName, data) {
} }
} }
// Emit an event with the given name and data /**
* Emit an event with the given name and data
*
* @export
* @param {string} eventName
*/
export function Emit(eventName) { export function Emit(eventName) {
// Calculate the data // Calculate the data
@ -102,10 +143,18 @@ export function Emit(eventName) {
SendMessage('event', payload); SendMessage('event', payload);
} }
// Callbacks for the heartbeat calls
const heartbeatCallbacks = {}; const heartbeatCallbacks = {};
// Heartbeat emits the event `eventName`, every `timeInMilliseconds` milliseconds until /**
// the event is acknowledged via `Event.Acknowledge`. Once this happens, `callback` is invoked ONCE * Heartbeat emits the event `eventName`, every `timeInMilliseconds` milliseconds until
* the event is acknowledged via `Event.Acknowledge`. Once this happens, `callback` is invoked ONCE
*
* @export
* @param {string} eventName
* @param {number} timeInMilliseconds
* @param {function} callback
*/
export function Heartbeat(eventName, timeInMilliseconds, callback) { export function Heartbeat(eventName, timeInMilliseconds, callback) {
// Declare interval variable // Declare interval variable
@ -128,6 +177,12 @@ export function Heartbeat(eventName, timeInMilliseconds, callback) {
}, timeInMilliseconds); }, timeInMilliseconds);
} }
/**
* Acknowledges a heartbeat event by name
*
* @export
* @param {string} eventName
*/
export function Acknowledge(eventName) { export function Acknowledge(eventName) {
// If we are waiting for acknowledgement for this event type // If we are waiting for acknowledgement for this event type
if (heartbeatCallbacks[eventName]) { if (heartbeatCallbacks[eventName]) {

View File

@ -9,14 +9,23 @@ The lightweight framework for web-like apps
*/ */
/* jshint esversion: 6 */ /* jshint esversion: 6 */
/**
* Invoke sends the given message to the backend
*
* @param {string} message
*/
function Invoke(message) { function Invoke(message) {
if (window && window.external && window.external.invoke) {
window.external.invoke(message); window.external.invoke(message);
} else {
console.log(`[No external.invoke] ${message}`); // eslint-disable-line
}
} }
/**
* Sends a message to the backend based on the given type, payload and callbackID
*
* @export
* @param {string} type
* @param {string} payload
* @param {string=} callbackID
*/
export function SendMessage(type, payload, callbackID) { export function SendMessage(type, payload, callbackID) {
const message = { const message = {
type, type,

View File

@ -7,12 +7,17 @@
The lightweight framework for web-like apps The lightweight framework for web-like apps
(c) Lea Anthony 2019-present (c) Lea Anthony 2019-present
*/ */
/* jshint esversion: 6 */ /* jshint esversion: 6 */
import { SendMessage } from './ipc'; import { SendMessage } from './ipc';
// Sends a log message to the backend with the given /**
// level + message * Sends a log message to the backend with the given level + message
*
* @param {string} level
* @param {string} message
*/
function sendLogMessage(level, message) { function sendLogMessage(level, message) {
// Log Message // Log Message
@ -23,22 +28,52 @@ function sendLogMessage(level, message) {
SendMessage('log', payload); SendMessage('log', payload);
} }
/**
* Log the given debug message with the backend
*
* @export
* @param {string} message
*/
export function Debug(message) { export function Debug(message) {
sendLogMessage('debug', message); sendLogMessage('debug', message);
} }
/**
* Log the given info message with the backend
*
* @export
* @param {string} message
*/
export function Info(message) { export function Info(message) {
sendLogMessage('info', message); sendLogMessage('info', message);
} }
/**
* Log the given warning message with the backend
*
* @export
* @param {string} message
*/
export function Warning(message) { export function Warning(message) {
sendLogMessage('warning', message); sendLogMessage('warning', message);
} }
/**
* Log the given error message with the backend
*
* @export
* @param {string} message
*/
export function Error(message) { export function Error(message) {
sendLogMessage('error', message); sendLogMessage('error', message);
} }
/**
* Log the given fatal message with the backend
*
* @export
* @param {string} message
*/
export function Fatal(message) { export function Fatal(message) {
sendLogMessage('fatal', message); sendLogMessage('fatal', message);
} }

View File

@ -9,6 +9,7 @@ The lightweight framework for web-like apps
*/ */
/* jshint esversion: 6 */ /* jshint esversion: 6 */
import * as Log from './log'; import * as Log from './log';
import * as Browser from './browser';
import { On, Emit, Notify, Heartbeat, Acknowledge } from './events'; import { On, Emit, Notify, Heartbeat, Acknowledge } from './events';
import { NewBinding } from './bindings'; import { NewBinding } from './bindings';
import { Callback } from './calls'; import { Callback } from './calls';
@ -24,12 +25,14 @@ var internal = {
Callback, Callback,
Notify, Notify,
AddScript, AddScript,
InjectCSS InjectCSS,
Init,
}; };
// Setup runtime structure // Setup runtime structure
var runtime = { var runtime = {
Log, Log,
Browser,
Events: { Events: {
On, On,
Emit, Emit,
@ -44,3 +47,8 @@ Object.assign(window.wails, runtime);
// Emit loaded event // Emit loaded event
Emit('wails:loaded'); Emit('wails:loaded');
// Nothing to init in production
export function Init(callback) {
callback();
}

View File

@ -1 +0,0 @@
!function(n){var e={};function t(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return n[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,r){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:r})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var r=Object.create(null);if(t.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(r,o,function(e){return n[e]}.bind(null,o));return r},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){"use strict";t.r(e);var r={};function o(n,e,t){!function(n){window&&window.external&&window.external.invoke?window.external.invoke(n):console.log("[No external.invoke] ".concat(n))}(JSON.stringify({type:n,callbackID:t,payload:e}))}function a(n,e){o("log",{level:n,message:e})}function i(n){a("debug",n)}function c(n){a("info",n)}function u(n){a("warning",n)}function l(n){a("error",n)}function f(n){a("fatal",n)}t.r(r),t.d(r,"Debug",function(){return i}),t.d(r,"Info",function(){return c}),t.d(r,"Warning",function(){return u}),t.d(r,"Error",function(){return l}),t.d(r,"Fatal",function(){return f});var d=function n(e,t){(function(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")})(this,n),t=t||-1,this.Callback=function(n){return e.apply(null,n),-1!==t&&0===(t-=1)}},s={};function p(n,e,t){s[n]=s[n]||[];var r=new d(e,t);s[n].push(r)}function v(n){o("event",{name:n,data:JSON.stringify([].slice.apply(arguments).slice(1))})}var w={};var y={};var g=window.crypto?function(){var n=new Uint32Array(1);return window.crypto.getRandomValues(n)[0]}:function(){return 9007199254740991*Math.random()};function m(n,e,t){return(null==t||null==t)&&(t=0),new Promise(function(r,a){var i;do{i=n+"-"+g()}while(y[i]);if(0<t)var c=setTimeout(function(){a(Error("Call to "+n+" timed out. Request ID: "+i))},t);y[i]={timeoutHandle:c,reject:a,resolve:r};try{o("call",{bindingName:n,data:JSON.stringify(e)},i)}catch(n){console.error(n)}})}function b(n){try{return new Function("var "+n),!0}catch(n){return!1}}function h(){return(h=Object.assign||function(n){for(var e,t=1;t<arguments.length;t++)for(var r in e=arguments[t])Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}).apply(this,arguments)}window.backend={},window.wails=window.wails||{},window.backend={};var O={NewBinding:function(n){var e=[].concat(n.split(".").splice(1)),t=window.backend;if(1<e.length)for(var r,o=0;o<e.length-1;o+=1){if(!b(r=e[o]))return new Error("".concat(r," is not a valid javascript identifier."));t[r]={},t=t[r]}var a=e.pop();return b(a)?void(t[a]=function(){function e(){var e=[].slice.call(arguments);return m(n,e,t)}var t=0;return e.setTimeout=function(n){t=n},e.getTimeout=function(){return t},e}()):new Error("".concat(a," is not a valid javascript identifier."))},Callback:function(n){var e;n=decodeURIComponent(n.replace(/\s+/g,"").replace(/[0-9a-f]{2}/g,"%$&"));try{e=JSON.parse(n)}catch(e){return i("Invalid JSON passed to callback: "+e.message),void i("Message: "+n)}var t=e.callbackid,r=y[t];return r?(clearTimeout(r.timeoutHandle),delete y[t],e.error?r.reject(e.error):r.resolve(e.data)):void console.error("Callback '".concat(t,"' not registed!!!"))},Notify:function(n,e){if(s[n]){for(var t=s[n].slice(),r=0;r<s[n].length;r+=1){var o=s[n][r],a=[];if(e)try{a=JSON.parse(e)}catch(e){l("Invalid JSON data sent to notify. Event name = "+n)}o.Callback(a)&&t.splice(r,1)}s[n]=t}},AddScript:function(n,e){var t=document.createElement("script");t.text=n,document.body.appendChild(t),e&&v(e)},InjectCSS:function(n){var e=document.createElement("style");e.setAttribute("type","text/css"),e.styleSheet?e.styleSheet.cssText=n:e.appendChild(document.createTextNode(n)),(document.head||document.getElementsByTagName("head")[0]).appendChild(e)}},S={Log:r,Events:{On:function(n,e){p(n,e)},Emit:v,Heartbeat:function(n,e,t){var r=null;w[n]=function(){clearInterval(r),t()},r=setInterval(function(){v(n)},e)},Acknowledge:function(n){if(!w[n])throw new l("Cannot acknowledge unknown heartbeat '".concat(n,"'"));w[n]()}},_:O};h(window.wails,S),v("wails:loaded")}]);

View File

@ -4,7 +4,8 @@
"description": "The Javascript Wails Runtime", "description": "The Javascript Wails Runtime",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"build": "eslint src/ && webpack --config webpack.config.js", "build": "eslint core/ && npm run build:prod",
"build:prod": "webpack --env prod --colors",
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"repository": { "repository": {

View File

@ -0,0 +1 @@
bridge.js

View File

@ -0,0 +1,3 @@
# Wails Runtime
This module is the Javascript runtime library for the [Wails](https://wails.app) framework. It is intended to be installed as part of a [Wails](https://wails.app) project, not a standalone module.

View File

@ -0,0 +1,37 @@
/*
_ __ _ __
| | / /___ _(_) /____
| | /| / / __ `/ / / ___/
| |/ |/ / /_/ / / (__ )
|__/|__/\__,_/_/_/____/
The lightweight framework for web-like apps
(c) Lea Anthony 2019-present
*/
/* jshint esversion: 6 */
/**
* Opens the given URL in the system browser
*
* @export
* @param {string} url
* @returns
*/
function OpenURL(url) {
return window.wails.Browser.OpenURL(url);
}
/**
* Opens the given filename using the system's default file handler
*
* @export
* @param {sting} filename
* @returns
*/
function OpenFile(filename) {
return window.wails.Browser.OpenFile(filename);
}
module.exports = {
OpenURL,
OpenFile
};

View File

@ -0,0 +1,89 @@
/*
_ __ _ __
| | / /___ _(_) /____
| | /| / / __ `/ / / ___/
| |/ |/ / /_/ / / (__ )
|__/|__/\__,_/_/_/____/
The lightweight framework for web-like apps
(c) Lea Anthony 2019-present
*/
/* jshint esversion: 6 */
/**
* Registers an event listener that will be invoked `maxCallbacks` times before being destroyed
*
* @export
* @param {string} eventName
* @param {function} callback
* @param {number} maxCallbacks
*/
function OnMultiple(eventName, callback, maxCallbacks) {
window.wails.Events.OnMultiple(eventName, callback, maxCallbacks);
}
/**
* Registers an event listener that will be invoked every time the event is emitted
*
* @export
* @param {string} eventName
* @param {function} callback
*/
function On(eventName, callback) {
OnMultiple(eventName, callback);
}
/**
* Registers an event listener that will be invoked once then destroyed
*
* @export
* @param {string} eventName
* @param {function} callback
*/
function Once(eventName, callback) {
OnMultiple(eventName, callback, 1);
}
/**
* Emit an event with the given name and data
*
* @export
* @param {string} eventName
*/
function Emit(eventName) {
return window.wails.Events.Emit(eventName);
}
/**
* Heartbeat emits the event `eventName`, every `timeInMilliseconds` milliseconds until
* the event is acknowledged via `Event.Acknowledge`. Once this happens, `callback` is invoked ONCE
*
* @export
* @param {*} eventName
* @param {*} timeInMilliseconds
* @param {*} callback
*/
function Heartbeat(eventName, timeInMilliseconds, callback) {
window.wails.Events.Heartbeat(eventName, timeInMilliseconds, callback);
}
/**
* Acknowledges a heartbeat event by name
*
* @export
* @param {string} eventName
*/
function Acknowledge(eventName) {
return window.wails.Events.Acknowledge(eventName);
}
module.exports = {
OnMultiple,
On,
Once,
Emit,
Heartbeat,
Acknowledge
};

View File

@ -0,0 +1,23 @@
/*
_ __ _ __
| | / /___ _(_) /____
| | /| / / __ `/ / / ___/
| |/ |/ / /_/ / / (__ )
|__/|__/\__,_/_/_/____/
The lightweight framework for web-like apps
(c) Lea Anthony 2019-present
*/
/* jshint esversion: 6 */
/**
* Initialises the Wails runtime
*
* @param {function} callback
*/
function Init(callback) {
window.wails._.Init(callback);
}
module.exports = {
Init
};

70
runtime/js/runtime/log.js Normal file
View File

@ -0,0 +1,70 @@
/*
_ __ _ __
| | / /___ _(_) /____
| | /| / / __ `/ / / ___/
| |/ |/ / /_/ / / (__ )
|__/|__/\__,_/_/_/____/
The lightweight framework for web-like apps
(c) Lea Anthony 2019-present
*/
/* jshint esversion: 6 */
/**
* Log the given debug message with the backend
*
* @export
* @param {string} message
*/
function Debug(message) {
window.wails.Log.Debug(message);
}
/**
* Log the given info message with the backend
*
* @export
* @param {string} message
*/
function Info(message) {
window.wails.Log.Info(message);
}
/**
* Log the given warning message with the backend
*
* @export
* @param {string} message
*/
function Warning(message) {
window.wails.Log.Warning(message);
}
/**
* Log the given error message with the backend
*
* @export
* @param {string} message
*/
function Error(message) {
window.wails.Log.Error(message);
}
/**
* Log the given fatal message with the backend
*
* @export
* @param {string} message
*/
function Fatal(message) {
window.wails.Log.Fatal(message);
}
module.exports = {
Debug,
Info,
Warning,
Error,
Fatal
};

View File

@ -0,0 +1,22 @@
/*
_ __ _ __
| | / /___ _(_) /____
| | /| / / __ `/ / / ___/
| |/ |/ / /_/ / / (__ )
|__/|__/\__,_/_/_/____/
The lightweight framework for web-like apps
(c) Lea Anthony 2019-present
*/
/* jshint esversion: 6 */
const Log = require('./log');
const Browser = require('./browser');
const Events = require('./events');
const Init = require('./init');
module.exports = {
Log,
Browser,
Events,
Init
};

View File

@ -0,0 +1,24 @@
{
"name": "@wailsapp/runtime",
"version": "1.0.2",
"description": "Wails Javascript runtime library",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/wailsapp/wails.git"
},
"keywords": [
"Wails",
"Javascript",
"Go"
],
"author": "Lea Anthony <lea.anthony@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/wailsapp/wails/issues"
},
"homepage": "https://github.com/wailsapp/wails#readme"
}

View File

@ -1,38 +1,4 @@
/* eslint-disable */ /* eslint-disable */
module.exports = (env) => {
const path = require('path'); return require(`./webpack.${env}.js`);
module.exports = {
entry: './src/main',
mode: 'production',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'wails.js'
},
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
plugins: ['@babel/plugin-transform-object-assign'],
presets: [
[
'@babel/preset-env',
{
'useBuiltIns': 'entry',
'corejs': {
'version': 3,
'proposals': true
}
}
], ['minify']
]
}
}
}
]
}
}; };

View File

@ -0,0 +1,38 @@
/* eslint-disable */
const path = require('path');
module.exports = {
entry: './core/main',
mode: 'production',
output: {
path: path.resolve(__dirname, '..', 'assets'),
filename: 'wails.js'
},
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
plugins: ['@babel/plugin-transform-object-assign'],
presets: [
[
'@babel/preset-env',
{
'useBuiltIns': 'entry',
'corejs': {
'version': 3,
'proposals': true
}
}
]
]
}
}
}
]
}
};

View File

@ -5,8 +5,8 @@ import "github.com/wailsapp/wails/lib/logger"
// Log exposes the logging interface to the runtime // Log exposes the logging interface to the runtime
type Log struct{} type Log struct{}
// newLog creates a new Log struct // NewLog creates a new Log struct
func newLog() *Log { func NewLog() *Log {
return &Log{} return &Log{}
} }

View File

@ -7,7 +7,8 @@ type Window struct {
renderer interfaces.Renderer renderer interfaces.Renderer
} }
func newWindow(renderer interfaces.Renderer) *Window { // NewWindow creates a new Window struct
func NewWindow(renderer interfaces.Renderer) *Window {
return &Window{ return &Window{
renderer: renderer, renderer: renderer,
} }

View File

@ -1,9 +1,20 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Build runtime # Build runtime
echo "**** Building Runtime ****"
cd runtime/js cd runtime/js
npm run build npm run build
cd ../.. cd ../..
echo "**** Packing Assets ****"
mewn mewn
echo "**** Installing Wails locally ****"
cd cmd/wails
go install
cd ../..
echo "**** Tidying the mods! ****"
go mod tidy
echo "**** WE ARE DONE! ****"

4
wails-mewn.go Normal file
View File

@ -0,0 +1,4 @@
package wails
// Autogenerated by Mewn - Do not alter