Restore ENOTDIR semantic distinction in resolveLocalTarget on Windows

This commit is contained in:
Huanqi Cao
2026-07-05 18:57:23 +08:00
committed by imccyu
parent 130944caeb
commit 715aa7372a

View File

@@ -145,8 +145,22 @@ export async function resolveLocalTarget(cwd: string, path: string): Promise<Loc
while (true) {
try {
const realAncestor = await realpath(ancestor)
// On Windows, realpath of a regular file succeeds where POSIX returns
// ENOTDIR (the OS reports ENOENT for `regular-file/child`, not ENOTDIR).
// Stat the ancestor to restore the semantic distinction: a non-directory
// ancestor means the target passes through a file and can never be created.
/* v8 ignore start -- native Windows coverage exercises this repair; POSIX reports ENOTDIR before this point. */
if (process.platform === 'win32') {
const parentInfo = await stat(realAncestor)
if (!parentInfo.isDirectory()) {
throw new FsError(`cannot resolve "${displayPath}": a parent path segment is not a directory`, 'FS_NOT_FOUND')
}
}
/* v8 ignore stop */
return { displayPath, targetKey: FsTargetKey(join(realAncestor, ...missing)) }
} catch (error: unknown) {
/* v8 ignore next -- native Windows coverage exercises the FsError raised by the repair above. */
if (error instanceof FsError) throw error
/* v8 ignore next -- a non-ENOENT realpath failure needs a permission/IO fault. */
if (!isENOENT(error)) throw error
const parent = dirname(ancestor)