fix(router): allow UrlMatcher to return null (#36402)

The matcher is allowed to return null per
https://angular.io/api/router/UrlMatcher#usage-notes

And run `yarn gulp format` to pick up recent clang format changes.

Closes #29824

BREAKING CHANGE: UrlMatcher's type now reflects that it could always return
    null.

    If you implemented your own Router or Recognizer class, please update it to
    handle matcher returning null.

PR Close #36402
This commit is contained in:
Mikel Ward 2020-02-13 13:34:02 -08:00 committed by Kara Erickson
parent a555fdba32
commit 568e9df1d6
2 changed files with 28 additions and 27 deletions

View File

@ -500,7 +500,7 @@ export declare abstract class UrlHandlingStrategy {
abstract shouldProcessUrl(url: UrlTree): boolean; abstract shouldProcessUrl(url: UrlTree): boolean;
} }
export declare type UrlMatcher = (segments: UrlSegment[], group: UrlSegmentGroup, route: Route) => UrlMatchResult; export declare type UrlMatcher = (segments: UrlSegment[], group: UrlSegmentGroup, route: Route) => UrlMatchResult | null;
export declare type UrlMatchResult = { export declare type UrlMatchResult = {
consumed: UrlSegment[]; consumed: UrlSegment[];

View File

@ -36,7 +36,8 @@ export type Routes = Route[];
* @publicApi * @publicApi
*/ */
export type UrlMatchResult = { export type UrlMatchResult = {
consumed: UrlSegment[]; posParams?: {[name: string]: UrlSegment}; consumed: UrlSegment[];
posParams?: {[name: string]: UrlSegment};
}; };
/** /**
@ -64,7 +65,7 @@ export type UrlMatchResult = {
* @publicApi * @publicApi
*/ */
export type UrlMatcher = (segments: UrlSegment[], group: UrlSegmentGroup, route: Route) => export type UrlMatcher = (segments: UrlSegment[], group: UrlSegmentGroup, route: Route) =>
UrlMatchResult; UrlMatchResult|null;
/** /**
* *
@ -109,7 +110,7 @@ export type ResolveData = {
* @see `Route#loadChildren`. * @see `Route#loadChildren`.
* @publicApi * @publicApi
*/ */
export type LoadChildrenCallback = () => Type<any>| NgModuleFactory<any>| Observable<Type<any>>| export type LoadChildrenCallback = () => Type<any>|NgModuleFactory<any>|Observable<Type<any>>|
Promise<NgModuleFactory<any>|Type<any>|any>; Promise<NgModuleFactory<any>|Type<any>|any>;
/** /**
@ -123,7 +124,7 @@ export type LoadChildrenCallback = () => Type<any>| NgModuleFactory<any>| Observ
* @see `Route#loadChildren`. * @see `Route#loadChildren`.
* @publicApi * @publicApi
*/ */
export type LoadChildren = LoadChildrenCallback | DeprecatedLoadChildren; export type LoadChildren = LoadChildrenCallback|DeprecatedLoadChildren;
/** /**
* A string of the form `path/to/file#exportName` that acts as a URL for a set of routes to load. * A string of the form `path/to/file#exportName` that acts as a URL for a set of routes to load.
@ -147,7 +148,7 @@ export type DeprecatedLoadChildren = string;
* @see `RouterLink` * @see `RouterLink`
* @publicApi * @publicApi
*/ */
export type QueryParamsHandling = 'merge' | 'preserve' | ''; export type QueryParamsHandling = 'merge'|'preserve'|'';
/** /**
* *
@ -156,9 +157,9 @@ export type QueryParamsHandling = 'merge' | 'preserve' | '';
* @see `Route#runGuardsAndResolvers` * @see `Route#runGuardsAndResolvers`
* @publicApi * @publicApi
*/ */
export type RunGuardsAndResolvers = 'pathParamsChange' | 'pathParamsOrQueryParamsChange' | export type RunGuardsAndResolvers =
'paramsChange' | 'paramsOrQueryParamsChange' | 'always' | 'pathParamsChange'|'pathParamsOrQueryParamsChange'|'paramsChange'|'paramsOrQueryParamsChange'|
((from: ActivatedRouteSnapshot, to: ActivatedRouteSnapshot) => boolean); 'always'|((from: ActivatedRouteSnapshot, to: ActivatedRouteSnapshot) => boolean);
/** /**
* A configuration object that defines a single route. * A configuration object that defines a single route.
@ -519,36 +520,36 @@ function validateNode(route: Route, fullPath: string): void {
} }
if (!route.component && !route.children && !route.loadChildren && if (!route.component && !route.children && !route.loadChildren &&
(route.outlet && route.outlet !== PRIMARY_OUTLET)) { (route.outlet && route.outlet !== PRIMARY_OUTLET)) {
throw new Error( throw new Error(`Invalid configuration of route '${
`Invalid configuration of route '${fullPath}': a componentless route without children or loadChildren cannot have a named outlet set`); fullPath}': a componentless route without children or loadChildren cannot have a named outlet set`);
} }
if (route.redirectTo && route.children) { if (route.redirectTo && route.children) {
throw new Error( throw new Error(`Invalid configuration of route '${
`Invalid configuration of route '${fullPath}': redirectTo and children cannot be used together`); fullPath}': redirectTo and children cannot be used together`);
} }
if (route.redirectTo && route.loadChildren) { if (route.redirectTo && route.loadChildren) {
throw new Error( throw new Error(`Invalid configuration of route '${
`Invalid configuration of route '${fullPath}': redirectTo and loadChildren cannot be used together`); fullPath}': redirectTo and loadChildren cannot be used together`);
} }
if (route.children && route.loadChildren) { if (route.children && route.loadChildren) {
throw new Error( throw new Error(`Invalid configuration of route '${
`Invalid configuration of route '${fullPath}': children and loadChildren cannot be used together`); fullPath}': children and loadChildren cannot be used together`);
} }
if (route.redirectTo && route.component) { if (route.redirectTo && route.component) {
throw new Error( throw new Error(`Invalid configuration of route '${
`Invalid configuration of route '${fullPath}': redirectTo and component cannot be used together`); fullPath}': redirectTo and component cannot be used together`);
} }
if (route.path && route.matcher) { if (route.path && route.matcher) {
throw new Error( throw new Error(
`Invalid configuration of route '${fullPath}': 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) { if (route.redirectTo === void 0 && !route.component && !route.children && !route.loadChildren) {
throw new Error( throw new Error(`Invalid configuration of route '${
`Invalid configuration of route '${fullPath}'. One of the following must be provided: component, redirectTo, children or loadChildren`); fullPath}'. One of the following must be provided: component, redirectTo, children or loadChildren`);
} }
if (route.path === void 0 && route.matcher === void 0) { if (route.path === void 0 && route.matcher === void 0) {
throw new Error( throw new Error(`Invalid configuration of route '${
`Invalid configuration of route '${fullPath}': routes must have either a path or a matcher specified`); fullPath}': routes must have either a path or a matcher specified`);
} }
if (typeof route.path === 'string' && route.path.charAt(0) === '/') { if (typeof route.path === 'string' && route.path.charAt(0) === '/') {
throw new Error(`Invalid configuration of route '${fullPath}': path cannot start with a slash`); throw new Error(`Invalid configuration of route '${fullPath}': path cannot start with a slash`);
@ -556,12 +557,12 @@ function validateNode(route: Route, fullPath: string): void {
if (route.path === '' && route.redirectTo !== void 0 && route.pathMatch === void 0) { if (route.path === '' && route.redirectTo !== void 0 && route.pathMatch === void 0) {
const exp = const exp =
`The default value of 'pathMatch' is 'prefix', but often the intent is to use 'full'.`; `The default value of 'pathMatch' is 'prefix', but often the intent is to use 'full'.`;
throw new Error( throw new Error(`Invalid configuration of route '{path: "${fullPath}", redirectTo: "${
`Invalid configuration of route '{path: "${fullPath}", redirectTo: "${route.redirectTo}"}': please provide 'pathMatch'. ${exp}`); route.redirectTo}"}': please provide 'pathMatch'. ${exp}`);
} }
if (route.pathMatch !== void 0 && route.pathMatch !== 'full' && route.pathMatch !== 'prefix') { if (route.pathMatch !== void 0 && route.pathMatch !== 'full' && route.pathMatch !== 'prefix') {
throw new Error( throw new Error(`Invalid configuration of route '${
`Invalid configuration of route '${fullPath}': pathMatch can only be set to 'prefix' or 'full'`); fullPath}': pathMatch can only be set to 'prefix' or 'full'`);
} }
if (route.children) { if (route.children) {
validateConfig(route.children, fullPath); validateConfig(route.children, fullPath);