Last active
January 9, 2025 02:34
Revisions
-
mp035 revised this gist
Jan 9, 2025 . 1 changed file with 7 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,7 @@ import { Module } from '@nestjs/common'; import { ApiRoutesCommand } from './api-routes.command'; @Module({ providers: [ApiRoutesCommand], }) export class ApiRoutesModule {} -
mp035 revised this gist
Jan 9, 2025 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -148,7 +148,7 @@ type ApiRoute = { } // find and overwrite the src/lib/api-routes.js file throw new Error("You have to adjust the output path below to ensure the route list is generated where you expect."); const apiRoutesPath = path.join( __dirname, '../../../app/src/lib/api-routes.ts', -
mp035 revised this gist
Jan 9, 2025 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -148,6 +148,7 @@ type ApiRoute = { } // find and overwrite the src/lib/api-routes.js file Throw new Error("You have to adjust the output path below to ensure the route list is generated where you expect."); const apiRoutesPath = path.join( __dirname, '../../../app/src/lib/api-routes.ts', -
mp035 created this gist
Jan 9, 2025 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,160 @@ import { Injectable } from '@nestjs/common'; import { Command } from 'nestjs-command'; import * as fs from 'fs'; import * as glob from 'glob'; import * as path from 'path'; import * as ts from 'typescript'; @Injectable() export class ApiRoutesCommand { @Command({ command: 'api-routes:generate', describe: 'Generate a list of API routes for the frontend', }) async generateApiRoutes() { // grab each controller file from ../server/src/**/*.controller.ts const fileList = glob.sync(path.join(__dirname, '../**/*.controller.ts')); function findNodesByType(node, type) { const nodes = []; function findNodes(node) { if (node.kind === type) { nodes.push(node); } ts.forEachChild(node, findNodes); } findNodes(node); return nodes; } const apiRoutes = {}; // iterate over the file list for (const file of fileList) { // parse the file const program = ts.createProgram([file], { allowJs: true }); const source = program.getSourceFile(file); const classes = findNodesByType(source, ts.SyntaxKind.ClassDeclaration); for (const classObj of classes) { // check if the class has a 'Controller' decorator, if not, continue, otherwise, get the first parameter as the route const classDecoratorNodes = findNodesByType( classObj, ts.SyntaxKind.Decorator, ); const controllerDecorator = classDecoratorNodes.find( (decorator) => decorator.expression.expression.escapedText === 'Controller', ); if (!controllerDecorator) { continue; } const controllerRoute = controllerDecorator.expression.arguments.length ? controllerDecorator.expression.arguments[0].text : ''; const className = classObj.name.escapedText; apiRoutes[className] = {}; const apiController = apiRoutes[className]; // find the public methods of the class with decorators const methods = findNodesByType( classObj, ts.SyntaxKind.MethodDeclaration, ); for (const methodNode of methods) { const methodName = methodNode.name.escapedText; // find the decorators const methodDecoratorNodes = findNodesByType( methodNode, ts.SyntaxKind.Decorator, ); const methodDecorators = []; for (const decorator of methodDecoratorNodes) { // get the decorator name and first parameter (if any) methodDecorators.push({ name: decorator.expression.expression.escapedText, value: decorator.expression.arguments.length ? decorator.expression.arguments[0].text : '', }); } // if the decorator names do not contain at least one of 'Get', 'Post', 'Put', 'Delete', 'Patch', 'Options', 'Head' or 'All' then continue to the next method const validDecorators = [ 'Get', 'Post', 'Put', 'Delete', 'Patch', 'Options', 'Head', 'All', ]; const routeDecorator = methodDecorators.find((decorator) => validDecorators.includes(decorator.name), ); if (!routeDecorator) { continue; } // if the method has a decorator, convert it to a http VERB const method = routeDecorator.name.toUpperCase(); const path = routeDecorator.value; /* console.log( `${className}.${methodName} : ${method} ${controllerRoute}/${path}`, ); */ process.stdout.write('#'); apiController[methodName] = { method, path: path ? `${controllerRoute}/${path}` : controllerRoute, }; } } } //generate the source code for the api-routes file let sourceFileContent = `//THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. //To update this file, run 'npx nestjs-command api-routes:generate' //from the server project directory. type ApiRoute = { method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; path: string; }; `; for (const controller in apiRoutes) { sourceFileContent += `class ${controller}Class { `; for (const method in apiRoutes[controller]) { sourceFileContent += `public ${method}: ApiRoute = ${JSON.stringify( apiRoutes[controller][method], )}; `; } sourceFileContent += `} `; } for (const controller in apiRoutes) { sourceFileContent += `export const ${controller} = new ${controller}Class(); `; } // find and overwrite the src/lib/api-routes.js file const apiRoutesPath = path.join( __dirname, '../../../app/src/lib/api-routes.ts', ); fs.writeFileSync(apiRoutesPath, sourceFileContent); console.log(''); console.log(`apiRoutes written to ${apiRoutesPath}`); } }