Skip to content

Commit e89eba1

Browse files
committed
Initial
1 parent 108dc60 commit e89eba1

File tree

3 files changed

+298
-1
lines changed

3 files changed

+298
-1
lines changed

README.md

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,38 @@
11
# nodeStringTable
2-
A fork of the console.table and cli_table NodeJS source code which returns strings, and works in web browsers.
2+
A fork of the [`console.table`](https://nodejs.org/api/console.html#console_console_table_tabulardata_properties) and `cli_table.js` NodeJS source code which returns Strings, and works in web browsers.
3+
4+
This module works almost exactly like the NodeJS [`console.table`](https://nodejs.org/api/console.html#console_console_table_tabulardata_properties), with the main difference being that it returns a string rather than logging it to the console.
5+
6+
## Usage
7+
8+
You can use the table function exactly like the NodeJS [`console.table`](https://nodejs.org/api/console.html#console_console_table_tabulardata_properties), but you must store the result as a string.
9+
10+
```JavaScript
11+
const tabularData = {
12+
firstName: "John",
13+
lastName: "Smith"
14+
}
15+
const table = nsTable(tabularData);
16+
/* Creates a String table like this:
17+
┌───────────┬─────────┐
18+
│ (index) │ Values │
19+
├───────────┼─────────┤
20+
│ firstName │ 'John' │
21+
│ lastName │ 'Smith' │
22+
└───────────┴─────────┘
23+
*/
24+
```
25+
26+
## Building for Browsers
27+
28+
Bundle `index.js` with Browserify in standalone mode, which should include a copy of the NodeJS `util` module to be used inside this module.
29+
30+
```bash
31+
browserify index.js -o nsTable.js -s nsTable
32+
```
33+
34+
You can just run the included NPM script which does the same:
35+
36+
```bash
37+
npm run browserify
38+
```

index.js

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
const isSet = obj => obj instanceof Set;
2+
const isMap = obj => obj instanceof Map;
3+
const mapIteratorProto = (new Map())[Symbol.iterator]().__proto__;
4+
const setIteratorProto = (new Set())[Symbol.iterator]().__proto__;
5+
const isSetIterator = obj => obj.__proto__ === setIteratorProto;
6+
const isMapIterator = obj => obj.__proto__ === mapIteratorProto;
7+
function previewEntries(iterator, isMap) {
8+
const result = [];
9+
if (isMap) {
10+
result[1] = true;
11+
result[0] = [];
12+
}
13+
var state = iterator.next();
14+
if (isMap) {
15+
while (!state.done) {
16+
result[0].push(state.value[0]);
17+
result[0].push(state.value[1]);
18+
state = iterator.next();
19+
}
20+
} else {
21+
while (!state.done) {
22+
result.push(state.value);
23+
state = iterator.next();
24+
}
25+
}
26+
return result;
27+
}
28+
// Copyright Node.js contributors. All rights reserved.
29+
//
30+
// Permission is hereby granted, free of charge, to any person obtaining a copy
31+
// of this software and associated documentation files (the "Software"), to
32+
// deal in the Software without restriction, including without limitation the
33+
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
34+
// sell copies of the Software, and to permit persons to whom the Software is
35+
// furnished to do so, subject to the following conditions:
36+
//
37+
// The above copyright notice and this permission notice shall be included in
38+
// all copies or substantial portions of the Software.
39+
//
40+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
42+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
43+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
44+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
45+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
46+
// IN THE SOFTWARE.
47+
48+
const hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty);
49+
// Original colorRegExp and removeColors taken from: https://github.com/nodejs/node/blob/e0893f03c3e41c32eba29390a866d722fbb1dd7f/lib/internal/util.js
50+
const colorRegExp = /\u001b\[\d\d?m/g; // eslint-disable-line no-control-regex
51+
function removeColors(str) {
52+
return str.replace(colorRegExp, '');
53+
}
54+
// Original cliTable taken from: https://github.com/nodejs/node/blob/4270c13426fb8d86f1068735006b95bb614e9f38/lib/internal/cli_table.js
55+
const tableChars = {
56+
/* eslint-disable node-core/non-ascii-character */
57+
middleMiddle: '─',
58+
rowMiddle: '┼',
59+
topRight: '┐',
60+
topLeft: '┌',
61+
leftMiddle: '├',
62+
topMiddle: '┬',
63+
bottomRight: '┘',
64+
bottomLeft: '└',
65+
bottomMiddle: '┴',
66+
rightMiddle: '┤',
67+
left: '│ ',
68+
right: ' │',
69+
middle: ' │ ',
70+
/* eslint-enable node-core/non-ascii-character */
71+
};
72+
const countSymbols = (string) => {
73+
const normalized = removeColors(string);
74+
return normalized.length;
75+
};
76+
const renderRow = (row, columnWidths) => {
77+
let out = tableChars.left;
78+
for (var i = 0; i < row.length; i++) {
79+
const cell = row[i];
80+
const len = countSymbols(cell);
81+
const needed = (columnWidths[i] - len) / 2;
82+
// round(needed) + ceil(needed) will always add up to the amount
83+
// of spaces we need while also left justifying the output.
84+
out += `${' '.repeat(needed)}${cell}${' '.repeat(Math.ceil(needed))}`;
85+
if (i !== row.length - 1)
86+
out += tableChars.middle;
87+
}
88+
out += tableChars.right;
89+
return out;
90+
};
91+
function cliTable(head, columns) {
92+
const rows = [];
93+
const columnWidths = head.map((h) => countSymbols(h));
94+
const longestColumn = columns.reduce((n, a) => Math.max(n, a.length), 0);
95+
96+
for (var i = 0; i < head.length; i++) {
97+
const column = columns[i];
98+
for (var j = 0; j < longestColumn; j++) {
99+
if (rows[j] === undefined)
100+
rows[j] = [];
101+
const value = rows[j][i] = hasOwnProperty(column, j) ? column[j] : '';
102+
const width = columnWidths[i] || 0;
103+
const counted = countSymbols(value);
104+
columnWidths[i] = Math.max(width, counted);
105+
}
106+
}
107+
const divider = columnWidths.map(len => Math.ceil(len)).map((len) => tableChars.middleMiddle.repeat(len + 2));
108+
let result = `${tableChars.topLeft}${divider.join(tableChars.topMiddle)}` +
109+
`${tableChars.topRight}\n${renderRow(head, columnWidths)}\n` +
110+
`${tableChars.leftMiddle}${divider.join(tableChars.rowMiddle)}` +
111+
`${tableChars.rightMiddle}\n`;
112+
for (const row of rows) result += `${renderRow(row, columnWidths)}\n`;
113+
114+
result += `${tableChars.bottomLeft}${divider.join(tableChars.bottomMiddle)}` + tableChars.bottomRight;
115+
return result;
116+
}
117+
// Copyright Joyent, Inc. and other Node contributors.
118+
//
119+
// Permission is hereby granted, free of charge, to any person obtaining a
120+
// copy of this software and associated documentation files (the
121+
// "Software"), to deal in the Software without restriction, including
122+
// without limitation the rights to use, copy, modify, merge, publish,
123+
// distribute, sublicense, and/or sell copies of the Software, and to permit
124+
// persons to whom the Software is furnished to do so, subject to the
125+
// following conditions:
126+
//
127+
// The above copyright notice and this permission notice shall be included
128+
// in all copies or substantial portions of the Software.
129+
//
130+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
131+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
132+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
133+
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
134+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
135+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
136+
// USE OR OTHER DEALINGS IN THE SOFTWARE.
137+
138+
// Original table taken from: https://github.com/nodejs/node/blob/4270c13426fb8d86f1068735006b95bb614e9f38/lib/console.js
139+
const keyKey = 'Key';
140+
const valuesKey = 'Values';
141+
const indexKey = '(index)';
142+
const iterKey = '(iteration index)';
143+
const util = require('util');
144+
const isArray = Array.isArray;
145+
const ArrayIsArray = Array.isArray;
146+
const ArrayFrom = Array.from;
147+
const ObjectKeys = Object.keys;
148+
const ObjectValues = Object.values;
149+
module.exports = function table(tabularData, properties) {
150+
if (properties !== undefined && !ArrayIsArray(properties)) throw new TypeError("\"properties\" must be an Array");
151+
const final = (k, v) => cliTable(k, v);
152+
if (tabularData === null || typeof tabularData !== 'object') return final(tabularData);
153+
const inspect = (v) => {
154+
const opt = { depth: 0, maxArrayLength: 3 };
155+
if (v !== null && typeof v === 'object' && !isArray(v) && ObjectKeys(v).length > 2) opt.depth = -1;
156+
return util.inspect(v, opt);
157+
};
158+
const getIndexArray = (length) => ArrayFrom({ length }, (_, i) => inspect(i));
159+
const mapIter = isMapIterator(tabularData);
160+
let isKeyValue = false;
161+
let i = 0;
162+
if (mapIter) {
163+
const res = previewEntries(tabularData, true);
164+
tabularData = res[0];
165+
isKeyValue = res[1];
166+
}
167+
if (isKeyValue || isMap(tabularData)) {
168+
const keys = [];
169+
const values = [];
170+
let length = 0;
171+
if (mapIter) {
172+
for (; i < tabularData.length / 2; ++i) {
173+
keys.push(inspect(tabularData[i * 2]));
174+
values.push(inspect(tabularData[i * 2 + 1]));
175+
length++;
176+
}
177+
} else {
178+
for (const [k, v] of tabularData) {
179+
keys.push(inspect(k));
180+
values.push(inspect(v));
181+
length++;
182+
}
183+
}
184+
return final([
185+
iterKey, keyKey, valuesKey
186+
], [
187+
getIndexArray(length),
188+
keys,
189+
values,
190+
]);
191+
}
192+
const setIter = isSetIterator(tabularData);
193+
if (setIter) tabularData = previewEntries(tabularData);
194+
const setlike = setIter || (mapIter && !isKeyValue) || isSet(tabularData);
195+
if (setlike) {
196+
const values = [];
197+
let length = 0;
198+
for (const v of tabularData) {
199+
values.push(inspect(v));
200+
length++;
201+
}
202+
return final([setlike ? iterKey : indexKey, valuesKey], [
203+
getIndexArray(length),
204+
values,
205+
]);
206+
}
207+
const map = {};
208+
let hasPrimitives = false;
209+
const valuesKeyArray = [];
210+
const indexKeyArray = ObjectKeys(tabularData);
211+
for (; i < indexKeyArray.length; i++) {
212+
const item = tabularData[indexKeyArray[i]];
213+
const primitive = item === null || (typeof item !== 'function' && typeof item !== 'object');
214+
if (properties === undefined && primitive) {
215+
hasPrimitives = true;
216+
valuesKeyArray[i] = inspect(item);
217+
} else {
218+
const keys = properties || ObjectKeys(item);
219+
for (const key of keys) {
220+
if (map[key] === undefined) map[key] = [];
221+
if ((primitive && properties) || !hasOwnProperty(item, key)) map[key][i] = '';
222+
else map[key][i] = item == null ? item : inspect(item[key]);
223+
}
224+
}
225+
}
226+
const keys = ObjectKeys(map);
227+
const values = ObjectValues(map);
228+
if (hasPrimitives) {
229+
keys.push(valuesKey);
230+
values.push(valuesKeyArray);
231+
}
232+
keys.unshift(indexKey);
233+
values.unshift(indexKeyArray);
234+
return final(keys, values);
235+
};

package.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "nodestringtable",
3+
"description": "A fork of the console.table and cli_table NodeJS source code which returns strings, and works in web browsers.",
4+
"version": "1.0.0",
5+
"author": "Dani Glore (https://github.com/Floofies/)",
6+
"contributors": [
7+
"Ruben Bridgewater (https://github.com/BridgeAR)",
8+
"Gus Caplan (https://github.com/devsnek)",
9+
"Rich Trott (https://github.com/Trott)"
10+
],
11+
"repository": {
12+
"type": "git",
13+
"url": "git://github.com/Floofies/console-buffer.git"
14+
},
15+
"devDependencies": {
16+
"browserify": ""
17+
},
18+
"scripts": {
19+
"browserify": "browserify index.js -o nsTable.js -s nsTable"
20+
},
21+
"keywords": [
22+
"console.table",
23+
"table",
24+
"tabular"
25+
]
26+
}

0 commit comments

Comments
 (0)