執行環境套件管理器打包器測試執行器程式碼檢查格式化指南參考部落格意見回饋

YAML

通過 Bun 的執行環境 API 和打包器整合使用內建的 YAML 檔案支援

在 Bun 中,YAML 與 JSON 和 TOML 同等重要。你可以:

  • 使用 Bun.YAML.parse 解析 YAML 字串
  • 在執行環境將 YAML 檔案作為模組進行 import 和 require(包括熱過載和 watch 模式支援)
  • 使用 Bun 的打包器在前端應用中 import 和 require YAML 檔案

相容性

Bun 的 YAML 解析器使用 Rust 編寫,通過了官方 YAML 測試套件,覆蓋了絕大多數實際使用場景。


執行環境 API

Bun.YAML.parse()

將 YAML 字串解析為 JavaScript 物件。

TypeScript
import { YAML } from "bun";
const text = `
name: John Doe
age: 30
email: john@example.com
hobbies:
  - reading
  - coding
  - hiking
`;

const data = YAML.parse(text);
console.log(data);
// {
//   name: "John Doe",
//   age: 30,
//   email: "john@example.com",
//   hobbies: ["reading", "coding", "hiking"]
// }

多文件 YAML

解析由多個文件(用 --- 分隔)構成的 YAML 時,Bun.YAML.parse() 傳回一個陣列:

TypeScript
const multiDoc = `
---
name: Document 1
---
name: Document 2
---
name: Document 3
`;

const docs = Bun.YAML.parse(multiDoc);
console.log(docs);
// [
//   { name: "Document 1" },
//   { name: "Document 2" },
//   { name: "Document 3" }
// ]

支援的 YAML 特性

Bun 的 YAML 解析器支援 YAML 1.2 規範,包括:

  • 標量:字串、數字、布林值、null 值
  • 集合:序列(陣列)和對映(物件)
  • 錨點和別名:使用 & 和 * 定義可重用節點。別名集合共享同一物件身份。別名可以引用包含自身的集合,因此 Bun.YAML.parse 可以傳回迴圈物件。作為模組匯入的 YAML 不能包含迴圈引用。
  • 標籤:型別提示,例如 !!str、!!int、!!float、!!bool、!!null
  • 多行字串:字面量(|)和摺疊(>)標量
  • 註釋:使用 #
  • 指令:%YAML 和 %TAG
TypeScript
const yaml = `
# 員工記錄
employee: &emp
  name: Jane Smith
  department: Engineering
  skills:
    - JavaScript
    - TypeScript
    - React

manager: *emp  # 引用員工

config: !!str 123  # 顯式字串型別

description: |
  這是一個多行
  文字字串,保留
  換行和空格。

summary: >
  這是一個摺疊字串,
  用空格連線行,
  除非有空白行。
`;

const data = Bun.YAML.parse(yaml);

錯誤處理

如果 YAML 無效,Bun.YAML.parse() 會丟擲 SyntaxError:

TypeScript
try {
  Bun.YAML.parse("invalid: yaml: content:");
} catch (error) {
  console.error("解析 YAML 失敗:", error.message);
}

模組匯入

ES 模組

直接將 YAML 檔案作為 ES 模組匯入。Bun 會解析其內容,並將其同時作為預設匯出和命名匯出提供:

config.yaml
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.yaml";

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

命名匯入

頂層 YAML 屬性可作為命名匯入使用:

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

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

或者兩者結合:

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

// 使用完整設定物件
console.log(config);

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

CommonJS

你也可以在 CommonJS 中使用 require 匯入 YAML 檔案:

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

// 解構同樣有效
const { database, redis } = require("./config.yaml");
console.log(database.port); // 5432

YAML 熱過載

當你使用 bun --hot 執行應用程式時,Bun 會檢測 YAML 檔案的更改,並在不關閉連線的情況下重新載入它們。

設定熱過載範例

config.yaml
server:
  port: 3000
  host: localhost

features:
  debug: true
  verbose: false
server.ts
import { server, features } from "./config.yaml";

console.log(`啟動伺服器,地址為 ${server.host}:${server.port}`);

if (features.debug) {
  console.log("除錯模式已啟用");
}

// 伺服器程式碼
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

現在,當你修改 config.yaml 時,正在執行的應用程式會獲取更改:你可以在開發過程中調整設定或切換功能標誌,而無需重啟應用程式。


設定管理

基於環境的設定

一個 YAML 檔案可以儲存多個環境的設定,並通過錨點共享預設值:

config.yaml
defaults: &defaults
  timeout: 5000
  retries: 3
  cache:
    enabled: true
    ttl: 3600

development:
  <<: *defaults
  api:
    url: http://localhost:4000
    key: dev_key_12345
  logging:
    level: debug
    pretty: true

staging:
  <<: *defaults
  api:
    url: https://staging-api.example.com
    key: ${STAGING_API_KEY}
  logging:
    level: info
    pretty: false

production:
  <<: *defaults
  api:
    url: https://api.example.com
    key: ${PROD_API_KEY}
  cache:
    enabled: true
    ttl: 86400
  logging:
    level: error
    pretty: false
app.ts
import configs from "./config.yaml";

const env = process.env.NODE_ENV || "development";
const config = configs[env];

// YAML 值中的環境變數插值函式
function interpolateEnvVars(obj: any): any {
  if (typeof obj === "string") {
    return obj.replace(/\${(\w+)}/g, (_, key) => process.env[key] || "");
  }
  if (typeof obj === "object") {
    for (const key in obj) {
      obj[key] = interpolateEnvVars(obj[key]);
    }
  }
  return obj;
}

export default interpolateEnvVars(config);

功能標誌設定

features.yaml
features:
  newDashboard:
    enabled: true
    rolloutPercentage: 50
    allowedUsers:
      - admin@example.com
      - beta@example.com

  experimentalAPI:
    enabled: false
    endpoints:
      - /api/v2/experimental
      - /api/v2/beta

  darkMode:
    enabled: true
    default: auto # auto, light, dark
feature-flags.ts
import { features } from "./features.yaml";

export function isFeatureEnabled(featureName: string, userEmail?: string): boolean {
  const feature = features[featureName];

  if (!feature?.enabled) {
    return false;
  }

  // 檢查釋出比例
  if (feature.rolloutPercentage < 100) {
    const hash = hashCode(userEmail || "anonymous");
    if (hash % 100 >= feature.rolloutPercentage) {
      return false;
    }
  }

  // 檢查允許使用者列表
  if (feature.allowedUsers && userEmail) {
    return feature.allowedUsers.includes(userEmail);
  }

  return true;
}

// 與熱過載配合,實現即時切換功能
if (isFeatureEnabled("newDashboard", user.email)) {
  renderNewDashboard();
} else {
  renderLegacyDashboard();
}

資料庫設定

database.yaml
connections:
  primary:
    type: postgres
    host: ${DB_HOST:-localhost}
    port: ${DB_PORT:-5432}
    database: ${DB_NAME:-myapp}
    username: ${DB_USER:-postgres}
    password: ${DB_PASS}
    pool:
      min: 2
      max: 10
      idleTimeout: 30000

  cache:
    type: redis
    host: ${REDIS_HOST:-localhost}
    port: ${REDIS_PORT:-6379}
    password: ${REDIS_PASS}
    db: 0

  analytics:
    type: clickhouse
    host: ${ANALYTICS_HOST:-localhost}
    port: 8123
    database: analytics

migrations:
  autoRun: ${AUTO_MIGRATE:-false}
  directory: ./migrations

seeds:
  enabled: ${SEED_DB:-false}
  directory: ./seeds
db.ts
import { connections, migrations } from "./database.yaml";
import { createConnection } from "./database-driver";

// 解析帶預設值的環境變數
function parseConfig(config: any) {
  return JSON.parse(
    JSON.stringify(config).replace(
      /\${([^:-]+)(?::([^}]+))?}/g,
      (_, key, defaultValue) => process.env[key] || defaultValue || "",
    ),
  );
}

const dbConfig = parseConfig(connections);

export const db = await createConnection(dbConfig.primary);
export const cache = await createConnection(dbConfig.cache);
export const analytics = await createConnection(dbConfig.analytics);

// 如果設定自動執行遷移
if (parseConfig(migrations).autoRun === "true") {
  await runMigrations(db, migrations.directory);
}

打包器整合

當你打包匯入 YAML 檔案的應用程式時,Bun 會在建置時解析 YAML,並將其作為 JavaScript 模組包含在內:

terminal
bun build app.ts --outdir=dist

在建置時解析意味著:

  • 生產環境中無需執行環境解析 YAML
  • 更小的打包體積
  • 對未使用的設定進行搖樹最佳化(命名匯入)

動態匯入

動態匯入 YAML 檔案,按需載入設定:

TypeScript
// 根據環境載入設定
const env = process.env.NODE_ENV || "development";
const { default: config } = await import(`./configs/${env}.yaml`);

// 載入使用者特定設定
async function loadUserSettings(userId: string) {
  try {
    const settings = await import(`./users/${userId}/settings.yaml`);
    return settings.default;
  } catch {
    const { default: defaults } = await import("./users/default-settings.yaml");
    return defaults;
  }
}