~repos /gromer
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.
make new helmet api
- app_common.go +15 -0
- app_nowasm.go +6 -6
- app_wasm.go +2 -9
- attributes.go +15 -0
- attributes_test.go +2 -2
- example/about.go +7 -1
- example/example +0 -0
- example/main.go +4 -10
- makefile +0 -13
- tree.go → router.go +3 -3
- utils.go +0 -7
- utils_test.go +0 -22
app_common.go
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
package app
|
|
2
|
+
|
|
3
|
+
var (
|
|
4
|
+
body *elem
|
|
5
|
+
content UI
|
|
6
|
+
renderFunc RenderFunc
|
|
7
|
+
helmet = &Helmet{}
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
type Helmet struct {
|
|
11
|
+
Title string
|
|
12
|
+
Description string
|
|
13
|
+
Keywords string
|
|
14
|
+
Author string
|
|
15
|
+
}
|
app_nowasm.go
CHANGED
|
@@ -66,12 +66,12 @@ func Reload() {
|
|
|
66
66
|
panic("wasm required")
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
func Route(path string, render RenderFunc
|
|
69
|
+
func Route(path string, render RenderFunc) {
|
|
70
70
|
println("registering route: " + path)
|
|
71
71
|
router.GET(path, render)
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
func createPage(
|
|
74
|
+
func createPage(ui UI) *bytes.Buffer {
|
|
75
75
|
isLambda := os.Getenv("_LAMBDA_SERVER_PORT") != ""
|
|
76
76
|
page := bytes.NewBuffer(nil)
|
|
77
77
|
page.WriteString("<!DOCTYPE html>\n")
|
|
@@ -82,10 +82,10 @@ func createPage(info RouteInfo, ui UI) *bytes.Buffer {
|
|
|
82
82
|
}
|
|
83
83
|
Html(
|
|
84
84
|
Head(
|
|
85
|
-
Title(
|
|
85
|
+
Title(helmet.Title),
|
|
86
|
-
Meta("description",
|
|
86
|
+
Meta("description", helmet.Description),
|
|
87
|
-
Meta("author",
|
|
87
|
+
Meta("author", helmet.Author),
|
|
88
|
-
Meta("keywords",
|
|
88
|
+
Meta("keywords", helmet.Keywords),
|
|
89
89
|
Meta("viewport", "width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0, viewport-fit=cover"),
|
|
90
90
|
Link("icon", basePath+"icon.png"),
|
|
91
91
|
Link("apple-touch-icon", basePath+"icon.png"),
|
app_wasm.go
CHANGED
|
@@ -7,13 +7,6 @@ import (
|
|
|
7
7
|
"github.com/pyros2097/wapp/js"
|
|
8
8
|
)
|
|
9
9
|
|
|
10
|
-
var (
|
|
11
|
-
body *elem
|
|
12
|
-
content UI
|
|
13
|
-
rootPrefix string
|
|
14
|
-
renderFunc RenderFunc
|
|
15
|
-
)
|
|
16
|
-
|
|
17
10
|
func Run() {
|
|
18
11
|
handle, _, _ := router.Lookup("GET", js.Window.URL().Path)
|
|
19
12
|
if handle == nil {
|
|
@@ -52,7 +45,7 @@ func Reload() {
|
|
|
52
45
|
})
|
|
53
46
|
}
|
|
54
47
|
|
|
55
|
-
func Route(path string, render RenderFunc
|
|
48
|
+
func Route(path string, render RenderFunc) {
|
|
56
49
|
router.GET(path, render)
|
|
57
50
|
}
|
|
58
51
|
|
|
@@ -88,6 +81,6 @@ func initContent() {
|
|
|
88
81
|
// return u.Fragment != ""
|
|
89
82
|
// }
|
|
90
83
|
|
|
91
|
-
func createPage(
|
|
84
|
+
func createPage(ui UI) *bytes.Buffer {
|
|
92
85
|
return &bytes.Buffer{}
|
|
93
86
|
}
|
attributes.go
CHANGED
|
@@ -36,6 +36,11 @@ func OnInput(cb js.EventHandlerFunc) OnInputAttribute {
|
|
|
36
36
|
return OnInputAttribute{cb: cb}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
type HelmetTitle string
|
|
40
|
+
type HelmetDescription string
|
|
41
|
+
type HelmetAuthor string
|
|
42
|
+
type HelmetKeywords string
|
|
43
|
+
|
|
39
44
|
func mergeAttributes(parent *elem, uis ...interface{}) {
|
|
40
45
|
elems := []interface{}{}
|
|
41
46
|
for _, v := range uis {
|
|
@@ -54,8 +59,18 @@ func mergeAttributes(parent *elem, uis ...interface{}) {
|
|
|
54
59
|
parent.setEventHandler("change", c.cb)
|
|
55
60
|
case OnInputAttribute:
|
|
56
61
|
parent.setEventHandler("input", c.cb)
|
|
62
|
+
case HelmetTitle:
|
|
63
|
+
helmet.Title = string(c)
|
|
64
|
+
case HelmetDescription:
|
|
65
|
+
helmet.Description = string(c)
|
|
66
|
+
case HelmetAuthor:
|
|
67
|
+
helmet.Author = string(c)
|
|
68
|
+
case HelmetKeywords:
|
|
69
|
+
helmet.Keywords = string(c)
|
|
57
70
|
case UI:
|
|
58
71
|
elems = append(elems, c)
|
|
72
|
+
default:
|
|
73
|
+
panic("unknown type in render")
|
|
59
74
|
}
|
|
60
75
|
}
|
|
61
76
|
parent.setBody(elems...)
|
attributes_test.go
CHANGED
|
@@ -28,7 +28,7 @@ func Counter(c *RenderContext) UI {
|
|
|
28
28
|
)
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
func
|
|
31
|
+
func HomeRoute(c *RenderContext) UI {
|
|
32
32
|
return Div(
|
|
33
33
|
Div(),
|
|
34
34
|
Counter(c),
|
|
@@ -42,7 +42,7 @@ func TestCreatePage(t *testing.T) {
|
|
|
42
42
|
Head(
|
|
43
43
|
Title("Title"),
|
|
44
44
|
),
|
|
45
|
-
Body(
|
|
45
|
+
Body(HomeRoute(NewRenderContext())),
|
|
46
46
|
).Html(page)
|
|
47
47
|
assert.Equal(t, "<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"UTF-8\">\n <meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\">\n <meta http-equiv=\"encoding\" content=\"utf-8\">\n <title>\n Title\n </title>\n </head>\n <body>\n <div>\n <div></div>\n <div class=\"flex flex-col justify-center align-items-center\">\n <div class=\"flex flex-row justify-center align-items-center yellow\">\n Counter\n </div>\n <div class=\"flex flex-row justify-center align-items-center\">\n <div>\n -\n </div>\n <div>\n 0\n </div>\n <div>\n +\n </div>\n </div>\n </div>\n </div>\n </body>\n</html>", page.String())
|
|
48
48
|
}
|
example/about.go
CHANGED
|
@@ -5,5 +5,11 @@ import (
|
|
|
5
5
|
)
|
|
6
6
|
|
|
7
7
|
func About(c *RenderContext) UI {
|
|
8
|
+
return Div(
|
|
9
|
+
HelmetTitle("wapp-example"),
|
|
10
|
+
HelmetDescription("wapp is a framework"),
|
|
11
|
+
HelmetAuthor("pyros2097"),
|
|
12
|
+
HelmetKeywords("wapp,wapp-example,golang,framework,frontend,ui,wasm,isomorphic"),
|
|
8
|
-
|
|
13
|
+
Text("About Me"),
|
|
14
|
+
)
|
|
9
15
|
}
|
example/example
DELETED
|
Binary file
|
example/main.go
CHANGED
|
@@ -5,15 +5,9 @@ import (
|
|
|
5
5
|
)
|
|
6
6
|
|
|
7
7
|
func main() {
|
|
8
|
-
info := RouteInfo{
|
|
9
|
-
Title: "wapp-example",
|
|
10
|
-
Description: "wapp is a framework",
|
|
11
|
-
Author: "pyros2097",
|
|
12
|
-
Keywords: "wapp,wapp-example,golang,framework,frontend,ui,wasm,isomorphic",
|
|
13
|
-
}
|
|
14
|
-
Route("/about", About
|
|
8
|
+
Route("/about", About)
|
|
15
|
-
Route("/clock", Clock
|
|
9
|
+
Route("/clock", Clock)
|
|
16
|
-
Route("/container", Container
|
|
10
|
+
Route("/container", Container)
|
|
17
|
-
Route("/", Index
|
|
11
|
+
Route("/", Index)
|
|
18
12
|
Run()
|
|
19
13
|
}
|
makefile
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
bootstrap:
|
|
2
|
-
@echo "\033[94m• Setting up go test for wasm to run in the browser\033[00m"
|
|
3
|
-
go get -u github.com/agnivade/wasmbrowsertest
|
|
4
|
-
mv ${GOPATH}/bin/wasmbrowsertest ${GOPATH}/bin/go_js_wasm_exec
|
|
5
|
-
|
|
6
|
-
.PHONY: test
|
|
7
|
-
test:
|
|
8
|
-
@echo "\033[94m• Running Go vet\033[00m"
|
|
9
|
-
go vet ./...
|
|
10
|
-
@echo "\033[94m\n• Running Go tests\033[00m"
|
|
11
|
-
go test -race ./...
|
|
12
|
-
@echo "\033[94m\n• Running go wasm tests\033[00m"
|
|
13
|
-
GOARCH=wasm GOOS=js go test ./pkg/app
|
tree.go → router.go
RENAMED
|
@@ -1185,7 +1185,7 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|
|
1185
1185
|
if handle, _, tsr := root.getValue(path, r.getParams); handle != nil {
|
|
1186
1186
|
println("route: " + req.URL.Path)
|
|
1187
1187
|
if render, ok := handle.(RenderFunc); ok {
|
|
1188
|
-
page := createPage(
|
|
1188
|
+
page := createPage(render(NewRenderContext()))
|
|
1189
1189
|
w.Header().Set("Content-Length", strconv.Itoa(page.Len()))
|
|
1190
1190
|
w.Header().Set("Content-Type", "text/html")
|
|
1191
1191
|
w.WriteHeader(http.StatusOK)
|
|
@@ -1255,13 +1255,13 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|
|
1255
1255
|
|
|
1256
1256
|
// Handle 404
|
|
1257
1257
|
if r.NotFound != nil {
|
|
1258
|
-
page := createPage(
|
|
1258
|
+
page := createPage(r.NotFound(NewRenderContext()))
|
|
1259
1259
|
w.Header().Set("Content-Length", strconv.Itoa(page.Len()))
|
|
1260
1260
|
w.Header().Set("Content-Type", "text/html")
|
|
1261
1261
|
w.WriteHeader(http.StatusOK)
|
|
1262
1262
|
w.Write(page.Bytes())
|
|
1263
1263
|
} else {
|
|
1264
|
-
page := createPage(
|
|
1264
|
+
page := createPage(DefaultNotFound(NewRenderContext()))
|
|
1265
1265
|
w.Header().Set("Content-Length", strconv.Itoa(page.Len()))
|
|
1266
1266
|
w.Header().Set("Content-Type", "text/html")
|
|
1267
1267
|
w.WriteHeader(http.StatusOK)
|
utils.go
CHANGED
|
@@ -48,10 +48,3 @@ func btos(b []byte) string {
|
|
|
48
48
|
func stob(s string) []byte {
|
|
49
49
|
return *(*[]byte)(unsafe.Pointer(&s))
|
|
50
50
|
}
|
|
51
|
-
|
|
52
|
-
type RouteInfo struct {
|
|
53
|
-
Title string
|
|
54
|
-
Description string
|
|
55
|
-
Author string
|
|
56
|
-
Keywords string
|
|
57
|
-
}
|
utils_test.go
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
package app
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"testing"
|
|
5
|
-
|
|
6
|
-
"github.com/stretchr/testify/assert"
|
|
7
|
-
)
|
|
8
|
-
|
|
9
|
-
func stubRoute(c *RenderContext) UI {
|
|
10
|
-
return Div(Text("Stub"))
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
var testRoutes = map[string]RenderFunc{
|
|
14
|
-
"/about": stubRoute,
|
|
15
|
-
"/clock": stubRoute,
|
|
16
|
-
"/container": stubRoute,
|
|
17
|
-
"/": stubRoute,
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
func TestMatchPath(t *testing.T) {
|
|
21
|
-
assert.Equal(t, true, matchPath("/", "/about"))
|
|
22
|
-
}
|