Comparing version 0.3.2 to 0.4.0
0.4.0 / 2015-10-28 | ||
================== | ||
* adding tree.prune() to enable cleaning up orphaned files | ||
* restoring concept of entries internally | ||
* added cloning capability | ||
0.3.2 / 2015-10-27 | ||
@@ -3,0 +10,0 @@ ================== |
@@ -9,6 +9,7 @@ | ||
class File { | ||
constructor(location, tree) { | ||
constructor(location, tree, entry) { | ||
debug('initialize %s', location); | ||
this.type = extension(location); | ||
this.path = location; | ||
this.entry = !!entry; | ||
this.tree = tree; | ||
@@ -40,2 +41,9 @@ } | ||
} | ||
clone(tree) { | ||
let file = new File(this.location); | ||
Object.assign(file, this); | ||
file.tree = tree; | ||
return file; | ||
} | ||
} | ||
@@ -42,0 +50,0 @@ |
@@ -20,6 +20,6 @@ | ||
addFile(location) { | ||
debug('adding node: %s', location); | ||
addFile(location, entry) { | ||
debug('adding node: %s (entry: %j)', location, !!entry); | ||
if (!this.hasFile(location)) { | ||
let file = new File(location, this); | ||
let file = new File(location, this, !!entry); | ||
this.graph.addNewVertex(location, file); | ||
@@ -40,3 +40,3 @@ } | ||
isEntry(location) { | ||
return this.graph.outDegree(location) === 0; | ||
return !!this.getFile(location).entry; | ||
} | ||
@@ -46,14 +46,14 @@ | ||
debug('listing entry files'); | ||
let entries = Array.from(this.graph.sinks()).map(function (vertex) { | ||
return vertex[0]; | ||
}); | ||
let entries = Array.from(this.graph.sinks()) | ||
.filter(vertex => !!vertex[1].entry) | ||
.map(vertex => vertex[0]); | ||
if (from) { | ||
debug('filtering out entry files not linked to %s', from); | ||
entries = entries.filter(function (entry) { | ||
entries = entries.filter(entry => { | ||
return from === entry || this.graph.hasPath(from, entry); | ||
}, this); | ||
}); | ||
} | ||
debug('%s entries found %j', entries.length, entries); | ||
debug('%d entries found %j', entries.length, entries); | ||
return entries; | ||
@@ -67,3 +67,3 @@ } | ||
addDependency(parent, child) { | ||
debug('adding dependency %s to %s', child, parent); | ||
debug('adding dependency %s -> %s', child, parent); | ||
if (!this.hasFile(child)) this.addFile(child); | ||
@@ -75,16 +75,10 @@ this.graph.addEdge(child, parent); | ||
removeDependency(parent, child) { | ||
debug('removing dependency %s from %s', child, parent); | ||
debug('removing dependency %s -> %s', child, parent); | ||
this.graph.removeEdge(child, parent); | ||
// if nothing else depends on this node, let's remove it | ||
if (this.graph.outDegree(child) === 0) { | ||
debug('node %s no longer depended on, removing from tree', child); | ||
this.removeFile(child); | ||
} | ||
} | ||
moveDependency(from, to, child) { | ||
debug('moving dependency %s from %s to %s', child, from, to); | ||
this.addDependency(to, child); | ||
this.removeDependency(from, child); | ||
removeDependencies(parent) { | ||
debug('removing all dependencies from %s', parent); | ||
this.dependenciesOf(parent) | ||
.forEach(child => this.removeDependency(parent, child)); | ||
} | ||
@@ -97,5 +91,3 @@ | ||
return Array.from(deps).map(function (vertex) { | ||
return vertex[0]; | ||
}); | ||
return Array.from(deps).map(vertex => vertex[0]); | ||
} | ||
@@ -108,5 +100,3 @@ | ||
return Array.from(deps).map(function (vertex) { | ||
return vertex[0]; | ||
}); | ||
return Array.from(deps).map(vertex => vertex[0]); | ||
} | ||
@@ -121,2 +111,22 @@ | ||
} | ||
clone() { | ||
let tree = new Tree(); | ||
tree.graph = this.graph.clone(file => file.clone(tree), value => value); | ||
return tree; | ||
} | ||
prune() { | ||
debug('pruning orphaned files'); | ||
let entries = this.getEntries(); | ||
let files = this.topologicalOrder(); | ||
files | ||
// filter out files that have some path to a valid entry | ||
.filter(file => { | ||
if (this.isEntry(file)) return false; | ||
return !entries.some(entry => this.graph.hasPath(file, entry)); | ||
}) | ||
.forEach(file => this.graph.destroyVertex(file)); | ||
} | ||
} | ||
@@ -123,0 +133,0 @@ |
{ | ||
"name": "mako-tree", | ||
"version": "0.3.2", | ||
"version": "0.4.0", | ||
"description": "The build tree structure used internally by mako", | ||
@@ -5,0 +5,0 @@ "repository": "makojs/tree", |
@@ -27,11 +27,11 @@ | ||
let tree = new Tree(); | ||
let a = tree.addFile('a'); | ||
let a = tree.addFile('a', true); | ||
let b = tree.addFile('b'); | ||
tree.addDependency('a', 'b'); | ||
it('should return true if the file is a Entry', function () { | ||
it('should return true if the file is flagged as an entry', function () { | ||
assert.isTrue(a.isEntry()); | ||
}); | ||
it('should return false if the file is not a Entry', function () { | ||
it('should return false if the file is not flagged as an entry', function () { | ||
assert.isFalse(b.isEntry()); | ||
@@ -120,2 +120,44 @@ }); | ||
}); | ||
describe('#clone(tree)', function () { | ||
it('should create a new copy of the file', function () { | ||
let tree1 = new Tree(); | ||
let a1 = tree1.addFile('a'); | ||
let tree2 = new Tree(); | ||
let a2 = a1.clone(tree2); | ||
assert.notStrictEqual(a1, a2); | ||
assert.instanceOf(a2, File); | ||
}); | ||
it('should copy additional properties', function () { | ||
let tree1 = new Tree(); | ||
let a1 = tree1.addFile('a'); | ||
a1.contents = 'abc123'; | ||
let tree2 = new Tree(); | ||
let a2 = a1.clone(tree2); | ||
assert.strictEqual(a1.contents, a2.contents); | ||
}); | ||
it('should ensure that changes to type follow the clone', function () { | ||
let tree1 = new Tree(); | ||
let a1 = tree1.addFile('a.txt'); | ||
a1.type = 'html'; | ||
let tree2 = new Tree(); | ||
let a2 = a1.clone(tree2); | ||
assert.strictEqual(a1.type, a2.type); | ||
}); | ||
it('should ensure that the tree is not used from the original', function () { | ||
let tree1 = new Tree(); | ||
let a1 = tree1.addFile('a.txt'); | ||
a1.type = 'html'; | ||
let tree2 = new Tree(); | ||
let a2 = a1.clone(tree2); | ||
assert.strictEqual(a2.tree, tree2); | ||
}); | ||
}); | ||
}); |
176
test/tree.js
@@ -31,3 +31,3 @@ | ||
describe('#addFile(location)', function () { | ||
describe('#addFile(location, [entry])', function () { | ||
it('should add a new vertex', function () { | ||
@@ -47,2 +47,18 @@ let tree = new Tree(); | ||
}); | ||
context('with entry', function () { | ||
it('should set the file as an entry', function () { | ||
let tree = new Tree(); | ||
let a = tree.addFile('a', true); | ||
assert.isTrue(a.entry); | ||
}); | ||
it('should leave the file as not an entry by default', function () { | ||
let tree = new Tree(); | ||
let a = tree.addFile('a'); | ||
assert.isFalse(a.entry); | ||
}); | ||
}); | ||
}); | ||
@@ -97,3 +113,3 @@ | ||
let tree = new Tree(); | ||
tree.addFile('a'); | ||
tree.addFile('a', true); | ||
tree.addFile('b'); | ||
@@ -113,5 +129,5 @@ tree.addFile('c'); | ||
let tree = new Tree(); | ||
tree.addFile('a'); | ||
tree.addFile('a', true); | ||
tree.addFile('b'); | ||
tree.addFile('c'); | ||
tree.addFile('c', true); | ||
tree.addFile('d'); | ||
@@ -131,5 +147,5 @@ tree.addFile('e'); | ||
let tree = new Tree(); | ||
tree.addFile('a'); | ||
tree.addFile('a', true); | ||
tree.addFile('b'); | ||
tree.addFile('c'); | ||
tree.addFile('c', true); | ||
tree.addFile('d'); | ||
@@ -149,11 +165,11 @@ tree.addFile('e'); | ||
let tree = new Tree(); | ||
tree.addFile('a'); | ||
tree.addFile('a', true); | ||
tree.addFile('b'); | ||
tree.addDependency('a', 'b'); | ||
it('should return true when the file has no dependants', function () { | ||
it('should return true when the file is flagged as an entry', function () { | ||
assert.isTrue(tree.isEntry('a')); | ||
}); | ||
it('should return false when the file is depended upon', function () { | ||
it('should return false when the file is not flagged as an entry', function () { | ||
assert.isFalse(tree.isEntry('b')); | ||
@@ -236,8 +252,10 @@ }); | ||
}); | ||
}); | ||
it('should remove the unused nodes', function () { | ||
describe('#removeDependencies(parent)', function () { | ||
it('should remove the link between parent and all children', function () { | ||
// a <- b | ||
// <- c | ||
let tree = new Tree(); | ||
tree.addFile('a'); | ||
tree.addFile('a', true); | ||
tree.addFile('b'); | ||
@@ -248,49 +266,12 @@ tree.addFile('c'); | ||
// a <- b | ||
tree.removeDependency('a', 'c'); | ||
tree.removeDependencies('a'); | ||
assert.isTrue(tree.hasFile('a')); | ||
assert.isTrue(tree.hasFile('b')); | ||
assert.isFalse(tree.hasFile('c')); | ||
}); | ||
it('should not remove nodes that are still depended upon', function () { | ||
// a <- c | ||
// b <- | ||
let tree = new Tree(); | ||
tree.addFile('a'); | ||
tree.addFile('b'); | ||
tree.addFile('c'); | ||
tree.addDependency('a', 'c'); | ||
tree.addDependency('b', 'c'); | ||
// a <- c | ||
// b | ||
tree.removeDependency('b', 'c'); | ||
assert.isTrue(tree.hasFile('a')); | ||
assert.isTrue(tree.hasFile('b')); | ||
assert.isTrue(tree.hasFile('c')); | ||
assert.isFalse(tree.hasDependency('a', 'b')); | ||
assert.isFalse(tree.hasDependency('a', 'c')); | ||
}); | ||
}); | ||
describe('#moveDependency(from, to, child)', function () { | ||
it('should transfer the dependency from <- to', function () { | ||
// a <- b <- c | ||
let tree = new Tree(); | ||
tree.addFile('a'); | ||
tree.addFile('b'); | ||
tree.addFile('c'); | ||
tree.addDependency('a', 'b'); | ||
tree.addDependency('b', 'c'); | ||
tree.moveDependency('b', 'a', 'c'); | ||
// a <- b | ||
// <- c | ||
assert.isTrue(tree.hasDependency('a', 'b')); | ||
assert.isTrue(tree.hasDependency('a', 'c')); | ||
assert.isFalse(tree.hasDependency('b', 'c')); | ||
}); | ||
}); | ||
describe('#dependenciesOf(node, [recursive])', function () { | ||
@@ -357,2 +338,95 @@ // a <- b <- c <- d | ||
}); | ||
describe('#clone()', function () { | ||
// a <- b | ||
// <- c | ||
let tree = new Tree(); | ||
tree.addFile('a'); | ||
tree.addFile('b'); | ||
tree.addFile('c'); | ||
tree.addDependency('a', 'b'); | ||
tree.addDependency('a', 'c'); | ||
it('should make a clone of the original', function () { | ||
let clone = tree.clone(); | ||
assert.notStrictEqual(tree, clone); | ||
assert.instanceOf(clone, Tree); | ||
assert.strictEqual(tree.size(), clone.size()); | ||
assert.deepEqual(tree.topologicalOrder(), clone.topologicalOrder()); | ||
}); | ||
}); | ||
describe('#prune()', function () { | ||
it('should only remove orphaned files', function () { | ||
// a* <- b | ||
// c | ||
let tree = new Tree(); | ||
tree.addFile('a', true); | ||
tree.addFile('b'); | ||
tree.addFile('c'); | ||
tree.addDependency('a', 'b'); | ||
tree.prune(); | ||
assert.strictEqual(tree.size(), 2); | ||
assert.isFalse(tree.hasFile('c')); | ||
}); | ||
it('should recursively remove orphaned trees', function () { | ||
// a* <- b | ||
// c <- d | ||
let tree = new Tree(); | ||
tree.addFile('a', true); | ||
tree.addFile('b'); | ||
tree.addFile('c'); | ||
tree.addFile('d'); | ||
tree.addDependency('a', 'b'); | ||
tree.addDependency('c', 'd'); | ||
tree.prune(); | ||
assert.strictEqual(tree.size(), 2); | ||
assert.isFalse(tree.hasFile('c')); | ||
assert.isFalse(tree.hasFile('d')); | ||
}); | ||
it('should not remove dependencies that are still depended on elsewhere', function () { | ||
// a* <- b <- c | ||
// d <- | ||
let tree = new Tree(); | ||
tree.addFile('a', true); | ||
tree.addFile('b'); | ||
tree.addFile('c'); | ||
tree.addFile('d'); | ||
tree.addDependency('a', 'b'); | ||
tree.addDependency('b', 'c'); | ||
tree.addDependency('d', 'b'); | ||
tree.prune(); | ||
assert.deepEqual(tree.topologicalOrder(), [ 'c', 'b', 'a' ]); | ||
}); | ||
it('should not properly handle a complex case', function () { | ||
// a* <- b <- c <- d | ||
// e <- f <- | ||
let tree = new Tree(); | ||
tree.addFile('a', true); | ||
tree.addFile('b'); | ||
tree.addFile('c'); | ||
tree.addFile('d'); | ||
tree.addFile('e'); | ||
tree.addFile('f'); | ||
tree.addDependency('a', 'b'); | ||
tree.addDependency('b', 'c'); | ||
tree.addDependency('c', 'd'); | ||
tree.addDependency('e', 'f'); | ||
tree.addDependency('f', 'c'); | ||
tree.prune(); | ||
assert.deepEqual(tree.topologicalOrder(), [ 'd', 'c', 'b', 'a' ]); | ||
}); | ||
}); | ||
}); |
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
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
30877
619