Skip to content

Commit eb95f36

Browse files
authored
feat: support fs.exists async function (#65)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## 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. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 153e3ff commit eb95f36

File tree

5 files changed

+73
-1
lines changed

5 files changed

+73
-1
lines changed

.github/workflows/nodejs.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ jobs:
1111
name: Node.js
1212
uses: node-modules/github-actions/.github/workflows/node-test.yml@master
1313
with:
14-
os: 'ubuntu-latest'
1514
version: '16, 18, 20, 22, 23'
1615
secrets:
1716
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
[![Test coverage][codecov-image]][codecov-url]
66
[![npm download][download-image]][download-url]
77
[![Node.js Version](https://img.shields.io/node/v/utility.svg?style=flat)](https://nodejs.org/en/download/)
8+
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://makeapullrequest.com)
89

910
[npm-image]: https://img.shields.io/npm/v/utility.svg?style=flat-square
1011
[npm-url]: https://npmjs.org/package/utility

src/fs.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Stats } from 'node:fs';
2+
import { stat } from 'node:fs/promises';
3+
4+
/**
5+
* Check if a file exists.
6+
* Returns the file stats if it exists, or `false` if it doesn't.
7+
*/
8+
export async function exists(file: string): Promise<Stats | false> {
9+
try {
10+
return await stat(file);
11+
} catch (err: any) {
12+
if (err.code === 'ENOENT') {
13+
return false;
14+
}
15+
throw err;
16+
}
17+
}

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ export * from './string.js';
99
export * from './optimize.js';
1010
export * from './object.js';
1111
export * from './timeout.js';
12+
export * from './fs.js';

test/fs.test.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { strict as assert } from 'node:assert';
2+
import path from 'node:path';
3+
import { Stats } from 'node:fs';
4+
import { fileURLToPath } from 'node:url';
5+
import * as utility from '../src/index.js';
6+
import { exists } from '../src/index.js';
7+
8+
const __filename = fileURLToPath(import.meta.url);
9+
const __dirname = path.dirname(__filename);
10+
11+
describe('test/fs.test.ts', () => {
12+
describe('exists()', () => {
13+
it('should work', async () => {
14+
let stats = await exists(__filename);
15+
assert(stats instanceof Stats);
16+
assert(stats.size > 0, 'stats.size > 0');
17+
assert.equal(stats.isFile(), true);
18+
assert.equal(stats.isDirectory(), false);
19+
20+
stats = await utility.exists(__dirname);
21+
assert(stats instanceof Stats);
22+
// assert(stats.size > 0, 'stats.size > 0');
23+
assert.equal(stats.isDirectory(), true);
24+
assert.equal(stats.isFile(), false);
25+
assert.equal(await exists(__dirname + '/nonexistent'), false);
26+
});
27+
28+
it('should throw error on Linux', async () => {
29+
if (process.platform !== 'linux') {
30+
return;
31+
}
32+
await assert.rejects(async () => {
33+
await exists('/root/../../../../../etc/passwd');
34+
}, (err: any) => {
35+
// Error: EACCES: permission denied, stat '/root/../../../../../etc/passwd'
36+
assert.equal(err.code, 'EACCES');
37+
return true;
38+
});
39+
});
40+
41+
it.skip('should throw error on win32', async () => {
42+
if (process.platform !== 'win32') {
43+
return;
44+
}
45+
await assert.rejects(async () => {
46+
await exists('C:\\Windows\\System32\\drivers\\etc\\hosts');
47+
}, (err: any) => {
48+
// Error: EACCES: permission denied, stat 'C:\Windows\System32\drivers\etc\hosts'
49+
assert.equal(err.code, 'EPERM');
50+
return true;
51+
});
52+
});
53+
});
54+
});

0 commit comments

Comments
 (0)