Skip to content

Commit cb201c4

Browse files
committed
vfs: add Windows path compatibility
- Use path.isAbsolute() for cross-platform absolute path detection instead of checking for '/' prefix only - Use path.posix.join() in createScopedVFS to ensure forward slashes on all platforms since VFS uses '/' internally - Fix ESM module hook to recognize Windows absolute paths (C:\) - Fix symlink target resolution for Windows paths This enables VFS to work correctly on Windows where absolute paths use drive letters (e.g., C:\path) instead of Unix-style forward slashes.
1 parent 977cc3d commit cb201c4

File tree

4 files changed

+21
-10
lines changed

4 files changed

+21
-10
lines changed

lib/internal/vfs/entries.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ const {
1212
ERR_INVALID_STATE,
1313
},
1414
} = require('internal/errors');
15-
const { join } = require('path');
15+
// Use path.posix.join for cross-platform consistency - VFS uses forward slashes internally
16+
const { posix: { join } } = require('path');
1617
const { createFileStats, createDirectoryStats, createSymlinkStats } = require('internal/vfs/stats');
1718

1819
// Symbols for private properties

lib/internal/vfs/module_hooks.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const {
88
StringPrototypeStartsWith,
99
} = primordials;
1010

11-
const { dirname, resolve } = require('path');
11+
const { dirname, isAbsolute, resolve } = require('path');
1212
const { normalizePath } = require('internal/vfs/router');
1313
const { pathToFileURL, fileURLToPath, URL } = require('internal/url');
1414
const { createENOENT } = require('internal/vfs/errors');
@@ -331,8 +331,8 @@ function vfsResolveHook(specifier, context, nextResolve) {
331331
let checkPath;
332332
if (StringPrototypeStartsWith(specifier, 'file:')) {
333333
checkPath = fileURLToPath(specifier);
334-
} else if (specifier[0] === '/') {
335-
// Absolute path
334+
} else if (isAbsolute(specifier)) {
335+
// Absolute path (Unix / or Windows C:\)
336336
checkPath = specifier;
337337
} else if (specifier[0] === '.') {
338338
// Relative path - need to resolve against parent

lib/internal/vfs/router.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const {
99
StringPrototypeStartsWith,
1010
} = primordials;
1111

12-
const { basename, resolve, sep } = require('path');
12+
const { basename, isAbsolute, resolve, sep } = require('path');
1313

1414
/**
1515
* Normalizes a path for VFS lookup.
@@ -131,4 +131,5 @@ module.exports = {
131131
isUnderMountPoint,
132132
getRelativePath,
133133
joinMountPath,
134+
isAbsolutePath: isAbsolute,
134135
};

lib/internal/vfs/virtual_fs.js

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const {
2525
getBaseName,
2626
isUnderMountPoint,
2727
getRelativePath,
28+
isAbsolutePath,
2829
} = require('internal/vfs/router');
2930
const {
3031
createENOENT,
@@ -182,8 +183,8 @@ class VirtualFileSystem {
182183
* @returns {string} The resolved path
183184
*/
184185
resolvePath(inputPath) {
185-
// If path is absolute, return as-is
186-
if (inputPath.startsWith('/')) {
186+
// If path is absolute, return as-is (works for both Unix / and Windows C:\)
187+
if (isAbsolutePath(inputPath)) {
187188
return normalizePath(inputPath);
188189
}
189190

@@ -471,13 +472,21 @@ class VirtualFileSystem {
471472
const target = symlink.target;
472473
let targetPath;
473474

474-
if (target.startsWith('/')) {
475+
if (isAbsolutePath(target)) {
475476
// Absolute symlink target - interpret as VFS-internal path
476477
// If mounted, prepend the mount point to get the actual filesystem path
478+
// Normalize first to handle Windows paths (convert \ to /)
479+
const normalizedTarget = normalizePath(target);
477480
if (this[kMounted] && this[kMountPoint]) {
478-
targetPath = this[kMountPoint] + target;
481+
// For VFS-internal absolute paths starting with /, prepend mount point
482+
// For external absolute paths (like C:/...), use as-is
483+
if (target.startsWith('/')) {
484+
targetPath = this[kMountPoint] + normalizedTarget;
485+
} else {
486+
targetPath = normalizedTarget;
487+
}
479488
} else {
480-
targetPath = target;
489+
targetPath = normalizedTarget;
481490
}
482491
} else {
483492
// Relative symlink target - resolve relative to symlink's parent directory

0 commit comments

Comments
 (0)