UNPKG

fastify-print-routes

Version:

A simple plugin for Fastify prints all available routes.

128 lines (127 loc) 4.73 kB
import { clean, colorize } from 'acquerello'; import fastifyPlugin from 'fastify-plugin'; import { table } from 'table'; import { toJSONSchema, ZodObject } from 'zod'; const methodsOrder = ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'PATCH', 'OPTIONS']; function getRouteConfig(r) { return r.config ?? {}; } function sortRoutes(a, b) { return a.url.localeCompare(b.url); } function unifyRoutes(routes) { const routesMap = new Map(); for (const route of routes) { const unifiedRoute = routesMap.get(route.url); if (unifiedRoute) { if (typeof unifiedRoute.method === 'string') { unifiedRoute.method = [unifiedRoute.method]; } if (typeof route.method === 'string') { unifiedRoute.method.push(route.method); } else { unifiedRoute.method.push(...route.method); } const config = unifiedRoute?.config; if (config && config?.description !== route.config?.description) { config.description = undefined; } } else { routesMap.set(route.url, route); } } return [...routesMap.values()].sort(sortRoutes); } function printRoutes(routes, useColors, compact, filter, showQueryString) { if (routes.length === 0) { return; } const styler = useColors ? colorize : clean; routes = routes.filter(r => getRouteConfig(r).hide !== true && filter(r)).sort(sortRoutes); if (compact) { routes = unifyRoutes(routes); } const hasDescription = routes.some(r => 'description' in getRouteConfig(r)); const headers = [styler('{{bold white}}Method(s){{-}}'), styler('{{bold white}}Path{{-}}')]; if (hasDescription) { headers.push(styler('{{bold white}}Description{{-}}')); } const rows = [headers]; for (const route of routes) { const methods = Array.isArray(route.method) ? route.method : [route.method]; const url = route.url.replaceAll(/:\w+|\[:\w+]/g, '{{yellow}}$&{{-}}'); const querystringComponents = []; let queryString = ''; if (showQueryString) { if (route.schema?.querystring) { let schema = route.schema.querystring; if (schema instanceof ZodObject) { schema = toJSONSchema(schema); } const requiredProperties = schema.required ?? []; for (const property of Object.keys(schema.properties)) { const param = `${property}={{yellow}}value{{-}}`; const separator = querystringComponents.length === 0 ? '?' : '&'; if (requiredProperties.includes(property)) { querystringComponents.push(separator + param); } else { querystringComponents.push(`${separator}(${param})`); } } } queryString = querystringComponents .join('') .replaceAll('&(', '(&') .replaceAll(')&', '&)') .replaceAll(')(&', '&)('); } const row = [ styler(methods .sort((a, b) => methodsOrder.indexOf(a) - methodsOrder.indexOf(b)) .map(m => `{{cyan}}${m}{{-}}`) .join(' | ')), styler(`{{bold green}}${url}${queryString}{{-}}`) ]; if (hasDescription) { row.push(styler(`{{italic}}${getRouteConfig(route).description ?? ''}{{-}}`)); } rows.push(row); } const output = table(rows, { columns: { 0: { alignment: 'right' }, 1: { alignment: 'left' }, 2: { alignment: 'left' } }, drawHorizontalLine(index, size) { return index < 2 || index > size - 1; } }); console.log(`Available routes:\n\n${output}`); } export const plugin = fastifyPlugin(function (instance, options, done) { const useColors = options.useColors ?? true; const compact = options.compact ?? false; const filter = options.filter ?? (() => true); const querystring = options.querystring ?? true; const routes = []; instance.addHook('onRoute', route => { routes.push(route); }); instance.addHook('onReady', done => { printRoutes(routes, useColors, compact, filter, querystring); done(); }); instance.decorate('routes', routes); done(); }, { name: 'fastify-print-routes', fastify: '5.x' }); export default plugin;