@softwarecitadel/girouette
Version:
An AdonisJS package allowing decorators-based routing.
266 lines (265 loc) • 9.79 kB
JavaScript
import 'reflect-metadata';
import { cwd } from 'node:process';
import { join, relative } from 'node:path';
import { readdir } from 'node:fs/promises';
import { pathToFileURL } from 'node:url';
import { REFLECT_RESOURCE_KEY, REFLECT_RESOURCE_MIDDLEWARE_KEY, REFLECT_RESOURCE_NAME_KEY, REFLECT_ROUTES_KEY, REFLECT_GROUP_KEY, REFLECT_GROUP_MIDDLEWARE_KEY, REFLECT_GROUP_DOMAIN_KEY, REFLECT_RESOURCE_ONLY_KEY, REFLECT_RESOURCE_EXCEPT_KEY, REFLECT_RESOURCE_API_ONLY_KEY, } from '../src/constants.js';
/**
* The GirouetteProvider is responsible for registering all decorated routes with AdonisJS.
* It scans the application's controllers directory and processes route decorators,
* resource decorators, and group configurations.
*
* @example
* ```ts
* // In your adonisrc.ts
* providers: [
* () => import('@adonisjs/core/providers/app_provider'),
* () => import('./providers/girouette_provider')
* ]
* ```
*/
export default class GirouetteProvider {
app;
constructor(app) {
this.app = app;
}
/**
* Sets the path to the controllers
*/
set controllersPath(path) {
this.
}
/**
* Boot the provider when the application is ready
*/
async boot() {
// Provider is booted
}
/**
* Starts the provider by initializing the router and registering all routes
*/
async start() {
this.
this.
await this.
}
/**
* Recursively scans the directory for controller files and registers their routes
*/
async
const files = await readdir(directory, { withFileTypes: true });
for (const file of files) {
const fullPath = join(directory, file.name);
if (file.isDirectory()) {
await this.
continue;
}
if (this.
await this.
}
}
}
/**
* Checks if a file is a controller file based on its name
*/
return fileName.endsWith('_controller.ts') || fileName.endsWith('_controller.js');
}
/**
* Processes a controller file by importing it and registering its routes
*/
async
try {
const path = pathToFileURL(filePath);
const controllerToProcess = {
controller: await import(path.href),
importUrl: path,
};
this.
this.
}
catch (error) {
this.
}
}
/**
* Registers all decorated routes from a controller
*/
try {
const routes = Reflect.getMetadata(REFLECT_ROUTES_KEY, controller.controller.default);
if (!routes)
return;
for (const methodName in routes) {
this.
}
}
catch (error) {
this.
}
}
/**
* Registers a single route with the AdonisJS router, applying any group configurations
*/
try {
const group = Reflect.getMetadata(REFLECT_GROUP_KEY, controller.controller.default);
const groupMiddleware = Reflect.getMetadata(REFLECT_GROUP_MIDDLEWARE_KEY, controller.controller.default);
const groupDomain = Reflect.getMetadata(REFLECT_GROUP_DOMAIN_KEY, controller.controller.default);
const finalRoute = this.
const adonisRoute = this.
this.
}
catch (error) {
this.
}
}
/**
* Applies group configuration to a route
*/
if (!group && !groupMiddleware)
return route;
return {
...route,
pattern: group?.prefix
? this.
: route.pattern,
name: group?.name ? this.
middleware: this.
};
}
/**
* Prefixes a route pattern with a group prefix
*/
const cleanPrefix = prefix.startsWith('/') ? prefix : `/${prefix}`;
const cleanPattern = pattern.startsWith('/') ? pattern.slice(1) : pattern;
return `${cleanPrefix}/${cleanPattern}`;
}
/**
* Prefixes a route name with a group prefix
*/
return name ? `${prefix}.${name}` : name;
}
/**
* Merges route-specific middleware with group middleware
*/
const middleware = [...(routeMiddleware || [])];
if (groupMiddleware) {
if (Array.isArray(groupMiddleware)) {
middleware.unshift(...groupMiddleware);
}
else {
middleware.unshift(groupMiddleware);
}
}
return middleware;
}
/**
* Creates a new route in the AdonisJS router
*/
const relativePath = relative(this.app.appRoot.pathname, controller.importUrl.pathname)
.replaceAll('\\', '/')
.replace(/\.ts$/, '.js');
return this.
}
/**
* Configures a route with its name, constraints, middleware and domain
*/
if (route.name) {
adonisRoute.as(route.name);
}
if (route.where?.length) {
this.
}
if (route.middleware?.length) {
this.
}
if (domain) {
adonisRoute.domain(domain);
}
}
/**
* Applies route constraints (where clauses)
*/
for (const { key, matcher } of constraints) {
route.where(key, matcher);
}
}
/**
* Applies middleware to a route
*/
for (const m of middleware) {
route.use(m);
}
}
/**
* Registers resource routes for a controller
*/
try {
const resourcePattern = Reflect.getMetadata(REFLECT_RESOURCE_KEY, controller.controller.default);
if (!resourcePattern)
return;
const relativePath = relative(this.app.appRoot.pathname, controller.importUrl.pathname)
.replaceAll('\\', '/')
.replace(/\.ts$/, '.js');
const resource = this.
this.
}
catch (error) {
this.
}
}
/**
* Configures a resource with its name and middleware
*/
try {
const resourceName = Reflect.getMetadata(REFLECT_RESOURCE_NAME_KEY, controller.controller.default);
if (resourceName) {
resource.as(resourceName);
}
const resourceMiddleware = Reflect.getMetadata(REFLECT_RESOURCE_MIDDLEWARE_KEY, controller.controller.default);
if (resourceMiddleware) {
this.
}
this.
}
catch (error) {
this.
}
}
/**
* Applies middleware to resource routes
*/
for (const { actions, middleware } of middlewareConfig) {
resource.middleware(actions, middleware);
}
}
const apiOnly = Reflect.getMetadata(REFLECT_RESOURCE_API_ONLY_KEY, controller.controller.default);
if (apiOnly) {
resource.apiOnly();
}
const only = Reflect.getMetadata(REFLECT_RESOURCE_ONLY_KEY, controller.controller.default);
if (only) {
resource.only(only);
}
const except = Reflect.getMetadata(REFLECT_RESOURCE_EXCEPT_KEY, controller.controller.default);
if (except) {
resource.except(except);
}
}
}