From c259a7d669dd6cfa2e85e35431237c704fdc68c7 Mon Sep 17 00:00:00 2001 From: Ryuu Mitsuki Date: Wed, 24 Apr 2024 21:14:37 +0700 Subject: [PATCH 1/7] refactor(types): Refactor and improve the types declaration * Declared a new type called `StringPath` represents the path from a string * Changed the value of `LsOptionsInterface`'s properties to their constant values instead of their types (number) * Refactored the `LsTypesValues` type, ensuring values compatibility with changes of `LsTypesInterface` interface * Declared a new type called `ResolvedLsOptions` and an interface called `DefaultLsOptions` --- src/index.ts | 3 ++ types/index.d.ts | 76 ++++++++++++++++++++++++++++++++++++------------ 2 files changed, 60 insertions(+), 19 deletions(-) diff --git a/src/index.ts b/src/index.ts index 06775ce..f593772 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,11 +11,14 @@ export * from './lsTypes'; export * from './lsfnd'; export type { + StringPath, LsTypes, LsTypesInterface, LsTypesKeys, LsTypesValues, LsOptions, + ResolvedLsOptions, + DefaultLsOptions, LsEntries, LsResult } from '../types'; diff --git a/types/index.d.ts b/types/index.d.ts index 18d7b98..a793da0 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -13,13 +13,19 @@ /// /** - * This type alias represents an array of strings. It is typically used to + * A type representing the string path. + * @since 1.0.0 + */ +export declare type StringPath = string; + +/** + * This type alias represents an array of {@link StringPath}. It is typically used to * represent the list of file and/or directory entries returned by the `ls*` functions. - * Each string in the array represents the path of a file or directory within + * Each entry in the array represents the path of a file or directory within * the listed directory. * @since 0.1.0 */ -export declare type LsEntries = Array; +export declare type LsEntries = Array; /** * This type alias represents the possible return values of the `ls*` functions. @@ -31,8 +37,9 @@ export declare type LsResult = LsEntries | null; /** * A combination union types containing all possible values used to specify the - * returned results on {@link !lsfnd~ls ls} function. + * returned results on {@link !lsfnd~ls ls} function. * @since 1.0.0 + * @see {@link !lsTypes~lsTypes lsTypes} */ export declare type LsTypes = LsTypesKeys | LsTypesValues; @@ -47,12 +54,9 @@ export declare type LsTypesKeys = keyof LsTypesInterface; * Type representing all possible values of the {@link lsTypes} enum. * @since 0.1.0 * @see {@link LsTypesInterface} + * @see {@link LsTypesKeys} */ -export declare type LsTypesValues = - | 0b00 // 0 (interpreted the same as LS_A | 1) - | 0b01 // 1 (LS_A) - | 0b10 // 2 (LS_D) - | 0b100 // 4 (LS_F) +export declare type LsTypesValues = keyof typeof LsTypesInterface; /** * Interface defining the {@link lsTypes} enum with string literal keys @@ -66,17 +70,17 @@ export declare interface LsTypesInterface { * Represents an option to include all file types. * @defaultValue `0b01 << 0b00` (`0b01` | `0o01` | `0x01` | `1`) */ - readonly LS_A: number; // ALL + readonly LS_A: 0b01; // ALL /** * Represents an option to include only the directory type. * @defaultValue `0b01 << 0b01` (`0b10` | `0o02` | `0x02` | `2`) */ - readonly LS_D: number; // DIRECTORY + readonly LS_D: 0b10; // DIRECTORY /** * Represents an option to include only the file type. * @defaultValue `0b01 << 0b10` (`0b100` | `0o04` | `0x04` | `4`) */ - readonly LS_F: number; // FILE + readonly LS_F: 0b100; // FILE } /** @@ -90,25 +94,59 @@ export declare interface LsOptions { * Specifies the character encoding to be used when reading a directory. * @defaultValue `'utf8'` */ - encoding?: BufferEncoding | undefined, + encoding?: BufferEncoding | undefined; /** * A boolean flag indicating whether to include subdirectories in the listing. * @defaultValue `false` */ - recursive?: boolean | undefined, + recursive?: boolean | undefined; /** * A regular expression or string pattern used to filter the listed entries. * Only entries matching the pattern will be included in the results. * @defaultValue `/.+/` (match all files) */ - match?: RegExp | string | undefined, + match?: RegExp | string | undefined; /** * A regular expression or string pattern used to exclude entries from the * listing. Any entries matching this pattern will be filtered out of the * results, even if they match the {@link match} pattern. * @defaultValue `undefined` */ - exclude?: RegExp | string | undefined + exclude?: RegExp | string | undefined; +} + +/** + * Represents resolved options type for the `ls*` functions, where all properties are + * required and both `null` and `undefined` values are omitted, except for the + * {@link LsOptions.exclude `exclude`} property which keeps the `undefined` type. + * + * @since 1.0.0 + * @see {@link LsOptions} + * @see {@link DefaultLsOptions} + */ +export declare type ResolvedLsOptions = { + [T in keyof LsOptions]-?: T extends 'exclude' + ? NonNullable | undefined + : NonNullable +}; + +/** + * Represents the default options type for the `ls*` functions, used by + * {@link !lsfnd~defaultLsOptions `defaultLsOptions`}. + * + * @interface + * @since 1.0.0 + * @see {@link LsOptions} + * @see {@link ResolvedLsOptions} + * @see {@link !lsfnd~defaultLsOptions defaultLsOptions} + */ +export declare interface DefaultLsOptions { + encoding: 'utf8'; + recursive: false; + match: RegExp; + exclude: undefined; + rootDir: StringPath; + basename: false; } // ====== APIs ===== // @@ -128,20 +166,20 @@ export declare const lsTypes: Record< /** {@inheritDoc !lsfnd~ls} */ export declare function ls( - dirpath: string | URL, + dirpath: StringPath | URL, options?: LsOptions | RegExp | undefined, type?: LsTypes | undefined ): Promise /** {@inheritDoc !lsfnd~lsFiles} */ export declare function lsFiles( - dirpath: string | URL, + dirpath: StringPath | URL, options?: LsOptions | RegExp | undefined ): Promise /** {@inheritDoc !lsfnd~lsDirs} */ export declare function lsDirs( - dirpath: string | URL, + dirpath: StringPath | URL, options?: LsOptions | RegExp | undefined ): Promise From 7ccfa38879bf37d49fdeb6d82d8cb28f76510e47 Mon Sep 17 00:00:00 2001 From: Ryuu Mitsuki Date: Wed, 24 Apr 2024 21:30:50 +0700 Subject: [PATCH 2/7] feat(lsfnd): Introduce the `defaultLsOptions` This constant object contains all default options for `ls*` functions and it is publicly visible. In addition, refactored some types representation from specific parameters, variables, and even on explicit conversion types to more precise and improve types correctiveness. --- src/lsfnd.ts | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/src/lsfnd.ts b/src/lsfnd.ts index 0efcd8c..153a911 100644 --- a/src/lsfnd.ts +++ b/src/lsfnd.ts @@ -16,14 +16,26 @@ import { isRegExp } from 'node:util'; import { URL } from 'node:url'; import { lsTypes } from './lsTypes'; import type { + StringPath, LsEntries, LsResult, LsOptions, + ResolvedLsOptions, + DefaultLsOptions, LsTypes } from '../types'; type Unpack = A extends Array<(infer U)> ? U : A; +export const defaultLsOptions: DefaultLsOptions = { + encoding: 'utf8', + recursive: false, + match: /.+/, + exclude: undefined, + rootDir: process.cwd(), + basename: false +} as const; + /** * Converts a file URL to a file path. * @@ -56,7 +68,7 @@ type Unpack = A extends Array<(infer U)> ? U : A; * * @internal */ -function fileUrlToPath(url: URL | string): string { +function fileUrlToPath(url: URL | StringPath): StringPath { if ((url instanceof URL && url.protocol !== 'file:') || (typeof url === 'string' && !/^file:(\/\/?|\.\.?\/*)/.test(url))) { throw new URIError('Invalid URL file scheme'); @@ -183,13 +195,13 @@ function checkType( * @see {@link https://nodejs.org/api/fs.html#fsreaddirpath-options-callback fs.readdir} */ export async function ls( - dirpath: string | URL, + dirpath: StringPath | URL, options?: LsOptions | RegExp | undefined, type?: LsTypes | undefined ): Promise { - let absdirpath: string; let match: string | RegExp, exclude: string | RegExp | undefined; + let absdirpath: StringPath; if (dirpath instanceof URL) { if (dirpath.protocol !== 'file:') { @@ -208,9 +220,9 @@ export async function ls( } // Resolve its absolute path - absdirpath = path.isAbsolute( dirpath) - ? dirpath - : path.posix.resolve( dirpath); + absdirpath = path.isAbsolute( dirpath) + ? dirpath + : path.posix.resolve( dirpath); if (isRegExp(options)) { match = options; @@ -244,8 +256,8 @@ export async function ls( // Filter the entries result = await Promise.all( - entries.map(async function (entry: string): Promise<(string | null)> { - entry = path.join( dirpath, entry); + entries.map(async function (entry: StringPath): Promise<(StringPath | null)> { + entry = path.join( dirpath, entry); const stats: fs.Stats = await fs.promises.stat(entry); let resultType: boolean = false; @@ -268,7 +280,7 @@ export async function ls( ) ) ? entry : null; }) - ).then(function (results: Array): LsEntries { + ).then(function (results: (Unpack | null)[]): LsEntries { return results.filter( function (entry: Unpack<(typeof results)>): boolean { // Remove any null entries @@ -349,7 +361,7 @@ export async function ls( * @see {@link https://nodejs.org/api/fs.html#fsreaddirpath-options-callback fs.readdir} */ export async function lsFiles( - dirpath: string | URL, + dirpath: StringPath | URL, options?: LsOptions | RegExp | undefined ): Promise { return ls(dirpath, options, lsTypes.LS_F); @@ -420,7 +432,7 @@ export async function lsFiles( * @see {@link https://nodejs.org/api/fs.html#fsreaddirpath-options-callback fs.readdir} */ export async function lsDirs( - dirpath: string | URL, + dirpath: StringPath | URL, options?: LsOptions | RegExp | undefined ): Promise { return ls(dirpath, options, lsTypes.LS_D); From 17d99bbf4fea781c4eec8a7f01bbd2249b8238e6 Mon Sep 17 00:00:00 2001 From: Ryuu Mitsuki Date: Wed, 24 Apr 2024 21:37:31 +0700 Subject: [PATCH 3/7] refactor(ls): Enchance the resolution of `options` parameter By defining a new internal function called `resolveOptions` with consume one argument which for the unresoved `options` itself we can definitely improve the `options` parameter resolution to more flexible and use less code. This also make improvisation of type strict to that parameter. --- src/lsfnd.ts | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/lsfnd.ts b/src/lsfnd.ts index 153a911..8d200af 100644 --- a/src/lsfnd.ts +++ b/src/lsfnd.ts @@ -111,15 +111,27 @@ function checkType( 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 "${ + `Invalid 'type' value of ${ type} ('${typeof type}'). Valid type is "${ joinAll(validTypes.sort(), ' | ') }"`); } return; } +function resolveOptions(options: LsOptions | null | undefined): ResolvedLsOptions { + return > (!options ? defaultLsOptions : { + encoding: options?.encoding || defaultLsOptions.encoding, + recursive: options?.recursive || defaultLsOptions.recursive, + match: options?.match || defaultLsOptions.match, + exclude: options?.exclude || defaultLsOptions.exclude, + rootDir: options?.rootDir || defaultLsOptions.rootDir, + basename: options?.basename || defaultLsOptions.basename + }); +} + /** * Lists files and/or directories in a specified directory path, filtering by a * regular expression pattern. @@ -199,8 +211,6 @@ export async function ls( options?: LsOptions | RegExp | undefined, type?: LsTypes | undefined ): Promise { - let match: string | RegExp, - exclude: string | RegExp | undefined; let absdirpath: StringPath; if (dirpath instanceof URL) { @@ -216,7 +226,7 @@ export async function ls( dirpath = fileUrlToPath(dirpath); } } else { - throw new Error('Unknown type, expected a string or an URL object'); + throw new TypeError('Unknown type, expected a string or an URL object'); } // Resolve its absolute path @@ -225,21 +235,15 @@ export async function ls( : path.posix.resolve( dirpath); if (isRegExp(options)) { - match = options; - exclude = undefined; - options = { encoding: 'utf8', recursive: false }; - } else if (typeof options === 'undefined' || options === null) { - options = { encoding: 'utf8', recursive: false }; - match = /.+/; - } else if (options && typeof options === 'object' && !Array.isArray(options)) { - match = (typeof options!.match === 'string') - ? new RegExp(options!.match) - : (isRegExp(options!.match) ? options!.match : /.+/); - exclude = (typeof options!.exclude === 'string') - ? new RegExp(options!.exclude) - : (isRegExp(options!.exclude) ? options!.exclude : undefined); + // Store the regex value of `options` to temporary variable for `match` option + const temp: RegExp = new RegExp(options.source) || options; + options = resolveOptions(null); // Use the default options + ( options)!.match = temp; // Reassign the `match` field + } else if (!options || (typeof options === 'object' && !Array.isArray(options))) { + // Resolve the options, even it is not specified + options = resolveOptions(> options); } else { - throw new TypeError('Unknown type of "options": ' + throw new TypeError("Unknown type of 'options': " + (Array.isArray(options) ? 'array' : typeof options)); } @@ -275,8 +279,8 @@ export async function ls( return ( resultType && ( - ( match).test(entry) - && (exclude ? !( exclude).test(entry) : true) + ( options.match).test(entry) + && (options.exclude ? !( options.exclude).test(entry) : true) ) ) ? entry : null; }) From f5785891c6b034b153238e70746f1b0064cc80a6 Mon Sep 17 00:00:00 2001 From: Ryuu Mitsuki Date: Fri, 26 Apr 2024 21:39:05 +0700 Subject: [PATCH 4/7] feat(api): Add new several API options Here is the list new options that just being added in this change: - absolute?: { boolean } - Whether to return absolute paths for all entries. - basename? { boolean } - Whether to makes the returned entries only have their file and/or directories names. - rootDir?: { StringPath } - A string path represents the root directory to resolve the relative paths. They all were have their own priority tier, in the list above their priority are sorted from the highest (top) to the lowest (bottom). So, if you specified the `rootDir` but you also enabled the `absolute` option, then -- as a result, -- the `absolute` will be used instead (it is because the `absolute` option have the highest priority) and the `rootDir` value will be ignored. Please note, that the `basename` option are implicitly includes any directory names while listing a directory. If you want include the filenames only, then combining it with the `lsTypes.LS_F` is a good way if you are using `ls` function, or combine this option with `lsFiles` function for more flexible. --- src/lsfnd.ts | 40 +++++++++++----- types/index.d.ts | 119 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 140 insertions(+), 19 deletions(-) diff --git a/src/lsfnd.ts b/src/lsfnd.ts index 8d200af..94d8e04 100644 --- a/src/lsfnd.ts +++ b/src/lsfnd.ts @@ -33,6 +33,7 @@ export const defaultLsOptions: DefaultLsOptions = { match: /.+/, exclude: undefined, rootDir: process.cwd(), + absolute: false, basename: false } as const; @@ -128,6 +129,7 @@ function resolveOptions(options: LsOptions | null | undefined): ResolvedLsOption match: options?.match || defaultLsOptions.match, exclude: options?.exclude || defaultLsOptions.exclude, rootDir: options?.rootDir || defaultLsOptions.rootDir, + absolute: options?.absolute || defaultLsOptions.absolute, basename: options?.basename || defaultLsOptions.basename }); } @@ -211,7 +213,8 @@ export async function ls( options?: LsOptions | RegExp | undefined, type?: LsTypes | undefined ): Promise { - let absdirpath: StringPath; + let absdirpath: StringPath, + reldirpath: StringPath; if (dirpath instanceof URL) { if (dirpath.protocol !== 'file:') { @@ -229,11 +232,6 @@ export async function ls( throw new TypeError('Unknown type, expected a string or an URL object'); } - // Resolve its absolute path - absdirpath = path.isAbsolute( dirpath) - ? dirpath - : path.posix.resolve( dirpath); - if (isRegExp(options)) { // Store the regex value of `options` to temporary variable for `match` option const temp: RegExp = new RegExp(options.source) || options; @@ -241,12 +239,18 @@ export async function ls( ( options)!.match = temp; // Reassign the `match` field } else if (!options || (typeof options === 'object' && !Array.isArray(options))) { // Resolve the options, even it is not specified - options = resolveOptions(> options); + options = resolveOptions(options); } else { throw new TypeError("Unknown type of 'options': " + (Array.isArray(options) ? 'array' : typeof options)); } + // Resolve its absolute and relative path + absdirpath = path.isAbsolute( dirpath) + ? dirpath + : path.posix.resolve( dirpath); + reldirpath = path.relative(options.rootDir! || process.cwd(), absdirpath);; + // Check the type argument checkType(type!, [ ...Object.values(lsTypes), 0, null, undefined ]); @@ -261,7 +265,7 @@ export async function ls( // Filter the entries result = await Promise.all( entries.map(async function (entry: StringPath): Promise<(StringPath | null)> { - entry = path.join( dirpath, entry); + entry = path.join(absdirpath, entry); const stats: fs.Stats = await fs.promises.stat(entry); let resultType: boolean = false; @@ -277,18 +281,30 @@ export async function ls( default: resultType = (stats.isFile() || stats.isDirectory()); } - return ( + return (( resultType && ( ( options.match).test(entry) && (options.exclude ? !( options.exclude).test(entry) : true) ) - ) ? entry : null; + ) + ? ( + // *** High priority + (options.absolute && (options.basename || !options.basename)) + ? entry // already an absolute path + // *** Medium priority + : (!options.absolute && options.basename) + ? path.basename(entry) // get its basename + // *** Low priority + // convert back to the relative path + : path.join(reldirpath, path.relative(absdirpath, entry)) + ) + : null + ) }) ).then(function (results: (Unpack | null)[]): LsEntries { return results.filter( function (entry: Unpack<(typeof results)>): boolean { - // Remove any null entries - return !!entry!; + return !!entry!; // Remove any null entries } ); }); diff --git a/types/index.d.ts b/types/index.d.ts index a793da0..0ee1cb7 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -85,7 +85,8 @@ export declare interface LsTypesInterface { /** * This interface defines the optional configuration options that can be passed - * to the `ls*` function. These options control the behavior of the file listing. + * to every `ls*` functions. These options control the behavior of the directory listing. + * * @interface * @since 0.1.0 */ @@ -93,17 +94,20 @@ export declare interface LsOptions { /** * Specifies the character encoding to be used when reading a directory. * @defaultValue `'utf8'` + * @since 0.1.0 */ encoding?: BufferEncoding | undefined; /** * A boolean flag indicating whether to include subdirectories in the listing. * @defaultValue `false` + * @since 0.1.0 */ recursive?: boolean | undefined; /** * A regular expression or string pattern used to filter the listed entries. * Only entries matching the pattern will be included in the results. * @defaultValue `/.+/` (match all files) + * @since 0.1.0 */ match?: RegExp | string | undefined; /** @@ -111,8 +115,108 @@ export declare interface LsOptions { * listing. Any entries matching this pattern will be filtered out of the * results, even if they match the {@link match} pattern. * @defaultValue `undefined` + * @since 0.1.0 */ exclude?: RegExp | string | undefined; + /** + * A string path representing the root directory to resolve the results to + * relative paths. + * + * This option will be ignored if either one of the {@link absolute `absolute`} + * or {@link basename `basename`} option are enabled, this is due to their + * higher priority. This option have the lowest priority when compared with those + * options. + * + * @defaultValue `'.'` or `process.cwd()` + * @since 1.0.0 + */ + rootDir?: StringPath | undefined; + /** + * Determines whether to return absolute paths for all entries. + * + * When enabled (i.e., set to `true`), each entry of the returned results + * will be an absolute path. Otherwise, paths will be relative to the directory + * specified in the {@link rootDir `rootDir`} field. + * + * Enabling this option are equivalent with the following code. + * Let's assume we want to list all files within a directory named `'foo'`: + * + * ```js + * const { resolve } = require('node:path'); + * const { lsFiles } = require('lsfnd'); + * // Or ESM: + * // import { resolve } from 'node:path'; + * // import { lsFiles } from 'lsfnd'; + * + * const absfiles = (await lsFiles('foo/')).map((entry) => resolve(entry)); + * ``` + * + * In previous release (prior to version 0.1.0) you can literally use an + * explicit method that makes the returned results as absolute paths entirely. + * That is by utilizing the `path.resolve` function, here is an example: + * + * ```js + * const absfiles = await lsFiles(path.resolve('foo/')); + * ``` + * + * In the above code, the directory path is resolved to an absolute path before + * being passed to the {@link !lsfnd~lsFiles `lsFiles`} function. As a result, + * the function treats the specified directory path as a relative path and + * does not attempt to resolve it back to a relative path, thus returning + * absolute paths. This approach was considered unstable and problematic due + * to inconsistencies in the internal logic. Therefore, this option was + * introduced as a replacement and will default returns relative paths when + * this option are disabled (set to `false` or unspecified), they will relative + * to the path specified in the {@link rootDir `rootDir`} field. Refer to + * {@link rootDir `rootDir`} option for more details. + * + * @defaultValue `false` + * @since 1.0.0 + * @see {@link rootDir} + */ + absolute?: boolean | undefined; + /** + * Whether to make the returned result paths only have their basenames, trimming any + * directories behind the path separator (i.e., `\\` in Windows, and `/` in POSIX). + * + * When set to `true`, the returned paths will only include the file and/or + * directory names itself. This can be useful if you need only the names while + * listing a directory. + * + * If you enabled both this option and the {@link absolute `absolute`} option, + * the `absolute` option will be treated instead due to its higher priority rather + * than this option's priority. + * + * > ### Note + * > Please note, that this option implicitly includes any directories on the + * > returned entries. If you want to only include the filenames, then + * > combine this option with {@link !lsTypes~lsTypes.LS_F `LS_F`} type if you + * > are using {@link !lsfnd~ls `ls`} function, or use this option with + * > {@link !lsfnd~lsFiles `lsFiles`} function for better flexibility. + * + * This option achieves the same functionality as the following code: + * + * ```js + * const path = require('node:path'); + * // Or ESM: + * // import * as path from 'node:path'; + * + * // Assume you have `results` variable contains all files paths + * // from listing a directory using `lsFiles` function + * const baseResults = results.map((res) => res.split(path.sep).pop()); + * ``` + * + * Or even a bit more simple like this: + * ```js + * // ... + * const baseResults = results.map((res) => path.basename(res)); + * ``` + * + * @defaultValue `false` + * @since 1.0.0 + * @see {@link rootDir} + */ + basename?: boolean | undefined; } /** @@ -141,12 +245,13 @@ export declare type ResolvedLsOptions = { * @see {@link !lsfnd~defaultLsOptions defaultLsOptions} */ export declare interface DefaultLsOptions { - encoding: 'utf8'; - recursive: false; - match: RegExp; - exclude: undefined; - rootDir: StringPath; - basename: false; + readonly encoding: 'utf8'; + readonly recursive: false; + readonly match: RegExp; + readonly exclude: undefined; + readonly rootDir: StringPath; + readonly absolute: false; + readonly basename: false; } // ====== APIs ===== // From 2ba4a20d56126b495766500c65f3490392e6735c Mon Sep 17 00:00:00 2001 From: Ryuu Mitsuki Date: Fri, 26 Apr 2024 23:53:11 +0700 Subject: [PATCH 5/7] feat(rootDir): Add file URL support to `rootDir` option Please note, this only supports the URL with 'file:' protocol. Attempting to use other than 'file:' protocol will causing an error. --- src/lsfnd.ts | 9 ++++++++- types/index.d.ts | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/lsfnd.ts b/src/lsfnd.ts index 94d8e04..ce745bb 100644 --- a/src/lsfnd.ts +++ b/src/lsfnd.ts @@ -245,7 +245,14 @@ export async function ls( + (Array.isArray(options) ? 'array' : typeof options)); } - // Resolve its absolute and relative path + // Check and resolve the `rootDir` option + if (options.rootDir && (options.rootDir instanceof URL + || (typeof options.rootDir === 'string' && /^[a-zA-Z]+:/.test(options.rootDir)) + )) { + options.rootDir = fileUrlToPath(options.rootDir); + } + + // Resolve the absolute and relative of the dirpath argument absdirpath = path.isAbsolute( dirpath) ? dirpath : path.posix.resolve( dirpath); diff --git a/types/index.d.ts b/types/index.d.ts index 0ee1cb7..3c0c51b 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -130,7 +130,7 @@ export declare interface LsOptions { * @defaultValue `'.'` or `process.cwd()` * @since 1.0.0 */ - rootDir?: StringPath | undefined; + rootDir?: StringPath | URL | undefined; /** * Determines whether to return absolute paths for all entries. * @@ -249,7 +249,7 @@ export declare interface DefaultLsOptions { readonly recursive: false; readonly match: RegExp; readonly exclude: undefined; - readonly rootDir: StringPath; + readonly rootDir: StringPath | URL; readonly absolute: false; readonly basename: false; } From 968401cec68f83ed6f242472e22de7cc37df2f37 Mon Sep 17 00:00:00 2001 From: Ryuu Mitsuki Date: Fri, 26 Apr 2024 23:58:14 +0700 Subject: [PATCH 6/7] docs: Add some TypeDoc documentation Added TypeDoc documentation to the `defaultLsOptions` object and the `resolveOptions` (internal) function. --- src/lsfnd.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/lsfnd.ts b/src/lsfnd.ts index ce745bb..ca65877 100644 --- a/src/lsfnd.ts +++ b/src/lsfnd.ts @@ -27,6 +27,13 @@ import type { type Unpack = A extends Array<(infer U)> ? U : A; +/** + * An object containing all default values of {@link LsOptions `LsOptions`} type. + * + * @since 1.0.0 + * @see {@link DefaultLsOptions} + * @see {@link LsOptions} + */ export const defaultLsOptions: DefaultLsOptions = { encoding: 'utf8', recursive: false, @@ -122,6 +129,17 @@ function checkType( return; } +/** + * Resolves the given `options` ({@link LsOptions}). + * + * @param options - An object represents the options to be resolved. Set to `null` + * or `undefined` to gets the default options. + * @returns A new object represents the resolved options. Returns the default + * options if the `options` parameter not specified or `null`. + * + * @since 1.0.0 + * @internal + */ function resolveOptions(options: LsOptions | null | undefined): ResolvedLsOptions { return > (!options ? defaultLsOptions : { encoding: options?.encoding || defaultLsOptions.encoding, From d3817815e371f10dfcd9a24a628fb44b3db37a7b Mon Sep 17 00:00:00 2001 From: Ryuu Mitsuki Date: Sat, 27 Apr 2024 01:08:36 +0700 Subject: [PATCH 7/7] test: Fix tests error due to API changes --- test/lsfnd.spec.cjs | 6 +++--- test/lsfnd.spec.mjs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/lsfnd.spec.cjs b/test/lsfnd.spec.cjs index 61fa721..f5d57e5 100644 --- a/test/lsfnd.spec.cjs +++ b/test/lsfnd.spec.cjs @@ -14,21 +14,21 @@ const rootDirPosix = path.posix.resolve('..'); console.log(`\n\x1b[1m${path.basename(__filename)}:\x1b[0m`); it('test `ls` function by listing this file directory', async () => { - const results = await ls(__dirname, {}, 0); + const results = await ls(__dirname, { absolute: true }, 0); const expected = [ 'lib', 'lsfnd.spec.cjs', 'lsfnd.spec.mjs' ] .map((e) => path.join(__dirname, e)); deepEq(results, expected); }, false); it('test `lsFiles` function by listing this file directory', async () => { - const results = await lsFiles(__dirname); + const results = await lsFiles(__dirname, { absolute: true }); const expected = [ 'lsfnd.spec.cjs', 'lsfnd.spec.mjs' ] .map((e) => path.join(__dirname, e)); deepEq(results, expected); }, false); it('test `lsDirs` function by listing this file directory', async () => { - const results = await lsDirs(__dirname); + const results = await lsDirs(__dirname, { absolute: true }); const expected = [ 'lib' ].map((e) => path.join(__dirname, e)); deepEq(results, expected); }, false); diff --git a/test/lsfnd.spec.mjs b/test/lsfnd.spec.mjs index c32c996..b2ad20e 100644 --- a/test/lsfnd.spec.mjs +++ b/test/lsfnd.spec.mjs @@ -18,21 +18,21 @@ const rootDirPosix = path.posix.resolve('..'); console.log(`\n\x1b[1m${path.basename(__filename)}:\x1b[0m`); it('test `ls` function by listing this file directory', async () => { - const results = await ls(__dirname, {}, 0); + const results = await ls(__dirname, { absolute: true }, 0); const expected = [ 'lib', 'lsfnd.spec.cjs', 'lsfnd.spec.mjs' ] .map((e) => path.join(__dirname, e)); deepEq(results, expected); }, false); it('test `lsFiles` function by listing this file directory', async () => { - const results = await lsFiles(__dirname); + const results = await lsFiles(__dirname, { absolute: true }); const expected = [ 'lsfnd.spec.cjs', 'lsfnd.spec.mjs' ] .map((e) => path.join(__dirname, e)); deepEq(results, expected); }, false); it('test `lsDirs` function by listing this file directory', async () => { - const results = await lsDirs(__dirname); + const results = await lsDirs(__dirname, { absolute: true }); const expected = [ 'lib' ].map((e) => path.join(__dirname, e)); deepEq(results, expected); }, false);