Black lives matter
Portrait Dr. Axel Rauschmayer
Dr. Axel Rauschmayer
Homepage | Twitter
Cover of book “JavaScript for impatient programmers”
Book, exercises, quizzes
(free to read online)
Cover of book “Deep JavaScript”
Book (50% free online)
Cover of book “Tackling TypeScript”
Book (first part free online)
Logo of newsletter “ news”
Newsletter (free)

Node.js: checking if an ESM module is “main”

[2022-07-07] dev, javascript, nodejs
(Ad, please don’t block)

An ESM module can be used in two ways:

  1. It can be used as a library from which other modules can import values.
  2. It can be used as script that we run via Node.js – e.g., from a command line. In that case, it is called the main module.

If we want a module to be used in both ways, we need a way to check if the current module is the main module because only then do we execute the script functionality. In this blog post, we learn how to perform that check.

Determining if a CommonJS module is main  

With CommonJS, we can use the following pattern to detect if the current module was the entry point (source: Node.js documentation):

if (require.main === module) {
  // Main CommonJS module

Determining if an ESM module is main  

As of now, ESM modules have no simple built-in way to check if a module is main. Instead, we have to use the following workaround (based on a tweet by Rich Harris):

import * as url from 'node:url';

if (import.meta.url.startsWith('file:')) { // (A)
  const modulePath = url.fileURLToPath(import.meta.url);
  if (process.argv[1] === modulePath) { // (B)
    // Main ESM module


  • import.meta.url contains the URL of the currently executed ESM module.

  • If we are sure our code always runs locally (which may become less common in the future), we can omit the check in line A. If we do and the code does not run locally, at least we get an exception (and not a silent failure) – thanks to url.fileURLToPath() (see next item).

  • We use url.fileURLToPath() to convert the URL to a local path. This function throws an exception if the protocol isn’t file:.

  • process.argv[1] contains the path of the initial module. The comparison in line B works because this value is always an absolute path – Node.js sets it up as follows (source code):

    process.argv[1] = path.resolve(process.argv[1]);