~repos /gromer

#golang#htmx#ssr

git clone https://pyrossh.dev/repos/gromer.git

gromer is a framework and cli to build multipage web apps in golang using htmx and alpinejs.


3b377aa9 Peter John

3 years ago
make it simpler
api_explorer.go CHANGED
@@ -780,5 +780,8 @@ func ApiExplorer(ctx context.Context) (HtmlContent, int, error) {
780
780
  </script>
781
781
  </body>
782
782
  </html>
783
+ `).Props(
784
+ "routes", apiRoutes,
783
- `, M{"routes": apiRoutes, "apiData": template.HTML(string(apiData))})
785
+ "apiData", template.HTML(string(apiData)),
786
+ ).Render()
784
787
  }
cmd/gromer/main.go CHANGED
@@ -141,10 +141,26 @@ func main() {
141
141
  hasRouteMap[v.PkgPath] = true
142
142
  }
143
143
  }
144
+ componentNames := []string{}
145
+ err = filepath.Walk("components",
146
+ func(filesrc string, info os.FileInfo, err error) error {
147
+ if err != nil {
148
+ return err
149
+ }
150
+ if !info.IsDir() {
151
+ filename := strings.ReplaceAll(filepath.Base(filesrc), ".go", "")
152
+ componentNames = append(componentNames, strings.Title((filename)))
153
+ }
154
+ return nil
155
+ })
156
+ if err != nil {
157
+ log.Fatal(err)
158
+ }
144
159
  ctx := velvet.NewContext()
145
160
  ctx.Set("moduleName", moduleName)
146
161
  ctx.Set("routes", gromer.RouteDefs)
147
162
  ctx.Set("routeImports", routeImports)
163
+ ctx.Set("componentNames", componentNames)
148
164
  ctx.Set("tick", "`")
149
165
  s, err := velvet.Render(`// Code generated by gromer. DO NOT EDIT.
150
166
  package main
@@ -158,10 +174,16 @@ import (
158
174
  "gocloud.dev/server"
159
175
 
160
176
  "{{ moduleName }}/assets"
177
+ "{{ moduleName }}/components"
161
178
  {{#each routeImports as |route| }}"{{ moduleName }}/pages{{ route.PkgPath }}"
162
179
  {{/each}}
163
180
  )
164
181
 
182
+ func init() {
183
+ {{#each componentNames as |name| }}gromer.RegisterComponent(components.{{ name }})
184
+ {{/each}}
185
+ }
186
+
165
187
  func main() {
166
188
  port := os.Getenv("PORT")
167
189
  r := mux.NewRouter()
example/components/header.go CHANGED
@@ -4,57 +4,57 @@ import (
4
4
  . "github.com/pyros2097/gromer"
5
5
  )
6
6
 
7
- func Header() string {
7
+ func Header() *HandlersTemplate {
8
- return Component(`
8
+ return Html(`
9
9
  <nav class="navbar" role="navigation" aria-label="main navigation">
10
- <div class="navbar-brand">
10
+ <div class="navbar-brand">
11
- <a class="navbar-item" href="https://bulma.io">
11
+ <a class="navbar-item" href="https://bulma.io">
12
- <img src="https://bulma.io/images/bulma-logo.png" width="112" height="28">
12
+ <img src="https://bulma.io/images/bulma-logo.png" width="112" height="28">
13
- </a>
13
+ </a>
14
-
14
+
15
- <a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
15
+ <a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
16
- <span aria-hidden="true"></span>
16
+ <span aria-hidden="true"></span>
17
- <span aria-hidden="true"></span>
17
+ <span aria-hidden="true"></span>
18
- <span aria-hidden="true"></span>
18
+ <span aria-hidden="true"></span>
19
- </a>
19
+ </a>
20
- </div>
20
+ </div>
21
-
21
+
22
- <div id="navbarBasicExample" class="navbar-menu">
22
+ <div id="navbarBasicExample" class="navbar-menu">
23
- <div class="navbar-start">
23
+ <div class="navbar-start">
24
- <a class="navbar-item" href="/">
24
+ <a class="navbar-item" href="/">
25
- Home
25
+ Home
26
- </a>
26
+ </a>
27
-
27
+
28
- <a class="navbar-item" href="/about">
28
+ <a class="navbar-item" href="/about">
29
- About
29
+ About
30
- </a>
30
+ </a>
31
-
31
+
32
- <a class="navbar-item" href="/clock">
32
+ <a class="navbar-item" href="/clock">
33
- Clock
33
+ Clock
34
- </a>
34
+ </a>
35
-
35
+
36
- <a class="navbar-item" href="/counter">
36
+ <a class="navbar-item" href="/counter">
37
- Counter
37
+ Counter
38
- </a>
38
+ </a>
39
-
39
+
40
- <a class="navbar-item" href="/api">
40
+ <a class="navbar-item" href="/api">
41
- API
41
+ API
42
- </a>
42
+ </a>
43
- </div>
43
+ </div>
44
-
44
+
45
- <div class="navbar-end">
45
+ <div class="navbar-end">
46
- <div class="navbar-item">
46
+ <div class="navbar-item">
47
- <div class="buttons">
47
+ <div class="buttons">
48
- <a class="button is-primary">
48
+ <a class="button is-primary">
49
- <strong>Sign up</strong>
49
+ <strong>Sign up</strong>
50
- </a>
50
+ </a>
51
- <a class="button is-light">
51
+ <a class="button is-light">
52
- Log in
52
+ Log in
53
- </a>
53
+ </a>
54
- </div>
54
+ </div>
55
- </div>
55
+ </div>
56
- </div>
56
+ </div>
57
- </div>
57
+ </div>
58
- </nav>
58
+ </nav>
59
59
  `)
60
60
  }
example/components/init.go DELETED
@@ -1,10 +0,0 @@
1
- package components
2
-
3
- import (
4
- . "github.com/pyros2097/gromer"
5
- )
6
-
7
- func init() {
8
- RegisterComponent(Page)
9
- RegisterComponent(Header)
10
- }
example/components/page.go CHANGED
@@ -1,11 +1,13 @@
1
1
  package components
2
2
 
3
3
  import (
4
+ "html/template"
5
+
4
6
  . "github.com/pyros2097/gromer"
5
7
  )
6
8
 
7
- func Page() string {
9
+ func Page(title string, children template.HTML) *HandlersTemplate {
8
- return Component(`
10
+ return Html(`
9
11
  <!DOCTYPE html>
10
12
  <html lang="en">
11
13
  <head>
@@ -26,5 +28,8 @@ func Page() string {
26
28
  {{ children }}
27
29
  </body>
28
30
  </html>
31
+ `).Props(
32
+ "title", title,
33
+ "children", children,
29
- `)
34
+ )
30
35
  }
example/main.go CHANGED
@@ -10,6 +10,7 @@ import (
10
10
  "gocloud.dev/server"
11
11
 
12
12
  "github.com/pyros2097/gromer/example/assets"
13
+ "github.com/pyros2097/gromer/example/components"
13
14
  "github.com/pyros2097/gromer/example/pages"
14
15
  "github.com/pyros2097/gromer/example/pages/about"
15
16
  "github.com/pyros2097/gromer/example/pages/api/recover"
@@ -18,6 +19,12 @@ import (
18
19
 
19
20
  )
20
21
 
22
+ func init() {
23
+ gromer.RegisterComponent(components.Header)
24
+ gromer.RegisterComponent(components.Page)
25
+
26
+ }
27
+
21
28
  func main() {
22
29
  port := os.Getenv("PORT")
23
30
  r := mux.NewRouter()
example/pages/about/get.go CHANGED
@@ -16,5 +16,5 @@ func GET(c context.Context) (HtmlContent, int, error) {
16
16
  <h1>About Me</h1>
17
17
  </div>
18
18
  {{/Page}}
19
- `, M{})
19
+ `).Render()
20
20
  }
example/pages/get.go CHANGED
@@ -74,5 +74,7 @@ func GET(ctx context.Context, params GetParams) (HtmlContent, int, error) {
74
74
  </nav>
75
75
  </main>
76
76
  {{/Page}}
77
+ `).
77
- `, M{"todos": todos})
78
+ Prop("todos", todos).
79
+ Render()
78
80
  }
http.go CHANGED
@@ -40,23 +40,36 @@ type RouteDefinition struct {
40
40
  }
41
41
 
42
42
  type HtmlContent string
43
+ type HandlersTemplate struct {
44
+ text string
45
+ ctx *velvet.Context
46
+ }
47
+
48
+ func Html(tpl string) *HandlersTemplate {
49
+ return &HandlersTemplate{text: tpl, ctx: velvet.NewContext()}
50
+ }
43
51
 
44
- func Html(tpl string, params map[string]interface{}) (HtmlContent, int, error) {
52
+ func (t *HandlersTemplate) Prop(key string, v any) *HandlersTemplate {
45
- ctx := velvet.NewContext()
46
- for k, v := range params {
47
- ctx.Set(k, v)
53
+ t.ctx.Set(key, v)
54
+ return t
55
+ }
56
+
57
+ func (t *HandlersTemplate) Props(args ...any) *HandlersTemplate {
58
+ for i := 0; i < len(args); i += 2 {
59
+ key := fmt.Sprintf("%s", args[i])
60
+ t.ctx.Set(key, args[i+1])
48
61
  }
62
+ return t
63
+ }
64
+
65
+ func (t *HandlersTemplate) Render(args ...any) (HtmlContent, int, error) {
49
- s, err := velvet.Render(tpl, ctx)
66
+ s, err := velvet.Render(t.text, t.ctx)
50
67
  if err != nil {
51
- return HtmlContent(""), 500, err
68
+ return HtmlContent("Server Erorr"), 500, err
52
69
  }
53
70
  return HtmlContent(s), 200, nil
54
71
  }
55
72
 
56
- func Component(tpl string) string {
57
- return tpl
58
- }
59
-
60
73
  func HtmlErr(status int, err error) (HtmlContent, int, error) {
61
74
  return HtmlContent("ErrorPage/AccessDeniedPage/NotFoundPage based on status code"), status, err
62
75
  }
@@ -66,19 +79,25 @@ func GetFunctionName(temp interface{}) string {
66
79
  return strs[len(strs)-1]
67
80
  }
68
81
 
69
- func RegisterComponent(fn interface{}) {
82
+ func RegisterComponent(fn interface{}, props ...string) {
70
83
  name := GetFunctionName(fn)
84
+ fnType := reflect.TypeOf(fn)
71
- // reflect.New(reflect.FuncOf())
85
+ fnValue := reflect.ValueOf(fn)
72
86
  velvet.Helpers.Add(name, func(title string, c velvet.HelperContext) (template.HTML, error) {
73
87
  s, err := c.Block()
74
88
  if err != nil {
75
89
  return "", err
76
90
  }
77
- ctx := velvet.NewContext()
91
+ args := []reflect.Value{}
92
+ if fnType.NumIn() > 0 {
93
+ args = append(args, reflect.ValueOf(title))
78
- ctx.Set("title", title)
94
+ if s != "" {
79
- ctx.Set("children", template.HTML(s))
95
+ args = append(args, reflect.ValueOf(template.HTML(s)))
96
+ }
97
+ }
98
+ res := fnValue.Call(args)
80
- res := reflect.ValueOf(fn).Call([]reflect.Value{})
99
+ tpl := res[0].Interface().(*HandlersTemplate)
81
- comp, err := velvet.Render(res[0].Interface().(string), ctx)
100
+ comp, err := velvet.Render(tpl.text, tpl.ctx)
82
101
  if err != nil {
83
102
  return "", err
84
103
  }