UNPKG

express-enrouten

Version:

An express route initialization and configuration module.

125 lines (101 loc) 3.74 kB
'use strict'; var fs = require('fs'); var path = require('path'); var registry = require('./registry'); var debug = require('debuglog')('enrouten/directory'); module.exports = function directory(router, basedir, options) { var handler; handler = createFileHandler(router, options); traverse(basedir, '', '', handler); return router; }; /** * Traverses the provided basedir, invoking the provided function when a file * is encountered. * @param basedir the root directory where the traversal should begin * @param ancesors the relative path from the basedir to the current dir * @param current the current directory name * @param fn the function to invoke when a file is encountered: `function (basedir, ancestors, current)` */ function traverse(basedir, ancestors, current, fn) { var abs, stat; abs = path.join(basedir, ancestors, current); stat = fs.statSync(abs); if (stat.isDirectory()) { ancestors = ancestors ? path.join(ancestors, current) : current; fs.readdirSync(abs).forEach(function (child) { traverse(basedir, ancestors, child, fn); }); } if (stat.isFile()) { fn(basedir, ancestors, current); } } /** * Factory function that produces a fn implementation to * provide to the directory handler. Filters file/module * for the desired API `function(router)`, determines mount * point and mounts the router. * @param router the express Router against which child routers are mounted * @param options the options object to pass to each express Router created * @returns {Function} the implementation function to provide to direc`tory */ function createFileHandler(router, options) { return function handler(basedir, ancestors, current) { var abs, impl, filename; function mount(impl, filename) { var mountpath, subrouter; if (typeof impl === 'function' && impl.length === 1) { // Build current pount path, ignoring `index` in lieu of `/` mountpath = ancestors ? ancestors.split(path.sep) : []; filename !== 'index' && mountpath.push(filename); mountpath = '/' + mountpath.join('/'); debug('mounting', current, 'at', mountpath); subrouter = registry(mountpath, router, options); impl(subrouter); router.use(mountpath, subrouter._router); return true; } } abs = path.join(basedir, ancestors, current); filename = path.basename(current, path.extname(current)); if (isFileModule(abs)) { impl = require(abs); if (!mount(impl, filename)) { if (impl && impl.default) { mount(impl.default, filename); } } } }; } /** * Returns true if `require` is able to load the provided file * or false if not. * http://nodejs.org/api/modules.html#modules_file_modules * @param file the file for which to determine module-ness. * @returns {boolean} */ function isFileModule(file) { var ext = path.extname(file); // Omit dotfiles // NOTE: Temporary fix in lieu of more complete (cross platform) // fix using file flags/attributes. if (path.basename(file, ext)[0] === '.') { return false; } if (ext !== '' && !isValidExtension(ext)) { return false; } return true; } /** * Returns true if `ext` is an extension with a registered handler * in module.extensions. * @param ext the file extension to check * @returns {boolean} */ function isValidExtension(ext) { var keys = Object.keys(require.extensions); return keys.indexOf(ext) !== -1; }