-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.js
175 lines (160 loc) · 4.19 KB
/
index.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
var fs = require('fs')
, path = require('path')
, existsSync = fs.existsSync || path.existsSync
, cache = {};
/**
* Exposes both functional and streaming interfaces.
*
* Data is expected to be JSON containing either a
* single object to be formatted, or an array of
* objects.
*/
exports.style = function(data, style) {
// Accepts array of styles.
if (Array.isArray(style)) {
style.forEach(function (style) {
data = exports.style(data, style);
});
return data;
}
// Accepts style name.
if (typeof style === 'string') {
if (!exports.styles[style]) throw new Error('Style `' + style + '` not found');
style = exports.styles[style];
}
// Filter the data.
if (style.filter) {
if (Array.isArray(data)) {
data = data.filter(style.filter);
}
else if (!style.filter(data)) {
return null;
}
}
// Reduce the data, if data is an array.
if (style.reduce) {
if (Array.isArray(data)) {
if (style.reduceInitialValue) {
data = data.reduce(style.reduce, style.reduceInitialValue);
}
else {
data = data.reduce(style.reduce);
}
}
}
// Map the data.
if (style.map) {
if (Array.isArray(data)) {
data = data.map(style.map);
}
else {
data = style.map(data);
}
}
return data;
};
exports.stream = function (options) {
return require('through')(
function write (data) {
if (typeof data === 'string') {
data = exports.parse(data);
}
if (data && options.style) {
data = exports.style(data, options.style);
}
if (data && options.json) {
data = JSON.stringify(data, null, 2);
}
if (data) {
this.emit('data', data);
}
},
function end () {
this.emit('end');
}
);
};
exports.parse = function (data, fail) {
try {
return JSON.parse(data);
}
catch (e) {
if (fail) {
throw e;
}
else {
// Valid JSON might be surrounded by stuff, try to parse it out.
var match = data.match(/\{.*\}/);
if (match) {
var parsed = exports.parse(match[0], true);
parsed._extra = data.split(match[0]);
return parsed;
}
// Wrap non-JSON data.
else {
return {text: data};
}
}
}
};
/**
* Styles can be saved and reused. They can be loaded from the following
* locations (and override eachother in this order):
*
* - $HOME/.joli/styles/*.js
* - $CWD/.joli/styles/*.js
*
* A style is a node module that exports an object containing one or
* more of:
*
* - **filter** `function(data)` - A filter function to run on the data.
* - **reduce** `function(data, [initialValue])` - A reduce function to run
* on the data.
* - **reduceInitialValue** - A value to pass as the initial value for the
* reduce operation.
* - **map** `function(data)` - A map function to run on the data.
*
* Note: **reduce** will only effect arrays of objects, not single objects.
*
* The name of the style will be determined by its filename.
*/
exports.styles = loadModules.call(this, 'styles');
/**
* Outputters can be saved and reused. They can be loaded from the following
* locations (and override eachother in this order).
*
* - $HOME/.joli/outputters/*.js
* - $CWD/.joli/outputters/*.js
*
* An outputter is a node module that exports a function that accepts data
* and outputs it somewhere.
*
* The default 'console' outputter is available and just console.log's the
* data.
*/
exports.outputters = loadModules.call(this, 'outputters');
/**
* Loads modules in the joli fallback paths.
*/
function loadModules (type) {
if (!cache[type]) {
cache[type] = {};
// Load core modules.
loadFiles(type, __dirname);
// Load outputters relative to HOME.
loadFiles(type, process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME']);
// Load outputters relative to CWD.
loadFiles(type, process.cwd());
}
return cache[type];
}
function loadFiles (type, root) {
var dir = path.join(root, '.joli', type);
if (existsSync(dir)) {
fs.readdirSync(dir).forEach(function(file) {
if (file.match(/\.js$/)) {
cache[type][file.slice(0, -3)] = require(path.join(dir, file));
}
});
}
}