grunt-sync
Advanced tools
Comparing version 0.2.2 to 0.2.3
{ | ||
"name": "grunt-sync", | ||
"description": "Task to synchronize two directories. Similar to grunt-copy but updates only files that have been changed.", | ||
"version": "0.2.2", | ||
"homepage": "https://github.com/tomusdrw/grunt-sync.git", | ||
"author": { | ||
"name": "Tomasz Drwiega", | ||
"email": "tomusdrw@blacksoft.eu", | ||
"url": "http://blacksoft.eu" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git://github.com/tomusdrw/grunt-sync.git" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/tomusdrw/grunt-sync/issues" | ||
}, | ||
"licences": [{ | ||
"type": "MIT", | ||
"url": "http://opensource.org/licenses/MIT" | ||
}], | ||
"main": "Gruntfile.js", | ||
"readmeFilename": "readme.md", | ||
"engines": { | ||
"node": ">=0.8.0" | ||
}, | ||
"scripts": { | ||
"test": "mocha" | ||
}, | ||
"dependencies": { | ||
"glob": "^4.0.5", | ||
"promised-io": "0.3.3" | ||
}, | ||
"devDependencies": { | ||
"chai": "1.4.2", | ||
"grunt": "0.4.x", | ||
"grunt-complexity": "^0.1.51", | ||
"grunt-contrib-jshint": "0.1.x", | ||
"grunt-simple-mocha": "0.3.x", | ||
"mocha": "~1.8.1", | ||
"time-grunt": "^0.4.0" | ||
}, | ||
"keywords": [ | ||
"gruntplugin", | ||
"sync", | ||
"synchronize", | ||
"copy" | ||
] | ||
"name": "grunt-sync", | ||
"description": "Task to synchronize two directories. Similar to grunt-copy but updates only files that have been changed.", | ||
"version": "0.2.3", | ||
"homepage": "https://github.com/tomusdrw/grunt-sync.git", | ||
"author": { | ||
"name": "Tomasz Drwiega", | ||
"email": "tomusdrw@blacksoft.eu", | ||
"url": "http://blacksoft.eu" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git://github.com/tomusdrw/grunt-sync.git" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/tomusdrw/grunt-sync/issues" | ||
}, | ||
"licences": [ | ||
{ | ||
"type": "MIT", | ||
"url": "http://opensource.org/licenses/MIT" | ||
} | ||
], | ||
"main": "Gruntfile.js", | ||
"readmeFilename": "readme.md", | ||
"engines": { | ||
"node": ">=0.8.0" | ||
}, | ||
"scripts": { | ||
"test": "mocha" | ||
}, | ||
"dependencies": { | ||
"glob": "^4.0.5", | ||
"lodash": "^2.4.1", | ||
"promised-io": "0.3.3" | ||
}, | ||
"devDependencies": { | ||
"chai": "1.4.2", | ||
"grunt": "0.4.x", | ||
"grunt-complexity": "^0.1.51", | ||
"grunt-contrib-jshint": "0.1.x", | ||
"grunt-simple-mocha": "0.3.x", | ||
"mocha": "~1.8.1", | ||
"time-grunt": "^0.4.0" | ||
}, | ||
"keywords": [ | ||
"gruntplugin", | ||
"sync", | ||
"synchronize", | ||
"copy" | ||
] | ||
} |
{ | ||
"main": { | ||
"files": [{ | ||
"src": ["**"], | ||
"dest": "bin/to/", | ||
"expand": true, | ||
"cwd": "bin/from" | ||
}], | ||
"ignoreInDest": ["t", "*.js"], | ||
"updateAndDelete": true, | ||
"verbose": true, | ||
"pretend": true | ||
}, | ||
"rename": { | ||
"src": "bin/somefile.txt", | ||
"dest": "bin/somefile2.txt" | ||
}, | ||
"windows": { | ||
"files": [ | ||
{ "src": ["www\\**\\*.*", "!**\\*.ts"], "dest": "cordova\\"}], | ||
"verbose": true, | ||
"pretend": false, | ||
"updateAndDelete": true | ||
} | ||
"main": { | ||
"files": [{ | ||
"src": ["**"], | ||
"dest": "bin/to/", | ||
"expand": true, | ||
"cwd": "bin/from/" | ||
}], | ||
"verbose": true, | ||
"pretend": true | ||
}, | ||
"rename": { | ||
"src": "bin/somefile.txt", | ||
"dest": "bin/somefile2.txt" | ||
}, | ||
"windows": { | ||
"files": [{ | ||
"src": ["www\\**\\*.*", "!**\\*.ts"], | ||
"dest": "cordova\\" | ||
}], | ||
"verbose": true, | ||
"pretend": false, | ||
"updateAndDelete": true | ||
}, | ||
"no26": { | ||
"files": [{ | ||
"cwd": "bin/p2/icons", | ||
"expand": true, | ||
"src": "**/*", | ||
"dest": "bin/p2/build/imgs/icons" | ||
}, { | ||
"cwd": "bin/p2/src/js", | ||
"expand": true, | ||
"src": "**/*", | ||
"dest": "bin/p2/build/js" | ||
}, { | ||
"expand": true, | ||
"src": "bin/p2/index.html", | ||
"dest": "bin/p2/build" | ||
}], | ||
"verbose": true, | ||
"updateAndDelete": true | ||
} | ||
} |
var fs = require('promised-io/fs'), | ||
promise = require('promised-io/promise'), | ||
path = require('path'), | ||
glob = require('glob'), | ||
util = require('util'); | ||
promise = require('promised-io/promise'), | ||
path = require('path'), | ||
glob = require('glob'), | ||
util = require('util'), | ||
_ = require('lodash'); | ||
module.exports = function(grunt) { | ||
var tryCopy = function(src, dest) { | ||
try { | ||
grunt.file.copy(src, dest); | ||
} catch (e) { | ||
grunt.log.warn('Cannot copy to ' + dest.red); | ||
} | ||
}; | ||
var tryCopy = function(src, dest) { | ||
try { | ||
grunt.file.copy(src, dest); | ||
} catch (e) { | ||
grunt.log.warn('Cannot copy to ' + dest.red); | ||
} | ||
}; | ||
var tryMkdir = function(dest) { | ||
try { | ||
grunt.file.mkdir(dest); | ||
} catch (e) { | ||
grunt.log.warn('Cannot create directory ' + dest.red); | ||
} | ||
}; | ||
var tryMkdir = function(dest) { | ||
try { | ||
grunt.file.mkdir(dest); | ||
} catch (e) { | ||
grunt.log.warn('Cannot create directory ' + dest.red); | ||
} | ||
}; | ||
var overwriteDest = function(src, dest) { | ||
try { | ||
grunt.file['delete'](dest); | ||
grunt.file.copy(src, dest); | ||
} catch (e) { | ||
grunt.log.warn('Cannot overwrite ' + dest.red); | ||
} | ||
var overwriteDest = function(src, dest) { | ||
try { | ||
grunt.file['delete'](dest); | ||
grunt.file.copy(src, dest); | ||
} catch (e) { | ||
grunt.log.warn('Cannot overwrite ' + dest.red); | ||
} | ||
}; | ||
var processPair = function(justPretend, logger, src, dest) { | ||
var doOrPretend = function(operation) { | ||
if (justPretend) { | ||
return; | ||
} | ||
operation(); | ||
}; | ||
var overwriteOrUpdate = function(isSrcDirectory, typeDiffers, srcStat, destStat) { | ||
var processPair = function(justPretend, logger, src, dest) { | ||
var doOrPretend = function(operation) { | ||
if (justPretend) { | ||
return; | ||
} | ||
operation(); | ||
}; | ||
// If types differ we have to overwrite destination. | ||
if (typeDiffers) { | ||
logger.writeln('Overwriting ' + dest.cyan + ' because type differs.'); | ||
var overwriteOrUpdate = function(isSrcDirectory, typeDiffers, srcStat, destStat) { | ||
doOrPretend(function() { | ||
overwriteDest(src, dest); | ||
}); | ||
return; | ||
} | ||
// If types differ we have to overwrite destination. | ||
if (typeDiffers) { | ||
logger.writeln('Overwriting ' + dest.cyan + ' because type differs.'); | ||
// we can now compare modification dates of files | ||
if (isSrcDirectory || srcStat.mtime.getTime() <= destStat.mtime.getTime()) { | ||
return; | ||
} | ||
doOrPretend(function() { | ||
overwriteDest(src, dest); | ||
}); | ||
return; | ||
} | ||
logger.writeln('Updating file ' + dest.cyan); | ||
doOrPretend(function() { | ||
// and just update destination | ||
tryCopy(src, dest); | ||
}); | ||
}; | ||
// we can now compare modification dates of files | ||
if (isSrcDirectory || srcStat.mtime.getTime() <= destStat.mtime.getTime()) { | ||
return; | ||
} | ||
//stat destination file | ||
return promise.all([fs.stat(src), fs.stat(dest)]).then(function(result) { | ||
var srcStat = result[0], | ||
destStat = result[1]; | ||
logger.writeln('Updating file ' + dest.cyan); | ||
doOrPretend(function() { | ||
// and just update destination | ||
tryCopy(src, dest); | ||
}); | ||
}; | ||
var isSrcDirectory = srcStat.isDirectory(); | ||
var typeDiffers = isSrcDirectory !== destStat.isDirectory(); | ||
//stat destination file | ||
return promise.all([fs.stat(src), fs.stat(dest)]).then(function(result) { | ||
var srcStat = result[0], | ||
destStat = result[1]; | ||
overwriteOrUpdate(isSrcDirectory, typeDiffers, srcStat, destStat); | ||
}, function() { | ||
// we got an error which means that destination file does not exist | ||
// so make a copy | ||
if (grunt.file.isDir(src)) { | ||
logger.writeln('Creating ' + dest.cyan); | ||
var isSrcDirectory = srcStat.isDirectory(); | ||
var typeDiffers = isSrcDirectory !== destStat.isDirectory(); | ||
doOrPretend(function() { | ||
tryMkdir(dest); | ||
}); | ||
} else { | ||
logger.writeln('Copying ' + src.cyan + ' -> ' + dest.cyan); | ||
overwriteOrUpdate(isSrcDirectory, typeDiffers, srcStat, destStat); | ||
}, function() { | ||
// we got an error which means that destination file does not exist | ||
// so make a copy | ||
if (grunt.file.isDir(src)) { | ||
logger.writeln('Creating ' + dest.cyan); | ||
doOrPretend(function() { | ||
tryCopy(src, dest); | ||
}); | ||
} | ||
}); | ||
}; | ||
doOrPretend(function() { | ||
tryMkdir(dest); | ||
}); | ||
} else { | ||
logger.writeln('Copying ' + src.cyan + ' -> ' + dest.cyan); | ||
var removePaths = function(justPretend, logger, paths) { | ||
doOrPretend(function() { | ||
tryCopy(src, dest); | ||
}); | ||
} | ||
return promise.all(paths.map(function(file) { | ||
return fs.stat(file).then(function(stat) { | ||
return { | ||
file: file, | ||
isDirectory: stat.isDirectory() | ||
}; | ||
}); | ||
})).then(function(stats) { | ||
var paths = splitFilesAndDirs(stats); | ||
// First we need to process files | ||
return promise.all(paths.files.map(function(filePath) { | ||
logger.writeln('Unlinking ' + filePath.cyan + ' because it was removed from src.'); | ||
if (justPretend) { | ||
return; | ||
} | ||
return fs.unlink(filePath); | ||
})).then(function() { | ||
// Then process directories in ascending order | ||
var sortedDirs = paths.dirs.sort(function(a, b) { | ||
return b.length - a.length; | ||
}); | ||
}; | ||
var removePaths = function(justPretend, logger, paths) { | ||
return promise.all(sortedDirs.map(function(dir) { | ||
logger.writeln('Removing dir ' + dir.cyan + ' because not longer in src.'); | ||
if (justPretend) { | ||
return; | ||
} | ||
return fs.rmdir(dir); | ||
})); | ||
}); | ||
}); | ||
return promise.all(paths.map(function(file) { | ||
return fs.stat(file).then(function(stat) { | ||
return { | ||
file: file, | ||
isDirectory: stat.isDirectory() | ||
}; | ||
}); | ||
})).then(function(stats) { | ||
var paths = splitFilesAndDirs(stats); | ||
}; | ||
// First we need to process files | ||
return promise.all(paths.files.map(function(filePath) { | ||
logger.writeln('Unlinking ' + filePath.cyan + ' because it was removed from src.'); | ||
var splitFilesAndDirs = function(stats) { | ||
return stats.reduce(function(memo, stat) { | ||
if (stat.isDirectory) { | ||
memo.dirs.push(stat.file); | ||
} else { | ||
memo.files.push(stat.file); | ||
} | ||
return memo; | ||
}, { | ||
files: [], | ||
dirs: [] | ||
}); | ||
}; | ||
if (justPretend) { | ||
return; | ||
} | ||
return fs.unlink(filePath); | ||
})).then(function() { | ||
// Then process directories in ascending order | ||
var sortedDirs = paths.dirs.sort(function(a, b) { | ||
return b.length - a.length; | ||
}); | ||
var fastArrayDiff = function(from, diff) { | ||
diff.map(function(v) { | ||
from[from.indexOf(v)] = undefined; | ||
}); | ||
return from.filter(function(v) { | ||
return v; | ||
}); | ||
}; | ||
return promise.all(sortedDirs.map(function(dir) { | ||
logger.writeln('Removing dir ' + dir.cyan + ' because not longer in src.'); | ||
if (justPretend) { | ||
return; | ||
} | ||
return fs.rmdir(dir); | ||
})); | ||
}); | ||
}); | ||
var convertPathsToSystemSpecific = function(paths) { | ||
return paths.map(function(filePath) { | ||
return path.join.apply(path, filePath.split('/')); | ||
}); | ||
}; | ||
}; | ||
var addDirectoriesPaths = function(arr, dest) { | ||
var f = dest.split(path.sep); | ||
var i, p; | ||
p = f[0]; | ||
var splitFilesAndDirs = function(stats) { | ||
return stats.reduce(function(memo, stat) { | ||
if (stat.isDirectory) { | ||
memo.dirs.push(stat.file); | ||
} else { | ||
memo.files.push(stat.file); | ||
} | ||
return memo; | ||
}, { | ||
files: [], | ||
dirs: [] | ||
}); | ||
}; | ||
for (i = 1; i < f.length - 1; ++i) { | ||
p += path.sep + f[i]; | ||
if (arr.indexOf(p) === -1) { | ||
arr.push(p); | ||
} | ||
} | ||
}; | ||
var fastArrayDiff = function(from, diff) { | ||
diff.map(function(v) { | ||
from[from.indexOf(v)] = undefined; | ||
}); | ||
return from.filter(function(v) { | ||
return v; | ||
}); | ||
}; | ||
grunt.registerMultiTask('sync', 'Synchronize content of two directories.', function() { | ||
var done = this.async(), | ||
logger = grunt[this.data.verbose ? 'log' : 'verbose'], | ||
updateOnly = !this.data.updateAndDelete, | ||
justPretend = !!this.data.pretend, | ||
ignoredPatterns = this.data.ignoreInDest, | ||
expandedPaths = {}; | ||
var convertPathsToSystemSpecific = function(paths) { | ||
return paths.map(function(filePath) { | ||
return path.join.apply(path, filePath.split('/')); | ||
}); | ||
}; | ||
var addDirectoriesPaths = function(arr, dest) { | ||
var f = dest.split(path.sep); | ||
var i, p; | ||
p = f[0]; | ||
for (i=1; i<f.length - 1; ++i) { | ||
p += path.sep + f[i]; | ||
if (arr.indexOf(p) === -1) { | ||
arr.push(p); | ||
} | ||
} | ||
var getExpandedPaths = function(origDest) { | ||
if (!expandedPaths[origDest]) { | ||
// Always include destination as processed. | ||
expandedPaths[origDest] = [origDest.replace(new RegExp("\\" + path.sep + "$"), '')]; | ||
return expandedPaths[origDest]; | ||
} | ||
return expandedPaths[origDest]; | ||
}; | ||
grunt.registerMultiTask('sync', 'Synchronize content of two directories.', function() { | ||
var done = this.async(), | ||
logger = grunt[this.data.verbose ? 'log' : 'verbose'], | ||
updateOnly = !this.data.updateAndDelete, | ||
justPretend = !!this.data.pretend, | ||
ignoredPatterns = this.data.ignoreInDest, | ||
expandedPaths = {}; | ||
promise.all(this.files.map(function(fileDef) { | ||
var isCompactForm = this.data.src && this.data.dest; | ||
var cwd = fileDef.cwd ? fileDef.cwd : '.'; | ||
var isExpanded = fileDef.orig.expand; | ||
var origDest = path.join(fileDef.orig.dest, ''); | ||
var processedDestinations = getExpandedPaths(origDest); | ||
var getExpandedPaths = function(origDest) { | ||
if (!expandedPaths[origDest]) { | ||
// Always include destination as processed. | ||
expandedPaths[origDest] = [origDest.replace(new RegExp("\\" + path.sep + "$"), '')]; | ||
return expandedPaths[origDest] | ||
} | ||
return expandedPaths[origDest]; | ||
}; | ||
return promise.all(fileDef.src.map(function(src) { | ||
var dest; | ||
// when using expanded mapping dest is the destination file | ||
// not the destination folder | ||
if (isExpanded || isCompactForm) { | ||
dest = fileDef.dest; | ||
} else { | ||
dest = path.join(fileDef.dest, src); | ||
} | ||
if (!updateOnly) { | ||
processedDestinations.push(dest); | ||
// Make sure to add directory of file as well (handle cases when source has pattern for files only) | ||
addDirectoriesPaths(processedDestinations, dest); | ||
} | ||
// Process pair | ||
return processPair(justPretend, logger, path.join(cwd, src), dest); | ||
})); | ||
promise.all(this.files.map(function(fileDef) { | ||
var isCompactForm = this.data.src && this.data.dest; | ||
var cwd = fileDef.cwd ? fileDef.cwd : '.'; | ||
var isExpanded = fileDef.orig.expand; | ||
var origDest = path.join(fileDef.orig.dest, ''); | ||
}, this)).then(function() { | ||
if (updateOnly) { | ||
return; | ||
} | ||
var processedDestinations = getExpandedPaths(origDest); | ||
var getDestPaths = function(dest, pattern) { | ||
var defer = new promise.Deferred(); | ||
glob(path.join(dest, pattern), { | ||
dot: true | ||
}, function(err, result) { | ||
if (err) { | ||
defer.reject(err); | ||
return; | ||
} | ||
defer.resolve(result); | ||
}); | ||
return defer.promise; | ||
}; | ||
return promise.all(fileDef.src.map(function(src) { | ||
var dest; | ||
// when using expanded mapping dest is the destination file | ||
// not the destination folder | ||
if (isExpanded || isCompactForm) { | ||
dest = fileDef.dest; | ||
} else { | ||
dest = path.join(fileDef.dest, src); | ||
} | ||
if (!updateOnly) { | ||
processedDestinations.push(dest); | ||
// Make sure to add directory of file as well (handle cases when source has pattern for files only) | ||
addDirectoriesPaths(processedDestinations, dest); | ||
} | ||
// Process pair | ||
return processPair(justPretend, logger, path.join(cwd, src), dest); | ||
})); | ||
var getIgnoredPaths = function(dest, ignore) { | ||
var defer = new promise.Deferred(); | ||
if (!ignore) { | ||
defer.resolve([]); | ||
return defer.promise; | ||
} | ||
}, this)).then(function() { | ||
if (updateOnly) { | ||
return; | ||
} | ||
if (!util.isArray(ignore)) { | ||
ignore = [ignore]; | ||
} | ||
var getDestPaths = function(dest, pattern) { | ||
var defer = new promise.Deferred(); | ||
glob(path.join(dest, pattern), { | ||
dot: true | ||
}, function(err, result) { | ||
if (err) { | ||
defer.reject(err); | ||
return; | ||
} | ||
defer.resolve(result); | ||
}); | ||
return defer.promise; | ||
}; | ||
var getIgnoredPaths = function(dest, ignore) { | ||
var defer = new promise.Deferred(); | ||
if (!ignore) { | ||
defer.resolve([]); | ||
return defer.promise; | ||
} | ||
promise.all(ignore.map(function(pattern) { | ||
return getDestPaths(dest, pattern); | ||
})).then(function(results) { | ||
var flat = results.reduce(function(memo, a) { | ||
return memo.concat(a); | ||
}, []); | ||
defer.resolve(flat); | ||
}, function(err) { | ||
defer.reject(err); | ||
}); | ||
if (!util.isArray(ignore)) { | ||
ignore = [ignore] | ||
} | ||
return defer.promise; | ||
}; | ||
promise.all(ignore.map(function(pattern) { | ||
return getDestPaths(dest, pattern); | ||
})).then(function(results) { | ||
var flat = results.reduce(function(memo, a) { | ||
return memo.concat(a); | ||
}, []); | ||
defer.resolve(flat); | ||
}, function(err) { | ||
defer.reject(err); | ||
}); | ||
// Second pass | ||
return promise.all(Object.keys(expandedPaths).map(function(dest) { | ||
var processedDestinations = convertPathsToSystemSpecific(expandedPaths[dest]); | ||
return defer.promise; | ||
}; | ||
// We have to do second pass to remove objects from dest | ||
var destPaths = getDestPaths(dest, '**'); | ||
// Second pass | ||
return promise.all(Object.keys(expandedPaths).map(function(dest) { | ||
var processedDestinations = convertPathsToSystemSpecific(expandedPaths[dest]); | ||
// Check if we have any ignore patterns | ||
var ignoredPaths = getIgnoredPaths(dest, ignoredPatterns); | ||
// We have to do second pass to remove objects from dest | ||
var destPaths = getDestPaths(dest, '**'); | ||
return promise.all([destPaths, ignoredPaths, processedDestinations]); | ||
})).then(function(result) { | ||
var files = result.map(function(destAndIgnored) { | ||
var paths = convertPathsToSystemSpecific(destAndIgnored[0]); | ||
var ignoredPaths = convertPathsToSystemSpecific(destAndIgnored[1]); | ||
// Check if we have any ignore patterns | ||
var ignoredPaths = getIgnoredPaths(dest, ignoredPatterns); | ||
return [paths, ignoredPaths, destAndIgnored[2]]; | ||
}).reduce(function(memo, destAndIgnored) { | ||
return memo.map(function(val, key) { | ||
return val.concat(destAndIgnored[key]); | ||
}); | ||
}, [[], [], []]); | ||
// TODO Find some faster way to ensure uniqueness here | ||
var paths = _.uniq(files[0]); | ||
var ignoredPaths = _.uniq(files[1]); | ||
var processedDestinations = _.uniq(files[2]); | ||
return promise.all([destPaths, ignoredPaths]).then(function(result) { | ||
var paths = convertPathsToSystemSpecific(result[0]); | ||
var ignoredPaths = convertPathsToSystemSpecific(result[1]); | ||
// Calculate diff | ||
var toRemove = fastArrayDiff(paths, processedDestinations); | ||
// And filter also ignored paths | ||
toRemove = fastArrayDiff(toRemove, ignoredPaths); | ||
return removePaths(justPretend, logger, toRemove); | ||
}); | ||
})); | ||
}).then(done); | ||
}); | ||
// Calculate diff | ||
var toRemove = fastArrayDiff(paths, processedDestinations); | ||
// And filter also ignored paths | ||
toRemove = fastArrayDiff(toRemove, ignoredPaths); | ||
return removePaths(justPretend, logger, toRemove); | ||
}); | ||
}).then(done); | ||
}); | ||
}; |
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
336
15751
3
+ Addedlodash@^2.4.1
+ Addedlodash@2.4.2(transitive)