rescript-compiler
rescript-compiler copied to clipboard
Dynamic import ergonomics
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:
- 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. - 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 - 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.
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>
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.
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.
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 😃
Forum RFC posted here: https://forum.rescript-lang.org/t/rfc-dynamic-imports-in-rescript/3605
The example intends
@import
orimport
to transform theWidget.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
)
@mattdamon108 anything in here that should still affect the design of PPX V4? If not, moving to milestone 10.2.
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.
The dynamic import PR subsumes this.