From d751ca75967f31b9da863ed8c76a0a489bfa16c6 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Tue, 5 Nov 2019 20:25:46 +0100 Subject: [PATCH] fix(core): renderer-to-renderer2 migration not migrating methods (#33571) The `renderer-to-renderer2` migration currently does not work properly in v9 because the migration relies on the type checker for detecting references to `Renderer` from `@angular/core`. This is contradictory since the `Renderer` is no longer exported in v9 `@angular/core`. In order to make sure that the migration still works in v9, and that we can rely on the type checker for the best possible detection, we take advantage of module augmentation and in-memory add the `Renderer` export to the `@angular/core` module. PR Close #33571 --- .../migrations/renderer-to-renderer2/index.ts | 24 +++++++++++++++---- .../renderer_to_renderer2_migration_spec.ts | 1 - .../utils/typescript/compiler_host.ts | 7 ++++-- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/packages/core/schematics/migrations/renderer-to-renderer2/index.ts b/packages/core/schematics/migrations/renderer-to-renderer2/index.ts index bf8345705a..7144db130e 100644 --- a/packages/core/schematics/migrations/renderer-to-renderer2/index.ts +++ b/packages/core/schematics/migrations/renderer-to-renderer2/index.ts @@ -18,18 +18,17 @@ import {HelperFunction, getHelper} from './helpers'; import {migrateExpression, replaceImport} from './migration'; import {findCoreImport, findRendererReferences} from './util'; - +const MODULE_AUGMENTATION_FILENAME = 'ɵɵRENDERER_MIGRATION_CORE_AUGMENTATION.d.ts'; /** * Migration that switches from `Renderer` to `Renderer2`. More information on how it works: * https://hackmd.angular.io/UTzUZTnPRA-cSa_4mHyfYw */ export default function(): Rule { - return (tree: Tree, context: SchematicContext) => { + return (tree: Tree) => { const {buildPaths, testPaths} = getProjectTsConfigPaths(tree); const basePath = process.cwd(); const allPaths = [...buildPaths, ...testPaths]; - const logger = context.logger; if (!allPaths.length) { throw new SchematicsException( @@ -44,8 +43,23 @@ export default function(): Rule { function runRendererToRenderer2Migration(tree: Tree, tsconfigPath: string, basePath: string) { const parsed = parseTsconfigFile(tsconfigPath, dirname(tsconfigPath)); - const host = createMigrationCompilerHost(tree, parsed.options, basePath); - const program = ts.createProgram(parsed.fileNames, parsed.options, host); + const host = createMigrationCompilerHost(tree, parsed.options, basePath, fileName => { + // In case the module augmentation file has been requested, we return a source file that + // augments "@angular/core" to include a named export called "Renderer". This ensures that + // we can rely on the type checker for this migration in v9 where "Renderer" has been removed. + if (fileName === MODULE_AUGMENTATION_FILENAME) { + return ` + import '@angular/core'; + declare module "@angular/core" { + class Renderer {} + } + `; + } + return null; + }); + + const program = + ts.createProgram(parsed.fileNames.concat(MODULE_AUGMENTATION_FILENAME), parsed.options, host); const typeChecker = program.getTypeChecker(); const printer = ts.createPrinter(); const sourceFiles = program.getSourceFiles().filter( diff --git a/packages/core/schematics/test/renderer_to_renderer2_migration_spec.ts b/packages/core/schematics/test/renderer_to_renderer2_migration_spec.ts index adf94c84f3..c30919ea5d 100644 --- a/packages/core/schematics/test/renderer_to_renderer2_migration_spec.ts +++ b/packages/core/schematics/test/renderer_to_renderer2_migration_spec.ts @@ -35,7 +35,6 @@ describe('Renderer to Renderer2 migration', () => { })); // We need to declare the Angular symbols we're testing for, otherwise type checking won't work. writeFile('/node_modules/@angular/core/index.d.ts', ` - export declare abstract class Renderer {} export declare function forwardRef(fn: () => any): any {} `); diff --git a/packages/core/schematics/utils/typescript/compiler_host.ts b/packages/core/schematics/utils/typescript/compiler_host.ts index 7fcf339a39..25f4f083ab 100644 --- a/packages/core/schematics/utils/typescript/compiler_host.ts +++ b/packages/core/schematics/utils/typescript/compiler_host.ts @@ -10,7 +10,8 @@ import {relative} from 'path'; import * as ts from 'typescript'; export function createMigrationCompilerHost( - tree: Tree, options: ts.CompilerOptions, basePath: string): ts.CompilerHost { + tree: Tree, options: ts.CompilerOptions, basePath: string, + fakeRead?: (fileName: string) => string | null): ts.CompilerHost { const host = ts.createCompilerHost(options, true); // We need to overwrite the host "readFile" method, as we want the TypeScript @@ -18,7 +19,9 @@ export function createMigrationCompilerHost( // if we run multiple migrations we might have intersecting changes and // source files. host.readFile = fileName => { - const buffer = tree.read(relative(basePath, fileName)); + const treeRelativePath = relative(basePath, fileName); + const fakeOutput = fakeRead ? fakeRead(treeRelativePath) : null; + const buffer = fakeOutput === null ? tree.read(treeRelativePath) : fakeOutput; // Strip BOM as otherwise TSC methods (Ex: getWidth) will return an offset which // which breaks the CLI UpdateRecorder. // See: https://github.com/angular/angular/pull/30719