npm install @rush-fs/core
# or
pnpm add @rush-fs/core安装 @rush-fs/core 时,包管理器会通过 optionalDependencies 自动安装当前平台的本地绑定(例如 macOS ARM 上的 @rush-fs/rush-fs-darwin-arm64)。若未安装可能报错「Cannot find native binding」:
- 删除
node_modules和锁文件(package-lock.json或pnpm-lock.yaml)后重新执行pnpm install(或npm i)。 - 或手动安装对应平台包:
macOS ARM:pnpm add @rush-fs/rush-fs-darwin-arm64
macOS x64:pnpm add @rush-fs/rush-fs-darwin-x64
Windows x64:pnpm add @rush-fs/rush-fs-win32-x64-msvc
Linux x64 (glibc):pnpm add @rush-fs/rush-fs-linux-x64-gnu
从 rush-fs 迁移: 主包更名为 @rush-fs/core,详见 CHANGELOG.md。
import { readdir, stat, readFile, writeFile, mkdir, rm } from '@rush-fs/core'
// 读取目录
const files = await readdir('./src')
// 递归 + 返回文件类型
const entries = await readdir('./src', {
recursive: true,
withFileTypes: true,
})
// 读写文件
const content = await readFile('./package.json', { encoding: 'utf8' })
await writeFile('./output.txt', 'hello world')
// 文件信息
const s = await stat('./package.json')
console.log(s.size, s.isFile())
// 创建目录
await mkdir('./new-dir', { recursive: true })
// 删除
await rm('./temp', { recursive: true, force: true })测试环境:Apple Silicon (arm64),Node.js 24.0.2,release 构建(开启 LTO)。 运行
pnpm build && pnpm bench可复现。
这些场景中 Rust 的并行遍历和零拷贝 I/O 发挥了真正优势:
| 场景 | Node.js | Rush-FS | 提效 |
|---|---|---|---|
readdir 递归(node_modules,约 3 万条目) |
281 ms | 23 ms | 12x |
copyFile 4 MB |
4.67 ms | 0.09 ms | 50x |
readFile 4 MB utf8 |
1.86 ms | 0.92 ms | 2x |
readFile 64 KB utf8 |
42 µs | 18 µs | 2.4x |
rm 2000 个文件(4 线程) |
92 ms | 53 ms | 1.75x |
access R_OK(目录) |
4.18 µs | 1.55 µs | 2.7x |
cp 500 文件平铺目录(4 线程) |
86.45 ms | 32.88 ms | 2.6x |
cp 树形目录 ~363 节点(4 线程) |
108.73 ms | 46.88 ms | 2.3x |
glob 大树(node_modules/**/*.json,约 3 万条目)vs fast-glob |
303 ms | 30 ms | 10x |
单文件操作有约 0.3 µs 的 napi 桥接开销。大树下 glob 见上表;小/中树下 node-glob 更快(见「Node.js 更快的场景」)。
| 场景 | Node.js | Rush-FS | 比率 |
|---|---|---|---|
stat(单文件) |
1.45 µs | 1.77 µs | 1.2x |
readFile 小文件(Buffer) |
8.86 µs | 9.46 µs | 1.1x |
writeFile 小文件(string) |
74 µs | 66 µs | 0.9x |
writeFile 小文件(Buffer) |
115 µs | 103 µs | 0.9x |
appendFile |
30 µs | 27 µs | 0.9x |
极轻量级的内置调用 napi 开销占主导;小/中规模目录树下 node-glob 比 Rush-FS 的 glob 更快:
| 场景 | Node.js | Rush-FS | 说明 |
|---|---|---|---|
existsSync(已存在文件) |
444 ns | 1.34 µs | Node.js 内部有 fast path |
accessSync F_OK |
456 ns | 1.46 µs | 同上——napi 开销占主导 |
writeFile 4 MB string |
2.93 ms | 5.69 ms | 大字符串跨 napi 桥传输 |
glob 递归(**/*.rs,小树)vs node-glob |
22 ms | 40 ms | 此规模下 node-glob 更快 |
Rush-FS 在文件系统遍历类操作中使用多线程并行:
| API | 并行库 | concurrency 选项 |
默认值 |
|---|---|---|---|
readdir(递归) |
jwalk | ✅ | auto |
glob |
ignore | ✅ | 4 |
rm(递归) |
rayon | ✅ | 1 |
cp(递归) |
rayon | ✅ | 1 |
单文件操作(stat、readFile、writeFile、chmod 等)是原子系统调用,不适用并行化。
Rush-FS 在递归/批量文件系统操作上表现卓越(readdir、glob、rm、cp),Rust 的并行遍历器带来多倍加速(如 readdir 12x、copyFile 50x)。单文件操作与 Node.js 基本持平。napi 桥接带来固定约 0.3 µs 的每次调用开销,仅在亚微秒级操作(如 existsSync)中有感知。
cp 基准详情(Apple Silicon,release 构建):
| 场景 | Node.js | Rush-FS 1 线程 | Rush-FS 4 线程 | Rush-FS 8 线程 |
|---|---|---|---|---|
| 平铺目录(500 文件) | 86.45 ms | 61.56 ms | 32.88 ms | 36.67 ms |
| 树形目录(宽度=4,深度=3,~84 节点) | 23.80 ms | 16.94 ms | 10.62 ms | 9.76 ms |
| 树形目录(宽度=3,深度=5,~363 节点) | 108.73 ms | 75.39 ms | 46.88 ms | 46.18 ms |
cp 的最优并发数在 Apple Silicon 上为 4 线程——超过后受 I/O 带宽限制,收益趋于平稳。
以 readdir 为例:Node.js 在原生层串行执行目录读取,每条结果都在 V8 主线程上转成 JS 字符串,带来 GC 压力:
graph TD
A["JS: readdir"] -->|调用| B("Node.js C++ 绑定")
B -->|提交任务| C{"Libuv 线程池"}
subgraph "原生层(串行)"
C -->|"系统调用: getdents"| D[系统内核]
D -->|"返回文件列表"| C
C -->|"处理路径"| C
end
C -->|"结果就绪"| E("V8 主线程")
subgraph "V8 交互(较重)"
E -->|"创建 JS 字符串 1"| F[V8 堆]
E -->|"字符串 2"| F
E -->|"字符串 N…"| F
F -->|"GC 压力上升"| F
end
E -->|"返回数组"| G["JS 回调/Promise"]
以 readdir 为例,Rush-FS 把热路径留在 Rust:先构建 Vec<String>(递归时用 Rayon 并行遍历),再一次性交给 JS,遍历过程中不逐条进 V8:
graph TD
A["JS: readdir"] -->|"N-API 调用"| B("Rust 封装")
B -->|"派发任务"| C{"Rust(递归时为 Rayon 线程池)"}
subgraph "Rust「黑盒」"
C -->|"系统调用: getdents"| D[系统内核]
D -->|"返回文件列表"| C
C -->|"存入 Rust Vec<String>"| H[Rust 堆]
H -->|"尚未进 V8"| H
end
C -->|"全部完成"| I("转为 JS")
subgraph "N-API 桥"
I -->|"批量创建 JS 数组"| J[V8 堆]
end
J -->|返回| K["JS 结果"]
其它提效来源:递归 readdir 使用 jwalk + Rayon 并行遍历目录;cp、rm(递归)可通过 Rayon 并行遍历目录树并做 I/O;glob 支持多线程。整体上,热路径在 Rust、结果一次性(或批量)交给 JS,相比 Node 的 C++ binding 减少了反复进出 V8 与 GC 的开销。
我们正在逐个重写 fs 的 API。
图例
- ✅:完全支持
- 🚧:部分支持 / 开发中
- ✨:@rush-fs/core 的新增能力
- ❌:暂未支持
- Node.js 参数:
path: string; // ✅ options?: { encoding?: string; // 🚧(默认 'utf8';'buffer' 暂不支持) withFileTypes?: boolean; // ✅ recursive?: boolean; // ✅ concurrency?: number; // ✨ };
- 返回类型:
string[] | { name: string, // ✅ parentPath: string, // ✅ isDir: boolean // ✅ }[]
- Node.js 参数:
path: string; // ✅ options?: { encoding?: string; // ✅ (utf8, ascii, latin1, base64, base64url, hex) flag?: string; // ✅ (r, r+, w+, a+ 等) };
- 返回类型:
string | Buffer
- Node.js 参数:
path: string; // ✅ data: string | Buffer; // ✅ options?: { encoding?: string; // ✅ (utf8, ascii, latin1, base64, base64url, hex) mode?: number; // ✅ flag?: string; // ✅ (w, wx, a, ax) };
- Node.js 参数:
path: string; // ✅ data: string | Buffer; // ✅ options?: { encoding?: string; // ✅ (utf8, ascii, latin1, base64, base64url, hex) mode?: number; // ✅ flag?: string; // ✅ };
- Node.js 参数:
src: string; // ✅ dest: string; // ✅ mode?: number; // ✅ (COPYFILE_EXCL)
- Node.js 参数(Node 16.7+):
src: string; // ✅ dest: string; // ✅ options?: { recursive?: boolean; // ✅ force?: boolean; // ✅(默认 true) errorOnExist?: boolean; // ✅ preserveTimestamps?: boolean; // ✅ dereference?: boolean; // ✅ verbatimSymlinks?: boolean; // ✅ concurrency?: number; // ✨ };
- Node.js 参数:
path: string; // ✅ options?: { recursive?: boolean; // ✅ mode?: number; // ✅ };
- 返回类型:
string | undefined(recursive 模式下返回首个创建的路径)
- Node.js 参数:
path: string; // ✅ options?: { force?: boolean; // ✅ maxRetries?: number; // ✅ recursive?: boolean; // ✅ retryDelay?: number; // ✅(默认 100ms) concurrency?: number; // ✨ };
- Node.js 参数:
path: string // ✅
- Node.js 参数:
path: string // ✅
- 返回类型:
Stats- 数值字段:
dev,mode,nlink,uid,gid,rdev,blksize,ino,size,blocks,atimeMs,mtimeMs,ctimeMs,birthtimeMs - Date 字段:
atime,mtime,ctime,birthtime→Date对象 ✅ - 方法:
isFile(),isDirectory(),isSymbolicLink(), ...
- 数值字段:
- 错误区分:
ENOENTvsEACCES✅
- Node.js 参数:
path: string // ✅
- 返回类型:
Stats
- 状态:❌
- Node.js 参数:
path: string; // ✅ mode?: number; // ✅ (F_OK, R_OK, W_OK, X_OK)
- Node.js 参数:
path: string // ✅
- 返回类型:
boolean
- 状态:❌
- 状态:❌
- 状态:❌
- Node.js 参数:
path: string // ✅
- Node.js 参数:
oldPath: string // ✅ newPath: string // ✅
- Node.js 参数:
path: string // ✅
- 返回类型:
string
- Node.js 参数:
path: string // ✅
- 返回类型:
string
- Node.js 参数:
path: string // ✅ mode: number // ✅
- Node.js 参数:
path: string // ✅ uid: number // ✅ gid: number // ✅
- Node.js 参数:
path: string // ✅ atime: number // ✅ mtime: number // ✅
- Node.js 参数:
path: string; // ✅ len?: number; // ✅
- Node.js 参数:
pattern: string; // ✅ options?: { cwd?: string; // ✅ withFileTypes?: boolean; // ✅ exclude?: string[]; // ✅ concurrency?: number; // ✨ gitIgnore?: boolean; // ✨ 默认 false,与 Node.js fs.globSync 一致 };
- Node.js 参数:
target: string // ✅ path: string // ✅ type?: 'file' | 'dir' | 'junction' // ✅(仅 Windows 有效,Unix 忽略)
- Node.js 参数:
existingPath: string // ✅ newPath: string // ✅
- Node.js 参数:
prefix: string // ✅
- 返回类型:
string - 使用系统随机源(Unix:
/dev/urandom,Windows:BCryptGenRandom),最多重试 10 次 ✅
- 状态:❌
各版本变更见 CHANGELOG.md。发布 tag 列表见 GitHub Releases。
参阅 CONTRIBUTING-CN.md 获取完整开发指南:环境搭建、参考 Node.js 源码、编写 Rust 实现、测试与性能基准。
发布由 Release 工作流 完成:在 macOS(x64/arm64)、Windows、Linux 上构建原生二进制,并发布各平台包与主包到 npm。
- Secrets: 在仓库 Settings → Secrets and variables → Actions 中添加 NPM_TOKEN(npm Classic 或 Automation token,需具备 Publish 权限)。
- 发布: 在 Actions → Release → Run workflow 中手动运行(使用当前
main上的package.json版本),或先更新package.json与Cargo.toml中的版本号并推送到main,再创建并推送 tag:git tag v<版本号> && git push origin v<版本号>。 - 更新日志: 发布前或发布后更新 CHANGELOG.md(将
[Unreleased]下的条目移到新版本标题下并补充 compare 链接)。
工作流会自动注入 optionalDependencies 并发布所有包,无需在 package.json 中手动填写。
MIT