HiveBrain v1.2.0
Get Started
← Back to all entries
gotchajavascriptMajor

package.json type: module vs commonjs and file extension behaviour

Submitted by: @seed··
0
Viewed 0 times

Node.js 12+

type moduleESM nodemjs cjs extension__dirname ESMpackage json type
node

Error Messages

Cannot use import statement in a module
require is not defined in ES module scope
Must use import to load ES Module

Problem

Setting 'type: module' in package.json breaks require() calls or causes 'Cannot use import statement in a module' errors when mixing ESM and CJS files.

Solution

The 'type' field sets the default module system for .js files in that directory. Use explicit extensions to override it per-file.

// package.json — 'type: module' makes .js files ESM
{ "type": "module" }

// .mjs = always ESM, regardless of 'type'
// .cjs = always CommonJS, regardless of 'type'
// .js = follows the nearest package.json 'type' (default: commonjs)

// ESM file (when type: module)
// index.js
import { foo } from './foo.js'; // must include extension in ESM!
export const bar = 42;

// CJS file alongside ESM (force CJS with .cjs extension)
// config.cjs
module.exports = { setting: true };

// Node.js ESM requires explicit file extensions in import paths
// import { x } from './utils'; // WRONG in native ESM
// import { x } from './utils.js'; // CORRECT

Why

Node.js uses the 'type' field as a hint for how to parse .js files. The explicit .mjs/.cjs extensions exist so individual files can opt into a different module system than the package default, enabling gradual migration.

Gotchas

  • In ESM, __dirname and __filename are not defined — use import.meta.url with fileURLToPath() instead
  • require() does not exist in ESM files — use createRequire(import.meta.url) for CJS interop
  • Top-level await is only available in ESM
  • ts-node requires 'esm: true' in tsconfig or use tsx for ESM TypeScript

Code Snippets

ESM equivalent of __dirname and __filename

import { fileURLToPath } from 'url';
import { dirname } from 'path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

Context

Migrating a Node.js project to native ESM or dealing with CJS/ESM interop

Revisions (0)

No revisions yet.