import fs from "fs/promises";
import path from "path";
/**
 * Recursively reads a directory and returns the list of pathnames.
 *
 * @param rootDirectory the directory to read
 * @param options options to control the behavior of the recursive read
 * @returns the list of pathnames
 */ export async function recursiveReadDir(rootDirectory, options = {}) {
    // Grab our options.
    const { pathnameFilter, ignoreFilter, ignorePartFilter, sortPathnames = true, relativePathnames = true } = options;
    // The list of pathnames to return.
    const pathnames = [];
    /**
   * Coerces the pathname to be relative if requested.
   */ const coerce = relativePathnames ? (pathname)=>pathname.replace(rootDirectory, "") : (pathname)=>pathname;
    // The queue of directories to scan.
    let directories = [
        rootDirectory
    ];
    while(directories.length > 0){
        // Load all the files in each directory at the same time.
        const results = await Promise.all(directories.map(async (directory)=>{
            const result = {
                directories: [],
                pathnames: [],
                links: []
            };
            try {
                const dir = await fs.readdir(directory, {
                    withFileTypes: true
                });
                for (const file of dir){
                    // If enabled, ignore the file if it matches the ignore filter.
                    if (ignorePartFilter && ignorePartFilter(file.name)) {
                        continue;
                    }
                    // Handle each file.
                    const absolutePathname = path.join(directory, file.name);
                    // If enabled, ignore the file if it matches the ignore filter.
                    if (ignoreFilter && ignoreFilter(absolutePathname)) {
                        continue;
                    }
                    // If the file is a directory, then add it to the list of directories,
                    // they'll be scanned on a later pass.
                    if (file.isDirectory()) {
                        result.directories.push(absolutePathname);
                    } else if (file.isSymbolicLink()) {
                        result.links.push(absolutePathname);
                    } else if (!pathnameFilter || pathnameFilter(absolutePathname)) {
                        result.pathnames.push(coerce(absolutePathname));
                    }
                }
            } catch (err) {
                // This can only happen when the underlying directory was removed. If
                // anything other than this error occurs, re-throw it.
                // if (err.code !== 'ENOENT') throw err
                if (err.code !== "ENOENT" || directory === rootDirectory) throw err;
                // The error occurred, so abandon reading this directory.
                return null;
            }
            return result;
        }));
        // Empty the directories, we'll fill it later if some of the files are
        // directories.
        directories = [];
        // Keep track of any symbolic links we find, we'll resolve them later.
        const links = [];
        // For each result of directory scans...
        for (const result of results){
            // If the directory was removed, then skip it.
            if (!result) continue;
            // Add any directories to the list of directories to scan.
            directories.push(...result.directories);
            // Add any symbolic links to the list of symbolic links to resolve.
            links.push(...result.links);
            // Add any file pathnames to the list of pathnames.
            pathnames.push(...result.pathnames);
        }
        // Resolve all the symbolic links we found if any.
        if (links.length > 0) {
            const resolved = await Promise.all(links.map(async (absolutePathname)=>{
                try {
                    return await fs.stat(absolutePathname);
                } catch (err) {
                    // This can only happen when the underlying link was removed. If
                    // anything other than this error occurs, re-throw it.
                    if (err.code !== "ENOENT") throw err;
                    // The error occurred, so abandon reading this directory.
                    return null;
                }
            }));
            for(let i = 0; i < links.length; i++){
                const stats = resolved[i];
                // If the link was removed, then skip it.
                if (!stats) continue;
                // We would have already ignored the file if it matched the ignore
                // filter, so we don't need to check it again.
                const absolutePathname = links[i];
                if (stats.isDirectory()) {
                    directories.push(absolutePathname);
                } else if (!pathnameFilter || pathnameFilter(absolutePathname)) {
                    pathnames.push(coerce(absolutePathname));
                }
            }
        }
    }
    // Sort the pathnames in place if requested.
    if (sortPathnames) {
        pathnames.sort();
    }
    return pathnames;
}

//# sourceMappingURL=recursive-readdir.js.map