| var path = require('path'); |
| var crypto = require('crypto'); |
|
|
| module.exports = { |
| createFromFile: function (filePath, useChecksum) { |
| var fname = path.basename(filePath); |
| var dir = path.dirname(filePath); |
| return this.create(fname, dir, useChecksum); |
| }, |
|
|
| create: function (cacheId, _path, useChecksum) { |
| var fs = require('fs'); |
| var flatCache = require('flat-cache'); |
| var cache = flatCache.load(cacheId, _path); |
| var normalizedEntries = {}; |
|
|
| var removeNotFoundFiles = function removeNotFoundFiles() { |
| const cachedEntries = cache.keys(); |
| |
| cachedEntries.forEach(function remover(fPath) { |
| try { |
| fs.statSync(fPath); |
| } catch (err) { |
| if (err.code === 'ENOENT') { |
| cache.removeKey(fPath); |
| } |
| } |
| }); |
| }; |
|
|
| removeNotFoundFiles(); |
|
|
| return { |
| |
| |
| |
| |
| cache: cache, |
|
|
| |
| |
| |
| |
| |
| |
| getHash: function (buffer) { |
| return crypto.createHash('md5').update(buffer).digest('hex'); |
| }, |
|
|
| |
| |
| |
| |
| |
| |
| hasFileChanged: function (file) { |
| return this.getFileDescriptor(file).changed; |
| }, |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| analyzeFiles: function (files) { |
| var me = this; |
| files = files || []; |
|
|
| var res = { |
| changedFiles: [], |
| notFoundFiles: [], |
| notChangedFiles: [], |
| }; |
|
|
| me.normalizeEntries(files).forEach(function (entry) { |
| if (entry.changed) { |
| res.changedFiles.push(entry.key); |
| return; |
| } |
| if (entry.notFound) { |
| res.notFoundFiles.push(entry.key); |
| return; |
| } |
| res.notChangedFiles.push(entry.key); |
| }); |
| return res; |
| }, |
|
|
| getFileDescriptor: function (file) { |
| var fstat; |
|
|
| try { |
| fstat = fs.statSync(file); |
| } catch (ex) { |
| this.removeEntry(file); |
| return { key: file, notFound: true, err: ex }; |
| } |
|
|
| if (useChecksum) { |
| return this._getFileDescriptorUsingChecksum(file); |
| } |
|
|
| return this._getFileDescriptorUsingMtimeAndSize(file, fstat); |
| }, |
|
|
| _getFileDescriptorUsingMtimeAndSize: function (file, fstat) { |
| var meta = cache.getKey(file); |
| var cacheExists = !!meta; |
|
|
| var cSize = fstat.size; |
| var cTime = fstat.mtime.getTime(); |
|
|
| var isDifferentDate; |
| var isDifferentSize; |
|
|
| if (!meta) { |
| meta = { size: cSize, mtime: cTime }; |
| } else { |
| isDifferentDate = cTime !== meta.mtime; |
| isDifferentSize = cSize !== meta.size; |
| } |
|
|
| var nEntry = (normalizedEntries[file] = { |
| key: file, |
| changed: !cacheExists || isDifferentDate || isDifferentSize, |
| meta: meta, |
| }); |
|
|
| return nEntry; |
| }, |
|
|
| _getFileDescriptorUsingChecksum: function (file) { |
| var meta = cache.getKey(file); |
| var cacheExists = !!meta; |
|
|
| var contentBuffer; |
| try { |
| contentBuffer = fs.readFileSync(file); |
| } catch (ex) { |
| contentBuffer = ''; |
| } |
|
|
| var isDifferent = true; |
| var hash = this.getHash(contentBuffer); |
|
|
| if (!meta) { |
| meta = { hash: hash }; |
| } else { |
| isDifferent = hash !== meta.hash; |
| } |
|
|
| var nEntry = (normalizedEntries[file] = { |
| key: file, |
| changed: !cacheExists || isDifferent, |
| meta: meta, |
| }); |
|
|
| return nEntry; |
| }, |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| getUpdatedFiles: function (files) { |
| var me = this; |
| files = files || []; |
|
|
| return me |
| .normalizeEntries(files) |
| .filter(function (entry) { |
| return entry.changed; |
| }) |
| .map(function (entry) { |
| return entry.key; |
| }); |
| }, |
|
|
| |
| |
| |
| |
| |
| |
| normalizeEntries: function (files) { |
| files = files || []; |
|
|
| var me = this; |
| var nEntries = files.map(function (file) { |
| return me.getFileDescriptor(file); |
| }); |
|
|
| |
| return nEntries; |
| }, |
|
|
| |
| |
| |
| |
| |
| |
| |
| removeEntry: function (entryName) { |
| delete normalizedEntries[entryName]; |
| cache.removeKey(entryName); |
| }, |
|
|
| |
| |
| |
| |
| deleteCacheFile: function () { |
| cache.removeCacheFile(); |
| }, |
|
|
| |
| |
| |
| destroy: function () { |
| normalizedEntries = {}; |
| cache.destroy(); |
| }, |
|
|
| _getMetaForFileUsingCheckSum: function (cacheEntry) { |
| var contentBuffer = fs.readFileSync(cacheEntry.key); |
| var hash = this.getHash(contentBuffer); |
| var meta = Object.assign(cacheEntry.meta, { hash: hash }); |
| delete meta.size; |
| delete meta.mtime; |
| return meta; |
| }, |
|
|
| _getMetaForFileUsingMtimeAndSize: function (cacheEntry) { |
| var stat = fs.statSync(cacheEntry.key); |
| var meta = Object.assign(cacheEntry.meta, { |
| size: stat.size, |
| mtime: stat.mtime.getTime(), |
| }); |
| delete meta.hash; |
| return meta; |
| }, |
|
|
| |
| |
| |
| |
| reconcile: function (noPrune) { |
| removeNotFoundFiles(); |
|
|
| noPrune = typeof noPrune === 'undefined' ? true : noPrune; |
|
|
| var entries = normalizedEntries; |
| var keys = Object.keys(entries); |
|
|
| if (keys.length === 0) { |
| return; |
| } |
|
|
| var me = this; |
|
|
| keys.forEach(function (entryName) { |
| var cacheEntry = entries[entryName]; |
|
|
| try { |
| var meta = useChecksum |
| ? me._getMetaForFileUsingCheckSum(cacheEntry) |
| : me._getMetaForFileUsingMtimeAndSize(cacheEntry); |
| cache.setKey(entryName, meta); |
| } catch (err) { |
| |
| |
| if (err.code !== 'ENOENT') { |
| throw err; |
| } |
| } |
| }); |
|
|
| cache.save(noPrune); |
| }, |
| }; |
| }, |
| }; |
|
|