diff --git a/v2/cmd/wails/internal/commands/dev/dev.go b/v2/cmd/wails/internal/commands/dev/dev.go
index df452d1fe..349c21394 100644
--- a/v2/cmd/wails/internal/commands/dev/dev.go
+++ b/v2/cmd/wails/internal/commands/dev/dev.go
@@ -83,7 +83,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
flags := defaultDevFlags()
command.StringFlag("ldflags", "optional ldflags", &flags.ldflags)
command.StringFlag("compiler", "Use a different go compiler to build, eg go1.15beta1", &flags.compilerCommand)
- command.StringFlag("assetdir", "Serve assets from the given directory", &flags.assetDir)
+ command.StringFlag("assetdir", "Serve assets from the given directory instead of using the provided asset FS", &flags.assetDir)
command.StringFlag("e", "Extensions to trigger rebuilds (comma separated) eg go", &flags.extensions)
command.BoolFlag("browser", "Open application in browser", &flags.openBrowser)
command.BoolFlag("noreload", "Disable reload on asset change", &flags.noReload)
@@ -301,10 +301,6 @@ func loadAndMergeProjectConfig(cwd string, flags *devFlags) (*project.Project, e
var shouldSaveConfig bool
- if projectConfig.AssetDirectory == "" && flags.assetDir == "" {
- return nil, fmt.Errorf("No asset directory provided. Please use -assetdir to indicate which directory contains your built assets.")
- }
-
if flags.assetDir == "" && projectConfig.AssetDirectory != "" {
flags.assetDir = projectConfig.AssetDirectory
}
@@ -313,9 +309,11 @@ func loadAndMergeProjectConfig(cwd string, flags *devFlags) (*project.Project, e
projectConfig.AssetDirectory = filepath.ToSlash(flags.assetDir)
}
- flags.assetDir, err = filepath.Abs(flags.assetDir)
- if err != nil {
- return nil, err
+ if flags.assetDir != "" {
+ flags.assetDir, err = filepath.Abs(flags.assetDir)
+ if err != nil {
+ return nil, err
+ }
}
if flags.devServerURL == defaultDevServerURL && projectConfig.DevServerURL != defaultDevServerURL && projectConfig.DevServerURL != "" {
@@ -507,7 +505,8 @@ func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Proc
interval := time.Duration(flags.debounceMS) * time.Millisecond
timer := time.NewTimer(interval)
rebuild := false
- reload := false
+ assetDir := ""
+ changedPaths := map[string]struct{}{}
for quit == false {
//reload := false
select {
@@ -519,12 +518,13 @@ func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Proc
// Check for file writes
if item.Op&fsnotify.Write == fsnotify.Write {
// Ignore directories
- if fs.DirExists(item.Name) {
+ itemName := item.Name
+ if fs.DirExists(itemName) {
continue
}
// Iterate all file patterns
- ext := filepath.Ext(item.Name)
+ ext := filepath.Ext(itemName)
if ext != "" {
ext = ext[1:]
if _, exists := extensionsThatTriggerARebuild[ext]; exists {
@@ -534,9 +534,8 @@ func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Proc
}
}
- if strings.HasPrefix(item.Name, flags.assetDir) {
- reload = true
- }
+ changedPaths[filepath.Dir(itemName)] = struct{}{}
+
timer.Reset(interval)
}
// Check for new directories
@@ -568,6 +567,34 @@ func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Proc
debugBinaryProcess = newBinaryProcess
}
}
+ reload := false
+ if len(changedPaths) != 0 {
+ if assetDir == "" {
+ resp, err := http.Get("http://localhost:34115/wails/assetdir")
+ if err != nil {
+ LogRed("Error during retrieving assetdir: %s", err.Error())
+ } else {
+ content, err := io.ReadAll(resp.Body)
+ if err != nil {
+ LogRed("Error reading assetdir from devserver: %s", err.Error())
+ } else {
+ assetDir = string(content)
+ }
+ resp.Body.Close()
+ }
+ }
+
+ if assetDir != "" {
+ for path := range changedPaths {
+ if strings.HasPrefix(path, assetDir) {
+ reload = true
+ break
+ }
+ }
+ }
+
+ changedPaths = map[string]struct{}{}
+ }
if reload {
reload = false
_, err = http.Get("http://localhost:34115/wails/reload")
diff --git a/v2/cmd/wails/internal/commands/generate/template/base/NEXTSTEPS.md.template b/v2/cmd/wails/internal/commands/generate/template/base/NEXTSTEPS.md.template
index a357acd82..5363d10f2 100644
--- a/v2/cmd/wails/internal/commands/generate/template/base/NEXTSTEPS.md.template
+++ b/v2/cmd/wails/internal/commands/generate/template/base/NEXTSTEPS.md.template
@@ -10,7 +10,6 @@ The next steps to complete the template are:
- It is really important to ensure `helpurl` is valid as this is where users of the template will be directed for help.
2. Update `README.md`.
3. Edit `wails.json` and ensure all fields are correct, especially:
- - `assetdir` - path to your assets
- `wailsjsdir` - path to generate wailsjs modules
- `frontend:install` - The command to install your frontend dependencies
- `frontend:build` - The command to build your frontend
diff --git a/v2/cmd/wails/internal/commands/generate/template/base/wails.tmpl.json b/v2/cmd/wails/internal/commands/generate/template/base/wails.tmpl.json
index b8d08108d..63c4e6fe7 100644
--- a/v2/cmd/wails/internal/commands/generate/template/base/wails.tmpl.json
+++ b/v2/cmd/wails/internal/commands/generate/template/base/wails.tmpl.json
@@ -1,7 +1,6 @@
{
"name": "{{.ProjectName}}",
"outputfilename": "{{.BinaryName}}",
- "assetdir": "frontend/dist",
"frontend:install": "npm install",
"frontend:build": "npm run build",
"author": {
diff --git a/v2/cmd/wails/internal/commands/initialise/templates/ides/goland/workspace.tmpl.xml b/v2/cmd/wails/internal/commands/initialise/templates/ides/goland/workspace.tmpl.xml
index 42f98045b..9727844b6 100644
--- a/v2/cmd/wails/internal/commands/initialise/templates/ides/goland/workspace.tmpl.xml
+++ b/v2/cmd/wails/internal/commands/initialise/templates/ides/goland/workspace.tmpl.xml
@@ -36,7 +36,6 @@
-
diff --git a/v2/cmd/wails/internal/commands/initialise/templates/ides/vscode/launch.tmpl.json b/v2/cmd/wails/internal/commands/initialise/templates/ides/vscode/launch.tmpl.json
index d03d5ccdb..005f9bc41 100644
--- a/v2/cmd/wails/internal/commands/initialise/templates/ides/vscode/launch.tmpl.json
+++ b/v2/cmd/wails/internal/commands/initialise/templates/ides/vscode/launch.tmpl.json
@@ -9,11 +9,7 @@
"program": "${workspaceFolder}/{{.PathToDesktopBinary}}",
"preLaunchTask": "build",
"cwd": "${workspaceFolder}",
- "env": {},
- "args": [
- "-assetdir",
- "{{.AssetDir}}"
- ]
+ "env": {}
}
]
}
\ No newline at end of file
diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates.go b/v2/cmd/wails/internal/commands/initialise/templates/templates.go
index 641916783..2c81e05be 100644
--- a/v2/cmd/wails/internal/commands/initialise/templates/templates.go
+++ b/v2/cmd/wails/internal/commands/initialise/templates/templates.go
@@ -42,7 +42,6 @@ type Data struct {
AuthorNameAndEmail string
WailsDirectory string
GoSDKPath string
- AssetDir string
WindowsFlags string
CGOEnabled string
OutputFile string
@@ -60,7 +59,6 @@ type Options struct {
InitGit bool
AuthorName string
AuthorEmail string
- AssetDir string
IDE string
ProjectNameFilename string // The project name but as a valid filename
WailsVersion string
@@ -261,7 +259,6 @@ func Install(options *Options) (bool, *Template, error) {
AuthorName: options.AuthorName,
WailsVersion: options.WailsVersion,
GoSDKPath: options.GoSDKPath,
- AssetDir: options.AssetDir,
}
// Create a formatted name and email combo.
@@ -408,22 +405,6 @@ func installIDEFiles(o ideOptions) error {
binaryName += ".exe"
}
- // Parse wails.json for assetdir
- wailsJSONBytes, err := os.ReadFile(filepath.Join(o.options.TargetDir, "wails.json"))
- if err != nil {
- return err
- }
- var wailsJSON map[string]interface{}
- err = json.Unmarshal(wailsJSONBytes, &wailsJSON)
- if err != nil {
- return err
- }
- assetDir := wailsJSON["assetdir"]
- if assetDir == "" {
- return fmt.Errorf("Unable to find 'assetdir' in 'wails.json' ")
- }
-
- o.options.AssetDir = assetDir.(string)
o.options.PathToDesktopBinary = filepath.ToSlash(filepath.Join("build", "bin", binaryName))
o.options.WindowsFlags = ""
diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/wails.tmpl.json b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/wails.tmpl.json
index 5bf6b2a9b..eb4c38f71 100644
--- a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/wails.tmpl.json
+++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/wails.tmpl.json
@@ -1,7 +1,6 @@
{
"name": "{{.ProjectName}}",
"outputfilename": "{{.BinaryName}}",
- "assetdir": "frontend/dist",
"frontend:install": "npm install",
"frontend:build": "npm run build",
"wailsjsdir": "./frontend",
diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/wails.tmpl.json b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/wails.tmpl.json
index a4f2bc2dd..b010f3203 100644
--- a/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/wails.tmpl.json
+++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/wails.tmpl.json
@@ -1,7 +1,6 @@
{
"name": "{{.ProjectName}}",
"outputfilename": "{{.BinaryName}}",
- "assetdir": "frontend/src",
"wailsjsdir": "./frontend",
"author": {
"name": "{{.AuthorName}}",
diff --git a/v2/internal/appng/app_dev.go b/v2/internal/appng/app_dev.go
index b232e21dc..cf1bcdef7 100644
--- a/v2/internal/appng/app_dev.go
+++ b/v2/internal/appng/app_dev.go
@@ -5,7 +5,10 @@ package appng
import (
"context"
+ "embed"
"flag"
+ "fmt"
+ iofs "io/fs"
"os"
"path/filepath"
@@ -91,12 +94,30 @@ func CreateApp(appoptions *options.App) (*App, error) {
}
}
+ if assetdir == "" {
+ // If no assetdir has been defined, let's try to infer it from the project root and the asset FS.
+ assetdir, err = tryInferAssetDirFromFS(appoptions.Assets)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if assetdir != "" {
+ // Let's override the assets to serve from on disk, if needed
+ absdir, err := filepath.Abs(assetdir)
+ if err != nil {
+ return nil, err
+ }
+
+ myLogger.Info("Serving assets from disk: %s", absdir)
+ appoptions.Assets = os.DirFS(absdir)
+
+ ctx = context.WithValue(ctx, "assetdir", assetdir)
+ }
+
if devServerURL != "" {
ctx = context.WithValue(ctx, "devserverurl", devServerURL)
}
- if assetdir != "" {
- ctx = context.WithValue(ctx, "assetdir", assetdir)
- }
if loglevel != "" {
level, err := pkglogger.StringToLogLevel(loglevel)
@@ -203,3 +224,32 @@ func generateBindings(bindings *binding.Bindings) error {
return nil
}
+
+func tryInferAssetDirFromFS(assets iofs.FS) (string, error) {
+ if _, isEmbedFs := assets.(embed.FS); !isEmbedFs {
+ // We only infer the assetdir for embed.FS assets
+ return "", nil
+ }
+
+ path, err := fs.FindPathToFile(assets, "index.html")
+ if err != nil {
+ return "", err
+ }
+
+ path, err = filepath.Abs(path)
+ if err != nil {
+ return "", err
+ }
+
+ if _, err := os.Stat(filepath.Join(path, "index.html")); err != nil {
+ if os.IsNotExist(err) {
+ err = fmt.Errorf(
+ "inferred assetdir '%s' does not exist or does not contain an 'index.html' file, "+
+ "please specify it with -assetdir or set it in wails.json",
+ path)
+ }
+ return "", err
+ }
+
+ return path, nil
+}
diff --git a/v2/internal/frontend/assetserver/assetserver_browser_dev.go b/v2/internal/frontend/assetserver/assetserver_browser_dev.go
index 0363bcd05..93de7607a 100644
--- a/v2/internal/frontend/assetserver/assetserver_browser_dev.go
+++ b/v2/internal/frontend/assetserver/assetserver_browser_dev.go
@@ -12,7 +12,6 @@ import (
"github.com/wailsapp/wails/v2/internal/frontend/runtime"
"github.com/wailsapp/wails/v2/internal/logger"
"golang.org/x/net/html"
- "path/filepath"
)
/*
@@ -26,7 +25,6 @@ type BrowserAssetServer struct {
assets fs.FS
runtimeJS []byte
logger *logger.Logger
- fromDisk bool
}
func NewBrowserAssetServer(ctx context.Context, assets fs.FS, bindingsJSON string) (*BrowserAssetServer, error) {
@@ -37,7 +35,7 @@ func NewBrowserAssetServer(ctx context.Context, assets fs.FS, bindingsJSON strin
}
var err error
- result.assets, result.fromDisk, err = prepareAssetsForServing(ctx, "BrowserAssetServer", assets)
+ result.assets, err = prepareAssetsForServing(assets)
if err != nil {
return nil, err
}
@@ -108,11 +106,7 @@ func (a *BrowserAssetServer) Load(filename string) ([]byte, string, error) {
content = runtime.WebsocketIPC
default:
filename = strings.TrimPrefix(filename, "/")
- fromDisk := ""
- if a.fromDisk {
- fromDisk = " (disk)"
- }
- a.LogDebug("Loading file: %s%s", filename, fromDisk)
+ a.LogDebug("Loading file: %s", filename)
content, err = fs.ReadFile(a.assets, filename)
}
if err != nil {
diff --git a/v2/internal/frontend/assetserver/assetserver_common.go b/v2/internal/frontend/assetserver/assetserver_common.go
index 09e0c3c06..d8ab4c9c6 100644
--- a/v2/internal/frontend/assetserver/assetserver_common.go
+++ b/v2/internal/frontend/assetserver/assetserver_common.go
@@ -1,64 +1,25 @@
package assetserver
import (
- "context"
- "fmt"
- "io/fs"
+ iofs "io/fs"
"path"
- "path/filepath"
- "strings"
- "github.com/leaanthony/slicer"
+ "github.com/wailsapp/wails/v2/internal/fs"
)
-func prepareAssetsForServing(ctx context.Context, serverType string, assets fs.FS) (fs.FS, bool, error) {
- // Let's check if we need an assetdir override
- if assetdir, err := isAssetDirOverride(ctx, serverType, assets); err != nil {
- return nil, false, err
- } else if assetdir != nil {
- return assetdir, true, nil
- }
-
- // Otherwise let's search for the index.html
+func prepareAssetsForServing(assets iofs.FS) (iofs.FS, error) {
if _, err := assets.Open("."); err != nil {
- return nil, false, err
+ return nil, err
}
- subDir, err := pathToIndexHTML(assets)
+ subDir, err := fs.FindPathToFile(assets, "index.html")
if err != nil {
- return nil, false, err
+ return nil, err
}
- assets, err = fs.Sub(assets, path.Clean(subDir))
+ assets, err = iofs.Sub(assets, path.Clean(subDir))
if err != nil {
- return nil, false, err
+ return nil, err
}
- return assets, false, nil
-}
-
-func pathToIndexHTML(assets fs.FS) (string, error) {
- stat, _ := fs.Stat(assets, "index.html")
- if stat != nil {
- return ".", nil
- }
- var indexFiles slicer.StringSlicer
- err := fs.WalkDir(assets, ".", func(path string, d fs.DirEntry, err error) error {
- if err != nil {
- return err
- }
- if strings.HasSuffix(path, "index.html") {
- indexFiles.Add(path)
- }
- return nil
- })
- if err != nil {
- return "", err
- }
-
- if indexFiles.Length() > 1 {
- return "", fmt.Errorf("multiple 'index.html' files found in assets")
- }
-
- path, _ := filepath.Split(indexFiles.AsSlice()[0])
- return path, nil
+ return assets, nil
}
diff --git a/v2/internal/frontend/assetserver/assetserver_common_dev.go b/v2/internal/frontend/assetserver/assetserver_common_dev.go
deleted file mode 100644
index 51cbc6ba2..000000000
--- a/v2/internal/frontend/assetserver/assetserver_common_dev.go
+++ /dev/null
@@ -1,57 +0,0 @@
-//go:build dev
-// +build dev
-
-package assetserver
-
-import (
- "context"
- "embed"
- "io/fs"
- "os"
- "path/filepath"
-
- "github.com/wailsapp/wails/v2/internal/logger"
-)
-
-func isAssetDirOverride(ctx context.Context, serverType string, assets fs.FS) (fs.FS, error) {
- _logger, _ := ctx.Value("logger").(*logger.Logger)
- logWarning := func(msg string) {
- if _logger == nil {
- return
- }
-
- _logger.Warning("[%s] Reloading changed asset files won't work: %s", serverType, msg)
- }
-
- _, isEmbedFs := assets.(embed.FS)
- if assetdir := ctx.Value("assetdir"); assetdir == nil {
- // We are in dev mode, but have no assetdir, let's check the type of the assets FS.
- if isEmbedFs {
- logWarning("A 'embed.FS' has been provided, but no assetdir has been defined")
- } else {
- // That's fine, the user is using another fs.FS and also has not set an assetdir
- }
- } else {
- // We are in dev mode and an assetdir has been defined
- if isEmbedFs {
- // If the fs.FS is an embed.FS we assume it's serving the files from this directory, therefore replace the assets
- // fs.FS with this directory.
- // Are there any better ways to detect this? If we could somehow determine the compile time source path for the
- // embed.FS this would be great. Currently there seems no way to get to this information.
- absdir, err := filepath.Abs(assetdir.(string))
- if err != nil {
- return nil, err
- }
-
- if _logger != nil {
- _logger.Debug("[%s] Serving assets from disk: %s", serverType, absdir)
- }
-
- return os.DirFS(absdir), nil
- } else {
- logWarning("An assetdir has been defined, but no 'embed.FS' has been provided")
- }
- }
-
- return nil, nil
-}
diff --git a/v2/internal/frontend/assetserver/assetserver_common_nodev.go b/v2/internal/frontend/assetserver/assetserver_common_nodev.go
deleted file mode 100644
index 2d9fd7187..000000000
--- a/v2/internal/frontend/assetserver/assetserver_common_nodev.go
+++ /dev/null
@@ -1,12 +0,0 @@
-//go:build !dev
-
-package assetserver
-
-import (
- "context"
- "io/fs"
-)
-
-func isAssetDirOverride(ctx context.Context, serverType string, assets fs.FS) (fs.FS, error) {
- return nil, nil
-}
diff --git a/v2/internal/frontend/assetserver/assetserver_desktop.go b/v2/internal/frontend/assetserver/assetserver_desktop.go
index d48e80973..6d4c15b22 100644
--- a/v2/internal/frontend/assetserver/assetserver_desktop.go
+++ b/v2/internal/frontend/assetserver/assetserver_desktop.go
@@ -15,7 +15,6 @@ type DesktopAssetServer struct {
assets fs.FS
runtimeJS []byte
logger *logger.Logger
- fromDisk bool
}
func NewDesktopAssetServer(ctx context.Context, assets fs.FS, bindingsJSON string) (*DesktopAssetServer, error) {
@@ -27,7 +26,7 @@ func NewDesktopAssetServer(ctx context.Context, assets fs.FS, bindingsJSON strin
}
var err error
- result.assets, result.fromDisk, err = prepareAssetsForServing(ctx, "DesktopAssetServer", assets)
+ result.assets, err = prepareAssetsForServing(assets)
if err != nil {
return nil, err
}
@@ -84,11 +83,7 @@ func (a *DesktopAssetServer) Load(filename string) ([]byte, string, error) {
content = runtime.DesktopIPC
default:
filename = strings.TrimPrefix(filename, "/")
- fromDisk := ""
- if a.fromDisk {
- fromDisk = " (disk)"
- }
- a.LogDebug("Loading file: %s%s", filename, fromDisk)
+ a.LogDebug("Loading file: %s", filename)
content, err = fs.ReadFile(a.assets, filename)
}
if err != nil {
diff --git a/v2/internal/frontend/devserver/devserver.go b/v2/internal/frontend/devserver/devserver.go
index 4b9885fe3..3dcb84c56 100644
--- a/v2/internal/frontend/devserver/devserver.go
+++ b/v2/internal/frontend/devserver/devserver.go
@@ -49,6 +49,11 @@ func (d *DevWebServer) WindowReload() {
func (d *DevWebServer) Run(ctx context.Context) error {
d.ctx = ctx
+ assetdir, _ := ctx.Value("assetdir").(string)
+ d.server.Get("/wails/assetdir", func(fctx *fiber.Ctx) error {
+ return fctx.SendString(assetdir)
+ })
+
d.server.Get("/wails/reload", func(fctx *fiber.Ctx) error {
d.WindowReload()
d.desktopFrontend.WindowReload()
diff --git a/v2/internal/fs/fs.go b/v2/internal/fs/fs.go
index 862e003cb..0f6fbb7ea 100644
--- a/v2/internal/fs/fs.go
+++ b/v2/internal/fs/fs.go
@@ -4,9 +4,11 @@ import (
"crypto/md5"
"fmt"
"io"
+ "io/fs"
"os"
"path/filepath"
"runtime"
+ "strings"
"unsafe"
"github.com/leaanthony/slicer"
@@ -394,3 +396,30 @@ func MoveDirExtended(src string, dst string, ignore []string) (err error) {
return
}
+
+func FindPathToFile(fsys fs.FS, file string) (string, error) {
+ stat, _ := fs.Stat(fsys, file)
+ if stat != nil {
+ return ".", nil
+ }
+ var indexFiles slicer.StringSlicer
+ err := fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+ if strings.HasSuffix(path, file) {
+ indexFiles.Add(path)
+ }
+ return nil
+ })
+ if err != nil {
+ return "", err
+ }
+
+ if indexFiles.Length() > 1 {
+ return "", fmt.Errorf("multiple '%s' files found in assets", file)
+ }
+
+ path, _ := filepath.Split(indexFiles.AsSlice()[0])
+ return path, nil
+}