Code Reader
首页
帮助
设计文档
首页
帮助
设计文档
  • FileWatcher

FileWatcher

FileWatcher 是 OpenCode 的跨平台文件监听系统,用于监控项目文件和 Git 仓库的变更。

概述

FileWatcher 使用 @parcel/watcher 库提供跨平台的文件监听能力,支持 macOS 的 fs-events、Linux 的 inotify 和 Windows 的原生文件系统 API。它可以实时检测文件的创建、修改和删除,并通过事件总线通知其他组件。

定义位置

packages/opencode/src/file/watcher.ts

主要组件

Event.Updated

文件更新事件。

类型:

{
  file: string,      // 文件路径
  event: "add" | "change" | "unlink"  // 事件类型
}

位置: packages/opencode/src/file/watcher.ts:26

init()

初始化文件监听器。

工作流程:

  1. 检查 OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER 标志
  2. 使用懒加载模式实例化 watcher
  3. 为项目目录和 Git 目录创建订阅

位置: packages/opencode/src/file/watcher.ts:121

平台支持

FileWatcher 根据操作系统自动选择最佳的后端:

平台后端说明
macOSfs-events使用 macOS FSEvents API
Linuxinotify使用 Linux inotify API
Windowswindows使用 Windows ReadDirectoryChangesW

平台检测:

const backend = (() => {
  if (process.platform === "win32") return "windows"
  if (process.platform === "darwin") return "fs-events"
  if (process.platform === "linux") return "inotify"
})()

位置: packages/opencode/src/file/watcher.ts:52

监听的目录

1. 项目目录

  • 条件: OPENCODE_EXPERIMENTAL_FILEWATCHER 标志启用
  • 监听: Instance.directory(通常是项目根目录)
  • 忽略模式: FileIgnore.PATTERNS + 用户配置的 config.watcher.ignore

位置: packages/opencode/src/file/watcher.ts:78

2. Git 目录

  • 条件: 项目使用 Git 版本控制
  • 监听: .git/HEAD 文件(检测分支变更)
  • 忽略模式: Git 目录内容(除了 HEAD)

位置: packages/opencode/src/file/watcher.ts:91

事件类型

事件名Parcel 类型文件事件类型说明
addcreateadd文件创建
changeupdatechange文件修改
unlinkdeleteunlink文件删除

事件映射:

const subscribe = (err, evts) => {
  if (err) return
  for (const evt of evts) {
    if (evt.type === "create") Bus.publish(Event.Updated, { file: evt.path, event: "add" })
    if (evt.type === "update") Bus.publish(Event.Updated, { file: evt.path, event: "change" })
    if (evt.type === "delete") Bus.publish(Event.Updated, { file: evt.path, event: "unlink" })
  }
}

文件忽略模式

内置忽略模式

位置: packages/opencode/src/file/ignore.ts

export const PATTERNS = [
  // 文件夹
  "node_modules",
  "bower_components",
  ".pnpm-store",
  "vendor",
  ".npm",
  "dist",
  "build",
  "out",
  ".next",
  "target",
  "bin",
  "obj",
  ".git",
  ".svn",
  ".hg",
  ".vscode",
  ".idea",
  ".turbo",
  ".output",
  "desktop",
  ".sst",
  ".cache",
  ".webkit-cache",
  "__pycache__",
  ".pytest_cache",
  "mypy_cache",
  ".history",
  ".gradle",

  // 文件 glob 模式
  "**/*.swp",
  "**/*.swo",
  "**/*.pyc",
  "**/.DS_Store",
  "**/Thumbs.db",
  "**/logs/**",
  "**/tmp/**",
  "**/temp/**",
  "**/*.log",
  "**/coverage/**",
  "**/.nyc_output/**",
]

用户配置

用户可以在配置中添加自定义忽略模式:

{
  watcher: {
    ignore: ["node_modules", "*.log", "custom-dir/**"]
  }
}

位置: packages/opencode/src/config/config.ts:897

配置标志

环境变量

变量名说明
OPENCODE_EXPERIMENTAL_FILEWATCHER启用项目目录监听(实验性)
OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER完全禁用文件监听

配置选项

{
  watcher: {
    ignore: string[]  // 自定义忽略模式
  }
}

集成点

1. 项目初始化

位置: packages/opencode/src/project/bootstrap.ts:24

import { FileWatcher } from "@/file/watcher"

export async function bootstrap() {
  // ...
  FileWatcher.init()
  // ...
}

2. Git 分支检测

位置: packages/opencode/src/project/vcs.ts:49

Bus.subscribe(FileWatcher.Event.Updated, (evt) => {
  if (evt.file.endsWith("/.git/HEAD")) {
    // 检测到分支变更
    const newBranch = await getCurrentBranch()
    Bus.publish(VCS.Event.BranchUpdated, { branch: newBranch })
  }
})

3. 程序化文件变更

位置: packages/opencode/src/tool/apply_patch.ts:230

当通过工具修改文件时,手动发布更新事件:

await Bus.publish(FileWatcher.Event.Updated, {
  file: filePath,
  event: "change",
})

4. Web UI 自动重载

位置: packages/app/src/context/file.tsx:356

Bus.subscribe(FileWatcher.Event.Updated, (evt) => {
  if (evt.file.startsWith(".git/")) return // 跳过 Git 目录

  // 自动重新加载文件内容
  reloadFile(evt.file)
})

TypeScript 类型定义

export namespace FileWatcher {
  export const Event = {
    Updated: BusEvent.define(
      "file.watcher.updated",
      z.object({
        file: z.string(),
        event: z.union([z.literal("add"), z.literal("change"), z.literal("unlink")]),
      }),
    ),
  }

  export function init(): void
}

典型使用场景

1. 监听文件变更

import { FileWatcher } from "@/file/watcher"
import { Bus } from "@/bus"

Bus.subscribe(FileWatcher.Event.Updated, (evt) => {
  console.log(`文件 ${evt.file} 被修改`)
  console.log(`操作类型: ${evt.event}`)

  if (evt.event === "change") {
    // 处理文件修改
    handleFileChange(evt.file)
  } else if (evt.event === "add") {
    // 处理文件创建
    handleFileAdd(evt.file)
  } else if (evt.event === "unlink") {
    // 处理文件删除
    handleFileDelete(evt.file)
  }
})

2. 启用实验性文件监听

# 启用项目目录监听
export OPENCODE_EXPERIMENTAL_FILEWATCHER=1
bun dev

3. 禁用文件监听

# 完全禁用文件监听
export OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER=1
bun dev

4. 配置自定义忽略模式

// 在配置文件中
{
  "watcher": {
    "ignore": [
      "node_modules",
      "*.log",
      "build/**",
      "dist/**",
      ".cache/**"
    ]
  }
}

5. 检测 Git 分支变更

import { FileWatcher } from "@/file/watcher"
import { Bus } from "@/bus"

Bus.subscribe(FileWatcher.Event.Updated, (evt) => {
  // 检测 Git HEAD 文件变更(分支切换)
  if (evt.file.endsWith("/.git/HEAD")) {
    const currentBranch = await getCurrentBranch()
    console.log(`Git 分支已切换到: ${currentBranch}`)
  }
})

6. 手动发布文件变更事件

import { FileWatcher } from "@/file/watcher"
import { Bus } from "@/bus"

// 当程序化修改文件时
await writeFileSync("/path/to/file.js", content)

// 发布更新事件以通知其他组件
Bus.publish(FileWatcher.Event.Updated, {
  file: "/path/to/file.js",
  event: "change",
})

工作流程

初始化流程

项目启动
  ↓
project/bootstrap.ts
  ↓
FileWatcher.init()
  ├─ 检查是否禁用
  ├─ 懒加载 @parcel/watcher
  ├─ 检测平台后端
  ├─ 创建项目目录订阅(如果启用实验性标志)
  ├─ 创建 Git 目录订阅
  └─ 设置事件回调

事件处理流程

文件系统变更
  ↓
@parcel/watcher 检测
  ↓
发布到回调
  ↓
映射到 OpenCode 事件类型
  ↓
Bus.publish("file.watcher.updated")
  ↓
订阅者接收事件
  ├─ VCS: 检测分支变更
  ├─ Web UI: 自动重载文件
  └─ 其他组件处理

性能考虑

超时设置

订阅操作有 10 秒超时:

const SUBSCRIBE_TIMEOUT_MS = 10_000

位置: packages/opencode/src/file/watcher.ts:18

如果订阅超时,会自动清理并记录错误。

懒加载

平台特定的原生绑定使用懒加载:

const watcher = lazy((): typeof import("@parcel/watcher") | undefined => {
  try {
    const binding = require(`@parcel/watcher-${process.platform}-${process.arch}...`)
    return createWrapper(binding)
  } catch (error) {
    log.error("failed to load watcher binding", { error })
    return undefined
  }
})

这确保只有在需要时才加载绑定,减少启动时间。

清理

当实例销毁时,自动取消所有订阅:

;async (state) => {
  if (!state.subs) return
  await Promise.all(state.subs.map((sub) => sub?.unsubscribe()))
}

对象关系

架构概览

依赖项

核心依赖

  • @parcel/watcher (v2.5.1): 跨平台文件监听库

平台特定绑定

根据操作系统和架构自动加载:

  • @parcel/watcher-darwin-arm64
  • @parcel/watcher-darwin-x64
  • @parcel/watcher-linux-x64-glibc
  • @parcel/watcher-linux-x64-musl
  • @parcel/watcher-win32-x64

相关文档

  • Storage - 存储架构
  • Events - 事件总线
  • Project - 项目对象

变更历史

版本变更内容日期
v1初始 FileWatcher 架构-
v1.1添加 Git 分支检测-
v1.2添加实验性标志和配置-