Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions docs/TRIAGE_AND_LABELS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Triage Process and GitHub Labels for Angular

This document describes how the Angular team uses labels and milestones to triage issues on github.
This document describes how the Angular team uses labels and milestones to triage issues on GitHub.
The basic idea of the process is that caretaker only assigns a component (`comp: *`) label.
The owner of the component is then responsible for the secondary / component-level triage.

Expand Down Expand Up @@ -150,7 +150,7 @@ In addition, PRs can have the following states:
* _**Who adds it:** Any team member._
* _**Who removes it:** Any team member._

When a PR is ready for review, a review should be requested using the Reviewers interface in Github.
When a PR is ready for review, a review should be requested using the Reviewers interface in GitHub.


## PR Target
Expand All @@ -160,7 +160,7 @@ In our git workflow, we merge changes either to the `master` branch, the active
The decision about the target must be done by the PR author and/or reviewer.
This decision is then honored when the PR is being merged by the caretaker.

To communicate the target we use the following labels:
To communicate the target we use GitHub labels and only one target label may be applied to a PR.

Targeting an active release train:

Expand All @@ -176,9 +176,9 @@ Special Cases:

Notes:
- To land a change only in a patch/RC branch, without landing it in any other active release-train branch (such
as `master`), the patch/RC branch can be targeted in the Github UI with the appropriate
as `master`), the patch/RC branch can be targeted in the GitHub UI with the appropriate
`target: patch`/`target: rc` label.
- `target: lts` PRs must target the specific LTS branch they would need to merge into in the Github UI, in
- `target: lts` PRs must target the specific LTS branch they would need to merge into in the GitHub UI, in
cases which a change is desired in multiple LTS branches, individual PRs for each LTS branch must be created


Expand Down
4 changes: 2 additions & 2 deletions goldens/size-tracking/integration-payloads.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"master": {
"uncompressed": {
"runtime-es2015": 2289,
"main-es2015": 245303,
"main-es2015": 242351,
"polyfills-es2015": 36938,
"5-es2015": 751
}
Expand All @@ -49,7 +49,7 @@
"master": {
"uncompressed": {
"runtime-es2015": 2289,
"main-es2015": 221887,
"main-es2015": 218961,
"polyfills-es2015": 36723,
"5-es2015": 781
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,6 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
_reset() {
if (this.isDirty) {
let record: IterableChangeRecord_<V>|null;
let nextRecord: IterableChangeRecord_<V>|null;

for (record = this._previousItHead = this._itHead; record !== null; record = record._next) {
record._nextPrevious = record._next;
Expand All @@ -247,9 +246,8 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
}
this._additionsHead = this._additionsTail = null;

for (record = this._movesHead; record !== null; record = nextRecord) {
for (record = this._movesHead; record !== null; record = record._nextMoved) {
record.previousIndex = record.currentIndex;
nextRecord = record._nextMoved;
}
this._movesHead = this._movesTail = null;
this._removalsHead = this._removalsTail = null;
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/render3/node_manipulation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ function detachMovedView(declarationContainer: LContainer, lView: LView) {
// would be cleared and the counter decremented), we need to decrement the view counter here
// instead.
if (lView[FLAGS] & LViewFlags.RefreshTransplantedView) {
lView[FLAGS] &= ~LViewFlags.RefreshTransplantedView;
updateTransplantedViewCount(insertionLContainer, -1);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import {CommonModule} from '@angular/common';
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DoCheck, Input, TemplateRef, Type, ViewChild} from '@angular/core';
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DoCheck, Input, TemplateRef, Type, ViewChild, ViewContainerRef} from '@angular/core';
import {AfterViewChecked} from '@angular/core/src/core';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {expect} from '@angular/platform-browser/testing/src/matchers';
Expand Down Expand Up @@ -594,6 +594,40 @@ describe('change detection for transplanted views', () => {
'SheldonSheldonSheldon',
'Expected transplanted view to be refreshed even when insertion is not dirty');
});

it('should not fail when change detecting detached transplanted view', () => {
@Component({template: '<ng-template>{{incrementChecks()}}</ng-template>'})
class AppComponent {
@ViewChild(TemplateRef) templateRef!: TemplateRef<{}>;

constructor(readonly rootVref: ViewContainerRef, readonly cdr: ChangeDetectorRef) {}

checks = 0;
incrementChecks() {
this.checks++;
}
}

const fixture = TestBed.configureTestingModule({declarations: [AppComponent]})
.createComponent(AppComponent);
const component = fixture.componentInstance;
fixture.detectChanges();

const viewRef = component.templateRef.createEmbeddedView({});
// This `ViewContainerRef` is for the root view
component.rootVref.insert(viewRef);
// `detectChanges` on this `ChangeDetectorRef` will refresh this view and children, not the root
// view that has the transplanted `viewRef` inserted.
component.cdr.detectChanges();
// The template should not have been refreshed because it was inserted "above" the component so
// `detectChanges` will not refresh it.
expect(component.checks).toEqual(0);

// Detach view, manually call `detectChanges`, and verify the template was refreshed
component.rootVref.detach();
viewRef.detectChanges();
expect(component.checks).toEqual(1);
});
});

function trim(text: string|null): string {
Expand Down
13 changes: 6 additions & 7 deletions packages/router/src/apply_redirects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import {Injector, NgModuleRef} from '@angular/core';
import {defer, EmptyError, from, Observable, Observer, of} from 'rxjs';
import {EmptyError, from, Observable, Observer, of} from 'rxjs';
import {catchError, combineAll, concatMap, first, map, mergeMap, tap} from 'rxjs/operators';

import {LoadedRouterConfig, Route, Routes} from './config';
Expand Down Expand Up @@ -274,12 +274,11 @@ class ApplyRedirects {
segments: UrlSegment[]): Observable<UrlSegmentGroup> {
if (route.path === '**') {
if (route.loadChildren) {
return defer(
() => this.configLoader.load(ngModule.injector, route)
.pipe(map((cfg: LoadedRouterConfig) => {
route._loadedConfig = cfg;
return new UrlSegmentGroup(segments, {});
})));
return this.configLoader.load(ngModule.injector, route)
.pipe(map((cfg: LoadedRouterConfig) => {
route._loadedConfig = cfg;
return new UrlSegmentGroup(segments, {});
}));
}

return of(new UrlSegmentGroup(segments, {}));
Expand Down
6 changes: 3 additions & 3 deletions packages/router/src/directives/router_link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
3 changes: 1 addition & 2 deletions packages/router/src/operators/activate_routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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}'`);
Expand Down
8 changes: 5 additions & 3 deletions packages/router/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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()'?`);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/router/src/router_module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.`);
}
Expand Down
105 changes: 54 additions & 51 deletions packages/router/src/utils/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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);
Expand Down
Loading