diff --git a/cmd/templ/generatecmd/cmd.go b/cmd/templ/generatecmd/cmd.go index 3b8f97ae..246e51cd 100644 --- a/cmd/templ/generatecmd/cmd.go +++ b/cmd/templ/generatecmd/cmd.go @@ -12,6 +12,7 @@ import ( "path/filepath" "regexp" "runtime" + "strings" "sync" "sync/atomic" "time" @@ -185,9 +186,26 @@ func (cmd Generate) Run(ctx context.Context) (err error) { ) postGenerationEventsWG.Wait() cmd.Log.Debug( - "All post-generation events processed", + "All post-generation events processed, deleting watch mode text files", slog.Int64("errorCount", errorCount.Load()), ) + + fileEvents := make(chan fsnotify.Event) + go func() { + if err := watcher.WalkFiles(ctx, cmd.Args.Path, cmd.WatchPattern, fileEvents); err != nil { + cmd.Log.Error("Post dev mode WalkFiles failed", slog.Any("error", err)) + errs <- FatalError{Err: fmt.Errorf("failed to walk files: %w", err)} + return + } + close(fileEvents) + }() + for event := range fileEvents { + if strings.HasSuffix(event.Name, "_templ.txt") { + if err = os.Remove(event.Name); err != nil { + cmd.Log.Warn("Failed to remove watch mode text file", slog.Any("error", err)) + } + } + } }() // Start process to handle events. diff --git a/cmd/templ/generatecmd/main_test.go b/cmd/templ/generatecmd/main_test.go index 62c5c209..b13c8ebf 100644 --- a/cmd/templ/generatecmd/main_test.go +++ b/cmd/templ/generatecmd/main_test.go @@ -8,8 +8,10 @@ import ( "path" "regexp" "testing" + "time" "github.com/a-h/templ/cmd/templ/testproject" + "golang.org/x/sync/errgroup" ) func TestGenerate(t *testing.T) { @@ -42,6 +44,58 @@ func TestGenerate(t *testing.T) { t.Fatalf("templates_templ.go was not created: %v", err) } }) + t.Run("can generate a file in watch mode", func(t *testing.T) { + // templ generate -f templates.templ + dir, err := testproject.Create("github.com/a-h/templ/cmd/templ/testproject") + if err != nil { + t.Fatalf("failed to create test project: %v", err) + } + defer os.RemoveAll(dir) + + // Delete the templates_templ.go file to ensure it is generated. + err = os.Remove(path.Join(dir, "templates_templ.go")) + if err != nil { + t.Fatalf("failed to remove templates_templ.go: %v", err) + } + ctx, cancel := context.WithCancel(context.Background()) + + var eg errgroup.Group + eg.Go(func() error { + // Run the generate command. + return Run(ctx, log, Arguments{ + Path: dir, + Watch: true, + }) + }) + + // Check the templates_templ.go file was created, with backoff. + for i := 0; i < 5; i++ { + time.Sleep(time.Second * time.Duration(i)) + _, err = os.Stat(path.Join(dir, "templates_templ.go")) + if err != nil { + continue + } + _, err = os.Stat(path.Join(dir, "templates_templ.txt")) + if err != nil { + continue + } + break + } + if err != nil { + t.Fatalf("template files were not created: %v", err) + } + + cancel() + if err := eg.Wait(); err != nil { + t.Fatalf("generate command failed: %v", err) + } + + // Check the templates_templ.txt file was removed. + _, err = os.Stat(path.Join(dir, "templates_templ.txt")) + if err == nil { + t.Fatalf("templates_templ.txt was not removed") + } + }) } func TestDefaultWatchPattern(t *testing.T) {