.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / cli-engine / lint-result-cache.js
1 /**
2  * @fileoverview Utility for caching lint results.
3  * @author Kevin Partington
4  */
5 "use strict";
6
7 //-----------------------------------------------------------------------------
8 // Requirements
9 //-----------------------------------------------------------------------------
10
11 const assert = require("assert");
12 const fs = require("fs");
13 const fileEntryCache = require("file-entry-cache");
14 const stringify = require("json-stable-stringify-without-jsonify");
15 const pkg = require("../../package.json");
16 const hash = require("./hash");
17
18 const debug = require("debug")("eslint:lint-result-cache");
19
20 //-----------------------------------------------------------------------------
21 // Helpers
22 //-----------------------------------------------------------------------------
23
24 const configHashCache = new WeakMap();
25 const nodeVersion = process && process.version;
26
27 const validCacheStrategies = ["metadata", "content"];
28 const invalidCacheStrategyErrorMessage = `Cache strategy must be one of: ${validCacheStrategies
29     .map(strategy => `"${strategy}"`)
30     .join(", ")}`;
31
32 /**
33  * Tests whether a provided cacheStrategy is valid
34  * @param {string} cacheStrategy The cache strategy to use
35  * @returns {boolean} true if `cacheStrategy` is one of `validCacheStrategies`; false otherwise
36  */
37 function isValidCacheStrategy(cacheStrategy) {
38     return (
39         validCacheStrategies.indexOf(cacheStrategy) !== -1
40     );
41 }
42
43 /**
44  * Calculates the hash of the config
45  * @param {ConfigArray} config The config.
46  * @returns {string} The hash of the config
47  */
48 function hashOfConfigFor(config) {
49     if (!configHashCache.has(config)) {
50         configHashCache.set(config, hash(`${pkg.version}_${nodeVersion}_${stringify(config)}`));
51     }
52
53     return configHashCache.get(config);
54 }
55
56 //-----------------------------------------------------------------------------
57 // Public Interface
58 //-----------------------------------------------------------------------------
59
60 /**
61  * Lint result cache. This wraps around the file-entry-cache module,
62  * transparently removing properties that are difficult or expensive to
63  * serialize and adding them back in on retrieval.
64  */
65 class LintResultCache {
66
67     /**
68      * Creates a new LintResultCache instance.
69      * @param {string} cacheFileLocation The cache file location.
70      * @param {"metadata" | "content"} cacheStrategy The cache strategy to use.
71      */
72     constructor(cacheFileLocation, cacheStrategy) {
73         assert(cacheFileLocation, "Cache file location is required");
74         assert(cacheStrategy, "Cache strategy is required");
75         assert(
76             isValidCacheStrategy(cacheStrategy),
77             invalidCacheStrategyErrorMessage
78         );
79
80         debug(`Caching results to ${cacheFileLocation}`);
81
82         const useChecksum = cacheStrategy === "content";
83
84         debug(
85             `Using "${cacheStrategy}" strategy to detect changes`
86         );
87
88         this.fileEntryCache = fileEntryCache.create(
89             cacheFileLocation,
90             void 0,
91             useChecksum
92         );
93         this.cacheFileLocation = cacheFileLocation;
94     }
95
96     /**
97      * Retrieve cached lint results for a given file path, if present in the
98      * cache. If the file is present and has not been changed, rebuild any
99      * missing result information.
100      * @param {string} filePath The file for which to retrieve lint results.
101      * @param {ConfigArray} config The config of the file.
102      * @returns {Object|null} The rebuilt lint results, or null if the file is
103      *   changed or not in the filesystem.
104      */
105     getCachedLintResults(filePath, config) {
106
107         /*
108          * Cached lint results are valid if and only if:
109          * 1. The file is present in the filesystem
110          * 2. The file has not changed since the time it was previously linted
111          * 3. The ESLint configuration has not changed since the time the file
112          *    was previously linted
113          * If any of these are not true, we will not reuse the lint results.
114          */
115         const fileDescriptor = this.fileEntryCache.getFileDescriptor(filePath);
116         const hashOfConfig = hashOfConfigFor(config);
117         const changed =
118             fileDescriptor.changed ||
119             fileDescriptor.meta.hashOfConfig !== hashOfConfig;
120
121         if (fileDescriptor.notFound) {
122             debug(`File not found on the file system: ${filePath}`);
123             return null;
124         }
125
126         if (changed) {
127             debug(`Cache entry not found or no longer valid: ${filePath}`);
128             return null;
129         }
130
131         // If source is present but null, need to reread the file from the filesystem.
132         if (
133             fileDescriptor.meta.results &&
134             fileDescriptor.meta.results.source === null
135         ) {
136             debug(`Rereading cached result source from filesystem: ${filePath}`);
137             fileDescriptor.meta.results.source = fs.readFileSync(filePath, "utf-8");
138         }
139
140         return fileDescriptor.meta.results;
141     }
142
143     /**
144      * Set the cached lint results for a given file path, after removing any
145      * information that will be both unnecessary and difficult to serialize.
146      * Avoids caching results with an "output" property (meaning fixes were
147      * applied), to prevent potentially incorrect results if fixes are not
148      * written to disk.
149      * @param {string} filePath The file for which to set lint results.
150      * @param {ConfigArray} config The config of the file.
151      * @param {Object} result The lint result to be set for the file.
152      * @returns {void}
153      */
154     setCachedLintResults(filePath, config, result) {
155         if (result && Object.prototype.hasOwnProperty.call(result, "output")) {
156             return;
157         }
158
159         const fileDescriptor = this.fileEntryCache.getFileDescriptor(filePath);
160
161         if (fileDescriptor && !fileDescriptor.notFound) {
162             debug(`Updating cached result: ${filePath}`);
163
164             // Serialize the result, except that we want to remove the file source if present.
165             const resultToSerialize = Object.assign({}, result);
166
167             /*
168              * Set result.source to null.
169              * In `getCachedLintResults`, if source is explicitly null, we will
170              * read the file from the filesystem to set the value again.
171              */
172             if (Object.prototype.hasOwnProperty.call(resultToSerialize, "source")) {
173                 resultToSerialize.source = null;
174             }
175
176             fileDescriptor.meta.results = resultToSerialize;
177             fileDescriptor.meta.hashOfConfig = hashOfConfigFor(config);
178         }
179     }
180
181     /**
182      * Persists the in-memory cache to disk.
183      * @returns {void}
184      */
185     reconcile() {
186         debug(`Persisting cached results: ${this.cacheFileLocation}`);
187         this.fileEntryCache.reconcile();
188     }
189 }
190
191 module.exports = LintResultCache;