diff --git a/cli/htmgo/tasks/astgen/entry.go b/cli/htmgo/tasks/astgen/entry.go index e11ef0b..8472c03 100644 --- a/cli/htmgo/tasks/astgen/entry.go +++ b/cli/htmgo/tasks/astgen/entry.go @@ -2,7 +2,9 @@ package astgen import ( "fmt" + "github.com/maddalax/htmgo/cli/htmgo/internal/dirutil" "github.com/maddalax/htmgo/cli/htmgo/tasks/process" + "github.com/maddalax/htmgo/framework/h" "go/ast" "go/parser" "go/token" @@ -24,6 +26,7 @@ type Partial struct { FuncName string Package string Import string + Path string } const GeneratedDirName = "__htmgo" @@ -103,6 +106,7 @@ func findPublicFuncsReturningHPartial(dir string, predicate func(partial Partial if selectorExpr.Sel.Name == "Partial" { p := Partial{ Package: node.Name.Name, + Path: sliceCommonPrefix(cwd, path), Import: sliceCommonPrefix(cwd, strings.ReplaceAll(filepath.Dir(path), `\`, `/`)), FuncName: funcDecl.Name.Name, } @@ -254,12 +258,18 @@ func buildGetPartialFromContext(builder *CodeBuilder, partials []Partial) { } func writePartialsFile() { + config := dirutil.GetConfig() + cwd := process.GetWorkingDir() partialPath := filepath.Join(cwd, "partials") partials, err := findPublicFuncsReturningHPartial(partialPath, func(partial Partial) bool { return partial.FuncName != "GetPartialFromContext" }) + partials = h.Filter(partials, func(partial Partial) bool { + return !dirutil.IsGlobExclude(partial.Path, config.AutomaticPartialRoutingIgnore) + }) + if err != nil { fmt.Println(err) return @@ -317,6 +327,7 @@ func formatRoute(path string) string { } func writePagesFile() { + config := dirutil.GetConfig() builder := NewCodeBuilder(nil) builder.AppendLine(GeneratedFileLine) @@ -326,6 +337,10 @@ func writePagesFile() { pages, _ := findPublicFuncsReturningHPage("pages") + pages = h.Filter(pages, func(page Page) bool { + return !dirutil.IsGlobExclude(page.Path, config.AutomaticPageRoutingIgnore) + }) + if len(pages) > 0 { builder.AddImport(ModuleName) } diff --git a/framework/config/project.go b/framework/config/project.go index 72e222d..33f6cfc 100644 --- a/framework/config/project.go +++ b/framework/config/project.go @@ -5,12 +5,15 @@ import ( "log/slog" "os" "path" + "strings" ) type ProjectConfig struct { - Tailwind bool `yaml:"tailwind"` - WatchIgnore []string `yaml:"watch_ignore"` - WatchFiles []string `yaml:"watch_files"` + Tailwind bool `yaml:"tailwind"` + WatchIgnore []string `yaml:"watch_ignore"` + WatchFiles []string `yaml:"watch_files"` + AutomaticPageRoutingIgnore []string `yaml:"automatic_page_routing_ignore"` + AutomaticPartialRoutingIgnore []string `yaml:"automatic_partial_routing_ignore"` } func DefaultProjectConfig() *ProjectConfig { @@ -25,7 +28,7 @@ func DefaultProjectConfig() *ProjectConfig { } } -func (cfg *ProjectConfig) EnhanceWithDefaults() *ProjectConfig { +func (cfg *ProjectConfig) Enhance() *ProjectConfig { defaultCfg := DefaultProjectConfig() if len(cfg.WatchFiles) == 0 { cfg.WatchFiles = defaultCfg.WatchFiles @@ -33,6 +36,27 @@ func (cfg *ProjectConfig) EnhanceWithDefaults() *ProjectConfig { if len(cfg.WatchIgnore) == 0 { cfg.WatchIgnore = defaultCfg.WatchIgnore } + + for i, s := range cfg.AutomaticPartialRoutingIgnore { + parts := strings.Split(s, string(os.PathSeparator)) + if len(parts) == 0 { + continue + } + if parts[0] != "partials" { + cfg.AutomaticPartialRoutingIgnore[i] = path.Join("partials", s) + } + } + + for i, s := range cfg.AutomaticPageRoutingIgnore { + parts := strings.Split(s, string(os.PathSeparator)) + if len(parts) == 0 { + continue + } + if parts[0] != "pages" { + cfg.AutomaticPageRoutingIgnore[i] = path.Join("pages", s) + } + } + return cfg } @@ -50,7 +74,7 @@ func FromConfigFile(workingDir string) *ProjectConfig { slog.Error("Error parsing config file", slog.String("file", filePath), slog.String("error", err.Error())) os.Exit(1) } - return cfg.EnhanceWithDefaults() + return cfg.Enhance() } } } diff --git a/framework/config/project_test.go b/framework/config/project_test.go index f015635..bb492a0 100644 --- a/framework/config/project_test.go +++ b/framework/config/project_test.go @@ -41,6 +41,38 @@ func TestShouldNotSetTailwindTrue(t *testing.T) { assert.Equal(t, 8, len(cfg.WatchFiles)) } +func TestShouldPrefixAutomaticPageRoutingIgnore(t *testing.T) { + t.Parallel() + cfg := DefaultProjectConfig() + cfg.AutomaticPageRoutingIgnore = []string{"somefile"} + cfg.Enhance() + assert.Equal(t, []string{"pages/somefile"}, cfg.AutomaticPageRoutingIgnore) +} + +func TestShouldPrefixAutomaticPageRoutingIgnore_1(t *testing.T) { + t.Parallel() + cfg := DefaultProjectConfig() + cfg.AutomaticPageRoutingIgnore = []string{"pages/somefile/*"} + cfg.Enhance() + assert.Equal(t, []string{"pages/somefile/*"}, cfg.AutomaticPageRoutingIgnore) +} + +func TestShouldPrefixAutomaticPartialRoutingIgnore(t *testing.T) { + t.Parallel() + cfg := DefaultProjectConfig() + cfg.AutomaticPartialRoutingIgnore = []string{"somefile/*"} + cfg.Enhance() + assert.Equal(t, []string{"partials/somefile/*"}, cfg.AutomaticPartialRoutingIgnore) +} + +func TestShouldPrefixAutomaticPartialRoutingIgnore_1(t *testing.T) { + t.Parallel() + cfg := DefaultProjectConfig() + cfg.AutomaticPartialRoutingIgnore = []string{"partials/somefile/*"} + cfg.Enhance() + assert.Equal(t, []string{"partials/somefile/*"}, cfg.AutomaticPartialRoutingIgnore) +} + func writeConfigFile(t *testing.T, content string) string { temp := os.TempDir() os.Mkdir(temp, 0755) diff --git a/framework/h/lifecycle.go b/framework/h/lifecycle.go index c9ceb7d..719df74 100644 --- a/framework/h/lifecycle.go +++ b/framework/h/lifecycle.go @@ -30,7 +30,6 @@ func validateCommands(cmds []Command) { panic(fmt.Sprintf("element is not allowed in lifecycle events. Got: %v", t)) default: panic(fmt.Sprintf("type is not allowed in lifecycle events. Got: %v", t)) - } } } diff --git a/templates/starter/htmgo.yml b/templates/starter/htmgo.yml index d60d2ff..7647094 100644 --- a/templates/starter/htmgo.yml +++ b/templates/starter/htmgo.yml @@ -8,3 +8,11 @@ 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"] + +# files or directories to ignore when automatically registering routes for pages +# supports glob patterns through https://github.com/bmatcuk/doublestar +automatic_page_routing_ignore: ["root.go"] + +# files or directories to ignore when automatically registering routes for partials +# supports glob patterns through https://github.com/bmatcuk/doublestar +automatic_partial_routing_ignore: [] diff --git a/templates/starter/pages/index.go b/templates/starter/pages/index.go index ac1c004..08c6f17 100644 --- a/templates/starter/pages/index.go +++ b/templates/starter/pages/index.go @@ -6,15 +6,13 @@ import ( ) func IndexPage(ctx *h.RequestContext) *h.Page { - return h.NewPage( - RootPage( + return RootPage( + h.Div( + h.Class("flex flex-col gap-4 items-center pt-24 min-h-screen bg-neutral-100"), + h.H3(h.Id("intro-text"), h.Text("hello htmgo"), h.Class("text-5xl")), h.Div( - h.Class("flex flex-col gap-4 items-center pt-24 min-h-screen bg-neutral-100"), - h.H3(h.Id("intro-text"), h.Text("hello htmgo"), h.Class("text-5xl")), - h.Div( - h.Class("mt-3"), - partials.CounterForm(0), - ), + h.Class("mt-3"), + partials.CounterForm(0), ), ), ) diff --git a/templates/starter/pages/root.go b/templates/starter/pages/root.go index bacdd61..d930c81 100644 --- a/templates/starter/pages/root.go +++ b/templates/starter/pages/root.go @@ -4,28 +4,30 @@ import ( "github.com/maddalax/htmgo/framework/h" ) -func RootPage(children ...h.Ren) h.Ren { - return h.Html( - h.HxExtensions(h.BaseExtensions()), - h.Head( - h.Meta("viewport", "width=device-width, initial-scale=1"), - h.Link("/public/favicon.ico", "icon"), - h.Link("/public/apple-touch-icon.png", "apple-touch-icon"), - h.Meta("title", "htmgo template"), - h.Meta("charset", "utf-8"), - h.Meta("author", "htmgo"), - h.Meta("description", "this is a template"), - h.Meta("og:title", "htmgo template"), - h.Meta("og:url", "https://htmgo.dev"), - h.Link("canonical", "https://htmgo.dev"), - h.Meta("og:description", "this is a template"), - h.Link("/public/main.css", "stylesheet"), - h.Script("/public/htmgo.js"), - ), - h.Body( - h.Div( - h.Class("flex flex-col gap-2 bg-white h-full"), - h.Fragment(children...), +func RootPage(children ...h.Ren) *h.Page { + return h.NewPage( + h.Html( + h.HxExtensions(h.BaseExtensions()), + h.Head( + h.Meta("viewport", "width=device-width, initial-scale=1"), + h.Link("/public/favicon.ico", "icon"), + h.Link("/public/apple-touch-icon.png", "apple-touch-icon"), + h.Meta("title", "htmgo template"), + h.Meta("charset", "utf-8"), + h.Meta("author", "htmgo"), + h.Meta("description", "this is a template"), + h.Meta("og:title", "htmgo template"), + h.Meta("og:url", "https://htmgo.dev"), + h.Link("canonical", "https://htmgo.dev"), + h.Meta("og:description", "this is a template"), + h.Link("/public/main.css", "stylesheet"), + h.Script("/public/htmgo.js"), + ), + h.Body( + h.Div( + h.Class("flex flex-col gap-2 bg-white h-full"), + h.Fragment(children...), + ), ), ), )