Skip to content

Commit 2cbf45b

Browse files
meixgdanielleadams
authored andcommitted
loader: fix esm resolve for symlink file
Fix: #42195 PR-URL: #42197 Fixes: #42195 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>
1 parent 17172fe commit 2cbf45b

File tree

2 files changed

+55
-9
lines changed

2 files changed

+55
-9
lines changed

β€Žlib/internal/modules/esm/resolve.js

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -398,18 +398,24 @@ function finalizeResolution(resolved, base, preserveSymlinks) {
398398
resolved.pathname, 'must not include encoded "/" or "\\" characters',
399399
fileURLToPath(base));
400400

401-
const path = fileURLToPath(resolved);
401+
let path = fileURLToPath(resolved);
402402
if (getOptionValue('--experimental-specifier-resolution') === 'node') {
403403
let file = resolveExtensionsWithTryExactName(resolved);
404-
if (file !== undefined) return file;
405-
if (!StringPrototypeEndsWith(path, '/')) {
406-
file = resolveDirectoryEntry(new URL(`${resolved}/`));
407-
if (file !== undefined) return file;
408-
} else {
409-
return resolveDirectoryEntry(resolved) || resolved;
404+
405+
// Directory
406+
if (file === undefined) {
407+
file = StringPrototypeEndsWith(path, '/') ?
408+
(resolveDirectoryEntry(resolved) || resolved) : resolveDirectoryEntry(new URL(`${resolved}/`));
409+
410+
if (file === resolved) return file;
411+
412+
if (file === undefined) {
413+
throw new ERR_MODULE_NOT_FOUND(
414+
resolved.pathname, fileURLToPath(base), 'module');
415+
}
410416
}
411-
throw new ERR_MODULE_NOT_FOUND(
412-
resolved.pathname, fileURLToPath(base), 'module');
417+
418+
path = file;
413419
}
414420

415421
const stats = tryStatSync(StringPrototypeEndsWith(path, '/') ?
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import * as common from '../common/index.mjs';
2+
import path from 'path';
3+
import fs from 'fs/promises';
4+
import tmpdir from '../common/tmpdir.js';
5+
import { spawn } from 'child_process';
6+
import assert from 'assert';
7+
8+
tmpdir.refresh();
9+
const tmpDir = tmpdir.path;
10+
11+
// Create the following file structure:
12+
// β”œβ”€β”€ index.mjs
13+
// β”œβ”€β”€ subfolder
14+
// β”‚ β”œβ”€β”€ index.mjs
15+
// β”‚ └── node_modules
16+
// β”‚ └── package-a
17+
// β”‚ └── index.mjs
18+
// └── symlink.mjs -> ./subfolder/index.mjs
19+
const entry = path.join(tmpDir, 'index.mjs');
20+
const symlink = path.join(tmpDir, 'symlink.mjs');
21+
const real = path.join(tmpDir, 'subfolder', 'index.mjs');
22+
const packageDir = path.join(tmpDir, 'subfolder', 'node_modules', 'package-a');
23+
const packageEntry = path.join(packageDir, 'index.mjs');
24+
try {
25+
await fs.symlink(real, symlink);
26+
} catch (err) {
27+
if (err.code !== 'EPERM') throw err;
28+
common.skip('insufficient privileges for symlinks');
29+
}
30+
await fs.mkdir(packageDir, { recursive: true });
31+
await Promise.all([
32+
fs.writeFile(entry, 'import "./symlink.mjs";'),
33+
fs.writeFile(real, 'export { a } from "package-a/index.mjs"'),
34+
fs.writeFile(packageEntry, 'export const a = 1;'),
35+
]);
36+
37+
spawn(process.execPath, ['--experimental-specifier-resolution=node', entry],
38+
{ stdio: 'inherit' }).on('exit', common.mustCall((code) => {
39+
assert.strictEqual(code, 0);
40+
}));

0 commit comments

Comments
Β (0)