我正在开发一个 JavaScript 检测引擎,我会遍历源文件的 AST 并递归地将导入的模块排队以进行检测。为此,我需要将模块名称解析为其源文件。
对于 CommonJS 模块,这非常简单:
/**
* Resolves the given module to a path.
* @param currentFilePath The file that is currently instrumented. Serves as base path for the resolver.
* @param module The module to resolve to its file path.
*/
function resolveCommonJsModule(currentFilePath, module)
{
let requireFunc = createRequire(currentFilePath);
return requireFunc.resolve(module);
}
但是,这对于通过
import
加载的ES模块不起作用。如果库在其 package.json 中定义了 ES 和 CommonJS 模块的单独导出,则此函数将始终返回 CommonJS 版本。例如:
...
"exports": {
".": {
"types": "./index.d.ts",
"import": "./esm/index.js", <-- ES module
"default": "./index.js" <-- CommonJS module, which is returned by function above
},
},
...
import.meta.resolve()
,但这不允许我设置完成解析的“基本路径”。相反,它与检测脚本相关,后者位于完全不同的目录中。
用于解析给定基本路径的模块名称的 ES 模块等效项是什么?
import.meta.resolve(specifier, parent)
,这将允许解析相对于给定parent
模块的导入,类似于require.resolve
。
但是,它需要
--experimental-import-meta-resolve
标志,并且似乎不适用于所有模块:解析 ./file.mjs
成功,但解析 some-package
会导致 ERR_MODULE_NOT_FOUND
。
幸运的是,我找到了一个可以解决该问题的 ponyfill,import-meta-resolve:
import * as importMetaResolve from "import-meta-resolve";
let modulePath = importMetaResolve.resolve(module, pathToFileURL(currentFilePath));
此命令生成所有 CommonJS 和 ES 模块的正确路径。