universal-autorouter
Version:
An universal plugin that scans the file system and automatically loads to a server all routes in a target directory.
69 lines (65 loc) • 2.85 kB
JavaScript
import fs from 'node:fs';
import path from 'node:path';
const countParams = (filepath) => {
return (filepath.match(/\[(.*?)\]/gu) || []).length;
};
const toPosix = (filepath) => {
return filepath.replaceAll("\\", "/");
};
const sortRoutesByParams = (routes) => {
return routes.sort((a, b) => countParams(a) - countParams(b));
};
const filepathToRoute = (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 = "./api";
const DEFAULT_METHOD = "get";
var index = async (app, {
pattern = DEFAULT_PATTERN,
prefix = "",
routesDir = DEFAULT_ROUTES_DIR,
defaultMethod = DEFAULT_METHOD,
viteDevServer,
skipNoRoutes = false,
skipImportErrors = false
}) => {
const entryDir = path.isAbsolute(routesDir) ? toPosix(routesDir) : path.posix.join(process.cwd(), routesDir);
if (!fs.existsSync(entryDir)) {
throw new Error(`Directory ${entryDir} doesn't exist`);
}
if (!fs.statSync(entryDir).isDirectory()) {
throw new Error(`${entryDir} isn't a directory`);
}
const files = typeof Bun === "undefined" ? fs.globSync(pattern, { cwd: entryDir }) : [...new Bun.Glob(pattern).scanSync({ cwd: entryDir })];
if (files.length === 0 && !skipNoRoutes) {
throw new Error(`No matches found in ${entryDir} (you can disable this error with 'skipFailGlob' option to true)`);
}
let _import;
if (viteDevServer) {
_import = (filepath) => viteDevServer.ssrLoadModule(filepath, { fixStacktrace: true });
} else if (process.platform === "win32") {
const { pathToFileURL } = await import('node:url');
_import = (filepath) => import(pathToFileURL(filepath).href);
} else {
_import = (filepath) => import(filepath);
}
for (const file of sortRoutesByParams(files)) {
const endFilepath = toPosix(file);
const fullFilepath = `${entryDir}/${endFilepath}`;
const { default: handler } = await _import(fullFilepath);
if (!handler && !skipImportErrors) {
throw new Error(`${fullFilepath} doesn't have default export (you can disable this error with 'skipImportErrors' option to true)`);
}
if (typeof handler === "function") {
const matchedFile = endFilepath.match(/\/?\((.*?)\)/);
const method = matchedFile ? matchedFile[1] : defaultMethod;
const route = `${prefix}/${filepathToRoute(endFilepath)}`;
app[method](route, handler);
} else {
console.warn(`Exported function of ${fullFilepath} is not a function`);
}
}
return app;
};
export { DEFAULT_METHOD, DEFAULT_ROUTES_DIR, index as default, filepathToRoute, toPosix };