gromer

#golang#htmx#ssr

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

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


6f1b2b1 — Peter John 2021-09-24T23:23:32+05:30
publish wapp

Generated files not shown: go.sum

Files changed (4) hide show
  1. cmd/main.go +147 -0
  2. go.mod +14 -1
  3. html.go +0 -51
  4. readme.md +3 -61
cmd/main.go ADDED
@@ -0,0 +1,147 @@
1
+ package main
2
+
3
+ import (
4
+ "bytes"
5
+ "fmt"
6
+ "io/ioutil"
7
+ "log"
8
+ "os"
9
+ "path/filepath"
10
+ "strings"
11
+
12
+ "github.com/gobuffalo/velvet"
13
+ "golang.org/x/mod/modfile"
14
+ )
15
+
16
+ type Route struct {
17
+ Method string
18
+ Path string
19
+ }
20
+
21
+ func getMethod(src string) string {
22
+ if strings.HasSuffix(src, "get.go") {
23
+ return "GET"
24
+ } else if strings.HasSuffix(src, "post.go") {
25
+ return "POST"
26
+ } else if strings.HasSuffix(src, "put.go") {
27
+ return "PUT"
28
+ } else if strings.HasSuffix(src, "patch.go") {
29
+ return "PATCH"
30
+ } else if strings.HasSuffix(src, "delete.go") {
31
+ return "DELETE"
32
+ } else if strings.HasSuffix(src, "head.go") {
33
+ return "HEAD"
34
+ } else if strings.HasSuffix(src, "options.go") {
35
+ return "OPTIONS"
36
+ } else if strings.HasSuffix(src, "connect.go") {
37
+ return "CONNECT"
38
+ } else if strings.HasSuffix(src, "trace.go") {
39
+ return "TRACE"
40
+ } else {
41
+ panic(fmt.Sprintf("Uknown route found %s", src))
42
+ }
43
+ }
44
+
45
+ func getRoute(method, src string) string {
46
+ muxRoute := bytes.NewBuffer(nil)
47
+ baseRoute := strings.Replace(src, "/"+strings.ToLower(method)+".go", "", 1)
48
+ foundStart := false
49
+ for _, v := range baseRoute {
50
+ if string(v) == "_" && !foundStart {
51
+ foundStart = true
52
+ muxRoute.WriteString("{")
53
+ } else if string(v) == "_" && foundStart {
54
+ foundStart = false
55
+ muxRoute.WriteString("}")
56
+ } else {
57
+ muxRoute.WriteString(string(v))
58
+ }
59
+ }
60
+ return muxRoute.String()
61
+ }
62
+
63
+ func main() {
64
+ data, err := ioutil.ReadFile("go.mod")
65
+ if err != nil {
66
+ log.Fatalf("go.mod file not found %w", err)
67
+ }
68
+ modTree, err := modfile.Parse("go.mod", data, nil)
69
+ if err != nil {
70
+ log.Fatalf("could not parse go.mod %w", err)
71
+ }
72
+ moduleName := modTree.Module.Mod.Path
73
+ routes := []*Route{}
74
+ err = filepath.Walk("pages",
75
+ func(filesrc string, info os.FileInfo, err error) error {
76
+ if err != nil {
77
+ return err
78
+ }
79
+ if !info.IsDir() {
80
+ route := strings.Replace(filesrc, "pages", "", 1)
81
+ method := getMethod(route)
82
+ path := getRoute(method, route)
83
+ if path == "" { // for index page
84
+ path = "/"
85
+ }
86
+ routes = append(routes, &Route{Method: method, Path: path})
87
+ }
88
+ return nil
89
+ })
90
+ if err != nil {
91
+ log.Fatal(err)
92
+ }
93
+ for _, r := range routes {
94
+ println(r.Method, r.Path)
95
+ }
96
+ ctx := velvet.NewContext()
97
+ ctx.Set("moduleName", moduleName)
98
+ ctx.Set("routes", routes)
99
+ s, err := velvet.Render(`// GENERATED BY WAPP DO NOT EDIT THIS FILE
100
+ package main
101
+
102
+ import (
103
+ "embed"
104
+ "log"
105
+ "net/http"
106
+ "os"
107
+ "time"
108
+
109
+ "github.com/apex/gateway/v2"
110
+ "github.com/gorilla/mux"
111
+ . "{{ moduleName }}/context"
112
+ "{{ moduleName }}/pages"
113
+ "{{ moduleName }}/pages/about"
114
+ "{{ moduleName }}/pages/api/todos/_id_"
115
+ )
116
+
117
+ //go:embed assets/*
118
+ var assetsFS embed.FS
119
+
120
+ func main() {
121
+ isLambda := os.Getenv("_LAMBDA_SERVER_PORT") != ""
122
+ r := mux.NewRouter()
123
+ r.PathPrefix("/assets/").Handler(http.FileServer(http.FS(assetsFS)))
124
+
125
+ {{#each routes as |route| }}r.HandleFunc("{{ route.Path }}", Wrap(pages.GET)).Methods("{{ route.Method }}")
126
+ {{/each}}
127
+ if !isLambda {
128
+ println("http server listening on http://localhost:3000")
129
+ srv := &http.Server{
130
+ Handler: r,
131
+ Addr: "127.0.0.1:3000",
132
+ WriteTimeout: 30 * time.Second,
133
+ ReadTimeout: 30 * time.Second,
134
+ }
135
+ log.Fatal(srv.ListenAndServe())
136
+ } else {
137
+ log.Print("running in lambda mode")
138
+ log.Fatal(gateway.ListenAndServe(":3000", r))
139
+ }
140
+ }
141
+
142
+ `, ctx)
143
+ if err != nil {
144
+ panic(err)
145
+ }
146
+ println(s)
147
+ }
go.mod CHANGED
@@ -3,10 +3,23 @@ module github.com/pyros2097/wapp
3
3
  go 1.16
4
4
 
5
5
  require (
6
+ github.com/PuerkitoBio/goquery v1.7.1 // indirect
6
7
  github.com/apex/gateway/v2 v2.0.0
8
+ github.com/aymerick/raymond v2.0.2+incompatible // indirect
9
+ github.com/gobuffalo/velvet v0.0.0-20170320144106-d97471bf5d8f // indirect
7
10
  github.com/gorilla/mux v1.8.0
8
11
  github.com/kr/pretty v0.1.0 // indirect
12
+ github.com/markbates/inflect v1.0.4 // indirect
13
+ github.com/microcosm-cc/bluemonday v1.0.15 // indirect
14
+ github.com/sergi/go-diff v1.2.0 // indirect
15
+ github.com/shurcooL/github_flavored_markdown v0.0.0-20210228213109-c3a9aa474629 // indirect
16
+ github.com/shurcooL/highlight_diff v0.0.0-20181222201841-111da2e7d480 // indirect
17
+ github.com/shurcooL/highlight_go v0.0.0-20191220051317-782971ddf21b // indirect
18
+ github.com/shurcooL/octicon v0.0.0-20191102190552-cbb32d6a785c // indirect
19
+ github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d // indirect
20
+ github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e // indirect
9
21
  github.com/stretchr/testify v1.6.1
22
+ golang.org/x/mod v0.5.1 // indirect
10
- gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
23
+ golang.org/x/net v0.0.0-20210917221730-978cfadd31cf // indirect
11
24
  gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
12
25
  )
html.go CHANGED
@@ -389,54 +389,3 @@ func MergeAttributes(parent *Element, uis ...interface{}) *Element {
389
389
  }
390
390
  return parent
391
391
  }
392
-
393
- // func UseData(name, data string) *Element {
394
- // return Script(Text(fmt.Sprintf(`
395
- // document.addEventListener('alpine:init', () => {
396
- // Alpine.data('%s', () => {
397
- // return %s;
398
- // });
399
- // });
400
- // `, name, data)))
401
- // }
402
-
403
- // func HTML(html string, data map[string]interface{}) string {
404
- // return fmt.Sprintf("")
405
- // }
406
-
407
- // func Counter2(start int) string {
408
- // data := map[string]interface{}{
409
- // "count": 1,
410
- // "names": []string{"123", "123"},
411
- // "increment": func() string {
412
- // return "this.count += 1;"
413
- // },
414
- // "decrement": func() string {
415
- // return "this.count -= 1;"
416
- // },
417
- // }
418
- // return HTML(`
419
- // <div class="flex flex-col justify-center items-center text-3xl text-gray-700">
420
- // <div class="flex flex-row justify-center items-center">
421
- // <div class="flex flex-row justify-center items-center underline">
422
- // Counter
423
- // </div>
424
- // </div>
425
- // <div class="flex flex-row justify-center items-center" x-data="counter">
426
- // <button @click="decrement" class="btn m-20">-</button>
427
- // <div class="flex flex-row justify-center items-center m-20">{{ count }}</div>
428
- // <button @click="increment" class="btn m-20">+</button>
429
- // </div>
430
- // <div>
431
- // {{#if true }}
432
- // render this
433
- // {{/if}}
434
- // </div>
435
- // <div>
436
- // {{#each names}}
437
- // <li>{{ @index }} - {{ @value }}</li>
438
- // {{/each}}
439
- // </div>
440
- // </div>
441
- // `, data)
442
- // }
readme.md CHANGED
@@ -7,72 +7,14 @@
7
7
  # wapp
8
8
 
9
9
  **wapp** is a framework to build web apps in golang.
10
- It uses a declarative syntax using funcs that allows creating and dealing with HTML elements only by using Go, and without writing any HTML markup.ZZZIt is highly opioninated and integrates uses tailwind css and alpinejs.
10
+ It uses a declarative syntax using funcs that allows creating and dealing with HTML elements only by using Go, and without writing any HTML markup. It is highly opioninated and integrates uses tailwind css and alpinejs.
11
11
 
12
12
  # Install
13
13
 
14
- **wapp** requirements:
15
-
16
- - [Go 1.16](https://golang.org/doc/go1.16)
17
-
18
14
  ```sh
19
15
  go mod init
20
16
  go get -u -v github.com/pyros2097/wapp
21
17
  ```
18
+ # Example
22
19
 
23
- [Demo](https://github.com/pyros2097/wapp-example)
20
+ https://github.com/pyros2097/wapp-example
24
-
25
- **Example**
26
-
27
- ```go
28
- package main
29
-
30
- import (
31
- . "github.com/pyros2097/wapp"
32
- )
33
-
34
- func Header() *Element {
35
- return Row(Css("w-full mb-20 font-bold text-xl text-gray-700 p-4"),
36
- Div(Css("text-blue-700"),
37
- A(Href("https://wapp.pyros2097.dev"), Text("wapp.pyros2097.dev")),
38
- ),
39
- Div(Css("flex flex-row flex-1 justify-end items-end p-2"),
40
- Div(Css("border-b-2 border-white text-lg text-blue-700 mr-4"), Text("Examples: ")),
41
- Div(Css("link mr-4"), A(Href("/"), Text("Home"))),
42
- Div(Css("link mr-4"), A(Href("/clock"), Text("Clock"))),
43
- Div(Css("link mr-4"), A(Href("/about"), Text("About"))),
44
- Div(Css("link mr-4"), A(Href("/container"), Text("Container"))),
45
- Div(Css("link mr-4"), A(Href("/panic"), Text("Panic"))),
46
- ),
47
- )
48
- }
49
-
50
- func Index(w http.ResponseWriter, r *http.Request) *Element {
51
- return Page(
52
- Col(
53
- Header(),
54
- H1(Text("Hello this is a h1")),
55
- H2(Text("Hello this is a h2")),
56
- H2(XData("{ message: 'I ❤️ Alpine' }"), XText("message"), Text("")),
57
- Col(Css("text-3xl text-gray-700"),
58
- Row(
59
- Row(Css("underline"),
60
- Text("Counter"),
61
- ),
62
- ),
63
- Row(
64
- Button(Css("btn m-20"),
65
- Text("-"),
66
- ),
67
- Row(Css("m-20"),
68
- Text(strconv.Itoa(1)),
69
- ),
70
- Button(Css("btn m-20"),
71
- Text("+"),
72
- ),
73
- ),
74
- ),
75
- ),
76
- )
77
- }
78
- ```