2 * @fileoverview Utility for caching lint results.
3 * @author Kevin Partington
7 //-----------------------------------------------------------------------------
9 //-----------------------------------------------------------------------------
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");
18 //-----------------------------------------------------------------------------
20 //-----------------------------------------------------------------------------
22 const configHashCache = new WeakMap();
23 const nodeVersion = process && process.version;
26 * Calculates the hash of the config
27 * @param {ConfigArray} config The config.
28 * @returns {string} The hash of the config
30 function hashOfConfigFor(config) {
31 if (!configHashCache.has(config)) {
32 configHashCache.set(config, hash(`${pkg.version}_${nodeVersion}_${stringify(config)}`));
35 return configHashCache.get(config);
38 //-----------------------------------------------------------------------------
40 //-----------------------------------------------------------------------------
43 * Lint result cache. This wraps around the file-entry-cache module,
44 * transparently removing properties that are difficult or expensive to
45 * serialize and adding them back in on retrieval.
47 class LintResultCache {
50 * Creates a new LintResultCache instance.
51 * @param {string} cacheFileLocation The cache file location.
52 * configuration lookup by file path).
54 constructor(cacheFileLocation) {
55 assert(cacheFileLocation, "Cache file location is required");
57 this.fileEntryCache = fileEntryCache.create(cacheFileLocation);
61 * Retrieve cached lint results for a given file path, if present in the
62 * cache. If the file is present and has not been changed, rebuild any
63 * missing result information.
64 * @param {string} filePath The file for which to retrieve lint results.
65 * @param {ConfigArray} config The config of the file.
66 * @returns {Object|null} The rebuilt lint results, or null if the file is
67 * changed or not in the filesystem.
69 getCachedLintResults(filePath, config) {
72 * Cached lint results are valid if and only if:
73 * 1. The file is present in the filesystem
74 * 2. The file has not changed since the time it was previously linted
75 * 3. The ESLint configuration has not changed since the time the file
76 * was previously linted
77 * If any of these are not true, we will not reuse the lint results.
80 const fileDescriptor = this.fileEntryCache.getFileDescriptor(filePath);
81 const hashOfConfig = hashOfConfigFor(config);
82 const changed = fileDescriptor.changed || fileDescriptor.meta.hashOfConfig !== hashOfConfig;
84 if (fileDescriptor.notFound || changed) {
88 // If source is present but null, need to reread the file from the filesystem.
89 if (fileDescriptor.meta.results && fileDescriptor.meta.results.source === null) {
90 fileDescriptor.meta.results.source = fs.readFileSync(filePath, "utf-8");
93 return fileDescriptor.meta.results;
97 * Set the cached lint results for a given file path, after removing any
98 * information that will be both unnecessary and difficult to serialize.
99 * Avoids caching results with an "output" property (meaning fixes were
100 * applied), to prevent potentially incorrect results if fixes are not
102 * @param {string} filePath The file for which to set lint results.
103 * @param {ConfigArray} config The config of the file.
104 * @param {Object} result The lint result to be set for the file.
107 setCachedLintResults(filePath, config, result) {
108 if (result && Object.prototype.hasOwnProperty.call(result, "output")) {
112 const fileDescriptor = this.fileEntryCache.getFileDescriptor(filePath);
114 if (fileDescriptor && !fileDescriptor.notFound) {
116 // Serialize the result, except that we want to remove the file source if present.
117 const resultToSerialize = Object.assign({}, result);
120 * Set result.source to null.
121 * In `getCachedLintResults`, if source is explicitly null, we will
122 * read the file from the filesystem to set the value again.
124 if (Object.prototype.hasOwnProperty.call(resultToSerialize, "source")) {
125 resultToSerialize.source = null;
128 fileDescriptor.meta.results = resultToSerialize;
129 fileDescriptor.meta.hashOfConfig = hashOfConfigFor(config);
134 * Persists the in-memory cache to disk.
138 this.fileEntryCache.reconcile();
142 module.exports = LintResultCache;