Why is __dirname not defined in node REPL?
Asked Answered
C

15

300

From the node manual I see that I can get the directory of a file with __dirname, but from the REPL this seems to be undefined. Is this a misunderstanding on my side or where is the error?

$ node
> console.log(__dirname)
ReferenceError: __dirname is not defined
    at repl:1:14
    at REPLServer.eval (repl.js:80:21)
    at Interface.<anonymous> (repl.js:182:12)
    at Interface.emit (events.js:67:17)
    at Interface._onLine (readline.js:162:10)
    at Interface._line (readline.js:426:8)
    at Interface._ttyWrite (readline.js:603:14)
    at ReadStream.<anonymous> (readline.js:82:12)
    at ReadStream.emit (events.js:88:20)
    at ReadStream._emitKey (tty.js:320:10)
Carlitacarlo answered 11/1, 2012 at 10:5 Comment(3)
__dirname and __filename are also not available when node is called with the --experimental-modules flag.Jeffcott
I got this issue when using eslint, I had mistakenly set "browser": true instead of "node": true in my .eslintrc.json file.Sarah
If nothing works here's a hack https://mcmap.net/q/46785/-why-is-__dirname-not-defined-in-node-replNesta
D
236

__dirname is only defined in scripts. It's not available in REPL.

try make a script a.js

console.log(__dirname);

and run it:

node a.js

you will see __dirname printed.

Added background explanation: __dirname means 'The directory of this script'. In REPL, you don't have a script. Hence, __dirname would not have any real meaning.

Drypoint answered 11/1, 2012 at 10:12 Comment(6)
Also you can't use some of the Global variables inside RequireJS modules. If you use RequireJS on the server side, see #9027929.Khanna
Yeah, that should really be added to the answer Eye, because that's what got me.Donndonna
Not adding that in to the REPL's load script is obnoxious. I can't think of any reason it wouldn't be there...Esparto
I loaded a script file while inside the REPL using .load script.js. It's too bad __dirname still isn't available from within script.jsNorthernmost
upvoted! saved me 15 mins as I was wondering wtf is happeningAlight
this just restated the question without answering itJerrelljerri
S
344

If you are using Node.js modules, __dirname and __filename don't exist.

From the Node.js documentation:

No require, exports, module.exports, __filename, __dirname

These CommonJS variables are not available in ES modules.

require can be imported into an ES module using module.createRequire().

Equivalents of __filename and __dirname can be created inside of each file via import.meta.url:

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

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

https://nodejs.org/docs/latest-v15.x/api/esm.html#esm_no_filename_or_dirname

Sounding answered 14/7, 2020 at 9:53 Comment(5)
Technically this is the only correct answer here. All of the other solutions use the current working directory. Which is different from the __dirname feature that is really the folder holding the currently running script.Cowpea
This is the perfect answer for this exact question. Thank you, it helped me a lot! @RaviLuthra is 100% correct.Gerladina
Concurr this is the only correct answer here "if" problem is relating to ES6 modules type of architecture. Import statement needs to reference path that evetnually gets passed downt to __dirname which is the feature that is really the folder holding the currently running script. –Drudgery
Obviously import.meta.url is undefined when cat script.js | node --input-type moduleKarlynkarma
REPL context is not ES module. This answer is completely missing the point of the original question. import.meta is inaccessible from a REPL context as @SaberHayati mentioned, and this answer does not work at all. I don't know why it's getting so many upvotes.Allotropy
D
236

__dirname is only defined in scripts. It's not available in REPL.

try make a script a.js

console.log(__dirname);

and run it:

node a.js

you will see __dirname printed.

Added background explanation: __dirname means 'The directory of this script'. In REPL, you don't have a script. Hence, __dirname would not have any real meaning.

Drypoint answered 11/1, 2012 at 10:12 Comment(6)
Also you can't use some of the Global variables inside RequireJS modules. If you use RequireJS on the server side, see #9027929.Khanna
Yeah, that should really be added to the answer Eye, because that's what got me.Donndonna
Not adding that in to the REPL's load script is obnoxious. I can't think of any reason it wouldn't be there...Esparto
I loaded a script file while inside the REPL using .load script.js. It's too bad __dirname still isn't available from within script.jsNorthernmost
upvoted! saved me 15 mins as I was wondering wtf is happeningAlight
this just restated the question without answering itJerrelljerri
J
175

Building on the existing answers here, you could define this in your REPL:

__dirname = path.resolve(path.dirname(''));

Or:

__dirname = path.resolve();

If no path segments are passed, path.resolve() will return the absolute path of the current working directory.


Or @Jthorpe's alternatives:

__dirname = process.cwd();
__dirname = fs.realpathSync('.');
__dirname = process.env.PWD
Jarib answered 2/2, 2015 at 14:11 Comment(3)
if you use nesh you can define this as part of your load script; it's niftyEsparto
or __dirname = process.cwd() or __dirname=fs.realpathSync('.') or __dirname = process.env.PWDTether
path.dirname seems to not accept non-string values anymore in the newest major version, 6.0.0, so the first suggestion in this answer will not work.Warnerwarning
C
68

In ES6 use:

import path from 'path';
const __dirname = path.resolve();

also available when node is called with --experimental-modules

Crumley answered 17/9, 2019 at 10:21 Comment(7)
You don't need to import core modules in the REPL; it will load them on the fly for you.Jarib
This gives the current working directory, not the directory of the current .js file.Manyplies
@Dirbaio, what would be the current .js file when you're in the REPL?Jarib
@Jarib just checked, it seems to give the CWDManyplies
@Manyplies I think that was my point. When you enter the REPL, there is no current JS file. What would you want __dirname to be, if not the CWD?Jarib
This is wrong. __dirname is meant to be current modules directory, but your solution makes it current working directory. How did this nonsen get so many upvotes is beyond my understanding.Donndonna
path.resolve() is the root dir of the project or CWD, it is not __dirnameTineid
P
21

-> At ES6 version use :

import path from "path"
const __dirname = path.resolve();

-> And use it like this for example:

res.sendFile(path.join(__dirname ,'views','shop.html'))
Protrusive answered 24/3, 2021 at 20:40 Comment(3)
This is NOT equivalent to __dirname but rather process.cwd(). For example, you will get different results when running the script from different directories. When using ESM a better approach is to use import.meta.url. See nodejs.org/docs/latest-v14.x/api/esm.html#esm_import_meta and #46745514Paola
This worked for me inside a docker. ThanksDesmonddesmoulins
does it works with browser?Revels
S
7

As @qiao said, you can't use __dirname in the node repl. However, if you need need this value in the console, you can use path.resolve() or path.dirname(). Although, path.dirname() will just give you a "." so, probably not that helpful. Be sure to require('path').

Sunsunbaked answered 11/9, 2014 at 20:48 Comment(0)
W
7

I was also trying to join my path using path.join(__dirname, 'access.log') but it was throwing the same error.

Here is how I fixed it:

I first imported the path package and declared a variable named __dirname, then called the resolve path method.

In CommonJS

var path = require("path");

var __dirname = path.resolve();

In ES6+

import path  from 'path';

const __dirname = path.resolve();

Happy coding.......

Whitewood answered 12/4, 2020 at 19:22 Comment(2)
I am on node v15.3.0 and I do not need to use path.resolve(). __dirname just works out of the box. Is your answer still relevant to ES6+?Risk
@Risk __dirname is only available on Node 14 or higher if you are using CommonJS. But ECMA is pushing towards ES modules being the standard instead of CommonJS.Jacklyn
C
4

If you got node __dirname not defined with node --experimental-modules, you can do :

const __dirname = path.dirname(import.meta.url)
                      .replace(/^file:\/\/\//, '') // can be usefull

Because othe example, work only with current/pwd directory not other directory.

Cologarithm answered 8/7, 2020 at 10:14 Comment(0)
P
3

Seems like you could also do this:

__dirname=fs.realpathSync('.');

of course, dont forget fs=require('fs')

(it's not really global in node scripts exactly, its just defined on the module level)

Pashm answered 21/10, 2015 at 6:24 Comment(1)
You don't need to require core modules in the REPL; it will load them on the fly for you.Jarib
N
3

I was running a script from batch file as SYSTEM user and all variables like process.cwd() , path.resolve() and all other methods would give me path to C:\Windows\System32 folder instead of actual path. During experiments I noticed that when an error is thrown the stack contains a true path to the node file.

Here's a very hacky way to get true path by triggering an error and extracting path from e.stack. Do not use.

// this should be the name of currently executed file
const currentFilename = 'index.js';

function veryHackyGetFolder() {
  try {
    throw new Error();
  } catch(e) {
    const fullMsg = e.stack.toString();
    const beginning = fullMsg.indexOf('file:///') + 8;
    const end = fullMsg.indexOf('\/' + currentFilename);
    const dir = fullMsg.substr(beginning, end - beginning).replace(/\//g, '\\');
    return dir;
  }
}

Usage

const dir = veryHackyGetFolder();
Nesta answered 17/4, 2018 at 13:12 Comment(3)
If you're not running this through the REPL, you can use __dirname and __filename.Jarib
This is the only answer that at least tries to provide correct solution.Donndonna
@Jarib I tried __dirname, __filename and everything else. No standard solution worked in my case. Here's my project which runs a process as SYSTEM from task scheduler if you're bored github.com/DVLP/Unscheduler. Any standard solution that otherwise works is obviously better than my hack above :)Nesta
C
2

Though its not the solution to this problem I would like to add it as it may help others.

You should have two underscores before dirname, not one underscore (__dirname not _dirname).

NodeJS Docs

Closegrained answered 5/2, 2017 at 7:4 Comment(1)
__dirname (with two underscores) does not work in the REPLZak
T
0

This is a workaround, but it works both in CommonJS and in ESModules. This can be used so far NodeJS is in a crisis due to the transition from CommonJS to ESModules. I have not found other ways. import.meta.url cannot be used, because TypeScript files will cease to be compiled in CommonJS.

__filename.ts

import { fileURLToPath } from 'url'

export function get__filename() {
  const error = new Error()
  const stack = error.stack
  const match = stack.match(/^Error\s+at[^\r\n]+\s+at *(?:[^\r\n(]+\((.+?)(?::\d+:\d+)?\)|(.+?)(?::\d+:\d+)?) *([\r\n]|$)/)
  const filename = match[1] || match[2]
  if (filename.startsWith('file://')) {
    return fileURLToPath(filename)
  }
  return filename
}

code.ts

if (typeof __filename === 'undefined') {
  global.__filename = get__filename()
  global.__dirname = path.dirname(__filename)
}
Tineid answered 8/3, 2023 at 8:22 Comment(0)
T
0

All in one solution, for handling cases of js, mjs, cjs files existing inside a single project and errors from bundlers.

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

function getScriptFileAbsolutePath(importMeta) {
  try {
    return { __dirname, __filename };
  } catch (e) {
    if (!importMeta?.url) {
      throw new Error("bad importMeta");
    }
    let __filename = fileURLToPath(importMeta.url);
    let __dirname = dirname(__filename);
    return { __dirname, __filename };
  }
}

console.log(getScriptFileAbsolutePath(import.meta));
Tapia answered 21/1 at 23:59 Comment(0)
B
0

My issue was that there were no errors generated by either typescript or eslint about this in my ESM project. Adding the following to my eslint config fixed this:

{
  rules: {
    // Disallow commonjs globals
    "no-restricted-globals": [
      "error",
      { name: "__filename" },
      { name: "__dirname" },
      { name: "require" },
      { name: "module" },
      { name: "exports" },
    ],
  }
}
Benempt answered 6/2 at 15:39 Comment(0)
B
0

Starting from Nodejs v20, there is a better way:

const __dirname = import.meta.dirname;

https://nodejs.org/api/esm.html#differences-between-es-modules-and-commonjs

Bequeath answered 9/4 at 6:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.