bunchee
零配置的 JS/TS 包构建工具。
bunchee 是一个零配置的构建工具,可以轻松地打包 JS/TS 库。它基于 Rollup 和 SWC ⚡️ 构建,让你专注于编写代码,同时生成多个捆绑包(CommonJS 或 ESModule)。它使用 package.json
中的标准导出配置作为唯一的真实来源,并使用入口文件约定来匹配你的导出并将它们构建成捆绑包。
快速开始
安装
npm install --save-dev bunchee
如果你使用 TypeScript
npm install --save-dev bunchee typescript
配置
创建你的库入口文件和 package.json。
cd ./my-lib
mkdir src && touch ./src/index.ts
准备
# 使用 bunchee 准备 package.json 配置
npm exec bunchee --prepare
# "如果你使用其他包管理器如 pnpm"
# pnpm bunchee --prepare
# "或者使用 npx"
# npx bunchee@latest --prepare
或者你可以查看以下案例来配置你的 package.json。
JavaScript
然后使用 package.json 中的 exports 字段 来配置不同的条件,并利用与其他打包工具(如 webpack)相同的功能。exports 字段允许你定义多个条件。
{
"files": ["dist"],
"exports": {
"import": "./dist/es/index.mjs",
"require": "./dist/cjs/index.js"
},
"scripts": {
"build": "bunchee"
}
}
TypeScript
如果你正在构建一个 TypeScript 库,将类型与主入口文件分开,并在 package.json 中指定类型路径。当你在 TypeScript 和现代模块解析(高于 node16)中使用 .mjs
或 .cjs
扩展名时,TypeScript 将需要特定的类型声明文件,如 .d.mts
或 .d.cts
来匹配扩展名。bunchee
可以自动生成它们以匹配条件和扩展名的类型。一个例子是在 package.json 中这样配置你的导出:
{
"files": ["dist"],
"exports": {
"import": {
"types": "./dist/es/index.d.mts",
"default": "./dist/es/index.mjs"
},
"require": {
"types": "./dist/cjs/index.d.ts",
"default": "./dist/cjs/index.js"
}
},
"scripts": {
"build": "bunchee"
}
}
混合(CJS & ESM)模块解析与 TypeScript
如果你使用 TypeScript 和 Node 10 以及 Node 16 模块解析,你可以在 package.json 中使用 `types` 字段来指定类型路径。然后 `bunchee` 将生成与主入口文件相同扩展名的类型文件。{
"files": ["dist"],
"main": "./dist/cjs/index.js",
"module": "./dist/es/index.mjs",
"types": "./dist/cjs/index.d.ts",
"exports": {
"import": {
"types": "./dist/es/index.d.ts",
"default": "./dist/es/index.js"
},
"require": {
"types": "./dist/cjs/index.d.cts",
"default": "./dist/cjs/index.cjs"
}
},
"scripts": {
"build": "bunchee"
}
}
构建
然后 src
文件夹中的文件将被视为入口文件,并与 package.json 中的导出名称相匹配。例如:
src/index.ts
将匹配导出名称 "."
或唯一的主导出。
现在只需运行 npm run build
(或者如果你使用这些包管理器,则运行 pnpm build
/ yarn build
),bunchee
将找到入口文件并构建它们。
输出格式将基于导出条件和文件扩展名。举个例子:
- 根据导出条件,
require
是 CommonJS,import
是 ESM。 - 不考虑导出条件,
.js
是 CommonJS,.mjs
是 ESM。然后对于像 "node" 这样的导出条件,你可以用你的扩展名选择格式。
[!NOTE] 所有的
dependencies
和peerDependencies
将自动标记为外部依赖,不会包含在捆绑包中。如果你想将它们包含在捆绑包中,可以使用--no-external
选项。
使用
文件约定
虽然 exports
字段正在成为 node.js 中导出的标准,bunchee 也支持在一个命令中构建多个导出。
提供与 package.json 中 exports 字段导出名称匹配的入口文件名([name].[ext]
)。例如:
<cwd>/src/index.ts
将匹配"."
导出名称,或者如果只有一个主导出。<cwd>/src/lite.ts
将匹配"./lite"
导出名称。
构建脚本可以只是 bunchee
,无需为每个导出配置任何输入源。当然,你仍然可以根据需要指定其他参数。
简而言之,src/
文件夹中的入口文件将与 package.json
中的 exports
条件进行匹配,并将它们构建成捆绑包。
假设你有默认导出包 "."
和子路径导出 "./lite"
,在 package.json 中列出了不同的导出条件
{
"name": "example",
"scripts": {
"build": "bunchee"
},
"exports": {
"./lite": "./dist/lite.js",
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
}
}
然后你需要在项目根目录添加两个入口文件 index.ts
和 lite.ts
来匹配导出名称 "."
和 "./lite"
,bunchee 将这些入口文件与导出名称关联,然后将它们用作输入源和输出路径信息。
- my-lib/
|- src/
|- lite.ts
|- index.ts
|- package.json
它还会在具有导出路径名称的目录下查找 index.<ext>
文件。例如,如果你在导出字段中有 "./lite": "./dist/lite.js"
,那么它也会查找 ./lite/index.js
作为入口文件。
多运行时
对于像 react-native
、react-server
和 edge-light
这样的特殊平台的导出条件,它们可能有不同的导出或不同的代码条件。在这种情况下,如果你想将它们构建为不同的代码捆绑包,bunchee 提供了一个覆盖输入源文件约定。
例如:
{
"exports": {
"react-server": "./dist/react-server.mjs",
"edge-light": "./dist/edge-light.mjs",
"import": "./dist/index.mjs"
}
}
可执行文件
要使用 package.json 中的 bin
字段构建可执行文件,bunchee
要求你在 src
目录下创建 bin
目录。源文件匹配将与入口文件约定相同。
例如:
|- src/
|- bin/
|- index.ts
这将匹配 package.json 中的 bin
字段为:
{
"bin": "./dist/bin.js"
}
对于多个可执行文件,你可以在 bin
目录下创建多个文件。
|- src/
|- bin/
|- foo.ts
|- bar.ts
这将匹配 package.json 中的 bin
字段为:
{
"bin": {
"foo": "./dist/bin/a.js",
"bar": "./dist/bin/b.js"
}
}
注意:对于多个
bin
文件,文件名应与bin
字段中的键名匹配。
服务器组件
bunchee
支持构建服务器组件和服务器操作,使用库指令如 "use client"
或 "use server"
。它将为客户端和服务器生成相应的块,正确划分客户端和服务器边界。
然后当库集成到应用程序(如 Next.js)中时,应用程序打包工具可以正确转换客户端组件和服务器操作,最大化利用优势。
如果你在入口文件中使用 "use client"
或 "use server"
,那么它将被保留在顶部,该入口的 dist 文件将成为客户端组件。
如果你在作为入口依赖项的文件中使用 "use client"
或 "use server"
,那么包含指令的文件将被拆分成单独的块,并将指令提升到该块的顶部。
共享模块(实验性)
总有一些情况,你需要在捆绑包之间共享代码,但它们不必是单独的入口或导出。你希望将它们捆绑到一个共享块中,然后在不同的捆绑包中使用它们。你可以使用共享模块约定 [name].[layer]-runtime.[ext]
来创建共享模块捆绑包。
共享工具示例
// src/util.shared-runtime.js
export function sharedUtil() {
/* ... */
}
然后你可以在不同的入口文件中使用它们:
// src/index.js
import { sharedUtil } from './util.shared-runtime'
// src/lite.js
import { sharedUtil } from './util.shared-runtime'
bunchee
将把共享模块捆绑到一个单独的层中,该层与文件名约定匹配,在上面的例子中是 "shared",并且该捆绑包将被不同的入口捆绑包引用。
对于多个运行时捆绑包,比如同时有 default
和 react-server
。它们可能有需要共享并在不同运行时捆绑包之间保持只有一个实例的模块。你可以使用共享模块约定为不同的运行时捆绑包创建共享模块捆绑包。
共享运行时模块示例
'use client'
// src/app-context.shared-runtime.js
export const AppContext = React.createContext(null)
然后你可以在不同的入口文件中使用它们:
// src/index.js
import { AppContext } from './app-context.shared-runtime'
// src/index.react-server.js
import { AppContext } from './app-context.shared-runtime'
app-context.shared-runtime
将被捆绑到一个单独的块中,只有一个实例,并在不同的运行时捆绑包之间共享。
CLI
CLI 选项
bunchee
CLI 提供了几个选项来创建不同的捆绑包或生成类型。
- 输出(
-o <文件>
):指定输出文件名。 - 格式(
-f <格式>
):设置输出格式(默认:'esm'
)。 - 外部依赖(
--external <依赖,>
):指定额外的外部依赖,默认为package.json
中的dependencies
和peerDependencies
列表。值以逗号分隔。 - 目标(
--target <目标>
):设置ECMAScript目标(默认:'es2015'
)。 - 运行时(
--runtime <运行时>
):设置构建运行时(默认:'browser'
)。 - 环境(
--env <环境变量,>
):定义环境变量。(默认:NODE_ENV
,以逗号分隔) - 工作目录(
--cwd <工作目录>
):设置包含package.json
的当前工作目录。 - 压缩(
-m
):压缩输出。 - 监视(
-w
):监视源文件变化。 - 不清理(
--no-clean
):构建前不清理dist文件夹。(默认:false
) - TypeScript配置(
--tsconfig <路径>
):指定TypeScript配置文件的路径。(默认:tsconfig.json
)
cd <项目根目录>
# 指定输入、输出和格式
bunchee ./src/index.js -f cjs -o ./dist/bundle.js
bunchee ./src/index.js -f esm -o ./dist/bundle.esm.js
# 构建Node.js库,或将目标更改为es2019
bunchee ./src/index.js --runtime node --target es2019
指定额外的外部依赖
默认情况下,bunchee
会将所有dependencies
和peerDependencies
标记为外部依赖,因此您无需将它们作为CLI参数传递。
但如果有任何使用但不在依赖列表中的依赖,并且您想将其标记为外部依赖,可以使用--external
选项来指定它们。
bunchee --external=dep1,dep2,dep3
将dep1
、dep2
和dep3
替换为您想要从捆绑包中排除的依赖项名称。
捆绑所有内容而不包含外部依赖
要捆绑您的库而不包含外部依赖,请使用--no-external
选项:
bunchee --no-external
这将在输出捆绑包中包含所有依赖项。
环境变量
要将环境变量传递给捆绑的代码,请使用--env选项,后跟以逗号分隔的环境变量名称列表:
bunchee --env=ENV1,ENV2,ENV3
将ENV1
、ENV2
和ENV3
替换为您想要包含在捆绑代码中的环境变量名称。这些环境变量将在捆绑过程中被内联。
您可以使用index.<导出类型>.<扩展名>
来覆盖特定导出名称的输入源文件。或者使用<导出路径>/index.<导出类型>.<扩展名>
也可以。例如:
|- src/
|- index/.ts
|- index.react-server.ts
|- index.edge-light.ts
这将匹配导出名称"react-server"
和"edge-light"
,然后使用相应的输入源文件来构建捆绑包。
自动开发和生产模式
如果存在process.env.NODE_ENV
,默认会注入,您无需手动注入。如果需要分离开发构建和生产构建,bunchee
为开发和生产模式提供了不同的导出条件,分别为development
和production
导出条件。
{
"exports": {
"development": "./dist/index.development.js",
"production": "./dist/index.production.js"
}
}
然后您可以使用bunchee
自动构建开发捆绑包和生产捆绑包。
CSS
bunchee
对纯CSS文件导入提供基本支持。它将被捆绑到js捆绑包中,并在浏览器加载捆绑包时将样式标签插入文档头部。
/* src/style.css */
.foo {
color: orange;
}
// src/index.tsx
import './style.css'
export const Foo = () => <div className="foo">foo</div>
文本文件
如果您只想将文件作为字符串内容导入,可以将扩展名命名为.txt
或.data
,它将被捆绑为字符串内容。
例如:
src/index.ts
import data from './data.txt'
export default data
src/data.txt
hello world
输出
export default "hello world"
Node.js API
import path from 'path'
import { bundle, type BundleConfig } from 'bunchee'
// 这些选项的定义可以在帮助信息中找到
await bundle(path.resolve('./src/index.ts'), {
dts: false, // 布尔值
watch: false, // 布尔值
minify: false, // 布尔值
sourcemap: false, // 布尔值
external: [], // 字符串数组
format: 'esm', // 'esm' | 'cjs'
target: 'es2015', // ES语法目标
runtime: 'nodejs', // 'browser' | 'nodejs'
cwd: process.cwd(), // 字符串
clean: true, // 布尔值
tsconfig: 'tsconfig.json', // 字符串
})
监视模式
Bunchee提供了一个方便的监视模式,可以在源文件发生更改时重新构建您的库。要启用此功能,请使用-w
或--watch
。
target
如果您在tsconfig.json
中指定了target
选项,则无需再通过CLI传递它。
包检查
bunchee
支持检查包捆绑是否与包导出配置匹配。
许可证
MIT