Skip to content

kwooshung/Files

Repository files navigation

@kwooshung/files

GitHub Release Date - Published_At GitHub last commit GitHub code size in bytes GitHub top language GitHub pull requests GitHub issues NPM Version Npm.js Downloads/Week Github CI/CD codecov Maintainability GitHub License Gitee Repo Github Stars

中文 | English

Why Develop It?

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

Why Use It?

  • 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 an esm module, so you don't need to worry about the size after packaging.
  • There is also a commonjs (cjs) version available for those using the commonjs 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 as 0 third-party dependencies.

Install

npm

npm install @kwooshung/files

yarn

yarn add @kwooshung/files

pnpm

pnpm add @kwooshung/files

Functions

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 parameter append and overwrite are mutually exclusive, i.e.: when append is true, overwrite is false, 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: multiple FileHound 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);