Replies: 1 comment 2 replies
-
See my comment here: #529 (comment) This approach could be improved by using generics, but basically when implementing a JS library you should structure your code as you would in Javascript, rather than write a Go API and expose it to Javascript, if you know what I mean. |
Beta Was this translation helpful? Give feedback.
2 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
I'm starting to get the hang of Goja and seem to have a way forward for my ambitious project, a headless browser in Go.
I was hoping that I could get some feedback on the initial work, and the path I am following right now, if there are things I should do differently, or things that could be smarter.
I know this is a long write, and I open source maintainers get lots of request for help, so if this is beyond what you have time for, I fully respect that.
Requirements
As this should emulate a browser environment, all objects should have the right prototypes, the prototype chain should be correct, and the functions should exist as properties on the right object. E.g.
document.getElementById
is not an "own property" on the document, but exists onDocument.prototype.getElementById
; whereasdocument.location
is an own property on the document.Example,
EventTarget
andWindow
In a browser environment,
globalThis
is thewindow
object, which is an instance ofWindow
, which inherits fromEventTarget
. You cannot create new instances ofWindow
, but you can create new instances ofEventTarget
.The native implementations follow a pattern like this:
They are implemented in unexported structs
Creating prototypes
For each type in the code I create a
...Wrapper
type. For theEventTarget
where the JS code can create new instances, the constructor looks like thisThe prototype chains are are setup like this:
Handling method calls.
While Goja can automatically wrap Go objects, and forward calls, which is an amazing feature 👏, this doesn't seem like the right way for me. The functions should be defined on the prototype.
Here are two examples of functions on
EventTarget
, and how they are implemented. They use aGetInstance
to extract the underlying nativeGo
object.Getting the underlying instance
The previous examples were somewhat simplified. I have a generic wrapper, so a common function can be used for all wrapper types, here the
[T]
is the native Go interface being wrapped.One problem is that I need special handling for the
window
.The problem is that the
globalThis
should is the window object, but since it is created for me, I cannot wrap a native object withToValue
, and I couldn't find a way to inject one after the fact?Alternate solution
I was wondering is it made sense to create wrapper types for each instance,
And then construct the instance wrapper in the constructor
I was thinking the automatic wrapping of Go objects could be useful here. But then the functions would be "own properties" of the instance, not the prototype, as they should be
This might be a benefit for "own properties", but they seem to have to be defined using
DefineAccessorProperty
(unless mapping magic could set that up, e.g. based on tags on the wrapped go struct)Question: Handling complex argument conversion
I was considering placing this as a separate question, as it does make sense on it's own (Let me know if I should).
XMLHttpRequest.prototype.send
can accept anull
Document
XMLHttpRequestBodyInit
, which cabn be aBlob
,ArrayBuffer
, and many more.I was imagining that I could create a single type to handle parsing the body argument
If the
ExportTo
supports e.g. an interface I could implement, I could wrap all logic of converting the possible input values in one simple place.Is such a thing possible? Or are there other best practices for argument conversion?
Wrapper code is not a problem
There will be a lot of trivial wrapper code, but optimising for minimum wrapper code is not necessary from a code maintenance point of vies (but maybe from a build time point of view).
I have already setup for v8 that much of the wrapper code is generated based on information in IDL files from web specs. So once I have a sensible path forward, it's just a matter of making a new variant of the code generator targetting Goja.
Beta Was this translation helpful? Give feedback.
All reactions