vasille-router
Version:
The first Developer eXperience Orientated front-end framework (router library).
91 lines (90 loc) • 3.24 kB
JavaScript
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;
}
}