summaryrefslogtreecommitdiff
path: root/tools/eslint/lib/file-finder.js
blob: 3cdf9150fd765e890dc301424b1118a640072f0a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/**
 * @fileoverview Util class to find config files.
 * @author Aliaksei Shytkin
 * @copyright 2014 Michael McLaughlin. All rights reserved.
 */
"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

var fs = require("fs"),
    path = require("path");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
 * Get the entries for a directory. Including a try-catch may be detrimental to
 * function performance, so move it out here a separate function.
 * @param {string} directory The directory to search in.
 * @returns {string[]} The entries in the directory or an empty array on error.
 * @private
 */
function getDirectoryEntries(directory) {
    try {
        return fs.readdirSync(directory);
    } catch (ex) {
        return [];
    }
}

//------------------------------------------------------------------------------
// API
//------------------------------------------------------------------------------

/**
 * FileFinder
 * @constructor
 * @param {...string} arguments The basename(s) of the file(s) to find.
 */
function FileFinder() {
    this.fileNames = Array.prototype.slice.call(arguments);
    this.cache = {};
}

/**
 * Find one instance of a specified file name in directory or in a parent directory.
 * Cache the results.
 * Does not check if a matching directory entry is a file, and intentionally
 * only searches for the first file name in this.fileNames.
 * Is currently used by lib/ignored_paths.js to find an .eslintignore file.
 * @param  {string} directory The directory to start the search from.
 * @returns {string} Path of the file found, or an empty string if not found.
 */
FileFinder.prototype.findInDirectoryOrParents = function (directory) {
    var cache = this.cache,
        child,
        dirs,
        filePath,
        i,
        name,
        searched;

    if (!directory) {
        directory = process.cwd();
    }

    if (cache.hasOwnProperty(directory)) {
        return cache[directory];
    }

    dirs = [];
    searched = 0;
    name = this.fileNames[0];

    while (directory !== child) {
        dirs[searched++] = directory;

        if (getDirectoryEntries(directory).indexOf(name) !== -1) {
            filePath = path.resolve(directory, name);
            break;
        }

        child = directory;

        // Assign parent directory to directory.
        directory = path.dirname(directory);
    }

    for (i = 0; i < searched; i++) {
        cache[dirs[i]] = filePath;
    }

    return filePath || String();
};

/**
 * Find all instances of files with the specified file names, in directory and
 * parent directories. Cache the results.
 * Does not check if a matching directory entry is a file.
 * Searches for all the file names in this.fileNames.
 * Is currently used by lib/config.js to find .eslintrc and package.json files.
 * @param  {string} directory The directory to start the search from.
 * @returns {string[]} The file paths found.
 */
FileFinder.prototype.findAllInDirectoryAndParents = function (directory) {
    var cache = this.cache,
        child,
        dirs,
        name,
        fileNames,
        fileNamesCount,
        filePath,
        i,
        j,
        searched;

    if (!directory) {
        directory = process.cwd();
    }

    if (cache.hasOwnProperty(directory)) {
        return cache[directory];
    }

    dirs = [];
    searched = 0;
    fileNames = this.fileNames;
    fileNamesCount = fileNames.length;

    do {
        dirs[searched++] = directory;
        cache[directory] = [];

        for (i = 0; i < fileNamesCount; i++) {
            name = fileNames[i];

            if (getDirectoryEntries(directory).indexOf(name) !== -1) {
                filePath = path.resolve(directory, name);

                // Add the file path to the cache of each directory searched.
                for (j = 0; j < searched; j++) {
                    cache[dirs[j]].push(filePath);
                }
            }
        }
        child = directory;

        // Assign parent directory to directory.
        directory = path.dirname(directory);

        if (directory === child) {
            return cache[dirs[0]];
        }
    } while (!cache.hasOwnProperty(directory));

    // Add what has been cached previously to the cache of each directory searched.
    for (i = 0; i < searched; i++) {
        dirs.push.apply(cache[dirs[i]], cache[directory]);
    }

    return cache[dirs[0]];
};

module.exports = FileFinder;