UNPKG

hono-file-router

Version:

File-based router for easier route management for hono.

96 lines (90 loc) 3.49 kB
'use strict'; var fs = require('fs'); var path = require('path'); var hono = require('hono'); var factory = require('hono/factory'); const httpMethods = ["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH", "HEAD", "CONNECT", "TRACE", "index"]; function fileRouterScanner(cwd, initpath) { const normalFolders = directoryScanner(`${cwd}/${initpath}`); const resolvedPath = cleanupPath(path.resolve(initpath)); normalFolders.push({ name: ".", path: resolvedPath, directory: true }); const routes = []; const errors = []; normalFolders.forEach((folder) => { if (!folder.directory) return; const methodAssignment = {}; httpMethods.forEach((methodOrIndex) => { const pathToFile = `${folder.path}/${folder.name}/${methodOrIndex}`; const jsExists = fileExists(`${pathToFile}.js`); const tsExists = fileExists(`${pathToFile}.ts`); if (jsExists && tsExists) errors.push({ message: "Both js and ts files exist for that method. Only one must exist.", data: pathToFile }); if (jsExists || tsExists) { const route = `${folder.path.replaceAll(resolvedPath, "")}/${folder.name === "." ? "" : folder.name}`; methodAssignment[methodOrIndex] = { file: cleanupPath(`${pathToFile}.${jsExists ? "js" : "ts"}`), route: replaceParams(route), method: methodOrIndex }; } }); if (methodAssignment.index && Object.keys(methodAssignment).length !== 1) errors.push({ message: "An index file exists along with method files. Please remove either to avoid confusion.", data: methodAssignment }); routes.push(methodAssignment); }); function replaceParams(path2) { return path2.replace(/\[([^\]]+)\]/g, ":$1"); } if (errors.length) throw Error(JSON.stringify(errors, null, " ")); return routes; } function fileExists(file) { return fs.existsSync(file); } function directoryScanner(path2) { const folders = fs.readdirSync(path2, { recursive: true, withFileTypes: true }); return folders.map((folders2) => ({ name: folders2.name, path: cleanupPath(folders2.parentPath), directory: folders2.isDirectory() })); } function cleanupPath(path2) { return path2.replaceAll("/./", "/").replaceAll("\\", "/"); } function getCwd() { return process.cwd(); } const createHandler = factory.createFactory().createHandlers; async function createFolderRoute({ path } = {}) { if (!path) throw Error("path is required"); const cwd = getCwd(); const routes = fileRouterScanner(cwd, path); const flattenedRoutes = []; routes.forEach( (route) => httpMethods.forEach((method) => { const routeMethod = route[method]; if (routeMethod) flattenedRoutes.push(routeMethod); }) ); const promises = []; const app = new hono.Hono(); flattenedRoutes.forEach((route) => { if (route.method === "index") { promises.push( import(`file://${route.file}`).then((importedRoutes) => { httpMethods.forEach((method) => { if (method !== "index") { if (importedRoutes[method]) app.on(method, route.route, ...importedRoutes[method]); } }); }) ); } else { promises.push( import(`file://${route.file}`).then(async (importedRoute) => { app.on(route.method, route.route, ...importedRoute.default); }) ); } }); await Promise.all(promises); return app; } exports.createFolderRoute = createFolderRoute; exports.createHandler = createHandler;