-
-
Notifications
You must be signed in to change notification settings - Fork 33.2k
Description
- Version: v8.1.2
- Platform: 64-bit Windows 10
- Subsystem: path
On systems with case-insensitive filesystems, path.basename nevertheless matches its ext
parameter case-sensitively, which enables subtle bugs like istanbuljs/spawn-wrap#56 and is inconsistent with the behavior of other APIs like fs.existsSync that observe the case-sensitivity of the filesystem:
> process.execPath
'c:\\Program Files\\nodejs\\node.exe'
> process.execPath.toUpperCase()
'C:\\PROGRAM FILES\\NODEJS\\NODE.EXE'
> fs.existsSync(process.execPath)
true
> fs.existsSync(process.execPath.toUpperCase())
true
> path.basename(process.execPath, '.exe')
'node'
> path.basename(process.execPath.toUpperCase(), '.exe')
'NODE.EXE'
path.basename's behavior is, however, consistent with the basename program on the Unix-like systems I've tested, which also matches an extension case-sensitively, even on a case-insensitive filesystem (like my macOS system). Nevertheless, it feels like a footgun, as some developers will expect the behavior to be consistent across the fs and path APIs.
By comparison:
- Perl's equivalent matches the extension case-insensitively on case-insensitive filesystems: http://perldoc.perl.org/File/Basename.html
- Ruby's equivalent, on the other hand, appears to match case-sensitively on all platforms: https://stackoverflow.com/questions/15494565/is-there-a-file-basename-with-case-insensitive-suffix
- Python's equivalent doesn't even offer the option to strip an extension: https://docs.python.org/2/library/os.path.html#os.path.basename
- The POSIX specification for basename also doesn't offer the option to strip an extension: http://pubs.opengroup.org/onlinepubs/009695399/functions/basename.html
Note that 7c731ec added the comment "// TODO: make this comparison case-insensitive on windows? " to path.basename back in 2011. Then #5123 removed it as part of a broader rewrite (although there's no evidence that the latter change was intended to resolve the TODO one way or the other).
I could argue this either way, so I would understand intentionally not fixing it. But it does seem like a footgun, given the aforementioned bug (for which I've submitted a fix that does the extension stripping in a separate String.replace operation). At the very least, I thought it worth an issue, so even if you decide not to change the behavior, that decision will be documented somewhere.