fs-tree-diff
Advanced tools
Comparing version 0.0.1 to 0.1.0
128
lib/index.js
@@ -6,20 +6,11 @@ 'use strict'; | ||
var Set = require('fast-ordered-set'); | ||
var Entries = require('./entries'); | ||
var util = require('./util'); | ||
var Tree = require('./tree'); | ||
var EntryTree = require('./entry-tree'); | ||
var Entry = require('./entry'); | ||
var byRelativePath = util.byRelativePath; | ||
var ARBITRARY_START_OF_TIME = 0; | ||
module.exports = FSTree; | ||
function createEntriesMap(entries) { | ||
var _ret = {}; | ||
entries.forEach(function(entry) { | ||
_ret[entry.relativePath] = entry; | ||
}); | ||
return _ret; | ||
} | ||
function toChangeOp(change) { | ||
@@ -32,10 +23,26 @@ return ['change', change.relativePath]; | ||
this.files = new Set((options.files || []).slice()); | ||
this.entriesMap = createEntriesMap(options.entries || []); | ||
this.entries = options.entries; | ||
if (options._entries) { | ||
this.entries = options._entries; | ||
} else { | ||
this.entries = new Set(options.entries || [], 'relativePath'); | ||
} | ||
} | ||
FSTree.fromPaths = function (paths) { | ||
var entries = paths.map(function (path) { | ||
return new Entry(path, 0, ARBITRARY_START_OF_TIME); | ||
}); | ||
return new FSTree({ | ||
entries: entries, | ||
}); | ||
}; | ||
FSTree._fromOwnSet = function(set) { | ||
return new FSTree({ _entries: set }); | ||
}; | ||
Object.defineProperty(FSTree.prototype, 'size', { | ||
get: function() { | ||
return this.files.size; | ||
return this.entries.size; | ||
} | ||
@@ -45,58 +52,40 @@ }); | ||
FSTree.prototype.forEach = function (fn, context) { | ||
this.files.forEach(fn, context); | ||
this.entries.forEach(fn, context); | ||
}; | ||
FSTree.prototype.calculatePatch = function (_files) { | ||
var createOps, removeOps, changeOps, tree; | ||
if (this.entries) { | ||
var _entries = _files; | ||
tree = new EntryTree(this.entries); | ||
var entries = new Entries(this.entries); | ||
FSTree.prototype.difference = function(otherFSTree) { | ||
return FSTree._fromOwnSet(this.entries.difference(otherFSTree.entries)); | ||
}; | ||
var removals = entries.remove(_entries); | ||
var additions = entries.add(_entries); | ||
var updates = entries.update(_entries); | ||
FSTree.prototype.intersection = function(otherFSTree) { | ||
return FSTree._fromOwnSet(this.entries.intersection(otherFSTree.entries)); | ||
}; | ||
tree.addFiles(additions.map(byRelativePath)); | ||
tree.removeFiles(removals.map(byRelativePath)); | ||
FSTree.prototype.calculatePatch = function (otherFSTree) { | ||
// TODO: algorithimic complexity here isn't ideal. Future work can reduce | ||
// that cost. Today, the FS IO operations outweigh the cost, even with a | ||
// naive implementation | ||
var tree = new Tree(this.entries); | ||
removeOps = tree.postOrderDepthReducer(reduceRemovals, []); | ||
createOps = tree.preOrderDepthReducer(reduceAdditions, []); | ||
changeOps = updates.map(toChangeOp); | ||
var fsRemoveTree = this.difference(otherFSTree); | ||
var fsAddTree = otherFSTree.difference(this); | ||
this.entries = _entries; | ||
return removeOps.concat(createOps, changeOps); | ||
// TODO: removeEntries should be combined with the postOrderDepthReducer and return removeOps | ||
tree.removeEntries(fsRemoveTree.entries); | ||
var removeOps = tree.postOrderDepthReducer(reduceRemovals, []); | ||
} else { | ||
// TODO: addEntries should be combined with th preOrderDepthReducer and return addOps | ||
tree.addEntries(fsAddTree.entries); | ||
var createOps = tree.preOrderDepthReducer(reduceAdditions, []); | ||
// TODO: algorithimic complexity here isn't ideal. Future work can reduce | ||
// that cost. Today, the FS IO operations outweigh the cost, even with a | ||
// naive implementation | ||
tree = new Tree(this.files.values); | ||
var changes = this._findChanges(otherFSTree).map(function(change) { | ||
return ['change', change]; | ||
}); | ||
var files = _files instanceof this.constructor ? _files.files : new Set(_files); | ||
var filesToRemove = this.files.subtract(files).values; | ||
var filesToAdd = files.subtract(this.files).values; | ||
// TODO: removeFiles should be combined with the postOrderDepthReducer and return removeOps | ||
tree.removeFiles(filesToRemove); | ||
removeOps = tree.postOrderDepthReducer(reduceRemovals, []); | ||
// TODO: addFiles should be combined with th preOrderDepthReducer and return removeOps | ||
tree.addFiles(filesToAdd); | ||
createOps = tree.preOrderDepthReducer(reduceAdditions, []); | ||
var changes = findChanges(this.files, files).map(function(change) { | ||
return ['change', change]; | ||
}); | ||
return removeOps.concat(createOps).concat(changes); | ||
} | ||
return removeOps.concat(createOps).concat(changes); | ||
}; | ||
function findChanges(previousFiles, nextFiles) { | ||
var a = previousFiles.intersection(nextFiles).values; | ||
var b = nextFiles.intersection(previousFiles).values; | ||
FSTree.prototype._findChanges = function(nextTree) { | ||
var a = this.intersection(nextTree).entries.values; | ||
var b = nextTree.intersection(this).entries.values; | ||
@@ -109,9 +98,4 @@ if (a.length !== b.length) { | ||
for (var i = 0; i < a.length; i++) { | ||
// TODO: just to ensure expectations, but this will change when we | ||
// introduce complex types | ||
if (a[i] !== b[i]) { | ||
throw new Error('EWUT'); | ||
} | ||
if (needsUpdate(a[i], b[i])) { | ||
changes.push(b); | ||
changes.push(b[i].relativePath); | ||
} | ||
@@ -124,3 +108,9 @@ } | ||
function needsUpdate(before, after) { | ||
return false; | ||
if (before.isDirectory() && after.isDirectory()) { | ||
return false; | ||
} | ||
return before.size !== after.size || | ||
before.mtime !== after.mtime || | ||
before.mode !== after.mode; | ||
} | ||
@@ -177,2 +167,2 @@ | ||
} | ||
} | ||
} |
'use strict'; | ||
function Tree(files, path, isNew) { | ||
var Set = require('fast-ordered-set'); | ||
var Entry = require('./entry'); | ||
var chomp = require('./util').chomp; | ||
var ARBITRARY_START_OF_TIME = 0; | ||
function Tree(entries, path, isNew) { | ||
this.children = { }; | ||
@@ -10,9 +16,5 @@ this.operation = null; | ||
if (!Array.isArray(files)) { | ||
throw new Error('new Tree must be given a files argument'); | ||
if (entries.size > 0) { | ||
this.addEntries(entries, this.isNew); | ||
} | ||
if (files.length > 0) { | ||
this.addFiles(files, this.isNew); | ||
} | ||
} | ||
@@ -62,20 +64,20 @@ | ||
Tree.prototype.addFiles = function (files, _isNew) { | ||
Tree.prototype.addEntries = function (entries, _isNew) { | ||
var isNew = arguments.length > 1 ? arguments[1] : true; | ||
files.map(function (file) { | ||
return file.split('/'); | ||
}).forEach(function(file) { | ||
this.addFile(file, isNew); | ||
entries.forEach(function(entry) { | ||
this.addEntry(entry, isNew); | ||
}, this); | ||
}; | ||
function File(current, isNew) { | ||
function File(entry, isNew) { | ||
this.isFile = true; | ||
this.isNew = isNew; | ||
this.name = current; | ||
this.entry = entry; | ||
this.operation = undefined; | ||
// TODO: error if entry is a directory | ||
} | ||
Tree.prototype.addFile = function (fileParts, _isNew) { | ||
Tree.prototype.addEntry = function (entry, _isNew) { | ||
var fileParts = entry.relativePath.split('/'); | ||
var current = fileParts.shift(); | ||
@@ -85,2 +87,6 @@ var child = this.children[current]; | ||
if (current === '') { | ||
return; | ||
} | ||
if (fileParts.length === 0) { | ||
@@ -94,3 +100,3 @@ if (child && child.isFile) { | ||
// add a file | ||
this.children[current] = new File(current, isNew); | ||
this.children[current] = new File(entry, isNew); | ||
} else { | ||
@@ -103,7 +109,7 @@ if (child && child.isFile) { | ||
if (!tree) { | ||
this.children[current] = new Tree([ | ||
fileParts.join('/') | ||
], this.pathForChild(current), isNew); | ||
this.children[current] = new Tree(new Set([ | ||
new Entry( fileParts.join('/'), 0, ARBITRARY_START_OF_TIME) | ||
], 'relativePath'), this.pathForChild(current), isNew); | ||
} else { | ||
tree.addFile(fileParts, isNew); | ||
tree.addEntry(new Entry(fileParts.join('/'), entry.size, entry.mtime), isNew); | ||
} | ||
@@ -113,9 +119,8 @@ } | ||
Tree.prototype.removeFiles = function (files) { | ||
files.map(function (file) { | ||
return file.split('/'); | ||
}).forEach(this.removeFile, this); | ||
Tree.prototype.removeEntries = function (entries) { | ||
entries.forEach(this.removeEntry, this); | ||
}; | ||
Tree.prototype.removeFile = function (fileParts) { | ||
Tree.prototype.removeEntry = function (entry) { | ||
var fileParts = chomp(entry.relativePath, '/').split('/'); | ||
var current = fileParts.shift(); | ||
@@ -137,6 +142,7 @@ var child = this.children[current]; | ||
child.removeFile(fileParts); | ||
child.removeEntry(new Entry(fileParts.join('/'), null, null)); | ||
} | ||
}; | ||
module.exports = Tree; |
@@ -7,2 +7,11 @@ 'use strict'; | ||
module.exports.byRelativePath = byRelativePath; | ||
function chomp(string, character) { | ||
if (string.charAt(string.length-1) === character) { | ||
return string.substring(0, string.length-1); | ||
} else { | ||
return string; | ||
} | ||
} | ||
module.exports.byRelativePath = byRelativePath; | ||
module.exports.chomp = chomp; |
{ | ||
"name": "fs-tree-diff", | ||
"version": "0.0.1", | ||
"version": "0.1.0", | ||
"description": "Backs out file tree changes", | ||
@@ -16,3 +16,3 @@ "main": "lib/index.js", | ||
"dependencies": { | ||
"fast-ordered-set": "^1.0.1" | ||
"fast-ordered-set": "^1.0.2" | ||
}, | ||
@@ -19,0 +19,0 @@ "devDependencies": { |
@@ -5,4 +5,2 @@ 'use strict'; | ||
var FSTree = require('../lib/index'); | ||
var Entries = require('../lib/entries'); | ||
var context = describe; | ||
@@ -12,2 +10,15 @@ var fsTree; | ||
describe('FSTree', function() { | ||
function entry(options) { | ||
return { | ||
relativePath: options.relativePath, | ||
mode: options.mode, | ||
size: options.size, | ||
mtime: options.mtime, | ||
isDirectory: function() { | ||
return (this.mode & 61440) === 16384; | ||
} | ||
}; | ||
}; | ||
it('can be instantiated', function() { | ||
@@ -17,180 +28,35 @@ expect(new FSTree()).to.be.an.instanceOf(FSTree); | ||
describe('Entries', function() { | ||
var entries; | ||
beforeEach(function() { | ||
entries = new Entries([ | ||
{ relativePath: 'a/b.js', mode: '0o666', size: 1, mtime: 1 }, | ||
{ relativePath: 'b.js', mode: '0o666', size: 2, mtime: 1 } | ||
]); | ||
describe('.fromPaths', function() { | ||
it('creates empty trees', function() { | ||
fsTree = FSTree.fromPaths([ ]); | ||
expect(fsTree.size).to.eq(0); | ||
}); | ||
context('identity', function() { | ||
it('should return the initial entries', function() { | ||
expect(entries.identity()).to.deep.equal([ | ||
{ relativePath: 'a/b.js', mode: '0o666', size: 1, mtime: 1 }, | ||
{ relativePath: 'b.js', mode: '0o666', size: 2, mtime: 1 } | ||
]); | ||
}); | ||
}); | ||
it('creates trees from paths', function() { | ||
var result; | ||
context('add', function() { | ||
it('should return the added files', function() { | ||
var result = entries.add([ | ||
{ relativePath: 'a/b.js', mode: '0o666', size: 1, mtime: 1 }, | ||
{ relativePath: 'b.js', mode: '0o666', size: 2, mtime: 1 }, | ||
{ relativePath: 'c.js', mode: '0o666', size: 1, mtime: 1 } | ||
]); | ||
fsTree = FSTree.fromPaths([ | ||
'a.js', | ||
'foo/a.js', | ||
]); | ||
expect(result).to.deep.equal([ | ||
{ relativePath: 'c.js', mode: '0o666', size: 1, mtime: 1 } | ||
]); | ||
}); | ||
result = fsTree.calculatePatch( | ||
FSTree.fromPaths([ | ||
'a.js', | ||
'foo/b.js', | ||
]) | ||
); | ||
it('should handle directories', function() { | ||
var result = entries.add([ | ||
{ relativePath: 'a/b.js', mode: '0o666', size: 1, mtime: 1 }, | ||
{ relativePath: 'b.js', mode: '0o666', size: 2, mtime: 1 }, | ||
{ relativePath: 'c.js', mode: '0o666', size: 1, mtime: 1 }, | ||
{ relativePath: 'f/', mode: '16384', size: 1, mtime: 1 } | ||
]); | ||
expect(result).to.deep.equal([ | ||
{ relativePath: 'c.js', mode: '0o666', size: 1, mtime: 1 }, | ||
{ relativePath: 'f/', mode: '16384', size: 1, mtime: 1 } | ||
]); | ||
}); | ||
it('should return an empty array if there is a mismatch', function() { | ||
var result = entries.add([ | ||
{ relativePath: 'a/b.js', mode: '0o666', size: 1, mtime: 1 } | ||
]); | ||
expect(result).to.deep.equal([]); | ||
}); | ||
it('should return an empty array if there are no changes', function() { | ||
var result = entries.add([ | ||
{ relativePath: 'a/b.js', mode: '0o666', size: 1, mtime: 1 }, | ||
{ relativePath: 'b.js', mode: '0o666', size: 2, mtime: 1 } | ||
]); | ||
expect(result).to.deep.equal([]); | ||
}); | ||
expect(result).to.deep.equal([ | ||
['unlink', 'foo/a.js'], | ||
// This no-op is not fundamental: a future iteration could reasonably | ||
// optimize it away | ||
['rmdir', 'foo'], | ||
['mkdir', 'foo'], | ||
['create', 'foo/b.js'], | ||
]); | ||
}); | ||
context('remove', function() { | ||
it('should find the removals', function () { | ||
var result = entries.remove([ | ||
{ relativePath: 'a/b.js', mode: '0o666', size: 1, mtime: 1 } | ||
]); | ||
expect(result).to.deep.equal([ | ||
{ relativePath: 'b.js', mode: '0o666', size: 2, mtime: 1 } | ||
]); | ||
}); | ||
it('should find directory removals', function () { | ||
var localEntries = new Entries([ | ||
{ relativePath: 'a/b.js', mode: '0o666', size: 1, mtime: 1 }, | ||
{ relativePath: 'c/', mode: '16384', size: 1, mtime: 1 }, | ||
{ relativePath: 'c/c.js', mode: '0o666', size: 1, mtime: 1 } | ||
]); | ||
var result = localEntries.remove([ | ||
{ relativePath: 'a/b.js', mode: '0o666', size: 1, mtime: 1 } | ||
]); | ||
expect(result).to.deep.equal([ | ||
{ relativePath: 'c/', mode: '16384', size: 1, mtime: 1 }, | ||
{ relativePath: 'c/c.js', mode: '0o666', size: 1, mtime: 1 } | ||
]); | ||
}); | ||
it('should return an empty array if the file length is the same', function() { | ||
var result = entries.remove([ | ||
{ relativePath: 'a/b.js', mode: '0o666', size: 1, mtime: 1 }, | ||
{ relativePath: 'b.js', mode: '0o666', size: 2, mtime: 1 } | ||
]); | ||
expect(result).to.deep.equal([]); | ||
}); | ||
it('should return an empty array if their are additions', function() { | ||
var result = entries.remove([ | ||
{ relativePath: 'a/b.js', mode: '0o666', size: 1, mtime: 1 }, | ||
{ relativePath: 'b.js', mode: '0o666', size: 2, mtime: 1 }, | ||
{ relativePath: 'c.js', mode: '0o666', size: 2, mtime: 1 } | ||
]); | ||
expect(result).to.deep.equal([]); | ||
}); | ||
}); | ||
context('update', function() { | ||
it('should diff by size', function() { | ||
var result = entries.update([ | ||
{ relativePath: 'a/b.js', mode: '0o666', size: 10, mtime: 1 }, | ||
{ relativePath: 'b.js', mode: '0o666', size: 2, mtime: 1 } | ||
]); | ||
expect(result).to.deep.equal([ | ||
{ relativePath: 'a/b.js', mode: '0o666', size: 10, mtime: 1 } | ||
]); | ||
}); | ||
it('should diff by mtime (Number)', function() { | ||
var result = entries.update([ | ||
{ relativePath: 'a/b.js', mode: '0o666', size: 1, mtime: 10 }, | ||
{ relativePath: 'b.js', mode: '0o666', size: 2, mtime: 1 } | ||
]); | ||
expect(result).to.deep.equal([ | ||
{ relativePath: 'a/b.js', mode: '0o666', size: 1, mtime: 10 } | ||
]); | ||
}); | ||
it('should diff by mtime (Date)', function() { | ||
var date = new Date(10); | ||
var result = entries.update([ | ||
{ relativePath: 'a/b.js', mode: '0o666', size: 1, mtime: date }, | ||
{ relativePath: 'b.js', mode: '0o666', size: 2, mtime: 1 } | ||
]); | ||
expect(result).to.deep.equal([ | ||
{ relativePath: 'a/b.js', mode: '0o666', size: 1, mtime: date } | ||
]); | ||
}); | ||
it('should diff by mode', function() { | ||
var result = entries.update([ | ||
{ relativePath: 'a/b.js', mode: 'foo', size: 1, mtime: 1 }, | ||
{ relativePath: 'b.js', mode: '0o666', size: 2, mtime: 1 } | ||
]); | ||
expect(result).to.deep.equal([ | ||
{ relativePath: 'a/b.js', mode: 'foo', size: 1, mtime: 1 } | ||
]); | ||
}); | ||
it('should return an empty array if their are additions', function() { | ||
var result = entries.remove([ | ||
{ relativePath: 'a/b.js', mode: '0o666', size: 1, mtime: 1 }, | ||
{ relativePath: 'b.js', mode: '0o666', size: 2, mtime: 1 }, | ||
{ relativePath: 'c.js', mode: '0o666', size: 2, mtime: 1 } | ||
]); | ||
expect(result).to.deep.equal([]); | ||
}); | ||
it('should return an empty array if there are removals', function() { | ||
var result = entries.add([ | ||
{ relativePath: 'a/b.js', mode: '0o666', size: 1, mtime: 1 } | ||
]); | ||
expect(result).to.deep.equal([]); | ||
}); | ||
}); | ||
}); | ||
describe('.calculatePatch', function() { | ||
describe('#calculatePatch', function() { | ||
context('from an empty tree', function() { | ||
@@ -203,3 +69,3 @@ beforeEach( function() { | ||
it('returns 0 operations', function() { | ||
expect(fsTree.calculatePatch([])).to.deep.equal([]); | ||
expect(fsTree.calculatePatch(FSTree.fromPaths([]))).to.deep.equal([]); | ||
}); | ||
@@ -210,6 +76,6 @@ }); | ||
it('returns n create operations', function() { | ||
expect(fsTree.calculatePatch([ | ||
expect(fsTree.calculatePatch(FSTree.fromPaths([ | ||
'bar/baz.js', | ||
'foo.js', | ||
])).to.deep.equal([ | ||
]))).to.deep.equal([ | ||
['mkdir', 'bar'], | ||
@@ -225,8 +91,6 @@ ['create', 'foo.js'], | ||
beforeEach( function() { | ||
fsTree = new FSTree({ | ||
files: [ | ||
'bar/baz.js', | ||
'foo.js', | ||
], | ||
}); | ||
fsTree = FSTree.fromPaths([ | ||
'bar/baz.js', | ||
'foo.js', | ||
]); | ||
}); | ||
@@ -236,3 +100,3 @@ | ||
it('returns n rm operations', function() { | ||
expect(fsTree.calculatePatch([])).to.deep.equal([ | ||
expect(fsTree.calculatePatch(FSTree.fromPaths([]))).to.deep.equal([ | ||
['unlink', 'bar/baz.js'], | ||
@@ -250,5 +114,5 @@ ['rmdir', 'bar'], | ||
entries: [ | ||
{ relativePath: 'a/b.js', mode: '0o666', size: 1, mtime: 1 }, | ||
{ relativePath: 'c/d.js', mode: '0o666', size: 1, mtime: 1 }, | ||
{ relativePath: 'a/c.js', mode: '0o666', size: 1, mtime: 1 } | ||
entry({ relativePath: 'a/b.js', mode: '0o666', size: 1, mtime: 1 }), | ||
entry({ relativePath: 'c/d.js', mode: '0o666', size: 1, mtime: 1 }), | ||
entry({ relativePath: 'a/c.js', mode: '0o666', size: 1, mtime: 1 }) | ||
] | ||
@@ -259,8 +123,10 @@ }); | ||
it('should detect additions', function() { | ||
var result = fsTree.calculatePatch([ | ||
{ relativePath: 'a/b.js', mode: '0o666', size: 1, mtime: 1 }, | ||
{ relativePath: 'c/d.js', mode: '0o666', size: 1, mtime: 1 }, | ||
{ relativePath: 'a/c.js', mode: '0o666', size: 1, mtime: 1 }, | ||
{ relativePath: 'a/j.js', mode: '0o666', size: 1, mtime: 1 } | ||
]); | ||
var result = fsTree.calculatePatch(new FSTree({ | ||
entries: [ | ||
entry({ relativePath: 'a/b.js', mode: '0o666', size: 1, mtime: 1 }), | ||
entry({ relativePath: 'c/d.js', mode: '0o666', size: 1, mtime: 1 }), | ||
entry({ relativePath: 'a/c.js', mode: '0o666', size: 1, mtime: 1 }), | ||
entry({ relativePath: 'a/j.js', mode: '0o666', size: 1, mtime: 1 }) | ||
] | ||
})); | ||
@@ -273,5 +139,7 @@ expect(result).to.deep.equal([ | ||
it('should detect removals', function() { | ||
var result = fsTree.calculatePatch([ | ||
{ relativePath: 'a/b.js', mode: '0o666', size: 1, mtime: 1 } | ||
]); | ||
var result = fsTree.calculatePatch(new FSTree({ | ||
entries: [ | ||
entry({ relativePath: 'a/b.js', mode: '0o666', size: 1, mtime: 1 }) | ||
] | ||
})); | ||
@@ -286,11 +154,13 @@ expect(result).to.deep.equal([ | ||
it('should detect updates', function() { | ||
var result = fsTree.calculatePatch([ | ||
{ relativePath: 'a/b.js', mode: '0o666', size: 1, mtime: 1 }, | ||
{ relativePath: 'c/d.js', mode: '0o666', size: 1, mtime: 2 }, | ||
{ relativePath: 'a/c.js', mode: '0o666', size: 10, mtime: 1 } | ||
]); | ||
var result = fsTree.calculatePatch(new FSTree({ | ||
entries: [ | ||
entry({ relativePath: 'a/b.js', mode: '0o666', size: 1, mtime: 1 }), | ||
entry({ relativePath: 'c/d.js', mode: '0o666', size: 1, mtime: 2 }), | ||
entry({ relativePath: 'a/c.js', mode: '0o666', size: 10, mtime: 1 }) | ||
] | ||
})); | ||
expect(result).to.deep.equal([ | ||
['change', 'c/d.js'], | ||
['change', 'a/c.js'], | ||
['change', 'c/d.js'] | ||
]); | ||
@@ -300,17 +170,49 @@ }); | ||
context('\w updates', function() { | ||
context('FSTree with updates at several different depths', function () { | ||
beforeEach( function() { | ||
fsTree = new FSTree({ | ||
files: [ | ||
'bar/baz.js', | ||
'foo.js', | ||
], | ||
entries: [ | ||
entry({ relativePath: 'a.js', mode: '0o666', size: 1, mtime: 1 }), | ||
entry({ relativePath: 'b.js', mode: '0o666', size: 1, mtime: 1 }), | ||
entry({ relativePath: 'one/a.js', mode: '0o666', size: 1, mtime: 1 }), | ||
entry({ relativePath: 'one/b.js', mode: '0o666', size: 1, mtime: 1 }), | ||
entry({ relativePath: 'one/two/a.js', mode: '0o666', size: 1, mtime: 1 }), | ||
entry({ relativePath: 'one/two/b.js', mode: '0o666', size: 1, mtime: 1 }), | ||
] | ||
}); | ||
}); | ||
it('returns n rm operations', function() { | ||
expect(fsTree.calculatePatch([ | ||
it('catches each update', function() { | ||
var result = fsTree.calculatePatch(new FSTree({ | ||
entries: [ | ||
entry({ relativePath: 'a.js', mode: '0o666', size: 1, mtime: 2 }), | ||
entry({ relativePath: 'b.js', mode: '0o666', size: 1, mtime: 1 }), | ||
entry({ relativePath: 'one/a.js', mode: '0o666', size: 10, mtime: 1 }), | ||
entry({ relativePath: 'one/b.js', mode: '0o666', size: 1, mtime: 1 }), | ||
entry({ relativePath: 'one/two/a.js', mode: '0o667', size: 1, mtime: 1 }), | ||
entry({ relativePath: 'one/two/b.js', mode: '0o666', size: 1, mtime: 1 }), | ||
] | ||
})); | ||
expect(result).to.deep.equal([ | ||
['change', 'a.js'], | ||
['change', 'one/a.js'], | ||
['change', 'one/two/a.js'], | ||
]); | ||
}); | ||
}); | ||
context('with only unchanged paths', function() { | ||
beforeEach( function() { | ||
fsTree = FSTree.fromPaths([ | ||
'bar/baz.js', | ||
'foo.js', | ||
]); | ||
}); | ||
it('returns an empty changeset', function() { | ||
expect(fsTree.calculatePatch(FSTree.fromPaths([ | ||
'bar/baz.js', | ||
'foo.js' | ||
])).to.deep.equal([ | ||
]))).to.deep.equal([ | ||
// when we work with entries, will potentially return updates | ||
@@ -324,10 +226,8 @@ ]); | ||
beforeEach( function() { | ||
fsTree = new FSTree({ | ||
files: [ | ||
'foo/one.js', | ||
'foo/two.js', | ||
'bar/one.js', | ||
'bar/two.js', | ||
], | ||
}); | ||
fsTree = FSTree.fromPaths([ | ||
'foo/one.js', | ||
'foo/two.js', | ||
'bar/one.js', | ||
'bar/two.js', | ||
]); | ||
}); | ||
@@ -337,5 +237,5 @@ | ||
it('reduces the rm operations', function() { | ||
expect(fsTree.calculatePatch([ | ||
expect(fsTree.calculatePatch(FSTree.fromPaths([ | ||
'bar/two.js' | ||
])).to.deep.equal([ | ||
]))).to.deep.equal([ | ||
['unlink', 'foo/one.js'], | ||
@@ -351,5 +251,5 @@ ['unlink', 'foo/two.js'], | ||
it('reduces the rm operations', function() { | ||
expect(fsTree.calculatePatch([ | ||
expect(fsTree.calculatePatch(FSTree.fromPaths([ | ||
'bar/three.js' | ||
])).to.deep.equal([ | ||
]))).to.deep.equal([ | ||
['unlink', 'foo/one.js'], | ||
@@ -377,8 +277,6 @@ ['unlink', 'foo/two.js'], | ||
beforeEach( function() { | ||
fsTree = new FSTree({ | ||
files: [ | ||
'bar/quz/baz.js', | ||
'foo.js', | ||
], | ||
}); | ||
fsTree = FSTree.fromPaths([ | ||
'bar/quz/baz.js', | ||
'foo.js', | ||
]); | ||
}); | ||
@@ -388,3 +286,3 @@ | ||
it('returns n rm operations', function() { | ||
expect(fsTree.calculatePatch([])).to.deep.equal([ | ||
expect(fsTree.calculatePatch(FSTree.fromPaths([]))).to.deep.equal([ | ||
['unlink', 'bar/quz/baz.js'], | ||
@@ -401,8 +299,6 @@ ['rmdir', 'bar/quz'], | ||
beforeEach( function() { | ||
fsTree = new FSTree({ | ||
files: [ | ||
'bar/quz/baz.js', | ||
'bar/foo.js', | ||
], | ||
}); | ||
fsTree = FSTree.fromPaths([ | ||
'bar/quz/baz.js', | ||
'bar/foo.js', | ||
]); | ||
}); | ||
@@ -412,5 +308,5 @@ | ||
it('returns one unlink operation', function() { | ||
expect(fsTree.calculatePatch([ | ||
expect(fsTree.calculatePatch(FSTree.fromPaths([ | ||
'bar/quz/baz.js' | ||
])).to.deep.equal([ | ||
]))).to.deep.equal([ | ||
['unlink', 'bar/foo.js'] | ||
@@ -424,8 +320,6 @@ ]); | ||
beforeEach( function() { | ||
fsTree = new FSTree({ | ||
files: [ | ||
'subdir1/subsubdir1/foo.png', | ||
'subdir2/bar.css' | ||
], | ||
}); | ||
fsTree = FSTree.fromPaths([ | ||
'subdir1/subsubdir1/foo.png', | ||
'subdir2/bar.css' | ||
]); | ||
}); | ||
@@ -435,5 +329,5 @@ | ||
it('returns one unlink operation', function() { | ||
expect(fsTree.calculatePatch([ | ||
expect(fsTree.calculatePatch(FSTree.fromPaths([ | ||
'subdir1/subsubdir1/foo.png' | ||
])).to.deep.equal([ | ||
]))).to.deep.equal([ | ||
['unlink', 'subdir2/bar.css'], | ||
@@ -448,13 +342,11 @@ ['rmdir', 'subdir2'] | ||
beforeEach( function() { | ||
fsTree = new FSTree({ | ||
files: [ | ||
'subdir1/foo' | ||
], | ||
}); | ||
fsTree = FSTree.fromPaths([ | ||
'subdir1/foo' | ||
]); | ||
}); | ||
it('it unlinks the file, and rmdir the folder and then creates the file', function() { | ||
expect(fsTree.calculatePatch([ | ||
expect(fsTree.calculatePatch(FSTree.fromPaths([ | ||
'subdir1' | ||
])).to.deep.equal([ | ||
]))).to.deep.equal([ | ||
['unlink', 'subdir1/foo'], | ||
@@ -469,13 +361,11 @@ ['rmdir', 'subdir1'], | ||
beforeEach( function() { | ||
fsTree = new FSTree({ | ||
files: [ | ||
'subdir1' | ||
], | ||
}); | ||
fsTree = FSTree.fromPaths([ | ||
'subdir1' | ||
]); | ||
}); | ||
it('it unlinks the file, and makes the folder and then creates the file', function() { | ||
expect(fsTree.calculatePatch([ | ||
expect(fsTree.calculatePatch(FSTree.fromPaths([ | ||
'subdir1/foo' | ||
])).to.deep.equal([ | ||
]))).to.deep.equal([ | ||
['unlink', 'subdir1'], | ||
@@ -488,26 +378,30 @@ ['mkdir', 'subdir1'], | ||
// context('only folders', function() { | ||
// beforeEach( function() { | ||
// fsTree = new FSTree({ | ||
// files: [ | ||
// 'dir/', | ||
// 'dir2/subdir1/', | ||
// 'dir3/subdir1/' | ||
// ] | ||
// }); | ||
// }); | ||
context('only folders', function() { | ||
beforeEach( function() { | ||
fsTree = FSTree.fromPaths([ | ||
'dir/', | ||
'dir2/subdir1/', | ||
'dir3/subdir1/' | ||
]); | ||
}); | ||
// it('it unlinks the file, and makes the folder and then creates the file', function() { | ||
// expect(fsTree.calculatePatch([ | ||
// 'dir2/subdir1/', | ||
// 'dir3/', | ||
// 'dir4/', | ||
// ])).to.deep.equal([ | ||
// ['rmdir', 'dir1'], | ||
// ['rmdir', 'dir3/subdir1'], | ||
// ['mkdir', 'dir4'] | ||
// ]); | ||
// }); | ||
// }); | ||
it('it unlinks the file, and makes the folder and then creates the file', function() { | ||
var result = fsTree.calculatePatch(FSTree.fromPaths([ | ||
'dir2/subdir1/', | ||
'dir3/', | ||
'dir4/', | ||
])); | ||
expect(result).to.deep.equal([ | ||
['rmdir', 'dir3/subdir1'], | ||
['rmdir', 'dir'], | ||
// This no-op (rmdir dir3; mkdir dir3) is not fundamental: a future | ||
// iteration could reasonably optimize it away | ||
['rmdir', 'dir3'], | ||
['mkdir', 'dir3'], | ||
['mkdir', 'dir4'] | ||
]); | ||
}); | ||
}); | ||
}); | ||
}); |
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
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
20817
602
Updatedfast-ordered-set@^1.0.2