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