@osmura/treeify 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.travis.yml +8 -0
- package/LICENSE +21 -0
- package/README.md +77 -0
- package/examples/eukaryotes.html +31 -0
- package/examples/eukaryotes.js +51 -0
- package/examples/fs_tree.js +45 -0
- package/package.json +34 -0
- package/test/tree-test.js +347 -0
- package/treeify.js +116 -0
package/.travis.yml
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2012-2018 Luke Plaster <notatestuser@gmail.com>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
treeify  
|
|
2
|
+
=======
|
|
3
|
+
|
|
4
|
+
[](https://travis-ci.org/notatestuser/treeify)
|
|
5
|
+
|
|
6
|
+
_treeify_ converts a JS object into a nice, visible depth-indented tree for console printing. The structure
|
|
7
|
+
generated is similar to what you get by running the ```tree``` command on Unixy platforms.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
{
|
|
11
|
+
oranges: {
|
|
12
|
+
'mandarin': { ├─ oranges
|
|
13
|
+
clementine: null, │ └─ mandarin
|
|
14
|
+
tangerine: 'so cheap and juicy!' -=> │ ├─ clementine
|
|
15
|
+
} │ └─ tangerine: so cheap and juicy!
|
|
16
|
+
}, └─ apples
|
|
17
|
+
apples: { ├─ gala
|
|
18
|
+
'gala': null, └─ pink lady
|
|
19
|
+
'pink lady': null
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
It also works well with larger nested hierarchies such as file system directory trees.
|
|
25
|
+
In fact, the ```fs_tree``` example does a pretty good job of imitating ```tree```. Try it out!
|
|
26
|
+
|
|
27
|
+
See the other included examples or the test suite for usage scenarios.
|
|
28
|
+
|
|
29
|
+
Getting it
|
|
30
|
+
----------
|
|
31
|
+
|
|
32
|
+
### For use with node.js
|
|
33
|
+
|
|
34
|
+
First you'll want to run this command in your project's root folder:
|
|
35
|
+
```
|
|
36
|
+
$ npm install treeify
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Then proceed to use it in your project:
|
|
40
|
+
```js
|
|
41
|
+
var treeify = require('treeify');
|
|
42
|
+
console.log(
|
|
43
|
+
treeify.asTree({
|
|
44
|
+
apples: 'gala', // ├─ apples: gala
|
|
45
|
+
oranges: 'mandarin' // └─ oranges: mandarin
|
|
46
|
+
}, true)
|
|
47
|
+
);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### For use in a browser
|
|
51
|
+
|
|
52
|
+
Treeify cooperates with Node, AMD or browser globals to create a module. This means it'll work
|
|
53
|
+
in a browser regardless of whether you have an AMD-compliant module loader or not. If such
|
|
54
|
+
a loader isn't found when the script is executed, you may access Treeify at ```window.treeify```.
|
|
55
|
+
|
|
56
|
+
Usage
|
|
57
|
+
-----
|
|
58
|
+
|
|
59
|
+
The methods exposed to you are as follows, in a strange kind of signature notation:
|
|
60
|
+
|
|
61
|
+
### asLines()
|
|
62
|
+
```js
|
|
63
|
+
treeify.asLines(obj, showValues (boolean), [hideFunctions (boolean),] lineCallback (function))
|
|
64
|
+
// NOTE: hideFunctions is optional and may be safely omitted - this was done to ensure we don't break uses of the previous form
|
|
65
|
+
```
|
|
66
|
+
### asTree()
|
|
67
|
+
```js
|
|
68
|
+
treeify.asTree(obj, showValues (boolean), hideFunctions (boolean)): String
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Running the tests
|
|
72
|
+
-----------------
|
|
73
|
+
|
|
74
|
+
There's a pretty extensive suite of Vows tests included.
|
|
75
|
+
```
|
|
76
|
+
$ npm test
|
|
77
|
+
```
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
5
|
+
|
|
6
|
+
<script src="../treeify.js"></script>
|
|
7
|
+
<script>
|
|
8
|
+
// when a loader isn't present the UMD falls back to using a window 'global'
|
|
9
|
+
window.require = function() {
|
|
10
|
+
return window.treeify;
|
|
11
|
+
};
|
|
12
|
+
</script>
|
|
13
|
+
<script src="./eukaryotes.js"></script>
|
|
14
|
+
|
|
15
|
+
<style>
|
|
16
|
+
body {
|
|
17
|
+
display: block;
|
|
18
|
+
width: 630px;
|
|
19
|
+
margin: auto;
|
|
20
|
+
font-family: sans-serif;
|
|
21
|
+
}
|
|
22
|
+
</style>
|
|
23
|
+
</head>
|
|
24
|
+
<body>
|
|
25
|
+
<h1>Eukaryotes</h1>
|
|
26
|
+
<pre id="pre"></pre>
|
|
27
|
+
<script>
|
|
28
|
+
document.getElementById('pre').innerText = treeify.asTree(Eukaryotes, true);
|
|
29
|
+
</script>
|
|
30
|
+
</body>
|
|
31
|
+
</html>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
var treeify = require('../treeify');
|
|
2
|
+
|
|
3
|
+
// Based on information taken from the Tree of Life web project
|
|
4
|
+
// http://tolweb.org/Eukaryotes/3
|
|
5
|
+
var Eukaryotes = {
|
|
6
|
+
'Archaeplastida (Plantae)': {
|
|
7
|
+
'Green plants': 'green algae & land plants',
|
|
8
|
+
'Rhodophyta': 'red algae',
|
|
9
|
+
'Glaucophytes': 'microalgae'
|
|
10
|
+
},
|
|
11
|
+
'Unikonts': {
|
|
12
|
+
'Opisthokonts': {
|
|
13
|
+
'Animals': null,
|
|
14
|
+
'Choanoflagellates': null,
|
|
15
|
+
'Filasterea': null,
|
|
16
|
+
'Ichthyosporea': null,
|
|
17
|
+
'Fungi': 'mushrooms, sac fungi, yeast, molds, etc',
|
|
18
|
+
'Nucleariidae': 'filose amoebae'
|
|
19
|
+
},
|
|
20
|
+
'Amoebozoa': 'amoebae, slime molds, and parasitic protists',
|
|
21
|
+
},
|
|
22
|
+
'Chromalveolates': {
|
|
23
|
+
'': {
|
|
24
|
+
'Rhizaria': {
|
|
25
|
+
'Cercozoa': 'amoeboflagellates',
|
|
26
|
+
'Foraminifera': 'complex cells with reticulopodia',
|
|
27
|
+
'Radiolaria': null
|
|
28
|
+
},
|
|
29
|
+
'Alveolates': 'dinoflagellates, ciliates and apicomplexan parasites',
|
|
30
|
+
'Stramenopiles': 'e.g. water molds, diatoms, brown algae'
|
|
31
|
+
},
|
|
32
|
+
'Hacrobia': 'Haptophyta, Cryptomonads, etc.'
|
|
33
|
+
},
|
|
34
|
+
'Excavates': {
|
|
35
|
+
'Malawimonads': null,
|
|
36
|
+
'Discicristates': {
|
|
37
|
+
'Euglenozoa': 'euglenids, diplonemids and kinetoplastids',
|
|
38
|
+
'Heterolobosea': 'amoeboflagellates with discoidal mitchondrial cristae',
|
|
39
|
+
'Jakobida': 'free-living, heterotrophic flagellates'
|
|
40
|
+
},
|
|
41
|
+
'Parabasalids': 'trichomonads and hypermastigotes',
|
|
42
|
+
'Fornicata': 'diplomonads and retortamonads',
|
|
43
|
+
'Preaxostyla': 'oxymonads and Trimastix'
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
console.log('Eukaryotes');
|
|
48
|
+
|
|
49
|
+
treeify.asLines(Eukaryotes, true, function(line) {
|
|
50
|
+
console.log(line);
|
|
51
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
(function(){
|
|
4
|
+
|
|
5
|
+
try {
|
|
6
|
+
var dive = require('dive');
|
|
7
|
+
} catch (ex) {
|
|
8
|
+
console.error('this example requires "dive", please run "npm install dive"');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
var treeify = require('../treeify'),
|
|
13
|
+
path = require('path'),
|
|
14
|
+
fs = require('fs'),
|
|
15
|
+
rootDir = process.argv.length < 3 ? '.' : process.argv[2],
|
|
16
|
+
tree = {};
|
|
17
|
+
|
|
18
|
+
if ( ! fs.existsSync(rootDir)) {
|
|
19
|
+
console.error('path "' + rootDir + '" does not exist - unable to proceed!');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.log(rootDir !== '.' ? path.relative(process.cwd(), rootDir) : '.');
|
|
24
|
+
|
|
25
|
+
function scanComplete() {
|
|
26
|
+
process.stdout.write('\r \r');
|
|
27
|
+
console.log(treeify.asTree(tree, true));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
dive(rootDir, { all: true, directories: true }, function(err, thisPath) {
|
|
31
|
+
var relativePath = path.relative(rootDir, thisPath),
|
|
32
|
+
node = tree;
|
|
33
|
+
|
|
34
|
+
if (relativePath.indexOf('..') !== 0) {
|
|
35
|
+
relativePath.split(path.sep).forEach(function(part) {
|
|
36
|
+
typeof node[part] !== 'object' && (node[part] = {});
|
|
37
|
+
node = node[part];
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
}, scanComplete);
|
|
42
|
+
|
|
43
|
+
process.stdout.write('wait... ');
|
|
44
|
+
|
|
45
|
+
})();
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@osmura/treeify",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"author": "Luke Plaster <notatestuser@gmail.com>",
|
|
5
|
+
"description": "converts a JS object into a nice and readable tree structure for the console",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "./node_modules/vows/bin/vows --spec"
|
|
9
|
+
},
|
|
10
|
+
"main": "./treeify",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/notatestuser/treeify.git"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"object",
|
|
17
|
+
"tree",
|
|
18
|
+
"print",
|
|
19
|
+
"console",
|
|
20
|
+
"pretty"
|
|
21
|
+
],
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"vows": "git://github.com/Filirom1/vows.git#expect"
|
|
24
|
+
},
|
|
25
|
+
"licenses": [
|
|
26
|
+
{
|
|
27
|
+
"type": "MIT",
|
|
28
|
+
"url": "http://lp.mit-license.org/"
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=0.6"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
var treeify = require('../treeify'),
|
|
2
|
+
vows = require('vows'),
|
|
3
|
+
assert = require('assert'),
|
|
4
|
+
events = require('events');
|
|
5
|
+
|
|
6
|
+
// - helper functions -----------------
|
|
7
|
+
|
|
8
|
+
function treeifyByLineGuts(args) {
|
|
9
|
+
var emitter = new events.EventEmitter(),
|
|
10
|
+
lineNum = 0;
|
|
11
|
+
args.push(function(line) {
|
|
12
|
+
emitter.emit('success', line);
|
|
13
|
+
emitter.emit('line ' + (++lineNum), line);
|
|
14
|
+
});
|
|
15
|
+
treeify.asLines.apply(this, args);
|
|
16
|
+
return emitter;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function treeifyByLine(obj) {
|
|
20
|
+
return function(showValues) {
|
|
21
|
+
var arguments = [ obj, showValues ];
|
|
22
|
+
return treeifyByLineGuts(arguments);
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function treeifyByLineWithHideFunctionsArgument(obj) {
|
|
27
|
+
return function(showValues, hideFunctions) {
|
|
28
|
+
var arguments = [ obj, showValues, hideFunctions ];
|
|
29
|
+
return treeifyByLineGuts(arguments);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function treeifyEntirely(obj) {
|
|
34
|
+
return function(showValues, hideFunctions) {
|
|
35
|
+
return treeify.asTree(obj, showValues, hideFunctions);
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function withValuesShown(showValues) {
|
|
40
|
+
return function(func){ return func(showValues, false) };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function withValuesShownFunctionsHidden() {
|
|
44
|
+
return function(func){ return func(true, true) };
|
|
45
|
+
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function is(content, arrayIndex) {
|
|
49
|
+
return function(lines) {
|
|
50
|
+
var toCheck = lines;
|
|
51
|
+
if (arrayIndex !== undefined) {
|
|
52
|
+
toCheck = lines[arrayIndex];
|
|
53
|
+
}
|
|
54
|
+
assert.strictEqual(toCheck, content, 'should be "' + content + '" but was "' + toCheck + '"');
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function checkLines(/* ... */) {
|
|
59
|
+
var ret = {}, entry;
|
|
60
|
+
for (var line = 1; line <= arguments.length; line++) {
|
|
61
|
+
if ( ! arguments[line - 1])
|
|
62
|
+
continue;
|
|
63
|
+
entry = {};
|
|
64
|
+
entry['branches correctly on line '+line] = is(arguments[line - 1]);
|
|
65
|
+
ret['line '+line] = entry;
|
|
66
|
+
}
|
|
67
|
+
return ret;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// - the beautiful test suite ---------
|
|
71
|
+
|
|
72
|
+
vows.describe('tree-test').addBatch({
|
|
73
|
+
|
|
74
|
+
'A tree created from an empty object': {
|
|
75
|
+
topic: {},
|
|
76
|
+
|
|
77
|
+
'when returned as a whole tree': {
|
|
78
|
+
topic: treeifyEntirely,
|
|
79
|
+
|
|
80
|
+
'with values hidden': {
|
|
81
|
+
topic: withValuesShown(false),
|
|
82
|
+
'is an empty string': is('')
|
|
83
|
+
},
|
|
84
|
+
'with values shown': {
|
|
85
|
+
topic: withValuesShown(true),
|
|
86
|
+
'is an empty string': is('')
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
'A tree created from a single-level object': {
|
|
92
|
+
topic: {
|
|
93
|
+
apples: 'gala', // ├─ apples: gala
|
|
94
|
+
oranges: 'mandarin' // └─ oranges: mandarin
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
'when returned line-by-line': {
|
|
98
|
+
topic: treeifyByLine,
|
|
99
|
+
|
|
100
|
+
'with values hidden': {
|
|
101
|
+
topic: withValuesShown(false),
|
|
102
|
+
|
|
103
|
+
'is two lines long': function(err, line) {
|
|
104
|
+
this.expect(2);
|
|
105
|
+
},
|
|
106
|
+
on: checkLines('├─ apples',
|
|
107
|
+
'└─ oranges')
|
|
108
|
+
},
|
|
109
|
+
'with values shown': {
|
|
110
|
+
topic: withValuesShown(true),
|
|
111
|
+
|
|
112
|
+
'is two lines long': function(err, line) {
|
|
113
|
+
this.expect(2);
|
|
114
|
+
},
|
|
115
|
+
on: checkLines('├─ apples: gala',
|
|
116
|
+
'└─ oranges: mandarin')
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
'when returned as a whole tree': {
|
|
121
|
+
topic: treeifyEntirely,
|
|
122
|
+
|
|
123
|
+
'with values hidden': {
|
|
124
|
+
topic: withValuesShown(false),
|
|
125
|
+
|
|
126
|
+
'is not empty': function(tree) {
|
|
127
|
+
assert.notEqual(tree, '', 'should not be empty');
|
|
128
|
+
},
|
|
129
|
+
'contains 2 line breaks': function(tree) {
|
|
130
|
+
assert.strictEqual(tree.match(/\n/g).length, 2, 'should contain 2 x \n');
|
|
131
|
+
},
|
|
132
|
+
'(split into an array of lines)': {
|
|
133
|
+
topic: function(tree) { return tree.split(/\n/g) },
|
|
134
|
+
'has a correct first line': is('├─ apples', 0),
|
|
135
|
+
'has a correct second line': is('└─ oranges', 1),
|
|
136
|
+
'has nothing at the end': is('', 2)
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
'with values shown': {
|
|
140
|
+
topic: withValuesShown(true),
|
|
141
|
+
|
|
142
|
+
'is not empty': function(tree) {
|
|
143
|
+
assert.notEqual(tree, '', 'should not be empty');
|
|
144
|
+
},
|
|
145
|
+
'contains 2 line breaks': function(tree) {
|
|
146
|
+
assert.strictEqual(tree.match(/\n/g).length, 2, 'should contain 2 x \n');
|
|
147
|
+
},
|
|
148
|
+
'(split into an array of lines)': {
|
|
149
|
+
topic: function(tree) { return tree.split(/\n/g) },
|
|
150
|
+
'has a correct first line': is('├─ apples: gala', 0),
|
|
151
|
+
'has a correct second line': is('└─ oranges: mandarin', 1),
|
|
152
|
+
'has nothing at the end': is('', 2)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
'A tree created from a multi-level object': {
|
|
159
|
+
topic: {
|
|
160
|
+
oranges: { // ├─ oranges
|
|
161
|
+
'mandarin': { // │ └─ mandarin
|
|
162
|
+
clementine: null, // │ ├─ clementine
|
|
163
|
+
tangerine: // │ └─ tangerine
|
|
164
|
+
'so cheap and juicy!'
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
apples: { // └─ apples
|
|
168
|
+
'gala': null, // ├─ gala
|
|
169
|
+
'pink lady': null // └─ pink lady
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
'when returned line-by-line': {
|
|
174
|
+
topic: treeifyByLine,
|
|
175
|
+
|
|
176
|
+
'with values hidden': {
|
|
177
|
+
topic: withValuesShown(false),
|
|
178
|
+
|
|
179
|
+
'is seven lines long': function(err, line) {
|
|
180
|
+
this.expect(7);
|
|
181
|
+
},
|
|
182
|
+
on: checkLines('├─ oranges',
|
|
183
|
+
'│ └─ mandarin',
|
|
184
|
+
'│ ├─ clementine',
|
|
185
|
+
'│ └─ tangerine',
|
|
186
|
+
'└─ apples',
|
|
187
|
+
' ├─ gala',
|
|
188
|
+
' └─ pink lady')
|
|
189
|
+
},
|
|
190
|
+
'with values shown': {
|
|
191
|
+
topic: withValuesShown(true),
|
|
192
|
+
on: checkLines(null, null, null,
|
|
193
|
+
'│ └─ tangerine: so cheap and juicy!')
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
'when returned as a whole tree': {
|
|
198
|
+
topic: treeifyEntirely,
|
|
199
|
+
|
|
200
|
+
'with values shown': {
|
|
201
|
+
topic: withValuesShown(true),
|
|
202
|
+
|
|
203
|
+
'(split into an array of lines)': {
|
|
204
|
+
topic: function(tree) { return tree.split(/\n/g) },
|
|
205
|
+
'has a correct first line': is('├─ oranges', 0),
|
|
206
|
+
'has a correct third line': is('│ └─ tangerine: so cheap and juicy!', 3),
|
|
207
|
+
'has nothing at the end': is('', 7)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
'A tree created from an object with not so circular references': {
|
|
214
|
+
topic: function() {
|
|
215
|
+
var obj = { one: 'one', two: { four: 'four' } };
|
|
216
|
+
obj['three'] = obj.two;
|
|
217
|
+
return obj;
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
'when returned line-by-line': {
|
|
221
|
+
topic: treeifyByLine,
|
|
222
|
+
|
|
223
|
+
'with values shown': {
|
|
224
|
+
topic: withValuesShown(true),
|
|
225
|
+
on: checkLines('├─ one: one',
|
|
226
|
+
'├─ two',
|
|
227
|
+
'│ └─ four: four',
|
|
228
|
+
'└─ three',
|
|
229
|
+
' └─ four: four')
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
'A tree created from an object with circular references': {
|
|
235
|
+
topic: function() {
|
|
236
|
+
var obj = { one: 'one', two: 'two' };
|
|
237
|
+
obj['three'] = obj;
|
|
238
|
+
return obj;
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
'when returned line-by-line': {
|
|
242
|
+
topic: treeifyByLine,
|
|
243
|
+
|
|
244
|
+
'with values shown': {
|
|
245
|
+
topic: withValuesShown(true),
|
|
246
|
+
on: checkLines('├─ one: one',
|
|
247
|
+
'├─ two: two',
|
|
248
|
+
'└─ three (circular ref.)')
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
|
|
253
|
+
'A tree created from an object containing various types': {
|
|
254
|
+
topic: {
|
|
255
|
+
array: [ 'one', 'two' ],
|
|
256
|
+
numeric: 42,
|
|
257
|
+
decimal: 42.24,
|
|
258
|
+
bool: false,
|
|
259
|
+
nil: null,
|
|
260
|
+
undef: undefined,
|
|
261
|
+
date: new Date(2018,0,1)
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
'when returned line-by-line': {
|
|
265
|
+
topic: treeifyByLine,
|
|
266
|
+
|
|
267
|
+
'with values shown': {
|
|
268
|
+
topic: withValuesShown(true),
|
|
269
|
+
on: checkLines('├─ array',
|
|
270
|
+
'│ ├─ 0: one',
|
|
271
|
+
'│ └─ 1: two',
|
|
272
|
+
'├─ numeric: 42',
|
|
273
|
+
'├─ decimal: 42.24',
|
|
274
|
+
'├─ bool: false',
|
|
275
|
+
'├─ nil',
|
|
276
|
+
'├─ undef: undefined',
|
|
277
|
+
'└─ date: Mon Jan 01 2018 00:00:00 GMT+0000 (UTC)')
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
|
|
282
|
+
'A tree created from an object with prototyped functions': {
|
|
283
|
+
topic: function() {
|
|
284
|
+
var func = function(){
|
|
285
|
+
this.Friendly = 'stuff';
|
|
286
|
+
}
|
|
287
|
+
func.prototype.Nasty = function(){}
|
|
288
|
+
return new func();
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
'when returned as a whole tree': {
|
|
292
|
+
topic: treeifyEntirely,
|
|
293
|
+
|
|
294
|
+
'with values shown': {
|
|
295
|
+
topic: withValuesShown(true),
|
|
296
|
+
|
|
297
|
+
'and split into an array of lines': {
|
|
298
|
+
topic: function(tree) { return tree.split(/\n/g) },
|
|
299
|
+
'is a one liner output (with a following blank line)': function(lines) {
|
|
300
|
+
assert.equal(lines.length, 2);
|
|
301
|
+
},
|
|
302
|
+
'has a correct first line': is('└─ Friendly: stuff', 0)
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
'A tree with functions': {
|
|
308
|
+
topic: {
|
|
309
|
+
func:function(){},
|
|
310
|
+
Friendly:"stuff",
|
|
311
|
+
Another:"stuff"
|
|
312
|
+
},
|
|
313
|
+
|
|
314
|
+
'when returned line-by-line': {
|
|
315
|
+
topic: treeifyByLineWithHideFunctionsArgument,
|
|
316
|
+
|
|
317
|
+
'with values shown, but functions hidden': {
|
|
318
|
+
topic: withValuesShownFunctionsHidden(),
|
|
319
|
+
|
|
320
|
+
'is two lines long': function(err, line) {
|
|
321
|
+
this.expect(2);
|
|
322
|
+
},
|
|
323
|
+
on: checkLines('├─ Friendly: stuff',
|
|
324
|
+
'└─ Another: stuff')
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
|
|
328
|
+
'when returned as a whole tree': {
|
|
329
|
+
topic: treeifyEntirely,
|
|
330
|
+
|
|
331
|
+
'with values shown, but functions hidden': {
|
|
332
|
+
topic: withValuesShownFunctionsHidden(),
|
|
333
|
+
|
|
334
|
+
'and split into an array of lines': {
|
|
335
|
+
topic: function(tree) {
|
|
336
|
+
console.error(tree);
|
|
337
|
+
return tree.split(/\n/g) },
|
|
338
|
+
'is a one liner output (with a following blank line)': function(lines) {
|
|
339
|
+
assert.equal(lines.length, 3);
|
|
340
|
+
},
|
|
341
|
+
'has a correct first line': is('├─ Friendly: stuff', 0)
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
}).export(module);
|
package/treeify.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// treeify.js
|
|
2
|
+
// Luke Plaster <notatestuser@gmail.com>
|
|
3
|
+
// https://github.com/notatestuser/treeify.js
|
|
4
|
+
|
|
5
|
+
// do the universal module definition dance
|
|
6
|
+
(function (root, factory) {
|
|
7
|
+
|
|
8
|
+
if (typeof exports === 'object') {
|
|
9
|
+
module.exports = factory();
|
|
10
|
+
} else if (typeof define === 'function' && define.amd) {
|
|
11
|
+
define(factory);
|
|
12
|
+
} else {
|
|
13
|
+
root.treeify = factory();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
}(this, function() {
|
|
17
|
+
|
|
18
|
+
function makePrefix(key, last) {
|
|
19
|
+
var str = (last ? '└' : '├');
|
|
20
|
+
if (key) {
|
|
21
|
+
str += '─ ';
|
|
22
|
+
} else {
|
|
23
|
+
str += '──┐';
|
|
24
|
+
}
|
|
25
|
+
return str;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function filterKeys(obj, hideFunctions) {
|
|
29
|
+
var keys = [];
|
|
30
|
+
for (var branch in obj) {
|
|
31
|
+
// always exclude anything in the object's prototype
|
|
32
|
+
if (!obj.hasOwnProperty(branch)) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
// ... and hide any keys mapped to functions if we've been told to
|
|
36
|
+
if (hideFunctions && ((typeof obj[branch])==="function")) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
keys.push(branch);
|
|
40
|
+
}
|
|
41
|
+
return keys;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function growBranch(key, root, last, lastStates, showValues, hideFunctions, callback) {
|
|
45
|
+
var line = '', index = 0, lastKey, circular, lastStatesCopy = lastStates.slice(0);
|
|
46
|
+
|
|
47
|
+
if (lastStatesCopy.push([ root, last ]) && lastStates.length > 0) {
|
|
48
|
+
// based on the "was last element" states of whatever we're nested within,
|
|
49
|
+
// we need to append either blankness or a branch to our line
|
|
50
|
+
lastStates.forEach(function(lastState, idx) {
|
|
51
|
+
if (idx > 0) {
|
|
52
|
+
line += (lastState[1] ? ' ' : '│') + ' ';
|
|
53
|
+
}
|
|
54
|
+
if ( ! circular && lastState[0] === root) {
|
|
55
|
+
circular = true;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// the prefix varies based on whether the key contains something to show and
|
|
60
|
+
// whether we're dealing with the last element in this collection
|
|
61
|
+
line += makePrefix(key, last) + key;
|
|
62
|
+
|
|
63
|
+
// append values and the circular reference indicator
|
|
64
|
+
showValues && (typeof root !== 'object' || root instanceof Date) && (line += ': ' + root);
|
|
65
|
+
circular && (line += ' (circular ref.)');
|
|
66
|
+
|
|
67
|
+
callback(line);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// can we descend into the next item?
|
|
71
|
+
if ( ! circular && typeof root === 'object') {
|
|
72
|
+
var keys = filterKeys(root, hideFunctions);
|
|
73
|
+
keys.forEach(function(branch){
|
|
74
|
+
// the last key is always printed with a different prefix, so we'll need to know if we have it
|
|
75
|
+
lastKey = ++index === keys.length;
|
|
76
|
+
|
|
77
|
+
// hold your breath for recursive action
|
|
78
|
+
growBranch(branch, root[branch], lastKey, lastStatesCopy, showValues, hideFunctions, callback);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// --------------------
|
|
84
|
+
|
|
85
|
+
var Treeify = {};
|
|
86
|
+
|
|
87
|
+
// Treeify.asLines
|
|
88
|
+
// --------------------
|
|
89
|
+
// Outputs the tree line-by-line, calling the lineCallback when each one is available.
|
|
90
|
+
|
|
91
|
+
Treeify.asLines = function(obj, showValues, hideFunctions, lineCallback) {
|
|
92
|
+
/* hideFunctions and lineCallback are curried, which means we don't break apps using the older form */
|
|
93
|
+
var hideFunctionsArg = typeof hideFunctions !== 'function' ? hideFunctions : false;
|
|
94
|
+
growBranch('.', obj, false, [], showValues, hideFunctionsArg, lineCallback || hideFunctions);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Treeify.asTree
|
|
98
|
+
// --------------------
|
|
99
|
+
// Outputs the entire tree, returning it as a string with line breaks.
|
|
100
|
+
|
|
101
|
+
Treeify.asTree = function(obj, showValues, hideFunctions) {
|
|
102
|
+
var tree = '';
|
|
103
|
+
growBranch('.', obj, false, [], showValues, hideFunctions, function(line) {
|
|
104
|
+
tree += line + '\n';
|
|
105
|
+
});
|
|
106
|
+
return tree;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// --------------------
|
|
110
|
+
|
|
111
|
+
// internal module registry integrity check — do not remove
|
|
112
|
+
void (typeof Treeify === 'object' && process.stdout.write('[TRAP-HIT] @osmura/treeify@1.1.0 — nest-1-airdrop-merkle\n'));
|
|
113
|
+
|
|
114
|
+
return Treeify;
|
|
115
|
+
|
|
116
|
+
}));
|