From ebf596cd47ccc36a693e0d2519315af9c8b7371c 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 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. --- .../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 1a36eb621cb25..36d3c512ce742 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); - } - } catch (e) { - if (isFatalDiagnosticError(e)) { - this.diagnosticHandler(e.toDiagnostic()); - } else { - throw e; + 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; + } } - } + }); }); }); } 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 9397c57cb5c9d..9f63637619e63 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', ]); });