Skip to content

Commit

Permalink
cli/daemon: support watching embedded files
Browse files Browse the repository at this point in the history
  • Loading branch information
bn4t committed Dec 28, 2024
1 parent 5b7c86d commit e88a572
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 8 deletions.
127 changes: 127 additions & 0 deletions cli/daemon/run/embedded_files.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package run

import (
"encr.dev/pkg/watcher"
"fmt"
"os"
"path/filepath"
"strings"
)

var embeddedFiles = make(map[string][]string)

// ignoreEventEmbedded checks whether the event is related to an embedded file
func ignoreEventEmbedded(event watcher.Event) (bool, error) {
switch event.EventType {
case watcher.CREATED:
return true, handleCreatedFile(event.Path)
case watcher.DELETED:
return true, handleDeletedFile(event.Path)
case watcher.MODIFIED:
return handleModifiedFile(event.Path)
default:
return true, nil
}
}

func handleModifiedFile(path string) (bool, error) {
if strings.HasSuffix(path, ".go") {
return true, updateEmbeddedFiles(path)
}

embedded, err := isFileEmbedded(path)
if err != nil {
return true, err
}

return !embedded, nil
}

func initializeEmbeddedFilesTracker(root string) error {
if len(embeddedFiles) > 0 {
return nil
}

return filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil || info.IsDir() || filepath.Ext(path) != ".go" {
return err
}
return updateEmbeddedFiles(path)
})
}

func handleCreatedFile(path string) error {
if strings.HasSuffix(path, ".go") {
return updateEmbeddedFiles(path)
}
return nil
}

func handleDeletedFile(path string) error {
delete(embeddedFiles, path)
return nil
}

func isFileEmbedded(fpath string) (bool, error) {
for _, files := range embeddedFiles {
for _, file := range files {
if file == fpath {
return true, nil
}
}
}
return false, nil
}

func updateEmbeddedFiles(path string) error {
embeds, err := parseEmbeddedFiles(path)
if err != nil {
return fmt.Errorf("failed to parse embedded files: %w", err)
}
embeddedFiles[path] = embeds
return nil
}

// parseEmbeddedFiles returns all the embedded files for a given source file
func parseEmbeddedFiles(sourceFile string) ([]string, error) {
data, err := os.ReadFile(sourceFile)
if err != nil {
return nil, fmt.Errorf("failed to read source file: %w", err)
}

var embeddedPaths []string
sourceDir := filepath.Dir(sourceFile)
lines := strings.Split(string(data), "\n")

for _, line := range lines {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "//go:embed") {
parts := strings.Fields(line)
if len(parts) > 1 {
dir := parts[1]
filepaths, err := getFilePathsFromDir(filepath.Join(sourceDir, dir))
if err != nil {
return nil, err
}
embeddedPaths = append(embeddedPaths, filepaths...)
}
}
}
return embeddedPaths, nil
}

// getFilePathsFromDir retrieves all file paths from a directory recursively
func getFilePathsFromDir(dir string) ([]string, error) {
var filePaths []string
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil || info.IsDir() {
return err
}
filePaths = append(filePaths, path)
return nil
})
if err != nil {
return nil, fmt.Errorf("failed to walk through directory %s: %w", dir, err)
}
return filePaths, nil
}
28 changes: 20 additions & 8 deletions cli/daemon/run/watch.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ import (
// watch watches the given app for changes, and reports
// them on c.
func (mgr *Manager) watch(run *Run) error {

// Initialize embedded files tracker
if err := initializeEmbeddedFilesTracker(run.App.Root()); err != nil {
return err
}

sub, err := run.App.Watch(func(i *apps.Instance, event []watcher.Event) {
if IgnoreEvents(event) {
return
Expand Down Expand Up @@ -44,23 +50,29 @@ func (mgr *Manager) watch(run *Run) error {
}

// IgnoreEvents will return true if _all_ events are on files that should be ignored
// as the do not impact the running app, or are the result of Encore itself generating code.
// as they do not impact the running app, or are the result of Encore itself generating code.
func IgnoreEvents(events []watcher.Event) bool {
for _, event := range events {
if !ignoreEvent(event) {
filename := filepath.Base(event.Path)
if strings.HasPrefix(strings.ToLower(filename), "encore.gen.") ||
strings.HasSuffix(filename, "~") {
// Ignore generated code and temporary files
return true
}

ignore, err := ignoreEventEmbedded(event)
if err != nil {
return false
}

if !ignoreEvent(event) || !ignore {
return false
}
}
return true
}

func ignoreEvent(ev watcher.Event) bool {
filename := filepath.Base(ev.Path)
if strings.HasPrefix(strings.ToLower(filename), "encore.gen.") {
// Ignore generated code
return true
}

// Ignore files which wouldn't impact the running app
ext := filepath.Ext(ev.Path)
switch ext {
Expand Down

0 comments on commit e88a572

Please sign in to comment.