Purpose of .mjs and .cjs File Extensions

Modern JavaScript environments, such as Node.js, support two distinct module systems: CommonJS (CJS) and ECMAScript Modules (ESM). The .mjs and .cjs file extensions were introduced to provide an explicit, unambiguous way for runtimes and build tools to determine which module system to use when executing a file. By overriding default configurations, these extensions ensure that files are parsed correctly without relying solely on project-level settings.

The Two Module Systems in JavaScript

To understand why these file extensions exist, it is necessary to understand the two module formats:

  1. CommonJS (CJS): The original module system used by Node.js. It loads modules synchronously using the require() function and exports values via module.exports or exports.
  2. ECMAScript Modules (ESM): The official ECMAScript standard for JavaScript modules. It uses import and export statements, supports static analysis, allows top-level await, and loads asynchronously.

Because the syntax, parsing rules, and execution models of these two systems differ fundamentally, runtimes need a reliable way to know which parser to apply before executing the code.

The Purpose of .mjs

The .mjs extension stands for Module JavaScript. It explicitly indicates that the file should always be treated as an ECMAScript Module (ESM), regardless of external configuration.

When a runtime encounters a .mjs file: * It parses the code using ESM syntax (import and export). * Strict mode ("use strict") is enabled by default. * Top-level await is permitted. * CommonJS-specific globals such as require, module.exports, __dirname, and __filename are not defined. Instead, ESM equivalents like import.meta.url must be used.

The Purpose of .cjs

The .cjs extension stands for CommonJS JavaScript. It explicitly indicates that the file should always be treated as a CommonJS module.

When a runtime encounters a .cjs file: * It parses the code using CommonJS syntax (require() and module.exports). * Top-level await is not permitted. * Globals like require, exports, module, __filename, and __dirname are available. * Strict mode is not enabled unless explicitly declared with "use strict".

Interaction with package.json

By default, Node.js treats standard .js files as CommonJS. However, developers can add "type": "module" to their project’s package.json, which instructs the runtime to treat all standard .js files within that package scope as ESM.

The specific extensions serve as precise overrides to this setting: * In a "type": "module" package, standard .js files run as ESM, but any file named with .cjs will still execute as CommonJS. * In a default CommonJS package (or one with "type": "commonjs"), standard .js files run as CommonJS, but any file named with .mjs will execute as ESM.

Why Explicit Extensions Matter