运行时包管理器打包器测试运行器代码检查格式化指南参考博客反馈

TOML

通过运行时 API 和打包器集成,使用 Bun 对 TOML 文件的内置支持

在 Bun 中,TOML 与 JSON、JSON5 和 YAML 同为一等公民。你可以:

  • 使用 Bun.TOML.parse 解析 TOML 字符串
  • 在运行时将 TOML 文件作为模块进行 import 和 require(包括热重载和监视模式支持)
  • 在前端应用中使用 Bun 的打包器 import 和 require TOML 文件

运行时 API

Bun.TOML.parse()

将 TOML 字符串解析为 JavaScript 对象。

TypeScript
import { TOML } from "bun";
const text = `
name = "my-app"
version = "1.0.0"
debug = true

[database]
host = "localhost"
port = 5432

[features]
tags = ["web", "api"]
`;

const data = TOML.parse(text);
console.log(data);
// {
//   name: "my-app",
//   version: "1.0.0",
//   debug: true,
//   database: { host: "localhost", port: 5432 },
//   features: { tags: ["web", "api"] }
// }

支持的 TOML 特性

Bun 的 TOML 解析器实现了完整的 TOML v1.1.0 规范,并通过了完整的官方 toml-test 一致性测试套件。

  • 字符串:基本字符串("...")和字面字符串('...'),包括多行字符串,以及所有转义序列(\uHHHH、\UHHHHHHHH 和 TOML 1.1 中的 \xHH 与 \e)
  • 整数:十进制、十六进制(0x)、八进制(0o)和二进制(0b)。超出 ±(2^53 - 1) 的整数会抛出错误,因为 JavaScript 数字无法无损表示它们
  • 浮点数:包括 inf 和 nan
  • 布尔值:true 和 false
  • 日期/时间:以 Temporal 对象返回——带偏移量的日期时间对应 Temporal.Instant,本地日期时间对应 Temporal.PlainDateTime,本地日期对应 Temporal.PlainDate,本地时间对应 Temporal.PlainTime
  • 数组:包括混合类型和嵌套数组
  • 表:标准表([table])和内联表({ key = "value" }),包括 TOML 1.1 多行内联表
  • 表数组:[[array]]
  • 点分键:a.b.c = "value"
  • 注释:使用 #
TypeScript
const data = Bun.TOML.parse(`
# 应用配置
title = "My App"

[owner]
name = "John Doe"

[database]
enabled = true
ports = [8000, 8001, 8002]
connection_max = 5000

[servers.alpha]
ip = "10.0.0.1"
role = "frontend"

[servers.beta]
ip = "10.0.0.2"
role = "backend"
`);

日期/时间

TOML 的四种日期/时间类型分别一一对应一种 Temporal 类型。Temporal 支持纳秒精度;按照 TOML 规范的允许范围,超过九位数的小数秒会被截断:

TypeScript
const doc = Bun.TOML.parse(`
created = 1979-05-27T00:32:00-07:00  # offset date-time
meeting = 1979-05-27T07:32:00       # local date-time
birthday = 1979-05-27               # local date
opens = 07:32:00                    # local time
`);

doc.created; // Temporal.Instant (an offset date-time specifies an instant;
//              the written offset normalizes away: 1979-05-27T07:32:00Z)
doc.meeting; // Temporal.PlainDateTime
doc.birthday; // Temporal.PlainDate
doc.opens; // Temporal.PlainTime

错误处理

如果 TOML 无效,Bun.TOML.parse() 会抛出 SyntaxError:

TypeScript
try {
  Bun.TOML.parse("invalid = = =");
} catch (error) {
  console.error("Failed to parse TOML:", error.message);
  // 解析 TOML 失败:TOML 解析错误:应为值,但发现了 '='
}

Bun.TOML.stringify()

将 JavaScript 对象序列化为 TOML 文档。标量键位于前面,随后是 [table] 和 [[array-of-tables]] 部分:

TypeScript
Bun.TOML.stringify({
  name: "app",
  server: { host: "localhost", port: 8080 },
  points: [{ x: 1 }, { x: 2 }],
});
// name = "app"
//
// [server]
// host = "localhost"
// port = 8080
//
// [[points]]
// x = 1
//
// [[points]]
// x = 2

顶层值必须是一个对象——TOML 文档是一个表。Temporal.Instant、Temporal.PlainDateTime、Temporal.PlainDate 和 Temporal.PlainTime 值会转换为对应的 TOML 日期/时间字面量,因此 stringify(parse(doc)) 可以往返保留日期/时间类型。Temporal.ZonedDateTime 会转换为带偏移量的日期时间,Date 会转换为 UTC 时区中的带偏移量日期时间。TOML 没有用于时区或日历注解的语法,因此这些注解会被丢弃(写入 ISO 字段);其年份为四位数,因此超出 0000–9999 范围的日期值和无效的 Date 会抛出错误。由于 TOML 无法表示这些值,null 值、BigInt、循环结构、Temporal.PlainYearMonth、Temporal.PlainMonthDay 和 Temporal.Duration 也会抛出错误;undefined、函数和 symbol 属性会被跳过(在数组中会抛出错误,因为 TOML 数组不能有空位),将其中任意一种作为顶层值传入时会返回 undefined,与 JSON.stringify 的行为相同。


模块导入

ES 模块

直接将 TOML 文件作为 ES 模块导入。Bun 会解析 TOML,并将其同时作为默认导出和命名导出提供:

config.toml
[database]
host = "localhost"
port = 5432
name = "myapp"

[redis]
host = "localhost"
port = 6379

[features]
auth = true
rateLimit = true
analytics = false

默认导入

app.ts
import config from "./config.toml";

console.log(config.database.host); // "localhost"
console.log(config.redis.port); // 6379

命名导入

你可以将顶层 TOML 表解构为命名导入:

app.ts
import { database, redis, features } from "./config.toml";

console.log(database.host); // "localhost"
console.log(redis.port); // 6379
console.log(features.auth); // true

或者两者结合:

app.ts
import config, { database, features } from "./config.toml";

// 使用完整配置对象
console.log(config);

// 或者使用特定部分
if (features.rateLimit) {
  setupRateLimiting(database);
}

导入属性

使用导入属性将任意文件作为 TOML 加载:

app.ts
import myConfig from "./my.config" with { type: "toml" };

CommonJS

你也可以在 CommonJS 中使用 require 导入 TOML 文件:

app.ts
const config = require("./config.toml");
console.log(config.database.name); // "myapp"

// 解构同样可用
const { database, redis } = require("./config.toml");
console.log(database.port); // 5432

TOML 热重载

当你使用 bun --hot 运行应用程序时,Bun 会检测 TOML 文件的更改,并在不重启的情况下重新加载:

config.toml
[server]
port = 3000
host = "localhost"

[features]
debug = true
verbose = false
server.ts
import { server, features } from "./config.toml";

console.log(`启动服务器:${server.host}:${server.port}`);

Bun.serve({
  port: server.port,
  hostname: server.host,
  fetch(req) {
    if (features.verbose) {
      console.log(`${req.method} ${req.url}`);
    }
    return new Response("Hello World");
  },
});

使用热重载运行:

terminal
bun --hot server.ts

打包器集成

使用 Bun 进行打包时,打包器会在构建时解析导入的 TOML,并将其作为 JavaScript 模块包含在内:

terminal
bun build app.ts --outdir=dist

这意味着:

  • 生产环境中无需承担 TOML 运行时解析开销
  • 更小的打包体积
  • 对未使用的属性进行 Tree Shaking(命名导入)

动态导入

你也可以动态导入 TOML 文件:

TypeScript
const config = await import("./config.toml");