Skip to content

Commit

Permalink
fix(encoding): Fix malfunctioning encoding option (#7)
Browse files Browse the repository at this point in the history
These changes focus on fixing the malfunctioned `encoding` option due to internal error and limited to certain encodings.

Signed-off-by: Ryuu Mitsuki <[email protected]>
  • Loading branch information
mitsuki31 committed Apr 27, 2024
2 parents 8440263 + 46b776c commit 3686a6e
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 7 deletions.
95 changes: 89 additions & 6 deletions src/lsfnd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,79 @@ function resolveOptions(options: LsOptions | null | undefined): ResolvedLsOption
});
}

/**
* Encodes a string or an array of strings from one encoding to another.
*
* This function offers simplicity and flexibility by allowing encoding conversion
* between different encodings for either a string or a set of strings.
*
* @param val - The string to encode.
* @param from - The encoding of the input string.
* @param to - The encoding to convert the string to.
*
* @returns The encoded string.
*
* @throws {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/TypeError **TypeError**} -
* If the `from` or `to` encoding is unknown, or if the input value is
* neither a string nor an array of strings.
*
* @example
* Encode a string to 'base64' encoding:
* ```js
* const encodedString = encodeTo('Hello, world!', 'utf8', 'base64');
* console.log(encodedString);
* // Output: 'SGVsbG8sIHdvcmxkIQ=='
* ```
*
* Encode an array of strings to 'hex' encoding:
* ```js
* const encodedArray = encodeTo(['Hello', 'world'], 'utf8', 'hex');
* console.log(encodedArray);
* // Output: ['48656c6c6f', '776f726c64']
* ```
*
* @since 1.0.0
* @internal
*/
function encodeTo(
val: string,
from: BufferEncoding,
to: BufferEncoding
): string;
/**
* @param val - The array of strings to encode.
* @param from - The encoding of the input strings.
* @param to - The encoding to convert the strings to.
*
* @returns The array of encoded strings.
*/
function encodeTo(
val: Array<string>,
from: BufferEncoding,
to: BufferEncoding
): Array<string>;

function encodeTo(
val: string | Array<string>,
from: BufferEncoding,
to: BufferEncoding
): string | Array<string> {
const { isEncoding } = Buffer;
if (!isEncoding(from)) throw new TypeError("Unknown 'from' encoding: " + from);
else if (!isEncoding(to)) throw new TypeError("Unknown 'to' encoding: " + to);
else if (!(typeof val === 'string' || Array.isArray(val))) {
throw new TypeError('Expected a string or an array of string');
}

if (typeof val === 'string') {
return Buffer.from(val, from).toString(to);
}

return (<Array<string>> val).map(function (v: string): string {
return Buffer.from(v, from).toString(to);
});
}

/**
* Lists files and/or directories in a specified directory path, filtering by a
* regular expression pattern.
Expand Down Expand Up @@ -221,9 +294,9 @@ function resolveOptions(options: LsOptions | null | undefined): ResolvedLsOption
* console.log(entries));
*
* @since 0.1.0
* @see {@link lsTypes}
* @see {@link lsFiles}
* @see {@link lsDirs}
* @see {@link lsTypes}
* @see {@link https://nodejs.org/api/fs.html#fsreaddirpath-options-callback fs.readdir}
*/
export async function ls(
Expand Down Expand Up @@ -264,9 +337,12 @@ export async function ls(
}

// Check and resolve the `rootDir` option
if (options.rootDir && (options.rootDir instanceof URL
|| (typeof options.rootDir === 'string' && /^[a-zA-Z]+:/.test(options.rootDir))
)) {
if (options.rootDir
&& (options.rootDir instanceof URL
|| (typeof options.rootDir === 'string'
&& /^[a-zA-Z]+:/.test(options.rootDir))
)
) {
options.rootDir = fileUrlToPath(options.rootDir);
}

Expand All @@ -284,12 +360,15 @@ export async function ls(
// Read the specified directory path recursively
const entries: LsEntries = await fs.promises.readdir(absdirpath, {
encoding: options?.encoding || 'utf8',
recursive: options?.recursive,
recursive: options?.recursive
});
// Declare the copy of the entries with UTF-8 encoding to be used by `fs.stat`,
// this way we prevent the error due to invalid path thrown by `fs.stat` itself.
const utf8Entries: LsEntries = encodeTo(entries, options?.encoding!, 'utf8');

// Filter the entries
result = await Promise.all(
entries.map(async function (entry: StringPath): Promise<(StringPath | null)> {
utf8Entries.map(async function (entry: StringPath): Promise<(StringPath | null)> {
entry = path.join(absdirpath, entry);
const stats: fs.Stats = await fs.promises.stat(entry);
let resultType: boolean = false;
Expand Down Expand Up @@ -336,6 +415,10 @@ export async function ls(
} catch (err: unknown) {
if (err instanceof Error) throw err;
}

// Encode back the entries to the specified encoding
if (result && options?.encoding! !== 'utf8')
result = encodeTo(result, 'utf8', options.encoding!);
return result;
}

Expand Down
14 changes: 14 additions & 0 deletions test/lsfnd.spec.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ it('test if the type argument accepts a string value', async () => {
await doesNotReject(ls(__dirname, {}, 'LS_D'), TypeError);
}, false);

it("list this file directory with 'base64' encoding", async () => {
const results = await ls(__dirname, { rootDir: __dirname, encoding: 'base64' });
const expected = [ 'lib', 'lsfnd.spec.cjs', 'lsfnd.spec.mjs' ]
.map((e) => Buffer.from(e, 'utf8').toString('base64'));
deepEq(results, expected);
}, false);

// --- [ ERROR TESTS ] --- //

it('throws an error if the given directory path not exist', async () => {
await rejects(ls('./this/is/not/exist/directory/path'), Error);
}, false);
Expand All @@ -65,3 +74,8 @@ it('throws a `TypeError` if the given type is an unexpected value',
},
false
);

it('throws an error if the given encoding option is unknown', async () => {
await rejects(lsFiles(__dirname, { encoding: 'NotDefinedEncoding' }), TypeError);
await rejects(lsFiles(__dirname, { encoding: true }), TypeError);
});
14 changes: 14 additions & 0 deletions test/lsfnd.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ it('test if the type argument accepts a string value', async () => {
await doesNotReject(ls(__dirname, {}, 'LS_D'), TypeError);
}, false);

it("list this file directory with 'base64' encoding", async () => {
const results = await ls(__dirname, { rootDir: __dirname, encoding: 'base64' });
const expected = [ 'lib', 'lsfnd.spec.cjs', 'lsfnd.spec.mjs' ]
.map((e) => Buffer.from(e, 'utf8').toString('base64'));
deepEq(results, expected);
}, false);

// --- [ ERROR TESTS ] --- //

it('throws an error if the given directory path not exist', async () => {
await rejects(ls('./this/is/not/exist/directory/path'), Error);
}, false);
Expand All @@ -69,3 +78,8 @@ it('throws a `TypeError` if the given type is an unexpected value',
},
false
);

it('throws an error if the given encoding option is unknown', async () => {
await rejects(lsFiles(__dirname, { encoding: 'NotDefinedEncoding' }), TypeError);
await rejects(lsFiles(__dirname, { encoding: true }), TypeError);
});
4 changes: 3 additions & 1 deletion types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ export declare interface LsTypesInterface {
*/
export declare interface LsOptions {
/**
* Specifies the character encoding to be used when reading a directory.
* Specifies the character encoding to be used for the output encoding of
* returned entries.
*
* @defaultValue `'utf8'`
* @since 0.1.0
*/
Expand Down

0 comments on commit 3686a6e

Please sign in to comment.