Using url.fileURLToPath() adds an additional layer of cumbersome noise. Given import.meta.url is the replacement for __filename and __dirname, it would be awesome to have first-class support for file: in any function that accepts a file path, such as fs.readFile() and path.resolve().
This is especially painful when loading JSON in a post-CommonJS world:
Before:
const data = require('./data.json');
After (adapted from an SO answer):
import fs from 'fs';
import path from 'path';
import url from 'url';
(await () => {
const dirname = path.dirname(url.fileURLToPath(import.meta.url));
const dataPath = path.resolve(dirname, 'data.json');
const data = JSON.parse(await fs.promises.readFile(dataPath));
})();
Recommendation:
import fs from 'fs';
import path from 'path';
(await () => {
const dataPath = path.resolve(path.dirname(import.meta.url), 'data.json');
const data = JSON.parse(await fs.promises.readFile(dataPath));
})();
(Sorry if this has been requested previously. I couldn't find any prior art when searching issues.)
API's like fs.readFile() can already take a URL object but strings are always interpreted as files (as file: is a valid filename on some systems).
For clarity of the above answer you can do something like:
const dataFile = new URL("./data.json", import.meta.url);
const data = await fs.promises.readFile(dataFile);
There's also the asset references proposal which adds some syntax for it and similar use cases.
And there's also import.meta.resolve proposal for Node/Browsers which accomplishes a similar thing: https://github.com/nodejs/node/pull/31032
@jamesanthonyferguson Thanks for pointing that out. That's a good solution for the example case, but it doesn't resolve other cases where you might not know where a path came from. For example, path.resolve() allows you to use a root-relative path as an Nth argument:
path.resolve('/foo/bar', './baz');
// Returns: '/foo/bar/baz'
path.resolve('/foo/bar', '/tmp/file/');
// Returns: '/tmp/file'
This handling fails with file:/// urls:
path.resolve('/foo/bar', 'file:///tmp/file/');
// Returns: '/foo/bar/file:/tmp/file'
path.resolve('/foo/bar', new URL('file:///tmp/file/'));
// throws TypeError "path" must be string
path.resolve('/foo/bar', new URL('file:///tmp/a%20file/').pathname);
// Returns: '/tmp/a%20file'
path.resolve('/foo/bar', decodeURI(new URL('file:///tmp/a%20file/').pathname));
// Returns: '/tmp/a\ file'
I suspect all of this manipulation is why url.fileURLToPath() exists, which brings us back around to my original request.
It seems like file:/// could be handled specially, if not file: as @richardlau mentioned.
Wrong tag @shannonmoeller I think you mean @Jamesernator
@shannonmoeller any number of slashes are normalized to a single slash, so we can't special case that.
There's really nothing further actionable on this. Closing.
Most helpful comment
API's like
fs.readFile()can already take a URL object but strings are always interpreted as files (asfile:is a valid filename on some systems).