Plans for dynamic import?

Are there any plans to improve the support for dynamic imports? The current way of just binding to it via external works fine, it’s just that having to target the generated JS module explicitly is error prone.

Just to examplify, this is what I’m talking about:

external import: string => Js.Promise.t(myModule) = "import"

let x = import("./myModule.bs")

I vaguely recall there being talks of providing some form of helper for resolving the path of the generated JS file for any module. I also vaguely recall there being a mention about this in a roadmap somewhere.

Perhaps someone from the team could comment on this?

13 Likes

I’m working on a React app with an “admin” mode and a “client” mode. I started to go down the path of creating two separate React apps but this introduced a lot of complexity especially since I needed a third project that contained shared code between the two and 3 bsconfig.json. So I started investigating “code splitting” to make the admin stuff get downloaded only when the user goes to specific admin pages. I am finding the dynamic import approach awkward as @zth commented. I haven’t tried “Suspense” or “lazy” yet so don’t know if just fixing this problem really fixes the overall problem of working with dynamically imported components.

Here is an example Math.res file. I don’t think I can use a “.resi” file because I want to be able to reference the module type later. Instead I have to do a weird include syntax.

module type T = {
  let add: (int, int) => int
  let subtract: (int, int) => int
}

include (
  {
    let add = (a, b) => a + b
    let subtract = (a, b) => a - b
  }: T
)

And now to use it I’ve got to hardwire the path to the .bs file, which is fragile, and I’m not sure/how it works when there is a module inside a module. I also need a different external function for every module I want to dynamically import. I couldn’t figure out how to make this import statement “generic” to support any module type.

@val external importMath: string => Js.Promise.t<module(Math.T)> = "import"

let math = importMath("./Math.bs")
math
->Promise.thenResolve(m => {
  let module(M) = m
  let _sum = M.add(3, 14)
  let _dif = M.subtract(10, 25)
})
->ignore

Obviously it would be better if there was some kind of built-in dynamic import command that takes care of resolving the file path correctly…

let math1 = import(Math, Math.T) // pass a module type and module
let math2 = import(Math)
``

Yes, this is really needed to catch up with the TypeScript experience in this regard. Ideally we’d have both a way to resolve the path for the generated file for of a module, and some sort of primitive you can point to a module to import it that can automatically infer its types too. Just like in TS.

I have a Vite-specific solution to the first part that I can open source if there’s interest. I’m using it in production and it’s working fine. It doesn’t take care of the type inference, but it essentially allow you to do:

import("@rescriptModule/NameOfFile")

That’ll automatically be replaced with the correct absolute path to the generated file. With that, it’s easy to write types and a binding manually to get it all working.

But we really do need a proper, first class integration for this.

3 Likes

Ah man just realized this is missing, this is a pretty crucial feature for browser performance.

How are you getting the types for your modules? Are you just creating a new type to describe the interface of the module, or is there a way to convert a module into a type(?)

Cc @moondaddi who’s been doing some work to support dynamic import natively.

It is in the middle of implementation. I couldn’t finish the module mechanism yet.
I’m stuck with lots of time-sensitive tasks for my company, but I’m going to back normal mode I will work on it again. Hope it won’t take much time.

6 Likes