You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
142 lines
5.1 KiB
142 lines
5.1 KiB
/** |
|
* @fileoverview Utility for caching lint results. |
|
* @author Kevin Partington |
|
*/ |
|
"use strict"; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Requirements |
|
//----------------------------------------------------------------------------- |
|
|
|
const assert = require("assert"); |
|
const fs = require("fs"); |
|
const fileEntryCache = require("file-entry-cache"); |
|
const stringify = require("json-stable-stringify-without-jsonify"); |
|
const pkg = require("../../package.json"); |
|
const hash = require("./hash"); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Helpers |
|
//----------------------------------------------------------------------------- |
|
|
|
const configHashCache = new WeakMap(); |
|
const nodeVersion = process && process.version; |
|
|
|
/** |
|
* Calculates the hash of the config |
|
* @param {ConfigArray} config The config. |
|
* @returns {string} The hash of the config |
|
*/ |
|
function hashOfConfigFor(config) { |
|
if (!configHashCache.has(config)) { |
|
configHashCache.set(config, hash(`${pkg.version}_${nodeVersion}_${stringify(config)}`)); |
|
} |
|
|
|
return configHashCache.get(config); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Public Interface |
|
//----------------------------------------------------------------------------- |
|
|
|
/** |
|
* Lint result cache. This wraps around the file-entry-cache module, |
|
* transparently removing properties that are difficult or expensive to |
|
* serialize and adding them back in on retrieval. |
|
*/ |
|
class LintResultCache { |
|
|
|
/** |
|
* Creates a new LintResultCache instance. |
|
* @param {string} cacheFileLocation The cache file location. |
|
* configuration lookup by file path). |
|
*/ |
|
constructor(cacheFileLocation) { |
|
assert(cacheFileLocation, "Cache file location is required"); |
|
|
|
this.fileEntryCache = fileEntryCache.create(cacheFileLocation); |
|
} |
|
|
|
/** |
|
* Retrieve cached lint results for a given file path, if present in the |
|
* cache. If the file is present and has not been changed, rebuild any |
|
* missing result information. |
|
* @param {string} filePath The file for which to retrieve lint results. |
|
* @param {ConfigArray} config The config of the file. |
|
* @returns {Object|null} The rebuilt lint results, or null if the file is |
|
* changed or not in the filesystem. |
|
*/ |
|
getCachedLintResults(filePath, config) { |
|
|
|
/* |
|
* Cached lint results are valid if and only if: |
|
* 1. The file is present in the filesystem |
|
* 2. The file has not changed since the time it was previously linted |
|
* 3. The ESLint configuration has not changed since the time the file |
|
* was previously linted |
|
* If any of these are not true, we will not reuse the lint results. |
|
*/ |
|
|
|
const fileDescriptor = this.fileEntryCache.getFileDescriptor(filePath); |
|
const hashOfConfig = hashOfConfigFor(config); |
|
const changed = fileDescriptor.changed || fileDescriptor.meta.hashOfConfig !== hashOfConfig; |
|
|
|
if (fileDescriptor.notFound || changed) { |
|
return null; |
|
} |
|
|
|
// If source is present but null, need to reread the file from the filesystem. |
|
if (fileDescriptor.meta.results && fileDescriptor.meta.results.source === null) { |
|
fileDescriptor.meta.results.source = fs.readFileSync(filePath, "utf-8"); |
|
} |
|
|
|
return fileDescriptor.meta.results; |
|
} |
|
|
|
/** |
|
* Set the cached lint results for a given file path, after removing any |
|
* information that will be both unnecessary and difficult to serialize. |
|
* Avoids caching results with an "output" property (meaning fixes were |
|
* applied), to prevent potentially incorrect results if fixes are not |
|
* written to disk. |
|
* @param {string} filePath The file for which to set lint results. |
|
* @param {ConfigArray} config The config of the file. |
|
* @param {Object} result The lint result to be set for the file. |
|
* @returns {void} |
|
*/ |
|
setCachedLintResults(filePath, config, result) { |
|
if (result && Object.prototype.hasOwnProperty.call(result, "output")) { |
|
return; |
|
} |
|
|
|
const fileDescriptor = this.fileEntryCache.getFileDescriptor(filePath); |
|
|
|
if (fileDescriptor && !fileDescriptor.notFound) { |
|
|
|
// Serialize the result, except that we want to remove the file source if present. |
|
const resultToSerialize = Object.assign({}, result); |
|
|
|
/* |
|
* Set result.source to null. |
|
* In `getCachedLintResults`, if source is explicitly null, we will |
|
* read the file from the filesystem to set the value again. |
|
*/ |
|
if (Object.prototype.hasOwnProperty.call(resultToSerialize, "source")) { |
|
resultToSerialize.source = null; |
|
} |
|
|
|
fileDescriptor.meta.results = resultToSerialize; |
|
fileDescriptor.meta.hashOfConfig = hashOfConfigFor(config); |
|
} |
|
} |
|
|
|
/** |
|
* Persists the in-memory cache to disk. |
|
* @returns {void} |
|
*/ |
|
reconcile() { |
|
this.fileEntryCache.reconcile(); |
|
} |
|
} |
|
|
|
module.exports = LintResultCache;
|
|
|