Skip to content

Commit 1613ed7

Browse files
committed
fix: use temp directory for watch mode files, fixes 1093
1 parent 19362bd commit 1613ed7

File tree

6 files changed

+50
-29
lines changed

6 files changed

+50
-29
lines changed

.version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.3.840
1+
0.3.842

cmd/templ/generatecmd/cmd.go

+7-4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import (
1717
"sync/atomic"
1818
"time"
1919

20+
templruntime "github.com/a-h/templ/runtime"
21+
2022
"github.com/a-h/templ"
2123
"github.com/a-h/templ/cmd/templ/generatecmd/modcheck"
2224
"github.com/a-h/templ/cmd/templ/generatecmd/proxy"
@@ -28,7 +30,7 @@ import (
2830
"github.com/fsnotify/fsnotify"
2931
)
3032

31-
const defaultWatchPattern = `(.+\.go$)|(.+\.templ$)|(.+_templ\.txt$)`
33+
const defaultWatchPattern = `(.+\.go$)|(.+\.templ$)`
3234

3335
func NewGenerate(log *slog.Logger, args Arguments) (g *Generate, err error) {
3436
g = &Generate{
@@ -104,6 +106,7 @@ func (cmd Generate) Run(ctx context.Context) (err error) {
104106
cmd.Log.Warn("templ version check: " + err.Error())
105107
}
106108

109+
cmd.Log.Debug("Creating filesystem event handler")
107110
fseh := NewFSEventHandler(
108111
cmd.Log,
109112
cmd.Args.Path,
@@ -189,7 +192,6 @@ func (cmd Generate) Run(ctx context.Context) (err error) {
189192
"All post-generation events processed, deleting watch mode text files",
190193
slog.Int64("errorCount", errorCount.Load()),
191194
)
192-
193195
fileEvents := make(chan fsnotify.Event)
194196
go func() {
195197
if err := watcher.WalkFiles(ctx, cmd.Args.Path, cmd.WatchPattern, fileEvents); err != nil {
@@ -200,8 +202,9 @@ func (cmd Generate) Run(ctx context.Context) (err error) {
200202
close(fileEvents)
201203
}()
202204
for event := range fileEvents {
203-
if strings.HasSuffix(event.Name, "_templ.txt") {
204-
if err = os.Remove(event.Name); err != nil {
205+
if strings.HasSuffix(event.Name, "_templ.go") || strings.HasSuffix(event.Name, ".templ") {
206+
watchModeFileName := templruntime.GetDevModeTextFileName(event.Name)
207+
if err := os.Remove(watchModeFileName); err != nil && !errors.Is(err, os.ErrNotExist) {
205208
cmd.Log.Warn("Failed to remove watch mode text file", slog.Any("error", err))
206209
}
207210
}

cmd/templ/generatecmd/eventhandler.go

+3-15
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"github.com/a-h/templ/cmd/templ/visualize"
2222
"github.com/a-h/templ/generator"
2323
"github.com/a-h/templ/parser/v2"
24+
"github.com/a-h/templ/runtime"
2425
"github.com/fsnotify/fsnotify"
2526
)
2627

@@ -118,20 +119,6 @@ func (h *FSEventHandler) HandleEvent(ctx context.Context, event fsnotify.Event)
118119
}
119120
return GenerateResult{Updated: true, GoUpdated: true, TextUpdated: false}, nil
120121
}
121-
// Handle _templ.txt files.
122-
if !event.Has(fsnotify.Remove) && strings.HasSuffix(event.Name, "_templ.txt") {
123-
if h.devMode {
124-
// Don't delete the file in dev mode, ignore changes to it, since the .templ file
125-
// must have been updated in order to trigger a change in the _templ.txt file.
126-
return GenerateResult{Updated: false, GoUpdated: false, TextUpdated: false}, nil
127-
}
128-
h.Log.Debug("Deleting watch mode file", slog.String("file", event.Name))
129-
if err = os.Remove(event.Name); err != nil {
130-
h.Log.Warn("Failed to remove watch mode text file", slog.Any("error", err))
131-
return GenerateResult{}, nil
132-
}
133-
return GenerateResult{}, nil
134-
}
135122

136123
// If the file hasn't been updated since the last time we processed it, ignore it.
137124
lastModTime, updatedModTime := h.UpsertLastModTime(event.Name)
@@ -274,7 +261,8 @@ func (h *FSEventHandler) generate(ctx context.Context, fileName string) (result
274261

275262
// Add the txt file if it has changed.
276263
if h.devMode {
277-
txtFileName := strings.TrimSuffix(fileName, ".templ") + "_templ.txt"
264+
txtFileName := runtime.GetDevModeTextFileName(fileName)
265+
h.Log.Debug("Writing development mode text file", slog.String("file", fileName), slog.String("output", txtFileName))
278266
joined := strings.Join(generatorOutput.Literals, "\n")
279267
txtHash := sha256.Sum256([]byte(joined))
280268
if h.UpsertHash(txtFileName, txtHash) {

cmd/templ/generatecmd/main_test.go

+8-6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"time"
1212

1313
"github.com/a-h/templ/cmd/templ/testproject"
14+
"github.com/a-h/templ/runtime"
1415
"golang.org/x/sync/errgroup"
1516
)
1617

@@ -69,13 +70,14 @@ func TestGenerate(t *testing.T) {
6970
})
7071

7172
// Check the templates_templ.go file was created, with backoff.
73+
devModeTextFileName := runtime.GetDevModeTextFileName(path.Join(dir, "templates_templ.go"))
7274
for i := 0; i < 5; i++ {
7375
time.Sleep(time.Second * time.Duration(i))
7476
_, err = os.Stat(path.Join(dir, "templates_templ.go"))
7577
if err != nil {
7678
continue
7779
}
78-
_, err = os.Stat(path.Join(dir, "templates_templ.txt"))
80+
_, err = os.Stat(devModeTextFileName)
7981
if err != nil {
8082
continue
8183
}
@@ -91,7 +93,7 @@ func TestGenerate(t *testing.T) {
9193
}
9294

9395
// Check the templates_templ.txt file was removed.
94-
_, err = os.Stat(path.Join(dir, "templates_templ.txt"))
96+
_, err = os.Stat(path.Join(dir, devModeTextFileName))
9597
if err == nil {
9698
t.Fatalf("templates_templ.txt was not removed")
9799
}
@@ -110,14 +112,14 @@ func TestDefaultWatchPattern(t *testing.T) {
110112
matches: false,
111113
},
112114
{
113-
name: "*_templ.txt matches, Windows",
115+
name: "*_templ.txt is no longer matched, Windows",
114116
input: `C:\Users\adrian\github.com\a-h\templ\cmd\templ\testproject\strings_templ.txt`,
115-
matches: true,
117+
matches: false,
116118
},
117119
{
118-
name: "*_templ.txt matches, Unix",
120+
name: "*_templ.txt is no longer matched, Unix",
119121
input: "/Users/adrian/github.com/a-h/templ/cmd/templ/testproject/strings_templ.txt",
120-
matches: true,
122+
matches: false,
121123
},
122124
{
123125
name: "*.templ files match, Windows",

cmd/templ/main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ func generateCmd(stdout, stderr io.Writer, args []string) (code int) {
208208
includeVersionFlag := cmd.Bool("include-version", true, "")
209209
includeTimestampFlag := cmd.Bool("include-timestamp", false, "")
210210
watchFlag := cmd.Bool("watch", false, "")
211-
watchPatternFlag := cmd.String("watch-pattern", "(.+\\.go$)|(.+\\.templ$)|(.+_templ\\.txt$)", "")
211+
watchPatternFlag := cmd.String("watch-pattern", "(.+\\.go$)|(.+\\.templ$)", "")
212212
openBrowserFlag := cmd.Bool("open-browser", true, "")
213213
cmdFlag := cmd.String("cmd", "", "")
214214
proxyFlag := cmd.String("proxy", "", "")

runtime/watchmode.go

+30-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package runtime
22

33
import (
4+
"crypto/sha256"
5+
"encoding/hex"
46
"errors"
57
"fmt"
68
"io"
79
"os"
10+
"path/filepath"
811
"runtime"
912
"strconv"
1013
"strings"
@@ -14,19 +17,44 @@ import (
1417

1518
var developmentMode = os.Getenv("TEMPL_DEV_MODE") == "true"
1619

20+
func GetDevModeTextFileName(templFileName string) string {
21+
if strings.HasSuffix(templFileName, "_templ.go") {
22+
templFileName = strings.TrimSuffix(templFileName, "_templ.go") + ".templ"
23+
}
24+
absFileName, err := filepath.EvalSymlinks(templFileName)
25+
if err != nil {
26+
absFileName = templFileName
27+
}
28+
hashedFileName := sha256.Sum256([]byte(absFileName))
29+
outputFileName := fmt.Sprintf("templ_%s.txt", hex.EncodeToString(hashedFileName[:]))
30+
31+
root := os.TempDir()
32+
if os.Getenv("TEMPL_DEV_MODE_ROOT") != "" {
33+
root = os.Getenv("TEMPL_DEV_MODE_ROOT")
34+
}
35+
36+
return filepath.Join(root, outputFileName)
37+
}
38+
1739
// WriteString writes the string to the writer. If development mode is enabled
1840
// s is replaced with the string at the index in the _templ.txt file.
1941
func WriteString(w io.Writer, index int, s string) (err error) {
2042
if developmentMode {
43+
2144
_, path, _, _ := runtime.Caller(1)
2245
if !strings.HasSuffix(path, "_templ.go") {
2346
return errors.New("templ: attempt to use WriteString from a non templ file")
2447
}
25-
txtFilePath := strings.Replace(path, "_templ.go", "_templ.txt", 1)
48+
path, err := filepath.EvalSymlinks(path)
49+
if err != nil {
50+
return fmt.Errorf("templ: failed to eval symlinks for %q: %w", path, err)
51+
}
52+
53+
txtFilePath := GetDevModeTextFileName(path)
2654

2755
literals, err := getWatchedStrings(txtFilePath)
2856
if err != nil {
29-
return fmt.Errorf("templ: failed to cache strings: %w", err)
57+
return fmt.Errorf("templ: failed to get watched strings for %q: %w", path, err)
3058
}
3159

3260
if index > len(literals) {

0 commit comments

Comments
 (0)