What is the standard approach to ES2015 modular architecture?

I think I now mostly understand (in theory, at least) how ES2015 Modules work.

What I don't understand (and this is far from insignificant) is how to enable module scripts to inform my main script.

Usually, at the bottom of my HTML document, I have something like the following:

<script src="/global-script-is-a-classic-script.js"></script>

That's it. No defer, no async and no type="module".

I may wish to write some optional modules which declare functions, calculate variables and objects and then make all the declared and calculated values available to global-scripts.js:

<script src="/module-script-1.mjs" type="module"></script>
<script src="/module-script-2.mjs" type="module"></script>
<script src="/module-script-3.mjs" type="module"></script>

Naturally each of these module scripts will contain an exports statement.

But... how do I then import what these module scripts calculate into my global-script.js?

I can't use import - because that only works in a Module Script - and (unless I'm mistaken) it seems like it would be a bad idea to have global-scripts.js as anything other than a Classic Script.

So, do I now need to have two global scripts?

<script src="/global-script-is-a-classic-script.js"></script>
<script src="/module-mothership-is-a-module-script.js" type="module"></script>

Or is there an alternative standard architecture which we're all supposed to be using?


Solution 1:

It seems you aren't really understanding the change in design philosophy with a module architecture. You don't make things global. A module imports the other modules that it needs and gets its capabilities from the import, not from some global set of functions. In a modular architecture, there would be NO global script. That's why your thinking isn't quite fitting into the modular architecture.

exports is a means of making certain entry points or data publicly accessible to anyone who imports the module while keeping everything else local to the module. One must import the module to get access to these entry points.

So, is exports intended for communication between sub-modules and modules then, rather than for between modules and a global script? That is to say modules never export, only sub-modules do?

Mostly. But, even a top level module could have exports. They wouldn't be used for anything in the project in which it was a top level module, but it could perhaps be used in a different project where it wasn't a top level module and was imported by some other module. The module architecture gets you to think about code reuse and code sharing without having to do a lot of extra work. The normal way you write your project should automatically create a bunch of reusable code.

That might have been the final piece of the puzzle I needed. So exports is just for sub-modules (and sibling-modules), then? Standalone, self-contained top-level modules don't need exports because any functionality happens right there in the module - the data never needs to go anywhere else. (Is that right?)

Yes. Unless you might want to be able to reuse that module in another project where you did import it and did want to import some entry points or data.

I may wish to write some optional modules which declare functions, calculate variables and objects and, after calculation, make all the declared and calculated values available to global-scripts.js. But... how do I then import what these module scripts calculate into my global-script.js?

You will get rid of your global script entirely in a module design. You will have a top level module that imports other modules.

So, do I now need to have two global scripts?

Nope, you get rid of the global script and you don't make things global any more.

Or is there an alternative standard architecture which we're all supposed to be using?

The module architecture:

Top Level Module
    import module1
       import moduleA
          import moduleZ
       import moduleB
    import module2
       import moduleB
       import moduleC
    import module3
       import moduleA
       import moduleD

This can go to any arbitrary depth as needed. Modules are cached and only initialized once so importing the same module from more than one place is perfectly fine and perfectly efficient.