Introduction
This article provides a detailed historical overview of JavaScript and its module systems. The goal of this article is to clarify terminology that I believe is too often overlooked in entry-level teaching material (e.g., ECMA, CJS, ESM, etc.). Understanding the contents of this article is a prerequisite that is necessary to understand my follow-up article, How to Create Versatile npm Packages, as well as to become an adept JavaScript developer (IMHO).
This information was originally included in my npm article, but has been separated into its own independent article as it was brought to my attention that some readers may only be interested in one subject and not the other.
JavaScript Versions
JavaScript was originally called LiveScript by the engineers at Netscape Communications Corp. (Netscape) in 1995 as a mechanism to interact with the DOM, but it gets more complicated…
In April 1995, Sun Microsystems Inc. (Sun) and Netscape announced plans to integrate Netscape’s browser into Sun’s operating system and to run Sun’s operating system on Netscape’s servers, intending to ”[…] develop future technologies and promote open standards, […].” and to push back against Microsoft’s industry dominance.
In June 1995, Microsoft warned Netscape to stop competing in the same industry or Microsoft will kill Netscape’s company (classic Microsoft moment).
In September 1995, Netscape released the Navigator 2.0 browser in beta with a “Java-based scripting language”, aka LiveScript.
In November 1995, Netscape and Sun announced their creation of JavaScript as direct competition to Microsoft’s Visual Basic.
In December 1995, Netscape and Sun jointly announced the release of JavaScript (formerly LiveScript) as part of the Navigator 2.0 beta 3 browser, with plans to propose JavaScript to the W3C (an organization dedicated to standardizing technologies).
LiveScript was renamed to JavaScript because developers were familiar with the Sun-owned language, Java. In an interview with LiveScript/JavaScript creator, Brendan Eich states the rename was motivated by “JavaScript” being more marketable than “LiveScript.”
“Gotcha, some dudes made LiveScript and renamed it to JavaScript. What’s the confusing part?”
In September 19961, Microsoft created their own implementation of JavaScript, JScript.
In November 1996, Netscape submitted their version of JavaScript to ECMA International (another organization dedicated to standardizing technologies), rather than the W3C, to gain a larger foothold in the industry.
In June 1997, ECMA created a standard called ECMA-262 to describe the scripting language ECMAScript or ES for short.
“What’s the point?”
From this point in history, JavaScript is no longer just JavaScript. From then on, JavaScript, JScript, ActionScript, etc., are technically considered distinct implementations of ECMAScript aka ECMAScript compliant.
For example, when the ECMAScript specification is modified to add a function parseInt
that accepts a string
and return int
, your implementation must adopt parseInt
to maintain ECMAScript compliance.
Since JavaScript is compliant to ECMAScript, you must change ECMAScript in order to change JavaScript. You may change ECMAScript by making a proposal to TC39 (a technical committee within ECMA). Once accepted and an ECMA-262 edition is released, browsers and runtimes for JavaScript (V8, SpiderMonkey, Node.js, Deno, etc.) will update to support that proposal. Literally anyone can make changes to JavaScript if you follow the TC39 procedure. ECMA-262’s standards are tracked on the ECMA website.
“Why would people make different implementations of the same technology?”
It’s common practice for technology to be standardized with specifications (or standard) that describe the expected functionality of a technology without detailing the mechanics behind that technology.
Specific mechanics aren’t detailed so that developers may utilize alternative methods of creating the same technology to improve things like speed or memory without changing the expected results. Thus, users can swap between different versions of the technology without changing their existing usage.
To be clear using the earlier example, since different vendors can implement parseInt
in their own way some implementations may be faster or memory efficient if the underlying technology uses better algorithms or languages (i.e., the underlying technology written in C++ vs Rust).
“I still don’t get it, the terminology still hasn’t been clarified yet.”
The “JavaScript version” is often referred to by the ECMA-262 edition, the ECMAScript version, the ECMAScript year, or the JavaScript version.
Every edition of the ECMA-262 standard correlates to an ECMAScript edition (since ECMA-262 describes ECMAScript).
An ECMAScript’s version is referenced according to their correlating ECMA-262 edition or the edition publication year. W3 Schools claims ECMAScript versions are solely referenced by publication year since 2016, but that’s not always true.
JavaScript also has versions (used primarily by Mozilla) but was a depreciated practice as of 2013 with the last version being JavaScript v1.8.5 which was ES5 compliant.
Since JavaScript is an implementation of ECMAScript, the point to keep in mind when using or shipping JavaScript to users is which implementation of ECMAScript are you using.
ECMA-262 | ECMAScript edition | ECMAScript year | JavaScript (ES compliant) |
---|---|---|---|
Edition 1 | ES1 | ES1997 | JavaScript 1.3 |
Edition 2 | ES2 | ES1998 | JavaScript 1.3 |
Edition 3 | ES3 | ES1999 | JavaScript 1.5 |
Edition 4 | ES4 | (cancelled version) | not completed |
Edition 5 | ES5 | ES2009 | JavaScript 1.8.5 |
Edition 6 | ES6 | ES2015 | |
Edition 7 | ES7 | ES2016 | |
Edition 8 | ES8 | ES2017 | |
Edition 9 | ES9 | ES2018 | |
Edition 10 | ES10 | ES2019 | |
Edition 11 | ES11 | ES2020 | |
Edition 12 | ES12 | ES2021 | |
Edition 13 | ES13 | ES2022 | |
Edition 14 | ES14 | ES2023 | |
Upcoming version | ESNext |
Recap
JavaScript has different versions, each compliant to an ECMAScript version which is described in the corresponding ECMA-262 standard’s edition. I’m about to make you say “holy sh*t whyyyyy” because modules also have standardizations.
The JavaScript version provides the syntax and APIs you and your users can use. The module system determines how your code is imported/exported and which users can use your package.
JavaScript Module Systems
A module is some code that can be inserted (i.e., imported) into another piece of code.
In JavaScript, there are many module specifications (UMD, AMD, etc.) that utilize distinct declaration scoping, syntax, etc. This article will highlight the most common module specifications, CommonJS and ES Modules.
There are great resources2 that highlight alternative module specifications and the motivations behind their development.
CommonJS
In January 2009, Kevin Dangoor made a blog post describing a need for JavaScript to be easier to use on the server and started rolling the ball for a standard called ServerJS, to be renamed to CommonJS (CJS for short) in August 2009.
That’s right backend devs, you can blame this guy!
The CommonJS movement influenced a lot of JavaScript, but the specification most identifiable to CJS (and most relevant to my follow-up npm article) is the module specification that suggested JavaScript modules should look like this:
Hold on… did you just click my reference link to the CJS module specification and realize that the specification released by the CommonJS committee and the CommonJS above that we all know and love are not the same??
You probably recognize the code above from how we use CommonJS in Node.js. It’s true that CommonJS was in fact the original module standard adopted by Node.js but it’s again, more complicated…
In March 2013, npm (Node.js Package Manager) founder and previous Node.js head maintainer, Isaac Z. Schlueter released a scathing GitHub comment detailing the limitations of the CommonJS standard and stating that Node.js was moving past CommonJS, outright stating that “Node is the standard for server-side JavaScript platforms.”
I’m unable to find an up-to-date chart for CommonJS implementations as of writing this article, but in a snapshot from December 2014 Node.js and many other JavaScript runtimes only implemented portions of the CommonJS standard.
Despite many sources, including Node.js, stating that the Node.js implementation of CommonJS is CommonJS - it’s not actually accurate. In fact, Node.js isn’t even CommonJS compliant. It’s a mystery to me as to why Node.js continues to claim that their implementation of CommonJS is CommonJS despite their ex-head maintainer’s public aversion to complying with the CommonJS specification: “But it sort of proves the point that focusing on speed, portability, and extensibility is better than focusing on some vague normative “compliance” with a spec made up by people who are not invested in our project in the first place.”
“So how’s the Node.js implementation different from true CommonJS?”
I can only find one comprehensive breakdown comparing Node.js modules and CommonJS modules written by Wakanda (a CJS implementation) contributor Alexandre Morgaut, who publishes his analysis exclusively on Quora (why???).3
My summary of the main distinctions:
- Node.js adds additional properties to the
require
method:cache
,extensions
(deprecated),resolve
- Node.js does not support the
paths
property on therequire
method. - Node.js adds additional properties to the
module
object:children
,exports
,children
,exports
,filename
,loaded
,parent
. - Node.js exports with either
exports
ormodule.exports
object. CommonJS specifies exporting solely using theexports
object. - Node.js allows you to dangerously override the
exports
andmodule.exports
object.
What’s the takeaway?
CommonJS was a module system developed to make JavaScript easier to use on the server. The CommonJS group created a specification distinct from the module system we call CommonJS today. The CommonJS we see today in Node.js should actually be considered “CommonJS-esque” or “CommonJS-like”. I.e.,
“the Node.js version of [the CommonJS] standard (which has a few additional features).”
You will see the Node.js version of CommonJS falsely referred to as CommonJS on your journeys (even in this and in my follow-up npm article), but now you know the truth behind these lies!
Keeping this in mind, if you ever want to use CommonJS as god intended, you can use the Common Node package which is essentially a wrapper for Node.js.
“Should we author npm packages that support CommonJS module users?”
In a GitHub issue with no consensus, core Node.js contributor James M. Snell writes in March 2021 that CommonJS is not widely adopted but is also not close to being deprecated in Node.js.
Given CommonJS will be available as a module system for the foreseeable future, we should support those who choose to develop in CommonJS as well as the soldiers left behind in tech dept.
My follow-up npm article will show you an easy method to deliver CommonJS to your users.
ES Modules
In June 2015, ECMA-262 released the 6th edition, ECMAScript 6, which included a module standard known as ECMAScript Modules (aka ESM, ES Module, and incorrectly ES6 Modules) that looked like this:
In September 2017, Node.js thinks the ES module standard is great and creates an experimental implementation.
In November 2019, Node.js ships a stable implementation of ES Modules (woohoo!).
Finally… JavaScript has a built-in specification for using modules, and there was peace and harmony on Earth.
Jk.
“Ok, but why another module specification?? We already have CommonJS?!”
There are many CommonJS and ES Module comparisons online, such as Gaurav Roy’s detailed comparison of CommonJS vs ES Modules, and Lin Clark’s “ES modules: A cartoon deep-dive” which paints a picture of the motivation behind the ES Module standard.
Dr. Axel Rauschmayer concisely summarizes the benefits of ES Modules well in his book “JavaScript for impatient programmers”:
- “The syntax is even more compact than CommonJS’s.”
- “Modules have static structures (which can’t be changed at runtime). That helps with static checking, optimized access of imports, dead code elimination, and more.”
- “Support for cyclic imports is completely transparent.”
Note: my follow-up npm article discusses the benefits of static checking and tree shaking here
I’ll add that ES Modules are imported with their file extension (*.js
) and CommonJS is imported without their file extension.
What’s the takeaway?
Before ES Modules, there were many groups making distinct JavaScript module systems that would largely be incompatible with each other.
ES Modules are significant because its module system specification was created by the same group that creates the ECMAScript specification. JavaScript modules shouldn’t be in the hands of random opinionated programmers. We should let the adults who are already trusted to dictate how JavaScript works to also dictate how the module system works. They should know what they’re doing, right???
When you look for opinions on ES Modules you might find doom threads, like a GitHub gist from 2020, of developers complaining that ES Modules don’t actually achieve the goals it set out for itself, but only managed to divide the community from CommonJS to yet another module standard.
On the other side of the aisle, we can see advocates like Andy Jiang from Deno writing articles from June 2023 who argue that the remaining CommonJS zealots are the actual roadblock between a true module standardization.
Are ES Modules good? Hard to say. (perhaps a blog post for another time)
“Should we author npm packages that support ES Module users?”
In short, yes. Whether you like ES Modules or not, runtimes will support the ES Module implementation for the foreseeable future. We should support developers who choose to develop in ES Modules.
My follow-up npm article will show you an easy method to deliver ES Modules to your users.
Using CommonJS and ES Modules
Your code will be determined as either CommonJS or ES Module by your runtime environment using the following methods (sorted by priority):
CommonJS
- acceptable file extensions:
.js
,.cjs
package.json
attribute:"type":"commonjs"
- an empty
"type"
attribute defaults to"commonjs"
- an empty
- the module import/export methods described above
ES Module
- acceptable file extensions:
.js
,.mjs
package.json
attribute:"type":"module"
- the module import/export methods described above
Thanks for reading
JavaScript terms can get confusing, but it’s important for us to use accurate words. Developers have made public confusion worse in the past, such as when they were forced to intentionally use the term “ECMAScript” when they were referring to “JavaScript” lest they get sued by Oracle or have their app taken down by Oracle..
:)
As noted in the introduction, the contents of this article was originally included in my npm article, where I provide a detailed guide to make versatile npm packages configured with type safety, code formatting, dependency management, unit testing, and automatic releases. I present topics that should be considered when authoring an npm package, as well as provide justifications for tech/design decisions I’ve made to help give you informed opinions on how to make your next Game Changing Million Dollar npm Package (GCMDNP).
You can find my npm article here: eyas.ca/blog/how-to-create-versatile-npm-packages/
Enjoy my work? Subscribe and get notified when I write more things!
References
Footnotes
-
Microsoft releases JScript
↩ -
Alexandre Morgaut’s Quora answers relating to CommonJS in Node.js
- Are there implementations of CommonJS besides Node.js
- How is Node.js module system different than CommonJS
- How did Node.js decide on the CommonJS module format
-
JavaScript References
- “JavaScript brief history and ECMAScript(ES6,ES7,ES8,ES9) features” by Madasamy M
- “History of JavaScript — LiveScript, JScript, ECMAScript, ES6 and not still Java” by Aphinya Dechalert
- “Innovators of the Net: Brendan Eich and JavaScript” by Netscape
- “The History of JavaScript” by Softpanorama Society
- “The Weird History of JavaScript” by Jeff 🐐 Delaney
- “The history of JavaScript: A Journey from Netscape to Frameworks and Libraries” by Vikas Kaushik
- This article explains the browser war
- “Netscape Communications Corp” by Encyclopedia.com
- explaining Microsoft’s dominance
- “Speaking JavaScript”, Chapter 5 by Dr. Axel Rauschmayer
- ECMA-262 Introduction
-
CommonJS and ES Module References
- CommonJS Homepage, June 2013 snapshot
- “CommonJS effort sets JavaScript on path for world domination” by Kris Kowal
- “JavaScript Modules: A Brief History” by Object Partners
- “Server-side JavaScript with Node.js Basics” presentation by Alexander Lill
- “Node Modules at War: Why CommonJS and ES Modules Can’t Get Along” by Dan Fabulich
- “ECMAScript 6 modules: the future is now” by Dr. Axel Rauschmayer
- “ECMAScript 6 modules in future browsers” by Dr. Axel Rauschmayer
- “ECMAScript modules in Node.js: the new plan” by Dr. Axel Rauschmayer