broccoli-caching-writer
Advanced tools
Comparing version 0.5.4 to 0.5.5
@@ -0,1 +1,9 @@ | ||
## 0.5.5 | ||
* Add ability to debug which files are causing an invalidation of the cache. The following will generate output indicating which path changed: | ||
``` | ||
DEBUG=broccoli-caching-writer broccoli serve # if using broccoli-cli | ||
``` | ||
## 0.5.4 | ||
@@ -2,0 +10,0 @@ |
129
index.js
@@ -12,2 +12,3 @@ var fs = require('fs'); | ||
var CoreObject = require('core-object'); | ||
var debug = require('debug')('broccoli-caching-writer'); | ||
@@ -17,4 +18,4 @@ var proto = {}; | ||
proto.init = function(inputTrees, options) { | ||
this._inputTreeCacheHash = []; | ||
this._lastKeys = []; | ||
this._shouldBeIgnoredCache = Object.create(null); | ||
@@ -80,10 +81,13 @@ this._destDir = path.resolve(path.join('tmp', 'caching-writer-dest-dir_' + generateRandomString(6) + '.tmp')); | ||
var invalidateCache = false; | ||
var keys, dir, updateCacheResult; | ||
var key, dir, updateCacheResult; | ||
var lastKeys = []; | ||
for (var i = 0, l = inputPaths.length; i < l; i++) { | ||
dir = inputPaths[i]; | ||
keys = self.keysForTree(dir); | ||
inputTreeHashes[i] = helpers.hashStrings(keys); | ||
if (self._inputTreeCacheHash[i] !== inputTreeHashes[i]) { | ||
key = self.keyForTree(dir); | ||
lastKey = self._lastKeys[i]; | ||
lastKeys.push(key); | ||
if (!invalidateCache /* short circuit */ && !key.equal(lastKey)) { | ||
invalidateCache = true; | ||
@@ -97,2 +101,4 @@ } | ||
self._lastKeys = lastKeys; | ||
self._inputTreeCacheHash = inputTreeHashes; | ||
@@ -165,7 +171,75 @@ } | ||
var EMPTY_ARRAY; | ||
function Key(type, fullPath, path, stat, children) { | ||
this.type = type; | ||
this.fullPath = fullPath; | ||
this.path = path; | ||
this.stat = stat; | ||
this.children = children || EMPTY_ARRAY; | ||
} | ||
proto.keysForTree = function (fullPath, initialRelativePath) { | ||
Key.prototype.toString = function() { | ||
return ' type: ' + this.type + | ||
' fullPath: ' + this.fullPath + | ||
' path: ' + this.path + | ||
' stat.mode: ' + this.stat.mode + | ||
' stat.size: ' + this.stat.size + | ||
' stat.mtime: ' + this.stat.mtime.getTime(); | ||
}; | ||
Key.prototype.inspect = function() { | ||
return [ | ||
this.type, | ||
this.path, | ||
this.stat.mode, | ||
this.stat.size, | ||
this.stat.size, | ||
this.stat.mtime.getTime() | ||
].join(', '); | ||
}; | ||
function logNotEqual(previous, next) { | ||
debug(" cache eviction due to: \n - {%o} \n - {%o}", previous, next); | ||
} | ||
Key.prototype.equal = function(otherKey) { | ||
if (otherKey === undefined) { | ||
logNotEqual(this, otherKey); | ||
return false; | ||
} | ||
if (this.type === otherKey.type && this.type === 'directory') { | ||
var children = this.children; | ||
var otherChildren = otherKey.children; | ||
if (children.length === otherChildren.length) { | ||
for (var i = 0; i < children.length; i++) { | ||
if (children[i].equal(otherChildren[i])) { | ||
// they are the same | ||
} else { | ||
return false; | ||
} | ||
} | ||
} else { | ||
return false; | ||
} | ||
} | ||
// key represents a file, diff the file | ||
if (this.type === otherKey.type && | ||
this.path === otherKey.path && | ||
this.stat.mode === otherKey.stat.mode && | ||
this.stat.size === otherKey.stat.size && | ||
this.type === 'directory' ? true : this.stat.mtime.getTime() === otherKey.stat.mtime.getTime()) { | ||
return true; | ||
} else { | ||
logNotEqual(this, otherKey); | ||
} | ||
}; | ||
proto.keyForTree = function (fullPath, initialRelativePath) { | ||
var relativePath = initialRelativePath || '.'; | ||
var stats; | ||
var statKeys; | ||
var type; | ||
@@ -179,40 +253,35 @@ try { | ||
} | ||
var childKeys = []; | ||
if (stats) { | ||
statKeys = ['stats', stats.mode]; | ||
} else { | ||
statKeys = ['stat failed']; | ||
} | ||
var children; | ||
// has children; | ||
if (stats && stats.isDirectory()) { | ||
var fileIdentity = stats.dev + '\x00' + stats.ino; | ||
var entries; | ||
type = 'directory'; | ||
var files; | ||
try { | ||
entries = fs.readdirSync(fullPath).sort(); | ||
files = fs.readdirSync(fullPath).sort(); | ||
} catch (err) { | ||
console.warn('Warning: Failed to read directory ' + fullPath); | ||
console.warn(err.stack); | ||
childKeys = ['readdir failed']; | ||
// That's all there is to say about this directory. | ||
} | ||
if (entries) { | ||
for (var i = 0; i < entries.length; i++) { | ||
var keys = this.keysForTree( | ||
path.join(fullPath, entries[i]), | ||
path.join(relativePath, entries[i]) | ||
if (files) { | ||
children = files.map(function(file) { | ||
return this.keyForTree( | ||
path.join(fullPath, file), | ||
path.join(relativePath, file) | ||
); | ||
childKeys = childKeys.concat(keys); | ||
} | ||
}, this).filter(Boolean); | ||
} | ||
} else if (stats && stats.isFile()) { | ||
type = 'file'; | ||
if (this.shouldBeIgnored(fullPath)) { | ||
return []; | ||
return null; | ||
} | ||
statKeys.push(stats.mtime.getTime(), stats.size); | ||
} | ||
// Perhaps we should not use basename to infer the file name | ||
return ['path', relativePath] | ||
.concat(statKeys) | ||
.concat(childKeys); | ||
return new Key(type, fullPath, relativePath, stats, children); | ||
}; | ||
@@ -219,0 +288,0 @@ |
{ | ||
"name": "broccoli-caching-writer", | ||
"version": "0.5.4", | ||
"version": "0.5.5", | ||
"description": "Broccoli plugin that allows simple caching (while still allowing N:N) based on the input tree hash.", | ||
@@ -13,3 +13,4 @@ "main": "index.js", | ||
"scripts": { | ||
"test": "mocha tests/" | ||
"test": "mocha tests/", | ||
"test:debug": "mocha debug tests/" | ||
}, | ||
@@ -23,2 +24,3 @@ "keywords": [ | ||
"core-object": "0.0.3", | ||
"debug": "^2.1.1", | ||
"lodash-node": "^2.4.1", | ||
@@ -25,0 +27,0 @@ "promise-map-series": "^0.2.0", |
@@ -0,1 +1,4 @@ | ||
/* jshint node: true */ | ||
/* global it: true, describe: true, afterEach, beforeEach */ | ||
'use strict'; | ||
@@ -10,3 +13,2 @@ | ||
var broccoli = require('broccoli'); | ||
var cachingWriter = require('..'); | ||
@@ -33,4 +35,2 @@ | ||
fs.writeFileSync(existingJSFile, '"YIPPIE"\n'); | ||
if (builder) { | ||
@@ -41,9 +41,11 @@ return builder.cleanup(); | ||
function build() { | ||
return builder.build(); | ||
} | ||
function buildInSeries(count) { | ||
var promise = RSVP.resolve(); | ||
var promise = RSVP.Promise.resolve(); | ||
for (var i = 0; i < count; i++) { | ||
promise = promise.then(function() { | ||
return builder.build(); | ||
}); | ||
promise = promise.then(build); | ||
} | ||
@@ -99,3 +101,3 @@ | ||
builder = new broccoli.Builder(tree); | ||
return builder.build() | ||
return builder.build(); | ||
}); | ||
@@ -113,3 +115,3 @@ | ||
builder = new broccoli.Builder(tree); | ||
return builder.build() | ||
return builder.build(); | ||
}); | ||
@@ -126,5 +128,6 @@ | ||
builder = new broccoli.Builder(tree); | ||
return buildInSeries(3) | ||
.then(function() { | ||
expect(updateCacheCount).to.eql(1); | ||
expect(updateCacheCount).to.equal(1); | ||
}); | ||
@@ -135,2 +138,3 @@ }); | ||
var updateCacheCount = 0; | ||
var tree = cachingWriter([sourcePath, secondaryPath], { | ||
@@ -146,3 +150,3 @@ updateCache: function() { | ||
.finally(function() { | ||
expect(updateCacheCount).to.eql(1); | ||
expect(updateCacheCount).to.equal(1); | ||
}) | ||
@@ -155,3 +159,3 @@ .then(function() { | ||
.finally(function() { | ||
expect(updateCacheCount).to.eql(2); | ||
expect(updateCacheCount).to.equal(2); | ||
}) | ||
@@ -164,3 +168,3 @@ .then(function() { | ||
.finally(function() { | ||
expect(updateCacheCount).to.eql(3); | ||
expect(updateCacheCount).to.equal(3); | ||
}); | ||
@@ -181,6 +185,6 @@ }); | ||
.finally(function() { | ||
expect(updateCacheCount).to.eql(1); | ||
expect(updateCacheCount).to.equal(1); | ||
}) | ||
.then(function() { | ||
fs.writeFileSync(existingJSFile, '"YIPPIE"\n"KI-YAY"\n'); | ||
fs.writeFileSync(existingJSFile, '"YIPPIE"\n"KI-YAY!"\n'); | ||
@@ -190,3 +194,4 @@ return buildInSeries(3); | ||
.finally(function() { | ||
expect(updateCacheCount).to.eql(2); | ||
fs.writeFileSync(existingJSFile, '"YIPPIE"\n'); | ||
expect(updateCacheCount).to.equal(2); | ||
}); | ||
@@ -210,3 +215,3 @@ }); | ||
.finally(function() { | ||
expect(updateCacheCount).to.eql(1); | ||
expect(updateCacheCount).to.equal(1); | ||
}) | ||
@@ -219,3 +224,3 @@ .then(function() { | ||
.finally(function() { | ||
expect(updateCacheCount).to.eql(1); | ||
expect(updateCacheCount).to.equal(1); | ||
}); | ||
@@ -239,3 +244,3 @@ }); | ||
.finally(function() { | ||
expect(updateCacheCount).to.eql(1); | ||
expect(updateCacheCount).to.equal(1); | ||
}) | ||
@@ -248,3 +253,3 @@ .then(function() { | ||
.finally(function() { | ||
expect(updateCacheCount).to.eql(1); | ||
expect(updateCacheCount).to.equal(1); | ||
}); | ||
@@ -268,3 +273,3 @@ }); | ||
.finally(function() { | ||
expect(updateCacheCount).to.eql(1); | ||
expect(updateCacheCount).to.equal(1); | ||
}) | ||
@@ -277,3 +282,3 @@ .then(function() { | ||
.finally(function() { | ||
expect(updateCacheCount).to.eql(2); | ||
expect(updateCacheCount).to.equal(2); | ||
}); | ||
@@ -298,3 +303,3 @@ }); | ||
expect(fs.readFileSync(dir + '/something-cool.js', {encoding: 'utf8'})).to.eql('zomg blammo'); | ||
expect(fs.readFileSync(dir + '/something-cool.js', {encoding: 'utf8'})).to.equal('zomg blammo'); | ||
}); | ||
@@ -313,3 +318,3 @@ }); | ||
var dir = result.directory; | ||
expect(fs.readFileSync(dir + '/something-cool.js', {encoding: 'utf8'})).to.eql('zomg blammo'); | ||
expect(fs.readFileSync(dir + '/something-cool.js', {encoding: 'utf8'})).to.equal('zomg blammo'); | ||
}); | ||
@@ -324,3 +329,3 @@ }); | ||
.catch(function(reason) { | ||
expect(reason.message).to.eql('You must implement updateCache.'); | ||
expect(reason.message).to.equal('You must implement updateCache.'); | ||
}); | ||
@@ -417,3 +422,3 @@ }); | ||
var dir = result.directory; | ||
expect(fs.readFileSync(dir + '/something-cooler.js', {encoding: 'utf8'})).to.eql('whoa'); | ||
expect(fs.readFileSync(dir + '/something-cooler.js', {encoding: 'utf8'})).to.equal('whoa'); | ||
}); | ||
@@ -420,0 +425,0 @@ }); |
29235
592
9
+ Addeddebug@^2.1.1
+ Addeddebug@2.6.9(transitive)
+ Addedms@2.0.0(transitive)