Current Javascript adopts import from ES6 as a standard way to import modules.
However, I sometimes see codes using CommonJS require instead of import.
I first wondered whether two can be used together, but it seems like two are not interchangeable. (releated stackoverflow question)
So, is CommonJS 'require' something that is still used in projects? Or is it slowly dying and needed only for maintaining legacy codes?
CommonJS will likely be supported in nodejs for a long time as there are still millions of lines of code written using it. It is the original module loading mechanism in nodejs. It is not deprecated.
ESM modules (ECMAScript modules) that use import and export are the new Javascript standard for modules and we can expect that nodejs will support these for as long as they are the Javascript standard (probably forever).
These two modules standards are not entirely compatible so mixing and matching within the same project can lead to complications that you have to learn how to deal with.
New Projects
If I were starting a new project today, I'd choose to write my code using ESM modules as it is the future trajectory of the language and nodejs. And, you can still load CommonJS modules into ESM modules if you require backward compatibility with other modules, but you do have to know how to do it properly - it's not always seamless to mix and match module types.
When ESM modules were first supported in nodejs, the interoperability with CommonJS modules was not very full featured and created some difficulties. As of the more recent versions of nodejs, you can load a CommonJS module from an ESM module or vice versa- you just have to use the right techniques to do it. This makes working from an ESM project a lot more feasible now than it was when ESM support in nodejs first came out as you can still access libraries in NPM that only support CommonJS.
Note also that more and more libraries in NPM are supporting direct loading from either module type so they are equally easy to use whether your project is CommonJS or ESM.
Over time, I would expect that any actively developed, shared module on NPM will eventually support direct loading as an ESM module. But, we're in this transition period of time where many have not yet implemented that or there are particular challenges in implementing the new loading scheme (which comes with it's own and different set of rules). In the meantime, you can still load CommonJS modules into ESM projects.
Existing CommonJS Projects
If I had an existing project that was CommonJS, I would not be spending any time thinking about converting it to ESM because there is still widespread support for CommonJS and my developer time is probably better spent on adding features, testing, fixing bugs, etc... than converting module formats.
Interoperability
One of the important interoperability things to know is that you can load a CommonJS module from an ESM module in several different ways:
With a static import: import { callMeCommon } from '../otherProject/common.js';
With a dynamic import import(someFile).then(...)
By using module.createRequire(import.meta.url) and then using that require() function to load your CommonJS module.
You can also use the dynamic import('someModule').then(...) to load an ESM module from a CommonJS module, but it's not synchronous like require() was so it has to be dealt with differently.
It's also useful to know that ESM modules do not have some nodejs niceties that CommonJS modules had. There's no automatic __dirname or __filename to tell you exactly where this file was loaded from. Instead, you have to parse those values out of import.meta.url and compute them. So, it's still possible to get that information, it's just not as convenient as it used to be.
On the other hand, ESM modules have some features like top level await that CommonJS do not which can be very useful.
Your Specific Questions
So, is CommonJS 'require' something that is still used in projects?
Yes, it is still used a lot.
Or is it slowly dying and needed only for maintaining legacy codes?
[Comments as of early 2022] I wouldn't so much say that it is dying as there are very few projects on NPM that don't still support CommonJS. In fact, when a project releases a new version that no longer supports CommonJS, it creates quite a problem for their user base (we see some of these issues here on stackoverflow) because of the significant prevalence of CommonJS projects still and people not familiar with how to load different module types.
So, I'd say that we're still in a very early stages of a transition from CommonJS to ESM and it's more like ESM is getting more and more adoption rather than CommonJS is dying. I'd hazard a guess that the majority of modules on NPM will, over the next few years, move to support direct loading from both module formats.
Transpiling
Lastly, developers often use transpiling to allow them to write code using the newest syntax and features, but using a transpiler to convert the code back to a lower, common denominator that runs anywhere. That can also be done for module architecture (write code in ESM, transpile to CommonJS).
A Few Interesting References
What does it take to support Node.js ESM?
Hybrid npm packages (ESM and CommonJS)
Node Modules at War: Why CommonJS and ES Modules Can’t Get Along
All you need to know to move from CommonJS to ECMAScript Modules (ESM) in Node.js
In Nodejs it doesn't yet appear to be deprecated given the LTS v20 precursor (v19) documentation describes the feature:
https://nodejs.org/api/esm.html#interoperability-with-commonjs
https://github.com/nodejs/Release
If locked into Nodejs the above affords a length of time into 2025 for using LTS Nodejs runtimes, with legacy code, and possibly polyfill the same whenever the Nodejs product changes status (the module system or the whole runtime).
CommonJS is the legacy module system specific to Nodejs
see https://wiki.commonjs.org/wiki/Modules/Meta
Generally outside of Nodejs Commonjs/require has been displaced in JS/TS runtimes by ESModules:
https://developer.mozilla.org/docs/Web/JavaScript/Guide/Modules
As much as possible I've been moving away from Nodejs to Deno and haven't looked at the specifics and details of its npm and Nodejs compatibility. For those who're looking to implement software using contemporary web platform features it's the best forward-looking with current working solutions that I'm aware of (more at https://deno.land/).
import { createRequire } from "https://deno.land/std/node/module.ts";
// import.meta.url...like `__filename` ...
const require = createRequire(import.meta.url);
const path = require("path");
Related
I've noticed some libraries have duplicated code in an es folder. Why do developers do that?
examples:
Developers can ship their packages in a few different flavours, depending on how the user (e.g. you) wants to use them.
If you want to use the module code (i.e. import), or you want to use es2015 (i.e. the require), or you even might want to use it in a browser environment (standalone - think .min file).
The folders names are meaningless, developers can call them whatever they want, but they'll probably put their non-transpiled (i.e. import) modules in an es or esm directory
Keep in mind that just because you are "import"-ing their module doesn't mean that their module uses imports. Most nowadays are still transpiled before being shipped so the code you're import-ing is probably require-ing stuff.
It's kind of complicated...
I'm starting a project using NodeJS.
I did few Javascript classes and they say, that the all browsers support ES6.
I found this question here, that was asked 3 years ago and the last answer was made in 2015 and edited in 2016.
The technology moves on pretty fast, so i would like to know, if this still the case in 2018.
Copying my answer from that old post with all the high vote, old info...
The most important thing to know is that ES6 modules are, indeed, an official standard, while CommonJS (Node.js) modules are not.
In 2019, ES6 modules are supported by 84% of browsers. While Node.js puts them behind an --experimental-modules flag, there is also a convenient node package called esm, which makes the integration smooth.
Another issue you're likely to run into between these module systems is code location. Node.js assumes source is kept in a node_modules directory, while most ES6 modules are deployed in a flat directory structure. These are not easy to reconcile, but it can be done by hacking your package.json file with pre and post installation scripts. Here is an example isomorphic module and an article explaining how it works.
I am building Angular Apps, using 1.x and have been for some time now. I use Bower to install Angular and the various packages that go with it and some other bits and pieces like Shivs, JQuery, ChartJs etc etc. I love using Bower as it's nice and quick and keeps everything in a consistent place for me to reference. I use Grunt as well as my task-runner and so I'd also like to be able to automate this process for silky smooth development.
Now as my Angular knowledge has increased and the scale of the apps that I'm building are increasing, I'm finding myself including dozens of calls to files within the index.html and I really want to tidy all this up, ideally into a nice app.js making it all much more manageable, not only for myself but for anyone else coming in and working on these apps.
I've seen maaany tools like requirejs, browserify and commonjs to name but a few, which all provide the kind of functionality I'm after, but when reading various tutorials or watching conference talks on the process, they all seem to conflict with one another on which is the best. I know to some degree (as with all these competing technologies) it's personal preference, and I was leaning towards browserify, but apparently this removes bower from the flow and uses NPM instead. I'd like to stick with Bower if possible. I've really enjoyed using it.
Does anyone have any suggestions or best practices they can offer that might clear this up for me? Would a simple concat with grunt/gulp just be the way to go?
Any useful comments/answers would be much appreciated.
Many thanks.
Use ES6 Modules along with a module bundler (my recommendation would be Webpack).
As you have correctly identified RequireJS and commonjs evolved around different (and slightly conflicting) goals and are incompatible. ES6 modules is a standardized effort towards modular javascript that is already well supported by transpilers (eg. Babel).
This article provides a great introduction to this new feature:
Even though JavaScript never had built-in modules, the community has
converged on a simple style of modules, which is supported by
libraries in ES5 and earlier. This style has also been adopted by ES6:
Each module is a piece of code that is executed once it is loaded.
In
that code, there may be declarations (variable declarations, function
declarations, etc.).
By default, these declarations stay local to the
module.
You can mark some of them as exports, then other modules can
import them.
A module can import things from other modules. It refers
to those modules via module specifiers, strings that are either:
Relative paths ('../model/user'): these paths are interpreted
relatively to the location of the importing module. The file extension
.js can usually be omitted.
Absolute paths ('/lib/js/helpers'): point
directly to the file of the module to be imported.
Names ('util'):
What modules names refer to has to be configured. Modules are
singletons. Even if a module is imported multiple times, only a single
“instance” of it exists. This approach to modules avoids global
variables, the only things that are global are module specifiers.
Example of use of Javascript modules in practice:
//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
return x * x;
}
export function diag(x, y) {
return sqrt(square(x) + square(y));
}
//------ main.js ------
import { square, diag } from 'lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5
I've been looking all over the Internet without a clear answer for this.
Currently Node.js uses only CommonJS syntax to load modules, and if you really want to use the standard ECMAScript 2015 modules syntax, you either have to transpile it beforehand or use an external module loader at runtime.
Currently I'm not too positive to use either of those two methods, are the Node.js maintainers even planning to support ECMAScript 2015 modules or not? I haven't found an hint at all about this.
At the moment Node.js 6.x claims to support 96% of the ECMAScript 2015 features, but there isn't any reference to modules (Node.js ECMAScript 2015 support link).
Do you know if Node.js will support these modules out of the box, in the near future?
Node.js 13.2.0 & Above
Node.js 13.2.0 now supports ES Modules without a flag 🎉. However, the implementation is still marked as experimental so use in production with caution.
To enable ECMAScript module (ESM) support in 13.2.0, add the following to your package.json:
{
"type": "module"
}
All .js, .mjs (or files without an extension) will be treated as ESM.
There are a number of different options other than entire package.json opt-in, all of which are detailed in the documentation for 13.2.0.
Node.js 13.1.0 & Below
Those still using older versions of Node may want to try the [esm][3] module loader, which is a production-ready implementation of the ES Modules Specificaiton for Node.js:
node -r esm main.js
Detailed Updates...
23 April 2019
A PR recently landed to change the way ECMAScript modules are detected:
https://github.com/nodejs/node/pull/26745
It's still behind the --experimental-modules flag, but there are major changes in the way modules can be loaded:
package.type which can be either module or commonjs
type: "commonjs":
.js is parsed as CommonJS
the default for an entry point without an extension is CommonJS
type: "module":
.js is parsed as an ECMAScript module
does not support loading JSON or a native module by default
the default for an entry point without an extension is ECMAScript module
--type=[mode] to let you set the type on entry point. Will override package.type for entry point.
A new file extension .cjs.
this is specifically to support importing CommonJS in the module mode.
this is only in the ECMAScript module loader, the CommonJS loader remains untouched, but the extension will work in the old loader if you use the full file path.
--es-module-specifier-resolution=[type]
options are explicit (default) and node
by default our loader will not allow for optional extensions in the import, the path for a module must include the extension if there is one
by default our loader will not allow for importing directories that have an index file
developers can use --es-module-specifier-resolution=node to enable the CommonJS specifier resolution algorithm
This is not a “feature”, but rather an implementation for experimentation. It is expected to change before the flag is removed
--experimental-json-loader
the only way to import JSON when "type": "module"
when enable all import 'thing.json' will go through the experimental loader independent of mode
based on whatwg/html#4315
You can use package.main to set an entry point for a module
the file extensions used in main will be resolved based on the type of the module
17 January 2019
Node.js 11.6.0 still lists ES Modules as experimental, behind a flag.
13 September 2017
Node.js 8.5.0 has been released with support for mjs files behind a flag:
node --experimental-modules index.mjs
The plan for this is to remove the flag for the v10.0 LTS release.
--Outdated Information. Kept here for historical purposes--
8 September 2017
The Node.js master branch has been updated with initial support for ESM modules:
https://github.com/nodejs/node/commit/c8a389e19f172edbada83f59944cad7cc802d9d5
This should be available in the latest nightly (this can be installed via nvm to run alongside your existing install):
https://nodejs.org/download/nightly/
And enabled behind the --experimental-modules flag:
package.json
{
"name": "testing-mjs",
"version": "1.0.0",
"description": "",
"main": "index.mjs" <-- Set this to be an mjs file
}
Then run:
node --experimental-modules .
February 2017:
An Update on ES6 Modules in Node.js
The Node.js guys have decided that the least bad solution is to use the .mjs file extension. The takeaway from this is:
In other words, given two files foo.js and bar.mjs , using import * from 'foo' will treat foo.js as CommonJS while import * from 'bar'
will treat bar.mjs as an ES6 Module
And as for timelines...
At the current point in time, there are still a number of
specification and implementation issues that need to happen on the ES6
and Virtual Machine side of things before Node.js can even begin
working up a supportable implementation of ES6 modules. Work is in
progress but it is going to take some time — We’re currently looking
at around a year at least.
October 2016:
One of the developers on Node.js recently attended a TC-39 meeting and wrote up a superb article on the blockers to implementing for Node.js:
Node.js, TC-39, and Modules
The basic take-away from that is:
ECMAScript modules are statically analyzed, and CommonJS are evaluated
CommonJS modules allow for monkey-patching exports, and ECMAScript modules currently do not
It's difficult to detect what is an ECMAScript module and what is CommonJS without some form of user input, but they are trying.
*.mjs seems the most likely solution, unless they can accurately detect an ECMAScript module without user-input
-- Original Answer --
This has been a hot potato for quite some time. The bottom line is that yes, Node.js will eventually support the ES2015 syntax for importing/exporting modules - most likely when the specification for loading modules is finalized and agreed upon.
Here is a good overview of what's holding Node.js up. Essentially, they need to make sure that the new specification works for Node.js which is primarily conditional, synchronous loading and also HTML which is primarily asynchronous.
Nobody knows for sure right now, but I imagine Node.js will support import/export for static loading, in addition to the new System.import for dynamic loading - while still keeping require for legacy code.
Here's a few proposals on how Node might achieve this:
In defense of .js
.mjs modules
I'm approaching modules for the very first time and I'm a little bit confused.
I read from various docs that there are several modules systems, like commonjs (sync), and requirejs (AMD). From ES6 plain javascript has its own module sys, which is based on commonjs.
Then I started studying webpack, that resolves dependency using commonjs or requirejs module formats, and from here starts my confusion: as far as I understand, those two are module systems, they are designed to resolve the dependencies tree on its own; it's their purpose.
What is the sense to use the commonjs/requirejs format (aka syntax) and then implement webpack to resolve the graph?
CommonJS and AMD are both runtime module systems. Webpack is compiling your code, so you're not using it to resolve the graph specifically, you're using to create a build of an application from source files. To accomplish that, webpack 'understands' both types of modules so that it can analyze your code, identify dependencies, and bundle and optimize appropriately.
Webpack supports things that the original module systems don't: you can use more dynamic expressions to represent the module you're requiring; you can extend the require resolution behavior itself; you can specify an asynchronous runtime loading using its require.ensure syntax. Webpack's author could have defined a new module system to support these additional features, but then you'd have to rewrite all your existing source (and any third party modules) to accommodate the build tool. Instead, webpack is good about supporting whatever modules you already use, and giving you additional tools to handle more complex build needs.