__dirname: It is a local variable that returns the directory name of the current module. It returns the folder path of the current JavaScript file.
The __dirname or __filename global variables are not available in ECMAScript module files. To solve the "__dirname is not defined in ES module scope" error, import and use the dirname() method from the path module. The dirname method takes a path as a parameter and returns the directory name of the path.
As of Node.js 10.12 there's an alternative that doesn't require creating multiple files and handles special characters in filenames across platforms:
import { dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
The most standardized way in 2021
import { URL } from 'url'; // in Browser, the URL in native accessible on window
const __filename = new URL('', import.meta.url).pathname;
// Will contain trailing slash
const __dirname = new URL('.', import.meta.url).pathname;
And forget about join
to create paths from the current file, just use the URL
const pathToAdjacentFooFile = new URL('./foo.txt', import.meta.url).pathname;
const pathToUpperBarFile = new URL('../bar.json', import.meta.url).pathname;
For Node 10.12 +...
Assuming you are working from a module, this solution should work, and also gives you __filename support as well
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
The nice thing is that you are also only two lines of code away from supporting require() for CommonJS modules. For that you would add:
import { createRequireFromPath } from 'module';
const require = createRequireFromPath(__filename);
There have been proposals about exposing these variables through import.meta
, but for now, you need a hacky workaround that I found here:
// expose.js
module.exports = {__dirname};
// use.mjs
import expose from './expose.js';
const {__dirname} = expose;
In most cases, using what is native to Node.js (with ES Modules), not external resources, the use of __filename
and __dirname
for most cases can be totally unnecessary. Most (if not all) of the native methods for reading (streaming) supports the new URL
+ import.meta.url
, exactly as the official documentation itself suggests:
require.resolve
As you can see in the description of the methods, the path
parameter shows the supported formats, and in them include the <URL>
, examples:
Method | path param supports |
---|---|
fs.readFile(path[, options], callback) |
<string> , <Buffer> , <URL> , <integer>
|
fs.readFileSync(path[, options]) |
<string> , <Buffer> , <URL> , <integer>
|
fs.readdir(path[, options], callback) |
<string> , <Buffer> , <URL>
|
fs.readdirSync(path[, options]) |
<string> , <Buffer> , <URL> , <integer>
|
fsPromises.readdir(path[, options]) |
<string> , <Buffer> , <URL>
|
fsPromises.readFile(path[, options]) |
<string> , <Buffer> , <URL> , <FileHandle>
|
So with new URL('<path or file>', import.meta.url)
it solves and you don't need to be treating strings and creating variables to be concatenated later.
Examples:
See how it is possible to read a file at the same level as the script without needing __filename
or any workaround:
import { readFileSync } from 'fs';
const output = readFileSync(new URL('./foo.txt', import.meta.url));
console.log(output.toString());
List all files in the script directory:
import { readdirSync } from 'fs';
readdirSync(new URL('./', import.meta.url)).forEach((dirContent) => {
console.log(dirContent);
});
Note: In the examples I used the synchronous functions just to make it easier to copy and execute.
If the intention is to make a "own log" (or something similar) that will depend on third parties, it is worth some things done manually, but within the language and Node.js this is not necessary, with ESMODULES
it is totally possible not to depend on either __filename
and neither __dirname
, since native resources with new URL
with already solve it.
Note that if you are interested in using something like require
at strategic times and need the absolute path from the main script, you can use module.createRequire(filename)
(Node.js v12.2.0 + only) combined with import.meta.url
to load scripts at levels other than the current script level, as this already helps to avoid the need for __dirname
, an example using import.meta.url
with module.createRequire
:
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
// foo-bar.js is a CommonJS module.
const fooBar = require('./foo-bar');
fooBar();
Source from foo-bar.js
:
module.exports = () => {
console.log('hello world!');
};
Which is similar to using without "ECMAScript modules":
const fooBar = require('./foo-bar');
I used:
import path from 'path';
const __dirname = path.resolve(path.dirname(decodeURI(new URL(import.meta.url).pathname)));
decodeURI
was important: used spaces and other stuff within the path on my test system.
path.resolve()
handles relative urls.
edit:
fix to support windows (/C:/...
=> C:/...
):
import path from 'path';
const __dirname = (() => {let x = path.dirname(decodeURI(new URL(import.meta.url).pathname)); return path.resolve( (process.platform == "win32") ? x.substr(1) : x ); })();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With