123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- /**
- * @fileoverview Utility for caching lint results.
- * @author Kevin Partington
- */
- "use strict";
-
- //-----------------------------------------------------------------------------
- // Requirements
- //-----------------------------------------------------------------------------
-
- const assert = require("assert"),
- fs = require("fs"),
- fileEntryCache = require("file-entry-cache"),
- hash = require("./hash"),
- pkg = require("../../package.json"),
- stringify = require("json-stable-stringify-without-jsonify");
-
- //-----------------------------------------------------------------------------
- // Helpers
- //-----------------------------------------------------------------------------
-
- const configHashCache = new WeakMap();
-
- /**
- * Calculates the hash of the config file used to validate a given file
- * @param {Object} configHelper The config helper for retrieving configuration information
- * @param {string} filename The path of the file to retrieve a config object for to calculate the hash
- * @returns {string} The hash of the config
- */
- function hashOfConfigFor(configHelper, filename) {
- const config = configHelper.getConfig(filename);
-
- if (!configHashCache.has(config)) {
- configHashCache.set(config, hash(`${pkg.version}_${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.
- * @constructor
- * @param {string} cacheFileLocation The cache file location.
- * @param {Object} configHelper The configuration helper (used for
- * configuration lookup by file path).
- */
- constructor(cacheFileLocation, configHelper) {
- assert(cacheFileLocation, "Cache file location is required");
- assert(configHelper, "Config helper is required");
-
- this.fileEntryCache = fileEntryCache.create(cacheFileLocation);
- this.configHelper = configHelper;
- }
-
- /**
- * 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.
- * @returns {Object|null} The rebuilt lint results, or null if the file is
- * changed or not in the filesystem.
- */
- getCachedLintResults(filePath) {
-
- /*
- * 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(this.configHelper, filePath);
- 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 {Object} result The lint result to be set for the file.
- * @returns {void}
- */
- setCachedLintResults(filePath, 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(this.configHelper, result.filePath);
- }
- }
-
- /**
- * Persists the in-memory cache to disk.
- * @returns {void}
- */
- reconcile() {
- this.fileEntryCache.reconcile();
- }
- }
-
- module.exports = LintResultCache;
|