├── .gitignore
├── go.mod
├── example
└── main.go
├── README.md
└── main.go
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/dImrich/tinygo-wasmserve
2 |
3 | go 1.14
--------------------------------------------------------------------------------
/example/main.go:
--------------------------------------------------------------------------------
1 | // +build example
2 |
3 | package main
4 |
5 | import (
6 | "syscall/js"
7 | )
8 |
9 | func main() {
10 | p := js.Global().Get("document").Call("createElement", "p")
11 | p.Set("innerText", "Hello, World!")
12 | js.Global().Get("document").Get("body").Call("appendChild", p)
13 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TinyGo WasmServe
2 |
3 | An HTTP server for TinyGo Wasm testing like `gopherjs serve`
4 |
5 | Fork of [hajimehoshi/wasmserve][https://github.com/hajimehoshi/wasmserve]
6 |
7 | ## Installation
8 |
9 | ```sh
10 | go get -u github.com/dImrich/tinygo-wasmserve
11 | ```
12 |
13 | ## Usage
14 |
15 | ```
16 | Usage of tinygo-wasmserve
17 | -allow-origin string
18 | Allow specified origin (or * for all origins) to make requests to this server
19 | -http string
20 | HTTP bind address to serve (default ":8080")
21 | -tags string
22 | Build tags
23 | -no-debug bool
24 | Disable outputting debug symbols. Avoiding debug symbols can have a big impact on generated binary size, reducing them by more than half.
25 | ```
26 |
27 | ## Example
28 |
29 | Running a remote package
30 |
31 | ```sh
32 | # Be careful that `-tags=example` is required to run the below example application.
33 | tinygo-wasmserve -tags=example github.com/dImrich/tinygo-wasmserve/example
34 | ```
35 |
36 | And open `http://localhost:8080/` on your browser.
37 |
38 | ## Example 2
39 |
40 | Running a local package
41 |
42 | ```sh
43 | git clone https://github.com/hajimehoshi/ebiten # This might take several minutes.
44 | cd ebiten
45 | tinygo-wasmserve -tags=example ./examples/sprites
46 | ```
47 |
48 | And open `http://localhost:8080/` on your browser.
49 |
50 | ## Known issue with Windows Subsystem for Linux (WSL)
51 |
52 | This application sometimes does not work under WSL, due to bugs in WSL, see https://github.com/hajimehoshi/wasmserve/issues/5 for details.
53 |
54 | [https://github.com/hajimehoshi/wasmserve]: https://github.com/hajimehoshi/wasmserve
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "flag"
6 | "io/ioutil"
7 | "log"
8 | "net/http"
9 | "os"
10 | "os/exec"
11 | "path/filepath"
12 | "strings"
13 | "time"
14 | )
15 |
16 | const indexHTML = `
17 |
32 | `
33 |
34 | var (
35 | flagHTTP = flag.String("http", ":8080", "HTTP bind address to serve")
36 | flagTags = flag.String("tags", "", "Build tags")
37 | flagAllowOrigin = flag.String("allow-origin", "", "Allow specified origin (or * for all origins) to make requests to this server")
38 | flagNoDebug = flag.Bool("no-debug", false, "Disable outputting debug symbols. Avoiding debug symbols can have a big impact on generated binary size, reducing them by more than half.")
39 | )
40 |
41 | var tmpOutputDir = ""
42 |
43 | func ensureTmpOutputDir() (string, error) {
44 | if tmpOutputDir != "" {
45 | return tmpOutputDir, nil
46 | }
47 |
48 | tmp, err := ioutil.TempDir("", "")
49 | if err != nil {
50 | return "", err
51 | }
52 | tmpOutputDir = tmp
53 | return tmpOutputDir, nil
54 | }
55 |
56 |
57 | func handle(w http.ResponseWriter, r *http.Request) {
58 | if *flagAllowOrigin != "" {
59 | w.Header().Set("Access-Control-Allow-Origin", *flagAllowOrigin)
60 | }
61 |
62 | output, err := ensureTmpOutputDir()
63 | if err != nil {
64 | http.Error(w, err.Error(), http.StatusInternalServerError)
65 | return
66 | }
67 |
68 | upath := r.URL.Path[1:]
69 | fpath := filepath.Join(".", filepath.Base(upath))
70 | workdir := "."
71 |
72 | if !strings.HasSuffix(r.URL.Path, "/") {
73 | fi, err := os.Stat(fpath)
74 | if err != nil && !os.IsNotExist(err) {
75 | http.Error(w, err.Error(), http.StatusInternalServerError)
76 | return
77 | }
78 | if fi != nil && fi.IsDir() {
79 | http.Redirect(w, r, r.URL.Path+"/", http.StatusSeeOther)
80 | return
81 | }
82 | }
83 |
84 | switch filepath.Base(fpath) {
85 | case "index.html", ".":
86 | if _, err := os.Stat(fpath); err != nil && !os.IsNotExist(err) {
87 | http.Error(w, err.Error(), http.StatusInternalServerError)
88 | return
89 | }
90 | http.ServeContent(w, r, "index.html", time.Now(), bytes.NewReader([]byte(indexHTML)))
91 | return
92 | case "wasm_exec.js":
93 | if _, err := os.Stat(fpath); err != nil && !os.IsNotExist(err) {
94 | http.Error(w, err.Error(), http.StatusInternalServerError)
95 | return
96 | }
97 | tinygoRootEnv := exec.Command("tinygo", "env", "TINYGOROOT")
98 | rootEnv, err := tinygoRootEnv.CombinedOutput()
99 | if err != nil {
100 | http.Error(w, err.Error(), http.StatusInternalServerError)
101 | return
102 | }
103 | f := filepath.Join(strings.TrimSuffix(string(rootEnv), "\n"), "targets", "wasm_exec.js")
104 | http.ServeFile(w, r, f)
105 | return
106 | case "main.wasm":
107 | if _, err := os.Stat(fpath); err != nil && !os.IsNotExist(err) {
108 | http.Error(w, err.Error(), http.StatusInternalServerError)
109 | return
110 | }
111 | // tinygo build -o main.wasm -target wasm .
112 | args := []string{"build", "-o", filepath.Join(output, "main.wasm"), "-target", "wasm"}
113 | if *flagNoDebug {
114 | args = append(args, "-no-debug")
115 | }
116 | if *flagTags != "" {
117 | args = append(args, "-tags", *flagTags)
118 | }
119 | if len(flag.Args()) > 0 {
120 | args = append(args, flag.Args()[0])
121 | } else {
122 | args = append(args, ".")
123 | }
124 | log.Print("tinygo ", strings.Join(args, " "))
125 | cmdBuild := exec.Command("tinygo", args...)
126 | cmdBuild.Dir = workdir
127 | out, err := cmdBuild.CombinedOutput()
128 | if err != nil {
129 | log.Print(err)
130 | log.Print(string(out))
131 | http.Error(w, string(out), http.StatusInternalServerError)
132 | return
133 | }
134 | if len(out) > 0 {
135 | log.Print(string(out))
136 | }
137 |
138 | f, err := os.Open(filepath.Join(output, "main.wasm"))
139 | if err != nil {
140 | http.Error(w, err.Error(), http.StatusInternalServerError)
141 | return
142 | }
143 | defer f.Close()
144 | http.ServeContent(w, r, "main.wasm", time.Now(), f)
145 | return
146 | }
147 |
148 | http.ServeFile(w, r, filepath.Join(".", r.URL.Path))
149 | }
150 |
151 | func main() {
152 | flag.Parse()
153 | http.HandleFunc("/", handle)
154 | log.Fatal(http.ListenAndServe(*flagHTTP, nil))
155 | }
--------------------------------------------------------------------------------