UNPKG

5.52 kBJavaScriptView Raw
1/**
2 * @fileoverview Utility for caching lint results.
3 * @author Kevin Partington
4 */
5"use strict";
6
7//-----------------------------------------------------------------------------
8// Requirements
9//-----------------------------------------------------------------------------
10
11const assert = require("assert"),
12 fs = require("fs"),
13 fileEntryCache = require("file-entry-cache"),
14 hash = require("./hash"),
15 pkg = require("../../package.json"),
16 stringify = require("json-stable-stringify-without-jsonify");
17
18//-----------------------------------------------------------------------------
19// Helpers
20//-----------------------------------------------------------------------------
21
22const configHashCache = new WeakMap();
23
24/**
25 * Calculates the hash of the config file used to validate a given file
26 * @param {Object} configHelper The config helper for retrieving configuration information
27 * @param {string} filename The path of the file to retrieve a config object for to calculate the hash
28 * @returns {string} The hash of the config
29 */
30function hashOfConfigFor(configHelper, filename) {
31 const config = configHelper.getConfig(filename);
32
33 if (!configHashCache.has(config)) {
34 configHashCache.set(config, hash(`${pkg.version}_${stringify(config)}`));
35 }
36
37 return configHashCache.get(config);
38}
39
40//-----------------------------------------------------------------------------
41// Public Interface
42//-----------------------------------------------------------------------------
43
44/**
45 * Lint result cache. This wraps around the file-entry-cache module,
46 * transparently removing properties that are difficult or expensive to
47 * serialize and adding them back in on retrieval.
48 */
49class LintResultCache {
50
51 /**
52 * Creates a new LintResultCache instance.
53 * @constructor
54 * @param {string} cacheFileLocation The cache file location.
55 * @param {Object} configHelper The configuration helper (used for
56 * configuration lookup by file path).
57 */
58 constructor(cacheFileLocation, configHelper) {
59 assert(cacheFileLocation, "Cache file location is required");
60 assert(configHelper, "Config helper is required");
61
62 this.fileEntryCache = fileEntryCache.create(cacheFileLocation);
63 this.configHelper = configHelper;
64 }
65
66 /**
67 * Retrieve cached lint results for a given file path, if present in the
68 * cache. If the file is present and has not been changed, rebuild any
69 * missing result information.
70 * @param {string} filePath The file for which to retrieve lint results.
71 * @returns {Object|null} The rebuilt lint results, or null if the file is
72 * changed or not in the filesystem.
73 */
74 getCachedLintResults(filePath) {
75
76 /*
77 * Cached lint results are valid if and only if:
78 * 1. The file is present in the filesystem
79 * 2. The file has not changed since the time it was previously linted
80 * 3. The ESLint configuration has not changed since the time the file
81 * was previously linted
82 * If any of these are not true, we will not reuse the lint results.
83 */
84
85 const fileDescriptor = this.fileEntryCache.getFileDescriptor(filePath);
86 const hashOfConfig = hashOfConfigFor(this.configHelper, filePath);
87 const changed = fileDescriptor.changed || fileDescriptor.meta.hashOfConfig !== hashOfConfig;
88
89 if (fileDescriptor.notFound || changed) {
90 return null;
91 }
92
93 // If source is present but null, need to reread the file from the filesystem.
94 if (fileDescriptor.meta.results && fileDescriptor.meta.results.source === null) {
95 fileDescriptor.meta.results.source = fs.readFileSync(filePath, "utf-8");
96 }
97
98 return fileDescriptor.meta.results;
99 }
100
101 /**
102 * Set the cached lint results for a given file path, after removing any
103 * information that will be both unnecessary and difficult to serialize.
104 * Avoids caching results with an "output" property (meaning fixes were
105 * applied), to prevent potentially incorrect results if fixes are not
106 * written to disk.
107 * @param {string} filePath The file for which to set lint results.
108 * @param {Object} result The lint result to be set for the file.
109 * @returns {void}
110 */
111 setCachedLintResults(filePath, result) {
112 if (result && Object.prototype.hasOwnProperty.call(result, "output")) {
113 return;
114 }
115
116 const fileDescriptor = this.fileEntryCache.getFileDescriptor(filePath);
117
118 if (fileDescriptor && !fileDescriptor.notFound) {
119
120 // Serialize the result, except that we want to remove the file source if present.
121 const resultToSerialize = Object.assign({}, result);
122
123 /*
124 * Set result.source to null.
125 * In `getCachedLintResults`, if source is explicitly null, we will
126 * read the file from the filesystem to set the value again.
127 */
128 if (Object.prototype.hasOwnProperty.call(resultToSerialize, "source")) {
129 resultToSerialize.source = null;
130 }
131
132 fileDescriptor.meta.results = resultToSerialize;
133 fileDescriptor.meta.hashOfConfig = hashOfConfigFor(this.configHelper, result.filePath);
134 }
135 }
136
137 /**
138 * Persists the in-memory cache to disk.
139 * @returns {void}
140 */
141 reconcile() {
142 this.fileEntryCache.reconcile();
143 }
144}
145
146module.exports = LintResultCache;