/**
							 | 
						|
								 * @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;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        const cachedResults = fileDescriptor.meta.results;
							 | 
						|
								
							 | 
						|
								        // Just in case, not sure if this can ever happen.
							 | 
						|
								        if (!cachedResults) {
							 | 
						|
								            return cachedResults;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /*
							 | 
						|
								         * Shallow clone the object to ensure that any properties added or modified afterwards
							 | 
						|
								         * will not be accidentally stored in the cache file when `reconcile()` is called.
							 | 
						|
								         * https://github.com/eslint/eslint/issues/13507
							 | 
						|
								         * All intentional changes to the cache file must be done through `setCachedLintResults()`.
							 | 
						|
								         */
							 | 
						|
								        const results = { ...cachedResults };
							 | 
						|
								
							 | 
						|
								        // If source is present but null, need to reread the file from the filesystem.
							 | 
						|
								        if (results.source === null) {
							 | 
						|
								            debug(`Rereading cached result source from filesystem: ${filePath}`);
							 | 
						|
								            results.source = fs.readFileSync(filePath, "utf-8");
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        return 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;
							 |