.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / file-entry-cache / cache.js
1 var path = require('path');
2 var crypto = require('crypto');
3
4 module.exports = {
5   createFromFile: function (filePath, useChecksum) {
6     var fname = path.basename(filePath);
7     var dir = path.dirname(filePath);
8     return this.create(fname, dir, useChecksum);
9   },
10
11   create: function (cacheId, _path, useChecksum) {
12     var fs = require('fs');
13     var flatCache = require('flat-cache');
14     var cache = flatCache.load(cacheId, _path);
15     var normalizedEntries = {};
16
17     var removeNotFoundFiles = function removeNotFoundFiles() {
18       const cachedEntries = cache.keys();
19       // remove not found entries
20       cachedEntries.forEach(function remover(fPath) {
21         try {
22           fs.statSync(fPath);
23         } catch (err) {
24           if (err.code === 'ENOENT') {
25             cache.removeKey(fPath);
26           }
27         }
28       });
29     };
30
31     removeNotFoundFiles();
32
33     return {
34       /**
35        * the flat cache storage used to persist the metadata of the `files
36        * @type {Object}
37        */
38       cache: cache,
39
40       /**
41        * Given a buffer, calculate md5 hash of its content.
42        * @method getHash
43        * @param  {Buffer} buffer   buffer to calculate hash on
44        * @return {String}          content hash digest
45        */
46       getHash: function (buffer) {
47         return crypto.createHash('md5').update(buffer).digest('hex');
48       },
49
50       /**
51        * Return whether or not a file has changed since last time reconcile was called.
52        * @method hasFileChanged
53        * @param  {String}  file  the filepath to check
54        * @return {Boolean}       wheter or not the file has changed
55        */
56       hasFileChanged: function (file) {
57         return this.getFileDescriptor(file).changed;
58       },
59
60       /**
61        * given an array of file paths it return and object with three arrays:
62        *  - changedFiles: Files that changed since previous run
63        *  - notChangedFiles: Files that haven't change
64        *  - notFoundFiles: Files that were not found, probably deleted
65        *
66        * @param  {Array} files the files to analyze and compare to the previous seen files
67        * @return {[type]}       [description]
68        */
69       analyzeFiles: function (files) {
70         var me = this;
71         files = files || [];
72
73         var res = {
74           changedFiles: [],
75           notFoundFiles: [],
76           notChangedFiles: [],
77         };
78
79         me.normalizeEntries(files).forEach(function (entry) {
80           if (entry.changed) {
81             res.changedFiles.push(entry.key);
82             return;
83           }
84           if (entry.notFound) {
85             res.notFoundFiles.push(entry.key);
86             return;
87           }
88           res.notChangedFiles.push(entry.key);
89         });
90         return res;
91       },
92
93       getFileDescriptor: function (file) {
94         var fstat;
95
96         try {
97           fstat = fs.statSync(file);
98         } catch (ex) {
99           this.removeEntry(file);
100           return { key: file, notFound: true, err: ex };
101         }
102
103         if (useChecksum) {
104           return this._getFileDescriptorUsingChecksum(file);
105         }
106
107         return this._getFileDescriptorUsingMtimeAndSize(file, fstat);
108       },
109
110       _getFileDescriptorUsingMtimeAndSize: function (file, fstat) {
111         var meta = cache.getKey(file);
112         var cacheExists = !!meta;
113
114         var cSize = fstat.size;
115         var cTime = fstat.mtime.getTime();
116
117         var isDifferentDate;
118         var isDifferentSize;
119
120         if (!meta) {
121           meta = { size: cSize, mtime: cTime };
122         } else {
123           isDifferentDate = cTime !== meta.mtime;
124           isDifferentSize = cSize !== meta.size;
125         }
126
127         var nEntry = (normalizedEntries[file] = {
128           key: file,
129           changed: !cacheExists || isDifferentDate || isDifferentSize,
130           meta: meta,
131         });
132
133         return nEntry;
134       },
135
136       _getFileDescriptorUsingChecksum: function (file) {
137         var meta = cache.getKey(file);
138         var cacheExists = !!meta;
139
140         var contentBuffer;
141         try {
142           contentBuffer = fs.readFileSync(file);
143         } catch (ex) {
144           contentBuffer = '';
145         }
146
147         var isDifferent = true;
148         var hash = this.getHash(contentBuffer);
149
150         if (!meta) {
151           meta = { hash: hash };
152         } else {
153           isDifferent = hash !== meta.hash;
154         }
155
156         var nEntry = (normalizedEntries[file] = {
157           key: file,
158           changed: !cacheExists || isDifferent,
159           meta: meta,
160         });
161
162         return nEntry;
163       },
164
165       /**
166        * Return the list o the files that changed compared
167        * against the ones stored in the cache
168        *
169        * @method getUpdated
170        * @param files {Array} the array of files to compare against the ones in the cache
171        * @returns {Array}
172        */
173       getUpdatedFiles: function (files) {
174         var me = this;
175         files = files || [];
176
177         return me
178           .normalizeEntries(files)
179           .filter(function (entry) {
180             return entry.changed;
181           })
182           .map(function (entry) {
183             return entry.key;
184           });
185       },
186
187       /**
188        * return the list of files
189        * @method normalizeEntries
190        * @param files
191        * @returns {*}
192        */
193       normalizeEntries: function (files) {
194         files = files || [];
195
196         var me = this;
197         var nEntries = files.map(function (file) {
198           return me.getFileDescriptor(file);
199         });
200
201         //normalizeEntries = nEntries;
202         return nEntries;
203       },
204
205       /**
206        * Remove an entry from the file-entry-cache. Useful to force the file to still be considered
207        * modified the next time the process is run
208        *
209        * @method removeEntry
210        * @param entryName
211        */
212       removeEntry: function (entryName) {
213         delete normalizedEntries[entryName];
214         cache.removeKey(entryName);
215       },
216
217       /**
218        * Delete the cache file from the disk
219        * @method deleteCacheFile
220        */
221       deleteCacheFile: function () {
222         cache.removeCacheFile();
223       },
224
225       /**
226        * remove the cache from the file and clear the memory cache
227        */
228       destroy: function () {
229         normalizedEntries = {};
230         cache.destroy();
231       },
232
233       _getMetaForFileUsingCheckSum: function (cacheEntry) {
234         var contentBuffer = fs.readFileSync(cacheEntry.key);
235         var hash = this.getHash(contentBuffer);
236         var meta = Object.assign(cacheEntry.meta, { hash: hash });
237         delete meta.size;
238         delete meta.mtime;
239         return meta;
240       },
241
242       _getMetaForFileUsingMtimeAndSize: function (cacheEntry) {
243         var stat = fs.statSync(cacheEntry.key);
244         var meta = Object.assign(cacheEntry.meta, {
245           size: stat.size,
246           mtime: stat.mtime.getTime(),
247         });
248         delete meta.hash;
249         return meta;
250       },
251
252       /**
253        * Sync the files and persist them to the cache
254        * @method reconcile
255        */
256       reconcile: function (noPrune) {
257         removeNotFoundFiles();
258
259         noPrune = typeof noPrune === 'undefined' ? true : noPrune;
260
261         var entries = normalizedEntries;
262         var keys = Object.keys(entries);
263
264         if (keys.length === 0) {
265           return;
266         }
267
268         var me = this;
269
270         keys.forEach(function (entryName) {
271           var cacheEntry = entries[entryName];
272
273           try {
274             var meta = useChecksum
275               ? me._getMetaForFileUsingCheckSum(cacheEntry)
276               : me._getMetaForFileUsingMtimeAndSize(cacheEntry);
277             cache.setKey(entryName, meta);
278           } catch (err) {
279             // if the file does not exists we don't save it
280             // other errors are just thrown
281             if (err.code !== 'ENOENT') {
282               throw err;
283             }
284           }
285         });
286
287         cache.save(noPrune);
288       },
289     };
290   },
291 };