~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.
a49aad0d
—
pyros2097 5 years ago
improve example
- app_nowasm.go +64 -2
- app_wasm.go +4 -3
- cli/cli.go +360 -351
- example/{pages/about.go → about.go} +0 -0
- example/{config.css → assets/config.css} +0 -0
- example/assets/manifest.json +0 -21
- example/{pages/clock.go → clock.go} +0 -0
- example/{pages/container.go → container.go} +0 -0
- example/{pages/index.go → index.go} +0 -0
- example/main.go +9 -2
- example/makefile +10 -1
- example/pages/main.go +0 -27
- example/readme.md +6 -2
- example/samconfig.toml +9 -0
- example/template.yml +43 -0
- go.mod +3 -1
- go.sum +75 -0
- utils.go +31 -0
- utils_test.go +22 -0
app_nowasm.go
CHANGED
|
@@ -2,10 +2,72 @@
|
|
|
2
2
|
|
|
3
3
|
package app
|
|
4
4
|
|
|
5
|
+
import (
|
|
6
|
+
"bytes"
|
|
7
|
+
"fmt"
|
|
8
|
+
"net/http"
|
|
9
|
+
"os"
|
|
10
|
+
"path/filepath"
|
|
11
|
+
"strconv"
|
|
12
|
+
"strings"
|
|
13
|
+
|
|
14
|
+
"github.com/akrylysov/algnhsa"
|
|
15
|
+
"github.com/markbates/pkger"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
func Run(isAwsLambda bool, routes map[string]RenderFunc) {
|
|
19
|
+
wd, err := os.Getwd()
|
|
20
|
+
if err != nil {
|
|
21
|
+
fmt.Printf("could not get wd")
|
|
22
|
+
return
|
|
23
|
+
}
|
|
24
|
+
assetsFileServer := http.FileServer(pkger.Dir(filepath.Join(wd, "assets")))
|
|
25
|
+
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
26
|
+
println("route: " + r.URL.Path)
|
|
27
|
+
renderFunc := MatchRoute(routes, r.URL.Path)
|
|
28
|
+
if strings.Contains(r.URL.Path, "/assets") {
|
|
29
|
+
r.URL.Path = strings.Replace(r.URL.Path, "/assets", "", 1)
|
|
30
|
+
assetsFileServer.ServeHTTP(w, r)
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
page := createPage("wapp-example", renderFunc(NewRenderContext()))
|
|
34
|
+
w.Header().Set("Content-Length", strconv.Itoa(page.Len()))
|
|
35
|
+
w.Header().Set("Content-Type", "text/html")
|
|
36
|
+
w.WriteHeader(http.StatusOK)
|
|
37
|
+
w.Write(page.Bytes())
|
|
38
|
+
})
|
|
39
|
+
if !isAwsLambda {
|
|
40
|
+
println("Serving on HTTP port: 1234")
|
|
41
|
+
http.ListenAndServe(":1234", nil)
|
|
42
|
+
} else {
|
|
43
|
+
println("algnhsa serving default mux")
|
|
44
|
+
algnhsa.ListenAndServe(http.DefaultServeMux, nil)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
5
48
|
func Reload() {
|
|
6
49
|
panic("wasm required")
|
|
7
50
|
}
|
|
8
51
|
|
|
9
|
-
func
|
|
10
|
-
|
|
52
|
+
func createPage(title string, ui UI) *bytes.Buffer {
|
|
53
|
+
page := bytes.NewBuffer(nil)
|
|
54
|
+
page.WriteString("<!DOCTYPE html>\n")
|
|
55
|
+
elems := FilterUIElems(ui)
|
|
56
|
+
Html(
|
|
57
|
+
Head(
|
|
58
|
+
Title(title),
|
|
59
|
+
Meta("author", "pyros2097"),
|
|
60
|
+
Meta("description", "Description"),
|
|
61
|
+
Meta("keywords", ""),
|
|
62
|
+
Meta("theme-color", ""),
|
|
63
|
+
Meta("viewport", "width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0, viewport-fit=cover"),
|
|
64
|
+
Link("icon", "/assets/icon.png"),
|
|
65
|
+
Link("apple-touch-icon", "/assets/icon.png"),
|
|
66
|
+
Link("stylesheet", "/assets/styles.css"),
|
|
67
|
+
Link("manifest", "manifest"),
|
|
68
|
+
Script(`const enosys = () => { const a = new Error("not implemented"); return a.code = "ENOSYS", a }; let outputBuf = ""; window.fs = { constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, writeSync(a, b) { outputBuf += decoder.decode(b); const c = outputBuf.lastIndexOf("\n"); return -1 != c && (console.log(outputBuf.substr(0, c)), outputBuf = outputBuf.substr(c + 1)), b.length }, write(a, b, c, d, e, f) { if (0 !== c || d !== b.length || null !== e) return void f(enosys()); const g = this.writeSync(a, b); f(null, g) } }; const encoder = new TextEncoder("utf-8"), decoder = new TextDecoder("utf-8"); class Go { constructor() { this.argv = ["js"], this.env = {}, this.exit = a => { 0 !== a && console.warn("exit code:", a) }, this._exitPromise = new Promise(a => { this._resolveExitPromise = a }), this._pendingEvent = null, this._scheduledTimeouts = new Map, this._nextCallbackTimeoutID = 1; const a = (a, b) => { this.mem.setUint32(a + 0, b, !0), this.mem.setUint32(a + 4, Math.floor(b / 4294967296), !0) }, b = a => { const b = this.mem.getUint32(a + 0, !0), c = this.mem.getInt32(a + 4, !0); return b + 4294967296 * c }, c = a => { const b = this.mem.getFloat64(a, !0); if (0 !== b) { if (!isNaN(b)) return b; const c = this.mem.getUint32(a, !0); return this._values[c] } }, d = (a, b) => { const c = 2146959360; if ("number" == typeof b) return isNaN(b) ? (this.mem.setUint32(a + 4, 2146959360, !0), void this.mem.setUint32(a, 0, !0)) : 0 === b ? (this.mem.setUint32(a + 4, 2146959360, !0), void this.mem.setUint32(a, 1, !0)) : void this.mem.setFloat64(a, b, !0); switch (b) { case void 0: return void this.mem.setFloat64(a, 0, !0); case null: return this.mem.setUint32(a + 4, c, !0), void this.mem.setUint32(a, 2, !0); case !0: return this.mem.setUint32(a + 4, c, !0), void this.mem.setUint32(a, 3, !0); case !1: return this.mem.setUint32(a + 4, c, !0), void this.mem.setUint32(a, 4, !0); }let d = this._ids.get(b); d === void 0 && (d = this._idPool.pop(), d === void 0 && (d = this._values.length), this._values[d] = b, this._goRefCounts[d] = 0, this._ids.set(b, d)), this._goRefCounts[d]++; let e = 1; switch (typeof b) { case "string": e = 2; break; case "symbol": e = 3; break; case "function": e = 4; }this.mem.setUint32(a + 4, 2146959360 | e, !0), this.mem.setUint32(a, d, !0) }, e = a => { const c = b(a + 0), d = b(a + 8); return new Uint8Array(this._inst.exports.mem.buffer, c, d) }, f = d => { const e = b(d + 0), f = b(d + 8), g = Array(f); for (let a = 0; a < f; a++)g[a] = c(e + 8 * a); return g }, g = a => { const c = b(a + 0), d = b(a + 8); return decoder.decode(new DataView(this._inst.exports.mem.buffer, c, d)) }, h = Date.now() - performance.now(); this.importObject = { go: { "runtime.wasmExit": a => { const b = this.mem.getInt32(a + 8, !0); this.exited = !0, delete this._inst, delete this._values, delete this._goRefCounts, delete this._ids, delete this._idPool, this.exit(b) }, "runtime.wasmWrite": a => { const c = b(a + 8), d = b(a + 16), e = this.mem.getInt32(a + 24, !0); fs.writeSync(c, new Uint8Array(this._inst.exports.mem.buffer, d, e)) }, "runtime.resetMemoryDataView": () => { this.mem = new DataView(this._inst.exports.mem.buffer) }, "runtime.nanotime1": b => { a(b + 8, 1e6 * (h + performance.now())) }, "runtime.walltime1": b => { const c = new Date().getTime(); a(b + 8, c / 1e3), this.mem.setInt32(b + 16, 1e6 * (c % 1e3), !0) }, "runtime.scheduleTimeoutEvent": a => { const c = this._nextCallbackTimeoutID; this._nextCallbackTimeoutID++, this._scheduledTimeouts.set(c, setTimeout(() => { for (this._resume(); this._scheduledTimeouts.has(c);)console.warn("scheduleTimeoutEvent: missed timeout event"), this._resume() }, b(a + 8) + 1)), this.mem.setInt32(a + 16, c, !0) }, "runtime.clearTimeoutEvent": a => { const b = this.mem.getInt32(a + 8, !0); clearTimeout(this._scheduledTimeouts.get(b)), this._scheduledTimeouts.delete(b) }, "runtime.getRandomData": a => { crypto.getRandomValues(e(a + 8)) }, "syscall/js.finalizeRef": a => { const b = this.mem.getUint32(a + 8, !0); if (this._goRefCounts[b]--, 0 === this._goRefCounts[b]) { const a = this._values[b]; this._values[b] = null, this._ids.delete(a), this._idPool.push(b) } }, "syscall/js.stringVal": a => { d(a + 24, g(a + 8)) }, "syscall/js.valueGet": a => { const b = Reflect.get(c(a + 8), g(a + 16)); a = this._inst.exports.getsp(), d(a + 32, b) }, "syscall/js.valueSet": a => { Reflect.set(c(a + 8), g(a + 16), c(a + 32)) }, "syscall/js.valueDelete": a => { Reflect.deleteProperty(c(a + 8), g(a + 16)) }, "syscall/js.valueIndex": a => { d(a + 24, Reflect.get(c(a + 8), b(a + 16))) }, "syscall/js.valueSetIndex": a => { Reflect.set(c(a + 8), b(a + 16), c(a + 24)) }, "syscall/js.valueCall": a => { try { const b = c(a + 8), e = Reflect.get(b, g(a + 16)), h = f(a + 32), i = Reflect.apply(e, b, h); a = this._inst.exports.getsp(), d(a + 56, i), this.mem.setUint8(a + 64, 1) } catch (b) { d(a + 56, b), this.mem.setUint8(a + 64, 0) } }, "syscall/js.valueInvoke": a => { try { const b = c(a + 8), e = f(a + 16), g = Reflect.apply(b, void 0, e); a = this._inst.exports.getsp(), d(a + 40, g), this.mem.setUint8(a + 48, 1) } catch (b) { d(a + 40, b), this.mem.setUint8(a + 48, 0) } }, "syscall/js.valueNew": a => { try { const b = c(a + 8), e = f(a + 16), g = Reflect.construct(b, e); a = this._inst.exports.getsp(), d(a + 40, g), this.mem.setUint8(a + 48, 1) } catch (b) { d(a + 40, b), this.mem.setUint8(a + 48, 0) } }, "syscall/js.valueLength": b => { a(b + 16, parseInt(c(b + 8).length)) }, "syscall/js.valuePrepareString": b => { const e = encoder.encode(c(b + 8) + ""); d(b + 16, e), a(b + 24, e.length) }, "syscall/js.valueLoadString": a => { const b = c(a + 8); e(a + 16).set(b) }, "syscall/js.valueInstanceOf": a => { this.mem.setUint8(a + 24, c(a + 8) instanceof c(a + 16)) }, "syscall/js.copyBytesToGo": b => { const d = e(b + 8), f = c(b + 32); if (!(f instanceof Uint8Array)) return void this.mem.setUint8(b + 48, 0); const g = f.subarray(0, d.length); d.set(g), a(b + 40, g.length), this.mem.setUint8(b + 48, 1) }, "syscall/js.copyBytesToJS": b => { const d = c(b + 8), f = e(b + 16); if (!(d instanceof Uint8Array)) return void this.mem.setUint8(b + 48, 0); const g = f.subarray(0, d.length); d.set(g), a(b + 40, g.length), this.mem.setUint8(b + 48, 1) }, debug: a => { console.log(a) } } } } async run(a) { this._inst = a, this.mem = new DataView(this._inst.exports.mem.buffer), this._values = [NaN, 0, null, !0, !1, window, this], this._goRefCounts = [], this._ids = new Map, this._idPool = [], this.exited = !1; let b = 4096; const c = a => { const c = b, d = encoder.encode(a + "\0"); return new Uint8Array(this.mem.buffer, b, d.length).set(d), b += d.length, 0 != b % 8 && (b += 8 - b % 8), c }, d = this.argv.length, e = []; this.argv.forEach(a => { e.push(c(a)) }), e.push(0); const f = Object.keys(this.env).sort(); f.forEach(a => { e.push(c(a + "=" + this.env[a])) }), e.push(0); const g = b; e.forEach(a => { this.mem.setUint32(b, a, !0), this.mem.setUint32(b + 4, 0, !0), b += 8 }), this._inst.exports.run(d, g), this.exited && this._resolveExitPromise(), await this._exitPromise } _resume() { if (this.exited) throw new Error("Go program has already exited"); this._inst.exports.resume(), this.exited && this._resolveExitPromise() } _makeFuncWrapper(a) { const b = this; return function () { const c = { id: a, this: this, args: arguments }; return b._pendingEvent = c, b._resume(), c.result } } } const go = new Go; WebAssembly.instantiateStreaming(fetch("/assets/main.wasm"), go.importObject).then(a => go.run(a.instance)).catch(a => console.error("could not load wasm", a));`),
|
|
69
|
+
),
|
|
70
|
+
Body(elems[0]),
|
|
71
|
+
).Html(page)
|
|
72
|
+
return page
|
|
11
73
|
}
|
app_wasm.go
CHANGED
|
@@ -11,7 +11,8 @@ var (
|
|
|
11
11
|
rootPrefix string
|
|
12
12
|
)
|
|
13
13
|
|
|
14
|
-
func Run(
|
|
14
|
+
func Run(isAwsLambda bool, routes map[string]RenderFunc) {
|
|
15
|
+
renderFunc := MatchRoute(routes, js.Window.URL().Path)
|
|
15
16
|
defer func() {
|
|
16
17
|
err := recover()
|
|
17
18
|
// show alert
|
|
@@ -20,10 +21,10 @@ func Run(render RenderFunc) {
|
|
|
20
21
|
|
|
21
22
|
initBody()
|
|
22
23
|
initContent()
|
|
23
|
-
if err := body.replaceChildAt(0,
|
|
24
|
+
if err := body.replaceChildAt(0, renderFunc); err != nil {
|
|
24
25
|
panic("replacing content failed")
|
|
25
26
|
}
|
|
26
|
-
content =
|
|
27
|
+
content = renderFunc
|
|
27
28
|
|
|
28
29
|
for {
|
|
29
30
|
select {
|
cli/cli.go
CHANGED
|
@@ -1,353 +1,362 @@
|
|
|
1
1
|
package cli
|
|
2
2
|
|
|
3
|
-
import (
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
3
|
+
// import (
|
|
4
|
+
// "bytes"
|
|
5
|
+
// "fmt"
|
|
6
|
+
// "io/ioutil"
|
|
7
|
+
// "log"
|
|
8
|
+
// "net/http"
|
|
9
|
+
// "os"
|
|
10
|
+
// "os/exec"
|
|
11
|
+
// "path/filepath"
|
|
12
|
+
// "plugin"
|
|
13
|
+
// "strconv"
|
|
14
|
+
// "strings"
|
|
15
|
+
// "text/template"
|
|
16
|
+
// "time"
|
|
17
|
+
|
|
18
|
+
// "github.com/akrylysov/algnhsa"
|
|
19
|
+
// "github.com/markbates/pkger"
|
|
20
|
+
// app "github.com/pyros2097/wapp"
|
|
21
|
+
// "gopkg.in/fsnotify.v1"
|
|
22
|
+
// )
|
|
23
|
+
|
|
24
|
+
// const wasmExecTemplate = `const enosys=()=>{const a=new Error("not implemented");return a.code="ENOSYS",a};let outputBuf="";window.fs={constants:{O_WRONLY:-1,O_RDWR:-1,O_CREAT:-1,O_TRUNC:-1,O_APPEND:-1,O_EXCL:-1},writeSync(a,b){outputBuf+=decoder.decode(b);const c=outputBuf.lastIndexOf("\n");return-1!=c&&(console.log(outputBuf.substr(0,c)),outputBuf=outputBuf.substr(c+1)),b.length},write(a,b,c,d,e,f){if(0!==c||d!==b.length||null!==e)return void f(enosys());const g=this.writeSync(a,b);f(null,g)}};const encoder=new TextEncoder("utf-8"),decoder=new TextDecoder("utf-8");class Go{constructor(){this.argv=["js"],this.env={},this.exit=a=>{0!==a&&console.warn("exit code:",a)},this._exitPromise=new Promise(a=>{this._resolveExitPromise=a}),this._pendingEvent=null,this._scheduledTimeouts=new Map,this._nextCallbackTimeoutID=1;const a=(a,b)=>{this.mem.setUint32(a+0,b,!0),this.mem.setUint32(a+4,Math.floor(b/4294967296),!0)},b=a=>{const b=this.mem.getUint32(a+0,!0),c=this.mem.getInt32(a+4,!0);return b+4294967296*c},c=a=>{const b=this.mem.getFloat64(a,!0);if(0!==b){if(!isNaN(b))return b;const c=this.mem.getUint32(a,!0);return this._values[c]}},d=(a,b)=>{const c=2146959360;if("number"==typeof b)return isNaN(b)?(this.mem.setUint32(a+4,2146959360,!0),void this.mem.setUint32(a,0,!0)):0===b?(this.mem.setUint32(a+4,2146959360,!0),void this.mem.setUint32(a,1,!0)):void this.mem.setFloat64(a,b,!0);switch(b){case void 0:return void this.mem.setFloat64(a,0,!0);case null:return this.mem.setUint32(a+4,c,!0),void this.mem.setUint32(a,2,!0);case!0:return this.mem.setUint32(a+4,c,!0),void this.mem.setUint32(a,3,!0);case!1:return this.mem.setUint32(a+4,c,!0),void this.mem.setUint32(a,4,!0);}let d=this._ids.get(b);d===void 0&&(d=this._idPool.pop(),d===void 0&&(d=this._values.length),this._values[d]=b,this._goRefCounts[d]=0,this._ids.set(b,d)),this._goRefCounts[d]++;let e=1;switch(typeof b){case"string":e=2;break;case"symbol":e=3;break;case"function":e=4;}this.mem.setUint32(a+4,2146959360|e,!0),this.mem.setUint32(a,d,!0)},e=a=>{const c=b(a+0),d=b(a+8);return new Uint8Array(this._inst.exports.mem.buffer,c,d)},f=d=>{const e=b(d+0),f=b(d+8),g=Array(f);for(let a=0;a<f;a++)g[a]=c(e+8*a);return g},g=a=>{const c=b(a+0),d=b(a+8);return decoder.decode(new DataView(this._inst.exports.mem.buffer,c,d))},h=Date.now()-performance.now();this.importObject={go:{"runtime.wasmExit":a=>{const b=this.mem.getInt32(a+8,!0);this.exited=!0,delete this._inst,delete this._values,delete this._goRefCounts,delete this._ids,delete this._idPool,this.exit(b)},"runtime.wasmWrite":a=>{const c=b(a+8),d=b(a+16),e=this.mem.getInt32(a+24,!0);fs.writeSync(c,new Uint8Array(this._inst.exports.mem.buffer,d,e))},"runtime.resetMemoryDataView":()=>{this.mem=new DataView(this._inst.exports.mem.buffer)},"runtime.nanotime1":b=>{a(b+8,1e6*(h+performance.now()))},"runtime.walltime1":b=>{const c=new Date().getTime();a(b+8,c/1e3),this.mem.setInt32(b+16,1e6*(c%1e3),!0)},"runtime.scheduleTimeoutEvent":a=>{const c=this._nextCallbackTimeoutID;this._nextCallbackTimeoutID++,this._scheduledTimeouts.set(c,setTimeout(()=>{for(this._resume();this._scheduledTimeouts.has(c);)console.warn("scheduleTimeoutEvent: missed timeout event"),this._resume()},b(a+8)+1)),this.mem.setInt32(a+16,c,!0)},"runtime.clearTimeoutEvent":a=>{const b=this.mem.getInt32(a+8,!0);clearTimeout(this._scheduledTimeouts.get(b)),this._scheduledTimeouts.delete(b)},"runtime.getRandomData":a=>{crypto.getRandomValues(e(a+8))},"syscall/js.finalizeRef":a=>{const b=this.mem.getUint32(a+8,!0);if(this._goRefCounts[b]--,0===this._goRefCounts[b]){const a=this._values[b];this._values[b]=null,this._ids.delete(a),this._idPool.push(b)}},"syscall/js.stringVal":a=>{d(a+24,g(a+8))},"syscall/js.valueGet":a=>{const b=Reflect.get(c(a+8),g(a+16));a=this._inst.exports.getsp(),d(a+32,b)},"syscall/js.valueSet":a=>{Reflect.set(c(a+8),g(a+16),c(a+32))},"syscall/js.valueDelete":a=>{Reflect.deleteProperty(c(a+8),g(a+16))},"syscall/js.valueIndex":a=>{d(a+24,Reflect.get(c(a+8),b(a+16)))},"syscall/js.valueSetIndex":a=>{Reflect.set(c(a+8),b(a+16),c(a+24))},"syscall/js.valueCall":a=>{try{const b=c(a+8),e=Reflect.get(b,g(a+16)),h=f(a+32),i=Reflect.apply(e,b,h);a=this._inst.exports.getsp(),d(a+56,i),this.mem.setUint8(a+64,1)}catch(b){d(a+56,b),this.mem.setUint8(a+64,0)}},"syscall/js.valueInvoke":a=>{try{const b=c(a+8),e=f(a+16),g=Reflect.apply(b,void 0,e);a=this._inst.exports.getsp(),d(a+40,g),this.mem.setUint8(a+48,1)}catch(b){d(a+40,b),this.mem.setUint8(a+48,0)}},"syscall/js.valueNew":a=>{try{const b=c(a+8),e=f(a+16),g=Reflect.construct(b,e);a=this._inst.exports.getsp(),d(a+40,g),this.mem.setUint8(a+48,1)}catch(b){d(a+40,b),this.mem.setUint8(a+48,0)}},"syscall/js.valueLength":b=>{a(b+16,parseInt(c(b+8).length))},"syscall/js.valuePrepareString":b=>{const e=encoder.encode(c(b+8)+"");d(b+16,e),a(b+24,e.length)},"syscall/js.valueLoadString":a=>{const b=c(a+8);e(a+16).set(b)},"syscall/js.valueInstanceOf":a=>{this.mem.setUint8(a+24,c(a+8)instanceof c(a+16))},"syscall/js.copyBytesToGo":b=>{const d=e(b+8),f=c(b+32);if(!(f instanceof Uint8Array))return void this.mem.setUint8(b+48,0);const g=f.subarray(0,d.length);d.set(g),a(b+40,g.length),this.mem.setUint8(b+48,1)},"syscall/js.copyBytesToJS":b=>{const d=c(b+8),f=e(b+16);if(!(d instanceof Uint8Array))return void this.mem.setUint8(b+48,0);const g=f.subarray(0,d.length);d.set(g),a(b+40,g.length),this.mem.setUint8(b+48,1)},debug:a=>{console.log(a)}}}}async run(a){this._inst=a,this.mem=new DataView(this._inst.exports.mem.buffer),this._values=[NaN,0,null,!0,!1,window,this],this._goRefCounts=[],this._ids=new Map,this._idPool=[],this.exited=!1;let b=4096;const c=a=>{const c=b,d=encoder.encode(a+"\0");return new Uint8Array(this.mem.buffer,b,d.length).set(d),b+=d.length,0!=b%8&&(b+=8-b%8),c},d=this.argv.length,e=[];this.argv.forEach(a=>{e.push(c(a))}),e.push(0);const f=Object.keys(this.env).sort();f.forEach(a=>{e.push(c(a+"="+this.env[a]))}),e.push(0);const g=b;e.forEach(a=>{this.mem.setUint32(b,a,!0),this.mem.setUint32(b+4,0,!0),b+=8}),this._inst.exports.run(d,g),this.exited&&this._resolveExitPromise(),await this._exitPromise}_resume(){if(this.exited)throw new Error("Go program has already exited");this._inst.exports.resume(),this.exited&&this._resolveExitPromise()}_makeFuncWrapper(a){const b=this;return function(){const c={id:a,this:this,args:arguments};return b._pendingEvent=c,b._resume(),c.result}}}const go=new Go;WebAssembly.instantiateStreaming(fetch("__path__"),go.importObject).then(a=>go.run(a.instance)).catch(a=>console.error("could not load wasm",a));`
|
|
25
|
+
|
|
26
|
+
// func wasmExecJs(path string) string {
|
|
27
|
+
// return strings.Replace(wasmExecTemplate, "__path__", path, 1)
|
|
28
|
+
// }
|
|
29
|
+
|
|
30
|
+
// var watchDelta = 1000 * time.Millisecond
|
|
31
|
+
|
|
32
|
+
// type Watcher struct {
|
|
33
|
+
// rootdir string
|
|
34
|
+
// watcher *fsnotify.Watcher
|
|
35
|
+
// watchVendor bool
|
|
36
|
+
// update chan string
|
|
37
|
+
// }
|
|
38
|
+
|
|
39
|
+
// // MustRegisterWatcher creates a new Watcher and starts listening to
|
|
40
|
+
// // given folders
|
|
41
|
+
// func MustRegisterWatcher() *Watcher {
|
|
42
|
+
// w := &Watcher{
|
|
43
|
+
// update: make(chan string),
|
|
44
|
+
// watchVendor: false,
|
|
45
|
+
// }
|
|
46
|
+
// var err error
|
|
47
|
+
// w.watcher, err = fsnotify.NewWatcher()
|
|
48
|
+
// if err != nil {
|
|
49
|
+
// log.Fatalf("Could not register watcher: %s", err)
|
|
50
|
+
// }
|
|
51
|
+
// w.watchFolders()
|
|
52
|
+
// return w
|
|
53
|
+
// }
|
|
54
|
+
|
|
55
|
+
// // Watch listens file updates, and sends signal to
|
|
56
|
+
// // update channel when .go and .tmpl files are updated
|
|
57
|
+
// func (w *Watcher) Watch() {
|
|
58
|
+
// eventSent := false
|
|
59
|
+
// for {
|
|
60
|
+
// select {
|
|
61
|
+
// case event := <-w.watcher.Events:
|
|
62
|
+
// // discard chmod events
|
|
63
|
+
// if event.Op&fsnotify.Chmod != fsnotify.Chmod {
|
|
64
|
+
// // test files do not need a rebuild
|
|
65
|
+
// if isTestFile(event.Name) {
|
|
66
|
+
// continue
|
|
67
|
+
// }
|
|
68
|
+
// if !isWatchedFileType(event.Name) {
|
|
69
|
+
// continue
|
|
70
|
+
// }
|
|
71
|
+
// if eventSent {
|
|
72
|
+
// continue
|
|
73
|
+
// }
|
|
74
|
+
// eventSent = true
|
|
75
|
+
// // prevent consequent builds
|
|
76
|
+
// go func() {
|
|
77
|
+
// w.update <- event.Name
|
|
78
|
+
// time.Sleep(watchDelta)
|
|
79
|
+
// eventSent = false
|
|
80
|
+
// }()
|
|
81
|
+
|
|
82
|
+
// }
|
|
83
|
+
// case err := <-w.watcher.Errors:
|
|
84
|
+
// if err != nil {
|
|
85
|
+
// log.Fatalf("Watcher error: %s", err)
|
|
86
|
+
// }
|
|
87
|
+
// return
|
|
88
|
+
// }
|
|
89
|
+
// }
|
|
90
|
+
// }
|
|
91
|
+
|
|
92
|
+
// func isTestFile(fileName string) bool {
|
|
93
|
+
// return strings.HasSuffix(filepath.Base(fileName), "_test.go")
|
|
94
|
+
// }
|
|
95
|
+
|
|
96
|
+
// func isWatchedFileType(fileName string) bool {
|
|
97
|
+
// ext := filepath.Ext(fileName)
|
|
98
|
+
|
|
99
|
+
// return ext == ".go"
|
|
100
|
+
// }
|
|
101
|
+
|
|
102
|
+
// // Close closes the fsnotify watcher channel
|
|
103
|
+
// func (w *Watcher) Close() {
|
|
104
|
+
// w.watcher.Close()
|
|
105
|
+
// close(w.update)
|
|
106
|
+
// }
|
|
107
|
+
|
|
108
|
+
// // watchFolders recursively adds folders that will be watched against the changes,
|
|
109
|
+
// // starting from the working directory
|
|
110
|
+
// func (w *Watcher) watchFolders() {
|
|
111
|
+
// wd, err := os.Getwd()
|
|
112
|
+
|
|
113
|
+
// if err != nil {
|
|
114
|
+
// log.Fatalf("Could not get root working directory: %s", err)
|
|
115
|
+
// }
|
|
116
|
+
|
|
117
|
+
// filepath.Walk(wd, func(path string, info os.FileInfo, err error) error {
|
|
118
|
+
// // skip files
|
|
119
|
+
// if info == nil {
|
|
120
|
+
// log.Fatalf("wrong watcher package: %s", path)
|
|
121
|
+
// }
|
|
122
|
+
|
|
123
|
+
// if !info.IsDir() {
|
|
124
|
+
// return nil
|
|
125
|
+
// }
|
|
126
|
+
|
|
127
|
+
// if !w.watchVendor {
|
|
128
|
+
// // skip vendor directory
|
|
129
|
+
// vendor := fmt.Sprintf("%s/vendor", wd)
|
|
130
|
+
// if strings.HasPrefix(path, vendor) {
|
|
131
|
+
// return filepath.SkipDir
|
|
132
|
+
// }
|
|
133
|
+
// }
|
|
134
|
+
|
|
135
|
+
// // skip hidden folders
|
|
136
|
+
// if len(path) > 1 && strings.HasPrefix(filepath.Base(path), ".") {
|
|
137
|
+
// return filepath.SkipDir
|
|
138
|
+
// }
|
|
139
|
+
|
|
140
|
+
// w.addFolder(path)
|
|
141
|
+
|
|
142
|
+
// return err
|
|
143
|
+
// })
|
|
144
|
+
// }
|
|
145
|
+
|
|
146
|
+
// // addFolder adds given folder name to the watched folders, and starts
|
|
147
|
+
// // watching it for further changes
|
|
148
|
+
// func (w *Watcher) addFolder(name string) {
|
|
149
|
+
// println("watch: " + name)
|
|
150
|
+
// if err := w.watcher.Add(name); err != nil {
|
|
151
|
+
// log.Fatalf("Could not watch folder: %s", err)
|
|
152
|
+
// }
|
|
153
|
+
// }
|
|
154
|
+
|
|
155
|
+
// type RouteInfo struct {
|
|
156
|
+
// Path string
|
|
157
|
+
// FuncName string
|
|
158
|
+
// RenderFunc func(*app.RenderContext) app.UI
|
|
159
|
+
// }
|
|
160
|
+
|
|
161
|
+
// var routesMap = map[string]RouteInfo{}
|
|
162
|
+
|
|
163
|
+
// func getSOPath(p string) string {
|
|
164
|
+
// return "build/" + filepath.Base(filepath.Dir(p)) + ".so"
|
|
165
|
+
// }
|
|
166
|
+
|
|
167
|
+
// func getWasmPath(p string) string {
|
|
168
|
+
// return "build/" + filepath.Base(filepath.Dir(p)) + ".wasm"
|
|
169
|
+
// }
|
|
170
|
+
|
|
171
|
+
// func buildSo(path string) (string, error) {
|
|
172
|
+
// soPath := getSOPath(path)
|
|
173
|
+
// out, err := exec.Command("go", "build", "-buildmode=plugin", "-o", soPath, path).CombinedOutput()
|
|
174
|
+
// if err != nil {
|
|
175
|
+
// println(string(out))
|
|
176
|
+
// println(err.Error())
|
|
177
|
+
// return "", err
|
|
178
|
+
// }
|
|
179
|
+
// fmt.Printf("wrote: %s\n", soPath)
|
|
180
|
+
// return soPath, nil
|
|
181
|
+
// }
|
|
182
|
+
|
|
183
|
+
// func buildWasm(path string) (string, error) {
|
|
184
|
+
// wasmPath := getWasmPath(path)
|
|
185
|
+
// cmd := exec.Command("go", "build", "-o", wasmPath, path)
|
|
186
|
+
// cmd.Env = os.Environ()
|
|
187
|
+
// cmd.Env = append(cmd.Env, "GOOS=js", "GOARCH=wasm")
|
|
188
|
+
// out, err := cmd.CombinedOutput()
|
|
189
|
+
// if err != nil {
|
|
190
|
+
// println(string(out))
|
|
191
|
+
// println(err.Error())
|
|
192
|
+
// return "", err
|
|
193
|
+
// }
|
|
194
|
+
// fmt.Printf("wrote: %s\n", wasmPath)
|
|
195
|
+
// return wasmPath, nil
|
|
196
|
+
// }
|
|
197
|
+
|
|
198
|
+
// func getRoute(basePath, p string) (string, string) {
|
|
199
|
+
// clean := strings.Replace(strings.Replace(p, basePath, "", 1), ".go", "", -1)
|
|
200
|
+
// return strings.Replace(clean, "index", "", -1), strings.Title(strings.Replace(clean, "/", "", -1))
|
|
201
|
+
// }
|
|
202
|
+
|
|
203
|
+
// func writeMainFile(basePath string) {
|
|
204
|
+
// tpl, err := template.New("writeMain").Parse(`// GENERATED FILE DO NOT EDIT
|
|
205
|
+
// package main
|
|
206
|
+
|
|
207
|
+
// import (
|
|
208
|
+
// . "github.com/pyros2097/wapp"
|
|
209
|
+
// "github.com/pyros2097/wapp/js"
|
|
210
|
+
// )
|
|
211
|
+
|
|
212
|
+
// func main() {
|
|
213
|
+
// {{range $key, $element := .}}
|
|
214
|
+
// if js.Window.URL().Path == "{{.Path}}" {
|
|
215
|
+
// Run({{.FuncName}})
|
|
216
|
+
// }
|
|
217
|
+
// {{end}}
|
|
218
|
+
// }
|
|
219
|
+
// `)
|
|
220
|
+
// if err != nil {
|
|
221
|
+
// panic(err)
|
|
222
|
+
// }
|
|
223
|
+
// buf := bytes.NewBuffer(nil)
|
|
224
|
+
// err = tpl.Execute(buf, routesMap)
|
|
225
|
+
// if err != nil {
|
|
226
|
+
// panic(err)
|
|
227
|
+
// }
|
|
228
|
+
// err = ioutil.WriteFile("pages/main.go", buf.Bytes(), 0644)
|
|
229
|
+
// if err != nil {
|
|
230
|
+
// panic(err)
|
|
231
|
+
// }
|
|
232
|
+
// }
|
|
233
|
+
|
|
234
|
+
// func loadRoutes(p *plugin.Plugin, basePath string, dry bool) {
|
|
235
|
+
// err := filepath.Walk(basePath, func(path string, info os.FileInfo, err error) error {
|
|
236
|
+
// if err != nil {
|
|
237
|
+
// fmt.Printf("prevent panic by handling failure accessing a path %q: %v\n", path, err)
|
|
238
|
+
// return err
|
|
239
|
+
// }
|
|
240
|
+
// if !info.IsDir() {
|
|
241
|
+
// routePath, routeFunc := getRoute(basePath, path)
|
|
242
|
+
// if routePath == "/main" {
|
|
243
|
+
// return nil
|
|
244
|
+
// }
|
|
245
|
+
// routesMap[routePath] = RouteInfo{
|
|
246
|
+
// Path: routePath,
|
|
247
|
+
// FuncName: routeFunc,
|
|
248
|
+
// }
|
|
249
|
+
// if dry {
|
|
250
|
+
// // println("Dry")
|
|
251
|
+
// return nil
|
|
252
|
+
// }
|
|
253
|
+
// fmt.Printf("route: %s routeFunc: %s\n", routePath, routeFunc)
|
|
254
|
+
// renderFn, err := p.Lookup(routeFunc)
|
|
255
|
+
// if err != nil {
|
|
256
|
+
// return err
|
|
257
|
+
// }
|
|
258
|
+
// routesMap[routePath] = RouteInfo{
|
|
259
|
+
// Path: routePath,
|
|
260
|
+
// FuncName: routeFunc,
|
|
261
|
+
// RenderFunc: renderFn.(func(*app.RenderContext) app.UI),
|
|
262
|
+
// }
|
|
263
|
+
// // println(createPage(routesMap[routePath](app.NewRenderContext())).String())
|
|
264
|
+
// }
|
|
265
|
+
// return nil
|
|
266
|
+
// })
|
|
267
|
+
// if err != nil {
|
|
268
|
+
// fmt.Printf("error walking the path %q: %v\n", basePath, err)
|
|
269
|
+
// panic(err)
|
|
270
|
+
// }
|
|
271
|
+
// }
|
|
272
|
+
|
|
273
|
+
// func CreatePage(title string, ui app.UI, wasmPath string) *bytes.Buffer {
|
|
274
|
+
// page := bytes.NewBuffer(nil)
|
|
275
|
+
// page.WriteString("<!DOCTYPE html>\n")
|
|
276
|
+
// elems := app.FilterUIElems(ui)
|
|
277
|
+
// app.Html(
|
|
278
|
+
// app.Head(
|
|
279
|
+
// app.Title(title),
|
|
280
|
+
// app.Meta("author", "pyros2097"),
|
|
281
|
+
// app.Meta("description", "Description"),
|
|
282
|
+
// app.Meta("keywords", ""),
|
|
283
|
+
// app.Meta("theme-color", ""),
|
|
284
|
+
// app.Meta("viewport", "width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0, viewport-fit=cover"),
|
|
285
|
+
// app.Link("icon", "/assets/icon.png"),
|
|
286
|
+
// app.Link("apple-touch-icon", "/assets/icon.png"),
|
|
287
|
+
// app.Link("stylesheet", "/assets/styles.css"),
|
|
288
|
+
// app.Link("manifest", "manifest"),
|
|
289
|
+
// app.Script(`const enosys = () => { const a = new Error("not implemented"); return a.code = "ENOSYS", a }; let outputBuf = ""; window.fs = { constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, writeSync(a, b) { outputBuf += decoder.decode(b); const c = outputBuf.lastIndexOf("\n"); return -1 != c && (console.log(outputBuf.substr(0, c)), outputBuf = outputBuf.substr(c + 1)), b.length }, write(a, b, c, d, e, f) { if (0 !== c || d !== b.length || null !== e) return void f(enosys()); const g = this.writeSync(a, b); f(null, g) } }; const encoder = new TextEncoder("utf-8"), decoder = new TextDecoder("utf-8"); class Go { constructor() { this.argv = ["js"], this.env = {}, this.exit = a => { 0 !== a && console.warn("exit code:", a) }, this._exitPromise = new Promise(a => { this._resolveExitPromise = a }), this._pendingEvent = null, this._scheduledTimeouts = new Map, this._nextCallbackTimeoutID = 1; const a = (a, b) => { this.mem.setUint32(a + 0, b, !0), this.mem.setUint32(a + 4, Math.floor(b / 4294967296), !0) }, b = a => { const b = this.mem.getUint32(a + 0, !0), c = this.mem.getInt32(a + 4, !0); return b + 4294967296 * c }, c = a => { const b = this.mem.getFloat64(a, !0); if (0 !== b) { if (!isNaN(b)) return b; const c = this.mem.getUint32(a, !0); return this._values[c] } }, d = (a, b) => { const c = 2146959360; if ("number" == typeof b) return isNaN(b) ? (this.mem.setUint32(a + 4, 2146959360, !0), void this.mem.setUint32(a, 0, !0)) : 0 === b ? (this.mem.setUint32(a + 4, 2146959360, !0), void this.mem.setUint32(a, 1, !0)) : void this.mem.setFloat64(a, b, !0); switch (b) { case void 0: return void this.mem.setFloat64(a, 0, !0); case null: return this.mem.setUint32(a + 4, c, !0), void this.mem.setUint32(a, 2, !0); case !0: return this.mem.setUint32(a + 4, c, !0), void this.mem.setUint32(a, 3, !0); case !1: return this.mem.setUint32(a + 4, c, !0), void this.mem.setUint32(a, 4, !0); }let d = this._ids.get(b); d === void 0 && (d = this._idPool.pop(), d === void 0 && (d = this._values.length), this._values[d] = b, this._goRefCounts[d] = 0, this._ids.set(b, d)), this._goRefCounts[d]++; let e = 1; switch (typeof b) { case "string": e = 2; break; case "symbol": e = 3; break; case "function": e = 4; }this.mem.setUint32(a + 4, 2146959360 | e, !0), this.mem.setUint32(a, d, !0) }, e = a => { const c = b(a + 0), d = b(a + 8); return new Uint8Array(this._inst.exports.mem.buffer, c, d) }, f = d => { const e = b(d + 0), f = b(d + 8), g = Array(f); for (let a = 0; a < f; a++)g[a] = c(e + 8 * a); return g }, g = a => { const c = b(a + 0), d = b(a + 8); return decoder.decode(new DataView(this._inst.exports.mem.buffer, c, d)) }, h = Date.now() - performance.now(); this.importObject = { go: { "runtime.wasmExit": a => { const b = this.mem.getInt32(a + 8, !0); this.exited = !0, delete this._inst, delete this._values, delete this._goRefCounts, delete this._ids, delete this._idPool, this.exit(b) }, "runtime.wasmWrite": a => { const c = b(a + 8), d = b(a + 16), e = this.mem.getInt32(a + 24, !0); fs.writeSync(c, new Uint8Array(this._inst.exports.mem.buffer, d, e)) }, "runtime.resetMemoryDataView": () => { this.mem = new DataView(this._inst.exports.mem.buffer) }, "runtime.nanotime1": b => { a(b + 8, 1e6 * (h + performance.now())) }, "runtime.walltime1": b => { const c = new Date().getTime(); a(b + 8, c / 1e3), this.mem.setInt32(b + 16, 1e6 * (c % 1e3), !0) }, "runtime.scheduleTimeoutEvent": a => { const c = this._nextCallbackTimeoutID; this._nextCallbackTimeoutID++, this._scheduledTimeouts.set(c, setTimeout(() => { for (this._resume(); this._scheduledTimeouts.has(c);)console.warn("scheduleTimeoutEvent: missed timeout event"), this._resume() }, b(a + 8) + 1)), this.mem.setInt32(a + 16, c, !0) }, "runtime.clearTimeoutEvent": a => { const b = this.mem.getInt32(a + 8, !0); clearTimeout(this._scheduledTimeouts.get(b)), this._scheduledTimeouts.delete(b) }, "runtime.getRandomData": a => { crypto.getRandomValues(e(a + 8)) }, "syscall/js.finalizeRef": a => { const b = this.mem.getUint32(a + 8, !0); if (this._goRefCounts[b]--, 0 === this._goRefCounts[b]) { const a = this._values[b]; this._values[b] = null, this._ids.delete(a), this._idPool.push(b) } }, "syscall/js.stringVal": a => { d(a + 24, g(a + 8)) }, "syscall/js.valueGet": a => { const b = Reflect.get(c(a + 8), g(a + 16)); a = this._inst.exports.getsp(), d(a + 32, b) }, "syscall/js.valueSet": a => { Reflect.set(c(a + 8), g(a + 16), c(a + 32)) }, "syscall/js.valueDelete": a => { Reflect.deleteProperty(c(a + 8), g(a + 16)) }, "syscall/js.valueIndex": a => { d(a + 24, Reflect.get(c(a + 8), b(a + 16))) }, "syscall/js.valueSetIndex": a => { Reflect.set(c(a + 8), b(a + 16), c(a + 24)) }, "syscall/js.valueCall": a => { try { const b = c(a + 8), e = Reflect.get(b, g(a + 16)), h = f(a + 32), i = Reflect.apply(e, b, h); a = this._inst.exports.getsp(), d(a + 56, i), this.mem.setUint8(a + 64, 1) } catch (b) { d(a + 56, b), this.mem.setUint8(a + 64, 0) } }, "syscall/js.valueInvoke": a => { try { const b = c(a + 8), e = f(a + 16), g = Reflect.apply(b, void 0, e); a = this._inst.exports.getsp(), d(a + 40, g), this.mem.setUint8(a + 48, 1) } catch (b) { d(a + 40, b), this.mem.setUint8(a + 48, 0) } }, "syscall/js.valueNew": a => { try { const b = c(a + 8), e = f(a + 16), g = Reflect.construct(b, e); a = this._inst.exports.getsp(), d(a + 40, g), this.mem.setUint8(a + 48, 1) } catch (b) { d(a + 40, b), this.mem.setUint8(a + 48, 0) } }, "syscall/js.valueLength": b => { a(b + 16, parseInt(c(b + 8).length)) }, "syscall/js.valuePrepareString": b => { const e = encoder.encode(c(b + 8) + ""); d(b + 16, e), a(b + 24, e.length) }, "syscall/js.valueLoadString": a => { const b = c(a + 8); e(a + 16).set(b) }, "syscall/js.valueInstanceOf": a => { this.mem.setUint8(a + 24, c(a + 8) instanceof c(a + 16)) }, "syscall/js.copyBytesToGo": b => { const d = e(b + 8), f = c(b + 32); if (!(f instanceof Uint8Array)) return void this.mem.setUint8(b + 48, 0); const g = f.subarray(0, d.length); d.set(g), a(b + 40, g.length), this.mem.setUint8(b + 48, 1) }, "syscall/js.copyBytesToJS": b => { const d = c(b + 8), f = e(b + 16); if (!(d instanceof Uint8Array)) return void this.mem.setUint8(b + 48, 0); const g = f.subarray(0, d.length); d.set(g), a(b + 40, g.length), this.mem.setUint8(b + 48, 1) }, debug: a => { console.log(a) } } } } async run(a) { this._inst = a, this.mem = new DataView(this._inst.exports.mem.buffer), this._values = [NaN, 0, null, !0, !1, window, this], this._goRefCounts = [], this._ids = new Map, this._idPool = [], this.exited = !1; let b = 4096; const c = a => { const c = b, d = encoder.encode(a + "\0"); return new Uint8Array(this.mem.buffer, b, d.length).set(d), b += d.length, 0 != b % 8 && (b += 8 - b % 8), c }, d = this.argv.length, e = []; this.argv.forEach(a => { e.push(c(a)) }), e.push(0); const f = Object.keys(this.env).sort(); f.forEach(a => { e.push(c(a + "=" + this.env[a])) }), e.push(0); const g = b; e.forEach(a => { this.mem.setUint32(b, a, !0), this.mem.setUint32(b + 4, 0, !0), b += 8 }), this._inst.exports.run(d, g), this.exited && this._resolveExitPromise(), await this._exitPromise } _resume() { if (this.exited) throw new Error("Go program has already exited"); this._inst.exports.resume(), this.exited && this._resolveExitPromise() } _makeFuncWrapper(a) { const b = this; return function () { const c = { id: a, this: this, args: arguments }; return b._pendingEvent = c, b._resume(), c.result } } } const go = new Go; WebAssembly.instantiateStreaming(fetch("/assets/main.wasm"), go.importObject).then(a => go.run(a.instance)).catch(a => console.error("could not load wasm", a));`),
|
|
290
|
+
// ),
|
|
291
|
+
// app.Body(elems[0]),
|
|
292
|
+
// ).Html(page)
|
|
293
|
+
// return page
|
|
294
|
+
// }
|
|
295
|
+
|
|
296
|
+
// func serve(wd string, isDev bool) {
|
|
297
|
+
// assetsFileServer := http.FileServer(pkger.Dir(filepath.Join(wd, "assets")))
|
|
298
|
+
// buildFileServer := http.FileServer(pkger.Dir(filepath.Join(wd, "build")))
|
|
299
|
+
// http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
300
|
+
// println("path: " + r.URL.Path)
|
|
301
|
+
// if routeInfo, ok := routesMap[r.URL.Path]; ok {
|
|
302
|
+
// wasmPath := "/build/" + filepath.Base(wd) + ".wasm"
|
|
303
|
+
// page := CreatePage(routeInfo.RenderFunc(app.NewRenderContext()), wasmPath)
|
|
304
|
+
// w.Header().Set("Content-Length", strconv.Itoa(page.Len()))
|
|
305
|
+
// w.Header().Set("Content-Type", "text/html")
|
|
306
|
+
// w.WriteHeader(http.StatusOK)
|
|
307
|
+
// w.Write(page.Bytes())
|
|
308
|
+
// } else if strings.Contains(r.URL.Path, "/build") {
|
|
309
|
+
// r.URL.Path = strings.Replace(r.URL.Path, "/build", "", 1)
|
|
310
|
+
// buildFileServer.ServeHTTP(w, r)
|
|
311
|
+
// } else {
|
|
312
|
+
// r.URL.Path = strings.Replace(r.URL.Path, "/assets", "", 1)
|
|
313
|
+
// assetsFileServer.ServeHTTP(w, r)
|
|
314
|
+
// }
|
|
315
|
+
// })
|
|
316
|
+
// if isDev {
|
|
317
|
+
// println("Serving on HTTP port: 1234")
|
|
318
|
+
// http.ListenAndServe(":1234", nil)
|
|
319
|
+
// } else {
|
|
320
|
+
// println("algnhsa serving default mux")
|
|
321
|
+
// algnhsa.ListenAndServe(http.DefaultServeMux, nil)
|
|
322
|
+
// }
|
|
323
|
+
// }
|
|
324
|
+
|
|
325
|
+
// func buildAll(basePath string) {
|
|
326
|
+
// loadRoutes(nil, basePath, true)
|
|
327
|
+
// writeMainFile(basePath)
|
|
328
|
+
// buildWasm(basePath)
|
|
329
|
+
// soPath, err := buildSo(basePath)
|
|
330
|
+
// if err != nil {
|
|
331
|
+
// println("could not build")
|
|
332
|
+
// panic(err)
|
|
333
|
+
// }
|
|
334
|
+
// p, err := plugin.Open(soPath)
|
|
335
|
+
// if err != nil {
|
|
336
|
+
// println("could not load so plugin")
|
|
337
|
+
// panic(err)
|
|
338
|
+
// }
|
|
339
|
+
// // fmt.Printf("%+v\n", p)
|
|
340
|
+
// loadRoutes(p, basePath, false)
|
|
341
|
+
// }
|
|
342
|
+
|
|
343
|
+
// func Watch(isDev bool) {
|
|
344
|
+
// wd, err := os.Getwd()
|
|
345
|
+
// if err != nil {
|
|
346
|
+
// fmt.Printf("could not get wd")
|
|
347
|
+
// return
|
|
348
|
+
// }
|
|
349
|
+
// if isDev {
|
|
350
|
+
// basePath := filepath.Join(wd, "pages")
|
|
351
|
+
// buildAll(basePath)
|
|
352
|
+
// watcher := MustRegisterWatcher()
|
|
353
|
+
// go watcher.Watch()
|
|
354
|
+
// go serve(wd, isDev)
|
|
355
|
+
// for file := range watcher.update {
|
|
356
|
+
// println("changed: " + file)
|
|
357
|
+
// buildAll(basePath)
|
|
358
|
+
// }
|
|
359
|
+
// } else {
|
|
360
|
+
// serve(wd, isDev)
|
|
361
|
+
// }
|
|
362
|
+
// }
|
example/{pages/about.go → about.go}
RENAMED
|
File without changes
|
example/{config.css → assets/config.css}
RENAMED
|
File without changes
|
example/assets/manifest.json
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"short_name": "{{.ShortName}}",
|
|
3
|
-
"name": "{{.Name}}",
|
|
4
|
-
"icons": [
|
|
5
|
-
{
|
|
6
|
-
"src": "{{.DefaultIcon}}",
|
|
7
|
-
"type": "image/png",
|
|
8
|
-
"sizes": "192x192"
|
|
9
|
-
},
|
|
10
|
-
{
|
|
11
|
-
"src": "{{.LargeIcon}}",
|
|
12
|
-
"type": "image/png",
|
|
13
|
-
"sizes": "512x512"
|
|
14
|
-
}
|
|
15
|
-
],
|
|
16
|
-
"scope": "{{.Scope}}",
|
|
17
|
-
"start_url": "{{.StartURL}}",
|
|
18
|
-
"background_color": "{{.BackgroundColor}}",
|
|
19
|
-
"display": "standalone",
|
|
20
|
-
"theme_color": "{{.ThemeColor}}"
|
|
21
|
-
}
|
example/{pages/clock.go → clock.go}
RENAMED
|
File without changes
|
example/{pages/container.go → container.go}
RENAMED
|
File without changes
|
example/{pages/index.go → index.go}
RENAMED
|
File without changes
|
example/main.go
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
package main
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
-
"github.com/pyros2097/wapp
|
|
4
|
+
app "github.com/pyros2097/wapp"
|
|
5
5
|
)
|
|
6
6
|
|
|
7
|
+
var routes = map[string]app.RenderFunc{
|
|
8
|
+
"/about": About,
|
|
9
|
+
"/clock": Clock,
|
|
10
|
+
"/container": Container,
|
|
11
|
+
"/": Index,
|
|
12
|
+
}
|
|
13
|
+
|
|
7
14
|
func main() {
|
|
8
|
-
|
|
15
|
+
app.Run(false, routes)
|
|
9
16
|
}
|
example/makefile
CHANGED
|
@@ -1,2 +1,11 @@
|
|
|
1
|
+
run:
|
|
2
|
+
go run *.go
|
|
3
|
+
|
|
4
|
+
build:
|
|
5
|
+
go build
|
|
6
|
+
|
|
7
|
+
build-wasm:
|
|
8
|
+
GOOS=js GOARCH=wasm go build -o assets/main.wasm
|
|
9
|
+
|
|
1
10
|
build-css:
|
|
2
|
-
npx tailwindcss-cli@latest build config.css -o assets/styles.css
|
|
11
|
+
npx tailwindcss-cli@latest build assets/config.css -o assets/styles.css
|
example/pages/main.go
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
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
|
-
}
|
example/readme.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
An example to demostrate the wapp framework
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
1. Compile wasm frontend,
|
|
6
|
+
`GOOS=js GOARCH=wasm go build -o assets/main.wasm`
|
|
6
7
|
|
|
8
|
+
2. Run the server,
|
|
7
|
-
`go run
|
|
9
|
+
`go run *.go`
|
|
10
|
+
|
|
11
|
+
These commands are also available in the makefile for convenience
|
example/samconfig.toml
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
version = 0.1
|
|
2
|
+
[default]
|
|
3
|
+
[default.deploy]
|
|
4
|
+
[default.deploy.parameters]
|
|
5
|
+
stack_name = "go-app"
|
|
6
|
+
s3_bucket = "aws-sam-cli-managed-default-samclisourcebucket-grb3hle00qk9"
|
|
7
|
+
s3_prefix = "go-app"
|
|
8
|
+
region = "us-east-1"
|
|
9
|
+
capabilities = "CAPABILITY_IAM"
|
example/template.yml
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
AWSTemplateFormatVersion: 2010-09-09
|
|
2
|
+
Outputs:
|
|
3
|
+
ApiUrl:
|
|
4
|
+
Export:
|
|
5
|
+
Name: ApiUrl
|
|
6
|
+
Value:
|
|
7
|
+
Fn::Sub: https://${HttpApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/Prod/
|
|
8
|
+
Resources:
|
|
9
|
+
HttpApi:
|
|
10
|
+
Properties:
|
|
11
|
+
AccessLogSettings:
|
|
12
|
+
DestinationArn:
|
|
13
|
+
Fn::GetAtt:
|
|
14
|
+
- HttpApiAccessLogs
|
|
15
|
+
- Arn
|
|
16
|
+
Format:
|
|
17
|
+
'{"requestId":"$context.requestId", "ip": "$context.identity.sourceIp",
|
|
18
|
+
"caller":"$context.identity.caller", "user":"$context.identity.user", "requestTime":"$context.requestTime","routeKey":"$context.routeKey",
|
|
19
|
+
"status":"$context.status", "error": "$context.integrationErrorMessage"}'
|
|
20
|
+
StageName: Prod
|
|
21
|
+
Type: AWS::Serverless::HttpApi
|
|
22
|
+
HttpApiAccessLogs:
|
|
23
|
+
Properties:
|
|
24
|
+
LogGroupName: /aws/http/py-app
|
|
25
|
+
RetentionInDays: 365
|
|
26
|
+
Type: AWS::Logs::LogGroup
|
|
27
|
+
RouteIndex:
|
|
28
|
+
Properties:
|
|
29
|
+
Handler: ./example
|
|
30
|
+
Events:
|
|
31
|
+
ApiEvent:
|
|
32
|
+
Properties:
|
|
33
|
+
ApiId:
|
|
34
|
+
Ref: HttpApi
|
|
35
|
+
Method: GET
|
|
36
|
+
Path: $default
|
|
37
|
+
PayloadFormatVersion: '1.0'
|
|
38
|
+
Type: HttpApi
|
|
39
|
+
MemorySize: 128
|
|
40
|
+
Runtime: go1.x
|
|
41
|
+
Timeout: 30
|
|
42
|
+
Type: AWS::Serverless::Function
|
|
43
|
+
Transform: AWS::Serverless-2016-10-31
|
go.mod
CHANGED
|
@@ -3,7 +3,9 @@ module github.com/pyros2097/wapp
|
|
|
3
3
|
go 1.15
|
|
4
4
|
|
|
5
5
|
require (
|
|
6
|
+
github.com/akrylysov/algnhsa v0.12.1
|
|
7
|
+
github.com/aws/aws-lambda-go v1.20.0
|
|
6
|
-
github.com/
|
|
8
|
+
github.com/awslabs/goformation/v4 v4.15.6
|
|
7
9
|
github.com/markbates/pkger v0.17.1
|
|
8
10
|
github.com/stretchr/testify v1.6.1
|
|
9
11
|
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd // indirect
|
go.sum
CHANGED
|
@@ -1,10 +1,35 @@
|
|
|
1
|
+
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
|
2
|
+
github.com/akrylysov/algnhsa v0.12.1 h1:A9Ojt4hZrL77mhBc3qGO3Sn9reyf+tvM3DmR0SfXguc=
|
|
3
|
+
github.com/akrylysov/algnhsa v0.12.1/go.mod h1:xAcJ/X8DV+81e+dUjIoB/r5CbISrSXV9//leoMDHcdk=
|
|
4
|
+
github.com/aws/aws-lambda-go v1.9.0/go.mod h1:zUsUQhAUjYzR8AuduJPCfhBuKWUaDbQiPOG+ouzmE1A=
|
|
5
|
+
github.com/aws/aws-lambda-go v1.20.0 h1:ZSweJx/Hy9BoIDXKBEh16vbHH0t0dehnF8MKpMiOWc0=
|
|
6
|
+
github.com/aws/aws-lambda-go v1.20.0/go.mod h1:jJmlefzPfGnckuHdXX7/80O3BvUUi12XOkbv4w9SGLU=
|
|
7
|
+
github.com/awslabs/goformation v1.4.1 h1:jws9kTrcI53Hq2COJAy50uAhgxB5N/Enb9Gmclr/MP4=
|
|
8
|
+
github.com/awslabs/goformation/v4 v4.15.6 h1:9F0MbtJVSMkuI19G6Fm+qHc1nqScHcOIf+3YRRv+Ohc=
|
|
9
|
+
github.com/awslabs/goformation/v4 v4.15.6/go.mod h1:wB5lKZf1J0MYH1Lt4B9w3opqz0uIjP7MMCAcib3QkwA=
|
|
10
|
+
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
|
11
|
+
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
|
1
12
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
2
13
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
3
14
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
4
15
|
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
|
5
16
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
|
17
|
+
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
|
6
18
|
github.com/gobuffalo/here v0.6.0 h1:hYrd0a6gDmWxBM4TnrGw8mQg24iSVoIkHEk7FodQcBI=
|
|
7
19
|
github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM=
|
|
20
|
+
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
21
|
+
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
|
22
|
+
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
|
23
|
+
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
|
24
|
+
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
|
25
|
+
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
|
26
|
+
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
27
|
+
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
28
|
+
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
29
|
+
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
30
|
+
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
|
31
|
+
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
|
|
32
|
+
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
|
8
33
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
|
9
34
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
|
10
35
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
@@ -12,20 +37,70 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|
|
12
37
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|
13
38
|
github.com/markbates/pkger v0.17.1 h1:/MKEtWqtc0mZvu9OinB9UzVN9iYCwLWuyUv4Bw+PCno=
|
|
14
39
|
github.com/markbates/pkger v0.17.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI=
|
|
40
|
+
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
|
41
|
+
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
|
42
|
+
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
|
43
|
+
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
|
44
|
+
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
|
45
|
+
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
|
46
|
+
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
|
|
15
47
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
16
48
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
49
|
+
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
|
50
|
+
github.com/sanathkr/go-yaml v0.0.0-20170819195128-ed9d249f429b h1:jUK33OXuZP/l6babJtnLo1qsGvq6G9so9KMflGAm4YA=
|
|
51
|
+
github.com/sanathkr/go-yaml v0.0.0-20170819195128-ed9d249f429b/go.mod h1:8458kAagoME2+LN5//WxE71ysZ3B7r22fdgb7qVmXSY=
|
|
52
|
+
github.com/sanathkr/yaml v0.0.0-20170819201035-0056894fa522 h1:fOCp11H0yuyAt2wqlbJtbyPzSgaxHTv8uN1pMpkG1t8=
|
|
53
|
+
github.com/sanathkr/yaml v0.0.0-20170819201035-0056894fa522/go.mod h1:tQTYKOQgxoH3v6dEmdHiz4JG+nbxWwM5fgPQUpSZqVQ=
|
|
54
|
+
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
|
17
55
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
56
|
+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
18
57
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
|
19
58
|
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
|
20
59
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
60
|
+
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
|
61
|
+
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
|
62
|
+
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
|
63
|
+
github.com/xeipuuv/gojsonschema v0.0.0-20181112162635-ac52e6811b56/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
|
|
64
|
+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
65
|
+
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
66
|
+
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
67
|
+
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
68
|
+
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
|
69
|
+
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
70
|
+
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
71
|
+
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
72
|
+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
73
|
+
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
74
|
+
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
75
|
+
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
76
|
+
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
77
|
+
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
78
|
+
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
79
|
+
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
21
80
|
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY=
|
|
22
81
|
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
82
|
+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
83
|
+
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|
84
|
+
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
85
|
+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
86
|
+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
87
|
+
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
|
88
|
+
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
|
89
|
+
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
|
90
|
+
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
|
91
|
+
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
|
92
|
+
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
23
93
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
24
94
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
|
25
95
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
26
96
|
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
|
27
97
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
|
98
|
+
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
|
28
99
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
100
|
+
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
29
101
|
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
102
|
+
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
30
103
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
|
31
104
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
105
|
+
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
|
106
|
+
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
utils.go
CHANGED
|
@@ -2,6 +2,7 @@ package app
|
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
4
|
"io"
|
|
5
|
+
"regexp"
|
|
5
6
|
"unsafe"
|
|
6
7
|
|
|
7
8
|
"github.com/pyros2097/wapp/js"
|
|
@@ -48,3 +49,33 @@ func btos(b []byte) string {
|
|
|
48
49
|
func stob(s string) []byte {
|
|
49
50
|
return *(*[]byte)(unsafe.Pointer(&s))
|
|
50
51
|
}
|
|
52
|
+
|
|
53
|
+
func matchPath(k, p string) bool {
|
|
54
|
+
validRoute := regexp.MustCompile(k)
|
|
55
|
+
if validRoute.MatchString(p) {
|
|
56
|
+
return true
|
|
57
|
+
}
|
|
58
|
+
return false
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
func MatchRoute(routes map[string]RenderFunc, path string) RenderFunc {
|
|
62
|
+
for key, renderFn := range routes {
|
|
63
|
+
if matchPath(key, path) {
|
|
64
|
+
return renderFn
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
notFound, ok := routes["/notfound"]
|
|
68
|
+
if ok {
|
|
69
|
+
return notFound
|
|
70
|
+
}
|
|
71
|
+
return func(c *RenderContext) UI {
|
|
72
|
+
return Col(
|
|
73
|
+
Row(
|
|
74
|
+
"This is the default 404 - Not Found Route handler",
|
|
75
|
+
),
|
|
76
|
+
Row(
|
|
77
|
+
"Create a notfound.go file and add a func NotFound(c *RenderContext) UI {} to override it",
|
|
78
|
+
),
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
}
|
utils_test.go
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
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
|
+
}
|