From 57781847c18cf99e11aa18c9fc114a8cef7de231 Mon Sep 17 00:00:00 2001 From: Hari Krishnan Date: Tue, 26 Nov 2024 13:24:02 +0530 Subject: [PATCH 1/2] Inlining express-list-endpoints dependency --- package-lock.json | 19 -- package.json | 2 - src/core/index.ts | 2 +- src/lib/express-list-endpoints/index.d.ts | 29 +++ src/lib/express-list-endpoints/index.js | 241 ++++++++++++++++++++++ 5 files changed, 271 insertions(+), 22 deletions(-) create mode 100644 src/lib/express-list-endpoints/index.d.ts create mode 100644 src/lib/express-list-endpoints/index.js diff --git a/package-lock.json b/package-lock.json index d8e33a9..80a082e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,6 @@ "license": "ISC", "dependencies": { "axios": "^1.7.7", - "express-list-endpoints": "github:znsio/express-list-endpoints#21c92d15159abffd772acb27027ab84fcec1a6ac", "fast-xml-parser": "^4.5.0", "terminate": "^2.8.0", "tree-kill": "^1.2.2", @@ -26,7 +25,6 @@ "@babel/preset-env": "^7.25.4", "@babel/preset-typescript": "^7.24.7", "@types/express": "^5.0.0", - "@types/express-list-endpoints": "github:znsio/express-list-endpoints#21c92d15159abffd772acb27027ab84fcec1a6ac", "@types/jest": "^29.5.13", "@types/jest-when": "^3.5.5", "@types/node": "^22.7.2", @@ -2812,16 +2810,6 @@ "@types/serve-static": "*" } }, - "node_modules/@types/express-list-endpoints": { - "name": "express-list-endpoints", - "version": "7.1.0", - "resolved": "git+ssh://git@github.com/znsio/express-list-endpoints.git#21c92d15159abffd772acb27027ab84fcec1a6ac", - "integrity": "sha512-tWnPmyjUHz5Fn077xkRlvWGypAUhpOO62d4Fpx5REXIQp6hRKGWkqEsMfXhScW4GwcMkTMalmq8cvH+j/Vlb+A==", - "dev": true, - "engines": { - "node": ">=18" - } - }, "node_modules/@types/express-serve-static-core": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.0.tgz", @@ -4211,13 +4199,6 @@ "node": ">= 0.10.0" } }, - "node_modules/express-list-endpoints": { - "version": "7.1.0", - "resolved": "git+ssh://git@github.com/znsio/express-list-endpoints.git#21c92d15159abffd772acb27027ab84fcec1a6ac", - "engines": { - "node": ">=18" - } - }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", diff --git a/package.json b/package.json index 025c728..7fb286e 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,6 @@ }, "dependencies": { "axios": "^1.7.7", - "express-list-endpoints": "github:znsio/express-list-endpoints#21c92d15159abffd772acb27027ab84fcec1a6ac", "fast-xml-parser": "^4.5.0", "terminate": "^2.8.0", "tree-kill": "^1.2.2", @@ -61,7 +60,6 @@ "@babel/preset-env": "^7.25.4", "@babel/preset-typescript": "^7.24.7", "@types/express": "^5.0.0", - "@types/express-list-endpoints": "github:znsio/express-list-endpoints#21c92d15159abffd772acb27027ab84fcec1a6ac", "@types/jest": "^29.5.13", "@types/jest-when": "^3.5.5", "@types/node": "^22.7.2", diff --git a/src/core/index.ts b/src/core/index.ts index d22db78..20e094c 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -5,7 +5,7 @@ import { XMLParser } from 'fast-xml-parser' import fs from 'fs' import logger from '../common/logger' import { callCore } from '../common/runner' -import listExpressEndpoints from 'express-list-endpoints' +import listExpressEndpoints from '../lib/express-list-endpoints'; import http from 'http' import { AddressInfo } from 'net' import { gracefulShutdown } from './shutdownUtils' diff --git a/src/lib/express-list-endpoints/index.d.ts b/src/lib/express-list-endpoints/index.d.ts new file mode 100644 index 0000000..bbf0346 --- /dev/null +++ b/src/lib/express-list-endpoints/index.d.ts @@ -0,0 +1,29 @@ +export = expressListEndpoints; +/** + * Returns an array of strings with all the detected endpoints + * @param {import('express').Express | import('express').Router | any} app The express/router instance to get the endpoints from + * @returns {Endpoint[]} + */ +declare function expressListEndpoints(app: import('express').Express | import('express').Router | any): Endpoint[]; +declare namespace expressListEndpoints { + export { Route, Endpoint }; +} +type Endpoint = { + /** + * Path name + */ + path: string; + /** + * Methods handled + */ + methods: string[]; + /** + * Mounted middlewares + */ + middlewares: string[]; +}; +type Route = { + methods: any; + path: string | string[]; + stack: any[]; +}; diff --git a/src/lib/express-list-endpoints/index.js b/src/lib/express-list-endpoints/index.js new file mode 100644 index 0000000..c421189 --- /dev/null +++ b/src/lib/express-list-endpoints/index.js @@ -0,0 +1,241 @@ +/** + * @typedef {Object} Route + * @property {Object} methods + * @property {string | string[]} path + * @property {any[]} stack + * + * @typedef {Object} Endpoint + * @property {string} path Path name + * @property {string[]} methods Methods handled + * @property {string[]} middlewares Mounted middlewares + */ + +const regExpToParseExpressPathRegExp = /^\/\^\\?\/?(?:(:?[\w\\.-]*(?:\\\/:?[\w\\.-]*)*)|(\(\?:\\?\/?\([^)]+\)\)))\\\/.*/ +const regExpToReplaceExpressPathRegExpParams = /\(\?:\\?\/?\([^)]+\)\)/ +const regexpExpressParamRegexp = /\(\?:\\?\\?\/?\([^)]+\)\)/g +const regexpExpressPathParamRegexp = /(:[^)]+)\([^)]+\)/g + +const EXPRESS_ROOT_PATH_REGEXP_VALUE = '/^\\/?(?=\\/|$)/i' +const STACK_ITEM_VALID_NAMES = [ + 'router', + 'bound dispatch', + 'mounted_app' +] + +/** + * Returns all the verbs detected for the passed route + * @param {Route} route + */ +const getRouteMethods = function (route) { + let methods = Object.keys(route.methods) + + methods = methods.filter((method) => method !== '_all') + methods = methods.map((method) => method.toUpperCase()) + + return methods +} + +/** + * Returns the names (or anonymous) of all the middlewares attached to the + * passed route + * @param {Route} route + * @returns {string[]} + */ +const getRouteMiddlewares = function (route) { + return route.stack.map((item) => { + return item.handle.name || 'anonymous' + }) +} + +/** + * Returns true if found regexp related with express params + * @param {string} expressPathRegExp + * @returns {boolean} + */ +const hasParams = function (expressPathRegExp) { + return regexpExpressParamRegexp.test(expressPathRegExp) +} + +/** + * Combine base and path into a single path. Squashes multiple slashes and + * removes trailing slash (except root "/") + * @param {string} base The base path + * @param {string} path The path to combine with the base + * @returns {string} The combined path + */ +function combinePaths(base, path) { + return (base + path).replace(/\/+/g, "/").replace(/\/$/, "") || "/"; +} + +/** + * @param {Route} route Express route object to be parsed + * @param {string} basePath The basePath the route is on + * @return {Endpoint[]} Endpoints info + */ +const parseExpressRoute = function (route, basePath) { + const paths = [] + + if (Array.isArray(route.path)) { + paths.push(...route.path) + } else { + paths.push(route.path) + } + + /** @type {Endpoint[]} */ + const endpoints = paths.map((path) => { + const completePath = combinePaths(basePath, path); + + /** @type {Endpoint} */ + const endpoint = { + path: completePath.replace(regexpExpressPathParamRegexp, '$1'), + methods: getRouteMethods(route), + middlewares: getRouteMiddlewares(route) + } + + return endpoint + }) + + return endpoints +} + +/** + * @param {RegExp} expressPathRegExp + * @param {any[]} params + * @returns {string} + */ +const parseExpressPath = function (expressPathRegExp, params) { + let parsedRegExp = expressPathRegExp.toString() + let expressPathRegExpExec = regExpToParseExpressPathRegExp.exec(parsedRegExp) + let paramIndex = 0 + + while (hasParams(parsedRegExp)) { + const paramName = params[paramIndex].name + const paramId = `:${paramName}` + + parsedRegExp = parsedRegExp + .replace(regExpToReplaceExpressPathRegExpParams, (str) => { + // Express >= 4.20.0 uses a different RegExp for parameters: it + // captures the slash as part of the parameter. We need to check + // for this case and add the slash to the value that will replace + // the parameter in the path. + if (str.startsWith('(?:\\/')) { + return `\\/${paramId}` + } + + return paramId + }) + + paramIndex++ + } + + if (parsedRegExp !== expressPathRegExp.toString()) { + expressPathRegExpExec = regExpToParseExpressPathRegExp.exec(parsedRegExp) + } + + const parsedPath = expressPathRegExpExec[1].replace(/\\\//g, '/') + + return parsedPath +} + +/** + * @param {import('express').Express | import('express').Router | any} app + * @param {string} [basePath] + * @param {Endpoint[]} [endpoints] + * @returns {Endpoint[]} + */ +const parseEndpoints = function (app, basePath, endpoints) { + const stack = app.stack || (app._router && app._router.stack) + + endpoints = endpoints || [] + basePath = basePath || '' + + if (!stack) { + if (endpoints.length) { + endpoints = addEndpoints(endpoints, [{ + path: basePath, + methods: [], + middlewares: [] + }]) + } + } else { + endpoints = parseStack(stack, basePath, endpoints) + } + + return endpoints +} + +/** + * Ensures the path of the new endpoints isn't yet in the array. + * If the path is already in the array merges the endpoints with the existing + * one, if not, it adds them to the array. + * + * @param {Endpoint[]} currentEndpoints Array of current endpoints + * @param {Endpoint[]} endpointsToAdd New endpoints to be added to the array + * @returns {Endpoint[]} Updated endpoints array + */ +const addEndpoints = function (currentEndpoints, endpointsToAdd) { + endpointsToAdd.forEach((newEndpoint) => { + const existingEndpoint = currentEndpoints.find( + (endpoint) => endpoint.path === newEndpoint.path + ) + + if (existingEndpoint !== undefined) { + const newMethods = newEndpoint.methods.filter( + (method) => !existingEndpoint.methods.includes(method) + ) + + existingEndpoint.methods = existingEndpoint.methods.concat(newMethods) + } else { + currentEndpoints.push(newEndpoint) + } + }) + + return currentEndpoints +} + +/** + * @param {any[]} stack + * @param {string} basePath + * @param {Endpoint[]} endpoints + * @returns {Endpoint[]} + */ +const parseStack = function (stack, basePath, endpoints) { + stack.forEach((stackItem) => { + if (stackItem.route) { + const newEndpoints = parseExpressRoute(stackItem.route, basePath) + + endpoints = addEndpoints(endpoints, newEndpoints) + } else if (STACK_ITEM_VALID_NAMES.includes(stackItem.name)) { + const isExpressPathRegexp = regExpToParseExpressPathRegExp.test(stackItem.regexp) + + let newBasePath = basePath + + if (isExpressPathRegexp) { + const parsedPath = parseExpressPath(stackItem.regexp, stackItem.keys) + + newBasePath += `/${parsedPath}` + } else if (!stackItem.path && stackItem.regexp && stackItem.regexp.toString() !== EXPRESS_ROOT_PATH_REGEXP_VALUE) { + const regExpPath = ` RegExp(${stackItem.regexp}) ` + + newBasePath += `/${regExpPath}` + } + + endpoints = parseEndpoints(stackItem.handle, newBasePath, endpoints) + } + }) + + return endpoints +} + +/** + * Returns an array of strings with all the detected endpoints + * @param {import('express').Express | import('express').Router | any} app The express/router instance to get the endpoints from + * @returns {Endpoint[]} + */ +const expressListEndpoints = function (app) { + const endpoints = parseEndpoints(app) + + return endpoints +} + +module.exports = expressListEndpoints From 5a57862a50f61db1af581839a4b4afdb4aacef80 Mon Sep 17 00:00:00 2001 From: Hari Krishnan Date: Tue, 26 Nov 2024 14:39:03 +0530 Subject: [PATCH 2/2] Changes to copy index.js to dist folder --- package.json | 2 +- src/lib/express-list-endpoints/index.d.ts | 16 +- src/lib/express-list-endpoints/index.js | 341 ++++++++-------------- 3 files changed, 116 insertions(+), 243 deletions(-) diff --git a/package.json b/package.json index 7fb286e..6799203 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "type-check:watch": "npm run type-check -- --watch", "build": "npm test && rimraf dist && npm run build:types && npm run build:js", "build:types": "tsc --emitDeclarationOnly", - "build:js": "babel src --out-dir dist --ignore 'src/**/__tests__/**/*.ts' --extensions \".ts,.tsx\" --source-maps inline", + "build:js": "babel src --out-dir dist --ignore 'src/**/__tests__/**/*.ts' --extensions \".ts,.js,.tsx\" --source-maps inline", "test": "rimraf coverage && jest --coverage", "prepack": "node src/downloadSpecmaticJar.js", "prepare": "npm run prepack && npm run build:types && npm run build:js" diff --git a/src/lib/express-list-endpoints/index.d.ts b/src/lib/express-list-endpoints/index.d.ts index bbf0346..7928cd7 100644 --- a/src/lib/express-list-endpoints/index.d.ts +++ b/src/lib/express-list-endpoints/index.d.ts @@ -1,29 +1,15 @@ export = expressListEndpoints; -/** - * Returns an array of strings with all the detected endpoints - * @param {import('express').Express | import('express').Router | any} app The express/router instance to get the endpoints from - * @returns {Endpoint[]} - */ declare function expressListEndpoints(app: import('express').Express | import('express').Router | any): Endpoint[]; declare namespace expressListEndpoints { export { Route, Endpoint }; } type Endpoint = { - /** - * Path name - */ path: string; - /** - * Methods handled - */ methods: string[]; - /** - * Mounted middlewares - */ middlewares: string[]; }; type Route = { - methods: any; + methods: Object; path: string | string[]; stack: any[]; }; diff --git a/src/lib/express-list-endpoints/index.js b/src/lib/express-list-endpoints/index.js index c421189..474502a 100644 --- a/src/lib/express-list-endpoints/index.js +++ b/src/lib/express-list-endpoints/index.js @@ -1,241 +1,128 @@ -/** - * @typedef {Object} Route - * @property {Object} methods - * @property {string | string[]} path - * @property {any[]} stack - * - * @typedef {Object} Endpoint - * @property {string} path Path name - * @property {string[]} methods Methods handled - * @property {string[]} middlewares Mounted middlewares - */ - -const regExpToParseExpressPathRegExp = /^\/\^\\?\/?(?:(:?[\w\\.-]*(?:\\\/:?[\w\\.-]*)*)|(\(\?:\\?\/?\([^)]+\)\)))\\\/.*/ -const regExpToReplaceExpressPathRegExpParams = /\(\?:\\?\/?\([^)]+\)\)/ -const regexpExpressParamRegexp = /\(\?:\\?\\?\/?\([^)]+\)\)/g -const regexpExpressPathParamRegexp = /(:[^)]+)\([^)]+\)/g - -const EXPRESS_ROOT_PATH_REGEXP_VALUE = '/^\\/?(?=\\/|$)/i' +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const regExpToParseExpressPathRegExp = /^\/\^\\?\/?(?:(:?[\w\\.-]*(?:\\\/:?[\w\\.-]*)*)|(\(\?:\\?\/?\([^)]+\)\)))\\\/.*/; +const regExpToReplaceExpressPathRegExpParams = /\(\?:\\?\/?\([^)]+\)\)/; +const regexpExpressParamRegexp = /\(\?:\\?\\?\/?\([^)]+\)\)/g; +const regexpExpressPathParamRegexp = /(:[^)]+)\([^)]+\)/g; +const EXPRESS_ROOT_PATH_REGEXP_VALUE = '/^\\/?(?=\\/|$)/i'; const STACK_ITEM_VALID_NAMES = [ - 'router', - 'bound dispatch', - 'mounted_app' -] - -/** - * Returns all the verbs detected for the passed route - * @param {Route} route - */ + 'router', + 'bound dispatch', + 'mounted_app' +]; const getRouteMethods = function (route) { - let methods = Object.keys(route.methods) - - methods = methods.filter((method) => method !== '_all') - methods = methods.map((method) => method.toUpperCase()) - - return methods -} - -/** - * Returns the names (or anonymous) of all the middlewares attached to the - * passed route - * @param {Route} route - * @returns {string[]} - */ + let methods = Object.keys(route.methods); + methods = methods.filter((method) => method !== '_all'); + methods = methods.map((method) => method.toUpperCase()); + return methods; +}; const getRouteMiddlewares = function (route) { - return route.stack.map((item) => { - return item.handle.name || 'anonymous' - }) -} - -/** - * Returns true if found regexp related with express params - * @param {string} expressPathRegExp - * @returns {boolean} - */ + return route.stack.map((item) => { + return item.handle.name || 'anonymous'; + }); +}; const hasParams = function (expressPathRegExp) { - return regexpExpressParamRegexp.test(expressPathRegExp) -} - -/** - * Combine base and path into a single path. Squashes multiple slashes and - * removes trailing slash (except root "/") - * @param {string} base The base path - * @param {string} path The path to combine with the base - * @returns {string} The combined path - */ + return regexpExpressParamRegexp.test(expressPathRegExp); +}; function combinePaths(base, path) { - return (base + path).replace(/\/+/g, "/").replace(/\/$/, "") || "/"; + return (base + path).replace(/\/+/g, "/").replace(/\/$/, "") || "/"; } - -/** - * @param {Route} route Express route object to be parsed - * @param {string} basePath The basePath the route is on - * @return {Endpoint[]} Endpoints info - */ const parseExpressRoute = function (route, basePath) { - const paths = [] - - if (Array.isArray(route.path)) { - paths.push(...route.path) - } else { - paths.push(route.path) - } - - /** @type {Endpoint[]} */ - const endpoints = paths.map((path) => { - const completePath = combinePaths(basePath, path); - - /** @type {Endpoint} */ - const endpoint = { - path: completePath.replace(regexpExpressPathParamRegexp, '$1'), - methods: getRouteMethods(route), - middlewares: getRouteMiddlewares(route) + const paths = []; + if (Array.isArray(route.path)) { + paths.push(...route.path); } - - return endpoint - }) - - return endpoints -} - -/** - * @param {RegExp} expressPathRegExp - * @param {any[]} params - * @returns {string} - */ + else { + paths.push(route.path); + } + const endpoints = paths.map((path) => { + const completePath = combinePaths(basePath, path); + const endpoint = { + path: completePath.replace(regexpExpressPathParamRegexp, '$1'), + methods: getRouteMethods(route), + middlewares: getRouteMiddlewares(route) + }; + return endpoint; + }); + return endpoints; +}; const parseExpressPath = function (expressPathRegExp, params) { - let parsedRegExp = expressPathRegExp.toString() - let expressPathRegExpExec = regExpToParseExpressPathRegExp.exec(parsedRegExp) - let paramIndex = 0 - - while (hasParams(parsedRegExp)) { - const paramName = params[paramIndex].name - const paramId = `:${paramName}` - - parsedRegExp = parsedRegExp - .replace(regExpToReplaceExpressPathRegExpParams, (str) => { - // Express >= 4.20.0 uses a different RegExp for parameters: it - // captures the slash as part of the parameter. We need to check - // for this case and add the slash to the value that will replace - // the parameter in the path. - if (str.startsWith('(?:\\/')) { - return `\\/${paramId}` - } - - return paramId - }) - - paramIndex++ - } - - if (parsedRegExp !== expressPathRegExp.toString()) { - expressPathRegExpExec = regExpToParseExpressPathRegExp.exec(parsedRegExp) - } - - const parsedPath = expressPathRegExpExec[1].replace(/\\\//g, '/') - - return parsedPath -} - -/** - * @param {import('express').Express | import('express').Router | any} app - * @param {string} [basePath] - * @param {Endpoint[]} [endpoints] - * @returns {Endpoint[]} - */ + let parsedRegExp = expressPathRegExp.toString(); + let expressPathRegExpExec = regExpToParseExpressPathRegExp.exec(parsedRegExp); + let paramIndex = 0; + while (hasParams(parsedRegExp)) { + const paramName = params[paramIndex].name; + const paramId = `:${paramName}`; + parsedRegExp = parsedRegExp + .replace(regExpToReplaceExpressPathRegExpParams, (str) => { + if (str.startsWith('(?:\\/')) { + return `\\/${paramId}`; + } + return paramId; + }); + paramIndex++; + } + if (parsedRegExp !== expressPathRegExp.toString()) { + expressPathRegExpExec = regExpToParseExpressPathRegExp.exec(parsedRegExp); + } + const parsedPath = expressPathRegExpExec[1].replace(/\\\//g, '/'); + return parsedPath; +}; const parseEndpoints = function (app, basePath, endpoints) { - const stack = app.stack || (app._router && app._router.stack) - - endpoints = endpoints || [] - basePath = basePath || '' - - if (!stack) { - if (endpoints.length) { - endpoints = addEndpoints(endpoints, [{ - path: basePath, - methods: [], - middlewares: [] - }]) + const stack = app.stack || (app._router && app._router.stack); + endpoints = endpoints || []; + basePath = basePath || ''; + if (!stack) { + if (endpoints.length) { + endpoints = addEndpoints(endpoints, [{ + path: basePath, + methods: [], + middlewares: [] + }]); + } } - } else { - endpoints = parseStack(stack, basePath, endpoints) - } - - return endpoints -} - -/** - * Ensures the path of the new endpoints isn't yet in the array. - * If the path is already in the array merges the endpoints with the existing - * one, if not, it adds them to the array. - * - * @param {Endpoint[]} currentEndpoints Array of current endpoints - * @param {Endpoint[]} endpointsToAdd New endpoints to be added to the array - * @returns {Endpoint[]} Updated endpoints array - */ -const addEndpoints = function (currentEndpoints, endpointsToAdd) { - endpointsToAdd.forEach((newEndpoint) => { - const existingEndpoint = currentEndpoints.find( - (endpoint) => endpoint.path === newEndpoint.path - ) - - if (existingEndpoint !== undefined) { - const newMethods = newEndpoint.methods.filter( - (method) => !existingEndpoint.methods.includes(method) - ) - - existingEndpoint.methods = existingEndpoint.methods.concat(newMethods) - } else { - currentEndpoints.push(newEndpoint) + else { + endpoints = parseStack(stack, basePath, endpoints); } - }) - - return currentEndpoints -} - -/** - * @param {any[]} stack - * @param {string} basePath - * @param {Endpoint[]} endpoints - * @returns {Endpoint[]} - */ + return endpoints; +}; +const addEndpoints = function (currentEndpoints, endpointsToAdd) { + endpointsToAdd.forEach((newEndpoint) => { + const existingEndpoint = currentEndpoints.find((endpoint) => endpoint.path === newEndpoint.path); + if (existingEndpoint !== undefined) { + const newMethods = newEndpoint.methods.filter((method) => !existingEndpoint.methods.includes(method)); + existingEndpoint.methods = existingEndpoint.methods.concat(newMethods); + } + else { + currentEndpoints.push(newEndpoint); + } + }); + return currentEndpoints; +}; const parseStack = function (stack, basePath, endpoints) { - stack.forEach((stackItem) => { - if (stackItem.route) { - const newEndpoints = parseExpressRoute(stackItem.route, basePath) - - endpoints = addEndpoints(endpoints, newEndpoints) - } else if (STACK_ITEM_VALID_NAMES.includes(stackItem.name)) { - const isExpressPathRegexp = regExpToParseExpressPathRegExp.test(stackItem.regexp) - - let newBasePath = basePath - - if (isExpressPathRegexp) { - const parsedPath = parseExpressPath(stackItem.regexp, stackItem.keys) - - newBasePath += `/${parsedPath}` - } else if (!stackItem.path && stackItem.regexp && stackItem.regexp.toString() !== EXPRESS_ROOT_PATH_REGEXP_VALUE) { - const regExpPath = ` RegExp(${stackItem.regexp}) ` - - newBasePath += `/${regExpPath}` - } - - endpoints = parseEndpoints(stackItem.handle, newBasePath, endpoints) - } - }) - - return endpoints -} - -/** - * Returns an array of strings with all the detected endpoints - * @param {import('express').Express | import('express').Router | any} app The express/router instance to get the endpoints from - * @returns {Endpoint[]} - */ + stack.forEach((stackItem) => { + if (stackItem.route) { + const newEndpoints = parseExpressRoute(stackItem.route, basePath); + endpoints = addEndpoints(endpoints, newEndpoints); + } + else if (STACK_ITEM_VALID_NAMES.includes(stackItem.name)) { + const isExpressPathRegexp = regExpToParseExpressPathRegExp.test(stackItem.regexp); + let newBasePath = basePath; + if (isExpressPathRegexp) { + const parsedPath = parseExpressPath(stackItem.regexp, stackItem.keys); + newBasePath += `/${parsedPath}`; + } + else if (!stackItem.path && stackItem.regexp && stackItem.regexp.toString() !== EXPRESS_ROOT_PATH_REGEXP_VALUE) { + const regExpPath = ` RegExp(${stackItem.regexp}) `; + newBasePath += `/${regExpPath}`; + } + endpoints = parseEndpoints(stackItem.handle, newBasePath, endpoints); + } + }); + return endpoints; +}; const expressListEndpoints = function (app) { - const endpoints = parseEndpoints(app) - - return endpoints -} - -module.exports = expressListEndpoints + const endpoints = parseEndpoints(app); + return endpoints; +}; +module.exports = expressListEndpoints;