Node: Support "file:///" URIs in fs and path functions

Created on 22 Jan 2020  路  6Comments  路  Source: nodejs/node

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.)

fs path

Most helpful comment

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).

All 6 comments

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mcollina picture mcollina  路  3Comments

cong88 picture cong88  路  3Comments

danielstaleiny picture danielstaleiny  路  3Comments

danialkhansari picture danialkhansari  路  3Comments

addaleax picture addaleax  路  3Comments