New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support dynamic import #2765

Open
wegry opened this Issue Apr 21, 2018 · 9 comments

Comments

Projects
None yet
8 participants
@wegry
Copy link
Contributor

wegry commented Apr 21, 2018

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);
@xtuc

This comment has been minimized.

Copy link

xtuc commented Apr 22, 2018

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

This comment has been minimized.

Copy link

kennetpostigo commented Apr 23, 2018

@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

This comment has been minimized.

Copy link

gilbert commented May 8, 2018

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

This comment has been minimized.

Copy link
Contributor

bloodyowl commented Jun 6, 2018

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

This comment has been minimized.

Copy link

cortopy commented Aug 19, 2018

@cortopy

This comment has been minimized.

Copy link

cortopy commented Aug 19, 2018

@outkine

This comment has been minimized.

Copy link

outkine commented Dec 4, 2018

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

This comment has been minimized.

Copy link
Contributor Author

wegry commented Dec 4, 2018

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

@yawaramin

This comment has been minimized.

Copy link
Contributor

yawaramin commented Jan 17, 2019

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 */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment