v8go icon indicating copy to clipboard operation
v8go copied to clipboard

Support for ES Modules

Open robfig opened this issue 3 years ago • 6 comments

It looks like v8go does not support ES Modules. For example, I translated the Context example to use modules:

package main

import (
	"fmt"

	"rogchap.com/v8go"
)

func main() {
	ctx, _ := v8go.NewContext()
	val, err := ctx.RunScript("export function add(a, b) { return a + b };", "math.mjs")
	fmt.Println(val, err)
	val, err = ctx.RunScript("import { add } from 'math.mjs';\nconst result = add(3, 4)", "main.js")
	fmt.Println(val, err)
	val, err = ctx.RunScript("result", "value.js")
	fmt.Println(val, err)
	fmt.Println(val)
}

and it resulted in these errors:

<nil> SyntaxError: Unexpected token 'export'
<nil> SyntaxError: Cannot use import statement outside a module
...

I do not typically develop JS or use Node.js, so I might be doing something dumb too.

Do you have any particular ideas around how this might work? I'd be happy to submit a pull request if so.

Thanks! Rob

robfig avatar Apr 01 '21 11:04 robfig

Hi Rob!

Until V8 has direct support for interpreting that syntax I think you'll need a step in between the V8 context and that source.

Thankfully we have the https://pkg.go.dev/github.com/evanw/esbuild/pkg/api project that is rapidly growing in popularity that gives us access to exactly this type of bundling task.

Perhaps we should consider some sort of v8goext package that could provide some helpers for this sort of thing.

tmc avatar Apr 01 '21 13:04 tmc

This comment here points to various implementations of modules using the V8 api: https://stackoverflow.com/a/52031275/105456

I'm personally a bit skeptical we should add this type and amount of code to this project - perhaps another candidate for a ext or contrib package.

tmc avatar Apr 01 '21 13:04 tmc

Thanks for the pointers. So you're saying that V8 itself does not deal with ES Modules and expects to be handed JS to execute after all the module stuff has been taken care of.

I should mention my higher level goal is to accept Deno / TypeScript programs and run a function defined by them. To do that, I used the Deno CLI to execute "deno bundle" to convert the provided program to plain JS. However, Deno uses ES Modules and the bundle output is a ES Module. My goal is to execute an exported function from that module.

Using esbuild to transform into FormatIIFE allows this to work! Many thanks

package main

import (
	"fmt"
	"os"

	"github.com/evanw/esbuild/pkg/api"
	"rogchap.com/v8go"
)

func main() {
	script := "export function add(a, b) { return a + b };"
	result := api.Transform(script, api.TransformOptions{
		Loader:     api.LoaderJS,
		Format:     api.FormatIIFE,
		GlobalName: "global",
	})
	os.Stdout.Write(result.Code)
	fmt.Printf("%d errors and %d warnings\n",
		len(result.Errors), len(result.Warnings))

	ctx, _ := v8go.NewContext()
	ctx.RunScript(string(result.Code), "math.mjs")
	global, _ := ctx.Global().Get("global")
	globalObj, _ := global.AsObject()
	fmt.Println(globalObj.Get("add"))
}

robfig avatar Apr 01 '21 21:04 robfig

Reopening to link with a pull request

robfig avatar Apr 03 '21 18:04 robfig

Tested this again today, looks like we are still getting the same type of response. That being said I'm not sure if anything has changed with the default v8 binary bundled with v8go.

etiennemartin avatar May 10 '22 15:05 etiennemartin

@etiennemartin Modules are not just a backwards compatible extensions to scripts, so it isn't just going to change using the current APIs. Support needs to be added for the separate V8 APIs for modules such as v8::ScriptCompiler::CompileModule and the v8::Module methods. E.g. see https://github.com/rogchap/v8go/pull/101 for a previous abandoned attempt to add support for ES modules

dylanahsmith avatar May 10 '22 15:05 dylanahsmith