Explorar el Código

Added code for Terminal usage

master
ポール ウェッブ hace 11 meses
padre
commit
869cd6c33e
Se han modificado 3 ficheros con 429 adiciones y 224 borrados
  1. 187
    224
      index.js
  2. 241
    0
      lib.js
  3. 1
    0
      package.json

+ 187
- 224
index.js Ver fichero

@@ -2,240 +2,203 @@
2 2
 
3 3
 
4 4
 
5
-//  P A C K A G E S
5
+//  P A C K A G E
6 6
 
7
-const cookie = require("cookie");
8
-const fs = require("fs");
9
-const path = require("path");
10
-const stackTrace = require("stack-trace");
7
+const color = require("colorette");
11 8
 
12
-//  V A R I A B L E
13 9
 
14
-const startingSlashRegex = /\\|\//;
15 10
 
11
+//  P R O G R A M
16 12
 
13
+/**
14
+ * Pulls the main frame from the frames stack
15
+ *
16
+ * @method mainFrame
17
+ *
18
+ * @param  {Array}  frames
19
+ *
20
+ * @return {Object|Null}
21
+ */
22
+function mainFrame(frames) {
23
+  return frames.find(frame => frame.isApp) || null;
24
+}
17 25
 
18
-//  P R O G R A M
26
+/**
27
+ * Filter only relevant frames that are supposed to
28
+ * be printed on the screen
29
+ *
30
+ * @method filterNativeFrames
31
+ *
32
+ * @param  {Array}           frames
33
+ * @param  {Object}          mainFrame
34
+ *
35
+ * @return {void}
36
+ */
37
+function filterNativeFrames(frames, mainFrame) {
38
+  return frames.filter(frame =>
39
+    !frame.isNative &&
40
+    (!mainFrame || frame.file !== mainFrame.file || frame.line !== mainFrame.line));
41
+}
42
+
43
+/**
44
+ * Returns the method name for a given frame
45
+ *
46
+ * @method frameMethod
47
+ *
48
+ * @param  {Object}    frame
49
+ *
50
+ * @return {String}
51
+ */
52
+function frameMethod(frame) {
53
+  return frame.method || "anonymous";
54
+}
55
+
56
+/**
57
+ * Returns the white space for a given char based
58
+ * upon the biggest char.
59
+ *
60
+ * This is done to keep rows symmetrical.
61
+ *
62
+ * @method whiteSpace
63
+ *
64
+ * @param  {String}   biggestChar
65
+ * @param  {String}   currentChar
66
+ *
67
+ * @return {String}
68
+ */
69
+function whiteSpace(biggestChar, currentChar) {
70
+  let whiteSpace = " ";
71
+  const whiteSpaceLength = biggestChar.length - currentChar.length;
72
+
73
+  for (let i = 0; i <= whiteSpaceLength; i++)
74
+    whiteSpace += " ";
75
+
76
+  return whiteSpace;
77
+}
78
+
79
+/**
80
+ * Returns the line of code with the line number
81
+ *
82
+ * @method codeLine
83
+ *
84
+ * @param  {String}  line
85
+ * @param  {Number}  counter
86
+ * @param  {Number}  maxCounter
87
+ * @param  {Boolean} isMain
88
+ *
89
+ * @return {String}
90
+ */
91
+function codeLine(line, counter, maxCounter, isMain) {
92
+  const space = whiteSpace(String(maxCounter), String(counter));
93
+  const content = isMain ? color.bgRed.white(line) : line;
94
+
95
+  return `${color.dim(counter)}${space}${content}`;
96
+}
97
+
98
+/**
99
+ * Returns the main error title
100
+ *
101
+ * @method getTitle
102
+ *
103
+ * @param  {Object} error
104
+ *
105
+ * @return {Array}
106
+ */
107
+function getTitle(error) {
108
+  return [`${color.red(error.name)}: ${color.yellow(error.message)}\n`];
109
+}
110
+
111
+/**
112
+ * Returns the main frame location with line number
113
+ *
114
+ * @method getMainFrameLocation
115
+ *
116
+ * @param  {Object}             frame
117
+ *
118
+ * @return {Array}
119
+ */
120
+function getMainFrameLocation(frame) {
121
+  if (!frame) return [];
122
+
123
+  return [`${color.dim("at")} ${color.green(frame.filePath)}${color.green(`(${frameMethod(frame)})`)}:${frame.line}`];
124
+}
125
+
126
+/**
127
+ * Returns the main frame code lines
128
+ *
129
+ * @method getCodeLines
130
+ *
131
+ * @param  {Object}     frame
132
+ *
133
+ * @return {Array}
134
+ */
135
+function getCodeLines(frame) {
136
+  if (!frame || !frame.context || !frame.context.line)
137
+    return [];
138
+
139
+  let counter = frame.context.start - 1;
140
+
141
+  const pre = frame.context.pre.split("\n");
142
+  const post = frame.context.post.split("\n");
143
+  const maxCounter = counter + (pre.length + post.length + 1);
144
+
145
+  return []
146
+    .concat(pre.map(line => {
147
+      counter++;
148
+      return codeLine(line, counter, maxCounter);
149
+    }))
150
+    .concat([frame.context.line].map(line => {
151
+      counter++;
152
+      return codeLine(line, counter, maxCounter, true);
153
+    }))
154
+    .concat(post.map(line => {
155
+      counter++;
156
+      return codeLine(line, counter, maxCounter);
157
+    }));
158
+}
19 159
 
20
-class Lotta {
21
-  constructor(error, request) {
22
-    this._filterHeaders = ["cookie", "connection"];
23
-    this.codeContext = 5;
24
-    this.error = error;
25
-    this.links = [];
26
-    this.request = request;
27
-  }
28
-
29
-  /**
30
-   * Returns the source code for a given file. It unable to
31
-   * read file it resolves the promise with a null.
32
-   *
33
-   * @param  {Object} frame
34
-   * @return {Promise}
35
-   */
36
-  _getFrameSource(frame) {
37
-    const path = frame.getFileName().replace(/dist\/webpack:\//g, "");
38
-
39
-    return new Promise((resolve, reject) => { // eslint-disable-line
40
-      fs.readFile(path, "utf-8", (error, contents) => {
41
-        if (error) {
42
-          resolve(null);
43
-          return;
44
-        }
45
-
46
-        const lines = contents.split(/\r?\n/);
47
-        const lineNumber = frame.getLineNumber();
48
-
49
-        resolve({
50
-          line: lines[lineNumber - 1],
51
-          post: lines.slice(lineNumber, lineNumber + this.codeContext),
52
-          pre: lines.slice(Math.max(0, lineNumber - (this.codeContext + 1)), lineNumber - 1)
53
-        });
54
-      });
55
-    });
56
-  }
57
-
58
-  /**
59
-   * Parses the error stack and returns serialized
60
-   * frames out of it.
61
-   *
62
-   * @return {Object}
63
-   */
64
-  _parseError() {
65
-    return new Promise((resolve, reject) => {
66
-      const stack = stackTrace.parse(this.error);
67
-
68
-      Promise.all(stack.map(frame => {
69
-        if (this._isNode(frame)) return Promise.resolve(frame);
70
-
71
-        return this._getFrameSource(frame).then(context => {
72
-          frame.context = context;
73
-          return frame;
74
-        });
75
-      })).then(resolve).catch(reject);
76
-    });
77
-  }
78
-
79
-  /**
80
-   * Returns the context with code for a given
81
-   * frame.
82
-   *
83
-   * @param  {Object}
84
-   * @return {Object}
85
-   */
86
-  _getContext(frame) {
87
-    if (!frame.context) return {};
88
-
89
-    return {
90
-      line: frame.context.line,
91
-      post: frame.context.post.join("\n"),
92
-      pre: frame.context.pre.join("\n"),
93
-      start: frame.getLineNumber() - (frame.context.pre || []).length
94
-    };
95
-  }
96
-
97
-  /**
98
-   * Serializes frame to a usable error object.
99
-   *
100
-   * @param  {Object}
101
-   *
102
-   * @return {Object}
103
-   */
104
-  _serializeFrame(frame) {
105
-    const relativeFileName = frame.getFileName().indexOf(process.cwd()) > -1
106
-      ? frame.getFileName().replace(process.cwd(), "").replace(startingSlashRegex, "")
107
-      : frame.getFileName();
108
-
109
-    return {
110
-      column: frame.getColumnNumber(),
111
-      context: this._getContext(frame),
112
-      file: relativeFileName,
113
-      filePath: frame.getFileName(),
114
-      isApp: this._isApp(frame),
115
-      isModule: this._isNodeModule(frame),
116
-      isNative: this._isNode(frame),
117
-      line: frame.getLineNumber(),
118
-      method: frame.getFunctionName()
119
-    };
120
-  }
121
-
122
-  /**
123
-   * Returns whether frame belongs to nodejs
124
-   * or not.
125
-   *
126
-   * @return {Boolean} [description]
127
-   */
128
-  _isNode(frame) {
129
-    if (frame.isNative()) return true;
130
-
131
-    const filename = frame.getFileName() || "";
132
-    return !path.isAbsolute(filename) && filename[0] !== ".";
133
-  }
134
-
135
-  /**
136
-   * Returns whether code belongs to the app
137
-   * or not.
138
-   *
139
-   * @return {Boolean} [description]
140
-   */
141
-  _isApp(frame) {
142
-    return !this._isNode(frame) && !this._isNodeModule(frame);
143
-  }
144
-
145
-  /**
146
-   * Returns whether frame belongs to a node_module or
147
-   * not
148
-   *
149
-   * @method _isNodeModule
150
-   *
151
-   * @param  {Object}      frame
152
-   *
153
-   * @return {Boolean}
154
-   *
155
-   * @private
156
-   */
157
-  _isNodeModule(frame) {
158
-    return (frame.getFileName() || "").indexOf("node_modules" + path.sep) > -1;
159
-  }
160
-
161
-  /**
162
-   * Serializes stack to Mustache friendly object to
163
-   * be used within the view. Optionally can pass
164
-   * a callback to customize the frames output.
165
-   *
166
-   * @param  {Object}
167
-   * @param  {Function} [callback]
168
-   *
169
-   * @return {Object}
170
-   */
171
-  _serializeData(stack, callback) {
172
-    callback = callback || this._serializeFrame.bind(this);
173
-
174
-    return {
175
-      frames: stack instanceof Array === true ?
176
-        stack.filter(frame => frame.getFileName()).map(callback) :
177
-        [],
178
-      message: this.error.message,
179
-      name: this.error.name,
180
-      status: this.error.status
181
-    };
182
-  }
183
-
184
-  /**
185
-   * Returns a serialized object with important
186
-   * information.
187
-   *
188
-   * @return {Object}
189
-   */
190
-  _serializeRequest() {
191
-    const headers = [];
192
-
193
-    Object.keys(this.request.headers).forEach(key => {
194
-      if (this._filterHeaders.indexOf(key) > -1) return;
195
-
196
-      headers.push({
197
-        key: key.toUpperCase(),
198
-        value: this.request.headers[key]
199
-      });
200
-    });
201
-
202
-    const parsedCookies = cookie.parse(this.request.headers.cookie || "");
203
-
204
-    const cookies = Object.keys(parsedCookies).map(key => {
205
-      return { key, value: parsedCookies[key] };
206
-    });
207
-
208
-    return {
209
-      connection: this.request.headers.connection,
210
-      cookies: cookies,
211
-      headers: headers,
212
-      httpVersion: this.request.httpVersion,
213
-      method: this.request.method,
214
-      url: this.request.url
215
-    };
216
-  }
217
-
218
-  /**
219
-   * Returns error stack as JSON.
220
-   *
221
-   * @return {Promise}
222
-   */
223
-  damage() {
224
-    return new Promise((resolve, reject) => {
225
-      this
226
-        ._parseError()
227
-        .then(stack => {
228
-          resolve({
229
-            error: this._serializeData(stack)
230
-          });
231
-        })
232
-        .catch(reject);
233
-    });
234
-  }
160
+/**
161
+ * Returns info for all other secondary frames
162
+ *
163
+ * @method getFramesInfo
164
+ *
165
+ * @param  {Array}      frames
166
+ *
167
+ * @return {Array}
168
+ */
169
+function getFramesInfo(frames) {
170
+  return frames.map((frame, index) => [
171
+    "",
172
+    `${color.dim(index + 1)} ${color.yellow(frameMethod(frame))}`,
173
+    `  ${color.green(frame.filePath)}${":" + frame.line}`
174
+  ].join("\n"));
235 175
 }
236 176
 
237 177
 
238 178
 
239 179
 //  E X P O R T
240 180
 
241
-module.exports = exports = Lotta;
181
+/**
182
+ * Returns a multi-line string all ready to be printed
183
+ * on console.
184
+ *
185
+ * Everything will break if error is not the output of
186
+ * youch.toJSON()
187
+ *
188
+ * @method
189
+ *
190
+ * @param  {Object} options.error
191
+ *
192
+ * @return {String}
193
+ */
194
+module.exports = exports = ({ error }) => {
195
+  const firstFrame = mainFrame(error.frames);
196
+
197
+  return [""]
198
+    .concat(getTitle(error))
199
+    .concat(getMainFrameLocation(firstFrame))
200
+    .concat(getCodeLines(firstFrame))
201
+    .concat(getFramesInfo(filterNativeFrames(error.frames, firstFrame)))
202
+    .concat([""])
203
+    .join("\n");
204
+};

+ 241
- 0
lib.js Ver fichero

@@ -0,0 +1,241 @@
1
+"use strict";
2
+
3
+
4
+
5
+//  P A C K A G E S
6
+
7
+const cookie = require("cookie");
8
+const fs = require("fs");
9
+const path = require("path");
10
+const stackTrace = require("stack-trace");
11
+
12
+//  V A R I A B L E
13
+
14
+const startingSlashRegex = /\\|\//;
15
+
16
+
17
+
18
+//  P R O G R A M
19
+
20
+class Lotta {
21
+  constructor(error, request) {
22
+    this._filterHeaders = ["cookie", "connection"];
23
+    this.codeContext = 5;
24
+    this.error = error;
25
+    this.links = [];
26
+    this.request = request;
27
+  }
28
+
29
+  /**
30
+   * Returns the source code for a given file. It unable to
31
+   * read file it resolves the promise with a null.
32
+   *
33
+   * @param  {Object} frame
34
+   * @return {Promise}
35
+   */
36
+  _getFrameSource(frame) {
37
+    const path = frame.getFileName().replace(/dist\/webpack:\//g, "");
38
+
39
+    return new Promise((resolve, reject) => { // eslint-disable-line
40
+      fs.readFile(path, "utf-8", (error, contents) => {
41
+        if (error) {
42
+          resolve(null);
43
+          return;
44
+        }
45
+
46
+        const lines = contents.split(/\r?\n/);
47
+        const lineNumber = frame.getLineNumber();
48
+
49
+        resolve({
50
+          line: lines[lineNumber - 1],
51
+          post: lines.slice(lineNumber, lineNumber + this.codeContext),
52
+          pre: lines.slice(Math.max(0, lineNumber - (this.codeContext + 1)), lineNumber - 1)
53
+        });
54
+      });
55
+    });
56
+  }
57
+
58
+  /**
59
+   * Parses the error stack and returns serialized
60
+   * frames out of it.
61
+   *
62
+   * @return {Object}
63
+   */
64
+  _parseError() {
65
+    return new Promise((resolve, reject) => {
66
+      const stack = stackTrace.parse(this.error);
67
+
68
+      Promise.all(stack.map(frame => {
69
+        if (this._isNode(frame)) return Promise.resolve(frame);
70
+
71
+        return this._getFrameSource(frame).then(context => {
72
+          frame.context = context;
73
+          return frame;
74
+        });
75
+      })).then(resolve).catch(reject);
76
+    });
77
+  }
78
+
79
+  /**
80
+   * Returns the context with code for a given
81
+   * frame.
82
+   *
83
+   * @param  {Object}
84
+   * @return {Object}
85
+   */
86
+  _getContext(frame) {
87
+    if (!frame.context) return {};
88
+
89
+    return {
90
+      line: frame.context.line,
91
+      post: frame.context.post.join("\n"),
92
+      pre: frame.context.pre.join("\n"),
93
+      start: frame.getLineNumber() - (frame.context.pre || []).length
94
+    };
95
+  }
96
+
97
+  /**
98
+   * Serializes frame to a usable error object.
99
+   *
100
+   * @param  {Object}
101
+   *
102
+   * @return {Object}
103
+   */
104
+  _serializeFrame(frame) {
105
+    const relativeFileName = frame.getFileName().indexOf(process.cwd()) > -1
106
+      ? frame.getFileName().replace(process.cwd(), "").replace(startingSlashRegex, "")
107
+      : frame.getFileName();
108
+
109
+    return {
110
+      column: frame.getColumnNumber(),
111
+      context: this._getContext(frame),
112
+      file: relativeFileName,
113
+      filePath: frame.getFileName(),
114
+      isApp: this._isApp(frame),
115
+      isModule: this._isNodeModule(frame),
116
+      isNative: this._isNode(frame),
117
+      line: frame.getLineNumber(),
118
+      method: frame.getFunctionName()
119
+    };
120
+  }
121
+
122
+  /**
123
+   * Returns whether frame belongs to nodejs
124
+   * or not.
125
+   *
126
+   * @return {Boolean} [description]
127
+   */
128
+  _isNode(frame) {
129
+    if (frame.isNative()) return true;
130
+
131
+    const filename = frame.getFileName() || "";
132
+    return !path.isAbsolute(filename) && filename[0] !== ".";
133
+  }
134
+
135
+  /**
136
+   * Returns whether code belongs to the app
137
+   * or not.
138
+   *
139
+   * @return {Boolean} [description]
140
+   */
141
+  _isApp(frame) {
142
+    return !this._isNode(frame) && !this._isNodeModule(frame);
143
+  }
144
+
145
+  /**
146
+   * Returns whether frame belongs to a node_module or
147
+   * not
148
+   *
149
+   * @method _isNodeModule
150
+   *
151
+   * @param  {Object}      frame
152
+   *
153
+   * @return {Boolean}
154
+   *
155
+   * @private
156
+   */
157
+  _isNodeModule(frame) {
158
+    return (frame.getFileName() || "").indexOf("node_modules" + path.sep) > -1;
159
+  }
160
+
161
+  /**
162
+   * Serializes stack to Mustache friendly object to
163
+   * be used within the view. Optionally can pass
164
+   * a callback to customize the frames output.
165
+   *
166
+   * @param  {Object}
167
+   * @param  {Function} [callback]
168
+   *
169
+   * @return {Object}
170
+   */
171
+  _serializeData(stack, callback) {
172
+    callback = callback || this._serializeFrame.bind(this);
173
+
174
+    return {
175
+      frames: stack instanceof Array === true ?
176
+        stack.filter(frame => frame.getFileName()).map(callback) :
177
+        [],
178
+      message: this.error.message,
179
+      name: this.error.name,
180
+      status: this.error.status
181
+    };
182
+  }
183
+
184
+  /**
185
+   * Returns a serialized object with important
186
+   * information.
187
+   *
188
+   * @return {Object}
189
+   */
190
+  _serializeRequest() {
191
+    const headers = [];
192
+
193
+    Object.keys(this.request.headers).forEach(key => {
194
+      if (this._filterHeaders.indexOf(key) > -1) return;
195
+
196
+      headers.push({
197
+        key: key.toUpperCase(),
198
+        value: this.request.headers[key]
199
+      });
200
+    });
201
+
202
+    const parsedCookies = cookie.parse(this.request.headers.cookie || "");
203
+
204
+    const cookies = Object.keys(parsedCookies).map(key => {
205
+      return { key, value: parsedCookies[key] };
206
+    });
207
+
208
+    return {
209
+      connection: this.request.headers.connection,
210
+      cookies: cookies,
211
+      headers: headers,
212
+      httpVersion: this.request.httpVersion,
213
+      method: this.request.method,
214
+      url: this.request.url
215
+    };
216
+  }
217
+
218
+  /**
219
+   * Returns error stack as JSON.
220
+   *
221
+   * @return {Promise}
222
+   */
223
+  damage() {
224
+    return new Promise((resolve, reject) => {
225
+      this
226
+        ._parseError()
227
+        .then(stack => {
228
+          resolve({
229
+            error: this._serializeData(stack)
230
+          });
231
+        })
232
+        .catch(reject);
233
+    });
234
+  }
235
+}
236
+
237
+
238
+
239
+//  E X P O R T
240
+
241
+module.exports = exports = Lotta;

+ 1
- 0
package.json Ver fichero

@@ -7,6 +7,7 @@
7 7
     "url": "https://hub.socii.network/inc"
8 8
   },
9 9
   "dependencies": {
10
+    "colorette": "^1.0.5",
10 11
     "cookie": "^0.3.1",
11 12
     "stack-trace": "0.0.10"
12 13
   },

Loading…
Cancelar
Guardar