@angular/router 21.0.0-next.0 → 21.0.0-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/router.mjs +3 -3
- package/fesm2022/router.mjs.map +1 -1
- package/fesm2022/router2.mjs +550 -62
- package/fesm2022/router2.mjs.map +1 -1
- package/fesm2022/router_module.mjs +31 -2
- package/fesm2022/router_module.mjs.map +1 -1
- package/fesm2022/testing.mjs +1 -1
- package/fesm2022/testing.mjs.map +1 -1
- package/fesm2022/upgrade.mjs +1 -1
- package/fesm2022/upgrade.mjs.map +1 -1
- package/index.d.ts +7 -2
- package/package.json +4 -4
- package/router_module.d.d.ts +1 -1
- package/testing/index.d.ts +1 -1
- package/upgrade/index.d.ts +1 -1
package/fesm2022/router2.mjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Angular v21.0.0-next.
|
|
2
|
+
* @license Angular v21.0.0-next.2
|
|
3
3
|
* (c) 2010-2025 Google LLC. https://angular.io/
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { DOCUMENT, Location } from '@angular/common';
|
|
8
8
|
import * as i0 from '@angular/core';
|
|
9
|
-
import { ɵisPromise as _isPromise, ɵRuntimeError as _RuntimeError, Injectable, ɵisNgModule as _isNgModule, isStandalone, createEnvironmentInjector, InjectionToken, EventEmitter, input, inject, ViewContainerRef, ChangeDetectorRef, Directive, Input, Output, reflectComponentType, Component, ɵisInjectable as _isInjectable, runInInjectionContext, Compiler, NgModuleFactory, ɵresolveComponentResources as _resolveComponentResources, afterNextRender, signal, EnvironmentInjector, DestroyRef, untracked, ɵConsole as _Console, ɵPendingTasksInternal as _PendingTasksInternal, ɵINTERNAL_APPLICATION_ERROR_HANDLER as _INTERNAL_APPLICATION_ERROR_HANDLER } from '@angular/core';
|
|
10
|
-
import { isObservable, from, of, BehaviorSubject, combineLatest, EmptyError, concat, defer, pipe, throwError, EMPTY, ConnectableObservable, Subject,
|
|
11
|
-
import { map, switchMap, take, startWith, filter, mergeMap, first, concatMap, tap, catchError, scan, defaultIfEmpty, last as last$1, takeLast, finalize, refCount
|
|
9
|
+
import { ɵisPromise as _isPromise, ɵRuntimeError as _RuntimeError, Injectable, ɵisNgModule as _isNgModule, isStandalone, createEnvironmentInjector, InjectionToken, EventEmitter, input, inject, ViewContainerRef, ChangeDetectorRef, Directive, Input, Output, reflectComponentType, Component, ɵisInjectable as _isInjectable, runInInjectionContext, makeEnvironmentProviders, Compiler, NgModuleFactory, ɵresolveComponentResources as _resolveComponentResources, afterNextRender, signal, EnvironmentInjector, DestroyRef, untracked, ɵConsole as _Console, ɵPendingTasksInternal as _PendingTasksInternal, ɵINTERNAL_APPLICATION_ERROR_HANDLER as _INTERNAL_APPLICATION_ERROR_HANDLER } from '@angular/core';
|
|
10
|
+
import { isObservable, from, of, BehaviorSubject, combineLatest, EmptyError, Observable, concat, defer, pipe, throwError, EMPTY, ConnectableObservable, Subject, Subscription } from 'rxjs';
|
|
11
|
+
import { map, switchMap, take, startWith, filter, takeUntil, mergeMap, first, concatMap, tap, catchError, scan, defaultIfEmpty, last as last$1, takeLast, finalize, refCount } from 'rxjs/operators';
|
|
12
12
|
import * as i1 from '@angular/platform-browser';
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -3569,6 +3569,28 @@ function isRedirect(val) {
|
|
|
3569
3569
|
return isUrlTree(val) || val instanceof RedirectCommand;
|
|
3570
3570
|
}
|
|
3571
3571
|
|
|
3572
|
+
/**
|
|
3573
|
+
* Converts an AbortSignal to an Observable<void>.
|
|
3574
|
+
* Emits and completes when the signal is aborted.
|
|
3575
|
+
* If the signal is already aborted, it emits and completes immediately.
|
|
3576
|
+
*/
|
|
3577
|
+
function abortSignalToObservable(signal) {
|
|
3578
|
+
if (signal.aborted) {
|
|
3579
|
+
return of(undefined).pipe(take(1)); // Emit and complete immediately
|
|
3580
|
+
}
|
|
3581
|
+
return new Observable((subscriber) => {
|
|
3582
|
+
const handler = () => {
|
|
3583
|
+
subscriber.next();
|
|
3584
|
+
subscriber.complete();
|
|
3585
|
+
};
|
|
3586
|
+
signal.addEventListener('abort', handler);
|
|
3587
|
+
return () => signal.removeEventListener('abort', handler);
|
|
3588
|
+
});
|
|
3589
|
+
}
|
|
3590
|
+
function takeUntilAbort(signal) {
|
|
3591
|
+
return takeUntil(abortSignalToObservable(signal));
|
|
3592
|
+
}
|
|
3593
|
+
|
|
3572
3594
|
function checkGuards(injector, forwardEvent) {
|
|
3573
3595
|
return mergeMap((t) => {
|
|
3574
3596
|
const { targetSnapshot, currentSnapshot, guards: { canActivateChecks, canDeactivateChecks }, } = t;
|
|
@@ -3674,7 +3696,7 @@ function runCanDeactivate(component, currARS, currRSS, futureRSS, injector) {
|
|
|
3674
3696
|
});
|
|
3675
3697
|
return of(canDeactivateObservables).pipe(prioritizedGuardValue());
|
|
3676
3698
|
}
|
|
3677
|
-
function runCanLoadGuards(injector, route, segments, urlSerializer) {
|
|
3699
|
+
function runCanLoadGuards(injector, route, segments, urlSerializer, abortSignal) {
|
|
3678
3700
|
const canLoad = route.canLoad;
|
|
3679
3701
|
if (canLoad === undefined || canLoad.length === 0) {
|
|
3680
3702
|
return of(true);
|
|
@@ -3684,7 +3706,8 @@ function runCanLoadGuards(injector, route, segments, urlSerializer) {
|
|
|
3684
3706
|
const guardVal = isCanLoad(guard)
|
|
3685
3707
|
? guard.canLoad(route, segments)
|
|
3686
3708
|
: runInInjectionContext(injector, () => guard(route, segments));
|
|
3687
|
-
|
|
3709
|
+
const obs$ = wrapIntoObservable(guardVal);
|
|
3710
|
+
return abortSignal ? obs$.pipe(takeUntilAbort(abortSignal)) : obs$;
|
|
3688
3711
|
});
|
|
3689
3712
|
return of(canLoadObservables).pipe(prioritizedGuardValue(), redirectIfUrlTree(urlSerializer));
|
|
3690
3713
|
}
|
|
@@ -3695,7 +3718,7 @@ function redirectIfUrlTree(urlSerializer) {
|
|
|
3695
3718
|
throw redirectingNavigationError(urlSerializer, result);
|
|
3696
3719
|
}), map((result) => result === true));
|
|
3697
3720
|
}
|
|
3698
|
-
function runCanMatchGuards(injector, route, segments, urlSerializer) {
|
|
3721
|
+
function runCanMatchGuards(injector, route, segments, urlSerializer, abortSignal) {
|
|
3699
3722
|
const canMatch = route.canMatch;
|
|
3700
3723
|
if (!canMatch || canMatch.length === 0)
|
|
3701
3724
|
return of(true);
|
|
@@ -3704,67 +3727,79 @@ function runCanMatchGuards(injector, route, segments, urlSerializer) {
|
|
|
3704
3727
|
const guardVal = isCanMatch(guard)
|
|
3705
3728
|
? guard.canMatch(route, segments)
|
|
3706
3729
|
: runInInjectionContext(injector, () => guard(route, segments));
|
|
3707
|
-
|
|
3730
|
+
let obs$ = wrapIntoObservable(guardVal);
|
|
3731
|
+
return abortSignal ? obs$.pipe(takeUntilAbort(abortSignal)) : obs$;
|
|
3708
3732
|
});
|
|
3709
3733
|
return of(canMatchObservables).pipe(prioritizedGuardValue(), redirectIfUrlTree(urlSerializer));
|
|
3710
3734
|
}
|
|
3711
3735
|
|
|
3712
|
-
|
|
3736
|
+
/** replacement for firstValueFrom in rxjs 7. We must support rxjs v6 so we cannot use it */
|
|
3737
|
+
function firstValueFrom(source) {
|
|
3738
|
+
return new Promise((resolve, reject) => {
|
|
3739
|
+
source.pipe(first()).subscribe({
|
|
3740
|
+
next: (value) => resolve(value),
|
|
3741
|
+
error: (err) => reject(err),
|
|
3742
|
+
});
|
|
3743
|
+
});
|
|
3744
|
+
}
|
|
3745
|
+
|
|
3746
|
+
const NO_MATCH_ERROR_NAME = 'ɵNoMatch';
|
|
3747
|
+
let NoMatch$1 = class NoMatch extends Error {
|
|
3748
|
+
name = NO_MATCH_ERROR_NAME;
|
|
3713
3749
|
segmentGroup;
|
|
3714
3750
|
constructor(segmentGroup) {
|
|
3751
|
+
super();
|
|
3715
3752
|
this.segmentGroup = segmentGroup || null;
|
|
3716
3753
|
}
|
|
3717
|
-
}
|
|
3718
|
-
|
|
3754
|
+
};
|
|
3755
|
+
const ABSOLUTE_REDIRECT_ERROR_NAME = 'ɵAbsoluteRedirect';
|
|
3756
|
+
let AbsoluteRedirect$1 = class AbsoluteRedirect extends Error {
|
|
3719
3757
|
urlTree;
|
|
3758
|
+
name = ABSOLUTE_REDIRECT_ERROR_NAME;
|
|
3720
3759
|
constructor(urlTree) {
|
|
3721
3760
|
super();
|
|
3722
3761
|
this.urlTree = urlTree;
|
|
3723
3762
|
}
|
|
3763
|
+
};
|
|
3764
|
+
function namedOutletsRedirect$1(redirectTo) {
|
|
3765
|
+
throw new _RuntimeError(4000 /* RuntimeErrorCode.NAMED_OUTLET_REDIRECT */, (typeof ngDevMode === 'undefined' || ngDevMode) &&
|
|
3766
|
+
`Only absolute redirects can have named outlets. redirectTo: '${redirectTo}'`);
|
|
3724
3767
|
}
|
|
3725
|
-
function
|
|
3726
|
-
|
|
3727
|
-
}
|
|
3728
|
-
function namedOutletsRedirect(redirectTo) {
|
|
3729
|
-
return throwError(new _RuntimeError(4000 /* RuntimeErrorCode.NAMED_OUTLET_REDIRECT */, (typeof ngDevMode === 'undefined' || ngDevMode) &&
|
|
3730
|
-
`Only absolute redirects can have named outlets. redirectTo: '${redirectTo}'`));
|
|
3768
|
+
function canLoadFails$1(route) {
|
|
3769
|
+
throw navigationCancelingError((typeof ngDevMode === 'undefined' || ngDevMode) &&
|
|
3770
|
+
`Cannot load children because the guard of the route "path: '${route.path}'" returned false`, NavigationCancellationCode.GuardRejected);
|
|
3731
3771
|
}
|
|
3732
|
-
|
|
3733
|
-
return throwError(navigationCancelingError((typeof ngDevMode === 'undefined' || ngDevMode) &&
|
|
3734
|
-
`Cannot load children because the guard of the route "path: '${route.path}'" returned false`, NavigationCancellationCode.GuardRejected));
|
|
3735
|
-
}
|
|
3736
|
-
class ApplyRedirects {
|
|
3772
|
+
let ApplyRedirects$1 = class ApplyRedirects {
|
|
3737
3773
|
urlSerializer;
|
|
3738
3774
|
urlTree;
|
|
3739
3775
|
constructor(urlSerializer, urlTree) {
|
|
3740
3776
|
this.urlSerializer = urlSerializer;
|
|
3741
3777
|
this.urlTree = urlTree;
|
|
3742
3778
|
}
|
|
3743
|
-
lineralizeSegments(route, urlTree) {
|
|
3779
|
+
async lineralizeSegments(route, urlTree) {
|
|
3744
3780
|
let res = [];
|
|
3745
3781
|
let c = urlTree.root;
|
|
3746
3782
|
while (true) {
|
|
3747
3783
|
res = res.concat(c.segments);
|
|
3748
3784
|
if (c.numberOfChildren === 0) {
|
|
3749
|
-
return
|
|
3785
|
+
return res;
|
|
3750
3786
|
}
|
|
3751
3787
|
if (c.numberOfChildren > 1 || !c.children[PRIMARY_OUTLET]) {
|
|
3752
|
-
|
|
3788
|
+
throw namedOutletsRedirect$1(`${route.redirectTo}`);
|
|
3753
3789
|
}
|
|
3754
3790
|
c = c.children[PRIMARY_OUTLET];
|
|
3755
3791
|
}
|
|
3756
3792
|
}
|
|
3757
|
-
applyRedirectCommands(segments, redirectTo, posParams, currentSnapshot, injector) {
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
}));
|
|
3793
|
+
async applyRedirectCommands(segments, redirectTo, posParams, currentSnapshot, injector) {
|
|
3794
|
+
const redirect = await getRedirectResult$1(redirectTo, currentSnapshot, injector);
|
|
3795
|
+
if (redirect instanceof UrlTree) {
|
|
3796
|
+
throw new AbsoluteRedirect$1(redirect);
|
|
3797
|
+
}
|
|
3798
|
+
const newTree = this.applyRedirectCreateUrlTree(redirect, this.urlSerializer.parse(redirect), segments, posParams);
|
|
3799
|
+
if (redirect[0] === '/') {
|
|
3800
|
+
throw new AbsoluteRedirect$1(newTree);
|
|
3801
|
+
}
|
|
3802
|
+
return newTree;
|
|
3768
3803
|
}
|
|
3769
3804
|
applyRedirectCreateUrlTree(redirectTo, urlTree, segments, posParams) {
|
|
3770
3805
|
const newRoot = this.createSegmentGroup(redirectTo, urlTree.root, segments, posParams);
|
|
@@ -3815,24 +3850,24 @@ class ApplyRedirects {
|
|
|
3815
3850
|
}
|
|
3816
3851
|
return redirectToUrlSegment;
|
|
3817
3852
|
}
|
|
3818
|
-
}
|
|
3819
|
-
function getRedirectResult(redirectTo, currentSnapshot, injector) {
|
|
3853
|
+
};
|
|
3854
|
+
function getRedirectResult$1(redirectTo, currentSnapshot, injector) {
|
|
3820
3855
|
if (typeof redirectTo === 'string') {
|
|
3821
|
-
return
|
|
3856
|
+
return Promise.resolve(redirectTo);
|
|
3822
3857
|
}
|
|
3823
3858
|
const redirectToFn = redirectTo;
|
|
3824
3859
|
const { queryParams, fragment, routeConfig, url, outlet, params, data, title } = currentSnapshot;
|
|
3825
|
-
return wrapIntoObservable(runInInjectionContext(injector, () => redirectToFn({ params, data, queryParams, fragment, routeConfig, url, outlet, title })));
|
|
3860
|
+
return firstValueFrom(wrapIntoObservable(runInInjectionContext(injector, () => redirectToFn({ params, data, queryParams, fragment, routeConfig, url, outlet, title }))));
|
|
3826
3861
|
}
|
|
3827
3862
|
|
|
3828
|
-
const noMatch = {
|
|
3863
|
+
const noMatch$1 = {
|
|
3829
3864
|
matched: false,
|
|
3830
3865
|
consumedSegments: [],
|
|
3831
3866
|
remainingSegments: [],
|
|
3832
3867
|
parameters: {},
|
|
3833
3868
|
positionalParamSegments: {},
|
|
3834
3869
|
};
|
|
3835
|
-
function matchWithChecks(segmentGroup, route, segments, injector, urlSerializer) {
|
|
3870
|
+
function matchWithChecks(segmentGroup, route, segments, injector, urlSerializer, abortSignal) {
|
|
3836
3871
|
const result = match(segmentGroup, route, segments);
|
|
3837
3872
|
if (!result.matched) {
|
|
3838
3873
|
return of(result);
|
|
@@ -3840,7 +3875,7 @@ function matchWithChecks(segmentGroup, route, segments, injector, urlSerializer)
|
|
|
3840
3875
|
// Only create the Route's `EnvironmentInjector` if it matches the attempted
|
|
3841
3876
|
// navigation
|
|
3842
3877
|
injector = getOrCreateRouteInjectorIfNeeded(route, injector);
|
|
3843
|
-
return runCanMatchGuards(injector, route, segments, urlSerializer).pipe(map((v) => (v === true ? result : { ...noMatch })));
|
|
3878
|
+
return runCanMatchGuards(injector, route, segments, urlSerializer, abortSignal).pipe(map((v) => (v === true ? result : { ...noMatch$1 })));
|
|
3844
3879
|
}
|
|
3845
3880
|
function match(segmentGroup, route, segments) {
|
|
3846
3881
|
if (route.path === '**') {
|
|
@@ -3848,7 +3883,7 @@ function match(segmentGroup, route, segments) {
|
|
|
3848
3883
|
}
|
|
3849
3884
|
if (route.path === '') {
|
|
3850
3885
|
if (route.pathMatch === 'full' && (segmentGroup.hasChildren() || segments.length > 0)) {
|
|
3851
|
-
return { ...noMatch };
|
|
3886
|
+
return { ...noMatch$1 };
|
|
3852
3887
|
}
|
|
3853
3888
|
return {
|
|
3854
3889
|
matched: true,
|
|
@@ -3861,7 +3896,7 @@ function match(segmentGroup, route, segments) {
|
|
|
3861
3896
|
const matcher = route.matcher || defaultUrlMatcher;
|
|
3862
3897
|
const res = matcher(segments, segmentGroup, route);
|
|
3863
3898
|
if (!res)
|
|
3864
|
-
return { ...noMatch };
|
|
3899
|
+
return { ...noMatch$1 };
|
|
3865
3900
|
const posParams = {};
|
|
3866
3901
|
Object.entries(res.posParams ?? {}).forEach(([k, v]) => {
|
|
3867
3902
|
posParams[k] = v.path;
|
|
@@ -3938,6 +3973,449 @@ function noLeftoversInUrl(segmentGroup, segments, outlet) {
|
|
|
3938
3973
|
return segments.length === 0 && !segmentGroup.children[outlet];
|
|
3939
3974
|
}
|
|
3940
3975
|
|
|
3976
|
+
/**
|
|
3977
|
+
* Class used to indicate there were no additional route config matches but that all segments of
|
|
3978
|
+
* the URL were consumed during matching so the route was URL matched. When this happens, we still
|
|
3979
|
+
* try to match child configs in case there are empty path children.
|
|
3980
|
+
*/
|
|
3981
|
+
let NoLeftoversInUrl$1 = class NoLeftoversInUrl {
|
|
3982
|
+
};
|
|
3983
|
+
async function recognize$2(injector, configLoader, rootComponentType, config, urlTree, urlSerializer, paramsInheritanceStrategy = 'emptyOnly', abortSignal) {
|
|
3984
|
+
return new Recognizer$1(injector, configLoader, rootComponentType, config, urlTree, paramsInheritanceStrategy, urlSerializer, abortSignal).recognize();
|
|
3985
|
+
}
|
|
3986
|
+
const MAX_ALLOWED_REDIRECTS$1 = 31;
|
|
3987
|
+
let Recognizer$1 = class Recognizer {
|
|
3988
|
+
injector;
|
|
3989
|
+
configLoader;
|
|
3990
|
+
rootComponentType;
|
|
3991
|
+
config;
|
|
3992
|
+
urlTree;
|
|
3993
|
+
paramsInheritanceStrategy;
|
|
3994
|
+
urlSerializer;
|
|
3995
|
+
abortSignal;
|
|
3996
|
+
applyRedirects;
|
|
3997
|
+
absoluteRedirectCount = 0;
|
|
3998
|
+
allowRedirects = true;
|
|
3999
|
+
constructor(injector, configLoader, rootComponentType, config, urlTree, paramsInheritanceStrategy, urlSerializer, abortSignal) {
|
|
4000
|
+
this.injector = injector;
|
|
4001
|
+
this.configLoader = configLoader;
|
|
4002
|
+
this.rootComponentType = rootComponentType;
|
|
4003
|
+
this.config = config;
|
|
4004
|
+
this.urlTree = urlTree;
|
|
4005
|
+
this.paramsInheritanceStrategy = paramsInheritanceStrategy;
|
|
4006
|
+
this.urlSerializer = urlSerializer;
|
|
4007
|
+
this.abortSignal = abortSignal;
|
|
4008
|
+
this.applyRedirects = new ApplyRedirects$1(this.urlSerializer, this.urlTree);
|
|
4009
|
+
}
|
|
4010
|
+
noMatchError(e) {
|
|
4011
|
+
return new _RuntimeError(4002 /* RuntimeErrorCode.NO_MATCH */, typeof ngDevMode === 'undefined' || ngDevMode
|
|
4012
|
+
? `Cannot match any routes. URL Segment: '${e.segmentGroup}'`
|
|
4013
|
+
: `'${e.segmentGroup}'`);
|
|
4014
|
+
}
|
|
4015
|
+
async recognize() {
|
|
4016
|
+
const rootSegmentGroup = split(this.urlTree.root, [], [], this.config).segmentGroup;
|
|
4017
|
+
const { children, rootSnapshot } = await this.match(rootSegmentGroup);
|
|
4018
|
+
const rootNode = new TreeNode(rootSnapshot, children);
|
|
4019
|
+
const routeState = new RouterStateSnapshot('', rootNode);
|
|
4020
|
+
const tree = createUrlTreeFromSnapshot(rootSnapshot, [], this.urlTree.queryParams, this.urlTree.fragment);
|
|
4021
|
+
// https://github.com/angular/angular/issues/47307
|
|
4022
|
+
// Creating the tree stringifies the query params
|
|
4023
|
+
// We don't want to do this here so reassign them to the original.
|
|
4024
|
+
tree.queryParams = this.urlTree.queryParams;
|
|
4025
|
+
routeState.url = this.urlSerializer.serialize(tree);
|
|
4026
|
+
return { state: routeState, tree };
|
|
4027
|
+
}
|
|
4028
|
+
async match(rootSegmentGroup) {
|
|
4029
|
+
// Use Object.freeze to prevent readers of the Router state from modifying it outside
|
|
4030
|
+
// of a navigation, resulting in the router being out of sync with the browser.
|
|
4031
|
+
const rootSnapshot = new ActivatedRouteSnapshot([], Object.freeze({}), Object.freeze({ ...this.urlTree.queryParams }), this.urlTree.fragment, Object.freeze({}), PRIMARY_OUTLET, this.rootComponentType, null, {});
|
|
4032
|
+
try {
|
|
4033
|
+
const children = await this.processSegmentGroup(this.injector, this.config, rootSegmentGroup, PRIMARY_OUTLET, rootSnapshot);
|
|
4034
|
+
return { children, rootSnapshot };
|
|
4035
|
+
}
|
|
4036
|
+
catch (e) {
|
|
4037
|
+
if (e?.name === ABSOLUTE_REDIRECT_ERROR_NAME) {
|
|
4038
|
+
this.urlTree = e.urlTree;
|
|
4039
|
+
return this.match(e.urlTree.root);
|
|
4040
|
+
}
|
|
4041
|
+
if (e?.name === NO_MATCH_ERROR_NAME) {
|
|
4042
|
+
throw this.noMatchError(e);
|
|
4043
|
+
}
|
|
4044
|
+
throw e;
|
|
4045
|
+
}
|
|
4046
|
+
}
|
|
4047
|
+
async processSegmentGroup(injector, config, segmentGroup, outlet, parentRoute) {
|
|
4048
|
+
if (segmentGroup.segments.length === 0 && segmentGroup.hasChildren()) {
|
|
4049
|
+
return this.processChildren(injector, config, segmentGroup, parentRoute);
|
|
4050
|
+
}
|
|
4051
|
+
const child = await this.processSegment(injector, config, segmentGroup, segmentGroup.segments, outlet, true, parentRoute);
|
|
4052
|
+
return child instanceof TreeNode ? [child] : [];
|
|
4053
|
+
}
|
|
4054
|
+
/**
|
|
4055
|
+
* Matches every child outlet in the `segmentGroup` to a `Route` in the config. Returns `null` if
|
|
4056
|
+
* we cannot find a match for _any_ of the children.
|
|
4057
|
+
*
|
|
4058
|
+
* @param config - The `Routes` to match against
|
|
4059
|
+
* @param segmentGroup - The `UrlSegmentGroup` whose children need to be matched against the
|
|
4060
|
+
* config.
|
|
4061
|
+
*/
|
|
4062
|
+
async processChildren(injector, config, segmentGroup, parentRoute) {
|
|
4063
|
+
// Expand outlets one at a time, starting with the primary outlet. We need to do it this way
|
|
4064
|
+
// because an absolute redirect from the primary outlet takes precedence.
|
|
4065
|
+
const childOutlets = [];
|
|
4066
|
+
for (const child of Object.keys(segmentGroup.children)) {
|
|
4067
|
+
if (child === 'primary') {
|
|
4068
|
+
childOutlets.unshift(child);
|
|
4069
|
+
}
|
|
4070
|
+
else {
|
|
4071
|
+
childOutlets.push(child);
|
|
4072
|
+
}
|
|
4073
|
+
}
|
|
4074
|
+
let children = [];
|
|
4075
|
+
for (const childOutlet of childOutlets) {
|
|
4076
|
+
const child = segmentGroup.children[childOutlet];
|
|
4077
|
+
// Sort the config so that routes with outlets that match the one being activated
|
|
4078
|
+
// appear first, followed by routes for other outlets, which might match if they have
|
|
4079
|
+
// an empty path.
|
|
4080
|
+
const sortedConfig = sortByMatchingOutlets(config, childOutlet);
|
|
4081
|
+
const outletChildren = await this.processSegmentGroup(injector, sortedConfig, child, childOutlet, parentRoute);
|
|
4082
|
+
children.push(...outletChildren);
|
|
4083
|
+
}
|
|
4084
|
+
// Because we may have matched two outlets to the same empty path segment, we can have
|
|
4085
|
+
// multiple activated results for the same outlet. We should merge the children of
|
|
4086
|
+
// these results so the final return value is only one `TreeNode` per outlet.
|
|
4087
|
+
const mergedChildren = mergeEmptyPathMatches$1(children);
|
|
4088
|
+
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
|
4089
|
+
// This should really never happen - we are only taking the first match for each
|
|
4090
|
+
// outlet and merge the empty path matches.
|
|
4091
|
+
checkOutletNameUniqueness$1(mergedChildren);
|
|
4092
|
+
}
|
|
4093
|
+
sortActivatedRouteSnapshots$1(mergedChildren);
|
|
4094
|
+
return mergedChildren;
|
|
4095
|
+
}
|
|
4096
|
+
async processSegment(injector, routes, segmentGroup, segments, outlet, allowRedirects, parentRoute) {
|
|
4097
|
+
for (const r of routes) {
|
|
4098
|
+
try {
|
|
4099
|
+
return await this.processSegmentAgainstRoute(r._injector ?? injector, routes, r, segmentGroup, segments, outlet, allowRedirects, parentRoute);
|
|
4100
|
+
}
|
|
4101
|
+
catch (e) {
|
|
4102
|
+
if (e?.name === NO_MATCH_ERROR_NAME || isEmptyError(e)) {
|
|
4103
|
+
continue;
|
|
4104
|
+
}
|
|
4105
|
+
throw e;
|
|
4106
|
+
}
|
|
4107
|
+
}
|
|
4108
|
+
if (noLeftoversInUrl(segmentGroup, segments, outlet)) {
|
|
4109
|
+
return new NoLeftoversInUrl$1();
|
|
4110
|
+
}
|
|
4111
|
+
throw new NoMatch$1(segmentGroup);
|
|
4112
|
+
}
|
|
4113
|
+
async processSegmentAgainstRoute(injector, routes, route, rawSegment, segments, outlet, allowRedirects, parentRoute) {
|
|
4114
|
+
// We allow matches to empty paths when the outlets differ so we can match a url like `/(b:b)` to
|
|
4115
|
+
// a config like
|
|
4116
|
+
// * `{path: '', children: [{path: 'b', outlet: 'b'}]}`
|
|
4117
|
+
// or even
|
|
4118
|
+
// * `{path: '', outlet: 'a', children: [{path: 'b', outlet: 'b'}]`
|
|
4119
|
+
//
|
|
4120
|
+
// The exception here is when the segment outlet is for the primary outlet. This would
|
|
4121
|
+
// result in a match inside the named outlet because all children there are written as primary
|
|
4122
|
+
// outlets. So we need to prevent child named outlet matches in a url like `/b` in a config like
|
|
4123
|
+
// * `{path: '', outlet: 'x' children: [{path: 'b'}]}`
|
|
4124
|
+
// This should only match if the url is `/(x:b)`.
|
|
4125
|
+
if (getOutlet(route) !== outlet &&
|
|
4126
|
+
(outlet === PRIMARY_OUTLET || !emptyPathMatch(rawSegment, segments, route))) {
|
|
4127
|
+
throw new NoMatch$1(rawSegment);
|
|
4128
|
+
}
|
|
4129
|
+
if (route.redirectTo === undefined) {
|
|
4130
|
+
return this.matchSegmentAgainstRoute(injector, rawSegment, route, segments, outlet, parentRoute);
|
|
4131
|
+
}
|
|
4132
|
+
if (this.allowRedirects && allowRedirects) {
|
|
4133
|
+
return this.expandSegmentAgainstRouteUsingRedirect(injector, rawSegment, routes, route, segments, outlet, parentRoute);
|
|
4134
|
+
}
|
|
4135
|
+
throw new NoMatch$1(rawSegment);
|
|
4136
|
+
}
|
|
4137
|
+
async expandSegmentAgainstRouteUsingRedirect(injector, segmentGroup, routes, route, segments, outlet, parentRoute) {
|
|
4138
|
+
const { matched, parameters, consumedSegments, positionalParamSegments, remainingSegments } = match(segmentGroup, route, segments);
|
|
4139
|
+
if (!matched)
|
|
4140
|
+
throw new NoMatch$1(segmentGroup);
|
|
4141
|
+
// TODO(atscott): Move all of this under an if(ngDevMode) as a breaking change and allow stack
|
|
4142
|
+
// size exceeded in production
|
|
4143
|
+
if (typeof route.redirectTo === 'string' && route.redirectTo[0] === '/') {
|
|
4144
|
+
this.absoluteRedirectCount++;
|
|
4145
|
+
if (this.absoluteRedirectCount > MAX_ALLOWED_REDIRECTS$1) {
|
|
4146
|
+
if (ngDevMode) {
|
|
4147
|
+
throw new _RuntimeError(4016 /* RuntimeErrorCode.INFINITE_REDIRECT */, `Detected possible infinite redirect when redirecting from '${this.urlTree}' to '${route.redirectTo}'.\n` +
|
|
4148
|
+
`This is currently a dev mode only error but will become a` +
|
|
4149
|
+
` call stack size exceeded error in production in a future major version.`);
|
|
4150
|
+
}
|
|
4151
|
+
this.allowRedirects = false;
|
|
4152
|
+
}
|
|
4153
|
+
}
|
|
4154
|
+
const currentSnapshot = new ActivatedRouteSnapshot(segments, parameters, Object.freeze({ ...this.urlTree.queryParams }), this.urlTree.fragment, getData$1(route), getOutlet(route), route.component ?? route._loadedComponent ?? null, route, getResolve$1(route));
|
|
4155
|
+
const inherited = getInherited(currentSnapshot, parentRoute, this.paramsInheritanceStrategy);
|
|
4156
|
+
currentSnapshot.params = Object.freeze(inherited.params);
|
|
4157
|
+
currentSnapshot.data = Object.freeze(inherited.data);
|
|
4158
|
+
if (this.abortSignal.aborted) {
|
|
4159
|
+
throw new Error(this.abortSignal.reason);
|
|
4160
|
+
}
|
|
4161
|
+
const newTree = await this.applyRedirects.applyRedirectCommands(consumedSegments, route.redirectTo, positionalParamSegments, currentSnapshot, injector);
|
|
4162
|
+
const newSegments = await this.applyRedirects.lineralizeSegments(route, newTree);
|
|
4163
|
+
return this.processSegment(injector, routes, segmentGroup, newSegments.concat(remainingSegments), outlet, false, parentRoute);
|
|
4164
|
+
}
|
|
4165
|
+
async matchSegmentAgainstRoute(injector, rawSegment, route, segments, outlet, parentRoute) {
|
|
4166
|
+
if (this.abortSignal.aborted) {
|
|
4167
|
+
throw new Error(this.abortSignal.reason);
|
|
4168
|
+
}
|
|
4169
|
+
const result = await firstValueFrom(matchWithChecks(rawSegment, route, segments, injector, this.urlSerializer, this.abortSignal));
|
|
4170
|
+
if (route.path === '**') {
|
|
4171
|
+
// Prior versions of the route matching algorithm would stop matching at the wildcard route.
|
|
4172
|
+
// We should investigate a better strategy for any existing children. Otherwise, these
|
|
4173
|
+
// child segments are silently dropped from the navigation.
|
|
4174
|
+
// https://github.com/angular/angular/issues/40089
|
|
4175
|
+
rawSegment.children = {};
|
|
4176
|
+
}
|
|
4177
|
+
if (!result?.matched) {
|
|
4178
|
+
throw new NoMatch$1(rawSegment);
|
|
4179
|
+
}
|
|
4180
|
+
// If the route has an injector created from providers, we should start using that.
|
|
4181
|
+
injector = route._injector ?? injector;
|
|
4182
|
+
const { routes: childConfig } = await this.getChildConfig(injector, route, segments);
|
|
4183
|
+
const childInjector = route._loadedInjector ?? injector;
|
|
4184
|
+
const { parameters, consumedSegments, remainingSegments } = result;
|
|
4185
|
+
const snapshot = new ActivatedRouteSnapshot(consumedSegments, parameters, Object.freeze({ ...this.urlTree.queryParams }), this.urlTree.fragment, getData$1(route), getOutlet(route), route.component ?? route._loadedComponent ?? null, route, getResolve$1(route));
|
|
4186
|
+
const inherited = getInherited(snapshot, parentRoute, this.paramsInheritanceStrategy);
|
|
4187
|
+
snapshot.params = Object.freeze(inherited.params);
|
|
4188
|
+
snapshot.data = Object.freeze(inherited.data);
|
|
4189
|
+
const { segmentGroup, slicedSegments } = split(rawSegment, consumedSegments, remainingSegments, childConfig);
|
|
4190
|
+
if (slicedSegments.length === 0 && segmentGroup.hasChildren()) {
|
|
4191
|
+
const children = await this.processChildren(childInjector, childConfig, segmentGroup, snapshot);
|
|
4192
|
+
return new TreeNode(snapshot, children);
|
|
4193
|
+
}
|
|
4194
|
+
if (childConfig.length === 0 && slicedSegments.length === 0) {
|
|
4195
|
+
return new TreeNode(snapshot, []);
|
|
4196
|
+
}
|
|
4197
|
+
const matchedOnOutlet = getOutlet(route) === outlet;
|
|
4198
|
+
// If we matched a config due to empty path match on a different outlet, we need to
|
|
4199
|
+
// continue passing the current outlet for the segment rather than switch to PRIMARY.
|
|
4200
|
+
// Note that we switch to primary when we have a match because outlet configs look like
|
|
4201
|
+
// this: {path: 'a', outlet: 'a', children: [
|
|
4202
|
+
// {path: 'b', component: B},
|
|
4203
|
+
// {path: 'c', component: C},
|
|
4204
|
+
// ]}
|
|
4205
|
+
// Notice that the children of the named outlet are configured with the primary outlet
|
|
4206
|
+
const child = await this.processSegment(childInjector, childConfig, segmentGroup, slicedSegments, matchedOnOutlet ? PRIMARY_OUTLET : outlet, true, snapshot);
|
|
4207
|
+
return new TreeNode(snapshot, child instanceof TreeNode ? [child] : []);
|
|
4208
|
+
}
|
|
4209
|
+
async getChildConfig(injector, route, segments) {
|
|
4210
|
+
if (route.children) {
|
|
4211
|
+
// The children belong to the same module
|
|
4212
|
+
return { routes: route.children, injector };
|
|
4213
|
+
}
|
|
4214
|
+
if (route.loadChildren) {
|
|
4215
|
+
// lazy children belong to the loaded module
|
|
4216
|
+
if (route._loadedRoutes !== undefined) {
|
|
4217
|
+
return { routes: route._loadedRoutes, injector: route._loadedInjector };
|
|
4218
|
+
}
|
|
4219
|
+
if (this.abortSignal.aborted) {
|
|
4220
|
+
throw new Error(this.abortSignal.reason);
|
|
4221
|
+
}
|
|
4222
|
+
const shouldLoadResult = await firstValueFrom(runCanLoadGuards(injector, route, segments, this.urlSerializer, this.abortSignal));
|
|
4223
|
+
if (shouldLoadResult) {
|
|
4224
|
+
const cfg = await firstValueFrom(this.configLoader.loadChildren(injector, route));
|
|
4225
|
+
if (!cfg) {
|
|
4226
|
+
throw canLoadFails$1(route);
|
|
4227
|
+
}
|
|
4228
|
+
route._loadedRoutes = cfg.routes;
|
|
4229
|
+
route._loadedInjector = cfg.injector;
|
|
4230
|
+
return cfg;
|
|
4231
|
+
}
|
|
4232
|
+
throw canLoadFails$1(route);
|
|
4233
|
+
}
|
|
4234
|
+
return { routes: [], injector };
|
|
4235
|
+
}
|
|
4236
|
+
};
|
|
4237
|
+
function sortActivatedRouteSnapshots$1(nodes) {
|
|
4238
|
+
nodes.sort((a, b) => {
|
|
4239
|
+
if (a.value.outlet === PRIMARY_OUTLET)
|
|
4240
|
+
return -1;
|
|
4241
|
+
if (b.value.outlet === PRIMARY_OUTLET)
|
|
4242
|
+
return 1;
|
|
4243
|
+
return a.value.outlet.localeCompare(b.value.outlet);
|
|
4244
|
+
});
|
|
4245
|
+
}
|
|
4246
|
+
function hasEmptyPathConfig$1(node) {
|
|
4247
|
+
const config = node.value.routeConfig;
|
|
4248
|
+
return config && config.path === '';
|
|
4249
|
+
}
|
|
4250
|
+
/**
|
|
4251
|
+
* Finds `TreeNode`s with matching empty path route configs and merges them into `TreeNode` with
|
|
4252
|
+
* the children from each duplicate. This is necessary because different outlets can match a
|
|
4253
|
+
* single empty path route config and the results need to then be merged.
|
|
4254
|
+
*/
|
|
4255
|
+
function mergeEmptyPathMatches$1(nodes) {
|
|
4256
|
+
const result = [];
|
|
4257
|
+
// The set of nodes which contain children that were merged from two duplicate empty path nodes.
|
|
4258
|
+
const mergedNodes = new Set();
|
|
4259
|
+
for (const node of nodes) {
|
|
4260
|
+
if (!hasEmptyPathConfig$1(node)) {
|
|
4261
|
+
result.push(node);
|
|
4262
|
+
continue;
|
|
4263
|
+
}
|
|
4264
|
+
const duplicateEmptyPathNode = result.find((resultNode) => node.value.routeConfig === resultNode.value.routeConfig);
|
|
4265
|
+
if (duplicateEmptyPathNode !== undefined) {
|
|
4266
|
+
duplicateEmptyPathNode.children.push(...node.children);
|
|
4267
|
+
mergedNodes.add(duplicateEmptyPathNode);
|
|
4268
|
+
}
|
|
4269
|
+
else {
|
|
4270
|
+
result.push(node);
|
|
4271
|
+
}
|
|
4272
|
+
}
|
|
4273
|
+
// For each node which has children from multiple sources, we need to recompute a new `TreeNode`
|
|
4274
|
+
// by also merging those children. This is necessary when there are multiple empty path configs
|
|
4275
|
+
// in a row. Put another way: whenever we combine children of two nodes, we need to also check
|
|
4276
|
+
// if any of those children can be combined into a single node as well.
|
|
4277
|
+
for (const mergedNode of mergedNodes) {
|
|
4278
|
+
const mergedChildren = mergeEmptyPathMatches$1(mergedNode.children);
|
|
4279
|
+
result.push(new TreeNode(mergedNode.value, mergedChildren));
|
|
4280
|
+
}
|
|
4281
|
+
return result.filter((n) => !mergedNodes.has(n));
|
|
4282
|
+
}
|
|
4283
|
+
function checkOutletNameUniqueness$1(nodes) {
|
|
4284
|
+
const names = {};
|
|
4285
|
+
nodes.forEach((n) => {
|
|
4286
|
+
const routeWithSameOutletName = names[n.value.outlet];
|
|
4287
|
+
if (routeWithSameOutletName) {
|
|
4288
|
+
const p = routeWithSameOutletName.url.map((s) => s.toString()).join('/');
|
|
4289
|
+
const c = n.value.url.map((s) => s.toString()).join('/');
|
|
4290
|
+
throw new _RuntimeError(4006 /* RuntimeErrorCode.TWO_SEGMENTS_WITH_SAME_OUTLET */, (typeof ngDevMode === 'undefined' || ngDevMode) &&
|
|
4291
|
+
`Two segments cannot have the same outlet name: '${p}' and '${c}'.`);
|
|
4292
|
+
}
|
|
4293
|
+
names[n.value.outlet] = n.value;
|
|
4294
|
+
});
|
|
4295
|
+
}
|
|
4296
|
+
function getData$1(route) {
|
|
4297
|
+
return route.data || {};
|
|
4298
|
+
}
|
|
4299
|
+
function getResolve$1(route) {
|
|
4300
|
+
return route.resolve || {};
|
|
4301
|
+
}
|
|
4302
|
+
|
|
4303
|
+
class NoMatch {
|
|
4304
|
+
segmentGroup;
|
|
4305
|
+
constructor(segmentGroup) {
|
|
4306
|
+
this.segmentGroup = segmentGroup || null;
|
|
4307
|
+
}
|
|
4308
|
+
}
|
|
4309
|
+
class AbsoluteRedirect extends Error {
|
|
4310
|
+
urlTree;
|
|
4311
|
+
constructor(urlTree) {
|
|
4312
|
+
super();
|
|
4313
|
+
this.urlTree = urlTree;
|
|
4314
|
+
}
|
|
4315
|
+
}
|
|
4316
|
+
function noMatch(segmentGroup) {
|
|
4317
|
+
return throwError(new NoMatch(segmentGroup));
|
|
4318
|
+
}
|
|
4319
|
+
function namedOutletsRedirect(redirectTo) {
|
|
4320
|
+
return throwError(new _RuntimeError(4000 /* RuntimeErrorCode.NAMED_OUTLET_REDIRECT */, (typeof ngDevMode === 'undefined' || ngDevMode) &&
|
|
4321
|
+
`Only absolute redirects can have named outlets. redirectTo: '${redirectTo}'`));
|
|
4322
|
+
}
|
|
4323
|
+
function canLoadFails(route) {
|
|
4324
|
+
return throwError(navigationCancelingError((typeof ngDevMode === 'undefined' || ngDevMode) &&
|
|
4325
|
+
`Cannot load children because the guard of the route "path: '${route.path}'" returned false`, NavigationCancellationCode.GuardRejected));
|
|
4326
|
+
}
|
|
4327
|
+
class ApplyRedirects {
|
|
4328
|
+
urlSerializer;
|
|
4329
|
+
urlTree;
|
|
4330
|
+
constructor(urlSerializer, urlTree) {
|
|
4331
|
+
this.urlSerializer = urlSerializer;
|
|
4332
|
+
this.urlTree = urlTree;
|
|
4333
|
+
}
|
|
4334
|
+
lineralizeSegments(route, urlTree) {
|
|
4335
|
+
let res = [];
|
|
4336
|
+
let c = urlTree.root;
|
|
4337
|
+
while (true) {
|
|
4338
|
+
res = res.concat(c.segments);
|
|
4339
|
+
if (c.numberOfChildren === 0) {
|
|
4340
|
+
return of(res);
|
|
4341
|
+
}
|
|
4342
|
+
if (c.numberOfChildren > 1 || !c.children[PRIMARY_OUTLET]) {
|
|
4343
|
+
return namedOutletsRedirect(`${route.redirectTo}`);
|
|
4344
|
+
}
|
|
4345
|
+
c = c.children[PRIMARY_OUTLET];
|
|
4346
|
+
}
|
|
4347
|
+
}
|
|
4348
|
+
applyRedirectCommands(segments, redirectTo, posParams, currentSnapshot, injector) {
|
|
4349
|
+
return getRedirectResult(redirectTo, currentSnapshot, injector).pipe(map((redirect) => {
|
|
4350
|
+
if (redirect instanceof UrlTree) {
|
|
4351
|
+
throw new AbsoluteRedirect(redirect);
|
|
4352
|
+
}
|
|
4353
|
+
const newTree = this.applyRedirectCreateUrlTree(redirect, this.urlSerializer.parse(redirect), segments, posParams);
|
|
4354
|
+
if (redirect[0] === '/') {
|
|
4355
|
+
throw new AbsoluteRedirect(newTree);
|
|
4356
|
+
}
|
|
4357
|
+
return newTree;
|
|
4358
|
+
}));
|
|
4359
|
+
}
|
|
4360
|
+
applyRedirectCreateUrlTree(redirectTo, urlTree, segments, posParams) {
|
|
4361
|
+
const newRoot = this.createSegmentGroup(redirectTo, urlTree.root, segments, posParams);
|
|
4362
|
+
return new UrlTree(newRoot, this.createQueryParams(urlTree.queryParams, this.urlTree.queryParams), urlTree.fragment);
|
|
4363
|
+
}
|
|
4364
|
+
createQueryParams(redirectToParams, actualParams) {
|
|
4365
|
+
const res = {};
|
|
4366
|
+
Object.entries(redirectToParams).forEach(([k, v]) => {
|
|
4367
|
+
const copySourceValue = typeof v === 'string' && v[0] === ':';
|
|
4368
|
+
if (copySourceValue) {
|
|
4369
|
+
const sourceName = v.substring(1);
|
|
4370
|
+
res[k] = actualParams[sourceName];
|
|
4371
|
+
}
|
|
4372
|
+
else {
|
|
4373
|
+
res[k] = v;
|
|
4374
|
+
}
|
|
4375
|
+
});
|
|
4376
|
+
return res;
|
|
4377
|
+
}
|
|
4378
|
+
createSegmentGroup(redirectTo, group, segments, posParams) {
|
|
4379
|
+
const updatedSegments = this.createSegments(redirectTo, group.segments, segments, posParams);
|
|
4380
|
+
let children = {};
|
|
4381
|
+
Object.entries(group.children).forEach(([name, child]) => {
|
|
4382
|
+
children[name] = this.createSegmentGroup(redirectTo, child, segments, posParams);
|
|
4383
|
+
});
|
|
4384
|
+
return new UrlSegmentGroup(updatedSegments, children);
|
|
4385
|
+
}
|
|
4386
|
+
createSegments(redirectTo, redirectToSegments, actualSegments, posParams) {
|
|
4387
|
+
return redirectToSegments.map((s) => s.path[0] === ':'
|
|
4388
|
+
? this.findPosParam(redirectTo, s, posParams)
|
|
4389
|
+
: this.findOrReturn(s, actualSegments));
|
|
4390
|
+
}
|
|
4391
|
+
findPosParam(redirectTo, redirectToUrlSegment, posParams) {
|
|
4392
|
+
const pos = posParams[redirectToUrlSegment.path.substring(1)];
|
|
4393
|
+
if (!pos)
|
|
4394
|
+
throw new _RuntimeError(4001 /* RuntimeErrorCode.MISSING_REDIRECT */, (typeof ngDevMode === 'undefined' || ngDevMode) &&
|
|
4395
|
+
`Cannot redirect to '${redirectTo}'. Cannot find '${redirectToUrlSegment.path}'.`);
|
|
4396
|
+
return pos;
|
|
4397
|
+
}
|
|
4398
|
+
findOrReturn(redirectToUrlSegment, actualSegments) {
|
|
4399
|
+
let idx = 0;
|
|
4400
|
+
for (const s of actualSegments) {
|
|
4401
|
+
if (s.path === redirectToUrlSegment.path) {
|
|
4402
|
+
actualSegments.splice(idx);
|
|
4403
|
+
return s;
|
|
4404
|
+
}
|
|
4405
|
+
idx++;
|
|
4406
|
+
}
|
|
4407
|
+
return redirectToUrlSegment;
|
|
4408
|
+
}
|
|
4409
|
+
}
|
|
4410
|
+
function getRedirectResult(redirectTo, currentSnapshot, injector) {
|
|
4411
|
+
if (typeof redirectTo === 'string') {
|
|
4412
|
+
return of(redirectTo);
|
|
4413
|
+
}
|
|
4414
|
+
const redirectToFn = redirectTo;
|
|
4415
|
+
const { queryParams, fragment, routeConfig, url, outlet, params, data, title } = currentSnapshot;
|
|
4416
|
+
return wrapIntoObservable(runInInjectionContext(injector, () => redirectToFn({ params, data, queryParams, fragment, routeConfig, url, outlet, title })));
|
|
4417
|
+
}
|
|
4418
|
+
|
|
3941
4419
|
/**
|
|
3942
4420
|
* Class used to indicate there were no additional route config matches but that all segments of
|
|
3943
4421
|
* the URL were consumed during matching so the route was URL matched. When this happens, we still
|
|
@@ -3945,7 +4423,7 @@ function noLeftoversInUrl(segmentGroup, segments, outlet) {
|
|
|
3945
4423
|
*/
|
|
3946
4424
|
class NoLeftoversInUrl {
|
|
3947
4425
|
}
|
|
3948
|
-
function recognize$1(injector, configLoader, rootComponentType, config, urlTree, urlSerializer, paramsInheritanceStrategy = 'emptyOnly') {
|
|
4426
|
+
function recognize$1(injector, configLoader, rootComponentType, config, urlTree, urlSerializer, paramsInheritanceStrategy = 'emptyOnly', abortSignal) {
|
|
3949
4427
|
return new Recognizer(injector, configLoader, rootComponentType, config, urlTree, paramsInheritanceStrategy, urlSerializer).recognize();
|
|
3950
4428
|
}
|
|
3951
4429
|
const MAX_ALLOWED_REDIRECTS = 31;
|
|
@@ -4044,7 +4522,7 @@ class Recognizer {
|
|
|
4044
4522
|
return children;
|
|
4045
4523
|
}), defaultIfEmpty(null), last$1(), mergeMap((children) => {
|
|
4046
4524
|
if (children === null)
|
|
4047
|
-
return noMatch
|
|
4525
|
+
return noMatch(segmentGroup);
|
|
4048
4526
|
// Because we may have matched two outlets to the same empty path segment, we can have
|
|
4049
4527
|
// multiple activated results for the same outlet. We should merge the children of
|
|
4050
4528
|
// these results so the final return value is only one `TreeNode` per outlet.
|
|
@@ -4071,7 +4549,7 @@ class Recognizer {
|
|
|
4071
4549
|
if (noLeftoversInUrl(segmentGroup, segments, outlet)) {
|
|
4072
4550
|
return of(new NoLeftoversInUrl());
|
|
4073
4551
|
}
|
|
4074
|
-
return noMatch
|
|
4552
|
+
return noMatch(segmentGroup);
|
|
4075
4553
|
}
|
|
4076
4554
|
throw e;
|
|
4077
4555
|
}));
|
|
@@ -4090,7 +4568,7 @@ class Recognizer {
|
|
|
4090
4568
|
// This should only match if the url is `/(x:b)`.
|
|
4091
4569
|
if (getOutlet(route) !== outlet &&
|
|
4092
4570
|
(outlet === PRIMARY_OUTLET || !emptyPathMatch(rawSegment, segments, route))) {
|
|
4093
|
-
return noMatch
|
|
4571
|
+
return noMatch(rawSegment);
|
|
4094
4572
|
}
|
|
4095
4573
|
if (route.redirectTo === undefined) {
|
|
4096
4574
|
return this.matchSegmentAgainstRoute(injector, rawSegment, route, segments, outlet, parentRoute);
|
|
@@ -4098,12 +4576,12 @@ class Recognizer {
|
|
|
4098
4576
|
if (this.allowRedirects && allowRedirects) {
|
|
4099
4577
|
return this.expandSegmentAgainstRouteUsingRedirect(injector, rawSegment, routes, route, segments, outlet, parentRoute);
|
|
4100
4578
|
}
|
|
4101
|
-
return noMatch
|
|
4579
|
+
return noMatch(rawSegment);
|
|
4102
4580
|
}
|
|
4103
4581
|
expandSegmentAgainstRouteUsingRedirect(injector, segmentGroup, routes, route, segments, outlet, parentRoute) {
|
|
4104
4582
|
const { matched, parameters, consumedSegments, positionalParamSegments, remainingSegments } = match(segmentGroup, route, segments);
|
|
4105
4583
|
if (!matched)
|
|
4106
|
-
return noMatch
|
|
4584
|
+
return noMatch(segmentGroup);
|
|
4107
4585
|
// TODO(atscott): Move all of this under an if(ngDevMode) as a breaking change and allow stack
|
|
4108
4586
|
// size exceeded in production
|
|
4109
4587
|
if (typeof route.redirectTo === 'string' && route.redirectTo[0] === '/') {
|
|
@@ -4137,7 +4615,7 @@ class Recognizer {
|
|
|
4137
4615
|
}
|
|
4138
4616
|
return matchResult.pipe(switchMap((result) => {
|
|
4139
4617
|
if (!result.matched) {
|
|
4140
|
-
return noMatch
|
|
4618
|
+
return noMatch(rawSegment);
|
|
4141
4619
|
}
|
|
4142
4620
|
// If the route has an injector created from providers, we should start using that.
|
|
4143
4621
|
injector = route._injector ?? injector;
|
|
@@ -4261,8 +4739,22 @@ function getResolve(route) {
|
|
|
4261
4739
|
return route.resolve || {};
|
|
4262
4740
|
}
|
|
4263
4741
|
|
|
4264
|
-
|
|
4265
|
-
|
|
4742
|
+
const RECOGNIZE_IMPL = new InjectionToken('RECOGNIZE_IMPL', {
|
|
4743
|
+
providedIn: 'root',
|
|
4744
|
+
factory: () => {
|
|
4745
|
+
return recognize$2;
|
|
4746
|
+
},
|
|
4747
|
+
});
|
|
4748
|
+
/**
|
|
4749
|
+
* Provides a way to use the synchronous version of the recognize function using rxjs.
|
|
4750
|
+
*/
|
|
4751
|
+
function provideSometimesSyncRecognize() {
|
|
4752
|
+
return makeEnvironmentProviders([{ provide: RECOGNIZE_IMPL, useValue: recognize$1 }]);
|
|
4753
|
+
}
|
|
4754
|
+
function recognize(injector, configLoader, rootComponentType, config, serializer, paramsInheritanceStrategy, abortSignal) {
|
|
4755
|
+
// TODO(atscott): Simplify once we do not need to support both forms of recognize
|
|
4756
|
+
const recognizeImpl = injector.get(RECOGNIZE_IMPL);
|
|
4757
|
+
return mergeMap((t) => of(t).pipe(switchMap((t) => recognizeImpl(injector, configLoader, rootComponentType, config, t.extractedUrl, serializer, paramsInheritanceStrategy, abortSignal)), map(({ state: targetSnapshot, tree: urlAfterRedirects }) => {
|
|
4266
4758
|
return { ...t, targetSnapshot, urlAfterRedirects };
|
|
4267
4759
|
})));
|
|
4268
4760
|
}
|
|
@@ -4808,7 +5300,7 @@ class NavigationTransitions {
|
|
|
4808
5300
|
return Promise.resolve(t);
|
|
4809
5301
|
}),
|
|
4810
5302
|
// Recognize
|
|
4811
|
-
recognize(this.environmentInjector, this.configLoader, this.rootComponentType, router.config, this.urlSerializer, this.paramsInheritanceStrategy),
|
|
5303
|
+
recognize(this.environmentInjector, this.configLoader, this.rootComponentType, router.config, this.urlSerializer, this.paramsInheritanceStrategy, overallTransitionState.abortController.signal),
|
|
4812
5304
|
// Update URL if in `eager` update mode
|
|
4813
5305
|
tap((t) => {
|
|
4814
5306
|
overallTransitionState.targetSnapshot = t.targetSnapshot;
|
|
@@ -4945,12 +5437,7 @@ class NavigationTransitions {
|
|
|
4945
5437
|
// Ensure that if some observable used to drive the transition doesn't
|
|
4946
5438
|
// complete, the navigation still finalizes This should never happen, but
|
|
4947
5439
|
// this is done as a safety measure to avoid surfacing this error (#49567).
|
|
4948
|
-
take(1), takeUntil(
|
|
4949
|
-
const abortSignal = overallTransitionState.abortController.signal;
|
|
4950
|
-
const handler = () => subscriber.next();
|
|
4951
|
-
abortSignal.addEventListener('abort', handler);
|
|
4952
|
-
return () => abortSignal.removeEventListener('abort', handler);
|
|
4953
|
-
}).pipe(
|
|
5440
|
+
take(1), takeUntil(abortSignalToObservable(overallTransitionState.abortController.signal).pipe(
|
|
4954
5441
|
// Ignore aborts if we are already completed, canceled, or are in the activation stage (we have targetRouterState)
|
|
4955
5442
|
filter(() => !completedOrAborted && !overallTransitionState.targetRouterState), tap(() => {
|
|
4956
5443
|
this.cancelNavigationTransition(overallTransitionState, overallTransitionState.abortController.signal.reason + '', NavigationCancellationCode.Aborted);
|
|
@@ -4976,6 +5463,7 @@ class NavigationTransitions {
|
|
|
4976
5463
|
takeUntil(this.transitionAbortWithErrorSubject.pipe(tap((err) => {
|
|
4977
5464
|
throw err;
|
|
4978
5465
|
}))), finalize(() => {
|
|
5466
|
+
overallTransitionState.abortController.abort();
|
|
4979
5467
|
/* When the navigation stream finishes either through error or success,
|
|
4980
5468
|
* we set the `completed` or `errored` flag. However, there are some
|
|
4981
5469
|
* situations where we could get here without either of those being set.
|
|
@@ -5992,5 +6480,5 @@ function validateCommands(commands) {
|
|
|
5992
6480
|
}
|
|
5993
6481
|
}
|
|
5994
6482
|
|
|
5995
|
-
export { ActivatedRoute, ActivatedRouteSnapshot, ActivationEnd, ActivationStart, BaseRouteReuseStrategy, CREATE_VIEW_TRANSITION, ChildActivationEnd, ChildActivationStart, ChildrenOutletContexts, DefaultTitleStrategy, DefaultUrlSerializer, EventType, GuardsCheckEnd, GuardsCheckStart, IMPERATIVE_NAVIGATION, INPUT_BINDER, NAVIGATION_ERROR_HANDLER, NavigationCancel, NavigationCancellationCode, NavigationEnd, NavigationError, NavigationSkipped, NavigationSkippedCode, NavigationStart, NavigationTransitions, OutletContext, PRIMARY_OUTLET, ROUTER_CONFIGURATION, ROUTER_OUTLET_DATA, ROUTES, RedirectCommand, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, RouteReuseStrategy, RoutedComponentInputBinder, Router, RouterConfigLoader, RouterEvent, RouterOutlet, RouterState, RouterStateSnapshot, RoutesRecognized, Scroll, TitleStrategy, UrlHandlingStrategy, UrlSegment, UrlSegmentGroup, UrlSerializer, UrlTree, VIEW_TRANSITION_OPTIONS, afterNextNavigation, convertToParamMap, createUrlTreeFromSnapshot, createViewTransition, defaultUrlMatcher, isUrlTree, loadChildren, stringifyEvent, ɵEmptyOutletComponent };
|
|
6483
|
+
export { ActivatedRoute, ActivatedRouteSnapshot, ActivationEnd, ActivationStart, BaseRouteReuseStrategy, CREATE_VIEW_TRANSITION, ChildActivationEnd, ChildActivationStart, ChildrenOutletContexts, DefaultTitleStrategy, DefaultUrlSerializer, EventType, GuardsCheckEnd, GuardsCheckStart, IMPERATIVE_NAVIGATION, INPUT_BINDER, NAVIGATION_ERROR_HANDLER, NavigationCancel, NavigationCancellationCode, NavigationEnd, NavigationError, NavigationSkipped, NavigationSkippedCode, NavigationStart, NavigationTransitions, OutletContext, PRIMARY_OUTLET, ROUTER_CONFIGURATION, ROUTER_OUTLET_DATA, ROUTES, RedirectCommand, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, RouteReuseStrategy, RoutedComponentInputBinder, Router, RouterConfigLoader, RouterEvent, RouterOutlet, RouterState, RouterStateSnapshot, RoutesRecognized, Scroll, TitleStrategy, UrlHandlingStrategy, UrlSegment, UrlSegmentGroup, UrlSerializer, UrlTree, VIEW_TRANSITION_OPTIONS, afterNextNavigation, convertToParamMap, createUrlTreeFromSnapshot, createViewTransition, defaultUrlMatcher, isUrlTree, loadChildren, provideSometimesSyncRecognize, stringifyEvent, ɵEmptyOutletComponent };
|
|
5996
6484
|
//# sourceMappingURL=router2.mjs.map
|