Skip to main content
JavaScript 生态系统正处于从 CommonJS 模块到原生 ES 模块(ESM)的多年过渡期,不同的运行时和构建工具在历史上对导入说明符如何映射到磁盘上的文件存在分歧。Bun 旨在提供一致且可预测的模块解析系统,无需配置即可工作。

语法

考虑以下文件。
运行 index.ts 会打印 “Hello world!”。
terminal
这里 ./hello 是一个没有扩展名的相对路径。带扩展名的导入是可选的,但支持。 要解析此导入,Bun 按顺序检查以下文件:
  • ./hello.tsx
  • ./hello.jsx
  • ./hello.mts
  • ./hello.ts
  • ./hello.mjs
  • ./hello.js
  • ./hello.cts
  • ./hello.cjs
  • ./hello.json
  • ./hello/index.tsx
  • ./hello/index.jsx
  • ./hello/index.mts
  • ./hello/index.ts
  • ./hello/index.mjs
  • ./hello/index.js
  • ./hello/index.cts
  • ./hello/index.cjs
  • ./hello/index.json
具体顺序因上下文而异:require() 在 ESM 扩展名(.mts.mjs)之前先尝试 CommonJS 扩展名(.cts.cjs),而 node_modules 内的导入优先尝试 JavaScript 扩展名,然后才是 TypeScript 扩展名。上述列表显示了本地 ESM import 的顺序。
如果导入路径包含扩展名,Bun 首先检查确切的文件。如果没有精确匹配,Bun 会回退到尝试附加到完整路径的上述扩展名列表(因此 ./hello.world 可以解析为 ./hello.world.ts)。
index.ts
还有一个额外的 TypeScript 兼容性规则:如果你从 *.js*.jsx 导入,Bun 也会检查匹配的 *.ts*.tsx 文件,并在 node_modules 之外,*.mjs 也匹配 *.mts。这遵循 TypeScript 编译器的文件扩展名替换规则,允许源文件通过其编译后的输出路径相互引用。注意与 TypeScript 不同,Bun 不会将 .cjs 重写为 .cts
index.ts
Bun 同时支持 ES 模块(import/export 语法)和 CommonJS 模块(require()/module.exports)。以下 CommonJS 版本在 Bun 中也可以工作。
尽管如此,不推荐在新项目中使用 CommonJS。

模块系统

Bun 原生支持 CommonJS 和 ES 模块。ES 模块是新项目的推荐模块格式,但 CommonJS 模块在 Node.js 生态系统中仍被广泛使用。 在 Bun 的 JavaScript 运行时中,ES 模块和 CommonJS 模块都可以使用 require。如果目标模块是 ES 模块,require 返回模块命名空间对象(相当于 import * as)。如果目标模块是 CommonJS 模块,require 返回 module.exports 对象(如 Node.js 中)。

使用 require()

你可以 require() 任何文件或包,甚至是 .ts.mjs 文件。
index.ts
在 2016 年,ECMAScript 增加了对 ES 模块的支持。ES 模块是 JavaScript 模块的标准。然而,数百万个 npm 包仍然使用 CommonJS 模块。CommonJS 模块使用 module.exports 导出值,通常使用 require 导入。
my-commonjs.cjs
CommonJS 和 ES 模块最大的区别在于 CommonJS 模块是同步的,而 ES 模块是异步的。其他区别:
  • ES 模块支持顶层 await,而 CommonJS 模块不支持。
  • ES 模块始终处于严格模式,而 CommonJS 模块则不是。
  • 浏览器原生不支持 CommonJS 模块,但它们通过 <script type="module"> 原生支持 ES 模块。
  • CommonJS 模块不能被静态分析,而 ES 模块只允许静态导入和导出。
  • 静态 import 语句是同步执行的,就像 CommonJS 的 require。ES 模块也可以通过异步的 import() 函数动态加载,称为”动态导入”。

使用 import

你可以 import 任何文件或包,甚至是 .cjs 文件。
index.ts

同时使用 importrequire()

在 Bun 中,你可以在同一个文件中使用 importrequire——它们都能工作,始终如此。
index.ts

顶层 await

此规则的唯一例外是顶层 await。你不能 require() 一个使用顶层 await 的文件,因为 require() 函数本质上是同步的。 幸运的是,很少有库使用顶层 await,所以这很少成为问题。但如果你在应用代码中使用顶层 await,请确保该文件没有从应用中的其他地方被 require()。请改用 import动态 import()

导入包

Bun 实现了 Node.js 的模块解析算法,因此你可以使用裸说明符从 node_modules 导入包。
index.ts
完整算法在 Node.js 文档中有说明。简而言之:如果你从 foo 导入,Bun 会向上扫描文件系统,查找包含包 foonode_modules 目录。

NODE_PATH

Bun 支持 NODE_PATH 用于额外的模块解析目录:
多个路径使用平台的分隔符(Unix 上为 :,Windows 上为 ;):
一旦找到 foo 包,Bun 会读取其 package.json 以确定包的入口点。Bun 首先读取 exports 字段并检查以下条件。
package.json
这些条件中_最先_出现在 package.json 中的决定包的入口点。 Bun 支持子路径 "exports""imports"
package.json
子路径导入和条件导入可以结合使用。
package.json
与 Node.js 一样,在 "exports" 映射中指定任何子路径都会阻止导入其他子路径;你只能导入那些明确导出的文件。给定前面的 package.json
index.ts
发布 TypeScript — Bun 支持特殊的 "bun" 导出条件。如果你的库是用 TypeScript 编写的,你可以直接将未转译的 TypeScript 文件发布到 npm。如果你在 "bun" 条件中指定了包的 *.ts 入口点,Bun 会直接导入和执行你的 TypeScript 源文件。
如果没有定义 exports,Bun 会回退到遗留的顶级入口点字段。在运行时,Bun 在有 "main" 时优先使用它(或隐式的 index.* 文件),否则使用 "module"
package.json

自定义条件

--conditions 标志指定从 package.json"exports" 解析包时使用的条件。 bun build 和 Bun 的运行时都支持此标志。
terminal
你也可以通过 Bun.build 以编程方式使用 conditions
build.ts

路径重映射

Bun 通过 tsconfig.json 中的 TypeScript compilerOptions.paths 支持导入路径重映射,与编辑器配合良好。如果你不是 TypeScript 用户,可以在项目根目录使用 jsconfig.json 获得相同行为。
tsconfig.json
Bun 还支持 package.json 中的 Node.js 风格子路径导入,其中映射路径必须以 # 开头。TypeScript 和编辑器也会解析这些路径,你可以同时使用两种机制。
package.json
Bun 的 JavaScript 运行时原生支持 CommonJS。当 Bun 的 JavaScript 转译器检测到 module.exports 的使用时,它会将该文件视为 CommonJS 文件。然后模块加载器将转译后的模块包装成一个形状如下的函数:
moduleexportsrequire 与 Node.js 中的 moduleexportsrequire 非常相似。这些是通过 C++ 中的 with scope 分配的。一个内部的 Map 存储 exports 对象以处理模块完全加载之前的循环 require 调用。一旦 CommonJS 模块成功求值,会创建一个合成模块记录(Synthetic Module Record),其 default ES 模块导出设置为 module.exports,并且 module.exports 对象的键会作为命名导出重新导出(如果 module.exports 对象是一个对象)。Bun 的打包器工作方式不同:它将 CommonJS 模块包装在一个 require_${moduleName} 函数中,该函数返回 module.exports 对象。

import.meta

import.meta 对象暴露了当前模块的信息。它是 JavaScript 语言的一部分,但其内容没有标准化:每个”宿主”(浏览器或运行时)都在 import.meta 对象上实现自己的属性。 Bun 实现了以下属性。
/path/to/project/file.ts