@izzyjs/route
Version:
Use your AdonisJs routes in your Inertia.js application
139 lines (138 loc) • 5.83 kB
JavaScript
/**
* @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');
}