Bun

对比 esbuild

Bun 的打包器 API 主要受 esbuild 启发。从 esbuild 迁移到 Bun 的打包器应该相对轻松。本指南将简要解释为什么你可能考虑迁移到 Bun 的打包器,并为已经熟悉 esbuild API 的人提供一个并排的 API 比较参考。

需要注意一些行为差异。

  • 默认情况下进行打包。与 esbuild 不同,Bun 始终默认进行打包。这就是为什么 Bun 示例中不需要 --bundle 标志。要单独转换每个文件,请使用 Bun.Transpiler
  • 它只是一个打包器。与 esbuild 不同,Bun 的打包器不包含内置开发服务器或文件监视器。它只是一个打包器。打包器旨在与 Bun.serve 和其他运行时 API 结合使用,以实现相同的效果。因此,所有与 HTTP/文件监视相关的选项均不适用。

性能

Bun 的捆绑器结合了注重性能的 API 和经过广泛优化的基于 Zig 的 JS/TS 解析器,在 esbuild 的 three.js 基准测试 中比 esbuild 快 1.75 倍。

从头开始捆绑 10 份 three.js,包括源映射和缩小

CLI API

Bun 和 esbuild 都提供命令行界面。

esbuild <entrypoint> --outdir=out --bundle
bun build <entrypoint> --outdir=out

在 Bun 的 CLI 中,--minify 等简单的布尔标志不接受参数。其他标志(如 --outdir <path>)接受参数;这些标志可以写成 --outdir out--outdir=out。一些标志(如 --define)可以指定多次:--define foo=bar --define bar=baz

esbuildbun build
--bundlen/aBun 始终捆绑,使用 --no-bundle 禁用此行为。

--define:K=V

--define K=V

语法差异很小;没有冒号。

esbuild --define:foo=bar
bun build --define foo=bar

--external:<pkg>

--external <pkg>

语法差异很小;没有冒号。

esbuild --external:react
bun build --external react
--format--formatBun 目前仅支持 "esm",但计划支持其他模块格式。esbuild 默认使用 "iife"

--loader:.ext=loader

--loader .ext:loader

Bun 支持与 esbuild 不同的内置加载器集;请参阅 捆绑器 > 加载器 以获取完整参考。esbuild 加载器 dataurlbinarybase64copyempty 尚未实现。

--loader 的语法略有不同。

esbuild app.ts --bundle --loader:.svg=text
bun build app.ts --loader .svg:text
--minify--minify无差异
--outdir--outdir无差异
--outfile--outfile
--packagesn/a不受支持
--platform--target为与 tsconfig 保持一致而重命名为 --target。不支持 neutral
--serven/a不适用
--sourcemap--sourcemap无差异
--splitting--splitting无差异
--targetn/a不受支持。Bun 的捆绑器目前不执行任何语法降级。
--watch--watch无差异
--allow-overwriten/a永远不允许覆盖
--analyzen/a不受支持
--asset-names--asset-naming为与 JS API 中的 naming 保持一致而重命名
--bannern/a不受支持
--certfilen/a不适用
--charset=utf8n/a不受支持
--chunk-names--chunk-naming为与 JS API 中的 naming 保持一致而重命名
--colorn/a始终启用
--dropn/a不受支持
--entry-names--entry-naming为与 JS API 中的 naming 保持一致而重命名
--footern/a不受支持
--global-namen/a不适用,Bun 目前不支持 iife 输出
--ignore-annotationsn/a不受支持
--injectn/a不受支持
--jsx--jsx-runtime <runtime>支持 "automatic"(使用 jsx 转换)和 "classic"(使用 React.createElement
--jsx-devn/aBun 从 tsconfig.json 中读取 compilerOptions.jsx 以确定默认值。如果 compilerOptions.jsx"react-jsx",或者如果 NODE_ENV=production,Bun 将使用 jsx 转换。否则,它将使用 jsxDEV。对于任何 Bun,它都使用 jsxDEV。bundler 不支持 preserve
--jsx-factory--jsx-factory
--jsx-fragment--jsx-fragment
--jsx-import-source--jsx-import-source
--jsx-side-effectsn/aJSX 始终被假定为无副作用的
--keep-namesn/a不受支持
--keyfilen/a不适用
--legal-commentsn/a不受支持
--log-leveln/a不支持。这可以在 bunfig.toml 中设置为 logLevel
--log-limitn/a不受支持
--log-override:X=Yn/a不受支持
--main-fieldsn/a不受支持
--mangle-cachen/a不受支持
--mangle-propsn/a不受支持
--mangle-quotedn/a不受支持
--metafilen/a不受支持
--minify-whitespace--minify-whitespace
--minify-identifiers--minify-identifiers
--minify-syntax--minify-syntax
--out-extensionn/a不受支持
--outbase--root
--preserve-symlinksn/a不受支持
--public-path--public-path
--puren/a不受支持
--reserve-propsn/a不受支持
--resolve-extensionsn/a不受支持
--servedirn/a不适用
--source-rootn/a不受支持
--sourcefilen/a不支持。Bun 还不支持 stdin 输入。
--sourcemap--sourcemap无差异
--sources-contentn/a不受支持
--supportedn/a不受支持
--tree-shakingn/a始终为 true
--tsconfig--tsconfig-override
--versionn/a运行 bun --version 以查看 Bun 的版本。

JavaScript API

esbuild.build()Bun.build()
absWorkingDirn/a始终设置为 process.cwd()
aliasn/a不受支持
allowOverwriten/a始终为 false

assetNames

naming.asset

使用与 esbuild 相同的模板语法,但必须显式包含 [ext]

Bun.build({
  entrypoints: ["./index.tsx"],
  naming: {
    asset: "[name].[ext]",
  },
});
bannern/a不受支持
bundlen/a始终为 true。使用 Bun.Transpiler 在不捆绑的情况下转换。
charsetn/a不受支持

chunkNames

naming.chunk

使用与 esbuild 相同的模板语法,但必须显式包含 [ext]

Bun.build({
  entrypoints: ["./index.tsx"],
  naming: {
    chunk: "[name].[ext]",
  },
});
colorn/aBun 在构建结果的 logs 属性中返回日志。
conditionsn/a不支持。导出条件优先级由 target 确定。
definedefine
dropn/a不受支持

entryNames

namingnaming.entry

Bun 支持一个 naming 键,它可以是字符串或对象。使用与 esbuild 相同的模板语法,但必须显式包含 [ext]

Bun.build({
  entrypoints: ["./index.tsx"],
  // when string, this is equivalent to entryNames
  naming: "[name].[ext]",

  // granular naming options
  naming: {
    entry: "[name].[ext]",
    asset: "[name].[ext]",
    chunk: "[name].[ext]",
  },
});
entryPointsentrypoints大小写差异
externalexternal无差异
页脚n/a不受支持
格式格式目前仅支持 "esm"。计划支持 "cjs""iife"
全局名称n/a不受支持
忽略注释n/a不受支持
注入n/a不受支持
JSXJSXJS API 不支持,在 tsconfig.json 中配置
JSX 开发JSX 开发JS API 不支持,在 tsconfig.json 中配置
JSX 工厂JSX 工厂JS API 不支持,在 tsconfig.json 中配置
JSX 片段JSX 片段JS API 不支持,在 tsconfig.json 中配置
JSX 导入源JSX 导入源JS API 不支持,在 tsconfig.json 中配置
JSX 副作用JSX 副作用JS API 不支持,在 tsconfig.json 中配置
保留名称n/a不受支持
合法注释n/a不受支持
加载器加载器Bun 支持与 esbuild 不同的内置加载器集;请参阅 捆绑器 > 加载器 以获取完整参考。esbuild 加载器 dataurlbinarybase64copyempty 尚未实现。
日志级别n/a不受支持
日志限制n/a不受支持
日志覆盖n/a不受支持
主字段n/a不受支持
混淆缓存n/a不受支持
混淆属性n/a不受支持
混淆引号n/a不受支持
元文件n/a不受支持

最小化

最小化

在 Bun 中,minify 可以是布尔值或对象。

Bun.build({
  entrypoints: ['./index.tsx'],
  // enable all minification
  minify: true

  // granular options
  minify: {
    identifiers: true,
    syntax: true,
    whitespace: true
  }
})
最小化标识符minify.identifiers参见 minify
最小化语法minify.syntax参见 minify
最小化空白minify.whitespace参见 minify
节点路径n/a不受支持
输出扩展名n/a不受支持
输出基础名称不同
输出目录输出目录无差异
输出文件输出文件无差异
n/a不支持,使用 external
平台目标支持 "bun""node""browser"(默认)。不支持 "neutral"
插件插件Bun 的插件 API 是 esbuild 的一个子集。一些 esbuild 插件可以与 Bun 一起开箱即用。
保留符号链接n/a不受支持
公共路径公共路径无差异
n/a不受支持
保留属性n/a不受支持
解析扩展名n/a不受支持
源根n/a不受支持
源映射源映射支持 "inline""external""none"
源内容n/a不受支持
拆分拆分无差异
标准输入n/a不受支持
支持n/a不受支持
目标n/a不支持语法降级
摇树n/a始终为 true
tsconfign/a不受支持
写入n/a如果设置了 outdir/outfile,则设置为 true,否则设置为 false

插件 API

Bun 的插件 API 旨在与 esbuild 兼容。Bun 不支持 esbuild 的整个插件 API 表面,但实现了核心功能。许多第三方 esbuild 插件可以与 Bun 一起开箱即用。

从长远来看,我们的目标是与 esbuild 的 API 实现功能对等,因此如果某些功能不起作用,请提交一个问题以帮助我们确定优先级。

Bun 和 esbuild 中的插件使用 builder 对象定义。

import type { BunPlugin } from "bun";

const myPlugin: BunPlugin = {
  name: "my-plugin",
  setup(builder) {
    // define plugin
  },
};

builder 对象提供了一些方法,用于挂接到捆绑过程的各个部分。Bun 实现了 onResolveonLoad;它尚未实现 esbuild 钩子 onStartonEndonDispose 以及 resolve 实用程序。initialOptions 已部分实现,它只读且仅包含 esbuild 选项的一个子集;改为使用 config(与 Bun 的 BuildConfig 格式相同)。

import type { BunPlugin } from "bun";
const myPlugin: BunPlugin = {
  name: "my-plugin",
  setup(builder) {
    builder.onResolve(
      {
        /* onResolve.options */
      },
      args => {
        return {
          /* onResolve.results */
        };
      },
    );
    builder.onLoad(
      {
        /* onLoad.options */
      },
      args => {
        return {
          /* onLoad.results */
        };
      },
    );
  },
};

onResolve

选项

🟢过滤器
🟢命名空间

参数

🟢路径
🟢导入者
🔴命名空间
🔴解析目录
🔴类型
🔴插件数据

结果

🟢命名空间
🟢路径
🔴错误
🔴external
🔴插件数据
🔴插件名称
🔴副作用
🔴后缀
🔴警告
🔴监视目录
🔴监视文件

onLoad

选项

🟢过滤器
🟢命名空间

参数

🟢路径
🔴命名空间
🔴后缀
🔴插件数据

结果

🟢内容
🟢加载器
🔴错误
🔴插件数据
🔴插件名称
🔴解析目录
🔴警告
🔴监视目录
🔴监视文件