Skip to content

Commit 1f09634

Browse files
committed
vfs: fix module loader paths on Windows
1 parent efc7db4 commit 1f09634

2 files changed

Lines changed: 33 additions & 9 deletions

File tree

lib/internal/modules/esm/resolve.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const {
1919
StringPrototypeSlice,
2020
StringPrototypeSplit,
2121
StringPrototypeStartsWith,
22+
decodeURIComponent,
2223
encodeURIComponent,
2324
} = primordials;
2425
const assert = require('internal/assert');
@@ -239,9 +240,17 @@ function finalizeResolution(resolved, base, preserveSymlinks) {
239240
try {
240241
path = fileURLToPath(resolved);
241242
} catch (err) {
242-
setOwnProperty(err, 'input', `${resolved}`);
243-
setOwnProperty(err, 'module', `${base}`);
244-
throw err;
243+
// Windows rejects drive-less file URLs such as file:///mnt/app.js before
244+
// loader hooks can see them. If a mounted VFS owns the URL pathname, use
245+
// that pathname as the virtual absolute path; otherwise preserve the
246+
// native file URL validation error.
247+
const pathname = decodeURIComponent(resolved.pathname);
248+
if (loaderGetLayerForPath(pathname) === undefined) {
249+
setOwnProperty(err, 'input', `${resolved}`);
250+
setOwnProperty(err, 'module', `${base}`);
251+
throw err;
252+
}
253+
path = pathname;
245254
}
246255

247256
const stats = loaderStat(

lib/internal/vfs/file_system.js

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const {
1515
const { validateBoolean } = require('internal/validators');
1616
const { MemoryProvider } = require('internal/vfs/providers/memory');
1717
const path = require('path');
18-
const { posix: pathPosix, isAbsolute, resolve: resolvePath } = path;
18+
const { posix: pathPosix, resolve: resolvePath, toNamespacedPath } = path;
1919
const { join: joinPath } = pathPosix;
2020
const {
2121
isUnderMountPoint,
@@ -51,6 +51,19 @@ const kLayerId = Symbol('kLayerId');
5151
// messages.
5252
let nextLayerId = 0;
5353

54+
/**
55+
* Normalizes paths for mount-point comparisons. On Windows, `path.resolve('/x')`
56+
* yields `C:\x`, while loader internals often pass namespaced paths like
57+
* `\\?\C:\x`. Compare using a single canonical representation so POSIX-style
58+
* rooted test paths, drive-absolute paths, and namespaced paths all route to the
59+
* same VFS mount.
60+
* @param {string} inputPath
61+
* @returns {string}
62+
*/
63+
function normalizeMountedPath(inputPath) {
64+
return toNamespacedPath(resolvePath(inputPath));
65+
}
66+
5467
// Lazy-loaded VFS setup
5568
let registerVFS;
5669
let deregisterVFS;
@@ -195,8 +208,9 @@ class VirtualFileSystem {
195208
if (!this[kMounted] || !this[kMountPoint]) {
196209
return false;
197210
}
198-
const normalized = isAbsolute(inputPath) ? inputPath : resolvePath(inputPath);
199-
return isUnderMountPoint(normalized, this[kMountPoint]);
211+
const normalized = normalizeMountedPath(inputPath);
212+
const mountPoint = normalizeMountedPath(this[kMountPoint]);
213+
return isUnderMountPoint(normalized, mountPoint);
200214
}
201215

202216
// ==================== Path Resolution ====================
@@ -209,11 +223,12 @@ class VirtualFileSystem {
209223
*/
210224
#toProviderPath(inputPath) {
211225
if (this[kMounted] && this[kMountPoint]) {
212-
const resolved = isAbsolute(inputPath) ? inputPath : resolvePath(inputPath);
213-
if (!isUnderMountPoint(resolved, this[kMountPoint])) {
226+
const resolved = normalizeMountedPath(inputPath);
227+
const mountPoint = normalizeMountedPath(this[kMountPoint]);
228+
if (!isUnderMountPoint(resolved, mountPoint)) {
214229
throw createENOENT('open', inputPath);
215230
}
216-
return getRelativePath(resolved, this[kMountPoint]);
231+
return getRelativePath(resolved, mountPoint);
217232
}
218233
return pathPosix.normalize(inputPath);
219234
}

0 commit comments

Comments
 (0)