rescript-compiler icon indicating copy to clipboard operation
rescript-compiler copied to clipboard

Dynamic import ergonomics

Open nkrkv opened this issue 2 years ago • 8 comments

This is a follow up of the forum post

When it comes to dynamic import of modules in ReScript, it looks verbose. For example, given a React component, we can make its dynamic counterpart like this:

module DynamicWidget = {
  let makeProps = () => Js.Obj.empty() // <- 1
  let make = {
    // The example uses NextJS but it does not matter much
    // The focus is on `import`
    open Next.Dynamic
    dynamic(
      () =>
        import_("./Widgets/Widget.bs.js") // <- 2
        ->PromiseX.map(x => x["make"]), // <- 3
      options(~loading=() => <MaterialUi.CircularProgress />, ~ssr=false, ()),
    )
  }
}

// Later use <DynamicWidget /> as a regular component

Three issues inconveniences:

  1. Since we don’t use @react.component here, we have to define makeProps for myself. It should go away when JSX V4 will be released.
  2. Here we should know the exact path to .bs.js. Perhaps some magic possible to just mention a module name and the path comes out automatically
  3. We have to tweak the import result since ReScript components don’t provide the default export (aren’t they?).

The dynamic import problem is not specific to JSX / React components only. import might be used for regular modules as well. I wonder if ReScript can make dynamic imports a little more ergonomic.

nkrkv avatar Jul 29 '22 09:07 nkrkv

One thought about how could it look like. What if ReScript introduce an @import annotation that can be used like this:

let modulePromise = @import ModuleName.functionName
// or later, with syntax support
let modulePromise = import(ModuleName.functionName)

Given that, along with JSX V4, the original example will be:

module DynamicWidget = {
  open Next.Dynamic
  let make = dynamic(() => import(Widget.make), options(~ssr=false, ()))
}

// Then

<Suspense fallback={...}>
  <DynamicWidget />
</Suspense>

nkrkv avatar Jul 29 '22 09:07 nkrkv

AFAIK, the import() should have a string arg which is the js module path. The example intends @import or import to transform the Widget.make to the component's path as a string. Is it your idea? IMHO, the pain points are no. 1 and no.3 which can be resolved by compiler. ~~No. 2 (should know the component's path) seems inevitable to me.~~

EDIT: No 2. seems possible to be transformed to the component's path. It is not very different from the normal import in output js.

mununki avatar Jul 30 '22 05:07 mununki

module DynamicWidget = {
  open Next.Dynamic
  @react.component(dynamic)
  let make = dynamic(() => import(Widget.make), { ssr: false })
}

// generated to

module DynamicWidget = {
  open Next.Dynamic
  type props = Widget.props // <-- 1
  let make = dynamic(() => import(Widget.make), { ssr: false })
}

// output in js
var DynamicWidget = dynamic(() => import("./Widgets/Widget.bs.js"), { ssr: false }) // <-- 2

1 can be processed by jsx ppx. but 2 needs to be processed in js emitter, I guess. Widget.bs.js needs export default additionally in order to resolve 3.

mununki avatar Jul 30 '22 06:07 mununki

I'm shortly making a RFC post on the forums so we can start discussing a spec for dynamic imports in ReScript, and figure out all of the use cases we want to support, and how to support them best. Just a heads up 😃

zth avatar Jul 30 '22 06:07 zth

Forum RFC posted here: https://forum.rescript-lang.org/t/rfc-dynamic-imports-in-rescript/3605

zth avatar Jul 30 '22 09:07 zth

The example intends @import or import to transform the Widget.make to the component's path as a string. Is it your idea?

Almost. I mean whenever I use import and provide Foo.Bar.Baz.qux as its argument, where Foo.Bar is a file-module Foo__Bar.res and Baz is a nested module, it would be great if the compiler infers the module path (say, ../../Components/Foo__Bar.bs.js) and then extracts the rest from that module as a part of an automatic Promise.then (e.g., m => m.Baz.qux)

nkrkv avatar Jul 31 '22 10:07 nkrkv

@mattdamon108 anything in here that should still affect the design of PPX V4? If not, moving to milestone 10.2.

cristianoc avatar Sep 08 '22 08:09 cristianoc

Thank you for asking. I don't think this feature should affect the PPX V4 for now. I agree to move to milestone 10.2.

mununki avatar Sep 08 '22 08:09 mununki

The dynamic import PR subsumes this.

cristianoc avatar Apr 11 '23 02:04 cristianoc