3

As title. Since Node.js now supports ES6 module, you can simply add "type": "module" in your package.json to facilitate the import/export syntax even without the --experimental-modules flag to run your code via the node CLI-command without any problem. To me:

  1. This also implies that we actually don't need to transpile our ES6 module syntax to those require() from CommonJS spec in our code.
  2. But in Node.js v16.x documentation, they explicitly list some differences between the specs of both CommonJS (module) and ECMAScript module (loaders). So it's actually NOT as simple as doing the same thing differently. There must be some trade-offs to prefer one over the other, and this is what I want to know!

So why do some people want to use ES6 module? Is it only for importing modules asynchronously? I don't think so. Their most be some reasons more important than this. But apparently moving toward the newest syntax will be a trend. Any idea?


p.s. I've read some old threads saying that most testing frameworks haven't supported ES6 module syntax, and from the last point on the list of CommonJS:

It cannot be used to load ECMAScript modules (although it is possible to load ECMAScript modules from CommonJS modules).

It means (well, indeed not that readable at first look) that direct import of ES6 modules is NOT possible with CommonJS module but you can do it indirectly. This could be one of the reasons most people don't bother to migrate to the newest import syntax on Node.js.

Can anyone please correct me if some statement(s) I provided above is wrong?

NeoZoom.lua
  • 2,269
  • 4
  • 30
  • 64
  • You can import CJS modules with ES6, but you can't import ES6 modules with CJS. That's the main reason. Many library are only available as ES6 module. – jabaa Mar 24 '22 at 10:08
  • "[...] That's the main reason." to prefer ES6 module syntax, do you mean this? And notice that this is the quote I have added in my post @jabaa – NeoZoom.lua Mar 24 '22 at 10:15
  • The main reason from what I see is just fashion. It's trendy to use ES6 syntax. That's all. CommonJS `require()` being a function instead of a syntax is much more flexible and powerful IMHO. But some people (especially from the OO world) don't feel comfortable with dynamic behavior – slebetman Mar 24 '22 at 10:15
  • @slebetman: I understood. But your statement implies companies __won't__ care about using the ES6 module syntax, since no need to spend money on things already work without benefits, and people working on Node.js actually don't need to provide the list I linked. They can just say that they were the same. – NeoZoom.lua Mar 24 '22 at 10:24
  • 1
    AFAIK @slebetman Is wrong. ES6 has the import syntax and the `import` function, see last line in the first code snippet https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import ES6 is a superset of CJS. You can replace CJS with ES6 but you can't replace ES6 with CJS in many cases. – jabaa Mar 24 '22 at 10:28
  • You should also compare the functionality and performance. AFAIK ES6 is technically better, e.g. it allows (better) tree shaking. – jabaa Mar 24 '22 at 10:34
  • 1
    ES6 modules are a standardized part of the language, not a solution (more or less) specific to Node.js. Sticking to language standards ensures better compatibility across environments. The "static-ness" of ES6 modules also makes things such as tree shaking easier for module bundles. And an increasingly number of packages are shared across Node.js and the web. Sure, if you write something that is specifically only for Node.js and you are not using Typescript then you might as well stick with `require()`. – Felix Kling Mar 24 '22 at 10:36
  • @jabaa: Hi, thanks for your confirmation. If possible, could you provide the reference of "ES6 is a superset of CJS"? (I suppose your CJS should mean CommonJS) – NeoZoom.lua Mar 24 '22 at 10:51
  • @FelixKling: Hi, thanks for your info. Your(and jabaa mentioned this too) point "[...] The "static-ness" of ES6 modules also makes things such as tree shaking easier for module bundles. [...]" convince me of migrating to ES6 should be a good idea in the long run, since minimizing things are important in both front/back-ends. If you're willing to, you could put it into an answer. – NeoZoom.lua Mar 24 '22 at 10:55
  • 1
    I don't have a reference. I started my comment with AFAIK. I don't know any case where I can't replace CJS modules with ES6, but I know cases where I can't replace ES6 modules with CJS. ES6 import can import ES6 modules and CJS modules, but CJS require can't import some ES6 modules. You can replace `require()` with `import()` and handle the promise. AFAIK there are no advantages for CJS modules and you should prefer ES6 modules. – jabaa Mar 24 '22 at 11:16
  • @jabaa: It's OK. I just guessed that people might ask for it in the future. You could organize your comments into an answer if you have time, and I will be really appreciated about that. Btw, is the asynchronous-nature of `import` really a thing? I would like to know whether people really found it useful in practice. (It must be useful on front-end, so my focus is on environments outside front-end) – NeoZoom.lua Mar 24 '22 at 12:31
  • 1
    There is no synchronous `import` function, and you need the asynchronous `import` function for dynamic imports in ES6, e.g. optional dependencies and optional functionality, unit tests with mocked modules. I don't see the point of this question. You're asking about the advantages of ES6, but I would ask about advantages of CJS and reasons to use it nowadays. I guess, CJS is going to die out soon, because more and more libraries are written in or converted to ES6 modules. Browsers, Node.js and Deno support ES6 modules. – jabaa Mar 24 '22 at 12:45
  • @jabaa: To me, an apparent inconvenience of (async.-)`import` is that I'm not sure about whether I can directly use the package on the next line of the `import`(not `import()`) statement __without__ adding many `Promise`-related checking(e.g. `.then()`) to make sure that those packages _did imported_ (I consider this a drawback of async., not backward-compatible). (I'm not an export on `Promise`-syntax, so if anything is wrong please kindly notify me.) Fun fact: this comment section is gradually turning into a `node_modules/`. Maybe we could discuss this under your answer. – NeoZoom.lua Mar 24 '22 at 13:04
  • @Rainning As you can see, it's just fashion. People really wanted to write ES6 modules because they want to be trendy and follow "browser" standards. The browser guys decided that a function is not what they wanted. In terms of sync or async in node.js (or even in browser) the `import` behaves synchronously in terms of the next line of code. The interpreter suspends the execution of the module until the import succeeds - thus within the module the import is synchronous. It is only asynchronous in terms of the DOM (which is meaningless in nodejs code) – slebetman Mar 24 '22 at 16:13
  • @jabaa ES6 imports being a syntax makes it less flexible than `require()`. You cannot change/modify the behavior of syntax without making your own language (eg. Typescript or Coffeescript). But you can override the behavior of **functions**. Because `require()` is just a function you can use it just like regular function inside `if` or loops or even replace it (see for example `rewire` (https://www.npmjs.com/package/rewire) which allows you to override `require()` functions inside modules and replace them with mocks) – slebetman Mar 24 '22 at 16:17
  • @slebetman Again. `import` is a function, too. That's the last line in the code snippet in my link: `var promise = import("module-name");` – jabaa Mar 24 '22 at 16:37
  • 1
    Another big feature (IMO) is top-level await, only supported in (ES6) modules. ES6 modules aren't just trendy. They are superior. They allow everything you can do with CJS and multiple advantages you don't have in CJS. – jabaa Mar 24 '22 at 16:41
  • @Rainning: I might misunderstand your comment, but the `import` *statement* is guaranteed to have imported the other module before the importing module is even evaluated. So the imported values are available. The `import()` *function* is guaranteed to always return a promise. – Felix Kling Mar 24 '22 at 19:40
  • @slebetman: FWIW, the specification leaves it up to the host environment how to interpret module identifiers and Node.js has an experimental API to [hook into the module resolution process](https://nodejs.org/api/esm.html#loaders). So loading mocks shouldn't be a problem with that. – Felix Kling Mar 24 '22 at 19:49
  • @jabaa If you are aware of the `import()` function then you should be aware that the `import()` function is available in CJS allowing CJS module to import ES6 modules similar to how `createRequire()` allow ES6 modules to import CJS modules – slebetman Mar 25 '22 at 06:49

1 Answers1

3

The comment section under my OP has become quite big. So let me try to provide a conclusion for myself(and the future readers), after many tabs(probably over 100) now got closed after I (finally) had organized them.


What's the benefit of using ES6 module syntax in Node.js without transpilation?

The "without transpilation" means that the version of Node.js you're using has to support ES6 module, which it does since v12, as per @Felix Kling's answer.


[...] So it's actually NOT as simple as doing the same thing differently.

The point is that "You can reliably import a CommonJS module in an ES module" (quoted from an article by Simon Plenderleith), where there is a link to the related pull request in Node.js repository. This is also mentioned by @jabaa under the comment section of my OP. But unfortunately, he/she doesn't have a reference for it.


So why do some people want to use ES6 module? Is it only for importing modules asynchronously? I don't think so. Their most be some reasons more important than this. [...]

One of those important reasons must be tree-shaking, which is mentioned twice in the comment section under my OP. And notice that while this might be possible on CommonJS modules, the performance of it with ES6 modules must be better, since the syntax-level ES6 import is static in nature(I'm not talking about the import() version), and ES6 modules will only be initialized once. With tree-shaking, your code gets smaller thus your website/library will run/load faster.


But apparently moving toward the newest syntax will be a trend. Any idea?

Absolutely. At the time I'm writing this, Node.js is default to "type": "commonjs" because of compatibility, since during the old days ES6 modules syntax has not been merged/invented. Now using ES6 modules syntax is future-proof, it will work even when people will not use Node.js in the future(sorry about saying this! But apparently it will probably be replaced by something new/fancy in the future...)

NeoZoom.lua
  • 2,269
  • 4
  • 30
  • 64