'use strict';
var os = require('os');
var fs = require('fs');
var glob = require('glob');
var shell = require('..');
var shellMethods = Object.create(shell);
exports.extend = Object.assign;
var isElectron = Boolean(process.versions.electron);
var DEFAULT_CONFIG = {
fatal: false,
globOptions: {},
maxdepth: 255,
noglob: false,
silent: false,
verbose: false,
execPath: null,
bufLength: 64 * 1024, };
var config = {
reset: function () {
Object.assign(this, DEFAULT_CONFIG);
if (!isElectron) {
this.execPath = process.execPath;
}
},
resetForTesting: function () {
this.reset();
this.silent = true;
},
};
config.reset();
exports.config = config;
var state = {
error: null,
errorCode: 0,
currentCmd: 'shell.js',
};
exports.state = state;
delete process.env.OLDPWD;
function isObject(a) {
return typeof a === 'object' && a !== null;
}
exports.isObject = isObject;
function log() {
if (!config.silent) {
console.error.apply(console, arguments);
}
}
exports.log = log;
function convertErrorOutput(msg) {
if (typeof msg !== 'string') {
throw new TypeError('input must be a string');
}
return msg.replace(/\\/g, '/');
}
exports.convertErrorOutput = convertErrorOutput;
function error(msg, _code, options) {
if (typeof msg !== 'string') throw new Error('msg must be a string');
var DEFAULT_OPTIONS = {
continue: false,
code: 1,
prefix: state.currentCmd + ': ',
silent: false,
};
if (typeof _code === 'number' && isObject(options)) {
options.code = _code;
} else if (isObject(_code)) { options = _code;
} else if (typeof _code === 'number') { options = { code: _code };
} else if (typeof _code !== 'number') { options = {};
}
options = Object.assign({}, DEFAULT_OPTIONS, options);
if (!state.errorCode) state.errorCode = options.code;
var logEntry = convertErrorOutput(options.prefix + msg);
state.error = state.error ? state.error + '\n' : '';
state.error += logEntry;
if (config.fatal) throw new Error(logEntry);
if (msg.length > 0 && !options.silent) log(logEntry);
if (!options.continue) {
throw {
msg: 'earlyExit',
retValue: (new ShellString('', state.error, state.errorCode)),
};
}
}
exports.error = error;
function ShellString(stdout, stderr, code) {
var that;
if (stdout instanceof Array) {
that = stdout;
that.stdout = stdout.join('\n');
if (stdout.length > 0) that.stdout += '\n';
} else {
that = new String(stdout);
that.stdout = stdout;
}
that.stderr = stderr;
that.code = code;
pipeMethods.forEach(function (cmd) {
that[cmd] = shellMethods[cmd].bind(that);
});
return that;
}
exports.ShellString = ShellString;
function parseOptions(opt, map, errorOptions) {
if (typeof opt !== 'string' && !isObject(opt)) {
throw new Error('options must be strings or key-value pairs');
} else if (!isObject(map)) {
throw new Error('parseOptions() internal error: map must be an object');
} else if (errorOptions && !isObject(errorOptions)) {
throw new Error('parseOptions() internal error: errorOptions must be object');
}
if (opt === '--') {
return {};
}
var options = {};
Object.keys(map).forEach(function (letter) {
var optName = map[letter];
if (optName[0] !== '!') {
options[optName] = false;
}
});
if (opt === '') return options;
if (typeof opt === 'string') {
if (opt[0] !== '-') {
throw new Error("Options string must start with a '-'");
}
var chars = opt.slice(1).split('');
chars.forEach(function (c) {
if (c in map) {
var optionName = map[c];
if (optionName[0] === '!') {
options[optionName.slice(1)] = false;
} else {
options[optionName] = true;
}
} else {
error('option not recognized: ' + c, errorOptions || {});
}
});
} else { Object.keys(opt).forEach(function (key) {
var c = key[1];
if (c in map) {
var optionName = map[c];
options[optionName] = opt[key]; } else {
error('option not recognized: ' + c, errorOptions || {});
}
});
}
return options;
}
exports.parseOptions = parseOptions;
function expand(list) {
if (!Array.isArray(list)) {
throw new TypeError('must be an array');
}
var expanded = [];
list.forEach(function (listEl) {
if (typeof listEl !== 'string') {
expanded.push(listEl);
} else {
var ret;
try {
ret = glob.sync(listEl, config.globOptions);
ret = ret.length > 0 ? ret : [listEl];
} catch (e) {
ret = [listEl];
}
expanded = expanded.concat(ret);
}
});
return expanded;
}
exports.expand = expand;
var buffer = typeof Buffer.alloc === 'function' ?
function (len) {
return Buffer.alloc(len || config.bufLength);
} :
function (len) {
return new Buffer(len || config.bufLength);
};
exports.buffer = buffer;
function unlinkSync(file) {
try {
fs.unlinkSync(file);
} catch (e) {
if (e.code === 'EPERM') {
fs.chmodSync(file, '0666');
fs.unlinkSync(file);
} else {
throw e;
}
}
}
exports.unlinkSync = unlinkSync;
function statFollowLinks() {
return fs.statSync.apply(fs, arguments);
}
exports.statFollowLinks = statFollowLinks;
function statNoFollowLinks() {
return fs.lstatSync.apply(fs, arguments);
}
exports.statNoFollowLinks = statNoFollowLinks;
function randomFileName() {
function randomHash(count) {
if (count === 1) {
return parseInt(16 * Math.random(), 10).toString(16);
}
var hash = '';
for (var i = 0; i < count; i++) {
hash += randomHash(1);
}
return hash;
}
return 'shelljs_' + randomHash(20);
}
exports.randomFileName = randomFileName;
function wrap(cmd, fn, options) {
options = options || {};
return function () {
var retValue = null;
state.currentCmd = cmd;
state.error = null;
state.errorCode = 0;
try {
var args = [].slice.call(arguments, 0);
if (config.verbose) {
console.error.apply(console, [cmd].concat(args));
}
state.pipedValue = (this && typeof this.stdout === 'string') ? this.stdout : '';
if (options.unix === false) { retValue = fn.apply(this, args);
} else { if (isObject(args[0]) && args[0].constructor.name === 'Object') {
} else if (args.length === 0 || typeof args[0] !== 'string' || args[0].length <= 1 || args[0][0] !== '-') {
args.unshift(''); }
args = args.reduce(function (accum, cur) {
if (Array.isArray(cur)) {
return accum.concat(cur);
}
accum.push(cur);
return accum;
}, []);
args = args.map(function (arg) {
if (isObject(arg) && arg.constructor.name === 'String') {
return arg.toString();
}
return arg;
});
var homeDir = os.homedir();
args = args.map(function (arg) {
if (typeof arg === 'string' && arg.slice(0, 2) === '~/' || arg === '~') {
return arg.replace(/^~/, homeDir);
}
return arg;
});
if (!config.noglob && options.allowGlobbing === true) {
args = args.slice(0, options.globStart).concat(expand(args.slice(options.globStart)));
}
try {
if (isObject(options.cmdOptions)) {
args[0] = parseOptions(args[0], options.cmdOptions);
}
retValue = fn.apply(this, args);
} catch (e) {
if (e.msg === 'earlyExit') {
retValue = e.retValue;
} else {
throw e; }
}
}
} catch (e) {
if (!state.error) {
e.name = 'ShellJSInternalError';
throw e;
}
if (config.fatal) throw e;
}
if (options.wrapOutput &&
(typeof retValue === 'string' || Array.isArray(retValue))) {
retValue = new ShellString(retValue, state.error, state.errorCode);
}
state.currentCmd = 'shell.js';
return retValue;
};
} exports.wrap = wrap;
function _readFromPipe() {
return state.pipedValue;
}
exports.readFromPipe = _readFromPipe;
var DEFAULT_WRAP_OPTIONS = {
allowGlobbing: true,
canReceivePipe: false,
cmdOptions: null,
globStart: 1,
pipeOnly: false,
wrapOutput: true,
unix: true,
};
var pipeMethods = [];
function _register(name, implementation, wrapOptions) {
wrapOptions = wrapOptions || {};
Object.keys(wrapOptions).forEach(function (option) {
if (!DEFAULT_WRAP_OPTIONS.hasOwnProperty(option)) {
throw new Error("Unknown option '" + option + "'");
}
if (typeof wrapOptions[option] !== typeof DEFAULT_WRAP_OPTIONS[option]) {
throw new TypeError("Unsupported type '" + typeof wrapOptions[option] +
"' for option '" + option + "'");
}
});
wrapOptions = Object.assign({}, DEFAULT_WRAP_OPTIONS, wrapOptions);
if (shell.hasOwnProperty(name)) {
throw new Error('Command `' + name + '` already exists');
}
if (wrapOptions.pipeOnly) {
wrapOptions.canReceivePipe = true;
shellMethods[name] = wrap(name, implementation, wrapOptions);
} else {
shell[name] = wrap(name, implementation, wrapOptions);
}
if (wrapOptions.canReceivePipe) {
pipeMethods.push(name);
}
}
exports.register = _register;