diff --git a/src/lsfnd.ts b/src/lsfnd.ts index 9824bfa..5dd7755 100644 --- a/src/lsfnd.ts +++ b/src/lsfnd.ts @@ -23,6 +23,8 @@ import type { LsTypesValues } from '../types'; +type Unpack = A extends Array<(infer U)> ? U : A; + /** * Converts a file URL to a file path. * @@ -37,8 +39,8 @@ import type { * or a string representing a file URL and must starts with `"file:"` * protocol. * @returns A string representing the corresponding file path. - * @throws {URIError} If the URL is not a valid file URL or if it contains - * unsupported formats. + * @throws {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/URIError **URIError**} - + * If the URL is not a valid file URL or if it contains unsupported formats. * * @example * // Convert a file URL to a file path @@ -63,6 +65,50 @@ function fileUrlToPath(url: URL | string): string { return (url instanceof URL) ? url.pathname : url.replace(/^file:/, ''); } +/** + * Checks if a provided type matches any of the allowed types. + * + * This function verifies if a provided `type` argument matches any of the + * allowed types specified in the `validTypes` array. It throws a `TypeError` + * if the `type` doesn't match any valid type. + * + * @param type - The type value to be checked. + * @param validTypes - An array containing the allowed types for the `type` parameter. + * + * @throws {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/TypeError **TypeError**} - + * If the provided `type` doesn't match any of the valid types. + * + * @since 1.0.0 + * @internal + */ +function checkType( + type: lsTypes | LsTypesKeys | LsTypesValues | N, + validTypes: Array<(string | number | N)> +): void { + function joinAll(arr: (typeof validTypes), delim: string): string { + let str: string = ''; + arr.forEach((e: Unpack<(typeof validTypes)>, i: number) => { + if (i > 0 && i <= arr.length - 1) str += delim; + str += (typeof e === 'string') ? `'${e}'` + : (e === null) ? 'null' + : (typeof e === 'undefined') ? 'undefined' : e; + }); + return str; + } + + let match: boolean = false; + validTypes.forEach((validType: Unpack<(typeof validTypes)>) => { + if (!match && type === validType) match = true; + }); + if (!match) { + throw new TypeError( + `Invalid 'type' value of ${type} ('${typeof type}'). Valid type is "${ + joinAll(validTypes.sort(), ' | ') + }"`); + } + return; +} + /** * Lists files and/or directories in a specified directory path, filtering by a * regular expression pattern. @@ -113,9 +159,11 @@ function fileUrlToPath(url: URL | string): string { * @returns A promise that resolves with an array of string representing the * entries result excluding `'.'` and `'..'` or an empty array (`[]`) * if any files and directories does not match with the specified filter options. - * @throws {Error} If there is an error occurred while reading a directory. - * @throws {URIError} If the given URL path contains invalid file URL scheme or - * using unsupported protocols. + * @throws {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error **Error**} - + * If there is an error occurred while reading a directory. + * @throws {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/URIError **URIError**} - + * If the given URL path contains invalid file URL scheme or using + * unsupported protocols. * * @example * // List all installed packages in 'node_modules' directory @@ -188,6 +236,9 @@ export async function ls( + (Array.isArray(options) ? 'array' : typeof options)); } + // Check the type argument + checkType(type!, [ ...Object.values(lsTypes), 0, null, undefined ]); + let result: LsResult = null; try { // Read the specified directory path recursively @@ -205,13 +256,14 @@ export async function ls( switch (type) { case lsTypes.LS_D: + case 'LS_D': resultType = (!stats.isFile() && stats.isDirectory()); break; case lsTypes.LS_F: + case 'LS_F': resultType = (stats.isFile() && !stats.isDirectory()); break; - // If set to `LS_A` or not known value, default to include all types - default: resultType = true; + default: resultType = (stats.isFile() || stats.isDirectory()); } return ( @@ -222,10 +274,12 @@ export async function ls( ) ? entry : null; }) ).then(function (results: Array): LsEntries { - return results.filter(function (entry: string | null): boolean { - // Remove any null entries - return !!entry!; - }); + return results.filter( + function (entry: Unpack<(typeof results)>): boolean { + // Remove any null entries + return !!entry!; + } + ); }); } catch (err: unknown) { if (err instanceof Error) throw err; @@ -279,9 +333,11 @@ export async function ls( * @returns A promise that resolves with an array of string representing the * entries result excluding `'.'` and `'..'` or an empty array (`[]`) * if any files and directories does not match with the specified filter options. - * @throws {Error} If there is an error occurred while reading a directory. - * @throws {URIError} If the given URL path contains invalid file URL scheme or - * using unsupported protocols. + * @throws {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error **Error**} - + * If there is an error occurred while reading a directory. + * @throws {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/URIError **URIError**} - + * If the given URL path contains invalid file URL scheme or using + * unsupported protocols. * * @example * // List all JavaScript files in current directory recursively, @@ -350,9 +406,11 @@ export async function lsFiles( * @returns A promise that resolves with an array of string representing the * entries result excluding `'.'` and `'..'` or an empty array (`[]`) * if any files and directories does not match with the specified filter options. - * @throws {Error} If there is an error occurred while reading a directory. - * @throws {URIError} If the given URL path contains invalid file URL scheme or - * using unsupported protocols. + * @throws {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error **Error**} - + * If there is an error occurred while reading a directory. + * @throws {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/URIError **URIError**} - + * If the given URL path contains invalid file URL scheme or using + * unsupported protocols. * * @example * // Search and list directory named 'foo' in 'src' directory diff --git a/test/lib/simpletest.js b/test/lib/simpletest.js index 035744c..c60f239 100644 --- a/test/lib/simpletest.js +++ b/test/lib/simpletest.js @@ -44,9 +44,9 @@ async function it(desc, func, continueOnErr=false) { const { isAsyncFunction } = require('node:util').types; try { isAsyncFunction(func) ? await func() : func(); - console.log(` \x1b[92m\u2714 \x1b[0m\x1b[1m${desc}\x1b[0m`); + console.log(` \x1b[92m\u2714 \x1b[0m\x1b[2m${desc}\x1b[0m`); } catch (err) { - console.error(` \x1b[91m\u2718 \x1b[0m\x1b[1m${desc}\x1b[0m\n`); + console.error(` \x1b[91m\u2718 \x1b[0m${desc}\n`); console.error(new TestError(err.message)); !!continueOnErr || process.exit(1); // Force terminate the process } diff --git a/test/lsfnd.spec.cjs b/test/lsfnd.spec.cjs index 1cbeb72..61fa721 100644 --- a/test/lsfnd.spec.cjs +++ b/test/lsfnd.spec.cjs @@ -27,6 +27,12 @@ it('test `lsFiles` function by listing this file directory', async () => { deepEq(results, expected); }, false); +it('test `lsDirs` function by listing this file directory', async () => { + const results = await lsDirs(__dirname); + const expected = [ 'lib' ].map((e) => path.join(__dirname, e)); + deepEq(results, expected); +}, false); + it('list root directory using URL object', async () => { await doesNotReject(ls(pathToFileURL(rootDirPosix)), URIError); }, false); @@ -35,17 +41,27 @@ it('list root directory using file URL path', async () => { await doesNotReject(ls('file:'.concat(rootDirPosix)), URIError); }, false); -it('test `lsDirs` function by listing this file directory', async () => { - const results = await lsDirs(__dirname); - const expected = [ 'lib' ].map((e) => path.join(__dirname, e)); - deepEq(results, expected); +it('test if the options argument allows explicit null value', async () => { + await doesNotReject(lsFiles(__dirname, null), TypeError); +}, false); + +it('test if the type argument accepts a string value', async () => { + await doesNotReject(ls(__dirname, {}, 'LS_D'), TypeError); }, false); it('throws an error if the given directory path not exist', async () => { await rejects(ls('./this/is/not/exist/directory/path'), Error); }, false); -it('throws an URIError if the given file URL path using unsupported protocol', +it('throws a `URIError` if the given file URL path using unsupported protocol', async () => await rejects(ls('http:'.concat(rootDirPosix)), URIError), false ); + +it('throws a `TypeError` if the given type is an unexpected value', + async () => { + await rejects(ls(__dirname, {}, 'LS_FOO'), TypeError); // Invalid string value test + await rejects(ls(__dirname, {}, []), TypeError); // Array test + }, + false +); diff --git a/test/lsfnd.spec.mjs b/test/lsfnd.spec.mjs index a84abd1..c32c996 100644 --- a/test/lsfnd.spec.mjs +++ b/test/lsfnd.spec.mjs @@ -45,6 +45,14 @@ it('list root directory using file URL path', async () => { await doesNotReject(ls('file:'.concat(rootDirPosix)), URIError); }, false); +it('test if the options argument allows explicit null value', async () => { + await doesNotReject(lsFiles(__dirname, null), TypeError); +}, false); + +it('test if the type argument accepts a string value', async () => { + await doesNotReject(ls(__dirname, {}, 'LS_D'), TypeError); +}, false); + it('throws an error if the given directory path not exist', async () => { await rejects(ls('./this/is/not/exist/directory/path'), Error); }, false); @@ -53,3 +61,11 @@ it('throws an URIError if the given file URL path using unsupported protocol', async () => await rejects(ls('http:'.concat(rootDirPosix)), URIError), false ); + +it('throws a `TypeError` if the given type is an unexpected value', + async () => { + await rejects(ls(__dirname, {}, 'LS_FOO'), TypeError); // Invalid string value test + await rejects(ls(__dirname, {}, []), TypeError); // Array test + }, + false +);