express-inspector
Advanced tools
Comparing version 0.2.0 to 0.3.0
{ | ||
"name": "express-inspector", | ||
"version": "0.2.0", | ||
"version": "0.3.0", | ||
"description": "Get a better understanding of your express app", | ||
@@ -8,8 +8,15 @@ "author": "Yacine Hmito <yacine.hmito@gmail.com>", | ||
"main": "index.js", | ||
"bin": { | ||
"express-inspector": "bin/express-inspector" | ||
}, | ||
"files": [ | ||
"bin/express-inspector", | ||
"index.js", | ||
"cli.js", | ||
"src" | ||
], | ||
"dependencies": { | ||
"stack-trace": "^0.0.10" | ||
"pad-end": "^1.0.2", | ||
"stack-trace": "^0.0.10", | ||
"yargs": "^10.1.1" | ||
}, | ||
@@ -20,2 +27,3 @@ "peerDependencies": { | ||
"devDependencies": { | ||
"@types/yargs": "^10.0.1", | ||
"eslint": "^4.15.0", | ||
@@ -25,2 +33,3 @@ "eslint-config-prettier": "^2.9.0", | ||
"eslint-plugin-prettier": "^2.4.0", | ||
"express": "^4.16.2", | ||
"jest": "^22.0.4", | ||
@@ -35,2 +44,3 @@ "prettier": "^1.9.2" | ||
"test": "jest", | ||
"coverage": "jest --coverage", | ||
"lint": "eslint .", | ||
@@ -44,18 +54,3 @@ "prettify": "eslint --fix .", | ||
] | ||
}, | ||
"eslintConfig": { | ||
"root": true, | ||
"parserOptions": { | ||
"ecmaVersion": 2017 | ||
}, | ||
"extends": [ | ||
"eslint:recommended", | ||
"plugin:prettier/recommended", | ||
"plugin:jest/recommended" | ||
], | ||
"env": { | ||
"node": true, | ||
"es6": true | ||
} | ||
} | ||
} |
199
README.md
# express-inspector | ||
[![npm](https://img.shields.io/npm/v/express-inspector.svg)](https://www.npmjs.com/package/express-inspector) [![Build Status](https://travis-ci.org/yacinehmito/express-inspector.svg?branch=master)](https://travis-ci.org/yacinehmito/express-inspector) [![Codacy Grade Badge](https://api.codacy.com/project/badge/Grade/405ea9dabfd744468e7646bd60b70a5c)](https://www.codacy.com/app/yacinehmito/express-inspector?utm_source=github.com&utm_medium=referral&utm_content=yacinehmito/express-inspector&utm_campaign=Badge_Grade) [![Codacy Coverage Badge](https://api.codacy.com/project/badge/Coverage/405ea9dabfd744468e7646bd60b70a5c)](https://www.codacy.com/app/yacinehmito/express-inspector?utm_source=github.com&utm_medium=referral&utm_content=yacinehmito/express-inspector&utm_campaign=Badge_Coverage) [![Greenkeeper badge](https://badges.greenkeeper.io/yacinehmito/express-inspector.svg)](https://greenkeeper.io/) [![License Badge](https://img.shields.io/npm/l/express-inspector.svg)](LICENSE) | ||
The tool express-inspector helps you understand your express app by reporting all its routes and the files where they were defined. | ||
@@ -12,4 +14,2 @@ | ||
```javascript | ||
const inspector = require("express-inspector"); | ||
inspector.trace(); | ||
const express = require("express"); | ||
@@ -23,15 +23,13 @@ | ||
inspector.inspect(app); | ||
app.listen(); | ||
``` | ||
When executing, it will output the following lines: | ||
Run it with `express-inspector run index.js` and it will output: | ||
``` | ||
Route /:name index.js:7:5 | ||
GET / <anonymous> index.js:7:5 | ||
Route /:name index.js:5:5 | ||
GET / <anonymous> index.js:5:5 | ||
``` | ||
Obviously it gets more interesting if you have more complicated setup. | ||
Obviously it gets more interesting if you have a more complicated setup. | ||
@@ -42,4 +40,6 @@ ## Getting Started | ||
The package has only been tested with express 4.15.0. | ||
The package has only been tested with express 4.15.\*. | ||
It is declared as a peer dependency of express-inspector so make sure that you have express installed. | ||
### Installing | ||
@@ -57,9 +57,67 @@ | ||
We recommand **not** to install express-inspector globally as it should use your project's version of express. | ||
### Usage | ||
You first need to enable tracing for the express objects. | ||
To do so, you need to call `trace` before requiring express. | ||
The easiest way to use express-inspector is with the command line interface. | ||
Then you can call `inspect` on an application or on a router. It will output a nice report in your console. | ||
Let's assume that your express server is being run by the file `server.js` in your project's root directory. | ||
In `package.json`, add the following script: | ||
```json | ||
{ | ||
"scripts": { | ||
"start": "express-inspector run server.js" | ||
} | ||
} | ||
``` | ||
Run `yarn start` to start your server. The method `listen` of your express app will output the inspection report when it is called. | ||
## Advanced features | ||
### Inspect an app without starting the server | ||
If you have a file that exports the express application (say `app.js`), you can inspecting from the CLI without starting any server. To do so, add the following script to your `package.json` file: | ||
```json | ||
{ | ||
"scripts": { | ||
"inspect": "express-inspector inspect app.js" | ||
} | ||
} | ||
``` | ||
Now, do `yarn inspect` to output the report. | ||
### Use express-inspector with nodemon | ||
The following script is often used with express projects: | ||
```json | ||
{ | ||
"scripts": { | ||
"dev": "nodemon server.js" | ||
} | ||
} | ||
``` | ||
To inspect the express app every time the server restart, replace it with: | ||
```json | ||
{ | ||
"scripts": { | ||
"dev": "nodemon --exec \"express-inspector run server.js\"" | ||
} | ||
} | ||
``` | ||
Another way would be to inspect the express app programmatically. | ||
### Programmatic inspection with the Node API | ||
You can also output the report directly from your code using the Node API. | ||
To do so, you first need to enable tracing for the express objects by calling the function `trace` before requiring express. Then you can call `inspect` on an application or on a router. It will output the report in your console. | ||
Example: | ||
@@ -81,75 +139,108 @@ | ||
Of course you can disable tracing in production by first checking the value of `process.env.NODE_ENV`. See the file in `examples/standard/index.js` for an example. | ||
Of course you can disable tracing in production by first checking the value of `process.env.NODE_ENV`, as in the example file `examples/full/index.js`. | ||
## Troubleshooting | ||
### Changing the format of the report | ||
### Layer has been instanciated without tracing | ||
The library comes bundled with three formats: `compact` (the default), `flat` and `json`. You can pass any of those to the command line interface with the `format` option. Example: | ||
If get this error it means that tracing wasn't enable. It can be cause by one of tree things: | ||
```json | ||
{ | ||
"scripts": { | ||
"start": "express-inspector --format=flat server.js" | ||
} | ||
} | ||
``` | ||
- The function `trace` has been called *after* express got imported. Make sure that you call the function as soon as possible, ideally at the top of your entry file. | ||
- The object exported by express is not the one that express-inspector is tracing. You may have multiple versions of express used by your dependencies or something else is playing with your module cache. | ||
- You are in an environement where the usual module cache is not being used. It is the case for example for tests run with jest. The circumvent this you need to provide a suitable _replacer_. See the section _How tracing works_ for more info. | ||
If you use the Node API, you can set options by passing an object to the second argument of the function `inspect`. | ||
The `format` key can take as value either any of the three formats or a custom function (see next section). | ||
### Object must be an express app or an express Router | ||
Example: | ||
Either `inspect` or `tree` has been called on an unexpected object. Check that you are indeed passing an express app or an express Router and that those are defined. | ||
```javascript | ||
inspector.inspect(appOrRouter, { | ||
format: "flat" | ||
} | ||
``` | ||
### Module doesn't seem to be compatible with express-inspector | ||
The compact format provides you with all the useful information you may need: how the app is structure and where in your code each router, route and handler has been defined. It ignores the middlewares. | ||
Tracing works by proxying a specific file in the express package. If that file is not found this error is thrown. Maybe you specified the wrong module or are using an unsupported version of express. | ||
The flat format simply lists all the endpoints and where each is defined in your code. | ||
## How tracing works | ||
The json format outputs the whole report tree in JSON so you can then feed it to another program. | ||
When you add a router, a middleware or a simple handler to an app or a router, express will create internally a _layer_. When that layer gets intanciated, the path is given as an argument to the constructor. | ||
### Using a custom format | ||
Tracing works by proxying the Layer constructor. When it is called, we save the path that is being passed as well as the current stack-trace (using the `stack-trace` package). | ||
The option `format` can also be set to a function. Such a function will be provided a report tree as its first argument, which is an object whose internal structure matches the app's or the router's. It should then return a string. | ||
To make express use the proxy, we first load the file that exports the Layer constructor. We then call a _replacer_, which is a function that takes the path of the file and the proxied version of Layer. The replacer that is being used by default replaces the original module in node's module cache by the proxy. That way, when you require express, node will use the Layer constructor from the cache, so the proxy instead of the regular one. | ||
A report tree is equal to its root node. A node has the following structure (as per Flowtype or Typescript type definitions): | ||
This approach has numerous caveats: | ||
```typescript | ||
type Node = { | ||
type: "app" | "router" | "route" | "handler", | ||
path: string, | ||
fullpath: string, | ||
instance: Object, // The express object that the node describes | ||
children?: Array<Node> | ||
trace?: Array<CallSite> // The call sites as captured by `stack-trace` | ||
} | ||
``` | ||
- it relies on behaviours that are very specific to the implementation; as such, any update of express may completely break the package | ||
- it relies on node's caching behaviour; if you are in a specific environment where caching works differently (like a test runner), proxying will not work | ||
- tracing must always be enabled before express is loaded | ||
- if for some reason you have multiple instances of express modules you will not be able to use express-inspector | ||
The JSON that you get by using the format `json` has a similar structure. It has only two meaningful differences: | ||
## Advanced features | ||
- The value of `instance` is replaced by a string that describes the type of the object. | ||
- Call sites are serialized by calling every one of their methods and saving the output into a property named appropriately. | ||
### Changing logger | ||
### Changing how the report is logged | ||
By default, `inspect` will write the report to standard output. If you would rather plug your own logger, you can pass it as an option. | ||
Example when using `console.info` as a logger: | ||
Example when writing the report to standard error: | ||
```javascript | ||
inspector.inspect(app, { | ||
logger: console.info | ||
logger(report) { | ||
process.stderr.write(report); | ||
} | ||
}) | ||
``` | ||
### Changing format | ||
### Using the lower-level API | ||
You can specify your own formatter to `inspect` by setting the format option. | ||
A formatter is a function that takes a report tree as input and outputs a string. | ||
A report tree is an object whose internal structure matches the app's or the router's. | ||
You can build your own `inspect` function with `tree` and `format`. | ||
The package comes bundled with two formatters: `compact` (the default) and `json`. You can also set the format option to one of theses strings. | ||
The function `tree` will build a report tree from an express app or router, whereas `format` is an object whose methods are named according to the three formats (`compact`, `flat` and `json`). Any of those methods can turn a report tree into a string ready to be logged. | ||
Example when we only output the type of the object being inspected: | ||
## Troubleshooting | ||
```javascript | ||
inspector.inspect(appOrRouter, { | ||
format(object) { | ||
return object.type | ||
} | ||
}) | ||
``` | ||
### Layer has been instanciated without tracing | ||
### Using the lower-level API | ||
If you get this error it means that tracing wasn't enable. It can be cause by one of tree things: | ||
You can build your own `inspect` function with `tree`, `format.compact` and/or `format.json`. | ||
- The function `trace` has been called *after* express got imported. Make sure that you call the function as soon as possible, ideally at the top of your entry file. | ||
- The object exported by express is not the one that express-inspector is tracing. You may have multiple versions of express used by your dependencies or something else is playing with your module cache. | ||
- You are in an environement where the usual module cache is not being used. It is the case for example for tests run with jest. To circumvent this you need to provide a suitable _replacer_. See the section _How tracing works_ for more info. | ||
The function `tree` will build a report tree from an express app or router, whereas `format.compact` and `format.json` would take such a tree and turn it into a string. | ||
### Object must be an express app or an express Router | ||
Either `inspect` or `tree` has been called on an unexpected object. Check that you are indeed passing an express app or an express Router and that those are defined. | ||
### Module doesn't seem to be compatible with express-inspector | ||
Tracing works by proxying a specific file in the express package. If that file is not found this error is thrown. Maybe you specified the wrong module or are using an unsupported version of express. | ||
## How tracing works | ||
When you add a router, a middleware or a simple handler to an app or a router, express will create internally a _layer_. When that layer gets intantiated, the path is given as an argument to the constructor. | ||
Tracing works by proxying the Layer constructor. When it is called, we save the path that is being passed as well as the current stack trace (using the `stack-trace` package). | ||
To make express use the proxy, we first load the file that exports the Layer constructor. We then call a _replacer_, which is a function that takes the path of the file and the proxied version of Layer. The replacer that is being used by default replaces the original module in node's module cache by the proxy. That way, when you require express, node will use the Layer constructor from the cache, so the proxy instead of the regular one. | ||
This approach has numerous caveats: | ||
- it relies on behaviours that are very specific to the implementation; as such, any update of express may completely break the package | ||
- it relies on node's caching behaviour; if you are in a specific environment where caching works differently (like a test runner), proxying will not work | ||
- tracing must always be enabled before express is loaded | ||
- if for some reason you have multiple instances of express modules you will not be able to use express-inspector | ||
## Contributing | ||
@@ -173,3 +264,3 @@ | ||
Do `examples/run <name>` where `<name>` is one of the subdirectories of `examples`. | ||
Do `bin/run-example <name>` where `<name>` is one of the subdirectories of `examples`. | ||
@@ -183,3 +274,3 @@ ### Code style | ||
This project is licensed under the MIT License - see the [LICENSE](LICENSE.md) file for details | ||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details | ||
@@ -186,0 +277,0 @@ ## Acknowledgments |
@@ -0,4 +1,12 @@ | ||
if (!String.prototype.padEnd) { | ||
const padEndPolyfill = require("pad-end"); | ||
String.prototype.padEnd = function padEnd(...args) { | ||
return padEndPolyfill(this, ...args); | ||
}; | ||
} | ||
module.exports = { | ||
compact: require("./compact"), | ||
json: require("./json") | ||
json: require("./json"), | ||
flat: require("./flat") | ||
}; |
@@ -12,23 +12,32 @@ const buildTree = require("./tree"); | ||
let formatter; | ||
if (typeof opts.format === "function") { | ||
formatter = opts.format; | ||
} else if (typeof opts.format === "string") { | ||
if (!format[opts.format]) { | ||
throw new Error(`express-inspector: format ${opts.format} unknown`); | ||
const formatter = processFormatOption(opts.format); | ||
const logger = processLoggerOption(opts.logger); | ||
const reportTree = buildTree(root); | ||
const formattedReport = formatter(reportTree); | ||
logger(formattedReport); | ||
} | ||
function processFormatOption(formatOption) { | ||
if (typeof formatOption === "function") { | ||
return formatOption; | ||
} | ||
if (typeof formatOption === "string") { | ||
if (!format[formatOption]) { | ||
throw new Error(`express-inspector: format ${formatOption} unknown`); | ||
} | ||
formatter = format[opts.format]; | ||
} else { | ||
throw new TypeError( | ||
"express-inspector: format must either be a function or a string" | ||
); | ||
return format[formatOption]; | ||
} | ||
throw new TypeError( | ||
"express-inspector: format must either be a function or a string" | ||
); | ||
} | ||
if (typeof opts.logger !== "function") { | ||
function processLoggerOption(loggerOption) { | ||
if (typeof loggerOption !== "function") { | ||
throw new TypeError("express-inspector: logger must be a function"); | ||
} | ||
opts.logger(formatter(buildTree(root))); | ||
return loggerOption; | ||
} | ||
module.exports = inspect; |
@@ -11,2 +11,5 @@ const StackTrace = require("stack-trace"); | ||
// and if it is not inside express | ||
if (callSite.isNative()) { | ||
return false; | ||
} | ||
const fileName = callSite.getFileName(); | ||
@@ -13,0 +16,0 @@ return fileName && !/node_modules\/express\//.test(fileName); |
@@ -15,6 +15,16 @@ function capitalize(string) { | ||
function flatMap(array, callback) { | ||
const output = []; | ||
const { length } = array; | ||
for (let i = 0; i < length; i++) { | ||
output.push(...callback(array[i], i, array)); | ||
} | ||
return output; | ||
} | ||
module.exports = { | ||
capitalize, | ||
takeUntil, | ||
dropUntil | ||
dropUntil, | ||
flatMap | ||
}; |
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
27097
17
460
273
4
8
+ Addedpad-end@^1.0.2
+ Addedyargs@^10.1.1
+ Addedansi-regex@2.1.13.0.1(transitive)
+ Addedcamelcase@4.1.0(transitive)
+ Addedcliui@4.1.0(transitive)
+ Addedcode-point-at@1.1.0(transitive)
+ Addedcross-spawn@5.1.0(transitive)
+ Addeddecamelize@1.2.0(transitive)
+ Addedexeca@0.7.0(transitive)
+ Addedfind-up@2.1.0(transitive)
+ Addedget-caller-file@1.0.3(transitive)
+ Addedget-stream@3.0.0(transitive)
+ Addedinvert-kv@1.0.0(transitive)
+ Addedis-fullwidth-code-point@1.0.02.0.0(transitive)
+ Addedis-stream@1.1.0(transitive)
+ Addedisexe@2.0.0(transitive)
+ Addedlcid@1.0.0(transitive)
+ Addedlocate-path@2.0.0(transitive)
+ Addedlru-cache@4.1.5(transitive)
+ Addedmem@1.1.0(transitive)
+ Addedmimic-fn@1.2.0(transitive)
+ Addednpm-run-path@2.0.2(transitive)
+ Addednumber-is-nan@1.0.1(transitive)
+ Addedos-locale@2.1.0(transitive)
+ Addedp-finally@1.0.0(transitive)
+ Addedp-limit@1.3.0(transitive)
+ Addedp-locate@2.0.0(transitive)
+ Addedp-try@1.0.0(transitive)
+ Addedpad-end@1.0.2(transitive)
+ Addedpath-exists@3.0.0(transitive)
+ Addedpath-key@2.0.1(transitive)
+ Addedpseudomap@1.0.2(transitive)
+ Addedrequire-directory@2.1.1(transitive)
+ Addedrequire-main-filename@1.0.1(transitive)
+ Addedset-blocking@2.0.0(transitive)
+ Addedshebang-command@1.2.0(transitive)
+ Addedshebang-regex@1.0.0(transitive)
+ Addedsignal-exit@3.0.7(transitive)
+ Addedstring-width@1.0.22.1.1(transitive)
+ Addedstrip-ansi@3.0.14.0.0(transitive)
+ Addedstrip-eof@1.0.0(transitive)
+ Addedwhich@1.3.1(transitive)
+ Addedwhich-module@2.0.1(transitive)
+ Addedwrap-ansi@2.1.0(transitive)
+ Addedy18n@3.2.2(transitive)
+ Addedyallist@2.1.2(transitive)
+ Addedyargs@10.1.2(transitive)
+ Addedyargs-parser@8.1.0(transitive)