UNPKG

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