Browse Source

Initial commit

master
ポール ウェッブ 1 year ago
parent
commit
0cfba84f87
7 changed files with 327 additions and 1 deletions
  1. 10
    0
      .editorconfig
  2. 3
    0
      .eslintrc
  3. 7
    0
      .gitignore
  4. 2
    0
      .npmrc
  5. 17
    1
      README.md
  6. 241
    0
      index.js
  7. 47
    0
      package.json

+ 10
- 0
.editorconfig View File

@@ -0,0 +1,10 @@
1
+root = true
2
+
3
+[*]
4
+charset = utf-8
5
+end_of_line = lf
6
+indent_size = 2
7
+indent_style = space
8
+insert_final_newline = true
9
+trim_trailing_whitespace = true
10
+

+ 3
- 0
.eslintrc View File

@@ -0,0 +1,3 @@
1
+{
2
+  "extends": "@inc"
3
+}

+ 7
- 0
.gitignore View File

@@ -0,0 +1,7 @@
1
+# Files
2
+.DS_Store
3
+Thumbs.db
4
+
5
+# Folders
6
+node_modules
7
+

+ 2
- 0
.npmrc View File

@@ -0,0 +1,2 @@
1
+package-lock=false
2
+

+ 17
- 1
README.md View File

@@ -1,3 +1,19 @@
1 1
 # lotta
2 2
 
3
-That's a lotta damage!
3
+[![INC](https://img.shields.io/badge/%F0%9F%92%A1-IdeasNeverCease/lotta-51dcfb.svg?style=flat-square)](https://git.inc.sh/IdeasNeverCease/lotta)
4
+
5
+> That's a lotta damage!
6
+>
7
+> Lotta offers beautiful error reporting, because code is ugly enough.
8
+
9
+
10
+
11
+### Installation
12
+
13
+```bash
14
+$ npm i lotta -S
15
+```
16
+
17
+### Usage
18
+
19
+TODO

+ 241
- 0
index.js View File

@@ -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;

+ 47
- 0
package.json View File

@@ -0,0 +1,47 @@
1
+{
2
+  "author": {
3
+    "email": "paul@inc.sh",
4
+    "name": "Paul Anthony Webb"
5
+  },
6
+  "bugs": {
7
+    "url": "https://hub.socii.network/inc"
8
+  },
9
+  "dependencies": {
10
+    "cookie": "^0.3.1",
11
+    "stack-trace": "0.0.10"
12
+  },
13
+  "description": "Beautiful error reporting",
14
+  "devDependencies": {
15
+    "@inc/eslint-config": "^1.0.2",
16
+    "eslint": "^5.6.1",
17
+    "husky": "^1.1.0",
18
+    "npm-run-all": "^4.1.3",
19
+    "snazzy": "^8.0.0",
20
+    "standardx": "^3.0.1",
21
+    "updates": "^4.5.0"
22
+  },
23
+  "husky": {
24
+    "hooks": {
25
+      "pre-commit": "npm run test:lint"
26
+    }
27
+  },
28
+  "keywords": [
29
+    "beautiful",
30
+    "error",
31
+    "gorgeous",
32
+    "pretty"
33
+  ],
34
+  "license": "MIT",
35
+  "main": "index.js",
36
+  "name": "lotta",
37
+  "repository": {
38
+    "type": "git",
39
+    "url": "https://git.inc.sh/IdeasNeverCease/lotta.git"
40
+  },
41
+  "scripts": {
42
+    "test": "run-s test:*",
43
+    "test:dependencies": "updates -u ./",
44
+    "test:lint": "standardx --verbose | snazzy"
45
+  },
46
+  "version": "1.0.0"
47
+}

Loading…
Cancel
Save