Skip to content

Support dynamic import #2765

Closed
Closed
@wegry

Description

@wegry
Contributor

What version of OCaml are you using?
This is using bs-platform@3.0.

It would be nice if bucklescript supported code splitting via dynamic imports. Even though dynamic import is stage 3, Webpack has supported a version of it for quite some time code splitting.

/* Foo.re */
let x = 5;

/* Importer.re */
import(Foo /* maybe just a relative path? */) 
|> Js.Promise.then_(/* hand waving */);

would generate the following code

import ('./Foo.js').then(foo => foo.x);

Activity

xtuc

xtuc commented on Apr 22, 2018

@xtuc

If you have a webpack loader for buckelscript you can just compile it down to the JavaScript import() and Webpack will handle the compilation.

kennetpostigo

kennetpostigo commented on Apr 23, 2018

@kennetpostigo

@xtuc this is not ideal. Not everyone uses webpack, nor should they be forced to if they are using bucklescript. I think this is more of, "Please support this within bucklescript platform itself" Just like bucklescript can support outputting ES Modules and CommonJS.

gilbert

gilbert commented on May 8, 2018

@gilbert

import(Foo /* maybe just a relative path? */)

Supporting dynamic import like this might be tricky. In a browser environment, I don't know how BuckleScript would know what path to render, as it doesn't know the asset urls your server responds to. Also, what happens when you dynamically load an npm module?.

My personal feature wish is for BuckleScript to support import : string => Js.Promise.t(???) where we can, somehow, typecast the ??? to a module of our choice.

bloodyowl

bloodyowl commented on Jun 6, 2018

@bloodyowl
Collaborator

right now I think things could unlock pretty easily:

/* X.re */
let x = 24;
/* App.re */
module type XT = {
  include (module type of {
             include X;
           });
};

/* the external is pretty stupid but it's just for demonstration purposes */
[@bs.val] external import : string => Js.Promise.t((module XT)) = "";

import("./X.bs")
|> Js.Promise.then_((res: (module XT)) => {
     module Y = (val res);
     Js.log(Y.x);
     Js.Promise.resolve();
   });

outputs

// Generated by BUCKLESCRIPT VERSION 2.2.3, PLEASE EDIT WITH CARE
'use strict';


import("./X").then((function (res) {
        console.log(res[/* x */0]);
        return Promise.resolve(/* () */0);
      }));

/*  Not a pure module */

It'd only require BuckleScript to understand that in case X is a file, accessing its exports (here x) is through importedX##x and not importedX[0]

cortopy

cortopy commented on Aug 19, 2018

@cortopy
cortopy

cortopy commented on Aug 19, 2018

@cortopy
outkine

outkine commented on Dec 4, 2018

@outkine

My use case is more simple - I just need to import a json dynamically using webpack, but because [%%bs.raw] doesn't support dynamic strings, I can't follow the advice presented in the bsb docs. Is there a different way?

wegry

wegry commented on Dec 4, 2018

@wegry
ContributorAuthor

@outkine Webpack's dynamic import can't accept arbitrary strings at runtime either. It only works with hard-coded strings.

yawaramin

yawaramin commented on Jan 17, 2019

@yawaramin
Contributor

Here's a hacky workaround that relies on BuckleScript's nice feature of not mangling names.

/** Dataloader.re - just a binding to Facebook's Dataloader for demonstration purposes */

/** The type of the dynamically-imported module. */
type dynamic;

/** The [Dataloader] type. */
type t('a, 'b);

/** [import()] generates a dynamic import to [dataloader].
    {i PLEASE ENSURE} you use it like: {[
Dataloader.import() |> Js.Promise.then_(_dataloader => ...)
    ]}

    The [_dataloader] is especially important because it generates code
    that works with the rest of the below externals. */
[@bs.val] external import: (
  [@bs.as {json|"dataloader"|json}] _,
  unit
) => Js.Promise.t(dynamic) = "import";

type batchFn('a, 'b) = (. array('a)) => Js.Promise.t(array('b));
[@bs.new] external make: batchFn('a, 'b) => t('a, 'b) = "_dataloader.Dataloader";

/* Use.re */

let loader: Js.Promise.t(Dataloader.t(int, int)) = Dataloader.import()
  |> Js.Promise.then_(_dataloader => /* [_dataloader] naming is important here */
    Js.Promise.resolve(Dataloader.make((. array) =>
      Js.Promise.resolve(array)))
  );

This generates the output:

// Generated by BUCKLESCRIPT VERSION 4.0.14, PLEASE EDIT WITH CARE
'use strict';


var loader = import(("dataloader")).then((function (_dataloader) {
        return Promise.resolve(new _dataloader.Dataloader((function (array) {
                          return Promise.resolve(array);
                        })));
      }));

exports.loader = loader;
/* loader Not a pure module */
BlueHotDog

BlueHotDog commented on Jul 9, 2020

@BlueHotDog

@bobzhang maybe close this issue and consolidate discussion to:
#3919

wegry

wegry commented on Jun 16, 2022

@wegry
ContributorAuthor

Closing as I think #3919 probably supersedes this. I've been out of the Reason/Rescript game for quite some time now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @yawaramin@gilbert@BlueHotDog@xtuc@bloodyowl

        Issue actions

          Support dynamic import · Issue #2765 · rescript-lang/rescript