UNPKG

@stekker/node-perforce

Version:
334 lines (289 loc) 9.98 kB
'use strict'; var S = require('string'); var os = require('os'); var exec = require('child_process').exec; var p4options = require('./p4options'); var ztagRegex = /^\.\.\.\s+(\w+)\s+(.+)/; var p4 = process.platform === 'win32'? 'p4.exe' : 'p4'; // build a list of options/arguments for the p4 command function optionBuilder(options) { options = options || {}; var results = {stdin: [], args: [], files: []}; Object.keys(options).map(function (option) { var p4option = p4options[option]; if (!p4option) return; if (p4option.category !== 'unary') { if ((options[option] || {}).constructor !== p4option.type) return; } if (p4option.category === 'stdin') { results.stdin.push(p4option.cmd + options[option]); if (results.args.indexOf('-i') < 0) results.args.push('-i'); } else if (p4option.cmd) { results.args.push(p4option.cmd); if (p4option.category === 'mixed') results.args.push(options[option]); } else { results.files = results.files.concat(options[option]); } }); return results; } // filter passed-in options to get a hash of child process options // (i.e., not p4 command arguments) function execOptionBuilder(options) { var validKeys = { cwd: true, env: true, encoding: true, shell: true, timeout: true, maxBuffer: true, killSignal: true, uid: true, gid: true }; options = options || {}; return Object.keys(options).reduce(function(result, key) { if(validKeys[key]) { result[key] = options[key]; } return result; }, {}); } function execP4(p4cmd, options, callback) { if (typeof options === 'function') { callback = options; options = undefined; } let c = callback; let p = new Promise((resolve, reject) => { callback = function (err, out) { if(c) { out = c(err, out); } if(err) { reject(err); } else { resolve(out); } return out; }; }); var ob = optionBuilder(options); var childProcessOptions = execOptionBuilder(options); var cmd = [p4, p4cmd, ob.args.join(' '), ob.files.join(' ')]; var child = exec(cmd.join(' '), childProcessOptions, function (err, stdout, stderr) { if (err) return callback(err); if (stderr) return callback(new Error(stderr)); return callback(null, stdout); }); if (ob.stdin.length > 0) { ob.stdin.forEach(function (line) { child.stdin.write(line + '\n'); }); child.stdin.emit('end'); } return p; } // process group of lines of output from a p4 command executed with -ztag function processZtagOutput(output) { return output.split('\n').reduce(function(memo, line) { var match, key, value; match = ztagRegex.exec(line); if(match) { key = match[1]; value = match[2]; memo[key] = value; } return memo; }, {}); } function NodeP4() {} NodeP4.prototype.changelist = { create: function (options, callback) { if (typeof options === 'function') { callback = options; options = undefined; } var newOptions = { _change: 'new', description: options.description || '<saved by node-perforce>' }; return execP4('change', newOptions, function (err, stdout) { if (err) return callback(err); var matched = stdout.match(/([0-9]+)/g); if (matched.length > 0) return callback(null, parseInt(matched[0], 10)); else return callback(new Error('Unknown error')); }); }, edit: function (options, callback) { callback = callback || function(){}; if (!options || !options.changelist) return callback(new Error('Missing parameter/argument')); if (!options.description) return callback(); var newOptions = { _change: options.changelist.toString(), description: options.description }; return execP4('change', newOptions, function (err) { if (err) return callback(err); return callback(); }); }, delete: function (options, callback) { callback = callback || function(){}; if (!options || !options.changelist) return callback(new Error('Missing parameter/argument')); return execP4('change', {_delete: options.changelist}, function (err) { if (err) return callback(err); return callback(); }); }, view: function (options, callback) { if (!options || !options.changelist) return callback(new Error('Missing parameter/argument')); return execP4('change', {_output: options.changelist}, function (err, stdout) { if (err) return callback(err); // preprocessing file status stdout = stdout.replace(/(\t)+#(.)*/g, function (match) { return '@@@' + match.substring(3); }); var result = {}; var lines = stdout.replace(/#(.)*\n/g, '').split(os.EOL + os.EOL); lines.forEach(function (line) { var key = S(line.split(':')[0].toLowerCase()).trim().camelize().s; if (key) { result[key] = S(line).between(':').trim().s; } }); if (result.files) { result.files = result.files.split('\n').map(function (file) { var file = file.replace(/\t*/g, '').split('@@@'); return {file: file[0], action: file[1]}; }); } else { result.files = []; } return callback(null, result); }); }, submit: function (options, callback) { if (!options || !options.changelist) return callback(new Error('Missing parameter/argument')); return execP4('submit', options, function (err, stdout) { if (err) return callback(err); }); } }; NodeP4.prototype.info = function (options, callback) { if(typeof options === 'function') { callback = options; options = undefined; } return execP4('info', options, function (err, stdout) { if (err) return callback(err); var result = {}; S(stdout).lines().forEach(function (line) { if (!line) return; var key = S((line.split(':')[0]).toLowerCase()).camelize().s; result[key] = S(line).between(':').trim().s; }); return callback(null, result); }); }; // return an array of file info objects for each file opened in the workspace NodeP4.prototype.opened = function (options, callback) { if(typeof options === 'function') { callback = options; options = undefined; } return execP4('-ztag opened', options, function (err, stdout) { var result; if (err) return callback(err); // process each file result = stdout.trim().split(/\r\n\r\n|\n\n/).reduce(function(memo, fileinfo) { // process each line of file info, transforming into a hash memo.push(processZtagOutput(fileinfo)); return memo; }, []); return callback(null, result); }); }; NodeP4.prototype.fstat = function (options, callback) { if(typeof options === 'function') { callback = options; options = undefined; } if(!callback) { callback = (err, val) => {return val;}; } return execP4('fstat', options, function (err, stdout) { var result; if (err) return callback(err); // process each file fstat info result = stdout.trim().split(/\r\n\r\n|\n\n/).reduce(function(memo, fstatinfo) { // process each line of file info, transforming into a hash memo.push(processZtagOutput(fstatinfo)); return memo; }, []); return callback(null, result); }); } NodeP4.prototype.changes = function (options, callback) { if(typeof options === 'function') { callback = options; options = undefined; } return execP4('-ztag changes', options, function (err, stdout) { var result; if (err) return callback(err); // process each change result = stdout.trim().split(/\r\n\r\n|\n\n(?=\.\.\.)/).reduce(function(memo, changeinfo) { // process each line of change info, transforming into a hash memo.push(processZtagOutput(changeinfo)); return memo; }, []); return callback(null, result); }); }; NodeP4.prototype.user = function (options, callback) { if(typeof options === 'function') { callback = options; options = undefined; } return execP4('-ztag user', options, function (err, stdout) { var result; if (err) return callback(err); // process ztagged user information result = processZtagOutput(stdout.trim()); return callback(null, result); }); }; NodeP4.prototype.users = function (options, callback) { if(typeof options === 'function') { callback = options; options = undefined; } return execP4('-ztag users', options, function (err, stdout) { var result; if (err) return callback(err); // process each change result = stdout.trim().split(/\r\n\r\n|\n\n(?=\.\.\.)/).reduce(function(memo, userinfo) { // process each line of user info, transforming into a hash memo.push(processZtagOutput(userinfo)); return memo; }, []); return callback(null, result); }); }; NodeP4.prototype.custom = function (call, options, callback) { if(typeof options === 'function') { callback = options; options = undefined; } return execP4(call, options, callback); }; var commonCommands = ['add', 'delete', 'edit', 'revert', 'sync', 'diff', 'reconcile', 'reopen', 'resolved', 'shelve', 'unshelve', 'client', 'resolve', 'submit', 'dirs']; commonCommands.forEach(function (command) { NodeP4.prototype[command] = function (options, callback) { return execP4(command, options, callback); }; }); module.exports = new NodeP4();