2

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?

Community
  • 1
  • 1
Leo Li
  • 247
  • 4
  • 20
  • do you get a `ExperimentalWarning: The ESM module loader is experimental` warning? – Jaromanda X Apr 26 '18 at 03:54
  • Yes I did... I did see `(node:28118) ExperimentalWarning: The ESM module loader is experimental.` warning. – Leo Li Apr 26 '18 at 03:55
  • 2
    is your file extension `.mjs`? – Jaromanda X Apr 26 '18 at 03:57
  • Not sure, because `fs` is a core module in Node, I believe is not. Does it really have to be affiliated with `.mjs`? Because I just follow the one shows on the official [doc](https://nodejs.org/api/esm.html). And I read couple days ago forget from where saying that if it is not a ES6 module, it will use CommonJS to load the module by `import`, which is what I want for now. – Leo Li Apr 26 '18 at 04:03
  • 1
    no, **your file** what is the extension if **your file** – Jaromanda X Apr 26 '18 at 04:04
  • Oh, it is `app.js`... wait, so the file calling `import/export` have to be named as in `.mjs`?! I though just the module itself to be loaded have to named in `.mjs`. It seems working now. But all my current project is named in `.js`... Is that means if I want to use this syntax I have to renamed all my file...? That is really weird. – Leo Li Apr 26 '18 at 04:07
  • 2
    no idea, while it's experimental, I avoid it :p – Jaromanda X Apr 26 '18 at 04:08
  • 2
    what was so wrong with common.js? Why couldn't that module system be the official spec client side? Es modules import/export feels unnatural in JavaScript. – Darkrum Apr 26 '18 at 04:32
  • The sentence "*Note that this policy will be revisited once the semantics are fully-standard.*" in the style guide doesn't mean that module semantics are not yet finalized. It just means that the style guide is severely outdated. – Bergi Apr 26 '18 at 07:52
  • 2
    @Darkrum Commonjs `require` is not declarative and has no support for asynchronous loading – Bergi Apr 26 '18 at 07:54
  • If you want to rename all your files you can run in the root directory that you want to change: `for /R %x in (*.js) do ren "%x" *.mjs` – Get Off My Lawn Apr 26 '18 at 16:01
  • @Bergi last I checked JavaScript wasn't a completely declarative language like that. And I don't think you understand want asynchronous loading actually means in the es module sense. Let's call it what it is. People that came to JavaScript from another language when node.js arrived and wanted to bring there languages syntax into JavaScript where it doesn't belong. – Darkrum Apr 27 '18 at 20:46
  • 1
    @Darkrum That's how language evolution works: good and proven features get integrated when they are useful. It's the same for `class` declarations. They *do* belong to JS, and so does a module syntax - missing native modules were one of the major headaches. – Bergi Apr 27 '18 at 21:01

1 Answers1

2

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.

Cyanhaze
  • 90
  • 9