--preserve-symlinks-main not respected when using --require and --inspect-brk together
まだ誰も着手していません。
- 主要言語
- JavaScript
- スター
- 122k
- フォーク
- 37.3k
- 平均マージ
- 4日 2時間
- マージ済み PR(30日)
- 283
説明
Version
16.11.0
Platform
Darwin NAMTJ0390J2KQ 21.6.0 Darwin Kernel Version 21.6.0: Mon Aug 22 20:19:52 PDT 2022; root:xnu-8020.140.49~2/RELEASE_ARM64_T6000 arm64
Subsystem
internal/modules/cjs
What steps will reproduce the bug?
No response
How often does it reproduce? Is there a required condition?
No response
What is the expected behavior?
That symlinks are preserved for main entrypoint file path resolution when --require, --preserve-symlinks-main, and --inspect-brk flags are all used.
What do you see instead?
When using a symlinked entry point, and calling node with all of --require, --preserve-symlinks-main, and --inspect-brk, the symlink for the entry point is not preserved, and the realpath of the entrypoint is used for module loading (and future module loads relative to that entry point).
Additional information
When using node --require preload.js --inspect-brk thing.js, Module.prototype._compile is entered for preload.js before the main entrypoint is loaded. Note the following code block:
https://github.com/nodejs/node/blob/main/lib/internal/modules/cjs/loader.js#L1126
if (getOptionValue('--inspect-brk') && process._eval == null) {
if (!resolvedArgv) {
// We enter the repl if we're not given a filename argument.
if (process.argv[1]) {
try {
resolvedArgv = Module._resolveFilename(process.argv[1], null, false);
} catch {
// We only expect this codepath to be reached in the case of a
// preloaded module (it will fail earlier with the main entry)
assert(ArrayIsArray(getOptionValue('--require')));
}
} else {
resolvedArgv = 'repl';
}
}
Note especially that resolvedArgv = Module._resolveFilename(process.argv[1], null, false); will resolve the main entrypoint filename with the isMain arg set to false. This will cause --preserve-symlinks-main not to be applied for the filename resolution, and the realpath rather than the superficial (not following the links) path to be used.
Later, when the main entrypoint is loaded "for real", Module._resolveFilename is called with isMain = true (correctly). However, when this enters Module._findPath, the filename from the previous resolution, with isMain = false, is pulled from the cache on Module._pathCache, because the cache key does not incorporate the isMain arg value - only path info -, and this value is returned from Module._resolveFilename:
const cacheKey = request + '\x00' + ArrayPrototypeJoin(paths, '\x00');
const entry = Module._pathCache[cacheKey];
if (entry)
return entry;
So, even though a different file path would potentially emerge as a result of the logic below, the value from the cache is used:
if (!isMain) {
if (preserveSymlinks) {
filename = path.resolve(basePath);
} else {
filename = toRealPath(basePath);
}
} else if (preserveSymlinksMain) {
// For the main module, we use the preserveSymlinksMain flag instead
// mainly for backward compatibility, as the preserveSymlinks flag
// historically has not applied to the main module. Most likely this
// was intended to keep .bin/ binaries working, as following those
// symlinks is usually required for the imports in the corresponding
// files to resolve; that said, in some use cases following symlinks
// causes bigger problems which is why the preserveSymlinksMain option
// is needed.
filename = path.resolve(basePath);
} else {
filename = toRealPath(basePath);
}
Potential actions:
- Add
isMainto the cache key used forModule._pathCacheinModule._findPath. I think this is important, as other code in node that usesModule._findPathmay create race conditions, because the cache key is insufficiently inclusive, making the function, in a sense, impure. - Pass
isMain = trueto theModule._resolveFilenamecall in the --inspect-brk logic inModule.prototype._compile. Isn't it guaranteed that we are dealing with the main module here, anyway?
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
調査の方向性
lib/internal/modules/cjs/loader.js から調査を始め、Module.prototype._compile、Module._resolveFilename、および Module._findPath の Module._pathCache キーにおける --inspect-brk の経路に注目します。シンボリックリンクされたエントリポイントと --require、--preserve-symlinks-main、--inspect-brk を使って再現し、メインのエントリポイントが実際のパスに解決されるのではなく、シンボリックリンクのパスを保持することを確認します。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- javascript, nodejs
- 領域
- backend
- issue の種類
- バグ
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 活発さ
- 静か
- 明瞭さ
- おおむね明確
- 初心者へのやさしさ
- 48/100