├── README.md ├── examples ├── commits │ ├── app.go │ ├── index.html │ ├── vue.js │ └── wasm_exec.js └── todolist │ ├── data.go │ ├── index.html │ ├── item.go │ ├── todolist.go │ ├── vue.js │ ├── vuego_data.go │ ├── vuego_item.go │ └── wasm_exec.js ├── gen └── gen.go ├── methods.go ├── vuego.go └── xhr └── xhr.go /README.md: -------------------------------------------------------------------------------- 1 | Vuego Framework 2 | =============== 3 | 4 | This is a WASM-based Vue.js wrapper written in Go. 5 | It uses the Go 1.11+ Web Assembly compiler to compile the wrapper. 6 | 7 | The first version of Vuego aims to be a thin Vue.js wrapper which allows writing frontend in the Go programming language. 8 | 9 | The immediate benefit of Vuego is that it allows Vue.js users to deliver their Web applications in binary (Web Assembly) forms. 10 | So your companies would have a better way to protect IPs for Vue.js applications. -------------------------------------------------------------------------------- /examples/commits/app.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strings" 7 | "syscall/js" 8 | 9 | . "github.com/chanwit/vuego" 10 | "github.com/chanwit/vuego/xhr" 11 | ) 12 | 13 | const ( 14 | apiURL = "https://api.github.com/repos/vuejs/vue/commits?per_page=3&sha=" 15 | ) 16 | 17 | type Commits struct { 18 | js.Value 19 | Branches []string `json:"branches"` 20 | CurrentBranch string `json:"currentBranch"` 21 | Commits []Commit `json:"commits"` 22 | } 23 | 24 | type Commit = map[string]interface{} 25 | 26 | func (this *Commits) FetchData() { 27 | req := xhr.New() 28 | req.Open("GET", apiURL + this.CurrentBranch) 29 | req.OnLoad(func(args []js.Value) { 30 | this.Commits = []Commit{} 31 | json.Unmarshal([]byte(req.GetResponseText()), &this.Commits) 32 | fmt.Println(this.Commits[0]["html_url"]) 33 | arrayConstructor := js.Global().Get("Array") 34 | a := arrayConstructor.New(len(this.Commits)) 35 | for i, s := range this.Commits { 36 | a.SetIndex(i, js.ValueOf(s)) 37 | } 38 | this.Value.Set("commits", a) 39 | }) 40 | req.Send() 41 | } 42 | 43 | func main() { 44 | 45 | New(&Vue{ 46 | El: "#demo", 47 | Data: &Commits{ 48 | Branches: []string{"master", "dev"}, 49 | CurrentBranch: "master", 50 | Commits: nil, 51 | }, 52 | Created: func(this Any, args []js.Value) { 53 | this.(*Commits).FetchData() 54 | }, 55 | Watch: &Watch{ 56 | "currentBranch": "fetchData", 57 | }, 58 | Filters: &Filters{ 59 | "truncate": func(v Any) Any { 60 | s := v.(js.Value).String() 61 | return strings.TrimSuffix(s, "\n") 62 | }, 63 | "formatDate": func(v Any) Any { 64 | return strings.Replace(v.(js.Value).String(), "Z", " ", -1) 65 | }, 66 | }, 67 | Methods: &Methods{ 68 | "fetchData": func(this Any) { 69 | this.(*Commits).FetchData() 70 | }, 71 | }, 72 | }) 73 | 74 | select { } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /examples/commits/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Vue.js github commits example 5 | 24 | 25 | 26 | 32 | 33 | 34 |
35 |

Latest Vue.js Commits

36 | 44 |

vuejs/vue@{{ currentBranch }}

45 | 53 |
54 | 55 | -------------------------------------------------------------------------------- /examples/commits/vue.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Vue.js v2.5.17 3 | * (c) 2014-2018 Evan You 4 | * Released under the MIT License. 5 | */ 6 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Vue=t()}(this,function(){"use strict";var y=Object.freeze({});function M(e){return null==e}function D(e){return null!=e}function S(e){return!0===e}function T(e){return"string"==typeof e||"number"==typeof e||"symbol"==typeof e||"boolean"==typeof e}function P(e){return null!==e&&"object"==typeof e}var r=Object.prototype.toString;function l(e){return"[object Object]"===r.call(e)}function i(e){var t=parseFloat(String(e));return 0<=t&&Math.floor(t)===t&&isFinite(e)}function t(e){return null==e?"":"object"==typeof e?JSON.stringify(e,null,2):String(e)}function F(e){var t=parseFloat(e);return isNaN(t)?e:t}function s(e,t){for(var n=Object.create(null),r=e.split(","),i=0;ie.id;)n--;bt.splice(n+1,0,e)}else bt.push(e);Ct||(Ct=!0,Ze(At))}}(this)},St.prototype.run=function(){if(this.active){var e=this.get();if(e!==this.value||P(e)||this.deep){var t=this.value;if(this.value=e,this.user)try{this.cb.call(this.vm,e,t)}catch(e){Fe(e,this.vm,'callback for watcher "'+this.expression+'"')}else this.cb.call(this.vm,e,t)}}},St.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},St.prototype.depend=function(){for(var e=this.deps.length;e--;)this.deps[e].depend()},St.prototype.teardown=function(){if(this.active){this.vm._isBeingDestroyed||f(this.vm._watchers,this);for(var e=this.deps.length;e--;)this.deps[e].removeSub(this);this.active=!1}};var Tt={enumerable:!0,configurable:!0,get:$,set:$};function Et(e,t,n){Tt.get=function(){return this[t][n]},Tt.set=function(e){this[t][n]=e},Object.defineProperty(e,n,Tt)}function jt(e){e._watchers=[];var t=e.$options;t.props&&function(n,r){var i=n.$options.propsData||{},o=n._props={},a=n.$options._propKeys=[];n.$parent&&ge(!1);var e=function(e){a.push(e);var t=Ie(e,r,i,n);Ce(o,e,t),e in n||Et(n,"_props",e)};for(var t in r)e(t);ge(!0)}(e,t.props),t.methods&&function(e,t){e.$options.props;for(var n in t)e[n]=null==t[n]?$:v(t[n],e)}(e,t.methods),t.data?function(e){var t=e.$options.data;l(t=e._data="function"==typeof t?function(e,t){se();try{return e.call(t,t)}catch(e){return Fe(e,t,"data()"),{}}finally{ce()}}(t,e):t||{})||(t={});var n=Object.keys(t),r=e.$options.props,i=(e.$options.methods,n.length);for(;i--;){var o=n[i];r&&p(r,o)||(void 0,36!==(a=(o+"").charCodeAt(0))&&95!==a&&Et(e,"_data",o))}var a;we(t,!0)}(e):we(e._data={},!0),t.computed&&function(e,t){var n=e._computedWatchers=Object.create(null),r=Y();for(var i in t){var o=t[i],a="function"==typeof o?o:o.get;r||(n[i]=new St(e,a||$,$,Nt)),i in e||Lt(e,i,o)}}(e,t.computed),t.watch&&t.watch!==G&&function(e,t){for(var n in t){var r=t[n];if(Array.isArray(r))for(var i=0;iparseInt(this.max)&&bn(a,s[0],s,this._vnode)),t.data.keepAlive=!0}return t||e&&e[0]}}};$n=hn,Cn={get:function(){return j}},Object.defineProperty($n,"config",Cn),$n.util={warn:re,extend:m,mergeOptions:Ne,defineReactive:Ce},$n.set=xe,$n.delete=ke,$n.nextTick=Ze,$n.options=Object.create(null),k.forEach(function(e){$n.options[e+"s"]=Object.create(null)}),m(($n.options._base=$n).options.components,kn),$n.use=function(e){var t=this._installedPlugins||(this._installedPlugins=[]);if(-1=a&&l()};setTimeout(function(){c\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,oo="[a-zA-Z_][\\w\\-\\.]*",ao="((?:"+oo+"\\:)?"+oo+")",so=new RegExp("^<"+ao),co=/^\s*(\/?)>/,lo=new RegExp("^<\\/"+ao+"[^>]*>"),uo=/^]+>/i,fo=/^",""":'"',"&":"&"," ":"\n"," ":"\t"},go=/&(?:lt|gt|quot|amp);/g,_o=/&(?:lt|gt|quot|amp|#10|#9);/g,bo=s("pre,textarea",!0),$o=function(e,t){return e&&bo(e)&&"\n"===t[0]};var wo,Co,xo,ko,Ao,Oo,So,To,Eo=/^@|^v-on:/,jo=/^v-|^@|^:/,No=/([^]*?)\s+(?:in|of)\s+([^]*)/,Lo=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,Io=/^\(|\)$/g,Mo=/:(.*)$/,Do=/^:|^v-bind:/,Po=/\.[^.]+/g,Fo=e(eo);function Ro(e,t,n){return{type:1,tag:e,attrsList:t,attrsMap:function(e){for(var t={},n=0,r=e.length;n]*>)","i")),n=i.replace(t,function(e,t,n){return r=n.length,ho(o)||"noscript"===o||(t=t.replace(//g,"$1").replace(//g,"$1")),$o(o,t)&&(t=t.slice(1)),d.chars&&d.chars(t),""});a+=i.length-n.length,i=n,A(o,a-r,a)}else{var s=i.indexOf("<");if(0===s){if(fo.test(i)){var c=i.indexOf("--\x3e");if(0<=c){d.shouldKeepComment&&d.comment(i.substring(4,c)),C(c+3);continue}}if(po.test(i)){var l=i.indexOf("]>");if(0<=l){C(l+2);continue}}var u=i.match(uo);if(u){C(u[0].length);continue}var f=i.match(lo);if(f){var p=a;C(f[0].length),A(f[1],p,a);continue}var _=x();if(_){k(_),$o(v,i)&&C(1);continue}}var b=void 0,$=void 0,w=void 0;if(0<=s){for($=i.slice(s);!(lo.test($)||so.test($)||fo.test($)||po.test($)||(w=$.indexOf("<",1))<0);)s+=w,$=i.slice(s);b=i.substring(0,s),C(s)}s<0&&(b=i,i=""),d.chars&&b&&d.chars(b)}if(i===e){d.chars&&d.chars(i);break}}function C(e){a+=e,i=i.substring(e)}function x(){var e=i.match(so);if(e){var t,n,r={tagName:e[1],attrs:[],start:a};for(C(e[0].length);!(t=i.match(co))&&(n=i.match(io));)C(n[0].length),r.attrs.push(n);if(t)return r.unarySlash=t[1],C(t[0].length),r.end=a,r}}function k(e){var t=e.tagName,n=e.unarySlash;m&&("p"===v&&ro(t)&&A(v),g(t)&&v===t&&A(t));for(var r,i,o,a=y(t)||!!n,s=e.attrs.length,c=new Array(s),l=0;l-1"+("true"===d?":("+l+")":":_q("+l+","+d+")")),Ar(c,"change","var $$a="+l+",$$el=$event.target,$$c=$$el.checked?("+d+"):("+v+");if(Array.isArray($$a)){var $$v="+(f?"_n("+p+")":p)+",$$i=_i($$a,$$v);if($$el.checked){$$i<0&&("+Er(l,"$$a.concat([$$v])")+")}else{$$i>-1&&("+Er(l,"$$a.slice(0,$$i).concat($$a.slice($$i+1))")+")}}else{"+Er(l,"$$c")+"}",null,!0);else if("input"===$&&"radio"===w)r=e,i=_,a=(o=b)&&o.number,s=Or(r,"value")||"null",Cr(r,"checked","_q("+i+","+(s=a?"_n("+s+")":s)+")"),Ar(r,"change",Er(i,s),null,!0);else if("input"===$||"textarea"===$)!function(e,t,n){var r=e.attrsMap.type,i=n||{},o=i.lazy,a=i.number,s=i.trim,c=!o&&"range"!==r,l=o?"change":"range"===r?Pr:"input",u="$event.target.value";s&&(u="$event.target.value.trim()"),a&&(u="_n("+u+")");var f=Er(t,u);c&&(f="if($event.target.composing)return;"+f),Cr(e,"value","("+t+")"),Ar(e,l,f,null,!0),(s||a)&&Ar(e,"blur","$forceUpdate()")}(e,_,b);else if(!j.isReservedTag($))return Tr(e,_,b),!1;return!0},text:function(e,t){t.value&&Cr(e,"textContent","_s("+t.value+")")},html:function(e,t){t.value&&Cr(e,"innerHTML","_s("+t.value+")")}},isPreTag:function(e){return"pre"===e},isUnaryTag:to,mustUseProp:Sn,canBeLeftOpenTag:no,isReservedTag:Un,getTagNamespace:Vn,staticKeys:(Go=Wo,Go.reduce(function(e,t){return e.concat(t.staticKeys||[])},[]).join(","))},Qo=e(function(e){return s("type,tag,attrsList,attrsMap,plain,parent,children,attrs"+(e?","+e:""))});function ea(e,t){e&&(Zo=Qo(t.staticKeys||""),Xo=t.isReservedTag||O,function e(t){t.static=function(e){if(2===e.type)return!1;if(3===e.type)return!0;return!(!e.pre&&(e.hasBindings||e.if||e.for||c(e.tag)||!Xo(e.tag)||function(e){for(;e.parent;){if("template"!==(e=e.parent).tag)return!1;if(e.for)return!0}return!1}(e)||!Object.keys(e).every(Zo)))}(t);if(1===t.type){if(!Xo(t.tag)&&"slot"!==t.tag&&null==t.attrsMap["inline-template"])return;for(var n=0,r=t.children.length;n|^function\s*\(/,na=/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/,ra={esc:27,tab:9,enter:13,space:32,up:38,left:37,right:39,down:40,delete:[8,46]},ia={esc:"Escape",tab:"Tab",enter:"Enter",space:" ",up:["Up","ArrowUp"],left:["Left","ArrowLeft"],right:["Right","ArrowRight"],down:["Down","ArrowDown"],delete:["Backspace","Delete"]},oa=function(e){return"if("+e+")return null;"},aa={stop:"$event.stopPropagation();",prevent:"$event.preventDefault();",self:oa("$event.target !== $event.currentTarget"),ctrl:oa("!$event.ctrlKey"),shift:oa("!$event.shiftKey"),alt:oa("!$event.altKey"),meta:oa("!$event.metaKey"),left:oa("'button' in $event && $event.button !== 0"),middle:oa("'button' in $event && $event.button !== 1"),right:oa("'button' in $event && $event.button !== 2")};function sa(e,t,n){var r=t?"nativeOn:{":"on:{";for(var i in e)r+='"'+i+'":'+ca(i,e[i])+",";return r.slice(0,-1)+"}"}function ca(t,e){if(!e)return"function(){}";if(Array.isArray(e))return"["+e.map(function(e){return ca(t,e)}).join(",")+"]";var n=na.test(e.value),r=ta.test(e.value);if(e.modifiers){var i="",o="",a=[];for(var s in e.modifiers)if(aa[s])o+=aa[s],ra[s]&&a.push(s);else if("exact"===s){var c=e.modifiers;o+=oa(["ctrl","shift","alt","meta"].filter(function(e){return!c[e]}).map(function(e){return"$event."+e+"Key"}).join("||"))}else a.push(s);return a.length&&(i+="if(!('button' in $event)&&"+a.map(la).join("&&")+")return null;"),o&&(i+=o),"function($event){"+i+(n?"return "+e.value+"($event)":r?"return ("+e.value+")($event)":e.value)+"}"}return n||r?e.value:"function($event){"+e.value+"}"}function la(e){var t=parseInt(e,10);if(t)return"$event.keyCode!=="+t;var n=ra[e],r=ia[e];return"_k($event.keyCode,"+JSON.stringify(e)+","+JSON.stringify(n)+",$event.key,"+JSON.stringify(r)+")"}var ua={on:function(e,t){e.wrapListeners=function(e){return"_g("+e+","+t.value+")"}},bind:function(t,n){t.wrapData=function(e){return"_b("+e+",'"+t.tag+"',"+n.value+","+(n.modifiers&&n.modifiers.prop?"true":"false")+(n.modifiers&&n.modifiers.sync?",true":"")+")"}},cloak:$},fa=function(e){this.options=e,this.warn=e.warn||$r,this.transforms=wr(e.modules,"transformCode"),this.dataGenFns=wr(e.modules,"genData"),this.directives=m(m({},ua),e.directives);var t=e.isReservedTag||O;this.maybeComponent=function(e){return!t(e.tag)},this.onceId=0,this.staticRenderFns=[]};function pa(e,t){var n=new fa(t);return{render:"with(this){return "+(e?da(e,n):'_c("div")')+"}",staticRenderFns:n.staticRenderFns}}function da(e,t){if(e.staticRoot&&!e.staticProcessed)return va(e,t);if(e.once&&!e.onceProcessed)return ha(e,t);if(e.for&&!e.forProcessed)return f=t,v=(u=e).for,h=u.alias,m=u.iterator1?","+u.iterator1:"",y=u.iterator2?","+u.iterator2:"",u.forProcessed=!0,(d||"_l")+"(("+v+"),function("+h+m+y+"){return "+(p||da)(u,f)+"})";if(e.if&&!e.ifProcessed)return ma(e,t);if("template"!==e.tag||e.slotTarget){if("slot"===e.tag)return function(e,t){var n=e.slotName||'"default"',r=_a(e,t),i="_t("+n+(r?","+r:""),o=e.attrs&&"{"+e.attrs.map(function(e){return g(e.name)+":"+e.value}).join(",")+"}",a=e.attrsMap["v-bind"];!o&&!a||r||(i+=",null");o&&(i+=","+o);a&&(i+=(o?"":",null")+","+a);return i+")"}(e,t);var n;if(e.component)a=e.component,c=t,l=(s=e).inlineTemplate?null:_a(s,c,!0),n="_c("+a+","+ya(s,c)+(l?","+l:"")+")";else{var r=e.plain?void 0:ya(e,t),i=e.inlineTemplate?null:_a(e,t,!0);n="_c('"+e.tag+"'"+(r?","+r:"")+(i?","+i:"")+")"}for(var o=0;o':'
',0 { 6 | // Map web browser API and Node.js API to a single common API (preferring web standards over Node.js API). 7 | const isNodeJS = typeof process !== "undefined"; 8 | if (isNodeJS) { 9 | global.require = require; 10 | global.fs = require("fs"); 11 | 12 | const nodeCrypto = require("crypto"); 13 | global.crypto = { 14 | getRandomValues(b) { 15 | nodeCrypto.randomFillSync(b); 16 | }, 17 | }; 18 | 19 | global.performance = { 20 | now() { 21 | const [sec, nsec] = process.hrtime(); 22 | return sec * 1000 + nsec / 1000000; 23 | }, 24 | }; 25 | 26 | const util = require("util"); 27 | global.TextEncoder = util.TextEncoder; 28 | global.TextDecoder = util.TextDecoder; 29 | } else { 30 | if (typeof window !== "undefined") { 31 | window.global = window; 32 | } else if (typeof self !== "undefined") { 33 | self.global = self; 34 | } else { 35 | throw new Error("cannot export Go (neither window nor self is defined)"); 36 | } 37 | 38 | let outputBuf = ""; 39 | global.fs = { 40 | constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused 41 | writeSync(fd, buf) { 42 | outputBuf += decoder.decode(buf); 43 | const nl = outputBuf.lastIndexOf("\n"); 44 | if (nl != -1) { 45 | console.log(outputBuf.substr(0, nl)); 46 | outputBuf = outputBuf.substr(nl + 1); 47 | } 48 | return buf.length; 49 | }, 50 | openSync(path, flags, mode) { 51 | const err = new Error("not implemented"); 52 | err.code = "ENOSYS"; 53 | throw err; 54 | }, 55 | }; 56 | } 57 | 58 | const encoder = new TextEncoder("utf-8"); 59 | const decoder = new TextDecoder("utf-8"); 60 | 61 | global.Go = class { 62 | constructor() { 63 | this.argv = ["js"]; 64 | this.env = {}; 65 | this.exit = (code) => { 66 | if (code !== 0) { 67 | console.warn("exit code:", code); 68 | } 69 | }; 70 | this._callbackTimeouts = new Map(); 71 | this._nextCallbackTimeoutID = 1; 72 | 73 | const mem = () => { 74 | // The buffer may change when requesting more memory. 75 | return new DataView(this._inst.exports.mem.buffer); 76 | } 77 | 78 | const setInt64 = (addr, v) => { 79 | mem().setUint32(addr + 0, v, true); 80 | mem().setUint32(addr + 4, Math.floor(v / 4294967296), true); 81 | } 82 | 83 | const getInt64 = (addr) => { 84 | const low = mem().getUint32(addr + 0, true); 85 | const high = mem().getInt32(addr + 4, true); 86 | return low + high * 4294967296; 87 | } 88 | 89 | const loadValue = (addr) => { 90 | const f = mem().getFloat64(addr, true); 91 | if (!isNaN(f)) { 92 | return f; 93 | } 94 | 95 | const id = mem().getUint32(addr, true); 96 | return this._values[id]; 97 | } 98 | 99 | const storeValue = (addr, v) => { 100 | const nanHead = 0x7FF80000; 101 | 102 | if (typeof v === "number") { 103 | if (isNaN(v)) { 104 | mem().setUint32(addr + 4, nanHead, true); 105 | mem().setUint32(addr, 0, true); 106 | return; 107 | } 108 | mem().setFloat64(addr, v, true); 109 | return; 110 | } 111 | 112 | switch (v) { 113 | case undefined: 114 | mem().setUint32(addr + 4, nanHead, true); 115 | mem().setUint32(addr, 1, true); 116 | return; 117 | case null: 118 | mem().setUint32(addr + 4, nanHead, true); 119 | mem().setUint32(addr, 2, true); 120 | return; 121 | case true: 122 | mem().setUint32(addr + 4, nanHead, true); 123 | mem().setUint32(addr, 3, true); 124 | return; 125 | case false: 126 | mem().setUint32(addr + 4, nanHead, true); 127 | mem().setUint32(addr, 4, true); 128 | return; 129 | } 130 | 131 | let ref = this._refs.get(v); 132 | if (ref === undefined) { 133 | ref = this._values.length; 134 | this._values.push(v); 135 | this._refs.set(v, ref); 136 | } 137 | let typeFlag = 0; 138 | switch (typeof v) { 139 | case "string": 140 | typeFlag = 1; 141 | break; 142 | case "symbol": 143 | typeFlag = 2; 144 | break; 145 | case "function": 146 | typeFlag = 3; 147 | break; 148 | } 149 | mem().setUint32(addr + 4, nanHead | typeFlag, true); 150 | mem().setUint32(addr, ref, true); 151 | } 152 | 153 | const loadSlice = (addr) => { 154 | const array = getInt64(addr + 0); 155 | const len = getInt64(addr + 8); 156 | return new Uint8Array(this._inst.exports.mem.buffer, array, len); 157 | } 158 | 159 | const loadSliceOfValues = (addr) => { 160 | const array = getInt64(addr + 0); 161 | const len = getInt64(addr + 8); 162 | const a = new Array(len); 163 | for (let i = 0; i < len; i++) { 164 | a[i] = loadValue(array + i * 8); 165 | } 166 | return a; 167 | } 168 | 169 | const loadString = (addr) => { 170 | const saddr = getInt64(addr + 0); 171 | const len = getInt64(addr + 8); 172 | return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len)); 173 | } 174 | 175 | const timeOrigin = Date.now() - performance.now(); 176 | this.importObject = { 177 | go: { 178 | // func wasmExit(code int32) 179 | "runtime.wasmExit": (sp) => { 180 | const code = mem().getInt32(sp + 8, true); 181 | this.exited = true; 182 | delete this._inst; 183 | delete this._values; 184 | delete this._refs; 185 | this.exit(code); 186 | }, 187 | 188 | // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) 189 | "runtime.wasmWrite": (sp) => { 190 | const fd = getInt64(sp + 8); 191 | const p = getInt64(sp + 16); 192 | const n = mem().getInt32(sp + 24, true); 193 | fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n)); 194 | }, 195 | 196 | // func nanotime() int64 197 | "runtime.nanotime": (sp) => { 198 | setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000); 199 | }, 200 | 201 | // func walltime() (sec int64, nsec int32) 202 | "runtime.walltime": (sp) => { 203 | const msec = (new Date).getTime(); 204 | setInt64(sp + 8, msec / 1000); 205 | mem().setInt32(sp + 16, (msec % 1000) * 1000000, true); 206 | }, 207 | 208 | // func scheduleCallback(delay int64) int32 209 | "runtime.scheduleCallback": (sp) => { 210 | const id = this._nextCallbackTimeoutID; 211 | this._nextCallbackTimeoutID++; 212 | this._callbackTimeouts.set(id, setTimeout( 213 | () => { this._resolveCallbackPromise(); }, 214 | getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early 215 | )); 216 | mem().setInt32(sp + 16, id, true); 217 | }, 218 | 219 | // func clearScheduledCallback(id int32) 220 | "runtime.clearScheduledCallback": (sp) => { 221 | const id = mem().getInt32(sp + 8, true); 222 | clearTimeout(this._callbackTimeouts.get(id)); 223 | this._callbackTimeouts.delete(id); 224 | }, 225 | 226 | // func getRandomData(r []byte) 227 | "runtime.getRandomData": (sp) => { 228 | crypto.getRandomValues(loadSlice(sp + 8)); 229 | }, 230 | 231 | // func stringVal(value string) ref 232 | "syscall/js.stringVal": (sp) => { 233 | storeValue(sp + 24, loadString(sp + 8)); 234 | }, 235 | 236 | // func valueGet(v ref, p string) ref 237 | "syscall/js.valueGet": (sp) => { 238 | storeValue(sp + 32, Reflect.get(loadValue(sp + 8), loadString(sp + 16))); 239 | }, 240 | 241 | // func valueSet(v ref, p string, x ref) 242 | "syscall/js.valueSet": (sp) => { 243 | Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32)); 244 | }, 245 | 246 | // func valueIndex(v ref, i int) ref 247 | "syscall/js.valueIndex": (sp) => { 248 | storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16))); 249 | }, 250 | 251 | // valueSetIndex(v ref, i int, x ref) 252 | "syscall/js.valueSetIndex": (sp) => { 253 | Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24)); 254 | }, 255 | 256 | // func valueCall(v ref, m string, args []ref) (ref, bool) 257 | "syscall/js.valueCall": (sp) => { 258 | try { 259 | const v = loadValue(sp + 8); 260 | const m = Reflect.get(v, loadString(sp + 16)); 261 | const args = loadSliceOfValues(sp + 32); 262 | storeValue(sp + 56, Reflect.apply(m, v, args)); 263 | mem().setUint8(sp + 64, 1); 264 | } catch (err) { 265 | storeValue(sp + 56, err); 266 | mem().setUint8(sp + 64, 0); 267 | } 268 | }, 269 | 270 | // func valueInvoke(v ref, args []ref) (ref, bool) 271 | "syscall/js.valueInvoke": (sp) => { 272 | try { 273 | const v = loadValue(sp + 8); 274 | const args = loadSliceOfValues(sp + 16); 275 | storeValue(sp + 40, Reflect.apply(v, undefined, args)); 276 | mem().setUint8(sp + 48, 1); 277 | } catch (err) { 278 | storeValue(sp + 40, err); 279 | mem().setUint8(sp + 48, 0); 280 | } 281 | }, 282 | 283 | // func valueNew(v ref, args []ref) (ref, bool) 284 | "syscall/js.valueNew": (sp) => { 285 | try { 286 | const v = loadValue(sp + 8); 287 | const args = loadSliceOfValues(sp + 16); 288 | storeValue(sp + 40, Reflect.construct(v, args)); 289 | mem().setUint8(sp + 48, 1); 290 | } catch (err) { 291 | storeValue(sp + 40, err); 292 | mem().setUint8(sp + 48, 0); 293 | } 294 | }, 295 | 296 | // func valueLength(v ref) int 297 | "syscall/js.valueLength": (sp) => { 298 | setInt64(sp + 16, parseInt(loadValue(sp + 8).length)); 299 | }, 300 | 301 | // valuePrepareString(v ref) (ref, int) 302 | "syscall/js.valuePrepareString": (sp) => { 303 | const str = encoder.encode(String(loadValue(sp + 8))); 304 | storeValue(sp + 16, str); 305 | setInt64(sp + 24, str.length); 306 | }, 307 | 308 | // valueLoadString(v ref, b []byte) 309 | "syscall/js.valueLoadString": (sp) => { 310 | const str = loadValue(sp + 8); 311 | loadSlice(sp + 16).set(str); 312 | }, 313 | 314 | // func valueInstanceOf(v ref, t ref) bool 315 | "syscall/js.valueInstanceOf": (sp) => { 316 | mem().setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16)); 317 | }, 318 | 319 | "debug": (value) => { 320 | console.log(value); 321 | }, 322 | } 323 | }; 324 | } 325 | 326 | async run(instance) { 327 | this._inst = instance; 328 | this._values = [ // TODO: garbage collection 329 | NaN, 330 | undefined, 331 | null, 332 | true, 333 | false, 334 | global, 335 | this._inst.exports.mem, 336 | this, 337 | ]; 338 | this._refs = new Map(); 339 | this._callbackShutdown = false; 340 | this.exited = false; 341 | 342 | const mem = new DataView(this._inst.exports.mem.buffer) 343 | 344 | // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. 345 | let offset = 4096; 346 | 347 | const strPtr = (str) => { 348 | let ptr = offset; 349 | new Uint8Array(mem.buffer, offset, str.length + 1).set(encoder.encode(str + "\0")); 350 | offset += str.length + (8 - (str.length % 8)); 351 | return ptr; 352 | }; 353 | 354 | const argc = this.argv.length; 355 | 356 | const argvPtrs = []; 357 | this.argv.forEach((arg) => { 358 | argvPtrs.push(strPtr(arg)); 359 | }); 360 | 361 | const keys = Object.keys(this.env).sort(); 362 | argvPtrs.push(keys.length); 363 | keys.forEach((key) => { 364 | argvPtrs.push(strPtr(`${key}=${this.env[key]}`)); 365 | }); 366 | 367 | const argv = offset; 368 | argvPtrs.forEach((ptr) => { 369 | mem.setUint32(offset, ptr, true); 370 | mem.setUint32(offset + 4, 0, true); 371 | offset += 8; 372 | }); 373 | 374 | while (true) { 375 | const callbackPromise = new Promise((resolve) => { 376 | this._resolveCallbackPromise = () => { 377 | if (this.exited) { 378 | throw new Error("bad callback: Go program has already exited"); 379 | } 380 | setTimeout(resolve, 0); // make sure it is asynchronous 381 | }; 382 | }); 383 | this._inst.exports.run(argc, argv); 384 | if (this.exited) { 385 | break; 386 | } 387 | await callbackPromise; 388 | } 389 | } 390 | 391 | static _makeCallbackHelper(id, pendingCallbacks, go) { 392 | return function() { 393 | pendingCallbacks.push({ id: id, args: arguments }); 394 | go._resolveCallbackPromise(); 395 | }; 396 | } 397 | 398 | static _makeEventCallbackHelper(preventDefault, stopPropagation, stopImmediatePropagation, fn) { 399 | return function(event) { 400 | if (preventDefault) { 401 | event.preventDefault(); 402 | } 403 | if (stopPropagation) { 404 | event.stopPropagation(); 405 | } 406 | if (stopImmediatePropagation) { 407 | event.stopImmediatePropagation(); 408 | } 409 | fn(event); 410 | }; 411 | } 412 | } 413 | 414 | if (isNodeJS) { 415 | if (process.argv.length < 3) { 416 | process.stderr.write("usage: go_js_wasm_exec [wasm binary] [arguments]\n"); 417 | process.exit(1); 418 | } 419 | 420 | const go = new Go(); 421 | go.argv = process.argv.slice(2); 422 | go.env = process.env; 423 | go.exit = process.exit; 424 | WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => { 425 | process.on("exit", (code) => { // Node.js exits if no callback is pending 426 | if (code === 0 && !go.exited) { 427 | // deadlock, make Go print error and stack traces 428 | go._callbackShutdown = true; 429 | go._inst.exports.run(); 430 | } 431 | }); 432 | return go.run(result.instance); 433 | }).catch((err) => { 434 | throw err; 435 | }); 436 | } 437 | })(); 438 | -------------------------------------------------------------------------------- /examples/todolist/data.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "strings" 5 | "syscall/js" 6 | ) 7 | 8 | //go:generate go run github.com/chanwit/vuego/gen 9 | type Data struct { 10 | js.Value 11 | Items []*Item `json:"items"` 12 | Title string `json:"title"` 13 | NewItem string `json:"newItem"` 14 | } 15 | 16 | func (this *Data) AddItem() { 17 | text := strings.TrimSpace(this.GetNewItem()) 18 | if len(text) > 0 { 19 | item := &Item{ 20 | Text: text, 21 | Checked: false, 22 | } 23 | 24 | this.AddToItems(item) 25 | this.SetNewItem("") 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/todolist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 30 | 31 | 32 | 33 |
34 |

{{ title }}

35 |
36 | 38 | 39 | 40 | 41 |
42 |
    43 |
  • 45 |
    46 | 50 |
    51 |
  • 52 |
53 | 58 |
59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /examples/todolist/item.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "syscall/js" 4 | 5 | //go:generate go run github.com/chanwit/vuego/gen 6 | type Item struct { 7 | js.Value 8 | Text string `json:"text"` 9 | Checked bool `json:"checked"` 10 | } 11 | -------------------------------------------------------------------------------- /examples/todolist/todolist.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | . "github.com/chanwit/vuego" 5 | ) 6 | 7 | func main() { 8 | 9 | New(&Vue{ 10 | El: "#app", 11 | Data: &Data{ 12 | Items: []*Item { 13 | {Text: "Bananas", Checked: true}, 14 | {Text: "Apples", Checked: false}, 15 | }, 16 | Title: "Vuego Framework 0.1", 17 | NewItem: "", 18 | }, 19 | Methods: &Methods{ 20 | "addItem": func(this interface{}) { 21 | this.(*Data).AddItem() 22 | }, 23 | }, 24 | }) 25 | 26 | select {} 27 | } 28 | -------------------------------------------------------------------------------- /examples/todolist/vue.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Vue.js v2.5.17 3 | * (c) 2014-2018 Evan You 4 | * Released under the MIT License. 5 | */ 6 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Vue=t()}(this,function(){"use strict";var y=Object.freeze({});function M(e){return null==e}function D(e){return null!=e}function S(e){return!0===e}function T(e){return"string"==typeof e||"number"==typeof e||"symbol"==typeof e||"boolean"==typeof e}function P(e){return null!==e&&"object"==typeof e}var r=Object.prototype.toString;function l(e){return"[object Object]"===r.call(e)}function i(e){var t=parseFloat(String(e));return 0<=t&&Math.floor(t)===t&&isFinite(e)}function t(e){return null==e?"":"object"==typeof e?JSON.stringify(e,null,2):String(e)}function F(e){var t=parseFloat(e);return isNaN(t)?e:t}function s(e,t){for(var n=Object.create(null),r=e.split(","),i=0;ie.id;)n--;bt.splice(n+1,0,e)}else bt.push(e);Ct||(Ct=!0,Ze(At))}}(this)},St.prototype.run=function(){if(this.active){var e=this.get();if(e!==this.value||P(e)||this.deep){var t=this.value;if(this.value=e,this.user)try{this.cb.call(this.vm,e,t)}catch(e){Fe(e,this.vm,'callback for watcher "'+this.expression+'"')}else this.cb.call(this.vm,e,t)}}},St.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},St.prototype.depend=function(){for(var e=this.deps.length;e--;)this.deps[e].depend()},St.prototype.teardown=function(){if(this.active){this.vm._isBeingDestroyed||f(this.vm._watchers,this);for(var e=this.deps.length;e--;)this.deps[e].removeSub(this);this.active=!1}};var Tt={enumerable:!0,configurable:!0,get:$,set:$};function Et(e,t,n){Tt.get=function(){return this[t][n]},Tt.set=function(e){this[t][n]=e},Object.defineProperty(e,n,Tt)}function jt(e){e._watchers=[];var t=e.$options;t.props&&function(n,r){var i=n.$options.propsData||{},o=n._props={},a=n.$options._propKeys=[];n.$parent&&ge(!1);var e=function(e){a.push(e);var t=Ie(e,r,i,n);Ce(o,e,t),e in n||Et(n,"_props",e)};for(var t in r)e(t);ge(!0)}(e,t.props),t.methods&&function(e,t){e.$options.props;for(var n in t)e[n]=null==t[n]?$:v(t[n],e)}(e,t.methods),t.data?function(e){var t=e.$options.data;l(t=e._data="function"==typeof t?function(e,t){se();try{return e.call(t,t)}catch(e){return Fe(e,t,"data()"),{}}finally{ce()}}(t,e):t||{})||(t={});var n=Object.keys(t),r=e.$options.props,i=(e.$options.methods,n.length);for(;i--;){var o=n[i];r&&p(r,o)||(void 0,36!==(a=(o+"").charCodeAt(0))&&95!==a&&Et(e,"_data",o))}var a;we(t,!0)}(e):we(e._data={},!0),t.computed&&function(e,t){var n=e._computedWatchers=Object.create(null),r=Y();for(var i in t){var o=t[i],a="function"==typeof o?o:o.get;r||(n[i]=new St(e,a||$,$,Nt)),i in e||Lt(e,i,o)}}(e,t.computed),t.watch&&t.watch!==G&&function(e,t){for(var n in t){var r=t[n];if(Array.isArray(r))for(var i=0;iparseInt(this.max)&&bn(a,s[0],s,this._vnode)),t.data.keepAlive=!0}return t||e&&e[0]}}};$n=hn,Cn={get:function(){return j}},Object.defineProperty($n,"config",Cn),$n.util={warn:re,extend:m,mergeOptions:Ne,defineReactive:Ce},$n.set=xe,$n.delete=ke,$n.nextTick=Ze,$n.options=Object.create(null),k.forEach(function(e){$n.options[e+"s"]=Object.create(null)}),m(($n.options._base=$n).options.components,kn),$n.use=function(e){var t=this._installedPlugins||(this._installedPlugins=[]);if(-1=a&&l()};setTimeout(function(){c\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,oo="[a-zA-Z_][\\w\\-\\.]*",ao="((?:"+oo+"\\:)?"+oo+")",so=new RegExp("^<"+ao),co=/^\s*(\/?)>/,lo=new RegExp("^<\\/"+ao+"[^>]*>"),uo=/^]+>/i,fo=/^",""":'"',"&":"&"," ":"\n"," ":"\t"},go=/&(?:lt|gt|quot|amp);/g,_o=/&(?:lt|gt|quot|amp|#10|#9);/g,bo=s("pre,textarea",!0),$o=function(e,t){return e&&bo(e)&&"\n"===t[0]};var wo,Co,xo,ko,Ao,Oo,So,To,Eo=/^@|^v-on:/,jo=/^v-|^@|^:/,No=/([^]*?)\s+(?:in|of)\s+([^]*)/,Lo=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,Io=/^\(|\)$/g,Mo=/:(.*)$/,Do=/^:|^v-bind:/,Po=/\.[^.]+/g,Fo=e(eo);function Ro(e,t,n){return{type:1,tag:e,attrsList:t,attrsMap:function(e){for(var t={},n=0,r=e.length;n]*>)","i")),n=i.replace(t,function(e,t,n){return r=n.length,ho(o)||"noscript"===o||(t=t.replace(//g,"$1").replace(//g,"$1")),$o(o,t)&&(t=t.slice(1)),d.chars&&d.chars(t),""});a+=i.length-n.length,i=n,A(o,a-r,a)}else{var s=i.indexOf("<");if(0===s){if(fo.test(i)){var c=i.indexOf("--\x3e");if(0<=c){d.shouldKeepComment&&d.comment(i.substring(4,c)),C(c+3);continue}}if(po.test(i)){var l=i.indexOf("]>");if(0<=l){C(l+2);continue}}var u=i.match(uo);if(u){C(u[0].length);continue}var f=i.match(lo);if(f){var p=a;C(f[0].length),A(f[1],p,a);continue}var _=x();if(_){k(_),$o(v,i)&&C(1);continue}}var b=void 0,$=void 0,w=void 0;if(0<=s){for($=i.slice(s);!(lo.test($)||so.test($)||fo.test($)||po.test($)||(w=$.indexOf("<",1))<0);)s+=w,$=i.slice(s);b=i.substring(0,s),C(s)}s<0&&(b=i,i=""),d.chars&&b&&d.chars(b)}if(i===e){d.chars&&d.chars(i);break}}function C(e){a+=e,i=i.substring(e)}function x(){var e=i.match(so);if(e){var t,n,r={tagName:e[1],attrs:[],start:a};for(C(e[0].length);!(t=i.match(co))&&(n=i.match(io));)C(n[0].length),r.attrs.push(n);if(t)return r.unarySlash=t[1],C(t[0].length),r.end=a,r}}function k(e){var t=e.tagName,n=e.unarySlash;m&&("p"===v&&ro(t)&&A(v),g(t)&&v===t&&A(t));for(var r,i,o,a=y(t)||!!n,s=e.attrs.length,c=new Array(s),l=0;l-1"+("true"===d?":("+l+")":":_q("+l+","+d+")")),Ar(c,"change","var $$a="+l+",$$el=$event.target,$$c=$$el.checked?("+d+"):("+v+");if(Array.isArray($$a)){var $$v="+(f?"_n("+p+")":p)+",$$i=_i($$a,$$v);if($$el.checked){$$i<0&&("+Er(l,"$$a.concat([$$v])")+")}else{$$i>-1&&("+Er(l,"$$a.slice(0,$$i).concat($$a.slice($$i+1))")+")}}else{"+Er(l,"$$c")+"}",null,!0);else if("input"===$&&"radio"===w)r=e,i=_,a=(o=b)&&o.number,s=Or(r,"value")||"null",Cr(r,"checked","_q("+i+","+(s=a?"_n("+s+")":s)+")"),Ar(r,"change",Er(i,s),null,!0);else if("input"===$||"textarea"===$)!function(e,t,n){var r=e.attrsMap.type,i=n||{},o=i.lazy,a=i.number,s=i.trim,c=!o&&"range"!==r,l=o?"change":"range"===r?Pr:"input",u="$event.target.value";s&&(u="$event.target.value.trim()"),a&&(u="_n("+u+")");var f=Er(t,u);c&&(f="if($event.target.composing)return;"+f),Cr(e,"value","("+t+")"),Ar(e,l,f,null,!0),(s||a)&&Ar(e,"blur","$forceUpdate()")}(e,_,b);else if(!j.isReservedTag($))return Tr(e,_,b),!1;return!0},text:function(e,t){t.value&&Cr(e,"textContent","_s("+t.value+")")},html:function(e,t){t.value&&Cr(e,"innerHTML","_s("+t.value+")")}},isPreTag:function(e){return"pre"===e},isUnaryTag:to,mustUseProp:Sn,canBeLeftOpenTag:no,isReservedTag:Un,getTagNamespace:Vn,staticKeys:(Go=Wo,Go.reduce(function(e,t){return e.concat(t.staticKeys||[])},[]).join(","))},Qo=e(function(e){return s("type,tag,attrsList,attrsMap,plain,parent,children,attrs"+(e?","+e:""))});function ea(e,t){e&&(Zo=Qo(t.staticKeys||""),Xo=t.isReservedTag||O,function e(t){t.static=function(e){if(2===e.type)return!1;if(3===e.type)return!0;return!(!e.pre&&(e.hasBindings||e.if||e.for||c(e.tag)||!Xo(e.tag)||function(e){for(;e.parent;){if("template"!==(e=e.parent).tag)return!1;if(e.for)return!0}return!1}(e)||!Object.keys(e).every(Zo)))}(t);if(1===t.type){if(!Xo(t.tag)&&"slot"!==t.tag&&null==t.attrsMap["inline-template"])return;for(var n=0,r=t.children.length;n|^function\s*\(/,na=/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/,ra={esc:27,tab:9,enter:13,space:32,up:38,left:37,right:39,down:40,delete:[8,46]},ia={esc:"Escape",tab:"Tab",enter:"Enter",space:" ",up:["Up","ArrowUp"],left:["Left","ArrowLeft"],right:["Right","ArrowRight"],down:["Down","ArrowDown"],delete:["Backspace","Delete"]},oa=function(e){return"if("+e+")return null;"},aa={stop:"$event.stopPropagation();",prevent:"$event.preventDefault();",self:oa("$event.target !== $event.currentTarget"),ctrl:oa("!$event.ctrlKey"),shift:oa("!$event.shiftKey"),alt:oa("!$event.altKey"),meta:oa("!$event.metaKey"),left:oa("'button' in $event && $event.button !== 0"),middle:oa("'button' in $event && $event.button !== 1"),right:oa("'button' in $event && $event.button !== 2")};function sa(e,t,n){var r=t?"nativeOn:{":"on:{";for(var i in e)r+='"'+i+'":'+ca(i,e[i])+",";return r.slice(0,-1)+"}"}function ca(t,e){if(!e)return"function(){}";if(Array.isArray(e))return"["+e.map(function(e){return ca(t,e)}).join(",")+"]";var n=na.test(e.value),r=ta.test(e.value);if(e.modifiers){var i="",o="",a=[];for(var s in e.modifiers)if(aa[s])o+=aa[s],ra[s]&&a.push(s);else if("exact"===s){var c=e.modifiers;o+=oa(["ctrl","shift","alt","meta"].filter(function(e){return!c[e]}).map(function(e){return"$event."+e+"Key"}).join("||"))}else a.push(s);return a.length&&(i+="if(!('button' in $event)&&"+a.map(la).join("&&")+")return null;"),o&&(i+=o),"function($event){"+i+(n?"return "+e.value+"($event)":r?"return ("+e.value+")($event)":e.value)+"}"}return n||r?e.value:"function($event){"+e.value+"}"}function la(e){var t=parseInt(e,10);if(t)return"$event.keyCode!=="+t;var n=ra[e],r=ia[e];return"_k($event.keyCode,"+JSON.stringify(e)+","+JSON.stringify(n)+",$event.key,"+JSON.stringify(r)+")"}var ua={on:function(e,t){e.wrapListeners=function(e){return"_g("+e+","+t.value+")"}},bind:function(t,n){t.wrapData=function(e){return"_b("+e+",'"+t.tag+"',"+n.value+","+(n.modifiers&&n.modifiers.prop?"true":"false")+(n.modifiers&&n.modifiers.sync?",true":"")+")"}},cloak:$},fa=function(e){this.options=e,this.warn=e.warn||$r,this.transforms=wr(e.modules,"transformCode"),this.dataGenFns=wr(e.modules,"genData"),this.directives=m(m({},ua),e.directives);var t=e.isReservedTag||O;this.maybeComponent=function(e){return!t(e.tag)},this.onceId=0,this.staticRenderFns=[]};function pa(e,t){var n=new fa(t);return{render:"with(this){return "+(e?da(e,n):'_c("div")')+"}",staticRenderFns:n.staticRenderFns}}function da(e,t){if(e.staticRoot&&!e.staticProcessed)return va(e,t);if(e.once&&!e.onceProcessed)return ha(e,t);if(e.for&&!e.forProcessed)return f=t,v=(u=e).for,h=u.alias,m=u.iterator1?","+u.iterator1:"",y=u.iterator2?","+u.iterator2:"",u.forProcessed=!0,(d||"_l")+"(("+v+"),function("+h+m+y+"){return "+(p||da)(u,f)+"})";if(e.if&&!e.ifProcessed)return ma(e,t);if("template"!==e.tag||e.slotTarget){if("slot"===e.tag)return function(e,t){var n=e.slotName||'"default"',r=_a(e,t),i="_t("+n+(r?","+r:""),o=e.attrs&&"{"+e.attrs.map(function(e){return g(e.name)+":"+e.value}).join(",")+"}",a=e.attrsMap["v-bind"];!o&&!a||r||(i+=",null");o&&(i+=","+o);a&&(i+=(o?"":",null")+","+a);return i+")"}(e,t);var n;if(e.component)a=e.component,c=t,l=(s=e).inlineTemplate?null:_a(s,c,!0),n="_c("+a+","+ya(s,c)+(l?","+l:"")+")";else{var r=e.plain?void 0:ya(e,t),i=e.inlineTemplate?null:_a(e,t,!0);n="_c('"+e.tag+"'"+(r?","+r:"")+(i?","+i:"")+")"}for(var o=0;o':'
',0 { 6 | // Map web browser API and Node.js API to a single common API (preferring web standards over Node.js API). 7 | const isNodeJS = typeof process !== "undefined"; 8 | if (isNodeJS) { 9 | global.require = require; 10 | global.fs = require("fs"); 11 | 12 | const nodeCrypto = require("crypto"); 13 | global.crypto = { 14 | getRandomValues(b) { 15 | nodeCrypto.randomFillSync(b); 16 | }, 17 | }; 18 | 19 | global.performance = { 20 | now() { 21 | const [sec, nsec] = process.hrtime(); 22 | return sec * 1000 + nsec / 1000000; 23 | }, 24 | }; 25 | 26 | const util = require("util"); 27 | global.TextEncoder = util.TextEncoder; 28 | global.TextDecoder = util.TextDecoder; 29 | } else { 30 | if (typeof window !== "undefined") { 31 | window.global = window; 32 | } else if (typeof self !== "undefined") { 33 | self.global = self; 34 | } else { 35 | throw new Error("cannot export Go (neither window nor self is defined)"); 36 | } 37 | 38 | let outputBuf = ""; 39 | global.fs = { 40 | constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused 41 | writeSync(fd, buf) { 42 | outputBuf += decoder.decode(buf); 43 | const nl = outputBuf.lastIndexOf("\n"); 44 | if (nl != -1) { 45 | console.log(outputBuf.substr(0, nl)); 46 | outputBuf = outputBuf.substr(nl + 1); 47 | } 48 | return buf.length; 49 | }, 50 | openSync(path, flags, mode) { 51 | const err = new Error("not implemented"); 52 | err.code = "ENOSYS"; 53 | throw err; 54 | }, 55 | }; 56 | } 57 | 58 | const encoder = new TextEncoder("utf-8"); 59 | const decoder = new TextDecoder("utf-8"); 60 | 61 | global.Go = class { 62 | constructor() { 63 | this.argv = ["js"]; 64 | this.env = {}; 65 | this.exit = (code) => { 66 | if (code !== 0) { 67 | console.warn("exit code:", code); 68 | } 69 | }; 70 | this._callbackTimeouts = new Map(); 71 | this._nextCallbackTimeoutID = 1; 72 | 73 | const mem = () => { 74 | // The buffer may change when requesting more memory. 75 | return new DataView(this._inst.exports.mem.buffer); 76 | } 77 | 78 | const setInt64 = (addr, v) => { 79 | mem().setUint32(addr + 0, v, true); 80 | mem().setUint32(addr + 4, Math.floor(v / 4294967296), true); 81 | } 82 | 83 | const getInt64 = (addr) => { 84 | const low = mem().getUint32(addr + 0, true); 85 | const high = mem().getInt32(addr + 4, true); 86 | return low + high * 4294967296; 87 | } 88 | 89 | const loadValue = (addr) => { 90 | const f = mem().getFloat64(addr, true); 91 | if (!isNaN(f)) { 92 | return f; 93 | } 94 | 95 | const id = mem().getUint32(addr, true); 96 | return this._values[id]; 97 | } 98 | 99 | const storeValue = (addr, v) => { 100 | const nanHead = 0x7FF80000; 101 | 102 | if (typeof v === "number") { 103 | if (isNaN(v)) { 104 | mem().setUint32(addr + 4, nanHead, true); 105 | mem().setUint32(addr, 0, true); 106 | return; 107 | } 108 | mem().setFloat64(addr, v, true); 109 | return; 110 | } 111 | 112 | switch (v) { 113 | case undefined: 114 | mem().setUint32(addr + 4, nanHead, true); 115 | mem().setUint32(addr, 1, true); 116 | return; 117 | case null: 118 | mem().setUint32(addr + 4, nanHead, true); 119 | mem().setUint32(addr, 2, true); 120 | return; 121 | case true: 122 | mem().setUint32(addr + 4, nanHead, true); 123 | mem().setUint32(addr, 3, true); 124 | return; 125 | case false: 126 | mem().setUint32(addr + 4, nanHead, true); 127 | mem().setUint32(addr, 4, true); 128 | return; 129 | } 130 | 131 | let ref = this._refs.get(v); 132 | if (ref === undefined) { 133 | ref = this._values.length; 134 | this._values.push(v); 135 | this._refs.set(v, ref); 136 | } 137 | let typeFlag = 0; 138 | switch (typeof v) { 139 | case "string": 140 | typeFlag = 1; 141 | break; 142 | case "symbol": 143 | typeFlag = 2; 144 | break; 145 | case "function": 146 | typeFlag = 3; 147 | break; 148 | } 149 | mem().setUint32(addr + 4, nanHead | typeFlag, true); 150 | mem().setUint32(addr, ref, true); 151 | } 152 | 153 | const loadSlice = (addr) => { 154 | const array = getInt64(addr + 0); 155 | const len = getInt64(addr + 8); 156 | return new Uint8Array(this._inst.exports.mem.buffer, array, len); 157 | } 158 | 159 | const loadSliceOfValues = (addr) => { 160 | const array = getInt64(addr + 0); 161 | const len = getInt64(addr + 8); 162 | const a = new Array(len); 163 | for (let i = 0; i < len; i++) { 164 | a[i] = loadValue(array + i * 8); 165 | } 166 | return a; 167 | } 168 | 169 | const loadString = (addr) => { 170 | const saddr = getInt64(addr + 0); 171 | const len = getInt64(addr + 8); 172 | return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len)); 173 | } 174 | 175 | const timeOrigin = Date.now() - performance.now(); 176 | this.importObject = { 177 | go: { 178 | // func wasmExit(code int32) 179 | "runtime.wasmExit": (sp) => { 180 | const code = mem().getInt32(sp + 8, true); 181 | this.exited = true; 182 | delete this._inst; 183 | delete this._values; 184 | delete this._refs; 185 | this.exit(code); 186 | }, 187 | 188 | // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) 189 | "runtime.wasmWrite": (sp) => { 190 | const fd = getInt64(sp + 8); 191 | const p = getInt64(sp + 16); 192 | const n = mem().getInt32(sp + 24, true); 193 | fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n)); 194 | }, 195 | 196 | // func nanotime() int64 197 | "runtime.nanotime": (sp) => { 198 | setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000); 199 | }, 200 | 201 | // func walltime() (sec int64, nsec int32) 202 | "runtime.walltime": (sp) => { 203 | const msec = (new Date).getTime(); 204 | setInt64(sp + 8, msec / 1000); 205 | mem().setInt32(sp + 16, (msec % 1000) * 1000000, true); 206 | }, 207 | 208 | // func scheduleCallback(delay int64) int32 209 | "runtime.scheduleCallback": (sp) => { 210 | const id = this._nextCallbackTimeoutID; 211 | this._nextCallbackTimeoutID++; 212 | this._callbackTimeouts.set(id, setTimeout( 213 | () => { this._resolveCallbackPromise(); }, 214 | getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early 215 | )); 216 | mem().setInt32(sp + 16, id, true); 217 | }, 218 | 219 | // func clearScheduledCallback(id int32) 220 | "runtime.clearScheduledCallback": (sp) => { 221 | const id = mem().getInt32(sp + 8, true); 222 | clearTimeout(this._callbackTimeouts.get(id)); 223 | this._callbackTimeouts.delete(id); 224 | }, 225 | 226 | // func getRandomData(r []byte) 227 | "runtime.getRandomData": (sp) => { 228 | crypto.getRandomValues(loadSlice(sp + 8)); 229 | }, 230 | 231 | // func stringVal(value string) ref 232 | "syscall/js.stringVal": (sp) => { 233 | storeValue(sp + 24, loadString(sp + 8)); 234 | }, 235 | 236 | // func valueGet(v ref, p string) ref 237 | "syscall/js.valueGet": (sp) => { 238 | storeValue(sp + 32, Reflect.get(loadValue(sp + 8), loadString(sp + 16))); 239 | }, 240 | 241 | // func valueSet(v ref, p string, x ref) 242 | "syscall/js.valueSet": (sp) => { 243 | Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32)); 244 | }, 245 | 246 | // func valueIndex(v ref, i int) ref 247 | "syscall/js.valueIndex": (sp) => { 248 | storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16))); 249 | }, 250 | 251 | // valueSetIndex(v ref, i int, x ref) 252 | "syscall/js.valueSetIndex": (sp) => { 253 | Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24)); 254 | }, 255 | 256 | // func valueCall(v ref, m string, args []ref) (ref, bool) 257 | "syscall/js.valueCall": (sp) => { 258 | try { 259 | const v = loadValue(sp + 8); 260 | const m = Reflect.get(v, loadString(sp + 16)); 261 | const args = loadSliceOfValues(sp + 32); 262 | storeValue(sp + 56, Reflect.apply(m, v, args)); 263 | mem().setUint8(sp + 64, 1); 264 | } catch (err) { 265 | storeValue(sp + 56, err); 266 | mem().setUint8(sp + 64, 0); 267 | } 268 | }, 269 | 270 | // func valueInvoke(v ref, args []ref) (ref, bool) 271 | "syscall/js.valueInvoke": (sp) => { 272 | try { 273 | const v = loadValue(sp + 8); 274 | const args = loadSliceOfValues(sp + 16); 275 | storeValue(sp + 40, Reflect.apply(v, undefined, args)); 276 | mem().setUint8(sp + 48, 1); 277 | } catch (err) { 278 | storeValue(sp + 40, err); 279 | mem().setUint8(sp + 48, 0); 280 | } 281 | }, 282 | 283 | // func valueNew(v ref, args []ref) (ref, bool) 284 | "syscall/js.valueNew": (sp) => { 285 | try { 286 | const v = loadValue(sp + 8); 287 | const args = loadSliceOfValues(sp + 16); 288 | storeValue(sp + 40, Reflect.construct(v, args)); 289 | mem().setUint8(sp + 48, 1); 290 | } catch (err) { 291 | storeValue(sp + 40, err); 292 | mem().setUint8(sp + 48, 0); 293 | } 294 | }, 295 | 296 | // func valueLength(v ref) int 297 | "syscall/js.valueLength": (sp) => { 298 | setInt64(sp + 16, parseInt(loadValue(sp + 8).length)); 299 | }, 300 | 301 | // valuePrepareString(v ref) (ref, int) 302 | "syscall/js.valuePrepareString": (sp) => { 303 | const str = encoder.encode(String(loadValue(sp + 8))); 304 | storeValue(sp + 16, str); 305 | setInt64(sp + 24, str.length); 306 | }, 307 | 308 | // valueLoadString(v ref, b []byte) 309 | "syscall/js.valueLoadString": (sp) => { 310 | const str = loadValue(sp + 8); 311 | loadSlice(sp + 16).set(str); 312 | }, 313 | 314 | // func valueInstanceOf(v ref, t ref) bool 315 | "syscall/js.valueInstanceOf": (sp) => { 316 | mem().setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16)); 317 | }, 318 | 319 | "debug": (value) => { 320 | console.log(value); 321 | }, 322 | } 323 | }; 324 | } 325 | 326 | async run(instance) { 327 | this._inst = instance; 328 | this._values = [ // TODO: garbage collection 329 | NaN, 330 | undefined, 331 | null, 332 | true, 333 | false, 334 | global, 335 | this._inst.exports.mem, 336 | this, 337 | ]; 338 | this._refs = new Map(); 339 | this._callbackShutdown = false; 340 | this.exited = false; 341 | 342 | const mem = new DataView(this._inst.exports.mem.buffer) 343 | 344 | // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. 345 | let offset = 4096; 346 | 347 | const strPtr = (str) => { 348 | let ptr = offset; 349 | new Uint8Array(mem.buffer, offset, str.length + 1).set(encoder.encode(str + "\0")); 350 | offset += str.length + (8 - (str.length % 8)); 351 | return ptr; 352 | }; 353 | 354 | const argc = this.argv.length; 355 | 356 | const argvPtrs = []; 357 | this.argv.forEach((arg) => { 358 | argvPtrs.push(strPtr(arg)); 359 | }); 360 | 361 | const keys = Object.keys(this.env).sort(); 362 | argvPtrs.push(keys.length); 363 | keys.forEach((key) => { 364 | argvPtrs.push(strPtr(`${key}=${this.env[key]}`)); 365 | }); 366 | 367 | const argv = offset; 368 | argvPtrs.forEach((ptr) => { 369 | mem.setUint32(offset, ptr, true); 370 | mem.setUint32(offset + 4, 0, true); 371 | offset += 8; 372 | }); 373 | 374 | while (true) { 375 | const callbackPromise = new Promise((resolve) => { 376 | this._resolveCallbackPromise = () => { 377 | if (this.exited) { 378 | throw new Error("bad callback: Go program has already exited"); 379 | } 380 | setTimeout(resolve, 0); // make sure it is asynchronous 381 | }; 382 | }); 383 | this._inst.exports.run(argc, argv); 384 | if (this.exited) { 385 | break; 386 | } 387 | await callbackPromise; 388 | } 389 | } 390 | 391 | static _makeCallbackHelper(id, pendingCallbacks, go) { 392 | return function() { 393 | pendingCallbacks.push({ id: id, args: arguments }); 394 | go._resolveCallbackPromise(); 395 | }; 396 | } 397 | 398 | static _makeEventCallbackHelper(preventDefault, stopPropagation, stopImmediatePropagation, fn) { 399 | return function(event) { 400 | if (preventDefault) { 401 | event.preventDefault(); 402 | } 403 | if (stopPropagation) { 404 | event.stopPropagation(); 405 | } 406 | if (stopImmediatePropagation) { 407 | event.stopImmediatePropagation(); 408 | } 409 | fn(event); 410 | }; 411 | } 412 | } 413 | 414 | if (isNodeJS) { 415 | if (process.argv.length < 3) { 416 | process.stderr.write("usage: go_js_wasm_exec [wasm binary] [arguments]\n"); 417 | process.exit(1); 418 | } 419 | 420 | const go = new Go(); 421 | go.argv = process.argv.slice(2); 422 | go.env = process.env; 423 | go.exit = process.exit; 424 | WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => { 425 | process.on("exit", (code) => { // Node.js exits if no callback is pending 426 | if (code === 0 && !go.exited) { 427 | // deadlock, make Go print error and stack traces 428 | go._callbackShutdown = true; 429 | go._inst.exports.run(); 430 | } 431 | }); 432 | return go.run(result.instance); 433 | }).catch((err) => { 434 | throw err; 435 | }); 436 | } 437 | })(); 438 | -------------------------------------------------------------------------------- /gen/gen.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "go/ast" 7 | "go/parser" 8 | "go/token" 9 | "io/ioutil" 10 | "os" 11 | "strings" 12 | ) 13 | 14 | var b *bytes.Buffer 15 | 16 | func main() { 17 | gofile := os.Getenv("GOFILE") 18 | 19 | fset := token.NewFileSet() 20 | f, err := parser.ParseFile(fset, gofile, nil, parser.ParseComments) 21 | if err != nil { 22 | panic(err) 23 | } 24 | 25 | b = &bytes.Buffer{} 26 | 27 | fmt.Fprintln(b, "// Code generated by go generate; DO NOT EDIT.") 28 | fmt.Fprintln(b, "// This file was generated by Vuego generator.") 29 | fmt.Fprintf(b,"package %s\n\n", f.Name) 30 | fmt.Fprintln(b, "import \"github.com/chanwit/vuego\"\n") 31 | fmt.Fprintln(b, "var _ = vuego.New\n") 32 | 33 | for _, decl := range f.Decls { 34 | 35 | 36 | if gen, ok := decl.(*ast.GenDecl); !ok || gen.Tok != token.TYPE { 37 | continue 38 | } 39 | 40 | gen := decl.(*ast.GenDecl) 41 | for _, spec := range gen.Specs { 42 | if _, ok := spec.(*ast.TypeSpec); !ok { 43 | continue 44 | } 45 | 46 | typeSpec := spec.(*ast.TypeSpec) 47 | // fmt.Println(typeSpec.Name) 48 | 49 | structType := typeSpec.Type.(*ast.StructType) 50 | for i, field := range structType.Fields.List { 51 | // first field must be the embedded js.Value 52 | if i == 0 { 53 | if(len(field.Names) != 0) { 54 | panic("a") 55 | } 56 | jsValue := field.Type.(*ast.SelectorExpr) 57 | name := fmt.Sprintf("%s.%s", jsValue.X, jsValue.Sel) 58 | if name != "js.Value" { 59 | panic("b") 60 | } 61 | } else { 62 | // generate getter and setter 63 | // fmt.Println(field.Names[0]) 64 | // fmt.Println(reflect.TypeOf(field.Type)) 65 | switch v := field.Type.(type) { 66 | case *ast.Ident: 67 | typeName := fmt.Sprintf("%s", v.Name) 68 | valFuncName := strings.Title(typeName) 69 | tag := getTag(field) 70 | 71 | genGetter(typeSpec, field, typeName, tag, valFuncName) 72 | genSetter(typeSpec, field, typeName, tag, valFuncName) 73 | case *ast.SelectorExpr: 74 | typeName := fmt.Sprintf("%s.%s", v.X, v.Sel) 75 | valFuncName := "" // strings.Title(typeName) 76 | tag := getTag(field) 77 | 78 | genGetter(typeSpec, field, typeName, tag, valFuncName) 79 | genSetter(typeSpec, field, typeName, tag, valFuncName) 80 | case *ast.ArrayType: 81 | // typeName := fmt.Sprintf("[]*%s", v.Elt.(*ast.StarExpr).X) 82 | elemTypeName := fmt.Sprintf("*%s", v.Elt.(*ast.StarExpr).X) 83 | valFuncName := "" 84 | tag := getTag(field) 85 | 86 | // TODO required generate getters and setters? 87 | genAdder(typeSpec, field, elemTypeName, tag, valFuncName) 88 | } 89 | } 90 | } 91 | } 92 | } 93 | 94 | ioutil.WriteFile("vuego_" + gofile, b.Bytes(), 0644) 95 | } 96 | 97 | func getTag(f *ast.Field) string { 98 | parts := strings.Split(f.Tag.Value, ":") 99 | tag := f.Names[0].Name 100 | if len(parts) == 2 { 101 | tag = strings.Trim(parts[1], "\"`") 102 | } 103 | return tag 104 | } 105 | 106 | func genGetter(typeSpec *ast.TypeSpec, f *ast.Field, typeName string, tag string, valFuncName string) { 107 | if valFuncName != "" { 108 | valFuncName = "." + valFuncName + "()" 109 | } 110 | fmt.Fprintf(b,"func (this *%s) Get%s() %s {\n", typeSpec.Name, f.Names[0], typeName) 111 | fmt.Fprintf(b,"\tthis.%s = this.Value.Get(%q)%s\n", f.Names[0], tag, valFuncName) 112 | fmt.Fprintf(b,"\treturn this.%s\n", f.Names[0]) 113 | fmt.Fprintf(b,"}\n\n") 114 | } 115 | 116 | func genSetter(typeSpec *ast.TypeSpec, f *ast.Field, typeName string, tag string, valFuncName string) { 117 | fmt.Fprintf(b,"func (this *%s) Set%s(val %s) {\n", typeSpec.Name, f.Names[0], typeName) 118 | fmt.Fprintf(b,"\tthis.%s = val\n", f.Names[0]) 119 | fmt.Fprintf(b,"\tthis.Value.Set(%q, val)\n", tag) 120 | fmt.Fprintf(b,"}\n\n") 121 | } 122 | 123 | func genAdder(typeSpec *ast.TypeSpec, f *ast.Field, typeName string, tag string, valFuncName string) { 124 | fmt.Fprintf(b,"func (this *%s) AddTo%s(val %s) {\n", typeSpec.Name, f.Names[0], typeName) 125 | fmt.Fprintf(b,"\tthis.%s = append(this.%s, val)\n", f.Names[0], f.Names[0]) 126 | fmt.Fprintf(b,"\tthis.Value.Get(%q).Call(\"push\", vuego.ToJSON(val))\n", tag) 127 | fmt.Fprintf(b,"}\n\n") 128 | } 129 | -------------------------------------------------------------------------------- /methods.go: -------------------------------------------------------------------------------- 1 | package vuego 2 | 3 | type Any = interface{} 4 | 5 | type Methods map[string]func(Any) 6 | 7 | type Filters map[string]func(Any)Any 8 | 9 | type Watch = map[string]Any -------------------------------------------------------------------------------- /vuego.go: -------------------------------------------------------------------------------- 1 | package vuego 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "syscall/js" 7 | 8 | "github.com/segmentio/ksuid" 9 | ) 10 | 11 | type Vue struct { 12 | js.Value 13 | El string 14 | Data interface{} 15 | 16 | Methods *Methods 17 | Filters *Filters 18 | Watch *Watch 19 | 20 | Created func(interface{}, []js.Value) 21 | Mounted func(interface{}, []js.Value) 22 | } 23 | 24 | var ctor = js.Global().Get("Object") 25 | var arr = js.Global().Get("Array") 26 | 27 | func ValueOf(rv reflect.Value) js.Value { 28 | fmt.Printf(">> ValueOf: DEBUG calling ValueOf type=%s\n", rv.Type().Name()) 29 | switch rv.Kind() { 30 | case reflect.Slice: 31 | fmt.Printf(">>>> case slice (%v)\n", rv.Interface()) 32 | a := arr.New(rv.Len()) 33 | for i := 0; i < rv.Len(); i++ { 34 | fmt.Printf(">>>>>> case slice elem (%v)\n", rv.Index(i).Interface()) 35 | a.SetIndex(i, ToJSON(rv.Index(i).Interface())) 36 | } 37 | return a 38 | default: 39 | fmt.Printf(">>>> case default (%v)\n", rv.Interface()) 40 | return js.ValueOf(rv.Interface()) 41 | } 42 | } 43 | 44 | func ToJSON(this interface{}) js.Value { 45 | // this.Value 46 | thisValue := ctor.New() 47 | 48 | v := reflect.Indirect(reflect.ValueOf(this)) 49 | r := v.Type() 50 | 51 | if r.Name() == "string" { 52 | return js.ValueOf(v.Interface()) 53 | } 54 | 55 | fmt.Printf(">>> DEBUG ToJSON type=%s\n", r.Name()) 56 | 57 | // starting with field 1 58 | for i := 1; i < r.NumField(); i++ { 59 | // field to init 60 | // must be tagged with json:"name" 61 | name := r.Field(i).Tag.Get("json") 62 | if name != "" { 63 | thisValue.Set(name, ValueOf(v.Field(i))) 64 | } 65 | } 66 | 67 | v.Field(0).Set(reflect.ValueOf(thisValue)) 68 | 69 | return thisValue 70 | } 71 | 72 | func New(v *Vue) *Vue { 73 | arg := ctor.New() 74 | arg.Set("el", v.El) 75 | arg.Set("data", ToJSON(v.Data)) 76 | 77 | if v.Watch != nil { 78 | arg.Set("watch", js.ValueOf(*v.Watch)) 79 | } 80 | 81 | if v.Created != nil { 82 | arg.Set("created", js.NewCallback(func(args []js.Value) { 83 | v.Created(v.Data, args) 84 | })) 85 | } 86 | 87 | if v.Filters != nil { 88 | filters := ctor.New() 89 | for name, fn := range *v.Filters { 90 | id := "VUEGO_FILTER_" + ksuid.New().String() 91 | output := id + "_OUTPUT" 92 | cb := js.NewCallback(func(args []js.Value) { 93 | js.Global().Set(output, js.ValueOf(fn(args[0]))) 94 | }) 95 | js.Global().Set(id, cb) 96 | wrapper := js.Global().Call("eval", fmt.Sprintf("(function(v){ %s(v); return global.%s; })", id, output)); 97 | filters.Set(name, wrapper) 98 | } 99 | arg.Set("filters", filters) 100 | } 101 | 102 | if v.Methods != nil { 103 | methods := ctor.New() 104 | for name, fn := range *v.Methods { 105 | cb := js.NewCallback(func(args []js.Value) { 106 | fn(v.Data) 107 | }); 108 | methods.Set(name, js.ValueOf(cb)) 109 | } 110 | arg.Set("methods", methods) 111 | } 112 | 113 | v.Value = js.Global().Get("Vue").New(arg) 114 | return v 115 | } 116 | 117 | func (v *Vue) Watch0(prop string, callback func(newValue, oldValue js.Value)) { 118 | cb := js.NewCallback(func(args []js.Value) { 119 | callback(args[0], args[1]) 120 | }) 121 | v.Value.Call("$watch", js.ValueOf(prop), cb) 122 | } 123 | 124 | func Use(val js.Value) { 125 | js.Global().Get("Vue").Call("use", val) 126 | } 127 | -------------------------------------------------------------------------------- /xhr/xhr.go: -------------------------------------------------------------------------------- 1 | package xhr 2 | 3 | import "syscall/js" 4 | 5 | type XMLHttpRequest struct { 6 | js.Value 7 | } 8 | 9 | func New() *XMLHttpRequest { 10 | return &XMLHttpRequest{ 11 | Value: js.Global().Get("XMLHttpRequest").New(), 12 | } 13 | } 14 | 15 | func (x *XMLHttpRequest) Open(method string, path string) { 16 | x.Value.Call("open", method, path) 17 | } 18 | 19 | func (x *XMLHttpRequest) OnLoad(fn func([]js.Value)) { 20 | cb := js.NewCallback(fn) 21 | x.Value.Set("onload", cb) 22 | } 23 | 24 | func (x *XMLHttpRequest) Send() { 25 | x.Value.Call("send") 26 | } 27 | func (x *XMLHttpRequest) GetResponseText() string { 28 | return x.Get("responseText").String() 29 | } 30 | --------------------------------------------------------------------------------