UNPKG

universal-autoloader

Version:

Universal plugin, that use the _file system_ scan, to load in a server all routes in a directory.

56 lines (52 loc) 2.45 kB
import fs from 'node:fs'; import { pathToFileURL } from 'node:url'; const countParams = (filepath) => { return (filepath.match(/\[(.*?)\]/gu) || []).length; }; const sortRoutesByParams = (routes) => { return routes.sort((a, b) => countParams(a) - countParams(b)); }; const transformToRoute = (filepath) => { return filepath.replace(/\.(ts|tsx|mjs|js|jsx|cjs)$/u, "").replaceAll(/\[\.\.\..*?\]/gu, "*").replaceAll(/\[(.+?)\]/gu, (_, match) => `:${match}`).replace(/\/?\((.*?)\)/, "").replaceAll("]-[", "-:").replaceAll("]/", "/").replaceAll(/\[|\]/gu, "").replace(/\/?index$/, ""); }; const DEFAULT_PATTERN = "**/*.{ts,tsx,mjs,js,jsx,cjs}"; const DEFAULT_ROUTES_DIR = "./routes"; const DEFAULT_METHOD = "get"; const autoloadRoutes = async (app, { pattern = DEFAULT_PATTERN, prefix = "", routesDir = DEFAULT_ROUTES_DIR, defaultMethod = DEFAULT_METHOD, viteDevServer, skipNoRoutes = false, skipImportErrors = false }) => { if (!fs.existsSync(routesDir)) { throw new Error(`Directory ${routesDir} doesn't exist`); } if (!fs.statSync(routesDir).isDirectory()) { throw new Error(`${routesDir} isn't a directory`); } const files = typeof Bun === "undefined" ? fs.globSync(pattern, { cwd: routesDir }) : await Array.fromAsync(new Bun.Glob(pattern).scan({ cwd: routesDir })); if (files.length === 0 && !skipNoRoutes) { throw new Error(`No matches found in ${routesDir} (you can disable this error with 'skipFailGlob' option to true)`); } for (const file of sortRoutesByParams(files)) { const universalFilepath = file.replaceAll("\\", "/"); const initFilepath = `${routesDir}/${universalFilepath}`; const { default: importedRoute } = await (viteDevServer ? viteDevServer.ssrLoadModule(initFilepath, { fixStacktrace: true }) : import(pathToFileURL(initFilepath).href)); if (!importedRoute && !skipImportErrors) { throw new Error(`${initFilepath} doesn't have default export (you can disable this error with 'skipImportErrors' option to true)`); } if (typeof importedRoute === "function") { const matchedFile = universalFilepath.match(/\/?\((.*?)\)/); const method = matchedFile ? matchedFile[1] : defaultMethod; const route = `${prefix}/${transformToRoute(universalFilepath)}`; app[method](route, importedRoute); } else { console.warn(`Exported function of ${initFilepath} is not a function`); } } return app; }; export { autoloadRoutes };