bun-macros について
bun-macros は、バンドル時に JavaScript を実行し、その結果を出力へ埋め込む Bun マクロのスキルガイドです。環境値、ファイル埋め込み、コード生成、非同期マクロ、デバッグを扱います。
主な機能
- 01バンドル時の関数実行
- 02環境値とビルド値の埋め込み
- 03ファイル内容の埋め込み
- 04型や API コードの生成
- 05非同期マクロと引数付きマクロ
- 06マクロ評価のデバッグ
活用シーン
- 01ビルド時定数を出力に埋め込む
- 02ファイルデータをバンドルに含める
- 03スキーマからコードを生成
- 04評価されないマクロの原因調査
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ secondsky/ claude-skills --skill bun-macrosインストール後、Claude Code で/bun-macrosを入力して読み込まれたか確認します。
詳細
Bun マクロ
Bun マクロはバンドル時に JavaScript を実行し、その結果を出力にインライン化します。
クイックスタート
// src/config.ts (macro file)
export function getVersion() {
return "1.0.0";
}
export function getBuildTime() {
return new Date().toISOString();
}
// src/index.ts (consumer)
import { getVersion, getBuildTime } from "./config" with { type: "macro" };
// At bundle time, these become:
const version = "1.0.0";
const buildTime = "2024-01-15T12:00:00.000Z";
マクロの構文
// Import with macro attribute
import { fn } from "./macro-file" with { type: "macro" };
// Call the macro (evaluated at build time)
const result = fn();
よくあるユースケース
環境変数のインライン化
// macros/env.ts
export function env(key: string): string {
return process.env[key] ?? "";
}
// src/index.ts
import { env } from "./macros/env" with { type: "macro" };
const apiUrl = env("API_URL");
// Becomes: const apiUrl = "https://api.example.com";
Git 情報
// macros/git.ts
export function gitCommit(): string {
return Bun.spawnSync(["git", "rev-parse", "HEAD"])
.stdout.toString().trim();
}
export function gitBranch(): string {
return Bun.spawnSync(["git", "branch", "--show-current"])
.stdout.toString().trim();
}
// src/index.ts
import { gitCommit, gitBranch } from "./macros/git" with { type: "macro" };
const BUILD_INFO = {
commit: gitCommit(),
branch: gitBranch(),
};
// Inlined at build time
ファイルの埋め込み
// macros/embed.ts
export function embedFile(path: string): string {
return Bun.file(path).text();
}
export function embedJSON(path: string): unknown {
return Bun.file(path).json();
}
// src/index.ts
import { embedFile, embedJSON } from "./macros/embed" with { type: "macro" };
const license = embedFile("./LICENSE");
const config = embedJSON("./config.json");
ビルド定数
// macros/constants.ts
export function isDev(): boolean {
return process.env.NODE_ENV !== "production";
}
export function buildDate(): number {
return Date.now();
}
export function randomId(): string {
return Math.random().toString(36).slice(2);
}
// src/index.ts
import { isDev, buildDate, randomId } from "./macros/constants" with { type: "macro" };
if (isDev()) {
console.log("Development mode");
}
const BUILD_ID = randomId();
ディレクトリ一覧
// macros/files.ts
import { readdirSync } from "fs";
export function listRoutes(): string[] {
return readdirSync("./src/routes")
.filter(f => f.endsWith(".tsx"))
.map(f => f.replace(".tsx", ""));
}
// src/router.ts
import { listRoutes } from "./macros/files" with { type: "macro" };
const routes = listRoutes();
// Becomes: const routes = ["home", "about", "users"];
コード生成
// macros/codegen.ts
export function generateTypes(schema: string): string {
// Parse schema and generate types at build time
const types = parseSchemaToTypes(schema);
return types;
}
export function generateAPI(spec: string): string {
const openapi = JSON.parse(Bun.file(spec).text());
// Generate API client code
return generateClientFromOpenAPI(openapi);
}
戻り値の型
マクロは次の値を返せます。
// Primitives
export function getString(): string { return "hello"; }
export function getNumber(): number { return 42; }
export function getBoolean(): boolean { return true; }
export function getNull(): null { return null; }
// Objects (serializable)
export function getObject(): object {
return { key: "value", nested: { a: 1 } };
}
// Arrays
export function getArray(): string[] {
return ["a", "b", "c"];
}
// Response (for bundler plugins)
export function getResponse(): Response {
return new Response("content", {
headers: { "Content-Type": "text/plain" },
});
}
非同期マクロ
// macros/fetch.ts
export async function fetchSchema(): Promise<object> {
const response = await fetch("https://api.example.com/schema.json");
return response.json();
}
// src/index.ts
import { fetchSchema } from "./macros/fetch" with { type: "macro" };
const schema = await fetchSchema();
// Fetched at build time, inlined as object literal
パラメーター
// macros/parameterized.ts
export function repeat(str: string, count: number): string {
return str.repeat(count);
}
export function formatDate(format: string): string {
return new Date().toLocaleDateString("en-US", {
dateStyle: format as any,
});
}
// src/index.ts
import { repeat, formatDate } from "./macros/parameterized" with { type: "macro" };
const separator = repeat("-", 20);
const date = formatDate("long");
条件付きコード
// macros/platform.ts
export function platform(): "node" | "bun" | "browser" {
if (typeof Bun !== "undefined") return "bun";
if (typeof process !== "undefined") return "node";
return "browser";
}
// src/index.ts
import { platform } from "./macros/platform" with { type: "macro" };
if (platform() === "bun") {
// This block is kept or removed at build time
console.log("Running on Bun");
}
バンドル時のみの実行
マクロはバンドル時にのみ実行されます。
# Macros executed
bun build src/index.ts --outdir=dist
# Macros NOT executed (runtime)
bun run src/index.ts
エラー処理
// macros/safe.ts
export function requireEnv(key: string): string {
const value = process.env[key];
if (!value) {
throw new Error(`Missing required env var: ${key}`);
}
return value;
}
// Build fails if env var missing
import { requireEnv } from "./macros/safe" with { type: "macro" };
const secret = requireEnv("API_SECRET");
マクロのデバッグ
// macros/debug.ts
export function debug(label: string, value: unknown): unknown {
console.log(`[MACRO] ${label}:`, value);
return value;
}
// See output during build
bun build src/index.ts --outdir=dist
// [MACRO] config: { ... }
ベストプラクティス
- マクロを純粋に保つ - 副作用を避けます
- シリアライズ可能な値を返す - JSON 互換である必要があります
- エラーを処理する - ビルド失敗時に意味のあるエラーをスローします
- 依存関係を文書化する - 必要なファイルや環境変数を記載します
- 個別にテストする - マクロは通常の関数としてテストできます
よくあるエラー
| エラー | 原因 | 修正方法 |
|---|---|---|
Cannot find module | インポートパスが間違っています | マクロファイルの場所を確認します |
Not serializable | 関数またはクラスが返されました | プレーンなデータを返します |
Macro failed | マクロ内でランタイムエラーが発生しました | マクロを個別にデバッグします |
Not evaluated | with { type: "macro" } がありません | インポート属性を追加します |
参照資料を読み込むタイミング
次の場合は references/advanced-patterns.md を読み込みます。
- 複雑なコード生成
- プラグインの統合
- ビルドパイプライン カスタムマクロ実装のパターン、ビルドシステムとの統合例、高度なバンドラープラグインのワークフローを取得して提示します。
次の場合は references/debugging.md を読み込みます。
- マクロが評価されない
- 想定外の出力
- パフォーマンスの問題 デバッグ手法を取得し、手順に沿った診断を提供して、ユーザー固有の問題に応じた最適化戦略を提案します。
コメント
Markdown 対応