fs-jetpack
Advanced tools
Comparing version 0.9.2 to 0.10.0
@@ -0,1 +1,6 @@ | ||
0.10.0 (2016-10-17) | ||
------------------- | ||
* `copyAsync()` uses only streams (much more memory efficient). | ||
* `find()` supports `recursive` option. | ||
0.9.2 (2016-06-27) | ||
@@ -2,0 +7,0 @@ ------------------- |
129
lib/copy.js
@@ -11,3 +11,3 @@ 'use strict'; | ||
var fileMode = require('./utils/mode'); | ||
var inspectTree = require('./inspect_tree'); | ||
var treeWalker = require('./utils/tree_walker'); | ||
var write = require('./write'); | ||
@@ -81,3 +81,3 @@ | ||
var copyItemSync = function (inspectData, to) { | ||
var copyItemSync = function (from, inspectData, to) { | ||
var mode = fileMode.normalizeFileMode(inspectData.mode); | ||
@@ -87,5 +87,5 @@ if (inspectData.type === 'dir') { | ||
} else if (inspectData.type === 'file') { | ||
copyFileSync(inspectData.absolutePath, to, mode); | ||
copyFileSync(from, to, mode); | ||
} else if (inspectData.type === 'symlink') { | ||
copySymlinkSync(inspectData.absolutePath, to); | ||
copySymlinkSync(from, to); | ||
} | ||
@@ -96,16 +96,17 @@ }; | ||
var opts = parseOptions(options, from); | ||
var walker; | ||
var inspectData; | ||
var destPath; | ||
checksBeforeCopyingSync(from, to, opts); | ||
walker = inspectTree.createTreeWalkerSync(from); | ||
while (walker.hasNext()) { | ||
inspectData = walker.getNext(); | ||
destPath = pathUtil.join(to, inspectData.relativePath); | ||
if (opts.allowedToCopy(inspectData.absolutePath)) { | ||
copyItemSync(inspectData, destPath); | ||
treeWalker.sync(from, { | ||
inspectOptions: { | ||
mode: true, | ||
symlinks: true | ||
} | ||
} | ||
}, function (path, inspectData) { | ||
var rel = pathUtil.relative(from, path); | ||
var destPath = pathUtil.resolve(to, rel); | ||
if (opts.allowedToCopy(path)) { | ||
copyItemSync(path, inspectData, destPath); | ||
} | ||
}); | ||
}; | ||
@@ -117,3 +118,2 @@ | ||
var promisedReadFile = Q.denodeify(fs.readFile); | ||
var promisedSymlink = Q.denodeify(fs.symlink); | ||
@@ -140,7 +140,24 @@ var promisedReadlink = Q.denodeify(fs.readlink); | ||
var copyFileAsync = function (from, to, mode) { | ||
return promisedReadFile(from) | ||
.then(function (data) { | ||
return write.async(to, data, { mode: mode }); | ||
}); | ||
var copyFileAsync = function (from, to, mode, retriedAttempt) { | ||
var deferred = Q.defer(); | ||
fs.createReadStream(from) | ||
.pipe(fs.createWriteStream(to, { mode: mode })) | ||
.on('error', function (err) { | ||
var toDirPath = pathUtil.dirname(to); | ||
if (err.code === 'ENOENT' && retriedAttempt === undefined) { | ||
// Some parent directory doesn't exits. Create it and retry. | ||
promisedMkdirp(toDirPath).then(function () { | ||
// Make retry attempt only once to prevent vicious infinite loop | ||
// (when for some obscure reason I/O will keep returning ENOENT error). | ||
// Passing retriedAttempt = true. | ||
copyFileAsync(from, to, mode, true) | ||
.then(deferred.resolve) | ||
.catch(deferred.reject); | ||
}); | ||
} else { | ||
deferred.reject(err); | ||
} | ||
}) | ||
.on('finish', deferred.resolve); | ||
return deferred.promise; | ||
}; | ||
@@ -174,3 +191,3 @@ | ||
var copyItemAsync = function (inspectData, to) { | ||
var copyItemAsync = function (from, inspectData, to) { | ||
var mode = fileMode.normalizeFileMode(inspectData.mode); | ||
@@ -180,5 +197,5 @@ if (inspectData.type === 'dir') { | ||
} else if (inspectData.type === 'file') { | ||
return copyFileAsync(inspectData.absolutePath, to, mode); | ||
return copyFileAsync(from, to, mode); | ||
} else if (inspectData.type === 'symlink') { | ||
return copySymlinkAsync(inspectData.absolutePath, to); | ||
return copySymlinkAsync(from, to); | ||
} | ||
@@ -190,30 +207,2 @@ // Ha! This is none of supported file system entities. What now? | ||
var runCopyLoop = function (from, to, opts) { | ||
var deferred = Q.defer(); | ||
var inspectData; | ||
var destPath; | ||
var copyNext = function (walker) { | ||
if (walker.hasNext()) { | ||
inspectData = walker.getNext(); | ||
destPath = pathUtil.join(to, inspectData.relativePath); | ||
if (opts.allowedToCopy(inspectData.absolutePath)) { | ||
copyItemAsync(inspectData, destPath) | ||
.then(function () { | ||
copyNext(walker); | ||
}) | ||
.catch(deferred.reject); | ||
} else { | ||
copyNext(walker); | ||
} | ||
} else { | ||
deferred.resolve(); | ||
} | ||
}; | ||
inspectTree.createTreeWalkerAsync(from).then(copyNext); | ||
return deferred.promise; | ||
}; | ||
var copyAsync = function (from, to, options) { | ||
@@ -225,5 +214,39 @@ var deferred = Q.defer(); | ||
.then(function () { | ||
return runCopyLoop(from, to, opts); | ||
var allFilesDelivered = false; | ||
var filesInProgress = 0; | ||
var stream = treeWalker.stream(from, { | ||
inspectOptions: { | ||
mode: true, | ||
symlinks: true | ||
} | ||
}) | ||
.on('readable', function () { | ||
var item = stream.read(); | ||
var rel; | ||
var destPath; | ||
if (item) { | ||
rel = pathUtil.relative(from, item.path); | ||
destPath = pathUtil.resolve(to, rel); | ||
if (opts.allowedToCopy(item.path)) { | ||
filesInProgress += 1; | ||
copyItemAsync(item.path, item.item, destPath) | ||
.then(function () { | ||
filesInProgress -= 1; | ||
if (allFilesDelivered && filesInProgress === 0) { | ||
deferred.resolve(); | ||
} | ||
}) | ||
.catch(deferred.reject); | ||
} | ||
} | ||
}) | ||
.on('error', deferred.reject) | ||
.on('end', function () { | ||
allFilesDelivered = true; | ||
if (allFilesDelivered && filesInProgress === 0) { | ||
deferred.resolve(); | ||
} | ||
}); | ||
}) | ||
.then(deferred.resolve) | ||
.catch(deferred.reject); | ||
@@ -230,0 +253,0 @@ |
108
lib/find.js
@@ -5,3 +5,4 @@ 'use strict'; | ||
var Q = require('q'); | ||
var inspectTree = require('./inspect_tree'); | ||
var treeWalker = require('./utils/tree_walker'); | ||
var inspect = require('./inspect'); | ||
var matcher = require('./utils/matcher'); | ||
@@ -18,23 +19,8 @@ | ||
} | ||
if (opts.recursive === undefined) { | ||
opts.recursive = true; | ||
} | ||
return opts; | ||
}; | ||
var filterTree = function (tree, options) { | ||
var matchesAnyOfGlobs = matcher.create(options.matching, tree.absolutePath); | ||
return inspectTree.utils.flattenTree(tree) | ||
.filter(function (inspectObj) { | ||
return matchesAnyOfGlobs(inspectObj.absolutePath); | ||
}) | ||
.filter(function (inspectObj) { | ||
if (inspectObj.type === 'file' && options.files === true) { | ||
return true; | ||
} | ||
if (inspectObj.type === 'dir' && options.directories === true) { | ||
return true; | ||
} | ||
return false; | ||
}); | ||
}; | ||
var processFoundObjects = function (foundObjects, cwd) { | ||
@@ -63,17 +49,31 @@ return foundObjects.map(function (inspectObj) { | ||
var findSync = function (path, options) { | ||
var foundInspectObjects; | ||
var tree; | ||
var foundInspectObjects = []; | ||
var matchesAnyOfGlobs = matcher.create(options.matching, path); | ||
tree = inspectTree.sync(path, { | ||
absolutePath: true | ||
treeWalker.sync(path, { | ||
maxLevelsDeep: options.recursive ? Infinity : 1, | ||
inspectOptions: { | ||
absolutePath: true | ||
} | ||
}, function (itemPath, item) { | ||
if (matchesAnyOfGlobs(itemPath)) { | ||
if ((item.type === 'file' && options.files === true) | ||
|| (item.type === 'dir' && options.directories === true)) { | ||
foundInspectObjects.push(item); | ||
} | ||
} | ||
}); | ||
if (tree === undefined) { | ||
return processFoundObjects(foundInspectObjects, options.cwd); | ||
}; | ||
var findSyncInit = function (path, options) { | ||
var entryPointInspect = inspect.sync(path); | ||
if (entryPointInspect === undefined) { | ||
throw generatePathDoesntExistError(path); | ||
} else if (tree.type !== 'dir') { | ||
} else if (entryPointInspect.type !== 'dir') { | ||
throw generatePathNotDirectoryError(path); | ||
} | ||
foundInspectObjects = filterTree(tree, normalizeOptions(options)); | ||
return processFoundObjects(foundInspectObjects, options.cwd); | ||
return findSync(path, normalizeOptions(options)); | ||
}; | ||
@@ -87,22 +87,28 @@ | ||
var deferred = Q.defer(); | ||
var foundInspectObjects = []; | ||
var matchesAnyOfGlobs = matcher.create(options.matching, path); | ||
inspectTree.async(path, { | ||
relativePath: true, | ||
absolutePath: true | ||
var walker = treeWalker.stream(path, { | ||
maxLevelsDeep: options.recursive ? Infinity : 1, | ||
inspectOptions: { | ||
absolutePath: true | ||
} | ||
}) | ||
.then(function (tree) { | ||
var foundInspectObjects; | ||
var toReturn; | ||
if (tree === undefined) { | ||
throw generatePathDoesntExistError(path); | ||
} else if (tree.type !== 'dir') { | ||
throw generatePathNotDirectoryError(path); | ||
.on('readable', function () { | ||
var data = walker.read(); | ||
var item; | ||
if (data) { | ||
item = data.item; | ||
if (matchesAnyOfGlobs(data.path)) { | ||
if ((item.type === 'file' && options.files === true) | ||
|| (item.type === 'dir' && options.directories === true)) { | ||
foundInspectObjects.push(item); | ||
} | ||
} | ||
} | ||
foundInspectObjects = filterTree(tree, normalizeOptions(options)); | ||
toReturn = processFoundObjects(foundInspectObjects, options.cwd); | ||
deferred.resolve(toReturn); | ||
}) | ||
.catch(deferred.reject); | ||
.on('error', deferred.reject) | ||
.on('end', function () { | ||
deferred.resolve(processFoundObjects(foundInspectObjects, options.cwd)); | ||
}); | ||
@@ -112,2 +118,14 @@ return deferred.promise; | ||
var findAsyncInit = function (path, options) { | ||
return inspect.async(path) | ||
.then(function (entryPointInspect) { | ||
if (entryPointInspect === undefined) { | ||
throw generatePathDoesntExistError(path); | ||
} else if (entryPointInspect.type !== 'dir') { | ||
throw generatePathNotDirectoryError(path); | ||
} | ||
return findAsync(path, normalizeOptions(options)); | ||
}); | ||
}; | ||
// --------------------------------------------------------- | ||
@@ -117,3 +135,3 @@ // API | ||
exports.sync = findSync; | ||
exports.async = findAsync; | ||
exports.sync = findSyncInit; | ||
exports.async = findAsyncInit; |
@@ -26,18 +26,2 @@ 'use strict'; | ||
// Flattens tree structure to one list of inspectObjects. | ||
var flattenTree = function (tree) { | ||
var treeAsList = []; | ||
var crawl = function (inspectObj) { | ||
treeAsList.push(inspectObj); | ||
if (inspectObj.children) { | ||
inspectObj.children.forEach(crawl); | ||
} | ||
}; | ||
crawl(tree); | ||
return treeAsList; | ||
}; | ||
// --------------------------------------------------------- | ||
@@ -81,18 +65,2 @@ // Sync | ||
var createTreeWalkerSync = function (startPath) { | ||
var allFiles = flattenTree(inspectTreeSync(startPath, { | ||
absolutePath: true, | ||
relativePath: true, | ||
mode: true | ||
})); | ||
return { | ||
hasNext: function () { | ||
return allFiles.length > 0; | ||
}, | ||
getNext: function () { | ||
return allFiles.shift(); | ||
} | ||
}; | ||
}; | ||
// --------------------------------------------------------- | ||
@@ -171,25 +139,2 @@ // Async | ||
var createTreeWalkerAsync = function (startPath) { | ||
var deferred = Q.defer(); | ||
inspectTreeAsync(startPath, { | ||
absolutePath: true, | ||
relativePath: true, | ||
mode: true | ||
}) | ||
.then(function (wholeTree) { | ||
var allFiles = flattenTree(wholeTree); | ||
deferred.resolve({ | ||
hasNext: function () { | ||
return allFiles.length > 0; | ||
}, | ||
getNext: function () { | ||
return allFiles.shift(); | ||
} | ||
}); | ||
}); | ||
return deferred.promise; | ||
}; | ||
// --------------------------------------------------------- | ||
@@ -200,9 +145,2 @@ // API | ||
exports.sync = inspectTreeSync; | ||
exports.createTreeWalkerSync = createTreeWalkerSync; | ||
exports.async = inspectTreeAsync; | ||
exports.createTreeWalkerAsync = createTreeWalkerAsync; | ||
exports.utils = { | ||
flattenTree: flattenTree | ||
}; |
{ | ||
"name": "fs-jetpack", | ||
"description": "Better file system API", | ||
"version": "0.9.2", | ||
"version": "0.10.0", | ||
"author": "Jakub Szwacz <jakub@szwacz.com>", | ||
@@ -6,0 +6,0 @@ "dependencies": { |
@@ -330,2 +330,3 @@ fs-jetpack [![Build Status](https://travis-ci.org/szwacz/fs-jetpack.svg?branch=master)](https://travis-ci.org/szwacz/fs-jetpack) [![Build status](https://ci.appveyor.com/api/projects/status/er206e91fpuuqf58?svg=true)](https://ci.appveyor.com/project/szwacz/fs-jetpack) | ||
* `directories` (default `false`) whether or not should search for directories. | ||
* `recursive` (default `true`) whether the whole directory tree should be searched recursively, or only one-level of given directory (excluding it's subdirectories). | ||
@@ -332,0 +333,0 @@ **returns:** |
@@ -32,2 +32,23 @@ /* eslint-env jasmine */ | ||
it('if recursive option set will exclude subfolders from search', function (done) { | ||
var expectations = function (found) { | ||
var normalizedFound = helper.convertToUnixPathSeparators(found); | ||
expect(normalizedFound).toEqual(['x/file.txt']); | ||
}; | ||
fse.outputFileSync('x/file.txt', 'abc'); | ||
fse.outputFileSync('x/y/file.txt', '123'); | ||
fse.outputFileSync('x/y/b/file.txt', '456'); | ||
// SYNC | ||
expectations(jetpack.find('x', { matching: '*.txt', recursive: false })); | ||
// ASYNC | ||
jetpack.findAsync('x', { matching: '*.txt', recursive: false }) | ||
.then(function (found) { | ||
expectations(found); | ||
done(); | ||
}); | ||
}); | ||
it('defaults to CWD if no path provided', function (done) { | ||
@@ -34,0 +55,0 @@ var expectations = function (found) { |
Sorry, the diff of this file is not supported yet
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
189208
58
5516
555
35