Module loader is responsible for loading modules.
What I know is module loader loads modules in browser asynchronously whereas in Node.js it loads synchronously.
I wanted to confirm whether this information is correct or not.
ES6 module loaders will be asynchronous while node.js module loaders are not.
Here are some key aspects of module loaders:
Module code automatically runs in strict mode and there’s no way to opt-out of strict mode.
Variables created in the top level of a module are not automatically added to the shared global scope. They exist only within
the top-level scope of the module.
The value of this in the top level of a module is undefined. Does not allow HTML-style comments within the code (a leftover feature from
the early browser days).
Modules must export anything that should be available to code outside of the module.
https://leanpub.com/understandinges6/read#leanpub-auto-modules
Modules, in general, solve several problems for developers. First,
they allow the developer to separate code into smaller pieces, called
modules. Second, they make it easy for developers to load (inject)
those modules into other sections of code. Having modules injected
like this helps keep project code uncoupled from the module (read:
improved testability). And third, modules can load scripts
asynchronously. This means that apps can begin loading faster, as
they don’t require all scripts to be loaded prior to executing code.
http://chimera.labs.oreilly.com/books/1234000001623/ch03.html#_default_values
On the other hand because node.js is based on require which is synchronous this means node.js does not provide an asynchronous variant out of the box.
Of course there are async module loaders for node (async-require), but natively (with require) is not supported.
ESM is intentionally async (to accomodate loading over networks) and the rationale behind import also aims at knowing during code interpretation what dependencies exist (e.g. allowing bundlers to do treeshaking etc). This is also the reason to have imports at the beginning of your files and they can't be conditional (unless you use the await import() syntax).
You can also not eval(readFileSync(moduleFile)) if you use ESM syntax in the loaded file.
Related
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?
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.
So I'm planning on writing a package which a user will hopefully be able to use on both Node.js and in the browser.
On the Node.js side it will use the fs module. This does not exist in the browser perhaps. This could be accomplished with CommonJS with an if clause to check which environment the module is in and a simple require.
This is not the case with import as it is hoisted.
Does anyone have an idea of how to structure the code or the build environment to satisfy both enviroments in the same package?
Stick to ES6 imports
It seems there was an improvement in import support in Node.js. In the docs it says:
The --experimental-modules flag can be used to enable support for
ECMAScript modules (ES modules).
Once enabled, Node.js will treat the following as ES modules when
passed to node as the initial input, or when referenced by import
statements within ES module code:
Files ending in .mjs.
Files ending in .js, or extensionless files, when the nearest parent package.json file contains a top-level field "type" with a
value of "module".
Strings passed in as an argument to --eval or --print, or piped to node via STDIN, with the flag --input-type=module.
The second listed approach may be useable with node.js. Simply make sure your librarie's package.json contains "type": "module" and Node should treat it as a ES6 module instead of CommonJS.
Conditional import as promise - you will have to wait
Conditional imports are already available in node but not actually supported. They seem to work in browser. And there's a caveat: the imported module is a promise. What that means is that you can have partially isomorphic code as long as you plan your steps well. Knowing that you can do import("test.js") in browser and that you can test for window means you can write conditions in a smart way and have cross-platform code.
For example you can do:
if (typeof window !== 'undefined') const FooPromise = import("foo");
But not the opposite. Good luck. Hopefully more support for the es6 dynamic import will come to node.
Time is flying and now the Node.js ver.10.0.0 have been out for the public. While I'm working with my MERN stack, frequently I have to switch between ES6 import/export syntax for the front-end (React.JS) and the CommonJS module.exports/require syntax in my Node/Express server-side. And I'm really wish the writing style could be unified in import/export shortly without using Babel and get it from the native support.
A good news is since the last year I read James' post on Medium addressing in the difficulty of implementing the ES6 module system in Node.js, the experimental ECMAScript Modules is now in stability 1 for a while, which means it could be enabled by --experimental-modules flag.
However, when I'm trying to use import/export syntax on Node, it is never working. For example, I have try this:
import fs from 'fs';
console.log(typeof fs.readFile);
The above code will throw me error:
(function (exports, require, module, __filename, __dirname) { import fs from 'fs';
^^
SyntaxError: Unexpected identifier
I'm really sure I have enabled the experimental flag calling as $node --experimental-modules, so what should I really do in order to kick the ES6 import/export experimental module working on my Node local server? What am I missed?
Added:
The reason why I want to try this experimental feature is to have consistent coding style in both front and back. And because it is now available natively, so I want to get ready to it. Once it have been reach in stage 2, I then can adapted to import/export quickly and have less pain.
But obviously, Google (ref) and AirBnb(ref) have different view points upon if we should use import/export syntax or not in their code-style guide. And based on Google, I'm still surprising that the semantics of ES6 import/export is not yet finalized while ECMA2019 is on its way. I'm just wonder when I can really use import/export in my project, or what is really need to be finalized?
Updated:
After pointed out by Jaromanda X, if I changed my file name to .mjs then it works. Originally I thought is the module to be loaded have to be named in .mjs extension, but it seems I was wrong. However, if that is the case, which means I will need to renamed all my project file in .mjs... and that is not appealing. Just wonder if there is a way to use this feature it in the traditional .js file? What should I configure?
Right at this point ECMAScript Module is not yet finalized in node.js v10, so it is not recommended to use it in the production environment. Since the use of import/export is standardized in the ECMAScript specification, so no matter how, it is important for node to provide such supports for the community.
The major differences between require() and ECMAScript Module is how node is going to handle its cache. Unlike require(), so far ECMAScript Module have no such API to manipulate the module cache. Also, it is unknown if it would be directly supported in the normal *.js files without a loader, so stay tuned.
The direction right now is try to introduce a new *.mjs file to explicitly showing it used the standard JS module (in accordance with ES6), but if one read the doc closely you actually could see it is also possible to specify your own extension match, including the use of the more traditional *.js. However, it required more configurations and the use of --loader flag to load the extra configuration.
There are 6 different formats are supported, including the one for modules written in C++ (it can also be loaded by require()), which is very powerful. In short, to use *.js file extension, the key setting in the loader is to have:
const JS_EXTENSIONS = new Set(['.js', '.mjs']);
And the loader function will need to check as if the file to be loaded is in JS_EXTENSIONS data set. Here, the key in the loader function is that it should return an object that have two properties: url and format, i.e.:
return {
url: (...), // e.g. './x?n=1234'
format: 'esm', // there are 5 + 1 dynamic formatting
};
It would be recommended to named the loader as custom-loader.mjs, and use it as $node --experimental-modules --loader ./custom-loader.mjs since it was demonstrated in the official doc. Once you have configured the loader correctly (based on what you need), then you should be able to use import/export as if you may used in a front-end react app in your node project.
Unfortunately my knowledge of JavaScript module loaders is still growing and I'm trying to understand their relationship to the new ES6 Modules. As far as I can tell using a module loader like CommonJS or RequireJS using ES5 compliant JavaScript really needed the use of an asynchronous module loader to increase performance and load only as needed using the respective module loader's syntax.
However looking at the ES6 module documentation and reading other information, it appears to me that module loading is natively supported via the import and export keywords. If this is the case am I correct that ES6 JS modules natively support asynchronous module loading and therefore I do not need to use an additional tool like CommonJS or RequireJS?
it appears to me that module loading is natively supported via the import and export keywords.
Not exactly. The import and export declarations only define the dependencies and the interface of each module. They allow to statically extract strings that name the required modules, nothing else.
If this is the case, do I not need to use an additional tool like CommonJS or RequireJS?
No. You still need to use a loader for ES6 modules, which resolves the names or paths or whatever from the imports to actual module files and loads them with an implementation-dependent method.
There are many tools or toolchains available, examples for the different solutions are
webpack: bundles everything into one large script
System.js: loads single modules dynamically and asynchronously (similar to what require.js does)
native: node.js and web browsers are still figuring out how to support module loading without additional libraries
babel transpilation: you can convert ES6 modules to AMD or CommonJS format and use the known tools like require.js for these
As far as my understanding goes, ES6 supports the syntax for defining and importing modules. The actual act of importing the modules that are required are a job of the infrastructure.
In modern browsers (as of 2016 that is) do not have built in functionality to support module loading and as such you will still need something like SystemJS to do the actual loading.
ES6 JavaScript Files are inherently treated as a module. if you define anything in a .js file, it will only be visible within that file (local scope ). what export does is, it exposes the classes / variables defined as export, visible to outside. then you can import it to a another module. There are other ways to define modules such as using Commonjs or AMD etc.. . Module loaders are required if you want to dynamically lazy load modules. ex. Systemjs is a such a Dynamic Module loader. it will fetch the physical module file from server dynamically when it is requested, and will prevent having multiple loads the same file. in SPA application in past had to load everything at the beginning to it to work. with dynamic module loaders now we can have only the files we need to do the intended job. hope this will help you.
https://github.com/systemjs/systemjs
Im using requireJS to dynamically load JS modules in my html5 single page web application. Im just wanting to know whether I should dispose of the requireJS loaded modules once i have finished with them (so the garbage collector can clean them up)? And if so than how do you dispose a required module from requireJS?
Thanks in advance
It is possible to undefine a module:
There is a global function, requirejs.undef(), that allows undefining
a module. It will reset the loader's internal state to forget about
the previous definition of the module. However, it will not remove the
module from other modules that are already defined and got a handle on
that module as a dependency when they executed. So it is really only
useful to use in error situations when no other modules have gotten a
handle on a module value, or as part of any future module loading that
may use that module. See the errback section for an example. If you
want to do more sophisticated dependency graph analysis for undefining
work, the semi-private onResourceLoad API may be helpful.
http://requirejs.org/docs/api.html#undef
I'm not too sure on the internals but in my usage of the lib I've not found it necessary to do any manual clean up of modules.