mcpField
B
bun-file-io

bun-file-io

github.com/secondsky開発240232 回インストール5 週間前に更新
リポジトリ

bun-file-io について

Bun を使う開発者向けのファイル I/O スキルガイドです。Bun と Node.js の基本操作に加え、ストリーム、ディレクトリ、パス、ファイル監視の例を扱います。

主な機能

  1. 01テキスト、JSON、バイナリの読み取り
  2. 02ファイルへの書き込みと追記
  3. 03ファイルデータのストリーム処理と転送
  4. 04ディレクトリとメタデータの管理
  5. 05glob パターンによるファイル検索
  6. 06ファイル監視と一時ディレクトリの作成

活用シーン

  1. 01JSON 設定ファイルの読み込み
  2. 02大容量ファイルの分割処理
  3. 03データのファイルへのコピーやダウンロード
  4. 04glob パターンによるソースファイル検索

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/secondsky/claude-skills --skill bun-file-io

インストール後、Claude Code で/bun-file-ioを入力して読み込まれたか確認します。

詳細

Bun ファイル I/O

Bun は Bun.file() と Bun.write() を通じて、高速で最適化されたファイル操作を提供します。

ファイルの読み取り

Bun.file()
// Create file reference (lazy, doesn't read yet)
const file = Bun.file("./data.txt");

// File properties
console.log(file.size);        // Size in bytes
console.log(file.type);        // MIME type
console.log(file.name);        // File path
console.log(await file.exists()); // Boolean

// Read content
const text = await file.text();
const json = await file.json();
const buffer = await file.arrayBuffer();
const bytes = await file.bytes();   // Uint8Array
const stream = file.stream();       // ReadableStream
特定の型を読み取る
// JSON file
const config = await Bun.file("config.json").json();

// Text file
const content = await Bun.file("readme.md").text();

// Binary file
const binary = await Bun.file("image.png").arrayBuffer();

// With import attributes
import data from "./data.json" with { type: "json" };
import text from "./content.txt" with { type: "text" };

ファイルの書き込み

Bun.write()
// Write string
await Bun.write("./output.txt", "Hello World");

// Write JSON
await Bun.write("./data.json", JSON.stringify({ key: "value" }, null, 2));

// Write binary
await Bun.write("./output.bin", new Uint8Array([1, 2, 3]));

// Write from Response
const response = await fetch("https://example.com/image.png");
await Bun.write("./image.png", response);

// Write from another file (efficient copy)
await Bun.write("./copy.txt", Bun.file("./original.txt"));

// Write with options
await Bun.write("./file.txt", "content", {
  mode: 0o644,  // Unix permissions
});
追記
// Using Bun.file writer
const file = Bun.file("./log.txt");
const writer = file.writer();

writer.write("Line 1\n");
writer.write("Line 2\n");
await writer.flush();
writer.end();

// Or use node:fs
import { appendFile } from "node:fs/promises";
await appendFile("./log.txt", "New line\n");

ストリーミング

読み取りストリーム
const file = Bun.file("./large-file.txt");
const stream = file.stream();

const reader = stream.getReader();
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  // Process chunk (Uint8Array)
  console.log(value);
}
書き込みストリーム
const file = Bun.file("./output.txt");
const writer = file.writer();

for await (const chunk of dataSource) {
  writer.write(chunk);
}

await writer.end();
ストリームのパイプ接続
// File to file
const input = Bun.file("./input.txt");
const output = Bun.file("./output.txt");
await Bun.write(output, input);

// HTTP response to file
const response = await fetch(url);
await Bun.write("./download.zip", response);

// Process stream
const file = Bun.file("./data.txt");
const stream = file.stream();

const transformed = stream.pipeThrough(
  new TransformStream({
    transform(chunk, controller) {
      // Process chunk
      controller.enqueue(chunk.toUpperCase());
    },
  })
);

ディレクトリ操作

import { readdir, mkdir, rmdir, stat } from "node:fs/promises";
import { existsSync, mkdirSync } from "node:fs";

// List directory
const files = await readdir("./src");
const filesWithTypes = await readdir("./src", { withFileTypes: true });

for (const entry of filesWithTypes) {
  if (entry.isDirectory()) {
    console.log(`Dir: ${entry.name}`);
  } else if (entry.isFile()) {
    console.log(`File: ${entry.name}`);
  }
}

// Create directory
await mkdir("./new-dir", { recursive: true });

// Remove directory
await rmdir("./old-dir", { recursive: true });

// Check if exists
const exists = existsSync("./path");

Glob パターン

// Using Bun.Glob
const glob = new Bun.Glob("**/*.ts");

// Scan directory
for await (const file of glob.scan({ cwd: "./src" })) {
  console.log(file); // Relative paths
}

// Get all matches
const files = await Array.fromAsync(glob.scan("./src"));

// With options
const glob2 = new Bun.Glob("**/*.{ts,tsx}");
for await (const file of glob2.scan({
  cwd: "./src",
  dot: true,           // Include dotfiles
  absolute: true,      // Return absolute paths
  onlyFiles: true,     // Only files, not directories
})) {
  console.log(file);
}

// Test if path matches
const pattern = new Bun.Glob("*.ts");
pattern.match("file.ts");    // true
pattern.match("file.js");    // false

ファイルのメタデータ

import { stat, lstat } from "node:fs/promises";

const stats = await stat("./file.txt");

console.log(stats.size);          // Size in bytes
console.log(stats.isFile());      // Is regular file
console.log(stats.isDirectory()); // Is directory
console.log(stats.isSymbolicLink()); // Is symlink
console.log(stats.mtime);         // Modified time
console.log(stats.ctime);         // Changed time
console.log(stats.atime);         // Access time
console.log(stats.mode);          // Permissions

パス操作

import { join, dirname, basename, extname, resolve } from "node:path";

const filePath = "/home/user/project/src/index.ts";

join("a", "b", "c");           // "a/b/c"
dirname(filePath);             // "/home/user/project/src"
basename(filePath);            // "index.ts"
basename(filePath, ".ts");     // "index"
extname(filePath);             // ".ts"
resolve("./relative");         // Absolute path

// Bun-specific
import.meta.dir;   // Directory of current file
import.meta.file;  // Filename
import.meta.path;  // Full path

一般的なパターン

JSON 設定の読み取り
async function loadConfig<T>(path: string): Promise<T> {
  const file = Bun.file(path);
  if (!(await file.exists())) {
    throw new Error(`Config not found: ${path}`);
  }
  return file.json();
}

const config = await loadConfig<AppConfig>("./config.json");
ディレクトリのコピー
import { readdir, mkdir, stat } from "node:fs/promises";
import { join } from "node:path";

async function copyDir(src: string, dest: string) {
  await mkdir(dest, { recursive: true });

  for (const entry of await readdir(src, { withFileTypes: true })) {
    const srcPath = join(src, entry.name);
    const destPath = join(dest, entry.name);

    if (entry.isDirectory()) {
      await copyDir(srcPath, destPath);
    } else {
      await Bun.write(destPath, Bun.file(srcPath));
    }
  }
}
ファイルの監視
import { watch } from "node:fs";

watch("./src", { recursive: true }, (event, filename) => {
  console.log(`${event}: ${filename}`);
});

// Or with Bun's built-in (faster)
const watcher = Bun.spawn(["bun", "--watch", "src/index.ts"]);
一時ファイル
import { mkdtemp } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";

// Create temp directory
const tempDir = await mkdtemp(join(tmpdir(), "app-"));
console.log(tempDir); // /tmp/app-xxxxx

// Write temp file
const tempFile = join(tempDir, "data.txt");
await Bun.write(tempFile, "temporary data");

よくあるエラー

エラー原因解決方法
ENOENTファイルが見つかりませんパスを確認し、exists() を使用します
EACCESアクセスが拒否されましたファイルのアクセス許可を確認します
EISDIRディレクトリですディレクトリには readdir() を使用します
EEXISTすでに存在しますmkdir には recursive: true を使用します

リファレンスを読み込むタイミング

次の場合は references/streams-advanced.md を読み込みます:

  • トランスフォームストリーム
  • 圧縮ストリーム
  • バイナリプロトコル

次の場合は references/performance.md を読み込みます:

  • 大きなファイルの処理
  • メモリの最適化
  • 並行処理

コメント