@izzyjs/route
Version:
Use your AdonisJs routes in your Inertia.js application
221 lines (220 loc) • 6.83 kB
JavaScript
import { isBrowser } from './utils/is_browser.js';
export class Route extends String {
/**
* The complete URL with protocol and domain when baseUrl is configured
* @example
* ```ts
* const route = Route.new('user.show', { id: '1' })
* console.log(route.url) // https://example.com/users/1
* ```
*/
url;
/**
* The HTTP method for the route
*/
method;
/**
* The route parameters as an array
* @example
* ```ts
* const route = Route.new('user.show', { id: '1' })
* console.log(route.params) // ['id']
* ```
*/
params;
/**
* The route name as a string
* @example
* ```ts
* const route = Route.new('user.show', { id: '1' })
* console.log(route.name) // user.show
* ```
*/
name;
/**
* The route pattern
* @example
* ```ts
* const route = Route.new('user.show', { id: '1' })
* console.log(route.pattern) // /users/:id
* ```
*/
pattern;
/**
* The route path with parameters replaced
* @example
* ```ts
* const route = Route.new('user.show', { id: '1' })
* console.log(route.path) // /users/1
* ```
*/
path;
/**
* The query string
* @example
* ```ts
* const route = Route.new('user.show', { id: '1' }, { page: 1 })
* console.log(route.qs.toString()) // page=1
* ```
*/
qs;
constructor(routeName, params, qs = {}, prefix = '') {
const { routes } = Route.izzy();
const exist = routes.find((r) => 'name' in r && r.name === routeName);
if (!exist) {
throw new Error(`Route with name "${routeName}" not found`);
}
const searchParams = new URLSearchParams(qs);
let pattern;
if ('params' in exist && exist.params) {
if (!params) {
throw new Error(`Route "${routeName}" requires parameters: ${exist.params.map((p) => `"${p}"`).join(', ')}`);
}
pattern = Route.replaceRouteParams(exist.path, params);
}
else {
pattern = exist.path;
}
if (searchParams.toString()) {
pattern += `?${searchParams.toString()}`;
}
if (prefix) {
pattern = prefix + pattern;
}
super(pattern);
this.path = pattern;
this.name = exist.name;
this.method = exist.method;
this.params = exist.params;
this.pattern = exist.path;
this.qs = searchParams;
// Generate complete URL with protocol and domain if baseUrl is configured
this.url = this.generateCompleteUrl(pattern, exist.domain);
}
/**
* Generate complete URL with protocol and domain when baseUrl is configured
*/
generateCompleteUrl(path, routeDomain) {
try {
const config = Route.getConfig();
if (config?.baseUrl) {
const baseUrl = new URL(config.baseUrl);
// If route has a specific domain (not 'root'), use that domain
// Otherwise use the baseUrl.host
const host = routeDomain && routeDomain !== 'root' ? routeDomain : baseUrl.host;
return `${baseUrl.protocol}//${host}${path}`;
}
}
catch (error) {
// If baseUrl is invalid, fallback to path only
console.warn('Invalid baseUrl configuration, falling back to path only');
}
return path;
}
/**
* Get the configuration from the global Izzy object
*/
static getConfig() {
try {
if (isBrowser()) {
return window.__izzy_route__?.config || null;
}
else {
return globalThis.__izzy_route__?.config || null;
}
}
catch {
return null;
}
}
static replaceRouteParams(routePath, params) {
return Object.entries(params).reduce((acc, [key, value]) => acc.replace(`:${key}`, value), routePath);
}
static new(routeName, params, qs, prefix) {
if (!routeName) {
return new Routes();
}
return new Route(routeName, params, qs, prefix);
}
/**
* Get the `Route` instance from the global Izzy object
* @returns { GlobalIzzyJs }
*/
static izzy() {
let routes;
let currentRoute;
if (isBrowser()) {
routes = window.__izzy_route__.routes;
currentRoute = window.location.pathname;
}
else {
routes = globalThis.__izzy_route__.routes;
currentRoute = globalThis.__izzy_route__.current;
}
return { routes, current: currentRoute };
}
toString() {
return this.path;
}
}
export class Routes {
currentRoute;
routes;
constructor() {
const { routes, current: currentRoute } = Route.izzy();
this.routes = routes;
this.currentRoute = currentRoute;
}
current(routeName, params) {
if (!routeName) {
return this.currentRoute;
}
if (routeName.includes('*')) {
const regex = new RegExp(routeName.replace(/\*/g, '.*'));
return regex.test(this.currentRoute);
}
const route = Route.new(routeName, params);
return this.currentRoute === route.toString();
}
/**
* Check if the current route has the given route name
* @summary unstable
* @param routeName route name
* @example
* ```ts
* route().has('users.index') // => true or false
* route().has('user.*') // => true or false
* ```
*/
has(routeName) {
if (routeName.includes('*')) {
const regex = new RegExp(routeName.replace(/\*/g, '.*'));
return this.routes.some((r) => 'name' in r && regex.test(r.name));
}
return this.routes.some((r) => 'name' in r && r.name === routeName);
}
get params() {
const route = this.routes.find(({ path }) => {
const regex = new RegExp(path
.split('/')
.map((p) => (p.startsWith(':') ? '([^/]+)' : p))
.join('/'));
return regex.test(this.currentRoute);
});
if (route && route.params) {
const regex = new RegExp(route.path
.split('/')
.map((p) => (p.startsWith(':') ? '([^/]+)' : p))
.join('/'));
const values = this.currentRoute.match(regex);
if (!values) {
return {};
}
return route.params.reduce((acc, param, index) => ({
...acc,
[param]: values[index + 1],
}), {});
}
return {};
}
}