Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gopherjs runtime on frontend - inspired by gopherjs playground #76

Open
naorzr opened this issue Aug 2, 2023 · 4 comments
Open

Gopherjs runtime on frontend - inspired by gopherjs playground #76

naorzr opened this issue Aug 2, 2023 · 4 comments

Comments

@naorzr
Copy link

naorzr commented Aug 2, 2023

Hello!

I've been experimenting these past 2 weeks on my spare time, trying to expose a runCode global at the frontend, that will allow users to run arbitrary Go code, purely on the client side, it is heavily inspired by the gopher playground.

I removed the UI parts, and made some modifications, but I seem to be hitting a roadblock,
when trying to use

runCode(`
package main

import (
	"fmt"
	"syscall/js"
)

func main() {
	fmt.Println("Hello, playground")
	js.Global().Call("alert", "Hello, JavaScript")
	js.Global().Get("console").Call("log", "Hello, JS console")

}

`)

I end up getting

$mapIndex is not defined

this error originates from fmt.Println function only, other fmt functions I've tested work as expected.

Also, I've noticed that in the original code, there's a loading phase first, and then a run, it seems necessary in my case too, (running the code seems to be doing the loading phase + running, which throws some errors, but running again, will just run the code without loading, and result with no console errors) I'm just not sure as to how to implement it.

the altered code can be found here https://github.com/naorzr/gopherjs-runtime/blob/master/playground/main.go
and it can be run and played with by running the index.html from this repo
https://github.com/naorzr/gopherjs-runtime/tree/master/playground

@nevkontakte
Copy link
Member

I haven't tried to run your code, but I see at least one significant misunderstanding, which is probably causing at least some of your issues.

There aren't two phases per se. You provide the compiler with the main package and the importContext object. That object is supposed to load precompiled archives of imported packages on demand and return them to the compiler. The regular gopherjs compiler uses this to recursively compiler the whole import tree starting with the main package.

So instead of running one attempt to compiler, collecting dependencies, loading them, and making the next attempt you should just load the package when the compiler asks for it, completing the whole thing in a single pass.

@nevkontakte
Copy link
Member

nevkontakte commented Aug 4, 2023

Besides that, a couple of general tips:

  • GopherJS supports the standard net/http client, you could use that instead of the honnef.co/go/js/xhr package to load imported packages.
  • Instead of js.Global.Call("eval", js.InternalObject("console.log('code: "+string(escaped)+"')")) you can use println(code) to print to the debug console. fmt.Printf() works for that purpose too. To call console.error() you can simply do js.Global.Get("console").Call("error", "my message").

@naorzr
Copy link
Author

naorzr commented Aug 5, 2023

So instead of running one attempt to compiler, collecting dependencies, loading them, and making the next attempt you should just load the package when the compiler asks for it, completing the whole thing in a single pass.

if I get it right, do you suggest placing the http.get(...) logic for getting the package inside the import context?

@nevkontakte
Copy link
Member

Ok, so I spent some time looking at your code today, and there were two main issues:

  • Your runCodeAsync() function wasn't actually async — you were still blocking on a channel at the end, which is why on the first run it would throw an error after kicking off HTTP requests. The second execution would work because the HTTP request would complete in the background and then no blocking operations would happen.
  • There was some weird desynchronization between the version of runtime the compiler was outputting and the precompiled packages. Not sure why the compiler was picking up the wrong version, but I was able to fix that by upgrading the go.mod dependency to gopherjs 1.18.0-beta3 and running gopherjs clean.

On a more general note, part of the GopherJS philosophy is that you could try to write your code as you would with normal Go, as much as possible, and confine JS-aware parts to as few places as possible. That tends to yield a much more readable program. I did some refactoring of your code and you can find a working example here:

nevkontakte@5408ff2#diff-6639521c1bb3cd1e0ee9891ede42f19a89aab52a1417124481590cf761dfe432

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants