- There are many excellent file operation libraries in the community, but they were not quite suitable for me.
- Developing a new wheel doesn't mean the existing ones are bad; it's just about creating something more suited to my needs. I'm not creating a wheel for the sake of it, but rather to have a wheel that fits my requirements better.
- This project also utilizes some excellent community projects, such as filehound.
- I found this tool quite useful in my daily development. It's simple to use, which is why I decided to open-source it, hoping it could help others.
- It's composed entirely of functions, making it easy to use.
- All functions related to path operations are
normalized
, so you don't have to worry about the format of the paths. - As much as possible, recursion is avoided, which helps when dealing with a large number of files and prevents potential stack overflows.
- You can import as needed; it supports
tree-shaking
as anesm
module, so you don't need to worry about the size after packaging. - There is also a
commonjs
(cjs
) version available for those using thecommonjs
specification. - All functions in this project are based on
fs/promises
. - There is only 1 third-party dependency,
hound
, which relies on FileHound for file searching. If you don't need it, you can consider it as0
third-party dependencies.
npm install @kwooshung/files
yarn add @kwooshung/files
pnpm add @kwooshung/files
normalize Normalize Path
import { normalize } from '@kwooshung/files';
normalize('a/b/c'); // a/b/c
normalize('a//b/////c'); // a/b/c
normalize('a\\b\\c'); // a/b/c
exists Check if File or Directory Path Exists
import { exists } from '@kwooshung/files';
async () => {
await exists('a/b/c'); // true
};
exists/not Check if File or Directory Path Does Not Exist
import { notExists } from '@kwooshung/files';
async () => {
await notExists('a/b/c/aNotExists'); // true
};
isFile Check if Path is a File
import { isFile } from '@kwooshung/files';
async () => {
await isFile('a/b/c/test.txt'); // true
};
isDir Check if Path is a Directory
import { isDir } from '@kwooshung/files';
async () => {
await isDir('a/b/c'); // true
};
makeDir Create Directory
import { makeDir } from '@kwooshung/files';
async () => {
await makeDir('a/b/c'); // Create a single directory
await makeDir(['a/b/c/1', 'd/e/f/2']); // Create multiple directories
};
getDir Get Directory
import { getDir } from '@kwooshung/files';
async () => {
await getDir('a/b/c'); // Returns an array of file or directory paths
};
read Read File
import { read } from '@kwooshung/files';
async () => {
await read('a/b/c/test.txt'); // Returns the file content, the encoding is utf8 by default
await read('a/b/c/test.txt', 'utf8'); // Returns the file content, specifying the encoding
};
write Write to File
- The third parameter has a default value of
{ append: false, overwrite: true, encoding: 'utf8' }
- If you are using
ts
, then the types of the third parameterappend
andoverwrite
are mutually exclusive, i.e.: whenappend
istrue
,overwrite
isfalse
, and vice versa.
import { write } from '@kwooshung/files';
async () => {
await write('a/b/c/test.txt', 'hello world'); // true
await write('a/b/c/test.txt', 'hello world', {
append: true, // Append
overwrite: false, // Not Overwrite
encoding: 'utf8' // Specify encoding
}); // true
await write('a/b/c/test.txt', 'hello world', {
append: false, // Not Append
overwrite: true // Overwrite, if the file does not exist, it will be created
encoding: 'utf8' // Specify encoding
}); // true
await write('a/b/c/test.txt', 'hello world', {
append: false, // Not Append
overwrite: true // Overwrite, if the file does not exist, it will be created
}); // true
await write('a/b/c/test.txt', 'hello world', {
encoding: 'binary' // Specify encoding
}); // true
};
copy/file Copy File
As long as no exception is thrown, the copy is successful, even if the target file already exists.
import { copyFile } from '@kwooshung/files';
async () => {
await copyFile('a/b/c/1.txt', 'd/e/f/2.txt'); // 表示覆盖目标文件
await copyFile('a/b/c/1.txt', 'd/e/f/2.txt', false); // 即使目标文件已存在,虽然也不会覆盖,但是表示执行成功
};
copy/dir Copy Directory
- If no exception is thrown, it means the copy was successful, even if the target directory already exists.
- If the target directory exists, it will merge with the source. Identical files will be overwritten, but you can pass
false
as the third argument to prevent overwriting.
import { copyDir } from '@kwooshung/files';
async () => {
await copyDir('a/b/c', 'd/e/f');
await copyDir('a/b/c', 'd/e/f', false);
};
move/file Move File
Moving a file essentially copies the file and then deletes the source file. This approach ensures that if the move fails, the source file remains intact. It's a combination of copy/file
and remove
.
import { moveFile } from '@kwooshung/files';
async () => {
await moveFile('a/b/c/1.txt', 'd/e/f/2.txt'); // 表示覆盖目标文件
await moveFile('a/b/c/1.txt', 'd/e/f/2.txt', false); // 即使目标文件已存在,虽然也不会覆盖,但是表示执行成功
};
move/dir Move Directory
Moving a directory combines the functionalities of copy/dir
and remove
, thus it also follows the merging logic of copy/dir
.
import { moveDir } from '@kwooshung/files';
async () => {
await moveDir('a/b/c', 'd/e/f');
await moveDir('a/b/c', 'd/e/f', false);
};
remove Delete File or Directory
import { remove } from '@kwooshung/files';
async () => {
await remove('a/b/c'); // Delete a single file or directory
await remove(['a/b/c/1', 'd/e/f/2']); // Delete multiple files or directories
};
remove/emptyDirs Delete Empty Directories at a Specified Path
This will delete all empty directories under the specified path, including the path itself.
import { removeEmptyDirs } from '@kwooshung/files';
async () => {
await removeEmptyDirs('a/b/c'); // Delete a single file or directory
await removeEmptyDirs(['a/b/c/1', 'd/e/f/2']); // Delete multiple files or directories
};
size Get the Size of a Specified Path or Multiple Paths
import { size } from '@kwooshung/files';
async () => {
await size('a/b/c'); // [{path: 'a/b/c', size: 1200;}]
await size(['a/b/c', 'd/e/f']); // [{path: 'a/b/c', size: 1200;}, {path: 'd/e/f', size: 2024;}]
};
size/human Convert a Number to a Human-Readable Size
import { sizeHuman } from '@kwooshung/files';
sizeHuman(1024); // {size: '1.25', unit: 'KB'}
sizeHuman(1024, 3); // {size: '1.234', unit: 'KB'}
size/unit Convert the Size of Specified Paths to Human-Readable Format
recommended to use with size
import { sizeUnit } from '@kwooshung/files';
async () => {
await sizeUnit({path: 'a/b/c'; size: 1024;}); // [{path: 'a/b/c', size: '1200', unit: {size: '1.2', unit: 'KB'}}]
await sizeUnit([{path: 'a/b/c'; size: 1024;}, {path: 'd/e/f'; size: 2024;}]); // [{path: 'a/b/c', size: '1200', unit: {size: '1.2', unit: 'KB'}}, {path: 'd/e/f', size: '2024', unit: {size: '2.02', unit: 'KB'}}]
};
hound FileHound
- A wrapper for FileHound, used for creating a new instance of FileHound;
- Also features the capabilities of
FileHound.any
, which means: multipleFileHound
instances can be passed in, and the results will be merged;
import { hound } from '@kwooshung/files';
const fh1 = hound();
fh1
.modified("< 2 days")
.find()
.each(console.log);
const fh2 = FileHound.create();
filehound
.addFilter(customFilter)
.find()
.each(console.log);
hound(fh1, fh2);