From 6b267482d7ba9ec12d0a13eb7bba85a916d0e242 Mon Sep 17 00:00:00 2001 From: JoostK Date: Sun, 20 Oct 2019 23:36:04 +0200 Subject: [PATCH] feat(ngcc): enable migrations to apply schematics to libraries (#33362) When upgrading an Angular application to a new version using the Angular CLI, built-in schematics are being run to update user code from deprecated patterns to the new way of working. For libraries that have been built for older versions of Angular however, such schematics have not been executed which means that deprecated code patterns may still be present, potentially resulting in incorrect behavior. Some of the logic of schematics has been ported over to ngcc migrations, which are automatically run on libraries. These migrations achieve the same goal of the regular schematics, but operating on published library sources instead of used code. PR Close #33362 --- .../ngcc/src/analysis/decoration_analyzer.ts | 49 +++++++++++-------- .../test/analysis/decoration_analyzer_spec.ts | 4 +- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts b/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts index 1a36eb621c..36d3c512ce 100644 --- a/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts +++ b/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts @@ -7,6 +7,7 @@ */ import {ConstantPool} from '@angular/compiler'; import * as ts from 'typescript'; + import {BaseDefDecoratorHandler, ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ReferencesRegistry, ResourceLoader} from '../../../src/ngtsc/annotations'; import {CycleAnalyzer, ImportGraph} from '../../../src/ngtsc/cycles'; import {isFatalDiagnosticError} from '../../../src/ngtsc/diagnostics'; @@ -18,9 +19,12 @@ import {ClassDeclaration} from '../../../src/ngtsc/reflection'; import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../../src/ngtsc/scope'; import {CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence} from '../../../src/ngtsc/transform'; import {NgccClassSymbol, NgccReflectionHost} from '../host/ngcc_host'; -import {Migration, MigrationHost} from '../migrations/migration'; +import {Migration} from '../migrations/migration'; +import {MissingInjectableMigration} from '../migrations/missing_injectable_migration'; +import {UndecoratedParentMigration} from '../migrations/undecorated_parent_migration'; import {EntryPointBundle} from '../packages/entry_point_bundle'; import {isDefined} from '../utils'; + import {DefaultMigrationHost} from './migration_host'; import {AnalyzedClass, AnalyzedFile, CompiledClass, CompiledFile, DecorationAnalyses} from './types'; import {analyzeDecorators, isWithinPackage} from './util'; @@ -100,7 +104,7 @@ export class DecorationAnalyzer { this.reflectionHost, this.evaluator, this.metaRegistry, NOOP_DEFAULT_IMPORT_RECORDER, this.isCore), ]; - migrations: Migration[] = []; + migrations: Migration[] = [new UndecoratedParentMigration(), new MissingInjectableMigration()]; constructor( private fs: FileSystem, private bundle: EntryPointBundle, @@ -118,10 +122,9 @@ export class DecorationAnalyzer { .filter(sourceFile => isWithinPackage(this.packagePath, sourceFile)) .map(sourceFile => this.analyzeFile(sourceFile)) .filter(isDefined); - const migrationHost = new DefaultMigrationHost( - this.reflectionHost, this.fullMetaReader, this.evaluator, this.handlers, - this.bundle.entryPoint.path, analyzedFiles); - analyzedFiles.forEach(analyzedFile => this.migrateFile(migrationHost, analyzedFile)); + + this.applyMigrations(analyzedFiles); + analyzedFiles.forEach(analyzedFile => this.resolveFile(analyzedFile)); const compiledFiles = analyzedFiles.map(analyzedFile => this.compileFile(analyzedFile)); compiledFiles.forEach( @@ -147,21 +150,27 @@ export class DecorationAnalyzer { return analyzedClass; } - protected migrateFile(migrationHost: MigrationHost, analyzedFile: AnalyzedFile): void { - analyzedFile.analyzedClasses.forEach(({declaration}) => { - this.migrations.forEach(migration => { - try { - const result = migration.apply(declaration, migrationHost); - if (result !== null) { - this.diagnosticHandler(result); + protected applyMigrations(analyzedFiles: AnalyzedFile[]): void { + const migrationHost = new DefaultMigrationHost( + this.reflectionHost, this.fullMetaReader, this.evaluator, this.handlers, + this.bundle.entryPoint.path, analyzedFiles); + + this.migrations.forEach(migration => { + analyzedFiles.forEach(analyzedFile => { + analyzedFile.analyzedClasses.forEach(({declaration}) => { + try { + const result = migration.apply(declaration, migrationHost); + if (result !== null) { + this.diagnosticHandler(result); + } + } catch (e) { + if (isFatalDiagnosticError(e)) { + this.diagnosticHandler(e.toDiagnostic()); + } else { + throw e; + } } - } catch (e) { - if (isFatalDiagnosticError(e)) { - this.diagnosticHandler(e.toDiagnostic()); - } else { - throw e; - } - } + }); }); }); } diff --git a/packages/compiler-cli/ngcc/test/analysis/decoration_analyzer_spec.ts b/packages/compiler-cli/ngcc/test/analysis/decoration_analyzer_spec.ts index 9397c57cb5..9f63637619 100644 --- a/packages/compiler-cli/ngcc/test/analysis/decoration_analyzer_spec.ts +++ b/packages/compiler-cli/ngcc/test/analysis/decoration_analyzer_spec.ts @@ -199,10 +199,10 @@ runInEachFileSystem(() => { it('should call `apply()` on each migration for each class', () => { expect(migrationLogs).toEqual([ 'migration1:MyComponent', - 'migration2:MyComponent', 'migration1:MyDirective', - 'migration2:MyDirective', 'migration1:MyOtherComponent', + 'migration2:MyComponent', + 'migration2:MyDirective', 'migration2:MyOtherComponent', ]); });