286 lines
6.4 KiB
JavaScript
286 lines
6.4 KiB
JavaScript
/**
|
|
* Record
|
|
* Record your terminal and create a recording file
|
|
*
|
|
* @author Mohammad Fares <faressoft.com@gmail.com>
|
|
*/
|
|
|
|
/**
|
|
* The path of the recording file
|
|
* @type {String}
|
|
*/
|
|
var recordingFile = null;
|
|
|
|
/**
|
|
* The normalized configurations
|
|
* @type {Object} {json, raw}
|
|
*/
|
|
var config = {};
|
|
|
|
/**
|
|
* To keep tracking of the timestamp
|
|
* of the last inserted record
|
|
* @type {Number}
|
|
*/
|
|
var lastRecordTimestamp = null;
|
|
|
|
/**
|
|
* To store the records
|
|
* @type {Array}
|
|
*/
|
|
var records = [];
|
|
|
|
/**
|
|
* Normalize the config file
|
|
*
|
|
* - Set default values in the json and raw
|
|
* - Change the formatting of the values in the json and raw
|
|
*
|
|
* @param {Object} config {json, raw}
|
|
* @return {Object} {json, raw}
|
|
*/
|
|
function normalizeConfig(config) {
|
|
|
|
// Default value for command
|
|
if (!config.json.command) {
|
|
|
|
// Windows OS
|
|
if (di.os.platform() === 'win32') {
|
|
di.utility.changeYAMLValue(config, 'command', 'powershell.exe');
|
|
} else {
|
|
di.utility.changeYAMLValue(config, 'command', 'bash -l');
|
|
}
|
|
|
|
}
|
|
|
|
// Default value for cwd
|
|
if (!config.json.cwd) {
|
|
di.utility.changeYAMLValue(config, 'cwd', process.cwd());
|
|
} else {
|
|
di.utility.changeYAMLValue(config, 'cwd', di.path.resolve(config.json.cwd));
|
|
}
|
|
|
|
// Default value for cols
|
|
if (di.is.not.number(config.json.cols)) {
|
|
di.utility.changeYAMLValue(config, 'cols', process.stdout.columns);
|
|
}
|
|
|
|
// Default value for rows
|
|
if (di.is.not.number(config.json.rows)) {
|
|
di.utility.changeYAMLValue(config, 'rows', process.stdout.rows);
|
|
}
|
|
|
|
return config;
|
|
|
|
}
|
|
|
|
/**
|
|
* Calculate the duration from the last inserted record in ms,
|
|
* and update lastRecordTimestamp
|
|
*
|
|
* @return {Number}
|
|
*/
|
|
function getDuration() {
|
|
|
|
// Calculate the duration from the last inserted record
|
|
var duration = di.now().toFixed() - lastRecordTimestamp;
|
|
|
|
// Update the lastRecordTimestamp
|
|
lastRecordTimestamp = di.now().toFixed();
|
|
|
|
return duration;
|
|
|
|
}
|
|
|
|
/**
|
|
* When an input or output is received from the PTY instance
|
|
*
|
|
* @param {Buffer} content
|
|
*/
|
|
function onData(content) {
|
|
|
|
process.stdout.write(content);
|
|
|
|
var duration = getDuration();
|
|
|
|
if (duration < 5) {
|
|
var lastRecord = records[records.length - 1];
|
|
lastRecord.content += content;
|
|
return;
|
|
}
|
|
|
|
records.push({
|
|
delay: duration,
|
|
content: content
|
|
});
|
|
|
|
}
|
|
|
|
/**
|
|
* Executed after the command completes its task
|
|
* Store the output file with reserving the comments
|
|
*/
|
|
function done() {
|
|
|
|
var outputYAML = '';
|
|
|
|
// Add config parent element
|
|
outputYAML += '# The configurations that used for the recording, feel free to edit them\n';
|
|
outputYAML += 'config:\n\n';
|
|
|
|
// Add the configurations with indentation
|
|
outputYAML += config.raw.replace(/^/gm, ' ');
|
|
|
|
// Add the records
|
|
outputYAML += '\n# Records, feel free to edit them\n';
|
|
outputYAML += di.yaml.dump({records: records});
|
|
|
|
// Store the data into the recording file
|
|
try {
|
|
|
|
di.fs.writeFileSync(recordingFile, outputYAML, 'utf8');
|
|
|
|
} catch (error) {
|
|
|
|
return di.errorHandler(error);
|
|
|
|
}
|
|
|
|
console.log(di.chalk.green('Successfully Recorded'));
|
|
console.log('The recording data is saved into the file:');
|
|
console.log(di.chalk.magenta(recordingFile));
|
|
console.log('You can edit the file and even change the configurations.\n');
|
|
|
|
console.log(di.chalk.green('Let\'s now share your recording on https://terminalizer.com'));
|
|
console.log('The command ' + di.chalk.magenta('`terminalizer share`') + 'can be used anytime to share recordings!\n');
|
|
|
|
// Reset STDIN
|
|
process.stdin.removeAllListeners();
|
|
process.stdin.setRawMode(false);
|
|
process.stdin.pause();
|
|
|
|
// Invoke the share command
|
|
di.commands.share.handler({
|
|
recordingFile: recordingFile
|
|
});
|
|
|
|
}
|
|
|
|
/**
|
|
* The command's main function
|
|
*
|
|
* @param {Object} argv
|
|
*/
|
|
function command(argv) {
|
|
|
|
// Normalize the configurations
|
|
config = normalizeConfig(argv.config);
|
|
|
|
// Store the path of the recordingFile
|
|
recordingFile = argv.recordingFile;
|
|
|
|
// Overwrite the command to be executed
|
|
if (argv.command) {
|
|
di.utility.changeYAMLValue(config, 'command', argv.command);
|
|
}
|
|
|
|
// Split the command and its arguments
|
|
var args = di.stringArgv(config.json.command);
|
|
var command = args[0];
|
|
var commandArguments = args.slice(1);
|
|
|
|
// PTY instance
|
|
var ptyProcess = di.pty.spawn(command, commandArguments, {
|
|
cols: config.json.cols,
|
|
rows: config.json.rows,
|
|
cwd: config.json.cwd,
|
|
env: di.deepmerge(process.env, config.json.env)
|
|
});
|
|
|
|
console.log('The recording session is started');
|
|
console.log('Press', di.chalk.green('CTRL+D'), 'to exit and save the recording');
|
|
|
|
// Input and output capturing and redirection
|
|
ptyProcess.on('data', onData);
|
|
ptyProcess.on('exit', done);
|
|
process.stdin.on('data', ptyProcess.write.bind(ptyProcess));
|
|
|
|
// Input and output normalization
|
|
process.stdout.setDefaultEncoding('utf8');
|
|
process.stdin.setEncoding('utf8');
|
|
process.stdin.setRawMode(true);
|
|
process.stdin.resume();
|
|
|
|
}
|
|
|
|
////////////////////////////////////////////////////
|
|
// Command Definition //////////////////////////////
|
|
////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Command's usage
|
|
* @type {String}
|
|
*/
|
|
module.exports.command = 'record <recordingFile>';
|
|
|
|
/**
|
|
* Command's description
|
|
* @type {String}
|
|
*/
|
|
module.exports.describe = 'Record your terminal and create a recording file';
|
|
|
|
/**
|
|
* Handler
|
|
*
|
|
* @param {Object} argv
|
|
*/
|
|
module.exports.handler = function(argv) {
|
|
|
|
// Default value for the config option
|
|
if (typeof argv.config == 'undefined') {
|
|
argv.config = di.utility.getDefaultConfig();
|
|
}
|
|
|
|
// Execute the command
|
|
command(argv);
|
|
|
|
};
|
|
|
|
/**
|
|
* Builder
|
|
*
|
|
* @param {Object} yargs
|
|
*/
|
|
module.exports.builder = function(yargs) {
|
|
|
|
// Define the recordingFile argument
|
|
yargs.positional('recordingFile', {
|
|
describe: 'A name for the recording file',
|
|
type: 'string',
|
|
coerce: di._.partial(di.utility.resolveFilePath, di._, 'yml')
|
|
});
|
|
|
|
// Define the config option
|
|
yargs.option('c', {
|
|
alias: 'config',
|
|
type: 'string',
|
|
describe: 'Overwrite the default configurations',
|
|
requiresArg: true,
|
|
coerce: di.utility.loadYAML
|
|
});
|
|
|
|
// Define the config option
|
|
yargs.option('d', {
|
|
alias: 'command',
|
|
type: 'string',
|
|
describe: 'The command to be executed',
|
|
requiresArg: true,
|
|
default: null
|
|
});
|
|
|
|
// Add examples
|
|
yargs.example('$0 record foo', 'Start recording and create a recording file called foo.yml');
|
|
yargs.example('$0 record foo --config config.yml', 'Start recording with your own configurations');
|
|
|
|
};
|