1

I'd like to use the select2 Javascript library. Unfortunately, they force the use of AMD on their users.

I'd like to use ES6 modules exclusively in my code. Is there a way for a ES6 module to export variables that are initialized asynchronously by AMD? For example, I'd like to do something like this:

const result = $.fn.select2.amd.require([
    "select2/data/array",
    "select2/utils"
], function (ArrayData, Utils)
{
  return "something";
});

export {result as default};

That way, I can wrap the AMD ugliness is a single place and import it as ES6 everywhere else.

Gili
  • 76,473
  • 85
  • 341
  • 624
  • Not sure this solves your problem but have you looked at [esm](https://github.com/standard-things/esm)? – ray hatfield Sep 08 '19 at 03:47
  • @jfriend00 Yes, I want to ES6 export a component of the select2 library. That's what I'm asking for but I'm unable to figure out the implementation. – Gili Sep 08 '19 at 03:50
  • You cannot directly export something loaded asynchronously because the export assignment and return from the module happens before your async operation finishes. – jfriend00 Sep 08 '19 at 03:50
  • @jfriend00 We can export promises. The question is whether we can get a promise out of AMD's `require`. – Gili Sep 08 '19 at 03:51
  • You don't need to get a promise out of AMD. You can wrap your own promise around the AMD load and return the promise. But, that's going to give you an asynchronously loaded interface through the promise, not a typical ES6 interface. – jfriend00 Sep 08 '19 at 03:52
  • "*I'd like to use ES6 modules exclusively in my code*" - do you want to load them at the client as ES6 modules as well, or are you going to bundle/transpile them? – Bergi Sep 08 '19 at 03:53
  • Have you tried [simply converting their library file to module syntax](https://github.com/piuccio/rollup-plugin-amd)? – Bergi Sep 08 '19 at 03:55
  • @Bergi Today I am transpiling but in a month `node` is supposed to support ES6 by default, at which point I will no longer transpile. – Gili Sep 08 '19 at 03:56
  • @Gili Select2 looks like a clientside library, so what does this have to do with node? – Bergi Sep 08 '19 at 03:57
  • @Bergi Rollup will solve half my problem: I will get a ES6 version locally but when running from a browser the CDN version will get pulled down and it uses AMD. So unless I plan to bundle a non-CDN version of their library for browsers this solution won't work. – Gili Sep 08 '19 at 03:58
  • @Bergi Node support is only relevant during development. My IDE needs to be able to load and auto-complete library methods. But at deployment time, you are right: I only care about browsers... And I want browsers to use the CDN version of select2. – Gili Sep 08 '19 at 03:59
  • If you are transpiling anyway for bundling (and I doubt that your target browser landscape will change a lot in 6 months), then your bundled format should be able to work with the AMD version on the CDN? – Bergi Sep 08 '19 at 04:02
  • @jfriend00 I was hoping that `import` would wait for the promise to get resolve before returning a value. Is this not the case? So I'd have to `await` on the imported value? – Gili Sep 08 '19 at 04:02
  • import does not wait for a promise, it would just import the promise itself. There is a future proposal to allow stuff like that, but not what we have now. – jfriend00 Sep 08 '19 at 04:04
  • @Bergi I am using Webpack, so I suspect the bundled format would be compatible. But at development time I'm not sure how to mix ES6 modules + AMD code. I don't want to wrap my entire main block in AMD `require` just for the sake of importing a single component I need. – Gili Sep 08 '19 at 04:04
  • @Gili You wouldn't write any AMD at all. Just write `import ArrayData from 'select2/array/data';` (or whatever you need exactly), and let webpack handle the rest. Make sure to read the webpack docs on how AMD module exports are exposed (named imports? default imports? etc) – Bergi Sep 08 '19 at 04:07
  • @Bergi That would be great but I'm unable to get it to work. select2 is one of those annoying libraries that [augments the jquery object](https://stackoverflow.com/questions/38473207/how-can-i-using-select2-with-webpack). It's not clear how to import those lower-level components. They might be doing something abnormal in this library. The author has been [less than helpful](https://github.com/select2/select2/issues/5372#issuecomment-524153688) as well. – Gili Sep 08 '19 at 04:27
  • @Gili Oh, wow. This is gonna be ugly indeed if you need to import those submodules. – Bergi Sep 08 '19 at 04:41
  • @Bergi I think I figured it out, and yes it is extremely ugly. After `select2` augments the jQuery object, I can get at the loaded component using: `$.fn.select2.amd.require._defined["select2/data/array"]`. Thank you for pointing me in the right direction. You can post an answer mentioning the bit about importing the module and letting Webpack handle the rest and I will mark it as accepted. Out of curiosity, do you think there is a cleaner way to get at this component than accessing the AMD loader's internal variables? – Gili Sep 08 '19 at 04:43
  • @Gili I think `import ArrayData from 'select2/array/data';` is still the right way - that's the syntax you should use in your source files. The difficult task is only getting Webpack to emit code that either a) accesses these loader internals (though I'm pretty certain it should look more like `….amd.require("select2/data/array")`) or b) is an amd loader itself and lets select2 hook into the bundle's module registry – Bergi Sep 08 '19 at 04:55

1 Answers1

0

Per Bergi:

You wouldn't write any AMD at all. Just write import ArrayData from 'select2/array/data'; (or whatever you need exactly), and let webpack handle the rest.

It turns out that select2 is a bit more problematic than most libraries. It only augments the jQuery object instead of exporting anything meaningful. Fortunately, I didn't have to jump through too many hoops.

Once the jQuery object is augmented, invoking const ArrayAdapter = $.fn.select2.amd.require("select2/data/array") returned what I wanted.

My understanding is that once import "select2" returns, we are guaranteed that the module finished loading. As such, we don't need to use the asynchronous form of amd.require() because all components should be fully loaded. At least, I haven't run into any errors as of yet...

Gili
  • 76,473
  • 85
  • 341
  • 624