broccoli-persistent-filter
Advanced tools
Comparing version 1.1.0 to 1.1.1
202
index.js
@@ -8,8 +8,5 @@ 'use strict'; | ||
var Plugin = require('broccoli-plugin'); | ||
var helpers = require('broccoli-kitchen-sink-helpers'); | ||
var walkSync = require('walk-sync'); | ||
var mapSeries = require('promise-map-series'); | ||
var symlinkOrCopySync = require('symlink-or-copy').sync; | ||
var copyDereferenceSync = require('copy-dereference').sync; | ||
var Cache = require('./lib/cache'); | ||
var debugGenerator = require('debug'); | ||
@@ -20,8 +17,10 @@ var md5Hex = require('md5-hex'); | ||
var hashForDep = require('hash-for-dep'); | ||
var BlankObject = require('blank-object'); | ||
var FSTree = require('fs-tree-diff'); | ||
module.exports = Filter; | ||
Filter.prototype = Object.create(Plugin.prototype); | ||
Filter.prototype.constructor = Filter; | ||
function Filter(inputTree, options) { | ||
@@ -34,2 +33,3 @@ if (!this || !(this instanceof Filter) || | ||
var name = 'broccoli-persistent-filter:' + (this.constructor.name); | ||
if (this.description) { | ||
@@ -45,13 +45,11 @@ name += ' > [' + this.description + ']'; | ||
this.processor.setStrategy(defaultProccessor); | ||
this.currentTree = new FSTree(); | ||
this._persistentOutput = true; | ||
/* Destructuring assignment in node 0.12.2 would be really handy for this! */ | ||
if (options) { | ||
if (options.extensions != null) | ||
this.extensions = options.extensions; | ||
if (options.targetExtension != null) | ||
this.targetExtension = options.targetExtension; | ||
if (options.inputEncoding != null) | ||
this.inputEncoding = options.inputEncoding; | ||
if (options.outputEncoding != null) | ||
this.outputEncoding = options.outputEncoding; | ||
if (options.extensions != null) this.extensions = options.extensions; | ||
if (options.targetExtension != null) this.targetExtension = options.targetExtension; | ||
if (options.inputEncoding != null) this.inputEncoding = options.inputEncoding; | ||
if (options.outputEncoding != null) this.outputEncoding = options.outputEncoding; | ||
if (options.persist) { | ||
@@ -64,39 +62,40 @@ this.processor.setStrategy(require('./lib/strategies/persistent')); | ||
this._cache = new Cache(); | ||
this._canProcessCache = Object.create(null); | ||
this._destFilePathCache = Object.create(null); | ||
this._canProcessCache = new BlankObject(); | ||
this._destFilePathCache = new BlankObject(); | ||
} | ||
Filter.prototype.build = function build() { | ||
var self = this; | ||
Filter.prototype.build = function() { | ||
var srcDir = this.inputPaths[0]; | ||
var destDir = this.outputPath; | ||
var entries = walkSync.entries(srcDir); | ||
var paths = entries.map(byRelativePath); | ||
var nextTree = new FSTree.fromEntries(entries); | ||
var currentTree = this.currentTree; | ||
this._cache.deleteExcept(paths).forEach(function(key) { | ||
try { | ||
fs.unlinkSync(this.cachePath + '/' + key); | ||
} catch (e) { | ||
if(e.code !== 'ENOENT') { | ||
throw e; | ||
} | ||
this.currentTree = nextTree; | ||
var patches = currentTree.calculatePatch(nextTree); | ||
return mapSeries(patches, function(patch) { | ||
var operation = patch[0]; | ||
var relativePath = patch[1]; | ||
var entry = patch[2]; | ||
var outputPath = destDir + '/' + relativePath; | ||
this._debug('[operation:%s] %s', operation, relativePath); | ||
if (operation === 'change') { | ||
operation = 'create'; | ||
} | ||
}, this); | ||
return mapSeries(paths, function rebuildEntry(relativePath, i) { | ||
var destPath = destDir + '/' + relativePath; | ||
var entry = entries[i]; | ||
if (entry.isDirectory()) { | ||
mkdirp.sync(destPath); | ||
} else { | ||
if (self.canProcessFile(relativePath)) { | ||
return self.processAndCacheFile(srcDir, destDir, entry); | ||
switch (operation) { | ||
case 'mkdir': return fs.mkdirSync(outputPath); | ||
case 'rmdir': return fs.rmdirSync(outputPath); | ||
case 'unlink': return fs.unlinkSync(outputPath); | ||
case 'create': | ||
if (this.canProcessFile(relativePath)) { | ||
return this.processAndCacheFile(srcDir, destDir, entry); | ||
} else { | ||
var srcPath = srcDir + '/' + relativePath; | ||
symlinkOrCopySync(srcPath, destPath); | ||
return symlinkOrCopySync(srcPath, outputPath); | ||
} | ||
} | ||
}); | ||
}, this); | ||
}; | ||
@@ -144,4 +143,6 @@ | ||
Filter.prototype.getDestFilePath = function getDestFilePath(relativePath) { | ||
if (this.extensions == null) return relativePath; | ||
Filter.prototype.getDestFilePath = function(relativePath) { | ||
if (this.extensions == null) { | ||
return relativePath; | ||
} | ||
@@ -152,4 +153,3 @@ for (var i = 0, ii = this.extensions.length; i < ii; ++i) { | ||
if (this.targetExtension != null) { | ||
relativePath = | ||
relativePath.slice(0, -ext.length) + this.targetExtension; | ||
relativePath = relativePath.slice(0, -ext.length) + this.targetExtension; | ||
} | ||
@@ -159,32 +159,16 @@ return relativePath; | ||
} | ||
return null; | ||
}; | ||
Filter.prototype.processAndCacheFile = | ||
function processAndCacheFile(srcDir, destDir, entry) { | ||
var self = this; | ||
Filter.prototype.processAndCacheFile = function(srcDir, destDir, entry) { | ||
var filter = this; | ||
var relativePath = entry.relativePath; | ||
var cacheEntry = this._cache.get(relativePath); | ||
var outputRelativeFile = self.getDestFilePath(relativePath); | ||
var outputRelativeFile = filter.getDestFilePath(relativePath); | ||
if (cacheEntry) { | ||
var hashResult = hash(srcDir, cacheEntry.inputFile, entry); | ||
if (cacheEntry.hash.hash === hashResult.hash) { | ||
this._debug('cache hit: %s', relativePath); | ||
return symlinkOrCopyFromCache(cacheEntry, destDir, outputRelativeFile); | ||
} else { | ||
this._debug('cache miss: %s \n - previous: %o \n - next: %o ', relativePath, cacheEntry.hash.key, hashResult.key); | ||
} | ||
} else { | ||
this._debug('cache prime: %s', relativePath); | ||
} | ||
return Promise.resolve(). | ||
then(function asyncProcessFile() { | ||
return self.processFile(srcDir, destDir, relativePath); | ||
return filter.processFile(srcDir, destDir, relativePath); | ||
}). | ||
then(copyToCache, | ||
then(undefined, | ||
// TODO(@caitp): error wrapper is for API compat, but is not particularly | ||
@@ -199,21 +183,2 @@ // useful. | ||
}); | ||
function copyToCache() { | ||
var cacheEntry = { | ||
hash: hash(srcDir, relativePath, entry), | ||
inputFile: relativePath, | ||
outputFile: destDir + '/' + outputRelativeFile, | ||
cacheFile: self.cachePath + '/' + outputRelativeFile | ||
}; | ||
if (fs.existsSync(cacheEntry.cacheFile)) { | ||
fs.unlinkSync(cacheEntry.cacheFile); | ||
} else { | ||
mkdirp.sync(path.dirname(cacheEntry.cacheFile)); | ||
} | ||
copyDereferenceSync(cacheEntry.outputFile, cacheEntry.cacheFile); | ||
return self._cache.set(relativePath, cacheEntry); | ||
} | ||
}; | ||
@@ -227,25 +192,41 @@ | ||
Filter.prototype.processFile = | ||
function processFile(srcDir, destDir, relativePath) { | ||
var self = this; | ||
Filter.prototype.processFile = function(srcDir, destDir, relativePath) { | ||
var filter = this; | ||
var inputEncoding = this.inputEncoding; | ||
var outputEncoding = this.outputEncoding; | ||
if (inputEncoding === void 0) { inputEncoding = 'utf8'; } | ||
if (outputEncoding === void 0) { outputEncoding = 'utf8'; } | ||
var contents = fs.readFileSync( | ||
srcDir + '/' + relativePath, { encoding: inputEncoding }); | ||
if (inputEncoding === undefined) inputEncoding = 'utf8'; | ||
if (outputEncoding === undefined) outputEncoding = 'utf8'; | ||
var contents = fs.readFileSync(srcDir + '/' + relativePath, { | ||
encoding: inputEncoding | ||
}); | ||
var string = invoke(this.processor, this.processor.processString, [this, contents, relativePath]); | ||
return string.then(function asyncOutputFilteredFile(outputString) { | ||
var outputPath = self.getDestFilePath(relativePath); | ||
var outputPath = filter.getDestFilePath(relativePath); | ||
if (outputPath == null) { | ||
throw new Error('canProcessFile("' + relativePath + '") is true, but getDestFilePath("' + relativePath + '") is null'); | ||
throw new Error('canProcessFile("' + relativePath + | ||
'") is true, but getDestFilePath("' + | ||
relativePath + '") is null'); | ||
} | ||
outputPath = destDir + '/' + outputPath; | ||
mkdirp.sync(path.dirname(outputPath)); | ||
fs.writeFileSync(outputPath, outputString, { | ||
encoding: outputEncoding | ||
}); | ||
try { | ||
fs.writeFileSync(outputPath, outputString, { | ||
encoding: outputEncoding | ||
}); | ||
} catch(e) { | ||
// optimistically assume the DIR was patched correctly | ||
mkdirp.sync(path.dirname(outputPath)); | ||
fs.writeFileSync(outputPath, outputString, { | ||
encoding: outputEncoding | ||
}); | ||
} | ||
return outputString; | ||
@@ -255,4 +236,3 @@ }); | ||
Filter.prototype.processString = | ||
function unimplementedProcessString(contents, relativePath) { | ||
Filter.prototype.processString = function(/* contents, relativePath */) { | ||
throw new Error( | ||
@@ -262,29 +242,1 @@ 'When subclassing cauliflower-filter you must implement the ' + | ||
}; | ||
function hash(src, relativePath, entry) { | ||
var path = src + '/' + relativePath; | ||
if (entry.isDirectory()) { | ||
throw new Error('cannot diff directory'); | ||
} | ||
return { | ||
key: entry, | ||
hash: helpers.hashStrings([ | ||
path, | ||
entry.size, | ||
entry.mode, | ||
entry.mtime | ||
]) | ||
}; | ||
} | ||
function symlinkOrCopyFromCache(entry, dest, relativePath) { | ||
mkdirp.sync(path.dirname(entry.outputFile)); | ||
symlinkOrCopySync(entry.cacheFile, dest + '/' + relativePath); | ||
} | ||
function byRelativePath (entry) { | ||
return entry.relativePath; | ||
} |
@@ -0,1 +1,5 @@ | ||
'use strict'; | ||
module.exports = Processor; | ||
function Processor(options) { | ||
@@ -18,3 +22,1 @@ options = options || {}; | ||
}; | ||
module.exports = Processor; |
@@ -0,1 +1,3 @@ | ||
'use strict'; | ||
var AsyncDiskCache = require('async-disk-cache'); | ||
@@ -2,0 +4,0 @@ var Promise = require('rsvp').Promise; |
{ | ||
"name": "broccoli-persistent-filter", | ||
"version": "1.1.0", | ||
"version": "1.1.1", | ||
"description": "broccoli filter but with a persistent cache", | ||
@@ -32,2 +32,3 @@ "author": "Stefan Penner <stefan.penner@gmail.com>", | ||
"async-disk-cache": "^1.0.0", | ||
"blank-object": "^1.0.1", | ||
"broccoli-kitchen-sink-helpers": "^0.2.7", | ||
@@ -37,2 +38,3 @@ "broccoli-plugin": "^1.0.0", | ||
"debug": "^2.2.0", | ||
"fs-tree-diff": "^0.3.0", | ||
"hash-for-dep": "^1.0.0", | ||
@@ -39,0 +41,0 @@ "md5-hex": "^1.0.2", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
17513
14
8
248
3
+ Addedblank-object@^1.0.1
+ Addedfs-tree-diff@^0.3.0
+ Addedblank-object@1.0.2(transitive)
+ Addedfast-ordered-set@1.0.3(transitive)
+ Addedfs-tree-diff@0.3.1(transitive)