ES6 In Depth: Modules

ES6 In Depth is a series on new features being added to the JavaScript programming language in the 6th Edition of the ECMAScript standard, ES6 for short.

When I started on Mozilla’s JavaScript team back in 2007, the joke was that the length of a typical JavaScript program was one line.

This was two years after Google Maps launched. Not long before that, the predominant use of JavaScript had been form validation, and sure enough, your average <input onchange=> handler would be… one line of code.

Things have changed. JavaScript projects have grown to jaw-dropping sizes, and the community has developed tools for working at scale. One of the most basic things you need is a module system, a way to spread your work across multiple files and directories—but still make sure all your bits of code can access one another as needed—but also be able to load all that code efficiently. So naturally, JavaScript has a module system. Several, actually. There are also several package managers, tools for installing all that software and coping with high-level dependencies. You might think ES6, with its new module syntax, is a little late to the party.

Well, today we’ll see whether ES6 adds anything to these existing systems, and whether or not future standards and tools will be able to build on it. But first, let’s just dive in and see what ES6 modules look like.

Module basics

An ES6 module is a file containing JS code. There’s no special module keyword; a module mostly reads just like a script. There are two differences.

  • ES6 modules are automatically strict-mode code, even if you don’t write "use strict"; in them.

  • You can use import and export in modules.

Let’s talk about export first. Everything declared inside a module is local to the module, by default. If you want something declared in a module to be public, so that other modules can use it, you must export that feature. There are a few ways to do this. The simplest way is to add the export keyword.

<pre>
// kittydar.js - Find the locations of all the cats in an image.
// (<a href="https://harthur.github.io/kittydar/" target="_blank">Heather Arthur wrote this library for real</a>)
// (but she didn't use modules, because it was 2013)

<strong>export</strong> function detectCats(canvas, options) {
var kittydar = new Kittydar(options);
return kittydar.detectCats(canvas);
}

<strong>export</strong> class Kittydar {
... several methods doing image processing ...
}

// This helper function isn't exported.
function resizeCanvas() {
...
}
...
</pre>

You can export any top-level function, class, var, let, or const.

And that’s really all you need to know to write a module! You don’t have to put everything in an IIFE or a callback. Just go ahead and declare everything you need. Since the code is a module, not a script, all the declarations will be scoped to that module, not globally visible across all scripts and modules. Export the declarations that make up the module’s public API, and you’re done.

Apart from exports, the code in a module is pretty much just normal code. It can use globals like Object and Array. If your module runs in a web browser, it can use document and XMLHttpRequest.

In a separate file, we can import and use the detectCats() function:

<pre>
// demo.js - Kittydar demo program

import {detectCats} from "kittydar.js";

function go() {
var canvas = document.getElementById("catpix");
var cats = detectCats(canvas);
drawRectangles(canvas, cats);
}
</pre>

To import multiple names from a module, you would write:

<pre>
import {detectCats, Kittydar} from "kittydar.js";
</pre>

When you run a module containing an import declaration, the modules it imports are loaded first, then each module body is executed in a depth-first traversal of the dependency graph, avoiding cycles by skipping anything already executed.

And those are the basics of modules. It’s really quite simple. ;-)

Export lists

Rather than tagging each exported feature, you can write out a single list of all the names you want to export, wrapped in curly braces:

<pre>
export {detectCats, Kittydar};

// no `export` keyword required here
function detectCats(canvas, options) { ... }
class Kittydar { ... }
</pre>

An export list doesn’t have to be the first thing in the file; it can appear anywhere in a module file’s top-level scope. You can have multiple export lists, or mix export lists with other export declarations, as long as no name is exported more than once.

Renaming imports and exports

Once in a while, an imported name happens to collide with some other name that you also need to use. So ES6 lets you rename things when you import them:

<pre>
// suburbia.js

// Both these modules export something named `flip`.
// To import them both, we must rename at least one.
import {flip as flipOmelet} from "eggs.js";
import {flip as flipHouse} from "real-estate.js";
...
</pre>

Similarly, you can rename things when you export them. This is handy if you want to export the same value under two different names, which occasionally happens:

<pre>
// unlicensed_nuclear_accelerator.js - media streaming without drm
// (not a real library, but maybe it should be)

function v1() { ... }
function v2() { ... }

export {
v1 as streamV1,
v2 as streamV2,
v2 as streamLatestVersion
};
</pre>

Default exports

The new standard is designed to interoperate with existing CommonJS and AMD modules. So suppose you have a Node project and you’ve done npm install lodash. Your ES6 code can import individual functions from Lodash:

<pre>
import {each, map} from "lodash";

each([3, 2, 1], x => console.log(x));
</pre>

But perhaps you’ve gotten used to seeing _.each rather than each and you still want to write things that way. Or maybe you want to use _ as a function, since that’s a useful thing to do in Lodash.

For that, you can use a slightly different syntax: import the module without curly braces.

<pre>
import _ from "lodash";
</pre>

This shorthand is equivalent to import {default as _} from "lodash";. All CommonJS and AMD modules are presented to ES6 as having a default export, which is the same thing that you would get if you asked require() for that module—that is, the exports object.

ES6 modules were designed to let you export multiple things, but for existing CommonJS modules, the default export is all you get. For example, as of this writing, the famous colors package doesn’t have any special ES6 support as far as I can tell. It’s a collection of CommonJS modules, like most packages on npm. But you can import it right into your ES6 code.

<pre>
// ES6 equivalent of `var colors = require("colors/safe");`
import colors from "colors/safe";
</pre>

If you’d like your own ES6 module to have a default export, that’s easy to do. There’s nothing magic about a default export; it’s just like any other export, except it’s named "default". You can use the renaming syntax we already talked about:

<pre>
let myObject = {
field1: value1,
field2: value2
};
export {myObject as default};
</pre>

Or better yet, use this shorthand:

<pre>
<strong>export default</strong> {
field1: value1,
field2: value2
};
</pre>

The keywords export default can be followed by any value: a function, a class, an object literal, you name it.

Module objects

Sorry this is so long. But JavaScript is not alone: for some reason, module systems in all languages tend to have a ton of individually small, boring convenience features. Fortunately, there’s just one thing left. Well, two things.

<pre>
import * as cows from "cows";
</pre>

When you import *, what’s imported is a module namespace object. Its properties are the module’s exports. So if the “cows” module exports a function named moo(), then after importing “cows” this way, you can write: cows.moo().

Aggregating modules

Sometimes the main module of a package is little more than importing all the package’s other modules and exporting them in a unified way. To simplify this kind of code, there’s an all-in-one import-and-export shorthand:

<pre>
// world-foods.js - good stuff from all over

// import "sri-lanka" and re-export some of its exports
export {Tea, Cinnamon} from "sri-lanka";

// import "equatorial-guinea" and re-export some of its exports
export {Coffee, Cocoa} from "equatorial-guinea";

// import "singapore" and export ALL of its exports
export * from "singapore";
</pre>

Each one of these export-from statements is similar to an import-from statement followed by an export. Unlike a real import, this doesn’t add the re-exported bindings to your scope. So don’t use this shorthand if you plan to write some code in world-foods.js that makes use of Tea. You’ll find that it’s not there.

If any name exported by “singapore” happened to collide with the other exports, that would be an error, so use export * with care.

Whew! We’re done with syntax! On to the interesting parts.

What does import actually do?

Would you believe… nothing?

Oh, you’re not that gullible. Well, would you believe the standard mostly doesn’t say what import does? And that this is a good thing?

ES6 leaves the details of module loading entirely up to the implementation. The rest of module execution is specified in detail.

Roughly speaking, when you tell the JS engine to run a module, it has to behave as though these four steps are happening:

  1. Parsing: The implementation reads the source code of the module and checks for syntax errors.

  2. Loading: The implementation loads all imported modules (recursively). This is the part that isn’t standardized yet.

  3. Linking: For each newly loaded module, the implementation creates a module scope and fills it with all the bindings declared in that module, including things imported from other modules.

    This is the part where if you try to import {cake} from "paleo", but the “paleo” module doesn’t actually export anything named cake, you’ll get an error. And that’s too bad, because you were so close to actually running some JS code. And having cake!

  4. Run time: Finally, the implementation runs the statements in the body of each newly-loaded module. By this time, import processing is already finished, so when execution reaches a line of code where there’s an import declaration… nothing happens!

See? I told you the answer was “nothing”. I don’t lie about programming languages.

But now we get to the fun part of this system. There’s a cool trick. Because the system doesn’t specify how loading works, and because you can figure out all the dependencies ahead of time by looking at the import declarations in the source code, an implementation of ES6 is free to do all the work at compile time and bundle all your modules into a single file to ship them over the network! And tools like webpack actually do this.

This is a big deal, because loading scripts over the network takes time, and every time you fetch one, you may find that it contains import declarations that require you to load dozens more. A naive loader would require a lot of network round trips. But with webpack, not only can you use ES6 with modules today, you get all the software engineering benefits with no run-time performance hit.

A detailed specification of module loading in ES6 was originally planned—and built. One reason it isn’t in the final standard is that there wasn’t consensus on how to achieve this bundling feature. I hope someone figures it out, because as we’ll see, module loading really should be standardized. And bundling is too good to give up.

Static vs. dynamic, or: rules and how to break them

For a dynamic language, JavaScript has gotten itself a surprisingly static module system.

  • All flavors of import and export are allowed only at toplevel in a module. There are no conditional imports or exports, and you can’t use import in function scope.

  • All exported identifiers must be explicitly exported by name in the source code. You can’t programmatically loop through an array and export a bunch of names in a data-driven way.

  • Module objects are frozen. There is no way to hack a new feature into a module object, polyfill style.

  • All of a module’s dependencies must be loaded, parsed, and linked eagerly, before any module code runs. There’s no syntax for an import that can be loaded lazily, on demand.

  • There is no error recovery for import errors. An app may have hundreds of modules in it, and if anything fails to load or link, nothing runs. You can’t import in a try/catch block. (The upside here is that because the system is so static, webpack can detect those errors for you at compile time.)

  • There is no hook allowing a module to run some code before its dependencies load. This means that modules have no control over how their dependencies are loaded.

The system is quite nice as long as your needs are static. But you can imagine needing a little hack sometimes, right?

That’s why whatever module-loading system you use will have a programmatic API to go alongside ES6’s static import/export syntax. For example, webpack includes an API that you can use for “code splitting”, loading some bundles of modules lazily on demand. The same API can help you break most of the other rules listed above.

The ES6 module syntax is very static, and that’s good—it’s paying off in the form of powerful compile-time tools. But the static syntax was designed to work alongside a rich dynamic, programmatic loader API.

When can I use ES6 modules?

To use modules today, you’ll need a compiler such as Traceur or Babel. Earlier in this series, Gastón I. Silva showed how to use Babel and Broccoli to compile ES6 code for the web; building on that article, Gastón has a working example with support for ES6 modules. This post by Axel Rauschmayer contains an example using Babel and webpack.

The ES6 module system was designed mainly by Dave Herman and Sam Tobin-Hochstadt, who defended the static parts of the system against all comers (including me) through years of controversy. Jon Coppeard is implementing modules in Firefox. Additional work on a JavaScript Loader Standard is underway. Work to add something like <script type=module> to HTML is expected to follow.

And that’s ES6.

This has been so much fun that I don’t want it to end. Maybe we should do just one more episode. We could talk about odds and ends in the ES6 spec that weren’t big enough to merit their own article. And maybe a little bit about what the future holds. Please join me next week for the stunning conclusion of ES6 In Depth.

About Jason Orendorff

More articles by Jason Orendorff…


33 comments

  1. Filipe Silva

    This has been a great series of articles. Thank you for doing it.

    I’ve been following every week and am now in the process of updating part of my company front-end to es6, and reading all of these insights really helps.

    August 15th, 2015 at 02:06

  2. Vincent

    Thanks for the series! Looking forward to the conclusion :)

    August 15th, 2015 at 02:20

  3. voracity

    So ‘class’ brings us into the realm of static, but really it just codifies what everyone was messily doing before with new . (And everyone has been thoroughly warned about overuse of ‘extends’, so hopefully no hierarchy nightmares.)

    This looks different. I hadn’t given much thought to it when I first saw it years ago, but if the ‘class’ keyword is a little bit anti-JS (where in the past I used JS as a synonym for “dynamic almost-everything”), this looks completely anti-JS.

    What we critically needed was a simple in-built import() function that worked mostly like require(), maybe coupled with decorators (for exporting and/or privatising things) and destructuring to pick what we want to import. (Of course, we already have destructuring for exactly this kind of thing, but for some reason the module destructuring has a subtly different syntax to the standard ES6 destructuring! Why?) Anyway, the whole thing could have desugared into pretty straightforward ES6 minus modules. No DSLs required. (Didn’t I express concerns about these recently on here? :\ ) What we have got now instead is a lot of module-specific syntax, encoding some people’s *static* hunches (static in more ways than one) about what will work best for the web, which will probably be irrelevant in 10 years time or so.

    Sorry, that comes across a little negative. On a (mostly) more positive note: I’ve really enjoyed the ES6 articles and am disappointed to see them come to an end.

    August 15th, 2015 at 05:52

    1. Jason Orendorff

      I wish I’d had your response while I was writing the article. It would have been a better article.

      A “simple”, synchronous require() was never in the cards for ES, because ES has to work in the browser. Loading a module may involve fetching code off the internet, which can’t be done synchronously. Something asynchronous, like the require() function that require.js provides, will be standardized. But that kind of API isn’t pretty to use in every single file you write. Node users were not going to migrate to an asynchronous API; they’d stick with require(). New import syntax was a way—maybe the only way—to provide a standard module system that both client and server JS code would actually use.

      It’s unfair to call the static parts of the design “hunches”, because that ignores a decades-long history of programming languages with modules. Looking at systems designed 10 and 20 years ago, it looks like what’s in ES6 is stuff programmers need, and it’s hard to see those very basic needs being radically different in 10 years’ time. We’ll see how it plays out. I think the designers were influenced by Racket and maybe Python (both dynamic languages with good module systems). They also benefited from the experience of the JS community, particularly Yehuda Katz.

      I really like this system. It’s great to use in practice, and a big improvement over what we’ve been doing.

      I’m also impatient for the dynamic APIs to be standardized. I’ll poke the standard committee a little bit on this topic. While writing this, I realized it’s possible to standardize some of those APIs in the short term, while the details of the loading process (particularly user configuration and customization of loading) are still being worked out.

      August 15th, 2015 at 08:42

      1. voracity

        “New import syntax was a way—maybe the only way—to provide a standard module system that both client and server JS code would actually use.” Ah, OK. Thanks. So a new syntax was used so that the syntax for importing could be agnostic between synchronous and asynchronous loading?

        It doesn’t seem particular agnostic, though. As you mention below, a JS block with this import syntax has to block (and everything after it) until the import is finished. So it’s not possible to use this syntax on the client-side (as it stands) as PhistucK mentions. In fact, the syntax is pretty clearly not async friendly.

        In any event, as per David Mulder, yield and async/await would be great solutions on the server-side, no? I’ve already been using yield to de-uglify database code in node.js. In fact, I’m starting to think that every JS block should be implicitly treated as async. (Maybe that’s too expensive?)

        Sorry, “hunch” was a little heavy. It was in reference to the relatively new territory of modules for web-apps (in particular, network based module loading), which would require *anyone* to make guesses about what’s needed, not to modules in traditional software. One of those guesses is how the async story will play out. On reflection, I think this may be why it’s so hard to get HTML imports/web components/overlays or anything similar going — i.e. because nobody really has a good idea of what we need/want.

        I suppose the syntax will come in handy, but it just doesn’t integrate conceptually in any way with anything else in JS. :) But I eagerly look forward to seeing the dynamic APIs. System.import (if that’s what we get) looks quite nice.

        August 16th, 2015 at 06:21

        1. Jason Orendorff

          > So it’s not possible to use this syntax on the client-side (as it stands) as PhistucK mentions.

          People are already using it on the client via compilation. You should try it out; a test drive might be more enlightening than further conversation here.

          Future standards will make it possible without compilation.

          > In fact, the syntax is pretty clearly not async friendly.

          I have to disagree. The module system works very much like require.js in environments, like the browser, where all I/O is async. This is exactly what the CommonJS require() function cannot do because it’s a synchronous function.

          August 17th, 2015 at 13:42

  4. PhistucK

    What if I mix modules and global code?
    Do modules have access to developer-defined global variables and objects?

    For example –

    // module.js
    export function bar()
    {
    return foo;
    }
    // Regular external script
    import * from “module.js”
    var foo = 8;

    August 15th, 2015 at 07:32

    1. PhistucK

      More accurately –
      // Regular external script
      import { bar } from “module.js”
      var foo = 8;
      console.log(bar());

      Should it print 8?

      August 15th, 2015 at 07:33

    2. Jason Orendorff

      Modules do have access to global variables, yes. Like a function’s scope, they’re nested inside the global scope.

      But plain <script> scripts can’t use import syntax. One reason is that a normal <script> runs synchronously and blocks the web page. If you think about it, with import, each <script> might load not just a single file but a whole dependency tree. Definitely not what you want.

      This leaves the issue: modules can access globals easily, but how do global scripts access modules? One way is for the script to use an API provided by your module loader. That API is just like require.js, so this isn’t hard or unpleasant to do. It’s just not as nice as real import syntax.

      In the long term, you’ll be using modules for everything, so you’ll have import everywhere.

      August 15th, 2015 at 09:14

      1. PhistucK

        Huh… While you answered my question, you just made things less understandable…
        So, what is the entry point to modules, if you cannot use it in a (script src=”stuff.js”) where stuff.js has “import { bar } from ‘baz.js'”?
        Where does it start? Module loaders (what, require.js for example?) still load stuff using either XMLHttpRequest (and then eval, which cannot be used for modules, right?), or script elements (which you say cannot be used for modules as well).

        August 15th, 2015 at 12:23

        1. Jason Orendorff

          Sorry about that!

          The entry point to modules for now is that all your modules just compile down to ES5 scripts. Then it’s simple. You just load them like any other script, using <script src=...>.

          Once browsers get native support for modules, the entry point will be something like <script type="module" src=...>.

          August 17th, 2015 at 01:02

          1. PhistucK

            Hm. I do not understand. Suppose browsers implement the ECMAScript 2015 version of modules. How will I be able to use modules?
            Is script type=”module” standardized? If not, then this is not a way. What is the standard way of using modules in browsers, assuming browsers implement the entire ECMAScript 2015?

            August 17th, 2015 at 01:22

          2. Jason Orendorff

            > Is script type=”module” standardized?

            No, not yet.

            > What is the standard way of using modules in browsers, assuming browsers implement the entire ECMAScript 2015?

            There isn’t one yet.

            ES6 only specifies the JS language parts, not the HTML parts. Those will have to come later. In the meantime you can use compilation.

            August 17th, 2015 at 13:51

  5. David Mulder

    Already played around with modules in the past, but reading through this article I realized that I will probably stick to using `require` even once ES6 is generally available. That list of weaknesses (“Static vs. dynamic, or: rules and how to break them”) really hit home hard. I mean, not like I dislike static languages, but it doesn’t make any sense whatsoever in a language like Javascript. Why couldn’t an import simply return a promise and if you wanted the current behaviour you would simply write `await import x` (true that’s ES7, but that’s not going to take *that* long). I mean, the export side of things looks fine enough (don’t see why it had to be so strict, but don’t expect any real problems with that either), it’s just the import side which is crap. Something like ‘you can’t use import in a try catch’ should be enough reason to not use imports at all… :S Ach.

    And that’s quite a waste@next article being the last already, but thinking back you did cover mostly everything there was to cover yes.

    August 15th, 2015 at 09:03

  6. Thodoris Greasidis

    No comment about es6-module-loader
    and System.import :(
    There was a time that it was part of an ES6 draft.
    http://www.2ality.com/2014/09/es6-modules-final.html

    August 15th, 2015 at 09:41

    1. Jason Orendorff

      I did link to the ongoing work on that (the Loader Standard).

      It’s just not part of ES6, that’s all!

      August 17th, 2015 at 01:05

  7. Eduncle

    thanks for this article about ES6. In your next article I am expecting some more details about code splitting & modules for which it is required.

    August 17th, 2015 at 05:05

  8. Vladimir Starkov

    Can anybody explain will transpiling be required in the future, when browsers will support es modules natively?

    August 17th, 2015 at 05:48

  9. Šime Vidas

    Can a module import both the default export and other named exports from another module, at the same time?

    // e.g.
    import { each, map, default as _ } from ‘lodash’;

    August 17th, 2015 at 10:39

    1. Vladimir Starkov

      You can do it like this:

      import _, { each, map } from ‘lodash’;

      August 17th, 2015 at 10:59

    2. Jason Orendorff

      Yes. Your example and Vladimir’s example are equivalent.

      August 17th, 2015 at 12:18

  10. Rich Brown

    Great article, and I have a follow-on question about ES6 modules:

    I want to write a communication “module” that takes care of establishing a TLS connection, authentication to the other end, converting JSON into XML to send on the wire, tracking all kinds of internal state, etc, while exposing a pretty simple Open/Close/Set/Get API to the rest of the program.

    I strongly feel the urge to break this “module” into separate (short) files so the TLS connection handling can live separate from the JSON XML code, etc.

    What’s the best way to do this? Many thanks!

    August 18th, 2015 at 04:34

  11. Jury Xiong

    I’ve translated this article into Chinese, I wish I have your permission.

    http://www.pwhack.me/post/2015-08-18

    August 18th, 2015 at 06:02

  12. Cédric

    Great article and great series, thank you!

    Quick question.
    AFAIK, we have to do:

    import * as Rx from ‘rx’;
    Rx.Oservable.fromArray();

    What is the reason for not having?

    import * from ‘rx’;
    Observable.fromArray();

    We could do “import {Observable} from ‘rx'” but that is quickly painful when the module is exporting a lot of things…

    August 19th, 2015 at 02:35

    1. Jason Orendorff

      This kind of import * makes it so you can’t tell just by looking at the source code what names are imported.

      If you have two import * declarations in the same file, there can be a naming conflict (both modules export the same name).

      If an import * declaration imports something with the same name as a global, like Promise, it will simply shadow the global, and it’ll be unclear what’s going on.

      Worse yet, the imported names can change without any changes to your code. So if everything works out now, it can always break later if any of those imported modules ever add a new feature with the same name as something else in your module. (I really think adding features shouldn’t have to be a potentially breaking change!)

      For all these reasons, import * is somewhat frowned upon in languages that have it, like Haskell and Python. Fortunately, in JS, we already (as a general rule) try to keep our namespaces tidy by using namespace objects like Rx. We have good habits. So although import * was discussed, the designers decided it wasn’t needed.

      (Interestingly, Python’s style guide says “There is one defensible use case for a wildcard import, which is to republish an internal interface as part of a public API.” ES6 modules do support this, but it’s spelled differently: export * from "blah";.)

      August 21st, 2015 at 07:17

  13. Caridy Patiño

    Late to the party here, but for what It’s worth, some clarifications:

    @voracity we are definitely looking into async/await for the import declarations. The problem with node in general is that top level await is problematic in many ways, and something will have to change I’m afraid, it is just the fact that the consumer of any module with a top level away will mess up the entire sync loading process in node (even if you stick to `require`). We will see.

    @David Mulder, you cannot do a try/catch today with regular script tags unless you load and eval (which is subject to other restrictions) manually, nothing has changed in that regard. The fact that you can do try/catch in a systems like node (e.g.: try/catch a require call), does not help in a browser env. Keep in mind that you will be able to use the imperative API to have more control at the top level: `System.loader.import(‘foo’).catch(…)`.

    @Thodoris Greasidis, `System.loader.import()` will be the imperative form to load and execute modules from a regular script (working on the spec for that as we speak).

    @Vladimir Starkov, we will continue using transpilation for the foreseeable future, but lets not confuse transpilation with bundling. Transpilation from JS to JS will still be useful as a way to use new features of the language, extensions of the language on the user-land (a la JSX), optimizations (a la uglify), etc. On the other hand, bundling is just allowing us to load and execute various module formats as single scripts. That’s probably the part that will get reshaped once browsers ship modules and the loader, maybe focusing more on folding modules rather than bundling them into scripts.

    @Cédric, that’s ambiguous, now the importer has to know two things, the module specifier (`rx`) and the identifier `Observable` which somehow have to be defined in the exporter module. This will be a refactor hazard as well.

    August 20th, 2015 at 16:32

  14. Christian Bankester

    Class declarations are not hoisted, so your example above:

    “`
    export {detectCats, Kittydar};

    // no `export` keyword required here
    function detectCats(canvas, options) { … }
    class Kittydar { … }
    “`

    wouldn’t work, right?

    August 21st, 2015 at 09:36

    1. Jason Orendorff

      The class name is visible throughout the module—the name is hoisted to the top of the enclosing scope. But the actual creation of the class isn’t hoisted. That only happens when the class declaration runs.

      So the example will work, as long as we don’t somehow call detectCats() before the class declaration runs.

      And if you import this module, this class declaration will run before your code, so it should be fine.

      August 21st, 2015 at 10:01

  15. Owen Densmore

    I love modules. But there are problems.

    1 – Interoperability w/ es5. When you use modules in babel, it produces dependencies. Drop this into the babel repl
    import foo from ‘foo.js’
    var lib = {}
    export default lib

    2 – import exposes file locations for modules, unless you do a lot of webpack/jspm configuration. Yes, es5 has tag hell, but at least we understand it!

    3 – Webpack/jspm: God, not MORE workflow!! Both attempt “bundling” but I’m not sure if that works.

    4 – All this has lead my project-mates to avoid modules, just babel & standard es5 modules and tags galore. Hardly progress!

    I’d love an article on just module loading.

    August 21st, 2015 at 11:44

    1. Filipe Silva

      I’ve been converting a project to webpack+babel and I’ve found the module loading a godsend really. Paths being relative helped a lot keeping stuff modular and sharing them between projects.

      August 21st, 2015 at 12:01

    2. Jason Orendorff

      I won’t deny that there are problems. We’re using the half of the system that’s done (the syntax). The other half (the standard dynamic loading API, plus loader customization) isn’t ready yet.

      The fact that compilers, libraries, and tools built by the community have been able to pull everything together into a coherent system is actually really impressive, given the halfway state of things.

      But let me take your issues point by point.

      1 – I guess the online Babel playground is configured to produce CommonJS modules. That’s configurable, though.

      If you don’t want to configure it yourself, use webpack, and everything will Just Work. webpack tells Babel what kind of module format to output and automatically bundles it with the 20 or so lines of support code it needs.

      2 – This still isn’t standardized, so right now, each ES6 module compiler can do whatever it wants to do.

      The final standard could go either way. I guess I expect filenames to win at this point.

      I want to import packages by package name, not filename. But almost everyone I’ve talked to about this finds filenames more intuitive. And within a package, I have to admit filenames make a lot of sense.

      However, if you insist on not having filenames, I can’t understand also not wanting to configure your loader. That mapping has to be generated somehow. Languages like Java and Python (and Node is this way too) can get away from searching umpteen different directories every time you want to import something. The web is not like that. When you decide to fetch a URL and wait for it to come in, it better be the right one.

      3 – I know – but it worked for me, and I’m sold. If you reluctantly pick up one tool this year, make it webpack.

      Larry Wall used to call laziness one of the three virtues of programming. But he warned against the vice of False Laziness. The truly lazy are unstinting in their efforts to eliminate work.

      4 – Progress is slow because the standardization effort has been slow. That’s the root problem we have to fix.

      August 21st, 2015 at 20:14

      1. Owen Densmore

        Thanks, Jason, for the clear, thoughtful reply.

        September 1st, 2015 at 10:55

  16. Brian De Sousa

    Great article! I just came across this series for the first time… Defitnely going to have to go back and read the previous articles I’m the series. Thanks!

    August 28th, 2015 at 16:26

Comments are closed for this article.