mtgrosser / nodo

Call node.js from Ruby
MIT License
27 stars 2 forks source link

ESM Support #1

Closed aaronjensen closed 2 years ago

aaronjensen commented 3 years ago

Hi, this is a very interesting package. I'm trying to figure out if I can get ES modules imported, but running into a fairly opaque node error that I can't track down:

  class Test < Nodo::Core
    function :test, <<~JS
      async () => {
        const x = await import('react')
      }
    JS
  end
internal/errors.js:322:in `new NodeError'
internal/process/esm_loader.js:34:in `exports.importModuleDynamicallyCallback'
Sandbox::Test:30:in `__nodo_klass__.test'

I'm on Node 14, which supports dynamic import, at least with the CLI.

From what I can tell, this should set it up to support dynamic imports, but it does not appear to be doing that. Do you have any ideas?

Thanks!

Also, I'm curious what you're using this package for?

aaronjensen commented 3 years ago

I was able to get it working by requiring the file that had the import(...) in it. If I put the import(...) in the Nodo function, I get the above error.

  class Test < Nodo::Core
    require importReact: "./importReact.js"

    function :test, <<~JS
      async (props) => {
        const x = await importReact()
      }
    JS
  end
mtgrosser commented 3 years ago

I tried to reproduce the problem and got the following error from Node:

A dynamic import callback was not specified. (Nodo::JavaScriptError)

There are some hints on the net regarding this error, but I was unable to make the dynamic import() run properly. Sometimes the Node process even crashed, so I'm wondering what is the actual state of ESM support inside Node. Even some rather trivial code like

class Foo < Nodo::Core
  function :import_uuid, code: "async () => { return await import('uuid') }"
  function :v4, code: "async () => { const uuid = await import_uuid(); return uuid.v4() }"
end

didn't work.

The main motivation for the creation of Nodo was the need for a Ruby environment where cross-compilers and bundlers like @vue/compiler-sfc and rollup can be executed by the Rails asset pipeline, so ESM support inside Nodo itself was not a concern. It would be interesting to investigate whether it could be adapted to use a pure-ESM runtime like deno.

aaronjensen commented 3 years ago

That's the same error that I got. It seems to boil down to the fact that importModuleDynamically must be specified when using things like compileFunction or most of the other vm related things. I'm not sure which you use, and I have no idea how to get ahold of the importModuleDynamically that is used when you simply require a cjs module. All of that stuff is node internals, which they don't like you accessing: https://github.com/nodejs/node/blob/2cc7a91a5d855b4ff78f21f1bb8d4e55131d0615/lib/internal/modules/cjs/loader.js#L1018-L1022

Interestingly enough I can just create a file that exports a function that wraps import(...) and it'll work if I require that.

mtgrosser commented 2 years ago

The Nodo "class" definitions are run using vm.runInThisContext (code location)

I need to do some more research regarding the importModuleDynamically option. There is an implementation example here.

mtgrosser commented 2 years ago

Added nodo.import() which uses the regular top-level import().

aaronjensen commented 2 years ago

That's great, thanks.