UNPKG

4.37 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 return fs.readdirSync(directory);
29 } catch (ex) {
30 return [];
31 }
32}
33
34/**
35 * Create a hash of filenames from a directory listing
36 * @param {string[]} entries Array of directory entries.
37 * @param {string} directory Path to a current directory.
38 * @param {string[]} supportedConfigs List of support filenames.
39 * @returns {Object} Hashmap of filenames
40 */
41function normalizeDirectoryEntries(entries, directory, supportedConfigs) {
42 const fileHash = {};
43
44 entries.forEach(entry => {
45 if (supportedConfigs.indexOf(entry) >= 0) {
46 const resolvedEntry = path.resolve(directory, entry);
47
48 if (fs.statSync(resolvedEntry).isFile()) {
49 fileHash[entry] = resolvedEntry;
50 }
51 }
52 });
53 return fileHash;
54}
55
56//------------------------------------------------------------------------------
57// API
58//------------------------------------------------------------------------------
59
60/**
61 * FileFinder class
62 */
63class FileFinder {
64
65 /**
66 * @param {string[]} files The basename(s) of the file(s) to find.
67 * @param {stirng} cwd Current working directory
68 */
69 constructor(files, cwd) {
70 this.fileNames = Array.isArray(files) ? files : [files];
71 this.cwd = cwd || process.cwd();
72 this.cache = {};
73 }
74
75 /**
76 * Find all instances of files with the specified file names, in directory and
77 * parent directories. Cache the results.
78 * Does not check if a matching directory entry is a file.
79 * Searches for all the file names in this.fileNames.
80 * Is currently used by lib/config.js to find .eslintrc and package.json files.
81 * @param {string} directory The directory to start the search from.
82 * @returns {string[]} The file paths found.
83 */
84 findAllInDirectoryAndParents(directory) {
85 const cache = this.cache;
86
87 if (directory) {
88 directory = path.resolve(this.cwd, directory);
89 } else {
90 directory = this.cwd;
91 }
92
93 if (cache.hasOwnProperty(directory)) {
94 return cache[directory];
95 }
96
97 const dirs = [];
98 const fileNames = this.fileNames;
99 let searched = 0;
100
101 do {
102 dirs[searched++] = directory;
103 cache[directory] = [];
104
105 const filesMap = normalizeDirectoryEntries(getDirectoryEntries(directory), directory, fileNames);
106
107 if (Object.keys(filesMap).length) {
108 for (let k = 0; k < fileNames.length; k++) {
109
110 if (filesMap[fileNames[k]]) {
111 const filePath = filesMap[fileNames[k]];
112
113 // Add the file path to the cache of each directory searched.
114 for (let j = 0; j < searched; j++) {
115 cache[dirs[j]].push(filePath);
116 }
117
118 break;
119 }
120 }
121 }
122 const child = directory;
123
124 // Assign parent directory to directory.
125 directory = path.dirname(directory);
126
127 if (directory === child) {
128 return cache[dirs[0]];
129 }
130 } while (!cache.hasOwnProperty(directory));
131
132 // Add what has been cached previously to the cache of each directory searched.
133 for (let i = 0; i < searched; i++) {
134 dirs.push.apply(cache[dirs[i]], cache[directory]);
135 }
136
137 return cache[dirs[0]];
138 }
139}
140
141module.exports = FileFinder;