chore(router): clean up code and add comments (#23355)

This commit is contained in:
Victor Berchet
2021-05-28 05:57:52 -07:00
committed by GitHub
parent fee4e82f0e
commit 857550a0c4
4 changed files with 60 additions and 59 deletions

View File

@ -2,6 +2,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Listen, Me
import { AnimationBuilder, BackButtonEvent, RouteChain, RouterDirection, RouterEventDetail } from '../../interface';
import { debounce } from '../../utils/helpers';
import { NavigationHookResult } from '../route/route-interface';
import { ROUTER_INTENT_BACK, ROUTER_INTENT_FORWARD, ROUTER_INTENT_NONE } from './utils/constants';
import { printRedirects, printRoutes } from './utils/debug';
@ -186,6 +187,7 @@ export class Router implements ComponentInterface {
return true;
}
// This handler gets called when a `ion-route-redirect` component is added to the DOM or if the from or to property of such node changes.
private onRedirectChanged() {
const path = this.getPath();
if (path && findRouteRedirect(path, readRedirects(this.el))) {
@ -193,6 +195,7 @@ export class Router implements ComponentInterface {
}
}
// This handler gets called when a `ion-route` component is added to the DOM or if the from or to property of such node changes.
private onRoutesChanged() {
return this.writeNavStateRoot(this.getPath(), ROUTER_INTENT_NONE);
}
@ -202,7 +205,7 @@ export class Router implements ComponentInterface {
if (win.history.state === null) {
this.state++;
win.history.replaceState(this.state, win.document.title, win.document.location && win.document.location.href);
win.history.replaceState(this.state, win.document.title, win.document.location?.href);
}
const state = win.history.state;
@ -211,11 +214,11 @@ export class Router implements ComponentInterface {
if (state > lastState || (state >= lastState && lastState > 0)) {
return ROUTER_INTENT_FORWARD;
} else if (state < lastState) {
}
if (state < lastState) {
return ROUTER_INTENT_BACK;
} else {
return ROUTER_INTENT_NONE;
}
return ROUTER_INTENT_NONE;
}
private async writeNavStateRoot(path: string[] | null, direction: RouterDirection, animation?: AnimationBuilder): Promise<boolean> {
@ -281,7 +284,7 @@ export class Router implements ComponentInterface {
//
// When the beforeLeave hook does not return true (to allow navigating) then that value is returned early and the beforeEnter is executed.
// Otherwise the beforeEnterHook hook of the target route is executed.
private async runGuards(to: string[] | null = this.getPath(), from?: string[] | null) {
private async runGuards(to: string[] | null = this.getPath(), from?: string[] | null): Promise<NavigationHookResult> {
if (from === undefined) {
from = parsePath(this.previousPath).segments;
}

View File

@ -88,5 +88,5 @@ const searchNavNode = (root: HTMLElement | undefined): NavOutletElement | undefi
return root as NavOutletElement;
}
const outlet = root.querySelector<NavOutletElement>(QUERY);
return outlet ? outlet : undefined;
return outlet ?? undefined;
};

View File

@ -1,6 +1,16 @@
import { RouteChain, RouteNode, RouteRedirect, RouteTree } from './interface';
import { parsePath } from './path';
const readProp = (el: HTMLElement, prop: string): string | null | undefined => {
if (prop in el) {
return (el as any)[prop];
}
if (el.hasAttribute(prop)) {
return el.getAttribute(prop);
}
return null;
};
export const readRedirects = (root: Element): RouteRedirect[] => {
return (Array.from(root.children) as HTMLIonRouteRedirectElement[])
.filter(el => el.tagName === 'ION-ROUTE-REDIRECT')
@ -17,46 +27,33 @@ export const readRoutes = (root: Element): RouteChain[] => {
return flattenRouterTree(readRouteNodes(root));
};
export const readRouteNodes = (root: Element, node = root): RouteTree => {
export const readRouteNodes = (node: Element): RouteTree => {
return (Array.from(node.children) as HTMLIonRouteElement[])
.filter(el => el.tagName === 'ION-ROUTE' && el.component)
.map(el => {
const component = readProp(el, 'component');
if (component == null) {
throw new Error('component missing in ion-route');
}
const component = readProp(el, 'component') as string;
return {
path: parsePath(readProp(el, 'url')).segments,
id: component.toLowerCase(),
params: el.componentProps,
beforeLeave: el.beforeLeave,
beforeEnter: el.beforeEnter,
children: readRouteNodes(root, el)
children: readRouteNodes(el)
};
});
};
export const readProp = (el: HTMLElement, prop: string): string | null | undefined => {
if (prop in el) {
return (el as any)[prop];
}
if (el.hasAttribute(prop)) {
return el.getAttribute(prop);
}
return null;
};
export const flattenRouterTree = (nodes: RouteTree): RouteChain[] => {
const routes: RouteChain[] = [];
const chains: RouteChain[] = [];
for (const node of nodes) {
flattenNode([], routes, node);
flattenNode([], chains, node);
}
return routes;
return chains;
};
const flattenNode = (chain: RouteChain, routes: RouteChain[], node: RouteNode) => {
const s = chain.slice();
s.push({
const flattenNode = (chain: RouteChain, chains: RouteChain[], node: RouteNode) => {
chain = chain.slice();
chain.push({
id: node.id,
path: node.path,
params: node.params,
@ -65,10 +62,10 @@ const flattenNode = (chain: RouteChain, routes: RouteChain[], node: RouteNode) =
});
if (node.children.length === 0) {
routes.push(s);
chains.push(chain);
return;
}
for (const sub of node.children) {
flattenNode(s, routes, sub);
for (const child of node.children) {
flattenNode(chain, chains, child);
}
};

View File

@ -1,6 +1,7 @@
import { ROUTER_INTENT_FORWARD } from './constants';
import { ParsedRoute, RouteChain, RouterDirection } from './interface';
// Join the non empty segments with "/".
export const generatePath = (segments: string[]): string => {
const path = segments
.filter(s => s.length > 0)
@ -9,6 +10,26 @@ export const generatePath = (segments: string[]): string => {
return '/' + path;
};
const generateUrl = (segments: string[], useHash: boolean, queryString?: string) => {
let url = generatePath(segments);
if (useHash) {
url = '#' + url;
}
if (queryString !== undefined) {
url += '?' + queryString;
}
return url;
}
export const writePath = (history: History, root: string, useHash: boolean, path: string[], direction: RouterDirection, state: number, queryString?: string) => {
const url = generateUrl([...parsePath(root).segments, ...path], useHash, queryString);
if (direction === ROUTER_INTENT_FORWARD) {
history.pushState(state, '', url);
} else {
history.replaceState(state, '', url);
}
};
export const chainToPath = (chain: RouteChain): string[] | null => {
const path = [];
for (const route of chain) {
@ -27,25 +48,12 @@ export const chainToPath = (chain: RouteChain): string[] | null => {
return path;
};
export const writePath = (history: History, root: string, useHash: boolean, path: string[], direction: RouterDirection, state: number, queryString?: string) => {
let url = generatePath([
...parsePath(root).segments,
...path
]);
if (useHash) {
url = '#' + url;
}
if (queryString !== undefined) {
url = url + '?' + queryString;
}
if (direction === ROUTER_INTENT_FORWARD) {
history.pushState(state, '', url);
} else {
history.replaceState(state, '', url);
}
};
export const removePrefix = (prefix: string[], path: string[]): string[] | null => {
// Remove the prefix segments from the path segments.
//
// Return:
// - null when the path segments do not start with the passed prefix,
// - the path segments after the prefix otherwise.
const removePrefix = (prefix: string[], path: string[]): string[] | null => {
if (prefix.length > path.length) {
return null;
}
@ -53,7 +61,7 @@ export const removePrefix = (prefix: string[], path: string[]): string[] | null
return path;
}
for (let i = 0; i < prefix.length; i++) {
if (prefix[i].length > 0 && prefix[i] !== path[i]) {
if (prefix[i] !== path[i]) {
return null;
}
}
@ -64,15 +72,8 @@ export const removePrefix = (prefix: string[], path: string[]): string[] | null
};
export const readPath = (loc: Location, root: string, useHash: boolean): string[] | null => {
let pathname = loc.pathname;
if (useHash) {
const hash = loc.hash;
pathname = (hash[0] === '#')
? hash.slice(1)
: '';
}
const prefix = parsePath(root).segments;
const pathname = useHash ? loc.hash.slice(1) : loc.pathname;
const path = parsePath(pathname).segments;
return removePrefix(prefix, path);
};