Skip to content

Commit deeeef1

Browse files
author
Wails Documentation Agent
committed
fix(build): copy frontend assets to embed dir when frontend:dir is non-default
When frontend:dir is set to a directory other than the default "frontend", the built assets end up in the custom directory's dist/ folder but the go:embed directive in main.go still points to frontend/dist. This causes the built binary to have no frontend assets. After BuildFrontend completes, detect if the frontend dir differs from the embed target and copy the built assets to the embed directory so the Go compiler can find them. Fixes #5034
1 parent 81018ce commit deeeef1

2 files changed

Lines changed: 134 additions & 0 deletions

File tree

v2/pkg/commands/build/build.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ func Build(options *Options) (string, error) {
141141
if err != nil {
142142
return "", err
143143
}
144+
145+
if err := syncFrontendAssetsToEmbedDir(options); err != nil {
146+
return "", err
147+
}
144148
}
145149

146150
compileBinary := ""
@@ -192,6 +196,89 @@ func CreateEmbedDirectories(cwd string, buildOptions *Options) error {
192196
return nil
193197
}
194198

199+
func syncFrontendAssetsToEmbedDir(options *Options) error {
200+
frontendDir := options.ProjectData.GetFrontendDir()
201+
defaultEmbedDir := filepath.Join(options.ProjectData.Path, "frontend")
202+
absFrontendDir, err := filepath.Abs(frontendDir)
203+
if err != nil {
204+
return err
205+
}
206+
absDefaultDir, err := filepath.Abs(defaultEmbedDir)
207+
if err != nil {
208+
return err
209+
}
210+
211+
if absFrontendDir == absDefaultDir {
212+
return nil
213+
}
214+
215+
embedDetails, err := staticanalysis.GetEmbedDetails(options.ProjectData.Path)
216+
if err != nil {
217+
return nil
218+
}
219+
220+
for _, ed := range embedDetails {
221+
if filepath.Ext(ed.GetFullPath()) != "" {
222+
continue
223+
}
224+
relPath, err := filepath.Rel(options.ProjectData.Path, ed.GetFullPath())
225+
if err != nil {
226+
continue
227+
}
228+
if !strings.HasPrefix(relPath, "frontend"+string(filepath.Separator)) {
229+
continue
230+
}
231+
frontendSubPath := strings.TrimPrefix(relPath, "frontend"+string(filepath.Separator))
232+
srcDir := filepath.Join(absFrontendDir, frontendSubPath)
233+
234+
if !fs.DirExists(srcDir) {
235+
continue
236+
}
237+
238+
destDir := ed.GetFullPath()
239+
if fs.DirExists(destDir) {
240+
if err := os.RemoveAll(destDir); err != nil {
241+
return fmt.Errorf("failed to clear embed directory %s: %w", destDir, err)
242+
}
243+
}
244+
if err := fs.MkDirs(destDir, 0o755); err != nil {
245+
return fmt.Errorf("failed to create embed directory %s: %w", destDir, err)
246+
}
247+
if err := copyDirContents(srcDir, destDir); err != nil {
248+
return fmt.Errorf("failed to copy frontend assets from %s to %s: %w", srcDir, destDir, err)
249+
}
250+
}
251+
return nil
252+
}
253+
254+
func copyDirContents(src, dst string) error {
255+
entries, err := os.ReadDir(src)
256+
if err != nil {
257+
return err
258+
}
259+
for _, entry := range entries {
260+
srcPath := filepath.Join(src, entry.Name())
261+
dstPath := filepath.Join(dst, entry.Name())
262+
if entry.IsDir() {
263+
if err := fs.MkDirs(dstPath, 0o755); err != nil {
264+
return err
265+
}
266+
if err := copyDirContents(srcPath, dstPath); err != nil {
267+
return err
268+
}
269+
} else {
270+
data, err := os.ReadFile(srcPath)
271+
if err != nil {
272+
return err
273+
}
274+
if err := os.WriteFile(dstPath, data, 0o644); err != nil {
275+
return err
276+
}
277+
}
278+
}
279+
return nil
280+
}
281+
195282
func fatal(message string) {
196283
printer := pterm.PrefixPrinter{
197284
MessageStyle: &pterm.ThemeDefault.FatalMessageStyle,

v2/pkg/commands/build/sync_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package build
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
)
8+
9+
func TestCopyDirContents(t *testing.T) {
10+
srcDir := t.TempDir()
11+
dstDir := t.TempDir()
12+
13+
files := []struct {
14+
path string
15+
content string
16+
}{
17+
{"index.html", "<html></html>"},
18+
{"assets/main.js", "console.log('hi')"},
19+
{"assets/style.css", "body {}"},
20+
{"assets/sub/deep.txt", "deep"},
21+
}
22+
23+
for _, f := range files {
24+
fullPath := filepath.Join(srcDir, f.path)
25+
if err := os.MkdirAll(filepath.Dir(fullPath), 0o755); err != nil {
26+
t.Fatal(err)
27+
}
28+
if err := os.WriteFile(fullPath, []byte(f.content), 0o644); err != nil {
29+
t.Fatal(err)
30+
}
31+
}
32+
33+
if err := copyDirContents(srcDir, dstDir); err != nil {
34+
t.Fatalf("copyDirContents() error = %v", err)
35+
}
36+
37+
for _, f := range files {
38+
data, err := os.ReadFile(filepath.Join(dstDir, f.path))
39+
if err != nil {
40+
t.Errorf("file %s not found in destination: %v", f.path, err)
41+
continue
42+
}
43+
if string(data) != f.content {
44+
t.Errorf("file %s: got %q, want %q", f.path, string(data), f.content)
45+
}
46+
}
47+
}

0 commit comments

Comments
 (0)