~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.
50265f7e
—
pyros2097 5 years ago
improve api again
- app_nowasm.go +14 -9
- cli/cli.go +0 -362
- example/main.go +6 -1
app_nowasm.go
CHANGED
|
@@ -14,7 +14,14 @@ import (
|
|
|
14
14
|
"github.com/markbates/pkger"
|
|
15
15
|
)
|
|
16
16
|
|
|
17
|
+
type AppInfo struct {
|
|
18
|
+
Title string
|
|
19
|
+
Description string
|
|
20
|
+
Author string
|
|
21
|
+
Keywords string
|
|
22
|
+
}
|
|
23
|
+
|
|
17
|
-
func Run(routes map[string]RenderFunc) {
|
|
24
|
+
func Run(info AppInfo, routes map[string]RenderFunc) {
|
|
18
25
|
isLambda := os.Getenv("_LAMBDA_SERVER_PORT") != ""
|
|
19
26
|
wd, err := os.Getwd()
|
|
20
27
|
if err != nil {
|
|
@@ -27,7 +34,7 @@ func Run(routes map[string]RenderFunc) {
|
|
|
27
34
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
28
35
|
println("route: " + r.URL.Path)
|
|
29
36
|
renderFunc := MatchRoute(routes, r.URL.Path)
|
|
30
|
-
page := createPage(
|
|
37
|
+
page := createPage(info, renderFunc(NewRenderContext()))
|
|
31
38
|
w.Header().Set("Content-Length", strconv.Itoa(page.Len()))
|
|
32
39
|
w.Header().Set("Content-Type", "text/html")
|
|
33
40
|
w.WriteHeader(http.StatusOK)
|
|
@@ -48,7 +55,7 @@ func Reload() {
|
|
|
48
55
|
panic("wasm required")
|
|
49
56
|
}
|
|
50
57
|
|
|
51
|
-
func createPage(
|
|
58
|
+
func createPage(info AppInfo, ui UI) *bytes.Buffer {
|
|
52
59
|
isLambda := os.Getenv("_LAMBDA_SERVER_PORT") != ""
|
|
53
60
|
page := bytes.NewBuffer(nil)
|
|
54
61
|
page.WriteString("<!DOCTYPE html>\n")
|
|
@@ -59,16 +66,14 @@ func createPage(title string, ui UI) *bytes.Buffer {
|
|
|
59
66
|
}
|
|
60
67
|
Html(
|
|
61
68
|
Head(
|
|
62
|
-
Title(
|
|
69
|
+
Title(info.Title),
|
|
63
|
-
Meta("author", "pyros2097"),
|
|
64
|
-
Meta("description",
|
|
70
|
+
Meta("description", info.Description),
|
|
71
|
+
Meta("author", info.Author),
|
|
65
|
-
Meta("keywords",
|
|
72
|
+
Meta("keywords", info.Keywords),
|
|
66
|
-
Meta("theme-color", ""),
|
|
67
73
|
Meta("viewport", "width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0, viewport-fit=cover"),
|
|
68
74
|
Link("icon", basePath+"icon.png"),
|
|
69
75
|
Link("apple-touch-icon", basePath+"icon.png"),
|
|
70
76
|
Link("stylesheet", basePath+"styles.css"),
|
|
71
|
-
Link("manifest", "manifest"),
|
|
72
77
|
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("`+basePath+`main.wasm`+`"), go.importObject).then(a => go.run(a.instance)).catch(a => console.error("could not load wasm", a));`),
|
|
73
78
|
),
|
|
74
79
|
Body(elems[0]),
|
cli/cli.go
DELETED
|
@@ -1,362 +0,0 @@
|
|
|
1
|
-
package cli
|
|
2
|
-
|
|
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/main.go
CHANGED
|
@@ -12,5 +12,10 @@ var routes = map[string]RenderFunc{
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
func main() {
|
|
15
|
+
Run(AppInfo{
|
|
16
|
+
Title: "wapp-example",
|
|
17
|
+
Description: "wapp is a framework",
|
|
18
|
+
Author: "pyros2097",
|
|
19
|
+
Keywords: "wapp,wapp-example,golang,framework,frontend,ui,wasm,isomorphic",
|
|
15
|
-
|
|
20
|
+
}, routes)
|
|
16
21
|
}
|