@psenger/express-auto-router
Version:
A dynamic route composition system for Express.js applications that automatically discovers and mount routes and middleware based on your file system structure. Inspired by Next.js routing conventions.
1 lines • 46.6 kB
Source Map (JSON)
{"version":3,"file":"index.mjs","sources":["../src/index.js","../src/index.js?commonjs-entry"],"sourcesContent":["const path = require('path')\nconst { readdirSync, statSync } = require('fs')\n\n/**\n * Universal module loader that handles both CommonJS and ESM modules\n * Tries CommonJS first, falls back to dynamic import for ESM\n *\n * @param {string} modulePath - Path to the module to load\n * @returns {Promise<any>|any} - The loaded module\n */\n// function loadModule(modulePath) {\n// try {\n// // Try CommonJS first (synchronous)\n// return require(modulePath)\n// } catch (requireError) {\n// // If require fails, try dynamic import (asynchronous)\n// try {\n// // Use Function constructor to avoid static analysis issues\n// const dynamicImport = new Function('modulePath', 'return import(modulePath)')\n// return dynamicImport(modulePath).then(module => module.default || module)\n// } catch (importError) {\n// // If both fail, throw the original require error\n// throw requireError\n// }\n// }\n// }\nfunction loadModule(modulePath) {\n // Check if we're in a CommonJS environment (require is available)\n if (typeof require !== 'undefined') {\n try {\n // Try CommonJS first (synchronous)\n return require(modulePath)\n } catch (requireError) {\n // If require fails (e.g., ESM module), fall back to dynamic import\n try {\n const dynamicImport = new Function(\n 'modulePath',\n 'return import(modulePath)'\n )\n return dynamicImport(modulePath).then((module) => {\n // Handle both default and named exports properly\n if (module.default !== undefined) {\n return module.default\n }\n return module\n })\n } catch (importError) {\n console.warn('Dynamic import failed:', importError)\n requireError.cause = importError\n throw requireError\n }\n }\n } else {\n // We're in an ESM environment, use dynamic import directly\n try {\n const dynamicImport = new Function(\n 'modulePath',\n 'return import(modulePath)'\n )\n return dynamicImport(modulePath).then((module) => {\n // Handle both default and named exports properly\n if (module.default !== undefined) {\n return module.default\n }\n return module\n })\n } catch (importError) {\n console.warn('Dynamic import failed:', importError)\n throw importError\n }\n }\n}\n\nconst PLACEHOLDER_REGEX = /\\[(.+?)\\]/\nconst PLACEHOLDER_GLOBAL_REGEX = /\\[(.+?)\\]/g\n\n/**\n * Checks if a directory entry is a middleware file\n *\n * @param {Object} entry - The directory entry to check (fs.Dirent object)\n * @returns {boolean} - True if the entry is a file named '_middleware.js'\n *\n * @example\n * // With a file entry for '_middleware.js'\n * const middlewareEntry = { isFile: () => true, name: '_middleware.js' };\n * isMiddlewareFile(middlewareEntry); // Returns: true\n *\n * @example\n * // With a directory entry\n * const dirEntry = { isFile: () => false, name: '_middleware.js' };\n * isMiddlewareFile(dirEntry); // Returns: false\n *\n * @example\n * // With a different file\n * const otherFileEntry = { isFile: () => true, name: 'index.js' };\n * isMiddlewareFile(otherFileEntry); // Returns: false\n */\nfunction isMiddlewareFile(entry) {\n return entry.isFile() && entry.name === '_middleware.js'\n}\n\n/**\n * Ensures a value is always an array by wrapping non-array values\n *\n * @param {*} ary - The value to convert to an array\n * @returns {Array} - Wraps the value in an array, or if the input was an array already it will return it as is.\n *\n * @example\n * // With a non-array value\n * autoBox(5); // Returns: [5]\n *\n * @example\n * // With an array value\n * autoBox([1, 2, 3]); // Returns: [1, 2, 3]\n *\n * @example\n * // With null or undefined\n * autoBox(null); // Returns: [null]\n * autoBox(undefined); // Returns: [undefined]\n *\n * @example\n * // With an object\n * autoBox({ key: 'value' }); // Returns: [{ key: 'value' }]\n */\nfunction autoBox(ary) {\n return Array.isArray(ary) ? ary : [ary]\n}\n\n/**\n * Converts URL placeholder syntax [param] to Express parameter syntax :param\n *\n * @param {string} urlPath - The URL path containing placeholders\n * @returns {string} - The URL path with Express-style parameters\n *\n * @example\n * // With single placeholder\n * replaceUrlPlaceholders('/users/[id]'); // Returns: '/users/:id'\n *\n * @example\n * // With multiple placeholders\n * replaceUrlPlaceholders('/users/[id]/posts/[postId]'); // Returns: '/users/:id/posts/:postId'\n *\n * @example\n * // With no placeholders\n * replaceUrlPlaceholders('/users/list'); // Returns: '/users/list'\n *\n * @example\n * // With nested/complex placeholders\n * replaceUrlPlaceholders('/products/[category]/[id]/reviews/[reviewId]');\n * // Returns: '/products/:category/:id/reviews/:reviewId'\n */\nfunction replaceUrlPlaceholders(urlPath) {\n return urlPath.replace(\n PLACEHOLDER_GLOBAL_REGEX,\n (match, variable) => `:${variable}`\n )\n}\n\n/**\n * Checks if a URL path contains a placeholder\n *\n * @param {string} urlPath - The URL path to check\n * @returns {boolean} - True if the path contains a placeholder\n *\n * @example\n * // With placeholder\n * isPlaceholder('/users/[id]'); // Returns: true\n *\n * @example\n * // With multiple placeholders\n * isPlaceholder('/users/[id]/posts/[postId]'); // Returns: true\n *\n * @example\n * // Without placeholder\n * isPlaceholder('/users/list'); // Returns: false\n *\n * @example\n * // With square brackets in a different context (not a placeholder)\n * isPlaceholder('/users/list[all]'); // Returns: true (matches the regex pattern)\n */\nfunction isPlaceholder(urlPath) {\n return PLACEHOLDER_REGEX.test(urlPath)\n}\n\n/**\n * Validates if a path is a non-empty string\n *\n * @param {string} path - The path to validate\n * @throws {Error} If path is not a string or is empty\n *\n * @example\n * // With valid path\n * validatePath('/api/users'); // No error thrown\n *\n * @example\n * // With empty string\n * try {\n * validatePath('');\n * } catch (error) {\n * console.error(error.message); // Outputs: 'Invalid path provided'\n * }\n *\n * @example\n * // With null value\n * try {\n * validatePath(null);\n * } catch (error) {\n * console.error(error.message); // Outputs: 'Invalid path provided'\n * }\n *\n * @example\n * // With non-string value\n * try {\n * validatePath(123);\n * } catch (error) {\n * console.error(error.message); // Outputs: 'Invalid path provided'\n * }\n */\nfunction validatePath(path) {\n if (typeof path !== 'string' || !path) {\n throw new Error('Invalid path provided')\n }\n}\n\n/**\n * Safely joins URL paths without creating double slashes\n * Removes trailing slash from base and ensures segment starts with slash\n *\n * @param {string} base - The base URL path\n * @param {string} segment - The path segment to append\n * @returns {string} - The joined path without double slashes\n *\n * @example\n * // With base having trailing slash\n * joinUrlPaths('/api/', 'users')\n * // Returns: '/api/users'\n *\n * @example\n * // With base not having trailing slash\n * joinUrlPaths('/api', 'users')\n * // Returns: '/api/users'\n *\n * @example\n * // With segment having leading slash\n * joinUrlPaths('/api', '/users')\n * // Returns: '/api/users'\n *\n * @example\n * // Preventing double slashes\n * joinUrlPaths('/api/', '/users')\n * // Returns: '/api/users'\n *\n * @example\n * // With empty base (edge case)\n * joinUrlPaths('', 'users')\n * // Returns: '/users'\n *\n * @example\n * // With empty segment (edge case)\n * joinUrlPaths('/api', '')\n * // Returns: '/api/'\n *\n * @example\n * // With both empty (edge case)\n * joinUrlPaths('', '')\n * // Returns: '/'\n */\nfunction joinUrlPaths(base, segment) {\n // Remove trailing slash from base if it exists\n const cleanBase = base.endsWith('/') ? base.slice(0, -1) : base\n // Ensure segment starts with /\n const cleanSegment = segment.startsWith('/') ? segment : '/' + segment\n return cleanBase + cleanSegment\n}\n\n/**\n * Parses directory name for priority prefix, extracts route name, and detects route type\n *\n * @param {string} dirName - Directory name (e.g., \"10-users\", \"users\", \"05-[id]\", \"[sessionId]\")\n * @returns {Object} - { priority: number, name: string, hasPrefix: boolean, isDynamic: boolean }\n *\n * @example\n * // With priority prefix and static route\n * parseDirectoryPriority(\"10-users\")\n * // Returns: { priority: 10, name: \"users\", hasPrefix: true, isDynamic: false }\n *\n * @example\n * // With priority prefix and dynamic route\n * parseDirectoryPriority(\"05-[userId]\")\n * // Returns: { priority: 5, name: \"[userId]\", hasPrefix: true, isDynamic: true }\n *\n * @example\n * // Without priority prefix (static route)\n * parseDirectoryPriority(\"users\")\n * // Returns: { priority: 50, name: \"users\", hasPrefix: false, isDynamic: false }\n *\n * @example\n * // Without priority prefix (dynamic route)\n * parseDirectoryPriority(\"[sessionId]\")\n * // Returns: { priority: 50, name: \"[sessionId]\", hasPrefix: false, isDynamic: true }\n *\n * @example\n * // Invalid priority range (falls back to default)\n * parseDirectoryPriority(\"150-invalid\")\n * // Logs: \"Invalid priority prefix detected in directory \"150-invalid\", using default priority 50\"\n * // Returns: { priority: 50, name: \"150-invalid\", hasPrefix: false, isDynamic: false }\n *\n * @example\n * // Invalid priority format (falls back to default)\n * parseDirectoryPriority(\"x5-invalid\")\n * // Returns: { priority: 50, name: \"x5-invalid\", hasPrefix: false, isDynamic: false }\n *\n * @note Logs warning message to console.info when invalid priority prefix is detected (out of 00-99 range)\n * @note Valid priority range is 00-99; invalid ranges default to priority 50 with hasPrefix: false\n */\nfunction parseDirectoryPriority(dirName) {\n const match = dirName.match(/^(\\d{2})-(.+)$/)\n if (match) {\n const priority = parseInt(match[1], 10)\n const name = match[2]\n if (priority >= 0 && priority <= 99) {\n return {\n priority,\n name,\n hasPrefix: true,\n isDynamic: isPlaceholder(name)\n }\n }\n console.info(\n `Invalid priority prefix detected in directory \"${dirName}\", using default priority 50`\n )\n }\n return {\n priority: 50, // Default middle priority for non-prefixed directories\n name: dirName,\n hasPrefix: false,\n isDynamic: isPlaceholder(dirName)\n }\n}\n\n/**\n * Normalizes middleware to priority objects with consistent structure\n *\n * @param {Function|Object|Array} middleware - Middleware function(s) or priority objects\n * @param {number} sourceIndex - Original array position for tracking\n * @param {string} sourcePath - Source path for specificity tracking\n * @returns {Array} Array of {fn, priority, sourceIndex, sourcePath} objects\n *\n * @example\n * // With plain function\n * normalizeMiddlewarePriority(corsMiddleware, 0, '/api/')\n * // Returns: [{ fn: corsMiddleware, priority: 50, sourceIndex: 0, sourcePath: '/api/' }]\n *\n * @example\n * // With priority object\n * normalizeMiddlewarePriority({ fn: authMiddleware, priority: 10 }, 1, '/api/')\n * // Returns: [{ fn: authMiddleware, priority: 10, sourceIndex: 1, sourcePath: '/api/' }]\n *\n * @example\n * // With array of mixed types\n * normalizeMiddlewarePriority([corsMiddleware, { fn: authMiddleware, priority: 20 }], 0, '/api/')\n * // Returns: [\n * // { fn: corsMiddleware, priority: 50, sourceIndex: 0, sourcePath: '/api/' },\n * // { fn: authMiddleware, priority: 20, sourceIndex: 1, sourcePath: '/api/' }\n * // ]\n */\nfunction normalizeMiddlewarePriority(\n middleware,\n sourceIndex = 0,\n sourcePath = ''\n) {\n const items = Array.isArray(middleware) ? middleware : [middleware]\n return items.map((item, index) => {\n if (typeof item === 'function') {\n return {\n fn: item,\n priority: 50,\n sourceIndex: sourceIndex + index,\n sourcePath\n }\n }\n if (item && typeof item.fn === 'function') {\n return {\n fn: item.fn,\n priority: item.priority || 50,\n sourceIndex: sourceIndex + index,\n sourcePath\n }\n }\n throw new Error(\n 'Invalid middleware: must be function or {fn, priority} object'\n )\n })\n}\n\n/**\n * Sorts and flattens middleware functions by four-level priority system\n *\n * @param {Array} middlewareArray - Array of {fn, priority, sourceIndex, sourcePath} objects\n * @returns {Array} Array of middleware functions sorted by priority\n *\n * @example\n * // With mixed priority middleware\n * const middleware = [\n * { fn: authMiddleware, priority: 20, sourceIndex: 0, sourcePath: '/api/' },\n * { fn: corsMiddleware, priority: 5, sourceIndex: 1, sourcePath: '/api/' },\n * { fn: loggingMiddleware, priority: 90, sourceIndex: 0, sourcePath: '/api/users/' }\n * ]\n * sortMiddlewareFunctions(middleware)\n * // Returns: [corsMiddleware, authMiddleware, loggingMiddleware]\n */\nfunction sortMiddlewareFunctions(middlewareArray) {\n return middlewareArray\n .sort((a, b) => {\n // Level 1: Priority (00-99)\n if (a.priority !== b.priority) {\n return a.priority - b.priority\n }\n\n // Level 2: Function name alphabetically (arrow functions get inferred names)\n const aName = a.fn.name || ''\n const bName = b.fn.name || ''\n if (aName !== bName) {\n // Anonymous functions (\"\") come after named functions\n if (aName === '' && bName !== '') return 1\n if (aName !== '' && bName === '') return -1\n return aName.localeCompare(bName)\n }\n\n // Level 3: For anonymous functions, sort by original array position\n if (aName === '' && bName === '') {\n if (a.sourceIndex !== b.sourceIndex) {\n return a.sourceIndex - b.sourceIndex\n }\n }\n\n // Level 4: Source path specificity (longer paths = more specific)\n return a.sourcePath.length - b.sourcePath.length\n })\n .map((item) => item.fn)\n}\n\n/**\n * Retrieves and sorts middleware functions that match a given path\n * Finds all entries in the dictionary where the given path starts with the dictionary key,\n * sorts them by key length (shortest first), and returns the flattened array of middleware functions\n *\n * Supports both legacy middleware format (plain functions) and priority object format ({ fn, priority })\n * with backward compatibility. Priority objects are sorted using the four-level priority system.\n *\n * @param {Object<string, Function|Array<Function>|Array<Object>>} dictionary - Dictionary of paths to middleware functions or priority objects\n * @param {string} path - The path to match\n * @returns {Array<Function>} - Array of middleware functions that apply to the path, ordered by priority and path specificity\n *\n * @example\n * // With matching paths (legacy format)\n * const dict = {\n * '/api/': [authMiddleware],\n * '/api/users/': [userMiddleware]\n * };\n * dictionaryKeyStartsWithPath(dict, '/api/users/profile');\n * // Returns: [authMiddleware, userMiddleware] (in order from least to most specific)\n *\n * @example\n * // With priority objects (new format)\n * const dict = {\n * '/api/': [\n * { fn: corsMiddleware, priority: 5 },\n * { fn: authMiddleware, priority: 20 }\n * ],\n * '/api/users/': [\n * { fn: userValidationMiddleware, priority: 15 }\n * ]\n * };\n * dictionaryKeyStartsWithPath(dict, '/api/users/profile');\n * // Returns: [corsMiddleware, userValidationMiddleware, authMiddleware] (sorted by priority)\n *\n * @example\n * // With mixed legacy and priority format (backward compatible)\n * const dict = {\n * '/api/': [legacyMiddleware, { fn: priorityMiddleware, priority: 10 }],\n * '/api/users/': userMiddleware // Single function\n * };\n * dictionaryKeyStartsWithPath(dict, '/api/users/');\n * // Returns: [priorityMiddleware, legacyMiddleware, userMiddleware] (priority objects sorted first)\n *\n * @example\n * // With no matching paths\n * const dict = {\n * '/api/': [authMiddleware],\n * '/api/users/': [userMiddleware]\n * };\n * dictionaryKeyStartsWithPath(dict, '/admin/');\n * // Returns: []\n *\n * @example\n * // With null or undefined values in the dictionary (they are filtered out)\n * const dict = {\n * '/api/': [authMiddleware, null],\n * '/api/users/': undefined\n * };\n * dictionaryKeyStartsWithPath(dict, '/api/users/');\n * // Returns: [authMiddleware]\n *\n * @note Automatically converts legacy middleware functions to priority objects with default priority 50\n * @note Uses four-level sorting: priority → function name → source index → path specificity\n */\nfunction dictionaryKeyStartsWithPath(dictionary, path) {\n if (!dictionary || typeof dictionary !== 'object') {\n throw new Error('Dictionary must be an object')\n }\n const allMiddleware = Object.entries(dictionary)\n .filter(([key]) => path.startsWith(key))\n .sort(([aKey], [bKey]) => aKey.length - bKey.length)\n .flatMap(([, value]) => {\n const middlewareArray = Array.isArray(value) ? value : [value]\n // Check if we have priority objects or plain functions for backward compatibility\n return middlewareArray\n .map((item, index) => {\n if (typeof item === 'function') {\n // Legacy format - convert to priority object\n return {\n fn: item,\n priority: 50,\n sourceIndex: index,\n sourcePath: ''\n }\n } else if (item && typeof item.fn === 'function') {\n // New format - already a priority object\n return item\n }\n return null\n })\n .filter(Boolean)\n })\n .filter(Boolean)\n\n return sortMiddlewareFunctions(allMiddleware)\n}\n\n/**\n * Creates a curried router object with pre-configured URL path and middleware\n * Returns a proxy to the original router that applies the given URL path and middleware functions\n * to all HTTP method calls (get, post, put, etc.) automatically\n *\n * @param {Object} router - Express router instance\n * @param {string} urlPath - The URL path to be curried\n * @param {...Function} initialMiddleWareFunctions - Initial middleware functions to be applied (rest parameter, accepts multiple functions)\n * @returns {Object} - Curried router proxy with pre-configured path and middleware\n *\n * @example\n * // Basic usage with a single middleware function\n * const router = express.Router();\n * const curriedRouter = curryObjectMethods(router, '/users', authMiddleware);\n * curriedRouter.get((req, res) => res.json({}));\n * // Equivalent to: router.get('/users', authMiddleware, (req, res) => res.json({}));\n *\n * @example\n * // With multiple middleware functions\n * const curriedRouter = curryObjectMethods(router, '/posts', authMiddleware, logMiddleware);\n * curriedRouter.post((req, res) => res.status(201).json({}));\n * // Equivalent to: router.post('/posts', authMiddleware, logMiddleware, (req, res) => res.status(201).json({}));\n *\n * @example\n * // With no middleware\n * const curriedRouter = curryObjectMethods(router, '/public');\n * curriedRouter.get((req, res) => res.send('Hello'));\n * // Equivalent to: router.get('/public', (req, res) => res.send('Hello'));\n *\n * @example\n * // Accessing the original router object\n * const curriedRouter = curryObjectMethods(router, '/api');\n * const originalRouter = curriedRouter._getOriginalObject();\n * // originalRouter is the router instance passed in the first parameter\n */\nfunction curryObjectMethods(router, urlPath, ...initialMiddleWareFunctions) {\n const originalRouter = router\n const httpMethods = [\n 'get',\n 'post',\n 'put',\n 'delete',\n 'patch',\n 'options',\n 'head',\n 'all'\n ]\n const handler = {\n get(target, prop) {\n const originalHttpMethod = target[prop]\n if (\n typeof originalHttpMethod === 'function' &&\n httpMethods.includes(prop)\n ) {\n return (...remainingFns) => {\n const allFns = [...initialMiddleWareFunctions, ...remainingFns]\n return originalHttpMethod.call(target, urlPath, ...allFns)\n }\n }\n return originalHttpMethod\n }\n }\n const curriedRouterObject = new Proxy(router, handler)\n curriedRouterObject._getOriginalObject = () => originalRouter\n return curriedRouterObject\n}\n\n/**\n * Builds a dictionary of middleware functions from a directory structure\n * Recursively scans the given directory for '_middleware.js' files and builds a dictionary\n * mapping URL paths to their corresponding middleware functions\n *\n * @param {string} basePath - Base filesystem path to start scanning\n * @param {string} baseURL - Base URL path for the routes\n * @param {Object} [options=undefined] - Options that can be passed to all controllers when they are executed.\n * @returns {Object<string, Array<Function>>} Dictionary where keys are URL paths and values are arrays of middleware functions\n *\n * @example\n * // Basic directory structure with middleware\n * // ./src/routes/_middleware.js -> exports a global middleware\n * // ./src/routes/users/_middleware.js -> exports a users-specific middleware\n * const middlewares = buildMiddlewareDictionary('./src/routes', '/api');\n * // Returns: {\n * // '/api/': [globalMiddleware],\n * // '/api/users/': [usersMiddleware]\n * // }\n *\n * @example\n * // With dynamic route parameters\n * // ./src/routes/users/[id]/_middleware.js -> exports a user-specific middleware\n * const middlewares = buildMiddlewareDictionary('./src/routes', '/api');\n * // Returns: {\n * // '/api/': [globalMiddleware],\n * // '/api/users/': [usersMiddleware],\n * // '/api/users/:id/': [userSpecificMiddleware]\n * // }\n *\n * @example\n * // With middleware exporting multiple functions\n * // ./src/routes/_middleware.js -> exports [authMiddleware, logMiddleware]\n * const middlewares = buildMiddlewareDictionary('./src/routes', '/api');\n * // Returns: {\n * // '/api/': [authMiddleware, logMiddleware]\n * // }\n *\n * @example\n * // With middleware exporting a single function\n * // ./src/routes/_middleware.js -> exports singleMiddleware (not in an array)\n * const middlewares = buildMiddlewareDictionary('./src/routes', '/api');\n * // Returns: {\n * // '/api/': [singleMiddleware]\n * // }\n */\nasync function buildMiddlewareDictionary(basePath, baseURL, options) {\n if (!statSync(basePath).isDirectory()) {\n throw new Error(`Base path \"${basePath}\" is not a directory`)\n }\n const dictionary = {}\n const traverseDirectory = async (currentPath, currentURL) => {\n const dirEntries = readdirSync(currentPath, { withFileTypes: true })\n for (const entry of dirEntries) {\n const entryPath = path.resolve(currentPath, entry.name)\n let entryURL = joinUrlPaths(currentURL, entry.name)\n if (entry.isDirectory()) {\n const dirInfo = parseDirectoryPriority(entry.name)\n const routeName = dirInfo.name\n if (isPlaceholder(routeName)) {\n entryURL = joinUrlPaths(currentURL, replaceUrlPlaceholders(routeName))\n } else {\n entryURL = joinUrlPaths(currentURL, routeName)\n }\n await traverseDirectory(entryPath, entryURL)\n } else if (isMiddlewareFile(entry)) {\n try {\n const middlewareModule = await loadModule(entryPath)\n const middleware = middlewareModule(options)\n if (\n !middleware ||\n (typeof middleware !== 'function' && !Array.isArray(middleware))\n ) {\n throw new Error(\n `Middleware at ${entryPath} must export a function or array of functions`\n )\n }\n const middlewareURL = entryURL.replace('_middleware.js', '')\n const normalizedMiddleware = normalizeMiddlewarePriority(\n middleware,\n 0,\n middlewareURL\n )\n dictionary[middlewareURL] = normalizedMiddleware\n } catch (e) {\n throw new Error(\n `Failed to load middleware at ${entryPath}: ${e.message}`\n )\n }\n }\n }\n }\n await traverseDirectory(basePath, baseURL)\n return dictionary\n}\n\n/**\n * Builds an array of route mappings from a directory structure\n * Recursively scans the given directory for 'index.js' files and builds an array of\n * URL paths and their corresponding file paths, converting directory placeholders to Express params\n *\n * @param {string} basePath - Base filesystem path to start scanning\n * @param {string} baseURL - Base URL path for the routes\n * @returns {Array<Array<string>>} Array of tuples where first element is URL path and second is file path\n *\n * @example\n * // Basic directory structure\n * // ./src/routes/users/index.js\n * // ./src/routes/posts/index.js\n * const routes = buildRoutes('./src/routes', '/api');\n * // Returns: [\n * // ['/api/users/', './src/routes/users/index.js'],\n * // ['/api/posts/', './src/routes/posts/index.js']\n * // ]\n *\n * @example\n * // With dynamic route parameters\n * // ./src/routes/users/[id]/index.js\n * const routes = buildRoutes('./src/routes', '/api');\n * // Returns: [\n * // ['/api/users/:id/', './src/routes/users/[id]/index.js']\n * // ]\n *\n * @example\n * // With nested dynamic routes\n * // ./src/routes/users/[userId]/posts/[postId]/index.js\n * const routes = buildRoutes('./src/routes', '/api');\n * // Returns: [\n * // ['/api/users/:userId/posts/:postId/', './src/routes/users/[userId]/posts/[postId]/index.js']\n * // ]\n *\n * @example\n * // With root route\n * // ./src/routes/index.js\n * const routes = buildRoutes('./src/routes', '/api');\n * // Returns: [\n * // ['/api/', './src/routes/index.js']\n * // ]\n */\nfunction buildRoutes(basePath, baseURL) {\n if (!statSync(basePath).isDirectory()) {\n throw new Error(`Base path \"${basePath}\" is not a directory`)\n }\n const result = []\n const queue = [[basePath, baseURL.endsWith('/') ? baseURL : baseURL + '/']]\n while (queue.length > 0) {\n const [currentPath, currentURL] = queue.shift()\n const files = readdirSync(currentPath)\n const indexFile = files.find((file) => file === 'index.js')\n if (indexFile) {\n const indexFilePath = path.resolve(currentPath, indexFile)\n result.push([currentURL, indexFilePath])\n }\n const directories = files\n .filter((file) => statSync(path.resolve(currentPath, file)).isDirectory())\n .map((dir) => path.join(currentPath, dir))\n .sort((a, b) => {\n const aParsed = parseDirectoryPriority(path.basename(a))\n const bParsed = parseDirectoryPriority(path.basename(b))\n\n // Primary sort: by priority (00-99)\n if (aParsed.priority !== bParsed.priority) {\n return aParsed.priority - bParsed.priority\n }\n\n // Secondary sort: static routes before dynamic routes\n if (aParsed.isDynamic !== bParsed.isDynamic) {\n return aParsed.isDynamic ? 1 : -1\n }\n\n // Tertiary sort: alphabetical by name\n return aParsed.name.localeCompare(bParsed.name)\n })\n\n directories.forEach((dir) => {\n const dirInfo = parseDirectoryPriority(path.basename(dir))\n const routeName = dirInfo.name // Use name without priority prefix\n\n let entryURL = joinUrlPaths(currentURL, routeName) + '/'\n if (isPlaceholder(routeName)) {\n entryURL =\n joinUrlPaths(currentURL, replaceUrlPlaceholders(routeName)) + '/'\n }\n queue.push([dir, entryURL])\n })\n }\n return result\n}\n\n/**\n * Composes Express routes from a directory structure with middleware support.\n * This is the main function that processes route mappings, builds middleware dictionaries,\n * and configures an Express router with all discovered routes and middleware.\n *\n * @param {Object} express - The Express module instance\n * @param {Array<Object>} routeMappings - Array of route mapping configurations\n * @param {string} routeMappings[].basePath - Base filesystem path to start scanning\n * @param {string} routeMappings[].baseURL - Base URL path for the routes\n * @param {Object} [options] - Configuration options\n * @param {Object} [options.routerOptions] - Options for the Express router (default: `{ strict: true }` stay with this for best results but be advised it makes paths require to be terminated with `/` )\n * @param {Object} [options.middlewareOptions=undefined] - Options passed to every middleware.\n * @param {Object} [options.controllerOptions=undefined] - Options passed to every controller.\n * @returns {Object} Configured Express router with applied routes\n *\n * @example\n * // Basic usage with a single route mapping\n * const express = require('express');\n * const app = express();\n *\n * const router = composeRoutes(express, [\n * {\n * basePath: './src/routes',\n * baseURL: '/api'\n * }\n * ]);\n *\n * app.use(router);\n * // This will set up all routes found in './src/routes' with their middleware\n *\n * @example\n * // With multiple route mappings\n * const router = composeRoutes(express, [\n * {\n * basePath: './src/api/routes',\n * baseURL: '/api'\n * },\n * {\n * basePath: './src/admin/routes',\n * baseURL: '/admin'\n * }\n * ]);\n *\n * @example\n * // With custom router options\n * const router = composeRoutes(express, [\n * {\n * basePath: './src/routes',\n * baseURL: '/api'\n * }\n * ], {\n * routerOptions: {\n * strict: true,\n * }\n * });\n *\n * @example\n * // With an existing router instance\n * const existingRouter = express.Router();\n * const router = composeRoutes(express, [\n * {\n * basePath: './src/routes',\n * baseURL: '/api'\n * }\n * ], {\n * router: existingRouter\n * });\n */\nasync function composeRoutes(\n express,\n routeMappings,\n options = {\n routerOptions: { strict: true },\n middlewareOptions: undefined,\n controllerOptions: undefined\n }\n) {\n if (!Array.isArray(routeMappings)) {\n routeMappings = [routeMappings]\n }\n const routerOptions = options.routerOptions || { strict: true }\n const middlewareOptions = options.middlewareOptions || undefined\n const controllerOptions = options.controllerOptions || undefined\n const router = express.Router(routerOptions)\n\n for (const { basePath, baseURL } of routeMappings) {\n validatePath(basePath)\n validatePath(baseURL)\n const middlewareFunctionDictionary = await buildMiddlewareDictionary(\n basePath,\n baseURL,\n middlewareOptions\n )\n const routes = buildRoutes(basePath, baseURL)\n\n for (const [url, filepath] of routes) {\n // curry the Router, so that the URL is set to the route, and the Middleware is loaded.\n let curriedRouter = curryObjectMethods(\n router,\n url,\n ...dictionaryKeyStartsWithPath(middlewareFunctionDictionary, url)\n )\n const controllerModule = await loadModule(filepath)\n const controllers = controllerModule\n if (typeof controllers !== 'function') {\n throw new Error(`Controller at ${filepath} must export a function`)\n }\n curriedRouter = controllers(curriedRouter, controllerOptions)\n if (!curriedRouter?._getOriginalObject) {\n throw new Error(\n `Controller at ${filepath} did not return a valid router (returned: ${curriedRouter})`\n )\n }\n }\n }\n\n return router\n}\n\n// Define exports object\nconst moduleExports = {\n loadModule,\n isMiddlewareFile,\n autoBox,\n replaceUrlPlaceholders,\n isPlaceholder,\n validatePath,\n joinUrlPaths,\n parseDirectoryPriority,\n normalizeMiddlewarePriority,\n sortMiddlewareFunctions,\n dictionaryKeyStartsWithPath,\n curryObjectMethods,\n buildMiddlewareDictionary,\n buildRoutes,\n composeRoutes,\n default: composeRoutes // For ESM default import compatibility and unfortuntely, rollup does not allow any way of exporting otherwise.\n}\n\n// Export for CommonJS\nmodule.exports = moduleExports\n\n//\n// // CommonJS exports with ESM compatibility\n// const moduleExports = {\n// loadModule,\n// isMiddlewareFile,\n// autoBox,\n// replaceUrlPlaceholders,\n// isPlaceholder,\n// validatePath,\n// joinUrlPaths,\n// parseDirectoryPriority,\n// normalizeMiddlewarePriority,\n// sortMiddlewareFunctions,\n// dictionaryKeyStartsWithPath,\n// curryObjectMethods,\n// buildMiddlewareDictionary,\n// buildRoutes,\n// composeRoutes,\n// default: composeRoutes // Main export for ESM compatibility\n// }\n//\n// // Export all named functions\n// Object.assign(module.exports, moduleExports)\n// // Also export as default for ESM compatibility\n// module.exports.default = composeRoutes\n","import { getDefaultExportFromCjs } from \"\u0000commonjsHelpers.js\";\nimport { __require as requireSrc } from \"/Users/psenger/Developer/express-auto-router/src/index.js\";\nvar srcExports = requireSrc();\nexport { srcExports as __moduleExports };\nexport default /*@__PURE__*/getDefaultExportFromCjs(srcExports);"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,CAAA,MAAM,IAAI,GAAG;AACb,CAAA,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,GAAG;;AAElC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;CACA,SAAS,UAAU,CAAC,UAAU,EAAE;AAChC;AACA,GAAE,IAAI,OAAO,OAAO,KAAK,WAAW,EAAE;AACtC,KAAI,IAAI;AACR;OACM,OAAO,QAAQ,UAAU;MAC1B,CAAC,OAAO,YAAY,EAAE;AAC3B;AACA,OAAM,IAAI;AACV,SAAQ,MAAM,aAAa,GAAG,IAAI,QAAQ;AAC1C,WAAU,YAAY;WACZ;AACV;SACQ,OAAO,aAAa,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK;AAC1D;AACA,WAAU,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS,EAAE;aAChC,OAAO,MAAM,CAAC;AAC1B;AACA,WAAU,OAAO;UACR;QACF,CAAC,OAAO,WAAW,EAAE;AAC5B,SAAQ,OAAO,CAAC,IAAI,CAAC,wBAAwB,EAAE,WAAW;SAClD,YAAY,CAAC,KAAK,GAAG;AAC7B,SAAQ,MAAM;AACd;AACA;AACA,IAAG,MAAM;AACT;AACA,KAAI,IAAI;AACR,OAAM,MAAM,aAAa,GAAG,IAAI,QAAQ;AACxC,SAAQ,YAAY;SACZ;AACR;OACM,OAAO,aAAa,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK;AACxD;AACA,SAAQ,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS,EAAE;WAChC,OAAO,MAAM,CAAC;AACxB;AACA,SAAQ,OAAO;QACR;MACF,CAAC,OAAO,WAAW,EAAE;AAC1B,OAAM,OAAO,CAAC,IAAI,CAAC,wBAAwB,EAAE,WAAW;AACxD,OAAM,MAAM;AACZ;AACA;AACA;;AAEA,CAAA,MAAM,iBAAiB,GAAG;AAC1B,CAAA,MAAM,wBAAwB,GAAG;;AAEjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;CACA,SAAS,gBAAgB,CAAC,KAAK,EAAE;GAC/B,OAAO,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,KAAK;AAC1C;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;CACA,SAAS,OAAO,CAAC,GAAG,EAAE;GACpB,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG;AACxC;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;CACA,SAAS,sBAAsB,CAAC,OAAO,EAAE;GACvC,OAAO,OAAO,CAAC,OAAO;AACxB,KAAI,wBAAwB;KACxB,CAAC,KAAK,EAAE,QAAQ,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC;AACtC;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;CACA,SAAS,aAAa,CAAC,OAAO,EAAE;AAChC,GAAE,OAAO,iBAAiB,CAAC,IAAI,CAAC,OAAO;AACvC;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;CACA,SAAS,YAAY,CAAC,IAAI,EAAE;GAC1B,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,IAAI,EAAE;AACzC,KAAI,MAAM,IAAI,KAAK,CAAC,uBAAuB;AAC3C;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAC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"}