fs-tree-diff
Advanced tools
Comparing version 1.0.2 to 2.0.0
# master | ||
# 2.0.0 | ||
* port to typescript | ||
# 1.0.2 | ||
@@ -4,0 +8,0 @@ |
@@ -1,43 +0,43 @@ | ||
'use strict'; | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var DIRECTORY_MODE = 16877; | ||
module.exports = Entry; | ||
function Entry(relativePath, size, mtime, mode) { | ||
if (arguments.length < 4) { | ||
var isDirectory = relativePath.charAt(relativePath.length - 1) === '/'; | ||
this.mode = isDirectory ? DIRECTORY_MODE : 0; | ||
} else { | ||
var modeType = typeof mode; | ||
if (modeType !== 'number') { | ||
throw new TypeError('Expected `mode` to be of type `number` but was of type `' + modeType + '` instead.'); | ||
var Entry = /** @class */ (function () { | ||
function Entry(relativePath, size, mtime, mode) { | ||
if (mode === undefined) { | ||
var isDirectory = relativePath.charAt(relativePath.length - 1) === '/'; | ||
this.mode = isDirectory ? DIRECTORY_MODE : 0; | ||
} | ||
else { | ||
var modeType = typeof mode; | ||
if (modeType !== 'number') { | ||
throw new TypeError("Expected 'mode' to be of type 'number' but was of type '" + modeType + "' instead."); | ||
} | ||
this.mode = mode; | ||
} | ||
if (mtime !== undefined) { | ||
this.mtime = mtime; | ||
} | ||
this.relativePath = relativePath; | ||
this.size = size; | ||
} | ||
this.mode = mode; | ||
} | ||
// ---------------------------------------------------------------------- | ||
// required properties | ||
this.relativePath = relativePath; | ||
this.size = size; | ||
this.mtime = mtime; | ||
} | ||
Entry.isDirectory = function (entry) { | ||
return (entry.mode & 61440) === 16384; | ||
}; | ||
Entry.isFile = function (entry) { | ||
return !Entry.isDirectory(entry); | ||
}; | ||
Entry.fromStat = function(relativePath, stat) { | ||
var entry = new Entry(relativePath, stat.size, stat.mtime, stat.mode); | ||
return entry; | ||
}; | ||
// required methods | ||
Entry.prototype.isDirectory = function() { | ||
return Entry.isDirectory(this); | ||
}; | ||
Entry.isDirectory = function (entry) { | ||
if (entry.mode === undefined) { | ||
return false; | ||
} | ||
else { | ||
return (entry.mode & 61440) === 16384; | ||
} | ||
}; | ||
Entry.isFile = function (entry) { | ||
return !this.isDirectory(entry); | ||
}; | ||
Entry.fromStat = function (relativePath, stat) { | ||
return new this(relativePath, stat.size, stat.mtime, stat.mode); | ||
}; | ||
Entry.prototype.isDirectory = function () { | ||
return this.constructor.isDirectory(this); | ||
}; | ||
return Entry; | ||
}()); | ||
exports.default = Entry; | ||
; |
@@ -1,51 +0,39 @@ | ||
// Type definitions for "fs-tree-diff" | ||
// Definitions by: Adam Miller <https://github.com/amiller-gh> | ||
import { Stats } from "fs"; | ||
import Entry, { DefaultEntry, BaseEntry } from './entry'; | ||
declare namespace FSTree { | ||
export type ChangeType = "unlink" | "rmdir" | "mkdir" | "create" | "change"; | ||
export type Patch = [ChangeType, string, FSTreeEntry]; | ||
export type FSTreeDelegates = { | ||
[P in ChangeType]?: (inputPath: string, outputPath: string, relativePath: string) => void | ||
}; | ||
export interface Entry { | ||
relativePath: string; | ||
mode: number; | ||
size: number; | ||
mtime: Date; | ||
isDirectory(): boolean; | ||
} | ||
export class FSTreeEntry implements Entry { | ||
relativePath: string; | ||
mode: number; | ||
size: number; | ||
mtime: Date; | ||
isDirectory(): boolean; | ||
isFile(): boolean; | ||
static fromStat(relativePath: string, stat: Stats): Entry; | ||
} | ||
export interface FSTreeOptions { | ||
entries: Entry[]; | ||
sortAndExpand: boolean; | ||
} | ||
export type PartialFSTreeOptions = Partial<FSTreeOptions>; | ||
type Operand = 'unlink' | 'rmdir' | 'create' | 'change' | 'mkdir'; | ||
type Operation = [Operand, string, DefaultEntry] | [Operand, string]; | ||
type Patch = Operation[]; | ||
type Entry = import('./entry').DefaultEntry; | ||
interface Options { | ||
entries?: BaseEntry[]; | ||
sortAndExpand?: boolean; | ||
} | ||
interface StaticOptions { | ||
sortAndExpand?: boolean; | ||
} | ||
interface PatchDelegate { | ||
unlink?(inputPath: string, outputPath: string, relativePath: string): void; | ||
rmdir?(inputPath: string, outputPath: string, relativePath: string): void; | ||
mkdir?(inputPath: string, outputPath: string, relativePath: string): void; | ||
change?(inputPath: string, outputPath: string, relativePath: string): void; | ||
create?(inputPath: string, outputPath: string, relativePath: string): void; | ||
} | ||
} | ||
declare class FSTree { | ||
constructor(options?: FSTree.PartialFSTreeOptions); | ||
calculatePatch(tree: FSTree, isEqual?: (a: FSTree.Entry, b: FSTree.Entry) => boolean): FSTree.Patch[]; | ||
calculateAndApplyPatch(tree: FSTree, inputDir: string, outputDir: string, delegate?: FSTree.FSTreeDelegates): void; | ||
addEntries(entries: FSTree.Entry[], options?: FSTree.PartialFSTreeOptions): void; | ||
addPaths(paths: string[], options?: FSTree.PartialFSTreeOptions): void; | ||
forEach(cb: (entry: FSTree.Entry) => void, context?: any): void; | ||
static fromPaths(paths: string[]): FSTree; | ||
static fromEntries(entries: FSTree.Entry[]): FSTree; | ||
static applyPatch(inputDir: string, outputDir: string, patch: FSTree.Patch[]): void; | ||
static defaultIsEqual(a: FSTree.Entry, b: FSTree.Entry): boolean; | ||
declare class FSTree<T extends BaseEntry = DefaultEntry> { | ||
entries: T[]; | ||
constructor(options?: { | ||
entries?: T[]; | ||
sortAndExpand?: boolean; | ||
}); | ||
static fromPaths(paths: string[], options?: FSTree.StaticOptions): FSTree<Entry>; | ||
static fromEntries<T extends BaseEntry>(entries: T[], options?: FSTree.StaticOptions): FSTree<T>; | ||
readonly size: number; | ||
addEntries(entries: T[], options?: FSTree.StaticOptions): void; | ||
addPaths(paths: string[], options?: FSTree.StaticOptions): void; | ||
forEach(fn: (entry: T, index: number, collection: T[]) => void, context: any): void; | ||
calculatePatch<K extends BaseEntry>(theirFSTree: FSTree<K>, isEqual?: (a: T, b: K) => boolean): FSTree.Patch; | ||
calculateAndApplyPatch(otherFSTree: FSTree<T>, input: string, output: string, delegate?: FSTree.PatchDelegate): void; | ||
static defaultIsEqual(entryA: DefaultEntry, entryB: DefaultEntry): boolean; | ||
static applyPatch(input: string, output: string, patch: FSTree.Patch, _delegate?: FSTree.PatchDelegate): void; | ||
} | ||
export = FSTree; |
482
lib/index.js
@@ -1,245 +0,259 @@ | ||
'use strict'; | ||
var fs = require('fs'); | ||
var path = require('path-posix'); | ||
var assign = require('object-assign'); | ||
var symlinkOrCopy = require('symlink-or-copy'); | ||
var Entry = require('./entry'); | ||
var logger = require('heimdalljs-logger')('fs-tree-diff:'); | ||
var util = require('./util'); | ||
var sortAndExpand = util.sortAndExpand; | ||
var validateSortedUnique = util.validateSortedUnique; | ||
"use strict"; | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
var fs = require("fs"); | ||
var path = require("path-posix"); | ||
var symlinkOrCopy = require("symlink-or-copy"); | ||
var Logger = require("heimdalljs-logger"); | ||
var entry_1 = require("./entry"); | ||
var util_1 = require("./util"); | ||
var logger = Logger('fs-tree-diff:'); | ||
var ARBITRARY_START_OF_TIME = 0; | ||
var DEFAULT_DELEGATE = { | ||
unlink: function(inputPath, outputPath, relativePath) { | ||
try { | ||
fs.unlinkSync(outputPath); | ||
} catch (e) { | ||
if (typeof e === 'object' && e !== null && e.code === 'ENOENT') { | ||
return; | ||
} | ||
throw e; | ||
unlink: function (inputPath, outputPath, relativePath) { | ||
try { | ||
fs.unlinkSync(outputPath); | ||
} | ||
catch (e) { | ||
if (typeof e === 'object' && e !== null && e.code === 'ENOENT') { | ||
return; | ||
} | ||
throw e; | ||
} | ||
}, | ||
rmdir: function (inputPath, outputPath, relativePath) { | ||
fs.rmdirSync(outputPath); | ||
}, | ||
mkdir: function (inputPath, outputPath, relativePath) { | ||
fs.mkdirSync(outputPath); | ||
}, | ||
change: function (inputPath, outputPath, relativePath) { | ||
// We no-op if the platform can symlink, because we assume the output path | ||
// is already linked via a prior create operation. | ||
if (symlinkOrCopy.canSymlink) { | ||
return; | ||
} | ||
fs.unlinkSync(outputPath); | ||
symlinkOrCopy.sync(inputPath, outputPath); | ||
}, | ||
create: function (inputPath, outputPath, relativePath) { | ||
symlinkOrCopy.sync(inputPath, outputPath); | ||
} | ||
}, | ||
rmdir: function(inputPath, outputPath, relativePath) { | ||
fs.rmdirSync(outputPath); | ||
}, | ||
mkdir: function(inputPath, outputPath, relativePath) { | ||
fs.mkdirSync(outputPath); | ||
}, | ||
change: function(inputPath, outputPath, relativePath) { | ||
// We no-op if the platform can symlink, because we assume the output path | ||
// is already linked via a prior create operation. | ||
if (symlinkOrCopy.canSymlink) { | ||
return; | ||
} | ||
fs.unlinkSync(outputPath); | ||
symlinkOrCopy.sync(inputPath, outputPath); | ||
}, | ||
create: function(inputPath, outputPath, relativePath) { | ||
symlinkOrCopy.sync(inputPath, outputPath); | ||
} | ||
}; | ||
module.exports = FSTree; | ||
function FSTree(options) { | ||
options = options || {}; | ||
var entries = options.entries || []; | ||
if (options.sortAndExpand) { | ||
sortAndExpand(entries); | ||
} else { | ||
validateSortedUnique(entries); | ||
} | ||
this.entries = entries; | ||
} | ||
FSTree.fromPaths = function(paths, options) { | ||
if (typeof options !== 'object') { options = {}; } | ||
var entries = paths.map(function(path) { | ||
return new Entry(path, 0, ARBITRARY_START_OF_TIME); | ||
}); | ||
return new FSTree({ | ||
entries: entries, | ||
sortAndExpand: options.sortAndExpand, | ||
}); | ||
}; | ||
FSTree.fromEntries = function(entries, options) { | ||
if (typeof options !== 'object') { options = {}; } | ||
return new FSTree({ | ||
entries: entries, | ||
sortAndExpand: options.sortAndExpand, | ||
}); | ||
}; | ||
Object.defineProperty(FSTree.prototype, 'size', { | ||
get: function() { | ||
return this.entries.length; | ||
} | ||
}); | ||
FSTree.prototype.addEntries = function(entries, options) { | ||
if (!Array.isArray(entries)) { | ||
throw new TypeError('entries must be an array'); | ||
} | ||
if (options && options.sortAndExpand) { | ||
sortAndExpand(entries); | ||
} else { | ||
validateSortedUnique(entries); | ||
} | ||
var fromIndex = 0; | ||
var toIndex = 0; | ||
while (fromIndex < entries.length) { | ||
while (toIndex < this.entries.length && | ||
this.entries[toIndex].relativePath < entries[fromIndex].relativePath) { | ||
toIndex++; | ||
var FSTree = /** @class */ (function () { | ||
function FSTree(options) { | ||
if (options === void 0) { options = {}; } | ||
var entries = options.entries || []; | ||
if (options.sortAndExpand) { | ||
util_1.sortAndExpand(entries); | ||
} | ||
else { | ||
util_1.validateSortedUnique(entries); | ||
} | ||
this.entries = entries; | ||
} | ||
if (toIndex < this.entries.length && | ||
this.entries[toIndex].relativePath === entries[fromIndex].relativePath) { | ||
this.entries.splice(toIndex, 1, entries[fromIndex++]); | ||
} else { | ||
this.entries.splice(toIndex++, 0, entries[fromIndex++]); | ||
} | ||
} | ||
}; | ||
FSTree.prototype.addPaths = function(paths, options) { | ||
var entries = paths.map(function(path) { | ||
return new Entry(path, 0, ARBITRARY_START_OF_TIME); | ||
}); | ||
this.addEntries(entries, options); | ||
} | ||
FSTree.prototype.forEach = function(fn, context) { | ||
this.entries.forEach(fn, context); | ||
}; | ||
FSTree.prototype.calculatePatch = function(otherFSTree, isEqual) { | ||
if (arguments.length > 1 && typeof isEqual !== 'function') { | ||
throw new TypeError('calculatePatch\'s second argument must be a function'); | ||
} | ||
if (typeof isEqual !== 'function') { | ||
isEqual = FSTree.defaultIsEqual; | ||
} | ||
var ours = this.entries; | ||
var theirs = otherFSTree.entries; | ||
var additions = []; | ||
var i = 0; | ||
var j = 0; | ||
var removals = []; | ||
var command; | ||
while (i < ours.length && j < theirs.length) { | ||
var x = ours[i]; | ||
var y = theirs[j]; | ||
if (x.relativePath < y.relativePath) { | ||
// ours | ||
i++; | ||
removals.push(removeCommand(x)); | ||
// remove additions | ||
} else if (x.relativePath > y.relativePath) { | ||
// theirs | ||
j++; | ||
additions.push(addCommand(y)); | ||
} else { | ||
if (!isEqual(x, y)) { | ||
command = updateCommand(y); | ||
if (x.isDirectory()) { | ||
removals.push(command); | ||
} else { | ||
additions.push(command); | ||
FSTree.fromPaths = function (paths, options) { | ||
if (options === void 0) { options = {}; } | ||
var entries = paths.map(function (path) { | ||
return new entry_1.default(path, 0, ARBITRARY_START_OF_TIME); | ||
}); | ||
return new this({ | ||
entries: entries, | ||
sortAndExpand: options.sortAndExpand, | ||
}); | ||
}; | ||
FSTree.fromEntries = function (entries, options) { | ||
if (options === void 0) { options = {}; } | ||
return new this({ | ||
entries: entries, | ||
sortAndExpand: options.sortAndExpand, | ||
}); | ||
}; | ||
Object.defineProperty(FSTree.prototype, "size", { | ||
get: function () { | ||
return this.entries.length; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
FSTree.prototype.addEntries = function (entries, options) { | ||
if (!Array.isArray(entries)) { | ||
throw new TypeError('entries must be an array'); | ||
} | ||
} | ||
// both are the same | ||
i++; j++; | ||
if (options !== null && typeof options === 'object' && options.sortAndExpand) { | ||
util_1.sortAndExpand(entries); | ||
} | ||
else { | ||
util_1.validateSortedUnique(entries); | ||
} | ||
var fromIndex = 0; | ||
var toIndex = 0; | ||
while (fromIndex < entries.length) { | ||
while (toIndex < this.entries.length && | ||
this.entries[toIndex].relativePath < entries[fromIndex].relativePath) { | ||
toIndex++; | ||
} | ||
if (toIndex < this.entries.length && | ||
this.entries[toIndex].relativePath === entries[fromIndex].relativePath) { | ||
this.entries.splice(toIndex, 1, entries[fromIndex++]); | ||
} | ||
else { | ||
this.entries.splice(toIndex++, 0, entries[fromIndex++]); | ||
} | ||
} | ||
}; | ||
FSTree.prototype.addPaths = function (paths, options) { | ||
var entries = paths.map(function (path) { | ||
// TODO: | ||
// addPths + a custom isEqual comparator + custom Entry types are actually incompatible | ||
// As a addPaths will not abide by the custom Entry type | ||
// and will make this.entries be contain a mixture of types. | ||
// isEqual's arguments will then be typed incorrectly | ||
// | ||
// We should likely just deprecate `addPaths` in-favor of addEntries, | ||
// which correctly externalizes the creation of entry | ||
return new entry_1.default(path, 0, ARBITRARY_START_OF_TIME); | ||
}); | ||
this.addEntries(entries, options); | ||
}; | ||
FSTree.prototype.forEach = function (fn, context) { | ||
this.entries.forEach(fn, context); | ||
}; | ||
FSTree.prototype.calculatePatch = function (theirFSTree, isEqual) { | ||
if (arguments.length > 1 && typeof isEqual !== 'function') { | ||
throw new TypeError('calculatePatch\'s second argument must be a function'); | ||
} | ||
// TODO: the TS here is strange | ||
if (typeof isEqual !== 'function') { | ||
isEqual = this.constructor.defaultIsEqual; | ||
} | ||
var ours = this.entries; | ||
var theirs = theirFSTree.entries; | ||
var additions = []; | ||
var removals = []; | ||
var i = 0; | ||
var j = 0; | ||
var command; | ||
while (i < ours.length && j < theirs.length) { | ||
var x = ours[i]; | ||
var y = theirs[j]; | ||
if (x.relativePath < y.relativePath) { | ||
// ours | ||
i++; | ||
removals.push(removeOperation(x)); | ||
// remove additions | ||
} | ||
else if (x.relativePath > y.relativePath) { | ||
// theirs | ||
j++; | ||
additions.push(addOperation(y)); | ||
} | ||
else { | ||
if (!isEqual(x, y)) { | ||
command = updateOperation(y); | ||
if (x.isDirectory()) { | ||
removals.push(command); | ||
} | ||
else { | ||
additions.push(command); | ||
} | ||
} | ||
// both are the same | ||
i++; | ||
j++; | ||
} | ||
} | ||
// cleanup ours | ||
for (; i < ours.length; i++) { | ||
removals.push(removeOperation(ours[i])); | ||
} | ||
// cleanup theirs | ||
for (; j < theirs.length; j++) { | ||
additions.push(addOperation(theirs[j])); | ||
} | ||
return removals.reverse().concat(additions); | ||
}; | ||
FSTree.prototype.calculateAndApplyPatch = function (otherFSTree, input, output, delegate) { | ||
this.constructor.applyPatch(input, output, this.calculatePatch(otherFSTree), delegate); | ||
}; | ||
FSTree.defaultIsEqual = function (entryA, entryB) { | ||
if (entryA.isDirectory() && entryB.isDirectory()) { | ||
// ignore directory changes by default | ||
return true; | ||
} | ||
var equal; | ||
if (entryA.size === entryB.size && entryA.mode === entryB.mode) { | ||
if (entryA.mtime === entryB.mtime) { | ||
equal = true; | ||
} | ||
else if (entryA.mtime === undefined || entryB.mtime === undefined) { | ||
equal = false; | ||
} | ||
else if (+entryA.mtime === +entryB.mtime) { | ||
equal = true; | ||
} | ||
else { | ||
equal = false; | ||
} | ||
} | ||
else { | ||
equal = false; | ||
} | ||
if (equal === false) { | ||
logger.info('invalidation reason: \nbefore %o\n entryB %o', entryA, entryB); | ||
} | ||
return equal; | ||
}; | ||
FSTree.applyPatch = function (input, output, patch, _delegate) { | ||
var delegate = __assign({}, DEFAULT_DELEGATE, _delegate); | ||
for (var i = 0; i < patch.length; i++) { | ||
applyOperation(input, output, patch[i], delegate); | ||
} | ||
}; | ||
return FSTree; | ||
}()); | ||
function applyOperation(input, output, operation, delegate) { | ||
var methodName = operation[0]; | ||
var relativePath = operation[1]; | ||
var inputPath = path.join(input, relativePath); | ||
var outputPath = path.join(output, relativePath); | ||
var method = delegate[methodName]; | ||
if (typeof method === 'function') { | ||
method(inputPath, outputPath, relativePath); | ||
} | ||
} | ||
// cleanup ours | ||
for (; i < ours.length; i++) { | ||
removals.push(removeCommand(ours[i])); | ||
} | ||
// cleanup theirs | ||
for (; j < theirs.length; j++) { | ||
additions.push(addCommand(theirs[j])); | ||
} | ||
// operations = removals (in reverse) then additions | ||
return removals.reverse().concat(additions); | ||
}; | ||
FSTree.prototype.calculateAndApplyPatch = function(otherFSTree, input, output, delegate) { | ||
var patch = this.calculatePatch(otherFSTree); | ||
FSTree.applyPatch(input, output, patch, delegate); | ||
}; | ||
FSTree.defaultIsEqual = function defaultIsEqual(entryA, entryB) { | ||
if (entryA.isDirectory() && entryB.isDirectory()) { | ||
// ignore directory changes by default | ||
return true; | ||
} | ||
var equal = entryA.size === entryB.size && | ||
+entryA.mtime === +entryB.mtime && | ||
entryA.mode === entryB.mode; | ||
if (!equal) { | ||
logger.info('invalidation reason: \nbefore %o\n entryB %o', entryA, entryB); | ||
} | ||
return equal; | ||
}; | ||
FSTree.applyPatch = function(input, output, patch, _delegate) { | ||
var delegate = assign({}, DEFAULT_DELEGATE, _delegate); | ||
for (var i = 0; i < patch.length; i++) { | ||
applyOperation(input, output, patch[i], delegate); | ||
} | ||
}; | ||
function applyOperation(input, output, operation, delegate) { | ||
var method = operation[0]; | ||
var relativePath = operation[1]; | ||
var inputPath = path.join(input, relativePath); | ||
var outputPath = path.join(output, relativePath); | ||
var delegateType = typeof delegate[method]; | ||
if (delegateType === 'function') { | ||
delegate[method](inputPath, outputPath, relativePath); | ||
} else { | ||
throw new Error('Unable to apply patch operation: ' + method + '. The value of delegate.' + method + ' is of type ' + delegateType + ', and not a function. Check the `delegate` argument to `FSTree.prototype.applyPatch`.'); | ||
} | ||
else { | ||
throw new Error('Unable to apply patch operation: ' + methodName + '. The value of delegate.' + methodName + ' is of type ' + typeof method + ', and not a function. Check the `delegate` argument to `FSTree.prototype.applyPatch`.'); | ||
} | ||
} | ||
function addCommand(entry) { | ||
return [entry.isDirectory() ? 'mkdir' : 'create', entry.relativePath, entry]; | ||
function addOperation(entry) { | ||
return [ | ||
entry.isDirectory() ? 'mkdir' : 'create', | ||
entry.relativePath, | ||
entry | ||
]; | ||
} | ||
function removeCommand(entry) { | ||
return [entry.isDirectory() ? 'rmdir' : 'unlink', entry.relativePath, entry]; | ||
function removeOperation(entry) { | ||
return [ | ||
entry.isDirectory() ? 'rmdir' : 'unlink', | ||
entry.relativePath, | ||
entry | ||
]; | ||
} | ||
function updateCommand(entry) { | ||
return ['change', entry.relativePath, entry]; | ||
function updateOperation(entry) { | ||
return [ | ||
'change', | ||
entry.relativePath, | ||
entry | ||
]; | ||
} | ||
; | ||
module.exports = FSTree; |
192
lib/util.js
@@ -1,124 +0,102 @@ | ||
'use strict'; | ||
var Entry = require('./entry'); | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var entry_1 = require("./entry"); | ||
function validateSortedUnique(entries) { | ||
for (var i = 1; i < entries.length; i++) { | ||
var previous = entries[i - 1].relativePath; | ||
var current = entries[i].relativePath; | ||
if (previous < current) { | ||
continue; | ||
} else { | ||
throw new Error('expected entries[' + (i -1) + ']: `' + previous + | ||
'` to be < entries[' + i + ']: `' + current + '`, but was not. Ensure your input is sorted and has no duplicate paths'); | ||
for (var i = 1; i < entries.length; i++) { | ||
var previous = entries[i - 1].relativePath; | ||
var current = entries[i].relativePath; | ||
if (previous < current) { | ||
continue; | ||
} | ||
else { | ||
throw new Error('expected entries[' + (i - 1) + ']: `' + previous + | ||
'` to be < entries[' + i + ']: `' + current + '`, but was not. Ensure your input is sorted and has no duplicate paths'); | ||
} | ||
} | ||
} | ||
} | ||
exports.validateSortedUnique = validateSortedUnique; | ||
function commonPrefix(a, b, term) { | ||
var max = Math.min(a.length, b.length); | ||
var end = -1; | ||
for(var i = 0; i < max; ++i) { | ||
if (a[i] !== b[i]) { | ||
break; | ||
} else if (a[i] === term) { | ||
end = i; | ||
var max = Math.min(a.length, b.length); | ||
var end = -1; | ||
for (var i = 0; i < max; ++i) { | ||
if (a[i] !== b[i]) { | ||
break; | ||
} | ||
else if (a[i] === term) { | ||
end = i; | ||
} | ||
} | ||
} | ||
return a.substr(0, end + 1); | ||
return a.substr(0, end + 1); | ||
} | ||
exports.commonPrefix = commonPrefix; | ||
function basename(entry) { | ||
var path = entry.relativePath; | ||
var end = path.length - 2; | ||
for(var i = end; i >= 0; --i) { | ||
if (path[i] === '/') { | ||
return path.substr(0, i + 1); | ||
var path = entry.relativePath; | ||
var end = path.length - 2; | ||
for (var i = end; i >= 0; --i) { | ||
if (path[i] === '/') { | ||
return path.substr(0, i + 1); | ||
} | ||
} | ||
} | ||
return ''; | ||
return ''; | ||
} | ||
exports.basename = basename; | ||
function computeImpliedEntries(basePath, relativePath) { | ||
var rv = []; | ||
for (var i=0; i<relativePath.length; ++i) { | ||
if (relativePath[i] === '/') { | ||
var path = basePath + relativePath.substr(0, i + 1); | ||
rv.push(new Entry(path, 0, 0)); | ||
var rv = []; | ||
for (var i = 0; i < relativePath.length; ++i) { | ||
if (relativePath[i] === '/') { | ||
var path = basePath + relativePath.substr(0, i + 1); | ||
rv.push(new entry_1.default(path, 0, 0)); | ||
} | ||
} | ||
} | ||
return rv; | ||
return rv; | ||
} | ||
exports.computeImpliedEntries = computeImpliedEntries; | ||
function compareByRelativePath(entryA, entryB) { | ||
var pathA = entryA.relativePath; | ||
var pathB = entryB.relativePath; | ||
if (pathA < pathB) { | ||
return -1; | ||
} else if (pathA > pathB) { | ||
return 1; | ||
} | ||
return 0; | ||
var pathA = entryA.relativePath; | ||
var pathB = entryB.relativePath; | ||
if (pathA < pathB) { | ||
return -1; | ||
} | ||
else if (pathA > pathB) { | ||
return 1; | ||
} | ||
return 0; | ||
} | ||
exports.compareByRelativePath = compareByRelativePath; | ||
function sortAndExpand(entries) { | ||
entries.sort(compareByRelativePath); | ||
var path = ''; | ||
for (var i=0; i<entries.length; ++i) { | ||
var entry = entries[i]; | ||
// update our path eg | ||
// path = a/b/c/d/ | ||
// entry = a/b/q/r/s/ | ||
// path' = a/b/ | ||
path = commonPrefix(path, entry.relativePath, '/'); | ||
// a/b/ -> a/ | ||
// a/b -> a/ | ||
var base = basename(entry); | ||
// base - path | ||
var entryBaseSansCommon = base.substr(path.length); | ||
// determine what intermediate directories are missing eg | ||
// path = a/b/ | ||
// entryBaseSansCommon = c/d/e/ | ||
// impliedEntries = [a/b/c/, a/b/c/d/, a/b/c/d/e/] | ||
var impliedEntries = computeImpliedEntries(path, entryBaseSansCommon); | ||
// actually add our implied entries to entries | ||
if (impliedEntries.length > 0) { | ||
entries.splice.apply(entries, [i, 0].concat(impliedEntries)); | ||
i += impliedEntries.length; | ||
entries.sort(compareByRelativePath); | ||
var path = ''; | ||
for (var i = 0; i < entries.length; ++i) { | ||
var entry = entries[i]; | ||
// update our path eg | ||
// path = a/b/c/d/ | ||
// entry = a/b/q/r/s/ | ||
// path' = a/b/ | ||
path = commonPrefix(path, entry.relativePath, '/'); | ||
// a/b/ -> a/ | ||
// a/b -> a/ | ||
var base = basename(entry); | ||
// base - path | ||
var entryBaseSansCommon = base.substr(path.length); | ||
// determine what intermediate directories are missing eg | ||
// path = a/b/ | ||
// entryBaseSansCommon = c/d/e/ | ||
// impliedEntries = [a/b/c/, a/b/c/d/, a/b/c/d/e/] | ||
var impliedEntries = computeImpliedEntries(path, entryBaseSansCommon); | ||
// actually add our implied entries to entries | ||
if (impliedEntries.length > 0) { | ||
entries.splice.apply(entries, [i, 0].concat(impliedEntries)); | ||
i += impliedEntries.length; | ||
} | ||
// update path. Now that we've created all the intermediate directories, we | ||
// don't need to recreate them for subsequent entries. | ||
if (entry.isDirectory()) { | ||
path = entry.relativePath; | ||
} | ||
else { | ||
path = base; | ||
} | ||
} | ||
// update path. Now that we've created all the intermediate directories, we | ||
// don't need to recreate them for subsequent entries. | ||
if (entry.isDirectory()) { | ||
path = entry.relativePath; | ||
} else { | ||
path = base; | ||
} | ||
} | ||
return entries; | ||
return entries; | ||
} | ||
module.exports = { | ||
validateSortedUnique: validateSortedUnique, | ||
sortAndExpand: sortAndExpand, | ||
// exported for testing | ||
_commonPrefix: commonPrefix, | ||
_basename: basename, | ||
_computeImpliedEntries: computeImpliedEntries, | ||
}; | ||
exports.sortAndExpand = sortAndExpand; |
{ | ||
"name": "fs-tree-diff", | ||
"version": "1.0.2", | ||
"version": "2.0.0", | ||
"description": "Backs out file tree changes", | ||
@@ -11,6 +11,7 @@ "main": "lib/index.js", | ||
"scripts": { | ||
"test": "npm run test:js && npm run test:types", | ||
"test:js": "mocha tests/", | ||
"test:types": "tsc --noEmit tests.ts/index.ts", | ||
"test:debug": "mocha debug tests/" | ||
"test": "npm run test:js", | ||
"test:js": "mocha --require ts-node/register tests/*-test.ts", | ||
"test:js:debug": "mocha debug --require ts-node/register tests/*-test.ts", | ||
"build": "tsc", | ||
"prepublish": "tsc" | ||
}, | ||
@@ -23,2 +24,3 @@ "keywords": [ | ||
"dependencies": { | ||
"@types/symlink-or-copy": "^1.2.0", | ||
"heimdalljs-logger": "^0.1.7", | ||
@@ -30,7 +32,11 @@ "object-assign": "^4.1.0", | ||
"devDependencies": { | ||
"@types/node": "^10.12.11", | ||
"@types/chai": "^4.1.7", | ||
"@types/fs-extra": "^5.0.4", | ||
"@types/mocha": "^5.2.5", | ||
"@types/node": "^10.12.21", | ||
"chai": "^3.3.0", | ||
"fs-extra": "^1.0.0", | ||
"mocha": "^2.3.3", | ||
"typescript": "^3.2.1", | ||
"ts-node": "^8.0.2", | ||
"typescript": "^3.3.3", | ||
"walk-sync": "^0.3.1" | ||
@@ -37,0 +43,0 @@ }, |
@@ -27,8 +27,8 @@ # fs-tree-diff [![Build Status](https://travis-ci.org/stefanpenner/fs-tree-diff.svg?branch=master)](https://travis-ci.org/stefanpenner/fs-tree-diff) [![Build status](https://ci.appveyor.com/api/projects/status/qmhx48hrquq08fam/branch/master?svg=true)](https://ci.appveyor.com/project/embercli/fs-tree-diff/branch/master) | ||
```js | ||
var FSTree = require('fs-tree-diff'); | ||
var current = FSTree.fromPaths([ | ||
const FSTree = require('fs-tree-diff'); | ||
const current = FSTree.fromPaths([ | ||
'a.js' | ||
]); | ||
var next = FSTree.fromPaths([ | ||
const next = FSTree.fromPaths([ | ||
'b.js' | ||
@@ -46,4 +46,4 @@ ]); | ||
```js | ||
var FSTree = require('fs-tree-diff'); | ||
var current = FSTree.fromPaths([ | ||
const FSTree = require('fs-tree-diff'); | ||
const current = FSTree.fromPaths([ | ||
'a.js', | ||
@@ -54,3 +54,3 @@ 'b/', | ||
var next = FSTree.fromPaths([ | ||
const next = FSTree.fromPaths([ | ||
'b.js', | ||
@@ -84,7 +84,7 @@ 'b/', | ||
```js | ||
var walkSync = require('walk-sync'); | ||
const walkSync = require('walk-sync'); | ||
// path/to/root/foo.js | ||
// path/to/root/bar.js | ||
var current = new FSTree({ | ||
const current = new FSTree({ | ||
entries: walkSync.entries('path/to/root') | ||
@@ -96,3 +96,3 @@ }); | ||
var next = new FSTree({ | ||
const next = new FSTree({ | ||
entries: walkSync.entries('path/to/root') | ||
@@ -204,7 +204,7 @@ }); | ||
```js | ||
var walkSync = require('walk-sync'); | ||
const walkSync = require('walk-sync'); | ||
// path/to/root/foo.js | ||
// path/to/root/bar.js | ||
var current = FSTree.fromEntries(walkSync.entries('path/to/root')); | ||
const current = FSTree.fromEntries(walkSync.entries('path/to/root')); | ||
``` | ||
@@ -231,3 +231,3 @@ | ||
```js | ||
var defaultIsEqual = FSTree.defaultIsEqual; | ||
const defaultIsEqual = FSTree.defaultIsEqual; | ||
@@ -249,5 +249,5 @@ function isEqualCheckingMeta(a, b) { | ||
```js | ||
var patch = oldInputTree.calculatePatch(newInputTree); | ||
var inputDir = 'src'; | ||
var outputDir = 'dist'; | ||
const patch = oldInputTree.calculatePatch(newInputTree); | ||
const inputDir = 'src'; | ||
const outputDir = 'dist'; | ||
FSTree.applyPatch(inputDir, outputDir, patch); | ||
@@ -261,4 +261,4 @@ ``` | ||
```js | ||
var inputDir = 'src'; | ||
var outputDir = 'dist'; | ||
const inputDir = 'src'; | ||
const outputDir = 'dist'; | ||
oldInputTree.calculateAndApplyPatch(newInputTree, inputDir, outputDir); | ||
@@ -271,3 +271,3 @@ ``` | ||
```js | ||
var createCount = 0; | ||
let createCount = 0; | ||
FSTree.applyPatch(inputDir, outputDir, patch, { | ||
@@ -274,0 +274,0 @@ create: function(inputPath, outputPath, relativePath) { |
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
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
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
41561
12
868
5
10
1
+ Added@types/symlink-or-copy@1.2.2(transitive)