Node.js中 --preserve-symlinks 参数的实现方式
之前的文章写了--preserve-symlinks
可以解决软链文件包寻找的问题,那么它是如何实现的?
包解析简易流程
这里我们只看 commonjs 模块的加载。
当 require 一个模块时,nodejs会:
- 计算被引文件的完整路径。
- new 一个 Module 类,再调用 Module.load 方法执行模块代码返回输出结果。
这里需要注意的是,在计算被引文件的完整路径时,如果被引文件是一个相对路径,那么会以当前文件的路径进行查找。例如 a.js 使用相对路径引用 b.js,那么 b.js 文件的解析是按照 a.js 文件所在的目录来查找的。
-preserve-symlinks
lib/internal/modules/cjs/loader.js
与 --preserve-symlinks
有关的代码,关键的其实就只有这两行:
1 | if (preserveSymlinks && !isMain) { |
这部分代码处在“计算被引文件的完整路径”这一过程中。执行到这里时,requestPath 已经是一个完整的路径了。
判断 preserveSymlinks 为 true 时,会将原路径返回。
判断 preserveSymlinks 为 false 时,会调用 toRealPath 方法,返回软链原始文件的路径。
toRealPath 方法的实现非常简单,就是调用了 fs.realpathSync
:
1 | function toRealPath(requestPath) { |
举个栗子
假设我们有这样一个文件结构:
- folderA
- a.js
- folderB
- b.js -> a.js
- c.js
- index.js
folderB/b.js 是 folderA/a.js 的软链。
a.js 中这样引用了 c.js: require('./c.js');
。
index.js 中引用了 b.js: require('./folderB/b.js');
。
下面说一下当我们执行 node index.js 时会发生啥。
当我们执行 index.js 时,会去引用 b.js,此时
- 模块 id 为
./folderB/b.js
。 - 路径即为 index.js 的路径,我们记为
/
。
拼接后得到 requestPath:/folderB/b.js
。
之后会进入到我们上面贴的代码处,
- 如果 preserveSymlinks 为 true,那么会直接返回
/folderB/b.js
作为模块的 filename。 - 如果 preserveSymlinks 为 false,那么会调用 toRealPath 方法,返回
/folderA/a.js
,作为模块的 filename。
接下来,就要引用 c.js 了,模块 id 为 ./c.js
,而 path 就不一样了:
- 如果 preserveSymlinks 为 true,那么 path 是 b.js 所在的目录
/folderB
。 - 如果 preserveSymlinks 为 false,那么 path 是 a.js 所在的目录
/folderA
。
拼接之后得到 /folderB/c.js
和 /folderA/a.js
。显然 preserveSymlinks 为 false 的时候就会出现找不到包的情况。