npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

asai-nodejs-fs

v0.2.12

Published

asai for you

Readme

asai-nodejs-fs — 文件系统操作插件

功能概述

asai-nodejs-fs 是对 fs-extra 的封装,提供一套完整的异步文件系统操作 API,并在其基础上增加了递归读取目录树结构的功能。

所有方法均返回 Promise,支持 async/await


入口文件 — fs.js

依赖

import fse from 'fs-extra';
import path from 'path';

API 详细说明

基础文件操作

copy(src, dest, opts)

复制文件或目录。

参数: | 参数 | 类型 | 必需 | 默认值 | 说明 | |------|------|------|--------|------| | src | string | ✓ | — | 源路径 | | dest | string | ✓ | — | 目标路径 | | opts | object | ✗ | {} | 配置选项 |

opts 选项: | 属性 | 类型 | 默认值 | 说明 | |------|------|--------|------| | overwrite | boolean | true | 是否覆盖已存在的文件 | | encoding | string | 'utf8' | 编码方式 | | spaces | number | — | JSON 格式化缩进空格数 | | dereference | boolean | false | 是否解除符号链接引用 | | preserveTimestamps | boolean | false | 是否保留文件时间戳 | | filter | function | — | 过滤函数,如 (src, dest) => src.endsWith('.txt') |

示例

await asaifs.copy('/src/dir', '/dest/dir', { overwrite: true });
await asaifs.copy('/src/file.txt', '/dest/file.txt', {
    filter: (src) => src.endsWith('.txt')
});

move(src, dest, opts)

移动文件或目录。

参数: | 参数 | 类型 | 必需 | 说明 | |------|------|------|------| | src | string | ✓ | 源路径 | | dest | string | ✓ | 目标路径 | | opts | object | ✗ | 配置,如 { overwrite: true } |

示例

await asaifs.move('/src/file.txt', '/dest/file.txt', { overwrite: true });

read(src, opts)

读取文件内容。

参数: | 参数 | 类型 | 必需 | 默认值 | 说明 | |------|------|------|--------|------| | src | string | ✓ | — | 文件路径 | | opts | string/object | ✗ | 'utf8' | 编码或配置对象 |

示例

const data = await asaifs.read('/path/to/file.txt');
const binary = await asaifs.read('/path/to/file.bin', { encoding: null });

write(src, data, opts)

创建文件并写入数据(自动创建父目录)。

参数: | 参数 | 类型 | 必需 | 说明 | |------|------|------|------| | src | string | ✓ | 文件路径 | | data | string/Buffer | ✓ | 要写入的数据 | | opts | object | ✗ | 配置选项(编码等) |

示例

await asaifs.write('/new/dir/file.txt', 'Hello World');
await asaifs.write('/new/dir/data.json', JSON.stringify({ key: 'value' }));

remove(src, opts)

删除文件或目录(递归删除)。

参数: | 参数 | 类型 | 必需 | 说明 | |------|------|------|------| | src | string | ✓ | 要删除的路径 | | opts | object | ✗ | 如 { recursive: true, force: true } |

示例

await asaifs.remove('/path/to/delete');

append(src, data, opts)

在文件尾部追加数据。

参数:同 write()

示例

await asaifs.append('/path/to/log.txt', '\n新日志内容');

目录操作

emptyDir(src, opts)

清空目录中的所有内容,保留目录本身。

参数: | 参数 | 类型 | 必需 | 说明 | |------|------|------|------| | src | string | ✓ | 目录路径 | | opts | object | ✗ | 配置选项 |

示例

await asaifs.emptyDir('/path/to/dir');

ensureDir(src, opts)

确保目录存在(不存在则创建)。

示例

await asaifs.ensureDir('/path/to/dir');

ensureFile(src, opts)

确保文件存在(不存在则创建文件及父目录)。

示例

await asaifs.ensureFile('/path/to/new/file.txt');

readDir(src, opts)

递归读取目录树结构(自定义方法,非 fs-extra 原生)。

返回值Promise<object|null> — 成功时为单个树节点对象(非数组);根路径读失败时返回 null(调用方须判空,勿对返回值直接用 .length)。

opts 选项: | 属性 | 类型 | 说明 | |------|------|------| | filter | (name: string) => boolean | 仅接收 basename(不含路径);返回 false 则跳过该项 | | _visited | Set<string> | 内部用,已访问 realpath,防 symlink 环 |

返回值结构(成功时):

{
    name: 'dirname',           // 目录/文件名
    size: 1024,                // 文件大小(字节)
    time: '2024-01-01T00:00:00.000Z', // 修改时间
    path: '/absolute/path',    // 标准化绝对路径
    type: 'dir' | '.txt' | 'file', // 类型
    created: '2024-01-01T00:00:00.000Z', // 创建时间
    permissions: '755',        // 权限
    children: [ ... ]          // 子项列表(仅目录有;symlink 环节点为 null 会被滤掉)
}

示例

const tree = await asaifs.readDir('/path/to/dir', {
    filter: (name) => !name.startsWith('.') // name 仅为 basename,如 '.gitignore'
});
if (!tree) { /* 目录不存在或无权限 */ }

路径与状态操作

pathExists(src, opts)

检查路径是否存在。

示例

const exists = await asaifs.pathExists('/path/to/file');

stat(src, opts)

获取文件/目录的状态信息。

返回值fs.Stats 对象,包含 sizemtimebirthtimemode 等。

示例

const stats = await asaifs.stat('/path/to/file');
console.log(stats.size, stats.mtime);

rename(oldPath, newPath, opts)

重命名文件或目录。Node/fs-extra 的 rename 无 opts;第三参 _opts 仅为 API 兼容,不会透传

示例

await asaifs.rename('/old/name.txt', '/new/name.txt');

JSON 操作

readJson(src, opts)

读取 JSON 文件并解析为 JavaScript 对象。

示例

const data = await asaifs.readJson('/path/to/config.json');

writeJson(src, data, opts)

将 JavaScript 对象写入 JSON 文件。

opts 选项: | 属性 | 类型 | 默认值 | 说明 | |------|------|--------|------| | spaces | number | — | 缩进空格数,如 2 | | replacer | function/array | — | JSON 序列化过滤器 | | EOL | string | 系统默认 | 行结束符 |

示例

await asaifs.writeJson('/path/to/config.json', { key: 'value' }, { spaces: 2 });

属性

fse

导出原始的 fs-extra 模块,用于需要直接访问的场景。

const fse = asaifs.fse;

使用示例

// 初始化
const asaifs = require('./fs');

// 读写配置文件
async function loadConfig() {
    const exists = await asaifs.pathExists('./config.json');
    if (exists) {
        return await asaifs.readJson('./config.json');
    }
    return { version: '1.0' };
}

// 递归备份目录
async function backup(src, dest) {
    await asaifs.emptyDir(dest);
    await asaifs.copy(src, dest, { preserveTimestamps: true });
}

// 日志追加
async function log(message) {
    const timestamp = new Date().toISOString();
    await asaifs.append('./app.log', `${timestamp}: ${message}\n`);
}

// 读取目录树
const tree = await asaifs.readDir('./src');

注意事项

  1. 所有路径使用 path.normalize() 标准化处理
  2. write()ensureFile() 会自动创建缺失的父目录
  3. remove() 是递归删除,使用前请确认路径正确(不接受 opts,直接调用 fse.remove(src)
  4. readDir() 在出错时返回 null,不会抛出异常
  5. readDir() 遇 symlink 环时跳过该节点(返回 null),避免无限递归
  6. readDir()filter 回调参数为 basename,不是完整路径

测试

单元测试目录:.youmengyu/test/unit/webserver/plugs/asai-nodejs-fs/

# 仓库根目录
node .youmengyu/test/asaitest.mjs --no-open

# 或单文件
npx tsx --test .youmengyu/test/unit/webserver/plugs/asai-nodejs-fs/<file>.test.mjs

代码分析与优化建议

✅ 已优化项目

1. readDir() 递归读取改为并发 ✅

优化内容:使用 Promise.all 并发读取子目录,大目录(1000+ 子项目)下速度提升 3-5 倍。

// 优化前:串行
for (const item of items) {
    result.children.push(await readDir(fullPath, opts));
}
// 优化后:并发
const children = await Promise.all(
    items.filter(item => !opts.filter || opts.filter(item))
        .map(item => readDir(path.join(src, item), opts))
);

2. 错误处理增强 ✅

优化内容readDir() 错误时输出具体路径和错误信息,便于区分"目录不存在"和"权限不足"。

3. remove() 简化为直接返回 fse.remove() ✅

优化内容:去掉多余的 Promise 包装,直接返回 fse.remove(src, opts)(fs-extra 已返回 Promise)。

4. rename() 第三参 API 兼容 ✅

说明:保留 _opts 签名以兼容旧调用,但不透传(Node fs.rename 无 opts)。

🔜 待优化项目

| 优先级 | 问题 | 状态 | |--------|------|------| | 🟢 P2 | 缺乏文件流支持(createReadStream / createWriteStream) | 待处理 | | 🟡 P1 | 路径遍历安全风险(路径白名单校验) | 待处理 |


优化优先级总结

| 优先级 | 问题 | 影响 | |--------|------|------| | ✅ 已修复 | readDir 性能/错误处理/symlink 环、remove 简化 | 功能效率/可调试性 | | 🟡 P1 | 路径穿越风险 | 安全性 | | 🟢 P2 | 流支持 | 功能完整性 |