UNPKG

fastify-print-routes

Version:

A simple plugin for Fastify prints all available routes.

150 lines (149 loc) 5.16 kB
import { clean, colorize } from 'acquerello'; import fastifyPlugin from 'fastify-plugin'; import { table } from 'table'; import { ZodObject } from 'zod'; import { zodToJsonSchema } from 'zod-to-json-schema'; 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 ]; } // Unify the routes if (typeof route.method === 'string') { unifiedRoute.method.push(route.method); } else { unifiedRoute.method.push(...route.method); } // Remove the description when they don't match 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; // Sort and eventually unify routes 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)); // Build the output 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 = ''; /* c8 ignore next 3 */ if (showQueryString) { if (route.schema?.querystring) { // Get all properties let schema = route.schema.querystring; /* c8 ignore next 5 */ if (schema instanceof ZodObject) { schema = zodToJsonSchema(schema, { target: 'openApi3', $refStrategy: 'none' }); } 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 = []; // Utility to track all the RouteOptionss we add 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;