@contrast/route-coverage
Advanced tools
Comparing version 1.6.0 to 1.7.0
@@ -28,3 +28,3 @@ /* | ||
discoveryFinished(): void; | ||
observe(info: Pick<RouteInfo, 'method' | 'url'>): void; | ||
observe(info: Pick<RouteInfo>): void; | ||
} | ||
@@ -31,0 +31,0 @@ |
@@ -49,3 +49,3 @@ /* | ||
observe(info) { | ||
const route = routeInfo.get(routeIdentifier(info)); | ||
const route = info.signature ? info : routeInfo.get(routeIdentifier(info)); | ||
@@ -52,0 +52,0 @@ if (!route) { |
@@ -28,5 +28,6 @@ /* | ||
]; | ||
// eslint-disable-next-line node/no-extraneous-require | ||
const fnInspect = require('@contrast/fn-inspect'); | ||
const { createSignature, patchType } = require('../utils/route-info'); | ||
const { createSignature, patchType } = require('./../utils/route-info'); | ||
/** | ||
@@ -41,23 +42,77 @@ * @param {import('..').Core & { | ||
const routers = new Map(); | ||
const signatureMap = new Map(); | ||
const { patcher, depHooks, routeCoverage } = core; | ||
function emitRouteCoverage(router, url, method) { | ||
const event = { signature: createSignature(url, method), url, method }; | ||
const layers = routers.get(router) || []; | ||
layers.push(event); | ||
routers.set(router, layers); | ||
routeCoverage.discover(event); | ||
function formatUrl(url) { | ||
if (Array.isArray(url)) { | ||
return `/[${url.join(', ')}]`; | ||
} else if (url instanceof RegExp) { | ||
return `/{${url.toString().replace(/(^\/?)|(\/?$)/g, '')}}`; | ||
} else { | ||
return url; | ||
} | ||
} | ||
function handleRouteDiscovery(data, method) { | ||
const [url, fn] = data.args; | ||
if (!url || !fn) { | ||
return; | ||
function getLastLayer(router) { | ||
if (router.stack) { | ||
const len = router.stack.length; | ||
return router.stack[len - 1]; | ||
} | ||
if (Array.isArray(url)) { | ||
url.forEach((path) => { | ||
emitRouteCoverage(data.result, path, method); | ||
} | ||
function getLayerHandleMethod(layer) { | ||
let methodName = 'handle'; | ||
const __handleData = fnInspect.funcInfo(layer.__handle); | ||
if (__handleData && __handleData.file.includes('express-async-errors')) { | ||
methodName = '__handle'; | ||
} | ||
return methodName; | ||
} | ||
function patchHandler(layer, route) { | ||
if (!layer) return; | ||
const handle = getLayerHandleMethod(layer); | ||
patcher.patch(layer, handle, { | ||
name: 'express.Router.handle', | ||
patchType, | ||
post(data) { | ||
const [req] = data.args; | ||
const method = req?.method?.toLowerCase(); | ||
const url = `${req.baseUrl}${req._parsedUrl.pathname}`; | ||
const { signature } = signatureMap.get(route.signature); | ||
if (method) routeCoverage.observe({ signature, url, method }); | ||
} | ||
}); | ||
} | ||
function createRoute(url, method, id) { | ||
const signature = createSignature(url, method); | ||
const route = { signature, url, method, id: id || signature }; | ||
signatureMap.set(signature, route); | ||
return route; | ||
} | ||
function discoverRoute({ signature, url, method }) { | ||
routeCoverage.discover({ signature, url, method }); | ||
} | ||
function instrumentRoute(router, route) { | ||
if (!router) return; | ||
const layer = getLastLayer(router); | ||
patchHandler(layer, route); | ||
} | ||
function updateRoutes(prefix, router, updatedRouter) { | ||
const routes = routers.get(router); | ||
const updatedLayers = router === updatedRouter | ||
? [] : (routers.get(updatedRouter) || []); | ||
if (routes) { | ||
routes.forEach((route) => { | ||
const { url, method, id } = route; | ||
const newRoute = createRoute(`${prefix}${url}`, method, id); | ||
updatedLayers.push(newRoute); | ||
signatureMap.set(id, newRoute); | ||
}); | ||
} else { | ||
emitRouteCoverage(data.result, url, method); | ||
routers.set(router, updatedLayers); | ||
routers.set(updatedRouter, updatedLayers); | ||
} | ||
@@ -71,13 +126,2 @@ } | ||
(express) => { | ||
patcher.patch(express.Router, 'handle', { | ||
name: 'express.Router.handle', | ||
patchType, | ||
post(data) { | ||
// TODO: Can this handle all route observation? | ||
const [req] = data.args; | ||
const method = req?.method?.toLowerCase(); | ||
const url = `${req.baseUrl}${req._parsedUrl.pathname}`; | ||
if (method) routeCoverage.observe({ url, method }); | ||
} | ||
}); | ||
@@ -89,15 +133,19 @@ patcher.patch(express.Router, 'use', { | ||
const [prefix, router] = args; | ||
const layers = routers.get(router); | ||
const updatedLayers = []; | ||
if (layers) { | ||
layers.forEach((layer) => { | ||
const { url, method } = layer; | ||
const updatedUrl = `${prefix}${url}`; | ||
const event = { signature: createSignature(updatedUrl, method), url: updatedUrl, method }; | ||
updatedLayers.push(event); | ||
routeCoverage.delete(layer); | ||
routeCoverage.discover(event); | ||
if (typeof prefix === 'string' && prefix !== '/') { | ||
updateRoutes(prefix, router, result); | ||
} | ||
} | ||
}); | ||
patcher.patch(express.application, 'use', { | ||
name: 'express.application.use', | ||
patchType, | ||
post({ args }) { | ||
const idx = args.length; | ||
const router = args[idx - 1]; | ||
const routes = routers.get(router); | ||
if (routes) { | ||
routes.forEach((route) => { | ||
discoverRoute(route); | ||
}); | ||
routers.delete(router); | ||
routers.set(result, updatedLayers); | ||
} | ||
@@ -111,4 +159,8 @@ } | ||
patchType, | ||
post(data) { | ||
handleRouteDiscovery(data, method); | ||
post({ args, result }) { | ||
const [url, fn] = args; | ||
if (!url || !fn) return; | ||
const route = createRoute(formatUrl(url), method); | ||
instrumentRoute(result?._router, route); | ||
discoverRoute(route); | ||
} | ||
@@ -120,4 +172,11 @@ }); | ||
patchType, | ||
post(data) { | ||
handleRouteDiscovery(data, method); | ||
post({ args, obj: router }) { | ||
const [url, fn] = args; | ||
if (!url || !fn) return; | ||
const route = createRoute(formatUrl(url), method); | ||
const routes = routers.get(router) || []; | ||
instrumentRoute(router, route); | ||
routes.push(route); | ||
routers.set(router, routes); | ||
} | ||
@@ -124,0 +183,0 @@ }); |
{ | ||
"name": "@contrast/route-coverage", | ||
"version": "1.6.0", | ||
"version": "1.7.0", | ||
"description": "", | ||
@@ -17,4 +17,4 @@ "license": "SEE LICENSE IN LICENSE", | ||
"dependencies": { | ||
"@contrast/common": "1.10.0" | ||
"@contrast/common": "1.11.0" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
17132
470
+ Added@contrast/common@1.11.0(transitive)
- Removed@contrast/common@1.10.0(transitive)
Updated@contrast/common@1.11.0