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)
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());
List all files in the script directory:
import { readdirSync } from 'fs';
readdirSync(new URL('./', import.meta.url)).forEach((dirContent) => {
console.log(dirContent);
});
As you can see in the description of the methods, the parameter shows the supported formats, and in them include the URL, examples:
fs.readFile(path[, options], callback)
path <string> | <Buffer> | <URL> | <integer>
fs.readFileSync(path[, options])
path <string> | <Buffer> | <URL> | <integer>
fsPromises.readdir(path[, options])
path <string> | <Buffer> | <URL>
fs.readdir(path[, options], callback)
path <string> | <Buffer> | <URL>
fs.readdirSync(path[, options])
path <string> | <Buffer> | <URL> | <integer>
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();
foo-bar.js
contents:
module.exports = () => {
console.log('hello world!');
};