UNPKG

4.78 kBJavaScriptView Raw
1const repl = require('repl');
2const SpinnerView = require('@src/view/spinner-view');
3const Messenger = require('@src/view/messenger');
4const stringUtils = require('@src/utils/string-utils');
5
6module.exports = class CliReplView {
7 /**
8 * Constructor for CLI REPL View
9 * @param {String} prompt What string to display as the prompt for the REPL.
10 * @throws {Error} throws error if an undefined configuration is passed.
11 * @throws {Error} throws error if prompt is not a non-empty string.
12 */
13 constructor(configuration) {
14 if (!configuration) {
15 throw 'Cannot have an undefined configuration.';
16 }
17 const { prompt, evalFunc, header, footer, prettifyHeaderFooter, inputStream } = configuration;
18 this.prompt = prompt || '> ';
19 this.eval = evalFunc;
20 this.header = header;
21 this.footer = footer;
22 this.prettifyHeaderFooter = prettifyHeaderFooter || (arg => arg);
23 this.inputStream = inputStream || process.stdin;
24 this.progressSpinner = new SpinnerView();
25 if (!stringUtils.isNonEmptyString(this.prompt)) {
26 throw 'Prompt must be a non-empty string.';
27 }
28 this.printHeaderFooter(this.header);
29 // Initialize custom REPL server.
30 const replConfig = {
31 prompt: this.prompt,
32 eval: (cmd, context, filename, callback) => {
33 this.eval(cmd, callback);
34 },
35 ignoreUndefined: true,
36 input: this.inputStream,
37 output: process.stdout
38 };
39 this.replServer = repl.start(replConfig);
40 this.replServer.removeAllListeners('SIGINT');
41 this.clearSpecialCommands();
42 this.registerQuitCommand(() => {});
43 }
44
45 /**
46 * Function to print a header or footer line.
47 * @param {String} data the string that contains the information the caller whats to embed into header/footer.
48 */
49 printHeaderFooter(data) {
50 if (stringUtils.isNonEmptyString(data)) {
51 Messenger.getInstance().info(this.prettifyHeaderFooter(data));
52 }
53 }
54
55 /**
56 * Register a special command to the repl
57 * @param {String} name name of new special command.
58 * @param {String} name name of new special command.
59 * @param {String} helpMessage description of special command
60 * @param {Function} func function to execute when special command received
61 * @param {Boolean} displayPrompt specify whether or not to display the prompt after the special command executes. Default: true
62 */
63 registerSpecialCommand(name, helpMessage, func, displayPrompt) {
64 const shouldDisplayPrompt = displayPrompt === undefined ? true : displayPrompt;
65 this.replServer.defineCommand(name, {
66 help: helpMessage || this.replServer.commands[name].help,
67 action: (args) => {
68 func(args);
69 if (shouldDisplayPrompt) this.replServer.displayPrompt();
70 }
71 });
72 }
73
74 /**
75 * Register a special exit command to the repl
76 * @param {Function} func function to execute when special command received
77 */
78 registerQuitCommand(func) {
79 this.replServer.removeAllListeners('close');
80 this.registerSpecialCommand(
81 'quit',
82 'Quit repl session.',
83 () => {
84 this.close();
85 },
86 false
87 );
88 this.replServer.on('close', () => {
89 func();
90 this.progressSpinner.terminate();
91 this.printHeaderFooter(this.footer);
92 });
93 }
94
95 /**
96 * Remove all special commands from REPL server
97 * Remove close event listeners to remove all quit handlers.
98 */
99 clearSpecialCommands() {
100 this.replServer.commands = { help: this.replServer.commands.help };
101 this.replServer.removeAllListeners('close');
102 }
103
104 /**
105 * Wrapper to close instance. This involves terminating the SpinnerView, disposing the Messenger View, and closing the REPL Server.
106 */
107 close() {
108 this.replServer.close();
109 }
110
111 // SpinnerView wrapper functions
112
113 /**
114 * Wrapper for starting the SpinnerView with a specified message.
115 * @param {String} text text to display when the spinner starts.
116 */
117 startProgressSpinner(text) {
118 this.progressSpinner.start(text);
119 }
120
121 /**
122 * Wrapper for updating the text of the SpinnerView
123 * @param {String} text text to replace current message of spinner.
124 */
125 updateProgressSpinner(text) {
126 this.progressSpinner.update(text);
127 }
128
129 /**
130 * Wrapper for terminating the SpinnerView, clearing it from the console
131 */
132 terminateProgressSpinner() {
133 this.progressSpinner.terminate();
134 }
135};