text: /**
   * @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");
  
  const debug = require("debug")("eslint:lint-result-cache");
  
  //-----------------------------------------------------------------------------
  // Helpers
  //-----------------------------------------------------------------------------
  
  const configHashCache = new WeakMap();
  const nodeVersion = process && process.version;
  
  const validCacheStrategies = ["metadata", "content"];
  const invalidCacheStrategyErrorMessage = `Cache strategy must be one of: ${validCacheStrategies
      .map(strategy => `"${strategy}"`)
      .join(", ")}`;
  
  /**
   * Tests whether a provided cacheStrategy is valid
   * @param {string} cacheStrategy The cache strategy to use
   * @returns {boolean} true if `cacheStrategy` is one of `validCacheStrategies`; false otherwise
   */
  function isValidCacheStrategy(cacheStrategy) {
      return (
          validCacheStrategies.includes(cacheStrategy)
      );
  }
  
  /**
   * 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.
       * @param {"metadata" | "content"} cacheStrategy The cache strategy to use.
       */
      constructor(cacheFileLocation, cacheStrategy) {
          assert(cacheFileLocation, "Cache file location is required");
          assert(cacheStrategy, "Cache strategy is required");
          assert(
              isValidCacheStrategy(cacheStrategy),
              invalidCacheStrategyErrorMessage
          );
  
          debug(`Caching results to ${cacheFileLocation}`);
  
          const useChecksum = cacheStrategy === "content";
  
          debug(
              `Using "${cacheStrategy}" strategy to detect changes`
          );
  
          this.fileEntryCache = fileEntryCache.create(
              cacheFileLocation,
              void 0,
              useChecksum
          );
          this.cacheFileLocation = 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) {
              debug(`File not found on the file system: ${filePath}`);
              return null;
          }
  
          if (changed) {
              debug(`Cache entry not found or no longer valid: ${filePath}`);
              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
          ) {
              debug(`Rereading cached result source from filesystem: ${filePath}`);
              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) {
              debug(`Updating cached result: ${filePath}`);
  
              // 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() {
          debug(`Persisting cached results: ${this.cacheFileLocation}`);
          this.fileEntryCache.reconcile();
      }
  }
  
  module.exports = LintResultCache;
  