UNPKG

4.58 kBJavaScriptView Raw
1/**
2 * @fileoverview Util class to find config files.
3 * @author Aliaksei Shytkin
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
12const fs = require("fs"),
13 path = require("path");
14
15//------------------------------------------------------------------------------
16// Helpers
17//------------------------------------------------------------------------------
18
19/**
20 * Get the entries for a directory. Including a try-catch may be detrimental to
21 * function performance, so move it out here a separate function.
22 * @param {string} directory The directory to search in.
23 * @returns {string[]} The entries in the directory or an empty array on error.
24 * @private
25 */
26function getDirectoryEntries(directory) {
27 try {
28
29 return fs.readdirSync(directory);
30 } catch (ex) {
31 return [];
32 }
33}
34
35/**
36 * Create a hash of filenames from a directory listing
37 * @param {string[]} entries Array of directory entries.
38 * @param {string} directory Path to a current directory.
39 * @param {string[]} supportedConfigs List of support filenames.
40 * @returns {Object} Hashmap of filenames
41 */
42function normalizeDirectoryEntries(entries, directory, supportedConfigs) {
43 const fileHash = {};
44
45 entries.forEach(entry => {
46 if (supportedConfigs.indexOf(entry) >= 0) {
47 const resolvedEntry = path.resolve(directory, entry);
48
49 if (fs.statSync(resolvedEntry).isFile()) {
50 fileHash[entry] = resolvedEntry;
51 }
52 }
53 });
54 return fileHash;
55}
56
57//------------------------------------------------------------------------------
58// API
59//------------------------------------------------------------------------------
60
61/**
62 * FileFinder class
63 */
64class FileFinder {
65
66 /**
67 * @param {string[]} files The basename(s) of the file(s) to find.
68 * @param {stirng} cwd Current working directory
69 */
70 constructor(files, cwd) {
71 this.fileNames = Array.isArray(files) ? files : [files];
72 this.cwd = cwd || process.cwd();
73 this.cache = {};
74 }
75
76 /**
77 * Find all instances of files with the specified file names, in directory and
78 * parent directories. Cache the results.
79 * Does not check if a matching directory entry is a file.
80 * Searches for all the file names in this.fileNames.
81 * Is currently used by lib/config.js to find .eslintrc and package.json files.
82 * @param {string} relativeDirectory The directory to start the search from.
83 * @returns {GeneratorFunction} to iterate the file paths found
84 */
85 *findAllInDirectoryAndParents(relativeDirectory) {
86 const cache = this.cache;
87
88 const initialDirectory = relativeDirectory
89 ? path.resolve(this.cwd, relativeDirectory)
90 : this.cwd;
91
92 if (Object.prototype.hasOwnProperty.call(cache, initialDirectory)) {
93 yield* cache[initialDirectory];
94 return; // to avoid doing the normal loop afterwards
95 }
96
97 const dirs = [];
98 const fileNames = this.fileNames;
99 let searched = 0;
100 let directory = initialDirectory;
101
102 do {
103 dirs[searched++] = directory;
104 cache[directory] = [];
105
106 const filesMap = normalizeDirectoryEntries(getDirectoryEntries(directory), directory, fileNames);
107
108 if (Object.keys(filesMap).length) {
109 for (let k = 0; k < fileNames.length; k++) {
110
111 if (filesMap[fileNames[k]]) {
112 const filePath = filesMap[fileNames[k]];
113
114 // Add the file path to the cache of each directory searched.
115 for (let j = 0; j < searched; j++) {
116 cache[dirs[j]].push(filePath);
117 }
118 yield filePath;
119 break;
120 }
121 }
122 }
123
124 const child = directory;
125
126 // Assign parent directory to directory.
127 directory = path.dirname(directory);
128
129 if (directory === child) {
130 return;
131 }
132
133 } while (!Object.prototype.hasOwnProperty.call(cache, directory));
134
135 // Add what has been cached previously to the cache of each directory searched.
136 for (let i = 0; i < searched; i++) {
137 cache[dirs[i]].push(...cache[directory]);
138 }
139
140 yield* cache[dirs[0]];
141 }
142}
143
144module.exports = FileFinder;