mirror of
https://github.com/a-h/templ.git
synced 2025-02-06 10:03:16 +00:00
104 lines
2.3 KiB
Go
104 lines
2.3 KiB
Go
package templ
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// WriteWatchModeString is used when rendering templates in development mode.
|
|
// the generator would have written non-go code to the _templ.txt file, which
|
|
// is then read by this function and written to the output.
|
|
//
|
|
// Deprecated: since templ v0.3.x generated code uses WriteString.
|
|
func WriteWatchModeString(w io.Writer, lineNum int) error {
|
|
_, path, _, _ := runtime.Caller(1)
|
|
if !strings.HasSuffix(path, "_templ.go") {
|
|
return errors.New("templ: WriteWatchModeString can only be called from _templ.go")
|
|
}
|
|
txtFilePath := strings.Replace(path, "_templ.go", "_templ.txt", 1)
|
|
|
|
literals, err := getWatchedStrings(txtFilePath)
|
|
if err != nil {
|
|
return fmt.Errorf("templ: failed to cache strings: %w", err)
|
|
}
|
|
|
|
if lineNum > len(literals) {
|
|
return fmt.Errorf("templ: failed to find line %d in %s", lineNum, txtFilePath)
|
|
}
|
|
|
|
s, err := strconv.Unquote(`"` + literals[lineNum-1] + `"`)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = io.WriteString(w, s)
|
|
return err
|
|
}
|
|
|
|
var (
|
|
watchModeCache = map[string]watchState{}
|
|
watchStateMutex sync.Mutex
|
|
)
|
|
|
|
type watchState struct {
|
|
modTime time.Time
|
|
strings []string
|
|
}
|
|
|
|
func getWatchedStrings(txtFilePath string) ([]string, error) {
|
|
watchStateMutex.Lock()
|
|
defer watchStateMutex.Unlock()
|
|
|
|
state, cached := watchModeCache[txtFilePath]
|
|
if !cached {
|
|
return cacheStrings(txtFilePath)
|
|
}
|
|
|
|
if time.Since(state.modTime) < time.Millisecond*100 {
|
|
return state.strings, nil
|
|
}
|
|
|
|
info, err := os.Stat(txtFilePath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("templ: failed to stat %s: %w", txtFilePath, err)
|
|
}
|
|
|
|
if !info.ModTime().After(state.modTime) {
|
|
return state.strings, nil
|
|
}
|
|
|
|
return cacheStrings(txtFilePath)
|
|
}
|
|
|
|
func cacheStrings(txtFilePath string) ([]string, error) {
|
|
txtFile, err := os.Open(txtFilePath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("templ: failed to open %s: %w", txtFilePath, err)
|
|
}
|
|
defer txtFile.Close()
|
|
|
|
info, err := txtFile.Stat()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("templ: failed to stat %s: %w", txtFilePath, err)
|
|
}
|
|
|
|
all, err := io.ReadAll(txtFile)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("templ: failed to read %s: %w", txtFilePath, err)
|
|
}
|
|
|
|
literals := strings.Split(string(all), "\n")
|
|
watchModeCache[txtFilePath] = watchState{
|
|
modTime: info.ModTime(),
|
|
strings: literals,
|
|
}
|
|
|
|
return literals, nil
|
|
}
|