perf(router): use `ngDevMode` to tree-shake error messages in router (#38674)

This commit adds `ngDevMode` guard to throw some errors only in dev mode
The ngDevMode flag helps to tree-shake these error messages from production
builds (in dev mode everything will work as it works right now) to decrease
production bundle size.

PR Close #38674
This commit is contained in:
Sonu Kapoor 2020-09-02 08:01:39 -04:00 committed by Andrew Kushnir
parent 281865bbcf
commit db21c4fb44
6 changed files with 66 additions and 62 deletions

View File

@ -39,7 +39,7 @@
"master": {
"uncompressed": {
"runtime-es2015": 2289,
"main-es2015": 245303,
"main-es2015": 242351,
"polyfills-es2015": 36938,
"5-es2015": 751
}
@ -49,7 +49,7 @@
"master": {
"uncompressed": {
"runtime-es2015": 2289,
"main-es2015": 221887,
"main-es2015": 218961,
"polyfills-es2015": 36723,
"5-es2015": 781
}

View File

@ -7,7 +7,7 @@
*/
import {LocationStrategy} from '@angular/common';
import {Attribute, Directive, ElementRef, HostBinding, HostListener, Input, isDevMode, OnChanges, OnDestroy, Renderer2, SimpleChanges} from '@angular/core';
import {Attribute, Directive, ElementRef, HostBinding, HostListener, Input, OnChanges, OnDestroy, Renderer2, SimpleChanges} from '@angular/core';
import {Subject, Subscription} from 'rxjs';
import {QueryParamsHandling} from '../config';
@ -206,7 +206,7 @@ export class RouterLink implements OnChanges {
*/
@Input()
set preserveQueryParams(value: boolean) {
if (isDevMode() && <any>console && <any>console.warn) {
if ((typeof ngDevMode === 'undefined' || ngDevMode) && <any>console && <any>console.warn) {
console.warn('preserveQueryParams is deprecated!, use queryParamsHandling instead.');
}
this.preserve = value;
@ -342,7 +342,7 @@ export class RouterLinkWithHref implements OnChanges, OnDestroy {
*/
@Input()
set preserveQueryParams(value: boolean) {
if (isDevMode() && <any>console && <any>console.warn) {
if ((typeof ngDevMode === 'undefined' || ngDevMode) && <any>console && <any>console.warn) {
console.warn('preserveQueryParams is deprecated, use queryParamsHandling instead.');
}
this.preserve = value;

View File

@ -6,7 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
import {isDevMode} from '@angular/core';
import {MonoTypeOperatorFunction} from 'rxjs';
import {map} from 'rxjs/operators';
@ -186,7 +185,7 @@ export class ActivateRoutes {
// Activate the outlet when it has already been instantiated
// Otherwise it will get activated from its `ngOnInit` when instantiated
context.outlet.activateWith(future, cmpFactoryResolver);
} else if (isDevMode() && console && console.warn) {
} else if ((typeof ngDevMode === 'undefined' || ngDevMode) && console && console.warn) {
console.warn(
`A router outlet has not been instantiated during routes activation. URL Segment: '${
future.snapshot._urlSegment}'`);

View File

@ -7,7 +7,7 @@
*/
import {Location, PopStateEvent} from '@angular/common';
import {Compiler, Injectable, Injector, isDevMode, NgModuleFactoryLoader, NgModuleRef, NgZone, Type, ɵConsole as Console} from '@angular/core';
import {Compiler, Injectable, Injector, NgModuleFactoryLoader, NgModuleRef, NgZone, Type, ɵConsole as Console} from '@angular/core';
import {BehaviorSubject, EMPTY, Observable, of, Subject, SubscriptionLike} from 'rxjs';
import {catchError, filter, finalize, map, switchMap, tap} from 'rxjs/operators';
@ -1077,7 +1077,8 @@ export class Router {
queryParamsHandling,
preserveFragment
} = navigationExtras;
if (isDevMode() && preserveQueryParams && <any>console && <any>console.warn) {
if ((typeof ngDevMode === 'undefined' || ngDevMode) && preserveQueryParams && <any>console &&
<any>console.warn) {
console.warn('preserveQueryParams is deprecated, use queryParamsHandling instead.');
}
const a = relativeTo || this.routerState.root;
@ -1131,7 +1132,8 @@ export class Router {
*/
navigateByUrl(url: string|UrlTree, extras: NavigationExtras = {skipLocationChange: false}):
Promise<boolean> {
if (isDevMode() && this.isNgZoneEnabled && !NgZone.isInAngularZone()) {
if (typeof ngDevMode === 'undefined' ||
ngDevMode && this.isNgZoneEnabled && !NgZone.isInAngularZone()) {
this.console.warn(
`Navigation triggered outside Angular zone, did you forget to call 'ngZone.run()'?`);
}

View File

@ -188,7 +188,7 @@ export function provideLocationStrategy(
}
export function provideForRootGuard(router: Router): any {
if (router) {
if ((typeof ngDevMode === 'undefined' || ngDevMode) && router) {
throw new Error(
`RouterModule.forRoot() called twice. Lazy loaded modules should use RouterModule.forChild() instead.`);
}

View File

@ -20,8 +20,9 @@ export function validateConfig(config: Routes, parentPath: string = ''): void {
}
function validateNode(route: Route, fullPath: string): void {
if (!route) {
throw new Error(`
if (typeof ngDevMode === 'undefined' || ngDevMode) {
if (!route) {
throw new Error(`
Invalid configuration of route '${fullPath}': Encountered undefined route.
The reason might be an extra comma.
@ -32,55 +33,57 @@ function validateNode(route: Route, fullPath: string): void {
{ path: 'detail/:id', component: HeroDetailComponent }
];
`);
}
if (Array.isArray(route)) {
throw new Error(`Invalid configuration of route '${fullPath}': Array cannot be specified`);
}
if (!route.component && !route.children && !route.loadChildren &&
(route.outlet && route.outlet !== PRIMARY_OUTLET)) {
throw new Error(`Invalid configuration of route '${
fullPath}': a componentless route without children or loadChildren cannot have a named outlet set`);
}
if (route.redirectTo && route.children) {
throw new Error(`Invalid configuration of route '${
fullPath}': redirectTo and children cannot be used together`);
}
if (route.redirectTo && route.loadChildren) {
throw new Error(`Invalid configuration of route '${
fullPath}': redirectTo and loadChildren cannot be used together`);
}
if (route.children && route.loadChildren) {
throw new Error(`Invalid configuration of route '${
fullPath}': children and loadChildren cannot be used together`);
}
if (route.redirectTo && route.component) {
throw new Error(`Invalid configuration of route '${
fullPath}': redirectTo and component cannot be used together`);
}
if (route.path && route.matcher) {
throw new Error(
`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 '${
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 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 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 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 '${
fullPath}': pathMatch can only be set to 'prefix' or 'full'`);
}
if (Array.isArray(route)) {
throw new Error(`Invalid configuration of route '${fullPath}': Array cannot be specified`);
}
if (!route.component && !route.children && !route.loadChildren &&
(route.outlet && route.outlet !== PRIMARY_OUTLET)) {
throw new Error(`Invalid configuration of route '${
fullPath}': a componentless route without children or loadChildren cannot have a named outlet set`);
}
if (route.redirectTo && route.children) {
throw new Error(`Invalid configuration of route '${
fullPath}': redirectTo and children cannot be used together`);
}
if (route.redirectTo && route.loadChildren) {
throw new Error(`Invalid configuration of route '${
fullPath}': redirectTo and loadChildren cannot be used together`);
}
if (route.children && route.loadChildren) {
throw new Error(`Invalid configuration of route '${
fullPath}': children and loadChildren cannot be used together`);
}
if (route.redirectTo && route.component) {
throw new Error(`Invalid configuration of route '${
fullPath}': redirectTo and component cannot be used together`);
}
if (route.path && route.matcher) {
throw new Error(
`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 '${
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 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 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 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 '${
fullPath}': pathMatch can only be set to 'prefix' or 'full'`);
}
}
if (route.children) {
validateConfig(route.children, fullPath);