This commit is contained in:
2022-07-21 03:28:35 +00:00
parent d7c883d6df
commit 51b34b0e1d
30103 changed files with 4152204 additions and 23 deletions
+60
View File
@@ -0,0 +1,60 @@
<a name="2.0.5"></a>
## [2.0.5](https://github.com/poppinss/youch/compare/v2.0.4...v2.0.5) (2017-06-13)
### Bug Fixes
* **template:** improve css for smaller screens ([b07c77d](https://github.com/poppinss/youch/commit/b07c77d))
<a name="2.0.4"></a>
## [2.0.4](https://github.com/poppinss/youch/compare/v2.0.3...v2.0.4) (2017-01-31)
### Bug Fixes
* **test:** use mocha instead of japa ([8bf7039](https://github.com/poppinss/youch/commit/8bf7039))
<a name="2.0.3"></a>
## [2.0.3](https://github.com/poppinss/youch/compare/v2.0.2...v2.0.3) (2017-01-30)
### Bug Fixes
* **regex:** use plain regex over path.sep ([db3e2dc](https://github.com/poppinss/youch/commit/db3e2dc))
<a name="2.0.2"></a>
## [2.0.2](https://github.com/poppinss/youch/compare/v2.0.0...v2.0.2) (2017-01-27)
### Bug Fixes
* **package:** fix path to main file ([5ad3b4a](https://github.com/poppinss/youch/commit/5ad3b4a))
<a name="2.0.1"></a>
## [2.0.1](https://github.com/poppinss/youch/compare/v2.0.0...v2.0.1) (2017-01-26)
### Bug Fixes
* **package:** fix path to main file ([5ad3b4a](https://github.com/poppinss/youch/commit/5ad3b4a))
<a name="2.0.0"></a>
# 2.0.0 (2017-01-26)
### Features
* initial implementation ([aba222a](https://github.com/poppinss/youch/commit/aba222a))
+6
View File
@@ -0,0 +1,6 @@
# Contributing
In favor of active development we accept contributions from everyone. You can contribute by submitting a bug, creating pull requests or even by improving documentation.
Below is the guide to be followed strictly before submitting your pull requests.
http://adonisjs.com/docs/contributing
+89
View File
@@ -0,0 +1,89 @@
# Youch!
> Pretty error reporting for Node.js 🚀 (Modified for Nuxt.js & SSR Bundles)
<br />
<p>
<img src="https://user-images.githubusercontent.com/5158436/28990900-0a4766f8-7997-11e7-9f0b-4336fa2e2e0b.png" style="width: 600px;" />
</p>
<br />
---
<br />
[![NPM Version][npm-image]][npm-url]
[![Build Status][travis-image]][travis-url]
[![Downloads Stats][npm-downloads]][npm-url]
[![Appveyor][appveyor-image]][appveyor-url]
[![Gitter Channel][gitter-image]][gitter-url]
[![Trello][trello-image]][trello-url]
[![Patreon][patreon-image]][patreon-url]
Youch is inspired by [Whoops](https://filp.github.io/whoops) but with a modern design. Reading stack trace of the console slows you down from active development. Instead **Youch** print those errors in structured HTML to the browser.
## Features
1. HTML reporter
2. JSON reporter, if request accepts a json instead of text/html.
3. Sorted frames of error stack.
## Installation
```bash
npm i --save @nuxtjs/youch
```
## Basic Usage
Youch is used by [AdonisJs](http://adonisjs.com) and [Nuxt.js](https://nuxtjs.org), but it can be used by express or raw HTTP server as well.
```javascript
const Youch = require('@nuxtjs/youch')
const http = require('http')
http.createServer(function (req, res) {
// PERFORM SOME ACTION
if (error) {
const youch = new Youch(error, req)
youch
.toHTML()
.then((html) => {
res.writeHead(200, {'content-type': 'text/html'})
res.write(html)
res.end()
})
}
}).listen(8000)
```
## Release History
Checkout [CHANGELOG.md](CHANGELOG.md) file for release history.
## Meta
Checkout [LICENSE.txt](LICENSE.txt) for license information
Harminder Virk (Aman) - [https://github.com/thetutlage](https://github.com/thetutlage)
[appveyor-image]: https://ci.appveyor.com/api/projects/status/github/nuxt/youch?branch=master&svg=true&passingText=Passing%20On%20Windows
[appveyor-url]: https://ci.appveyor.com/project/nuxt/youch
[npm-image]: https://img.shields.io/npm/v/@nuxtjs/youch.svg?style=flat-square
[npm-url]: https://npmjs.org/package/@nuxtjs/youch
[travis-image]: https://img.shields.io/travis/nuxt/youch/master.svg?style=flat-square
[travis-url]: https://travis-ci.org/nuxt/youch
[gitter-url]: https://gitter.im/adonisjs/adonis-framework
[gitter-image]: https://img.shields.io/badge/gitter-join%20us-1DCE73.svg?style=flat-square
[trello-url]: https://trello.com/b/yzpqCgdl/adonis-for-humans
[trello-image]: https://img.shields.io/badge/trello-roadmap-89609E.svg?style=flat-square
[patreon-url]: https://www.patreon.com/adonisframework
[patreon-image]: https://img.shields.io/badge/patreon-support%20AdonisJs-brightgreen.svg?style=flat-square
[npm-downloads]: https://img.shields.io/npm/dm/@nuxtjs/youch.svg?style=flat-square
+21
View File
@@ -0,0 +1,21 @@
environment:
matrix:
- nodejs_version: 'Stable'
init:
git config --global core.autocrlf true
install:
- ps: Install-Product node $env:nodejs_version
- npm install
test_script:
- node --version
- npm --version
- npm run test:win
build: off
clone_depth: 1
matrix:
fast_finish: true
+40
View File
@@ -0,0 +1,40 @@
'use strict'
const http = require('http')
const Youch = require('../src/Youch')
class HttpException extends Error {
constructor (...args) {
super(...args)
this.name = this.constructor.name
}
}
function foo () {
const error = new HttpException('Some weird error')
error.status = 503
throw error
}
http.createServer((req, res) => {
let youch = null
try {
foo()
} catch (e) {
youch = new Youch(e, req)
}
youch
.toHTML()
.then((response) => {
res.writeHead(200, {'content-type': 'text/html'})
res.write(response)
res.end()
}).catch((error) => {
res.writeHead(500)
res.write(error.message)
res.end()
})
}).listen(8000, () => {
console.log('listening to port 8000')
})
+80
View File
@@ -0,0 +1,80 @@
{
"_args": [
[
"@nuxtjs/youch@4.2.3",
"/home/node/nuxt"
]
],
"_from": "@nuxtjs/youch@4.2.3",
"_id": "@nuxtjs/youch@4.2.3",
"_inBundle": false,
"_integrity": "sha512-XiTWdadTwtmL/IGkNqbVe+dOlT+IMvcBu7TvKI7plWhVQeBCQ9iKhk3jgvVWFyiwL2yHJDlEwOM5v9oVES5Xmw==",
"_location": "/@nuxtjs/youch",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "@nuxtjs/youch@4.2.3",
"name": "@nuxtjs/youch",
"escapedName": "@nuxtjs%2fyouch",
"scope": "@nuxtjs",
"rawSpec": "4.2.3",
"saveSpec": null,
"fetchSpec": "4.2.3"
},
"_requiredBy": [
"/@nuxt/server"
],
"_resolved": "https://registry.npmjs.org/@nuxtjs/youch/-/youch-4.2.3.tgz",
"_spec": "4.2.3",
"_where": "/home/node/nuxt",
"author": {
"name": "amanvirk"
},
"bugs": {
"url": "https://github.com/poppinss/nuxt/issues"
},
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
},
"dependencies": {
"cookie": "^0.3.1",
"mustache": "^2.3.0",
"stack-trace": "0.0.10"
},
"description": "Pretty error reporting for Node.js 🚀 (Modified for Nuxt.js & SSR Bundles)",
"devDependencies": {
"cz-conventional-changelog": "^2.0.0",
"japa": "^1.0.3",
"japa-cli": "^1.0.1",
"standard": "^10.0.2",
"supertest": "^3.0.0"
},
"directories": {
"example": "examples"
},
"homepage": "https://github.com/poppinss/nuxt#readme",
"keywords": [
"errors",
"error-reporting",
"whoops"
],
"license": "MIT",
"main": "src/Youch/index.js",
"name": "@nuxtjs/youch",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/nuxt/youch.git"
},
"scripts": {
"lint": "standard",
"test": "japa",
"test:win": "node ./node_modules/japa-cli/index.js"
},
"version": "4.2.3"
}
+349
View File
@@ -0,0 +1,349 @@
'use strict'
/*
* youch
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
const Mustache = require('mustache')
const path = require('path')
const stackTrace = require('stack-trace')
const fs = require('fs')
const cookie = require('cookie')
const VIEW_PATH = '../resources/error.mustache'
const startingSlashRegex = /\\|\//
const viewTemplate = fs.readFileSync(path.join(__dirname, VIEW_PATH), 'utf-8')
class Youch {
constructor (error, request, readSource, baseURL, addCol) {
this.error = error
this.request = request
this.readSource = typeof readSource === 'function' ? readSource : this._readSource
this.baseURL = baseURL || '/'
this.addCol = addCol === undefined ? true : Boolean(addCol)
this.codeContext = 5
this._filterHeaders = ['cookie', 'connection']
this._filterFrames = [
/regenerator-runtime/,
/babel-runtime/,
/core-js\/library/
]
}
/**
* Reads the source code for a given frame into frame.contents
*
* @param {String} path
* @return {Promise}
*/
_readSource (frame) {
return new Promise((resolve, reject) => {
if(!frame.fileName) {
return resolve()
}
fs.readFile(frame.fileName, 'utf-8', (error, contents) => {
if (!error && contents) {
frame.contents = contents
}
resolve()
})
})
}
/**
* Returns source code for a given frame.
*
* @param {Object} frame
* @return {Promise}
*/
_getFrameSource (frame) {
return this.readSource(frame).then(()=> {
if (!frame.contents) {
return
}
const lines = frame.contents.split(/\r?\n/)
const lineNumber = frame.getLineNumber()
return {
pre: lines.slice(Math.max(0, lineNumber - (this.codeContext + 1)), lineNumber - 1),
line: lines[lineNumber - 1],
post: lines.slice(lineNumber, lineNumber + this.codeContext)
}
})
}
/**
* Parses the error stack and returns serialized
* frames out of it.
*
* @return {Object}
*/
_parseError () {
const stack = stackTrace.parse(this.error)
return Promise.all(stack.map((frame) => {
if (this._isNode(frame)) {
return Promise.resolve(frame)
}
return this._getFrameSource(frame).then((context) => {
frame.context = context
return frame
})
}))
.then(stack => stack.filter(this._isVisible.bind(this)))
.then(stack => {
let hasInternal = false
for (let frame of stack) {
if (!this._isApp(frame) && !this._isNode(frame)) {
hasInternal = true
break
}
}
return {stack, hasInternal}
})
}
/**
* Returns the context with code for a given
* frame.
*
* @param {Object}
* @return {Object}
*/
_getContext (frame) {
if (!frame.context) {
return {}
}
return {
start: frame.getLineNumber() - (frame.context.pre || []).length,
pre: frame.context.pre.join('\n'),
line: frame.context.line,
post: frame.context.post.join('\n'),
}
}
/**
* Returns classes to be used inside HTML when
* displaying the frames list.
*
* @param {Object}
* @param {Number}
*
* @return {String}
*/
_getDisplayClasses (frame, index) {
const classes = []
if (index === 0) {
classes.push('active')
}
if (!this._isApp(frame)) {
classes.push('native-frame')
}
return classes.join(' ')
}
/**
* Compiles the view using HTML
*
* @param {String}
* @param {Object}
*
* @return {String}
*/
_compileView (view, data) {
return Mustache.render(view, data)
}
/**
* Serializes frame to a usable error object.
*
* @param {Object}
*
* @return {Object}
*/
_serializeFrame (frame) {
const relativeFileName = frame.getFileName().indexOf(process.cwd()) > -1
? frame.getFileName().replace(process.cwd(), '').replace(startingSlashRegex, '')
: frame.getFileName()
return {
file: relativeFileName,
method: frame.getFunctionName(),
line: frame.getLineNumber(),
column: frame.getColumnNumber(),
context: this._getContext(frame),
lang: this._getLang(frame),
open: this._openURL(frame)
}
}
_openURL(frame) {
if (!frame.fullPath) {
return
}
return this.baseURL + '__open-in-editor' +
'?file=' + encodeURI(frame.fullPath || frame.fileName) +
':' + (frame.getLineNumber() || 0) +
(this.addCol ? (':' + (frame.getColumnNumber() || 0)) : '')
}
/**
* Returns whether frame belongs to nodejs
* or not.
*
* @return {Boolean} [description]
*/
_isNode (frame) {
if (frame.isNative()) {
return true
}
// const filename = frame.getFileName() || ''
// return !path.isAbsolute(filename) && filename[0] !== '.'
return false
}
/**
* Returns whether code belongs to the app
* or not.
*
* @return {Boolean} [description]
*/
_isApp (frame) {
if (this._isNode(frame)) {
return false
}
return !~(frame.getFileName() || '').indexOf('node_modules' + path.sep)
}
/**
* Returns whether frame should be visible
* or not.
*
* @return {Boolean} [description]
*/
_isVisible (frame) {
return this._filterFrames.every(f => !f.test(frame.getFileName()))
}
_getLang(frame) {
let name = frame.getFileName() || ''
let lang = 'js'
if(name.indexOf('.vue') !== -1) {
lang = 'html'
}
return lang
}
/**
* Serializes stack to Mustache friendly object to
* be used within the view. Optionally can pass
* a callback to customize the frames output.
*
* @param {Object}
* @param {Function} [callback]
*
* @return {Object}
*/
_serializeData (stack, callback) {
callback = callback || this._serializeFrame.bind(this)
return {
message: this.error.message,
name: this.error.name,
status: this.error.status,
frames: stack instanceof Array === true ? stack.filter((frame) => frame.getFileName()).map(callback) : []
}
}
/**
* Returns a serialized object with important
* information.
*
* @return {Object}
*/
_serializeRequest () {
const headers = []
Object.keys(this.request.headers).forEach((key) => {
if (this._filterHeaders.indexOf(key) > -1) {
return
}
headers.push({
key: key.toUpperCase(),
value: this.request.headers[key]
})
})
const parsedCookies = cookie.parse(this.request.headers.cookie || '')
const cookies = Object.keys(parsedCookies).map((key) => {
return {key, value: parsedCookies[key]}
})
return {
url: this.request.url,
httpVersion: this.request.httpVersion,
method: this.request.method,
connection: this.request.headers.connection,
headers: headers,
cookies: cookies
}
}
/**
* Returns error stack as JSON.
*
* @return {Promise}
*/
toJSON () {
return new Promise((resolve, reject) => {
this
._parseError()
.then(({ stack, hasInternal }) => {
resolve({
error: this._serializeData(stack),
hasInternal
})
})
.catch(reject)
})
}
/**
* Returns HTML representation of the error stack
* by parsing the stack into frames and getting
* important info out of it.
*
* @return {Promise}
*/
toHTML () {
return new Promise((resolve, reject) => {
this
._parseError()
.then(({ stack, hasInternal }) => {
const data = this._serializeData(stack, (frame, index) => {
const serializedFrame = this._serializeFrame(frame)
serializedFrame.classes = this._getDisplayClasses(frame, index)
return serializedFrame
})
const request = this._serializeRequest()
data.request = request
data.hasInternal = hasInternal
resolve(this._compileView(viewTemplate, data))
})
.catch(reject)
})
}
}
module.exports = Youch
File diff suppressed because one or more lines are too long
+1359
View File
File diff suppressed because it is too large Load Diff