You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// https://github.com/webpack/enhanced-resolve/blob/main/README.mdconstfs=require("fs");const{ CachedInputFileSystem, ResolverFactory }=require("enhanced-resolve");// create a resolverconstmyResolver=ResolverFactory.createResolver({// Typical usage will consume the `fs` + `CachedInputFileSystem`, which wraps Node.js `fs` to add caching.fileSystem: newCachedInputFileSystem(fs,4000),extensions: [".js",".json"]/* any other resolver options here. Options/defaults can be seen below */});// resolve a file with the new resolverconstcontext={};constlookupStartPath="/Users/webpack/some/root/dir";constrequest="./path/to-look-up.js";constresolveContext={};myResolver.resolve(context,lookupStartPath,request,resolveContext,(err/*Error*/,filepath/*string*/)=>{// Do something with the path});
默认导出
对于开发一个 JavaScript 三方库供外部使用而言,
package.json
是其中不可缺少的一部分一般而言,对于库开发者来说,我们会在
package.json
中指定我们的导出入口。一般而言会涉及两个字段main
和export
,它们会涉及到当前模块在被导入的行为。通常我们会将main
字段指向 cjs 产物,module
字段指向 ES 产物main
main
字段指定了该模块的主入口文件,即 require 该模块时加载的文件。该字段的值应为相对于模块根目录的路径或者是一个模块名(如index.js
或lib/mymodule.js
,如果是模块名,则需要保证在该模块根目录下存在该模块)。主入口文件可以是 JavaScript 代码、JSON 数据或者是 Node.js C++扩展module
module
字段是 ES 模块规范下的入口文件,它被用于支持 import 语法。当使用 esm 或 webpack 等工具打包时,会优先采用 module 字段指定的入口文件。如果没有指定 module 字段,则会使用 main 字段指定的入口文件作为默认的 ES 模块入口文件指定导出
一般情况下,我们使用
main
和module
在大部分场景下对于开发一个库来说已经足够。但是如果想实现更精细化的导出控制就无法满足当我们一个库本身同时包含运行时和编译时的导出时,如果我们导出的模块在编译时(node 环境)包含副作用,如果运行时模块也从同一入口导出就会出现问题
当前,可以通过解决掉副作用规避这个问题,但是很可能我们依赖的第三方模块也是有复作用的这个时候就无解了。此时最好的办法是将这个库的运行时和编译时从两个入口进行导出,这样子就不存在某一方影响到另一方。库使用者也不需关心从统一入口导入的方法到底是编译时方法还是运行时方法
这个时候就可以利用
package.json
的exports
字段进行导出,当存在该字段时会忽略main
和module
字段。该字段在 Node.js 12 版本中引入,可用来大幅简化模块的导出方式,支持同时支持多个环境下的导出方式,提供了更好的可读性和可维护性支持以下用法
这样当运行
require('pkg')
时会加载dist/index.js
,而当运行require('pkg/runtime')
时会加载dist/runtime.js
,require('pkg/buildtime')
则会加载dist/buildtime.js
对于条件,目前 node 支持
import
、require
、node
、node-addons
和default
。同时社区对于其它环境也定义了如types
、deno
、browser
等供不同环境使用。具体规范可见支持目录的整体导出
类型
按照上述操作完成后,打包就能符合相关预期,但是对于 typescript 文件的导入如果使用
runtime
路径是会找不到相应的类型文件,typescript 并不会去识别该字段,已有的讨论issues此时需要借助
package.json
的typeVersions
字段进行声明供 ts 识别对于这个例子,我们在库的
package.json
中增加如下,表示各路径分别导出的类型文件路径此时我们就能看见能正确找到相应的类型提示
实现
目前 Node.js 12+和主流的打包工具都已经支持
exports
字段的解析,下面来简单看下 webpack 的实现Webpack
webpack 已经内置支持对于
exports
的解析,它的解析由enhance-resolve
实现createResolver
是enhance-resolve
导出的create
函数,用法如下通过创建一个自定义 resolver 函数后可调用
resolve
函数根据当前的模块路径和一些配置查找一个模块的绝对路径相关自定义 resolver 选项含义
extensions
查找的文件扩展名conditionNames
对应package.json
中的exports
条件exportsFields
指定从 package.json 哪个字段读取exports
条件fullySpecified
为 true 时,解析器会优先尝试使用完全指定的路径来解析模块请求,而忽略其他任何条件。如果找到了对应的模块文件,则直接返回该路径;否则抛出错误通过相关上述代码我们可以知道
es
导入,webpack 会尝试读取exports
字段的导出,依次读取import
和node
字段。并且这里也是直接配置了fullySpecified
。即处理相对路径的导入如import foo from './foo';
时,Webpack 在解析模块请求时会直接将 ./foo.js 当作完整路径来处理,而不进行路径的拼接和解析cjs
导入,webpack 会尝试读取exports
字段的导出,依次读取require
和node
字段。并且会尝试使用各种解析策略来解析该路径由于
enhance-resolve
是一个完全独立于 webpack 的模块,当我们自己实现一个三方打包器或者插件时,如果想实现类似的模块解析能力,也可以完全独立使用enhance-resolve
来实现总结
为了实现一个库更友好的导出,我们可以借助 package.json 的
exports
字段指定多条件的导出方式,主流打包工具以及 Node.js 都已经支持;对于 ts 类型,我们可以结合typeVersions
进行配置The text was updated successfully, but these errors were encountered: