fix(router): validate nested routes (#13224)

Fixes #12827
This commit is contained in:
Dzmitry Shylovich
2016-12-06 21:41:01 +03:00
committed by Alex Rickabaugh
parent 393c1007a8
commit 2893c2c0a2
3 changed files with 72 additions and 26 deletions

View File

@ -336,21 +336,23 @@ export interface Route {
canLoad?: any[];
data?: Data;
resolve?: ResolveData;
children?: Route[];
children?: Routes;
loadChildren?: LoadChildren;
}
export function validateConfig(config: Routes): void {
export function validateConfig(config: Routes, parentPath: string = ''): void {
// forEach doesn't iterate undefined values
for (let i = 0; i < config.length; i++) {
validateNode(config[i]);
const route: Route = config[i];
const fullPath: string = getFullPath(parentPath, route);
validateNode(route, fullPath);
}
}
function validateNode(route: Route): void {
function validateNode(route: Route, fullPath: string): void {
if (!route) {
throw new Error(`
Invalid route configuration: Encountered undefined route.
Invalid configuration of route '${fullPath}': Encountered undefined route.
The reason might be an extra comma.
Example:
@ -362,52 +364,69 @@ function validateNode(route: Route): void {
`);
}
if (Array.isArray(route)) {
throw new Error(`Invalid route configuration: Array cannot be specified`);
throw new Error(`Invalid configuration of route '${fullPath}': Array cannot be specified`);
}
if (!route.component && (route.outlet && route.outlet !== PRIMARY_OUTLET)) {
throw new Error(
`Invalid route configuration of route '${route.path}': a componentless route cannot have a named outlet set`);
`Invalid configuration of route '${fullPath}': a componentless route cannot have a named outlet set`);
}
if (route.redirectTo && route.children) {
throw new Error(
`Invalid configuration of route '${route.path}': redirectTo and children cannot be used together`);
`Invalid configuration of route '${fullPath}': redirectTo and children cannot be used together`);
}
if (route.redirectTo && route.loadChildren) {
throw new Error(
`Invalid configuration of route '${route.path}': redirectTo and loadChildren cannot be used together`);
`Invalid configuration of route '${fullPath}': redirectTo and loadChildren cannot be used together`);
}
if (route.children && route.loadChildren) {
throw new Error(
`Invalid configuration of route '${route.path}': children and loadChildren cannot be used together`);
`Invalid configuration of route '${fullPath}': children and loadChildren cannot be used together`);
}
if (route.redirectTo && route.component) {
throw new Error(
`Invalid configuration of route '${route.path}': redirectTo and component cannot be used together`);
`Invalid configuration of route '${fullPath}': redirectTo and component cannot be used together`);
}
if (route.path && route.matcher) {
throw new Error(
`Invalid configuration of route '${route.path}': path and matcher cannot be used together`);
`Invalid configuration of route '${fullPath}': path and matcher cannot be used together`);
}
if (route.redirectTo === void 0 && !route.component && !route.children && !route.loadChildren) {
throw new Error(
`Invalid configuration of route '${route.path}': one of the following must be provided (component or redirectTo or children or loadChildren)`);
`Invalid configuration of route '${fullPath}'. One of the following must be provided: component, redirectTo, children or loadChildren`);
}
if (route.path === void 0 && route.matcher === void 0) {
throw new Error(
`Invalid route configuration: routes must have either a path or a matcher specified`);
`Invalid configuration of route '${fullPath}': routes must have either a path or a matcher specified`);
}
if (typeof route.path === 'string' && route.path.charAt(0) === '/') {
throw new Error(
`Invalid route configuration of route '${route.path}': path cannot start with a slash`);
throw new Error(`Invalid configuration of route '${fullPath}': path cannot start with a slash`);
}
if (route.path === '' && route.redirectTo !== void 0 && route.pathMatch === void 0) {
const exp =
`The default value of 'pathMatch' is 'prefix', but often the intent is to use 'full'.`;
throw new Error(
`Invalid route configuration of route '{path: "${route.path}", redirectTo: "${route.redirectTo}"}': please provide 'pathMatch'. ${exp}`);
`Invalid configuration of route '{path: "${fullPath}", redirectTo: "${route.redirectTo}"}': please provide 'pathMatch'. ${exp}`);
}
if (route.pathMatch !== void 0 && route.pathMatch !== 'full' && route.pathMatch !== 'prefix') {
throw new Error(
`Invalid configuration of route '${route.path}': pathMatch can only be set to 'prefix' or 'full'`);
`Invalid configuration of route '${fullPath}': pathMatch can only be set to 'prefix' or 'full'`);
}
if (route.children) {
validateConfig(route.children, fullPath);
}
}
function getFullPath(parentPath: string, currentRoute: Route): string {
if (!currentRoute) {
return parentPath;
}
if (!parentPath && !currentRoute.path) {
return '';
} else if (parentPath && !currentRoute.path) {
return `${parentPath}/`;
} else if (!parentPath && currentRoute.path) {
return currentRoute.path;
} else {
return `${parentPath}/${currentRoute.path}`;
}
}