Spaces:
Runtime error
Runtime error
/** | |
* @fileoverview Utility for caching lint results. | |
* @author Kevin Partington | |
*/ | |
; | |
//----------------------------------------------------------------------------- | |
// 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; | |