From 163d4a3e2409684f3b43efc8d0353f59c0425dc7 Mon Sep 17 00:00:00 2001 From: meixg Date: Thu, 3 Mar 2022 15:12:30 +0800 Subject: [PATCH 1/2] loader: fix esm resolve for symlink file Fix: https://github.com/nodejs/node/issues/42195 --- lib/internal/modules/esm/resolve.js | 24 ++++++----- .../es-module/test-esm-specifiers-symlink.mjs | 40 +++++++++++++++++++ 2 files changed, 55 insertions(+), 9 deletions(-) create mode 100644 test/es-module/test-esm-specifiers-symlink.mjs diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js index 25ec44215d5689..9d002a9a5c2e88 100644 --- a/lib/internal/modules/esm/resolve.js +++ b/lib/internal/modules/esm/resolve.js @@ -374,18 +374,24 @@ function finalizeResolution(resolved, base, preserveSymlinks) { resolved.pathname, 'must not include encoded "/" or "\\" characters', fileURLToPath(base)); - const path = fileURLToPath(resolved); + let path = fileURLToPath(resolved); if (getOptionValue('--experimental-specifier-resolution') === 'node') { let file = resolveExtensionsWithTryExactName(resolved); - if (file !== undefined) return file; - if (!StringPrototypeEndsWith(path, '/')) { - file = resolveDirectoryEntry(new URL(`${resolved}/`)); - if (file !== undefined) return file; - } else { - return resolveDirectoryEntry(resolved) || resolved; + + // Directory + if (file === undefined) { + file = StringPrototypeEndsWith(path, '/') ? + (resolveDirectoryEntry(resolved) || resolved) : resolveDirectoryEntry(new URL(`${resolved}/`)); + + if (file === resolved) return file; + + if (file === undefined) { + throw new ERR_MODULE_NOT_FOUND( + resolved.pathname, fileURLToPath(base), 'module'); + } } - throw new ERR_MODULE_NOT_FOUND( - resolved.pathname, fileURLToPath(base), 'module'); + + path = file; } const stats = tryStatSync(StringPrototypeEndsWith(path, '/') ? diff --git a/test/es-module/test-esm-specifiers-symlink.mjs b/test/es-module/test-esm-specifiers-symlink.mjs new file mode 100644 index 00000000000000..0f586197e6bdbe --- /dev/null +++ b/test/es-module/test-esm-specifiers-symlink.mjs @@ -0,0 +1,40 @@ +import * as common from '../common/index.mjs'; +import path from 'path'; +import fs from 'fs/promises'; +import tmpdir from '../common/tmpdir.js'; +import { spawn } from 'child_process'; +import assert from 'assert'; + +tmpdir.refresh(); +const tmpDir = tmpdir.path; + +// Create the following file structure: +// ├── index.mjs +// ├── subfolder +// │ ├── index.mjs +// │ └── node_modules +// │ └── package-a +// │ └── index.mjs +// └── symlink.mjs -> ./subfolder/index.mjs +const entry = path.join(tmpDir, 'index.mjs'); +const symlink = path.join(tmpDir, 'symlink.mjs'); +const real = path.join(tmpDir, 'subfolder', 'index.mjs'); +const packageDir = path.join(tmpDir, 'subfolder', 'node_modules', 'package-a'); +const packageEntry = path.join(packageDir, 'index.mjs'); +try { + await fs.symlink(real, symlink); +} catch (err) { + if (err.code !== 'EPERM') throw err; + common.skip('insufficient privileges for symlinks'); +} +await fs.mkdir(packageDir, { recursive: true }); +Promise.all([ + fs.writeFile(entry, 'import "./symlink.mjs";'), + fs.writeFile(real, 'export { a } from "package-a/index.mjs"'), + fs.writeFile(packageEntry, 'export const a = 1;'), +]); + +spawn(process.execPath, ['--experimental-specifier-resolution=node', entry], + { stdio: 'inherit' }).on('exit', common.mustCall((code) => { + assert.strictEqual(code, 0); +})); From 3cbb1bb1ccb359cae9d5c0a096252266237607ba Mon Sep 17 00:00:00 2001 From: meixg Date: Sat, 5 Mar 2022 01:56:26 +0800 Subject: [PATCH 2/2] loader: fix esm resolve for symlink file --- test/es-module/test-esm-specifiers-symlink.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/es-module/test-esm-specifiers-symlink.mjs b/test/es-module/test-esm-specifiers-symlink.mjs index 0f586197e6bdbe..9c60c1da89706f 100644 --- a/test/es-module/test-esm-specifiers-symlink.mjs +++ b/test/es-module/test-esm-specifiers-symlink.mjs @@ -28,7 +28,7 @@ try { common.skip('insufficient privileges for symlinks'); } await fs.mkdir(packageDir, { recursive: true }); -Promise.all([ +await Promise.all([ fs.writeFile(entry, 'import "./symlink.mjs";'), fs.writeFile(real, 'export { a } from "package-a/index.mjs"'), fs.writeFile(packageEntry, 'export const a = 1;'),