From ecc596b9d95e5ccd11e626829d0ce102d841e76c Mon Sep 17 00:00:00 2001 From: Sergey Belov Date: Tue, 11 Dec 2012 16:49:27 +0400 Subject: [PATCH] bem mv work in progress --- lib/coa.js | 1 + lib/commands/mv.js | 221 +++++++++++++++++++++++++++++++++++++++++++++ lib/util.js | 36 ++++++-- package.json | 3 +- 4 files changed, 251 insertions(+), 10 deletions(-) create mode 100644 lib/commands/mv.js diff --git a/lib/coa.js b/lib/coa.js index 6e379c19..10a744bb 100644 --- a/lib/coa.js +++ b/lib/coa.js @@ -19,6 +19,7 @@ module.exports = require('coa').Cmd() .cmd().name('decl').apply(require('./commands/decl')).end() .cmd().name('build').apply(require('./commands/build')).end() .cmd().name('create').apply(require('./commands/create')).end() + .cmd().name('mv').apply(require('./commands/mv')).end() .cmd().name('make').apply(require('./commands/make')).end() .cmd().name('server').apply(require('./commands/server')).end() .completable() diff --git a/lib/commands/mv.js b/lib/commands/mv.js new file mode 100644 index 00000000..5fea67c5 --- /dev/null +++ b/lib/commands/mv.js @@ -0,0 +1,221 @@ +var Q = require('q'), + QFS = require('q-fs'), + UTIL = require('util'), + U = require('../util'); + +module.exports = function() { + + return this + .title('BEM entities move and rename tool.') + .helpful() + .apply(U.chdirOptParse) + .opt() + .name('sourceLevel') + .title('source level') + .short('s').long('src') + .apply(U.applyLevelOpt('source level')) + .end() + .opt() + .name('targetLevel') + .title('target level') + .short('t').long('target') + .apply(U.applyLevelOpt('destination level')) + .end() + .opt() + .name('cherryPick') + .title('cherry pick spicified entity only, without its contents') + .short('c').long('cherry-pick') + .flag() + .end() + .opt() + .name('dryRun') + .title('do not move anything, just output to the console') + .long('dry-run') + .flag() + .end() + .opt() + .name('force') + .title('force') + .short('f').long('force') + .flag() + .end() + .arg() + .name('source') + .title('Source BEM entity in the form of block__elem_mod_val.js') + .val(bemParse) + .req() + .end() + .arg() + .name('target') + .title('Target BEM entity') + .val(bemParse) + .end() + .act(function(opts, args) { + + var strict = opts.cherryPick, + force = opts.force, + source = args.source, + target = args.target || source, + sourceLevel = opts.sourceLevel, + targetLevel = opts.targetLevel, + items = getItems(sourceLevel, source, strict); + + if (!source.block) { + return Q.reject('bem mv: You should specify full BEM entity to move'); + } + + if (target.techs && target.techs.join(',') !== source.techs.join(',')) { + // FIXME: Support changing tech of the BEM entity + return Q.reject('bem mv: Changing tech of the BEM entity in not yet implemented'); + } + + if (U.bemKey(source) === U.bemKey(target) && sourceLevel.dir === targetLevel.dir) { + return Q.reject(UTIL.format("bem mv: Could not move '%s' to self", U.bemKey(source))); + } + + if (U.bemType(source) === U.bemType(target)) { + + // CASE: Rename BEM entity in the context of the same level + // CASE: Move BEM entity to another level + // CASE: Move BEM entity to another level with name change + + items = items.map( + function(source) { + return { + source: source, + target: U.extend({}, source, target) + } + }); + + } else { + + // CASE: Refactor BEM entity in the context of the same level + // CASE: Move BEM entity to another level with refactor + // FIXME: Support changing of BEM entity type + return Q.reject('bem mv: Changing of BEM entity type is not yet implemented'); + + } + + // Map BEM source and target BEM entities to paths + var paths = items.map(function(item) { + + return { + source: getPath(sourceLevel, item.source), + target: getPath(targetLevel, item.target) + } + + }); + + return checkExists(paths, force) + .then(function(exists) { + + if (opts.dryRun) return dryRun(paths); + + // Move + return Q.all( + paths.map(function(p) { + return copy(p.source, p.target); + })) + .get(0); + + }); + + }); + +}; + +function getItems(level, item, strict) { + + return level.getItemsByIntrospection() + .filter(getBemEntityFilter(item, strict)); + +} + +function checkExists(paths, force) { + + // Collect existent target paths + var exists = U.filterPaths( + paths.map(function(p) { + return p.target; + })); + + return exists.then(function(exists) { + + // Do not move if there are existent target paths and --force in not specified + if (!force && exists.length) { + return Q.reject('bem mv: Following target paths are exist, run with ' + + '--force to overwrite:\n' + exists.join('\n')); + } + + return exists; + + }); + +} + +function dryRun(paths) { + + return paths + .map(function(p) { + return UTIL.format('Moving %s to %s', p.source, p.target); + }) + .join('\n'); + +} + +function copy(source, target) { + + // FIXME: implement + console.log('Moving %s to %s', source, target); + +} + +function getPath(level, item) { + return level.getByObj(item) + item.suffix; +} + +function getBemEntityFilter(filter, strict) { + + var keys = Object.keys(filter); + + return function(item) { + + var res = true; + + if (strict && U.bemKey(filter) !== U.bemKey(item)) { + res = false; + } + + for (var i = 0, key; res && i < keys.length; i++) { + + key = keys[i]; + + if (key === 'techs') { + // filter on techs + if (!~filter.techs.indexOf(item.tech)) res = false; + } else { + // filter on other keys + if (item[key] !== filter[key]) res = false; + } + + } + + return res; + + } + +} + +function bemParse(key) { + + var item = U.bemParseKey(key); + + // Try to get techs specified using dot notation + if (item.tech) { + item.techs = item.tech.split(','); + delete item.tech; + } + + return item; + +} diff --git a/lib/util.js b/lib/util.js index 349552a5..51b4b4af 100644 --- a/lib/util.js +++ b/lib/util.js @@ -42,17 +42,35 @@ exports.techsOptParse = function() { }; exports.levelOptParse = function() { - var def = exports.findLevel(process.cwd()), - rel = PATH.relative(process.cwd(), def); + return this.opt() - .name('level').short('l').long('level') - .def(def) - .title(['level directory path, default: ', - !rel? '.' : rel, PATH.dirSep].join('')) - .val(function(l) { - return typeof l == 'string'? require('./level').createLevel(l) : l; - }) + .name('level') + .short('l') + .long('level') + .apply(exports.applyLevelOpt()) .end(); + +}; + +exports.applyLevelOpt = function(levelDesc) { + + levelDesc || (levelDesc = 'level'); + + var def = exports.findLevel(process.cwd()), + rel = PATH.relative(process.cwd(), def); + + return function() { + + return this + .def(def) + .title([levelDesc + ' directory path, default: ', + !rel? '.' : rel, PATH.dirSep].join('')) + .val(function(l) { + return typeof l == 'string'? require('./level').createLevel(l) : l; + }); + + } + }; exports.mergeTechs = function(level, opts) { diff --git a/package.json b/package.json index 897d32c4..a6659e6e 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,8 @@ "coffee-script": "*", "dom-js": "*", "mkdirp": "~0.3.4", - "node.extend": "~1.0.0" + "node.extend": "~1.0.0", + "fs.extra": "~1.2.0" }, "devDependencies": { "istanbul": "~0.1.21",