Skip to content

Commit cfab064

Browse files
committed
cli/daemon: support watching embedded files
1 parent 5b7c86d commit cfab064

File tree

2 files changed

+146
-8
lines changed

2 files changed

+146
-8
lines changed

cli/daemon/run/embedded_files.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package run
2+
3+
import (
4+
"encr.dev/pkg/watcher"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
"strings"
9+
)
10+
11+
var embeddedFiles = make(map[string][]string)
12+
13+
func ignoreEventEmbedded(event watcher.Event) (bool, error) {
14+
switch event.EventType {
15+
case watcher.CREATED:
16+
return true, handleCreatedFile(event.Path)
17+
case watcher.DELETED:
18+
return true, handleDeletedFile(event.Path)
19+
case watcher.MODIFIED:
20+
return handleModifiedFile(event.Path)
21+
default:
22+
return true, nil
23+
}
24+
}
25+
26+
func handleModifiedFile(path string) (bool, error) {
27+
if strings.HasSuffix(path, ".go") {
28+
return true, updateEmbeddedFiles(path)
29+
}
30+
31+
embedded, err := isFileEmbedded(path)
32+
if err != nil {
33+
return true, err
34+
}
35+
36+
return !embedded, nil
37+
}
38+
39+
func initializeEmbeddedFilesTracker(root string) error {
40+
if len(embeddedFiles) > 0 {
41+
return nil
42+
}
43+
44+
return filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
45+
if err != nil || info.IsDir() || filepath.Ext(path) != ".go" {
46+
return err
47+
}
48+
return updateEmbeddedFiles(path)
49+
})
50+
}
51+
52+
func handleCreatedFile(path string) error {
53+
if strings.HasSuffix(path, ".go") {
54+
return updateEmbeddedFiles(path)
55+
}
56+
return nil
57+
}
58+
59+
func handleDeletedFile(path string) error {
60+
delete(embeddedFiles, path)
61+
return nil
62+
}
63+
64+
func isFileEmbedded(fpath string) (bool, error) {
65+
for _, files := range embeddedFiles {
66+
for _, file := range files {
67+
if file == fpath {
68+
return true, nil
69+
}
70+
}
71+
}
72+
return false, nil
73+
}
74+
75+
func updateEmbeddedFiles(path string) error {
76+
embeds, err := parseEmbeddedFiles(path)
77+
if err != nil {
78+
return fmt.Errorf("failed to parse embedded files: %w", err)
79+
}
80+
embeddedFiles[path] = embeds
81+
return nil
82+
}
83+
84+
// parseEmbeddedFiles returns all the embedded files for a given source file
85+
func parseEmbeddedFiles(sourceFile string) ([]string, error) {
86+
data, err := os.ReadFile(sourceFile)
87+
if err != nil {
88+
return nil, fmt.Errorf("failed to read source file: %w", err)
89+
}
90+
91+
var embeddedPaths []string
92+
sourceDir := filepath.Dir(sourceFile)
93+
lines := strings.Split(string(data), "\n")
94+
95+
for _, line := range lines {
96+
line = strings.TrimSpace(line)
97+
if strings.HasPrefix(line, "//go:embed") {
98+
parts := strings.Fields(line)
99+
if len(parts) > 1 {
100+
dir := parts[1]
101+
filepaths, err := getFilePathsFromDir(filepath.Join(sourceDir, dir))
102+
if err != nil {
103+
return nil, err
104+
}
105+
embeddedPaths = append(embeddedPaths, filepaths...)
106+
}
107+
}
108+
}
109+
return embeddedPaths, nil
110+
}
111+
112+
// getFilePathsFromDir retrieves all file paths from a directory recursively
113+
func getFilePathsFromDir(dir string) ([]string, error) {
114+
var filePaths []string
115+
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
116+
if err != nil || info.IsDir() {
117+
return err
118+
}
119+
filePaths = append(filePaths, path)
120+
return nil
121+
})
122+
if err != nil {
123+
return nil, fmt.Errorf("failed to walk through directory %s: %w", dir, err)
124+
}
125+
return filePaths, nil
126+
}

cli/daemon/run/watch.go

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ import (
1111
// watch watches the given app for changes, and reports
1212
// them on c.
1313
func (mgr *Manager) watch(run *Run) error {
14+
15+
// Initialize embedded files tracker
16+
if err := initializeEmbeddedFilesTracker(run.App.Root()); err != nil {
17+
return err
18+
}
19+
1420
sub, err := run.App.Watch(func(i *apps.Instance, event []watcher.Event) {
1521
if IgnoreEvents(event) {
1622
return
@@ -44,23 +50,29 @@ func (mgr *Manager) watch(run *Run) error {
4450
}
4551

4652
// IgnoreEvents will return true if _all_ events are on files that should be ignored
47-
// as the do not impact the running app, or are the result of Encore itself generating code.
53+
// as they do not impact the running app, or are the result of Encore itself generating code.
4854
func IgnoreEvents(events []watcher.Event) bool {
4955
for _, event := range events {
50-
if !ignoreEvent(event) {
56+
filename := filepath.Base(event.Path)
57+
if strings.HasPrefix(strings.ToLower(filename), "encore.gen.") ||
58+
strings.HasSuffix(filename, "~") {
59+
// Ignore generated code and temporary files
60+
return true
61+
}
62+
63+
ignore, err := ignoreEventEmbedded(event)
64+
if err != nil {
65+
return false
66+
}
67+
68+
if !ignoreEvent(event) || !ignore {
5169
return false
5270
}
5371
}
5472
return true
5573
}
5674

5775
func ignoreEvent(ev watcher.Event) bool {
58-
filename := filepath.Base(ev.Path)
59-
if strings.HasPrefix(strings.ToLower(filename), "encore.gen.") {
60-
// Ignore generated code
61-
return true
62-
}
63-
6476
// Ignore files which wouldn't impact the running app
6577
ext := filepath.Ext(ev.Path)
6678
switch ext {

0 commit comments

Comments
 (0)