UNPKG

vasille-router

Version:

The first Developer eXperience Orientated front-end framework (router library).

91 lines (90 loc) 3.24 kB
export function composeUrl(route, params) { return Object.entries(params).reduce((link, [key, value]) => { return link.replace(new RegExp(`:${key}\\b`), value); }, route); } export class Router { constructor(init) { this.root = this.createRouting(); this.init = init; for (const route in init.routes) { let it = this.root; const parsedPath = route .split("/") .filter(value => !!value) .map(value => { if (value.startsWith(":")) { return { key: value.substring(1), static: false }; } else { return { key: value, static: true }; } }); const target = init.routes[route]; for (const item of parsedPath) { const placeIn = item.static ? it.static : it.dynamic; const key = item.key; it = placeIn[key] = placeIn[key] ?? this.createRouting(); } // typescript is going crazy here it.self = target; } } navigate(route, params, ...args) { this.doNavigate(composeUrl(route, params), true, ...args); } createRouting() { return { dynamic: {}, static: {}, }; } findTarget(routing, path, params) { if (path.length < 1) { return [routing.self, params]; } const [target, ...subPath] = path; if (target in routing.static) { const match = this.findTarget(routing.static[target], subPath, params); if (match[0]) { return match; } } for (const key in routing.dynamic) { const match = this.findTarget(routing.dynamic[key], subPath, { ...params, [key]: target }); if (match[0]) { return match; } } return [undefined, params]; } targetByUrl(url) { const [path, query, hash] = this.parseUrl(url); const [target, params] = this.findTarget(this.root, path.split("/").filter(v => !!v), {}); return { url, path, query, hash, target, params }; } async prepareNavigation(url, canNavigate, async, ...args) { const { target, ...props } = this.targetByUrl(url); let error = undefined; try { const accessLevel = (await this.init.getAccessLevel?.()) ?? 0; const minLevel = target?.minAccessLevel ?? 0; if (target && accessLevel >= minLevel) { await this.loadTarget(target, props, ...args); } else if (this.init.fallbackScreen) { await this.renderScreen(this.init.fallbackScreen, { cause: target ? "no-access" : "not-found" }, canNavigate ? "not-found" : "fallback", ...args); } else { throw new Error("No fallback screen"); } } catch (e) { if (async || !this.init.errorScreen) { throw e; } await this.renderScreen(this.init.errorScreen, { error: e }, "error", ...args); } return; } }