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!');
};
Top comments (3)
Some additions:
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
new URL('file:///C:/path/').pathname; // Incorrect: /C:/path/
fileURLToPath('file:///C:/path/'); // Correct: C:\path\ (Windows)
new URL('file://nas/foo.txt').pathname; // Incorrect: /foo.txt
fileURLToPath('file://nas/foo.txt'); // Correct: \nas\foo.txt (Windows)
new URL('file:///你好.txt').pathname; // Incorrect: /%E4%BD%A0%E5%A5%BD.txt
fileURLToPath('file:///你好.txt'); // Correct: /你好.txt (POSIX)
new URL('file:///hello world').pathname; // Incorrect: /hello%20world
fileURLToPath('file:///hello world'); // Correct: /hello world (POSIX)
@since — v10.12.0
@param url — The file URL string or URL object to convert to a path.
@return — The fully-resolved platform-specific Node.js file path.
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'
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.