Proposal Details
I don't know if this is a thing that already exists or not. I want to be able to import ESM modules in Go. I can do this in JavaScript via the dynamic import() expression. Right now the only way I know how to do this without extra modifications (where I expect the consumer to just copy-paste wasm_exec.js, wasm_exec_node.js, or wasm_exec.html into their source tree and use that) is to use eval() which seems... not ideal.
package main
import (
"syscall/js"
"log"
)
// import is a function-like keyword, not a global function
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import
var jsImportFunc = js.Global().Get("eval").Invoke("(s,o)=>import(s,o)") // 🟥🟥🟥
func jsImport(specifier string, options any) js.Value {
return jsImportFunc.Invoke(specifier, options)
}
// https://stackoverflow.com/questions/68415674/await-js-async-function-promise-inside-a-go-function
func jsAwait(promise js.Value) js.Value {
c := make(chan js.Value)
jsOnResolve := js.FuncOf(func(this js.Value, args []js.Value) any {
c <- args[0]
return nil
})
jsOnReject := js.FuncOf(func(this js.Value, args []js.Value) any {
// act the same as if a function threw
panic(&js.Error{Value:args[0]})
return nil
})
promise.Call("then", jsOnResolve, jsOnReject)
return <-c
}
func main() {
jsPrettier := jsAwait(jsImport("prettier", js.Value{}))
jsFormatted := jsAwait(jsPrettier.Call("format", "hello();world();", map[string]any{
"parser": "typescript", // https://prettier.io/docs/en/options#parser
}))
log.Print(jsFormatted.String())
}
The eval() breaks when a user of the Go wasm blob disallows unsafe eval() via Content-Security-Policy.
I know, I know. I could just do globalThis.import = (s,o)=>import(s,o) in JavaScript before I run the Go test.wasm blob. But I don't control how the user uses my WASM blob and it would be really nice if I didn't have to ask them to "hey please add this thing when you use the wasm_exec.js to load this wasm file".
A cool minor change to do this would be to add a Go.prototype._import() method that is just this:
class Go {
_import(specifier, options) {
return import(specifier, options)
}
}
and then in the syscall/js Go code:
func Import(specifier string, options any) js.Value {
return whateverThePrivateGoRuntimeVariableIsCalled.Call("_import", specifier, options)
}
or you could add it to the importObject thing instead. The point is that this syntax thing (not a global function) is currently unobtainable without resorting to eval() or forcing the user of the my-cool-go-thing.wasm to include a special snippet in addition to the wasm_exec.js standard Go wasm+js runtime.
Proposal Details
I don't know if this is a thing that already exists or not. I want to be able to import ESM modules in Go. I can do this in JavaScript via the dynamic
import()expression. Right now the only way I know how to do this without extra modifications (where I expect the consumer to just copy-pastewasm_exec.js,wasm_exec_node.js, orwasm_exec.htmlinto their source tree and use that) is to useeval()which seems... not ideal.The
eval()breaks when a user of the Go wasm blob disallows unsafeeval()via Content-Security-Policy.I know, I know. I could just do
globalThis.import = (s,o)=>import(s,o)in JavaScript before I run the Gotest.wasmblob. But I don't control how the user uses my WASM blob and it would be really nice if I didn't have to ask them to "hey please add this thing when you use thewasm_exec.jsto load this wasm file".A cool minor change to do this would be to add a
Go.prototype._import()method that is just this:and then in the syscall/js Go code:
or you could add it to the
importObjectthing instead. The point is that this syntax thing (not a global function) is currently unobtainable without resorting toeval()or forcing the user of themy-cool-go-thing.wasmto include a special snippet in addition to thewasm_exec.jsstandard Go wasm+js runtime.