Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

path.posix.resolve, path.posix.relative return wrong path on Windows #13887

Closed
DuanPengfei opened this issue Jun 23, 2017 · 22 comments
Closed

path.posix.resolve, path.posix.relative return wrong path on Windows #13887

DuanPengfei opened this issue Jun 23, 2017 · 22 comments
Labels
path Issues and PRs related to the path subsystem. windows Issues and PRs related to the Windows platform.

Comments

@DuanPengfei
Copy link
Contributor

DuanPengfei commented Jun 23, 2017

  • Version: v8.1.1
  • Platform: Windows 10
  • Subsystem: path

path.posix.resolve, path.posix.relative return wrong path on Windows.

For example:

path.posix.resolve('a/b/c');  // returns: 'C:\\Users\\fei\\Desktop\\share\\node/a/b/c'

Ref: #13683

@mscdex mscdex added the path Issues and PRs related to the path subsystem. label Jun 23, 2017
@DuanPengfei
Copy link
Contributor Author

Hope I can find a good way to fix it. 😂

@refack
Copy link
Contributor

refack commented Jun 23, 2017

I had an idea, add a new "strict mode" to path:

const path = require('path');
path.posix.resolve('a/b/c');  // returns: 'C:\\Users\\fei\\Desktop\\share\\node/a/b/c';
path.strict = true;
path.posix.resolve('a/b/c');  // throws

@seishun
Copy link
Contributor

seishun commented Jun 23, 2017

Can you clarify why it's wrong?

@refack
Copy link
Contributor

refack commented Jun 23, 2017

Can you clarify why it's wrong?

path.posix.resolve('a/b/c');  // returns: 'C:\\Users\\fei\\Desktop\\share\\node/a/b/c';

The result is not a valid path nor on windows (the host) and definitely not on POSIX (the expected target).
The root cause is that resolve uses process.cwd() which is the only point where the path module does something that is not pure string manipulation.

See discussion at: #13683 (comment)

@tniessen
Copy link
Member

I am against introducing a strict mode. Instead, I would prefer to always throw if process.cwd() needs to be used and the host platform is not the same as the API platform. If we care about breakage too much, we can introduce a deprecation cycle such that path.posix.resolve and path.win32.resolve will print warnings instead of throwing.

I could put together a PR if this gets approval.

@refack
Copy link
Contributor

refack commented Jun 24, 2017

I am against introducing a strict mode. Instead, I would prefer to always throw if process.cwd() needs to be used and the host platform is not the same as the API platform. If we care about breakage too much, we can introduce a deprecation cycle such that path.posix.resolve and path.win32.resolve will print warnings instead of throwing.

@tniessen I had the same opinion, but @mscdex and @addaleax are strongly against. The point they both raised is that this is not strictly a bug but a weird behavior that people are used to, and throwing will block "valid" code paths. With that in mind, strict mode would allow us in the future change the default behavior while allowing opting-out by setting path.strict = false;

@tniessen
Copy link
Member

Introducing a global setting for such a "strict mode" sounds like an unnecessary side effect to me (unless you want to make that setting per module or similar), e.g. if an application enables strict mode and a dependency expects non-strict mode or vice versa. If you really want to implement different modes, I would suggest require('path').strict().

I would like to include @addaleax in this discussion so we don't have to split it and continue parts of it in #13683.

Throwing an error instead of fixing the bug seems like a really bad idea; I think there is no reason inherent to the API why path.posix.relative('a/b/c', '../../x') wouldn’t be able to return a correct result on Windows.
(#13683 (comment))

I understand that; but that should work just fine on Windows, no?
(#13683 (comment))

Let's look at something simple:

path.posix.resolve('foo/bar')

So what would be the "correct result" on Windows? Let's say process.cwd() is F:\node. Would you expect F:\node/foo/bar? Not a good idea, it is not a valid posix path. How about F:/node/foo/bar? Looks good, but resolve must return an absolute path, and path.posix.isAbsolute('F:/node/foo/bar') is false, and there is no way to change that, as this is a relative path on posix systems. How about /F:/node/foo/bar? Too bad, now it is not even a valid path on Windows anymore.

@XadillaX
Copy link
Contributor

how about starting a vote?

@addaleax
Copy link
Member

I also really don’t like the idea of strict mode; all path functions currently are pure, up to their dependency on cwd(), and that’s a good thing. If you want it, add an extra parameter or add new methods, don’t introduce global mutable state.

Re: always throwing when process.cwd() is used… people are doing weird stuff with path, and the thing is, the end result isn’t always dependent on all of the input. For example, one could use path.resolve() on two different paths, where the cwd may or may not be used, and then call path.relative() and get a result that doesn’t actually depend on the cwd or only parts of it, even though the intermediate results do.

It’s a nice thing that explicit path.posix.resolve() is likely used much less frequently than just path.resolve(); but I am afraid users might just use that e.g. if they need URL-like paths for some reason, or basically just “want to get forward slashes” for any possible reason, rather than using the Windows method and then fixing up the result to fit their format.

How about F:/node/foo/bar? Looks good, but resolve must return an absolute path, and path.posix.isAbsolute('F:/node/foo/bar') is false

I would accept that; it’s strictly better than the current state of things, and even though path.posix.isAbsolute('F:/node/foo/bar') returns false, it is still an absolute path for all other purposes.

how about starting a vote?

Our process is consensus-seeking, so usually things don’t work that way; we’ll keep discussing the issue (and maybe coming up with new solutions) until we either reach consensus or it becomes clear that we won’t reach consensus. I don’t think we’re at either point yet.

If we won’t be able to get consensus, we’ll likely put this on the CTC meeting agenda, where it will be discussed, and we’ll only vote if the CTC won’t get consensus either. I think it’s unlikely that it will come to that.

@tniessen
Copy link
Member

don’t introduce global mutable state.

Definitely +1!

and the thing is, the end result isn’t always dependent on all of the input

Good point, we might need to come up with a better solution.

I would accept that; it’s strictly better than the current state of things, and even though path.posix.isAbsolute('F:/node/foo/bar') returns false, it is still an absolute path for all other purposes.

Then let's have a look at path.posix.resolve(process.cwd(), 'foo'). Everything is fine when executed under linux, but it will return something like 'F:\\node/F:\\node/foo' on Windows, and there is no way to change that, because the posix API considers the result of process.cwd() relative.
Sure, you could say that it does not make sense to do this, using a Windows API call with the posix API... But that's exactly what we are doing in the current path module.

If we decide not to throw an Error in these situations, we should at least add notes to the docs explaining the current behavior (after fixing bugs as in #13683).

@refack
Copy link
Contributor

refack commented Jun 25, 2017

don’t introduce global mutable state.

Definitely +1!

Offf I forgot about the globalness of modules... How about something like...

const path = require('path');
path.strict.posix.resolve('.\\gaga') // throws
const { strict: spath } = path;
spath.posix.resolve('.\\gaga') // throws

Again it's opt-in, and gives us a path to deprecation with explicit and opt-out in the future.

@refack
Copy link
Contributor

refack commented Jun 25, 2017

Another idea: an implementation that's pure strings, with an optional cwd as arg

const { Pure } = require('path');
const purePath1 = new Pure('C:\\fakepath')
purePath1.resolve('gaga') === 'C:\\fakepath\\gaga';
const purePath2 = new Pure();
purePath1.resolve('gaga') === Pure.resolve('gaga') // throws or returns just 'gaga'

[refack: tweaked code]

@DuanPengfei
Copy link
Contributor Author

I also prefer the pure function, but if this is done, does it mean that the history code needs to be modified?

@refack
Copy link
Contributor

refack commented Jun 26, 2017

@DuanPengfei the idea in not to change the behaviour of the old code, but add a new way to use it:

const path = require('path');
path.Pure.resolve('gaga') === path.Pure.posix.resolve('gaga') === path.Pure.windows.resolve('gaga') // either all throw or all returns just 'gaga'
// old code stays the same
path.posix.resolve('gaga') === 'C:\\bin\\dev\\node/gaga';

@DuanPengfei
Copy link
Contributor Author

@refack I see

@DuanPengfei
Copy link
Contributor Author

I will create a new PR do the same thing as the PR #13738. And then we discuss whether it is reasonable?

@refack
Copy link
Contributor

refack commented Jun 27, 2017

With just the improvements? Sounds good.

@GongT
Copy link

GongT commented Sep 3, 2018

path.posix.isAbsolute('//?/X:/yyy.txt') === true 😘

https://docs.microsoft.com/windows/desktop/FileIO/naming-a-file#win32-file-namespaces

@tniessen
Copy link
Member

tniessen commented Sep 3, 2018

@GongT That seems to be correct. You are using the POSIX API, why would it recognize Windows namespaces?

@GongT
Copy link

GongT commented Sep 3, 2018

@tniessen ? is valid file name on posix system. so //?/xxx is valid and absolute, that's works as indent.

@jeremyong
Copy link

I keep coming across this type of issue on github and the wild. The assumption that on Windows, the path separation and parsing desired is not POSIX is a bad assumption. People that develop in the wild use things like bash for windows, mingw, msys, etc. The API constraining you based on the OS used makes path among the least portable modules in NodeJS for developing cross platform scripts.

@jasnell
Copy link
Member

jasnell commented Jun 25, 2020

This is actually a duplicate of #13683 ... closing this one

@jasnell jasnell closed this as completed Jun 25, 2020
AngusMorton added a commit to AngusMorton/run that referenced this issue May 2, 2023
Using path.posix.relative is not an option because it returns the wrong path.
See:
* nodejs/node#13887
* nodejs/node#13738
rturnq pushed a commit to marko-js/run that referenced this issue May 3, 2023
Using path.posix.relative is not an option because it returns the wrong path.
See:
* nodejs/node#13887
* nodejs/node#13738
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
path Issues and PRs related to the path subsystem. windows Issues and PRs related to the Windows platform.
Projects
None yet
Development

No branches or pull requests