mirror of
https://github.com/taigrr/wasm-experiments
synced 2025-01-18 04:03:21 -08:00
Add jsgo.io example
This commit is contained in:
342
vendor/github.com/dave/wasmgo/cmd/deployer/deployer.go
generated
vendored
Normal file
342
vendor/github.com/dave/wasmgo/cmd/deployer/deployer.go
generated
vendored
Normal file
@@ -0,0 +1,342 @@
|
||||
package deployer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/dave/jsgo/assets/std"
|
||||
"github.com/dave/jsgo/server/servermsg"
|
||||
"github.com/dave/jsgo/server/wasm/messages"
|
||||
"github.com/dave/services/constor/constormsg"
|
||||
"github.com/dave/wasmgo/cmd/cmdconfig"
|
||||
"github.com/dave/wasmgo/config"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/pkg/browser"
|
||||
)
|
||||
|
||||
const CLIENT_VERSION = "1.0.0"
|
||||
|
||||
func Start(cfg *cmdconfig.Config) error {
|
||||
|
||||
var debug io.Writer
|
||||
if cfg.Verbose {
|
||||
debug = os.Stdout
|
||||
} else {
|
||||
debug = ioutil.Discard
|
||||
}
|
||||
|
||||
// create a temp dir
|
||||
tempDir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
fpath := filepath.Join(tempDir, "out.wasm")
|
||||
|
||||
sourceDir, err := runGoList(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := runGoBuild(cfg, fpath, debug); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
binaryBytes, err := ioutil.ReadFile(fpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
binarySha := sha1.New()
|
||||
if _, err := io.Copy(binarySha, bytes.NewBuffer(binaryBytes)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
files := map[messages.DeployFileType]messages.DeployFile{}
|
||||
|
||||
files[messages.DeployFileTypeWasm] = messages.DeployFile{
|
||||
DeployFileKey: messages.DeployFileKey{
|
||||
Type: messages.DeployFileTypeWasm,
|
||||
Hash: fmt.Sprintf("%x", binarySha.Sum(nil)),
|
||||
},
|
||||
Contents: binaryBytes,
|
||||
}
|
||||
|
||||
loaderBuf := &bytes.Buffer{}
|
||||
loaderSha := sha1.New()
|
||||
wasmUrl := fmt.Sprintf("%s://%s/%x.wasm", config.Protocol[config.Pkg], config.Host[config.Pkg], binarySha.Sum(nil))
|
||||
loaderVars := struct{ Binary string }{
|
||||
Binary: wasmUrl,
|
||||
}
|
||||
if err := loaderTemplateMin.Execute(io.MultiWriter(loaderBuf, loaderSha), loaderVars); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
files[messages.DeployFileTypeLoader] = messages.DeployFile{
|
||||
DeployFileKey: messages.DeployFileKey{
|
||||
Type: messages.DeployFileTypeLoader,
|
||||
Hash: fmt.Sprintf("%x", loaderSha.Sum(nil)),
|
||||
},
|
||||
Contents: loaderBuf.Bytes(),
|
||||
}
|
||||
|
||||
indexBuf := &bytes.Buffer{}
|
||||
indexSha := sha1.New()
|
||||
loaderUrl := fmt.Sprintf("%s://%s/%x.js", config.Protocol[config.Pkg], config.Host[config.Pkg], loaderSha.Sum(nil))
|
||||
indexVars := struct{ Script, Loader string }{
|
||||
Script: fmt.Sprintf("%s://%s/wasm_exec.%s.js", config.Protocol[config.Pkg], config.Host[config.Pkg], std.Wasm[true]),
|
||||
Loader: loaderUrl,
|
||||
}
|
||||
indexTemplate := defaultIndexTemplate
|
||||
if cfg.Index != "" {
|
||||
indexFilename := cfg.Index
|
||||
if cfg.Path != "" {
|
||||
indexFilename = filepath.Join(sourceDir, cfg.Index)
|
||||
}
|
||||
indexTemplateBytes, err := ioutil.ReadFile(indexFilename)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
if err == nil {
|
||||
indexTemplate, err = template.New("main").Parse(string(indexTemplateBytes))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := indexTemplate.Execute(io.MultiWriter(indexBuf, indexSha), indexVars); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
files[messages.DeployFileTypeIndex] = messages.DeployFile{
|
||||
DeployFileKey: messages.DeployFileKey{
|
||||
Type: messages.DeployFileTypeIndex,
|
||||
Hash: fmt.Sprintf("%x", indexSha.Sum(nil)),
|
||||
},
|
||||
Contents: indexBuf.Bytes(),
|
||||
}
|
||||
|
||||
indexUrl := fmt.Sprintf("%s://%s/%x", config.Protocol[config.Index], config.Host[config.Index], indexSha.Sum(nil))
|
||||
|
||||
message := messages.DeployQuery{
|
||||
Version: CLIENT_VERSION,
|
||||
Files: []messages.DeployFileKey{
|
||||
files[messages.DeployFileTypeWasm].DeployFileKey,
|
||||
files[messages.DeployFileTypeIndex].DeployFileKey,
|
||||
files[messages.DeployFileTypeLoader].DeployFileKey,
|
||||
},
|
||||
}
|
||||
|
||||
fmt.Fprintln(debug, "Querying server...")
|
||||
|
||||
protocol := "wss"
|
||||
if config.Protocol[config.Wasm] == "http" {
|
||||
protocol = "ws"
|
||||
}
|
||||
conn, _, err := websocket.DefaultDialer.Dial(
|
||||
fmt.Sprintf("%s://%s/_wasm/", protocol, config.Host[config.Wasm]),
|
||||
http.Header{"Origin": []string{fmt.Sprintf("%s://%s/", config.Protocol[config.Wasm], config.Host[config.Wasm])}},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
messageBytes, messageType, err := messages.Marshal(message)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := conn.WriteMessage(messageType, messageBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var response messages.DeployQueryResponse
|
||||
var done bool
|
||||
for !done {
|
||||
_, replyBytes, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
message, err := messages.Unmarshal(replyBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch message := message.(type) {
|
||||
case messages.DeployQueryResponse:
|
||||
response = message
|
||||
done = true
|
||||
case servermsg.Queueing:
|
||||
// don't print
|
||||
case servermsg.Error:
|
||||
return errors.New(message.Message)
|
||||
case messages.DeployClientVersionNotSupported:
|
||||
return errors.New("this client version is not supported - try `go get -u github.com/dave/wasmgo`")
|
||||
default:
|
||||
// unexpected
|
||||
fmt.Fprintf(debug, "Unexpected message from server: %#v\n", message)
|
||||
}
|
||||
}
|
||||
|
||||
if len(response.Required) > 0 {
|
||||
|
||||
fmt.Fprintf(debug, "Files required: %d.\n", len(response.Required))
|
||||
fmt.Fprintln(debug, "Bundling required files...")
|
||||
|
||||
var required []messages.DeployFile
|
||||
for _, k := range response.Required {
|
||||
file := files[k.Type]
|
||||
if file.Hash == "" {
|
||||
return errors.New("server requested file not found")
|
||||
}
|
||||
required = append(required, file)
|
||||
}
|
||||
|
||||
payload := messages.DeployPayload{Files: required}
|
||||
payloadBytes, payloadType, err := messages.Marshal(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := conn.WriteMessage(payloadType, payloadBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(debug, "Sending payload: %dKB.\n", len(payloadBytes)/1024)
|
||||
|
||||
done = false
|
||||
for !done {
|
||||
_, replyBytes, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
message, err := messages.Unmarshal(replyBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch message := message.(type) {
|
||||
case messages.DeployDone:
|
||||
done = true
|
||||
case servermsg.Queueing:
|
||||
// don't print
|
||||
case servermsg.Error:
|
||||
return errors.New(message.Message)
|
||||
case constormsg.Storing:
|
||||
if message.Remain > 0 || message.Finished > 0 {
|
||||
fmt.Fprintf(debug, "Storing, %d to go.\n", message.Remain)
|
||||
}
|
||||
default:
|
||||
// unexpected
|
||||
fmt.Fprintf(debug, "Unexpected message from server: %#v\n", message)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintln(debug, "Sending done.")
|
||||
|
||||
} else {
|
||||
fmt.Fprintln(debug, "No files required.")
|
||||
}
|
||||
|
||||
outputVars := struct {
|
||||
Page string
|
||||
Loader string
|
||||
}{
|
||||
Page: indexUrl,
|
||||
Loader: loaderUrl,
|
||||
}
|
||||
|
||||
if cfg.Json {
|
||||
out, err := json.Marshal(outputVars)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(out))
|
||||
} else {
|
||||
tpl, err := template.New("main").Parse(cfg.Template)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tpl.Execute(os.Stdout, outputVars)
|
||||
fmt.Println("")
|
||||
}
|
||||
|
||||
if cfg.Open {
|
||||
browser.OpenURL(indexUrl)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runGoBuild(cfg *cmdconfig.Config, fpath string, debug io.Writer) error {
|
||||
args := []string{"build", "-o", fpath}
|
||||
|
||||
extraFlags := strings.Fields(cfg.Flags)
|
||||
for _, f := range extraFlags {
|
||||
args = append(args, f)
|
||||
}
|
||||
|
||||
if cfg.BuildTags != "" {
|
||||
args = append(args, "-tags", cfg.BuildTags)
|
||||
}
|
||||
|
||||
path := "."
|
||||
if cfg.Path != "" {
|
||||
path = cfg.Path
|
||||
}
|
||||
args = append(args, path)
|
||||
|
||||
fmt.Fprintln(debug, "Compiling...")
|
||||
|
||||
cmd := exec.Command(cfg.Command, args...)
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Env = append(cmd.Env, "GOARCH=wasm")
|
||||
cmd.Env = append(cmd.Env, "GOOS=js")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
if strings.Contains(string(output), "unsupported GOOS/GOARCH pair js/wasm") {
|
||||
return errors.New("you need Go v1.11 to compile WASM. It looks like your default `go` command is not v1.11. Perhaps you need the -c flag to specify a custom command name - e.g. `-c=go1.11beta3`")
|
||||
}
|
||||
return fmt.Errorf("%v: %s", err, string(output))
|
||||
}
|
||||
if len(output) > 0 {
|
||||
return fmt.Errorf("%s", string(output))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runGoList(cfg *cmdconfig.Config) (string, error) {
|
||||
args := []string{"list"}
|
||||
|
||||
if cfg.BuildTags != "" {
|
||||
args = append(args, "-tags", cfg.BuildTags)
|
||||
}
|
||||
|
||||
args = append(args, "-f", "{{.Dir}}")
|
||||
|
||||
path := "."
|
||||
if cfg.Path != "" {
|
||||
path = cfg.Path
|
||||
}
|
||||
args = append(args, path)
|
||||
|
||||
cmd := exec.Command(cfg.Command, args...)
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Env = append(cmd.Env, "GOARCH=wasm")
|
||||
cmd.Env = append(cmd.Env, "GOOS=js")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
if strings.Contains(string(output), "unsupported GOOS/GOARCH pair js/wasm") {
|
||||
return "", errors.New("you need Go v1.11 to compile WASM. It looks like your default `go` command is not v1.11. Perhaps you need the -c flag to specify a custom command name - e.g. `-c=go1.11beta3`")
|
||||
}
|
||||
return "", fmt.Errorf("%v: %s", err, string(output))
|
||||
}
|
||||
return string(output), nil
|
||||
}
|
||||
24
vendor/github.com/dave/wasmgo/cmd/deployer/templates.go
generated
vendored
Normal file
24
vendor/github.com/dave/wasmgo/cmd/deployer/templates.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
package deployer
|
||||
|
||||
import "text/template"
|
||||
|
||||
var defaultIndexTemplate = template.Must(template.New("main").Parse(`<html>
|
||||
<head><meta charset="utf-8"></head>
|
||||
<body>
|
||||
<script src="{{ .Script }}"></script>
|
||||
<script src="{{ .Loader }}"></script>
|
||||
</body>
|
||||
</html>`))
|
||||
|
||||
var loaderTemplate = template.Must(template.New("main").Parse(`if (!WebAssembly.instantiateStreaming) {
|
||||
WebAssembly.instantiateStreaming = async (resp, importObject) => {
|
||||
const source = await (await resp).arrayBuffer();
|
||||
return await WebAssembly.instantiate(source, importObject);
|
||||
};
|
||||
}
|
||||
const go = new Go();
|
||||
WebAssembly.instantiateStreaming(fetch("{{ .Binary }}"), go.importObject).then(result => {
|
||||
go.run(result.instance);
|
||||
});`))
|
||||
|
||||
var loaderTemplateMin = template.Must(template.New("main").Parse(`WebAssembly.instantiateStreaming||(WebAssembly.instantiateStreaming=(async(t,a)=>{const e=await(await t).arrayBuffer();return await WebAssembly.instantiate(e,a)}));const go=new Go;WebAssembly.instantiateStreaming(fetch("{{ .Binary }}"),go.importObject).then(t=>{go.run(t.instance)});`))
|
||||
Reference in New Issue
Block a user