From eb95f36dea2a7cebb32d46038cc99b455fb734cd Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Mon, 23 Dec 2024 00:56:31 +0800 Subject: [PATCH] feat: support fs.exists async function (#65) ## Summary by CodeRabbit - **New Features** - Added a badge in the `README.md` to welcome pull requests. - Introduced a new asynchronous function `exists` to check for file existence. - Expanded the module's public API by exporting all functionalities from the `fs.js` module. - **Tests** - Added unit tests for the `exists` function, covering various scenarios including file existence checks and error handling. --- .github/workflows/nodejs.yml | 1 - README.md | 1 + src/fs.ts | 17 ++++++++++++ src/index.ts | 1 + test/fs.test.ts | 54 ++++++++++++++++++++++++++++++++++++ 5 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 src/fs.ts create mode 100644 test/fs.test.ts diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 8e62f3d..97d3f98 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -11,7 +11,6 @@ jobs: name: Node.js uses: node-modules/github-actions/.github/workflows/node-test.yml@master with: - os: 'ubuntu-latest' version: '16, 18, 20, 22, 23' secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/README.md b/README.md index 7869776..f47ce48 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![Test coverage][codecov-image]][codecov-url] [![npm download][download-image]][download-url] [![Node.js Version](https://img.shields.io/node/v/utility.svg?style=flat)](https://nodejs.org/en/download/) +[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://makeapullrequest.com) [npm-image]: https://img.shields.io/npm/v/utility.svg?style=flat-square [npm-url]: https://npmjs.org/package/utility diff --git a/src/fs.ts b/src/fs.ts new file mode 100644 index 0000000..d1e3a87 --- /dev/null +++ b/src/fs.ts @@ -0,0 +1,17 @@ +import { Stats } from 'node:fs'; +import { stat } from 'node:fs/promises'; + +/** + * Check if a file exists. + * Returns the file stats if it exists, or `false` if it doesn't. + */ +export async function exists(file: string): Promise { + try { + return await stat(file); + } catch (err: any) { + if (err.code === 'ENOENT') { + return false; + } + throw err; + } +} diff --git a/src/index.ts b/src/index.ts index aecde29..ee2c139 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,3 +9,4 @@ export * from './string.js'; export * from './optimize.js'; export * from './object.js'; export * from './timeout.js'; +export * from './fs.js'; diff --git a/test/fs.test.ts b/test/fs.test.ts new file mode 100644 index 0000000..98c0370 --- /dev/null +++ b/test/fs.test.ts @@ -0,0 +1,54 @@ +import { strict as assert } from 'node:assert'; +import path from 'node:path'; +import { Stats } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import * as utility from '../src/index.js'; +import { exists } from '../src/index.js'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +describe('test/fs.test.ts', () => { + describe('exists()', () => { + it('should work', async () => { + let stats = await exists(__filename); + assert(stats instanceof Stats); + assert(stats.size > 0, 'stats.size > 0'); + assert.equal(stats.isFile(), true); + assert.equal(stats.isDirectory(), false); + + stats = await utility.exists(__dirname); + assert(stats instanceof Stats); + // assert(stats.size > 0, 'stats.size > 0'); + assert.equal(stats.isDirectory(), true); + assert.equal(stats.isFile(), false); + assert.equal(await exists(__dirname + '/nonexistent'), false); + }); + + it('should throw error on Linux', async () => { + if (process.platform !== 'linux') { + return; + } + await assert.rejects(async () => { + await exists('/root/../../../../../etc/passwd'); + }, (err: any) => { + // Error: EACCES: permission denied, stat '/root/../../../../../etc/passwd' + assert.equal(err.code, 'EACCES'); + return true; + }); + }); + + it.skip('should throw error on win32', async () => { + if (process.platform !== 'win32') { + return; + } + await assert.rejects(async () => { + await exists('C:\\Windows\\System32\\drivers\\etc\\hosts'); + }, (err: any) => { + // Error: EACCES: permission denied, stat 'C:\Windows\System32\drivers\etc\hosts' + assert.equal(err.code, 'EPERM'); + return true; + }); + }); + }); +});