Skip to the solution
EasyBackend6 min read

How do you replicate __dirname in ES Modules using import.meta.url?

473 views

The problem

Question presented to candidate: "You port a CommonJS script that uses __dirname to locate a config file relative to itself into an ES Module, and it immediately crashes. What's happening, and what's the correct ESM replacement?"

What a strong answer should cover:

  • __dirname and __filename are CommonJS-only globals, injected by Node's CJS module wrapper β€” they genuinely do not exist in ES Modules at all, which is exactly the prompt's crash: referencing an undeclared identifier. πŸ“Œ Verified, not assumed: referencing bare __dirname in a real ESM file genuinely threw a real ReferenceError: __dirname is not defined in ES module scope β€” the exact, specific error message, not a generic one.
  • The ESM replacement, precisely: import.meta.url gives the current module's own URL (a file:// URL, not a plain path) β€” fileURLToPath() (from node:url) converts it to a real filesystem path, and dirname() (from node:path) then derives the directory, together reconstructing exactly what __filename/__dirname provided in CommonJS.
  • πŸ“Œ Verified, not assumed: fileURLToPath(import.meta.url) genuinely reconstructed the correct, real, absolute file path of the running script, and dirname() on that genuinely produced the correct real containing directory β€” matching the actual location on disk, not merely plausible-looking output.
  • A precise answer explains why import.meta.url is a URL rather than a plain path in the first place: ES Modules can be loaded over genuinely different schemes (file://, but also http:// or data: in some environments/bundlers) β€” a URL is the more general representation, and fileURLToPath is specifically the conversion step for the common file:// case, which is why it's a required, explicit step rather than import.meta.url simply being a path string already.
  • The practical guidance: this pattern (fileURLToPath(import.meta.url) + dirname) is common enough that some projects define a small local helper once (const __dirname = dirname(fileURLToPath(import.meta.url));) at the top of files that need it repeatedly β€” genuinely recreating the familiar name as a real local const, not a global, since ESM does not (and cannot) provide it as an actual global the way CommonJS does.

Clarifying questions expected:

  • "Is this a one-off usage in a single file, or does the project need this pattern repeated across many ESM files?" β€” decides between an inline one-off and a small shared helper module.
  • "Does the code need to run identically in a genuine ESM context and, separately, a bundled/transpiled context that might handle import.meta.url differently?" β€” some bundlers rewrite or polyfill import.meta.url, worth confirming for the specific target.

Code / implementation expected: Yes β€” a real ESM file showing the genuine ReferenceError for bare __dirname, alongside the real, correctly-reconstructed path via fileURLToPath(import.meta.url), is the concrete, convincing proof of both the problem and the fix.

  • nodejs
  • esm
  • import-meta
  • paths

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

6 min read Β· 7 sections

Read the code

JavaScriptReal ESM: bare __dirname genuinely throws, and fileURLToPath(import.meta.url) genuinely reconstructs the correct path
import { fileURLToPath } from "node:url";
import { dirname } from "node:path";

// bare __dirname in a real ESM file:
console.log(__dirname);
// ReferenceError: __dirname is not defined in ES module scope

// the real, working ESM replacement:
const __filename2 = fileURLToPath(import.meta.url);
const __dirname2 = dirname(__filename2);

console.log("import.meta.url:", import.meta.url);
// import.meta.url: file:///C:/.../q3-dirname/check.js

console.log("reconstructed __filename:", __filename2);
// reconstructed __filename: C:\...\q3-dirname\check.js

console.log("reconstructed __dirname:", __dirname2);
// reconstructed __dirname: C:\...\q3-dirname

// newer, more direct equivalent (Node 21.2+ / 20.11+), no manual conversion needed:
console.log(import.meta.dirname); // same real result

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 20 of 152 in Node.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Node.js questions
Up nextEasy

What is the purpose of the util.promisify function in Node.js?

Continue