DEV Community

loading...
Cover image for Alternative for __dirname in Node when using ECMAScript modules

Alternative for __dirname in Node when using ECMAScript modules

brcontainer profile image Guilherme Nascimento ・2 min read

As I searched the Stack Overflow I came across several solutions that suggest using import.meta.url with fileURLToPath, however what was not mentioned is that the purpose of fileURLToPath is beyond "resolving" URLs with file://, as the documentation itself demonstrates (url.fileURLToPath):

fileURLToPath('file:///C:/path/');    // Output:   C:\path\ (Windows)
fileURLToPath('file://nas/foo.txt');  // Output:   \\nas\foo.txt (Windows)
fileURLToPath('file:///你好.txt');    // Output:   /你好.txt (POSIX)
fileURLToPath('file:///hello world'); // Output:   /hello world (POSIX)
Enter fullscreen mode Exit fullscreen mode

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, as the Node.js documentation itself suggests that we use:

For example, reading a file at the same level as the current script:

import { readFileSync } from 'fs';

const output = readFileSync(new URL('./foo.txt', import.meta.url));

console.log(output.toString());
Enter fullscreen mode Exit fullscreen mode

List all files in the script directory:

import { readdirSync } from 'fs';

readdirSync(new URL('./', import.meta.url)).forEach((dirContent) => {
  console.log(dirContent);
});
Enter fullscreen mode Exit fullscreen mode

As you can see in the description of the methods, the parameter shows the supported formats, and in them include the URL, examples:

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.

Note: In the examples I used the synchronous functions just to make it easier to copy and execute.

Note that if you are interested in using something like "require" in strategic moments, you can use module.createRequire(filename) (Node 12.2.0+) to load scripts at different levels from the level of the current script, example:

import { createRequire } from 'module';
const require = createRequire(import.meta.url);

// foo-bar.js is a CommonJS module.
const fooBar = require('./foo-bar');

fooBar();
Enter fullscreen mode Exit fullscreen mode

foo-bar.js contents:

module.exports = () => {
    console.log('hello world!');
};
Enter fullscreen mode Exit fullscreen mode

Discussion (2)

Collapse
routinepoutine profile image
Matthew

I'm here because of Express, where I can't apply style sheets to pug through EcmaScript 2015 import, but only through require && __dirname, 'public'

Collapse
brcontainer profile image
Guilherme Nascimento Author

Thanks for commenting. I didn't get to test the "pug" with express, but I believe that "import" recognizes the different formats ("modules" and "export"), maybe it is an internal problem, I will test it as soon as possible. If it is something you need to configure comment explaining here.

Forem Open with the Forem app