Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Support for ES6 imports in ES5 module

For my 1st year students, I have provided a simple ES5-based library written using the Revealing Module Pattern. Here is a snippet of the "main" module/namespace, which will house other extensions:

window.Library = (function ($) {
    if (!$) {
        alert("The Library is dependent on jQuery, which is not loaded!");
    }

    return {};
})(window.jQuery);

This works for pretty much 99.9% of the students who are new to web-development and are not using fancy things like ES6 in combination with Webpack or Babel.

The 0.1% has now requested me to provide an ES6 based version, which can be imported properly. I'd be happy to provide this, but I'm kind of stuck on how to best approach this.

I obviously want to keep the ES5 way, so my students can just include the file using a script-tag and type Library.SomeExtension.aFunction(); where-ever they please. On top of that, some of the extensions are reliant on jQuery, which gets injected in a similar way as the snippet above.

I'm now looking for some maintainable way to get the best of both worlds, with one code-base, with jQuery as a dependency. I want to give 99.9% a window.Library, whereas I want to give the 0.1% a way to use import Library from 'library'.

Can I accomplish this with a single JS file that does both? Or would I need a special ES6 version (not a problem to do)? And most of all: How would I reshuffle my code (all similar to above snippet) in such a way I can support both situations?

Any and all pointers will be greatly appreciated!

EDIT: Just as a side note, I already have a gulpfile.js in place which runs this library through Babel, minifiers and other things. So having to extend that to solve the above problem is not a problem!

like image 665
Lennard Fonteijn Avatar asked Nov 25 '19 23:11

Lennard Fonteijn


People also ask

Is import supported in ES5?

ES5 uses the require module to import any member module in our script file.

Does ES6 support import?

Introduction to ES6 import:The import statement is used to import modules that are exported by some other module. A module is a file that contains a piece of reusable code. The import modules are in strict mode whether it is declared or not.

How can I conditionally import an ES6 module?

To conditionally import an ES6 module with JavaScript, we can use the import function. const myModule = await import(moduleName); in an async function to call import with the moduleName string to import the module named moduleName . And then we get the module returned by the promise returned by import with await .

Should I use ES6 or ES5?

From the perspective of browser support and code performance, I suggest you keep the ES6 syntax instead of converting to ES5. Unless you do have many customers using old browsers.


1 Answers

After a few nights of moving code around and installing so many gulp packages I don't even remember all the ones I've tried, I eventually settled on something I'm semi-happy with.

First, to answer my own question, which was also backed up by @Bergi in the comments:

Short answer: No, you cannot, in any way (right now), mix ES6 syntax with ES5 (modules) in a single file because the browser will error out on the use of export.

Long answer: Technically you can set your script-element type to "module", which will make browsers accept the export keyword. However, your code will simply run twice (once on load, and once after importing). If you can live with this, then the no becomes a yes, kinda.

So what did I end up doing? Let me put up front I really wanted to keep the IIFE Module setup I had (for now) in the output ES5 file. I updated my code-base to pure ES6 and tried all kinds of combinations of plugins (including Rollup.js mentioned by @David Bradshaw). But I simply couldn't get them working, or they would completely mangle the output in such a way the browsers couldn't load the module anymore, or drop sourcemap support. With the project the students are doing right now drawing to a close, I already wasted enough of my spare time for the 0.1% of my students. So better luck next year for a "nicer" solution.

  1. I based my UMD pattern (as mentioned by @Sly_cardinal) on jQuery and threw that code into a Library.umd.js. For the ES6 part, I made a Library.es6.js with only a export default Library in it.

  2. In both files, I placed a multiline-comment /*[Library.js]*/ where I wanted to inject the concatenated un-minified Library.js.

  3. I modified my existing Gulp task to just build the Library.js with concat and Babel and stored it on a temporary location. I chose to sacrifice source-map support in this process, as the concatenated code was perfectly readable in the output. Technically source-map support "worked", but it would still throw off the debugger too much.

  4. I added an additional Gulp task to read the contents of the temp Library.js, then for each of the files from Step 1, I would find and replace the multi-line comment with the contents. Save the resulting files, and then throw them through the final concat (eg. bundle with jQuery), terser and sourcemap generation.

The end result is two files:

  • One with UMD/ES5-browser support
  • One with ES6 support

While I'm happy with the result, I don't like the Gulp task chain and the loss of sourcemap support in the first half of the process. As said earlier, I've tried numerous gulp plugins to achieve the same effect (eg. gulp-inject), but they either didn't work properly or did weird things to the sourcemap (even if they claimed to support it).

For a next endeavour I might look into something different than Gulp, or whip up my own plugin which does the above, but properly :P

like image 66
Lennard Fonteijn Avatar answered Oct 20 '22 15:10

Lennard Fonteijn