-
Notifications
You must be signed in to change notification settings - Fork 0
/
util.js
185 lines (157 loc) · 4.08 KB
/
util.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
const assert = require("assert");
const blessed = require("blessed");
// Levels used by columns generated by components.js
const levels = {
DATABASE: 0,
COLLECTION: 1,
DOCUMENT_BASE: 2
};
/**
* Used for handling errors in async functions called by event handlers.
* Terminates the program if any error occurs.
*
* @param {Screen} screen blessed screen to terminate on error
* @param {function} fn async function to run
*/
function crashOnError(screen, fn) {
return (...args) => {
return fn(...args).catch(e => {
if (screen) screen.destroy();
console.error(e);
return process.exit(1);
});
};
}
const colCache = {};
/**
* Save a column's data, so that it can be loaded at a later time.
*
* @param {Column} col
*/
function saveColumn(col) {
colCache[col.level] = {
items: col.getItems(),
selected: col.selected,
keys: col.keys
};
}
/**
* Load a previously stored column's data into the specified target.
*
* @param {Column} col targets column to load data into
* @param {Number} level level of the column to load
*/
function loadColumn(col, level) {
col.setItems(colCache[level].items);
col.select(colCache[level].selected);
col.setKeys(colCache[level].keys);
col.setLevel(level);
}
/**
* Returns the column stored at this level.
* However, since columns stored in colCache are not currently visible
* on the screen, this is a "virtual" column rather than a true blessed.js column
*/
function getVirtualColumn(level) {
assert(level in colCache);
const virtualCol = colCache[level];
virtualCol.setItems = items => (virtualCol.items = items);
virtualCol.setKeys = keys => (virtualCol.keys = keys);
return virtualCol;
}
function isObject(obj) {
return !!obj && typeof obj === "object";
}
function stringifyWrapper(obj) {
return stringifyWithLimit(obj, (process.stdout.columns || 128) / 2);
}
function stringifyWithLimit(obj, maxLength) {
const str = stringify(obj);
if (!str || str.length < maxLength) return str;
// handle large string that may need to be trimmed
const parts = str.split("{/}");
let result = "";
for (const part of parts) {
result += part + "{/}";
if (
result.length >= maxLength && // short circuit to avoid many strip tags
blessed.stripTags(result).length >= maxLength
) {
result += "...";
break;
}
}
return result;
}
// like JSON.stringify but a bit prettier
function stringify(obj) {
if (obj === undefined || obj === null) {
return colorize("null");
}
if (obj.toJSON) {
// e.g. dates which implement a toJSON
const json = obj.toJSON();
if (json.length) return colorize(json);
return colorize("{Object}"); // some weird object
}
if (Array.isArray(obj)) {
if (obj.length > 8) {
// too big to display
return colorize(`[ Array of length ${obj.length} ]`);
}
return "[ " + obj.map(val => stringify(val)).join(", ") + " ]";
}
if (isObject(obj)) {
return (
"{ " +
Object.keys(obj)
.map(k => `{bold}${k}{/}: ${stringify(obj[k])}`)
.join(", ") +
" }"
);
}
return colorize(JSON.stringify(obj));
}
// syntax highlighting
function colorize(str) {
if (!str.length) return "";
if (str[0] === '"') {
return `{cyan-fg}${str}{/}`;
}
if (!isNaN(str)) {
return `{green-fg}${str}{/}`;
}
// some other type like ObjectID or date
return `{red-fg}${str}{/}`;
}
/**
* Convert string to a JS object, to be fed to the mongo driver.
* Will throw an error if the string is malformed.
* @param {String} string to convert
*/
function stringToObject(string) {
// todo: should avoid using eval, vars like colCache are exposed
return eval(`(${string})`);
}
// Things that aren't objects or arrays are simply considered empty
function isEmpty(item) {
if (Array.isArray(item)) {
return item.length === 0;
}
if (isObject(item)) {
return Object.keys(item).length === 0;
}
return true;
}
module.exports = {
crashOnError,
saveColumn,
loadColumn,
getVirtualColumn,
levels,
isObject,
stringify: stringifyWrapper,
colorize,
stringToObject,
isEmpty
};