file-entry-cache
Advanced tools
Comparing version 8.0.0 to 9.0.0
411
cache.js
@@ -1,41 +0,43 @@ | ||
var path = require('path'); | ||
var crypto = require('crypto'); | ||
/* eslint-disable unicorn/no-this-assignment, func-names, no-multi-assign */ | ||
const path = require('node:path'); | ||
const process = require('node:process'); | ||
const crypto = require('node:crypto'); | ||
module.exports = { | ||
createFromFile: function (filePath, useChecksum) { | ||
var fname = path.basename(filePath); | ||
var dir = path.dirname(filePath); | ||
return this.create(fname, dir, useChecksum); | ||
}, | ||
createFromFile(filePath, useChecksum) { | ||
const fname = path.basename(filePath); | ||
const 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 = {}; | ||
create(cacheId, _path, useChecksum) { | ||
const fs = require('node:fs'); | ||
const flatCache = require('flat-cache'); | ||
const cache = flatCache.load(cacheId, _path); | ||
let normalizedEntries = {}; | ||
var removeNotFoundFiles = function removeNotFoundFiles() { | ||
const cachedEntries = cache.keys(); | ||
// remove not found entries | ||
cachedEntries.forEach(function remover(fPath) { | ||
try { | ||
fs.statSync(fPath); | ||
} catch (err) { | ||
if (err.code === 'ENOENT') { | ||
cache.removeKey(fPath); | ||
} | ||
} | ||
}); | ||
}; | ||
const removeNotFoundFiles = function removeNotFoundFiles() { | ||
const cachedEntries = cache.keys(); | ||
// Remove not found entries | ||
for (const fPath of cachedEntries) { | ||
try { | ||
fs.statSync(fPath); | ||
} catch (error) { | ||
if (error.code === 'ENOENT') { | ||
cache.removeKey(fPath); | ||
} | ||
} | ||
} | ||
}; | ||
removeNotFoundFiles(); | ||
removeNotFoundFiles(); | ||
return { | ||
/** | ||
* the flat cache storage used to persist the metadata of the `files | ||
return { | ||
/** | ||
* The flat cache storage used to persist the metadata of the `files | ||
* @type {Object} | ||
*/ | ||
cache: cache, | ||
cache, | ||
/** | ||
/** | ||
* Given a buffer, calculate md5 hash of its content. | ||
@@ -46,7 +48,7 @@ * @method getHash | ||
*/ | ||
getHash: function (buffer) { | ||
return crypto.createHash('md5').update(buffer).digest('hex'); | ||
}, | ||
getHash(buffer) { | ||
return crypto.createHash('md5').update(buffer).digest('hex'); | ||
}, | ||
/** | ||
/** | ||
* Return whether or not a file has changed since last time reconcile was called. | ||
@@ -57,8 +59,8 @@ * @method hasFileChanged | ||
*/ | ||
hasFileChanged: function (file) { | ||
return this.getFileDescriptor(file).changed; | ||
}, | ||
hasFileChanged(file) { | ||
return this.getFileDescriptor(file).changed; | ||
}, | ||
/** | ||
* given an array of file paths it return and object with three arrays: | ||
/** | ||
* Given an array of file paths it return and object with three arrays: | ||
* - changedFiles: Files that changed since previous run | ||
@@ -71,99 +73,106 @@ * - notChangedFiles: Files that haven't change | ||
*/ | ||
analyzeFiles: function (files) { | ||
var me = this; | ||
files = files || []; | ||
analyzeFiles(files) { | ||
const me = this; | ||
files ||= []; | ||
var res = { | ||
changedFiles: [], | ||
notFoundFiles: [], | ||
notChangedFiles: [], | ||
}; | ||
const 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; | ||
}, | ||
for (const entry of me.normalizeEntries(files)) { | ||
if (entry.changed) { | ||
res.changedFiles.push(entry.key); | ||
continue; | ||
} | ||
getFileDescriptor: function (file) { | ||
var fstat; | ||
if (entry.notFound) { | ||
res.notFoundFiles.push(entry.key); | ||
continue; | ||
} | ||
try { | ||
fstat = fs.statSync(file); | ||
} catch (ex) { | ||
this.removeEntry(file); | ||
return { key: file, notFound: true, err: ex }; | ||
} | ||
res.notChangedFiles.push(entry.key); | ||
} | ||
if (useChecksum) { | ||
return this._getFileDescriptorUsingChecksum(file); | ||
} | ||
return res; | ||
}, | ||
return this._getFileDescriptorUsingMtimeAndSize(file, fstat); | ||
}, | ||
getFileDescriptor(file) { | ||
let fstat; | ||
_getFileDescriptorUsingMtimeAndSize: function (file, fstat) { | ||
var meta = cache.getKey(file); | ||
var cacheExists = !!meta; | ||
try { | ||
if (!path.isAbsolute(file)) { | ||
file = path.resolve(process.cwd(), file); | ||
} | ||
var cSize = fstat.size; | ||
var cTime = fstat.mtime.getTime(); | ||
fstat = fs.statSync(file); | ||
} catch (error) { | ||
this.removeEntry(file); | ||
return {key: file, notFound: true, err: error}; | ||
} | ||
var isDifferentDate; | ||
var isDifferentSize; | ||
if (useChecksum) { | ||
return this._getFileDescriptorUsingChecksum(file); | ||
} | ||
if (!meta) { | ||
meta = { size: cSize, mtime: cTime }; | ||
} else { | ||
isDifferentDate = cTime !== meta.mtime; | ||
isDifferentSize = cSize !== meta.size; | ||
} | ||
return this._getFileDescriptorUsingMtimeAndSize(file, fstat); | ||
}, | ||
var nEntry = (normalizedEntries[file] = { | ||
key: file, | ||
changed: !cacheExists || isDifferentDate || isDifferentSize, | ||
meta: meta, | ||
}); | ||
_getFileDescriptorUsingMtimeAndSize(file, fstat) { | ||
let meta = cache.getKey(file); | ||
const cacheExists = Boolean(meta); | ||
return nEntry; | ||
}, | ||
const cSize = fstat.size; | ||
const cTime = fstat.mtime.getTime(); | ||
_getFileDescriptorUsingChecksum: function (file) { | ||
var meta = cache.getKey(file); | ||
var cacheExists = !!meta; | ||
let isDifferentDate; | ||
let isDifferentSize; | ||
var contentBuffer; | ||
try { | ||
contentBuffer = fs.readFileSync(file); | ||
} catch (ex) { | ||
contentBuffer = ''; | ||
} | ||
if (meta) { | ||
isDifferentDate = cTime !== meta.mtime; | ||
isDifferentSize = cSize !== meta.size; | ||
} else { | ||
meta = {size: cSize, mtime: cTime}; | ||
} | ||
var isDifferent = true; | ||
var hash = this.getHash(contentBuffer); | ||
const nEntry = (normalizedEntries[file] = { | ||
key: file, | ||
changed: !cacheExists || isDifferentDate || isDifferentSize, | ||
meta, | ||
}); | ||
if (!meta) { | ||
meta = { hash: hash }; | ||
} else { | ||
isDifferent = hash !== meta.hash; | ||
} | ||
return nEntry; | ||
}, | ||
var nEntry = (normalizedEntries[file] = { | ||
key: file, | ||
changed: !cacheExists || isDifferent, | ||
meta: meta, | ||
}); | ||
_getFileDescriptorUsingChecksum(file) { | ||
let meta = cache.getKey(file); | ||
const cacheExists = Boolean(meta); | ||
return nEntry; | ||
}, | ||
let contentBuffer; | ||
try { | ||
contentBuffer = fs.readFileSync(file); | ||
} catch { | ||
contentBuffer = ''; | ||
} | ||
/** | ||
let isDifferent = true; | ||
const hash = this.getHash(contentBuffer); | ||
if (meta) { | ||
isDifferent = hash !== meta.hash; | ||
} else { | ||
meta = {hash}; | ||
} | ||
const nEntry = (normalizedEntries[file] = { | ||
key: file, | ||
changed: !cacheExists || isDifferent, | ||
meta, | ||
}); | ||
return nEntry; | ||
}, | ||
/** | ||
* Return the list o the files that changed compared | ||
@@ -176,18 +185,14 @@ * against the ones stored in the cache | ||
*/ | ||
getUpdatedFiles: function (files) { | ||
var me = this; | ||
files = files || []; | ||
getUpdatedFiles(files) { | ||
const me = this; | ||
files ||= []; | ||
return me | ||
.normalizeEntries(files) | ||
.filter(function (entry) { | ||
return entry.changed; | ||
}) | ||
.map(function (entry) { | ||
return entry.key; | ||
}); | ||
}, | ||
return me | ||
.normalizeEntries(files) | ||
.filter(entry => entry.changed) | ||
.map(entry => entry.key); | ||
}, | ||
/** | ||
* return the list of files | ||
/** | ||
* Return the list of files | ||
* @method normalizeEntries | ||
@@ -197,15 +202,13 @@ * @param files | ||
*/ | ||
normalizeEntries: function (files) { | ||
files = files || []; | ||
normalizeEntries(files) { | ||
files ||= []; | ||
var me = this; | ||
var nEntries = files.map(function (file) { | ||
return me.getFileDescriptor(file); | ||
}); | ||
const me = this; | ||
const nEntries = files.map(file => me.getFileDescriptor(file)); | ||
//normalizeEntries = nEntries; | ||
return nEntries; | ||
}, | ||
// NormalizeEntries = nEntries; | ||
return nEntries; | ||
}, | ||
/** | ||
/** | ||
* Remove an entry from the file-entry-cache. Useful to force the file to still be considered | ||
@@ -217,81 +220,85 @@ * modified the next time the process is run | ||
*/ | ||
removeEntry: function (entryName) { | ||
delete normalizedEntries[entryName]; | ||
cache.removeKey(entryName); | ||
}, | ||
removeEntry(entryName) { | ||
if (!path.isAbsolute(entryName)) { | ||
entryName = path.resolve(process.cwd(), entryName); | ||
} | ||
/** | ||
delete normalizedEntries[entryName]; | ||
cache.removeKey(entryName); | ||
}, | ||
/** | ||
* Delete the cache file from the disk | ||
* @method deleteCacheFile | ||
*/ | ||
deleteCacheFile: function () { | ||
cache.removeCacheFile(); | ||
}, | ||
deleteCacheFile() { | ||
cache.removeCacheFile(); | ||
}, | ||
/** | ||
* remove the cache from the file and clear the memory cache | ||
/** | ||
* Remove the cache from the file and clear the memory cache | ||
*/ | ||
destroy: function () { | ||
normalizedEntries = {}; | ||
cache.destroy(); | ||
}, | ||
destroy() { | ||
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; | ||
}, | ||
_getMetaForFileUsingCheckSum(cacheEntry) { | ||
const contentBuffer = fs.readFileSync(cacheEntry.key); | ||
const hash = this.getHash(contentBuffer); | ||
const meta = Object.assign(cacheEntry.meta, {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; | ||
}, | ||
_getMetaForFileUsingMtimeAndSize(cacheEntry) { | ||
const stat = fs.statSync(cacheEntry.key); | ||
const meta = Object.assign(cacheEntry.meta, { | ||
size: stat.size, | ||
mtime: stat.mtime.getTime(), | ||
}); | ||
delete meta.hash; | ||
return meta; | ||
}, | ||
/** | ||
/** | ||
* Sync the files and persist them to the cache | ||
* @method reconcile | ||
*/ | ||
reconcile: function (noPrune) { | ||
removeNotFoundFiles(); | ||
reconcile(noPrune) { | ||
removeNotFoundFiles(); | ||
noPrune = typeof noPrune === 'undefined' ? true : noPrune; | ||
noPrune = noPrune === undefined ? true : noPrune; | ||
var entries = normalizedEntries; | ||
var keys = Object.keys(entries); | ||
const entries = normalizedEntries; | ||
const keys = Object.keys(entries); | ||
if (keys.length === 0) { | ||
return; | ||
} | ||
if (keys.length === 0) { | ||
return; | ||
} | ||
var me = this; | ||
const me = this; | ||
keys.forEach(function (entryName) { | ||
var cacheEntry = entries[entryName]; | ||
for (const entryName of keys) { | ||
const cacheEntry = entries[entryName]; | ||
try { | ||
var meta = useChecksum | ||
? me._getMetaForFileUsingCheckSum(cacheEntry) | ||
: me._getMetaForFileUsingMtimeAndSize(cacheEntry); | ||
cache.setKey(entryName, meta); | ||
} catch (err) { | ||
// if the file does not exists we don't save it | ||
// other errors are just thrown | ||
if (err.code !== 'ENOENT') { | ||
throw err; | ||
} | ||
} | ||
}); | ||
try { | ||
const meta = useChecksum | ||
? me._getMetaForFileUsingCheckSum(cacheEntry) | ||
: me._getMetaForFileUsingMtimeAndSize(cacheEntry); | ||
cache.setKey(entryName, meta); | ||
} catch (error) { | ||
// If the file does not exists we don't save it | ||
// other errors are just thrown | ||
if (error.code !== 'ENOENT') { | ||
throw error; | ||
} | ||
} | ||
} | ||
cache.save(noPrune); | ||
}, | ||
}; | ||
}, | ||
cache.save(noPrune); | ||
}, | ||
}; | ||
}, | ||
}; |
{ | ||
"name": "file-entry-cache", | ||
"version": "8.0.0", | ||
"version": "9.0.0", | ||
"description": "Super simple cache for file metadata, useful for process that work o a given series of files and that only need to repeat the job on the changed ones since the previous run of the process", | ||
@@ -16,17 +16,15 @@ "repository": "jaredwray/file-entry-cache", | ||
"engines": { | ||
"node": ">=16.0.0" | ||
"node": ">=18" | ||
}, | ||
"scripts": { | ||
"eslint": "eslint --cache --cache-location=node_modules/.cache/ 'cache.js' 'test/**/*.js' 'perf.js'", | ||
"autofix": "npm run eslint -- --fix", | ||
"clean": "rimraf ./node_modules ./package-lock.json ./yarn.lock", | ||
"test": "npm run eslint --silent && c8 mocha -R spec test/specs", | ||
"test:ci": "npm run eslint --silent && c8 --reporter=lcov mocha -R spec test/specs", | ||
"clean": "rimraf ./coverage /node_modules ./package-lock.json ./yarn.lock ./pnpm-lock.yaml", | ||
"test": "xo --fix && c8 mocha -R spec test/specs", | ||
"test:ci": "xo && c8 --reporter=lcov mocha -R spec test/specs", | ||
"perf": "node perf.js" | ||
}, | ||
"prepush": [ | ||
"npm run eslint --silent" | ||
"npm run test" | ||
], | ||
"precommit": [ | ||
"npm run eslint --silent" | ||
"npm run test" | ||
], | ||
@@ -42,17 +40,21 @@ "keywords": [ | ||
"devDependencies": { | ||
"c8": "^8.0.1", | ||
"c8": "^9.1.0", | ||
"chai": "^4.3.10", | ||
"eslint": "^8.56.0", | ||
"eslint-config-prettier": "^9.1.0", | ||
"eslint-plugin-mocha": "^10.2.0", | ||
"eslint-plugin-prettier": "^5.0.1", | ||
"glob-expand": "^0.2.1", | ||
"mocha": "^10.2.0", | ||
"prettier": "^3.1.1", | ||
"rimraf": "^5.0.5", | ||
"write": "^2.0.0" | ||
"mocha": "^10.4.0", | ||
"rimraf": "^5.0.7", | ||
"webpack": "^5.91.0", | ||
"write": "^2.0.0", | ||
"xo": "^0.58.0" | ||
}, | ||
"dependencies": { | ||
"flat-cache": "^4.0.0" | ||
"flat-cache": "^5.0.0" | ||
}, | ||
"xo": { | ||
"rules": { | ||
"unicorn/prefer-module": "off", | ||
"n/prefer-global/process": "off", | ||
"unicorn/prevent-abbreviations": "off" | ||
} | ||
} | ||
} |
# file-entry-cache | ||
> Super simple cache for file metadata, useful for process that work on a given series of files | ||
> and that only need to repeat the job on the changed ones since the previous run of the process — Edit | ||
> Super simple cache for file metadata, useful for process that work on a given series of files and that only need to repeat the job on the changed ones since the previous run of the process | ||
@@ -5,0 +4,0 @@ [![NPM Version](https://img.shields.io/npm/v/file-entry-cache.svg?style=flat)](https://npmjs.org/package/file-entry-cache) |
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
8
249
1
15142
115
+ Addedflat-cache@5.0.0(transitive)
- Removedflat-cache@4.0.1(transitive)
Updatedflat-cache@^5.0.0