| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 |
- #! /usr/bin/env node
- const acorn = require('acorn')
- const acornWalk = require('acorn-walk')
- const fs = require('fs')
- const _ = require('lodash')
- const glob = require('glob')
- const escodegen = require('escodegen')
- const print = console.log
- const Methods = new Set([
- 'get',
- 'head',
- 'post',
- 'put',
- 'delete',
- 'connect',
- 'options',
- 'trace',
- 'patch',
- ])
- const isMethod = str => {
- return Methods.has(str)
- }
- // Check if the expression is a call on a router, return data about it, or null
- const routerCall = callExpression => {
- const callee = callExpression.callee
- const property = callee.property
- const args = callExpression.arguments
- if (!callee.object || !callee.object.name) {
- return false
- }
- const routerName = callee.object.name
- if (
- // Match known names for the Express routers: app, webRouter, whateverRouter, etc...
- isMethod(property.name) &&
- (routerName === 'app' || routerName.match('^.*[rR]outer$'))
- ) {
- return {
- routerName,
- method: property.name,
- args,
- }
- } else {
- return null
- }
- }
- const formatMethodCall = expression => {
- return escodegen.generate(expression, { format: { compact: true } })
- }
- const parseAndPrintRoutesSync = path => {
- const content = fs.readFileSync(path)
- // Walk the AST (Abstract Syntax Tree)
- acornWalk.simple(
- acorn.parse(content, { sourceType: 'module', ecmaVersion: 2020 }),
- {
- // We only care about call expression ( like `a.b()` )
- CallExpression(node) {
- const call = routerCall(node)
- if (call) {
- const firstArg = _.first(call.args)
- try {
- print(
- ` ${formatRouterName(call.routerName)}\t .${call.method} \t: ${
- firstArg.value
- } => ${call.args.slice(1).map(formatMethodCall).join(' => ')}`
- )
- } catch (e) {
- print('>> Error')
- print(e)
- print(JSON.stringify(call))
- process.exit(1)
- }
- }
- },
- }
- )
- }
- const routerNameMapping = {
- privateApiRouter: 'privateApi',
- publicApiRouter: 'publicApi',
- }
- const formatRouterName = name => {
- return routerNameMapping[name] || name
- }
- const main = () => {
- // Take an optional filter to apply to file names
- const filter = process.argv[2] || null
- if (filter && (filter === '--help' || filter === 'help')) {
- print('')
- print(' Usage: bin/routes [filter]')
- print(' Examples:')
- print(' bin/routes')
- print(' bin/routes GitBridge')
- print('')
- process.exit(0)
- }
- // Find all routers
- glob('*[rR]outer.*js', { matchBase: true }, (err, files) => {
- if (err) {
- console.error(err)
- process.exit(1)
- }
- for (const file of files) {
- if (file.match('^node_modules.*$') || file.match('.*/public/.*')) {
- continue
- }
- // Restrict to the filter (if filter is present)
- if (filter && !file.match(`.*${filter}.*`)) {
- continue
- }
- print(`[${file}]`)
- try {
- parseAndPrintRoutesSync(file)
- } catch (_e) {
- print('>> Error parsing file')
- continue
- }
- }
- process.exit(0)
- })
- }
- if (require.main === module) {
- main()
- }
|