~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.
14edf649
—
pyros2097 5 years ago
improve routing
cli/cli.go
CHANGED
|
@@ -3,6 +3,7 @@ package cli
|
|
|
3
3
|
import (
|
|
4
4
|
"bytes"
|
|
5
5
|
"fmt"
|
|
6
|
+
"io/ioutil"
|
|
6
7
|
"log"
|
|
7
8
|
"net/http"
|
|
8
9
|
"os"
|
|
@@ -11,6 +12,7 @@ import (
|
|
|
11
12
|
"plugin"
|
|
12
13
|
"strconv"
|
|
13
14
|
"strings"
|
|
15
|
+
"text/template"
|
|
14
16
|
"time"
|
|
15
17
|
|
|
16
18
|
"github.com/markbates/pkger"
|
|
@@ -149,7 +151,13 @@ func (w *Watcher) addFolder(name string) {
|
|
|
149
151
|
}
|
|
150
152
|
}
|
|
151
153
|
|
|
154
|
+
type RouteInfo struct {
|
|
155
|
+
Path string
|
|
156
|
+
FuncName string
|
|
152
|
-
|
|
157
|
+
RenderFunc func(*app.RenderContext) app.UI
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
var routesMap = map[string]RouteInfo{}
|
|
153
161
|
|
|
154
162
|
func getSOPath(p string) string {
|
|
155
163
|
return "build/" + filepath.Base(filepath.Dir(p)) + ".so"
|
|
@@ -159,7 +167,7 @@ func getWasmPath(p string) string {
|
|
|
159
167
|
return "build/" + filepath.Base(filepath.Dir(p)) + ".wasm"
|
|
160
168
|
}
|
|
161
169
|
|
|
162
|
-
func
|
|
170
|
+
func buildSo(path string) (string, error) {
|
|
163
171
|
soPath := getSOPath(path)
|
|
164
172
|
out, err := exec.Command("go", "build", "-buildmode=plugin", "-o", soPath, path).CombinedOutput()
|
|
165
173
|
if err != nil {
|
|
@@ -186,51 +194,79 @@ func buildWasm(path string) (string, error) {
|
|
|
186
194
|
return wasmPath, nil
|
|
187
195
|
}
|
|
188
196
|
|
|
189
|
-
func getRoute(
|
|
197
|
+
func getRoute(basePath, p string) (string, string) {
|
|
190
|
-
basePath := filepath.Join(wd, "pages")
|
|
191
|
-
routePath := strings.Replace(p, basePath, "", 1)
|
|
192
|
-
|
|
198
|
+
clean := strings.Replace(strings.Replace(p, basePath, "", 1), ".go", "", -1)
|
|
193
|
-
|
|
199
|
+
return strings.Replace(clean, "index", "", -1), strings.Title(strings.Replace(clean, "/", "", -1))
|
|
194
|
-
return routePath
|
|
195
200
|
}
|
|
196
201
|
|
|
197
|
-
func
|
|
202
|
+
func writeMainFile(basePath string) {
|
|
203
|
+
tpl, err := template.New("writeMain").Parse(`// GENERATED FILE DO NOT EDIT
|
|
204
|
+
package main
|
|
205
|
+
|
|
206
|
+
import (
|
|
207
|
+
. "github.com/pyros2097/wapp"
|
|
208
|
+
"github.com/pyros2097/wapp/js"
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
func main() {
|
|
212
|
+
{{range $key, $element := .}}
|
|
198
|
-
|
|
213
|
+
if js.Window.URL().Path == "{{.Path}}" {
|
|
214
|
+
Run({{.FuncName}})
|
|
215
|
+
}
|
|
216
|
+
{{end}}
|
|
217
|
+
}
|
|
218
|
+
`)
|
|
219
|
+
if err != nil {
|
|
220
|
+
panic(err)
|
|
221
|
+
}
|
|
222
|
+
buf := bytes.NewBuffer(nil)
|
|
223
|
+
err = tpl.Execute(buf, routesMap)
|
|
224
|
+
if err != nil {
|
|
225
|
+
panic(err)
|
|
226
|
+
}
|
|
227
|
+
err = ioutil.WriteFile("pages/main.go", buf.Bytes(), 0644)
|
|
228
|
+
if err != nil {
|
|
229
|
+
panic(err)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
func loadRoutes(p *plugin.Plugin, basePath string, dry bool) {
|
|
199
234
|
err := filepath.Walk(basePath, func(path string, info os.FileInfo, err error) error {
|
|
200
235
|
if err != nil {
|
|
201
236
|
fmt.Printf("prevent panic by handling failure accessing a path %q: %v\n", path, err)
|
|
202
237
|
return err
|
|
203
238
|
}
|
|
204
239
|
if !info.IsDir() {
|
|
240
|
+
routePath, routeFunc := getRoute(basePath, path)
|
|
241
|
+
if routePath == "/main" {
|
|
242
|
+
return nil
|
|
243
|
+
}
|
|
244
|
+
routesMap[routePath] = RouteInfo{
|
|
245
|
+
Path: routePath,
|
|
246
|
+
FuncName: routeFunc,
|
|
247
|
+
}
|
|
248
|
+
if dry {
|
|
249
|
+
// println("Dry")
|
|
250
|
+
return nil
|
|
251
|
+
}
|
|
205
|
-
fmt.Printf("route: %s\n",
|
|
252
|
+
fmt.Printf("route: %s routeFunc: %s\n", routePath, routeFunc)
|
|
206
|
-
|
|
253
|
+
renderFn, err := p.Lookup(routeFunc)
|
|
207
|
-
load(wd, path, soPath)
|
|
208
|
-
buildWasm(path)
|
|
209
254
|
if err != nil {
|
|
210
|
-
println("could not build")
|
|
211
255
|
return err
|
|
212
256
|
}
|
|
257
|
+
routesMap[routePath] = RouteInfo{
|
|
258
|
+
Path: routePath,
|
|
259
|
+
FuncName: routeFunc,
|
|
260
|
+
RenderFunc: renderFn.(func(*app.RenderContext) app.UI),
|
|
261
|
+
}
|
|
262
|
+
// println(createPage(routesMap[routePath](app.NewRenderContext())).String())
|
|
213
263
|
}
|
|
214
264
|
return nil
|
|
215
265
|
})
|
|
216
266
|
if err != nil {
|
|
217
|
-
fmt.Printf("error walking the path %q: %v\n",
|
|
267
|
+
fmt.Printf("error walking the path %q: %v\n", basePath, err)
|
|
218
|
-
return
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
func load(wd, file, soPath string) {
|
|
223
|
-
routePath := getRoute(wd, file)
|
|
224
|
-
p, err := plugin.Open(soPath)
|
|
225
|
-
if err != nil {
|
|
226
268
|
panic(err)
|
|
227
269
|
}
|
|
228
|
-
renderFn, err := p.Lookup("Route")
|
|
229
|
-
if err != nil {
|
|
230
|
-
panic(err)
|
|
231
|
-
}
|
|
232
|
-
routesMap[routePath] = renderFn.(func(*app.RenderContext) app.UI)
|
|
233
|
-
// println(createPage(routesMap[routePath](app.NewRenderContext())).String())
|
|
234
270
|
}
|
|
235
271
|
|
|
236
272
|
func createPage(ui app.UI, wasmPath string) *bytes.Buffer {
|
|
@@ -262,14 +298,9 @@ func serve(wd string) {
|
|
|
262
298
|
buildFileServer := http.FileServer(pkger.Dir(filepath.Join(wd, "build")))
|
|
263
299
|
|
|
264
300
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
265
|
-
if
|
|
301
|
+
if routeInfo, ok := routesMap[r.URL.Path]; ok {
|
|
266
|
-
wasmPath := "/build/"
|
|
267
|
-
if r.URL.Path == "/" {
|
|
268
|
-
wasmPath += "index.wasm"
|
|
269
|
-
} else {
|
|
270
|
-
|
|
302
|
+
wasmPath := "/build/" + filepath.Base(wd) + ".wasm"
|
|
271
|
-
}
|
|
272
|
-
page := createPage(
|
|
303
|
+
page := createPage(routeInfo.RenderFunc(app.NewRenderContext()), wasmPath)
|
|
273
304
|
w.Header().Set("Content-Length", strconv.Itoa(page.Len()))
|
|
274
305
|
w.Header().Set("Content-Type", "text/html")
|
|
275
306
|
w.WriteHeader(http.StatusOK)
|
|
@@ -286,28 +317,41 @@ func serve(wd string) {
|
|
|
286
317
|
log.Fatal(http.ListenAndServe(":1234", nil))
|
|
287
318
|
}
|
|
288
319
|
|
|
320
|
+
func buildAll(basePath string) {
|
|
321
|
+
loadRoutes(nil, basePath, true)
|
|
322
|
+
writeMainFile(basePath)
|
|
323
|
+
buildWasm(basePath)
|
|
324
|
+
soPath, err := buildSo(basePath)
|
|
325
|
+
if err != nil {
|
|
326
|
+
println("could not build")
|
|
327
|
+
panic(err)
|
|
328
|
+
}
|
|
329
|
+
p, err := plugin.Open(soPath)
|
|
330
|
+
if err != nil {
|
|
331
|
+
println("could not load so plugin")
|
|
332
|
+
panic(err)
|
|
333
|
+
}
|
|
334
|
+
// fmt.Printf("%+v\n", p)
|
|
335
|
+
loadRoutes(p, basePath, false)
|
|
336
|
+
}
|
|
337
|
+
|
|
289
338
|
func Watch() {
|
|
290
339
|
wd, err := os.Getwd()
|
|
291
340
|
if err != nil {
|
|
292
341
|
fmt.Printf("could not get wd")
|
|
293
342
|
return
|
|
294
343
|
}
|
|
344
|
+
basePath := filepath.Join(wd, "pages")
|
|
295
|
-
buildAll(
|
|
345
|
+
buildAll(basePath)
|
|
296
346
|
watcher := MustRegisterWatcher()
|
|
297
347
|
go watcher.Watch()
|
|
298
348
|
go serve(wd)
|
|
299
349
|
for file := range watcher.update {
|
|
300
350
|
println("changed: " + file)
|
|
301
|
-
soPath, err := build(file)
|
|
302
|
-
if err != nil {
|
|
303
|
-
println("go build error: " + soPath)
|
|
304
|
-
continue
|
|
305
|
-
}
|
|
306
|
-
|
|
351
|
+
buildAll(basePath)
|
|
307
|
-
wasmPath, err := buildWasm(file)
|
|
308
|
-
if err != nil {
|
|
309
|
-
println("wasm build error: " + wasmPath)
|
|
310
|
-
continue
|
|
311
|
-
}
|
|
312
352
|
}
|
|
313
353
|
}
|
|
354
|
+
|
|
355
|
+
func main() {
|
|
356
|
+
Watch()
|
|
357
|
+
}
|
example/pages/{about/main.go → about.go}
RENAMED
|
@@ -4,10 +4,6 @@ import (
|
|
|
4
4
|
. "github.com/pyros2097/wapp"
|
|
5
5
|
)
|
|
6
6
|
|
|
7
|
-
func
|
|
7
|
+
func About(c *RenderContext) UI {
|
|
8
8
|
return Div(Text("About Me"))
|
|
9
9
|
}
|
|
10
|
-
|
|
11
|
-
func main() {
|
|
12
|
-
Run(Route)
|
|
13
|
-
}
|
example/pages/{clock/main.go → clock.go}
RENAMED
|
@@ -6,7 +6,7 @@ import (
|
|
|
6
6
|
. "github.com/pyros2097/wapp"
|
|
7
7
|
)
|
|
8
8
|
|
|
9
|
-
func
|
|
9
|
+
func Clock(c *RenderContext) UI {
|
|
10
10
|
timeValue, setTime := c.UseState(time.Now())
|
|
11
11
|
running, setRunning := c.UseState(false)
|
|
12
12
|
startTimer := func() {
|
|
@@ -47,7 +47,3 @@ func Route(c *RenderContext) UI {
|
|
|
47
47
|
),
|
|
48
48
|
)
|
|
49
49
|
}
|
|
50
|
-
|
|
51
|
-
func main() {
|
|
52
|
-
Run(Route)
|
|
53
|
-
}
|
example/pages/{container/main.go → container.go}
RENAMED
|
@@ -7,7 +7,7 @@ import (
|
|
|
7
7
|
. "github.com/pyros2097/wapp/example/atoms"
|
|
8
8
|
)
|
|
9
9
|
|
|
10
|
-
func
|
|
10
|
+
func AtomCounter(c *RenderContext, no string) UI {
|
|
11
11
|
count := c.UseAtom(CountAtom)
|
|
12
12
|
return Col(
|
|
13
13
|
Row(
|
|
@@ -29,14 +29,10 @@ func Counter(c *RenderContext, no string) UI {
|
|
|
29
29
|
)
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
func
|
|
32
|
+
func Container(c *RenderContext) UI {
|
|
33
33
|
return Col(
|
|
34
|
-
|
|
34
|
+
AtomCounter(c, "1"),
|
|
35
|
-
|
|
35
|
+
AtomCounter(c, "2"),
|
|
36
|
-
|
|
36
|
+
AtomCounter(c, "3"),
|
|
37
37
|
)
|
|
38
38
|
}
|
|
39
|
-
|
|
40
|
-
func main() {
|
|
41
|
-
Run(Route)
|
|
42
|
-
}
|
example/pages/{index/main.go → index.go}
RENAMED
|
@@ -6,7 +6,7 @@ import (
|
|
|
6
6
|
. "github.com/pyros2097/wapp"
|
|
7
7
|
)
|
|
8
8
|
|
|
9
|
-
func
|
|
9
|
+
func Index(c *RenderContext) UI {
|
|
10
10
|
count, setCount := c.UseInt(0)
|
|
11
11
|
inc := func() { setCount(count() + 1) }
|
|
12
12
|
dec := func() { setCount(count() - 1) }
|
|
@@ -29,7 +29,3 @@ func Route(c *RenderContext) UI {
|
|
|
29
29
|
),
|
|
30
30
|
)
|
|
31
31
|
}
|
|
32
|
-
|
|
33
|
-
func main() {
|
|
34
|
-
Run(Route)
|
|
35
|
-
}
|
example/pages/main.go
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// GENERATED FILE DO NOT EDIT
|
|
2
|
+
package main
|
|
3
|
+
|
|
4
|
+
import (
|
|
5
|
+
. "github.com/pyros2097/wapp"
|
|
6
|
+
"github.com/pyros2097/wapp/js"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
func main() {
|
|
10
|
+
|
|
11
|
+
if js.Window.URL().Path == "/" {
|
|
12
|
+
Run(Index)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if js.Window.URL().Path == "/about" {
|
|
16
|
+
Run(About)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if js.Window.URL().Path == "/clock" {
|
|
20
|
+
Run(Clock)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if js.Window.URL().Path == "/container" {
|
|
24
|
+
Run(Container)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
}
|