yml-mvc-router
Version:
A configurable, Express-compatible routing module that maps routes from YAML to controllers following the MVC pattern
91 lines (82 loc) • 2.93 kB
JavaScript
const yaml = require('js-yaml');
/**
* Parses YAML routes file into normalized route objects
*/
class Parser {
constructor(config) {
this.config = config;
}
/**
* Parse YAML content into route definitions
* @param {string} yamlContent - Raw YAML content
* @returns {Array} Array of normalized route objects
*/
parse(yamlContent) {
const parsed = yaml.load(yamlContent);
if (!parsed || typeof parsed !== 'object') {
throw new Error('Invalid YAML: must contain route definitions');
}
const routes = [];
this._processRoutes(parsed, routes);
return routes;
}
/**
* Process route definitions recursively (handles groups)
* @param {Object} routesDef - Route definitions from YAML
* @param {Array} routes - Output array to populate
* @param {string} prefix - Current route prefix
* @param {Array} middlewares - Inherited middlewares
*/
_processRoutes(routesDef, routes, prefix = '', middlewares = []) {
for (const [pattern, definition] of Object.entries(routesDef)) {
if (definition.children) {
// Handle route groups
const groupPrefix = prefix + (definition.prefix || pattern);
const groupMiddlewares = [...middlewares, ...(definition.middlewares || [])];
this._processRoutes(definition.children, routes, groupPrefix, groupMiddlewares);
} else {
// Handle individual routes
const route = this._normalizeRoute(pattern, definition, prefix, middlewares);
routes.push(route);
}
}
}
/**
* Normalize a single route definition
* @param {string} pattern - Route pattern
* @param {Object} definition - Route definition
* @param {string} prefix - Route prefix
* @param {Array} inheritedMiddlewares - Inherited middlewares
* @returns {Object} Normalized route object
*/
_normalizeRoute(pattern, definition, prefix = '', inheritedMiddlewares = []) {
if (typeof definition === 'string') {
// Simple string format: "/path": "ControllerName.action"
definition = { controller: definition };
}
const fullPath = prefix + pattern;
const method = (definition.method || 'GET').toUpperCase();
const middlewares = [...inheritedMiddlewares, ...(definition.middlewares || [])];
// Parse controller.action
let controllerName, actionName;
if (definition.controller) {
const parts = definition.controller.split('.');
controllerName = parts[0];
actionName = parts[1] || 'index';
} else {
throw new Error(`Route ${fullPath} missing controller definition`);
}
return {
path: fullPath,
method,
controller: controllerName,
action: actionName,
middlewares,
assets: definition.assets || {},
name: definition.name,
response: definition.response, // 'json' | 'view' | undefined (auto-detect)
raw: definition
};
}
}
module.exports = Parser;