refactor: enable ng update migrations for v10 (#36921)
Enables the `ng update` migrations for v10. Status for individual migrations: **undecorated-classes-with-di**. This migration dealt exlusively with inherited constructors and cases where a derived component was undecorated. In those cases, the migration added `@Directive()` or copied the inherited decorator to the derived class. We don't need to run this migration again because ngtsc throws if constructor is inherited from an undecorated class. Also ngtsc will throw if a NgModule references an undecorated class in the declarations. ***undecorated-classes-with-decorated-fields*** This migration exclusively deals with undecorated classes that use Angular features but are not decorated. Angular features include the use of lifecycle hooks or class fields with Angular decorators, such as `@Input()`. We want to re-run this migration in v10 as we will disable the compatibility code in ngtsc that detects such undecorated classes as `@Directive`. **module-with-providers**: This migration adds an explicit generic type to `ModuleWithProviders`. As of v10, the generic type is required, so we need to re-run the migration again. **renderer-to-renderer2**: We don't need to re-run that migration again as the renderer has been already removed in v9. **missing-injectable**: This migration is exclusively concerned with undecorated providers referenced in an `NgModule`. We should re-run that migration again as we don't have proper backsliding prevention for this yet. We can consider adding an error in ngtsc for v10, or v11. In either way, we should re-run the migration. **dynamic-queries**: We ran this one in v9 to reduce code complexity in projects. Instead of explicitly passing `static: false`, not passing any object literal has the same semantics. We don't need to re-run the migration again since there is no good way to prevent backsliding and we cannot always run this migration for future versions (as some apps might actually intentionally use the explicit `static: false` option). PR Close #36921
This commit is contained in:
parent
4c92cf43cf
commit
0577bf0e3e
|
@ -1,4 +1,4 @@
|
||||||
import {Component, Directive, NgModule, NgZone} from '@angular/core';
|
import {Component, Directive, NgModule, NgZone, Injectable} from '@angular/core';
|
||||||
|
|
||||||
export class ComponentTypeProvider {}
|
export class ComponentTypeProvider {}
|
||||||
export class ComponentDontNeedCase {}
|
export class ComponentDontNeedCase {}
|
||||||
|
@ -28,8 +28,7 @@ export class ProvidersTestDirective {}
|
||||||
|
|
||||||
export class TypeCase {}
|
export class TypeCase {}
|
||||||
|
|
||||||
// undecorated-classes-with-di migration will add "@Injectable" here
|
@Injectable()
|
||||||
// because the constructor is inherited in "ProvideCase".
|
|
||||||
class BaseProviderCase {
|
class BaseProviderCase {
|
||||||
constructor(zone: NgZone) {}
|
constructor(zone: NgZone) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,8 +34,6 @@ export class ProvidersTestDirective {}
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TypeCase {}
|
export class TypeCase {}
|
||||||
|
|
||||||
// undecorated-classes-with-di migration will add "@Injectable" here
|
|
||||||
// because the constructor is inherited in "ProvideCase".
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class BaseProviderCase {
|
class BaseProviderCase {
|
||||||
constructor(zone: NgZone) {}
|
constructor(zone: NgZone) {}
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
import {Directive, NgModule, NgZone} from '@angular/core';
|
|
||||||
|
|
||||||
export class BaseClass1 {
|
|
||||||
constructor(zone: NgZone) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Directive({selector: 'sel'})
|
|
||||||
export class DirectiveWithInheritedCtor extends BaseClass1 {}
|
|
||||||
|
|
||||||
export class BaseClass2 {
|
|
||||||
constructor(zone: NgZone) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Directive({selector: 'sel'})
|
|
||||||
export class DirectiveWithExplicitCtor extends BaseClass2 {
|
|
||||||
constructor(zone: NgZone) {
|
|
||||||
super(zone);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class BaseClassWithoutCtor {}
|
|
||||||
|
|
||||||
@Directive({selector: 'sel'})
|
|
||||||
export class DirectiveWithoutInheritedCtor extends BaseClassWithoutCtor {}
|
|
||||||
|
|
||||||
export class BaseClass3 {
|
|
||||||
constructor(zone: NgZone) {}
|
|
||||||
}
|
|
||||||
export class PassThroughClass extends BaseClass3 {}
|
|
||||||
|
|
||||||
@Directive({selector: 'sel'})
|
|
||||||
export class DirectiveWithInheritedCtorAndClassesInBetween extends PassThroughClass {}
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [
|
|
||||||
DirectiveWithInheritedCtor,
|
|
||||||
DirectiveWithoutInheritedCtor,
|
|
||||||
DirectiveWithExplicitCtor,
|
|
||||||
DirectiveWithInheritedCtorAndClassesInBetween,
|
|
||||||
]
|
|
||||||
})
|
|
||||||
export class TestModule {}
|
|
|
@ -1,45 +0,0 @@
|
||||||
import {Directive, NgModule, NgZone} from '@angular/core';
|
|
||||||
|
|
||||||
@Directive()
|
|
||||||
export class BaseClass1 {
|
|
||||||
constructor(zone: NgZone) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Directive({selector: 'sel'})
|
|
||||||
export class DirectiveWithInheritedCtor extends BaseClass1 {}
|
|
||||||
|
|
||||||
export class BaseClass2 {
|
|
||||||
constructor(zone: NgZone) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Directive({selector: 'sel'})
|
|
||||||
export class DirectiveWithExplicitCtor extends BaseClass2 {
|
|
||||||
constructor(zone: NgZone) {
|
|
||||||
super(zone);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class BaseClassWithoutCtor {}
|
|
||||||
|
|
||||||
@Directive({selector: 'sel'})
|
|
||||||
export class DirectiveWithoutInheritedCtor extends BaseClassWithoutCtor {}
|
|
||||||
|
|
||||||
@Directive()
|
|
||||||
export class BaseClass3 {
|
|
||||||
constructor(zone: NgZone) {}
|
|
||||||
}
|
|
||||||
@Directive()
|
|
||||||
export class PassThroughClass extends BaseClass3 {}
|
|
||||||
|
|
||||||
@Directive({selector: 'sel'})
|
|
||||||
export class DirectiveWithInheritedCtorAndClassesInBetween extends PassThroughClass {}
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [
|
|
||||||
DirectiveWithInheritedCtor,
|
|
||||||
DirectiveWithoutInheritedCtor,
|
|
||||||
DirectiveWithExplicitCtor,
|
|
||||||
DirectiveWithInheritedCtorAndClassesInBetween,
|
|
||||||
]
|
|
||||||
})
|
|
||||||
export class TestModule {}
|
|
|
@ -49,6 +49,10 @@ export class MyCompBase extends MyCompSuperBase {}
|
||||||
})
|
})
|
||||||
export class MyComp extends MyCompBase {}
|
export class MyComp extends MyCompBase {}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-comp',
|
||||||
|
template: '',
|
||||||
|
})
|
||||||
export class WrappedMyComp extends MyComp {}
|
export class WrappedMyComp extends MyComp {}
|
||||||
|
|
||||||
@NgModule({declarations: [MyComp, WrappedMyComp]})
|
@NgModule({declarations: [MyComp, WrappedMyComp]})
|
||||||
|
@ -60,3 +64,15 @@ export class AbstractDir {}
|
||||||
export class DerivedAbstractDir extends AbstractDir {}
|
export class DerivedAbstractDir extends AbstractDir {}
|
||||||
|
|
||||||
export class WrappedDerivedAbstractDir extends DerivedAbstractDir {}
|
export class WrappedDerivedAbstractDir extends DerivedAbstractDir {}
|
||||||
|
|
||||||
|
export class UndecoratedService {
|
||||||
|
ngOnDestroy() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UndecoratedPipeBase {
|
||||||
|
ngOnDestroy() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WithDirectiveLifecycleHook {
|
||||||
|
ngOnInit() {}
|
||||||
|
}
|
||||||
|
|
|
@ -72,3 +72,18 @@ export class DerivedAbstractDir extends AbstractDir {}
|
||||||
|
|
||||||
@Directive()
|
@Directive()
|
||||||
export class WrappedDerivedAbstractDir extends DerivedAbstractDir {}
|
export class WrappedDerivedAbstractDir extends DerivedAbstractDir {}
|
||||||
|
|
||||||
|
// TODO: Add Angular decorator.
|
||||||
|
export class UndecoratedService {
|
||||||
|
ngOnDestroy() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add Angular decorator.
|
||||||
|
export class UndecoratedPipeBase {
|
||||||
|
ngOnDestroy() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive()
|
||||||
|
export class WithDirectiveLifecycleHook {
|
||||||
|
ngOnInit() {}
|
||||||
|
}
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
import {Directive, NgModule, NgZone} from '@angular/core';
|
|
||||||
import {CheckboxControlValueAccessor} from '@angular/forms';
|
|
||||||
import {BaseComponentFromOtherFile} from './base-component';
|
|
||||||
|
|
||||||
|
|
||||||
@Directive({
|
|
||||||
selector: 'my-base-dir',
|
|
||||||
providers: [{provide: NgZone, useValue: null}]
|
|
||||||
})
|
|
||||||
export class BaseDirective {}
|
|
||||||
|
|
||||||
export class DerivedDirective extends BaseDirective {}
|
|
||||||
|
|
||||||
export class DerivedDirectiveFromNodeModules extends CheckboxControlValueAccessor {}
|
|
||||||
|
|
||||||
export class DerivedComponentFromOtherSourceFile extends BaseComponentFromOtherFile {}
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [
|
|
||||||
DerivedDirective,
|
|
||||||
DerivedDirectiveFromNodeModules,
|
|
||||||
DerivedComponentFromOtherSourceFile,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
export class TestModule {}
|
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [BaseDirective],
|
|
||||||
})
|
|
||||||
export class BaseClassesModule {}
|
|
|
@ -1,62 +0,0 @@
|
||||||
import { Directive, NgModule, NgZone, Component } from '@angular/core';
|
|
||||||
import { CheckboxControlValueAccessor, NG_VALUE_ACCESSOR, NG_ASYNC_VALIDATORS } from '@angular/forms';
|
|
||||||
import { BaseComponentFromOtherFile, hostBindings } from './base-component';
|
|
||||||
|
|
||||||
|
|
||||||
@Directive({
|
|
||||||
selector: 'my-base-dir',
|
|
||||||
providers: [{provide: NgZone, useValue: null}]
|
|
||||||
})
|
|
||||||
export class BaseDirective {}
|
|
||||||
|
|
||||||
@Directive({
|
|
||||||
selector: 'my-base-dir',
|
|
||||||
providers: [{ provide: NgZone, useValue: null }]
|
|
||||||
})
|
|
||||||
export class DerivedDirective extends BaseDirective {}
|
|
||||||
|
|
||||||
@Directive({
|
|
||||||
selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]",
|
|
||||||
host: {
|
|
||||||
"(change)": "onChange($event.target.checked)",
|
|
||||||
"(blur)": "onTouched()"
|
|
||||||
},
|
|
||||||
providers: [{
|
|
||||||
provide: NG_VALUE_ACCESSOR,
|
|
||||||
useExisting: CheckboxControlValueAccessor,
|
|
||||||
multi: true
|
|
||||||
}]
|
|
||||||
})
|
|
||||||
export class DerivedDirectiveFromNodeModules extends CheckboxControlValueAccessor {}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'base-comp',
|
|
||||||
template: `
|
|
||||||
<span>This is the template.</span>
|
|
||||||
`,
|
|
||||||
host: hostBindings,
|
|
||||||
providers: [
|
|
||||||
{ provide: NG_ASYNC_VALIDATORS, useValue: null },
|
|
||||||
],
|
|
||||||
// The following fields were copied from the base class,
|
|
||||||
// but could not be updated automatically to work in the
|
|
||||||
// new file location. Please add any required imports for
|
|
||||||
// the properties below:
|
|
||||||
styleUrls: nonExportedStyleUrlsVar
|
|
||||||
})
|
|
||||||
export class DerivedComponentFromOtherSourceFile extends BaseComponentFromOtherFile {}
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [
|
|
||||||
DerivedDirective,
|
|
||||||
DerivedDirectiveFromNodeModules,
|
|
||||||
DerivedComponentFromOtherSourceFile,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
export class TestModule {}
|
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [BaseDirective],
|
|
||||||
})
|
|
||||||
export class BaseClassesModule {}
|
|
|
@ -18,8 +18,8 @@ const cliBinPath = path.join(projectDir, 'node_modules/@angular/cli/bin/ng');
|
||||||
|
|
||||||
const expectationFiles = glob.sync('**/*_expected.ts', {cwd: projectDir});
|
const expectationFiles = glob.sync('**/*_expected.ts', {cwd: projectDir});
|
||||||
|
|
||||||
const fromVersion = '8.0.0';
|
const fromVersion = '9.0.0';
|
||||||
const toVersion = '9.0.0';
|
const toVersion = '10.0.0';
|
||||||
// Note that we need to specify "--allow-dirty" as the repository will become dirty
|
// Note that we need to specify "--allow-dirty" as the repository will become dirty
|
||||||
// if dependencies for the integration test are installed (i.e. modified lock files)
|
// if dependencies for the integration test are installed (i.e. modified lock files)
|
||||||
const updateCommandArgs = ['@angular/core', '--migrate-only', '--from', fromVersion,
|
const updateCommandArgs = ['@angular/core', '--migrate-only', '--from', fromVersion,
|
||||||
|
|
|
@ -20,30 +20,30 @@
|
||||||
"description": "Renderer to Renderer2 migration. As of Angular 9, the Renderer class is no longer available. Renderer2 should be used instead. Read more about this here: https://v9.angular.io/guide/migration-renderer",
|
"description": "Renderer to Renderer2 migration. As of Angular 9, the Renderer class is no longer available. Renderer2 should be used instead. Read more about this here: https://v9.angular.io/guide/migration-renderer",
|
||||||
"factory": "./migrations/renderer-to-renderer2/index"
|
"factory": "./migrations/renderer-to-renderer2/index"
|
||||||
},
|
},
|
||||||
"migration-v9-missing-injectable": {
|
|
||||||
"version": "9.0.0-beta",
|
|
||||||
"description": "Missing @Injectable and incomplete provider definition migration. In Angular 9, enforcement of @Injectable decorators for DI is a bit stricter and incomplete provider definitions behave differently. Read more about this here: https://v9.angular.io/guide/migration-injectable",
|
|
||||||
"factory": "./migrations/missing-injectable/index"
|
|
||||||
},
|
|
||||||
"migration-v9-undecorated-classes-with-di": {
|
"migration-v9-undecorated-classes-with-di": {
|
||||||
"version": "9.0.0-beta",
|
"version": "9.0.0-beta",
|
||||||
"description": "Undecorated classes with DI migration. As of Angular 9, it is no longer supported to use Angular DI on a class that does not have an Angular decorator. Read more about this here: https://v9.angular.io/guide/migration-undecorated-classes",
|
"description": "Undecorated classes with DI migration. As of Angular 9, it is no longer supported to use Angular DI on a class that does not have an Angular decorator. Read more about this here: https://v9.angular.io/guide/migration-undecorated-classes",
|
||||||
"factory": "./migrations/undecorated-classes-with-di/index"
|
"factory": "./migrations/undecorated-classes-with-di/index"
|
||||||
},
|
},
|
||||||
"migration-v9-undecorated-classes-with-decorated-fields": {
|
|
||||||
"version": "9.0.0-beta",
|
|
||||||
"description": "Undecorated classes with decorated fields migration. As of Angular 9, it is no longer supported to have Angular field decorators on a class that does not have an Angular decorator. Read more about this here: https://v9.angular.io/guide/migration-undecorated-classes",
|
|
||||||
"factory": "./migrations/undecorated-classes-with-decorated-fields/index"
|
|
||||||
},
|
|
||||||
"migration-v9-dynamic-queries": {
|
"migration-v9-dynamic-queries": {
|
||||||
"version": "9-beta",
|
"version": "9-beta",
|
||||||
"description": "Static flag migration. Removes the `static` flag from dynamic queries. As of Angular 9, the \"static\" flag defaults to false and is no longer required for your view and content queries. Read more about this here: https://v9.angular.io/guide/migration-dynamic-flag",
|
"description": "Static flag migration. Removes the `static` flag from dynamic queries. As of Angular 9, the \"static\" flag defaults to false and is no longer required for your view and content queries. Read more about this here: https://v9.angular.io/guide/migration-dynamic-flag",
|
||||||
"factory": "./migrations/dynamic-queries/index"
|
"factory": "./migrations/dynamic-queries/index"
|
||||||
},
|
},
|
||||||
"migration-v9-module-with-providers": {
|
"migration-v10-missing-injectable": {
|
||||||
"version": "9.0.0-beta",
|
"version": "10.0.0-beta",
|
||||||
"description": "ModuleWithProviders migration. In Angular 9, the ModuleWithProviders type without a generic has been deprecated. This migration adds the generic where it is missing. Read more about this here: https://v9.angular.io/guide/migration-module-with-providers",
|
"description": "Missing @Injectable and incomplete provider definition migration. As of Angular 9, enforcement of @Injectable decorators for DI is a bit stricter and incomplete provider definitions behave differently. Read more about this here: https://v9.angular.io/guide/migration-injectable",
|
||||||
|
"factory": "./migrations/missing-injectable/index"
|
||||||
|
},
|
||||||
|
"migration-v10-module-with-providers": {
|
||||||
|
"version": "10.0.0-beta",
|
||||||
|
"description": "ModuleWithProviders migration. As of Angular 10, the ModuleWithProviders type requires a generic. This migration adds the generic where it is missing. Read more about this here: https://v10.angular.io/guide/migration-module-with-providers",
|
||||||
"factory": "./migrations/module-with-providers/index"
|
"factory": "./migrations/module-with-providers/index"
|
||||||
|
},
|
||||||
|
"migration-v10-undecorated-classes-with-decorated-fields": {
|
||||||
|
"version": "10.0.0-beta",
|
||||||
|
"description": "Undecorated classes with Angular features migration. In version 10, classes that use Angular features and do not have an Angular decorator are no longer supported. Read more about this here: https://v10.angular.io/guide/migration-undecorated-classes",
|
||||||
|
"factory": "./migrations/undecorated-classes-with-decorated-fields/index"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ describe('Missing injectable migration', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function runMigration() {
|
function runMigration() {
|
||||||
return runner.runSchematicAsync('migration-v9-missing-injectable', {}, tree).toPromise();
|
return runner.runSchematicAsync('migration-v10-missing-injectable', {}, tree).toPromise();
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('NgModule', () => createTests('NgModule', 'providers'));
|
describe('NgModule', () => createTests('NgModule', 'providers'));
|
||||||
|
|
|
@ -230,6 +230,6 @@ describe('ModuleWithProviders migration', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function runMigration() {
|
function runMigration() {
|
||||||
return runner.runSchematicAsync('migration-v9-module-with-providers', {}, tree).toPromise();
|
return runner.runSchematicAsync('migration-v10-module-with-providers', {}, tree).toPromise();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -383,7 +383,7 @@ describe('Undecorated classes with decorated fields migration', () => {
|
||||||
|
|
||||||
function runMigration() {
|
function runMigration() {
|
||||||
return runner
|
return runner
|
||||||
.runSchematicAsync('migration-v9-undecorated-classes-with-decorated-fields', {}, tree)
|
.runSchematicAsync('migration-v10-undecorated-classes-with-decorated-fields', {}, tree)
|
||||||
.toPromise();
|
.toPromise();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue