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.6

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 原生)。

返回值结构

{
    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: [ ... ]          // 子项列表(仅目录有)
}

示例

const tree = await asaifs.readDir('/path/to/dir', {
    filter: (name) => !name.startsWith('.') // 过滤隐藏文件
});

路径与状态操作

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)

重命名文件或目录。

示例

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() 是递归删除,使用前请确认路径正确
  4. readDir() 在出错时返回 null,不会抛出异常

代码分析与优化建议

✅ 已优化项目

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() 透传 opts 参数 ✅

优化内容:统一 opts 参数透传,确保兼容 fs-extra 的所有配置能力。

🔜 待优化项目

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


优化优先级总结

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