CommonJS modules consist of a module.exports object which can be of any type.

    When importing a CommonJS module, it can be reliably imported using the ES module default import or its corresponding sugar syntax:

    1. import { default as cjs } from 'cjs';
    2. // The following import statement is "syntax sugar" (equivalent but sweeter)
    3. // for `{ default as cjsSugar }` in the above import statement:
    4. import cjsSugar from 'cjs';
    5. console.log(cjs);
    6. console.log(cjs === cjsSugar);
    7. // Prints:
    8. // <module.exports>
    9. // true

    The ECMAScript Module Namespace representation of a CommonJS module is always a namespace with a default export key pointing to the CommonJS module.exports value.

    This Module Namespace Exotic Object can be directly observed either when using import * as m from 'cjs' or a dynamic import:

    1. import * as m from 'cjs';
    2. console.log(m);
    3. console.log(m === await import('cjs'));
    4. // Prints:
    5. // [Module] { default: <module.exports> }
    6. // true

    For better compatibility with existing usage in the JS ecosystem, Node.js in addition attempts to determine the CommonJS named exports of every imported CommonJS module to provide them as separate ES module exports using a static analysis process.

    For example, consider a CommonJS module written:

    1. // cjs.cjs
    2. exports.name = 'exported';

    The preceding module supports named imports in ES modules:

    1. import { name } from './cjs.cjs';
    2. console.log(name);
    3. // Prints: 'exported'
    4. import cjs from './cjs.cjs';
    5. console.log(cjs);
    6. // Prints: { name: 'exported' }
    7. import * as m from './cjs.cjs';
    8. console.log(m);
    9. // Prints: [Module] { default: { name: 'exported' }, name: 'exported' }

    As can be seen from the last example of the Module Namespace Exotic Object being logged, the name export is copied off of the module.exports object and set directly on the ES module namespace when the module is imported.

    Live binding updates or new exports added to module.exports are not detected for these named exports.

    The detection of named exports is based on common syntax patterns but does not always correctly detect named exports. In these cases, using the default import form described above can be a better option.

    Named exports detection covers many common export patterns, reexport patterns and build tool and transpiler outputs. See [cjs-module-lexer][] for the exact semantics implemented.