Are there any compatiblity issues with using CJS and ESM modules? - javascript

I have read several "ESM in NodeJS" articles as of late, consequently; I decided to convert a CJS module I had into an ES-Module (2020). Before I even began writing code, I noticed something that was overwhelming — though it wasn't completely unexpected — my page was one big red squiggly error. I found it easier to just rewrite the whole module, even though it took me a couple days.
So now I am at a point where I can't help but to wonder if ESM is necessary. To put it more simply: Am I going to run into comparability issues down the road if I don't start using ESM modules today, or can I continue to use CJS?

All new projects should be written as ESM modules as that is the present and the future of the Javascript language and nodejs. It's highly, highly unlikely that CJS modules will lose support for a very long time because a huge portion of the nodejs eco-system is still CJS modules and I rather doubt that the nodejs leadership wants to create the kind of situation Python did with 2.0 => 3.0 which was not pleasant in the developer community. Plus, the dual compatibility in nodejs is already there and not really harming anything right now.
There are some CJS conveniences like __dirname that are not as convenient in EJS. And, EJS requires static export declarations (they can't be computed like they can be in CJS) which is good news for bundlers, but rules out some situations where you might to do dynamic exports.
ESM is the module standard in Javascript. Nodejs is moving to fully support that standard. New code should be written as ESM modules. I would expect some new features in the future to only be available in ESM modules as that's where the development and innovation is happening.
CJS is node-specific, not a Javascript standard. The developers behind nodejs took ideas from existing 3rd party module systems, molded them to fit into the needs of nodejs and built them into the first version of nodejs. They are not a Javascript standard. ESM are now part of the Javascript standard and nodejs has been adopting that new standard and filling in a few bits where they needed more than the standard offers.

Related

Why does Node often us a different module system from browsers?

I was considering updating my Node/Express code to use import instead of require by adding to my package.json file the line:
"type": "module",
However, I did not know why most Node/Express code I see, still uses require while code I see in the browser via webpack uses import.
Is there a reason for this duality? Is upgrading to import recommended in general? Is import because it is newer considered an upgrade and a better technology?
This previous Q/A is over 7 years old and has lot of historical information. I am looking for more current information as the language has evolved.
The reason for having two types of modules is purely historical.
Back in 2009 when Node.js was created, ES modules (import syntax) did not exist yet, but there existed other nonstandard alternatives, with the most important being AMD and CommonJS, for which several browser implementations were available. Node.js decided to adopt CommonJS (require syntax). For this reason, all packages deployed up until 2015 are CommonJS packages.
Native ES modules were introduced with the ECMAScript 2015 standard, but they only made it as an experimental technology in Node.js 8.5 in 2017, and it wasn't until Node.js 12 (2019) that their support became good enough to place them as an alternative to CommonJS (although this is subjective).
For this reason, a large part of software developed for Node.js still uses CommonJS modules with require statements.
Whether ES modules are better is a matter of opinion, and not a proper question for StackOverflow. At least, they are standard JavaScript, which makes them fit for use both in browsers and in Node.js, which is imo a big advantage.
A disadvantage is that ES modules cannot be required in CommonJS code - the opposite is true: CommonJS modules can be imported by ES modules - so if any of your dependencies uses ES modules, that may be a good opportunity to switch your code to ES modules, too.
So should we upgrade to use import in general? Of course, that's also a matter of opinion. And if that was my opinion, the answer would be "no", because there is no reason to change a running system. CommonJS code is not going to rust or worsen simply because a better alternative exists nowadays, although ES modules are a better choice for new code. Node.js, as long as people use it, is not going to drop support for CommonJS either.
There is a project called Deno which is designed to provide a more modern and secure alternative to Node.js to run JavaScript code on the server. Deno was originally designed to work with ES modules only, but it soon became clear that not supporting CommonJS modules was a major issue, because so many npm packages were not working, so it introduced a compatibility mode to support CommonJS modules under a flag. The moral of the story is: removing support for require is good idea, but re-adding it is an even better one.

Can I build a production JavaScript web application without a bundler?

I've recently gone down the rabbit hole of learning about modular programming with JavaScript, including some history of JS module systems, bundlers and ES2015 Modules. I now understand some of the pains that bundlers help/ed alleviate, such as:
network latency (more effective caching of a single bundle, HTTP/1.0 connections),
performance constraints on the size of application modules (minification, tree-shaking),
ES2015 not supporting some features (bare imports),
and backwards compatibility with older module systems (transpilation of ES2015 Modules syntax).
However I'd like to know if it is possible to create a production JS web application in 2020 that doesn't use a bundler like webpack or Parcel and uses ES2015 Modules? A caveat being that a source-to-source compiler like Babel could still be used provided it preserves ES2015 Modules syntax. I'm not saying I would like to do this but for the sake of argument, what would be the downsides?
Client-side code does not have to be bundled when run in a modern browser, but if you're going to design your code into tons of small modules (for the sake of development efficiency and reuse), then it will be very inefficient to load if you don't use a bundler that can reduce the number of separate files that need to be loaded.
A bundler will be desirable if you design your client JS files for best modular development and thus don't design your client JS files for efficient delivery as the bundler can bundle things together for efficient delivery in a build process.
"Part of the JS SDK" is a matter of terminology and opinion, not facts so I won't really comment on that assertion.
It is certainly possible to design client-side JS files from the beginning for efficient client-side delivery and not use a bundler (like we used to do), but you will not be able to also design the layout of the files for modular development. A bundler allows you achieve both goals which is probably why they are so popular.
However I'd like to know if it is possible to create a production JS web application in 2020 that doesn't use a bundler like webpack or Parcel and uses ES2015 Modules?
Yes, it's possible without a bundler if you run in a browser that supports import and export.
I'm not saying I would like to do this but for the sake of argument, what would be the downsides?
As explained above, you would either give up efficient client-side loading or you would give up ideal modular design as the two have conflicting design parameters.

Modularity in Javascript

I know that if there are multiple JS files to be rendered by the browser, which apparently have identifiers inside, the global scope is polluted by this identifiers. I also know that one way to avoid this is using modules which(in my understanding) are just objects which have the aformentioned identifiers as members, thus sort of imitating C++ namespaces. I am also learning Node.js and there is a built in module system which eases this task so my question is: how to use modules in js files that are sent to the browser to be rendered?
Thanks.
Tools like browserify and WebPack are exactly what you are looking for (I personally prefer browserify over WebPack). Have a look at this answer, it explains a lot of your concerns.
In Node.JS, you can export a module using module.exports keyword, but you cannot just import those modules in your browser by just requiring them in a <script> tag. That's because, the browser doesn't understand the module system and everything works in the context of a global window object there, so module.exports simply becomes window.module.exports which I'm sure you'll not want. Hence you use tools like browserify that process the Node.JS scripts into something that your browser will understand.
This problem is usually solved by module bundlers or module loaders (e.g Webpack, Browserify, RequireJS). They are able to understand relations between your JS modules, skip unused modules and produce output that just works in your browser. All of that without the need to worry too much about global scope if you follow some conventions.
Some time ago, before ES6, two different approaches to this problem were widely used:
CommonJS:
var module = require('my-module');
widely known from Node.js
AMD:
define(['jquery'] , function ($) {
return function () {};
});
Which was suited for browser usage since it by design supported asynchronous loading of modules.
Then ES6 was introduced with native support for modules:
import * as lib from 'lib';
Main problem with new technology in web is that you often have variety of browsers to support which for a long time prevented developers from using new features. Nowadays, we have code transpilers and sophisticated code bundlers (e.g. Webpack). With their help you can use latest version of language, compile and bundle your code and at the end single "bundle.js" file is emitted which supports older browsers at the cost of slower execution times.

import and export in Javascript

According to this question How do I include a JavaScript file in another JavaScript file?. It seems a lots of people are interested in breaking big Javascript projects into small modules and export/import modules for code reuse.
After some research, import/export are designed for this feature. According to the references, they're initially defined in ES6.
Update
Latest version of main browsers shipped with this feature implemented. To have the latest status, please always refer to the References.
(If you're using nodejs, Modules (https://nodejs.org/dist/latest-v5.x/docs/api/modules.html) is the best aproach)
Refrerences:
https://developer.mozilla.org/en/docs/web/javascript/reference/statements/import
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export
In the current versions of JS there are a few methods of doing this. Require uses the AMD design pattern, and is the standard for frontend dependency injection / module loading. Frameworks such as Angular use this method.
Here is a link to the require docs.
http://requirejs.org/
Time goes by... Today I would rather suggest using Babel (within browserify or webpack) to turn ES modules into plain old javascript.
Then you have the full power of import/export syntax.
Require/AMD/CommonJS is just getting deprecated anytime soon.

Ultimate JS module solution for UMD with dependencies?

I maintain and collaborate on some JavaScript modules written in CommonJS that are in need of high-quality UMD wrappers.
The dependencies are sourced from npm but have at least CommonJS and AMD support (or I can add it).
The CommonJS version goes on npm The UMD wrapped module will be pushed to bower
The wrapper must work in browsers (AMD + globals), and in Node.js (any and other CommonJS systems if possible). Any automation should preferably happen using Grunt (I'm pretty handy in grunt).
I've spend ages trawling Google en SO but it is a huge mess.
Some hopeful ones that don't quite cut it (or I am missing something, which is entirely possible):
browserify
gluejs
grunt-umd
I'm finding desperate constructs like this everywhere: http://rathercurio.us/building-umd-modules-with-dependencies-with-browserify , but I'm not really cool with such hackery.
Any good tips on this? I'll take any pointer or link or tip.
Edit: clarification: that last thing said, the ideal solution should not require us to assemble chunks of boilerplate template code by hand and create new bugs. I cool with configuring and specifying stuff though.
Your 1st and last stop should be urequire.org, the Universal Module Converter that does much more that just converting CommonJS and AMD javascript modules to UMD (or AMD or CommonJS or a standalone using rjs/almond).
It allows you to manipulate Module's code and dependencies while converting: inject, replace or remove code and dependencies, export to global objects (window) or your bundle, inject & optionally merge common code (like initializations), add runtime information, minify and much much more.
Most of that using simple but hugely powerful declarations & optionally callbacks for fine grained manipulation. It works with standalone config files (.js, .coffee, .json, .yml etc) and as-is as a gruntjs config
uRequire compiles from source modules written in javascript, coffeescript, livescriped, coco & icedcoffeescript without any plugins.
Forget boilerplate, code ceremony and repeating requires. The conversion templates are based on the well known UMDjs but can be customized via declarations to hide or provide functionality.
uRequire is opensource, MIT license and hosted on github and authored by me :-)

Categories

Resources