Skip to content

Commit bf5bee6

Browse files
atscottthePunderWoman
authored andcommitted
refactor(router): move around some code to eliminate circular deps (#46752)
This commit eliminates some circular dependencies by moving around interfaces and type guards. PR Close #46752
1 parent e8ae0fe commit bf5bee6

12 files changed

Lines changed: 152 additions & 102 deletions

File tree

packages/core/test/bundling/router/bundle.golden_symbols.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1472,6 +1472,9 @@
14721472
{
14731473
"name": "isNameOnlyAttributeMarker"
14741474
},
1475+
{
1476+
"name": "isNavigationCancelingError"
1477+
},
14751478
{
14761479
"name": "isNodeMatchingSelector"
14771480
},
@@ -1493,6 +1496,9 @@
14931496
{
14941497
"name": "isPromise2"
14951498
},
1499+
{
1500+
"name": "isRedirectingNavigationCancelingError"
1501+
},
14961502
{
14971503
"name": "isScheduler"
14981504
},
@@ -1697,6 +1703,9 @@
16971703
{
16981704
"name": "redirectIfUrlTree"
16991705
},
1706+
{
1707+
"name": "redirectingNavigationError"
1708+
},
17001709
{
17011710
"name": "refCount"
17021711
},

packages/router/src/apply_redirects.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ import {catchError, concatMap, first, last, map, mergeMap, scan, switchMap, tap}
1313
import {RuntimeErrorCode} from './errors';
1414
import {NavigationCancellationCode} from './events';
1515
import {LoadedRouterConfig, Route, Routes} from './models';
16+
import {navigationCancelingError} from './navigation_canceling_error';
1617
import {runCanLoadGuards} from './operators/check_guards';
1718
import {RouterConfigLoader} from './router_config_loader';
18-
import {navigationCancelingError, Params, PRIMARY_OUTLET} from './shared';
19+
import {Params, PRIMARY_OUTLET} from './shared';
1920
import {createRoot, squashSegmentGroup, UrlSegment, UrlSegmentGroup, UrlSerializer, UrlTree} from './url_tree';
2021
import {forEach} from './utils/collection';
2122
import {getOrCreateRouteInjectorIfNeeded, getOutlet, sortByMatchingOutlets} from './utils/config';

packages/router/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ export {RouterLink, RouterLinkWithHref} from './directives/router_link';
1212
export {RouterLinkActive} from './directives/router_link_active';
1313
export {RouterOutlet, RouterOutletContract} from './directives/router_outlet';
1414
export {ActivationEnd, ActivationStart, ChildActivationEnd, ChildActivationStart, Event, EventType, GuardsCheckEnd, GuardsCheckStart, NavigationCancel, NavigationCancellationCode as NavigationCancellationCode, NavigationEnd, NavigationError, NavigationStart, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, RouterEvent, RoutesRecognized, Scroll} from './events';
15-
export {CanActivate, CanActivateChild, CanDeactivate, CanLoad, CanMatch, CanMatchFn, Data, LoadChildren, LoadChildrenCallback, QueryParamsHandling, Resolve, ResolveData, Route, Routes, RunGuardsAndResolvers, UrlMatcher, UrlMatchResult} from './models';
15+
export {CanActivate, CanActivateChild, CanDeactivate, CanLoad, CanMatch, CanMatchFn, Data, LoadChildren, LoadChildrenCallback, NavigationBehaviorOptions, QueryParamsHandling, Resolve, ResolveData, Route, Routes, RunGuardsAndResolvers, UrlMatcher, UrlMatchResult} from './models';
1616
export {DefaultTitleStrategy, TitleStrategy} from './page_title_strategy';
1717
export {BaseRouteReuseStrategy, DetachedRouteHandle, RouteReuseStrategy} from './route_reuse_strategy';
18-
export {Navigation, NavigationBehaviorOptions, NavigationExtras, Router, UrlCreationOptions} from './router';
18+
export {Navigation, NavigationExtras, Router, UrlCreationOptions} from './router';
1919
export {ROUTES} from './router_config_loader';
2020
export {ExtraOptions, InitialNavigation, provideRoutes, ROUTER_CONFIGURATION, ROUTER_INITIALIZER, RouterModule} from './router_module';
2121
export {ChildrenOutletContexts, OutletContext} from './router_outlet_context';

packages/router/src/models.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,3 +1139,56 @@ export interface CanLoad {
11391139

11401140
export type CanLoadFn = (route: Route, segments: UrlSegment[]) =>
11411141
Observable<boolean|UrlTree>|Promise<boolean|UrlTree>|boolean|UrlTree;
1142+
1143+
1144+
/**
1145+
* @description
1146+
*
1147+
* Options that modify the `Router` navigation strategy.
1148+
* Supply an object containing any of these properties to a `Router` navigation function to
1149+
* control how the navigation should be handled.
1150+
*
1151+
* @see [Router.navigate() method](api/router/Router#navigate)
1152+
* @see [Router.navigateByUrl() method](api/router/Router#navigatebyurl)
1153+
* @see [Routing and Navigation guide](guide/router)
1154+
*
1155+
* @publicApi
1156+
*/
1157+
export interface NavigationBehaviorOptions {
1158+
/**
1159+
* When true, navigates without pushing a new state into history.
1160+
*
1161+
* ```
1162+
* // Navigate silently to /view
1163+
* this.router.navigate(['/view'], { skipLocationChange: true });
1164+
* ```
1165+
*/
1166+
skipLocationChange?: boolean;
1167+
1168+
/**
1169+
* When true, navigates while replacing the current state in history.
1170+
*
1171+
* ```
1172+
* // Navigate to /view
1173+
* this.router.navigate(['/view'], { replaceUrl: true });
1174+
* ```
1175+
*/
1176+
replaceUrl?: boolean;
1177+
1178+
/**
1179+
* Developer-defined state that can be passed to any navigation.
1180+
* Access this value through the `Navigation.extras` object
1181+
* returned from the [Router.getCurrentNavigation()
1182+
* method](api/router/Router#getcurrentnavigation) while a navigation is executing.
1183+
*
1184+
* After a navigation completes, the router writes an object containing this
1185+
* value together with a `navigationId` to `history.state`.
1186+
* The value is written when `location.go()` or `location.replaceState()`
1187+
* is called before activating this route.
1188+
*
1189+
* Note that `history.state` does not pass an object equality test because
1190+
* the router adds the `navigationId` on each navigation.
1191+
*
1192+
*/
1193+
state?: {[k: string]: any};
1194+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {NavigationCancellationCode} from './events';
10+
import {NavigationBehaviorOptions} from './models';
11+
import {isUrlTree, UrlSerializer, UrlTree} from './url_tree';
12+
13+
export const NAVIGATION_CANCELING_ERROR = 'ngNavigationCancelingError';
14+
15+
export type NavigationCancelingError =
16+
Error&{[NAVIGATION_CANCELING_ERROR]: true, cancellationCode: NavigationCancellationCode};
17+
export type RedirectingNavigationCancelingError = NavigationCancelingError&{
18+
url: UrlTree;
19+
navigationBehaviorOptions?: NavigationBehaviorOptions;
20+
cancellationCode: NavigationCancellationCode.Redirect;
21+
};
22+
23+
export function redirectingNavigationError(
24+
urlSerializer: UrlSerializer, redirect: UrlTree): RedirectingNavigationCancelingError {
25+
const {redirectTo, navigationBehaviorOptions} =
26+
isUrlTree(redirect) ? {redirectTo: redirect, navigationBehaviorOptions: undefined} : redirect;
27+
const error =
28+
navigationCancelingError(
29+
ngDevMode && `Redirecting to "${urlSerializer.serialize(redirectTo)}"`,
30+
NavigationCancellationCode.Redirect, redirect) as RedirectingNavigationCancelingError;
31+
error.url = redirectTo;
32+
error.navigationBehaviorOptions = navigationBehaviorOptions;
33+
return error;
34+
}
35+
36+
export function navigationCancelingError(
37+
message: string|null|false, code: NavigationCancellationCode, redirectUrl?: UrlTree) {
38+
const error =
39+
new Error('NavigationCancelingError: ' + (message || '')) as NavigationCancelingError;
40+
error[NAVIGATION_CANCELING_ERROR] = true;
41+
error.cancellationCode = code;
42+
if (redirectUrl) {
43+
(error as RedirectingNavigationCancelingError).url = redirectUrl;
44+
}
45+
return error;
46+
}
47+
48+
export function isRedirectingNavigationCancelingError(
49+
error: unknown|
50+
RedirectingNavigationCancelingError): error is RedirectingNavigationCancelingError {
51+
return isNavigationCancelingError(error) && isUrlTree((error as any).url);
52+
}
53+
export function isNavigationCancelingError(error: unknown): error is NavigationCancelingError {
54+
return error && (error as any)[NAVIGATION_CANCELING_ERROR];
55+
}

packages/router/src/operators/check_guards.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ import {EnvironmentInjector, Injector} from '@angular/core';
1010
import {concat, defer, from, MonoTypeOperatorFunction, Observable, of, OperatorFunction, pipe} from 'rxjs';
1111
import {concatMap, first, map, mergeMap, tap} from 'rxjs/operators';
1212

13-
import {ActivationStart, ChildActivationStart, Event, NavigationCancellationCode} from '../events';
13+
import {ActivationStart, ChildActivationStart, Event} from '../events';
1414
import {CanLoad, CanLoadFn, CanMatch, CanMatchFn, Route} from '../models';
15+
import {redirectingNavigationError} from '../navigation_canceling_error';
1516
import {NavigationTransition} from '../router';
1617
import {ActivatedRouteSnapshot, RouterStateSnapshot} from '../router_state';
17-
import {navigationCancelingError, REDIRECTING_CANCELLATION_REASON} from '../shared';
18-
import {UrlSegment, UrlSerializer, UrlTree} from '../url_tree';
18+
import {isUrlTree, UrlSegment, UrlSerializer, UrlTree} from '../url_tree';
1919
import {wrapIntoObservable} from '../utils/collection';
2020
import {CanActivate, CanDeactivate, getCanActivateChild, getToken} from '../utils/preactivation';
21-
import {isBoolean, isCanActivate, isCanActivateChild, isCanDeactivate, isCanLoad, isCanMatch, isFunction, isUrlTree} from '../utils/type_guards';
21+
import {isBoolean, isCanActivate, isCanActivateChild, isCanDeactivate, isCanLoad, isCanMatch} from '../utils/type_guards';
2222

2323
import {prioritizedGuardValue} from './prioritized_guard_value';
2424

@@ -188,10 +188,7 @@ function redirectIfUrlTree(urlSerializer: UrlSerializer):
188188
tap((result: UrlTree|boolean) => {
189189
if (!isUrlTree(result)) return;
190190

191-
const error: Error&{url?: UrlTree} = navigationCancelingError(
192-
REDIRECTING_CANCELLATION_REASON + urlSerializer.serialize(result),
193-
NavigationCancellationCode.Redirect, result);
194-
throw error;
191+
throw redirectingNavigationError(urlSerializer, result);
195192
}),
196193
map(result => result === true),
197194
);

packages/router/src/operators/prioritized_guard_value.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99
import {combineLatest, Observable, OperatorFunction} from 'rxjs';
1010
import {filter, map, scan, startWith, switchMap, take} from 'rxjs/operators';
1111

12-
import {UrlTree} from '../url_tree';
13-
import {isUrlTree} from '../utils/type_guards';
12+
import {isUrlTree, UrlTree} from '../url_tree';
1413

1514
const INITIAL_VALUE = Symbol('INITIAL_VALUE');
1615
declare type INTERIM_VALUES = typeof INITIAL_VALUE | boolean | UrlTree;

packages/router/src/router.ts

Lines changed: 7 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import {createRouterState} from './create_router_state';
1515
import {createUrlTree} from './create_url_tree';
1616
import {RuntimeErrorCode} from './errors';
1717
import {Event, GuardsCheckEnd, GuardsCheckStart, NavigationCancel, NavigationCancellationCode, NavigationEnd, NavigationError, NavigationStart, NavigationTrigger, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, RoutesRecognized} from './events';
18-
import {QueryParamsHandling, Route, Routes} from './models';
18+
import {NavigationBehaviorOptions, QueryParamsHandling, Route, Routes} from './models';
19+
import {isNavigationCancelingError, isRedirectingNavigationCancelingError, redirectingNavigationError} from './navigation_canceling_error';
1920
import {activateRoutes} from './operators/activate_routes';
2021
import {applyRedirects} from './operators/apply_redirects';
2122
import {checkGuards} from './operators/check_guards';
@@ -27,12 +28,11 @@ import {DefaultRouteReuseStrategy, RouteReuseStrategy} from './route_reuse_strat
2728
import {RouterConfigLoader} from './router_config_loader';
2829
import {ChildrenOutletContexts} from './router_outlet_context';
2930
import {ActivatedRoute, ActivatedRouteSnapshot, createEmptyState, RouterState, RouterStateSnapshot} from './router_state';
30-
import {isNavigationCancelingError, navigationCancelingError, Params, REDIRECTING_CANCELLATION_REASON} from './shared';
31+
import {Params} from './shared';
3132
import {DefaultUrlHandlingStrategy, UrlHandlingStrategy} from './url_handling_strategy';
32-
import {containsTree, createEmptyUrlTree, IsActiveMatchOptions, UrlSerializer, UrlTree} from './url_tree';
33+
import {containsTree, createEmptyUrlTree, IsActiveMatchOptions, isUrlTree, UrlSerializer, UrlTree} from './url_tree';
3334
import {standardizeConfig, validateConfig} from './utils/config';
3435
import {Checks, getAllRouteGuards} from './utils/preactivation';
35-
import {isUrlTree} from './utils/type_guards';
3636

3737

3838
const NG_DEV_MODE = typeof ngDevMode === 'undefined' || !!ngDevMode;
@@ -145,58 +145,6 @@ export interface UrlCreationOptions {
145145
preserveFragment?: boolean;
146146
}
147147

148-
/**
149-
* @description
150-
*
151-
* Options that modify the `Router` navigation strategy.
152-
* Supply an object containing any of these properties to a `Router` navigation function to
153-
* control how the navigation should be handled.
154-
*
155-
* @see [Router.navigate() method](api/router/Router#navigate)
156-
* @see [Router.navigateByUrl() method](api/router/Router#navigatebyurl)
157-
* @see [Routing and Navigation guide](guide/router)
158-
*
159-
* @publicApi
160-
*/
161-
export interface NavigationBehaviorOptions {
162-
/**
163-
* When true, navigates without pushing a new state into history.
164-
*
165-
* ```
166-
* // Navigate silently to /view
167-
* this.router.navigate(['/view'], { skipLocationChange: true });
168-
* ```
169-
*/
170-
skipLocationChange?: boolean;
171-
172-
/**
173-
* When true, navigates while replacing the current state in history.
174-
*
175-
* ```
176-
* // Navigate to /view
177-
* this.router.navigate(['/view'], { replaceUrl: true });
178-
* ```
179-
*/
180-
replaceUrl?: boolean;
181-
182-
/**
183-
* Developer-defined state that can be passed to any navigation.
184-
* Access this value through the `Navigation.extras` object
185-
* returned from the [Router.getCurrentNavigation()
186-
* method](api/router/Router#getcurrentnavigation) while a navigation is executing.
187-
*
188-
* After a navigation completes, the router writes an object containing this
189-
* value together with a `navigationId` to `history.state`.
190-
* The value is written when `location.go()` or `location.replaceState()`
191-
* is called before activating this route.
192-
*
193-
* Note that `history.state` does not pass an object equality test because
194-
* the router adds the `navigationId` on each navigation.
195-
*
196-
*/
197-
state?: {[k: string]: any};
198-
}
199-
200148
/**
201149
* @description
202150
*
@@ -761,11 +709,7 @@ export class Router {
761709
checkGuards(this.ngModule.injector, (evt: Event) => this.triggerEvent(evt)),
762710
tap(t => {
763711
if (isUrlTree(t.guardsResult)) {
764-
throw navigationCancelingError(
765-
NG_DEV_MODE &&
766-
REDIRECTING_CANCELLATION_REASON +
767-
`"${this.serializeUrl(t.guardsResult)}"`,
768-
NavigationCancellationCode.Redirect, t.guardsResult);
712+
throw redirectingNavigationError(this.urlSerializer, t.guardsResult);
769713
}
770714

771715
const guardsEnd = new GuardsCheckEnd(
@@ -929,8 +873,7 @@ export class Router {
929873
/* This error type is issued during Redirect, and is handled as a
930874
* cancellation rather than an error. */
931875
if (isNavigationCancelingError(e)) {
932-
const redirecting = isUrlTree(e.url);
933-
if (!redirecting) {
876+
if (!isRedirectingNavigationCancelingError(e)) {
934877
// Set property only if we're not redirecting. If we landed on a page and
935878
// redirect to `/` route, the new navigation is going to see the `/`
936879
// isn't a change from the default currentUrlTree and won't navigate.
@@ -946,7 +889,7 @@ export class Router {
946889

947890
// When redirecting, we need to delay resolving the navigation
948891
// promise and push it to the redirect navigation
949-
if (!isUrlTree(e.url)) {
892+
if (!isRedirectingNavigationCancelingError(e)) {
950893
t.resolve(false);
951894
} else {
952895
const mergedTree =

packages/router/src/shared.ts

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {NavigationCancellationCode} from './events';
109
import {Route, UrlMatchResult} from './models';
11-
import {UrlSegment, UrlSegmentGroup, UrlTree} from './url_tree';
10+
import {UrlSegment, UrlSegmentGroup} from './url_tree';
1211

1312

1413
/**
@@ -112,23 +111,6 @@ export function convertToParamMap(params: Params): ParamMap {
112111
return new ParamsAsMap(params);
113112
}
114113

115-
export const REDIRECTING_CANCELLATION_REASON = 'Redirecting to ';
116-
const NAVIGATION_CANCELING_ERROR = 'ngNavigationCancelingError';
117-
118-
export function navigationCancelingError(
119-
message: string|null|false, code: NavigationCancellationCode, redirectUrl?: UrlTree) {
120-
const error = Error('NavigationCancelingError: ' + (message || ''));
121-
(error as any)[NAVIGATION_CANCELING_ERROR] = true;
122-
(error as any).cancellationCode = code;
123-
(error as any).url = redirectUrl;
124-
return error;
125-
}
126-
127-
export function isNavigationCancelingError(error: Error): error is Error&
128-
{cancellationCode: NavigationCancellationCode, url?: UrlTree} {
129-
return error && (error as any)[NAVIGATION_CANCELING_ERROR];
130-
}
131-
132114
// Matches the route configuration (`route`) against the actual URL (`segments`).
133115
export function defaultUrlMatcher(
134116
segments: UrlSegment[], segmentGroup: UrlSegmentGroup, route: Route): UrlMatchResult|null {

packages/router/src/url_tree.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -779,3 +779,7 @@ function mergeTrivialChildren(s: UrlSegmentGroup): UrlSegmentGroup {
779779

780780
return s;
781781
}
782+
783+
export function isUrlTree(v: any): v is UrlTree {
784+
return v instanceof UrlTree;
785+
}

0 commit comments

Comments
 (0)