UNPKG

@izzyjs/route

Version:

Use your AdonisJs routes in your Inertia.js application

139 lines (138 loc) 5.83 kB
/** * @izzyjs/route * * (c) IzzyJs - 2024 * For the full license information, please view the LICENSE file that was distributed with this source code. */ import { writeFile } from 'node:fs/promises'; import { existsSync, unlinkSync } from 'node:fs'; import { join } from 'node:path'; import { filterRoutes } from './utils/route_filter.js'; import { detectBuildPath, getRelativeBuildPath } from './utils/path_resolver.js'; export default async function generateRoutes() { const app = await import('@adonisjs/core/services/app').then((m) => m.default); const baseDir = await detectBuildPath(); const jsFile = join(baseDir, 'routes.js'); const dtsFile = join(baseDir, 'routes.d.ts'); console.log(`📁 Generating routes in: ${await getRelativeBuildPath()}`); const routes = await namedRoutes(app); const config = app.config.get('izzyjs.routes'); // Apply route filtering const filteredRoutes = filterRoutes(routes, config); const jsContent = javascriptContent(filteredRoutes, config); const dtsContent = definitionContent(filteredRoutes, config); if (existsSync(jsFile)) { unlinkSync(jsFile); } if (existsSync(dtsFile)) { unlinkSync(dtsFile); } await writeFile(jsFile, jsContent, 'utf-8'); await writeFile(dtsFile, dtsContent, 'utf-8'); console.log(`✅ Generated ${filteredRoutes.length} routes (${routes.length - filteredRoutes.length} filtered out)`); } export async function namedRoutes(app) { const router = await app.container.make('router'); router.commit(); const output = router.toJSON(); const bucket = Object.entries(output).reduce((acc, [domain, routes]) => { for (const route of routes) { if (!route.name) { console.warn(`Route ${route.pattern} has no name`); continue; } const params = route.pattern.match(/:\w+/g)?.map((param) => param.slice(1)); acc.push({ name: route.name, path: route.pattern, params: params ?? [], method: route.methods[0].toLowerCase(), domain, }); } return acc; }, []); if (bucket.length === 0) { throw new Error('No named routes found see: https://docs.adonisjs.com/guides/routing#route-identifier'); } return bucket; } export function javascriptContent(bucket, routeConfig) { const output = JSON.stringify(bucket, null, '\t'); let content = [ '/* eslint-disable prettier/prettier */\n// Generated automatically by named routes hook\n/* DO NOT EDIT THIS FILE DIRECTLY */', `export const routes = ${output};`, ]; // Add groups if configured if (routeConfig?.groups) { const groups = {}; for (const [groupName, patterns] of Object.entries(routeConfig.groups)) { groups[groupName] = bucket.filter((route) => patterns.some((pattern) => { const regexPattern = pattern.replace(/\./g, '\\.').replace(/\*/g, '.*'); const regex = new RegExp(`^${regexPattern}$`); return regex.test(route.name); })); } content.push(`export const groups = ${JSON.stringify(groups, null, '\t')};`); } return content.join('\n\n'); } export function definitionContent(bucket, routeConfig) { const makeRouteType = ({ method, path, name, params, domain }) => { if (params && params.length > 0) { const routeType = [ '\t{', `\t\treadonly name: '${name}';`, `\t\treadonly path: '${path}';`, `\t\treadonly method: '${method}';`, `\t\treadonly params: readonly ['${params.join("','")}'];`, `\t\treadonly domain: '${domain}';`, '\t}', ]; return routeType.join('\n'); } const routeType = [ '\t{', `\t\treadonly name: '${name}';`, `\t\treadonly path: '${path}';`, `\t\treadonly method: '${method}';`, `\t\treadonly domain: '${domain}';`, '\t}', ]; return routeType.join('\n'); }; const output = bucket.map(makeRouteType).join(',\n'); let content = [ '// Generated automatically by @izzyjs/route\n// Do not modify this file', 'export declare const routes: readonly [', ` ${output}`, '];', 'export type Routes = typeof routes;', 'export type Route = Routes[number];', 'export type RouteWithName = Extract<Route, { name: string }>;', 'export type RouteWithParams = Extract<Route, { params: ReadonlyArray<string>; }>;', "export type RouteName = Exclude<RouteWithName['name'], ''>;", ]; // Add groups types if configured (strongly typed to selected routes) if (routeConfig?.groups) { content.push(''); content.push('// Route groups'); content.push('export declare const groups: {'); for (const [groupName, patterns] of Object.entries(routeConfig.groups)) { const groupRoutes = bucket.filter((route) => { return patterns.some((pattern) => { const regexPattern = pattern.replace(/\./g, '\\.').replace(/\*/g, '.*'); const regex = new RegExp(`^${regexPattern}$`); return regex.test(route.name); }); }); const groupOutput = groupRoutes.map(makeRouteType).join(',\n'); content.push(`\t${groupName}: readonly [`); content.push(` ${groupOutput}`); content.push(`\t];`); } content.push('};'); content.push('export type RouteGroups = typeof groups;'); } return content.join('\n'); }