处理三方依赖
本节介绍如何在 bundle 模式下处理三方依赖。
通常,项目所需的三方依赖可以通过包管理器的 install 命令安装。安装成功后,它们通常会出现在项目的 package.json 文件中的 dependencies 和 devDependencies 字段下。
package.json
{
"dependencies": {},
"devDependencies": {}
}
dependencies 字段下的依赖通常来说是这个包运行所需的依赖,如果这些三方依赖声明在 devDependencies 字段下,那么在生产运行时会出现缺失依赖。
除了 dependencies 字段,peerDependencies 也可以声明在生产环境中需要的依赖,但它更强调这些依赖在项目运行时的存在,类似于插件机制。
三方依赖的默认处理
默认情况下,当生成 CJS 或 ESM 产物时,dependencies、optionalDependencies 和 peerDependencies 字段下的三方依赖不会被 Rslib 打包。
这是因为在 npm 包安装时,其 dependencies 也会被安装。通过不打包 dependencies,可以减少包的体积。
如果需要打包某些依赖,建议将它们从 dependencies 移动到 devDependencies,这相当于预打包依赖,并减少依赖安装的体积。
以下是一个简单示例,假设项目依赖 foo:
package.json
{
"dependencies": {
"foo": "^1.0.0"
},
// 或
"peerDependencies": {
"foo": "^1.0.0"
}
}
当在源代码中使用 foo 依赖时:
src/index.ts
import foo from 'foo';
console.info(foo);
此时产物中不会包含 foo 的代码:
dist/index.js
import foo from 'foo';
console.info(foo);
自定义三方依赖处理
Rslib 主要通过 output.autoExternal 和 output.externals 控制三方依赖是否被打包。
配置 autoExternal
如果希望覆盖 output.autoExternal 的上述默认行为,使这些依赖被打包进产物,可以将其设置为 false:
rslib.config.ts
export default defineConfig({
lib: [
{
output: {
autoExternal: false,
},
},
],
});
如果只想调整某几类依赖,可以使用对象形式:
rslib.config.ts
export default defineConfig({
lib: [
{
output: {
autoExternal: {
dependencies: true,
optionalDependencies: true,
peerDependencies: true,
devDependencies: false,
},
},
},
],
});
使用 exclude 可以从 output.autoExternal 自动生成的 external 规则中排除指定包。如果某个包被排除,它的子路径导入也不会被 external:
rslib.config.ts
export default defineConfig({
lib: [
{
output: {
autoExternal: {
exclude: ['react', /^@scope\//],
},
},
},
],
});
配置 externals
当你需要指定某些模块不被 Rslib 打包,或需要改变 external 后的请求路径时,可以使用 output.externals。
数组形式适合保留原始请求路径:
rslib.config.ts
export default defineConfig({
lib: [
{
output: {
externals: ['react', 'react/jsx-runtime'],
},
},
],
});
对象形式可以指定 external 后的请求路径,常用于给 external 的模块改名:
rslib.config.ts
export default defineConfig({
lib: [
{
output: {
externals: {
react: 'react-18',
'react/jsx-runtime': 'react-18/jsx-runtime',
},
},
},
],
});
Tip
react/jsx-runtime 这类子路径需要单独处理。只配置 react 不代表 react/jsx-runtime 也会使用相同的 external 规则。
如果要匹配一组模块,可以使用正则:
rslib.config.ts
export default defineConfig({
lib: [
{
output: {
externals: [/^react($|\/)/],
},
},
],
});
在一些复杂场景中,如果需要根据请求来源、上下文等信息决定是否 external,可以通过 tools.rspack 配置 Rspack 的 externals:
rslib.config.ts
export default defineConfig({
lib: [
{
tools: {
rspack: {
externals: [
({ request }, callback) => {
if (request?.startsWith('react')) {
callback(null, request);
return;
}
callback();
},
],
},
},
},
],
});
更多用法可参考 Rspack 的 Externals 文档。
打包通过 createRequire() 加载的依赖
Node.js 的 ES 模块环境不提供 CommonJS 的 require 函数。若需要在 ES 模块中使用 CommonJS 的加载语义,可以通过 Node.js 的 createRequire() 创建一个 require 函数:
src/index.ts
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
const foo = require('foo');
export const bar = foo.bar;
Rslib 默认会在产物中保留 createRequire() 调用。如果希望 Rspack 分析由它创建的 require() 调用,并打包由 require('foo') 这类可静态分析的调用所加载的依赖,可以启用 module.parser.javascript.createRequire。
rslib.config.ts
import { defineConfig } from '@rslib/core';
export default defineConfig({
tools: {
rspack: {
module: {
parser: {
javascript: {
createRequire: true,
},
},
},
},
},
});
如果被打包的依赖中存在需要保留到运行时的 createRequire() 调用,可以改用 module.rules,仅对 node_modules 之外的 JavaScript/TypeScript 模块启用该能力:
rslib.config.ts
import { defineConfig } from '@rslib/core';
export default defineConfig({
tools: {
rspack: {
module: {
rules: [
{
test: /\.[cm]?[jt]sx?$/,
exclude: /node_modules/,
parser: {
createRequire: true,
},
},
],
},
},
},
});