~repos /gromer

#golang#htmx#ssr

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
Files changed (3) hide show
  1. app_nowasm.go +14 -9
  2. cli/cli.go +0 -362
  3. 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("wapp-example", renderFunc(NewRenderContext()))
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(title string, ui UI) *bytes.Buffer {
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(title),
69
+ Title(info.Title),
63
- Meta("author", "pyros2097"),
64
- Meta("description", "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
- Run(routes)
20
+ }, routes)
16
21
  }