Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

grunt-sync

Package Overview
Dependencies
Maintainers
1
Versions
25
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

grunt-sync - npm Package Compare versions

Comparing version 0.0.8 to 0.1.0

3

Gruntfile.js
/*global module:false*/
module.exports = function(grunt) {
require('time-grunt')(grunt);
// Project configuration.

@@ -5,0 +6,0 @@ grunt.initConfig({

{
"name": "grunt-sync",
"description": "Task to synchronize two directories. Similar to grunt-copy but updates only files that have been changed.",
"version": "0.0.8",
"version": "0.1.0",
"homepage": "https://github.com/tomusdrw/grunt-sync.git",

@@ -33,11 +33,13 @@ "author": {

"dependencies": {
"glob": "^4.0.5",
"promised-io": "0.3.3"
},
"devDependencies": {
"mocha": "~1.8.1",
"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",
"grunt-contrib-jshint": "0.1.x",
"grunt-complexity": "^0.1.51"
"mocha": "~1.8.1",
"time-grunt": "^0.4.0"
},

@@ -44,0 +46,0 @@ "keywords": [

@@ -28,2 +28,3 @@ # Grunt-sync

}],
ignoreInDest: "**/*.js", // Never remove js files from destination
verbose: true // Display log messages when copying files

@@ -47,3 +48,6 @@ }

],
verbose: true
verbose: true,
pretend: true, // Don't do any disk operations - just write log
updateOnly: true // Don't remove any files from `dest` (works around 30% faster)
}

@@ -53,4 +57,7 @@ }

## Changelog
* 0.1.0 - Files missing that are not in `src` are deleted from `dest` (unless you specify `updateOnly`)
## TODO
Task does not remove any files and directories in `dest` that are no longer in `src`.
Research if it's possible to have better integration with `grunt-contrib-watch` - update only changed files instead of scanning everything.
{
"main": {
"files": [{
"src": ["**"],
"dest": "bin/to",
"cwd": "bin/from"
}],
"verbose": true
}
}
"main": {
"files": [{
"src": ["**"],
"dest": "bin/to",
"expand": true,
"cwd": "bin/from"
}],
"ignoreInDest": ["t", "*.js"],
"updateOnly": false,
"verbose": true,
"pretend": true
}
}

@@ -1,90 +0,252 @@

var fs = require('promised-io/fs');
var promise = require('promised-io/promise');
var path = require('path');
var fs = require('promised-io/fs'),
promise = require('promised-io/promise'),
path = require('path'),
glob = require('glob'),
util = require('util');
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(options, src, dest) {
grunt[options.logMethod].writeln('Overwriting ' + dest.cyan + 'because type differs.');
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 updateIfNeeded = function(options, src, dest, srcStat, destStat) {
// we can now compare modification dates of files
if(srcStat.mtime.getTime() > destStat.mtime.getTime()) {
grunt[options.logMethod].writeln('Updating file ' + dest.cyan);
// and just update destination
tryCopy(src, dest);
}
var processPair = function(justPretend, logger, src, dest) {
var doOrPretend = function(operation) {
if (justPretend) {
return;
}
operation();
};
var overwriteOrUpdate = function(isSrcDirectory, typeDiffers, srcStat, destStat) {
// If types differ we have to overwrite destination.
if (typeDiffers) {
logger.writeln('Overwriting ' + dest.cyan + ' because type differs.');
doOrPretend(function() {
overwriteDest(src, dest);
});
return;
}
// we can now compare modification dates of files
if (isSrcDirectory || srcStat.mtime.getTime() <= destStat.mtime.getTime()) {
return;
}
logger.writeln('Updating file ' + dest.cyan);
doOrPretend(function() {
// and just update destination
tryCopy(src, dest);
});
};
//stat destination file
return promise.all([fs.stat(src), fs.stat(dest)]).then(function(result) {
var srcStat = result[0],
destStat = result[1];
var isSrcDirectory = srcStat.isDirectory();
var typeDiffers = isSrcDirectory !== destStat.isDirectory();
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() {
tryMkdir(dest);
});
} else {
logger.writeln('Copying ' + src.cyan + ' -> ' + dest.cyan);
doOrPretend(function() {
tryCopy(src, dest);
});
}
});
};
var processPair = function(options, src, dest) {
//stat destination file
return promise.all([fs.stat(src), fs.stat(dest)]).then(function(result) {
var srcStat = result[0], destStat = result[1];
var removePaths = function(justPretend, logger, paths) {
var isSrcDirectory = srcStat.isDirectory();
var typeDiffers = isSrcDirectory !== destStat.isDirectory();
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);
// If types differ we have to overwrite destination.
if(typeDiffers) {
overwriteDest(options,src, dest);
} else if(!isSrcDirectory) {
updateIfNeeded(options, src, dest, srcStat, destStat);
}
}, function() {
// we got an error which means that destination file does not exist
// so make a copy
if(grunt.file.isDir(src)) {
grunt[options.logMethod].writeln('Creating ' + dest.cyan);
tryMkdir(dest);
} else {
grunt[options.logMethod].writeln('Copying ' + src.cyan + ' -> ' + dest.cyan);
tryCopy(src, dest);
}
});
// 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;
});
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);
}));
});
});
};
grunt.registerMultiTask('sync', 'Synchronize content of two directories.', function() {
var done = this.async();
var options = {
logMethod: this.data.verbose ? 'log' : 'verbose'
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: []
});
};
promise.all(this.files.map(function(fileDef) {
var cwd = fileDef.cwd ? fileDef.cwd : '.';
return promise.all(fileDef.src.map(function(src){
var dest = path.join(fileDef.dest, src);
// when using expanded mapping dest is the destination file
// not the destination folder
if(fileDef.orig.expand) {
dest = fileDef.dest;
}
return processPair(options, path.join(cwd, src), dest);
}));
})).then(function(promises) {
promise.all(promises).then(done);
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.updateOnly,
justPretend = !!this.data.pretend,
ignoredPatterns = this.data.ignoreInDest,
expandedPaths = {};
var getExpandedPaths = function(origDest) {
expandedPaths[origDest] = expandedPaths[origDest] || [];
return expandedPaths[origDest];
};
promise.all(this.files.map(function(fileDef) {
var cwd = fileDef.cwd ? fileDef.cwd : '.';
var isExpanded = fileDef.orig.expand;
var origDest = fileDef.orig.dest;
var processedDestinations = getExpandedPaths(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) {
dest = fileDef.dest;
} else {
dest = path.join(fileDef.dest, src);
}
processedDestinations.push(dest);
return processPair(justPretend, logger, path.join(cwd, src), dest);
}));
})).then(function() {
if (updateOnly) {
return;
}
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;
}
if (!util.isArray(ignore)) {
ignore = [ignore]
}
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);
});
return defer.promise;
};
// Second pass
return promise.all(Object.keys(expandedPaths).map(function(dest) {
var processedDestinations = expandedPaths[dest];
// We have to do second pass to remove objects from dest
var destPaths = getDestPaths(dest, '**');
// Check if we have any ignore patterns
var ignoredPaths = getIgnoredPaths(dest, ignoredPatterns);
return promise.all([destPaths, ignoredPaths]).then(function(result) {
// Calculate diff
var toRemove = fastArrayDiff(result[0], processedDestinations);
// And filter also ignored paths
toRemove = fastArrayDiff(toRemove, result[1]);
return removePaths(justPretend, logger, toRemove);
});
}));
}).then(done);
});
});
};
};
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc