Merge pull request #21 from maddalax/config-wip

htmgo yml config
This commit is contained in:
maddalax 2024-10-14 10:17:03 -05:00 committed by GitHub
commit 8503dffa4e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 188 additions and 16 deletions

View file

@ -11,3 +11,5 @@ require (
golang.org/x/sys v0.25.0 golang.org/x/sys v0.25.0
golang.org/x/tools v0.25.0 golang.org/x/tools v0.25.0
) )
require github.com/bmatcuk/doublestar/v4 v4.7.1 // indirect

View file

@ -1,3 +1,5 @@
github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q=
github.com/bmatcuk/doublestar/v4 v4.7.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo= github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo=
github.com/dave/jennifer v1.7.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc= github.com/dave/jennifer v1.7.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=

View file

@ -4,6 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/maddalax/htmgo/cli/htmgo/tasks/process" "github.com/maddalax/htmgo/cli/htmgo/tasks/process"
"github.com/maddalax/htmgo/framework/config"
"io" "io"
"log/slog" "log/slog"
"os" "os"
@ -17,6 +18,10 @@ func HasFileFromRoot(file string) bool {
return err == nil return err == nil
} }
func GetConfig() *config.ProjectConfig {
return config.FromConfigFile(process.GetWorkingDir())
}
func CreateHtmgoDir() { func CreateHtmgoDir() {
if !HasFileFromRoot("__htmgo") { if !HasFileFromRoot("__htmgo") {
CreateDirFromRoot("__htmgo") CreateDirFromRoot("__htmgo")

View file

@ -0,0 +1,31 @@
package dirutil
import (
"fmt"
"github.com/bmatcuk/doublestar/v4"
)
func matchesAny(patterns []string, path string) bool {
for _, pattern := range patterns {
matched, err := doublestar.Match(pattern, path)
if err != nil {
fmt.Printf("Error matching pattern: %v\n", err)
return false
}
if matched {
return true
}
}
return false
}
func IsGlobExclude(path string, excludePatterns []string) bool {
return matchesAny(excludePatterns, path)
}
func IsGlobMatch(path string, patterns []string, excludePatterns []string) bool {
if matchesAny(excludePatterns, path) {
return false
}
return matchesAny(patterns, path)
}

View file

@ -92,7 +92,7 @@ func CopyAssets() {
}) })
} }
if !dirutil.HasFileFromRoot("tailwind.config.js") { if dirutil.GetConfig().Tailwind && !dirutil.HasFileFromRoot("tailwind.config.js") {
err = dirutil.CopyFile( err = dirutil.CopyFile(
filepath.Join(assetCssDir, "tailwind.config.js"), filepath.Join(assetCssDir, "tailwind.config.js"),
filepath.Join(process.GetWorkingDir(), "tailwind.config.js"), filepath.Join(process.GetWorkingDir(), "tailwind.config.js"),

View file

@ -12,7 +12,7 @@ import (
) )
func IsTailwindEnabled() bool { func IsTailwindEnabled() bool {
return dirutil.HasFileFromRoot("tailwind.config.js") return dirutil.GetConfig().Tailwind && dirutil.HasFileFromRoot("tailwind.config.js")
} }
func Setup() bool { func Setup() bool {

View file

@ -4,6 +4,7 @@ import (
"github.com/fsnotify/fsnotify" "github.com/fsnotify/fsnotify"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/maddalax/htmgo/cli/htmgo/internal" "github.com/maddalax/htmgo/cli/htmgo/internal"
"github.com/maddalax/htmgo/cli/htmgo/internal/dirutil"
"github.com/maddalax/htmgo/cli/htmgo/tasks/module" "github.com/maddalax/htmgo/cli/htmgo/tasks/module"
"log" "log"
"log/slog" "log/slog"
@ -13,11 +14,10 @@ import (
"time" "time"
) )
var ignoredDirs = []string{".git", ".idea", "node_modules", "vendor"}
func startWatcher(cb func(version string, file []*fsnotify.Event)) { func startWatcher(cb func(version string, file []*fsnotify.Event)) {
events := make([]*fsnotify.Event, 0) events := make([]*fsnotify.Event, 0)
debouncer := internal.NewDebouncer(500 * time.Millisecond) debouncer := internal.NewDebouncer(500 * time.Millisecond)
config := dirutil.GetConfig()
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -35,23 +35,22 @@ func startWatcher(cb func(version string, file []*fsnotify.Event)) {
for { for {
select { select {
case event, ok := <-watcher.Events: case event, ok := <-watcher.Events:
slog.Debug("event:", slog.String("name", event.Name), slog.String("op", event.Op.String()))
if !ok { if !ok {
return return
} }
if event.Has(fsnotify.Remove) { if event.Has(fsnotify.Remove) {
info, err := os.Stat(event.Name) if dirutil.IsGlobMatch(event.Name, config.WatchFiles, config.WatchIgnore) {
if err != nil { watcher.Remove(event.Name)
continue continue
} }
if info.IsDir() {
_ = watcher.Remove(event.Name)
}
} }
if event.Has(fsnotify.Create) { if event.Has(fsnotify.Create) {
if dirutil.IsGlobMatch(event.Name, config.WatchFiles, config.WatchIgnore) {
watcher.Add(event.Name)
continue
}
info, err := os.Stat(event.Name) info, err := os.Stat(event.Name)
if err != nil { if err != nil {
slog.Error("Error getting file info:", slog.String("path", event.Name), slog.String("error", err.Error())) slog.Error("Error getting file info:", slog.String("path", event.Name), slog.String("error", err.Error()))
@ -68,6 +67,9 @@ func startWatcher(cb func(version string, file []*fsnotify.Event)) {
} }
if event.Has(fsnotify.Write) || event.Has(fsnotify.Remove) || event.Has(fsnotify.Rename) { if event.Has(fsnotify.Write) || event.Has(fsnotify.Remove) || event.Has(fsnotify.Rename) {
if !dirutil.IsGlobMatch(event.Name, config.WatchFiles, config.WatchIgnore) {
continue
}
events = append(events, &event) events = append(events, &event)
debouncer.Do(func() { debouncer.Do(func() {
seen := make(map[string]bool) seen := make(map[string]bool)
@ -82,6 +84,7 @@ func startWatcher(cb func(version string, file []*fsnotify.Event)) {
events = make([]*fsnotify.Event, 0) events = make([]*fsnotify.Event, 0)
}) })
} }
case err, ok := <-watcher.Errors: case err, ok := <-watcher.Errors:
if !ok { if !ok {
return return
@ -107,11 +110,10 @@ func startWatcher(cb func(version string, file []*fsnotify.Event)) {
return err return err
} }
// Ignore directories in the ignoredDirs list // Ignore directories in the ignoredDirs list
for _, ignoredDir := range ignoredDirs { if dirutil.IsGlobExclude(path, config.WatchIgnore) {
if ignoredDir == info.Name() { return filepath.SkipDir
return filepath.SkipDir
}
} }
// Only watch directories // Only watch directories
if info.IsDir() { if info.IsDir() {
err = watcher.Add(path) err = watcher.Add(path)
@ -123,6 +125,7 @@ func startWatcher(cb func(version string, file []*fsnotify.Event)) {
} }
return nil return nil
}) })
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

View file

@ -0,0 +1,58 @@
package config
import (
"gopkg.in/yaml.v3"
"log/slog"
"os"
"path"
)
type ProjectConfig struct {
Tailwind bool `yaml:"tailwind"`
WatchIgnore []string `yaml:"watch_ignore"`
WatchFiles []string `yaml:"watch_files"`
}
func DefaultProjectConfig() *ProjectConfig {
return &ProjectConfig{
Tailwind: true,
WatchIgnore: []string{
"node_modules", ".git", ".idea", "assets/dist",
},
WatchFiles: []string{
"**/*.go", "**/*.html", "**/*.css", "**/*.js", "**/*.json", "**/*.yaml", "**/*.yml", "**/*.md",
},
}
}
func (cfg *ProjectConfig) EnhanceWithDefaults() *ProjectConfig {
defaultCfg := DefaultProjectConfig()
if len(cfg.WatchFiles) == 0 {
cfg.WatchFiles = defaultCfg.WatchFiles
}
if len(cfg.WatchIgnore) == 0 {
cfg.WatchIgnore = defaultCfg.WatchIgnore
}
return cfg
}
func FromConfigFile(workingDir string) *ProjectConfig {
defaultCfg := DefaultProjectConfig()
names := []string{"htmgo.yaml", "htmgo.yml", "_htmgo.yaml", "_htmgo.yml"}
for _, name := range names {
filePath := path.Join(workingDir, name)
if _, err := os.Stat(filePath); err == nil {
cfg := &ProjectConfig{}
bytes, err := os.ReadFile(filePath)
if err == nil {
err = yaml.Unmarshal(bytes, cfg)
if err != nil {
slog.Error("Error parsing config file", slog.String("file", filePath), slog.String("error", err.Error()))
os.Exit(1)
}
return cfg.EnhanceWithDefaults()
}
}
}
return defaultCfg
}

View file

@ -0,0 +1,50 @@
package config
import (
"github.com/stretchr/testify/assert"
"os"
"path"
"testing"
)
func TestDefaultProjectConfig(t *testing.T) {
t.Parallel()
cfg := DefaultProjectConfig()
assert.Equal(t, true, cfg.Tailwind)
assert.Equal(t, 4, len(cfg.WatchIgnore))
assert.Equal(t, 8, len(cfg.WatchFiles))
}
func TestNoConfigFileUsesDefault(t *testing.T) {
t.Parallel()
cfg := FromConfigFile("non-existing-dir")
assert.Equal(t, true, cfg.Tailwind)
assert.Equal(t, 4, len(cfg.WatchIgnore))
assert.Equal(t, 8, len(cfg.WatchFiles))
}
func TestPartialConfigMerges(t *testing.T) {
t.Parallel()
dir := writeConfigFile(t, "tailwind: false")
cfg := FromConfigFile(dir)
assert.Equal(t, false, cfg.Tailwind)
assert.Equal(t, 4, len(cfg.WatchIgnore))
assert.Equal(t, 8, len(cfg.WatchFiles))
}
func TestShouldNotSetTailwindTrue(t *testing.T) {
t.Parallel()
dir := writeConfigFile(t, "someValue: true")
cfg := FromConfigFile(dir)
assert.Equal(t, false, cfg.Tailwind)
assert.Equal(t, 4, len(cfg.WatchIgnore))
assert.Equal(t, 8, len(cfg.WatchFiles))
}
func writeConfigFile(t *testing.T, content string) string {
temp := os.TempDir()
os.Mkdir(temp, 0755)
err := os.WriteFile(path.Join(temp, "htmgo.yml"), []byte(content), 0644)
assert.Nil(t, err)
return temp
}

10
htmgo-site/htmgo.yml Normal file
View file

@ -0,0 +1,10 @@
# htmgo configuration
# if tailwindcss is enabled, htmgo will automatically compile your tailwind and output it to assets/dist
tailwind: true
# which directories to ignore when watching for changes, supports glob patterns through https://github.com/bmatcuk/doublestar
watch_ignore: [".git", "node_modules", "dist/*"]
# files to watch for changes, supports glob patterns through https://github.com/bmatcuk/doublestar
watch_files: ["**/*.go", "**/*.css", "**/*.md"]

View file

@ -45,7 +45,8 @@ func PageWithNav(ctx *h.RequestContext, children ...h.Ren) *h.Element {
return RootPage(ctx, return RootPage(ctx,
h.Fragment( h.Fragment(
partials.NavBar(ctx, partials.NavBarProps{ partials.NavBar(ctx, partials.NavBarProps{
Expanded: false, Expanded: false,
ShowPreRelease: true,
}), }),
h.Div( h.Div(
children..., children...,

View file

@ -0,0 +1,10 @@
# htmgo configuration
# if tailwindcss is enabled, htmgo will automatically compile your tailwind and output it to assets/dist
tailwind: true
# which directories to ignore when watching for changes, supports glob patterns through https://github.com/bmatcuk/doublestar
watch_ignore: [".git", "node_modules", "dist/*"]
# files to watch for changes, supports glob patterns through https://github.com/bmatcuk/doublestar
watch_files: ["**/*.go", "**/*.css", "**/*.md"]